常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标。

仿射变换在:http://blog.csdn.net/xiaowei_cqu/article/details/7616044 这位大牛的博客中已经介绍的非常清楚。

关于仿射变换的详细介绍,请见上面链接的博客。

我这里主要介绍如何在已经知道原图像中若干特征点的坐标之后,计算这些特征点进行放射变换之后的坐标,然后做一些补充。

** 在原文中,很多功能函数都是使用的cvXXX,例如cv2DRotationMatrix( center, degree,1, &M);  这些都是老版本的函数,在opencv2以后,应该尽量的使用全新的函数,所以在我的代码中,都是使用的最新的函数,不再使用 cvMat, 而是全部使用 Mat 类型。 **

1. 特征点对应的新的坐标计算

假设已经有一个原图像中的特征点的坐标 CvPoint point;  那么计算这个point的对应的仿射变换之后在新的图像中的坐标位置,使用的方法如下函数:

// 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(const CvPoint &src, const CvPoint ¢er, double angle)
{
CvPoint dst;
int x = src.x - center.x;
int y = src.y - center.y; dst.x = cvRound(x * cos(angle) + y * sin(angle) + center.x);
dst.y = cvRound(-x * sin(angle) + y * cos(angle) + center.y);
return dst;
}

要特别注意的是,在对一个原图像中的像素的坐标进行计算仿射变换之后的坐标的时候,一定要按照仿射变换的基本原理,将原来的坐标减去仿射变换的旋转中心的坐标,这样仿射变换之后得到的坐标再加上仿射变换旋转中心坐标才是原坐标在新的仿射变换之后的图像中的正确坐标。

下面给出计算对应瞳孔坐标旋转之后的坐标位置的示例代码:

// AffineTransformation.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include "stdio.h"
#include "iostream" #include "opencv2/opencv.hpp" using namespace std;
using namespace cv; // 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(const CvPoint &src, const CvPoint ¢er, double angle);
Mat ImageRotate(Mat & src, const CvPoint &_center, double angle);
Mat ImageRotate2NewSize(Mat& src, const CvPoint &_center, double angle); int _tmain(int argc, _TCHAR* argv[])
{
string image_path = "D:/lena.jpg";
Mat img = imread(image_path);
cvtColor(img, img, CV_BGR2GRAY); Mat src;
img.copyTo(src); CvPoint Leye;
Leye.x = 265;
Leye.y = 265;
CvPoint Reye;
Reye.x = 328;
Reye.y = 265; // draw pupil
src.at<unsigned char>(Leye.y, Leye.x) = 255;
src.at<unsigned char>(Reye.y, Reye.x) = 255; imshow("src", src); //
CvPoint center;
center.x = img.cols / 2;
center.y = img.rows / 2; double angle = 15L; Mat dst = ImageRotate(img, center, angle); // 计算原特征点在旋转后图像中的对应的坐标
CvPoint l2 = getPointAffinedPos(Leye, center, angle * CV_PI / 180);
CvPoint r2 = getPointAffinedPos(Reye, center, angle * CV_PI / 180); // draw pupil
dst.at<unsigned char>(l2.y, l2.x) = 255;
dst.at<unsigned char>(r2.y, r2.x) = 255; //Mat dst = ImageRotate2NewSize(img, center, angle);
imshow("dst", dst); waitKey(0);
return 0;
} Mat ImageRotate(Mat & src, const CvPoint &_center, double angle)
{
CvPoint2D32f center;
center.x = float(_center.x);
center.y = float(_center.y); //计算二维旋转的仿射变换矩阵
Mat M = getRotationMatrix2D(center, angle, 1); // rotate
Mat dst;
warpAffine(src, dst, M, cvSize(src.cols, src.rows), CV_INTER_LINEAR);
return dst;
} // 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(const CvPoint &src, const CvPoint ¢er, double angle)
{
CvPoint dst;
int x = src.x - center.x;
int y = src.y - center.y; dst.x = cvRound(x * cos(angle) + y * sin(angle) + center.x);
dst.y = cvRound(-x * sin(angle) + y * cos(angle) + center.y);
return dst;
}

这里,我们先通过手工找到瞳孔坐标,然后计算在图像旋转之后瞳孔的坐标。

运行结果如图:

原图像

旋转之后的图像:

2. 旋转中心对于旋转的影响

然后我们看看仿射变换旋转点的选择对于旋转之后的图像的影响,一般情况下,我们选择图像的中心点作为仿射变换的旋转中心,获得的旋转之后的图像与原图像大小一样。

计算代码:

int _tmain(int argc, _TCHAR* argv[])
{
string image_path = "D:/lena.jpg";
Mat img = imread(image_path);
cvtColor(img, img, CV_BGR2GRAY); Mat src;
img.copyTo(src); CvPoint Leye;
Leye.x = 265;
Leye.y = 265;
CvPoint Reye;
Reye.x = 328;
Reye.y = 265; // draw pupil
src.at<unsigned char>(Leye.y, Leye.x) = 255;
src.at<unsigned char>(Reye.y, Reye.x) = 255; imshow("src", src); //
/*CvPoint center;
center.x = img.cols / 2;
center.y = img.rows / 2;*/
CvPoint center;
center.x = 0;
center.y = 0; double angle = 15L; Mat dst = ImageRotate(img, center, angle); // 计算原特征点在旋转后图像中的对应的坐标
CvPoint l2 = getPointAffinedPos(Leye, center, angle * CV_PI / 180);
CvPoint r2 = getPointAffinedPos(Reye, center, angle * CV_PI / 180); // draw pupil
dst.at<unsigned char>(l2.y, l2.x) = 255;
dst.at<unsigned char>(r2.y, r2.x) = 255; //Mat dst = ImageRotate2NewSize(img, center, angle);
imshow("dst", dst); waitKey(0);
return 0;
}

这里绕着(0,0)点进行旋转,旋转之后的图像:

绕着左下角旋转:

	CvPoint center;
center.x = 0;
center.y = img.rows;

旋转之后的图像:

3. 缩放因子对于旋转图像的影响

上面我们的代码都没有添加缩放信息,现在对上面的代码进行稍加修改,添加缩放参数,然后看一下如何计算对应的新的坐标。

#include "stdafx.h"
#include "stdio.h"
#include "iostream" #include "opencv2/opencv.hpp" using namespace std;
using namespace cv; // 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(const CvPoint &src, const CvPoint ¢er, double angle, double scale);
Mat ImageRotate(Mat & src, const CvPoint &_center, double angle, double scale);
Mat ImageRotate2NewSize(Mat& src, const CvPoint &_center, double angle, double scale); int _tmain(int argc, _TCHAR* argv[])
{
string image_path = "D:/lena.jpg";
Mat img = imread(image_path);
cvtColor(img, img, CV_BGR2GRAY);
double scale = 0.5; Mat src;
img.copyTo(src); CvPoint Leye;
Leye.x = 265;
Leye.y = 265;
CvPoint Reye;
Reye.x = 328;
Reye.y = 265; // draw pupil
src.at<unsigned char>(Leye.y, Leye.x) = 255;
src.at<unsigned char>(Reye.y, Reye.x) = 255; imshow("src", src); //
CvPoint center;
center.x = img.cols / 2;
center.y = img.rows / 2; double angle = 15L; Mat dst = ImageRotate(img, center, angle, scale); // 计算原特征点在旋转后图像中的对应的坐标
CvPoint l2 = getPointAffinedPos(Leye, center, angle * CV_PI / 180, scale);
CvPoint r2 = getPointAffinedPos(Reye, center, angle * CV_PI / 180, scale); // draw pupil
dst.at<unsigned char>(l2.y, l2.x) = 255;
dst.at<unsigned char>(r2.y, r2.x) = 255; //Mat dst = ImageRotate2NewSize(img, center, angle);
imshow("dst", dst); waitKey(0);
return 0;
} Mat ImageRotate(Mat & src, const CvPoint &_center, double angle, double scale)
{
CvPoint2D32f center;
center.x = float(_center.x);
center.y = float(_center.y); //计算二维旋转的仿射变换矩阵
Mat M = getRotationMatrix2D(center, angle, scale); // rotate
Mat dst;
warpAffine(src, dst, M, cvSize(src.cols, src.rows), CV_INTER_LINEAR);
return dst;
} // 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(const CvPoint &src, const CvPoint ¢er, double angle, double scale)
{
CvPoint dst;
int x = src.x - center.x;
int y = src.y - center.y; dst.x = cvRound(x * cos(angle) * scale + y * sin(angle) * scale + center.x);
dst.y = cvRound(-x * sin(angle) * scale + y * cos(angle) * scale + center.y);
return dst;
}

当缩放尺度为0.5的时候,程序的运行结果如图:

4.  根据旋转与缩放尺度获得与原始图像大小不同的图像大小(新的合适的大小)

上面的计算中,一直都是放射变换之后计算得到的图像和原始图像一样大,但是因为旋转、缩放之后图像可能会变大或者变小,我们再次对上面的代码进行修改,这样在获得仿射变换之后的图像前,需要重新计算生成的图像的大小。

计算方法:

	double angle2 = angle * CV_PI / 180;
int width = src.cols;
int height = src.rows; double alpha = cos(angle2) * scale;
double beta = sin(angle2) * scale; int new_width = (int)(width * fabs(alpha) + height * fabs(beta));
int new_height = (int)(width * fabs(beta) + height * fabs(alpha));

另外,因为我们的图像旋转是按照原图像的中心,所以当获取到图像的仿射变换矩阵之后,我们需要根据新生成的图像的大小,给仿射变换矩阵添加平移信息。

或者可以这么说,我们新计算得到的图像的大小,让原始图像绕着新的图像大小的中心进行旋转。

	//计算二维旋转的仿射变换矩阵
Mat M = getRotationMatrix2D(center, angle, scale); // 给计算得到的旋转矩阵添加平移 M.at<double>(0, 2) += (int)((new_width - width )/2);
M.at<double>(1, 2) += (int)((new_height - height )/2);

然后另外需要注意的是,如果你在原始图像中有一些特征点的坐标,这些特征点的坐标映射到新的图像上的时候,需要在以前的方法的基础上增加平移信息。

// 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(Mat & src, Mat & dst, const CvPoint &src_p, const CvPoint ¢er, double angle, double scale)
{
double alpha = cos(angle) * scale;
double beta = sin(angle) * scale; int width = src.cols;
int height = src.rows; CvPoint dst_p;
int x = src_p.x - center.x;
int y = src_p.y - center.y; dst_p.x = cvRound(x * alpha + y * beta + center.x);
dst_p.y = cvRound(-x * beta + y * alpha + center.y); int new_width = dst.cols;
int new_height = dst.rows; int movx = (int)((new_width - width)/2);
int movy = (int)((new_height - height)/2); dst_p.x += movx;
dst_p.y += movy; return dst_p;
}

我们仿射变换函数代码:

Mat ImageRotate2NewSize(Mat& src, const CvPoint &_center, double angle, double scale)
{
double angle2 = angle * CV_PI / 180;
int width = src.cols;
int height = src.rows; double alpha = cos(angle2) * scale;
double beta = sin(angle2) * scale; int new_width = (int)(width * fabs(alpha) + height * fabs(beta));
int new_height = (int)(width * fabs(beta) + height * fabs(alpha)); CvPoint2D32f center;
center.x = float(width / 2);
center.y = float(height / 2);
//计算二维旋转的仿射变换矩阵
Mat M = getRotationMatrix2D(center, angle, scale); // 给计算得到的旋转矩阵添加平移 M.at<double>(0, 2) += (int)((new_width - width )/2);
M.at<double>(1, 2) += (int)((new_height - height )/2); // rotate
Mat dst;
warpAffine(src, dst, M, cvSize(new_width, new_height), CV_INTER_LINEAR);
return dst;
}

主函数:

int _tmain(int argc, _TCHAR* argv[])
{
string image_path = "D:/lena.jpg";
Mat img = imread(image_path);
cvtColor(img, img, CV_BGR2GRAY);
double scale = 0.5; Mat src;
img.copyTo(src); CvPoint Leye;
Leye.x = 265;
Leye.y = 265;
CvPoint Reye;
Reye.x = 328;
Reye.y = 265; // draw pupil
src.at<unsigned char>(Leye.y, Leye.x) = 255;
src.at<unsigned char>(Reye.y, Reye.x) = 255; imshow("src", src); //
CvPoint center;
center.x = img.cols / 2;
center.y = img.rows / 2; double angle = 15L; //Mat dst = ImageRotate(img, center, angle, scale);
Mat dst = ImageRotate2NewSize(img, center, angle, scale); // 计算原特征点在旋转后图像中的对应的坐标
CvPoint l2 = getPointAffinedPos(src, dst, Leye, center, angle * CV_PI / 180, scale);
CvPoint r2 = getPointAffinedPos(src, dst, Reye, center, angle * CV_PI / 180, scale); // draw pupil
dst.at<unsigned char>(l2.y, l2.x) = 255;
dst.at<unsigned char>(r2.y, r2.x) = 255; imshow("dst", dst); waitKey(0);
return 0;
}

仿射变换结果以及瞳孔重新坐标计算结果:

5. 根据三个点进行仿射变换


根据给点的三个点,由这三个点之前的坐标以及变换之后的坐标,对原图像进行仿射变换,不过需要事先知道三个点仿射变换的坐标位置。
int _tmain(int argc, _TCHAR* argv[])
{
string image_path = "D:/lena.jpg";
Mat img = imread(image_path); Point2f src_points[3];
src_points[0] = Point2f(100, 100);
src_points[1] = Point2f(400, 100);
src_points[2] = Point2f(250, 300); Point2f dst_points[3];
dst_points[0] = Point2f(100, 100);
dst_points[1] = Point2f(400, 300);
dst_points[2] = Point2f(100, 300); Mat M1 = getAffineTransform(src_points, dst_points); Mat dst;
warpAffine(img, dst, M1, cvSize(img.cols, img.rows), INTER_LINEAR); imshow("dst", dst); //cvtColor(img, img, CV_BGR2GRAY);
//double scale = 1.5; //Mat src;
//img.copyTo(src); //CvPoint Leye;
//Leye.x = 265;
//Leye.y = 265;
//CvPoint Reye;
//Reye.x = 328;
//Reye.y = 265; //// draw pupil
//src.at<unsigned char>(Leye.y, Leye.x) = 255;
//src.at<unsigned char>(Reye.y, Reye.x) = 255; //imshow("src", src); ////
//CvPoint center;
//center.x = img.cols / 2;
//center.y = img.rows / 2; //double angle = 15L; ////Mat dst = ImageRotate(img, center, angle, scale);
//Mat dst = ImageRotate2NewSize(img, center, angle, scale); //// 计算原特征点在旋转后图像中的对应的坐标
//CvPoint l2 = getPointAffinedPos(src, dst, Leye, center, angle * CV_PI / 180, scale);
//CvPoint r2 = getPointAffinedPos(src, dst, Reye, center, angle * CV_PI / 180, scale); //// draw pupil
//dst.at<unsigned char>(l2.y, l2.x) = 255;
//dst.at<unsigned char>(r2.y, r2.x) = 255;
//imshow("dst", dst); waitKey(0);
return 0;
}

结果:





opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移的更多相关文章

  1. 图像配准建立仿射变换模型并用RANSAC算法评估

    当初选方向时就由于从小几何就不好.缺乏空间想像能力才没有选择摄影測量方向而是选择了GIS. 昨天同学找我帮他做图像匹配.这我哪里懂啊,无奈我是一个别人有求于我,总是不好意思开口拒绝的人.于是乎就看着他 ...

  2. opencv 用户文档 错误更正 仿射变换

    今天在看opencv官方给出的仿射变换计算仿射变换矩阵的文档的时候,发现官方文档中有个很明显的错误,再次给大家提个醒. 官方文档连接: http://opencv.willowgarage.com/d ...

  3. 【OpenCV学习】计算两幅图像的重叠区域

    问题描述:已知两幅图像Image1和Image2,计算出两幅图像的重叠区域,并在Image1和Image2标识出重叠区域. 算法思想: 若两幅图像存在重叠区域,则进行图像匹配后,会得到一张完整的全景图 ...

  4. OpenCV 学习(计算图像的直方图)

    OpenCV 计算图像的直方图 计算图像的直方图是图像处理领域一个非经常见的基本操作. OpenCV 中提供了 calcHist 函数来计算图像直方图.只是这个函数说实话挺难用的,研究了好久才掌握了些 ...

  5. Opencv中integral计算积分图

    Paul Viola和Michael Jones在2001年首次将积分图应用在图像特征提取上,在他们的论文"Rapid Object Detection using a Boosted Ca ...

  6. 图像特征提取三大法宝:HOG特征,LBP特征,Haar特征(转载)

    (一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...

  7. 图像特征提取三大法宝:HOG特征,LBP特征,Haar特征

    (一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...

  8. opencv使用convexityDefects计算轮廓凸缺陷

    引自:http://www.xuebuyuan.com/1684976.html http://blog.csdn.net/lichengyu/article/details/38392473 htt ...

  9. [CV笔记]图像特征提取三大法宝:HOG特征,LBP特征,Haar特征

    (一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...

随机推荐

  1. python成长笔记

    正则表达式 1.    择一匹配:管道符号(|),表示“从多个模式中选择其一”.例:at|home à at.home 2.    点号匹配除了换行符以外的任何字符 3.    边界匹配:\b匹配一个 ...

  2. 图像的影像地图超链接,<map>标签浅谈

    在HTML中还可以把图片划分成多个热点区域,每一个热点域链接到不同网页的资源.这种效果的实质是把一幅图片划分为不同的热点区域,再让不同的区域进行超链接.这就是影像地图.要完成地图区域超链接要用到三种标 ...

  3. tomcat安全配置之禁用Directory Listing

    什么是Directory Listing?通俗点讲,就是在webapp的目录下如果没有放置index.html或者类似的文件,如果从IE或者其它浏览器文章这个路径时,会惊喜的发现这个目录下的文件列表被 ...

  4. unity3d Find()使用

    1. Hierarchy 创建对象如两个cube时,未修改名称,名称都为cube时. js添加至Camera: private var cubeObj : GameObject; //private ...

  5. JAVA冒泡排序/JAVA冒泡排序再找出给定数据中的最大值最小值/JAVA数组排序

    //数组中排序    int in[] = {1,6,5,8,9};    Arrays.sort(in);    for(int i=0;i<in.length;i++){       Sys ...

  6. NYOJ-744蚂蚁的难题(一)

    这个题都说是水题,楞是没做出来,看了好多题解,感觉这个规律没看懂,后来在讨论区看到了一个题解,感觉有点懂了,写一下自己的理解 首先要明白异或的意思,简单一句话: 同0异1,既然这样,让求区间a,b 中 ...

  7. 一个好用的Python备份mysql的脚本

    前几天打算用Python写一个mysql脚本,上Google看了下老外写的,写的挺好的,原地址在http://tecadmin.net/python-script-for-mysql-database ...

  8. Linux基础知识之 系统启动流程

    [1]Linux启动的几个主要阶段 启动流程示意图

  9. ASP.NET-FineUI开发实践-4

    最近实在没时间研究东西,FineUI一直也没进一步实践,但是还是很想学点东西,所以找了个课题研究了下,在论坛里看见了又下角的提醒,自己想了想做了一个,我不是大神,接触EXTJS很少,就是用到哪看哪,没 ...

  10. AngularJs学习html转义

    MainApp.directive('ngHtml', function () { function watch(scope, el, watchExp){ scope.$watch(watchExp ...