OpenCV:图像几何变换(镜像,错切,缩放,旋转)
图像的几何变换是各种图像处理的基础,一般来说图像的几何变换是在不改变原有图像像素的基础上进行的,几何变换相当于是原图像到新图像的一种映射。图像的几何变换基本有镜像,平移、错切,缩放,旋转。常用的几何变换方法有“向前映射”和”向后映射“。对于图像大小不改变的几何变换,如镜像、平移等,一般采用“向前映射”,对于图像大小改变的几何变形,如错切、缩放、旋转,一般采用“向后映射”。
“向前映射”是指将输入图像像素一个个的映射到输出图像。如果输入像素被映射到四个输出像素之间的位置,则其像素值按插值算法在四个输出像素之间分配。
“向后映射”是指输出像素一个个地映射回输入图像中,以确定其像素值。如果输出像素被映射到邻近的四个输入像素之间,则其像素值由插值决定。
常用的插值方法:双线性插值
一、镜像变换
镜像变换分为水平镜像和垂直镜像。水平镜像以图像垂直中轴线为对称轴,垂直镜像以图像水平中轴线为对称轴。设点进行镜像后的对应点为
,图像高度为H,宽度为W。
经过水平镜像后,原图像坐标变为
。
矩阵表达式为:
逆运算为:
经过垂直镜像后,原图像坐标变为
矩阵表达式为:
逆运算为:
具体程序如下:
#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轴方向按比例缩放倍,在y轴方向缩放
倍,得到新图像。如果
,即x轴与y轴缩放比例相同,称为全比例缩放。比例缩放前后,两点
,
之间的关系矩阵表达式如下,
矩阵表达式为:
逆运算为:,即
图像的比例缩小,因为缩小后信息量变少,只需在原图像中按缩小比例隔行隔列选取像素点即可,构成新图像。
图像的比例放大,放大后的图像像素点变多,有些放大后的图像像素在原图像中找不到对应像素,需要进行近似处理。常用方法有两种(1)直接赋值为和他最相近的像素点(2)通过插值计算相应像素值。第一种计算方法虽然简单,但是会造成图像马赛克现象;第二种方法处理效果好些,但是运算量加大。
我们采用“向后映射”的方法,用双线性插值计算放大后图像的像素值。
放大后灰度值计算如下(彩色图像对RGB三通道分别按如下公式计算):
式中:为插值后新图像坐标
的灰度值;
为插值前坐标
的灰度值;
,
分别为不大于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方向放大、比例放大。
四、旋转变换
图像旋转变换会改变图像大小,如果采用“前向映射”生成图像会有一些空洞点,空洞点需要差值的方法补全,过程比较麻烦,所以采用“后向映射”效果较好,不会产生空洞点。
设旋转
角后对应的点为
(逆时针旋转)
旋转前后点、
的坐标分别是
矩阵表达式为:
逆运算为:
上述算法是以(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度