mirror of
https://github.com/furrtek/PrecIR.git
synced 2026-03-29 09:50:27 +00:00
216 lines
6.0 KiB
Python
216 lines
6.0 KiB
Python
# Converts, compresses and transmits images to dot matrix ESLs
|
|
# 2018 furrtek - furrtek.org
|
|
# See LICENSE
|
|
|
|
import pr
|
|
import tx
|
|
from imageio.v2 import imread
|
|
import sys
|
|
|
|
bytes_per_frame = 20
|
|
bits_per_frame = bytes_per_frame * 8
|
|
|
|
def image_convert(image, color_pass):
|
|
pixels = []
|
|
for row in image:
|
|
for rgb in row:
|
|
r, g, b = rgb[0] / 255, rgb[1] / 255, rgb[2] / 255
|
|
is_red = r > 0.5 and g < 0.5 and b < 0.5
|
|
is_yellow = r > 0.5 and g > 0.5 and b < 0.5
|
|
if color_pass:
|
|
# 0 = red or yellow, 1 = use first block (black/white)
|
|
pixels.append(0 if (is_red or is_yellow) else 1)
|
|
elif is_red and color_mode:
|
|
pixels.append(1) # red: first=1, second=0
|
|
elif is_yellow and color_mode:
|
|
pixels.append(0) # yellow: first=0, second=0
|
|
else:
|
|
luma = 0.21 * r + 0.72 * g + 0.07 * b
|
|
pixels.append(0 if luma < 0.5 else 1) # 0 codes black
|
|
return pixels
|
|
|
|
def record_run(run_count):
|
|
# Convert to binary
|
|
del bits[:]
|
|
while run_count:
|
|
bits.insert(0, run_count & 1)
|
|
run_count >>= 1
|
|
# Zero length coding - 1
|
|
for b in bits[1:]:
|
|
compressed.append(0)
|
|
if len(bits):
|
|
compressed.extend(bits)
|
|
|
|
def usage():
|
|
print("img2dm - Transmits image to Dot Matrix ESL\n")
|
|
print("Usage:")
|
|
print("img2dm.py port image barcode (page color x y pp4)\n")
|
|
print(" port: serial port name (0 for ESL Blaster)")
|
|
print(" image: image file")
|
|
print(" barcode: 17-character ESL barcode data")
|
|
print(" page: page number to update (0~15), default: 0")
|
|
print(" color: 0:Black and white only, 1:Color-capable ESL, default: 0")
|
|
print(" x y: top-left position of image, default: 0 0")
|
|
print(" pp4: force slow PP4 protocol, default: 0")
|
|
exit()
|
|
|
|
arg_count = len(sys.argv)
|
|
if arg_count < 4:
|
|
usage()
|
|
|
|
if arg_count >= 5:
|
|
page = int(sys.argv[4])
|
|
if page < 0 or page > 15:
|
|
print("Page must be between 0 and 15.")
|
|
exit()
|
|
else:
|
|
page = 1
|
|
|
|
port = sys.argv[1]
|
|
pp16 = 0
|
|
|
|
# Search for connected ESL Blaster if required
|
|
if (port == "0"):
|
|
blaster_info = tx.search_esl_blaster()
|
|
if blaster_info[0] == False:
|
|
exit()
|
|
if blaster_info[2] >= 2:
|
|
pp16 = 1 # ESL Blaster FW V2 and above is PP16 compatible
|
|
|
|
# Open image file
|
|
image = imread(sys.argv[2])
|
|
width = image.shape[1]
|
|
height = image.shape[0]
|
|
|
|
# ESLs only accept images with pixel counts multiple of 8
|
|
pixel_count = width * height
|
|
if pixel_count & 7:
|
|
print("The image's pixel count (currently %d) must be a multiple of 8. Please adjust its size." % pixel_count)
|
|
exit()
|
|
|
|
# Get PLID from barcode string
|
|
PLID = pr.get_plid(sys.argv[3])
|
|
|
|
color_mode = int(sys.argv[5]) if arg_count >= 6 else 0
|
|
pos_x = int(sys.argv[6]) if arg_count >= 7 else 0
|
|
pos_y = int(sys.argv[7]) if arg_count >= 8 else 0
|
|
if arg_count >= 9:
|
|
if int(sys.argv[8]):
|
|
pp16 = 0
|
|
|
|
# Medium size is 208*112
|
|
# Large size is 296*128
|
|
print("Image is %i*%i in %s mode, please make sure that this suits your ESL's display." % (width, height, "color" if color_mode else "black and white"))
|
|
|
|
# First pass for black and white
|
|
pixels = image_convert(image, 0)
|
|
|
|
if color_mode:
|
|
# Append second pass for color, if needed
|
|
pixels += image_convert(image, 1)
|
|
|
|
size_raw = len(pixels)
|
|
|
|
# First pixel
|
|
bits = []
|
|
compressed = []
|
|
run_pixel = pixels[0]
|
|
run_count = 1
|
|
compressed.append(run_pixel)
|
|
for pixel in pixels[1:]:
|
|
if pixel == run_pixel:
|
|
# Add to run
|
|
run_count += 1
|
|
else:
|
|
# Record run
|
|
record_run(run_count)
|
|
run_count = 1
|
|
run_pixel = pixel
|
|
|
|
# Record last run
|
|
if run_count > 1:
|
|
record_run(run_count)
|
|
|
|
size_compressed = len(compressed)
|
|
|
|
# Decide on compression or not
|
|
# size_compressed = size_raw # Disable compression
|
|
if size_compressed < size_raw:
|
|
print("Compression ratio: %.1f%% (%d -> %d bytes)" % (100 - ((size_compressed * 100) / float(size_raw)), size_raw, size_compressed))
|
|
data = compressed
|
|
compression_type = 2
|
|
else:
|
|
print("Compression ratio suxx, using raw data instead")
|
|
data = pixels
|
|
compression_type = 0
|
|
|
|
# Pad data to multiple of bits_per_frame
|
|
data_size = len(data)
|
|
|
|
padding = bits_per_frame - (data_size % bits_per_frame)
|
|
for b in range(0, padding):
|
|
data.append(0)
|
|
|
|
padded_data_size = len(data)
|
|
|
|
frame_count = padded_data_size // bits_per_frame
|
|
|
|
#print("Data size: %i (%i frames)" % (data_size, frame_count))
|
|
|
|
frames = []
|
|
|
|
# Wake-up ping frame
|
|
frames.append(pr.make_ping_frame(PLID, pp16, 400))
|
|
|
|
# Parameters frame
|
|
frame = pr.make_mcu_frame(PLID, 0x05)
|
|
pr.append_word(frame, padded_data_size // 8) #data_size // 8) # Total byte count for group
|
|
frame.append(0x00) # Unused
|
|
frame.append(compression_type)
|
|
frame.append(page)
|
|
pr.append_word(frame, width)
|
|
pr.append_word(frame, height)
|
|
pr.append_word(frame, pos_x)
|
|
pr.append_word(frame, pos_y)
|
|
pr.append_word(frame, 0x0000) # Keycode
|
|
frame.append(0x88) # 0x80 = update, 0x08 = set base page
|
|
pr.append_word(frame, 0x0000) # Enabled pages (bitmap)
|
|
frame.extend([0x00, 0x00, 0x00, 0x00])
|
|
pr.terminate_frame(frame, pp16, 1)
|
|
frames.append(frame)
|
|
|
|
# Data frames
|
|
i = 0
|
|
for fr in range(0, frame_count):
|
|
frame = pr.make_mcu_frame(PLID, 0x20)
|
|
pr.append_word(frame, fr)
|
|
for by in range(0, bytes_per_frame):
|
|
v = 0
|
|
# Bit string to byte
|
|
for bi in range(0, 8):
|
|
v <<= 1
|
|
v += data[i + bi]
|
|
frame.append(v)
|
|
i += 8
|
|
pr.terminate_frame(frame, pp16, 1)
|
|
frames.append(frame)
|
|
|
|
# Refresh frame
|
|
frames.append(pr.make_refresh_frame(PLID, pp16))
|
|
|
|
# DEBUG
|
|
f = open("frames.txt", "w")
|
|
for fr in frames:
|
|
for b in fr:
|
|
f.write(format(b, '02X') + " ")
|
|
f.write("\n")
|
|
|
|
input("Place ESL in front of transmitter and press enter.")
|
|
|
|
# Send data to IR transmitter
|
|
if (port == "0"):
|
|
tx.transmit_esl_blaster(frames, pp16, blaster_info[0])
|
|
else:
|
|
tx.transmit_serial(frames, port)
|
|
print("Done. Please allow a few seconds for the ESL to refresh.")
|