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: [email protected]

#ifndef FBC_CV_WARP_AFFINE_HPP_
#define FBC_CV_WARP_AFFINE_HPP_

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

#include 
#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_& dst);

// Applies an affine transformation to an image
// The function cannot operate in - place
// support type: uchar/float
template
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_ 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 _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(M[0] * x*AB_SCALE);
		bdelta[x] = saturate_cast(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_ _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((M[1] * (y + y1) + M[2])*AB_SCALE) + round_delta;
				int Y0 = saturate_cast((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(X);
						xy[x1 * 2 + 1] = saturate_cast(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(X >> INTER_BITS);
						xy[x1 * 2 + 1] = saturate_cast(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_(), interpolation, borderMode, borderValue);
			} else {
				Mat_ _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 
#include 
#include 
#include 

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_ 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_ warp_mat(2, 3);
		int ret = fbc::getAffineTransform(srcTri, dstTri, warp_mat);
		assert(ret == 0);

		fbc::Mat_ mat(matSrc.rows, matSrc.cols, matSrc.data);
		fbc::Mat_ 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_ warp_mat(2, 3);
		int ret = fbc::getAffineTransform(srcTri, dstTri, warp_mat);
		assert(ret == 0);

		fbc::Mat_ mat(matSrc.rows, matSrc.cols, matSrc.data);
		fbc::Mat_ 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;
}

GitHub: https://github.com/fengbingchun/OpenCV_Test

你可能感兴趣的:(OpenCV)