1.仿射变换(Affine Transformation)是一种二维线性变换,它保持了图像的"直线性"和"平行性",但不保持角度和长度。一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。
仿射变换包含以下几种基本变换:
平移(Translation)
旋转(Rotation)
缩放(Scaling)
剪切(Shearing)
函数原型: void warpAffine(InputArray src,OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, intborderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,需和源图片有一样的尺寸和类型。
第三个参数,InputArray类型的M,2×3的变换矩阵。
第四个参数,Size类型的dsize,表示输出图像的尺寸。
第五个参数,int类型的flags,插值方法的标识符。此参数有默认值INTER_LINEAR(线性插值),可选的插值方式如下:
INTER_NEAREST - 最近邻插值
INTER_LINEAR - 线性插值(默认值)
INTER_AREA - 区域插值
INTER_CUBIC –三次样条插值
INTER_LANCZOS4 -Lanczos插值
CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
CV_WARP_INVERSE_MAP –表示M为输出图像到输入图像的反变换,即 。因此可以直接用来做象素插值。否则, warpAffine函数从M矩阵得到反变换。
第六个参数,int类型的borderMode,边界像素模式,默认值为BORDER_CONSTANT。
第七个参数,const Scalar&类型的borderValue,在恒定的边界情况下取的值,默认值为Scalar(),即0。
案例1:使用变换矩阵
// 读取图像
Mat src = imread("input.jpg");
if (src.empty()) {
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
// 定义变换矩阵
// 这里创建一个旋转45度并缩放的变换
Point2f srcTri[3];
Point2f dstTri[3];
// 设置源图像和目标图像的三个点
srcTri[0] = Point2f(0, 0);
srcTri[1] = Point2f(src.cols - 1, 0);
srcTri[2] = Point2f(0, src.rows - 1);
dstTri[0] = Point2f(src.cols * 0.0, src.rows * 0.33);
dstTri[1] = Point2f(src.cols * 0.85, src.rows * 0.25);
dstTri[2] = Point2f(src.cols * 0.15, src.rows * 0.7);
// 计算仿射变换矩阵
Mat warpMat = getAffineTransform(srcTri, dstTri);
// 应用仿射变换
Mat warpDst = Mat::zeros(src.rows, src.cols, src.type());
warpAffine(src, warpDst, warpMat, warpDst.size());
案例2:使用旋转和缩放
Mat src = imread("input.jpg");
if (src.empty()) {
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
// 获取旋转中心
Point2f center(src.cols / 2.0f, src.rows / 2.0f);
// 定义旋转角度和缩放因子
double angle = 45.0; // 旋转45度
double scale = 0.6; // 缩放为原来的60%
// 获取旋转矩阵
Mat rotMat = getRotationMatrix2D(center, angle, scale);
// 应用变换
Mat dst;
warpAffine(src, dst, rotMat, src.size());
关键函数解析
getAffineTransform()
输入:源图像和目标图像的三个对应点
输出:2×3的仿射变换矩阵
getRotationMatrix2D()
输入:旋转中心、旋转角度和缩放因子
输出:2×3的旋转矩阵
warpAffine()
参数:源图像、目标图像、变换矩阵、输出图像大小
功能:应用仿射变换
应用场景
图像校正(如文档扫描后的倾斜校正)
图像配准(将不同视角的图像对齐)
数据增强(在深度学习中生成训练样本)
视角变换(模拟不同视角下的图像)
2.Halcon里面有个很常用的的算子vector_angle_to_rigid,,就是给定两组(x1,y1,angle1),(x2,y2,angle2)构建刚性仿射变换。下面我们用Opencv复刻他的效果。
#include <opencv2/opencv.hpp>
#include <cmath>
using namespace cv;
using namespace std;
// 构建刚性仿射变换矩阵
Mat buildRigidAffineMatrix(double x1, double y1, double angle1,
double x2, double y2, double angle2) {
// 计算平移分量
double tx = x2 - x1;
double ty = y2 - y1;
// 计算旋转角度(角度转弧度)
double rad = (angle2 - angle1) * CV_PI / 180.0;
double cosTheta = cos(rad);
double sinTheta = sin(rad);
// 构建刚性变换矩阵
Mat affineMat = (Mat_<double>(2, 3) <<
cosTheta, -sinTheta, tx,
sinTheta, cosTheta, ty);
return affineMat;
}
// 应用刚性变换到图像
void applyRigidTransformationToImage(const Mat& srcImage,
double x1, double y1, double angle1,
double x2, double y2, double angle2) {
// 构建刚性变换矩阵
Mat rigidMat = buildRigidAffineMatrix(x1, y1, angle1, x2, y2, angle2);
//注意:如果是为了保证变换后图像尺寸和比例不变,则不需要下面的代码。可以直接进行仿射变换
// 计算变换后的图像大小
// 由于旋转可能使图像变大,我们需要计算新的边界
vector<Point2f> corners = {
Point2f(0, 0),
Point2f(srcImage.cols, 0),
Point2f(srcImage.cols, srcImage.rows),
Point2f(0, srcImage.rows)
};
vector<Point2f> transformedCorners;
perspectiveTransform(corners, transformedCorners, rigidMat);
// 计算新图像的宽度和高度
Rect bbox = boundingRect(transformedCorners);
// 调整变换矩阵,使图像不被裁剪
rigidMat.at<double>(0, 2) += -bbox.x;
rigidMat.at<double>(1, 2) += -bbox.y;
// 应用仿射变换
Mat dstImage;
warpAffine(srcImage, dstImage, rigidMat, bbox.size(),
INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
}
int main() {
// 读取图像
Mat srcImage = imread("test.jpg");
if (srcImage.empty()) {
cerr << "Could not open or find the image!" << endl;
return -1;
}
// 定义变换参数
double x1 = srcImage.cols / 2.0; // 图像中心x坐标
double y1 = srcImage.rows / 2.0; // 图像中心y坐标
double angle1 = 0; // 原始角度
double x2 = x1 + 50; // 向右平移50像素
double y2 = y1 + 30; // 向下平移30像素
double angle2 = 45; // 旋转45度
// 应用刚性变换
applyRigidTransformationToImage(srcImage, x1, y1, angle1, x2, y2, angle2);
return 0;
}
关键步骤解析
构建刚性变换矩阵:
计算从(x1,y1)到(x2,y2)的平移量(tx, ty)
计算从angle1到angle2的旋转角度
构建2×3的仿射变换矩阵
处理图像边界问题:
计算原始图像四个角点变换后的位置
使用boundingRect确定变换后图像的最小包围矩形
调整变换矩阵,使变换后的图像完全可见
应用变换:
使用warpAffine函数应用刚性变换
设置插值方法(INTER_LINEAR)和边界填充方式(BORDER_CONSTANT)