I am trying to create a prototype to print bitmap data for a text file to my LAN enabled epson pos printer TM-T88V.
While I have no problems to send text and text formatting instructions, I dont understand, what I have to do, to make my printer print the data of the Arecibo message.
first few lines:
00000010101010000000000
00101000001010000000100
10001000100010010110010
10101010101010100100100
00000000000000000000000
00000000000011000000000
00000000001101000000000
00000000001101000000000
00000000010101000000000
00000000011111000000000
00000000000000000000000
11000011100011000011000
10000000000000110010000
11010001100011000011010
11111011111011111011111
00000000000000000000000
00010000000000000000010
00000000000000000000000
00001000000000000000001
The message has 73 rows and 23 columns resulting in 1679 picture elements. Each of this elements is defined by either a 1 for black or a 0 as white and should be printed as a square of 8x8 (or 16x16) dots. the result would result in
(source: satsig.net)
From the printer's specifications:
While — as I said — the connecting and sending to the printer is no problem, I just dont get, what this instruction want to tell me. What would in the case of the Arecibo message be
What numbers do I have to send to the printer? Do I need to send every dot? What does nL, nH specify the number of dots of the image data in the horizontal direction as (nL + nH × 256). mean?
Here is my simple Python program I use for prototyping:
# -*- coding: utf-8 -*-
import struct
import socket
def sendInstructions(mySocket,l):
for x in l:
mySocket.send(struct.pack('h', *[x]),1)
def emphasizeOn(mySocket):
sendInstructions(mySocket,[27,33,48])
def emphasizeOff(mySocket):
sendInstructions(mySocket,[27,33,0])
def lineFeed(mySocket,number):
for i in range(number):
sendInstructions(mySocket,[0x0a,])
def paperCut(mySocket):
sendInstructions(mySocket,[29,86,0])
def sendText(mySocket,string):
mySocket.send(string.encode('UTF-8'))
def main():
mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
mySocket.connect(('192.168.1.15',9100))
lines = ["Hello,","World!"]
emphasizeOff(mySocket)
lineFeed(mySocket,2)
for l in lines:
if lines.index(l) == 0:
emphasizeOn(mySocket)
else:
emphasizeOff(mySocket)
sendText(mySocket,l)
lineFeed(mySocket,2)
lineFeed(mySocket,4)
paperCut(mySocket)
mySocket.close()
if __name__=="__main__":
main()
This command generates one horizontal strip of the image at a time. The strip is either 8 or 24 dots tall, depending on the value of m.
nL and nH are the low and high bytes of an integer that specifies the width in dots of the horizontal strip of image. That width is computed as nL + nH * 256, so if you wanted the image to be 550 dots wide, then nH=2 and nL=38.
The argument d is the bitmap data; if the image strip is 8 dots tall, then each byte represents one column in the strip. If the strip is 24 dots tall, then three bytes represent one column.
So let's say you have arecibo in a WxH numpy array of ints, 1 or 0. You would:
data = np.zeros((W, H), dtype=np.ubyte)
## (fill in data here)
## Use m=33 since this is apparently the only mode with
## square pixels and also the highest resolution
## (unless it prints too slowly for your liking)
m = 33
nH = W // 256 ## note this is integer division, but SO's
## syntax hilighting thinks it looks like a comment.
nL = W % 256
## Divide the array into sections with shape Wx24:
for n in range(data.shape[1] // 24):
## Note that if the image height is not a multiple of 24,
## you'll have to pad it with zeros somehow.
strip = data[:, n*24:(n+1)*24]
## Convert each strip into a string of bytes:
strip = strip.reshape(W, 3, 8)
bytes = (strip * (2**np.arange(8)[np.newaxis, np.newaxis, :])).sum(axis=2) # magic
byteString = bytes.astype(np.ubyte).tostring()
## Send the command to POS
Related
I'm trying to get lsb from the line of an image,I managed to get here:
from PIL import Image
import sys
challengeImg = Image.open('image.png')
pixels = challengeImg.load()
for x in range(2944):
red = (pixels[x,310][0])
bred = format(red,"b")
#print(green)
#print(bred)
green = (pixels[x,310][1])
bgreen = format(green,"b")
#print(bgreen)
#print(green)
Well, until then I'm fine but now my problem, I managed to create the following code:
num = 10100001
n = 0
lsb = num >> n &1
print(lsb)
It works, but only with one byte, I suppose that with for I can achieve something but I am very beginner and I have not managed to make it work, how I can do to extract the lsb from each byte in the line of pixels of the red channel (or green, I guess it's the same procedure)?
It occurs to me that I could use a dictionary to group the bits in bytes (1: 10011001, 2: 01100110 ...) and then use the loop to apply the lsb code in each byte, anyway I do not know how I can do this and i dont think it's the best way (maybe it's not even valid).
I have a .png image of 2944x1912 that contains information hidden in the least significant bits, the first code that I put is the script that I am developing, and so far what it does is get the information of the pixels of the red channel in the line 310 and transform them into binary.
The second code is the code to get the lsb of a byte which I need to implement in the first code, so the second code should somehow group all the bits in 8 and select the last one for I save in a variable, resulting in (2944/8 = 368 = 368 bytes.)
The solution that came to me might not be the most optimal. I'll look for a better solution if it does not suffice, but in the meanwhile:
num = 10100001
num_string = str(num)
lsb_string = num_string[len(num_string)-1]
lsb = int(lsb_string)
print(lsb)
# output: 1
It works, thats the code;
from PIL import Image
import sys
challengeImg = Image.open('challenge.png')
pixels = challengeImg.load()
for x in range(2944):
red = (pixels[x,310][0])
bred = format(red,"b")
#print(green)
#print(bred)
green = (pixels[x,310][1])
bgreen = format(green,"b")
#print(bgreen)
#print(green)
rnum = format(red,"b")
rnum_string = str(rnum)
rlsb_string = rnum_string[len(rnum_string)-1]
rlsb = int(rlsb_string)
print(rlsb, end="")
Thanks!
I would like to use the full width of the 18mm strips in my Brother P950NW printer for an image. At the moment, I am using ESC/P (not ESC/POS, which this printer does not seem to support), but if it's not possible with that I'm fine with any other protocol this printer supports. (Update: with Brother's Windows software, full-width printing is possible, but it uses the LPR protocol, for which there don't seem to be any Python libraries.)
I'm using the ESC/P command ESC* with density 72 (the highest available according to the printer's documentation), which only allows filling up the width in steps of 48 dots.
How do I print 200 pixels wide on a strip in ESC/P-speak an image with height 200? That should easily fit onto the strip. However, due to ESC*72 only accepting blocks of 48, everything beyond 192 is output on another strip.
Here's my demo code:
import socket
import struct
def escp(density_code=72):
stack_size_in_bytes = {72: 6}[density_code]
height = 200
width = 130
yield b'\x1bia\x00' # ESC/P command mode: ESC/P standard
yield b'\x1b#' # Initialize
yield b'\x1bim\x00\x00' # margin: 0
yield b'\x1biXE2\x00\x00\x00' # barcode margin: 0
yield b'\x1b3' + struct.pack('!B', 24) # line feed length: 24 dots (i.e. no space between lines)
for y_offset in range(0, height, 8 * stack_size_in_bytes):
yield b'\x1b*' + struct.pack('!B', density_code) + struct.pack('<H', width)
yield b'\xff' * width * stack_size_in_bytes
yield b'\x0a' # linefeed (move position 24 dots down)
yield b'\x0c' # Print start
c = socket.create_connection(('10.222.2.206', 9100))
c.sendall(b''.join(escp()))
c.close()
I'm fine with solutions in raw binary; here is the binary file and shortened hexdump generated by the above program.
Base the page 8 of the DOC,we can specify the print position before print one line, and even with image ,it will print one line by one line.ESC $can specify absolute horizontal position and ESC J will Finishes input of the current line, then moves the vertical print position forward by n/180 inch.combine this two,maybe you can using all 234 printable area.
import socket
import struct
def escp(density_code=72):
stack_size_in_bytes = {72: 6}[density_code]
height = 200
width = 130
yield b'\x1bia\x00' # ESC/P command mode: ESC/P standard
yield b'\x1b#' # Initialize
yield b'\x1bim\x00\x00' # margin: 0
yield b'\x1biXE2\x00\x00\x00' # barcode margin: 0
yield b'\x1b3' + struct.pack('!B', 24) # line feed length: 24 dots (i.e. no space between lines)
for y_offset in range(0, height, 8 * stack_size_in_bytes):
yield b'\x1b*' + struct.pack('!B', density_code) + struct.pack('<H', width)
yield b'\xff' * width * stack_size_in_bytes
# the added command ECS J
yield b'\x1b4a' + struct.pack('!B', 42)
# ESC $ is b'1b24'+struct.pack('!B', 0)+struct.pack('!B', 0)
yield b'\x0a' # linefeed (move position 24 dots down)
yield b'\x0c' # Print start
c = socket.create_connection(('10.222.2.206', 9100))
c.sendall(b''.join(escp()))
c.close()
b'\x1b4a' + struct.pack('!B', 42)isESC J Forward paper feed
ASCII: ESC J n
Decimal: 27 74 n
Hexadecimal: 1B 4A n
Parameters
0≤n≤255
Description
Finishes input of the current line, then moves the vertical print position forward by n/180 inch.
If n is less than 24, the feed amount is 24/180 inch (approximately 0.34 cm).
b'1b24'+struct.pack('!B', 0)+struct.pack('!B', 0)is ESC $ Specify absolute horizontal position
ASCII: ESC $ n1 n2
Decimal: 27 36 n1 n2
Hexadecimal: 1B 24 n1 n2
Parameters
0≤n1≤255, 0≤n2≤255
Description
Specifies an absolute print position (in units of 1/60 inch) for the next data.
An absolute print position specifies the horizontal print position from the left margin.
The next character is printed at a position (n1 + 256 * n2) / 60 inch from the left margin.
The maximum number of dots can be specified by both n1 and n2 is 1023/60 inches.
Both the Brother PT-P950NW and Borther PT-9800PCN support a "raster protocol" (code 1 instead of 0 after ESC iA). With this protocol, it is possible to print raster graphics at full width.
However, I could not find any documentation (the closest was this PDF for another printer), so I reverse-engineered it (and tried out a lot). The result is the project rasterprynt, available as a PyPi package. With rasterprynt, you can print arbitrary images, like this:
import rasterprynt
import PIL.Image
# Enter the IP address of your printer below
printer_ip = '192.168.1.123'
img1 = PIL.Image.open('example1.png')
img2 = PIL.Image.open('example2.png')
data = rasterprynt.prynt([img1, img2, img1], printer_ip)
Where am I wrong ? I want to create a basic white pict from bytes
from PIL import Image
if __name__ == "__main__":
data = [chr(1)] * 8192
data = "".join(data)
im = Image.frombytes('1', (128,64), data, 'raw')
im = im.convert("RGB")
im.save("image.png", "PNG")
But I get this:
Just use Image.new instead:
im = Image.new(mode='RGB', size=(128,64), color=(255,255,255))
If you really want to make it from bytes, it would be like this:
Image.frombytes(mode='RGB', size=(128,64), data=b'\xff'*128*64*3)
edit: Image.frombytes expects bytes, not a list of integers. To convert a list of integers to the right type, use this:
>>> bytes([0,1,2]) # Python 3
b'\x00\x01\x02'
>>> bytes(bytearray([0,1,2])) # Python 2
'\x00\x01\x02'
edit 2: mode='1' or the docs have bug (see comment thread). Assuming you have a list of zeros and ones, 1024 elements long, and you want to convert this to an 128x64 monochromatic image (one bit per pixel) then you'll have to pack the bytes manually:
bits = [int(not (y%13 and x%7)) for x in range(64) for y in range(128)]
# asymmetric grid
octets = [bits[i:i+8] for i in range(0, len(bits), 8)]
def bits2byte(bits8):
result = 0
for bit in bits8:
result <<= 1
result |= bit
return result
data = bytes(bytearray([bits2byte(octet) for octet in octets]))
im = Image.frombytes(mode='1', size=(128,64), data=data)
im.show()
Result:
In mode 1 each byte represents 8 pixels (there might be zero padding at end of each row if the width does not divide by 8). So to get a white image, you have to pass in only the byte b'\xff'
data = b'\xff' * 1024
im = Image.frombytes('1', (128,64), data)
Even if the Pillow docs say that there's one pixel per byte in this mode, that is not true for the frombytes and tobytes methods, at least.
Any other repeating input other than \xff (all white) or \x00 (all black) will give some sort of pinstripe pattern, like the one in your question.
So my program is a Stenography program, it inserts an image into another image, and I'm trying to encrypt the data before inserting it into the cover image. However, most encryption modules expect strings and I'm trying to pass integers.
I've tried converting to string then encrypting, but the encryption is full of special characters and letters so converting back to integer for insertion is impossible.
Anyone know if I can somehow encrypt an integer? It doesn't have to be very secure.
I'm trying to add the encryption in here:
for i in range(0,3):
#verify we have reached the end of our hidden file
if count >= len(Stringbits):
#convert the bits to their rgb value and appened them
for rgbValue in pixelList:
pixelnumbers1 = int(''.join(str(b) for b in rgbValue), 2)
#print pixelnumbers1
rgb_Array.append(pixelnumbers1)
pixels[x, y] = (rgb_Array[0], rgb_Array[1], rgb_Array[2])
print "Completed"
return imageObject.save(output)
I've been trying to encrypt pixelnumbers1 then add it in. But pixels[x, y] requires an integer.
Below is the rest of the code in-case:
def write(mainimage, secret, output):
#string contains the header, data and length in binary
Stringbits = dcimage.createString(secret)
imageObject = Image.open(mainimage).convert('RGB')
imageWidth, imageHeight = imageObject.size
pixels = imageObject.load()
rgbDecimal_Array = []
rgb_Array = []
count = 0
#loop through each pixel
for x in range (imageWidth):
for y in range (imageHeight):
r,g,b = pixels[x,y]
#convert each pixel into an 8 bit representation
redPixel = list(bin(r)[2:].zfill(8))
greenPixel = list(bin(g)[2:].zfill(8))
bluePixel = list(bin(b)[2:].zfill(8))
pixelList = [redPixel, greenPixel, bluePixel]
#for each of rgb
for i in range(0,3):
#verify we have reached the end of our hidden file
if count >= len(Stringbits):
#convert the bits to their rgb value and appened them
for rgbValue in pixelList:
pixelnumbers1 = int(''.join(str(b) for b in rgbValue), 2)
#print pixelnumbers1
rgb_Array.append(pixelnumbers1)
pixels[x, y] = (rgb_Array[0], rgb_Array[1], rgb_Array[2])
print "Completed"
return imageObject.save(output)
#If we haven't rached the end of the file, store a bit
else:
pixelList[i][7] = Stringbits[count]
count+=1
pixels[x, y] = dcimage.getPixel(pixelList)
You have a fundamental misunderstanding of how computers see any type of data.
You read the bytestream of a file, which looks like a string to you, but each character is actually a byte, a value from 0 to 255. It just happens that some of them are represented by conventional string characters. Try print(bytes(range(256)) to see them all. Most standard encryption functions take a byte array in and spit a byte array out. It just happens that you get more of the bytes that don't have a "simple" representation. But they are not any less bytes than what you initially fed in.
Your dcimage.py has the following:
#get the file data in binary
fileData = bytearray(open(secret, 'rb').read())#opens the binary file in read or write mode
for bits in fileData:
binDataString += bin(bits)[2:].zfill(8)#convert the file data to binary
There is nothing that stops you from doing this
fileData = open(secret, 'rb').read() # a bytes object by default
encryptedData = myEncryptionFuction(fileData) # also a bytes object
for bits in encryptedData:
# ...
VERY IMPORTANT: You add a null byte at the end of your message so your extracting sequence knows when to stop. If you compress, or encrypt, a string (or byte array), it is likely a null byte will be part of that stream, which will break your extraction sequence. In that case you want to use a header that tells your program ahead of time how many bits to extract.
By the way, bytes are already in an integer form.
>>> some_byte = b'G'
>>> some_byte[0]
71
You're better of using bitwise operations for steganography. You take bytes and instead of using bitwise operations between them and your pixels, you turn both to binary strings, slice and stitch them and then turn them back to integers.
def bytes_to_bits(stream):
for byte in stream:
for shift in range(7, -1, -1):
yield (byte >> shift) & 0x01
secret_bits = tuple(bytes_to_bits(encoded_data))
# simplified for one colour plane
for x in range(image_height):
for y in range(image_width):
# (pixel AND 254) OR bit - the first part zeroes out the lsb
pixels[x,y] = (pixels[x,y] & 0xfe) | secret_bits[count]
count += 1
# -------------------------------------
# to extract the bit from a stego pixel
bit = pixel & 0x01
Integers can be encryted by adding each digit to a random integer stream in the range 0 to 9, subtracting 10 when the sum > 9. Modulo should be avoided because of ambiguities.
I have a Client that recieves an image from a TCP Server in a TCP Client.
After this the Image is serialized and of course as bytes. Now I want to to reshape this again into an image.
However my process takes really long so I was wondering if there is maybe a more pythonic,faster way of doing so ?
The code is commented for each step, but here they are:
1.Convert it from hex binary representation to a readable string (0.002 s)
2.Split up the string after each byte couple (0.08 s)
3.Cast each value of the list to integer (0.17 s)
4.Reshape into matrix representation for red,green,blue (0.02)
5.Reshape Matrices together to form image
After looking at timings I found out that the most time is taking by step 3.
#....Some TCP stuff before, and this code is in a loop:
#Convert from hex binary to string
asstr = binascii.hexlify(data)
#Split up after each byte couple
n = 2
split = [asstr[i:i+n] for i in range(0, len(asstr), n)]
#Convert each byte couple to integer from its hex representation
asint = [];
for i in split:
asint.append(int(i,16))
#Reshape into red,green and blue
try:
red = np.asarray(asint[::3]).reshape(240,424);
green = np.asarray(asint[1::3]).reshape(240,424);
blue = np.asarray(asint[2::3]).reshape(240,424);
except ValueError:
continue;
#Reshape into an Image representation for opencv
img = np.transpose(np.asarray([red,green,blue],dtype=np.uint8),axes=(1, 2, 0))
#Show image
cv2.imshow('something',img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
If I print out data I get some "ascii-crap" in the command line which are just representations of their numbers in ascii.
After decoding them with binascci.hexlify(data) and printing it out I get the values as one huge string like "0112311a3b2b1c312..." (just an example)
Are you sure you are not making things way more complicated than necessary? Maybe I'm missing something, but with a mock bytes object of the correct length I get the same output as your five-step method in a single line:
import numpy as np
import binascii
def the_humourless_route(data):
return np.frombuffer(data, dtype=np.uint8).reshape(240, 424, 3)
def the_scenic_route(data):
#....Some TCP stuff before, and this code is in a loop:
#Convert from hex binary to string
asstr = binascii.hexlify(data)
#Split up after each byte couple
n = 2
split = [asstr[i:i+n] for i in range(0, len(asstr), n)]
#Convert each byte couple to integer from its hex representation
asint = [];
for i in split:
asint.append(int(i,16))
#Reshape into red,green and blue
try:
red = np.asarray(asint[::3]).reshape(240,424);
green = np.asarray(asint[1::3]).reshape(240,424);
blue = np.asarray(asint[2::3]).reshape(240,424);
except ValueError:
pass
#Reshape into an Image representation for opencv
img = np.transpose(np.asarray([red,green,blue],dtype=np.uint8),axes=(1, 2, 0))
return img
data = bytes(np.random.randint(0, 256, (240*424*3,)).tolist())
print(np.all(the_scenic_route(data) == the_humourless_route(data)))
Output:
True