opencv-076-图像透视变换应用

知识点

对于很多的文本扫描图像,有时候因为放置的原因导致ROI区域倾斜,这个时候我们会想办法把它纠正为正确的角度视角来,方便下一步的布局分析与文字识别,这个时候通过透视变换就可以取得比较好的裁剪效果,一步就可以实现裁剪与调整。使用透视变换相关几何变换的好处如下:

  1. 透视变换不会涉及到几何变换角度旋转
  2. 透视变换对畸变图像有一定的展开效果
  3. 透视变换可以完成对图像ROI区域提取

API

代码(c++,python)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

/*
* 图像透视变换应用
*/
int main() {
Mat src = imread("../images/case1r.png");
if (src.empty()) {
cout << "could not load image.." << endl;
}
imshow("input", src);

// 二值图像
Mat gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

// 开操作
Mat se = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(binary, binary, MORPH_OPEN, se);

// 寻找最大轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
int index = -1;
int max = 0;
for (size_t t = 0; t < contours.size(); t++) {
double area = contourArea(contours[t]);
if (area > max) {
max = area;
index = t;
}
}

// 寻找最小外接矩形
RotatedRect rect = minAreaRect(contours[index]);
int width = static_cast<int>(rect.size.height);
int height = static_cast<int>(rect.size.width);

// 透视变换
Point2f vertices[4];
rect.points(vertices);
vector<Point> src_pts;
vector<Point> dst_pts;
dst_pts.push_back(Point(width, height));
dst_pts.push_back(Point(0, height));
dst_pts.push_back(Point(0, 0));
dst_pts.push_back(Point(width, 0));
for (int i = 0; i < 4; i++) {
src_pts.push_back(vertices[i]);
}
Mat M = findHomography(src_pts, dst_pts);
Mat result = Mat::zeros(Size(width, height), CV_8UC3);
warpPerspective(src, result, M, result.size());
imshow("result", result);

waitKey(0);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import cv2 as cv
import numpy as np

src = cv.imread("D:/images/st_02.png")
cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
cv.imshow("input", src)

# 图像二值化
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)

se = cv.getStructuringElement(cv.MORPH_RECT, (3, 3), (-1, -1))
binary = cv.morphologyEx(binary, cv.MORPH_OPEN, se)
cv.imshow("binary", binary)
cv.imwrite("D:/binary.png", binary)

# 轮廓提取, 发现最大轮廓
out, contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
index = 0
max = 0
for c in range(len(contours)):
area = cv.contourArea(contours[c])
if area > max:
max = area
index = c

# 寻找最小外接矩形
rect = cv.minAreaRect(contours[index])
print(rect[2])
print(rect[0])
# trick
height, width = rect[1]
print(rect[1])
box = cv.boxPoints(rect)
src_pts = np.int0(box)
print(src_pts)

dst_pts = []
dst_pts.append([width,height])
dst_pts.append([0, height])
dst_pts.append([0, 0])
dst_pts.append([width, 0])

# 透视变换
M, status = cv.findHomography(src_pts, np.array(dst_pts))
result = cv.warpPerspective(src, M, (np.int32(width), np.int32(height)))

if height < width:
result = cv.rotate(result, cv.ROTATE_90_CLOCKWISE)

cv.imshow("result", result)
cv.imwrite("D:/result.png", result)

cv.waitKey(0)
cv.destroyAllWindows()

结果

代码地址

github