【问题标题】:How to split images diagonally?如何对角分割图像?
【发布时间】:2020-06-04 08:38:02
【问题描述】:

我有如下图片。我想对角分割它们,可能使用图像的质心或获取边界矩形/椭圆的角度并沿着它切割。

蓝色和绿色代表不同的轮廓,实际上,这些将被二进制阈值化,所以所有颜色看起来都一样。

我可以使用列表切片水平或垂直切割图像:

eg img_split=img[:50%,:]

如何在 OpenCV Python/Numpy 中做对角线?

图片:

拆分样本:

【问题讨论】:

  • 程序将决定在哪里剪切或程序将直接剪切?
  • 暂时先剪一下,如果你能推荐第一个解决方案,那也很好。
  • 您可以使用 darContours 创建 2 个互补蒙版,并使用蒙版复制或覆盖其他区域

标签: python numpy opencv contour


【解决方案1】:

我基本上遵循你的想法:

  • 图像的反向二进制阈值(假设:纯白色背景上的不同对象)。
  • 查找外部轮廓(假设:只有一个对象/轮廓)。
  • 找到最小面积的旋转矩形。
  • 计算质心。
  • 使用质心和旋转角度,计算两个边缘点,其中“对角线”将图像分为两部分。
  • 使用遮罩和“反遮罩”为图像部分和分割图像生成遮罩。

这是完整的代码:

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

以下是结果(带有一些调试输出):

希望有帮助!

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

【讨论】:

    【解决方案2】:

    如果你只是想切割,你可以使用fillPoly 功能,它可以帮助你切割任何多边形。在下面的代码中,我只是手动给出了多边形点。

    注意:如果你想让程序检测这些点,对于第一张图像来说很容易,因为对象有不同的颜色。但是对于第二张图片我不知道该怎么做。

    结果:

    代码:

    注意:我的环境是基于 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;
    }
    

    【讨论】:

    • 我使用颜色来区分两个轮廓,想象它们是相同的颜色,我不知道哪个是哪个,就像在 LD 示例中一样。
    • 不幸的是,对于相同的颜色,会发生遮挡。很难为这种样本创建一个简单的算法。
    猜你喜欢
    • 1970-01-01
    • 2020-02-10
    • 2021-06-08
    • 2022-10-17
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    相关资源
    最近更新 更多