如何使用openCV准确检测此图片上的棕色/黑色/灰色/白色

2024-04-27 04:49:34 发布

您现在位置:Python中文网/ 问答频道 /正文

上下文:

我们正在建造一个机器人,它将被分配一个颜色的冰球来拾取,并且需要去抓取它。 因此,从世界视图摄像机,他将得到近似坐标

我首先使用HoughCircles来找到每个圆的中心,但后来我意识到我还需要知道找到的对应圆的颜色,所以我尝试了另一种方法(见下文)

注意:圆圈将随机放置,无硬编码

以下是图像的外观:

enter image description here

问题:

很难获得准确的HSV值来正确检测标题中的颜色,加上图像质量不是最好的。我认为这些冰球的中间圆圈是为了帮助我们区分它们,但由于大多数冰球都有一个浅蓝色的圆圈,我不知道这有什么帮助哈哈

我的尝试:

1.

我使用openCV轨迹栏来获得每种颜色的近似下限和上限(除了上面提到的那些颜色),这些颜色很难获得)

2.

我在图片上涂上遮罩,然后用力矩找到圆心

    import cv2
    import numpy as np
    
    img = cv2.imread('Photos/lastBoard.png')
    frame_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    #red color
    lower_values = np.array([0,123,40])
    upper_values = np.array([5,255,114])
    
    mask = cv2.inRange(frame_hsv, lower_values, upper_values)
    
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    for c in contours:
        M = cv2.moments(c)
        if M["m00"] != 0:
            #####
            (x,y),radius = cv2.minEnclosingCircle(c)
            center = (int(x),int(y))
            
            radius = int(radius)
            cX = int(M["m10"] / M["m00"])
    
            cY = int(M["m01"] / M["m00"])
            #this condition is just to tell to detect in that area of the image only
            if cX > 500 and radius >8:
                cv2.circle(img, (cX, cY), 2, (0,255,0), -1)
cv2.imshow("Image", img)

cv2.waitKey(0)

红色输出

enter image description here

我需要什么帮助

找到棕色、黑色、白色和灰色真的很难,我的方法似乎不太准确。有没有更好的方法让我这么做?非常感谢

enter image description here


Tags: 方法图像importimg颜色npcv2frame
1条回答
网友
1楼 · 发布于 2024-04-27 04:49:34

这是一种可能的方法,它仍然使用HSV颜色空间,您必须正确获取HSV范围值。查找目标颜色的RGB -> HSV等效值。您肯定可以从一些预处理中获益,从而更好地清理您的口罩。您还可以实现一个轮廓过滤器,因为您正在寻找的感兴趣的斑点(圆盘)具有非常独特的属性,例如,纵横比、面积,当然还有圆度。我提议采取以下步骤:

  1. 为要查找的每个目标冰球获取HSV
  2. 定义upperlower范围值
  3. 设置HSV图像的阈值以获得二进制掩码
  4. 应用区域过滤器以消除小噪音
  5. 应用一些形态学(Dilate+Erode)来改善你的目标斑点
  6. 获取外部轮廓(忽略内部轮廓)
  7. 将这些等高线转换为bounding rectangles
  8. 获取两个bounding rectangles属性:aspect ratioarea
  9. 根据阈值属性值过滤边框

让我们看看代码:

# importing cv2 and numpy:
import numpy as np
import cv2

# image path
path = "C://opencvImages//"
fileName = "board.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Convert the image to HSV:
frame_hsv = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)

# Prepare a dictionary to store the lower and upper
# HSV thresholds:
rangeDictionary = {}

# brown color
lower_values = np.array([6, 63, 0])
upper_values = np.array([23, 255, 81])

# push it into the dictionary:
rangeDictionary[0] = (lower_values, upper_values, "brown")

# gray color
lower_values = np.array([23, 0, 0])
upper_values = np.array([80, 105, 107])

# push it into the dictionary:
rangeDictionary[1] = (lower_values, upper_values, "gray")

# white color
lower_values = np.array([37, 0, 131])
upper_values = np.array([170, 25, 152])

# push it into the dictionary:
rangeDictionary[2] = (lower_values, upper_values, "white")

# Store results here:
targetRectangles = []

到目前为止,我已经有了目标颜色的looked up等价物。我已经为这些颜色定义了upperlower阈值,并将它们存储在dictionary中。我们的想法是循环使用此词典,并相应地提取每个颜色范围:

# Loop through the dictionary and locate each circle:
for i in rangeDictionary:

    # Get current lower and upper range values:
    current_LowRange = rangeDictionary[i][0]
    current_UppRange = rangeDictionary[i][1]

    # Create the HSV mask
    mask = cv2.inRange(frame_hsv, current_LowRange, current_UppRange)

    # Run a minimum area filter:
    minArea = 800
    mask = areaFilter(minArea, mask)

对于第一种颜色,这是未过滤的二进制遮罩:

您可以在这里看到,我已经实现了一个areaFilter。这将去除小于800的斑点,让我们开始以正确的方式清洁您的口罩。此函数在文章末尾定义。接下来是一些morphology来进一步定义目标blob:

    # Pre-process mask:
    kernelSize = 3

    structuringElement = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
    iterations = 10

    mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, structuringElement, None, None, iterations, cv2.BORDER_REFLECT101)
    mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, structuringElement, None, None, iterations, cv2.BORDER_REFLECT101)

这是过滤后的遮罩:

很好,嗯?没什么特别的,只是一条非常有攻击性的链。我想把冰球定义得干净漂亮。根据输入图像的大小,可能需要调整iterations值。让我们继续。下面的步骤(仍在循环中)是计算contours(仅外部的一个),并将每个contour近似为polygon,然后再近似为rectangle

    # Find the big contours/blobs on the filtered image:
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

    # List to store all the bounding rectangles:
    contours_poly = [None] * len(contours)
    boundRect = []

    # Alright, just look for the outer bounding boxes:
    for b, c in enumerate(contours):

        if hierarchy[0][b][3] == -1:

            # Approximate the contour to a polygon:
            contours_poly = cv2.approxPolyDP(c, 3, True)
            # Convert the polygon to a bounding rectangle:
            boundRect = cv2.boundingRect(contours_poly)

现在我们正在处理bounding rectangles,操作变得非常简单。让我们获得矩形的尺寸并计算两个参数:aspectRatioarea。使用一些启发式方法,我已经设置了用于过滤矩形的最小阈值:

            # Get the dimensions of the bounding rect:
            rectX = boundRect[0]
            rectY = boundRect[1]
            rectWidth = boundRect[2]
            rectHeight = boundRect[3]

            rectArea = rectWidth * rectHeight

            # Calculate the aspect ratio:
            aspectRatio = rectWidth / rectHeight
            delta = abs(1.0 - aspectRatio)

            # Set the min threshold values to identify the
            # blob of interest:
            minArea = 1000
            epsilon = 0.2

            # Is this bounding rectangle one the one we
            # are looking for?
            if rectArea > minArea and delta < epsilon:

                # Set a color:
                color = (0, 255, 0)
                inputCopy = inputImage.copy()

                # Draw the current rectangle on a copy of the BGR input:
                cv2.rectangle(inputCopy, (int(rectX), int(rectY)),
                              (int(rectX + rectWidth), int(rectY + rectHeight)), color, 2)
                # Store this bounding rectangle:
                targetRectangles.append(boundRect)


                # Label the current mask:
                currentColor = rangeDictionary[i][2]

                org = (rectX, rectY -10)
                font = cv2.FONT_HERSHEY_SIMPLEX
                color = (255, 0, 0)
                cv2.putText(inputCopy, currentColor, org, font,
                            0.5, color, 1, cv2.LINE_AA)

                cv2.imwrite(path + "colorMask_"+currentColor+".png", inputCopy)

我在输入的深层副本上另外绘制了目标矩形,并绘制了漂亮的文本以识别颜色,查看结果:

“但是,伙计,那黑色冰球呢?!”嗯,我得留点东西给你做。如果你一直遵循到现在,它应该很容易得到额外的面具。这是areaFilter函数的定义和实现:

def areaFilter(minArea, inputImage):

    # Perform an area filter on the binary blobs:
    componentsNumber, labeledImage, componentStats, componentCentroids = \
    cv2.connectedComponentsWithStats(inputImage, connectivity=4)

    # Get the indices/labels of the remaining components based on the area stat
    # (skip the background component at index 0)
    remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]

    # Filter the labeled pixels based on the remaining labels,
    # assign pixel intensity to 255 (uint8) for the remaining pixels
    filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')

    return filteredImage

该死,看看这些,我可能应该在你的项目报告中得到承认。希望这些信息对您有用

相关问题 更多 >