[英]Find a rotated bounding box of known size around a rectangle with noisy sides
我試圖在一個矩形不太完美的二值化圖像周圍找到一個旋轉的邊界框。 瑕疵總是不同的:有時它是空心的,有時里面有東西,有時其中一個邊緣缺少一塊,有時邊緣上有一塊額外的塊,它們總是稍微旋轉一個隨機量,但是大小並且預期邊界框的形狀總是幾乎相同的絕對值(以像素為單位)。
這里有一些我作為輸入的樣本(調整大小以便更好地適應帖子):
理想情況下,我想在白色矩形的外面找到一個邊界框(雖然我對邊緣只是感興趣),如下所示:
(通過反轉其中一個空心部件,獲得最大的連接部件,並獲得旋轉的強制尺寸)
到目前為止,我已經嘗試過一個旋轉的正確並隨后強制形狀,這幾乎適用於所有情況,除非沿着其中一個邊緣有一個額外的塊。 我已經嘗試過連接組件以隔離它的一部分並在它們周圍獲得邊界框,只要它們是空心的,它就適用於每種情況。 我試圖擴大和侵蝕圖像,獲得輪廓和線條以試圖找到只有四個角點,但我也沒有運氣。 我也在網上尋找任何有用的東西都無濟於事。
任何幫助或想法將不勝感激。
我的解決方案包括兩部分:
以下是一個簡單的程序,演示了這種方法。 通常從命令行傳入開頭的參數(文件名,已知矩形的大小,角度搜索范圍)。
import cv2
import numpy as np
# arguments
file = '1.png'
w0, h0 = 425, 630 # size of known rectangle
ang_range = 1 # posible range (+/-) of angle in degrees
# read image
img = cv2.imread(file, cv2.IMREAD_GRAYSCALE)
h, w = img.shape
# find biggest connceted components
nb_components, output, stats, _ = cv2.connectedComponentsWithStats(img, connectivity=4)
sizes = stats[:, -1]
max_label, max_size = 1, sizes[1]
for i in range(2, nb_components):
if sizes[i] > max_size:
max_label = i
max_size = sizes[i]
img2 = np.zeros(img.shape, np.uint8)
img2[output == max_label] = 128
# fill holes
contours, _ = cv2.findContours(img2, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
cv2.drawContours(img2, [contour], 0, 128, -1)
# find lines
edges = cv2.Canny(img2, 50, 150, apertureSize = 3)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 40)
# find bounding lines
xmax = ymax = 0
xmin, ymin = w-1, h-1
for i in range(lines.shape[0]):
x1 = lines[i][0][0]
y1 = lines[i][0][1]
x2 = lines[i][0][2]
y2 = lines[i][0][3]
cv2.line(img2, (x1,y1), (x2,y2), 255, 2, cv2.LINE_AA)
if abs(x1-x2) < abs(y1-y2):
# vertical line
xmin = min(xmin,x1,x2)
xmax = max(xmax,x1,x2)
else:
# horizcontal line
ymin = min(ymin,y1,y2)
ymax = max(ymax,y1,y2)
cv2.rectangle(img2, (xmin,ymin), (xmax,ymax), 255, 1, cv2.LINE_AA)
cv2.imwrite(file.replace('.png', '_intermediate.png'), img2)
# rectangle of known size centered at bounding box
xc = (xmax + xmin) / 2
yc = (ymax + ymin) / 2
box = np.zeros(img.shape, np.uint8)
box[int(yc-h0/2):int(yc+h0/2), int(xc-w0/2):int(xc+w0/2)] = 255
# find best match of this rectangle at different angles
smax = angmax = 0
for ang in np.linspace(-ang_range, ang_range, 20):
rm = cv2.getRotationMatrix2D((xc,yc), ang, 1)
rotbox = cv2.warpAffine(box, rm, (w,h))
s = cv2.countNonZero(cv2.bitwise_and(rotbox, img))
if s > smax:
smax = s
angmax = ang
# output and visualize result
def draw_rotated_rect(img, size, center, angle, color, thickness):
rm = cv2.getRotationMatrix2D(center, angle, 1)
p0 = np.dot(rm,(xc-w0/2, yc-h0/2,1))
p1 = np.dot(rm,(xc-w0/2, yc+h0/2,1))
p2 = np.dot(rm,(xc+w0/2, yc+h0/2,1))
p3 = np.dot(rm,(xc+w0/2, yc-h0/2,1))
pnts = np.int32(np.vstack([p0,p1,p2,p3]) + 0.5).reshape(-1,4,2)
cv2.polylines(img, pnts, True, color, thickness, cv2.LINE_AA)
print(f'{file}: edges {pnts[0].tolist()}, angle = {angle:.2f}°')
res = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
draw_rotated_rect(res, (w0,h0), (xc,yc), angmax, (0,255,0), 2)
cv2.imwrite(file.replace('.png', '_result.png'), res)
中間結果顯示它是如何工作的(灰色=填充最大連通分量,粗白線= Hough線,薄白色矩形=直立邊界框):
(要查看完整尺寸的圖片,請單擊它們,然后在文件擴展名之前刪除最后的m
)
結果的可視化(綠色=已知大小的旋轉矩形):
結果(最終應該被鉗制到[0,圖像大小),-1是由於浮點旋轉):
1.png: edges [[17, -1], [17, 629], [442, 629], [442, -1]], angle = 0.00°
2.png: edges [[7, 18], [9, 648], [434, 646], [432, 16]], angle = 0.26°
3.png: edges [[38, 25], [36, 655], [461, 657], [463, 27]], angle = -0.26°
4.png: edges [[36, 14], [28, 644], [453, 650], [461, 20]], angle = -0.79°
正如我們在圖3中看到的,匹配並不完美。 這可能是由於示例圖像縮小到有些不同的大小,當然我不知道已知矩形的大小,所以我只是為演示假設了一個合適的值。
如果這也與真實數據一起發生,您可能不僅要改變角度以找到最佳匹配,還要將匹配框向上/向下和向右/向左移動幾個像素。 例如,請參閱Dawson-Howe的第8.1節:OpenCV的計算機視覺實用介紹,了解更多詳情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.