从叠加图像中提取多边形

Extracting polygons from superimposed images

我有 2 张由三角形组成的图像。我将它们加在一起,形成了新的多边形。
这两个图像叠加时是否可以确定多边形? 我应该以处理结果图像为目标还是可以根据输入三角形的位置来确定它?

注意:我知道第一个和第二个图像顶点和三角形的确切位置为 (x,y)

Clockwise coordinates of triangles [Rectangle Width 512 pixels, Height 256 pixels]

triangle a1 = [0,0] [512,128] [0,256]
triangle a2 = [0,0] [512,0] [512,128]
triangle a3 = [0,256] [512,128] [512,256]

triangle b1 = [0,0] [200,256] [0,256]
triangle b2 = [0,0] [150,0] [200,256]
triangle b3 = [150,0] [512,0] [200,256]
triangle b4 = [512,0] [512,256] [200,256] 

如果你能用数学模型计算构造的多边形,你可能可以更准确地获得所需的输出。

我建议的方法不是很准确,但可能对你有帮助。

我展示了一种算法,可以帮助您分别提取和存储多边形。从这里开始,你需要在每个多边形中找到并排列角点(这部分在算法中不存在)。

import sys
import cv2
import numpy as np

# Load images
i1 = cv2.imread(sys.path[0]+'/rect1.jpg', cv2.IMREAD_GRAYSCALE)
i2 = cv2.imread(sys.path[0]+'/rect2.jpg', cv2.IMREAD_GRAYSCALE)

# Make a copy of images
r1 = i1.copy()
r2 = i2.copy()

# Get size of image
H, W = i1.shape[:2]

# Convert images to black/white
i1 = cv2.threshold(i1, 90, 255, cv2.THRESH_BINARY)[1]
i2 = cv2.threshold(i2, 90, 255, cv2.THRESH_BINARY)[1]

# Mix images together and make a copy
i1[np.where(i2 != 255)] = 0
mix = i1.copy()

# Try to focus of output lines
mix = cv2.GaussianBlur(mix, (3, 3), 2)
mix = cv2.threshold(mix, 225, 255, cv2.THRESH_BINARY)[1]

# Make a mask to find the center of each polygon
msk = i1.copy()
msk = cv2.erode(msk, np.ones((6, 6)))
msk = cv2.medianBlur(msk, 3)

# Fill the mask area with black color
cv2.floodFill(msk, np.zeros((H+2, W+2), np.uint8), (0, 0), 0)

# Find the position of each polygon
pos = msk.copy()
cnts, _ = cv2.findContours(pos, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
pos = cv2.cvtColor(pos, cv2.COLOR_GRAY2BGR)
c, i = 0, 0
for cnt in cnts:
    c += 25
    i += 1
    x, y, w, h = cv2.boundingRect(cnt)
    center = (x+w//2, y+h//2)
    cv2.rectangle(pos, (x, y), (x+w, y+h), (c, 220, 255-c), 1)

    # Extract each polygon in a separate image
    cur = mix.copy()
    cv2.floodFill(cur, np.zeros((H+2, W+2), np.uint8), (0, 0), 0)
    cv2.floodFill(cur, np.zeros((H+2, W+2), np.uint8), center, 127)
    cur[np.where(cur == 255)] = 30
    cur[np.where(cur == 127)] = 255
    cv2.imwrite(sys.path[0]+f'/tri_{i}.jpg', cur)

    if c >= 255:
        c = 0

# Print number of polygones
print(len(cnts))

# Change type of images
i1 = cv2.cvtColor(i1, cv2.COLOR_GRAY2BGR)
r1 = cv2.cvtColor(r1, cv2.COLOR_GRAY2BGR)
r2 = cv2.cvtColor(r2, cv2.COLOR_GRAY2BGR)
msk = cv2.cvtColor(msk, cv2.COLOR_GRAY2BGR)
mix = cv2.cvtColor(mix, cv2.COLOR_GRAY2BGR)

# Save the output
top = np.hstack((r1, r2, i1))
btm = np.hstack((mix, msk, pos))
cv2.imwrite(sys.path[0]+'/rect_out.jpg', np.vstack((top, btm)))

制作遮罩的步骤,找到每个多边形的坐标和中心。

如上所示;每个多边形都存储为单独的图像。从这里开始,您必须考虑下一步;您可以在每个图像中找到并排列每个多边形的角。


我强调;在我看来,这种方法不合逻辑,不够准确。但是如果你没有找到更好的解决方案,它可能对你有用。


更新

我用绘图软件绘制了这个假设图像并更新了代码。我觉得效果很好。您可以根据需要调整参数。最终图像不应该是彩色的。我只是想证明它可以正常工作。

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

# Load images
mix = cv2.imread(sys.path[0]+'/im.png', cv2.IMREAD_GRAYSCALE)
im = mix.copy()
H, W = mix.shape[:2]

# Try to focus of output lines
mix = cv2.GaussianBlur(mix, (3, 3), 2)
mix = cv2.threshold(mix, 225, 255, cv2.THRESH_BINARY)[1]

# Make a mask to find the center of each polygon
msk = mix.copy()
msk = cv2.erode(msk, np.ones((3, 3)))
msk = cv2.medianBlur(msk, 3)

# Fill the mask area with black color
cv2.floodFill(msk, np.zeros((H+2, W+2), np.uint8), (0, 0), 0)

# Find the position of each polygon
pos = msk.copy()
out = msk.copy()
out[:] = 0
out = cv2.cvtColor(out, cv2.COLOR_GRAY2BGR)

cnts, _ = cv2.findContours(pos, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
pos = cv2.cvtColor(pos, cv2.COLOR_GRAY2BGR)
c, i = 0, 0
for cnt in tqdm(cnts):
    c += 25
    i += 1
    x, y, w, h = cv2.boundingRect(cnt)
    center = (x+w//2, y+h//2)
    cv2.rectangle(pos, (x, y), (x+w, y+h), (c, 220, 255-c), 1)

    # Extract each polygon in a separate image
    cur = mix.copy()
    cv2.floodFill(cur, np.zeros((H+2, W+2), np.uint8), (0, 0), 0)
    cv2.floodFill(cur, np.zeros((H+2, W+2), np.uint8), center, 127)
    cur[np.where(cur == 255)] = 30
    cur[np.where(cur == 127)] = 255
    out[np.where(cur == 255)] = (random.randint(50, 255),
                                 random.randint(50, 255),
                                 random.randint(50, 255))
    #cv2.imwrite(sys.path[0]+f'/tri_{i}.jpg', cur)

    if c >= 255:
        c = 0

# Print number of polygones
print(len(cnts))

# Change type of images
im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
msk = cv2.cvtColor(msk, cv2.COLOR_GRAY2BGR)
mix = cv2.cvtColor(mix, cv2.COLOR_GRAY2BGR)

# Save the output
top = np.hstack((im, mix))
btm = np.hstack((msk, pos))
cv2.imwrite(sys.path[0]+'/rect_out.jpg', np.vstack((top, btm)))
cv2.imwrite(sys.path[0]+'/rect_out2.jpg', np.vstack((im, out)))

我选择了视觉而非分析方法:

  • 在你左边的图片中画出“a”三角形,并用 1、2、3 填充
  • 画出你右图中的“b”三角形,分别填充100、200、300
  • 添加左右图片
  • 在结果中找出唯一的颜色,每一种颜色都对应一个多边形,它的值会告诉你哪两个初始三角形在那里相交

这段代码都是为左图设置的:

#!/usr/bin/env python3

# 

import cv2
import numpy as np

# Make black canvas for left image and right image
left  = np.zeros((256,512),np.uint16)
right = np.zeros((256,512),np.uint16)

# Draw "a" triangles filled with 1, 2, 3 onto left image
a1 = np.array([[0,0],[512,128],[0,256]], np.int32).reshape((-1,1,2))
cv2.fillPoly(left,[a1],(1),8)
a2 = np.array([[0,0],[512,0],[512,128]], np.int32).reshape((-1,1,2))
cv2.fillPoly(left,[a2],(2),8)
a3 = np.array([[0,256],[512,128],[512,256]], np.int32).reshape((-1,1,2))
cv2.fillPoly(left,[a3],(3),8)
cv2.imwrite('left.png', left)

请注意,我对下面的左图进行了对比拉伸,以便您可以看到它:

此代码只是为正确的图像设置的:

# Draw "b" triangles filled with 100, 200, 300 onto right image
b1 = np.array([[0,0],[200,256],[0,256]], np.int32).reshape((-1,1,2))
cv2.fillPoly(right,[b1],(100),8)
b2 = np.array([[0,0],[150,0],[200,256]], np.int32).reshape((-1,1,2))
cv2.fillPoly(right,[b2],(200),8)
b3 = np.array([[150,0],[512,0],[200,256]], np.int32).reshape((-1,1,2))
cv2.fillPoly(right,[b3],(300),8)
b4 = np.array([[512,0],[512,256],[200,256]], np.int32).reshape((-1,1,2))
cv2.fillPoly(right,[b4],(400),8)
cv2.imwrite('right.png', right)

请注意,我对下面的右图进行了对比拉伸,以便您可以看到它:

而下面的代码才是真正的答案:

# Add the two images
result = left + right
cv2.imwrite('result.png', result)

# Find the unique colours in the image - that is the number of polygons
colours = np.unique(result)
print(f'Colours in result: {colours}')

# Iterate over the polygons, making one at a time black on a grey background
for c in colours:
   masked = np.where(result==c, 0, 128)
   cv2.imwrite(f'result-{c}.png', masked)

示例输出

Colours in result: [101 103 201 202 203 301 302 303 401 402 403]

输出图像

希望您能在输出图像中看到例如颜色 402 是用 2 填充的三角形与用 400 填充的三角形相交的地方,依此类推。

请注意,如果需要,您可以 运行 findContours() 在每个蒙版多边形上获取其顶点和面积。

对于每对三角形,您可以使用 Sutherland-Hodgman algorithm 求出它们的交点形成的多边形。