warpAffine()函数实现最小外接矩形和原始图像平行

前言

本来今天想一并吧图像的旋转和裁剪给一起完成了,但是图像在旋转之后的坐标定位一直有问题,所以今天只好放出在昨天的博客的基础上做的图像旋转的函数,旋转之后目标图像的最小外接矩形是和原始图像平行的,为的是设置ROI为裁剪图像做准备。

测试代码:

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
65
66
67
#include <iostream>
#include <string>
#include <cmath>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

float angle;
Point pointlu;
void minRect(Mat& srcImage);
void Rotate(Mat &src,Mat &dst,float angle);
static inline bool ContoursSortFunction(vector<Point> contour1,vector<Point> contour2);
int main(){
string path = "/Users/cezarbao/Desktop/TestImages/*.bmp";
vector<Mat>images;
vector<String>srcImages;
glob(path,srcImages,false);
size_t cnt = srcImages.size();
for(int i = 0; i < cnt; i++)
{
images.push_back(imread(srcImages[i]));
minRect(images[i]);
Rotate(images[i],images[i],angle);
imshow("image",images[i]);
waitKey(0);
}
}
void minRect(Mat& srcImg){
Mat dstImage = srcImg.clone();
cvtColor(dstImage, dstImage, COLOR_BGR2GRAY);
threshold(dstImage, dstImage, 254, 255, THRESH_BINARY);
//imshow("srcImage", dstImage);
vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(dstImage, contours, hierarcy, RETR_LIST, CHAIN_APPROX_NONE);
sort(contours.begin(),contours.end(),ContoursSortFunction);
contours.erase(contours.begin());
vector<RotatedRect> box(contours.size()); //定义最小外接矩形集合
Point2f rect[4];
for(int i=0; i < 1; i++)
{
box[i] = minAreaRect(Mat(contours[i])); //计算每个轮廓最小外接矩形
box[i].points(rect); //把最小外接矩形四个端点复制给rect数组
angle = box[i].angle;
for(int j=0; j<4; j++)
{
line(srcImg, rect[j], rect[(j+1)%4], Scalar(0, 0, 255)); //绘制最小外接矩形每条边
}
}
}
static inline bool ContoursSortFunction(vector<Point> contour1,vector<Point> contour2){
    return (contourArea(contour1) > contourArea(contour2));
}
void Rotate(Mat &src,Mat &dst,float angle){
    //fill
    int maxBorder =(int) (max(src.cols, src.rows)* 1.414); //即为sqrt(2)*max
    int dx = (maxBorder - src.cols)/2;
    int dy = (maxBorder - src.rows)/2;
    copyMakeBorder(src, dst, dy, dy, dx, dx, BORDER_CONSTANT,Scalar(0,0,0));
    //rotate
    Point2f center( (float)(dst.cols/2) , (float) (dst.rows/2));
    Mat affine_matrix = getRotationMatrix2D( center, angle, 1.0 );//求得旋转矩阵
    warpAffine(dst, dst, affine_matrix, dst.size());
}

一、copyMakeBorder()边缘扩充函数

函数原型:

1
2
3
4
5
void copyMakeBorder( 
    const Mat& src, Mat& dst,
    int top, int bottom, int left, int right,
    int borderType, const Scalar& value=Scalar()
)

第一个参数和第二个参数分别是输入图像和输出图像
top、bottom、left、right参数非常明显,分别是顶端、底部、左边和右边所需填充的距离
borderType:扩充边缘的类型,就是外插的类型,OpenCV中给出以下几种方式
BORDER_REPLICATE 对边界像素进行复制
BORDER_REFLECT 对感兴趣的图像中的像素在两边分别进行复制,也就是左右各复制一次
BORDER_REFLECT_101 只复制一次,左右各占一半
BORDER_WRAP 外包装(?)
BORDER_CONSTANT 常量,选择这种方式的时候,需要用到最后一个参数为扩充的边缘选择颜色

二、warpAffine()旋转函数

函数原型:

1
2
3
4
5
6
7
8
9
void warpAffine(
    InputArray src,
    OutputArray dst,
    InputArray M,
    Size dsize,
    int flags = INTER_LINEAR,
    int borderMode = BORDER_CONSTANT,
    const Scalar & borderValue = Scalar()
)

src: 输入图像
dst: 输出图像,尺寸由dsize指定,图像类型与原图像一致
M: 2X3的变换矩阵
dsize: 指定图像输出尺寸
flags: 插值算法标识符,有默认值INTER_LINEAR
borderMode: 边界像素模式,有默认值BORDER_CONSTANT
borderValue: 边界取值,有默认值Scalar()即0

三、getRotationMatrix2D()旋转图像矩阵取得函数

warpAffine()函数的InputArrey M的取得需要用到这个函数
函数原型:

1
2
3
4
5
Mat getRotationMatrix2D(
    Point2f center,
    double angle,
    double scale
)

center: Point2f类型,表示原图像的旋转中心
angle: double类型,表示图像旋转角度,角度为正则表示逆时针旋转,角度为负表示逆时针旋转(坐标原点是图像左上角)
scale: 缩放系数