【opencv】教程代码 —ImgProc (6)通过Wiener滤波器恢复运动模糊图像

news2024/11/17 22:24:00

6. motion_deblur_filter.cpp通过Wiener滤波器恢复运动模糊图像(参数难调)

041b19fe4367cd3ad97226acf09690f5.jpeg

1107288b85498e9aa15070fd3da23972.jpeg

03584f318a7bc14b8e13ea2098877960.jpeg

您将学习如何使用维纳滤波器恢复具有运动模糊失真的图像

d204a81fa2df0e6ddb530361fd762800.png

/**
* @brief 学习如何使用Wiener滤波器恢复运动模糊失真的图像。
* @author 混沌鱼, karpushin@ngs.ru, https://github.com/VladKarpushin
*/
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"


// 使用OpenCV和标准库的命名空间
using namespace cv;
using namespace std;


// 函数声明
void help();
void calcPSF(Mat& outputImg, Size filterSize, int len, double theta);
void fftshift(const Mat& inputImg, Mat& outputImg);
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr);
void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma = 5.0, double beta = 0.2);


// 定义程序可能接受的命令行参数。
const String keys =
"{help h usage ? |             | print this message                }"
"{image          |input.png    | input image name                  }"
"{LEN            |125          | length of a motion                }"
"{THETA          |0            | angle of a motion in degrees      }"
"{SNR            |700          | signal to noise ratio             }"
;


// 主函数
int main(int argc, char *argv[])
{
    // 显示帮助信息
    help();
    // 解析命令行参数
    CommandLineParser parser(argc, argv, keys);
    // 如果请求帮助,则打印帮助信息并退出程序。
    if (parser.has("help"))
    {
        parser.printMessage();
        return 0;
    }
    
    // 从命令行参数中获取LEN, THETA, SNR和图像文件名的值。
    int LEN = parser.get<int>("LEN");
    double THETA = parser.get<double>("THETA");
    int snr = parser.get<int>("SNR");
    string strInFileName = parser.get<String>("image");


    // 检查解析的命令行参数是否有误。
    if (!parser.check())
    {
        parser.printErrors();
        return 0;
    }


    // 加载图像文件
    Mat imgIn;
    imgIn = imread(strInFileName, IMREAD_GRAYSCALE);
    // 如果图像为空,则载入失败,打印错误信息并退出。
    if (imgIn.empty())
    {
        cout << "ERROR : Image cannot be loaded..!!" << endl;
        return -1;
    }
    // 图像处理后保存的输出图像。
    Mat imgOut;


// 主要的图像处理过程开始
    // 只处理偶数大小的图像
    Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);


    // 计算Hw(滤波器),过程开始
    Mat Hw, h;
    calcPSF(h, roi.size(), LEN, THETA);
    calcWnrFilter(h, Hw, 1.0 / double(snr));
    // 计算Hw(滤波器),过程结束


    // 将图像转换为浮点型并进行边缘锥化处理。
    imgIn.convertTo(imgIn, CV_32F);
    edgetaper(imgIn, imgIn);


    // 开始滤波处理
    filter2DFreq(imgIn(roi), imgOut, Hw);
    // 结束滤波处理
// 主要的图像处理过程结束


    // 将处理后的图像转换回8位无符号整数型并进行归一化。
    imgOut.convertTo(imgOut, CV_8U);
    normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
    // 将处理后的图像保存至文件。
    imwrite("result.jpg", imgOut);
    return 0;
}


// 显示帮助信息的函数实现。
void help()
{
    cout << "2018-08-14" << endl;
    cout << "Motion_deblur_v2" << endl;
    cout << "You will learn how to recover an image with motion blur distortion using a Wiener filter" << endl;
}


// 计算点扩散函数(PSF)的函数实现。
void calcPSF(Mat& outputImg, Size filterSize, int len, double theta)
{
    // 创建图像并用0填充。
    Mat h(filterSize, CV_32F, Scalar(0));
    // 获取滤波器的中心点。
    Point point(filterSize.width / 2, filterSize.height / 2);
    // 在图像中绘制椭圆(运动模糊PSF)。
    ellipse(h, point, Size(0, cvRound(float(len) / 2.0)), 90.0 - theta, 0, 360, Scalar(255), FILLED);
    // 对图像求和得到总和。
    Scalar summa = sum(h);
    // 将图像h除以总和summa[0]来规格化PSF。
    outputImg = h / summa[0];
}


// 频率域上对信号进行中心化的函数实现。
void fftshift(const Mat& inputImg, Mat& outputImg)
{
    // 克隆输入图像以获得与其大小相同的输出图像。
    outputImg = inputImg.clone();
    // 计算图像的中心点坐标。
    int cx = outputImg.cols / 2;
    int cy = outputImg.rows / 2;
    // 划分图像为四块。
    Mat q0(outputImg, Rect(0, 0, cx, cy));
    Mat q1(outputImg, Rect(cx, 0, cx, cy));
    Mat q2(outputImg, Rect(0, cy, cx, cy));
    Mat q3(outputImg, Rect(cx, cy, cx, cy));
    // 交换这四块图像,实现中心化。
    Mat tmp;
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);
}


// 在频率域上应用滤波器的函数实现。
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
{
    // 创建两个平面的数组,其中一个为浮点型的输入图像,另一个为和输入图像同等大小的零矩阵。
    Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
    Mat complexI;
    // 合并两个平面形成复数图像。
    merge(planes, 2, complexI);
    // 进行离散傅里叶变换。
    dft(complexI, complexI, DFT_SCALE);


    Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
    Mat complexH;
    // 为H矩阵也创建复数形式。
    merge(planesH, 2, complexH);
    Mat complexIH;
    // 对输入图像和滤波器进行逐个元素的复数乘法。
    mulSpectrums(complexI, complexH, complexIH, 0);


    // 进行离散傅里叶逆变换。
    idft(complexIH, complexIH);
    // 分离平面得到结果图像。
    split(complexIH, planes);
    outputImg = planes[0];
}


// 计算维纳滤波器的函数实现。
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr)
{
    Mat h_PSF_shifted;
    // 对输入的PSF进行中心化。
    fftshift(input_h_PSF, h_PSF_shifted);
    // 为h_PSF_shifted创建两个平面,其中一个为浮点型形式的h_PSF_shifted,另一个为同等大小的零矩阵。
    Mat planes[2] = { Mat_<float>(h_PSF_shifted.clone()), Mat::zeros(h_PSF_shifted.size(), CV_32F) };
    Mat complexI;
    // 合并两个平面形成复数图像。
    merge(planes, 2, complexI);
    // 进行离散傅里叶变换。
    dft(complexI, complexI);
    // 分离平面得到h_PSF的幅度。
    split(complexI, planes);
    Mat denom;
    // 计算|h_PSF|的平方和加上噪声功率谱比(nsr)。
    pow(abs(planes[0]), 2, denom);
    denom += nsr;
    // 对h_PSF除以denom得到Wiener滤波器。
    divide(planes[0], denom, output_G);
}


// 对图像边缘执行锥形衰减的函数实现。
//! [edgetaper]
// 执行边缘渐变处理的功能,减少频谱泄露
void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma, double beta)
{
    // 获取输入图像的列数Nx和行数Ny
    int Nx = inputImg.cols;
    int Ny = inputImg.rows;
    
    // 创建两个类型为浮点型的Mat矩阵w1和w2,w1的大小是1xNx,w2的大小是Nyx1
    // 并使用无效的黑色标量像素初始化(初始化为0)
    Mat w1(1, Nx, CV_32F, Scalar(0));
    Mat w2(Ny, 1, CV_32F, Scalar(0));


    // 获取w1和w2的指针,以便后面直接修改其值
    float* p1 = w1.ptr<float>(0);
    float* p2 = w2.ptr<float>(0);
    
    // dx是x方向的间隔参数,初始化为对应于-π到π的步长
    float dx = float(2.0 * CV_PI / Nx);
    // 初始化x的初始值为-π
    float x = float(-CV_PI);
    // 计算每一列的权重,存入w1中
    for (int i = 0; i < Nx; i++)
    {
        p1[i] = float(0.5 * (tanh((x + gamma / 2) / beta) - tanh((x - gamma / 2) / beta)));
        x += dx;
    }
    
    // dy是y方向的间隔参数,初始化为对应于-π到π的步长
    float dy = float(2.0 * CV_PI / Ny);
    // 初始化y的初始值为-π
    float y = float(-CV_PI);
    // 计算每一行的权重,存入w2中
    for (int i = 0; i < Ny; i++)
    {
        p2[i] = float(0.5 * (tanh((y + gamma / 2) / beta) - tanh((y - gamma / 2) / beta)));
        y += dy;
    }
    
    // 创建矩阵w,它是w1和w2的外积,代表整个图像的每个像素的权重
    Mat w = w2 * w1;
    // 使用权重矩阵w对输入图像进行点乘,以便对图像进行边缘渐变处理,结果存入输出图像outputImg
    multiply(inputImg, w, outputImg);
}
//! [edgetaper]

c3a957fe6732e173d484e08f2b1a4b55.png

d1c758cb7db1500ae5289aef7e260be3.png

2896aabdceb168562fd9ee9e6ac1cda5.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1550362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java图书管理系统(简易)

实现的基本功能&#xff1a; 登录时&#xff0c;需要输入姓名&#xff0c;然后选择作为管理者还是普通用户。选择成功后选择想要实现的功能。管理者的目录下方有有五个功能&#xff0c;而普通用户有4个功能&#xff0c;如下图 首先我们要建立Book这个类&#xff0c;里面包含书…

Swagger3探索之游龙入海

引言 后端开发中常用的接口调用工具一般使用Postman、ApiPost工具&#xff0c;但后期需要与前端联调&#xff0c;要补充接口文档花费大量时间&#xff0c;此时Swagger3应运而生&#xff0c;大大提高沟通交流的效率。 引用依赖 <!-- Swagger3 调用方式 http://ip:port/swa…

Echarts之x轴,Y轴配置项大全

ECharts是一个强大的数据可视化库&#xff0c;提供了丰富的配置项来定制图表的x轴和y轴。下面是ECharts中x轴和y轴的配置项大全&#xff1a; xAxis配置项&#xff1a; type&#xff1a;轴类型&#xff0c;可选值有&#xff1a;“value”&#xff08;数值轴&#xff09;, “cat…

IP如何异地共享文件?

【天联】 组网由于操作简单、跨平台应用、无网络要求、独创的安全加速方案等原因&#xff0c;被几十万用户广泛应用&#xff0c;解决了各行业客户的远程连接需求。采用穿透技术&#xff0c;简单易用&#xff0c;不需要在硬件设备中端口映射即可实现远程访问。 异地共享文件 在…

excel匹配替换脱敏身份证等数据

假如excel sheet1中有脱敏的身份证号码和姓名&#xff0c;如&#xff1a; sheet2中有未脱敏的数据数据 做法如下&#xff1a; 1、在sheet2的C列用公式 LEFT(A2,6)&REPT("*",8)&RIGHT(A2,4) 做出脱敏数据&#xff0c;用来与sheet1的脱敏数据匹配 2、在sheet…

案例研究|DataEase实现物业数据可视化管理与决策支持

河北隆泰物业服务有限责任公司&#xff08;以下简称为“隆泰物业”&#xff09;创建于2002年&#xff0c;总部设在河北省高碑店市&#xff0c;具有国家一级物业管理企业资质&#xff0c;通过了质量体系、环境管理体系、职业健康安全管理体系等认证。自2016年至今&#xff0c;隆…

以太网链路聚合——增加带宽,解决生成树收敛慢的问题

目录 一.对STP生成树的补充 1.STP接口状态 2.STP生成树的改进 二.网络可靠性 1.单板可靠性 2.设备可靠性 3.链路可靠性 三.链路聚合 1.多条链路聚合增加带宽 2.链路聚合术语 四.链路聚合模式 1.手动模式 2.LASP模式 &#xff08;1).LASP术语 &#xff08;2&…

[Qt] QString::fromLocal8Bit 的使用误区

QString::fromLocal8Bit 是一个平台相关的函数。默认情况下在 Windows 下 就是 gbk 转 utf-8 ,在 Linux就应该是无事发生。因为Linux平台默认的编码方式就是 utf-8 可以通过 void QTextCodec::setCodecForLocale(QTextCodec *c)来修改 Qt默认的编码方式。如下 第一输出乱码的…

深入探讨分布式ID生成方案

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

图神经网络实战(6)——使用PyTorch构建图神经网络

图神经网络实战&#xff08;6&#xff09;——使用PyTorch构建图神经网络 0. 前言1. 传统机器学习与人工智能2. 人工神经网络基础2.1 人工神经网络组成2.2 神经网络的训练 3. 图神经网络4. 使用香草神经网络执行节点分类4.1 数据集构建4.2 模型构建4.3 模型训练 5. 实现香草图神…

广发期货:从灾备中心、信创云到主中心,超融合支撑云化与国产化双转型

案例亮点 超过 30 节点承载灾备中心、信创云及主中心的 60% 以上业务系统。超融合信创资源池稳定运行超 1 年&#xff0c;承载 80% 以上的信创系统&#xff0c;顺利通过信创验收。引入超融合架构后&#xff0c;业务在 1 周内快速上线&#xff0c;稳定运行 3 年&#xff1b;减少…

Spring Boot项目启动过程中为什么日志打印没有显示完整包名呢?

一、前言 不知道大家注意过没有&#xff0c;在Spring Boot项目启动过程中日志打印并没有显示完整的报名&#xff0c;而是显示一些o.a.c&#xff0c;o.s.web形式的包名&#xff0c;如下图&#xff1a; 这是为什么呢&#xff1f; 二、原理 首先&#xff0c;我们先看一下Spring…

Vue时间组件:Dayjs与Moment对比

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

亚马逊云科技:基于老服务器打造的旧实例类型

内容摘要&#xff1a; 2021年&#xff0c;距离第一个EC2实例上线已经十五周年了。 在漫长的开发过程中&#xff0c;很多EC2实例自然会基于旧服务器构建。 随着时间的推移&#xff0c;旧的服务器总是需要更换硬件&#xff0c;实例也得更换&#xff0c;但并不是所有的用户都想迁…

vue3 - 前端优秀案例 vue-admin-box 推荐

vue-admin-box 简介 vue-admin-box是一个免费并且开源的中后台管理系统模板。使用最新版本的vue3开发而成。 演示地址 https://cmdparkour.gitee.io/vue-admin-box/https://cmdparkour.gitee.io/vue-admin-box/ ​编辑 简介 更新日志经过三个多月的迭代&#xff0c;于202…

BRICK POP展示了有趣的链上游戏玩法与奖励机制

新游戏BRICK POP将Sui区块链技术与低Gas费用&#xff0c;以及其在Web3游戏开发方面的专业知识无缝结合。通过充分利用Sui和ONBUFF的INNO平台优势&#xff0c;BRICK POP为玩家提供了一个融合了前沿技术和引人入胜游戏的沉浸式游戏体验。BRICK POP游戏设计为实时交易和高用户参与…

Android 性能优化(六):启动优化的详细流程

书接上文&#xff0c;Android 性能优化&#xff08;一&#xff09;&#xff1a;闪退、卡顿、耗电、APK 从用户体验角度有四个性能优化方向&#xff1a; 追求稳定&#xff0c;防止崩溃追求流畅&#xff0c;防止卡顿追求续航&#xff0c;防止耗损追求精简&#xff0c;防止臃肿 …

【MySQL】5.2MySQL高级语句与sql语句

模板 test、class、class0 mysql> select * from test; -------------------------------- | idcard | name | age | hobbid | -------------------------------- | 01 | lizi | 18 | guangjie | | 02 | monor | 22 | zhaijia | | 03 | sansan | …

FFMPEG C++封装(一)(C++ FFMPEG)

1 概述 FFMPEG是一个C语言开源视音频编解码库。本文将FFMPG4.1.3进行C封装&#xff0c;形成C FFMPG库。 2 架构 架构图如下所示&#xff1a; 架构说明: Init 初始化FFMPEG库。IStream 输入流&#xff0c;FFMPEG的输入音视频文件。Packet 音视频数据包Decoder 音视频编码器F…

electron打包桌面版.exe之vue项目踩坑(vue3+electron 解决打包后首页打开空白,打包后路由不跳转及请求不到后端数据等问题)

vue项目https://www.qingplus.cn/components-web/index打包桌面版问题集合 一、静态资源加载问题 npm run electron_dev桌面版运行后页面空白&#xff0c;内容未加载。 填坑&#xff1a; 打包配置要用相对路径 vite.config.ts文件中的base要改成./&#xff0c;之前加了项目…