使用 OpenCV 的 Python 图像中的颜色百分比

Posted

技术标签:

【中文标题】使用 OpenCV 的 Python 图像中的颜色百分比【英文标题】:Color percentage in image for Python using OpenCV 【发布时间】:2021-03-23 04:14:56 【问题描述】:

我正在创建一个可以检测图像中绿色百分比的代码。

.

我对 OpenCV 有一点经验,但对图像处理还是很陌生,希望对我的代码有所帮助。我应该如何更改此代码,以便它能够计算绿色而不是棕色的百分比?如果不是太麻烦,有人可以解释一下这些更改如何影响代码吗?下面是我想使用的图像的链接。 代码归功于@mmensing

import numpy as np
import cv2

img = cv2.imread('potato.jpg')

brown = [145, 80, 40]  # RGB
diff = 20
boundaries = [([brown[2]-diff, brown[1]-diff, brown[0]-diff],
               [brown[2]+diff, brown[1]+diff, brown[0]+diff])]

for (lower, upper) in boundaries:
    lower = np.array(lower, dtype=np.uint8)
    upper = np.array(upper, dtype=np.uint8)
    mask = cv2.inRange(img, lower, upper)
    output = cv2.bitwise_and(img, img, mask=mask)

    ratio_brown = cv2.countNonZero(mask)/(img.size/3)
    print('brown pixel percentage:', np.round(ratio_brown*100, 2))

    cv2.imshow("images", np.hstack([img, output]))
    cv2.waitKey(0)

【问题讨论】:

【参考方案1】:

我已经修改了您的脚本,以便您可以在测试图像中找到(大约)绿色百分比。我添加了一些 cmets 来解释代码:

# Imports
import cv2
import numpy as np

# Read image
imagePath = "D://opencvImages//"
img = cv2.imread(imagePath+"leaves.jpg")

# Here, you define your target color as
# a tuple of three values: RGB
green = [130, 158, 0]

# You define an interval that covers the values
# in the tuple and are below and above them by 20
diff = 20

# Be aware that opencv loads image in BGR format,
# that's why the color values have been adjusted here:
boundaries = [([green[2], green[1]-diff, green[0]-diff],
           [green[2]+diff, green[1]+diff, green[0]+diff])]

# Scale your BIG image into a small one:
scalePercent = 0.3

# Calculate the new dimensions
width = int(img.shape[1] * scalePercent)
height = int(img.shape[0] * scalePercent)
newSize = (width, height)

# Resize the image:
img = cv2.resize(img, newSize, None, None, None, cv2.INTER_AREA)

# check out the image resized:
cv2.imshow("img resized", img)
cv2.waitKey(0)


# for each range in your boundary list:
for (lower, upper) in boundaries:

    # You get the lower and upper part of the interval:
    lower = np.array(lower, dtype=np.uint8)
    upper = np.array(upper, dtype=np.uint8)

    # cv2.inRange is used to binarize (i.e., render in white/black) an image
    # All the pixels that fall inside your interval [lower, uipper] will be white
    # All the pixels that do not fall inside this interval will
    # be rendered in black, for all three channels:
    mask = cv2.inRange(img, lower, upper)

    # Check out the binary mask:
    cv2.imshow("binary mask", mask)
    cv2.waitKey(0)

    # Now, you AND the mask and the input image
    # All the pixels that are white in the mask will
    # survive the AND operation, all the black pixels
    # will remain black
    output = cv2.bitwise_and(img, img, mask=mask)

    # Check out the ANDed mask:
    cv2.imshow("ANDed mask", output)
    cv2.waitKey(0)

    # You can use the mask to count the number of white pixels.
    # Remember that the white pixels in the mask are those that
    # fall in your defined range, that is, every white pixel corresponds
    # to a green pixel. Divide by the image size and you got the
    # percentage of green pixels in the original image:
    ratio_green = cv2.countNonZero(mask)/(img.size/3)

    # This is the color percent calculation, considering the resize I did earlier.
    colorPercent = (ratio_green * 100) / scalePercent

    # Print the color percent, use 2 figures past the decimal point
    print('green pixel percentage:', np.round(colorPercent, 2))

    # numpy's hstack is used to stack two images horizontally,
    # so you see the various images generated in one figure:
    cv2.imshow("images", np.hstack([img, output]))
    cv2.waitKey(0)

输出:

green pixel percentage: 89.89

我已经制作了一些图像,这是绿色的二进制掩码:

这是ANDed 出掩码和输入图像:

关于这个sn-p的一些补充说明:

    加载带有OpenCV 的图像时要小心,因为它们已加载到 BGR 格式而不是通常的 RGB。在这里,sn-p 有这个 通过反转边界列表中的元素来覆盖,但保持 睁大眼睛看看这个常见的陷阱。

    您的输入图像太大而无法正确显示 cv2.imshow。我调整了它的大小并进行了处理。在末尾, 你看我在最后的百分比中考虑了这个调整大小的比例 计算。

    取决于您定义的目标颜色和您的差异 使用,您可能会产生负值。在这种情况下,对于 例如,对于 R = 0 值,在减去 diff 之后,您将 获取-20。当您对颜色进行编码时,这没有意义 无符号 8 位的强度。这些值必须在[0, 255] 范围内。 使用此方法注意负值。

现在,您可能会发现该方法不是很健壮。根据您的操作,您可以切换到HSV color space 以获得更好、更准确的二进制掩码。

你可以试试HSV-based这个掩码:

# The HSV mask values, defined for the green color:
lowerValues = np.array([29, 89, 70])
upperValues = np.array([179, 255, 255])

# Convert the image to HSV:
hsvImage = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Create the HSV mask
hsvMask = cv2.inRange(hsvImage, lowerValues, upperValues)

# AND mask & input image:
hsvOutput = cv2.bitwise_and(img, img, mask=hsvMask)

这会给你这个漂亮的蒙版图像:

【讨论】:

非常感谢!这真的帮助了我,我真的很感激。解释也很清楚!我只是想问一下如何测试基于 HSV 的掩码。我应该将其替换/添加到代码的哪个部分? @LimWZ 在您的代码中,您已经定义了颜色范围的下限和上限,如下所示:boundaries = [([green[2], green[1]... 作为green 的函数。 HSV 版本具有通过lowerValuesupperValues 数组硬编码的间隔值。这就是你必须修改的部分。另外,我正在使用cv2.cvtColor 将输入图像转换为HSV。该位不会出现在您的代码中,因为您已经在使用 RGB 颜色空间。您可以像这样在 HSV 版本中获取ratio_greenratio_green = cv2.countNonZero(hsvMask)

以上是关于使用 OpenCV 的 Python 图像中的颜色百分比的主要内容,如果未能解决你的问题,请参考以下文章

Python:如何从图像中切出具有特定颜色的区域(OpenCV,Numpy)

使用Python和OpenCV在图像之间执行超快速的颜色转换

opencv python中的图像匹配

如何使用 OpenCV 在 Python 中找到图像的平均颜色?

RGB 图像中最主要的颜色 - OpenCV / NumPy / Python

python-opencv-图像颜色映射