i am trying to change the value of a pixel in an image to the closest value i have in my list, and i cant figure out why i cant change the pixel value.
I've tried converting the image to RGB or RGBA and for some reason sometimes it takes 3 arguments sometime 4.
im = Image.open('rick.png') # Can be many different formats.
rgb_im = im.convert('RGBA')
pix = im.load()
height, width = im.size
image = ImageGrab.grab()
COLORS = (
(0, 0, 0),
(127, 127, 127),
(136, 0, 21),
(237, 28, 36),
(255, 127, 39),
)
def closest_color(r, g, b, COLORS):
min_diff = 9999
answer = None
for color in COLORS:
cr, cg, cb = color
color_diff = abs(r - cr) + abs(g - cg) + abs(b - cb)
if color_diff < min_diff:
answer = color
min_diff = color_diff
return answer
def read_color(height,width, COLORS, pix):
for x in range(height):
for y in range(width):
r,g,b,a = rgb_im.getpixel((x,y))
color = closest_color(r, g, b, COLORS) # color is returned as tuple
pix[x,y] = color # Changing color value? -Here i get the error-
read_color(height,width, COLORS, pix)
im.save('try.png')
I keep getting this error even tho closest_value returns one argument and i dont know why, thnk you for your help!
COLORS - is a list of colors, i've tested the closest_color() function and it works good
Error message:
'Exception has occurred: TypeError
function takes exactly 1 argument (3 given)
File "C:\Users\user\Desktop\תוכנות שעשיתי\program.py", line 133, in
read_color
pix[x,y] = color
File "C:\Users\user\Desktop\תוכנות שעשיתי\program.py", line 137, in
<module>
read_color(height,width, COLORS, pix)'
EDIT!
Apperantly the code is working for most of the images but not for all of them, for exmaple this image doesn't work and i get this error
You are being inconsistent by reading the pixels from the RGBA converted image but setting the pixels in the original maybe-not-RGBA image. Fixing that makes your code work with the sample image.
pix = rgb_im.load()
Related
I'm building a line in PIL on two coordinates, for each coordinate I need to choose a random color, and I can't do it in any way
from PIL import Image, ImageDraw
import numpy as np
N = 20
x = np.linspace(1,10,N)
y = x**2
z = np.random.rand(N)
im = Image.new('L', (256, 256), 255)
draw = ImageDraw.Draw(im)
for i in range(len(x)-1):
draw.line((x[i],y[i], x[i+1], y[i+1]),fill=0,width=2)
im.show()
# tried to change color with fill
#for i in range(len(x)-1):
# draw.line((x[i],y[i], x[i+1], y[i+1]),fill=z,width=2)
# there was such an error
# color must be int or tuple
Thanks in advance!
According to the error message, it expects an integer. But in your case, the input z is passed which is an array of shape (20,).
Since Image has been declared with mode L, it expects an 8-bit value in fill, i.e. a value between 0 and 255 (Modes in PIL)
Now since you want to choose a random color, I used np.random.randint(0,256) to choose one between 0-255 at every iteration:
for i in range(len(x)-1):
draw.line((x[i],y[i], x[i+1], y[i+1]),fill=np.random.randint(0,256),width=2)
Result:
The line drawn above is in grayscale.
For RGB image:
# set the mode to `RGB`
im2 = Image.new('RGB', (256, 256))
draw2 = ImageDraw.Draw(im2)
for i in range(len(x)-1):
# pass a tuple of 3 random values between 0-255
draw2.line((x[i],y[i], x[i+1], y[i+1]),fill=tuple(np.random.choice(range(255),size=3)),width=2)
Result:
The result is a random color line drawn between points.
Apologies if this question has been answered elsewhere, I looked at a few suggestions but didn't find a match.
I am trying to get the RGB values for each pixel in a png image. Based on the pillow access documentation (https://pillow.readthedocs.io/en/stable/reference/PixelAccess.html#pixelaccess) I tried the following:
def image_vec(fname):
im = Image.open(fname)
pix = im.load()
x, y = im.size
for i in range(x):
for j in range(y):
print(pix[i, j])
The documentation example seems to suggest that I should expect a triplet when I call pix[i,j]. However the snippet above prints out a quadruplet like so:
(182, 183, 172, 255)
(206, 206, 197, 255)
(156, 155, 148, 248)
So what are these values I am looking at? Is it R, G, B, alpha? If so isn't the alpha value supposed to be between 0 and 1?
Yes, those values are the R, G, B, alphas of the pixel.
Here the alpha range from 0 to 255, where 255 is completely opaque and 0 is completely transparent.
I'm supposed to write a method that converts an RGB image to Grayscale by using the "average method" where I take the average of the 3 colors (not the weighted method or luminosity method). I then must display the original RGB image and grayscale image next to each other (concatenated). The language I'm writing in is Python. This is what my code looks like currently.
import numpy as np
import cv2
def getRed(redVal):
return '#%02x%02x%02x' % (redVal, 0, 0)
def getGreen(greenVal):
return '#%02x%02x%02x' % (0, greenVal, 0)
def getBlue(blueVal):
return '#%02x%02x%02x' % (0, 0, blueVal)
# Grayscale = (R + G + B / 3)
# For each pixel,
# 1- Get pixels red, green, and blue
# 2- Calculate the average value
# 3- Set each of red, green, and blue values to average value
def average_method(img):
for p in img:
red = p.getRed()
green = p.getGreen()
blue = p.getBlue()
average = (red + green + blue) / 3
p.setRed(average)
p.setGreen(average)
p.setBlue(average)
def main():
img1 = cv2.imread('html/images/sun.jpeg')
img1 = cv2.resize(img1, (0, 0), None, .50, .50)
img2 = average_method(img1)
img2 = np.stack(3 * [img2], axis=2)
numpy_concat = np.concatenate((img1, img2), 1)
cv2.imshow('Numpy Concat', numpy_concat)
cv2.waitKey(0)
cv2.destroyAllWindows
if __name__ =="__main__":
main()
The portion that is commented within the average_method function is the steps that I must follow.
When I try to run the code, I get
File "test.py", line 38, in <module>
main()
File "test.py", line 30, in main
img2 = average_method(img1)
File "test.py", line 15, in average_method
red = p.getRed()
AttributeError: 'numpy.ndarray' object has no attribute 'getRed'
I thought that defining the functions for getRed, getGreen, and getBlue up above would mean they'd become recognizable in my average_method function (I got those functions from online so I hope they're right). I'm also not sure what it has to do with numpy.ndarray. If anyone could help me fill in this average_method function with code that follows the commented steps correctly, I would really appreciate it.
EDIT:::
New code looks like this:
import cv2
import numpy as np
def average_method(img):
for p in img:
gray = sum(p)/3
for i in range(3):
p[i] = gray
def main():
img1 = cv2.imread('html/images/sun.jpeg')
img1 = cv2.resize(img1, (0, 0), None, .50, .50)
img2 = average_method(img1)
img2 = np.stack(3 * [img2], axis=2)
numpy_concat = np.concatenate((img1, img2), 1)
cv2.imshow('Numpy Concat', numpy_concat)
cv2.waitKey(0)
cv2.destroyAllWindows
if __name__ =="__main__":
main()
I now get the error
File "test.py", line 50, in <module>
main()
File "test.py", line 43, in main
img2 = np.stack(3 * [img2], axis=2)
File "<__array_function__ internals>", line 5, in stack
File "C:\Users\myname\AppData\Local\Programs\Python\Python38-32\lib\site-packages\numpy\core\shape_base.py", line 430, in stack
axis = normalize_axis_index(axis, result_ndim)
numpy.AxisError: axis 2 is out of bounds for array of dimension 1
I have that line "img2 = np.stack(3 * [img2], axis=2)" since I was previously told on Stack Overflow I need it due to my img2 now being a greyscale (single-channel) image, when img1 is still color (three-channel). This line apparently fixes that. But now it seems like there is something wrong with that?
In Java, the for loop you highlighted is called an "enhanced for loop". Python doesn't have these because Python for loops pog (in terms of concision).
The Python equivalent of the line in question would be:
for p in img:
No need to state object class or anything like that.
EDIT: After OP changed question
The problem now is that you're not calling the functions correctly. p is an array containing the RGB values for that pixel. To call the function as you defined above do:
for p in img:
red = getRed(p[0])
green = getGreen(p[1])
blue = getBlue(p[2])
average = (red + green + blue) / 3
p[0] = average
p[1] = average
p[2] = average
Remember when you moved the code to Python, you seem to no longer be working in Object Oriented Programming! Pixels don't come with methods that you can call like that anymore.
However, as pointed out by Guimoute in the comments, the code can be much simpler if you get rid of the get[Color] functions and do the following:
for p in img:
gray = sum(p)/3
for i in range(3):
p[i] = gray
I want to compare two images and save a difference image, where the differences are marked in red.
Unfortunately I get the following error:
Traceback (most recent call last):
File "pythontest.py", line 216, in <module>
nDiff = compare(sPathCur, sPathRef, sPathDif)
File "pythontest.py", line 88, in compare
pix_diff[y, x] = (255, 0, 0)
TypeError: function takes exactly 1 argument (3 given)
def compare(sPathCur, sPathRef, sPathDif):
im_cur = Image.open(sPathCur)
im_ref = Image.open(sPathRef)
im_dif = im_cur.convert('L') # convert image to grey scale
delta = ImageChops.difference(im_cur, im_ref)
width, height = delta.size
pix_delta = delta.load()
pix_diff = im_dif.load()
for y in range(width):
for x in range(height):
r, g, b = pix_delta[y, x]
if (r > 0 or g > 0 or b > 0):
pix_diff[y, x] = (255, 0, 0)
im_dif.save(sPathDif)
Once you have performed the conversion to a greyscale image, each pixel is assigned a single value, rather than an RGB triplet.
Taken from http://effbot.org/imagingbook/image.htm :
When converting from a colour image to black and white, the library
uses the ITU-R 601-2 luma transform:
L = R * 299/1000 + G * 587/1000 + B * 114/1000
So if your pixel at [x,y]=[0,0] had (R,G,B) value of (100,150,200), then after converting to greyscale, it would contain the single value 140.75 (which would then be rounded to an integer)
You can verify this by checking the value of pix_diff[0,0] before your nested loops. It should return you only a single value.
So you either need to assign a single greyscale value to each pixel in your pix_diff[y, x], or convert your pix_diff image back into an RGB-compatible format before you can assign each pixel your value of (255, 0, 0)
This code shall convert my RGB-image into Black/White and provide me the RGB-value -which should be (0, 0, 0) or (255, 255, 255).
import cv2
import numpy as np
template = cv2.imread('C:\colorbars.png')
gray = cv2.cvtColor(template, cv2.COLOR_RGB2GRAY)
gray = cv2.resize(gray,(640,480))
ret,gray = cv2.threshold(gray,120,255,0)
gray2 = gray.copy()
mask = np.zeros(gray.shape,np.uint8)
contours, hier = cv2.findContours(gray,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if 200<cv2.contourArea(cnt)<5000:
cv2.drawContours(gray2,[cnt],0,(0,255,0),2)
cv2.drawContours(mask,[cnt],0,(0,255,0),-1)
cv2.bitwise_not(gray2,gray2,mask)
y = 250
x = 200
r, g, b = gray2[y,x]
print r, g, b
It works if I check the RGB value of the colored image with the line r, g, b = template[y,x]; however as soon as I want to have the RGB value of the Black/White image, following error message appears:
File "C:\Python27\Lib\site-packages\myprogram.py", Line 22, in <module>
r, g, b = gray2[y,x]
TypeError: ´numpy.uint8´ object is not iterable
I assume it means that there are not enough objects in the array and I assume that the problem lies somewhere in the conversion from color to B/W.
Your "gray" variable is a 2D matrix (because of the grayscale), so when you ask for gray2[x,y], it returns a single unsigned integer in 8 bit (np.unint8) corresponding to the grayscale value of the [x,y] pixel.
When you do : r,g,b =gray2[x,y], you expect 3 values (r, g, b), but it returns only 1 so you get an error.
You should precise what you are trying to do, as asking for the RGB values of a grayscale image makes no sense.
Please try to use just ONE channel to get the result instead of 3 channels,
for example: r = gray2[x,y]