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

问题描述 投票:0回答:10

我制作了一个函数来查找图像中的颜色,并返回 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

结果:

从答案中得出,比较两种颜色的常规方法是欧几里德距离或切比雪夫距离。

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

例如,LAB、RGB 和 XYZ 是一个简单的平方 euc。距离可以解决问题:

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

LCH 和 HSL 稍微复杂一些,因为两者都有圆柱形色调,但一些数学解决了这个问题,然后就可以使用平方 eucl 了。这里也有。

在大多数情况下,我为每个通道的容差添加了“单独的参数”(使用 1 个全局容差和替代“修饰符”

HueTol := Tolerance * hueMod
LightTol := Tolerance * LightMod
)。


看起来建立在 XYZ(LAB、LCH)之上的色彩空间在我的许多场景中确实表现最佳。虽然 HSL 在某些情况下会产生非常好的结果,而且从 RGB 转换成的价格要便宜得多,但 RGB 也很棒,可以满足我的大部分需求。

python colors find
10个回答
24
投票

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

这里有一篇关于此的有趣文章:http://www.compuphase.com/cmetric.htm

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应该不会太困难。

编辑:

或者,按照此答案中的建议,您可以使用HLS 和 HSV

colorsys
模块似乎具有从RGB进行转换的功能。它的文档还链接到这些页面,这些页面值得阅读以了解为什么 RGB 欧几里德距离实际上不起作用:

编辑2:

根据这个答案,这个库应该很有用:http://code.google.com/p/python-colormath/


8
投票

这是一个优化的

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

3
投票

而不是这个:

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:

其中

tolerance
是您在任何颜色通道中愿意接受的最大差异。

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


2
投票

假设 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

1
投票

来自 pyautogui 源代码

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)

您只需要稍作修改即可开始。


1
投票

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

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


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)
    

0
投票
简单:

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
    

0
投票
我将欧几里得方法与 Bruno 针对 512 种不同颜色发布的感知颜色方法进行了比较。结果已标准化,因此 1 表示最大距离,0 表示无距离。在这里您可以看到两个值相互对比:

如您所见,欧几里德距离并没有从感知颜色距离中得出太多。 以下是在 Python 中获取值的代码:

def get_perceived_color_distance_unnormalized(color1, color2): r1, g1, b1 = color1 r2, g2, b2 = color2 r = r1 - r2 g = g1 - g2 b = b1 - b2 r_mean = (r1 + r2) / 2 return math.sqrt( (512 + r_mean) / 256 * r ** 2 # >> 8 is same as dividing by 256 + 4 * g ** 2 + (767 - r_mean) / 256 * b ** 2) def get_euclidean_color_distance_unnormalized(color1, color2): r1, g1, b1 = color1 r2, g2, b2 = color2 return math.sqrt( (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2) Black = (0, 0, 0) White = (255, 255, 255) maximum_perceived_distance = get_perceived_color_distance_unnormalized(White, Black) maximum_euclidean_distance = get_euclidean_color_distance_unnormalized(White, Black) def get_perceived_color_distance(color1, color2): """ Calculates a normalized distance (from 0 to 1) between two colors meaningful to the eyes (https://www.compuphase.com/cmetric.htm). """ return get_perceived_color_distance_unnormalized(color1, color2) / maximum_perceived_distance def get_euclidean_color_distance(color1, color2): """ Calculates a normalized Euclidean distance (from 0 to 1) between two colors. """ return get_euclidean_color_distance_unnormalized(color1, color2) / maximum_euclidean_distance
    

-1
投票
这是一个简单的函数,不需要任何库:

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 元组

© www.soinside.com 2019 - 2024. All rights reserved.