当前位置:   article > 正文

探索OpenCV的AI实现视频超分

视频超分

OpenCV除了使用光流算法与普通插值实现图像视频超分,还提供AI深度学习实现视频超分。算法模型包括:edsr、espcn、fsrcnn、lapsrn,实现超分的倍数有2、3、4、8。通过AI实现的视频超分比传统算法的效果更好,图像更清晰。

1、超分算法对比

在opencv_contrib外置库的dnn_superres模块,就是用AI实现的图像/视频超分。接下来,我们对比AI算法、双三次插值、最近邻插值、Lanczos插值的超分效果。通过计算图像的PSNR、SSIM来评估超分质量。

  1. #include <iostream>
  2. #include <opencv2/opencv_modules.hpp>
  3. #include <opencv2/dnn_superres.hpp>
  4. #include <opencv2/quality.hpp>
  5. #include <opencv2/imgproc.hpp>
  6. using namespace std;
  7. using namespace cv;
  8. using namespace dnn_superres;
  9. static Vec2d getQualityValues(Mat orig, Mat upsampled)
  10. {
  11. double psnr = PSNR(upsampled, orig);
  12. Scalar q = quality::QualitySSIM::compute(upsampled, orig, noArray());
  13. double ssim = mean(Vec3d((q[0]), q[1], q[2]))[0];
  14. return Vec2d(psnr, ssim);
  15. }
  16. int main(int argc, char *argv[])
  17. {
  18. if (argc < 4) {
  19. cout << "usage:";
  20. cout << "Arg 1: image path | Path to image\n";
  21. cout << "Arg 2: algorithm | edsr, espcn, fsrcnn or lapsrn\n";
  22. cout << "Arg 3: path to model file \n";
  23. cout << "Arg 4: scale | 2, 3, 4 or 8 \n";
  24. cout << "-----------------------------------------------" << endl;
  25. return -1;
  26. }
  27. string path = string(argv[1]);
  28. string algorithm = string(argv[2]);
  29. string model = string(argv[3]);
  30. int scale = atoi(argv[4]);
  31. Mat img = imread(path);
  32. if (img.empty()) {
  33. cerr << "Couldn't load image: " << img << "\n";
  34. return -2;
  35. }
  36. // 裁剪图像
  37. int width = img.cols - (img.cols % scale);
  38. int height = img.rows - (img.rows % scale);
  39. Mat cropped = img(Rect(0, 0, width, height));
  40. Mat img_downscaled;
  41. resize(cropped, img_downscaled, Size(), 1.0 / scale, 1.0 / scale);
  42. Mat img_new;
  43. DnnSuperResImpl sr;
  44. vector <Mat> allImages;
  45. // 读取模型:以ESPCN为例,"models/ESPCN_x2.pb"
  46. sr.readModel(model);
  47. // 设置超分算法、超分倍数
  48. sr.setModel(algorithm, scale);
  49. sr.upsample(img_downscaled, img_new);
  50. vector<double> psnrValues = vector<double>();
  51. vector<double> ssimValues = vector<double>();
  52. // 1、深度学习模型
  53. Vec2f quality = getQualityValues(cropped, img_new);
  54. psnrValues.push_back(quality[0]);
  55. ssimValues.push_back(quality[1]);
  56. cout << sr.getAlgorithm() << ":" << endl;
  57. cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;
  58. // 2、双三次插值
  59. Mat bicubic;
  60. resize(img_downscaled, bicubic, Size(), scale, scale, INTER_CUBIC);
  61. quality = getQualityValues(cropped, bicubic);
  62. psnrValues.push_back(quality[0]);
  63. ssimValues.push_back(quality[1]);
  64. cout << "Bicubic " << endl;
  65. cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;
  66. // 3、最近邻插值
  67. Mat nearest;
  68. resize(img_downscaled, nearest, Size(), scale, scale, INTER_NEAREST);
  69. quality = getQualityValues(cropped, nearest);
  70. psnrValues.push_back(quality[0]);
  71. ssimValues.push_back(quality[1]);
  72. cout << "Nearest neighbor" << endl;
  73. cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;
  74. // 4、LANCZOS插值
  75. Mat lanczos;
  76. resize(img_downscaled, lanczos, Size(), scale, scale, INTER_LANCZOS4);
  77. quality = getQualityValues(cropped, lanczos);
  78. psnrValues.push_back(quality[0]);
  79. ssimValues.push_back(quality[1]);
  80. cout << "Lanczos" << endl;
  81. cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;
  82. return 0;
  83. }

2、超分效果对比

以4倍超分为例,各个算法实现超分的效果如下图所示。可以看到双三次插值、最近邻插值、Lanczos插值的图像有马赛克方块,而通过AI实现超分的图像比较清晰。

 具体的耗时、PSNR、SSIM数据,如下表所示。可以看到,传统算法的耗时很少,PSNR值在26左右。而AI算法的耗时比较高,其中EDSR模型的耗时有1.6s,PSNR值最高(超过28)。

 3、AI视频超分

通过VideoCapture来读取视频帧,然后使用DnnSuperResImpl实现逐帧超分,接着用VideoWriter写视频文件。完整的示例代码如下:

  1. int main(int argc, char *argv[])
  2. {
  3. if (argc < 4) {
  4. cout << "usage: Arg 1: input video path" << endl;
  5. cout << "\t Arg 2: output video path" << endl;
  6. cout << "\t Arg 3: algorithm | edsr, espcn, fsrcnn or lapsrn" << endl;
  7. cout << "\t Arg 4: scale | 2, 3, 4 or 8 \n";
  8. cout << "\t Arg 5: path to model file \n";
  9. return -1;
  10. }
  11. string input_path = string(argv[1]);
  12. string output_path = string(argv[2]);
  13. string algorithm = string(argv[3]);
  14. int scale = atoi(argv[4]);
  15. string path = string(argv[5]);
  16. VideoCapture input_video(input_path);
  17. int ex = static_cast<int>(input_video.get(CAP_PROP_FOURCC));
  18. Size S = Size((int) input_video.get(CAP_PROP_FRAME_WIDTH) * scale,
  19. (int) input_video.get(CAP_PROP_FRAME_HEIGHT) * scale);
  20. VideoWriter output_video;
  21. output_video.open(output_path, ex, input_video.get(CAP_PROP_FPS), S, true);
  22. if (!input_video.isOpened())
  23. {
  24. std::cerr << "Could not open the video." << std::endl;
  25. return -1;
  26. }
  27. // 读取超分模型、设置超分倍数
  28. DnnSuperResImpl sr;
  29. sr.readModel(path);
  30. sr.setModel(algorithm, scale);
  31. for(;;)
  32. {
  33. Mat frame, output_frame;
  34. // 读取视频帧
  35. input_video >> frame;
  36. if ( frame.empty() )
  37. break;
  38. // 执行超分
  39. sr.upsample(frame, output_frame);
  40. // 写入超分后的视频帧
  41. output_video << output_frame;
  42. char c=(char)waitKey(25);
  43. if(c==27)
  44. break;
  45. }
  46. input_video.release();
  47. output_video.release();
  48. return 0;
  49. }

4、AI超分源码

首先把图像转换为浮点格式,然后拆分YCbCr通道,传入深度学习网络进行超分,最后重建图像。源码如下:

  1. void DnnSuperResImpl::upsample(InputArray img, OutputArray result)
  2. {
  3. if (net.empty())
  4. CV_Error(Error::StsError, "Model not specified. Please set model via setModel().");
  5. if (this->alg == "espcn" || this->alg == "lapsrn" || this->alg == "fsrcnn")
  6. {
  7. // 预处理: 转成浮点格式
  8. Mat preproc_img;
  9. preprocess_YCrCb(img, preproc_img);
  10. // 拆分通道,仅用Y通道进行推理
  11. Mat ycbcr_channels[3];
  12. split(preproc_img, ycbcr_channels);
  13. Mat Y = ycbcr_channels[0];
  14. // 创建blob
  15. cv::Mat blob;
  16. dnn::blobFromImage(Y, blob, 1.0);
  17. // 使用神经网络进行超分
  18. this->net.setInput(blob);
  19. Mat blob_output = this->net.forward();
  20. // 从blob转换回image
  21. std::vector <Mat> model_outs;
  22. dnn::imagesFromBlob(blob_output, model_outs);
  23. Mat out_img = model_outs[0];
  24. // 重建图像: 对Cr、Cb进行超分,融合第三层网络
  25. reconstruct_YCrCb(out_img, preproc_img, result, this->sc);
  26. }
  27. else if (this->alg == "edsr")
  28. {
  29. // Div2K数据集的平均值
  30. Scalar mean = Scalar(103.1545782, 111.561547, 114.35629928);
  31. // 转成浮点格式
  32. Mat float_img;
  33. img.getMat().convertTo(float_img, CV_32F, 1.0);
  34. // 创建blob、抽取数据集的平均值
  35. cv::Mat blob;
  36. dnn::blobFromImage(float_img, blob, 1.0, Size(), mean);
  37. // 使用神经网络进行超分
  38. this->net.setInput(blob);
  39. Mat blob_output = this->net.forward();
  40. // 从blob转换回image
  41. std::vector <Mat> model_outs;
  42. dnn::imagesFromBlob(blob_output, model_outs);
  43. // 后处理: 添加平均值
  44. Mat(model_outs[0] + mean).convertTo(result, CV_8U);
  45. }
  46. else
  47. {
  48. CV_Error(cv::Error::StsNotImplemented, String("Unknown/unsupported superres algorithm: ") + this->alg);
  49. }
  50. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/80134
推荐阅读
相关标签
  

闽ICP备14008679号