从给定遮罩近似四边形 [英] Approximating a quadrilateral from a given mask

查看:28
本文介绍了从给定遮罩近似四边形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标:

我要估计图像+中所示的给定遮罩对象的4个坐标四边形(不仅是矩形),而不会丢失该遮罩对象的任何像素。

试验:

我尝试使用CV2,但最终无法找到解决方案。

  1. cv2.rangingRect:返回外接矩形的坐标(四边形估计不一定是完美的矩形)
  2. cv2.findContors+cv2.ApproxPolyDP:这不是很准确吗,并返回对象的估计极值点(需要更多的工作来估计四边形的4坐标,可能会有一个更容易、更快的解决方案)。

代码段:

正在尝试cv2.ranginRect:

#mask = grayed image with only a specific object being masked
#image = the original rgb image
x,y,x_width,y_height = cv2.boundingRect(mask)
image=np.array(im[0])
cv2.rectangle(image,(x,y),(x+x_width,y+y_height),(0,255,0),2)
plt.imshow(image)

正在尝试cv2.findContors+cv2.ApproxPolyDP:

#mask = grayed image with only a specific object being masked
#image = the original rgb image
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
selected_contour = max(contours, key=lambda x: cv2.contourArea(x))
approx = cv2.approxPolyDP(selected_contour, 0.0035 * cv2.arcLength(selected_contour, True), True)
cv2.drawContours(image, [approx], 0, (0, 0, 255), 5)
plt.imshow(image)

推荐答案

我不确定有没有更好的或内置的版本;但我有一个基于随机数的简单想法:

我只对顶部执行了此操作,但您也可以对其他端执行相同的操作。其思想是首先找到物体的包围盒,然后将物体等分,这样我们就可以找到最高峰。

在每个范围中,您可以随机查找点;但为了获得最佳效果,最好检查形状的所有顶点,以正确找到最高点。

找到最高峰后,我们必须计算关于这两个点的直线方程,以便我们可以相对于该直线方程绘制一条全局直线。

import sys
import cv2
import random
import numpy as np
from tqdm import tqdm


def rndPt(l, t, r, b):
    # Generate a random point in given ROI
    return (random.randint(int(l), int(r)), random.randint(int(t), int(b)))


def intArr(arr):
    # Cast each item of 1D array to integer
    return [int(x) for x in arr]


# Load our image
pth = sys.path[0]
org = cv2.imread(pth+'/bound.png')
im = org.copy()
H, W = im.shape[:2]

# Make mask and copy from that image
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
bw = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)[1]
im = bw.copy()

# Find the ROI of object
cnts, _ = cv2.findContours(bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnts.sort(key=lambda x: cv2.boundingRect(x)[0])
ROI = None
for cnt in cnts:
    x, y, w, h = cv2.boundingRect(cnt)
    if w < W-1 and h < H-1:
        cv2.rectangle(bw, (x, y), (x+w, y+h), 127, 2)
        ROI = {'x': x, 'y': y, 'w': w, 'h': h, 'h2': y+h}

# We have to find the peaks; so we have to
# divide the bounding-box of shape into several
# ranges.
spaces = 5
sw = ROI['w']//spaces

# Each range can have a peak as a candidate point
candidates = [(ROI['x']+(sw*x)+sw//2, ROI['h']//2) for x in range(0, spaces)]

# Divide the object and find the highest point in
# each range
for s in tqdm(range(0, spaces)):
    l = ROI['x']+(sw*s)
    cv2.line(im, pt1=(l, ROI['y']), pt2=(l, ROI['h2']),
             color=127, thickness=2)
    for x in range(0, sw):
        for i in range(0, 200):
            pt = rndPt(l, ROI['y'], l+sw, ROI['h2']//4)
            if pt[1] < candidates[s][1] and bw[pt[1], pt[0]] == 0:
                candidates[s] = pt
l = ROI['x']+(sw*spaces)
cv2.line(im, pt1=(l, ROI['y']), pt2=(l, ROI['h2']), color=127, thickness=2)
print(candidates)

# We remove duplicate points and also sort the points
# according to the peak
candidates = list(set(candidates))
candidates.sort(key=lambda p: p[1])
print(candidates)
c = candidates

# Now that we have found two of the highest points, we can
# write a line equation for these two points
xA, xB = ROI['x'], ROI['x']+ROI['w']
x1, y1 = c[0][0], c[0][1]
x2, y2 = c[1][0], c[1][1]
m = (y2-y1)/(x2-x1)
# y=mx+b -> y-mx=b
b = y1-m*x1
yA = m*xA+b
yB = m*xB+b

# Convert images to BGR
im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
bw = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR)

# Make a copy of image to draw candidate points
marker = im.copy()
for p in candidates:
    cv2.circle(marker, (p[0],p[1]), 
        h//25, color=(50, 100, 200),thickness=4)

# Draw lines
cv2.line(im, pt1=intArr((xA, yA)), pt2=intArr((xB, yB)),
         color=(255, 0, 100), thickness=4, lineType=cv2.LINE_AA)
cv2.line(bw, pt1=intArr(c[0]), pt2=intArr(c[1]),
         color=(100, 0, 255), thickness=4, lineType=cv2.LINE_AA)

# Save final output
top = np.hstack((org, marker))
btm = np.hstack((bw, im))
cv2.imwrite(pth+'/out.png', np.vstack((top, btm)))

这篇关于从给定遮罩近似四边形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆