How to use XBM image data in Python without referencing external file - image

Apologies if this is a duplicate; I've been searching with every combination of keywords I can think of but I can't find an article that addresses this.
I'm building a Python Tkinter application (Python 2.7.x, Win 7) which includes buttons with image data (drawn from XBM files). For example,
self._ResetIcon = tk.BitmapImage(file='ResetIcon.xbm')
self._Reset = tk.Button(self,
width=20, height=20,
image=self._ResetIcon,
command=self._reset)
Now I'm 99.9% sure there's a way to include the XBM image data as a declaration of some kind directly in the Python module itself, rather than pulling it from the external file. But I can't find anything online that describes how to do it.
Is there a way to do this?

Did some more digging via Google and found it.
http://effbot.org/tkinterbook/bitmapimage.htm
An X11 bitmap image consists of a C fragment that defines a width, a
height, and a data array containing the bitmap. To embed a bitmap in a
Python program, you can put it inside a triple-quoted string:
BITMAP = """
#define im_width 32
#define im_height 32
static char im_bits[] = {
0xaf,0x6d,0xeb,0xd6,0x55,0xdb,0xb6,0x2f,
0xaf,0xaa,0x6a,0x6d,0x55,0x7b,0xd7,0x1b,
0xad,0xd6,0xb5,0xae,0xad,0x55,0x6f,0x05,
0xad,0xba,0xab,0xd6,0xaa,0xd5,0x5f,0x93,
0xad,0x76,0x7d,0x67,0x5a,0xd5,0xd7,0xa3,
0xad,0xbd,0xfe,0xea,0x5a,0xab,0x69,0xb3,
0xad,0x55,0xde,0xd8,0x2e,0x2b,0xb5,0x6a,
0x69,0x4b,0x3f,0xb4,0x9e,0x92,0xb5,0xed,
0xd5,0xca,0x9c,0xb4,0x5a,0xa1,0x2a,0x6d,
0xad,0x6c,0x5f,0xda,0x2c,0x91,0xbb,0xf6,
0xad,0xaa,0x96,0xaa,0x5a,0xca,0x9d,0xfe,
0x2c,0xa5,0x2a,0xd3,0x9a,0x8a,0x4f,0xfd,
0x2c,0x25,0x4a,0x6b,0x4d,0x45,0x9f,0xba,
0x1a,0xaa,0x7a,0xb5,0xaa,0x44,0x6b,0x5b,
0x1a,0x55,0xfd,0x5e,0x4e,0xa2,0x6b,0x59,
0x9a,0xa4,0xde,0x4a,0x4a,0xd2,0xf5,0xaa
};
"""
To create X11 bitmaps, you can use the X11 bitmap editor provided with
most Unix systems, or draw your image in some other drawing program
and convert it to a bitmap using e.g. the Python Imaging Library.
The BitmapImage class can read X11 bitmaps from strings or text files:
bitmap = BitmapImage(data=BITMAP)
bitmap = BitmapImage(file="bitmap.xbm")

Related

extracting images from PDF with page and screen coordinate information

I want to extract images from PDFs retaining a knowledge of their content (page_number and coordinates on page). (Some tools (e.g. pdfminer) only emit image files with non-semantic names, e.g. Img0.bmp). I can do this with PDFBox (Java) but I'd ideally like a Python tool
My current (arbitrary) designs is to create filenames of the form:
image_<page>_<serial_in_page>_<x1>_<x2>__<y1>_<y2>.png
Currently pdfplumber exposes cooordinates but with a PDFStream and encoding information rather than an image. Code to convert the stream to a *.png would solve the problem.
(NOTE: the pdfplumber approach of rendering to the screen and capturing the known rectangle (which I use) is not a solution as the image is often degraded and frequently overwritten with text.)
(NOTE: I have had problems with several Python tools (pdfminer.six, PuMuPDF) extracting images as they make the background black which obscures black text, etc. PDFBox (Java) doesn't have this problem.)
Python tools are likely to have similar problems to any tools even those that require a single line to manipulate images or extract their details.
Here we can see a visual layout of all the compressed images in the file by using one command line to extract images. Here the individual object references have been converted into normal tiff or jpg (other tools may use pbm and pgm especially for OCR but the result is generally similar). The Greyscale Alpha softmask (B&W) transparency components are not necessarily tied direct to a page or an image other than by internal references, and usually appear like negatives.
What you may note is that the objects that were inserted most likely as one PNG are broken in two when injected into the PDF and their scaled placement is defined. Note that a raw PNG (whatever its source common resolution was) will retain number of dots but its scale when inserted into the PDF could be totally different horizontal and vertical, thus the only meaningful data is W x H in pixel values.
It is not trivial to overlay the mask on the RGB component when simply extracted but can allow for colour changes if desired.
So PDFbox is one of the simpler/better tools for blending to a suitable output, (as you have discovered) but for Python it is generally the top end library products that can identify the placement of the two images and combine into a suitable alpha output like a new PNG.
For many suggestions see Extract images from PDF without resampling, in python?.
Your related part question was knowing where those components are placed on each page since one image (and its alpha mask) could be placed multiple times such as a heading logo on each page. Again it is easy in a single command line to see which pages are referenced by a group of images, but to see which image is placed where requires analyzing each pages resources, again requiring a library interrogation of page contents, thus best done via power house libraries such as iText or any other like PDFtron for python.
For a related command in PyMuPDF see https://pymupdf.readthedocs.io/en/latest/page.html#Page.get_image_rects
I don't have a solution in Python but here is a small script using Ruby and HexaPDF:
require 'hexapdf'
class ImageBorderProcessor < HexaPDF::Content::Processor
def initialize(page, index)
super()
#page = page
#index = index
#count = 0
end
def paint_xobject(name)
super
xobject = resources.xobject(name)
return unless xobject[:Subtype] == :Image
w, h = xobject.width, xobject.height
llx, lly = graphics_state.ctm.evaluate(0, 0)
lrx, lry = graphics_state.ctm.evaluate(1, 0)
urx, ury = graphics_state.ctm.evaluate(1, 1)
ulx, uly = graphics_state.ctm.evaluate(0, 1)
# If the image is rotated, you will need all 4 coordinates, nut just the 2
filename = "image_#{#index}_#{#count}_#{llx}_#{urx}_#{lly}_#{ury}"
xobject.write(filename) rescue puts "Can write image #{#index}-#{#count}"
#count += 1
end
end
doc = HexaPDF::Document.open(ARGV[0])
doc.pages.each_with_index do |page, index|
processor = ImageBorderProcessor.new(page, index)
page.process_contents(processor)
end
It will iterate over all pages of the input document provided on the command line and create files using your file naming scheme. Since HexaPDF doesn't currently support writing all types of PDF images, you might get some error messages for those that can't be written.
If a supported image has an associated image mask defined, it will automatically be used to create a transparent image.
The script will output all images found, even repeated ones. This could easily be changed so that just a soft link is created for repeated images.

How does python support the display of images on canvas or label widgets?

The question is not if Python supports the display of images, but rather how it supports the display of images. By choice I am using Python 2.7, and this has Tkinter and Image libraries/modules installed. I don't have the pygame module, which seems to readily support the use of images on the graphical user interface.
To open the file or filename on the system, I am using tkFileDialog and this module works as intended. Then, I can combine the output of the tkFileDialog command with the Image module to create an image object (using the open method). Having done this, I can show my graphic using Image's show method, see below:
import Image
import Tkinter
import tkFileDialog as File
root = Tkinter.Tk()
root.title('Image Test')
root.file = File.askopenfile()
root.image = Image.open(root.file)
root.label = Tkinter.Label(image=root.image)
root.label.grid()
Python uses Image Magick to present a zoomed image, if after line 7, I use
root.image.show()
However, when I attempt to load the image to a Tkinter window using the Label (as illustrated in my code) and PhotoImage widgets (which are described to support image display) python throws a TclError and Runtime error, respectively. The messages are: it is "too early to create image" when using PhotoImage, and "image doesn't exist" when using Label (line 8 in my code). Can anyone provide assistance without suggesting that I add modules to my python (version: 2.7) install?
I am looking to work with bitmaps and jpeg/jpg/jpe images. Preferably, I would like to load the image onto the Canvas object using the create_image method. That is, if and only if I can load the image in to a PhotoImage object (no code included). I will settle for a Label with the image, that will eventually be loaded onto a Canvas.
Useful stackoverflow questions, for reference:
•How do I insert a JPEG image into a python Tkinter window?
•How to add an image in Tkinter?
•Cannot construct tkinter.PhotoImage from PIL Image
To everyone else following, it seems as though one can do just about any image file extension by opening their image using the Image module and programmatically saving that to a gif file type. Then, use the PhotoImage module to load the result to a Canvas widget.
import Tkinter, Image, tkFileDialog as File
filename = File.askopenfilename() #choose jpg
image = Image.open(filename)
image.save(fp='somename', format='GIF')
temporarygif = File.askopenfilename() #choose gift
photo = Tkinter.PhotoImage(file=temporarygif)
root = Tkinter.Tk()
root.title('Simple Image Display')
root.canvas = Tkinter. Canvas(root, options)
root.canvas.create_image(0,0,image=photo)
root.canvas.pack()
root.mainloop()
Great head turner.
Problems: the saved gif experiences some loss of color quality. It looks like a 256 color image, and not a full color image.

How to control the size of a picture using tkinter?

I want to control the size of the picture in m_image (say, I want it to be 420x380).
How do I do that?
import tkinter as tk
m_gui = tk.Tk()
m_image = tk.PhotoImage(file = 'pic.gif')
m_canvas = tk.Canvas(m_gui)
m_canvas.create_image(0, 0, image = m_image)
m_canvas.pack()
m_gui.mainloop()
It would be great if you could also give an explanation about the first two parameters of the create_image func.
Thank you
tk is a gui framework, not an image manipulation program. Two solutions:
Use an external image manipulation program to create a .bmp, .gif, or (if using tcl/tk 8.6) .png file with the size you want.
Install pillow (pip install pillow worked for me), use it to do the needed image manipulation, and use its ImageTk module to "create and modify Tkinter BitmapImage and PhotoImage objects from PIL images".

How to print a logo on labels using a Zebra printer and sending ZPL instructions to it

I would like send ZPL instructions to a Zebra printer (GK420t for now).
I'm printing 50mm x 20mm labels.
I would like a logo (small ~ 5mm x 5mm image) to be printed on the upper left corner of the label.
I would like to know the steps I should follow to do this.
I have been reading and trying a few things from the ZPL manual but I don't really understand how it works and couldn't find a working example.
It looks like I have to "load" the image into the printer first (in a so-called "storage area"/DRAM?) and then print it.
The .GRF file extension is mentioned many times in the manual.
I couldn't find the tool to convert a .PNG or .BMP image into a .GRF file.
I read that a .GRF file is an ASCII HEX representation of a graphic image... but it didn't help me do the work.
I could print the logo on the labels using the "Zebra Setup Utilities", by "Downloading Fonts and Graphics", choosing any available .MMF file, adding a .BMP picture, downloading it [to the printer] and printing a test page.
But until now, I couldn't do it using ZPL instructions.
I am also wondering what are the best dimensions I should use given the fact that I need a small image ~5mm x 5mm to be printed on the labels.
The image I printed is a 40px x 40px image.
Also, if I have to make a .GRF file from an original image what should be the type of this file (.BMP, .PNG, .JPG)?
Can you advise me how to proceed?
It sounds like you have some existing ZPL code, and all you want to do is add an image to it.
If that's the case, the easiest solution is probably to go to the Labelary online ZPL viewer, paste your ZPL into the viewer, click "Add image", and upload the image that you want to add to the ZPL.
This should modify your ZPL by adding the image ZPL commands that you need, and you can then tweak the position, etc.
Here is another option: I created my own image to .GRF converter in python. Feel free to use it.
from PIL import Image, ImageOps
import re
import itertools
import numpy as np
# Use: https://www.geeksforgeeks.org/round-to-next-greater-multiple-of-8/
def RoundUp(x, multiple_of = 8):
return ((x + 7) & (-1 * multiple_of))
def image2grf(filePath, width = None, height = None, rotate = None):
image = Image.open(filePath).convert(mode = "1")
#Resize image to desired size
if (width != None):
size = (width, height or width)
if (isinstance(size[0], float)):
size = (int(size[0] * image.width), int(size[1] * image.height))
#Size must be a multiple of 8
size = (RoundUp(size[0]), RoundUp(size[1]))
# image.thumbnail(size, Image.ANTIALIAS)
image = image.resize(size)
if (rotate != None):
image = image.rotate(rotate, expand = True)
image_asArray = np.asarray(np.asarray(image, dtype = 'int'), dtype = 'str').tolist()
bytesPerRow = len(image_asArray[0])
nibblesPerRow = bytesPerRow // 4
totalBytes = nibblesPerRow * len(image_asArray)
#Convert image to hex string
hexString = "".join(
format(int("".join(row[i*4:i*4 + 4]), 2) ^ 0xF, "x")
for row in image_asArray
for i in range(nibblesPerRow)
)
#Compose data
data = "~DGimage," + str(totalBytes // 2) + "," + str(nibblesPerRow // 2) + "," + hexString
#Save image
fileHandle = open(r"labelPicture.grf", "w")
fileHandle.write(data)
fileHandle.close()
if __name__ == '__main__':
# image2grf(r"warning.bmp")
image2grf(r"pallet_label_icons.png", rotate = 90)
Edit: I updated the code above to use my new conversion method, which produces better resolution GRF files
Just install ZebraDesigner, create a blank label, insert a image object to the template and add the required logo image.
Print to File this label (a *.prn file) and open the recently created file with Notepad++ (MS Notepad will ruin the data if opened and saved with). Find a huge string of seemingly random characters, and there is your image's data. Careful not to lose any of those characters, including the control ones, as the whole string is a textual representation of your image (as it would be if it were base64).
Tip0: Always have your ZPLII Programmer's Guide at hand, you'll need/want to check if ZebraDesigner sent the image to memory or directly to the printer buffer.
Tip1: Before adding the logo to the label and get the text, prepare the image making it greyscale (remember to check the printer's dithering configuration!) or, in my case, plain black and white (best result IMHO). The image can be colored, the ZebraDesigner will make it work for the printer converting the image to greyscale before conversion to commands and text.
I created a PHP script to convert PNG images to .GRF similar to Josh Mayberry's image2grf.py:
https://gist.github.com/thomascube/9651d6fa916124a9c52cb0d4262f2c3f
It uses PHP's GD image function and therefore can work with all the file formats GD can open. With small modifications, the Imagick extension could be used but performance seems to be better with GD.
Try codeproject's sharpzebra project. The test program that is part of project prints a graphic and I know this works at least it did on a ZM400
go to Object ==> Picture and your curser will change to something else.. when it changed go and click on the working area and a dialog box iwll apear... so on there select the image so you can see the image whant you wanna print on the printer i am using GT800 so for me i did like that hope this will helps you
Use ZebraNet Bridge Enterprise Software to convert BMP to GRF file format
I had to figure this out again today. In the ZPL code, you can output the graphic bytes for every single label (which means a lot of additional data when you're printing a few thousand labels), or you can define the image first and then refer to it.
I used an online ZPL viewer to save on the number of labels printed when testing. I used:
http://staging.advanced-technology-group.com/
and here is another that does the same:
http://labelary.com/viewer.html
These (currently) have an 'add image' function. This transfers a png to the GRF format that ZPL works with (see the other answers if you need to generate these bytes yourself).
Outputting the bytes for every label
Using the "Add image" function generates a command and the graphic bytes, which looks like:
^FO50,50^GFA,11118,11118,17,,<lots of data>
You can adjust the FO as that tells the printer where to position the graphic.
That should be fine for shorter runs / smaller pictures / you're in a hurry.
Downloading the image once and then referring
This is what I had to do, so I needed to rearrange the bytes a bit (nice pun?).
THe ^GF command stands for Graphic Field: ^GFa,b,c,d,data where
a: A|B (A, non-binary, B = binary)
b: number of bytes transmitted
c: number of bytes comprising the graphic format
d: number of bytes per row
and what I needed to do is to reformat this as ~DGR:000.GRF,11118,17,, so that I could refer to it with ^XGR:000.GRF,1,1. After the print run, I'd need to delete the graphic from memory again with: ^ID000.GRF
The properties for ~DGd:o.x,t,w,data mean
d: memory destination - R for RAM
o: image name (1-8 alphanumeric chars)
x: filename extension, always GRF
t: number of bytes in the graphic
w: number of bytes per row
So I turned:
^FO50,50^GFA,11118,11118,17,,<data>
into:
~DGR:000.GRF,11118,17,,<data>
This definition goes before the label-definition, so:
~DGR:000.GRF,11118,17,,<data>
^XA (start of label)
...
^FT360,700^XGR:000.GRF,1,1^FS <-- this outputs the graphic
...
^XZ (end of label)
^ID000.GRF

FreeImage portable float map (PFM) RGB channel order

I'm currently using FreeImage to load PFMs into a program that otherwise uses IplImages (the old data type for OpenCV). Here's a sample of what I'm doing (ignore the part about img being an array of Mats, that's related to some other code).
FIBITMAP *src;
// Load a PFM file using freeimage
src = FreeImage_Load(FIF_PFM, "test0.pfm", 0);
Mat* img;
img = new Mat[3];
// Create a copy of the image in an OpenCV matrix (using .clone() copies the data)
img[1] = Mat(FreeImage_GetHeight(src), FreeImage_GetWidth(src), CV_32FC3, FreeImage_GetScanLine(src, 0)).clone();
// Flip the image verticall because OpenCV row ordering is reverse of FreeImage
flip(img[1], img[1], 0);
// Save a copy
imwrite("OpenCV_converted_image.jpg", img[1]);
What's strange is that if I use FreeImage to load JPEGs instead by changing FIF_PFM to FIF_JPEG and CV_32FC3 to CV_8U, this works fine, i.e. the copied picture comes out unchanged. This makes me think that OpenCV and FreeImage generally agree on the ordering of RGB channels, and that the problem is related to PFMs specifically and their being a non-standardized format.
The PFMs I'm loading were written with this code (under "Local Histogram Equalization"), which appears to write them in RGB order although I could be wrong about that. It just takes the data from a MATLAB 3D matrix of doubles and dumps it into a file using fwrite. Also, if I modify that code to write PPMs instead, then view them in IrfanView, they look correct.
So, that leaves me thinking FreeImage is taking the file data to be BGR ordered on disk already which it is not, and should not be.
Any thoughts? Is there an error in FreeImage's reading of PFMs, or is there something more subtle going on here? Thanks.
Well, I never really got this one sorted out; long story short, FreeImage and OpenCV agree on color channel order (BGR) when loading most image formats, but not when loading PFMs. I can only assume that the makers of FreeImage have therefore misinterpreted the admittedly not very solidified specs for PFMs. Since I was only using FreeImage to read/write PFMs, and it was proving quite complicated to get data back into a FreeImage structure after processing with OpenCV functions, I wrote my own PFM read/write code which turned out to be very simple.

Resources