opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移
常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标。
仿射变换在: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 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移的更多相关文章
- 图像配准建立仿射变换模型并用RANSAC算法评估
当初选方向时就由于从小几何就不好.缺乏空间想像能力才没有选择摄影測量方向而是选择了GIS. 昨天同学找我帮他做图像匹配.这我哪里懂啊,无奈我是一个别人有求于我,总是不好意思开口拒绝的人.于是乎就看着他 ...
- opencv 用户文档 错误更正 仿射变换
今天在看opencv官方给出的仿射变换计算仿射变换矩阵的文档的时候,发现官方文档中有个很明显的错误,再次给大家提个醒. 官方文档连接: http://opencv.willowgarage.com/d ...
- 【OpenCV学习】计算两幅图像的重叠区域
问题描述:已知两幅图像Image1和Image2,计算出两幅图像的重叠区域,并在Image1和Image2标识出重叠区域. 算法思想: 若两幅图像存在重叠区域,则进行图像匹配后,会得到一张完整的全景图 ...
- OpenCV 学习(计算图像的直方图)
OpenCV 计算图像的直方图 计算图像的直方图是图像处理领域一个非经常见的基本操作. OpenCV 中提供了 calcHist 函数来计算图像直方图.只是这个函数说实话挺难用的,研究了好久才掌握了些 ...
- Opencv中integral计算积分图
Paul Viola和Michael Jones在2001年首次将积分图应用在图像特征提取上,在他们的论文"Rapid Object Detection using a Boosted Ca ...
- 图像特征提取三大法宝:HOG特征,LBP特征,Haar特征(转载)
(一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...
- 图像特征提取三大法宝:HOG特征,LBP特征,Haar特征
(一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...
- opencv使用convexityDefects计算轮廓凸缺陷
引自:http://www.xuebuyuan.com/1684976.html http://blog.csdn.net/lichengyu/article/details/38392473 htt ...
- [CV笔记]图像特征提取三大法宝:HOG特征,LBP特征,Haar特征
(一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...
随机推荐
- android ViewPager具体解释
Viewpager 在android界面布局中属于经常使用类型 ,它能够做导航,页面菜单,进入软件是的欢迎界面 等等.比方今最流行的几款手机软件 ,QQ,微信,微博 等 ,其主界面 都用到了View ...
- LeetCode——Valid Palindrome
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...
- 【网络流#2】hdu 1533 - 最小费用最大流模板题
最小费用最大流,即MCMF(Minimum Cost Maximum Flow)问题 嗯~第一次写费用流题... 这道就是费用流的模板题,找不到更裸的题了 建图:每个m(Man)作为源点,每个H(Ho ...
- 手势识别 GestureDetector ScaleGestureDetector
识别器GestureDetector基本介绍 当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等.一般情况下,我们可以通过View或Activity的onTouch ...
- 小学生之使用Mybatis反向生成dao,entity,xml
本小学生刚进公司的时候,就一顿装逼,不管别人问我啥我都会说:"会"!毕竟在公司吗,什么都要装,不要别人看出你的底细.不过有一天,听说用Mybatis可以反向生成dao(第一次听说) ...
- 小学生之SpringMVC
1. Springmvc是什么? Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基 ...
- AppDomain.CurrentDomain.GetAssemblies()
AppDomain.CurrentDomain.GetAssemblies() ,获取已加载到此应用程序域的执行上下文中的程序集 解释地址 从微软的解释也可以得知,这个方法只能获取已经加载到此应用程序 ...
- Nginx配置文件nginx.conf详解(转)
#定义Nginx运行的用户和用户组 user www www; #nginx进程数,建议设置为等于CPU总核心数. worker_processes 8; #全局错误日志定义类型,[ debug | ...
- Arcgis Android 基本概念 - 浅谈
MapView MapView 是 Android 中 ViewGroup的子类,也是 ArcGIS Runtime SDK for Android 中的地图容器,与很多 ArcGIS API ...
- SQL数据库中把一个表中的数据复制到另一个表中
1.如果是整个表复制表达如下: insert into table1 select * from table2 2.如果是有选择性的复制数据表达如下: insert into table1(colu ...