【问题标题】:How can I count holes on Lego bricks using opencv python?如何使用 opencv python 计算乐高积木上的孔?
【发布时间】:2020-05-17 10:05:25
【问题描述】:

我正在处理我的 python 项目,我需要计算每个乐高积木组件中有多少个孔。关于我需要计算哪个程序集的信息,我将从输入 .json 文件中获取,如下所示:

"img_001": [
    {
        "red": "0",
        "blue": "2",
        "white": "1",
        "grey": "1",
        "yellow": "1"
    },
    {
        "red": "0",
        "blue": "1",
        "white": "0",
        "grey": "1",
        "yellow": "0"

所以我需要识别我必须按颜色计数的组件。然后我必须和孔的数量特别组装砖块。

这是我使用的图像示例:

我已经开始将我的图像更改为 hsv 颜色空间,并使用轨迹栏为每种颜色找到了一个遮罩。使用cv2.inRange 我得到一个红色的面具,例如: 如您所见,反射光无济于事。 在这一点上,我不知道我该如何前进。我觉得我应该使用cv2.findContour 来获取每个组件的轮廓。我在想直方图均衡在这里可能有用。要检测我想使用cv2.HoughCirclescv2.SimpleBloopDetector 的圈子。但我不知道如何检查每个区域有多少砖。输出只是特定组件中的一些孔。 你能给我一些想法吗?哪个 OpenCv 函数可能适用于这里?你将如何解决这种图像处理问题?感谢您的回答。

【问题讨论】:

  • 您可以尝试在 HSV 颜色空间中为每种砖色找到色调的阈值。然后找到外部和内部轮廓。如果颜色分割效果足够好,孔的数量等于内部轮廓的数量。
  • 如果您控制照片,如果您选择与您的积木有很大不同的背景(例如黑色背景),您将拥有更好的时间。 (黑色背景也有助于阴影!)
  • @nneonneo 所以你建议我删除背景?我需要处理像这样的图像。所以我不知道我是否应该使用 GrabCut、Watershed 或其他东西。也许我应该以某种方式计算像素值的中值来模糊背景?你有什么建议?
  • 好吧,我建议如果可以的话,在不同的(更容易分割的)背景上拍摄你的积木。如果你不能,那么背景删除将是你最好的选择。移除背景后,您应该会发现更容易识别孔洞。
  • 但问题是从这张图片中删除背景的最佳方法是什么?

标签: python opencv image-processing


【解决方案1】:

这是一个简单但非常有趣的颜色分割练习。这个主题已经在各处得到了广泛的报道,有几个例子遍布Stackoverflow。在许多情况下,颜色分割在 HSV 颜色空间中效果最好。

在左下图中,你可以看到带有蓝色孔洞的黄砖的分割结果,只是为了表明它们也被这种方法检测到。

在这个答案中,我提供了检测黄砖并识别其中的孔所需的操作的高级概述。但是,它没有演示如何计算特定砖块内的孔数以避免破坏您的作业。我故意省略了答案的那部分,以便为您留下一些工作。

以下是我的方法的主要步骤

  • 预处理图像以改进分割:这里使用的技术称为颜色量化it reduces the numbers of colors in the image~42颜色。在下图中很难看到结果,但如果放大,它显示的颜色会比原始图像少:

  • 将预处理后的图像转换为 HSV 颜色空间,以实现更好的颜色分割。

  • 由于此方法仅关注黄砖的分割,因此该算法定义黄色的低值和高值(以 HSV 为单位)以使用此范围对图像进行阈值处理:超出范围的任何颜色都将变为黑色像素。图像编辑器可以帮助您放大原始图像并检查像素的确切 HSV 值。这是分割的结果:

  • 然后对分割后的图像进行处理,我们丢弃小块以仅保留最大的块(即砖块)。有了这个过滤机制,就可以统计有多少黄砖了。这里有一个绝妙的技巧:如果您使用cv2.fillPoly() 绘制砖的轮廓并用白色填充,您将能够在单独的图像中绘制没有任何孔的整个砖来创建蒙版。这很快就会派上用场!黄色面具如下所示:

  • 在这个阶段,我们已经有了图像中所有黄砖的位置。剩下要做的就是识别每块砖上的洞。这就是蒙版的用武之地:如果你注意上面的两张图片,分割后的图像和蒙版的区别主要是砖块的洞:

  • 处理此图像的轮廓允许丢弃所有不属于孔的小斑点,只留下砖的孔。我们可以在分割图像或原始图像上绘制孔的位置以显示它们:

总之,这段代码提供了一个黄砖列表和另一个包含这些砖中孔的列表。从这一点开始,由你决定。该代码可以轻松扩展以处理其他颜色的积木。玩得开心:

import cv2
import numpy as np

# convertToOpenCVHSV():
#   converts from HSV range (H: 0-360, S: 0-100, V: 0-100)
#   to what OpenCV expects: (H: 0-179, S: 0-255, V: 0-255)
def convertToOpenCVHSV(H, S, V):
    return np.array([H // 2, S * 2.55, V * 2.55], np.uint8)


# 1. Load input image
img = cv2.imread('test_images/legos.jpg')

# 2. Preprocess: quantize the image to reduce the number of colors
div = 6
img = img // div * div + div // 2
cv2.imwrite('lego2_quantized.jpg', img)


# 3. Convert to HSV color space
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)


# 4. Segment the image using predefined values of yellow (min and max colors)
low_yellow = convertToOpenCVHSV(40, 35, 52)
high_yellow = convertToOpenCVHSV(56, 95, 93)
yellow_seg_img = cv2.inRange(hsv_img, low_yellow, high_yellow)
#cv2.imshow('yellow_seg_img', yellow_seg_img)
cv2.imwrite('lego4_yellow_seg_img.jpg', yellow_seg_img)

# 5. Identify and count the number of yellow bricks and create a mask with just the yellow objects
bricks_list = []
min_size = 5

contours, hierarchy = cv2.findContours(yellow_seg_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for contourIdx, cnt in enumerate(contours):
    # filter out tiny segments
    x, y, w, h = cv2.boundingRect(cnt)
    if (w < min_size) or (h < min_size):
        continue

    #print('contourIdx=', contourIdx, 'w=', w, 'h=', h)

    bricks_list.append(cnt)

    # debug: draw green contour in the original image
    #cv2.drawContours(img, cnt, -1, (0, 255, 0), 2) # green

print('Detected', len(bricks_list), 'yellow pieces.')

# Iterate the list of bricks and draw them (filled) on a new image to be used as a mask
yellow_mask_img = np.zeros((img.shape[0], img.shape[1]), np.uint8)
for cnt in bricks_list:
    cv2.fillPoly(yellow_mask_img, pts=[cnt], color=(255,255,255))

cv2.imshow('yellow_mask_img', yellow_mask_img)
cv2.imwrite('lego5_yellow_mask_img.jpg', yellow_mask_img)

# debug: display only the original yellow bricks found
bricks_img = cv2.bitwise_and(img, img, mask=yellow_mask_img)
#cv2.imshow('bricks_img', bricks_img)
cv2.imwrite('lego5_bricks_img.jpg', bricks_img)

# 6. Identify holes in each Lego brick
diff_img = yellow_mask_img - yellow_seg_img
cv2.imshow('diff_img', diff_img)
cv2.imwrite('lego6_diff_img.jpg', diff_img)

# debug: create new BGR image for debugging purposes
dbg_img = cv2.cvtColor(yellow_mask_img, cv2.COLOR_GRAY2RGB)
#dbg_img = bricks_img

holes_list = []
min_area_size = 10
max_area_size = 24
contours, hierarchy = cv2.findContours(yellow_seg_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for contourIdx, cnt in enumerate(contours):
    # filter out tiny segments by area
    area = cv2.contourArea(contours[contourIdx])

    if (area < min_area_size) or (area > max_area_size):
        #print('contourIdx=', contourIdx, 'w=', w, 'h=', h, 'area=', area, '(ignored)')
        #cv2.drawContours(dbg_img, cnt, -1, (0, 0, 255), 2) # red
        continue

    #print('contourIdx=', contourIdx, 'w=', w, 'h=', h, 'area=', area)
    holes_list.append(cnt)

# debug: draw a blue-ish contour on any BGR image to show the holes of the bricks
for cnt in holes_list:
    cv2.fillPoly(dbg_img, pts=[cnt], color=(255, 128, 0))
    cv2.fillPoly(img, pts=[cnt], color=(255, 128, 0))

cv2.imwrite('lego6_dbg_img.jpg', dbg_img)
cv2.imwrite('lego6_img.jpg', img)

# 7. Iterate though the list of holes and associate them with a particular brick
# TODO

cv2.imshow('img', img)
cv2.imshow('dbg_img', dbg_img)
cv2.waitKey(0)

【讨论】:

    猜你喜欢
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多