如何对角分割图像?
How to split images diagonally?
我有如下图片。我想对角线分割它们,可能使用图像的质心或获取边界的角度 rect/ellipse 并沿其切割。
蓝色和绿色代表不同的轮廓,在实践中,这些将被二进制阈值处理,所以所有颜色看起来都一样。
我可以使用列表切片水平或垂直切割图像:
eg img_split=img[:50%,:]
我如何在 OpenCV 中对角线 Python/Numpy?
图片:
拆分样本:
如果您只想切割,可以使用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;
}
我基本上遵循你的想法:
- 图像的逆二进制阈值(假设:纯白色背景上的不同对象)。
- 寻找外部轮廓(假设:只有一个object/contour)。
- 找到最小面积的旋转矩形。
- 计算质心。
- 利用质心和旋转角度,计算两个边缘点,其中"diagonal"将图像分成两部分。
- 使用蒙版和"anti-mask"为图像部分和分割图像生成蒙版。
完整代码如下:
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
----------------------------------------
我有如下图片。我想对角线分割它们,可能使用图像的质心或获取边界的角度 rect/ellipse 并沿其切割。
蓝色和绿色代表不同的轮廓,在实践中,这些将被二进制阈值处理,所以所有颜色看起来都一样。
我可以使用列表切片水平或垂直切割图像:
eg img_split=img[:50%,:]
我如何在 OpenCV 中对角线 Python/Numpy?
图片:
拆分样本:
如果您只想切割,可以使用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;
}
我基本上遵循你的想法:
- 图像的逆二进制阈值(假设:纯白色背景上的不同对象)。
- 寻找外部轮廓(假设:只有一个object/contour)。
- 找到最小面积的旋转矩形。
- 计算质心。
- 利用质心和旋转角度,计算两个边缘点,其中"diagonal"将图像分成两部分。
- 使用蒙版和"anti-mask"为图像部分和分割图像生成蒙版。
完整代码如下:
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
----------------------------------------