利用OpenCV实现关键点的仿射变换
1. 基本思路
本文主要用一个函数实现图像的放缩、旋转,并最终得到仿射变换矩阵tm 和逆仿射变换的矩阵 tm_inv。仿射变换矩阵 tm 可以将原图中坐标为 \((x, y)\) 的像素值映射到仿射变换后的图中坐标为 \((x^{\prime}, y^{\prime})\) 的位置。
2. 具体步骤
实现该变换的基本逻辑是找到不共线的 3 个点,然后将
3. C++ 代码实现
具体实现如下:
#include <algorithm>
#include <cmath>
#include <iostream>
#include <opencv2/opencv.hpp>
/**
* @brief 逆时针旋转
* @param x 原坐标 x
* @param y 原坐标 y
* @param rx 旋转后的坐标 x
* @param ry 旋转后的坐标 y
* @param angle 逆时针旋转角度,单位: 度
*/
void getCounterclockwiseRotatePoint(float x, float y, float &rx, float &ry, float angle) {
float sn = sin(angle);
float cs = cos(angle);
rx = x * cs - y * sn;
ry = x * sn + y * cs;
}
/**
* @brief 获取与前两个点不共线的第三个点
*/
void getNotCollinear3rdPoint(float x1, float y1, float x2, float y2, float &x3, float &y3) {
float dx = x1 - x2;
float dy = y1 - y2;
x3 = x2 - dy;
y3 = y2 + dx;
}
/**
* @brief 图像执行缩放、顺时针旋转操作
* @param src_img 原图
* @param dst_img 目标图
* @param dst_w 目标图的宽度
* @param dst_h 目标图的高度
* @param angle 顺时针旋转角度
* @param padVal 背景值
* @param tm 变换矩阵
* @param tm_inv 反向变换矩阵
*/
void rotateAffineTransformByOpenCV(cv::Mat &src_img, cv::Mat &dst_img, float dst_w, float dst_h, float angle, int padVal, cv::Mat& tm, cv::Mat& tm_inv) {
float src_w = src_img.cols, src_h = src_img.rows;
float r = dst_w / dst_h;
float sw, sh; // scaled width, height
// 缩放0.8x 能够完成在目标图像大小内的任意角度旋转
if (src_w > r * src_h) {
sw = src_w * 1.25f;
sh = src_w / r * 1.25f;
} else {
sw = src_h * r * 1.25f;
sh = src_h * 1.25f;
}
cv::Point2f src_pts[3];
cv::Point2f dst_pts[3];
// 1st point: Pick a center point
src_pts[0].x = src_w / 2;
src_pts[0].y = src_h / 2;
dst_pts[0].x = dst_w / 2;
dst_pts[0].y = dst_h / 2;
// 2nd point: Pick a point at random and rotate it counterclockwise
angle = angle / 180.f * M_PI;
float rx, ry; // rotated x, y coordinate
getCounterclockwiseRotatePoint(0, -0.5 * sw, rx, ry, angle);
src_pts[1].x = src_pts[0].x + rx;
src_pts[1].y = src_pts[0].y + ry;
dst_pts[1].x = dst_pts[0].x;
dst_pts[1].y = dst_pts[0].y - 0.5 * dst_w;
// 3rd point
getNotCollinear3rdPoint(src_pts[0].x, src_pts[0].y, src_pts[1].x, src_pts[1].y, src_pts[2].x, src_pts[2].y);
getNotCollinear3rdPoint(dst_pts[0].x, dst_pts[0].y, dst_pts[1].x, dst_pts[1].y, dst_pts[2].x, dst_pts[2].y);
tm = cv::getAffineTransform(src_pts, dst_pts);
cv::invertAffineTransform(tm, tm_inv);
cv::warpAffine(src_img, dst_img, tm, cv::Size((int)dst_w, (int)dst_h), 1, 0, cv::Scalar(padVal));
}
int main() {
cv::Mat src_img = cv::imread("input.jpg");
if (src_img.empty()) {
std::cout << "Failed to load image" << std::endl;
return -1;
}
int dst_w = 320, dst_h = 320;
float angle = 45;
int padVal = 0;
cv::Mat dst_img(dst_w, dst_h, CV_8UC3);
cv::Mat tm, tm_inv; // 注意:输出的数据类型为 double
rotateAffineTransformByOpenCV(src_img, dst_img, dst_w, dst_h, angle, padVal, tm, tm_inv);
cv::imwrite("output.jpg", dst_img);
return 0;
}

浙公网安备 33010602011771号