我想檢測此影像中的圓圈和五個正方形:

這是我目前使用的代碼的相關部分:
# detect shapes in black-white RGB formatted cv2 image
def detect_shapes(img, approx_poly_accuracy=APPROX_POLY_ACCURACY):
res_dict = {
"rectangles": [],
"squares": []
}
vis = img.copy()
shape = img.shape
height, width = shape[0], shape[1]
total_area = height * width
# Morphological closing: get rid of holes
# img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
# Morphological opening: get rid of extensions at the border of the objects
# img = cv2.morphologyEx(img, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (121, 121)))
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# cv2.imshow('intermediate', img)
# cv2.waitKey(0)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
logging.info("Number of found contours for shape detection: {0}".format(len(contours)))
# vis = img.copy()
# cv2.drawContours(vis, contours, -1, (0, 255, 0), 2)
cv2.imshow('vis', vis)
cv2.waitKey(0)
for contour in contours:
area = cv2.contourArea(contour)
if area < MIN_SHAPE_AREA:
logging.warning("Area too small: {0}. Skipping.".format(area))
continue
if area > MAX_SHAPE_AREA_RATIO * total_area:
logging.warning("Area ratio too big: {0}. Skipping.".format(area / total_area))
continue
approx = cv2.approxPolyDP(contour, approx_poly_accuracy * cv2.arcLength(contour, True), True)
cv2.drawContours(vis, [approx], -1, (0, 0, 255), 2)
la = len(approx)
# find the center of the shape
M = cv2.moments(contour)
if M['m00'] == 0.0:
logging.warning("Unable to compute shape center! Skipping.")
continue
x = int(M['m10'] / M['m00'])
y = int(M['m01'] / M['m00'])
if la < 3:
logging.warning("Invalid shape detected! Skipping.")
continue
if la == 3:
logging.info("Triangle detected at position {0}".format((x, y)))
elif la == 4:
logging.info("Quadrilateral detected at position {0}".format((x, y)))
if approx.shape != (4, 1, 2):
raise ValueError("Invalid shape before reshape to (4, 2): {0}".format(approx.shape))
approx = approx.reshape(4, 2)
r_check, data = check_rect_or_square(approx)
blob_data = {"position": (x, y), "approx": approx}
blob_data.update(data)
if r_check == 2:
res_dict["squares"].append(blob_data)
elif r_check == 1:
res_dict["rectangles"].append(blob_data)
elif la == 5:
logging.info("Pentagon detected at position {0}".format((x, y)))
elif la == 6:
logging.info("Hexagon detected at position {0}".format((x, y)))
else:
logging.info("Circle, ellipse or arbitrary shape detected at position {0}".format((x, y)))
cv2.drawContours(vis, [contour], -1, (0, 255, 0), 2)
cv2.imshow('vis', vis)
cv2.waitKey(0)
logging.info("res_dict: {0}".format(res_dict))
return res_dict
問題是:如果我將approx_poly_accuracy引數設定得太高,則圓被檢測為多邊形(例如,六邊形或八邊形)。如果我將其設定得太低,則正方形不會被檢測為正方形,而是五角形,例如:

紅線是近似輪廓,綠線是原始輪廓。文本被檢測為一個完全錯誤的輪廓,它不應該被逼近到這個水平(我不太關心文本,但如果它被檢測為一個少于 5 個頂點的多邊形,它將是一個誤報)。
For a human, it is obvious that the left object is a circle and that the five objects on the right are squares, so there should be a way to make the computer realize that with high accuracy too. How do I modify this code to properly detect all objects?
What I already tried:
- Apply filters like
MedianFilter. It made things worse, because the rounded edges of the squares promoted them being detected as a polygon with more than four vertices. - Variate the
approx_poly_accuracyparameter. There is no value that fits my purposes, considering that some other images might even have a some more noise. - 找到 RDP 演算法的實作,它允許我指定精確數量的輸出點。這將允許我為一定數量的點(例如在 3..10 范圍內)計算建議的多邊形,然后計算
(A_1 A_2) / A_common - 1使用面積而不是弧長作為精度,這可能會導致更好的結果。我還沒有找到一個很好的實作。我現在將嘗試使用數值求解器方法來動態計算 RDP 的正確 epsilon 引數。但是,這種方法并不是真正干凈和有效。我會盡快在此處發布結果。如果有人有更好的方法,請告訴我。
uj5u.com熱心網友回復:
一種可能的方法是根據這些屬性計算一些 blob 描述符和過濾 blob。例如,您可以計算 blob 的縱橫比、(近似)頂點數和面積。步驟非常簡單:
- 加載影像并將其轉換為灰度。
- (反轉)影像的閾值。讓我們確保斑點是白色的。
- 獲取二值影像的輪廓。
- 計算兩個特征:縱橫比和頂點數
- 根據這些特征過濾斑點
讓我們看看代碼:
# Imports:
import cv2
import numpy as np
# Load the image:
fileName = "yh6Uz.png"
path = "D://opencvImages//"
# Reading an image in default mode:
inputImage = cv2.imread(path fileName)
# Prepare a deep copy of the input for results:
inputImageCopy = inputImage.copy()
# Grayscale conversion:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Threshold via Otsu:
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
# Find the blobs on the binary image:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Store the bounding rectangles here:
circleData = []
squaresData = []
好吧。到目前為止,我已經在輸入影像上加載、閾值化和計算輪廓。此外,我準備了兩個串列來存盤正方形和圓形的邊界框。讓我們創建特征過濾器:
for i, c in enumerate(contours):
# Get blob perimeter:
currentPerimeter = cv2.arcLength(c, True)
# Approximate the contour to a polygon:
approx = cv2.approxPolyDP(c, 0.04 * currentPerimeter, True)
# Get polygon's number of vertices:
vertices = len(approx)
# Get the polygon's bounding rectangle:
(x, y, w, h) = cv2.boundingRect(approx)
# Compute bounding box area:
rectArea = w * h
# Compute blob aspect ratio:
aspectRatio = w / h
# Set default color for bounding box:
color = (0, 0, 255)
我遍歷每個輪廓并計算當前 blob 的perimeter和polygon approximation。此資訊用于近似計算 blob vertices。該aspect ratio計算是很容易的。我第一次拿到BLOB的邊框,并得到其尺寸:左上角(x, y),width和height。縱橫比就是寬度除以高度。
正方形和圓形非常緊湊。這意味著它們的縱橫比應該接近于1.0。然而,正方形有精確的4頂點,而(近似的)圓有更多的頂點。我使用此資訊來構建一個非常基本的功能過濾器。它首先檢查aspect ratio,area然后檢查數量vertices。我使用了理想特征和真實特征之間的差異。該引數delta調整過濾器容差。一定還要過濾微小的斑點,為此使用該區域:
# Set minimum tolerable difference between ideal
# feature and actual feature:
delta = 0.15
# Set the minimum area:
minArea = 400
# Check features, get blobs with aspect ratio
# close to 1.0 and area > min area:
if (abs(1.0 - aspectRatio) < delta) and (rectArea > minArea):
print("Got target blob.")
# If the blob has 4 vertices, it is a square:
if vertices == 4:
print("Target is square")
# Save bounding box info:
tempTuple = (x, y, w, h)
squaresData.append(tempTuple)
# Set green color:
color = (0, 255, 0)
# If the blob has more than 6 vertices, it is a circle:
elif vertices > 6:
print("Target is circle")
# Save bounding box info:
tempTuple = (x, y, w, h)
circleData.append(tempTuple)
# Set blue color:
color = (255, 0, 0)
# Draw bounding rect:
cv2.rectangle(inputImageCopy, (int(x), int(y)), (int(x w), int(y h)), color, 2)
cv2.imshow("Rectangles", inputImageCopy)
cv2.waitKey(0)
這是結果。正方形用綠色矩形標識,圓形用藍色矩形標識。此外,邊界框分別存盤在squaresData和 中circleData:
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/387277.html
下一篇:FileExistsError:[Errno17]檔案存在:'/usr/bin/python'->'/home/had2000/.virtualenvs/cv/bi
