algorithm - 将一组线段近似为一条最佳拟合直线

标签 algorithm opencv image-processing statistics linear-regression

假设我有一组线段,如这张图片中的红线(或绿线)Sample picture

我想知道如何用最接近它们的一条线段替换它们。或者您可以建议搜索什么,因为这可能是统计中的常见问题。

问题背景:这实际上来自于使用 OpenCV 的概率 Hough 变换。我想检测一张纸的角。当我将它应用于图像时,我在边缘得到了一组线,我想将它们转换为单线连续线段。

我想到的一种方法是从直线上取几个点,然后使用线性回归得到直线的方程。一旦我有了它,我应该把它切成一个片段。

最佳答案

这是一个潜在的解决方案:

  1. 获取二值图像。加载图像,转换为灰度,和Otsu的阈值

  2. 执行形态学操作。我们变形接近以将轮廓组合成单个轮廓

  3. 找到凸包。我们创建一个空白掩码,然后在二值图像上找到凸包。我们将线条绘制到蒙版上,变形关闭,然后找到轮廓并填充轮廓以获得实体图像

  4. 执行线性回归。我们在二值图像上找到最合适的线,然后将此结果线绘制到新掩码上

  5. 按位凸包和线蒙版在一起。我们按位和两个蒙版在一起,并将得到的轮廓绘制到原始图像上。


这是每个步骤的可视化:

使用这个截屏的输入图像

enter image description here

二值图像 -> Morph close

enter image description here enter image description here

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)

凸包轮廓 -> 凸包填充

enter image description here enter image description here

# Create line mask and convex hull mask
line_mask = np.zeros(image.shape, dtype=np.uint8)
convex_mask = np.zeros(image.shape, dtype=np.uint8)

# Find convex hull on the binary image
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnt = cnts[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(convex_mask,start,end,[255,255,255],5)

# Morph close the convex hull mask, find contours, and fill in the outline
convex_mask = cv2.cvtColor(convex_mask, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
convex_mask = cv2.morphologyEx(convex_mask, cv2.MORPH_CLOSE, kernel, iterations=3)
cnts = cv2.findContours(convex_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.fillPoly(convex_mask, cnts, (255,255,255))

线性回归

enter image description here

# Perform linear regression on the binary image
[vx,vy,x,y] = cv2.fitLine(cnt,cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((image.shape[1]-x)*vy/vx)+y)
cv2.line(line_mask,(image.shape[1]-1,righty),(0,lefty),[255,255,255],2)

按位填充凸包和线性回归掩码一起

enter image description here

# Bitwise-and the line and convex hull masks together
result = cv2.bitwise_and(line_mask, line_mask, mask=convex_mask)
result = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)

结果

enter image description here

# Find resulting contour and draw onto original image
cnts = cv2.findContours(result, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.drawContours(image, cnts, -1, (200,100,100), 3)

这是另一个输入图像的结果

enter image description here enter image description here

完整代码

import cv2
import numpy as np

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)

# Create line mask and convex hull mask
line_mask = np.zeros(image.shape, dtype=np.uint8)
convex_mask = np.zeros(image.shape, dtype=np.uint8)

# Find convex hull on the binary image
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnt = cnts[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(convex_mask,start,end,[255,255,255],5)

# Morph close the convex hull mask, find contours, and fill in the outline
convex_mask = cv2.cvtColor(convex_mask, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
convex_mask = cv2.morphologyEx(convex_mask, cv2.MORPH_CLOSE, kernel, iterations=3)
cnts = cv2.findContours(convex_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.fillPoly(convex_mask, cnts, (255,255,255))

# Perform linear regression on the binary image
[vx,vy,x,y] = cv2.fitLine(cnt,cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((image.shape[1]-x)*vy/vx)+y)
cv2.line(line_mask,(image.shape[1]-1,righty),(0,lefty),[255,255,255],2)

# Bitwise-and the line and convex hull masks together
result = cv2.bitwise_and(line_mask, line_mask, mask=convex_mask)
result = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)

# Find resulting contour and draw onto original image
cnts = cv2.findContours(result, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.drawContours(image, cnts, -1, (200,100,100), 3)

cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('image', image)
cv2.imshow('line_mask', line_mask)
cv2.imshow('convex_mask', convex_mask)
cv2.imshow('result', result)
cv2.waitKey()

关于algorithm - 将一组线段近似为一条最佳拟合直线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25368615/

相关文章:

java - 在堆栈等资源有限的机器上无需递归即可创建数独矩阵

algorithm - 按最大元素对 k 个排序列表进行排序

c - 如何将 IplImage 转换为标准 Web 图像格式以在 url 中显示?

opencv - cv::Mat 是线程安全的(原子分配+引用计数)吗?

python - 如何将 cv::Mat 转换为 python

numpy - 查找一组像素附近的所有像素

c++ - OpenCV 在图像上运行 kmeans 算法

java - 多线程合并排序,添加额外的线程

algorithm - 如何找到函数的上界

c++ - 使用opencv在受控环境中进行视频跟踪的跟踪标记和跟踪算法选择