智能老旧模糊照片修复——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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值