0%

2019-10-29随记

OpenCV实现仿射变换

OpenCV实现仿射变换涉及两个主要函数——gerRotationMatrix2DwarpAffine,前者可以根据旋转角度,缩放因子等获得旋转矩阵,后者可以根据已知的变换矩阵实现一些简单的重映射。

cv::getRotationMatrix2D函数

函数原型

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

  • center: 原图像的旋转中心
  • angle: 图像旋转角度,正逆负顺
  • scale: 缩放系数
cv::warpAffine函数

函数原型

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

  • src: 输入图像
  • dst: 输出图像
  • M: 2$\times$3的变换矩阵
  • dsize: 图像输出尺寸
  • flags: 插值算法标识符,默认值为INTER_LINEAR,常用插值算法如下
    • INTER_NEAREST: 最临近插值算法
    • INTER_LINEAR: 线性插值算法
    • INTER_CUBIC: 双立方插值算法
    • INTER_AREA: 区域插值算法(使用像素区域关系的重采样时图像抽取的首选方法,但是当图像被放大,它类似于INTER_NEAREST方法)
    • INTER_LANCZOS4: Lanczos插值(超过8$\times$8邻域的插值算法
    • INTER_MAX: 用于插值的掩模版
    • WARP_FILL_OUTLIERS: 标志位,用于填充目标图像的像素值,如果其中的一些值对应于原图像中的异常值,那么这些值将被设置为0
    • WARP_INVERSE_MAP: 标志位,反变换
  • borderMode: 边界像素模式,默认值为BORDER_CONSTANT
  • borderValue: 边界取值,有默认值为Scalar(),即为0

图像翻转 - cv::flip(InputArray src, OutputArray dst, int flipCode)

  • src: 输入矩阵
  • dst: 翻转后矩阵,类型与src一致
  • flipCode: 翻转模式,flipCode==0垂直翻转(沿X轴翻转),flipCode>0水平翻转(沿Y轴翻转),flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)

cv::Mat重载运算符operate()

函数原型

1
2
3
4
inline Mat Mat::operator()( Range _rowRange, Range _colRange ) const
{
return Mat(*this, _rowRange, _colRange);
}

除了分别传入行列的范围外,还可以直接传入cv::Rect类来截取图像中的指定区域。

1
2
3
4
5
cv::Mat img, target_img;
cv::Point2f center;
int width, height;
cv::Rect target = cv::Rect(center.x, center.y, width, height);
target_img = img(target);

如何截取旋转矩形内的图像

由于旋转矩形不是常规矩形(与轴平行),所以首先要将图像作旋转变化,旋转中心即为旋转矩形中心,使得旋转矩形内部的图像在窗口中是与坐标轴平行的,然后从旋转矩阵得到一般矩阵,通过cv::Mat的重载运算符operate()得到局部图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   //const cv::Mat &image, const cv::RotatedRect &rect
cv::Point2f vertex[4];

rect.points(vertex);
auto center = rect.center;
cv::Mat rot_mat = cv::getRotationMatrix2D(rect.center, rect.angle, 1);
cv::Mat rot_image;
cv::Mat roi_image;
warpAffine(image, rot_image, rot_mat, rot_image.size(), INTER_LINEAR, BORDER_CONSTANT); // warpAffine use 2ms
cv::Rect target = cv::Rect(center.x - (rect.size.width / 2),
center.y - (rect.size.height / 2),
rect.size.width, rect.size.height);
if (makeRectSafe(target, image.size()) == true)
{
roi_image = rot_image(target);
cv::resize(roi_image, roi_image, cv::Size(80, 60));
char str[100];
sprintf_s(str, "D:\\theThirdYear\\RM\\task1\\number\\%d.jpg", img_idx++);
cv::imwrite(str, roi_image);
}

其中用到的函数是用来保证target矩形是正常的,否则可能会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
bool makeRectSafe(cv::Rect & rect, cv::Size size) {
if (rect.x < 0)
rect.x = 0;
if (rect.x + rect.width > size.width)
rect.width = size.width - rect.x;
if (rect.y < 0)
rect.y = 0;
if (rect.y + rect.height > size.height)
rect.height = size.height - rect.y;
if (rect.width <= 0 || rect.height <= 0)
return false;
return true;
}