1.1 超分辨率初体验
简单来讲,图像超分辨率就是提高图像的空间分辨率,例如将一幅图片的分辨率由352x288扩大到704x576,方便用户在大尺寸的显示设备上观看。图像的超分辨率,是图像处理相关问题中的基础问题之一,并具有广泛的实际需求和应用场景,在数字成像技术,视频编码通信技术,深空卫星遥感技术,目标识别分析技术和医学影像分析技术等方面,视频图像超分辨率技术都能够应对显示设备分辨率大于图像源分辨率的问题。
![](https://img2020.cnblogs.com/blog/508489/202110/508489-20211004074339111-1321076907.png)
简单来说超分辨率技术可以分为以下两种:
1)只参考当前低分辨率图像,不依赖其他相关图像的超分辨率技术,称之为单幅图像的超分辨率(single image super resolution),也可以称之为图像插值(image interpolation);
2)参考多幅图像或多个视频帧的超分辨率技术,称之为多帧视频/多图的超分辨率(multi-frame super resolution)。
1.2 超分辨率理论描述
这个很直观的超分辨率问题,它的理论描述又是什么样子的呢?如下图所示,超分辨率就是将左图中像素点之间的空间位置用像素点进行填充,使得整个图像具有更多的像素点,更丰富的细节,从信号的角度讲就是补充出更多的高频成分。
通常在处理这个超分辨率问题的时候,我们常常探索这个退化信号是如何从我们希望的理想信号变化得到的(即分辨率的退化过程),如果对退化过程进行精确地描述,往往对其逆问题的求解有重要的意义。
在本文的问题中,即超分辨率的退化模型,可以通过以下公式来描述:
Y = HDX + n
其中Y为低分辨率的视频帧/图像,X为我们理想高分辨率的视频帧/图像,而H和D分别为模糊算子和分辨率下采样算子,n为退化过程中产生的噪声。
超分代码:https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L68
模型文件:
https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.prototxt
参数文件:
https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.caffemodel
Mat src = imread("e:/template/bj1.png"); Mat dst;
std::shared_ptr<SuperScale> sr = std::make_shared<SuperScale>();//智能指针
sr->init("E:/template/sr.prototxt", "E:/template/sr.caffemodel");
bool use_nn_sr = true;
sr->processImageScale(src, dst, 3, use_nn_sr);
path-to-opencvsourcesdoc utorialsvideoiovideo-input-psnr-ssim
Scalar getMSSIM( const Mat& i1, const Mat& i2){ const double C1 = 6.5025, C2 = 58.5225; /***************************** INITS **********************************/ int d = CV_32F; Mat I1, I2; i1.convertTo(I1, d); // cannot calculate on one byte large values i2.convertTo(I2, d); Mat I2_2 = I2.mul(I2); // I2^2 Mat I1_2 = I1.mul(I1); // I1^2 Mat I1_I2 = I1.mul(I2); // I1 * I2 /*************************** END INITS **********************************/ Mat mu1, mu2; // PRELIMINARY COMPUTING GaussianBlur(I1, mu1, Size(11, 11), 1.5); GaussianBlur(I2, mu2, Size(11, 11), 1.5); Mat mu1_2 = mu1.mul(mu1); Mat mu2_2 = mu2.mul(mu2); Mat mu1_mu2 = mu1.mul(mu2); Mat sigma1_2, sigma2_2, sigma12; GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5); sigma1_2 -= mu1_2; GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5); sigma2_2 -= mu2_2; GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5); sigma12 -= mu1_mu2; ///////////////////////////////// FORMULA //////////////////////////////// Mat t1, t2, t3; t1 = 2 * mu1_mu2 + C1; t2 = 2 * sigma12 + C2; t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2)) t1 = mu1_2 + mu2_2 + C1; t2 = sigma1_2 + sigma2_2 + C2; t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2)) Mat ssim_map; divide(t3, t1, ssim_map); // ssim_map = t3./t1; Scalar mssim = mean(ssim_map); // mssim = average of ssim map return mssim;}
Scalar getMSSIM(const Mat& i1, const Mat& i2)
{
const double C1 = 6.5025, C2 = 58.5225;
/***************************** INITS **********************************/
int d = CV_32F;
Mat I1, I2;
i1.convertTo(I1, d); // cannot calculate on one byte large values
i2.convertTo(I2, d);
Mat I2_2 = I2.mul(I2); // I2^2
Mat I1_2 = I1.mul(I1); // I1^2
Mat I1_I2 = I1.mul(I2); // I1 * I2
/*************************** END INITS **********************************/
Mat mu1, mu2; // PRELIMINARY COMPUTING
GaussianBlur(I1, mu1, Size(11, 11), 1.5);
GaussianBlur(I2, mu2, Size(11, 11), 1.5);
Mat mu1_2 = mu1.mul(mu1);
Mat mu2_2 = mu2.mul(mu2);
Mat mu1_mu2 = mu1.mul(mu2);
Mat sigma1_2, sigma2_2, sigma12;
GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
sigma1_2 -= mu1_2;
GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
sigma2_2 -= mu2_2;
GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
sigma12 -= mu1_mu2;
///////////////////////////////// FORMULA ////////////////////////////////
Mat t1, t2, t3;
t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigma12 + C2;
t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigma2_2 + C2;
t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
Mat ssim_map;
divide(t3, t1, ssim_map); // ssim_map = t3./t1;
Scalar mssim = mean(ssim_map); // mssim = average of ssim map
return mssim;
}
int main(int argc, char* argv[])
{
//对于一张分辨率比较大的图片,
Mat src = imread("E:/template/test_3000X2000.bmp"); Mat dstResize; Mat dstSF;
Mat temp;
//将其按照不同比例变小后
resize(src, temp, cv::Size(750, 500));
std::shared_ptr<SuperScale> sr = std::make_shared<SuperScale>();//智能指针
sr->init("E:/template/sr.prototxt", "E:/template/sr.caffemodel");
bool use_nn_sr = true;
//主运算,并打印运算时间
double t1 = (double)getTickCount();
resize(temp, dstResize, cv::Size(3000, 2000));
double t2 = (double)getTickCount();
sr->processImageScale(temp, dstSF,6, use_nn_sr);
double t3 = (double)getTickCount();
double timeResize = (t2 - t1) / getTickFrequency();
double timeSF = (t3 - t2) / getTickFrequency();
cout << "Resize耗时" << timeResize << "s " << "SF耗时" << timeSF << "s" << endl;
/////计算并打印ssim
Scalar mssimV = getMSSIM(src, src);
cout << "自身对比结果: "
<< " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"
<< " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"
<< " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%" << endl;
mssimV = getMSSIM(src, dstResize);
cout << " resize结果: "
<< " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"
<< " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"
<< " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%" << endl;
mssimV = getMSSIM(src, dstSF);
cout << " sf结果: "
<< " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"
<< " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"
<< " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%";
waitKey();
}
Resize耗时0.0121942s SF耗时0.0245566s
自身对比结果: R 100.00% G 100.00% B 100.00%
resize结果: R 75.12% G 74.45% B 73.46%
sf结果: R 76.90% G 76.09% B 75.11%
Resize耗时0.0107573s SF耗时0.0202321s
自身对比结果: R 100.00% G 100.00% B 100.00%
resize结果: R 93.68% G 93.49% B 93.23%
sf结果: R 97.25% G 97.20% B 97.07%
Resize耗时0.0121942s SF耗时0.0245566s
自身对比结果: R 100.00% G 100.00% B 100.00%
resize结果: R 75.12% G 74.45% B 73.46%
sf结果: R 76.90% G 76.09% B 75.11%
Resize耗时0.0116356s SF耗时0.0207268s
自身对比结果: R 100.00% G 100.00% B 100.00%
resize结果: R 67.05% G 66.16% B 65.05%
sf结果: R 66.74% G 65.48% B 64.20%
Resize耗时0.0197857s SF耗时0.0182384s
自身对比结果: R 100.00% G 100.00% B 100.00%
resize结果: R 52.94% G 51.89% B 51.05%
sf结果: R 51.66% G 50.15% B 49.13%
Resize耗时0.0175215s SF耗时0.0138289s
自身对比结果: R 100.00% G 100.00% B 100.00%
resize结果: R 47.59% G 46.66% B 46.18%
sf结果: R 46.14% G 44.85% B 44.22%