Extracting polygons from superimposed images

Viewed 388

superimposed images

I have 2 images composed of triangles. I add them together and new polygons formed.
Is it possible to determine the polygons when these two images superimposed? Should I aim for processing the resultant image or can I determine it from the locations of input triangles?

Note: I know exact locations of 1st and 2nd image vertices and triangles as (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] 
3 Answers

I went for a visual rather than analytical approach:

  • draw the "a" triangles in your left picture filled with 1, 2, 3
  • draw the "b" triangles in your right picture filled with 100, 200, 300
  • add the left and right pictures
  • find the unique colours in the result, each will correspond to a polygon and its value will tell you which two initial triangles intersect there

This code is all just set-up for the left image:

#!/usr/bin/env python3

# https://stackoverflow.com/q/68938410/2836621

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)

Note that I contrast-stretched the left image below so you can see it:

enter image description here

This code is all just set-up for the right image:

# 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)

Note that I contrast-stretched the right image below so you can see it:

enter image description here

And the following code is the actual answer:

# 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)

Sample Output

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

Output Images

enter image description here

Hopefully you can see that colour 402 for example in the output image is where the triangle filled with 2 intersects with the triangle filled with 400, and so on.

Note that you can run findContours() on each masked polygon to get its vertices and area, if you want to.

If you can calculate the constructed polygons with a mathematical model, you can probably achieve the desired output with better accuracy.

The method I suggest is not very accurate but it may help you.

I show an algorithm that helps you extract and store polygons separately. From this point on, you need to find and arrange the corners in each polygon (this part does not exist in the algorithm).

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)))

Steps of making masks to find the coordinates and center of each polygon.

enter image description here

As indicated; Each polygon is stored as a separate image. From here you have to think about the next step; You can find and arrange the corners of each polygon in each image.

enter image description here


I emphasize; In my opinion, this method is not logical and is not accurate enough. But if you do not find a better solution, it may be useful for you.


Update

I drew this hypothetical image with graphic software and updated the code. I think it works great. You can adjust the parameters according to your needs. The final image was not supposed to be in color. I just wanted to show that it works properly.

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)))

enter image description here

Related