当前位置: 首页 > news >正文

OpenCV:图像几何变换(镜像,错切,缩放,旋转)

        图像的几何变换是各种图像处理的基础,一般来说图像的几何变换是在不改变原有图像像素的基础上进行的,几何变换相当于是原图像到新图像的一种映射。图像的几何变换基本有镜像,平移、错切,缩放,旋转。常用的几何变换方法有“向前映射”和”向后映射“。对于图像大小不改变的几何变换,如镜像、平移等,一般采用“向前映射”,对于图像大小改变的几何变形,如错切、缩放、旋转,一般采用“向后映射”。

       “向前映射”是指将输入图像像素一个个的映射到输出图像。如果输入像素被映射到四个输出像素之间的位置,则其像素值按插值算法在四个输出像素之间分配。

        “向后映射”是指输出像素一个个地映射回输入图像中,以确定其像素值。如果输出像素被映射到邻近的四个输入像素之间,则其像素值由插值决定。

常用的插值方法:双线性插值

一、镜像变换

镜像变换分为水平镜像和垂直镜像。水平镜像以图像垂直中轴线为对称轴,垂直镜像以图像水平中轴线为对称轴。设点P_{0}(x_{0},y_{0})进行镜像后的对应点为P(x,y),图像高度为H,宽度为W。

经过水平镜像后,原图像P_{0}(x_{0},y_{0})坐标变为P(W-x_{0},y_{0})

矩阵表达式为:\begin{bmatrix} x \\ y \\1 \end{bmatrix}=\begin{bmatrix} -1 & 0 & W\\ 0& 1 & 0\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x_{0}\\ y_{0} \\ 1 \end{bmatrix}

逆运算为:\begin{bmatrix} x_{0} \\ y_{0} \\1 \end{bmatrix}=\begin{bmatrix} -1 & 0 & W\\ 0& 1 & 0\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

经过垂直镜像后,原图像P_{0}(x_{0},y_{0})坐标变为P(x_{0},H-y_{0})

矩阵表达式为:\begin{bmatrix} x \\ y \\1 \end{bmatrix}=\begin{bmatrix} 1 & 0 & 0\\ 0& -1 & H\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x_{0}\\ y_{0} \\ 1 \end{bmatrix}

逆运算为:\begin{bmatrix} x_{0} \\ y_{0} \\1 \end{bmatrix}=\begin{bmatrix} 1 & 0 & 0\\ 0& -1 & H\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

具体程序如下:

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include "opencv2/opencv.hpp"int main()
{const char *filename = "island.bmp";IplImage *inputimage = cvLoadImage(filename, -1);// x_mirror(垂直镜像),y_mirror(水平镜像),xy_mirror(中心对称)IplImage *xmirrorimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);IplImage *ymirrorimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);IplImage *xymirrorimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);for (int i = 0; i < xmirrorimage->height; i++){for (int j = 0; j < xmirrorimage->width; j++){for (int k = 0; k < xmirrorimage->nChannels; k++){ymirrorimage->imageData[i*ymirrorimage->widthStep + j * ymirrorimage->nChannels + k]= inputimage->imageData[i*inputimage->widthStep + (inputimage->width - 1 - j)*inputimage->nChannels + k];xmirrorimage->imageData[i*xmirrorimage->widthStep + j * xmirrorimage->nChannels + k]= inputimage->imageData[(inputimage->height-1-i)*inputimage->widthStep + j*inputimage->nChannels + k];xymirrorimage->imageData[i*xymirrorimage->widthStep + j * xymirrorimage->nChannels + k]= inputimage->imageData[(inputimage->height - 1 - i)*inputimage->widthStep + (inputimage->width-1-j) * inputimage->nChannels + k];}}}cvNamedWindow("inputimage", 1);cvNamedWindow("ymirrorimage", 1);cvNamedWindow("xmirrorimage", 1);cvNamedWindow("xymirrorimage", 1);cvShowImage("inputimage", inputimage);cvShowImage("ymirrorimage", ymirrorimage);cvShowImage("xmirrorimage", xmirrorimage);cvShowImage("xymirrorimage", xymirrorimage);cvWaitKey(0);cv::Mat amplification = cv::cvarrToMat(ymirrorimage);cv::Mat amplification1 = cv::cvarrToMat(xmirrorimage);cv::Mat amplification2 = cv::cvarrToMat(xymirrorimage);cv::imwrite("ymirrorimage.bmp", amplification);cv::imwrite("xmirrorimage.bmp", amplification1);cv::imwrite("xymirrorimage.bmp", amplification2);cvDestroyWindow("inputimage");cvDestroyWindow("ymirrorimage");cvDestroyWindow("xmirrorimage");cvDestroyWindow("xymirrorimage");cvReleaseImage(&inputimage);cvReleaseImage(&ymirrorimage);cvReleaseImage(&xmirrorimage);cvReleaseImage(&xymirrorimage);
}

二、错切变换


#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include<math.h>
#include "opencv2/opencv.hpp"
//#define pi 3.141592653;   //定义pi的值
//#define a pi/4;int main()
{float pi = 3.141592653;float a = pi / 3;//错切角度const char *filename = "island.bmp";IplImage *inputimage = cvLoadImage(filename, -1);IplImage *x_shear = cvCreateImage(cvSize((inputimage->width+inputimage->height/tan(a)), inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);IplImage *y_shear = cvCreateImage(cvSize(inputimage->width, (inputimage->height + inputimage->width / tan(a))), IPL_DEPTH_8U, inputimage->nChannels);for (int i = 0; i < x_shear->height; i++){for (int j = 0; j < x_shear->width; j++){for (int k = 0; k < x_shear->nChannels; k++)if (j < x_shear->height / tan(a) - i / tan(a) || j > x_shear->height / tan(a) - i / tan(a) + inputimage->width)x_shear->imageData[i*x_shear->widthStep + j*x_shear->nChannels + k]= 0;elsex_shear->imageData[i*x_shear->widthStep+ j*x_shear->nChannels+k]= inputimage->imageData[i*inputimage->widthStep+ (j - int(x_shear->height / tan(a) - i / tan(a)))*inputimage->nChannels+k];}}for (int i = 0; i < y_shear->height; i++){for (int j = 0; j < y_shear->width; j++){for (int k = 0; k < y_shear->nChannels; k++)if(i < y_shear->width / tan(a) - j / tan(a) || i > y_shear->width / tan(a) - j / tan(a) + inputimage->height)y_shear->imageData[i*y_shear->widthStep + j * y_shear->nChannels + k]= 0;elsey_shear->imageData[i*y_shear->widthStep + j *y_shear->nChannels + k]= inputimage->imageData[(i- int(y_shear->width / tan(a) - j / tan(a)))*inputimage->widthStep + j * inputimage->nChannels + k];}}cvNamedWindow("inputimage", 1);cvNamedWindow("x_shear", 1);cvNamedWindow("y_shear", 1);//显示图像cvShowImage("inputimage", inputimage);cvShowImage("x_shear", x_shear);cvShowImage("y_shear", y_shear);cvWaitKey(0);//保存图像cv::Mat amplification1 = cv::cvarrToMat(x_shear);cv::Mat amplification2 = cv::cvarrToMat(y_shear);cv::imwrite("x_island.bmp", amplification1);cv::imwrite("y_island.bmp", amplification2);cvDestroyWindow("inputimage");cvDestroyWindow("x_shear");cvDestroyWindow("y_shear");cvReleaseImage(&inputimage);cvReleaseImage(&x_shear);cvReleaseImage(&y_shear);
}

以下为原图及延水平方向和垂直方向不同角度的错切

三、图像缩放

        图像比例缩放是将给定图像在x轴方向按比例缩放f_{x}倍,在y轴方向缩放f_{y}倍,得到新图像。如果f_{x}=f_{y},即x轴与y轴缩放比例相同,称为全比例缩放。比例缩放前后,两点P_{0}(x_{0},y_{0})P(x,y)之间的关系矩阵表达式如下,

矩阵表达式为:\begin{bmatrix} x \\ y \\1 \end{bmatrix}=\begin{bmatrix} f_{x} & 0 & 0\\ 0& f_{y} & 0\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x_{0}\\ y_{0} \\ 1 \end{bmatrix}

逆运算为:\begin{bmatrix} x_{0} \\ y_{0} \\1 \end{bmatrix}=\begin{bmatrix} 1/f_{x} & 0 & 0\\ 0& 1/f_{y} & 0\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x\\ y \\ 1 \end{bmatrix},即 \left\{\begin{matrix} x_{0}=x/f_{x}\\ y_{0}=y/f_{y} \end{matrix}\right.

       图像的比例缩小,因为缩小后信息量变少,只需在原图像中按缩小比例隔行隔列选取像素点即可,构成新图像。

       图像的比例放大,放大后的图像像素点变多,有些放大后的图像像素在原图像中找不到对应像素,需要进行近似处理。常用方法有两种(1)直接赋值为和他最相近的像素点(2)通过插值计算相应像素值。第一种计算方法虽然简单,但是会造成图像马赛克现象;第二种方法处理效果好些,但是运算量加大。

我们采用“向后映射”的方法,用双线性插值计算放大后图像的像素值。

 

放大后灰度值计算如下(彩色图像对RGB三通道分别按如下公式计算):

g(x,y)=(1-q)*\left ( (1-p)*f([x],[y])+p*f([x]+1,[y]) \right )+ q*\left ( (1-p)*f([x],[y]+1)+p*f([x]+1,[y]+1) \right )

式中:g(x,y)为插值后新图像坐标(x,y)的灰度值;f(x,y)为插值前坐标(x,y)的灰度值;[x][y]分别为不大于x,y的整数。

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include<math.h>
#include "opencv2/opencv.hpp"int main()
{//amplificationconst char *filename = "island.bmp";IplImage *inputimage = cvLoadImage(filename, -1);float multiple = 2.5;//放大倍数IplImage * ScaleAmplifying = cvCreateImage(cvSize(inputimage->width * multiple, inputimage->height * multiple), IPL_DEPTH_8U, inputimage->nChannels);IplImage * X_Amplifying = cvCreateImage(cvSize(inputimage->width * multiple, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);IplImage * Y_Amplifying = cvCreateImage(cvSize(inputimage->width , inputimage->height * multiple), IPL_DEPTH_8U, inputimage->nChannels);//比例缩放for (int i = 0; i < ScaleAmplifying->height; i++){for (int j = 0; j < ScaleAmplifying->width; j++){for (int k = 0; k < ScaleAmplifying->nChannels; k++){float q = i / multiple - floor(i / multiple);float p = j / multiple - floor(j / multiple);int x = floor(i / multiple);int y = floor(j / multiple);if (q < 0.001 && p < 0.001){ScaleAmplifying->imageData[i*ScaleAmplifying->widthStep + j * ScaleAmplifying->nChannels + k]= inputimage->imageData[x * inputimage->widthStep + y * inputimage->nChannels + k];}else{float temp1 = 0,temp2 = 0;temp1 = (1-p)*(unsigned char)inputimage->imageData[x * inputimage->widthStep + y * inputimage->nChannels + k]+p* (unsigned char)inputimage->imageData[x * inputimage->widthStep + (y + 1) * inputimage->nChannels + k];temp2=(1-p)*(unsigned char)inputimage->imageData[(x + 1) * inputimage->widthStep + y * inputimage->nChannels + k]+ p*(unsigned char)inputimage->imageData[(x + 1)* inputimage->widthStep + (y + 1) * inputimage->nChannels + k];ScaleAmplifying->imageData[i*ScaleAmplifying->widthStep + j * ScaleAmplifying->nChannels + k]= (1 - q)*temp1 + q * temp2;}}}}//水平缩放for (int i = 0; i < X_Amplifying->height; i++){for (int j = 0; j < X_Amplifying->width; j++){for (int k = 0; k < X_Amplifying->nChannels; k++){float p = j / multiple - floor(j / multiple);int x = floor(i / multiple);int y = floor(j / multiple);if ( p < 0.001){X_Amplifying->imageData[i*X_Amplifying->widthStep + j * X_Amplifying->nChannels + k]= inputimage->imageData[i * inputimage->widthStep + y * inputimage->nChannels + k];}else{float temp = 0;temp = (1 - p)*(unsigned char)inputimage->imageData[i * inputimage->widthStep + y * inputimage->nChannels + k]+ p * (unsigned char)inputimage->imageData[i * inputimage->widthStep + (y + 1) * inputimage->nChannels + k];X_Amplifying->imageData[i*X_Amplifying->widthStep + j * X_Amplifying->nChannels + k]= temp ;}}}}//垂直缩放for (int i = 0; i < Y_Amplifying->height; i++){for (int j = 0; j < Y_Amplifying->width; j++){for (int k = 0; k < Y_Amplifying->nChannels; k++){float p = j / multiple - floor(j / multiple);float q = i / multiple - floor(i / multiple);int x = floor(i / multiple);int y = floor(j / multiple);if (q < 0.001){Y_Amplifying->imageData[i*Y_Amplifying->widthStep + j * Y_Amplifying->nChannels + k]= inputimage->imageData[x * inputimage->widthStep + j * inputimage->nChannels + k];}else{float temp = 0;temp = (1 - q)*(unsigned char)inputimage->imageData[(x + 1) * inputimage->widthStep + j * inputimage->nChannels + k]+ q * (unsigned char)inputimage->imageData[x* inputimage->widthStep + j * inputimage->nChannels + k];Y_Amplifying->imageData[i*Y_Amplifying->widthStep + j * Y_Amplifying->nChannels + k]= temp;}}}}cvNamedWindow("inputimage", 1);cvNamedWindow("ScaleAmplifying", 1);cvNamedWindow("X_Amplifying", 1);cvNamedWindow("Y_Amplifying", 1);//显示图像cvShowImage("inputimage", inputimage);cvShowImage("ScaleAmplifying", ScaleAmplifying);cvShowImage("X_Amplifying", X_Amplifying);cvShowImage("Y_Amplifying", Y_Amplifying);cvWaitKey(0);//保存图像cv::Mat amplification = cv::cvarrToMat(ScaleAmplifying);cv::Mat amplification1 = cv::cvarrToMat(X_Amplifying);cv::Mat amplification2 = cv::cvarrToMat(Y_Amplifying);cv::imwrite("ScaleAmplifying.bmp", amplification);cv::imwrite("X_Amplifying.bmp", amplification1);cv::imwrite("Y_Amplifying.bmp", amplification2);cvDestroyWindow("inputimage");cvDestroyWindow("ScaleAmplifying");cvDestroyWindow("X_Amplifying");cvDestroyWindow("Y_Amplifying");cvReleaseImage(&inputimage);cvReleaseImage(&ScaleAmplifying);cvReleaseImage(&X_Amplifying);cvReleaseImage(&Y_Amplifying);
}

图像依次为原图、延x方向放大、延y方向放大、比例放大。

四、旋转变换

        图像旋转变换会改变图像大小,如果采用“前向映射”生成图像会有一些空洞点,空洞点需要差值的方法补全,过程比较麻烦,所以采用“后向映射”效果较好,不会产生空洞点。

        设P_{0}(x_{0},y_{0})旋转\theta角后对应的点为P(x,y)(逆时针旋转)

旋转前后点P_{0}(x_{0},y_{0})P(x,y)的坐标分别是

\left\{\begin{matrix} x_{0}=r*\cos \alpha \\ y_{0}=r*\sin \alpha \end{matrix}\right.

\left\{\begin{matrix} x=r*\cos(\alpha -\theta ) =r*\cos \alpha \cos \theta +r*\sin \alpha \sin \theta =x_{0}*\cos \theta +y_{0}*\sin \theta \\ y=r*\sin(\alpha -\theta )=r*\sin \alpha \cos \theta -r*\cos \alpha \sin \theta =-x_{0}*\sin \theta +y_{0}*\cos \theta \end{matrix}\right.

矩阵表达式为:\begin{bmatrix} x \\ y \\1 \end{bmatrix}=\begin{bmatrix} \cos \theta & \sin \theta & 0\\ -\sin \theta & \cos \theta & 0\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x_{0}\\ y_{0} \\ 1 \end{bmatrix}

逆运算为:\begin{bmatrix} x_{0} \\ y_{0} \\1 \end{bmatrix}=\begin{bmatrix} \cos \theta & \--sin \theta & 0\\ \sin \theta & \cos \theta & 0\\ 0& 0 & 1 \end{bmatrix}*\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

上述算法是以(0,0)点为中心进行旋转,如果需要以(a,b)点为中心进行旋转,需要先将图像平移至该点,然后进行旋转,旋转后再平移回原点。

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include<math.h>
#include "opencv2/opencv.hpp"int main()
{float pi = 3.141592653;float a = pi / 4.0;//控制旋转角度float b = pi / 3.0;// Rotationconst char *filename = "football.jpg";IplImage *inputimage = cvLoadImage(filename, -1);float diagonal = sqrt(inputimage->width * inputimage->width + inputimage->height*inputimage->height);float angle = atan((float)inputimage->width / (float)inputimage->height);float a_x = pi / 2.0 - angle - a;float a_y = angle - a;int width = diagonal * cos(a_x);int height = diagonal * cos(a_y);float d_x = (width - inputimage -> width) / 2.0;float d_y = (height - inputimage->height) / 2.0;//printf("%f", angle);//printf("%d %d %f %f %f %d %d %f", width,height,a_x,a_y,angle,inputimage->width,inputimage->height, (float)inputimage->width/(float)inputimage->height);IplImage *Rotateimage = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, inputimage->nChannels);for (int i = 0; i < Rotateimage->height; i++){for (int j = 0; j < Rotateimage->width; j++){for (int k = 0; k < Rotateimage->nChannels; k++){float x = j * cos(a) + i * sin(a);float y = -j * sin(a) + i * cos(a);float x0 = ((j- Rotateimage->width/2) * cos(a) - (i - Rotateimage->height/2)* sin(a))+ Rotateimage->width/2;float y0 = ((j - Rotateimage->width/2)* sin(a) + (i - Rotateimage->height/2) * cos(a))+ Rotateimage->height/2;if (x0 >(int)d_x && x0 < Rotateimage->width-d_x && y0>d_y && y0 < Rotateimage->height-d_y){int x_coordinate = x0;int y_coordinate = y0;Rotateimage->imageData[i*Rotateimage->widthStep + j * Rotateimage->nChannels + k]= inputimage->imageData[int(y_coordinate-d_y)*inputimage->widthStep + (int)(x_coordinate-d_x) * inputimage->nChannels + k];}elseRotateimage->imageData[i*Rotateimage->widthStep + j * Rotateimage->nChannels + k]= 0;}}}cvNamedWindow("inputimage", 1);cvNamedWindow("Rotateimage", 1);//显示图像cvShowImage("inputimage", inputimage);cvShowImage("Rotateimage", Rotateimage);cvWaitKey(0);//储存图像cv::Mat rotate = cv::cvarrToMat(Rotateimage);cv::imwrite("Rotation.bmp", rotate);cvDestroyWindow("inputimage");cvDestroyWindow("Rotateimage");cvReleaseImage(&inputimage);cvReleaseImage(&Rotateimage);
}

图像依次为原图、逆时针旋15转度、逆时针旋转30度、逆时针旋转45度、逆时针旋转60度、逆时针旋转90度

http://www.xdnf.cn/news/860203.html

相关文章:

  • 网络IP地址冲突故障,快速解决方案(非常详细)零基础入门到精通,收藏这一篇就够了_内网总提示有ip冲突(1)
  • 诺基亚S60各机型对应的系统版本清单
  • dsound.dll文件丢失导致程序无法运行问题
  • 门萨智商测试
  • EVE-NG
  • html中如何写div中div的位置,position设置div的位置
  • C++类设计和实现的十大最佳实践
  • 160款程序员专属情人节表白网站【建议收藏】HTML+CSS+JavaScript (1)
  • 如何黑掉一台根本不联网的电脑呢?
  • sxe增加服务器,sXe Injected服务端使用说明
  • B2C垂直电商细分
  • 洛谷日报索引
  • GPT-1,GPT-2和GPT-3发展历程及核心思想,GTP-4展望
  • Android 4.3是什么版本,安卓4.3系统能用微信哪个版本
  • Delphi从红极一时到沉寂,备受冷落的国产平台,却已逆袭
  • 【QPSK信号生成】生成正交相移键控信号研究(Matlab代码实现)
  • 谈一谈|你不知道的黑客
  • html help编辑,HTML编辑利器--Html help Workshop
  • YUI3配置
  • 旅游管理系统(源码+开题)
  • 【JavaScript】JS基础语法
  • HTML个人网页制作教程
  • 隐藏 IP 地址的 5 种方法
  • 木马导致inetinfo.exe进程占100% CPU的解决方法
  • IE浏览器页面卡死问题
  • iRShell功能介绍
  • 详解-黑莓7290激活教程
  • XP系统常用的登录密码方法破解(一共9种)
  • 20个基本电路图讲解_最简单音调电路图大全
  • 【Cadence】stb仿真和ac仿真——以一个简单的全差分反相放大器仿真为例