图像处理(七):图像变换

图像的平移、镜像原理都很简单,一搜一大堆。

具体可以参考博客:点击打开链接

实现代码如下:

#include <opencv2/opencv.hpp>

using namespace cv;

int main()
{
	Mat src = imread("1.jpg");
	Mat gray;
	cvtColor(src, gray, CV_BGR2GRAY);

	//1、 平移变换
	int Xoffset = 30, Yoffset = 50;
	Mat Moved_img = Mat::zeros(gray.rows + Xoffset, gray.cols + Yoffset, CV_8UC1);
	int i, j;
	for (i = Xoffset; i < Moved_img.rows; i++)
		for (j = Yoffset; j < Moved_img.cols; j++)
		{
			Moved_img.at<uchar>(i, j) = gray.at<uchar>(i - Xoffset , j - Yoffset);
		}

	// 2、镜像
	// 2.1、水平镜像
	Mat Rotated_x = Mat::zeros(gray.rows, gray.cols, CV_8UC1);
	Mat Rotated_y = Mat::zeros(gray.rows, gray.cols, CV_8UC1);
	int c = 0;
	// 计算对称中心
	if (gray.cols % 2 ==  0)
	{
		c = gray.cols / 2;
	}
	else
	{
		c = gray.cols / 2 + 1;
	}

	for (i = 0; i < gray.rows; i++)
		for (j = 0; j < c; j++)
		{
			Rotated_x.at<uchar>(i, j) = gray.at<uchar>(i, gray.cols - 1 - j);
			Rotated_x.at<uchar>(i, gray.cols - 1 - j) = gray.at<uchar>(i, j);
		}

	// 2.2、垂直方向
	// 计算对称中心
	c = 0;
	if (gray.rows % 2 == 0)
	{
		c = gray.rows / 2;
	}
	else
	{
		c = gray.rows / 2 + 1;
	}

	for (i = 0; i < c; i++)
		for (j = 0; j < gray.cols; j++)
		{
			Rotated_y.at<uchar>(i, j) = gray.at<uchar>(gray.rows - 1 - i, j);
			Rotated_y.at<uchar>(gray.rows - 1 - i, j) = gray.at<uchar>(i, j);
		}
	namedWindow("gray", 0);
	resizeWindow("gray", 300, 300);

	namedWindow("Moved_img", 0);
	resizeWindow("Moved_img", 300, 300);

	namedWindow("Rotated_x", 0);
	resizeWindow("Rotated_x", 300, 300);

	namedWindow("Rotated_y", 0);
	resizeWindow("Rotated_y", 300, 300);

	imshow("gray", gray);
	imshow("Moved_img", Moved_img);
	imshow("Rotated_x", Rotated_x);
	imshow("Rotated_y", Rotated_y);

	waitKey();

	return 0;


}
效果如下:


图像旋转原理,可以参考
JoStudio博客:点击打开链接,他的这个系列博客,使用opencv实现photoshop系列博客真心不错,有兴趣的可以follow一下,代码如下:

#include <opencv2/opencv.hpp>
#include <math.h>

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("1.jpg");
	float angle = 30 * CV_PI / 180;

	int new_R = ceil(abs(src.cols * cos(angle)) + abs(src.rows * sin(angle)));
	int new_C = ceil(abs(src.cols * sin(angle)) + abs(src.rows * cos(angle)));

	Mat Rotated_img = Mat::zeros(new_R, new_C, CV_8UC3);

	// 旋转
	// 1、旋转中心
	Point C = Point(src.rows / 2, src.cols / 2);  // 旋转中心
	int i, j;
	for (i = 0; i < src.rows; i++)
		for (j = 0; j < src.cols; j++)
		{
			Point curr_p = { 0 };

			// 绕中心旋转
			curr_p.x = (i - src.rows / 2) * cos(angle) - (j - src.cols / 2) * sin(angle);
			curr_p.y = (i - src.rows / 2) * sin(angle) + (j - src.cols / 2) * cos(angle);

			// 对应于新坐标系中坐标
			curr_p.x += new_R / 2;
			curr_p.y += new_C / 2;

			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[0] = src.at<Vec3b>(i, j)[0];
			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[1] = src.at<Vec3b>(i, j)[1];
			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[2] = src.at<Vec3b>(i, j)[2];
		}

	imshow("Rotated", Rotated_img);
	waitKey();

	return 0;



}
代码效果如下:


可以看到,图像变得模糊了,准确地说是图像中有的位置没有像素,这就有点郁闷了,然后参看了MLK的博客:点击打开链接,了解到我现在使用的旋转方法为:正向映射法,使用正向映射法时,由于求旋转坐标过程中会出现小数,直接取整会导致有的像素取不到值,所以旋转过程一半采用反向映射法,然后进行插值使图像连续。

反向映射+插值的代码正在写~~

双线性插值原理可以参考博客:点击打开链接,向大佬致敬!

代码如下:

#include <opencv2/opencv.hpp>
#include <math.h>

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("1.jpg");
	float angle = 30 * CV_PI / 180;

	// 保证图像宽和高都为正
	int new_R = ceil( abs(src.cols * cos(angle)) + abs(src.rows * sin(angle)) );
	int new_C = ceil( abs(src.cols * sin(angle)) + abs(src.rows * cos(angle)) );

	Mat Rotated_img = Mat::zeros(new_R, new_C, CV_8UC3);
	Mat Rotated_img_back = Mat::zeros(new_R, new_C, CV_8UC3);

	// 旋转
	// 前向映射法
	// 1、旋转中心
	Point C = Point(src.rows / 2, src.cols / 2);  // 旋转中心
	int i, j, k;
	for (i = 0; i < src.rows; i++)
		for (j = 0; j < src.cols; j++)
		{
			Point curr_p = { 0 };

			// 绕中心旋转
			curr_p.x = (i - src.rows / 2) * cos(angle) - (j - src.cols / 2) * sin(angle);
			curr_p.y = (i - src.rows / 2) * sin(angle) + (j - src.cols / 2) * cos(angle);

			// 对应于新坐标系中坐标
			curr_p.x += new_R / 2;
			curr_p.y += new_C / 2;

			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[0] = src.at<Vec3b>(i, j)[0];
			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[1] = src.at<Vec3b>(i, j)[1];
			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[2] = src.at<Vec3b>(i, j)[2];
		}

	// 反向映射
	for (i = 0; i < Rotated_img_back.rows; i++)
		for (j = 0; j < Rotated_img_back.cols; j++)
		{
			Point2f curr_p = { 0 };

			// 将旋转中心移到图像中心
			float cx = i - Rotated_img_back.rows / 2.0;
			float cy = j - Rotated_img_back.cols / 2.0;

			// 反向投影回原坐标系
			curr_p.x = cx * cos(angle) + cy * sin(angle) + src.rows / 2;
			curr_p.y = -cx * sin(angle) + cy * cos(angle) + src.cols / 2;

			// 双线性插值,四邻域
			int low_x = 0;
			int low_y = 0;
			int high_x = 0;
			int high_y = 0;

			// ceil 向上取整, floor 向下取整
			low_x = floor(curr_p.x);
			low_y = floor(curr_p.y);
			high_x = ceil(curr_p.x);
			high_y = ceil(curr_p.y);

			float f0 = 0, f1 = 0;
			if ( (low_x >= 0 ) && (low_y >= 0)  && 
				(high_x < src.cols) && (high_y < src.rows))
			{
				for (k = 0; k < 3; k++)
				{
			
					f0 = (high_x - curr_p.x) * src.at<Vec3b>(low_x, low_y)[k] + (curr_p.x - low_x) * src.at<Vec3b>(high_x, low_y)[k];
					f1 = (high_x - curr_p.x) * src.at<Vec3b>(low_x, high_y)[k] + (curr_p.x - low_x) * src.at<Vec3b>(high_x, high_y)[k];
					
					Rotated_img_back.at<Vec3b>(i, j)[k] =(high_y - curr_p.y) * f0 + (curr_p.y - low_y) * f1;
				}
				
			}


		}


	imshow("src", src);
	imshow("Rotated", Rotated_img);
	imshow("Rotated_back", Rotated_img_back);
	waitKey();

	return 0;



}
效果如下:


可以看到,旋转后,图片中有两条直线,原因是在进行双线性插值时,可能图片映射到原图时,刚好有对应的整数坐标,这里需要进行一下坐标判断,然后,将图片缩放功能加进去了,代码如下:

#include <opencv2/opencv.hpp>
#include <math.h>

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("1.jpg");
	float angle = 30 * CV_PI / 180;

	// 保证图像宽和高都为正
	int new_R = ceil( abs(src.cols * cos(angle)) + abs(src.rows * sin(angle)) );
	int new_C = ceil( abs(src.cols * sin(angle)) + abs(src.rows * cos(angle)) );

	Mat Rotated_img = Mat::zeros(new_R, new_C, CV_8UC3);
	Mat Rotated_img_back = Mat::zeros(new_R, new_C, CV_8UC3);

	// 旋转
	// 前向映射法
	// 1、旋转中心
	Point C = Point(src.rows / 2, src.cols / 2);  // 旋转中心
	int i, j, k;
	for (i = 0; i < src.rows; i++)
		for (j = 0; j < src.cols; j++)
		{
			Point curr_p = { 0 };

			// 绕中心旋转
			curr_p.x = (i - src.rows / 2) * cos(angle) - (j - src.cols / 2) * sin(angle);
			curr_p.y = (i - src.rows / 2) * sin(angle) + (j - src.cols / 2) * cos(angle);

			// 对应于新坐标系中坐标
			curr_p.x += new_R / 2;
			curr_p.y += new_C / 2;

			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[0] = src.at<Vec3b>(i, j)[0];
			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[1] = src.at<Vec3b>(i, j)[1];
			Rotated_img.at<Vec3b>(curr_p.x, curr_p.y)[2] = src.at<Vec3b>(i, j)[2];
		}

	//2、 反向映射
	for (i = 0; i < Rotated_img_back.rows; i++)
		for (j = 0; j < Rotated_img_back.cols; j++)
		{
			Point2f curr_p = { 0 };

			// 将旋转中心移到图像中心
			float cx = i - Rotated_img_back.rows / 2.0;
			float cy = j - Rotated_img_back.cols / 2.0;

			// 反向投影回原坐标系
			curr_p.x = cx * cos(angle) + cy * sin(angle) + src.rows / 2;
			curr_p.y = -cx * sin(angle) + cy * cos(angle) + src.cols / 2;

			// 双线性插值,四邻域
			int low_x = 0;
			int low_y = 0;
			int high_x = 0;
			int high_y = 0;

			// ceil 向上取整, floor 向下取整
			low_x = floor(curr_p.x);
			low_y = floor(curr_p.y);
			high_x = ceil(curr_p.x);
			high_y = ceil(curr_p.y);

			// 如果刚好有对应位置
			float f0 = 0, f1 = 0;
			if (abs(low_x - high_x) < 0.1 && abs(low_y - high_y) < 0.1)
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{
						Rotated_img_back.at<Vec3b>(i, j)[k] = src.at<Vec3b>(low_x, low_y)[k];
					}
				}

			}
			else if (abs(low_x - high_x) < 0.1 && abs(low_y - high_y) == 1)
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{
						Rotated_img_back.at<Vec3b>(i, j)[k] = (high_y - curr_p.y) * src.at<Vec3b>(low_x, low_y)[k] +
							(curr_p.y - low_y) * src.at<Vec3b>(low_x, high_y)[k];
					}
				}
			}
			else if (abs(low_x - high_x) == 1 && abs(low_y - high_y) < 0.1)
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{
						Rotated_img_back.at<Vec3b>(i, j)[k] = (high_x - curr_p.x) * src.at<Vec3b>(low_x, low_y)[k] +
							(curr_p.x - low_x) *  src.at<Vec3b>(high_x, high_y)[k];
					}
				}
			}
			else
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{

						f0 = (high_x - curr_p.x) * src.at<Vec3b>(low_x, low_y)[k] + (curr_p.x - low_x) * src.at<Vec3b>(high_x, low_y)[k];
						f1 = (high_x - curr_p.x) * src.at<Vec3b>(low_x, high_y)[k] + (curr_p.x - low_x) * src.at<Vec3b>(high_x, high_y)[k];

						Rotated_img_back.at<Vec3b>(i, j)[k] = (high_y - curr_p.y) * f0 + (curr_p.y - low_y) * f1;
					}
				}
			}

		


		}

	// 3、缩放
	float m_scale = 1.2;
	// 保证图像宽和高都为正
	int new_R_s = ceil(m_scale * src.rows);
	int new_C_s = ceil(m_scale * src.cols);

	Mat scale_img = Mat::zeros(new_R_s, new_C_s, CV_8UC3);
	for (i = 0; i < new_R_s; i++)
		for (j = 0; j < new_C_s; j++)
		{
			Point2f curr_p = { 0 };
			curr_p.x = i / m_scale;
			curr_p.y = j / m_scale;

			// 双线性插值,四邻域
			int low_x = 0;
			int low_y = 0;
			int high_x = 0;
			int high_y = 0;

			// ceil 向上取整, floor 向下取整
			low_x = floor(curr_p.x);
			low_y = floor(curr_p.y);
			high_x = ceil(curr_p.x);
			high_y = ceil(curr_p.y);

			// 如果刚好有对应位置
			float f0 = 0, f1 = 0;
			if (abs(low_x - high_x) < 0.1 && abs(low_y - high_y) < 0.1)
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{
						scale_img.at<Vec3b>(i, j)[k] = src.at<Vec3b>(low_x, low_y)[k];
					}
				}

			}
			else if (abs(low_x - high_x) < 0.1 && abs(low_y - high_y) == 1)
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{
						scale_img.at<Vec3b>(i, j)[k] = (high_y - curr_p.y) * src.at<Vec3b>(low_x, low_y)[k] + 
							                           (curr_p.y - low_y) * src.at<Vec3b>(low_x, high_y)[k];
					}
				}
			}
			else if (abs(low_x - high_x) == 1 && abs(low_y - high_y) < 0.1)
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{
						scale_img.at<Vec3b>(i, j)[k] = (high_x - curr_p.x) * src.at<Vec3b>(low_x, low_y)[k] +
							(curr_p.x - low_x) *  src.at<Vec3b>(high_x, high_y)[k];
					}
				}
			}
			else 
			{
				if ((low_x >= 0) && (low_y >= 0) &&
					(high_x < src.cols) && (high_y < src.rows))
				{
					for (k = 0; k < 3; k++)
					{

						f0 = (high_x - curr_p.x) * src.at<Vec3b>(low_x, low_y)[k] + (curr_p.x - low_x) * src.at<Vec3b>(high_x, low_y)[k];
						f1 = (high_x - curr_p.x) * src.at<Vec3b>(low_x, high_y)[k] + (curr_p.x - low_x) * src.at<Vec3b>(high_x, high_y)[k];

						scale_img.at<Vec3b>(i, j)[k] = (high_y - curr_p.y) * f0 + (curr_p.y - low_y) * f1;
					}
				}
			}


			


		}






	imshow("src", src);
	imshow("Rotated", Rotated_img);
	imshow("Rotated_back", Rotated_img_back);
	imshow("Scale_img", scale_img);
	waitKey();

	return 0;



}
效果如下:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值