空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:
所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:
以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:
因此,平移变换的变换矩阵及逆矩阵记为:
将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:
图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针,特别说明,图像的坐标轴与一般数学意义的坐标轴不同)为:
实验环境:
(1)OpenCV3.4.3
(2)Ubuntu16.04
(3)VS Code
(4)C++
// 图像的旋转 与 缩放
#include
#include
#include
#include
#include
#include
class Extra1{
public:
Extra1(std::vector path){
for(int i = 0; i < path.size(); i++){
// 读取彩色图片、灰度图片
original_color_image.push_back(cv::imread(path[i]));
original_gray_image.push_back(color2Gray(original_color_image[i]));
}
}
// 0、彩色图像转灰度图像
cv::Mat color2Gray(cv::Mat src_image){
//创建与原图同类型和同大小的矩阵
cv::Mat gray_image(src_image.rows, src_image.cols, CV_8UC1);
if(src_image.channels()!=1){
for(int i = 0; i < src_image.rows; i++)
for(int j = 0; j < src_image.cols; j++)
gray_image.at(i, j) = (src_image.at(i, j)[0] + src_image.at(i, j)[1] + src_image.at(i, j)[2]) / 3;
}
else
gray_image = src_image.clone();
return gray_image;
}
// 2 旋转图像
void RotateScalingImage(cv::Mat& src_image, int opt=1){
int angle = 0, delta = 1;
std::string s[] = {"仅仅旋转","旋转加缩放"};
while(true){
double factor;
if(opt)
factor = (cos(angle*CV_PI/180.) + 1.05)*2;
else
factor = 1;
int w = src_image.cols, h = src_image.rows;
cv::Mat rot_mat = cv::Mat(2, 2, CV_32F);
//
// [ m0 m1 m2 ] ===> [ A11 A12 b1 ] ===> [cos(a) sin(a) b1]
// [ m3 m4 m5 ] ===> [ A21 A22 b2 ] ===> [-sin(a) cos(a) b2]
//
rot_mat.at(0, 0) = (float)(factor*cos(-angle*2*CV_PI/180.));
rot_mat.at(0, 1) = (float)(factor*sin(-angle*2*CV_PI/180.));
rot_mat.at(1, 0) = -rot_mat.at(0, 1);
rot_mat.at(1, 1) = rot_mat.at(0, 0);
// dst(x,y) = A * src(x,y) + b
cv::Mat rotate_image = cv::Mat::zeros(src_image.rows, src_image.cols, src_image.type());
realizeRotateScaling(src_image, rot_mat, rotate_image);
//cv::Mat middle_filter_image = cv::Mat::zeros(rotate_image.rows, rotate_image.cols, rotate_image.type());
grayFiltering(rotate_image);
cv::imshow(s[opt], rotate_image);
if(cv::waitKey(5) == 27)
break;
angle =(int)(angle + delta) % 360;
}
return;
}
// 2.1 实现旋转 与 缩放 操作
void realizeRotateScaling(cv::Mat& src, cv::Mat& rotmat, cv::Mat& dst){
int i, j, i1, j1;
int centeri = src.rows/2, centerj = src.cols/2;
for(i = 0; i < src.cols; i++)
for(j = 0; j < src.rows; j++){
i1 = (i - centeri)*rotmat.at(0, 0) + (j - centerj)*rotmat.at(1, 0) + centeri;
j1 = (i - centeri)*rotmat.at(0, 1) + (j - centerj)*rotmat.at(1, 1) + centerj;
if(0 <= i1 && i1 < dst.cols && 0 <= j1 && j1 < dst.rows)
dst.at(i1, j1) = src.at(i, j);
}
}
// 3 灰度图像滤波
void grayFiltering(cv::Mat& src, int select=0, int filter_size=9, double Q=1){
int m = filter_size/2;
int n = filter_size*filter_size;
cv::Mat src_clone = src.clone();
for(int i=m; i < src.rows - m; i++)
for(int j=m; j < src.cols - m; j++){
cv::Mat sub_matrix = src_clone(cv::Rect(j - m, i - m, filter_size, filter_size));
if(src.at(i,j) == 0)
src.at(i,j) = maxValueConvolution(sub_matrix, filter_size);
}
}
// 3.0 最大值滤波
int maxValueConvolution(cv::Mat& image_block, int size){
int max = 0;
for(int i=0; i < image_block.rows; i++)
for(int j=0; j < image_block.cols; j++){
if(image_block.at(i, j) > max)
max = image_block.at(i, j);
}
return max;
}
// 3.1 中值滤波
int middleValueConvolution(cv::Mat& image_block, int size=5){
int min = 0, k = 0, pos = size*size/2;
std::vector nums;
for(int k1 = 0; k1 < size; k1++)
for(int k2 = 0; k2 < size; k2++)
nums.push_back(image_block.at(k1,k2));
int middle = findMiddleNum(nums, 0, size*size - 1, pos);
return middle;
}
// 3.2.1 快速查找中位数
int findMiddleNum(std::vector& nums, int begin, int end, int n){
int i = partition(nums, begin, end);
if(i == n)
return nums[i];
else if(i > n)
return findMiddleNum(nums, begin, i-1, n);
else
return findMiddleNum(nums, i+1, end, n);
}
// 3.2.2 交换
void exchange(std::vector& nums, int a,int b){
int c = nums[a];
nums[a] = nums[b];
nums[b] = c;
return;
}
// 3.2.2 快速查找中位数
int partition(std::vector& nums, int begin, int end){
int i = begin, j = end + 1;
int x = nums[begin];
while (true) {
while (nums[++i] < x) {// 向右扫描
if (i == end)
break;
}
while (nums[--j] > x) {// 向左扫描
if (j == begin)
break;
}
if (i >= j) // 指针相遇,切分位置确定
break;
exchange(nums, i, j);// 交换左右逆序元素
}
// 运行到最后:i指向从左往右第一个大于x的元素,j指向从右往左第一个小于x的元素。
exchange(nums, begin, j);// 将切分元素放在切分位置
return j;
}
// 运行
void run(){
RotateScalingImage(original_gray_image[0]);
}
private:
std::vector original_color_image;
std::vector original_gray_image;
};
int main(){
std::vector path;
path.push_back("/home/lyd/image_process/pic/lena.jpg");
Extra1 ex6(path);
ex6.run();
return 1;
}