屏幕翻拍满是彩色条纹?用 C# + Vulkan 做了一个去摩尔纹工具,支持单图与文件夹批量

用手机拍显示器上的图片,拍完放大一看,经常会出现一层彩色条纹。画面本来是清楚的,却像蒙上了一张不规则的网,越看越难受。

这次我把 ESDNet 去摩尔纹模型做成了一款 Windows 桌面工具:C# WinForms 界面、C++ Vulkan GPU 推理,支持单张图片和文件夹批量处理。

程序打成了约 23 MB 的部署包,完整解压后就能打开使用。面向开发者,也准备了包含 C#、C++、模型资源的完整 VS2022 解决方案,方便继续修改界面、接入业务或研究 GPU 推理。

先看一下实际界面:

ESDNet Vulkan 去摩尔纹工具主界面

图 1:左侧原图、右侧结果,单图与批量处理入口都在同一个窗口中。

一、摩尔纹为什么难处理

屏幕翻拍是一个很典型的场景。屏幕的子像素排列与相机采样发生相互作用,照片里就可能出现原画面没有的条纹和彩色干扰。相关问题与 ESDNet 的研究背景,可以查看作者的官方项目介绍(https://github.com/CVMI-Lab/UHDM)。

实际处理时,难点在于:干扰纹理和图像本身的细节混在了一起。

如果只是把整张图模糊,彩色条纹可能变淡,但文字、窗框、头发等细节也会一起变糊。我们需要的是在抑制干扰的同时,尽量保留内容。因此,这个工具使用训练好的图像恢复模型来生成处理结果。

这类处理也存在取舍:模型可能同时平滑真实细纹理。是否适合自己的图片,最好先拿几张有代表性的样本试一下。

二、这个工具已经能做什么

当前版本把常用的图片处理流程做到了界面里:

功能 使用方式
单张去摩尔纹 选择图片,处理后左右查看原图与结果
文件夹批量 逐张处理,可包含子目录
保留目录结构 子目录中的图片保存到对应输出子目录
PNG / JPEG PNG 默认保存;JPEG 可以设置质量
原尺寸处理 最长边设为 0,输出保持输入尺寸
主动缩小大图 设置最长边,降低处理耗时与显存需求
继续处理 批量任务可以跳过同名已有结果
资源模式 提供桌面友好、均衡、全速三种模式
安全停止 等待当前 GPU 调用结束,保留已经保存的结果

界面使用 .NET Framework 4.8 WinForms ,控件定义放在标准的 MainForm.Designer.cs 中。需要修改布局时,可以继续用 VS2022 设计器调整。

看一下处理前后的变化

下面使用作者演示图片中的一个 640×480 原尺寸裁块。左右展示的是同一块图像,未通过缩小图片来掩盖条纹。

640×480 官方演示图裁块处理前后对比

图 2:左侧为输入,右侧为本工具的 Vulkan 推理结果。示例原图来自作者的 Hugging Face 演示 Space(https://huggingface.co/spaces/ECCV2022/Screen_Image_Demoireing)。

可以看到,建筑表面的部分彩色条纹和波纹减弱了,画面也有一定程度的平滑。这里展示的是实际输出,具体效果还要看摩尔纹的尺度、拍摄条件和画面内容。

三、拿到部署包后怎样使用

1. 完整解压并启动

部署版约 23 MB ,解压后约 27.5 MiB,已经包含程序、模型和所需的 Release VC++ 运行库。

完整解压到普通可写目录,双击:

复制代码
EsdNet.WinForms.exe

也可以双击随包的 启动.cmd。

使用部署版无需安装 Visual Studio、Python、CUDA、OnnxRuntime 或 Vulkan SDK。电脑需要 64 位 Windows、.NET Framework 4.8 或兼容版本,以及支持 Vulkan 的显卡驱动。部署包没有包含 .NET Framework 安装器和显卡驱动;系统缺少时需要先安装。

请保留同目录的 DLL、assets 和 licenses。只复制 exe 会丢失运行所需的资源。

2. 处理单张图片

选择"单张图片"页签,按下面的顺序操作:

  1. 点击"选择图片",也可以先点击"载入官方示例"。
  2. 选择 Vulkan GPU。
  3. 设置输出目录和 PNG/JPEG 格式。
  4. 首次尝试可以把"最长边"保持为 0。
  5. 点击"开始去摩尔纹"。

处理完成后,窗口会显示结果与耗时。点击"打开输出目录"就能找到保存的图片。

3. 批量处理文件夹

切换到"文件夹批量",选择输入、输出目录,按需勾选"批量包含子目录"。

输出会保留子目录层级。例如:

复制代码
输入图片/
  page.jpg
  子目录/
    page.jpg

输出/
  page.jpg.demoire.png
  子目录/
    page.jpg.demoire.png

文件名里保留原扩展名,可以区分同一目录下的 page.jpg 和 page.png。

批量勾选"跳过同名已有结果"后,再次执行时会跳过已经存在的目标文件。这个判断只检查文件是否存在;更换参数后,建议换一个输出目录,避免把之前的结果当作本次结果跳过。

4. 最长边和资源模式怎样选

"最长边 0"表示保持原尺寸。对于显存需求较高的大图,可以主动改为 1600 等值,软件会按比例缩小输入,输出也对应缩小后的尺寸。

程序不会在显存不足时悄悄缩图。遇到预算不足提示后,可以明确调整最长边再试。

资源模式可以按电脑的使用场景选择:

  • 桌面友好:在图片之间留出更多休息时间,适合边处理边办公。
  • 均衡:减少休息时间,兼顾持续处理效率。
  • 全速:连续处理,适合集中跑任务。

这里的休息发生在一次 GPU 调用完成之后,单张图片计算时仍然可能产生较高负载。

所有相对路径都以 exe 所在目录 为基准,默认结果保存到 输出 目录。移动整个软件目录后,可以继续使用包内示例和默认相对路径。

四、从 ESDNet 到 Vulkan,中间做了哪些工作

1. 模型选择与转换

ESDNet 来自 ECCV 2022 的图像去摩尔纹研究,官方发布了模型与预训练权重。本项目选用 UHDM 权重的小模型,FP32 精度;上游网络与权重信息见官方仓库(https://github.com/CVMI-Lab/UHDM)。

开发时先加载官方权重,再导出用于核对的 ONNX,以及应用实际执行需要的图描述和打包权重。

运行时读取的是:

复制代码
assets/models/
  esdnet-uhdm.graph.json
  esdnet-uhdm.weights.bin

开发用 ONNX 和原始 PyTorch checkpoint 留在源码工程中。部署版只携带实际运行所需的模型资源,因此体积更小。

权重加载时会检查 SHA256 和长度,避免解压不完整或模型文件不配套后继续计算。

2. Vulkan 在这里负责什么

Vulkan 是 Khronos 维护的图形与计算 API,提供跨平台、跨厂商的 GPU 编程能力,也允许应用显式控制 GPU 资源与工作提交。其定位可以参考 Khronos 的 Vulkan Guide(https://docs.vulkan.org/guide/latest/what_is_vulkan.html)。

在这个项目里,Vulkan 执行计算着色器,完成卷积、激活、缩放、像素重排和归约等网络运算。

C# 负责界面、图片读写、任务调度和尺寸计划;C++ 负责 Vulkan 设备、缓冲区、计算管线与命令提交。两部分通过 C ABI 和 P/Invoke 对接。

Vulkan 本身不会直接执行任意 ONNX。当前工程有一层针对 ESDNet 的图编译逻辑,把已经支持的模型运算映射到 Vulkan 计算内核。

3. 一次完整的推理流程

复制代码
读取图片
   ↓
转换为 RGB,像素除以 255
   ↓
居中补边到 32 的倍数
   ↓
根据实际尺寸生成张量与执行计划
   ↓
准备 GPU 缓冲、计算管线与命令
   ↓
上传输入,提交 Vulkan 计算
   ↓
等待完成,读回最终输出
   ↓
裁回有效区域,量化为 8 位 RGB
   ↓
保存 PNG / JPEG

预处理采用与官方实现相同的 RGB 通道常量补边。比如一张 257×193 的图片,会补到 288×224 进入网络,处理结束再裁回 257×193。

因此,不需要为每一种图片大小提前生成一套固定计划。尺寸变化时,程序根据实际输入重新推导形状和缓冲地址;可处理的尺寸仍受显存、设备限制和程序上限约束。

4. 为什么采用整图执行

当前实现保留了模型的多尺度处理和全局注意力。整张图片作为同一次网络输入,可以保留这些全局计算的作用范围。

代价是大图需要较多工作区。当前版本采用整图方案,显存不足时由用户主动限制最长边。

执行层也会把超过单次派发数量限制的计算拆成多次派发。这是 GPU 调度层的拆分,整张图片仍属于同一次网络推理。

五、实际速度是多少

测试环境是 RTX 4060 Laptop GPU,8 GiB 显存,Release x64,FP32。

每种尺寸在同一个会话里连续处理三次,下表取第 2、3 次原生调用耗时的平均值:

输入尺寸 实际网络输入 原生调用平均耗时 逻辑工作区
257×193 288×224 34.78 ms 51.68 MiB
640×480 640×480 94.90 ms 246.09 MiB
1920×1080 1920×1088 674.07 ms 1673.44 MiB

这里的耗时包含输入上传、GPU 执行、等待与结果读回。 图片解码、预处理、后处理、保存、首次准备和资源模式休息需要另外计算。

以 1080p 为例,本次后两轮的预处理约 106~110 ms ,后处理约 19~25 ms,读写图片的耗时另计。估算整个文件夹任务时,应把这些环节一起考虑。

表中的"逻辑工作区"是执行计划计算出来的缓冲空间。进程或整卡显存还包括权重、传输缓冲和驱动开销。本次没有采集 GPU 利用率和整卡显存峰值,所以这里只报告已经记录的指标。

首次运行时,驱动可能需要创建和编译计算管线,本机曾测得约 15~16 秒 的准备开销;缓存命中后,小图准备约 0.18 秒。批量处理中复用会话,可以把初始化开销分摊到后续图片上。

这些数据来自本机的少量基线测试,适合了解当前版本的量级。其他显卡、驱动和图片内容需要实际测试。

输出有没有对照参考实现

有。开发阶段使用相同权重导出的 ONNX,通过 OnnxRuntime CPU 计算参考输出,再与 Vulkan 的未量化 FP32 输出对照。

输入尺寸 最大绝对误差
64×64 约 1.01×10⁻⁶
257×193 约 2.09×10⁻⁶
640×480 约 3.40×10⁻⁶
1920×1080 约 1.93×10⁻⁵

这些数值用于检查 Vulkan 实现与参考计算的一致性。图像恢复质量则需要结合真实样本、对应真值或人工观察另行评估。

六、源码工程怎样编译

源码版提供一个完整的 VS2022 解决方案:

复制代码
ESDNet.Vulkan.WinForms.VS2022/
  ESDNet.Vulkan.WinForms.VS2022.sln
  Build.cmd
  Run.cmd
  WinForms/                 C# 界面与业务逻辑
  native/Lw.Vulkan/         C++ Vulkan 核心
  shared/                  公共基础代码
  Assets/                  模型与示例
  Tools/                   转换、验证、打包工具
  Docs/                    使用说明与测试记录
  Licenses/                第三方许可

VS2022 需要安装".NET 桌面开发""使用 C++ 的桌面开发",以及 .NET Framework 4.8 SDK/目标包、MSVC v143 和 Windows SDK。

打开解决方案,选择 Release | x64 ,把 EsdNet.WinForms 设为启动项目,然后生成并运行。也可以使用根目录的 Build.cmd 和 Run.cmd。

项目已经包含 Vulkan 编译依赖和冻结的 SPIR-V,按现有源码编译无需额外安装 Vulkan SDK;如果继续修改着色器,则需要对应的编译工具与更新步骤。

目前已经验证了 Debug、Release 两种 x64 配置,并把源码包解压到新的中文目录,从零编译和运行过示例。

做二次开发时,核心调用在哪里

在 WinForms 工程内部,可以从 EsdEngine 和 ImageJobs 入手。下面是使用现有内部类型的简化调用示例:

复制代码
// 在 WinForms 事件中交给后台线程,避免阻塞界面。
await Task.Run(() =>
{
    var paths = new AppPaths();
    var device = NativeApi.DefaultDevice(NativeApi.Devices());
    string inputPath = paths.Resolve(@"assets\samples\screen-moire.png");
    string outputPath = ImageJobs.UniquePath(
        paths.Resolve(@"输出\sample.demoire.png"));

    using (var engine = new EsdEngine(
        paths.Resolve(@"assets\models"), device))
    using (var input = ImageCodec.Load(inputPath, maxSide: 0))
    using (var result = engine.Process(input, CancellationToken.None))
    {
        ImageCodec.Save(result, outputPath, jpegQuality: 95);
    }
});

批量场景应把引擎放到循环外复用。工程里的 ImageJobs 已经处理了会话复用、子目录遍历、输出命名、进度与取消,可以直接沿着这层业务逻辑继续改。

七、资源包与当前边界

本项目准备了两种资源:

资源 适合用途 大小
Windows x64 部署包 解压后直接使用单图、批量功能 约 23 MB
VS2022 完整源码包 修改界面、研究实现、接入其他业务 约 71 MiB

部署包内附使用说明、静态文件 SHA256 清单和第三方许可;源码包包含模型转换脚本和数值验证记录。

当前版本使用 ESDNet-UHDM 小模型、FP32 整图推理,已在本机 NVIDIA GPU 上验证。AMD、Intel 等设备还需要补充实机测试。图片恢复可能平滑细节,大尺寸也可能遇到显存限制。

输出为 8 位 RGB,透明图片先合成到白底,TIFF 处理首帧;EXIF、ICC 等原图信息不会随输出保留。

源码里的尺寸计划器针对当前 ESDNet 图实现。后续接入其他网络时,需要核对算子、布局和形状处理;视频场景还需要额外考虑帧间一致性。

对我来说,这次开发比较有价值的一点,是把一个图像恢复模型连同界面、GPU 执行、批处理和部署一起做完整了。普通用户可以直接拿图片试效果,C# 开发者也能从一个可编译、可运行的工程开始,继续做自己的图像工具。

欢迎结合自己的屏幕翻拍样本试用,也欢迎交流处理效果和二次开发中的实现思路。

部署包下载

源码下载

相关推荐
2501_930707781 小时前
使用C#代码在 Word 文档中添加、插入或删除页面
开发语言·c#·word
淡海水3 小时前
03-03-架构篇-SGen GC架构解析
unity·架构·c#·gc·sgen
曹牧17 小时前
C#:输入的字符串格式不正确
c#
根号五1 天前
JetBrains Rider 使用教程(1)新建解决方案
c#·jetbrains rider
海天鹰1 天前
C#:接收从打开方式打开的参数
c#
宝桥南山1 天前
.NET 11 - 尝试一下Minimal API中的异步验证新特性
microsoft·微软·c#·asp.net·.net·.netcore
开开心心_Every1 天前
文件查重软件批量删除重复文件释放空间
java·开发语言·c#·计算机外设·ocr·excel·perl
波力海苔夹心脆6751 天前
VisionPro 与 VisionMaster 对比(上):软件定位、开发环境与核心视觉工具对照
图像处理·算法·计算机视觉·c#·视觉检测·个人开发·vp9
疯狂戴夫冒险1 天前
歇一会 · 久坐休息提醒工具(番茄钟)工具分享
c#