图像的平移、镜像原理都很简单,一搜一大堆。
具体可以参考博客:点击打开链接
实现代码如下:
#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;
}