OpenCV图像几何变换

发布于:2025-08-10 ⋅ 阅读:(10) ⋅ 点赞:(0)

    图像几何变换是图像处理中最基础的一类操作,本文不讲任何公式和理论,只用 OpenCV C++ 演示如何实现常见几何变换,包括:缩放、平移、旋转、仿射变换、透视变换。

欧式变换

    欧式变换是一类特殊的变换,包括旋转与平移,该变换保持了物体间的距离,角度等物理量,因此也被称作刚体变换(即变换不发生形变)。OpenCV提供cv::getRotationMatrix2D生成欧式变换的变换矩阵,定义如下:

Mat getRotationMatrix2D(Point2f center, double angle, double scale);
  • center:旋转中心点,我们可以绕图像中心旋转,也可以绕原点旋转
  • angle:旋转角度,使用角度值表示(非弧度),表示绕旋转点逆时针旋转
  • scale:缩放因子,一般情况下该因子设置为1,大于1表示放大图像,小于1表示缩小图像

    以下给出旋转矩阵获取示例:

// 绕图像中心旋转45度,执行如下步骤:
// 1 将图像中心center平移到原点(-cx,-cy)
// 2 施加旋转
// 3 平移回原坐标(+cx,+cy)
// 以上构成了绕中心旋转矩阵
cv::Point2f center(src.cols / 2.0f, src.rows / 2.0f);
// 参数scale表示缩放系数,大于1时放大图像,小于1时缩小图像
// 这里使用scale=1保持图像尺寸(即仅欧式变换)
cv::Mat M = cv::getRotationMatrix2D(center, 45, 1.);
std::cout << "rotate 45 around image center:\n" << M << std::endl;

    以上代码中我们备注了平移相关内容,注意此处的平移仅仅是为了实现绕图像中心点旋转功能。我们使用了两次相反方向的平移抵消了平移效果,最终实现了绕图像中心点旋转的变换。

    为了实现平移,我们可以构造平移矩阵。所谓平移矩阵,即为旋转量为单位矩阵,平移量为(t_{x},t_{y})的矩阵,如\begin{bmatrix} 1 &0 &t_{x} \\ 0&1 & t_{y} \end{bmatrix}

    给定一个变换:如对图像平移(t_{x},t_{y})并绕图像中心旋转\theta,可以使用矩阵乘法实现,如下:

  • 使用cv::getRotationMatrix2D获取绕图像中心旋转\theta的旋转矩阵
  • 由于该矩阵为2*3维度矩阵,我们添加第三行数据(0,0,1)构成齐次矩阵
  • 构造平移(t_{x},t_{y})的齐次矩阵为\begin{bmatrix} 1 &0 &t_{x} \\ 0&1 & t_{y}\\ 0& 0 & 1 \end{bmatrix}
  • 将两个3*3的矩阵相乘即可得到平移+旋转的效果

    有了平移+旋转矩阵后,使用cv::warpAffine函数可实现图像变换,具体如下:

void 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×3 仿射变换矩阵
  • dsize:输出图像大小,通过修改该参数可以避免变换后越界问题
  • flags:插值方式,在深入理解图像插值:从原理到应用中有系统讲解,一般选择默认参数即可
  • borderMode:边界处理,如 BORDER_CONSTANT, BORDER_REFLECT,函数根据不同选择策略进行边界填充
  • borderValue:当边界模式为BORDER_CONSTANT时,该参数给定填充常量值

   以下实现了平移+旋转的效果:

  void shiftAndRotate()
{
    cv::Mat src = cv::imread("1_src.bmp", cv::IMREAD_COLOR);
	if (src.empty()) return;  // 检查是否成功读取图像

	cv::imwrite("1_src.png", src);

	// 绕图像中心旋转45度,执行如下步骤:
	// 1 将图像中心center平移到原点(-cx,-cy)
	// 2 施加旋转
	// 3 平移回原坐标(+cx,+cy)
	// 以上构成了绕中心旋转矩阵
	cv::Point2f center(src.cols / 2.0f, src.rows / 2.0f);
	// 参数scale表示缩放系数,大于1时放大图像,小于1时缩小图像
	// 这里使用scale=1保持图像尺寸(即仅欧式变换)
	cv::Mat M = cv::getRotationMatrix2D(center, 45, 1.);
	std::cout << "rotate 45 around image center:\n" << M << std::endl;

	// 构造一个3x3单位矩阵
	cv::Mat _M = cv::Mat::eye(3, 3, CV_32F);

	// 将2x3前两行复制到3x3前两行
	M.row(0).copyTo(_M.row(0).colRange(0, 3));
	M.row(1).copyTo(_M.row(1).colRange(0, 3));

	// 平移(20,30)
	cv::Mat M2 = (cv::Mat_<float>(3, 3) << 1, 0, 50, 0, 1, 50, 0,0,1); 

	// 获取平移+旋转矩阵
	cv::Mat M3 = M2 * _M;

	// 裁剪到2*3矩阵
	cv::Mat M4 = cv::Mat::zeros(2, 3, CV_32F);
	M3.row(0).copyTo(M4.row(0).colRange(0, 3));
	M3.row(1).copyTo(M4.row(1).colRange(0, 3));

	cv::Mat dst;
	warpAffine(src, dst, M4, src.size());
	cv::imwrite("2_rotate_shift.png", dst);
}
原图

旋转+平移

仿射变换与透视变换

    对于一般的仿射变换与透视变换,在OpenCV中,他们的变换矩阵维度不一样。仿射变换矩阵为2*3的变换矩阵,透视变换为3*3的变换矩阵。除此之外,其调用流程基本一致。下面给出仿射变换与透视变换的使用示例:

void affineAndPerspective()
{
	cv::Mat src = cv::imread("1_src.bmp", cv::IMREAD_COLOR);
	if (src.empty()) return;  // 检查是否成功读取图像


	// 使用getAffineTransform获取仿射变换矩阵
	// 需要3对点确定变换矩阵!
	cv::Point2f srcTri[] = {
		cv::Point2f(0, 0),
		cv::Point2f(src.cols - 1, 0),
		cv::Point2f(0, src.rows - 1)
	};
	cv::Point2f dstTri[] = {
		cv::Point2f(src.cols * 0.f, src.rows * .3f),
		cv::Point2f(src.cols * .9f, src.rows * .2f),
		cv::Point2f(src.cols * .15f, src.rows * .8f)
	};

	cv::Mat M = cv::getAffineTransform(srcTri, dstTri);
	cv::Mat dst;
	cv::warpAffine(src, dst, M, src.size(), cv::INTER_CUBIC);
	cv::imwrite("2_affine.bmp", dst);

	// 使用getPerspectiveTransform获取透视变换矩阵
	// 需要4对点确定变换矩阵!
	cv::Point2f srcTri2[] = {
		cv::Point2f(0, 0),
		cv::Point2f(src.cols - 1, 0),
		cv::Point2f(0, src.rows - 1),
		cv::Point2f(src.cols - 1, src.rows - 1)
	};
	cv::Point2f dstTri2[] = {
		cv::Point2f(src.cols * 0.f, src.rows * .3f),
		cv::Point2f(src.cols * .9f, src.rows * .2f),
		cv::Point2f(src.cols * .15f, src.rows * .8f),
		cv::Point2f(src.cols * .7f, src.rows * .7f)
	};

	cv::Mat M2 = cv::getPerspectiveTransform(srcTri2, dstTri2);
	cv::Mat dst2;
	cv::warpPerspective(src, dst2, M2, src.size(), cv::INTER_CUBIC);
	cv::imwrite("3_perspective.bmp", dst2);
}

    我们首先通过变换点对计算变换矩阵。对于平面上的仿射变换来说,只需要3个不共线的点就可以确定一个仿射变换矩阵。对于平面上的透视变换来说,需要不共线的4个点确保一个透视变换矩阵。仿射变换保持了平行性,透视变换不再保持平行性。

仿射变换
透视变换

深入理解图像几何变换

    在解析图像几何变换:从欧式到仿射再到透视中,我们对图像几何变换进行了详细的描述,包括几何变换相关的数学原理以及完整的示例代码,您可以参考该博文以深入理解图像几何变换相关内容。


网站公告

今日签到

点亮在社区的每一天
去签到