[英]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.