【问题标题】:How to detect rectangular items in image with Python如何使用 Python 检测图像中的矩形项目
【发布时间】:2018-01-02 23:41:24
【问题描述】:

我发现了很多关于使用 openCV 等在图像中查找“事物”的问题。在 Python 中,但到目前为止,我一直无法将它们拼凑在一起,以便为我的问题提供可靠的解决方案。

我正在尝试使用计算机视觉来帮助计算微小的表面贴装电子零件。我的想法是把零件倒在一张纯色纸上,拍张照片,然后让软件告诉我里面有多少物品。

“事物”因一张图片而异,但在任何一张图片中总是相同的。我似乎能够手动调整特定部分的色相/饱和度等参数,但每次更改为新部分时往往需要调整。

我当前的半功能代码发布在下面:

import imutils
import numpy
import cv2
import sys

def part_area(contours, round=10):
    """Finds the mode of the contour area.  The idea is that most of the parts in an image will be separated and that
    finding the most common area in the list of areas should provide a reasonable value to approximate by.  The areas
    are rounded to the nearest multiple of 200 to reduce the list of options."""
    # Start with a list of all of the areas for the provided contours.
    areas = [cv2.contourArea(contour) for contour in contours]
    # Determine a threshold for the minimum amount of area as 1% of the overall range.
    threshold = (max(areas) - min(areas)) / 100
    # Trim the list of areas down to only those that exceed the threshold.
    thresholded = [area for area in areas if area > threshold]
    # Round the areas to the nearest value set by the round argument.
    rounded = [int((area + (round / 2)) / round) * round for area in thresholded]
    # Remove any areas that rounded down to zero.
    cleaned = [area for area in rounded if area != 0]
    # Count the areas with the same values.
    counts = {}
    for area in cleaned:
        if area not in counts:
            counts[area] = 0
        counts[area] += 1
    # Reduce the areas down to only those that are in groups of three or more with the same area.
    above = []
    for area, count in counts.iteritems():
        if count > 2:
            for _ in range(count):
                above.append(area)
    # Take the mean of the areas as the average part size.
    average = sum(above) / len(above)
    return average

def find_hue_mode(hsv):
    """Given an HSV image as an input, compute the mode of the list of hue values to find the most common hue in the
    image.  This is used to determine the center for the background color filter."""
    pixels = {}
    for row in hsv:
        for pixel in row:
            hue = pixel[0]
            if hue not in pixels:
                pixels[hue] = 0
            pixels[hue] += 1
    counts = sorted(pixels.keys(), key=lambda key: pixels[key], reverse=True)
    return counts[0]


if __name__ == "__main__":
    # load the image and resize it to a smaller factor so that the shapes can be approximated better
    image = cv2.imread(sys.argv[1])

    # define range of blue color in HSV
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    center = find_hue_mode(hsv)
    print 'Center Hue:', center

    lower = numpy.array([center - 10, 50, 50])
    upper = numpy.array([center + 10, 255, 255])
    # Threshold the HSV image to get only blue colors
    mask = cv2.inRange(hsv, lower, upper)
    inverted = cv2.bitwise_not(mask)

    blurred = cv2.GaussianBlur(inverted, (5, 5), 0)
    edged = cv2.Canny(blurred, 50, 100)
    dilated = cv2.dilate(edged, None, iterations=1)
    eroded = cv2.erode(dilated, None, iterations=1)

    # find contours in the thresholded image and initialize the shape detector
    contours = cv2.findContours(eroded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if imutils.is_cv2() else contours[1]

    # Compute the area for a single part to use when setting the threshold and calculating the number of parts within
    # a contour area.
    part_area = part_area(contours)
    # The threshold for a part's area - can't be too much smaller than the part itself.
    threshold = part_area * 0.5

    part_count = 0
    for contour in contours:
        if cv2.contourArea(contour) < threshold:
            continue

        # Sometimes parts are close enough together that they become one in the image.  To battle this, the total area
        # of the contour is divided by the area of a part (derived earlier).
        part_count += int((cv2.contourArea(contour) / part_area) + 0.1)  # this 0.1 "rounds up" slightly and was determined empirically

        # Draw an approximate contour around each detected part to give the user an idea of what the tool has computed.
        epsilon = 0.1 * cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(contour, epsilon, True)
        cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)

    # Print the part count and show off the processed image.
    print 'Part Count:', part_count
    cv2.imshow("Image", image)
    cv2.waitKey(0)

这是我正在使用的输入图像类型的示例: 或这个:

我目前得到这样的结果:

结果清楚地表明,脚本在识别某些部分时遇到了问题,而真正的阿喀琉斯之踵似乎是部分相互接触。

所以我的问题/挑战是,我能做些什么来提高这个脚本的可靠性?

该脚本将被集成到现有的 Python 工具中,因此我正在寻找使用 Python 的解决方案。该解决方案不需要必须是纯 Python,因为我愿意安装可能需要的任何 3rd 方库。

【问题讨论】:

    标签: python opencv computer-vision


    【解决方案1】:

    如果对象都是相似的类型,您可能会更成功地隔离图像中的单个示例,然后使用特征匹配来检测它们。

    完整的解决方案超出了 Stack Overflow 的范围,但我对进展的建议是首先使用您当前的矩形检索方法以某种方式找到一个或多个“正确”示例。您可能会查找所有具有预期大小或精确矩形的样本。

    一旦你分离出一些正面的例子,使用一些特征匹配技术来找到其他的。有一个lot of reading up you probably need to do on it,但这是一个潜在的解决方案。

    一般总结是,您使用正面示例来查找要检测的对象的“特征”。这些“特征”通常是诸如角落或渐变变化之类的东西。 OpenCV 包含许多您可以使用的方法。

    一旦你有了这些特征,你可以查看 OpenCV 中的几种算法,这些算法将在图像中搜索所有匹配的特征。您需要一个旋转不变的(可以检测以不同旋转排列的相同特征),但您可能不需要尺度不变(可以在多个尺度上检测相同的特征)。

    我对这种方法的一个担忧是您在图像中搜索的项目非常小。可能很难找到好的、一致的特征来匹配。

    【讨论】:

      【解决方案2】:

      您正在处理 2D 对象识别问题,有很多可能的方法。您已经使用背景/前景分割进行了处理,这没关系,因为您可以控制场景(放下背景纸张)。然而,当物体接触时,这总是有基本的限制。解决您的问题的一个简单方法是:

      1)您假设触摸物体是罕见的事件(这是您问题中的一个很好的假设)。因此,您可以计算每个分割区域的面积,并计算这些区域的中值,这将为对象的面积提供可靠的估计。让我们将此稳健估计称为 A(以平方像素为单位)。如果少于 50% 的区域对应于触摸对象,这将很好。

      2) 然后您继续测量每个分割区域中的对象数量。设 Ai 为第 i 个区域的面积。然后通过 Ni=round(Ai/A) 计算每个区域中的对象数。然后将 Ni 相加得到对象的总数。

      只要满足以下条件,这种方法就可以了: A) 接触的物体没有明显重叠 B)你没有东西躺在它们的侧面。如果你这样做,你也许可以使用两个面积估计(侧面和平坦)来处理这个问题。为简单起见,最好消除这种情况。 C) 物体到相机的距离都大致相同。如果不是这种情况,则对象的区域(以像素为单位)无法通过单个值很好地建模。 D) 图像边界处没有部分可见的物体。 E) 您确保在每张图像中只能看到相同类型的对象。

      【讨论】:

      • 到目前为止,我似乎使用这种方法最幸运。好消息是我确实可以很好地控制此设置中的对象,这样我可以确保与相机的距离基本一致,确保它们平放并且不重叠。感谢您提供非常详细的建议!
      • 嗨 Julienj,我的回答对您有帮助吗?
      • 是的。我会继续接受你的回答。使用你的技术我取得了最大的成功。
      猜你喜欢
      • 1970-01-01
      • 2014-04-30
      • 2021-07-03
      • 2016-09-19
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      相关资源
      最近更新 更多