一、图像的缩放
1.1 图像缩放概念
图像缩放是通过增减像素来改变图像的尺寸的。
1.2 功效
图像缩小,图像变清晰;图像放大,图像变模糊,所以需要插值进一步处理。
1.3 图像缩放变换的矩阵公式
:等比缩放;大于1,放大,小于1,缩小。
1.4 图像的缩小
图像的缩小分为等比缩小和非等比缩小,处理原理是采样偶数或奇数行/列进而得到新的图像。
1.5 图像的放大
图像的放大分为等比放大和等比缩小,处理原理是采用适当的插值添加像素的灰度或颜色值。注:引出图像处理的超分辨问题的研究。
1.6 图像的插值运算
1.6.1 后向插值和前向插值
需求:对于图像缩放、旋转等几何变换一般需要经过变换和插值两个步骤。当数字图像变换后的坐标不一定是整数,需要将非整数坐标点变换到整数坐标处,通常采用插值运算。插值运算的方式分为后向映射和前向映射。
后向映射:通过目标图像中的像素去计算元图像中的像素位置,遇到非整数,利用最近邻插值和双向性插值等插值方法进行得到目标图像的灰度值或颜色值。
前向映射:通过当前元图像的像素去计算得到目标图像中的映射位置,遇到非整数,将该像素的灰度值或颜色值映射到响应的像素。
注:在实际应用中,由于前向映射会使目标图像出现空白像素,后向映射不会出现空白像素,所以常采用带有最近邻插值、双线性插值的后向映射。
1.6.1 灰度重采样
灰度重采样是指通过几何变换的图像,需要重新插值像素灰度的过程,即对输入图像的像素点进行重采样并将结果赋予相应的输出像素。根据目标像素的最近相关像素通过最近邻插值或双线性插值来求目标像素。
1.6.2 图像处理常用的插值方法
图像处理常用的插值方法有:(1)最近邻插值;(2)双线性插值;(3)双三次插值。
1.7 图像处理常用插值算法代码实现--Opencv处理像素方法
1.7.1 主程序
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
// 函数声明
void NearInter(Mat& inputImag_1, Mat& outputImg, double dx, double dy);
int main()
{
double dx = 1.5, dy = 1.5; // 缩放系数
// 载入图像并显示
Mat originImage_boy = imread("baby.png", 1);
imshow("original_boy", originImage_boy);
// 创建效果图
Mat resultImage;
resultImage.create(round(originImage_boy.rows * dx), round(originImage_boy.cols * dy), originImage_boy.type());
// 记录时间
double timeClock = static_cast<double>(getTickCount());
// 函数调用
// 可定义和调用不同的函数
NearInter(originImage_boy, resultImage, dx, dy);
// 输出时间
timeClock = ((double)getTickCount() - timeClock) / getTickCount();
cout << "run time:" << timeClock << "seconds" << endl;
imshow("result",resultImage);
waitKey(0);
return 0;
}
1.7.1 最近邻插值-OpenCV遍历像素
// 函数定义
// 使用动态地址计算图像
void NearInter(Mat& inputImag_1, Mat& outputImg, double dx, double dy)
{
int rowsNum = inputImag_1.rows;
int colsNum = inputImag_1.cols;
for (int i = 0; i < rowsNum; i++) {
for (int j = 0; j < colsNum; j++) {
for (int o_i = round(dx * i); o_i < round(dx * (i + 1)); o_i++) {
for (int o_j = round(dx * j); o_j < round(dy * (j + 1));o_j++) {
outputImg.at<Vec3b>(o_i, o_j)[0] = inputImag_1.at<Vec3b>(i, j)[0];
outputImg.at<Vec3b>(o_i, o_j)[1] = inputImag_1.at<Vec3b>(i, j)[1];
outputImg.at<Vec3b>(o_i, o_j)[2] = inputImag_1.at<Vec3b>(i, j)[2];
}
}
}
}
}
结果图:
1.7.2 双线性邻插值-OpenCV遍历像素
// 函数定义
// 使用动态地址计算图像
void LinearInter(Mat& inputImag_1, Mat& outputImg, double dx, double dy)
{
int rowsNum = inputImag_1.rows;
int colsNum = inputImag_1.cols;
int new_rowsNum = outputImg.rows;
int new_colsNum = outputImg.cols;
int mi, nj;
double ci, cj;
int mci, mcj;
for (int i = 0; i < new_rowsNum; i++) {
ci = i / dx;
mi = (int)ci;
mci = mi + 1;
double u = ci - mi;
for (int j = 0; j < new_colsNum; j++) {
cj = j /dy;
nj = (int)cj;
mcj = nj + 1;
double v = cj - nj;
if (mci > (rowsNum - 1)) {mci = mi - 1;}
if (mcj > (colsNum - 1)) {mcj = nj - 1;}
//计算新点在原图像像上的位置
outputImg.at<Vec3b>(i, j)[0] = (int)inputImag_1.at<Vec3b>(mi, nj)[0] * (1 - u) * (1 - v)
+ inputImag_1.at<Vec3b>(mci, nj)[0] * u * (1 - v)
+ inputImag_1.at<Vec3b>(mi, mcj)[0] * (1 - u) * v
+ inputImag_1.at<Vec3b>(mci, mcj)[0] * u * v;
outputImg.at<Vec3b>(i, j)[1] = (int)inputImag_1.at<Vec3b>(mi, nj)[1] * (1 - u) * (1 - v)
+ inputImag_1.at<Vec3b>(mci, nj)[1] * u * (1 - v)
+ inputImag_1.at<Vec3b>(mi, mcj)[1] * (1 - u) * v
+ inputImag_1.at<Vec3b>(mci, mcj)[1] * u * v;
outputImg.at<Vec3b>(i, j)[2] = (int)inputImag_1.at<Vec3b>(mi, nj)[2] * (1 - u) * (1 - v)
+ inputImag_1.at<Vec3b>(mci, nj)[2] * u * (1 - v)
+ inputImag_1.at<Vec3b>(mi, mcj)[2] * (1 - u) * v
+ inputImag_1.at<Vec3b>(mci, mcj)[2] * u * v;
}
}
}
结果图:
1.7.2 双三次邻插值-OpenCV函数resize()调用
resize()函数原型:其中,可以根据响应的参数,调用最近邻插值、双线性i插值、双三次插值(立方插值)
/************************************************************************/
/*
OpenCV图像缩放使用的函数是:resize
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
参数含义:
InputArray src -原图像
OutputArray dst -输出图像
Size dsize -目标图像的大小
double fx=0 -在x轴上的缩放比例
double fy=0 -在y轴上的缩放比例
int interpolation -插值方式,有以下四种方式
INTER_NN -最近邻插值
INTER_LINEAR -双线性插值 (缺省使用)
INTER_AREA -使用象素关系重采样,当图像缩小时候,该方法可以避免波纹出现。当图像放大时,类似于 INTER_NN 方法。
INTER_CUBIC -立方插值。
说明:dsize与fx和fy必须不能同时为零
*/
/************************************************************************/
1.7.3 调用OpenCV-reszie()实现-双三次插值
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
// 函数声明
int main()
{
// 载入图像并显示
Mat originImage_boy = imread("baby.png", 1);
imshow("original_boy", originImage_boy);
// 创建效果图
Mat resultImage;
// resultImage.create(round(originImage_boy.rows), round(originImage_boy.cols), originImage_boy.type());
// 记录时间
double timeClock = static_cast<double>(getTickCount());
// 函数调用
resize(originImage_boy, resultImage, Size(), 1.5, 1.5, INTER_CUBIC);
// 输出时间
timeClock = ((double)getTickCount() - timeClock) / getTickCount();
cout << "run time:" << timeClock << "seconds" << endl;
imshow("result",resultImage);
waitKey(0);
return 0;
}
双三次插值结果图: