Python-找到相似颜色的最佳方法

Python-找到相似颜色的最佳方法,python,colors,find,Python,Colors,Find,我做了一个函数,在图像中找到一种颜色,然后返回x,y。现在我需要添加一个新函数,在这里我可以找到具有给定公差的颜色。应该很容易吗 用于在图像中查找颜色并返回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 = ima

我做了一个函数,在图像中找到一种颜色,然后返回x,y。现在我需要添加一个新函数,在这里我可以找到具有给定公差的颜色。应该很容易吗

用于在图像中查找颜色并返回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

结果:

根据答案,比较两种颜色的常规方法是欧几里德距离或切比雪夫距离

我决定主要使用(平方)欧几里德距离和多个不同的颜色空间。实验室、三角洲(LCH)、XYZ、HSL和RGB。在我的代码中,大多数颜色空间使用平方欧几里德距离来计算差异

例如,对于LAB,RGB和XYZ是一个简单的平方euc。距离是关键:

if ((X-X1)^2 + (Y-Y1)^2 + (Z-Z1)^2) <= (Tol^2) then
  ...
如果((X-X1)^2+(Y-Y1)^2+(Z-Z1)^2)简单:

def eq_with_tolerance(a, b, t):
    return a-t <= 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
def eq_与_公差(a、b、t):

返回a-t假设rtol、gtol和btol分别是r、g和b的公差,为什么不:

if abs(px[0]- r) <= rtol and \
   abs(px[1]- g) <= gtol and \
   abs(px[2]- b) <= btol:
    return x, y
如果abs(px[0]-r)而不是这个:

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

if max(map(lambda a,b: abs(a-b), px, (r,g,b))) < tolerance:
if max(map(λa,b:abs(a-b),px,(r,g,b))<公差:
其中,
公差
是您愿意在任何颜色通道中接受的最大差异


它所做的是从目标值中减去每个通道,取绝对值,然后取其中的最大值。

计算RGB颜色之间的距离,以一种对眼睛有意义的方式,并不像计算两个RGB向量之间的欧几里德距离那么容易

这里有一篇有趣的文章:

C中的示例实现如下所示:

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));
}
移植到Python应该不会太困难

编辑:

或者,如中所建议的,您可以使用。该模块似乎具有从RGB进行转换的功能。其文档还链接到这些页面,这些页面值得一读,以了解RGB欧几里得距离为何不起作用:

编辑2:


根据,这个库应该很有用:

这里有一个优化的
Python
版本,它是从Bruno的asnwer改编而来的:

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
用法:

>>> import numpy
>>> rgb1 = numpy.array([1,1,0])
>>> rgb2 = numpy.array([0,0,0])
>>> ColorDistance(rgb1,rgb2)
2.5495097567963922
来自pyautogui

def像素匹配颜色(x,y,expectedRGBColor,公差=0):
r、 g,b=屏幕截图().getpixel((x,y))
exR、exG、exB=expectedRGBColor

return(abs(r-exR)这是一个不需要任何库的简单函数:

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

假设rgb1和rgb2是RBG元组

这是Bruno和开发者答案的矢量化Python(numpy)版本(即,近似推导的实现),它接受一对形状
(x,3)
的numpy数组,其中单个行位于
[R,G,B]
顺序和单个颜色值∈[0,1]

你可以以牺牲可读性为代价把它减少两行。我不完全确定它是否是最优化的版本,但它应该足够好

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))

它是根据上面开发人员的每元素版本进行评估的,并产生相同的结果(除了千分之二的浮动精度错误).

一个更简洁的python函数实现,该函数采用2个图像路径,使用cv.imread读取它们,并输出一个矩阵,每个矩阵单元具有不同的颜色。您可以轻松地将其更改为仅匹配2种颜色

        import numpy as np
        import cv2 as cv    
        
        def col_diff(img1, img2):
            img_bgr1 = cv.imread(img1) # since opencv reads as B, G, R
            img_bgr2 = cv.imread(img2)
            r_m = 0.5 * (img_bgr1[:, :, 2] + img_bgr2[:, :, 2])
            delta_rgb = np.square(img_bgr1- img_bgr2)
            cols_diffs = delta_rgb[:, :, 2] * (2 + r_m / 256) + delta_rgb[:, :, 1] * (4) + 
                            delta_rgb[:, :, 0] * (2 + (255 - r_m) / 256)
            cols_diffs = np.sqrt(cols_diffs)            
            # lets normalized the values to range [0 , 1] 
            cols_diffs_min = np.min(cols_diffs)
            cols_diffs_max = np.max(cols_diffs)
            cols_diffs_normalized = (cols_diffs - cols_diffs_min) / (cols_diffs_max - cols_diffs_min)
            
            return np.sqrt(cols_diffs_normalized)

如果在图像中找不到颜色,您应该返回一些内容。例如,一个错误代码。您是如何定义公差的?分别为
r
g
b
?我同意约翰的说法:您已经尝试了什么?您可以查看并搜索Python实现。@jb:返回的都是Python实现,这就是他的意思代码已经有了。@SLACKY,你需要先导入操作符。
。(尽管如此,这仍然是欧几里德距离的公式:这不会给你视觉上预期的结果。)@Bruno:我的度量甚至比欧几里德距离还要差!我没有强调这一部分(但对你的答案投了赞成票).如果公差很小,这可能无关紧要,但如果公差很大,这可能会很重要。@JohnZwinch:啊,是的,对不起,我误读了公式(没有正方形…)。它看起来像一个.Oops--Python abs一次只做一项。我已经更新了我的答案,使用lambda对每个元素进行减法和abs。请参阅我的答案,以获得优化的
Python
版本。据我所见,
x**0.5
比math import sqrt
中的
慢得多,然后使用
sqrt(x)
。但是如果你导入数学
并使用
math.sqrt(x)
你将看不到什么不同。
(35255,24)
vs
(38,38120)
返回
nan
这根本不起作用,我只是浪费了30分钟调试它给出了错误的答案。我的整个项目即将因此而毁灭
        import numpy as np
        import cv2 as cv    
        
        def col_diff(img1, img2):
            img_bgr1 = cv.imread(img1) # since opencv reads as B, G, R
            img_bgr2 = cv.imread(img2)
            r_m = 0.5 * (img_bgr1[:, :, 2] + img_bgr2[:, :, 2])
            delta_rgb = np.square(img_bgr1- img_bgr2)
            cols_diffs = delta_rgb[:, :, 2] * (2 + r_m / 256) + delta_rgb[:, :, 1] * (4) + 
                            delta_rgb[:, :, 0] * (2 + (255 - r_m) / 256)
            cols_diffs = np.sqrt(cols_diffs)            
            # lets normalized the values to range [0 , 1] 
            cols_diffs_min = np.min(cols_diffs)
            cols_diffs_max = np.max(cols_diffs)
            cols_diffs_normalized = (cols_diffs - cols_diffs_min) / (cols_diffs_max - cols_diffs_min)
            
            return np.sqrt(cols_diffs_normalized)