用OpenCV实现Photoshop算法(一): 图像旋转

最近学习了OpenCV,于是想用它实现Photoshop的主要功能,用于照片处理。

对于一张照片,PS的一般处理步骤包括:

1, 旋转图片,校正位置。

2,剪切,调整大小,重新构图。

3,调整色阶、曲线,使图片曝光正确、对比适中。

4,调整对比度、饱和度

5,印章去掉不想要的东西,液化调整形体线条

6,对于人像图片,美肤、美白

7, 用色彩平衡、可选颜色等调整色调,形成照片调性

8,加一些光效

9,锐化

以后的一系列博文将采用OpenCV逐一实现Photoshop的算法和功能, 并用计算机视觉人工智能方式,尝试超越Photoshop一点点。

本系列博文基于OpenCV,  编程语言为C++.    由于OpenCV的跨平台性,代码可以在用于Windows, Linux, 作个接口后可用于Android,IOS.

一、图像旋转

OpenCV中, 用 warpAffine()  仿射变换函数即可以实现旋转。

例如,写一个 旋转函数 imageRotate1() 如下:

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

//src为原图像, dst为新图像, angle为旋转角度(正值为顺时针旋转,负值为逆时针旋转)
int imageRotate1(InputArray src, OutputArray dst, double angle)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(width, height));

	return 0;
}

图像旋转 -17度 的结果

在函数 imageRotate1()中,新图像沿用原图像大小。旋转后,图像的角部被切掉了。

这样显然不正确,需要调整图像尺寸。

调整方式一: 扩大图片,将原图片包含进去,计算示意图如下:

新图片大小为: out_width = (width*cos(a)+height*sin(a);   out_height = height*cos(a)+width*sin(a))

修改原函数为 imageRotate2() :

//图像旋转: src为原图像, dst为新图像, angle为旋转角度
int imageRotate2(InputArray src, OutputArray dst, double angle)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//计算新图像大小
	double angle1 = angle  * CV_PI / 180. ;
	double a = sin(angle1) * scale;
	double b = cos(angle1) * scale;
	double out_width = height * fabs(a) + width * fabs(b);
	double out_height = width * fabs(a) + height * fabs(b);

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(out_width, out_height));

	return 0;
}

图像旋转 -17度 的结果

还是不对,新图像变大了,但图像中心点不对,需要在旋转矩阵中加入平移,在一次变换中同时完成旋转和平移,将新图像的中心点移到正确位置。

再次修改函数为: imageRotate3()

//图像旋转: src为原图像, dst为新图像, angle为旋转角度
int imageRotate3(InputArray src, OutputArray dst, double angle)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//计算新图像大小
	double angle1 = angle  * CV_PI / 180. ;
	double a = sin(angle1) * scale;
	double b = cos(angle1) * scale;
	double out_width = height * fabs(a) + width * fabs(b);
	double out_height = width * fabs(a) + height * fabs(b);

	//在旋转变换矩阵中加入平移量
	trans_mat.at<double>(0, 2) += cvRound( (out_width - width) / 2 );
	trans_mat.at<double>(1, 2) += cvRound( (out_height - height) / 2);

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(out_width, out_height));

	return 0;
}

这一次正确了,新图像变大了,同时图像中心点移到了新的中心点,原图像全部能显示出来。

在实际照片旋转中,我们经常采用另一种剪切形式的调整方式:图像旋转后,缩小图片,使图片各个边角均不出现黑边。 下图红框即为新图象大小,如下:

这种调整方式下,新图像大小的计算稍为有点复杂,在网上也没有找到范例,只能自己计算了。

1,如上,旋转后的外边框大小为:    out_width =(width*cos(a)+height*sin(a);     out_height = height*cos(a)+width*sin(a))

2,  画几根辅助线,如下图:(注意右边图中的粉红三角形)

其最长的边长 len =  width*cos(a)

角a 即旋转角度

由于外边框大小已知,则角b 可计算出来。

求解 Y:    Y = len / ( 1 / tan( a ) + 1 / tan( b ) )

X =  Y * 1 /  tan( b )

最后求得  红框的长、宽为:   new_width = out_width - 2 * X;     new_height = out_height - 2 * Y

再次修改函数为: imageRotate4()

增加了一个参数: isClip ,    当isClip为true时,采取缩小图片的剪切方式,否则采取放大图片的方式。

//图像旋转: src为原图像, dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式
int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//计算新图像大小
	double angle1 = angle  * CV_PI / 180. ;
	double a = sin(angle1) * scale;
	double b = cos(angle1) * scale;
	double out_width = height * fabs(a) + width * fabs(b); //外边框长度
	double out_height = width * fabs(a) + height * fabs(b);//外边框高度

	int new_width, new_height;
	if ( ! isClip ) {
		new_width = cvRound(out_width);
		new_height = cvRound(out_height);
	} else {
		//calculate width and height of clip rect
		double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
		double len = width * fabs(b);
		double Y = len / ( 1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)) );
		double X = Y * 1 / fabs(tan(angle2));
		new_width = cvRound(out_width - X * 2);
		new_height= cvRound(out_height - Y * 2);
	}

	//在旋转变换矩阵中加入平移量
	trans_mat.at<double>(0, 2) += cvRound( (new_width - width) / 2 );
	trans_mat.at<double>(1, 2) += cvRound( (new_height - height) / 2);

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(new_width, new_height));

	return 0;
}

以下是 isClip为true,  旋转角度为 10 的结果,可见图片旋转了、缩小了,没有黑边

由于不注意,人们拍照时经常拍歪了,一般歪得也不多,但照片就不好看了。

因此,有这么一个问题:  能否智能判别图像是否拍歪了,如果歪了,则自动计算出要旋转摆正的角度。从而使得人们一拍照,就自动拍正。

(PS:这个功能是Photoshop没有的,如果能实现,算不算超越Photoshop一点点呢?)

解决思路是这样的:

1, 图像一般有一个或两条长直线(通常这个可能是地平线、建筑物等),且倾斜角度不大

2, 利用 OpenCV图像识别能力,识别出图中有哪些直线。

3, 分析这些直线,  如果长度足够长、且位置相对居中,选取最长的两条直线,测算摆正它所需的角度,做为返回值。

事实上,人工纠正图片的Photoshop操作方式也是这样的:我们在图中人眼找一个基准线,用“度量工具”画一条线,再点菜单“图象/ 旋转画布/ 任意角度", 则Photoshop将计算出需要旋转的角度。

尝试写了一个函数:   detectRotation(),  用于自动检测摆正图像的所需的旋转角度, 如下:

/**
 * 智能检测图像倾斜度
 * 返回值:返回0表示无检测结果,返回非0表示摆正图象需要旋转的角度(-10至10度)
 */
double detectRotation(InputArray src)
{
	double max_angle = 6; //可旋转的最大角度

	Mat in = src.getMat();
	if( in.empty() ) return 0;

	Mat input;

	//转为灰度图
	if ( in.type() == CV_8UC1 )
		input = in;
	else if ( in.type() == CV_8UC3 )
		cvtColor(in, input, CV_BGR2GRAY);
	else if ( in.type() == CV_8UC3 )
		cvtColor(in, input, CV_BGRA2GRAY);
	else
		return 0;

	Mat dst, cdst;

	//执行Canny边缘检测(检测结果为dst, 为黑白图)
	double threshold1 = 90;
	Canny(src, dst, threshold1, threshold1 * 3, 3);

	//将Canny边缘检测结果转化为灰度图像(cdst)
	cvtColor(dst, cdst, CV_GRAY2BGR);

	//执行霍夫线变换,检测直线
	vector<Vec4i> lines; //存放检测结果的vector
	double minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度
	double maxLineGap = std::min(dst.cols, dst.rows) * 0.03 ; //最小线间距
	int threshold = 90;
	HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap );

	//分析所需变量
	int x1, y1, x2 , y2; //直线的两个端点
	int x, y;  //直线的中点
	double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度
	double line_length; //直线长度
	double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
	double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)
	double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度
	main_lens[0] = main_lens[1] = 0;
	main_angles[0] = main_angles[1] = 0;

	//逐个分析各条直线,判断哪个是主线条
	for( size_t i = 0; i < lines.size(); i++ ) {
		//取得直线的两个端点座标
		x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];
		x = (x1 + x2 ) / 2; y = (y1 + y2) / 2;
		//计算直线的角度
		angle =	(x1 == x2) ? 90 : ( atan ( (y1 - y2) * 1.0 / (x2 - x1) ) ) / CV_PI * 180;
		//摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。
		if ( fabs(angle - 0) <= max_angle ) {
			rotate_angle = angle - 0;
		} else if ( fabs(angle - 90) <= max_angle ) {
			rotate_angle = angle - 90;
		} else {
			continue;
		}

		//计算线的长度
		line_length = sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)  );
		//计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
		position_weighted = 1;
		if ( x < dst.cols / 4 || x > dst.cols * 3 / 4  ) position_weighted *= 0.8;
		if ( x < dst.cols / 6 || x > dst.cols * 5 / 6  ) position_weighted *= 0.5;
		if ( x < dst.cols / 8 || x > dst.cols * 7 / 8  ) position_weighted *= 0.5;
		if ( y < dst.rows / 4 || y > dst.rows * 3 / 4  ) position_weighted *= 0.8;
		if ( y < dst.rows / 6 || y > dst.rows * 5 / 6  ) position_weighted *= 0.5;
		if ( y < dst.rows / 8 || y > dst.rows * 7 / 8  ) position_weighted *= 0.5;

		//如果 直线长度 * 位置权重 < 最小长度, 则这条线无效
		line_length = line_length * position_weighted;
		if ( line_length < minLineLength ) continue;

		//如果长度为前两名,则存入数据
		if ( line_length > main_lens[1] )  {
			if (line_length > main_lens[0]) {
				 main_lens[1] = main_lens[0];
				 main_lens[0] = line_length;
				 main_angles[1] = main_angles[0];
				 main_angles[0] = rotate_angle;
				 //如果定义了 SHOW_LINE, 则将该线条画出来
				 #ifdef SHOW_LINE
				 line( cdst, Point(x1, y1), Point(x2, y2), Scalar(0,0,255), 3, CV_AA);
				 #endif
			} else {
				main_lens[1] = line_length;
				main_angles[1] = rotate_angle;
			}
		}
	}

	//如果定义了 SHOW_LINE, 则在source_window中显示cdst
       #ifdef SHOW_LINE
	imshow(source_window, cdst);
	#endif

	//最后,分析最长的二条直线,得出结果
	if ( main_lens[0] > 0 ) {
		//如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值
		if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2 )) {
			return (main_angles[0] + main_angles[1] ) / 2;
		} else {
			return main_angles[0];   //否则,返回最长的线需要旋转的角度
		}
	} else {
		return 0;
	}
}

使用detectRotation()函数自动测试角度,并显示出主要线条,运行结果:

恩,有那么一点意思, 找出了几个主线条,得出旋转 -5 度,则可以摆正图片。

当然,这个 detectRotation()函数还不是很智能,可用性还有待改进。

最后, 把本文所有代码和主程序贴上来(有点长,不过方便复制)。配置好OpenCV开发环境,把代码复制下来,就可以调试了。

代码中需要说明的是:  由于opencv的滚动条只能显示正值。 本例中rotation 的 滚动条,值为100时表示旋转角度为0。 如果小于100, 表示旋转角度为负。

#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <cmath>

using namespace std;
using namespace cv;

#define SHOW_LINE

#define BASE 100

static string source_window = "source";
static string window_name = "image rotate";
static Mat src;
static int rotateDegree = 0 + BASE;
static int clip = 0;

//图像旋转: src为原图像, dst为新图像, angle为旋转角度(正值为顺时针旋转,负值为逆时针旋转)
int imageRotate1(InputArray src, OutputArray dst, double angle)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(width, height));

	return 0;
}

//图像旋转: src为原图像, dst为新图像, angle为旋转角度
int imageRotate2(InputArray src, OutputArray dst, double angle)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//计算新图像大小
	double angle1 = angle  * CV_PI / 180. ;
	double a = sin(angle1) * scale;
	double b = cos(angle1) * scale;
	double out_width = height * fabs(a) + width * fabs(b);
	double out_height = width * fabs(a) + height * fabs(b);

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(out_width, out_height));

	return 0;
}

//图像旋转: src为原图像, dst为新图像, angle为旋转角度
int imageRotate3(InputArray src, OutputArray dst, double angle)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//计算新图像大小
	double angle1 = angle  * CV_PI / 180. ;
	double a = sin(angle1) * scale;
	double b = cos(angle1) * scale;
	double out_width = height * fabs(a) + width * fabs(b);
	double out_height = width * fabs(a) + height * fabs(b);

	//在旋转变换矩阵中加入平移量
	trans_mat.at<double>(0, 2) += cvRound( (out_width - width) / 2 );
	trans_mat.at<double>(1, 2) += cvRound( (out_height - height) / 2);

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(out_width, out_height));

	return 0;
}

//图像旋转: src为原图像, dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式
int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	//得到图像大小
	int width = input.cols;
	int height = input.rows;

	//计算图像中心点
	Point2f center;
	center.x = width / 2.0;
	center.y = height / 2.0;

	//获得旋转变换矩阵
	double scale = 1.0;
	Mat trans_mat = getRotationMatrix2D( center, -angle, scale );

	//计算新图像大小
	double angle1 = angle  * CV_PI / 180. ;
	double a = sin(angle1) * scale;
	double b = cos(angle1) * scale;
	double out_width = height * fabs(a) + width * fabs(b); //外边框长度
	double out_height = width * fabs(a) + height * fabs(b);//外边框高度

	int new_width, new_height;
	if ( ! isClip ) {
		new_width = cvRound(out_width);
		new_height = cvRound(out_height);
	} else {
		//calculate width and height of clip rect
		double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
		double len = width * fabs(b);
		double Y = len / ( 1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)) );
		double X = Y * 1 / fabs(tan(angle2));
		new_width = cvRound(out_width - X * 2);
		new_height= cvRound(out_height - Y * 2);
	}

	//在旋转变换矩阵中加入平移量
	trans_mat.at<double>(0, 2) += cvRound( (new_width - width) / 2 );
	trans_mat.at<double>(1, 2) += cvRound( (new_height - height) / 2);

	//仿射变换
	warpAffine( input, dst, trans_mat, Size(new_width, new_height));

	return 0;
}

/**
 * 检测图像倾斜度
 * 返回值:返回0表示无检测结果,返回非0表示摆正图象需要旋转的角度(-10至10度)
 */
double detectRotation(InputArray src)
{
	double max_angle = 6; //可旋转的最大角度

	Mat in = src.getMat();
	if( in.empty() ) return 0;

	Mat input;

	//转为灰度图
	if ( in.type() == CV_8UC1 )
		input = in;
	else if ( in.type() == CV_8UC3 )
		cvtColor(in, input, CV_BGR2GRAY);
	else if ( in.type() == CV_8UC3 )
		cvtColor(in, input, CV_BGRA2GRAY);
	else
		return 0;

	Mat dst, cdst;

	//执行Canny边缘检测(检测结果为dst, 为黑白图)
	double threshold1 = 90;
	Canny(src, dst, threshold1, threshold1 * 3, 3);

	//将Canny边缘检测结果转化为灰度图像(cdst)
	cvtColor(dst, cdst, CV_GRAY2BGR);

	//执行霍夫线变换,检测直线
	vector<Vec4i> lines; //存放检测结果的vector
	double minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度
	double maxLineGap = std::min(dst.cols, dst.rows) * 0.03 ; //最小线间距
	int threshold = 90;
	HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap );

	//分析所需变量
	int x1, y1, x2 , y2; //直线的两个端点
	int x, y;  //直线的中点
	double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度
	double line_length; //直线长度
	double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
	double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)
	double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度
	main_lens[0] = main_lens[1] = 0;
	main_angles[0] = main_angles[1] = 0;

	//逐个分析各条直线,判断哪个是主线条
	for( size_t i = 0; i < lines.size(); i++ ) {
		//取得直线的两个端点座标
		x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];
		x = (x1 + x2 ) / 2; y = (y1 + y2) / 2;
		//计算直线的角度
		angle =	(x1 == x2) ? 90 : ( atan ( (y1 - y2) * 1.0 / (x2 - x1) ) ) / CV_PI * 180;
		//摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。
		if ( fabs(angle - 0) <= max_angle ) {
			rotate_angle = angle - 0;
		} else if ( fabs(angle - 90) <= max_angle ) {
			rotate_angle = angle - 90;
		} else {
			continue;
		}

		//计算线的长度
		line_length = sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)  );
		//计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
		position_weighted = 1;
		if ( x < dst.cols / 4 || x > dst.cols * 3 / 4  ) position_weighted *= 0.8;
		if ( x < dst.cols / 6 || x > dst.cols * 5 / 6  ) position_weighted *= 0.5;
		if ( x < dst.cols / 8 || x > dst.cols * 7 / 8  ) position_weighted *= 0.5;
		if ( y < dst.rows / 4 || y > dst.rows * 3 / 4  ) position_weighted *= 0.8;
		if ( y < dst.rows / 6 || y > dst.rows * 5 / 6  ) position_weighted *= 0.5;
		if ( y < dst.rows / 8 || y > dst.rows * 7 / 8  ) position_weighted *= 0.5;

		//如果 直线长度 * 位置权重 < 最小长度, 则这条线无效
		line_length = line_length * position_weighted;
		if ( line_length < minLineLength ) continue;

		//如果长度为前两名,则存入数据
		if ( line_length > main_lens[1] )  {
			if (line_length > main_lens[0]) {
				 main_lens[1] = main_lens[0];
				 main_lens[0] = line_length;
				 main_angles[1] = main_angles[0];
				 main_angles[0] = rotate_angle;
				 //如果定义了 SHOW_LINE, 则将该线条画出来
				 #ifdef SHOW_LINE
				 line( cdst, Point(x1, y1), Point(x2, y2), Scalar(0,0,255), 3, CV_AA);
				 #endif
			} else {
				main_lens[1] = line_length;
				main_angles[1] = rotate_angle;
			}
		}
	}

	//如果定义了 SHOW_LINE, 则在source_window中显示cdst
    #ifdef SHOW_LINE
	imshow(source_window, cdst);
	#endif

	//最后,分析最长的二条直线,得出结果
	if ( main_lens[0] > 0 ) {
		//如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值
		if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2 )) {
			return (main_angles[0] + main_angles[1] ) / 2;
		} else {
			return main_angles[0];   //否则,返回最长的线需要旋转的角度
		}
	} else {
		return 0;
	}
}

static void callbackAdjust(int , void *)
{
	Mat dst;

	//imageRotate1(src, dst, rotateDegree - BASE);
	//imageRotate2(src, dst, rotateDegree - BASE);
	//imageRotate3(src, dst, rotateDegree - BASE);

	bool isClip = ( clip == 1 );
	imageRotate4(src, dst, rotateDegree - BASE,  isClip );

	imshow(window_name, dst);
}

int main()
{
	src = imread("building.jpg");

	if ( !src.data ) {
		cout << "error read image" << endl;
		return -1;
	}

	namedWindow(source_window);
	imshow(source_window, src);

	namedWindow(window_name);
	createTrackbar("rotate", window_name, &rotateDegree, BASE * 2, callbackAdjust);
	createTrackbar("clip", window_name, &clip, 1, callbackAdjust);

	//自动检测旋转角度
	double angle = detectRotation(src);
	if ( angle != 0 ) {
		rotateDegree = angle + BASE;
		setTrackbarPos("rotate", window_name, rotateDegree);
	}

	callbackAdjust(0, 0);

	waitKey();

        return 0;

}
时间: 2024-10-10 06:50:42

用OpenCV实现Photoshop算法(一): 图像旋转的相关文章

用OpenCV实现Photoshop算法(三): 曲线调整

http://blog.csdn.net/c80486/article/details/52499919 系列文章: 用OpenCV实现Photoshop算法(一): 图像旋转 用OpenCV实现Photoshop算法(二): 图像剪切 用OpenCV实现Photoshop算法(三): 曲线调整 用OpenCV实现Photoshop算法(四): 色阶调整 用OpenCV实现Photoshop算法(五): 亮度对比度调整 用OpenCV实现Photoshop算法(六): 变为黑白图像 用OpenC

opencv分水岭算法对图像进行切割

先看效果 说明 使用分水岭算法对图像进行切割,设置一个标记图像能达到比較好的效果,还能防止过度切割. 1.这里首先对阈值化的二值图像进行腐蚀,去掉小的白色区域,得到图像的前景区域.并对前景区域用255白色标记 2.相同对阈值化后的图像进行膨胀,然后再阈值化并取反.得到背景区域. 并用128灰度表示 3.将前景和背景叠加在一起在同一幅图像中显示. 4.用标记图和原图,利用opencv的watershed对图像进行切割. 源代码 class WatershedSegment{ private: cv

每日算法之三十七:Rotate Image (图像旋转)

You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). Follow up: Could you do this in-place? 原地图像顺时针旋转90度.因为要求空间复杂度是常数,因此应该迭代旋转操作. class Solution { public: void rotate(vector<vector<int> > &mat

使用OpenCL+OpenCV实现图像旋转(一)

[题外话]近期申请了一个微信公众号:平凡程式人生.有兴趣的朋友可以关注,那里将会涉及更多更新OpenCL+OpenCV以及图像处理方面的文章. 最近在学习<OPENCL异构计算>,其中有一个实例是使用OpenCL实现图像旋转.这个实例中并没有涉及读取.保存.显示图像等操作,其中也存在一些小bug.在学习OpenCL之初,完整地实现这个实例还是很有意义的事情. 1.图像旋转原理 所谓图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程.这个点通常就是图像的中心. 由于是按照中心旋

【OpenCV】图像旋转详解,边缘用黑色填充

项目要用到图像旋转,OpenCV里面居然没有专门封装好的函数,只好自己写了.根据<learnning OpenCV>发现效果不是很理想,旋转后图像大小不变,可是图像却被裁减了. 例子如下: int main( int argc, char** argv ) { IplImage* src=cvLoadImage("C:\\Users\\Liu\\Desktop\\bridge.bmp",1); IplImage* dst = cvCloneImage( src ); int

opencv分水岭算法对图像进行分割

先看效果 说明 使用分水岭算法对图像进行分割,设置一个标记图像能达到比较好的效果,还能防止过度分割. 1.这里首先对阈值化的二值图像进行腐蚀,去掉小的白色区域,得到图像的前景区域.并对前景区域用255白色标记 2.同样对阈值化后的图像进行膨胀,然后再阈值化并取反.得到背景区域.并用128灰度表示 3.将前景和背景叠加在一起在同一幅图像中显示. 4.用标记图和原图,利用opencv的watershed对图像进行分割. 源码 class WatershedSegment{ private: cv::

OpenCV利用矩阵实现图像旋转

利用OpenCV的矩阵操作实现图像的逆时针旋转90度操作 代码 Mat src = imread("C:\\Users\\fenggl\\Desktop\\测试.jpg",MREAD_UNCHANGED); imshow("原图", src); transpose(src, src); flip(src, src, 0); imshow("逆时针旋转90度", src); 效果 解释说明 第一步:读入原图 Mat src = imread(&qu

基于c++和opencv底层的图像旋转

图像旋转:本质上是对旋转后的图片中的每个像素计算在原图的位置. 在opencv包里有自带的旋转函数,当你知道倾斜角度theta时: 用getRotationMatrix2D可得2X3的旋转变换矩阵 M,在用warpaffine函数可得倾斜后的图像dst. 很方便啊,为什么还要自己实现底层的图像旋转呢?因为有些地方你用这两个函数就会出现问题,比如说: 当原图的size是MXN,且图像是完全填充的(因为如果有留白可能还不能将问题完全反映出来),现在你需要将它90°变换(为了形象说明),可是用前面两个

图像旋转算法

图像旋转是指把定义的图像绕某一点以逆时针或顺时针方向旋转一定的角度,通常是指绕图像的中心以逆时针方向旋转. 假设图像的左上角为(left, top),右下角为(right, bottom),则图像上任意点(x0, y0)绕其中心(xcenter, ycenter)顺时针旋转θ角度后,新的坐标位置(x′, y′)的计算公式为: xcenter = (right - left + 1) / 2 + left;ycenter = (bottom - top + 1) / 2 + top;x′ = (x