之前已经实现了自定义画布上的二维图片的平移和旋转,今天在此基础上实现图片旋转。
虽然有关OpenCV对图像进行旋转的代码和函数非常多,但是我的代码的要求比较多:能在固定大小的画布上随意地方旋转、旋转与平移和缩放要无缝连接。
经过仔细分析以后,实现这个想法主要有两种途径:
第一种:编写的函数参数均是每两次图片显示的变化量:比如平移量dx dy、旋转角度量等等。这就需要一直定位图片的相关特征,最终发现实现起来比较困难,尤其是由旋转之后接着缩放和平移,坐标点很难把握。
第二种就比较简单了:每一次显示的图片都是由原始图片得来的,这样虽然方便但是每一次都要用到坐标、缩放因子、旋转角度这三个参数。
总结起来就是一个用的是增量、一个用的是总量,后者远远简单一点。

#include <iostream>
#include <cstring>
#include <windows.h> 
#include <stdio.h>  
#include <conio.h> 
#include <opencv2/opencv.hpp> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp> 

using namespace cv;
using namespace std;


class show_picture //定义一个新的用来与图像交互的类
{
public :
	Mat image, image_size_change, image_for_display;//image是原始图片,永远不做更改。image_size_change相当于每一次更改后的图片。image_for_display是用来显示的,相当于包含了image_size_change的画布。
		float mul=1.0; 
		void imshow_FullScreen(Mat img);//在画布上显示图片
		void Translation(int dx, int dy);//图片平移
		void Change_Size(float div);//图片放大缩小
		void Revolve(float degree);//图片旋转
		void Picture_Init();//由于采用了第二种方法,因此每一次显示前都需要初始化
		void Display(int x, int y, float div, float degree);//最终用来的显示函数
		show_picture(int width, int length, int x, int y)
		{
			Screen_Width = width;
			Screen_Length = length;
			point_O.x = x;
			point_O.y = y;
		}
		show_picture()
		{
			Screen_Width = 1080;
			Screen_Length = 1920;
			point_O.x = 0;
			point_O.y = 0;
		}
private:
	    Point point_O;//图片的左上方原点(用来定位图片)
		int Screen_Width, Screen_Length;//画布的长和宽
};

/****************带有平移操作的图片显示函数,自定义画布*****************/
void show_picture::Translation(int dx, int dy)
{
	point_O.x += dx;
	point_O.y += dy;
	imshow_FullScreen(image_size_change);
}

/****************显示带有画布的图片*****************/
void show_picture::imshow_FullScreen(Mat img)
{
	copyMakeBorder(img, image_for_display, point_O.y, Screen_Width-point_O.y-img.rows, point_O.x, Screen_Length-point_O.x-img.cols, BORDER_CONSTANT, Scalar(0, 0, 0));//OpenCV自带的扩充图片的函数
	namedWindow("Canvases", WINDOW_AUTOSIZE);
	circle(image_for_display, point_O, 3, Scalar(0, 0, 255));//标注原点位置方便调试
	imshow("Canvases", image_for_display);//最终显示的是扩充后的图片
	
}

/****************带有缩放操作的图片显示函数,自定义画布*****************/
void show_picture::Change_Size(float div)
{
	//Picture_Init();
	point_O.x += (image_size_change.cols - image_size_change.cols*div)/ 2;//计算缩放后的新的坐标
	point_O.y += (image_size_change.rows - image_size_change.rows*div) / 2;
	resize(image_size_change, image_size_change, Size(), div, div);
	imshow_FullScreen(image_size_change);
	
}

void show_picture::Picture_Init()
{
	point_O.x = 200;
	point_O.y = 200;
	resize(image, image_size_change, Size(), 0.5, 0.5);//image不用再读入了,只需要初始化image_size_change

}
void show_picture::Revolve(float degree)
{
		//填充图像使其符合旋转要求
	int uniSize = (int)(max(image_size_change.cols, image_size_change.rows)* 1.414);//保证图片怎样都不会旋转出画布
	int dx = (int)(uniSize - image_size_change.cols) / 2;
	int dy = (int)(uniSize - image_size_change.rows) / 2;

	copyMakeBorder(image_size_change, image_size_change, dy, dy, dx, dx, BORDER_CONSTANT, Scalar(0, 0, 0));
	Point2f center((float)(image_size_change.cols / 2), (float)(image_size_change.rows / 2));
	Mat affine_matrix = getRotationMatrix2D(center, degree, 1.0);
	warpAffine(image_size_change, image_size_change, affine_matrix, image_size_change.size());//直接用OpenCV自带的变换函数 
	imshow_FullScreen(image_size_change);

}

void show_picture::Display(int x, int y, float div, float degree)
{
	Picture_Init();//每次显示前先初始化
	Translation(x, y);//这是相对于原点的坐标
	Change_Size(div);//这是相对于初始图片的缩放倍数
	Revolve(degree);
}

int main(int argc, char **argv)
{
	show_picture picture(1000,1000,200,200);//定义一个1000*1000大小的画布,初始原点在(10,10)的位置
	picture.image = imread("C:\\Users\\MacheNike\\Desktop\\123.jpg");//读入原始图片
	
	float i = 0;
	
	for (i = 0; i < 720; i++)
	{
		picture.Display(100, 100, 0.8, i/2.0);
		waitKey(1);
	}
	
	
	return 0;
	
}



多次实验测试,代码效果比较不错,三种操作切换完全无任何卡顿感。这次采用总量的形式还有一个重要原因就是:Leap Motion 采集到的相关特征值都是相对于原点的,并非变化量。所以下一步与Leap Motion联合交互时会大大的便利。

更多推荐

OpenCV实现自由画布上的图片平移缩放旋转,代码详解!