plasmo wrote:
Yes, I would like to see your Python example. I know there are online image converter as well. I can also tweak the CPLD design to be compatible with the image conversion output.
Bill
Here are some of my old Python scripts - I tuned them on a case by case basis for whatever I needed to do at the time.
First of all, for a palettized image I'm pretty sure this works fine:
Code:
from PIL import Image
im = Image.open("finch-indexed.png")
with open("finch-indexed.img", "wb") as f:
f.write(im.tobytes())
f.close()
I've created some custom palettes in GIMP such as the 4-bit RGBI palette, a 3-bit RGB palette, and a 2-bit-per-pixel one with black, white, light blue, and orange, and used them for various things. GIMP seems better at reducing colour depth than PIL.
You often need to manipulate the data before writing it, but that's fairly easy to do in Python. In my 640x480x3bpp system, I store four pixels per "byte" with the red data in a separate "plane", and the green and blue data packed together, and also store the data backwards so that the loops on the 6502 count down rather than up:
Code:
from PIL import Image
im = Image.open("finch-640x480x3bpp.png")
print(im.size)
print(im.getpalette())
reddata = []
greenbluedata = []
def pack(bit,*v):
r = 0
for x in v:
r <<= 1
if x & bit:
r += 1
return r
for y in range(im.height):
for x in range(0, im.width, 4):
v0 = im.getpixel((x,y))
v1 = im.getpixel((x+1,y))
v2 = im.getpixel((x+2,y))
v3 = im.getpixel((x+3,y))
r = pack(1, v0, v1, v2, v3)
g = pack(2, v0, v1, v2, v3)
b = pack(4, v0, v1, v2, v3)
reddata.append(r)
greenbluedata.append((g << 4) + b)
print(len(reddata), len(greenbluedata))
with open("imgfinch.dat", "wb") as fp:
fp.write(bytes(reversed(greenbluedata)))
fp.write(bytes(reversed(reddata)))
fp.close()
This last file is quite large (about 300k I think) and it gets written to an SD card rather than being embedded in the code.
But I've also used PIL for this as follows - one advantage being that you can tweak the palette in code and see what the results are like, or preview what it would look like with different hardware configurations before committing to them:
Code:
from PIL import Image
from PIL import ImagePalette
# Create a palette that matches what the hardware does
palette = []
for v in range(16):
base = 0
mag = 160
if v>=8:
base += 255-mag
r = base + mag * ((v>>2)&1)
g = base + mag * ((v>>1)&1)
b = base + mag * ((v>>0)&1)
palette.extend((r,g,b))
# Make a copy of the palette with some bias towards 0x80, so that the quantization works better
palettedata = []
bias = 12
for v in range(16):
r,g,b = palette[3*v:3*(v+1)]
r = bias + (r * (0xff-2*bias)) // 0xff
g = bias + (g * (0xff-2*bias)) // 0xff
b = bias + (b * (0xff-2*bias)) // 0xff
palettedata.extend((r,g,b))
# PIL requires 256-colour palettes
palettedata.extend([0] * (768-len(palettedata)))
# PIL has a quirky API that requires us to wrap an image around the palette in order to use it
palimage = Image.new("P", (1,1))
palimage.putpalette(palettedata)
im = Image.open("finch.jpg")
im2 = im.resize((160,100)).quantize("P", palette=palimage, kmeans=10)
im2.save("finch2.png")
# Save out a scaled-up version of the palettized image so that it's easier to see for debugging
scale = 8
im3 = Image.new("RGB", (im2.width*scale,im2.height*scale))
for y in range(im2.height):
for x in range(im2.width):
v = im2.getpixel((x,y)) & 15
for v in range(16):
r,g,b = palette[3*v:3*(v+1)]
r = bias + (r * (0xff-2*bias)) // 0xff
g = bias + (g * (0xff-2*bias)) // 0xff
b = bias + (b * (0xff-2*bias)) // 0xff
palettedata.extend((r,g,b))
r,g,b = palette[3*v:3*(v+1)]
for xx in range(scale):
for yy in range(scale):
im3.putpixel((x*scale+xx,y*scale+yy),(r,g,b))
im3.save("finch3.png")
# Save out the binary data
with open("finch.img", "wb") as f:
f.write(im2.tobytes())
f.close()
Ultimately it looks like I stopped using PIL for palettization though.