【问题标题】:Bounding box on objects based on color python基于颜色python的对象边界框
【发布时间】:2023-03-08 14:36:01
【问题描述】:

我尝试在这张图片中的每个对象上绘制一个边界框,我从documentation编写了这段代码

import cv2 as cv2
import os
import numpy as np


img = cv2.imread('1 (2).png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
ret,thresh = cv2.threshold(img,127,255,0)
im2,contours,hierarchy = cv2.findContours(thresh, 1, 2)
for item in range(len(contours)):
    cnt = contours[item]
    if len(cnt)>20:
        print(len(cnt))
        M = cv2.moments(cnt)
        cx = int(M['m10']/M['m00'])
        cy = int(M['m01']/M['m00'])
        x,y,w,h = cv2.boundingRect(cnt)
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
        cv2.imshow('image',img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

结果只有一个对象,

当我将此行中的值 127 更改为此行中的 200 ret,thresh = cv2.threshold(img,127,255,0) 时,我得到了不同的对象。

这是原图

问题是如何一次检测所有对象?

【问题讨论】:

  • 问题是?
  • 第一步是阅读手册:docs.opencv.org/2.4/modules/imgproc/doc/…
  • 由于您对颜色(或具体的色调)感兴趣,通过将 BGR 图像转换为灰度来丢弃所有颜色信息似乎会适得其反。也许使用 HSV 或 HSL 中的色调组件会更好?
  • @ZeyadEtman 太好了。只是阅读代码,我注意到以下内容——在对cv2.thresholdcv2.findContours 的调用中,您对某些参数使用了“幻数”。在第一种情况下,0 应该是 cv2.THRESH_BINARY,在第二种情况下,1cv2.RETR_LIST2cv2.CHAIN_APPROX_SIMPLE。对这些参数使用命名常量总是更好——我希望你看到这如何使代码更容易理解。
  • @ZeyadEtman this 做的事情接近你想要的吗? - 我只是找到主要色调,创建包含该色调像素的区域的蒙版,然后使用蒙版创建单独的图像。它忽略了色调略有不同的区域之间的过渡。您可以使用掩码来确定边界框。

标签: python python-3.x opencv image-processing opencv3.0


【解决方案1】:

第一步是了解你的算法在做什么......特别是这个函数: ret,thresh = cv2.threshold(img,127,255,0)

127 的值是 0 到 255 之间的灰度值。阈值函数将低于 127 的像素值更改为 0,高于 127 的像素值更改为 255

参考您的彩色图像,绿色斑点和黄色斑点的灰度输出均高于 127,因此两者都更改为 255,因此两者都被 findContours() 方法捕获

您可以在 thresh 对象上运行 imshow 以准确了解发生了什么。

现在,当您将127 替换为200 时,只有黄色斑点的灰度值高于200,因此在thresh Mat 中只能看到该斑点

要一次检测“所有对象”,请进一步试验threshold 方法并使用imshow 研究thresh 对象

【讨论】:

    【解决方案2】:

    方法相当简单。我们首先转换为 HSV 并仅抓取色调通道。

    image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    h,_,_ = cv2.split(image_hsv)
    

    接下来,我们找到主要的色调——首先使用numpy.bincount 计算每个色调的出现次数(我们@9​​87654322@ 色调通道图像使其成为一维):

    bins = np.bincount(h.flatten())
    

    然后使用numpy.where找出哪些足够常见:

    MIN_PIXEL_CNT_PCT = (1.0/20.0)
    peaks = np.where(bins > (h.size * MIN_PIXEL_CNT_PCT))[0]
    

    现在我们已经确定了所有的主要色调,我们可以重复处理图像以找到对应于它们中的每一个的区域:

    for i, peak in enumerate(peaks):
    

    我们首先创建一个遮罩,选择该色调的所有像素(cv2.inRange,然后从输入的 BGR 图像中提取相应的部分(cv2.bitwise_and

    mask = cv2.inRange(h, peak, peak)
    blob = cv2.bitwise_and(image, image, mask=mask)
    

    接下来,我们找到这个色调的所有连续区域的轮廓(cv2.findContours,这样我们就可以单独处理它们了

    _, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    

    现在,对于每个已识别的连续区域

    for j, contour in enumerate(contours):
    

    我们确定边界框(cv2.boundingRect,并通过用白色填充轮廓多边形(numpy.zeros_likecv2.drawContours)来创建与该轮廓对应的蒙版

    bbox = cv2.boundingRect(contour)
    contour_mask = np.zeros_like(mask)
    cv2.drawContours(contour_mask, contours, j, 255, -1)
    

    那么我们就可以只增加边界框对应的ROI

    region = blob.copy()[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
    region_mask = contour_mask[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
    region_masked = cv2.bitwise_and(region, region, mask=region_mask)
    

    或者可视化(cv2.rectangle边界框:

    result = cv2.bitwise_and(blob, blob, mask=contour_mask)
    top_left, bottom_right = (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3])
    cv2.rectangle(result, top_left, bottom_right, (255, 255, 255), 2)
    

    或者做你想做的任何其他处理。


    完整脚本

    import cv2
    import numpy as np
    
    # Minimum percentage of pixels of same hue to consider dominant colour
    MIN_PIXEL_CNT_PCT = (1.0/20.0)
    
    image = cv2.imread('colourblobs.png')
    if image is None:
        print("Failed to load iamge.")
        exit(-1)
    
    image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    # We're only interested in the hue
    h,_,_ = cv2.split(image_hsv)
    # Let's count the number of occurrences of each hue
    bins = np.bincount(h.flatten())
    # And then find the dominant hues
    peaks = np.where(bins > (h.size * MIN_PIXEL_CNT_PCT))[0]
    
    # Now let's find the shape matching each dominant hue
    for i, peak in enumerate(peaks):
        # First we create a mask selecting all the pixels of this hue
        mask = cv2.inRange(h, peak, peak)
        # And use it to extract the corresponding part of the original colour image
        blob = cv2.bitwise_and(image, image, mask=mask)
    
        _, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
        for j, contour in enumerate(contours):
            bbox = cv2.boundingRect(contour)
            # Create a mask for this contour
            contour_mask = np.zeros_like(mask)
            cv2.drawContours(contour_mask, contours, j, 255, -1)
    
            print "Found hue %d in region %s." % (peak, bbox)
            # Extract and save the area of the contour
            region = blob.copy()[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
            region_mask = contour_mask[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
            region_masked = cv2.bitwise_and(region, region, mask=region_mask)
            file_name_section = "colourblobs-%d-hue_%03d-region_%d-section.png" % (i, peak, j)
            cv2.imwrite(file_name_section, region_masked)
            print " * wrote '%s'" % file_name_section
    
            # Extract the pixels belonging to this contour
            result = cv2.bitwise_and(blob, blob, mask=contour_mask)
            # And draw a bounding box
            top_left, bottom_right = (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3])
            cv2.rectangle(result, top_left, bottom_right, (255, 255, 255), 2)
            file_name_bbox = "colourblobs-%d-hue_%03d-region_%d-bbox.png" % (i, peak, j)
            cv2.imwrite(file_name_bbox, result)
            print " * wrote '%s'" % file_name_bbox
    

    控制台输出

    Found hue 32 in region (186, 184, 189, 122).
     * wrote 'colourblobs-0-hue_032-region_0-section.png'
     * wrote 'colourblobs-0-hue_032-region_0-bbox.png'
    Found hue 71 in region (300, 197, 1, 1).
     * wrote 'colourblobs-1-hue_071-region_0-section.png'
     * wrote 'colourblobs-1-hue_071-region_0-bbox.png'
    Found hue 71 in region (301, 195, 1, 1).
     * wrote 'colourblobs-1-hue_071-region_1-section.png'
     * wrote 'colourblobs-1-hue_071-region_1-bbox.png'
    Found hue 71 in region (319, 190, 1, 1).
     * wrote 'colourblobs-1-hue_071-region_2-section.png'
     * wrote 'colourblobs-1-hue_071-region_2-bbox.png'
    Found hue 71 in region (323, 176, 52, 14).
     * wrote 'colourblobs-1-hue_071-region_3-section.png'
     * wrote 'colourblobs-1-hue_071-region_3-bbox.png'
    Found hue 71 in region (45, 10, 330, 381).
     * wrote 'colourblobs-1-hue_071-region_4-section.png'
     * wrote 'colourblobs-1-hue_071-region_4-bbox.png'
    Found hue 109 in region (0, 0, 375, 500).
     * wrote 'colourblobs-2-hue_109-region_0-section.png'
     * wrote 'colourblobs-2-hue_109-region_0-bbox.png'
    Found hue 166 in region (1, 397, 252, 103).
     * wrote 'colourblobs-3-hue_166-region_0-section.png'
     * wrote 'colourblobs-3-hue_166-region_0-bbox.png'
    

    示例输出图像

    黄色边框:

    黄色提取区域:

    最大的绿色边界框(还有其他几个不相交的小区域):

    ...以及对应的提取区域:

    【讨论】:

    • 如何将这种方法用于灰度或单通道图像?
    • 我得到“TypeError: lowerb is not a numpy array, not a scalar” on line mask = cv2.inRange(h, peak, peak)
    猜你喜欢
    • 1970-01-01
    • 2017-07-24
    • 2021-09-07
    • 2014-05-24
    • 1970-01-01
    • 2021-12-23
    • 2021-12-29
    • 1970-01-01
    • 2020-07-18
    相关资源
    最近更新 更多