So I was working on this school project (I know really basic programming, and python is the only language I know) where I need to change my pixel colour to encode a message in a picture, but PIL's putpixel doesn't seem to be working, here is my code.
P.S.: all my PIL information is self taught and English isn't my main language so if you could talk simplified I'd be grateful
from PIL import Image
e=input('file and location? ')
img=Image.open(e)
pmap=img.load()
imy=img.height
imx=img.width
if int(input('1 for encoding, 2 for decoding '))==1:
a=input('Your message? ')
for i in range(len(a)):
r , g , b=img.getpixel((i+10,imy//2))
img.putpixel((i+10,imy//2),(ord(a[i]),g,b))
r,g,b=img.getpixel((len(a)+10,imy//2))
img.putpixel((len(a)+10,imy//2),(999,g,b)) #999 is the stop code in decoding
else:
r=u=0
m=''
while r!=999:
r , g , b=img.getpixel((10+u,imy//2))
m+=chr(r)
u+=1
print(m[:len(a)-1])
img.save(e)
please bare in mind that I'm not looking to make a visual difference and I've already done debugging.There are also no errors,putpixel is not working for some reason though.
as I said, I'm new to programming, so sorry if it includes stupid mistakes.
After using your code and trying it out on an image, putpixel is working as expected. The change in the pixels is very hard to see and that may be why you believe that it isn't working. Believe me, it is working, you just can't see it.
However, there are two problems I see with your code:
1) 999 is not encodable
999 can not be encoded in a single pixel. The maximum value for a pixel is 255 (The range is 0-255). You need to choose a different stop code/sequence. I recommend changing the stop code to 255.
2) When decoding, a has never been defined
You need to get the length of the message by another means. I suggest doing this with a counter:
counter = 0
while something:
counter += 1
# do something with count here
All in all, a working version of your code would look like:
e=input('file and location? ')
img=Image.open(e)
pmap=img.load()
imy=img.height
imx=img.width
if int(input('1 for encoding, 2 for decoding '))==1:
a=input('Your message? ')
for i in range(len(a)):
r , g , b= img.getpixel((i+10,imy//2))
img.putpixel((i+10,imy//2),(ord(a[i]),g,b))
r,g,b=img.getpixel((len(a)+10,imy//2))
img.putpixel((len(a)+10,imy//2),(255,g,b)) #255 is the stop code in decoding
else:
r=u=0
m=''
message_length=0
while r!=255:
message_length+=1
r , g , b=img.getpixel((10+u,imy//2))
m+=chr(r)
u+=1
print(m[:message_length-1])
img.save(e)
The difference is there, but it's just a few single pixels. If I calculate the difference between original and new image, you'll see it in the middle left, stored in test2.png. In order to enhance contrast I have "equalized" the image.
from PIL import Image, ImageChops, ImageOps
img=Image.open("image.jpg")
pmap=img.load()
img2=img.copy()
imy=img.height
imx=img.width
if int(input('1 for encoding, 2 for decoding '))==1:
a=input('Your message? ')
for i in range(len(a)):
r , g , b=img.getpixel((i+10,imy//2))
img.putpixel((i+10,imy//2),(ord(a[i]),g,b))
r,g,b=img.getpixel((len(a)+10,imy//2))
img.putpixel((len(a)+10,imy//2),(999,g,b)) #999 is the stop code in decoding
else:
r=u=0
m=''
while r!=999:
r , g , b=img.getpixel((10+u,imy//2))
m+=chr(r)
u+=1
print(m[:len(a)-1])
img.save("test.png")
img3=ImageChops.difference(img, img2)
img3=ImageOps.equalize(img3)
img3.save("test2.png")
This is the result:
Related
I am doing some simple DSP and playing the original and the filtered. I am using librosa to load the mp3 files in as arrays.
Here is the code of me loading the files in.
PATH = os.getcwd() + "/audio files"
to_load_array = os.listdir(PATH)
random_mp3 = to_load_array[randint(0, len(to_load_array) - 1)]
random_mp3_path = "audio files/" + random_mp3
data, sr = librosa.load(random_mp3_path, duration = MP3(random_mp3_path).info.length, mono = False, sr = 44100)
Else where in my program I have a GUI with a button that allows you to switch between playing the filtered version and and the unfiltered version. I am doing this by using different channels in pygame.mixer playing them both at the same time but alternating which one is being muted. I am using pygame.mixer because it allows for this simultaneous playing feature.
Currently I am filtering and then exporting the filtered file as an mp3 and then playing the mp3. This feels like too many I/O operations and I would like to speed up the program a little bit by playing the filtered and unfiltered arrays directly from within python.
I found the function pygame.mixer.sndarray.make_sound which is suppused to turn your array into a sound object that can be played on the mixer.play() function.
This is how I am using the function:
def setSoundObjects(self, original, filtered):
print('orignal data shape: ',original.shape)
print('format (From get_init): ', pygame.mixer.get_init()[2])
print("this is the original data: ", original)
original = numpy.int16(original * (2 ** 15))
filtered = numpy.int16(filtered * (2 ** 15))
print("this is the data after numpy conversion: ", original)
#set the sound objects
print("Number of channels: ", pygame.mixer.get_num_channels())
self.original = pygame.sndarray.make_sound(original)
self.filtered = pygame.sndarray.make_sound(filtered)
However, whenever I try to use it I keep getting this error:
self.original = pygame.mixer.Sound(array = original)
ValueError: Array depth must match number of mixer channels
here is the output from what I am printing to the terminal to try and debug this:
format (From get_init): 2
this is the original data: [[ 0. 0. 0. ... -0.00801174 -0.00784447
-0.01003712]
[ 0. 0. 0. ... -0.00695544 -0.00674583
-0.00865676]]
this is the data after numpy conversion: [[ 0 0 0 ... -262 -257 -328]
[ 0 0 0 ... -227 -221 -283]]
Number of channels: 8
I was trying the numpy conversion because of this post but it didn't help.
Any help would be greatly appreciated thank you!
let me know if I should add anything else to help clarify what im doing. I did not include the entire code because the program is quite large already.
This question is different than this question because I am already changing the format of the array. I have tried this solution and it does not work in my code. I get the same error if I try and pass the size = 32 parameter into the init.
I am using Python's colorgram library to extract color information from an image. One version of my code is as follows:
import colorgram
num_cols = 25
rgb_colors = []
colors = colorgram.extract('image.jpg', num_cols)
for color in colors:
r = color.rgb.r
g = color.rgb.g
b = color.rgb.b
new_color = (r, g, b)
rgb_colors.append(new_color)
print(rgb_colors)
print(len(rgb_colors))
The code works just fine, but if I give 'num_cols' a value of more than 36, the code only finds a maximum of 36 colors. I have tried different hi res images, and have tried different versions of the same code that I found online, but I always get this same limit of 36 colors; I just don't know where the problem can be, and I have not seen anyone else report the same issue. Can someone please tell me if I am doing something stupid here? Thanks.
I have an application that uses bitmaps for image storage. Currently these are stored as uncompressed bitmaps, but since the number of colors is almost always <=16, I should be able to use a compressed bitmap format, which makes the files nearly 6 times smaller. With the help of the Wikipedia entry at https://en.wikipedia.org/wiki/BMP_file_format I tried to implement a compression program in Python - shown below. This successfully reads the input bmp and outputs a smaller bmp, but the resulting bmp is not readable, so I must be doing something wrong. In windows explorer, the properties of the file include "Bit Depth: 32", which is clearly not the intention - but I'm pretty sure I got the header info for bit depth and compression right. It would help if I could find an example of a compressed bitmap for inspection, but haven't been able to track one down. Is this format not generally supported, or am I doing something wrong?
import math
def rgb(r,g,b):
return hex(256*256*256+r*256*256+g*256+b)[3:9]
def read_bmp(path):
bm={}
with open(path,"rb") as f:
byte=f.read(54)
header_size=byte[14]+byte[15]*256
if header_size!=40: return None
bm['w']=w=byte[18]+byte[19]*256
bm['h']=byte[22]+byte[23]*256
bm['color_depth']=byte[28]
bm['compression']=byte[30]
bm['im_size']=byte[34]+byte[35]*256+byte[36]*256*256
if bm['compression']!=0:
ct=f.read(4*bm['color_depth'])
color_table=[]
for i in range(0,bm['color_depth']):
color_table.append((ct[i*4],ct[i*4+1],ct[i*4+2]))
if bm['color_depth']==24:
bmp=f.read(bm['im_size'])
row_size=bm['im_size']//bm['h']
col={}
pixel=[]
for y in range(0,bm['h']):
s=""
row=[]
for x in range(0,w):
row.append((bmp[y*row_size+x*3],bmp[y*row_size+x*3+1],bmp[y*row_size+x*3+2]))
color=rgb(bmp[y*row_size+x*3],bmp[y*row_size+x*3+1],bmp[y*row_size+x*3+2])
if color in col:
col[color]+=1
else:
col[color]=1
pixel.append(row)
bm['pix']=pixel
bm['color_count']=len(col)
return bm
def putint(b,p,i):
b[p]=i & 255
b[p+1]=(i>>8)&255
if i>65535:
b[p+2]=(i>>16)&255
b[p+3]=(i>>24)&255
def write_compressed_bmp(bm,path):
pix=bm['pix']
col=[]
color_index=[]
for row in pix:
rowindex=[]
for color in row:
if not color in col: col.append(color)
rowindex.append(col.index(color))
color_index.append(rowindex)
print(col)
if len(col)>16: return
with open(path,"wb") as f:
row_size=math.ceil(bm['w']/8)*4
image_size=row_size*bm['h']
file_len=54+64+image_size
header=bytearray(54)
header[0]=ord('B')
header[1]=ord('M')
putint(header,2,file_len)
putint(header,10,54+64)
putint(header,14,40)
putint(header,18,bm['w'])
putint(header,22,bm['h'])
putint(header,26,1)
putint(header,28,4)
putint(header,34,image_size)
f.write(header)
color_map=bytearray(64)
for i in range(0,len(col)):
color_map[i*4]=col[i][0]
color_map[i*4+1]=col[i][1]
color_map[i*4+2]=col[i][2]
f.write(color_map)
for row in pix:
row_bytes=bytearray(row_size)
i=0
for color in row:
ci=col.index(color)
if i&1:
row_bytes[i>>1]|=ci
else:
row_bytes[i>>1]|=ci<<4
i+=1
f.write(row_bytes)
bm=read_bmp("bitmap.bmp")
if bm['color_count']<=16:
write_compressed_bmp(bm,"bitmap2.bmp")
I found out what was wrong - I forgot that the range() method in Python takes one larger than the largest desired value as its second parameter, so one too few rows was being written. Fixing that, and leaving the "compression" bit at 0 fixed the problem - I edited the code above accordingly. If it's OK, I'll leave this up here for possible use by others.
I am trying to make a program that will scrape the text off of a screenshot using tesseract and python, and am having no issue getting one piece of it, however some text is lighter colored and is not being picked up by tesseract. Below is an example of a picture I am using:
I am am to get the text at the top of the picture, but not the 3 options below.
Here is the code I am using for grabbing the text
result = pytesseract.image_to_string(
screen, config="load_system_dawg=0 load_freq_dawg=0")
print("below is the total value scraped by the tesseract")
print(result)
# Split up newlines until we have our question and answers
parts = result.split("\n\n")
question = parts.pop(0).replace("\n", " ")
q_terms = question.split(" ")
q_terms = list(filter(lambda t: t not in stop, q_terms))
q_terms = set(q_terms)
parts = "\n".join(parts)
parts = parts.split("\n")
answers = list(filter(lambda p: len(p) > 0, parts))
I when I have plain text in black without a colored background I can get the answers array to be populated by the 3 below options, however not in this case. Is there any way I can go about fixing this?
You're missing binarization, or thresholding step.
In your case you can simply apply binary threshold on grayscale image.
Here is result image with threshold = 177
Here1 you can learn more about Thresholding with opencv python library
I am using a simple code to compare an image to a desktop screenshot through the function getcolors() from PIL. When I open an image, it works:
im = Image.open('sprites\Bowser\BowserOriginal.png')
current_sprite = im.getcolors()
print current_sprite
However, using both pyautogui.screenshot() and ImageGrab.grab() for the screenshot, my code returns none. I have tried using the RGB conversion as shown here: Cannot use im.getcolors.
Additionally, even when I save a screenshot to a .png, it STILL returns none.
i = pyautogui.screenshot('screenshot.png')
f = Image.open('screenshot.png')
im = f.convert('RGB')
search_image = im.getcolors()
print search_image
First time posting, help is much appreciated.
Pretty old question but for those who sees this now:
Image.getcolors() takes as a parameter "maxcolors – Maximum number of colors." (from the docs here).
The maximum number of colors an image can have, equals to the number of pixels it contains.
For example, an image of 50*60px will have maximum 3,000 colors.
To translate it into code, try this:
# Open the image.
img = Image.open("test.jpg")
# Set the maxcolors number to the image's pixels number.
colors = img.getcolors(img.size[0]*img.size[1])
If you'd check the docs, getcolors returns None if the number of colors in the image is greater than the default parameter, which is set to 256.