Pixel is there but .getpixel isn't detecting it - python

I'm currently having an issues with my program that im not too sure how to fix.
I am doing the following:
x = 0
y = 0
im = ImageGrab.grab()
time.sleep(1)
while True:
xy = (x, y)
x = x + 1
if im.getpixel(xy) == (0,158,187):
time.sleep(0.3)
pyautogui.click(x,y)
break
if x >= 1200:
x = 0
y = y + 1
print('cant find pixel')
if y >= 950:
y = 0
x = 0
And it works about 90% of the time and then theres this random time it just says it can't detect the pixel despite the pixel being there 100%.
EDIT: Managed to catch the following error in the 10% it happens:
AttributeError: 'NoneType' object has no attribute 'getpixel'
Which makes no sense since I'm doing im = ImageGrab.grab() beforehand and it works 90% of the time

You should check your ImageGrab() was successful before using the data, so something like:
im = ImageGrab.grab()
if im is not None:
processImage
You'll be there all day if you run double for loops over an image and call a function for every one! Try to get in the habit of using Numpy vectorised code for images in Python.
Basically, you appear to be testing if any pixel in your 1200x950 image matches all three RGB components (0,158,187).
You can do that with Numpy like this:
np.any(np.all(na==(0,158,187), axis=-1))
In the demo below the double for loops take 800ms and the Numpy test takes 20ms, so 40x faster.
#!/usr/bin/env python3
import numpy as np
from PIL import Image
def loopy(im):
for x in range(im.width):
for y in range(im.height):
if im.getpixel((x,y)) == crucialPixel:
return True
return False
def me(im):
# Make image into Numpy array
na = np.array(im)
# Test if there is any pixel where all RGB components match crucialPixel
return np.any(np.all(na==crucialPixel, axis=-1))
# Define our beloved crucial pixel
crucialPixel = (0,158,187)
# Construct a new, solid black image
im = Image.new('RGB', (1200,950))
# Neither should find crucialPixel in black image
result = loopy(im)
result = me(im)
# Insert the crucial pixel
im.putpixel((600,475), crucialPixel)
# Both should find crucialPixel
result = loopy(im)
result = me(im)

Related

I have one case, where numpy.where() does not work as expected. Where is my error?

I have a binary segmentation map as output from a neural network (niftii format) and want to maintain only the biggest island, to get rid of unwanted false positives.
I am able to achieve this with:
import nibabel as nib
import numpy as np
from scipy.ndimage import label
vol = 'PATH_TO_VOLUME'
elements_in_biggest_island = 0
biggest_index = 0
aNii = nib.load(vol)
a = aNii.get_fdata()
s = np.ones((3,3,3), dtype = 'uint8')
labelled_array, num_features = label(a, structure=s)
for i in range (1, num_features + 1):
tempArray = labelled_array
if (np.count_nonzero(tempArray == i) > elements_in_biggest_island):
elements_in_biggest_island = np.count_nonzero(tempArray == i)
biggest_index = I
print("Biggest Island was at index ", biggest_index, " with a total of ", elements_in_biggest_island, " members.")
labelled_array[labelled_array == biggest_index] = 1.0
labelled_array[labelled_array < biggest_index ] = 0.0
labelled_array[labelled_array > biggest_index] = 0.0
ni_img = nib.Nifti1Image(labelled_array, aNii.affine)
nib.save(ni_img, f'PATH_TO_PROCESSED_VOL')
But the "thresholding" is very inefficient. In another application I work with numpy.where(), which generates a good speedup compared to the shown way of thresholding.
My approach was, to remove the array[array>I] == x lines by:
labelled_array = np.where(labelled_array==biggest_index, 1, 0)
This exact line works perfectly in another application, but here I only get a black 3D volume, which does not work for me.
Is anybody able to point out the mistake, that I have made?

How Could I increase the speed

I am using below code for an image processing related study. The code works fine as functionality but it is too slow that one step takes up to 10 seconds.
I need faster process speed to reach at the aim.
import numpy
import glob, os
import cv2
import os
input = cv2.imread(path)
def nothing(x): # for trackbar
pass
windowName = "Image"
cv2.namedWindow(windowName)
cv2.createTrackbar("coef", windowName, 0, 25000, nothing)
condition = True
while (condition):
coef = cv2.getTrackbarPos("coef", windowName)
temp_img = input
row = temp_img.shape[0]
col = temp_img.shape[1]
print(coef)
red = []
green = []
for i in range(row):
for y in range(col):
# temp_img[i][y][0] = 0
temp_img[i][y][1] = temp_img[i][y][1]* (coef / 100)
temp_img[i][y][1] = temp_img[i][y][2] * (1 - (coef / 100))
# relative_diff = value_g - value_r
# temp =cv2.resize(temp,(1000,800))
cv2.imshow(windowName, temp_img)
# cv2.imwrite("output2.jpg", temp)
print("fin")
# cv2.waitKey(0)
if cv2.waitKey(30) >= 0:
condition = False
cv2.destroyAllWindows()
Is there anybody have an idea having faster result on the aim?
It's not entirely clear to me what object temp_img is exactly, but if it behaves like a numpy array, you could replace your loop by
temp_img[:,:,0] = temp_img[:,:,1]*(coef/100)
temp_img[:,:,1] = temp_img[:,:,2]*(1-coef/1000)
which should result in a significant speed up if your array is large. The implementation of such operations on arrays are optimised very well, whereas python loops are generally quite slow.
Edit based on comments:
Since you're working with large images and have some expensive operations that need an unscaled version but only need to be executed once, your code could get the following kind of structure
import... #do all your imports
def expensive_operations(image, *args, **kwargs):
#do all your expensive operations like object detection
def scale_image(image, scale):
#create a scaled version of image
def cheap_operations(scaled_image, windowName):
#perform cheap operations, e.g.
coef = cv2.getTrackbarPos("coef", windowName)
temp_img = np.copy(scaled_image)
temp_img[:,:,1] = temp_img[:,:,1]* (coef / 100)
temp_img[:,:,2] = temp_img[:,:,2] * (1 - (coef / 100))
cv2.imshow(windowName, temp_img)
input = cv2.imread(path)
windowName = "Image"
cv2.namedWindow(windowName)
cv2.createTrackbar("coef", windowName, 0, 25000, nothing)
condition = True
expensive_results = expensive_operations(input) #possibly with some more args and keyword args
scaled_image = scale_image(input)
while condition:
cheap_operations(scaled_image, windowName)
if cv2.waitKey(30) >= 0:
condition = False
cv2.destroyAllWindows()
I do this kind of thing in nip2. It's an image processing spreadsheet that can manipulate huge images quickly. It has no problems doing this kind of operation on any size image at 60fps.
I made you an example workspace: http://www.rollthepotato.net/~john/coeff.ws
Here's what it looks like working on a 1gb starfield image:
You can drag the slider to change coeff. The processed image updates instantly as you drag. You can zoom and pan around the processed image to check details and adjust coeff.
The underlying image processing library is libvips, which has a Python binding, pyvips. In pyvips, your program would be:
import pyvips
def adjust(image, coeff):
return image * [1, coeff / 100, 1 - coeff / 100]
Though that's without the GUI elements, of course.

I would like to know how to calculate the percentage of a color in an image

I would like to know how to calculate the percentage of a color in an image, the image below represents 100%:
already this, when the level decreases:
I wanted to learn correctly how do I get the percentage that the bar has at the moment, I tried to use the Matplotlib library, but I could not get the expected result, could anyone help me please? I do not need something ready, someone to teach me ...
I think you want to calculate the progress by looking at the image
I'm not sure if there's a library to this specific thing but here's my simple approach to it,
you can compare images to get until which column they are similar and then can calculate the % task done, let me demonstrate..
!wget https://i.stack.imgur.com/jnxX3.png
a = plt.imread( './jnxX3.png')
plt.imshow( a )
This shall load the image with 100% completion in variable a
c =a
c = c[: , 0:c.shape[1] - 50]
aa = np.zeros( dtype= float , shape=( 11,50, 3 ))
c = np.append( c, aa , axis= 1 )
plt.imshow( c)
plt.imshow( c )
made a sample incomplete image which you should have provided
def status( complete_img , part_image):
"""inputs must be numpy arrays """
complete_img = complete_img[:, 1: ] # as the first pixel column doesn't belong to % completion
part_image = part_image[:, 1:]
counter = 0
while(counter < part_image.shape[1] and counter < complete_img.shape[1]):
if (complete_img[:, counter ] == part_image[:,counter]).all():
counter += 1
else :
break
perc = 100*( float(counter) / complete_img.shape[1])
return
status( a ,c ) # this will return % columns similar in the two images
A proposition:
import numpy as np
from PIL import Image
from urllib.request import urlopen
full = np.asarray(Image.open(urlopen("https://i.stack.imgur.com/jnxX3.png")))
probe = np.asarray(Image.open(urlopen("https://i.stack.imgur.com/vx5zt.png")))
# crop the images to the same shape
# (this step should be avoided, best compare equal shaped arrays)
full = full[:,1:probe.shape[1]+1,:]
def get_percentage(full, probe, threshold):
def profile_red(im):
pr = im[:,:,0] - im[:,:,1]
return pr[pr.shape[0]//2]
def zero(arr):
z = np.argwhere(np.abs(np.diff(np.sign(arr))).astype(bool))
if len(z):
return z[0,0]
else:
return len(arr)
full_red = profile_red(full)
probe_red = profile_red(probe)
mask = full_red > threshold
diff = full_red[mask] - probe_red[mask]
x0 = zero(diff - threshold)
percentage = x0 / diff.size * 100
err = 2./diff.size * 100
return percentage, err
print("{:.1f} p\m {:.1f} %".format(*get_percentage(full, probe, 75.0)))
Result:
94.6 p\m 2.2 %
You're looking for the Pillow library. There are two ways to measure color, Hue, Saturation, Luminance (HSL), and Red, Blue, Green (RGB). There are functionsto do both in the library.

Plot a gamut in cie1931 colour space Python 2.7

Gamut I want to plot in CIE1931 space: https://www.google.co.uk/search?biw=1337&bih=1257&tbm=isch&sa=1&ei=9x3kW7rqBo3ygQb-8aWYBw&q=viewpixx+gamut&oq=viewpixx+gamut&gs_l=img.3...2319.2828.0.3036.5.5.0.0.0.0.76.270.5.5.0....0...1c.1.64.img..0.0.0....0.KT8w80tcZik#imgrc=77Ufw31S6UVlYM
I want to create a triangle plot of the ciexyY colours within the these coordinates: (.119,.113),(.162,.723),(.695,.304) as in the image - with a set luminance Y at 30.0.
I have created a 3d array of xy values between 0-1.
I then created a matrix with 1s inside the triangle and 0s outside the triangle.
I multiplied the triangle matrix by the xyY ndarray.
Then I looped through the xyY ndarray and converted xyY values to rgb, and displayed them.
The result is somewhat close but not correct. I think the error is in the last section when I convert to rgb, but I'm not sure why. This is the current image: https://imgur.com/a/7cWY0FI. Any recommendations would be really appreciated.
from __future__ import division
import numpy as np
from colormath.color_objects import sRGBColor, xyYColor
from colormath.color_conversions import convert_color
import matplotlib.pyplot as plt
def frange(x,y,jump):
while x < y:
yield x
x += jump
def onSameSide(p1,p2, A,B):
cp1 = np.cross(B-A, p1-A)
cp2 = np.cross(B-A, p2-A)
if(np.dot(cp1, cp2) >= 0):
return True
else:
return False
def isPointInTriangle(p,A,B,C):
if(onSameSide(p,A,B,C) and onSameSide(p,B,A,C) and onSameSide(p,C,A,B)):
return True
else:
return False
xlen = 400
ylen = 400
#CIExyY colour space
#Make an array (1,1,3) with each plane representing how x,y,Y vary in the coordinate space
ciexyY = np.zeros((3,xlen,ylen))
ciexyY[2,:,:]=30.0
for x in frange(0,1,1/xlen):
ciexyY[0,:,int(xlen*x)]=x
for y in frange(0,1,1/xlen):
ciexyY[1,int(ylen*y),:]=y
#coordinates from Viewpixx gamut, scaled up to 100
blue=np.array((.119,.113,30.0))
green=np.array((.162,.723,30.0))
red=np.array((.695,.304,30.0))
#scale up to size of image
blue = np.multiply(blue,xlen)
green = np.multiply(green,xlen)
red = np.multiply(red,xlen)
#make an array of zeros and ones to plot the shape of Viewpixx triangle
triangleZeros = np.zeros((xlen,ylen))
for x in frange(0,xlen,1):
for y in frange(0,ylen,1):
if(isPointInTriangle((x,y,0),blue,green,red)):
triangleZeros[x,y]=1
else:
triangleZeros[x,y]=0
#cieTriangle
cieTriangle = np.multiply(ciexyY,triangleZeros)
#convert cieTriangle xyY to rgb
rgbTriangle = np.zeros((3,xlen,ylen))
for x in frange(0,xlen,1):
for y in range(0,ylen,1):
xyYcolour = xyYColor(cieTriangle[0,x,y],cieTriangle[1,x,y],cieTriangle[2,x,y])
rgbColour = convert_color(xyYcolour,sRGBColor)
rgbTriangle[0,x,y] = rgbColour.rgb_r
rgbTriangle[1,x,y] = rgbColour.rgb_g
rgbTriangle[2,x,y] = rgbColour.rgb_b
rgbTriangle = np.transpose(rgbTriangle)
plt.imshow(rgbTriangle)
plt.show()
We have all the common Chromaticity Diagrams in Colour, I would recommend it over python-colormath because Colour is vectorised and thus much faster.
Do you have a render of your current image to share though?
from colour.plotting import plot_chromaticity_diagram_CIE1931
plot_chromaticity_diagram_CIE1931()

Fast RGB Thresholding in Python (possibly some smart OpenCV code?)

I need to do some fast thresholding of a large amount of images, with a specific range for each of the RGB channels, i.e. remove (make black) all R values not in [100;110], all G values not in [80;85] and all B values not in [120;140]
Using the python bindings to OpenCV gives me a fast thresholding, but it thresholds all three RGP channels to a single value:
cv.Threshold(cv_im,cv_im,threshold+5, 100,cv.CV_THRESH_TOZERO_INV)
cv.Threshold(cv_im,cv_im,threshold-5, 100,cv.CV_THRESH_TOZERO)
Alternatively I tried to do it manually by converting the image from PIL to numpy:
arr=np.array(np.asarray(Image.open(filename).convert('RGB')).astype('float'))
for x in range(img.size[1]):
for y in range(img.size[0]):
bla = 0
for j in range(3):
if arr[x,y][j] > threshold2[j] - 5 and arr[x,y][j] < threshold2[j] + 5 :
bla += 1
if bla == 3:
arr[x,y][0] = arr[x,y][1] = arr[x,y][2] = 200
else:
arr[x,y][0] = arr[x,y][1] = arr[x,y][2] = 0
While this works as intended, it is horribly slow!
Any ideas as to how I can get a fast implementation of this?
Many thanks in advance,
Bjarke
I think the inRange opencv method is what you are interested in. It will let you set multiple thresholds simultaneously.
So, with your example you would use
# Remember -> OpenCV stores things in BGR order
lowerBound = cv.Scalar(120, 80, 100);
upperBound = cv.Scalar(140, 85, 110);
# this gives you the mask for those in the ranges you specified,
# but you want the inverse, so we'll add bitwise_not...
cv.InRange(cv_im, lowerBound, upperBound, cv_rgb_thresh);
cv.Not(cv_rgb_thresh, cv_rgb_thresh);
Hope that helps!
You can do it with numpy in a much faster way if you don't use loops.
Here's what I came up with:
def better_way():
img = Image.open("rainbow.jpg").convert('RGB')
arr = np.array(np.asarray(img))
R = [(90,130),(60,150),(50,210)]
red_range = np.logical_and(R[0][0] < arr[:,:,0], arr[:,:,0] < R[0][1])
green_range = np.logical_and(R[1][0] < arr[:,:,0], arr[:,:,0] < R[1][1])
blue_range = np.logical_and(R[2][0] < arr[:,:,0], arr[:,:,0] < R[2][1])
valid_range = np.logical_and(red_range, green_range, blue_range)
arr[valid_range] = 200
arr[np.logical_not(valid_range)] = 0
outim = Image.fromarray(arr)
outim.save("rainbowout.jpg")
import timeit
t = timeit.Timer("your_way()", "from __main__ import your_way")
print t.timeit(number=1)
t = timeit.Timer("better_way()", "from __main__ import better_way")
print t.timeit(number=1)
The omitted your_way function was a slightly modified version of your code above. This way runs much faster:
$ python pyrgbrange.py
10.8999910355
0.0717720985413
That's 10.9 seconds vs. 0.07 seconds.
The PIL point function takes a table of 256 values for each band of the image and uses it as a mapping table. It should be pretty fast. Here's how you would apply it in this case:
def mask(low, high):
return [x if low <= x <= high else 0 for x in range(0, 256)]
img = img.point(mask(100,110)+mask(80,85)+mask(120,140))
Edit: The above doesn't produce the same output as your numpy example; I followed the description rather than the code. Here's an update:
def mask(low, high):
return [255 if low <= x <= high else 0 for x in range(0, 256)]
img = img.point(mask(100,110)+mask(80,85)+mask(120,140)).convert('L').point([0]*255+[200]).convert('RGB')
This does a few conversions on the image, making copies in the process, but it should still be faster than operating on individual pixels.
If you stick to using OpenCV, then just cv.Split the image into multiple channels first and then cv.Threshold each channel individually. I'd use something like this (untested):
# Temporary images for each color channel
b = cv.CreateImage(cv.GetSize(orig), orig.depth, 1)
g = cv.CloneImage(b)
r = cv.CloneImage(b)
cv.Split(orig, b, g, r, None)
# Threshold each channel using individual lo and hi thresholds
channels = [ b, g, r ]
thresh = [ (B_LO, B_HI), (G_LO, G_HI), (R_LO, R_HI) ]
for c, (lo, hi) in zip(channels, thresh):
cv.Threshold(ch, ch, hi, 100, cv.CV_THRESH_TOZERO_INV)
cv.Threshold(ch, ch, lo, 100, cv.CV_THRESH_TOZERO)
# Compose a new RGB image from the thresholded channels (if you need it)
dst = cv.CloneImage(orig)
cv.Merge(b, g, r, None, dst)
If your images are all the same size, then you can re-use the created images to save time.

Categories