常见图像插值方法比较(opencv)
2017-10-13 17:01
295 查看
图像插值方法比较
本文介绍几种经典插值的原理,代码过程以及对性能作出比较。简单说来,在对原始图像进行缩放、旋转变换的时候,因为在目标图像中像素分布发生了变换,所以需根据一定的映射规则建立从原始图像到目标图像的转换。
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 = 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<ElementType>(h, w) = src.at<ElementType>(sy, sx); break; case 2: for (int m = 0; m < 2; m++) { dstMat1.at<Vec<ElementType, 2>>(h, w)[m] = src.at<Vec<ElementType, 2>>(sy, sx)[m]; } break; case 3: for (int m = 0; m < 3; m++) { dstMat1.at<Vec<ElementType, 3>>(h, w)[m] = src.at<Vec<ElementType, 3>>(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 = 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<ElementType>(h, w) = (1 - rx)*(1 - ry)*(src.at<ElementType>(sy, sx)) + (1 - rx)*ry*(src.at<ElementType>(sy, sx + 1)) + rx*(1 - ry)*(src.at<ElementType>(sy + 1, sx)) + rx*ry*(src.at<ElementType>(sy + 1, sx + 1)); break; case 2: for (int m = 0; m < 2; m++) { dstMat1.at<Vec<ElementType, 2>>(h, w)[m] = (1 - rx)*(1 - ry)*(src.at<Vec<ElementType, 2>>(sy, sx)[m]) + (1 - rx)*ry*(src.at<Vec<ElementType, 2>>(sy, sx + 1)[m]) + rx*(1 - ry)*(src.at<Vec<ElementType, 2>>(sy + 1, sx)[m]) + rx*ry*(src.at<Vec<ElementType, 2>>(sy + 1, sx + 1)[m]); } break; case 3: for (int m = 0; m < 3; m++) { dstMat1.at<Vec<ElementType, 3>>(h, w)[m] = (1 - rx)*(1 - ry)*(src.at<Vec<ElementType, 3>>(sy, sx)[m]) + (1 - rx)*ry*(src.at<Vec<ElementType, 3>>(sy, sx + 1)[m]) + rx*(1 - ry)*(src.at<Vec<ElementType, 3>>(sy + 1, sx)[m]) + rx*ry*(src.at<Vec<ElementType, 3>>(sy + 1, sx + 1)[m]); } } } } if (dstMat1.data) { imwrite(name, dstMat1); } return 1; }
本质上插值的过程是目标图像到原始图像的映射,由此需要保证的一点就是前后两者之间的中心点应该是对齐的。实际运算中,目标图像像素值计算有可能会在原始图像中有所偏移。
查看上图,因为直接计算会导致目标图像映射到原始图像的像素值向左上角偏移,所以在计算之前做了一个(dx,dy)偏移, 实际过程中取dx=0.5,dy=0.5。
相关文章推荐
- iOS 基于OpenCV图像比较的常见方法
- [转]openCV: 线性插值方法进行图像放大
- 【OpenCV学习笔记 023】两种图像分割方法比较
- OpenCV2学习笔记(四):两种图像分割方法比较
- OpenCV: 线性插值方法进行图像放大
- 【OpenCV】图像特征的提取以及相似性比较方法
- OpenCV2+入门系列(三):遍历图像的几种方法
- 在Visual C++ 6.0下显示JPEG、GIF等格式标准的图像的一种实现起来比较简便的方法
- [置顶] opencv图像分类方法汇总
- OPENCV+MFC单文档显示图像的两种方法
- 常用图像插值算法分析与比较
- 几种常见排序方法的比较
- OpenCv与Qt的结合,几种方法的比较
- 图像处理常用插值方法总结
- OpenCV学习1 Mat图像的常见读写方式
- 调用OpenCV的cvFindContours方法获取图像边界
- 图像处理库的比较:OpenCV,FreeImage,CImg,CxImage
- opencv Mat 图像数据元素进行排序 、常见Mat数据元素统计计算
- 从读取图像程序比较opencv1.0与opencv2.0以上版本
- OPENCV2 访问图像像素值的三种方法