The goal of this code is to encode a 64 by 64 image in binary and store it in a txt file, and then decode and load it with another script
from PIL import Image
img = Image.open('image.png')
map = img.load()
width,height = img.size
pixellist = []
index = 0
def binaryToDecimal(n):
return int(n,2)
with open('encimg.txt','r') as f:
content = f.read()
repeatnum = len(content)/24
for i in range(int(repeatnum)):
red = content[0:8:1]
content = content[8:]
green = content[0:8:1]
content = content[8:]
blue = content[0:8:1]
content = content[8:]
pixel = [binaryToDecimal(red),binaryToDecimal(green),binaryToDecimal(blue)]
pixellist.append(pixel)
pixellist = [i for i in pixellist if i != ['','','',]]
for column in range(width):
for row in range(height):
map[column,row] =tuple(pixellist[column*row])
index += 1
img.save('newimage',format='png')
from PIL import Image
input_image = Image.open("images.png")
pixel_map = input_image.load()
width, height = input_image.size
binarylist = []
index = 0
def decimalToBinary(n):
return bin(n).replace("0b", "")
for i in range(width):
for j in range(height):
r, g, b = input_image.getpixel((i, j))
binarylist.extend([decimalToBinary(r),decimalToBinary(g),decimalToBinary(b)])
with open('encimg.txt','w') as f:
for item in binarylist:
f.write(item)
index += 1
print(index, end = '\r')
I tried to try the same code but with a 16x16 image instead, which worked, but wouldn't work when I scaled it back up to 64x64.
Your "reader" code assumes 8 binary digits per colour, but your writer/encoder doesn't produce 8 binary digits per colour.
You use:
return bin(n).replace("0b","")
which will produce 0
when X=0, rather than the 00000000
you were probably expecting.
Try the following instead
return f'{n:08b}'
Just FYI, if you haven't seen such things before they are call "f-strings" and are described here.
Also, your loops need interchanging in the encoder, so they read:
for i in range(width):
for j in range(height):
Your whole concept is grossly inefficient, but your decoder is especially bad and will take minutes for large images because you are constantly shuffling the entire variable content
to the left by 8 characters with:
content = content[8:]
You would be better to use indexing into the variable, rather than recreating it for every pixel, i.e.:
offset = 0
for i in range(int(repeatnum)):
red = content[offset:offset+8:1]
green = content[offset+8:offset+16:1]
blue = content[offset+16:offset+24:1]
offset += 24
pixel = (binaryToDecimal(red),binaryToDecimal(green),binaryToDecimal(blue))
Also, you need to make a tuple to put in map
.
Also, your population of map
miscalculates the offset into your pixel list, it needs to be:
for column in range(width):
for row in range(height):
this = pixellist[column+(width*row)]
map[column,row] = this
To be sure of being able to save palette images and images with transparency in your RGB format, you should change this line:
input_image = Image.open("images.png")
to this:
input_image = Image.open("images.png").convert('RGB')
The whole code (as hacked to work, rather than being what I would recommend is as follows for the encoder:
#!/usr/bin/env python3
from PIL import Image
input_image = Image.open("a.png").convert('RGB')
pixel_map = input_image.load()
width, height = input_image.size
binarylist = []
index = 0
def decimalToBinary(n):
return f'{n:08b}'
for j in range(height):
for i in range(width):
r, g, b = input_image.getpixel((i, j))
print(f'{r=} {g=} {b=}')
binarylist.extend([decimalToBinary(r),decimalToBinary(g),decimalToBinary(b)])
with open('encimg.txt','w') as f:
for item in binarylist:
f.write(item)
print(f'{item=}')
and as follows for the decoder:
#!/usr/bin/env python3
from PIL import Image
img = Image.open('a.png').convert('RGB')
map = img.load()
width,height = img.size
print(f'DEBUG: {width=} {height=}')
pixellist = []
index = 0
def binaryToDecimal(n):
return int(n,2)
with open('encimg.txt','r') as f:
content = f.read()
repeatnum = len(content)/24
offset = 0
for i in range(int(repeatnum)):
red = content[offset:offset+8:1]
green = content[offset+8:offset+16:1]
blue = content[offset+16:offset+24:1]
offset += 24
pixel = (binaryToDecimal(red),binaryToDecimal(green),binaryToDecimal(blue))
pixellist.append(pixel)
#pixellist = [i for i in pixellist if i != ['','','',]]
for column in range(width):
for row in range(height):
this = pixellist[column+(width*row)]
map[column,row] = this
print(f'{column=},{row=} {this=}')
img.save('result.png')
If I had to code this using lists and loops and stuff, I would probably go with something like this for the encoder:
#!/usr/bin/env python3
from PIL import Image
# Open image and ensure in RGB mode - not palette, not alpha
input_image = Image.open("a.png").convert('RGB')
with open('encimg.txt','w') as f:
# Get pixels via PIL generator
for pixel in input_image.getdata():
str = f'{pixel[0]:08b}{pixel[1]:08b}{pixel[2]:08b}'
f.write(str)
and something like this for the decoder:
#!/usr/bin/env python3
from PIL import Image
width, height = 640, 480
print(f'DEBUG: {width=} {height=}')
pixels = []
with open('encimg.txt','r') as f:
while this := f.read(24):
R = this[0:8:1]
G = this[8:16:1]
B = this[16:24:1]
pixels.append((int(R,2), int(G,2), int(B,2)))
# Create new, empty output image
result = Image.new('RGB', (width,height))
# Stuff decoded data into it
result.putdata(pixels)
result.save('result.png')