繁体   English   中英

Object 使用 OpenCV-Python 进行检测

[英]Object Detection with OpenCV-Python

我正在尝试检测图像中所有重叠的圆形/椭圆形状,所有这些形状上都有数字。 我使用 OpenCV 尝试了不同类型的图像处理技术,但是我无法检测到与树重叠的形状。 我尝试过腐蚀和膨胀,但没有帮助。

任何关于如何 go 的指针都会很棒。 我在下面附上了我的代码

original = frame.copy()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
canny = cv2.Canny(blurred, 120, 255, 1)
kernel = np.ones((5, 5), np.uint8)
dilate = cv2.dilate(canny, kernel, iterations=1)

# Find contours
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

image_number = 0
for c in cnts:
    x, y, w, h = cv2.boundingRect(c)
    cv2.rectangle(frame, (x, y), (x + w, y + h), (36, 255, 12), 2)
    ROI = original[y:y + h, x:x + w]
    cv2.imwrite("ROI_{}.png".format(image_number), ROI)
    image_number += 1

cv2.imshow('canny', canny)
cv2.imshow('image', frame)
cv2.waitKey(0)

这是一个可能的解决方案。 我假设目标斑点(类似碟子的东西)总是被标记 - 也就是说,它们里面总是有一个白色数字。 这个想法是创建一个数字掩码,因为它们的大小和颜色似乎是恒定的。 我使用数字作为指南来获取椭圆的样本像素。 然后,我将这些BGR像素转换为HSV ,创建一个二进制掩码并使用该信息来阈值并定位椭圆。 让我们看看代码:

# imports:
import cv2
import numpy as np

# image path
path = "D://opencvImages//"
fileName = "4dzfr.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Deep copy for results:
inputImageCopy = inputImage.copy()

# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Get binary image via Otsu:
binaryImage = np.where(grayscaleImage >= 200, 255, 0)
# The above operation converted the image to 32-bit float,
# convert back to 8-bit uint
binaryImage = binaryImage.astype(np.uint8)

第一步是制作数字的掩码。 我还创建了BGR图像的深层副本 数字接近白色(即接近255的强度)。 我使用200作为阈值并获得以下结果:

现在,让我们在这个二进制掩码上定位这些轮廓。 我正在根据aspect ratio进行过滤,因为数字的aspect ratio接近0.70 我还在根据hierarchy过滤轮廓 - 因为我只对外部轮廓(没有孩子的轮廓)感兴趣。 那是因为我真的不需要数字8内的“孔”之类的轮廓:

# Find the contours on the binary image:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# Store the sampled pixels here:
sampledPixels = []

# Look for the outer bounding boxes (no children):
for i, c in enumerate(contours):

    # Get the contour bounding rectangle:
    boundRect = cv2.boundingRect(c)

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

    # Compute the aspect ratio:
    aspectRatio = rectWidth / rectHeight

    # Create the filtering threshold value:
    delta = abs(0.7 - aspectRatio)
    epsilon = 0.1

    # Get the hierarchy:
    currentHierarchy = hierarchy[0][i][3]

    # Prepare the list of sampling points (One for the ellipse, one for the circle):
    samplingPoints = [ (rectX - rectWidth, rectY), (rectX, rectY - rectHeight) ]

    # Look for the target contours:
    if delta < epsilon and currentHierarchy == -1:

        # This list will hold both sampling pixels:
        pixelList = []

        # Get sampling pixels from the two locations:
        for s in range(2):

            # Get sampling point:
            sampleX = samplingPoints[s][0]
            sampleY = samplingPoints[s][1]

            # Get sample BGR pixel:
            samplePixel = inputImageCopy[sampleY, sampleX]

            # Store into temp list:
            pixelList.append(samplePixel)

        # convert list to tuple:
        pixelList = tuple(pixelList)
        
        # Save pixel value:
        sampledPixels.append(pixelList)

好的,在最后一段代码中发生了一些事情。 我们想从椭圆和圆形中采样像素。 我们将使用两个采样位置,即每个数字的原始 position 的 function。 这些位置在samplingPoints元组中定义。 对于椭圆,我在数字的右上角 position 之前一点点采样。 对于圆圈,我在右上方 position 正上方种植树苗——我们最终得到每个图形两个像素

你会注意到我正在做一些数据类型的杂耍,将列表转换为元组。 为了方便起见,我希望将这些像素存储为元组。 如果我绘制数字的边界矩形,这将是结果图像:

现在,让我们遍历像素列表,将它们转换为HSV并在原始BGR图像上创建HSV蒙版。 椭圆的最终边界矩形存储在boundingRectangles中,另外我在原始输入的深层副本上绘制结果:

# Final bounding rectangles are stored here:
boundingRectangles = []

# Loop through sampled pixels:
for p in range(len(sampledPixels)):
    # Get current pixel tuple:
    currentPixelTuple = sampledPixels[p]

    # Prepare the HSV mask:
    imageHeight, imageWidth = binaryImage.shape[:2]
    hsvMask = np.zeros((imageHeight, imageWidth), np.uint8)

    # Process the two sampling pixels:
    for m in range(len(currentPixelTuple)):
        # Get current pixel in the list:
        currentPixel = currentPixelTuple[m]

        # Create BGR Mat:
        pixelMat = np.zeros([1, 1, 3], dtype=np.uint8)
        pixelMat[0, 0] = currentPixel

        # Convert the BGR pixel to HSV:
        hsvPixel = cv2.cvtColor(pixelMat, cv2.COLOR_BGR2HSV)
        H = hsvPixel[0][0][0]
        S = hsvPixel[0][0][1]
        V = hsvPixel[0][0][2]

        # Create HSV range for this pixel:
        rangeThreshold = 5
        lowerValues = np.array([H - rangeThreshold, S - rangeThreshold, V - rangeThreshold])
        upperValues = np.array([H + rangeThreshold, S + rangeThreshold, V + rangeThreshold])

        # Create HSV mask:
        hsvImage = cv2.cvtColor(inputImage.copy(), cv2.COLOR_BGR2HSV)
        tempMask = cv2.inRange(hsvImage, lowerValues, upperValues)
        hsvMask = hsvMask + tempMask

首先,我创建一个只有BGR像素值的 1 x 1矩阵(或Numpy Array )——我之前采样的两个像素值中的第一个。 这样,我就可以使用cv2.cvtColor来获取对应的HSV值了。 然后,我为HSV掩码创建下限和上限阈值。 然而,图像似乎是合成的,基于范围的阈值可以简化为唯一的元组。 之后,我使用cv2.inRange创建HSV掩码。

这将产生椭圆的HSV掩码。 应用圆的方法后,我们将得到两个HSV掩码。 好吧,我刚刚添加了两个 arrays 来组合两个掩码。 最后你会得到这样的东西,这是为第一个碟状图形创建的“复合” HSV掩码:

我们可以应用一点形态来连接这两个形状,只需稍微闭合即可:

    # Set kernel (structuring element) size:
    kernelSize = 3
    # Set morph operation iterations:
    opIterations = 2
    # Get the structuring element:
    morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
    # Perform closing:
    hsvMask = cv2.morphologyEx(hsvMask, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations,cv2.BORDER_REFLECT101)

这是结果:

好的。 让我们继续并获取所有形状的边界矩形。 我正在使用boundingRectangles列表来存储每个边界矩形,如下所示:

    # Process current contour:
    currentContour, _ = cv2.findContours(hsvMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for _, c in enumerate(currentContour):
        # Get the contour's bounding rectangle:
        boundRect = cv2.boundingRect(c)

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

        # Store and set bounding rect:
        boundingRectangles.append(boundRect)
        color = (0, 0, 255)
        cv2.rectangle(inputImageCopy, (int(rectX), int(rectY)),
                  (int(rectX + rectWidth), int(rectY + rectHeight)), color, 2)

        cv2.imshow("Objects", inputImageCopy)
        cv2.waitKey(0)

这是处理每个采样像素后定位矩形的图像:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM