Python - Find Similar Colors, Better Way

I made a function to find the color inside the image and return x, y. Now I need to add a new function where I can find a color with a certain degree of tolerance. Should it be easy?

Code for finding color in the image and returning x, y:

def FindColorIn(r,g,b, xmin, xmax, ymin, ymax): image = ImageGrab.grab() for x in range(xmin, xmax): for y in range(ymin,ymax): px = image.getpixel((x, y)) if px[0] == r and px[1] == g and px[2] == b: return x, y def FindColor(r,g,b): image = ImageGrab.grab() size = image.size pos = FindColorIn(r,g,b, 1, size[0], 1, size[1]) return pos 

Result:

The usual methods for comparing two colors, taken from the answers, are located at Euclidean distance or Chebyshev distance.

I decided to mainly use the (square) Euclidean distance and several different color spaces. LAB, deltaE (LCH), XYZ, HSL and RGB. In my code, most color spaces use the squared Euclidean distance to calculate the difference.

For example, with LAB, RGB and XYZ a simple euc square. Distance does the trick:

 if ((X-X1)^2 + (Y-Y1)^2 + (Z-Z1)^2) <= (Tol^2) then ... 

LCH and HSL are a bit more complicated, since both have a cylindrical hue, but some parts of mathematics solve this and then use square eucl. here.

In most cases, I added “separate parameters” for the tolerance for each channel (using 1 global tolerance and alternative “modifiers” HueTol := Tolerance * hueMod or LightTol := Tolerance * LightMod ).


It seems that color spaces built on top of XYZ (LAB, LCH) work best in many of my scenarios. Tho HSL gives very good results in some cases, and it’s much cheaper to convert to RGB, RGB is also great for most of my needs.

+11
source share
8 answers

Calculating the distances between RGB colors in a way that is meaningful to the eyes is not as simple as taking the Euclidean distance between two RGB vectors.

There is an interesting article about this here: http://www.compuphase.com/cmetric.htm

An example implementation in C is this:

 typedef struct { unsigned char r, g, b; } RGB; double ColourDistance(RGB e1, RGB e2) { long rmean = ( (long)e1.r + (long)e2.r ) / 2; long r = (long)e1.r - (long)e2.r; long g = (long)e1.g - (long)e2.g; long b = (long)e1.b - (long)e2.b; return sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8)); } 

It should not be too difficult to port to Python.

EDIT:

Alternatively, as suggested in this answer , you can use HLS and HSV . The colorsys module seems to have functions for converting from RGB. His documentation also links to these pages that are worth reading to understand why the Euclidean RGB distance does not actually work:

EDIT 2:

According to this answer , this library should be useful: http://code.google.com/p/python-colormath/

+20
source

The following is an optimized version of Python adapted from the Bruno accessory:

 def ColorDistance(rgb1,rgb2): '''d = {} distance between two colors(3)''' rm = 0.5*(rgb1[0]+rgb2[0]) d = sum((2+rm,4,3-rm)*(rgb1-rgb2)**2)**0.5 return d 

using:

 >>> import numpy >>> rgb1 = numpy.array([1,1,0]) >>> rgb2 = numpy.array([0,0,0]) >>> ColorDistance(rgb1,rgb2) 2.5495097567963922 
+3
source

Assuming rtol, gtol, and btol are valid for r, g, and b, respectively, why not:

 if abs(px[0]- r) <= rtol and \ abs(px[1]- g) <= gtol and \ abs(px[2]- b) <= btol: return x, y 
+2
source

Instead of this:

 if px[0] == r and px[1] == g and px[2] == b: 

Try the following:

 if max(map(lambda a,b: abs(ab), px, (r,g,b))) < tolerance: 

Where tolerance is the maximum difference that you are ready to accept in any of the color channels.

What he does is subtract each channel from your target values, accept the absolute values, and then the maximum values.

+1
source

Plain:

 def eq_with_tolerance(a, b, t): return at <= b <= a+t def FindColorIn(r,g,b, xmin, xmax, ymin, ymax, tolerance=0): image = ImageGrab.grab() for x in range(xmin, xmax): for y in range(ymin,ymax): px = image.getpixel((x, y)) if eq_with_tolerance(r, px[0], tolerance) and eq_with_tolerance(g, px[1], tolerance) and eq_with_tolerance(b, px[2], tolerance): return x, y 
0
source

from pyautogui source code

 def pixelMatchesColor(x, y, expectedRGBColor, tolerance=0): r, g, b = screenshot().getpixel((x, y)) exR, exG, exB = expectedRGBColor return (abs(r - exR) <= tolerance) and (abs(g - exG) <= tolerance) and (abs(b - exB) <= tolerance) 

you just need a little fix, and you're ready to go.

0
source

Here is a simple function that does not require any libraries:

 def color_distance(rgb1, rgb2): rm = 0.5 * (rgb1[0] + rgb2[0]) rd = ((2 + rm) * (rgb1[0] - rgb2[0])) ** 2 gd = (4 * (rgb1[1] - rgb2[1])) ** 2 bd = ((3 - rm) * (rgb1[2] - rgb2[2])) ** 2 return (rd + gd + bd) ** 0.5 

assuming rgb1 and rgb2 are RBG tuples

0
source

Here's a vectorized version of Python (numpy) answers by Bruno and Developer (i.e. , the implementation of the approximation obtained here ), which takes a pair of numpy arrays of the form (x, 3) where the individual lines are in the order of [R, G, B] and individual color values ∈ [0, 1].

You can reduce it with two two-layer due to readability. I'm not quite sure if this is the most optimized version possible, but it should be good enough.

 def colour_dist(fst, snd): rm = 0.5 * (fst[:, 0] + snd[:, 0]) drgb = (fst - snd) ** 2 t = np.array([2 + rm, 4 + 0 * rm, 3 - rm]).T return np.sqrt(np.sum(t * drgb, 1)) 

It was evaluated in comparison with the Developer version for each element above and gives the same results (with the exception of floating-point errors in two out of a thousand cases).

0
source

Source: https://habr.com/ru/post/905977/


All Articles