initial version
This commit is contained in:
48
epd/epd_config.py
Normal file
48
epd/epd_config.py
Normal file
@@ -0,0 +1,48 @@
|
||||
palArr=[[[0,0,0],[255,255,255]],
|
||||
[[0,0,0],[255,255,255],[127,0,0]],
|
||||
[[0,0,0],[255,255,255],[127,127,127]],
|
||||
[[0,0,0],[255,255,255],[127,127,127],[127,0,0]],
|
||||
[[0,0,0],[255,255,255]],
|
||||
[[0,0,0],[255,255,255],[220,180,0]],
|
||||
[[0,0,0]],
|
||||
[[0,0,0],[255,255,255],[0,255,0],[0,0,255],[255,0,0],[255,255,0],[255,128,0]]];
|
||||
|
||||
epdArr=[[200,200,0],[200,200,3],[152,152,5],
|
||||
[122,250,0],[104,212,1],[104,212,5],[104,212,0],
|
||||
[176,264,0],[176,264,1],
|
||||
[128,296,0],[128,296,1],[128,296,5],[128,296,0],
|
||||
[400,300,0],[400,300,1],[400,300,5],
|
||||
[600,448,0],[600,448,1],[600,448,5],
|
||||
[640,384,0],[640,384,1],[640,384,5],
|
||||
[800,480,0],[800,480,1],[880,528,1],
|
||||
[600,448,7],[880,528,0],[280,480,0],
|
||||
[152,296,0],[648,480,1],[128,296,1],
|
||||
[200,200,1],[104,214,1],[128,296,0],
|
||||
[400,300,1],[152,296,1],[648,480,0],
|
||||
[640,400,7],[176,264,1],[122,250,0],
|
||||
[122,250,1],[240,360,0],[176,264,0],
|
||||
[122,250,0],[400,300,0],[960,680,0],
|
||||
[800,480,0],[128,296,1],[960,680,1]];
|
||||
|
||||
EPD_TYPES = {
|
||||
'1.54': 0, '1.54b': 1, '1.54c': 2,
|
||||
'2.13': 3, '2.13b': 4, '2.13c': 5, '2.13d': 6,
|
||||
'2.7': 7, '2.7b': 8,
|
||||
'2.9': 9, '2.9b': 10, '2.9c': 11, '2.9d': 12,
|
||||
'4.2': 13, '4.2b': 14, '4.2c': 15,
|
||||
'5.83': 16, '5.83b': 17, '5.83c': 18,
|
||||
'7.5': 19, '7.5b': 20, '7.5c': 21,
|
||||
'7.5 V2': 22, '7.5b V2': 23,
|
||||
'7.5b HD': 24, '5.65f': 25,
|
||||
'7.5 HD': 26, '3.7': 27, '2.66': 28,
|
||||
'5.83b V2': 29, '2.9b V3': 30,
|
||||
'1.54b V2': 31, '2.13b V3': 32,
|
||||
'2.9 V2': 33, '4.2b V2': 34,
|
||||
'2.66b': 35, '5.83 V2': 36,
|
||||
'4.01 f': 37, '2.7b V2': 38,
|
||||
'2.13 V3': 39, '2.13 B V4': 40,
|
||||
'3.52': 41, '2.7 V2': 42,
|
||||
'2.13 V4': 43, '4.2 V2': 44,
|
||||
'13.3k': 45, '4.26': 46,
|
||||
'2.9bV4': 47, '13.3b': 48
|
||||
}
|
201
epd/epd_uploader.py
Normal file
201
epd/epd_uploader.py
Normal file
@@ -0,0 +1,201 @@
|
||||
import requests
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def byteToStr(v):
|
||||
return chr((v & 0xF) + 97) + chr(((v >> 4) & 0xF) + 97)
|
||||
|
||||
def wordToStr(v):
|
||||
return byteToStr(v & 0xFF) + byteToStr((v >> 8) & 0xFF)
|
||||
|
||||
# Reimplementation of u_data from scriptD.js
|
||||
def _u_data(pixel_indices, c, px_ind_start):
|
||||
rqMsg = ''
|
||||
pxInd = px_ind_start
|
||||
max_rq_len = 1000 # Max length of rqMsg
|
||||
|
||||
if c == -1: # 16-bit values
|
||||
while pxInd < len(pixel_indices) and len(rqMsg) < max_rq_len:
|
||||
v = 0
|
||||
for i in range(0, 16, 2): # This means 8 iterations, packing 8 pixels
|
||||
if pxInd < len(pixel_indices):
|
||||
v |= (pixel_indices[pxInd] << i) # This is likely wrong, should be 4 bits per pixel
|
||||
pxInd += 1
|
||||
rqMsg += wordToStr(v)
|
||||
elif c == -2: # 7-color, 4-bit values
|
||||
while pxInd < len(pixel_indices) and len(rqMsg) < max_rq_len:
|
||||
v = 0
|
||||
for i in range(0, 16, 4): # 4 iterations, packing 4 pixels
|
||||
if pxInd < len(pixel_indices):
|
||||
v |= (pixel_indices[pxInd] << i)
|
||||
pxInd += 1
|
||||
rqMsg += wordToStr(v)
|
||||
else: # Monochrome, 1-bit values
|
||||
while pxInd < len(pixel_indices) and len(rqMsg) < max_rq_len:
|
||||
v = 0
|
||||
for i in range(8): # Packs 8 pixels into 1 byte
|
||||
if pxInd < len(pixel_indices) and pixel_indices[pxInd] != c:
|
||||
v |= (128 >> i)
|
||||
pxInd += 1
|
||||
rqMsg += byteToStr(v)
|
||||
return rqMsg, pxInd
|
||||
|
||||
# Reimplementation of u_line from scriptD.js
|
||||
def _u_line(pixel_indices, c, px_ind_start):
|
||||
rqMsg = ''
|
||||
pxInd = px_ind_start
|
||||
max_rq_len = 1000 # Max length of rqMsg
|
||||
|
||||
while len(rqMsg) < max_rq_len:
|
||||
x = 0
|
||||
while x < 122: # Processes 122 pixels at a time
|
||||
v = 0
|
||||
for i in range(8):
|
||||
if pxInd < len(pixel_indices) and x < 122:
|
||||
if pixel_indices[pxInd] != c:
|
||||
v |= (128 >> i)
|
||||
pxInd += 1
|
||||
x += 1
|
||||
else:
|
||||
break # Break if no more pixels or 122 pixels processed
|
||||
rqMsg += byteToStr(v)
|
||||
if x >= 122: # If 122 pixels processed, break inner loop
|
||||
break
|
||||
return rqMsg, pxInd
|
||||
|
||||
def upload_image(img, ip_addr, epd_ind, epdArr, palArr, getNear):
|
||||
print(f"[DEBUG] Starting upload_image for EPD index {epd_ind} to {ip_addr}")
|
||||
url_prefix = f"http://{ip_addr}/"
|
||||
w, h = img.size
|
||||
|
||||
pal_ind = epdArr[epd_ind][2]
|
||||
curPal = palArr[pal_ind]
|
||||
pixels = list(img.getdata())
|
||||
pixel_indices = [getNear(r,g,b, curPal) for r,g,b in pixels]
|
||||
|
||||
pxInd = 0
|
||||
stInd = 0
|
||||
|
||||
def u_send(cmd, next_state=False):
|
||||
nonlocal stInd
|
||||
full_url = url_prefix + cmd
|
||||
try:
|
||||
response = requests.post(full_url)
|
||||
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
|
||||
if next_state:
|
||||
stInd += 1
|
||||
return 0
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"[ERROR] Error sending command {cmd}: {e}")
|
||||
raise # Re-raise the exception to stop execution
|
||||
|
||||
def u_next():
|
||||
nonlocal pxInd
|
||||
pxInd = 0
|
||||
return u_send('NEXT_', True)
|
||||
|
||||
def u_done():
|
||||
sys.stdout.write('\n') # Ensure progress bar finishes on a new line
|
||||
print("Image upload complete!")
|
||||
return u_send('SHOW_', True)
|
||||
|
||||
def u_show(a_len, k1, k2, rqMsg_val):
|
||||
nonlocal pxInd
|
||||
total_pixels = len(pixel_indices)
|
||||
current_progress_pixels = pxInd
|
||||
|
||||
overall_percentage = (current_progress_pixels / total_pixels) * 100 if total_pixels > 0 else 0
|
||||
|
||||
bar_length = 50
|
||||
filled_length = int(bar_length * overall_percentage // 100)
|
||||
bar = '#' * filled_length + '-' * (bar_length - filled_length)
|
||||
sys.stdout.write(f'\rProgress: |{bar}| {overall_percentage:.2f}%')
|
||||
sys.stdout.flush()
|
||||
|
||||
return u_send(rqMsg_val + wordToStr(len(rqMsg_val)) + 'LOAD_', pxInd >= a_len)
|
||||
|
||||
# Main upload logic based on scriptD.js
|
||||
try:
|
||||
# Initial EPD command
|
||||
epd_cmd_char = chr(epd_ind + 97) if epd_ind < 26 else chr(epd_ind - 26 + 65)
|
||||
u_send(f'EPD{epd_cmd_char}_')
|
||||
|
||||
# Conditional logic based on epd_ind, mimicking scriptD.js
|
||||
if epd_ind in [3, 39, 43]:
|
||||
# u_line flow
|
||||
while True:
|
||||
if stInd == 0:
|
||||
rqMsg, pxInd = _u_line(pixel_indices, 0, pxInd)
|
||||
u_show(len(pixel_indices), 0, 100, rqMsg)
|
||||
elif stInd == 1:
|
||||
u_done()
|
||||
break
|
||||
elif epd_ind == 40:
|
||||
# u_line with u_next flow
|
||||
while True:
|
||||
if stInd == 0:
|
||||
rqMsg, pxInd = _u_line(pixel_indices, 0, pxInd)
|
||||
u_show(len(pixel_indices), 0, 50, rqMsg)
|
||||
elif stInd == 1:
|
||||
u_next()
|
||||
elif stInd == 2:
|
||||
rqMsg, pxInd = _u_line(pixel_indices, 3, pxInd)
|
||||
u_show(len(pixel_indices), 50, 50, rqMsg)
|
||||
elif stInd == 3:
|
||||
u_done()
|
||||
break
|
||||
elif epd_ind in [0, 3, 6, 7, 9, 12, 16, 19, 22, 26, 27, 28]:
|
||||
# u_data flow (monochrome)
|
||||
while True:
|
||||
if stInd == 0:
|
||||
rqMsg, pxInd = _u_data(pixel_indices, 0, pxInd)
|
||||
u_show(len(pixel_indices), 0, 100, rqMsg)
|
||||
elif stInd == 1:
|
||||
u_done()
|
||||
break
|
||||
elif 15 < epd_ind < 22:
|
||||
# u_data flow (16-bit values, c=-1)
|
||||
while True:
|
||||
if stInd == 0:
|
||||
rqMsg, pxInd = _u_data(pixel_indices, -1, pxInd)
|
||||
u_show(len(pixel_indices), 0, 100, rqMsg)
|
||||
elif stInd == 1:
|
||||
u_done()
|
||||
break
|
||||
elif epd_ind in [25, 37]:
|
||||
# u_data flow (7-color, c=-2)
|
||||
while True:
|
||||
if stInd == 0:
|
||||
rqMsg, pxInd = _u_data(pixel_indices, -2, pxInd)
|
||||
u_show(len(pixel_indices), 0, 100, rqMsg)
|
||||
elif stInd == 1:
|
||||
u_done()
|
||||
break
|
||||
else:
|
||||
# Default u_data flow with u_next
|
||||
while True:
|
||||
if stInd == 0:
|
||||
c_val = -1 if epd_ind in [1, 12] else 0
|
||||
rqMsg, pxInd = _u_data(pixel_indices, c_val, pxInd)
|
||||
u_show(len(pixel_indices), 0, 50, rqMsg)
|
||||
elif stInd == 1:
|
||||
u_next()
|
||||
elif stInd == 2:
|
||||
# Special handling for 7.5b V2 (epd_ind 23) red channel
|
||||
if epd_ind == 23:
|
||||
# Encode only red pixels (palette index 2)
|
||||
red_pixel_indices = [0 if p == 2 else 2 for p in pixel_indices] # Invert red channel: 0 for red, 2 for non-red
|
||||
rqMsg, pxInd = _u_data(red_pixel_indices, 0, pxInd) # Use monochrome encoding for this layer
|
||||
else:
|
||||
rqMsg, pxInd = _u_data(pixel_indices, 3, pxInd)
|
||||
u_show(len(pixel_indices), 50, 50, rqMsg)
|
||||
elif stInd == 3:
|
||||
u_done()
|
||||
break
|
||||
|
||||
print("Image uploaded successfully!")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"[ERROR] Error during image upload: {e}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] An unexpected error occurred during upload: {e}")
|
96
epd/image_processor.py
Normal file
96
epd/image_processor.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from PIL import Image
|
||||
|
||||
def getErr(r,g,b,stdCol):
|
||||
r-=stdCol[0]
|
||||
g-=stdCol[1]
|
||||
b-=stdCol[2]
|
||||
return r*r + g*g + b*b
|
||||
|
||||
def getNear(r,g,b, curPal):
|
||||
ind=0
|
||||
err=getErr(r,g,b,curPal[0])
|
||||
for i in range(1,len(curPal)):
|
||||
cur=getErr(r,g,b,curPal[i])
|
||||
if (cur<err):
|
||||
err=cur
|
||||
ind=i
|
||||
return ind
|
||||
|
||||
def addVal(c,r,g,b,k):
|
||||
return[c[0]+(r*k)/32,c[1]+(g*k)/32,c[2]+(b*k)/32]
|
||||
|
||||
def procImg(img, dW, dH, dX, dY, palInd, isLvl, isRed, palArr):
|
||||
if isRed and ((palInd & 1) == 0):
|
||||
raise ValueError('This white-black display')
|
||||
|
||||
if not isRed:
|
||||
palInd = palInd & 0xFE
|
||||
|
||||
curPal = palArr[palInd]
|
||||
|
||||
sW = img.width
|
||||
sH = img.height
|
||||
|
||||
pDst = Image.new('RGB', (dW, dH))
|
||||
pixels = pDst.load()
|
||||
|
||||
if isLvl:
|
||||
# Level-based processing
|
||||
for j in range(dH):
|
||||
y = dY + j
|
||||
for i in range(dW):
|
||||
x = dX + i
|
||||
if 0 <= x < sW and 0 <= y < sH:
|
||||
r, g, b = img.getpixel((x, y))
|
||||
color_index = getNear(r, g, b, curPal)
|
||||
pixels[i, j] = tuple(curPal[color_index])
|
||||
else:
|
||||
pixels[i, j] = tuple(curPal[(i + j) % 2]) # checkerboard for out of bounds
|
||||
else:
|
||||
# Dithering
|
||||
errArr = [[[0, 0, 0] for _ in range(dW)] for _ in range(2)]
|
||||
aInd = 0
|
||||
bInd = 1
|
||||
|
||||
for j in range(dH):
|
||||
y = dY + j
|
||||
aInd, bInd = bInd, aInd # swap buffers
|
||||
for i in range(dW):
|
||||
errArr[bInd][i] = [0,0,0]
|
||||
|
||||
for i in range(dW):
|
||||
x = dX + i
|
||||
if 0 <= x < sW and 0 <= y < sH:
|
||||
old = errArr[aInd][i]
|
||||
r_orig, g_orig, b_orig = img.getpixel((x, y))
|
||||
r = r_orig + old[0]
|
||||
g = g_orig + old[1]
|
||||
b = b_orig + old[2]
|
||||
|
||||
color_index = getNear(r, g, b, curPal)
|
||||
colVal = curPal[color_index]
|
||||
pixels[i, j] = tuple(colVal)
|
||||
|
||||
r_err = r - colVal[0]
|
||||
g_err = g - colVal[1]
|
||||
b_err = b - colVal[2]
|
||||
|
||||
# Propagate error (Floyd-Steinberg-like)
|
||||
if i == 0:
|
||||
errArr[bInd][i] = addVal(errArr[bInd][i], r_err, g_err, b_err, 7.0)
|
||||
if dW > 1:
|
||||
errArr[bInd][i+1] = addVal(errArr[bInd][i+1], r_err, g_err, b_err, 2.0)
|
||||
errArr[aInd][i+1] = addVal(errArr[aInd][i+1], r_err, g_err, b_err, 7.0)
|
||||
elif i == dW - 1:
|
||||
errArr[bInd][i-1] = addVal(errArr[bInd][i-1], r_err, g_err, b_err, 7.0)
|
||||
errArr[bInd][i] = addVal(errArr[bInd][i], r_err, g_err, b_err, 9.0)
|
||||
else:
|
||||
errArr[bInd][i-1] = addVal(errArr[bInd][i-1], r_err, g_err, b_err, 3.0)
|
||||
errArr[bInd][i] = addVal(errArr[bInd][i], r_err, g_err, b_err, 5.0)
|
||||
errArr[bInd][i+1] = addVal(errArr[bInd][i+1], r_err, g_err, b_err, 1.0)
|
||||
errArr[aInd][i+1] = addVal(errArr[aInd][i+1], r_err, g_err, b_err, 7.0)
|
||||
|
||||
else:
|
||||
pixels[i, j] = tuple(curPal[(i + j) % 2]) # checkerboard
|
||||
|
||||
return pDst
|
Reference in New Issue
Block a user