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

本文主要是介绍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 pupilsrc.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 pupildst.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);// rotateMat 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 pupilsrc.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 pupildst.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 pupilsrc.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 pupildst.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);// rotateMat 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);// rotateMat 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 pupilsrc.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 pupildst.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 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1073294

相关文章

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

计算绕原点旋转某角度后的点的坐标

问题: A点(x, y)按顺时针旋转 theta 角度后点的坐标为A1点(x1,y1)  ,求x1 y1坐标用(x,y)和 theta 来表示 方法一: 设 OA 向量和x轴的角度为 alpha , 那么顺时针转过 theta后 ,OA1 向量和x轴的角度为 (alpha - theta) 。 使用圆的参数方程来表示点坐标。A的坐标可以表示为: \[\left\{ {\begin{ar

vcpkg安装opencv中的特殊问题记录(无法找到opencv_corexd.dll)

我是按照网上的vcpkg安装opencv方法进行的(比如这篇:从0开始在visual studio上安装opencv(超详细,针对小白)),但是中间出现了一些别人没有遇到的问题,虽然原因没有找到,但是本人给出一些暂时的解决办法: 问题1: 我在安装库命令行使用的是 .\vcpkg.exe install opencv 我的电脑是x64,vcpkg在这条命令后默认下载的也是opencv2:x6

KLayout ------ 旋转物体90度并做平移

KLayout ------ 旋转创建的物体 正文 正文 前段时间,有个小伙伴留言问我,KLayout 中如何旋转自己创建的物体,这里特来说明一下。 import pyapoly = pya.DPolygon([pya.DPoint(0, 0), pya.DPoint(0, 5), pya

brew install opencv@2 时报错 Error: Can't create update lock in /usr/local/var/homebrew/locks!

解决方案,报错里已经说明了: 我的解决方案: sudo chown -R "$USER":admin /usr/local   stackoverflow上的答案 I was able to solve the problem by using chown on the folder: sudo chown -R "$USER":admin /usr/local Also you'

【云计算 复习】第1节 云计算概述和 GFS + chunk

一、云计算概述 1.云计算的商业模式 (1)软件即服务(SaaS) 有些景区给游客提供烧烤场地,游客需要自己挖坑或者砌烧烤台,然后买肉、串串、烧烤。 (2)平台即服务(PaaS) 有些景区给游客提供烧烤场地,同时搭建好烧烤台,游客只需要自己带食材和调料、串串、烧烤。 (3)基础设施即服务(IaaS) 有些景区给游客提供烧烤场地,同时搭建好烧烤台,还有专门的厨师来烧烤,用户不需要关心前面的所有

剑指offer(C++)--左旋转字符串

题目 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它! class Solution {public:string LeftRotateStri

AI学习指南机器学习篇-朴素贝叶斯处理连续特征和离散特征

AI学习指南机器学习篇-朴素贝叶斯处理连续特征和离散特征 在机器学习领域,朴素贝叶斯是一种常用的分类算法,它的简单性和高效性使得它在实际应用中得到了广泛的应用。然而,在使用朴素贝叶斯算法进行分类时,我们通常会面临一个重要的问题,就是如何处理连续特征和离散特征。因为朴素贝叶斯算法基于特征的条件独立性假设,所以对于不同类型的特征,我们需要采取不同的处理方式。 在本篇博客中,我们将探讨如何有效地处理

陀螺仪LSM6DSV16X与AI集成(8)----MotionFX库解析空间坐标

陀螺仪LSM6DSV16X与AI集成.8--MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启CRC串口设置开启X-CUBE-MEMS1设置加速度和角速度量程速率选择设置FIFO速率设置FIFO时间戳批处理速率配置过滤链初始化定义MotionFX文件卡尔曼滤波算法主程序执行流程lsm6dsv16x_motion_fx_determin欧拉角简介演示 概述 本文将探讨

《学习OpenCV》课后习题解答7

题目:(P105) 创建一个结构,结构中包含一个整数,一个CvPoint和一个 CvRect;称结构体为“my_struct”。 a. 写两个函数:void Write_my_strct(CvFileStorage* fs, const char * name, my_struct* ms) 和 void read_my_struct(CvFileStorage* fs, CvFileNode