图像缩放与旋转
图像缩放
具体方法
-
最近邻插值: 最近邻插值是一种简单的图像缩放方法。它基于这样的假设:在缩放过程中,像素值不变,只是改变了像素的位置。在最近邻插值中,当需要缩小图像时,将目标图像中每个像素对应到原始图像中最接近的像素,然后将该像素的值赋给目标图像。当需要放大图像时,目标图像中的每个像素会有多个最近邻像素,可以通过插值方法(如取最近邻像素的平均值)来确定像素值。
-
双线性插值: 双线性插值是一种基于像素邻域的插值方法。它基于这样的假设:在缩放过程中,像素值的变化是平滑的。在双线性插值中,对于目标图像中的每个像素,它的像素值是由原始图像中距离最近的四个像素的像素值进行加权平均得到的。这四个像素是目标图像像素在原始图像中所在位置的四个最近邻像素。
-
双三次插值: 双三次插值是一种更高级的插值方法,它考虑了更多像素邻域的信息。在双三次插值中,对于目标图像中的每个像素,它的像素值是由原始图像中距离最近的16个像素的像素值进行加权平均得到的。这些像素是目标图像像素在原始图像中所在位置的一个邻域。
实现代码
c++
// 双线性插值法实现图像缩放函数
cv::Mat myresize(const cv::Mat& inputImage, double fscale_x, double fscale_y)
{
int width = inputImage.cols;
int height = inputImage.rows;
int newWidth = static_cast<int>(width * fscale_x);
int newHeight = static_cast<int>(height * fscale_y);
cv::Mat resizedImage(newHeight, newWidth, inputImage.type());
// 计算缩放比例的倒数
double invScale_x = 1.0 / fscale_x;
double invScale_y = 1.0 / fscale_y;
// 遍历目标图像的每个像素
for (int i = 0; i < newHeight; i++)
{
for (int j = 0; j < newWidth; j++)
{
// 计算原图像上的对应位置
double x = j * invScale_x;
double y = i * invScale_y;
// 计算四个最近邻像素坐标
int x1 = static_cast<int>(std::floor(x));
int y1 = static_cast<int>(std::floor(y));
int x2 = x1 + 1;
int y2 = y1 + 1;
// 边界处理
if (x2 >= width) x2 = width - 1;
if (y2 >= height) y2 = height - 1;
// 计算四个最近邻像素的权重
double weight_x = x - x1;
double weight_y = y - y1;
// 双线性插值计算像素值
double top_left = inputImage.at<uchar>(y1, x1);
double top_right = inputImage.at<uchar>(y1, x2);
double bottom_left = inputImage.at<uchar>(y2, x1);
double bottom_right = inputImage.at<uchar>(y2, x2);
double interpolated_value = (1 - weight_x) * (1 - weight_y) * top_left +
weight_x * (1 - weight_y) * top_right +
(1 - weight_x) * weight_y * bottom_left +
weight_x * weight_y * bottom_right;
// 将插值结果赋给目标图像
resizedImage.at<uchar>(i, j) = static_cast<uchar>(interpolated_value);
}
}
return resizedImage;
}
matlab
function outputImage = mysize(inputImage, x, y)
% 缩小图像到指定寸x和y,使用双线性插值法
% 获取输入图像的大小
[height, width, ~] = size(inputImage);
% 计算输出图像的大小
outputWidth = round(width*x);
outputHeight = round(height*y);
% 初始化输出图像
outputImage = zeros(outputHeight, outputWidth, 3, 'uint8');
% 计算插值系数
for i = 1:outputHeight-1
for j = 1:outputWidth-1
% 计算在输入图像中的位置
xIn = (j+0.5)/x-0.5;
%防止越界
if xIn<0
xIn=0;
end
if xIn>=width
xIn = width-2;
end
yIn = (i+0.5)/y-0.5;
if yIn<0
yIn=0;
end
if yIn>=width
yIn = width-2;
end
% 计算四个最近邻像素的位置
x1 = floor(xIn);
x2 = ceil(xIn);
y1 = floor(yIn);
y2 = ceil(yIn);
% 计算四个最近邻像素的权重
wx1 = (x2 - xIn) / (x2 - x1);
wx2 = (xIn - x1) / (x2 - x1);
wy1 = (y2 - yIn) / (y2 - y1);
wy2 = (yIn - y1) / (y2 - y1);
% 计算插值像素的值
pixel1 = inputImage(y1, x1, :);
pixel2 = inputImage(y1, x2, :);
pixel3 = inputImage(y2, x1, :);
pixel4 = inputImage(y2, x2, :);
outputImage(i, j, :) = wx1 * wy1 * double(pixel1) + wx2 * wy1 * double(pixel2) + wx1 * wy2 * double(pixel3) + wx2 * wy2 * double(pixel4);
end
end
实验结果
c++
输入参数
double fscale_x = 0.5; // 水平缩放比例为原图像的一半
double fscale_y = 0.5; // 垂直缩放比例为原图像的一半
cv::Mat resizedImage = myresize(img, fscale_x, fscale_y);
cv::imshow("缩放后的图像", resizedImage);
matlab
输入参数
%实现图像缩放
x=0.5;
y=0.5;
I4 =mysize(scrimage,x,y);
subplot(336);imshow(uint8(I4));title('缩放图像');
axis on;
图像旋转
代码实现
c++
// 图像旋转函数
cv::Mat myrotate(const cv::Mat& img, double degree, int centerX, int centerY)
{
// 将角度转换为弧度
double radians = degree * CV_PI / 180.0;
// 获取原图像尺寸
int m = img.rows;
int n = img.cols;
// 计算旋转后图像的大小
int m2 = std::ceil(std::abs(m * std::cos(radians)) + std::abs(n * std::sin(radians)));
int n2 = std::ceil(std::abs(n * std::cos(radians)) + std::abs(m * std::sin(radians)));
// 创建旋转后图像
cv::Mat new_img(m2, n2, CV_8UC1, cv::Scalar(0));
// 计算旋转中心相对于图像中心的偏移量
int offsetX = centerX - n / 2;
int offsetY = centerY - m / 2;
// 旋转逆运算的矩阵描述
cv::Mat mat_1 = (cv::Mat_<double>(3, 3) << 1, 0, 0, 0, -1, 0, -0.5 * n2, 0.5 * m2, 1);
cv::Mat mat_2 = (cv::Mat_<double>(3, 3) << std::cos(radians), std::sin(radians), 0, -std::sin(radians), std::cos(radians), 0, 0, 0, 1);
cv::Mat mat_3 = (cv::Mat_<double>(3, 3) << 1, 0, 0, 0, -1, 0, 0.5 * n, 0.5 * m, 1);
for (int i = 0; i < n2; i++)
{
for (int j = 0; j < m2; j++)
{
// 计算原图坐标
cv::Mat old_coordinate = (cv::Mat_<double>(1, 3) << i, j, 1) * mat_1 * mat_2 * mat_3;
double col = old_coordinate.at<double>(0);
double row = old_coordinate.at<double>(1);
// 将坐标平移到旋转中心
col -= offsetX;
row -= offsetY;
// 防止数组越界
if (row < 0 || col < 0 || row >= m || col >= n)
{
new_img.at<uchar>(j, i) = 0;
}
else
{
// 取周围四点
int left = std::floor(col);
int right = std::ceil(col);
int top = std::floor(row);
int bottom = std::ceil(row);
double a = col - left;
double b = row - top;
// 利用双线性插值计算灰度值
uchar interpolated_value = (1 - a) * (1 - b) * img.at<uchar>(top, left) +
a * (1 - b) * img.at<uchar>(top, right) +
(1 - a) * b * img.at<uchar>(bottom, left) +
a * b * img.at<uchar>(bottom, right);
new_img.at<uchar>(j, i) = interpolated_value;
}
}
}
return new_img;
}
matlab
function new_img=myrotate(image,frangle)
%将角度化成弧度
frangle =deg2rad(frangle);
%获得原图像的大小
[height,width,channels]=size(image);
%计算旋转后图像大小
H = round(abs(height*cos(frangle))+abs(width*sin(frangle)));
W = round(abs(width*cos(frangle))+abs(height*sin(frangle)));
new_img = zeros(H,W,channels);
%旋转逆矩阵的计算
mat_1 = [1 0 0;0 -1 0;-0.5*W 0.5*H 1];%将变换后的坐标恢复成标准
mat_2 = [cos(frangle) sin(frangle) 0;-sin(frangle) cos(frangle) 0;0 0 1];%旋转矩阵的逆矩阵
mat_3 = [1 0 0;0 -1 0;0.5*width 0.5 *height 1];%
for i = 1:W
for j=1:H
%计算原图坐标
old_coordinate = [i j 1]*mat_1*mat_2*mat_3;
col = old_coordinate(1);
row = old_coordinate(2);
%防止数组越界
if row<1 || col<1 || row>height || col>width
new_img(j,i,:) = 0;
else
%取周围四点
left = floor(col);
right = ceil(col);
top = floor(row);
bottom = ceil(row);
a = col - left;
b = row - top;
%利用双线性插值计算灰度值
new_img(j,i,:) = (1-a)*(1-b)*image(top,left,:)+a*(1-b)*image(top,right,:)+...
(1-a)*b*image(bottom,left,:)+a*b*image(bottom,right,:);
end
end
end
实验结果
c++
输入参数
double fangle = 45.0; // 旋转角度为45度
double cx = img.cols/2 ; // 图像中心点的x坐标
double cy = img.rows/2; // 图像中心点的y坐标
cv::Mat rotatedImage = myrotate(img, fangle,cx,cy);
cv::imshow("旋转后的图像", rotatedImage);
matlab
输入参数
%实现图像旋转
frangle=60;
I5=myrotate(scrimage,frangle);
subplot(337);imshow(uint8(I5));title('旋转图像');
axis on;