不能用 HSV 使黄色消失(OpenCV,Python)

Can't make yellow disappear with HSV (OpenCV, Python)

我有下图:

我想做的是保留所有红色数字。

使用此代码..

import cv2
import numpy as np

def callback(x):
    pass

cap = cv2.VideoCapture(0)
cv2.namedWindow('image')

ilowH = 0
ihighH = 179
ilowS = 0
ihighS = 255
ilowV = 0
ihighV = 255

# create trackbars for color change
cv2.createTrackbar('lowH', 'image', ilowH, 179, callback)
cv2.createTrackbar('highH', 'image', ihighH, 179, callback)

cv2.createTrackbar('lowS', 'image', ilowS, 255, callback)
cv2.createTrackbar('highS', 'image', ihighS, 255, callback)

cv2.createTrackbar('lowV', 'image', ilowV, 255, callback)
cv2.createTrackbar('highV', 'image', ihighV, 255, callback)

while True:
    # grab the frame
    frame = cv2.imread('color_test.png')

    # get trackbar positions
    ilowH = cv2.getTrackbarPos('lowH', 'image')
    ihighH = cv2.getTrackbarPos('highH', 'image')
    ilowS = cv2.getTrackbarPos('lowS', 'image')
    ihighS = cv2.getTrackbarPos('highS', 'image')
    ilowV = cv2.getTrackbarPos('lowV', 'image')
    ihighV = cv2.getTrackbarPos('highV', 'image')

    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    lower_hsv = np.array([ilowH, ilowS, ilowV])
    higher_hsv = np.array([ihighH, ihighS, ihighV])
    mask = cv2.inRange(hsv, lower_hsv, higher_hsv)

    frame = cv2.bitwise_and(frame, frame, mask=mask)

    # show thresholded image
    cv2.imshow('image', frame)
    k = cv2.waitKey(1) & 0xFF  # large wait time to remove freezing
    if k == 113 or k == 27:
        break

cv2.destroyAllWindows()
cap.release()

...我最多能得到的是:

如何去除黄色并保留 3 个红色图形?在这种情况下,HSL 是一个很好的替代方案吗? 请记住,中间的红色与其他两个不同;一个是全红 (255, 0, 0) 另一个是 less (237, 28, 36) RGB.

我前一段时间做了类似的事情,最后在 HSV space 中定义了我自己的基本颜色,包括单色定义(我承认这有点武断)。

无论如何,正如评论中所说,在 HSV 中,红色色调被分割,所以我做了一个简单的函数来组合我定义的任何颜色,以便轻松地为它们创建蒙版:

import cv2
import numpy as np

HSV_RANGES = {
    # red is a major color
    'red': [
        {
            'lower': np.array([0, 39, 64]),
            'upper': np.array([20, 255, 255])
        },
        {
            'lower': np.array([161, 39, 64]),
            'upper': np.array([180, 255, 255])
        }
    ],
    # yellow is a minor color
    'yellow': [
        {
            'lower': np.array([21, 39, 64]),
            'upper': np.array([40, 255, 255])
        }
    ],
    # green is a major color
    'green': [
        {
            'lower': np.array([41, 39, 64]),
            'upper': np.array([80, 255, 255])
        }
    ],
    # cyan is a minor color
    'cyan': [
        {
            'lower': np.array([81, 39, 64]),
            'upper': np.array([100, 255, 255])
        }
    ],
    # blue is a major color
    'blue': [
        {
            'lower': np.array([101, 39, 64]),
            'upper': np.array([140, 255, 255])
        }
    ],
    # violet is a minor color
    'violet': [
        {
            'lower': np.array([141, 39, 64]),
            'upper': np.array([160, 255, 255])
        }
    ],
    # next are the monochrome ranges
    # black is all H & S values, but only the lower 25% of V
    'black': [
        {
            'lower': np.array([0, 0, 0]),
            'upper': np.array([180, 255, 63])
        }
    ],
    # gray is all H values, lower 15% of S, & between 26-89% of V
    'gray': [
        {
            'lower': np.array([0, 0, 64]),
            'upper': np.array([180, 38, 228])
        }
    ],
    # white is all H values, lower 15% of S, & upper 10% of V
    'white': [
        {
            'lower': np.array([0, 0, 229]),
            'upper': np.array([180, 38, 255])
        }
    ]
}


def create_mask(hsv_img, colors):
    """
    Creates a binary mask from HSV image using given colors.
    """

    # noinspection PyUnresolvedReferences
    mask = np.zeros((hsv_img.shape[0], hsv_img.shape[1]), dtype=np.uint8)

    for color in colors:
        for color_range in HSV_RANGES[color]:
            # noinspection PyUnresolvedReferences
            mask += cv2.inRange(
                hsv_img,
                color_range['lower'],
                color_range['upper']
            )

    return mask

将它应用于您的示例(我将其命名为 "color_shapes.png"),我得到了很好的结果:

img = cv2.imread('color_shapes.png')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

red_mask = create_mask(img_hsv, ['red'])

mask_img = cv2.bitwise_and(img_hsv, img_hsv, mask=red_mask)

背景

我想提出一个替代方法。对于 segmenting/isolating 主色(红色、蓝色、绿色和黄色); LAB color space 给你更多的自由。在 HSV space 中,您必须更改所有三个通道中存在的值。但在 LAB space 中,您可以只关注 1 个通道,具体取决于您要分割的颜色。

LAB space 有 3 个通道(2 个颜色通道和 1 个亮度通道):

  • L-channel:合并亮度
  • A-channel: 结合了红色和绿色
  • B-channel: 合并了蓝色和黄色

如果仔细观察下图中的 a-axis,可以在该轴的两端分割红色和绿色。

代码:

img = cv2.imread('colored_blobs.png')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_channel = lab[:,:,1]

如您所见,红色物体位于光谱较亮的一端。对于此图像,我从 a_channel 中的最大值减去像素强度 10 并将其设置为阈值:

th = cv2.threshold(dst,int(np.max(a_channel) - 10),255,cv2.THRESH_BINARY)[1]

剩下要做的就是屏蔽:

masked = cv2.bitwise_and(img, img, mask = th)

添加信息:

我在这里写了一个类似的答案来检测绿色颜色:How to define a threshold value to detect only green colour objects in an image with Python OpenCV?

Performing the same operation on the B-channel will help segment blue/yellow colors.