常见的缩放插值方法有:最邻近插值、双线性内插值、三次卷积法
传统缩放插值是方形矩阵的,所以马赛克都是方形的
绿色区域面积:S(绿)=(R/3)*sqrt((R^2-(R/3)^2))
B所在区域白色区域面积:S(白) = (R*2/3)*(R-sqrt((R^2-(R/3)^2)))-((S(弧)-S(绿))
S(B)=(R*2/3)^2-S(白)
S(A)=(π*R^2)/4-S(B)-(R/3)^2
设R为1,则:
SA = 0.24240268452708302834111528260373
SB = 0.43188436775925417016343445210503
SC = 0.44444444444444444444444444444444
SC/SB = 1.0290820358939030947876874261659
SC/SA = 1.8334963794296915471537108683276
SB/SA = 1.781681455392466335545886919352
|100 178 100|
|100 178 100|
6 PC 7
P2
? P1 PC P3 ?
P4
? ? ? ? ?
可以看出,S(PC)依赖于上下左右四个待计算的区域P1/P2/P3/P4像素值。剩下的就是要考虑怎么从缩放图像的边缘向中心区域递推计算S(PC),最终得到高质量的整幅放大图像。
直观上理解,它是为了得到图像整体平滑,没有马赛克,所以才需要从边界向中间像水一样漫延的全图计算――不是以前的只考虑已知邻域的简单双向线性插值。
// resize.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include "pch.h" #include <iostream> #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include "opencv2/core/hal/interface.h" #include <iostream> using namespace cv; using namespace std; template <typename T> string basic2str(const T &int_temp) { string string_temp; stringstream stream; stream << int_temp; string_temp = stream.str(); //此处也可以用 stream>>string_temp return string_temp; } int resizeCompare(Mat& image, double div, String fileName) { Mat imageNew(image.rows / div, image.cols / div, CV_8UC1, Scalar(1)); Mat imageResize(image.rows / div, image.cols / div, CV_8UC1, Scalar(1)); Mat imageResizeLine(image.rows / div, image.cols / div, CV_8UC1, Scalar(1)); Mat imageResizeLanczos4(image.rows / div, image.cols / div, CV_8UC1, Scalar(1)); resize(image, imageResizeLine, Size(image.cols / div, image.rows / div)); resize(image, imageResizeLanczos4, Size(image.cols / div, image.rows / div), 0, 0, INTER_LANCZOS4); cv::imwrite(fileName.substr(0, fileName.length() - 4) + "Line.png", imageResizeLine); cv::imwrite(fileName.substr(0, fileName.length() - 4) + "Lanczos4.png", imageResizeLanczos4); // 调整分区的权重 double rightRatioValueArray[] = { 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0 }; for (int k = 0; k < 11; k++) { for (int i = 0; i < imageNew.rows; i++) { for (int j = 0; j < imageNew.cols; j++) { int center_x = i * div + div / 2; int center_y = j * div + div / 2; if (center_x > 0 && center_y > 0 && center_x < image.rows - 1 && center_y < image.cols - 1) { int a[10] = { 0 }; a[1] = image.at<uchar>(center_x - 1, center_y - 1); a[2] = image.at<uchar>(center_x - 1, center_y); a[3] = image.at<uchar>(center_x - 1, center_y + 1); a[4] = image.at<uchar>(center_x, center_y - 1); a[5] = image.at<uchar>(center_x, center_y); a[6] = image.at<uchar>(center_x, center_y + 1); a[7] = image.at<uchar>(center_x + 1, center_y - 1); a[8] = image.at<uchar>(center_x + 1, center_y); a[9] = image.at<uchar>(center_x + 1, center_y + 1); // 强行保留边缘部分 int b1 = 4, b2 = 4; for (int p = 1; p < 10; p++) { if (abs(a[5] - a[p]) > 10) { a[p] = 0; if(0 == p%2) { b2--; } else { b1--; } } } // 使用圆形插值,保证平滑特性 int a0 = ((a[1] + a[3] + a[7] + a[9]) * 100.0 / rightRatioValueArray[k] + (a[2] + a[4] + a[6] + a[8]) * 178.0 / rightRatioValueArray[k] + a[5] * 183) * 1.0 / ((b1 * 100 + b2 * 178)/ rightRatioValueArray[k] + 183) + 0.5; imageNew.at<uchar>(i, j) = a0; } else { imageNew.at<uchar>(i, j) = image.at<uchar>(i * div + div / 2, j * div + div / 2); } } } string strRightRatio = basic2str(k); string strDiv = basic2str(div); cv::imwrite(fileName.substr(0, fileName.length() - 4) + "Circle" + "-" + strDiv + "-" +strRightRatio + ".png", imageNew); } return 0; } int main() { bool useCanny = false; bool useRefine = false; bool overlay = false; String filename[2] = { "D:\\opencv\\resize\\Debug\\Text.png" , "D:\\opencv\\resize\\Debug\\Lena.png" }; for (int k = 0; k < 2; k++) { for (int i = 0; i < 2; i++) { Mat image = imread(filename[i], IMREAD_GRAYSCALE); if (k % 2 == 0) { resizeCompare(image, 2, filename[i]); } else { resizeCompare(image, 0.5, filename[i]); } } } waitKey(); return 0; }
文章来源: https://blog.csdn.net/lonelyrains/article/details/82714508