How to split images diagonally?

ohyesyoucan :

I have images like below. I would like to split them diagonally, probably using the centroid of the image or get the angle of the bounding rect/ellipse and cut along that.

The blue and green colors are to represent different contours, in practice, these will be binary thresholded, so all colors look the same.

I can cut the images horizontally or vertically using list slicing:

eg img_split=img[:50%,:]

How do I do it diagonally in OpenCV Python/Numpy?

Images:

enter image description here

enter image description here

Split sample:

enter image description here

enter image description here

HansHirse :

I'm basically following your idea:

  • Inverse binary threshold the image (assumption: distinct object(s) on plain white background).
  • Find external contour (assumption: only one object/contour).
  • Find rotated rectangle of minimum area.
  • Calculate centroid.
  • With the centroid and the rotation angle, calculate the two edge points, where the "diagonal" divides the images in two parts.
  • Generate a mask for both image parts and split image using the mask and the "anti-mask".

Here's the full code:

import cv2
import numpy as np
from skimage import io              # Only needed for web reading images


def process(img_url):

    # Web read image
    img = cv2.cvtColor(io.imread(img_url), cv2.COLOR_RGB2BGR)
    img_c = img.copy()

    # Inverse binary threshold grayscale version of image
    # Assumption: plain white background
    img_thr = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 248, 255, cv2.THRESH_BINARY_INV)[1]

    # Find external contour
    # Assumption: only one object/contour
    cnts = cv2.findContours(img_thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    # Find rotated rectangle of the minimum area
    rect = cv2.minAreaRect(cnts[0])
    rect_pts = np.int0(cv2.boxPoints(rect))

    # Calculate centroid
    cent = np.int0((rect_pts[0] + rect_pts[2]) / 2)

    # Calculate tangent of rotation angle
    alpha = rect[2]
    if np.abs(alpha) > 45:
        alpha += 90
    tan_alpha = np.tan(np.deg2rad(alpha))

    # Calculate first edge point
    x0 = np.int32(cent[0] - cent[1] / tan_alpha)
    if x0 < 0:
        x0 = 0
        y0 = np.int32(-cent[0] * tan_alpha + cent[1])
    else:
        y0 = 0

    # Calculate second edge point
    x1 = np.int32(cent[0] + (img.shape[0] - cent[1]) / tan_alpha)
    if x1 > img.shape[1]:
        x1 = img.shape[1]
        y1 = np.int32((x1 - cent[0]) * tan_alpha + cent[1])
    else:
        y1 = img.shape[0]

    # Generate mask for cutting
    # Assumption: Image is sufficient large
    mask = np.zeros_like(img_thr)
    mask = cv2.line(mask, (x0, y0), (x1, y1), 255, 1)
    cv2.floodFill(mask, None, (cent[0] - 5, cent[1] - 5), 255)

    # Split image
    mask3 = np.repeat(np.expand_dims(mask, 2), 3, 2)
    img1 = ~mask3 + cv2.bitwise_and(img_c, img_c, mask=mask)
    img2 = mask3 + cv2.bitwise_and(img_c, img_c, mask=255-mask)

    # Debug output
    img = cv2.line(img, (x0, y0), (x1, y1), (0, 0, 255), 2)
    img = cv2.drawContours(img, [rect_pts], -1, (128, 128, 128), 2)
    img = cv2.circle(img, tuple(cent), 5, (255, 0, 0), 4)

    cv2.imshow('img', img)
    cv2.imshow('img1', img1)
    cv2.imshow('img2', img2)
    cv2.waitKey(0)


img_urls = [
    'https://i.stack.imgur.com/suzjF.png',
    'https://i.stack.imgur.com/Rl2WN.png'
]

for i in img_urls:
    process(i)

cv2.destroyAllWindows()

And here are the results (with some debug output):

First image: Debug

First image: Upper half

First image: Lower half

Second image: Debug

Second image: Upper half

Second image: Lower half

Hope that helps!

----------------------------------------
System information
----------------------------------------
Platform:    Windows-10-10.0.16299-SP0
Python:      3.8.1
NumPy:       1.18.1
OpenCV:      4.2.0
----------------------------------------

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=11490&siteId=1