首页 > 解决方案 > 使用 opencv-python 进行颜色检测

问题描述

图片

如何使用 python-opencv 检测给定图像中球的颜色?

标签: pythonpython-3.xopencv-python

解决方案


介绍

我将在以下三个部分拆解这个问题

  • 从 RGB 或 Hex 值中获取颜色的英文名称
  • 找到图像上的圆圈
  • 获取每圈的英文名字

从 RGB 或 Hex 获取颜色名称

使用以下答案:

我们几乎完成了,除了 cv2 使用 BGR 而不是 RGB 的小变化,因此我们采用 RGB[2](蓝色通道)来匹配 webcolors 的红色通道。

def color_rgb_to_name(rgb: tuple[int, int, int]) -> str:
    """
    Translates an rgb value to the closest English color name known

    Args:
        rgb: The rgb value that has to be translated to the color name.

    Returns:
        The name of the colors that most closely defines the rgb value in CSS3.
    """
    min_colours = {}
    for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
        r_c, g_c, b_c = webcolors.hex_to_rgb(key)
        rd = (r_c - rgb[2]) ** 2
        gd = (g_c - rgb[1]) ** 2
        bd = (b_c - rgb[0]) ** 2
        min_colours[(rd + gd + bd)] = name
    return min_colours[min(min_colours.keys())]

如果您只关心图像中使用的颜色,这已经足以解决问题。

image = cv2.imread('image.jpg')
colors = set([color_rgb_to_name(val) for val in np.unique(image.reshape(-1, 3), axis=0)])

颜色:

{'firebrick', 'cadetblue', 'peru', 'indianred', 'darkturquoise', 'cyan', 'darkviolet', 'darkorange', 'midnightblue', 'indigo', 'lightseagreen', 'mediumturquoise', 'blue', 'brown', 'chocolate', 'saddlebrown', 'mediumblue', 'darkslateblue', 'turquoise', 'blueviolet', 'sienna', 'black', 'orangered', 'slateblue'}

笔记:

  • 这使用webcolors包,但您可以创建自己的字典。这使您可以更好地控制允许/禁止的颜色。

找到圆圈

我们在上面找到的颜色都是图像中包含的所有独特颜色。这通常不是我们真正想要的。相反,我们想找到圆圈内最常用的颜色。

为了定义圆圈中的颜色,我们可以使用几个来源:

它结合了以下代码:

def locate_circles(img: np.ndarray, vmin=10, vmax=30) -> np.ndarray:
    """
    Locates circles on a gray image.

    Args:
        img: a gray image with black background.
        vmin: The minimum radius value of the circles.
        vmax: The maximum radius value of the circles.

    Returns:
        A numpy array containing the center location of the circles and the radius.
    """
    img = cv2.medianBlur(img, 5)
    circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=20, minRadius=vmin, maxRadius=vmax)
    circles = np.round(circles[0, :]).astype("int")
    return circles

我添加了 medianBlur 以增加定位圆圈的一致性,或者您可以更多地使用param值或半径大小。

测试代码:

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

for (x, y, r) in locate_circles(gray, vmin=10, vmax=30):
    print(x, y, r)

答案:

262 66 12
186 74 12
136 60 12

获取每个圈子的英文名字

现在我们知道了圆圈的位置,我们可以得到每个圆圈的平均颜色,并将其与上面的代码结合得到最终结果。

以下代码定位圆内的所有 x 和 y 值。

def coordinates(x: int, y: int, r: int, width: int, height: int) -> np.ndarray:
    """
    Locates all valid x and y coordinates inside a circle.

    Args:
        x: Center column position.
        y: Center row position.
        r: Radius of the circle.
        width: the maximum width value that is still valid (in bounds)
        height: the maximum height values that is still valid (in bounds)

    Returns:
        A numpy array with all valid x and y coordinates that fall within the circle.
    """
    indices = []
    for dx in range(-r, r):
        for dy in range(-r, r):
            if 0 <= x + dx < width and 0 <= y + dy < height:
                indices.append([x + dx, y + dy])
    return np.array(indices).T.reshape(2, -1)

然后可以使用它来获得每个圆圈的平均颜色值。

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

for (x, y, r) in locate_circles(gray, vmin=10, vmax=30):
    columns, rows = coordinates(x, y, r, *gray.shape[:2])
    color = np.average(image[rows, columns], axis=0).astype(np.uint8)
    name = color_rgb_to_name(color)

    # Draw the information on the screen
    cv2.putText(image, name, (x - 20, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 1)

回答:

indigo
firebrick
darkturquoise

在此处输入图像描述

TL;博士

import cv2
import numpy as np
import webcolors


def imshow(img, delay=0):
    cv2.imshow('Test', img)
    cv2.waitKey(delay)


def locate_circles(img: np.ndarray, vmin=10, vmax=30) -> np.ndarray:
    """
    https://www.tutorialspoint.com/find-circles-in-an-image-using-opencv-in-python
    https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/
    https://stackoverflow.com/questions/67764821/how-to-find-the-circle-in-the-given-images-using-opencv-python-hough-circles


    Locates circles on a gray image.

    Args:
        img: a gray image with black background.
        vmin: The minimum radius value of the circles.
        vmax: The maximum radius value of the circles.

    Returns:
        A numpy array containing the center location of the circles and the radius.
    """
    img = cv2.medianBlur(img, 5)
    circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=20, minRadius=vmin, maxRadius=vmax)
    circles = np.round(circles[0, :]).astype("int")
    return circles


def coordinates(x: int, y: int, r: int, width: int, height: int) -> np.ndarray:
    """
    Locates all valid x and y coordinates inside a circle.

    Args:
        x: Center column position.
        y: Center row position.
        r: Radius of the circle.
        width: the maximum width value that is still valid (in bounds)
        height: the maximum height values that is still valid (in bounds)

    Returns:
        A numpy array with all valid x and y coordinates that fall within the circle.
    """
    indices = []
    for dx in range(-r, r):
        for dy in range(-r, r):
            if 0 <= x + dx < width and 0 <= y + dy < height:
                indices.append([x + dx, y + dy])
    return np.array(indices).T.reshape(2, -1)


def draw_circles(img: np.ndarray, x: int, y: int, r: int):
    """
    draw the circle in the output image, then draw a rectangle corresponding to the center of the circle

    Args:
        img: Image on which to draw the circle location and center.
        x: Center column position.
        y: Center row position.
        r: Radius of the circle.

    Modifies:
        The input image by drawing a circle on it and a rectangle on the image.
    """
    cv2.circle(img, (x, y), r, (0, 255, 0), 4)
    cv2.rectangle(img, (x - 2, y - 2), (x + 2, y + 2), (0, 128, 255), -1)


def color_rgb_to_name(rgb: tuple[int, int, int]) -> str:
    """
    https://stackoverflow.com/questions/9694165/convert-rgb-color-to-english-color-name-like-green-with-python

    Translates an rgb value to the closest English color name known

    Args:
        rgb: The rgb value that has to be translated to the color name.

    Returns:
        The name of the colors that most closely defines the rgb value in CSS3.
    """
    min_colours = {}
    for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
        r_c, g_c, b_c = webcolors.hex_to_rgb(key)
        rd = (r_c - rgb[2]) ** 2
        gd = (g_c - rgb[1]) ** 2
        bd = (b_c - rgb[0]) ** 2
        min_colours[(rd + gd + bd)] = name
    return min_colours[min(min_colours.keys())]


if __name__ == '__main__':
    image = cv2.imread('image.jpg')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    for (x, y, r) in locate_circles(gray, vmin=10, vmax=30):
        columns, rows = coordinates(x, y, r, *gray.shape[:2])
        color = np.average(image[rows, columns], axis=0).astype(np.uint8)
        name = color_rgb_to_name(color)
        print(name)

        # Draw extra information on the screen
        # draw_circles(image, x, y, r)
        cv2.putText(image, name, (x - 20, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 1)

    # show the output image
    imshow(image)


推荐阅读