智能老旧模糊照片修复——C++实现GFPGAN模型推理
前言
1.回家過年,總是有意或無意間翻到一些舊時的照片,舊照片作為時光記憶的載體和歲月流轉的見證,不單單是過去美好時光的傳承者,同時也是每個人的情結和懷念的寄托。隨著時間的流逝,許多老照片都因為自然或人為原因,受到了侵蝕損壞,畫面模糊、褪色、照片磨損嚴重等現象,甚至還有的因為保管不好導致照片面目全非。作為一個程序員,隨著舊照片的損壞,你再也無法證明你也曾經滿頭秀發,你也曾經意氣風發,有過明亮的眼眸。
2.GFPGAN是Tencent開源的一個舊照片修復算法,它能夠讓這些老照片恢復原有的光澤,使用了GAN算法對照片進行修復,效果比其他同類模型都有更好的表現。如果對算法和如何訓練模型可轉到github:https://github.com/TencentARC/GFPGAN .
3.這里只是演示如何使用C++調用官方給出訓練好的模型,開發環境是win10, vs2019, opencv4.5, ncnn,如果要啟用GPU加速,所以用到VulkanSDK,實現語言是C++。
4.官方給出的測試效果對比圖:
C++代碼實現:
第一張是原圖,第二張是只修復人臉,第三張是修復全身:
二、實現代碼
#include <vector> #include <ostream> #include <random> #include <chrono> #include <stdio.h> #include <fstream> #include <opencv2/opencv.hpp> #include <ncnn/net.h> #include <ncnn/cpu.h> #include "gfpgan.h" #include "face.h" #include "realesrgan.h"static void toOcv( const ncnn::Mat& result, cv::Mat& out) {cv::Mat cv_result_32F = cv::Mat::zeros(cv::Size(512, 512), CV_32FC3);for (int i = 0; i < result.h; i++){for (int j = 0; j < result.w; j++){cv_result_32F.at<cv::Vec3f>(i, j)[2] = (result.channel(0)[i * result.w + j] + 1) / 2;cv_result_32F.at<cv::Vec3f>(i, j)[1] = (result.channel(1)[i * result.w + j] + 1) / 2;cv_result_32F.at<cv::Vec3f>(i, j)[0] = (result.channel(2)[i * result.w + j] + 1) / 2;}}cv::Mat cv_result_8U;cv_result_32F.convertTo(cv_result_8U, CV_8UC3, 255.0, 0);cv_result_8U.copyTo(out);}void mergeImage(std::vector<cv::Mat>& src_vor, cv::Mat& cv_dst, int channel) {cv::Mat img_merge;cv::Size size(src_vor.at(0).cols * src_vor.size(), src_vor.at(0).rows);if (channel == 1){img_merge.create(size, CV_8UC1);}else if (channel == 3){img_merge.create(size, CV_8UC3);}for (int i = 0; i < src_vor.size(); i++){cv::Mat cv_temp = img_merge(cv::Rect(src_vor.at(i).cols * i, 0, src_vor.at(i).cols, src_vor.at(i).rows));src_vor.at(i).copyTo(cv_temp);}cv_dst = img_merge.clone(); }static void pasteFacesInputImage(const cv::Mat& restored_face,cv::Mat& trans_matrix_inv,cv::Mat& bg_upsample) {trans_matrix_inv.at<float>(0, 2) += 1.0;trans_matrix_inv.at<float>(1, 2) += 1.0;cv::Mat inv_restored;cv::warpAffine(restored_face, inv_restored, trans_matrix_inv, bg_upsample.size(), 1, 0);cv::Mat mask = cv::Mat::ones(cv::Size(512, 512), CV_8UC1) * 255;cv::Mat inv_mask;cv::warpAffine(mask, inv_mask, trans_matrix_inv, bg_upsample.size(), 1, 0);cv::Mat inv_mask_erosion;cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(4, 4));cv::erode(inv_mask, inv_mask_erosion, kernel);cv::Mat pasted_face;cv::bitwise_and(inv_restored, inv_restored, pasted_face, inv_mask_erosion);int total_face_area = cv::countNonZero(inv_mask_erosion);int w_edge = int(std::sqrt(total_face_area) / 20);int erosion_radius = w_edge * 2;cv::Mat inv_mask_center;kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(erosion_radius, erosion_radius));cv::erode(inv_mask_erosion, inv_mask_center, kernel);int blur_size = w_edge * 2;cv::Mat inv_soft_mask;cv::GaussianBlur(inv_mask_center, inv_soft_mask, cv::Size(blur_size + 1, blur_size + 1), 0, 0, 4);for (int h = 0; h < bg_upsample.rows; h++){for (int w = 0; w < bg_upsample.cols; w++){float alpha = inv_soft_mask.at<uchar>(h, w) / 255.0;bg_upsample.at<cv::Vec3b>(h, w)[0] = pasted_face.at<cv::Vec3b>(h, w)[0] * alpha + (1 - alpha) * bg_upsample.at<cv::Vec3b>(h, w)[0];bg_upsample.at<cv::Vec3b>(h, w)[1] = pasted_face.at<cv::Vec3b>(h, w)[1] * alpha + (1 - alpha) * bg_upsample.at<cv::Vec3b>(h, w)[1];bg_upsample.at<cv::Vec3b>(h, w)[2] = pasted_face.at<cv::Vec3b>(h, w)[2] * alpha + (1 - alpha) * bg_upsample.at<cv::Vec3b>(h, w)[2];}} }int main(int argc, char** argv) {GFPGAN gfpgan;gfpgan.initModel("models/encoder.param", "models/encoder.bin", "models/style.bin");Face face_detector;face_detector.initModel("models/blazeface.param", "models/blazeface.bin");RealESRGAN real_esrgan;real_esrgan.initModel("models/real_esrgan.param", "models/real_esrgan.bin");std::string path = "images";std::vector<std::string> filenames;cv::glob(path, filenames, false);int i = 0;for (auto v : filenames){cv::Mat img = cv::imread(v, 1);cv::Mat bg_upsample;real_esrgan.tile_process(img, bg_upsample);std::vector<cv::Mat> trans_img;std::vector<cv::Mat> trans_matrix_inv;std::vector<Object> objects;face_detector.detect(img, objects);face_detector.align_warp_face(img, objects, trans_matrix_inv, trans_img);for (size_t i = 0; i < objects.size(); i++){ncnn::Mat gfpgan_result;gfpgan.process(trans_img[i], gfpgan_result);cv::Mat restored_face;toOcv(gfpgan_result, restored_face);pasteFacesInputImage(restored_face, trans_matrix_inv[i], bg_upsample);}ncnn::Mat gfpgan_result;gfpgan.process(img, gfpgan_result);cv::Mat restored_face;toOcv(gfpgan_result, restored_face);cv::resize(img, img, bg_upsample.size());cv::resize(restored_face, restored_face, bg_upsample.size());std::vector<cv::Mat> cv_dsts{ img, restored_face, bg_upsample };cv::Mat cv_dst;mergeImage(cv_dsts, cv_dst, 3);cv::imwrite(std::to_string(i)+".png", cv_dst);i++;}return 0; }效果圖:
三.資源
1.帶界面的可執行文件:https://download.csdn.net/download/matt45m/79972131
2.不帶界面的小源碼和模型:https://download.csdn.net/download/matt45m/79973523
總結
以上是生活随笔為你收集整理的智能老旧模糊照片修复——C++实现GFPGAN模型推理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rus入门到放弃——字符串与字符切片
- 下一篇: Rus入门到放弃——HashMap和BT