OpenCV代码提取:warpAffine函数的实现

仿射变换(affine transformation),又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间.它是一种二维坐标到二维坐标之间的线性变换,保持二维图形的”平直性”(straightness,即变换后直线还是直线不会打弯,圆弧还是圆弧)和”平行性”(parallelness,保持二维图形间的相对位置关系不变,平行线还是平行线,而直线上点的位置顺序不变,但向量间夹角可能会发生变化)。

一个任意的仿射变换都能表示为:乘以一个矩阵(线性变换)接着再加上一个向量(平移)。仿射变换能够表示:旋转(rotation)、平移(translation)、缩放(scale)、翻转(flip)和错切(剪切、shear)操作。事实上,仿射变换代表了两幅图像之间的关系。通常使用2*3矩阵来表示仿射变换。

         有了仿射变换矩阵后,计算目的图像的公式为:

dst(x,y)= src(M11x + M12y + M13, M21x + M22y + M23)

这里对OpenCV中warpAffine函数进行了提取,目前支持uchar和float两种类型,经测试,与OpenCV3.1结果完全一致。

实现代码warpAffine.hpp:

// fbc_cv is free software and uses the same licence as OpenCV
// Email: fengbingchun@163.com

#ifndef FBC_CV_WARP_AFFINE_HPP_
#define FBC_CV_WARP_AFFINE_HPP_

/* reference: include/opencv2/imgproc.hpp
	      modules/imgproc/src/imgwarp.cpp
*/

#include <typeinfo>
#include "core/mat.hpp"
#include "solve.hpp"
#include "imgproc.hpp"
#include "remap.hpp"

namespace fbc {

// Calculates an affine transform from three pairs of the corresponding points
FBC_EXPORTS int getAffineTransform(const Point2f src1[], const Point2f src2[], Mat_<double, 1>& dst);

// Applies an affine transformation to an image
// The function cannot operate in - place
// support type: uchar/float
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
int warpAffine(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst, const Mat_<_Tp2, chs2>& M_,
	int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar())
{
	FBC_Assert(src.data != NULL && dst.data != NULL && M_.data != NULL);
	FBC_Assert(src.cols > 0 && src.rows > 0 && dst.cols > 0 && dst.rows > 0);
	FBC_Assert(src.data != dst.data);
	FBC_Assert(typeid(double) == typeid(_Tp2) && M_.rows == 2 && M_.cols == 3);
	FBC_Assert((typeid(uchar).name() == typeid(_Tp1).name()) || (typeid(float).name() == typeid(_Tp1).name())); // uchar/float

	double M[6];
	Mat_<double, 1> matM(2, 3, M);
	M_.convertTo(matM);

	int interpolation = flags & INTER_MAX;
	if (interpolation == INTER_AREA)
		interpolation = INTER_LINEAR;

	if (!(flags & WARP_INVERSE_MAP)) {
		double D = M[0] * M[4] - M[1] * M[3];
		D = D != 0 ? 1. / D : 0;
		double A11 = M[4] * D, A22 = M[0] * D;
		M[0] = A11; M[1] *= -D;
		M[3] *= -D; M[4] = A22;
		double b1 = -M[0] * M[2] - M[1] * M[5];
		double b2 = -M[3] * M[2] - M[4] * M[5];
		M[2] = b1; M[5] = b2;
	}

	int x;
	AutoBuffer<int> _abdelta(dst.cols * 2);
	int* adelta = &_abdelta[0], *bdelta = adelta + dst.cols;
	const int AB_BITS = MAX(10, (int)INTER_BITS);
	const int AB_SCALE = 1 << AB_BITS;

	for (x = 0; x < dst.cols; x++) {
		adelta[x] = saturate_cast<int>(M[0] * x*AB_SCALE);
		bdelta[x] = saturate_cast<int>(M[3] * x*AB_SCALE);
	}

	Range range(0, dst.rows);

	const int BLOCK_SZ = 64;
	short XY[BLOCK_SZ*BLOCK_SZ * 2], A[BLOCK_SZ*BLOCK_SZ];;
	int round_delta = interpolation == INTER_NEAREST ? AB_SCALE / 2 : AB_SCALE / INTER_TAB_SIZE / 2, y, x1, y1;

	int bh0 = std::min(BLOCK_SZ / 2, dst.rows);
	int bw0 = std::min(BLOCK_SZ*BLOCK_SZ / bh0, dst.cols);
	bh0 = std::min(BLOCK_SZ*BLOCK_SZ / bw0, dst.rows);

	for (y = range.start; y < range.end; y += bh0) {
		for (x = 0; x < dst.cols; x += bw0) {
			int bw = std::min(bw0, dst.cols - x);
			int bh = std::min(bh0, range.end - y);

			Mat_<short, 2> _XY(bh, bw, XY);
			Mat_<_Tp1, chs1> dpart;
			dst.getROI(dpart, Rect(x, y, bw, bh));

			for (y1 = 0; y1 < bh; y1++) {
				short* xy = XY + y1*bw * 2;
				int X0 = saturate_cast<int>((M[1] * (y + y1) + M[2])*AB_SCALE) + round_delta;
				int Y0 = saturate_cast<int>((M[4] * (y + y1) + M[5])*AB_SCALE) + round_delta;

				if (interpolation == INTER_NEAREST) {
					x1 = 0;
					for (; x1 < bw; x1++) {
						int X = (X0 + adelta[x + x1]) >> AB_BITS;
						int Y = (Y0 + bdelta[x + x1]) >> AB_BITS;
						xy[x1 * 2] = saturate_cast<short>(X);
						xy[x1 * 2 + 1] = saturate_cast<short>(Y);
					}
				} else {
					short* alpha = A + y1*bw;
					x1 = 0;
					for (; x1 < bw; x1++) {
						int X = (X0 + adelta[x + x1]) >> (AB_BITS - INTER_BITS);
						int Y = (Y0 + bdelta[x + x1]) >> (AB_BITS - INTER_BITS);
						xy[x1 * 2] = saturate_cast<short>(X >> INTER_BITS);
						xy[x1 * 2 + 1] = saturate_cast<short>(Y >> INTER_BITS);
						alpha[x1] = (short)((Y & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE +
							(X & (INTER_TAB_SIZE - 1)));
					}
				}
			}

			if (interpolation == INTER_NEAREST) {
				remap(src, dpart, _XY, Mat_<float, 1>(), interpolation, borderMode, borderValue);
			} else {
				Mat_<ushort, 1> _matA(bh, bw, A);
				remap(src, dpart, _XY, _matA, interpolation, borderMode, borderValue);
			}
		}
	}

	return 0;
}

} // namespace fbc

#endif // FBC_CV_WARP_AFFINE_HPP_

测试代码test_warpAffine.cpp:

#include "test_warpAffine.hpp"
#include <assert.h>
#include <opencv2/opencv.hpp>
#include <core/mat.hpp>
#include <warpAffine.hpp>

int test_getAffineTransform()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}

	fbc::Point2f srcTri[3];
	fbc::Point2f dstTri[3];

	// Set your 3 points to calculate the  Affine Transform
	srcTri[0] = fbc::Point2f(0, 0);
	srcTri[1] = fbc::Point2f(matSrc.cols - 1, 0);
	srcTri[2] = fbc::Point2f(0, matSrc.rows - 1);

	dstTri[0] = fbc::Point2f(matSrc.cols*0.0, matSrc.rows*0.33);
	dstTri[1] = fbc::Point2f(matSrc.cols*0.85, matSrc.rows*0.25);
	dstTri[2] = fbc::Point2f(matSrc.cols*0.15, matSrc.rows*0.7);

	// Get the Affine Transform
	fbc::Mat_<double, 1> warp_mat(2, 3);
	int ret = fbc::getAffineTransform(srcTri, dstTri, warp_mat);
	assert(ret == 0);

	cv::Point2f srcTri_[3];
	cv::Point2f dstTri_[3];

	// Set your 3 points to calculate the  Affine Transform
	srcTri_[0] = cv::Point2f(0, 0);
	srcTri_[1] = cv::Point2f(matSrc.cols - 1, 0);
	srcTri_[2] = cv::Point2f(0, matSrc.rows - 1);

	dstTri_[0] = cv::Point2f(matSrc.cols*0.0, matSrc.rows*0.33);
	dstTri_[1] = cv::Point2f(matSrc.cols*0.85, matSrc.rows*0.25);
	dstTri_[2] = cv::Point2f(matSrc.cols*0.15, matSrc.rows*0.7);

	// Get the Affine Transform
	cv::Mat warp_mat_(2, 3, CV_64FC1);
	warp_mat_ = cv::getAffineTransform(srcTri_, dstTri_);

	assert(warp_mat.cols == warp_mat_.cols && warp_mat.rows == warp_mat_.rows);
	assert(warp_mat.step == warp_mat_.step);
	for (int y = 0; y < warp_mat.rows; y++) {
		const fbc::uchar* p = warp_mat.ptr(y);
		const uchar* p_ = warp_mat_.ptr(y);

		for (int x = 0; x < warp_mat.step; x++) {
			assert(p[x] == p_[x]);
		}
	}

	return 0;
}

int test_warpAffine_uchar()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}

	for (int interpolation = 0; interpolation < 5; interpolation++) {
		fbc::Point2f srcTri[3];
		fbc::Point2f dstTri[3];

		// Set your 3 points to calculate the  Affine Transform
		srcTri[0] = fbc::Point2f(0, 0);
		srcTri[1] = fbc::Point2f(matSrc.cols - 1, 0);
		srcTri[2] = fbc::Point2f(0, matSrc.rows - 1);

		dstTri[0] = fbc::Point2f(matSrc.cols*0.0, matSrc.rows*0.33);
		dstTri[1] = fbc::Point2f(matSrc.cols*0.85, matSrc.rows*0.25);
		dstTri[2] = fbc::Point2f(matSrc.cols*0.15, matSrc.rows*0.7);

		// Get the Affine Transform
		fbc::Mat_<double, 1> warp_mat(2, 3);
		int ret = fbc::getAffineTransform(srcTri, dstTri, warp_mat);
		assert(ret == 0);

		fbc::Mat_<uchar, 3> mat(matSrc.rows, matSrc.cols, matSrc.data);
		fbc::Mat_<uchar, 3> warp_dst;
		warp_dst.zeros(mat.rows, mat.cols);

		fbc::warpAffine(mat, warp_dst, warp_mat, interpolation);

		cv::Point2f srcTri_[3];
		cv::Point2f dstTri_[3];

		// Set your 3 points to calculate the  Affine Transform
		srcTri_[0] = cv::Point2f(0, 0);
		srcTri_[1] = cv::Point2f(matSrc.cols - 1, 0);
		srcTri_[2] = cv::Point2f(0, matSrc.rows - 1);

		dstTri_[0] = cv::Point2f(matSrc.cols*0.0, matSrc.rows*0.33);
		dstTri_[1] = cv::Point2f(matSrc.cols*0.85, matSrc.rows*0.25);
		dstTri_[2] = cv::Point2f(matSrc.cols*0.15, matSrc.rows*0.7);

		// Get the Affine Transform
		cv::Mat warp_mat_(2, 3, CV_64FC1);
		warp_mat_ = cv::getAffineTransform(srcTri_, dstTri_);

		// Set the dst image the same type and size as src
		cv::Mat warp_dst_ = cv::Mat::zeros(matSrc.rows, matSrc.cols, matSrc.type());
		cv::Mat mat_;
		matSrc.copyTo(mat_);

		// Apply the Affine Transform just found to the src image
		cv::warpAffine(mat_, warp_dst_, warp_mat_, warp_dst_.size(), interpolation);

		assert(warp_mat.cols == warp_mat_.cols && warp_mat.rows == warp_mat_.rows);
		assert(warp_mat.step == warp_mat_.step);
		for (int y = 0; y < warp_mat.rows; y++) {
			const fbc::uchar* p = warp_mat.ptr(y);
			const uchar* p_ = warp_mat_.ptr(y);

			for (int x = 0; x < warp_mat.step; x++) {
				assert(p[x] == p_[x]);
			}
		}
	}

	return 0;
}

int test_warpAffine_float()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}
	cv::cvtColor(matSrc, matSrc, CV_BGR2GRAY);
	matSrc.convertTo(matSrc, CV_32FC1);

	for (int interpolation = 0; interpolation < 5; interpolation++) {
		fbc::Point2f srcTri[3];
		fbc::Point2f dstTri[3];

		// Set your 3 points to calculate the  Affine Transform
		srcTri[0] = fbc::Point2f(0, 0);
		srcTri[1] = fbc::Point2f(matSrc.cols - 1, 0);
		srcTri[2] = fbc::Point2f(0, matSrc.rows - 1);

		dstTri[0] = fbc::Point2f(matSrc.cols*0.0, matSrc.rows*0.33);
		dstTri[1] = fbc::Point2f(matSrc.cols*0.85, matSrc.rows*0.25);
		dstTri[2] = fbc::Point2f(matSrc.cols*0.15, matSrc.rows*0.7);

		// Get the Affine Transform
		fbc::Mat_<double, 1> warp_mat(2, 3);
		int ret = fbc::getAffineTransform(srcTri, dstTri, warp_mat);
		assert(ret == 0);

		fbc::Mat_<float, 1> mat(matSrc.rows, matSrc.cols, matSrc.data);
		fbc::Mat_<float, 1> warp_dst;
		warp_dst.zeros(mat.rows, mat.cols);

		fbc::warpAffine(mat, warp_dst, warp_mat, interpolation);

		cv::Point2f srcTri_[3];
		cv::Point2f dstTri_[3];

		// Set your 3 points to calculate the  Affine Transform
		srcTri_[0] = cv::Point2f(0, 0);
		srcTri_[1] = cv::Point2f(matSrc.cols - 1, 0);
		srcTri_[2] = cv::Point2f(0, matSrc.rows - 1);

		dstTri_[0] = cv::Point2f(matSrc.cols*0.0, matSrc.rows*0.33);
		dstTri_[1] = cv::Point2f(matSrc.cols*0.85, matSrc.rows*0.25);
		dstTri_[2] = cv::Point2f(matSrc.cols*0.15, matSrc.rows*0.7);

		// Get the Affine Transform
		cv::Mat warp_mat_(2, 3, CV_64FC1);
		warp_mat_ = cv::getAffineTransform(srcTri_, dstTri_);

		// Set the dst image the same type and size as src
		cv::Mat warp_dst_ = cv::Mat::zeros(matSrc.rows, matSrc.cols, matSrc.type());
		cv::Mat mat_;
		matSrc.copyTo(mat_);

		// Apply the Affine Transform just found to the src image
		cv::warpAffine(mat_, warp_dst_, warp_mat_, warp_dst_.size(), interpolation);

		assert(warp_mat.cols == warp_mat_.cols && warp_mat.rows == warp_mat_.rows);
		assert(warp_mat.step == warp_mat_.step);
		for (int y = 0; y < warp_mat.rows; y++) {
			const fbc::uchar* p = warp_mat.ptr(y);
			const uchar* p_ = warp_mat_.ptr(y);

			for (int x = 0; x < warp_mat.step; x++) {
				assert(p[x] == p_[x]);
			}
		}
	}

	return 0;
}

GitHubhttps://github.com/fengbingchun/OpenCV_Test

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: OpenCV是一个非常强大的计算机视觉库,提供了许多函数,下面是OpenCV中的一些常用函数: 1. cv::imread() - 读取图像文件 2. cv::imshow() - 显示图像 3. cv::waitKey() - 等待键盘输入 4. cv::cvtColor() - 转换图像颜色空间 5. cv::resize() - 调整图像大小 6. cv::flip() - 翻转图像 7. cv::threshold() - 图像阈值处理 8. cv::canny() - 边缘检测 9. cv::HoughLines() - 直线检测 10. cv::findContours() - 查找轮廓 除此之外,OpenCV还提供了许多其他的函数,可以用于图像处理、特征提取、目标检测、机器学习等任务。 ### 回答2: OpenCV是一个开源的计算机视觉库,提供了丰富的图像处理和计算机视觉算法。下面列举一些常见的OpenCV函数: 1. imread函数:用于读取图像文件并返回一个表示图像的矩阵。 2. imshow函数:用于显示图像。 3. blur函数:用于对图像进行模糊处理。 4. resize函数:用于调整图像的尺寸。 5. cvtColor函数:用于转换图像的颜色空间。 6. threshold函数:用于图像的二值化处理。 7. findContours函数:用于寻找图像中的轮廓。 8. drawContours函数:用于在图像上绘制轮廓。 9. matchTemplate函数:用于在图像中寻找与模板匹配的区域。 10. HoughLines函数:用于检测图像中的直线。 11. HoughCircles函数:用于检测图像中的圆。 12. cornerHarris函数:用于检测图像中的角点。 13. warpAffine函数:用于对图像进行仿射变换。 14. warpPerspective函数:用于对图像进行透视变换。 15. filter2D函数:用于对图像进行卷积操作。 这仅仅是OpenCV中的一小部分函数OpenCV还提供了很多其他功能丰富的图像处理和计算机视觉函数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值