图像插值方法比较 【Opencv|常见图像插值方法比较(opencv)】本文介绍几种经典插值的原理,代码过程以及对性能作出比较。
简单说来,在对原始图像进行缩放、旋转变换的时候,因为在目标图像中像素分布发生了变换,所以需根据一定的映射规则建立从原始图像到目标图像的转换。
Nearest 差值 顾名思义,最近邻插值就是从原始目标中找到最匹配的位置。不多说,直接上代码。
static int nearestArea(Mat &src, float scale_x, float scale_y, char *argc)
{
Mat dstMat1 = Mat(src.rows * scale_y, src.cols * scale_x, src.type());
typedef BOOST_TYPEOF(*src.data) ElementType;
int WidthSrc = https://www.it610.com/article/src.cols;
int HeightSrc = src.rows;
int WidthDst = dstMat1.cols;
int HeightDst = dstMat1.rows;
int chal_num = src.channels();
char name[128];
char **ful_name = &argc;
char *prefix = nullptr;
char *subfix = nullptr;
prefix = strsep(ful_name,".");
subfix = strsep(ful_name, ".");
if (!prefix || !subfix) {
printf("Get subfix from file name failed\n");
return -1;
}
snprintf(name, sizeof(name), "%s_%.2f_%.2f_Nearest.%s", prefix, scale_x, scale_y, subfix);
clock_t start = clock();
for (int w = 0;
w < WidthDst;
w++) {
float rx = ((float)w + 0.5) / scale_x - 0.5;
int sx = cvFloor(rx);
sx = MIN(sx, WidthSrc - 2);
sx = MAX(0, sx);
short rx_sh_buf[2];
rx_sh_buf[0] = (short)((1.f - rx) * 2048);
rx_sh_buf[1] = 2048 - rx_sh_buf[0];
for (int h = 0;
h < HeightDst;
h++) {
float ry = ((float)h + 0.5) / scale_y - 0.5;
int sy = cvFloor(ry);
sy = MIN(sy, HeightSrc - 2);
sy = MAX(0, sy);
switch (chal_num) {
case 1:
dstMat1.at(h, w) =
src.at(sy, sx);
break;
case 2:
for (int m = 0;
m < 2;
m++) {
dstMat1.at>(h, w)[m] =
src.at>(sy, sx)[m];
}
break;
case 3:
for (int m = 0;
m < 3;
m++) {
dstMat1.at>(h, w)[m] =
src.at>(sy, sx)[m];
}
break;
default:
break;
}
}
}
clock_t end = clock();
printf("Interpolation time: %f\n", (float)(end - start) / CLOCKS_PER_SEC * 1000);
if (dstMat1.data) {
imwrite(name, dstMat1);
}return 1;
}
Bi-linear 双线性插值 双线性插值的思路就是利用映射到原始图像匹配点像素临近四个点对目标图像的像素点进行求解。原因在于反向求解匹配像素点的时候,会产生小数,舍入之后会产生较大误差。计算思路可以用下图表示:
文章图片
X方向
f(R1)≈x?x1x2?x?f(Q21)+x2?xx2?x1?f(Q11)
f(R2)≈x?x1x2?x?f(Q12+x2?xx2?x1?f(Q22
Y方向
f(P)≈y?y1y2?y1?f(R1)+y2?yy2?y1?f(R2)
将求得的像素点表示为 (x+u,y+v) , 其中u, v表示小数部分
根据上述推导,目标像素值可以表示为:
f(P)≈(1?u)(1?v)(f(x,y))+(1?u)vf(x,j+1)+u(1?v)f(x+1,y)+uvf(x,y)
将上述公式转化为代码:
static int bilinear(Mat &src, float scale_x, float scale_y, char* argc)
{
Mat dstMat1 = Mat(src.rows * scale_y, src.cols * scale_x, src.type());
typedef BOOST_TYPEOF(*src.data) ElementType;
//用boost 在编译阶段获得数据类型
int WidthSrc = https://www.it610.com/article/src.cols;
int HeightSrc = src.rows;
int WidthDst = dstMat1.cols;
int HeightDst = dstMat1.rows;
int chal_num = src.channels();
char name[128];
char **ful_name = &argc;
char *prefix = nullptr;
char *suffix = nullptr;
prefix = strsep(ful_name,".");
subfix = strsep(ful_name, ".");
//Get Image type through suffix
if (!prefix || ! suffix) {
printf("Get suffix from file name failed\n");
return -1;
}
snprintf(name, sizeof(name), "%s_%.2f_%.2f.%s", prefix, scale_x, scale_y, suffix);
for(int w = 0;
w < WidthDst;
w++) {
float rx = ((float)w + 0.5) / scale_x - 0.5;
//根据中心对称,向左上平移,优化插值
int sx = cvFloor(rx);
rx -= sx;
sx = MIN(sx, WidthSrc - 2);
sx = MAX(0, sx);
for (int h = 0;
h < HeightDst;
h++) {
float ry = ((float)h + 0.5) / scale_y - 0.5;
int sy = cvFloor(ry);
ry -= sy;
sy = MIN(sy, HeightSrc - 2);
sy = MAX(0, sy);
switch (chal_num) {
case 1:
dstMat1.at(h, w) =
(1 - rx)*(1 - ry)*(src.at(sy, sx)) +
(1 - rx)*ry*(src.at(sy, sx + 1)) +
rx*(1 - ry)*(src.at(sy + 1, sx)) +
rx*ry*(src.at(sy + 1, sx + 1));
break;
case 2:
for (int m = 0;
m < 2;
m++) {
dstMat1.at>(h, w)[m] =
(1 - rx)*(1 - ry)*(src.at>(sy, sx)[m]) +
(1 - rx)*ry*(src.at>(sy, sx + 1)[m]) +
rx*(1 - ry)*(src.at>(sy + 1, sx)[m]) +
rx*ry*(src.at>(sy + 1, sx + 1)[m]);
}
break;
case 3:
for (int m = 0;
m < 3;
m++) {
dstMat1.at>(h, w)[m] =
(1 - rx)*(1 - ry)*(src.at>(sy, sx)[m]) +
(1 - rx)*ry*(src.at>(sy, sx + 1)[m]) +
rx*(1 - ry)*(src.at>(sy + 1, sx)[m]) +
rx*ry*(src.at>(sy + 1, sx + 1)[m]);
}
}}
}
if (dstMat1.data) {
imwrite(name, dstMat1);
}return 1;
}
本质上插值的过程是目标图像到原始图像的映射,由此需要保证的一点就是前后两者之间的中心点应该是对齐的。实际运算中,目标图像像素值计算有可能会在原始图像中有所偏移。
文章图片
查看上图,因为直接计算会导致目标图像映射到原始图像的像素值向左上角偏移,所以在计算之前做了一个 (dx,dy) 偏移, 实际过程中取 dx=0.5,dy=0.5 。
推荐阅读
- 人脸识别|【人脸识别系列】| 实现自动化妆
- OpenCV|OpenCV-Python实战(18)——深度学习简介与入门示例
- opencv|图像处理之椒盐噪声的添加与去除
- 人脸识别|【人脸识别系列】| 实现人脸截图保存并编写128维特征向量
- opencv|网络爬虫入门练习
- OpenCV|【OpenCV 完整例程】89. 带阻滤波器的传递函数
- OpenCV|【OpenCV 完整例程】90. 频率域陷波滤波器
- OpenCV|【OpenCV 完整例程】22. 图像添加非中文文字
- OpenCV|【OpenCV 完整例程】91. 高斯噪声、瑞利噪声、爱尔兰噪声
- opencv|python+opencv车道线,实线虚线的检测