简体   繁体   English

使用 opencv-python 进行颜色检测

[英]Color detection using opencv-python

图片

How to detect color of balls in the given image using python-opencv?如何使用python-opencv检测给定图像中球的颜色?

Introduction介绍

I will dismantle the question in the following three sections我将在以下三个部分拆解问题

  • Obtain the English name of a color from a RGB or Hex value从 RGB 或 Hex 值中获取颜色的英文名称
  • Locate the circles on the image找到图像上的圆圈
  • Obtain the English name on per circle获取每个圆圈的英文名称

Obtain color name from RGB or Hex从 RGB 或 Hex 获取颜色名称

Using the following answer:使用以下答案:

We are almost done, except for the small change that cv2 uses BGR instead of RGB, therefore we take RGB[2] (the blue channel) to match the red channel of the webcolors.我们几乎完成了,除了 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())]

Which is already enough to solve the question if you only care about the colors that are used in the image.如果您只关心图像中使用的颜色,这已经足以解决问题。

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

Colors:颜色:

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

Notes:笔记:

  • This uses the webcolors package, but you can create your own dictionary.这使用webcolors包,但您可以创建自己的字典。 This gives you a higher control on the colors that you allow / disallow.这使您可以更好地控制允许/禁止的颜色。

Locate the Circles找到圆圈

The colors that we found above are all the unique colors that are contained in the image.我们在上面找到的颜色都是图像中包含的所有独特颜色。 This is often not really what we want.这往往不是我们真正想要的。 Instead we want to find the color that is most commonly used inside the circle.相反,我们想找到圆圈内最常用的颜色。

In order to define the color in a circle there are several sources that we can use:为了定义圆圈中的颜色,我们可以使用多种来源:

Which combines to the following code:它结合了以下代码:

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

I added the medianBlur to increase the consistency in locating the circles, alternatively you could play a bit more with the param values or radius sizes.我添加了中值模糊以增加定位圆的一致性,或者您可以更多地使用param值或半径大小。

Test code:测试代码:

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)

Answers:答案:

262 66 12
186 74 12
136 60 12

Obtain the English name per circle获取每圈英文名称

Now that we know where the circle is located, we can get the average color per circle and combine this with the above code obtain the final result.现在我们知道圆的位置,我们可以得到每个圆的平均颜色,并结合上面的代码得到最终的结果。

The following code locates all x and y values that are inside the circle.以下代码定位圆内的所有 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_x = [[x + dx for dx in range(-r, r) if 0 <= x + dx < width]]
    indices_y = [[y + dy for dy in range(-r, r) if 0 <= y + dy < height]]
    return np.array(np.meshgrid(indices_x, indices_y)).reshape(2, -1)

Which can then be used to obtain the average color value per circle.然后可以使用它来获得每个圆圈的平均颜色值。

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)

Answer:回答:

indigo
firebrick
darkturquoise

在此处输入图片说明

TL;DR 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_x = [[x + dx for dx in range(-r, r) if 0 <= x + dx < width]]
    indices_y = [[y + dy for dy in range(-r, r) if 0 <= y + dy < height]]
    return np.array(np.meshgrid(indices_x, indices_y)).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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM