Eigen入门 使用eigen库实现图像旋转变换

Eigen是一个基础的矩阵运算库,支持的矩阵运算操作十分便捷。相比opencv简洁许多,值得一试。

https://blog.csdn.net/u012541187/article/details/53420432 这篇博客介绍了eigen的基本函数用法。

/*
旋转变换的实现并不难,难的地方在于如何减少循环,加快计算速度
opencv的仿射变换是一个现成的实现。
这份代码的速度与opencv大致相当,而且还有许多值得优化的点
*/

#include "stdafx.h"
#include 
#include 
#include 

using namespace Eigen;
using namespace std;


#define MAT Matrix
#define VEC Matrix
#define MAT2d Matrix
#define MATONES(m,n) MAT::Ones(m,n)
#define MATZEROS(m,n) MAT::Zero(m,n)
#define VECONES(n) VEC::Ones(n)
#define VECZEROS(n) VEC::Zero(n)
#define PI 3.141592653589793238462643;   // pi 的精度会影响结果的精度
const double border = 1e-2;     // 由于数值误差,输出图上有些点经过反向变换可能刚刚好在边界上,但是算出来的坐标却在边界外
								// 旋转到边界上的点会被旋转到边界外,使用这个值来允许有一定的误差

#define printMat(M) cout << #M <<"= "<(section_index_l.data(), ROWS, COLS));
	printMat(Eigen::Map(section_index_r.data(), ROWS, COLS));
	printMat(Eigen::Map(section_index_t.data(), ROWS, COLS));
	printMat(Eigen::Map(yi.data(), ROWS, COLS));
	printMat(Eigen::Map(section_index_b.data(), ROWS, COLS));
	VEC A(SZ), B(SZ), C(SZ), D(SZ);     //A B C D 表示四个格点(上左 上右 左下 右下)的值
	for (int i = 0; i < SZ; i++) {       //唯一的一个for循环,因为eigen3.3 还不支持向量索引,据说3.4会支持了。
		xi_in_section(i) = xi(i) - section_index_l(i);
		yi_in_section(i) = yi(i) - section_index_t(i);
		if (xi(i) < 0-border || xi(i) > (COLS - 1)+ border || yi(i) < 0- border || yi(i) > (ROWS - 1)+ border) { //越界点 将其四角设置为0
			A(i) = 0;
			B(i) = 0;
			C(i) = 0;
			D(i) = 0;
		}
		else {  // 非越界的点 正常给其四角赋值  //空间换时间的做法
			if (xi(i) < 0)section_index_l(i) = 0;
			if (xi(i) > COLS-1)section_index_r(i) = COLS-1;
			if (yi(i) < 0)section_index_t(i) = 0;
			if (yi(i) > ROWS -1)section_index_b(i) = ROWS -1;
			A(i) = Z(section_index_t(i), section_index_l(i));
			B(i) = Z(section_index_t(i), section_index_r(i));
			C(i) = Z(section_index_b(i), section_index_l(i));
			D(i) = Z(section_index_b(i), section_index_r(i));
		}
	}
	VEC c_d = C.cwiseProduct(VECONES(SZ) - xi_in_section) + D.cwiseProduct(xi_in_section);   // 这三行就是双线性插值了
	VEC a_b = A.cwiseProduct(VECONES(SZ) - xi_in_section) + B.cwiseProduct(xi_in_section);
	zi = a_b.cwiseProduct(VECONES(SZ) - yi_in_section) + c_d.cwiseProduct(yi_in_section);
}

MAT2d getRotationMatrix2d(double angle) {   //逆时针旋转矩阵
	MAT2d rotation_matrix;
	angle = (angle /180.0)*PI; // angle in degree -> angle in radian
	rotation_matrix << cos(angle), -sin(angle), sin(angle), cos(angle);
	return rotation_matrix;
}

MAT2d getRotationMatrix2d_inv(double angle) {  //顺时针旋转矩阵
	MAT2d rotation_matrix;
	angle = (angle / 180.0)*PI; // angle in degree -> angle in radian
	rotation_matrix << cos(angle), sin(angle), -sin(angle), cos(angle);
	return rotation_matrix;
}

void imrotate(MAT &image, MAT &dst_image, Vector2d center,double angle) {
	int ROWS = image.rows();
	int COLS = image.cols();
	int SZ = image.size();
	MAT X, Y;
	VEC x_in_row, y_in_row, image_in_row;
	VEC x_r = VEC::LinSpaced(COLS, 0, COLS-1) - VECONES(COLS)*center(0);   //使用角标当点的坐标
	VEC y_r = VEC::LinSpaced(ROWS, 0, ROWS-1) - VECONES(ROWS)*center(1);
	meshgrid(X, Y, x_r, y_r); // 得到矩阵坐标
	printMat(X);
	printMat(Y);
	printMat(x_r);
	printMat(y_r);
	x_in_row = Eigen::Map(X.data(), X.size()); // 坐标矩阵压缩成行向量
	y_in_row = Eigen::Map(Y.data(), Y.size());
	MAT coords_in_row(2, SZ), coords_in_row_mapped_to_src(2, SZ);
	coords_in_row.row(0) = x_in_row;   // x y 两个行向量拼接起来便于使用矩阵乘法
	coords_in_row.row(1) = y_in_row;
	coords_in_row_mapped_to_src = getRotationMatrix2d_inv(angle)*coords_in_row;   //逆变换 将输出空间中点的坐标反向旋转到输入空间
	VEC dst_image_in_row(SZ),x_mapped_to_src(SZ), y_mapped_to_src(SZ);
	x_mapped_to_src = coords_in_row_mapped_to_src.row(0);   // 从中将两行取出来
	y_mapped_to_src = coords_in_row_mapped_to_src.row(1);
	printMat(Eigen::Map(x_mapped_to_src.data(), ROWS, COLS));
	printMat(Eigen::Map(y_mapped_to_src.data(), ROWS, COLS));
	interp2d(X, Y, image, x_mapped_to_src, y_mapped_to_src, dst_image_in_row);  // 将输出点在输入空间中的坐标传入插值函数,计算每个点的值
	dst_image = Eigen::Map(dst_image_in_row.data(), ROWS, COLS);   //将输出向量转型成矩阵
}



// 下面都是测试函数
void test_meshgrid() {
	MAT X, Y;
	VEC X_r, Y_r;
	int C = 12;
	X_r.setLinSpaced(12, 1, 3);
	Y_r.setLinSpaced(12, 1, 3);
	meshgrid(X, Y, X_r, Y_r);
	printMat(X);
	printMat(Y);
}

void test_interp() {
	MAT X(3,3), Y(3,3),Z(3,3);
	X << 1, 3,5, 1, 3,5,1,3,5;
	Y << 1, 1,1, 3, 3,3,5,5,5;
	Z << 0, 0, 0, 0, 1,0,0,0,2;
	printMat(X);
	printMat(Y);
	printMat(Z);
	MAT xi, yi, zi;
	VEC xi_row, yi_row, zi_row;
	VEC x_r = VEC::LinSpaced(12, 1, 5);
	VEC y_r = VEC::LinSpaced(12, 1, 5);
	meshgrid(xi, yi, x_r, y_r);
	xi_row = Eigen::Map(xi.data(),xi.size());
	yi_row = Eigen::Map(yi.data(), yi.size());
	printMat(xi);
	printMat(yi);
	interp2d(X, Y, Z, xi_row, yi_row, zi_row);
	zi = Eigen::Map(zi_row.data(), xi.rows(), xi.cols());
	printMat(zi);
}



void test_basic() {
	MAT x = MATONES(2, 2);
	printMat(x);
	x = MATZEROS(3, 4);
	printMat(x);
}

void test_rot() {
	MAT r = getRotationMatrix2d(30);
	printMat(r);
	Vector2d x(1, 0);
	printMat(x);
	printMat(r*x);
	printMat(r*r*x);
	printMat(r*r*r*x);
}

void test_imrotate() {
	int C = 128;
	MAT r = MATZEROS(C,C);
	for (int i = 20; i < 70; i++) {
		for (int j = 20; j < 70; j++) {
			r(i, j) = 1;
		}
	}
	printMat(r);
	MAT x;
	for (int i = 0; i < 360; i+=10) {
		cout << "angle " << i << endl;
		imrotate(r, x, Vector2d(float(C - 1) / 2.0, float(C - 1) / 2.0), i);
		printMat(r);
		printMat(x);
		dumpEigenMat(x, "F:\\matlabCodes\\testInterp\\imrotate_dump"+to_string(int(i)));
	}
	

}


void test() {
	//test_meshgrid();
	//test_interp();
	//test_rot();
	test_imrotate();
}

与matlab的imrotate做比较

close all
clear all
file = 'F:\matlabCodes\testInterp\imrotate_dump0';
fid = fopen(file,'rb');
data = fread(fid,128*128,'double');
fclose(fid);
data = transpose(reshape(data,128,128));
figure(1)
h = pcolor(data);
axis equal
set(h, 'LineStyle','none');
title('原图')
colorbar

file = 'F:\matlabCodes\testInterp\imrotate_dump10';
fid = fopen(file,'rb');
data = fread(fid,128*128,'double');
fclose(fid);
data = transpose(reshape(data,128,128));
figure(2)
h = pcolor(data);
axis equal
set(h, 'LineStyle','none');
title('eigen 实现10度旋转')
colorbar

d = zeros(128,128);
d(21:70,21:70) = 1;
[x,y] = meshgrid(1:128,1:128);
d_t = imrotate(d,-10,'bilinear','crop');
figure(3)
h = pcolor(d_t);
axis equal
set(h, 'LineStyle','none');
title('matlab 实现10度旋转')
colorbar


figure(4)
for i=0:10:30%350
    file = ['F:\matlabCodes\testInterp\imrotate_dump' num2str(i)];
    fid = fopen(file,'rb');
    data = fread(fid,128*128,'double');
    fclose(fid);
    data = transpose(reshape(data,128,128));
    
    d_t = imrotate(d,-i,'bilinear','crop');
    h = surf(x,y,data-d_t);
    set(h, 'LineStyle','none');
    title('残差 eigen-matlab')
    colorbar
    drawnow;
end

figure(5)
for i=0:10:30%350
    file = ['F:\matlabCodes\testInterp\imrotate_dump' num2str(i)];
    fid = fopen(file,'rb');
    data = fread(fid,128*128,'double');
    fclose(fid);
    data = transpose(reshape(data,128,128));
    
    d_t = imrotate(d,-i,'bilinear','crop');
    h = pcolor(x,y,data-d_t);
    set(h, 'LineStyle','none');
    title('残差 eigen-matlab')
    colorbar
    drawnow;
end

结果:

Eigen入门 使用eigen库实现图像旋转变换_第1张图片

Eigen入门 使用eigen库实现图像旋转变换_第2张图片Eigen入门 使用eigen库实现图像旋转变换_第3张图片Eigen入门 使用eigen库实现图像旋转变换_第4张图片

误差在1e-14量级,这已经快到双精度浮点数的极限了。

你可能感兴趣的:(c++,线性代数,算法)