Как разделить изображения по диагонали? - PullRequest
3 голосов
/ 20 февраля 2020

У меня есть изображения, как показано ниже. Я хотел бы разделить их по диагонали, возможно, используя центроид изображения или получить угол ограничивающего прямоугольника / эллипса и обрезать его вдоль.

Синий и зеленый цвета на практике представляют разные контуры, это будет двоичный порог, поэтому все цвета выглядят одинаково.

Я могу вырезать изображения по горизонтали или вертикали, используя нарезку списка:

eg img_split=img[:50%,:]

Как мне сделать это по диагонали в OpenCV Python / Numpy?

Изображения:

enter image description here

enter image description here

Разделенный образец :

enter image description here

enter image description here

Ответы [ 2 ]

3 голосов
/ 20 февраля 2020

Я в основном придерживаюсь вашей идеи:

  • Обратный двоичный порог изображения (допущение: различные объекты на простом белом фоне).
  • Поиск внешнего контура (допущение : только один объект / контур).
  • Найти повернутый прямоугольник минимальной площади.
  • Рассчитать центроид.
  • С центроидом и углом поворота рассчитать две точки ребра, где «диагональ» делит изображения на две части.
  • Создайте маску для обеих частей изображения и разделите изображение, используя маску и «антимаску».

Вот полный код:

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

И вот результаты (с некоторыми результатами отладки):

First image: Debug

First image: Upper half

First image: Lower half

Second image: Debug

Second image: Upper half

Second image: Lower half

Надеюсь, это поможет!

----------------------------------------
System information
----------------------------------------
Platform:    Windows-10-10.0.16299-SP0
Python:      3.8.1
NumPy:       1.18.1
OpenCV:      4.2.0
----------------------------------------
1 голос
/ 20 февраля 2020

Если вы просто хотите вырезать, вы можете использовать функцию fillPoly , которая поможет вам вырезать любую многоугольную форму. В приведенном ниже коде я только что дал многоугольные точки вручную.

Примечание: Если вы хотите, чтобы программа обнаружила эти точки, для первого изображения это легко, поскольку объекты имеют разные цвета , Но для второго изображения я не знаю, как это сделать.

Результат:

enter image description here

Код:

Примечание : Моя среда основана на C ++, поэтому я использовал ее, но ее легко конвертировать.

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;

int main()
{


    cv::Mat img = cv::imread("/ur/image/directory/image.png");
    imshow("original",img);

    cv::Point corners[1][4];
    corners[0][0] = Point( 0, 0 );
    corners[0][1] = Point( img.cols/2, img.rows/2 );
    corners[0][2] = Point( img.cols, img.rows );
    corners[0][3] = Point( img.cols, 0 );

    const Point* corner_list[1] = { corners[0] };

    int num_points = 4;
    int num_polygons = 1;
    int line_type = 8;

    cv::Mat mask = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
    cv::fillPoly( mask, corner_list, &num_points, num_polygons, cv::Scalar( 255, 255, 255 ),  line_type);
    cv::Mat result(img.size(), img.type(), cv::Scalar(255, 255, 255));
    img.copyTo(result, mask);

    imshow("Output",result);
    cv::waitKey(0);
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...