超分辨率与图像放大:从ESRGAN的纹理重建到UltimateSDUpscale的4K分块实战

一、为什么"把图拉大"这件事,AI 做的和 Photoshop 完全不是一回事

把一张 1024×1024 的图放大到 4K,听起来只是像素数量从约 100 万变成约 830 万。用双三次插值(Bicubic)也能在几毫秒内完成,但结果往往是一片"均匀的模糊"------因为插值算法本质上是在已知像素之间做数学平均,它没有能力创造原本不存在的边缘、纹理和微观结构。超分辨率(Super-Resolution, SR)要做的,是从低分辨率图像中"重建"出这些缺失的高频信息。这里的"重建"不是凭空捏造,而是模型在海量高---低分辨率图像对上学习到了一种先验:当看到这样一组梯度、这样一块色块过渡时,真实世界中对应的细节通常长什么样。ESRGAN 的作者在论文中把这件事的目标说得很清楚:不仅要像素接近,更要让生成出来的纹理在视觉上"像真的"。这也解释了为什么基于 GAN 的超分模型能在放大 4 倍甚至 8 倍之后,依然让织物纤维、皮肤毛孔、建筑边缘保持可辨识的锐度,而不是退化成蜡像质感。

下面这条简化的流程对比,能快速建立直觉:
#mermaid-svg-tTnUrfLQWvseY8AI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tTnUrfLQWvseY8AI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tTnUrfLQWvseY8AI .error-icon{fill:#552222;}#mermaid-svg-tTnUrfLQWvseY8AI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tTnUrfLQWvseY8AI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tTnUrfLQWvseY8AI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tTnUrfLQWvseY8AI .marker.cross{stroke:#333333;}#mermaid-svg-tTnUrfLQWvseY8AI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tTnUrfLQWvseY8AI p{margin:0;}#mermaid-svg-tTnUrfLQWvseY8AI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tTnUrfLQWvseY8AI .cluster-label text{fill:#333;}#mermaid-svg-tTnUrfLQWvseY8AI .cluster-label span{color:#333;}#mermaid-svg-tTnUrfLQWvseY8AI .cluster-label span p{background-color:transparent;}#mermaid-svg-tTnUrfLQWvseY8AI .label text,#mermaid-svg-tTnUrfLQWvseY8AI span{fill:#333;color:#333;}#mermaid-svg-tTnUrfLQWvseY8AI .node rect,#mermaid-svg-tTnUrfLQWvseY8AI .node circle,#mermaid-svg-tTnUrfLQWvseY8AI .node ellipse,#mermaid-svg-tTnUrfLQWvseY8AI .node polygon,#mermaid-svg-tTnUrfLQWvseY8AI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tTnUrfLQWvseY8AI .rough-node .label text,#mermaid-svg-tTnUrfLQWvseY8AI .node .label text,#mermaid-svg-tTnUrfLQWvseY8AI .image-shape .label,#mermaid-svg-tTnUrfLQWvseY8AI .icon-shape .label{text-anchor:middle;}#mermaid-svg-tTnUrfLQWvseY8AI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tTnUrfLQWvseY8AI .rough-node .label,#mermaid-svg-tTnUrfLQWvseY8AI .node .label,#mermaid-svg-tTnUrfLQWvseY8AI .image-shape .label,#mermaid-svg-tTnUrfLQWvseY8AI .icon-shape .label{text-align:center;}#mermaid-svg-tTnUrfLQWvseY8AI .node.clickable{cursor:pointer;}#mermaid-svg-tTnUrfLQWvseY8AI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tTnUrfLQWvseY8AI .arrowheadPath{fill:#333333;}#mermaid-svg-tTnUrfLQWvseY8AI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tTnUrfLQWvseY8AI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tTnUrfLQWvseY8AI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tTnUrfLQWvseY8AI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tTnUrfLQWvseY8AI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tTnUrfLQWvseY8AI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tTnUrfLQWvseY8AI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tTnUrfLQWvseY8AI .cluster text{fill:#333;}#mermaid-svg-tTnUrfLQWvseY8AI .cluster span{color:#333;}#mermaid-svg-tTnUrfLQWvseY8AI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tTnUrfLQWvseY8AI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tTnUrfLQWvseY8AI rect.text{fill:none;stroke-width:0;}#mermaid-svg-tTnUrfLQWvseY8AI .icon-shape,#mermaid-svg-tTnUrfLQWvseY8AI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tTnUrfLQWvseY8AI .icon-shape p,#mermaid-svg-tTnUrfLQWvseY8AI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tTnUrfLQWvseY8AI .icon-shape .label rect,#mermaid-svg-tTnUrfLQWvseY8AI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tTnUrfLQWvseY8AI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tTnUrfLQWvseY8AI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tTnUrfLQWvseY8AI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 低分辨率输入 1024×1024
放大方式
传统插值

Bicubic/Lanczos
AI 超分辨率

ESRGAN/Real-ESRGAN
像素数增加

高频信息为零

边缘均匀模糊
RRDB 特征提取
GAN 对抗训练得到的纹理先验
重建边缘/纹理/微观结构

4K 输出保持可辨识细节

这张图想强调一个容易被忽略的点:"放大"和"超分"在信息论意义上完全不同。插值是在已有采样点之间做确定性填充,信息量没有增加;超分模型则是把训练阶段压缩在权重里的自然图像先验"注入"到当前输入中,信息量是增加的。理解了这一点,后面关于模型选择、参数调优和"放大不等于修复"的讨论,才会有共同的落脚点。

二、ESRGAN 的关键一跃:RRDB 与相对判别器

ESRGAN 发表于 2018 年 ECCV Workshops,它的基础是 SRGAN,但针对 SRGAN "纹理有了、伪影也来了"的问题做了三处关键修改。第一处是生成器的基本单元从普通残差块换成了 RRDB(Residual-in-Residual Dense Block,残差嵌套密集块) 。RRDB 的内部逻辑可以这样理解:在一个残差分支里,先放一个"密集块"------密集块中每一层卷积的输入都包含前面所有层的输出,层与层之间用跳跃连接短接;然后把这个密集块的输出再与输入做残差相加。如此嵌套之后,网络能在不显著加深整体深度的情况下,让特征和梯度在多个尺度上反复混合。论文作者的原话是"更多的层和连接总是能提升性能",RRDB 就是顺着这个观察设计出来的。更关键的一个细节是:RRDB 去掉了批归一化(Batch Normalization)。BN 层在训练时会统计 batch 内的均值方差,这种统计波动对分类任务可能无伤大雅,但对超分任务来说,它会干扰网络对局部对比度和细微纹理的敏感度。去掉 BN 之后,ESRGAN 对边缘和纹理的恢复能力有了可感知的提升。

第二处修改是判别器从"判断真假"变成了"判断相对真实度"。传统 GAN 的判别器输出一个标量,表示输入是真实图像的概率;ESRGAN 采用的是相对判别器(Relativistic Discriminator),它比较的是一张真实图像和一张生成图像放在一起时,"真实图像比生成图像更真实的概率"有多大。这种相对比较让生成器收到的梯度信号更平滑,训练不容易在"真/假"的绝对边界上震荡,生成出来的纹理分布也更接近真实图像。

第三处修改在感知损失上:ESRGAN 使用 VGG 特征提取网络中 激活函数之前 的特征来计算感知损失,而不是激活之后。这个改动看似细枝末节,但它让损失函数在"亮度一致性"和"纹理恢复"之间找到了一个更好的平衡点,生成图像的对比度和细节层次更自然。

把这三处修改放在一起看,ESRGAN 的贡献不只是"更大的网络",而是用结构设计(RRDB)、训练信号设计(相对判别器)和损失设计(激活前特征)三者配合,把 GAN 超分从"能生成纹理"推进到了"生成纹理且伪影可控"的阶段。论文中报告的 PIRM2018-SR Challenge 区域三第一名,也是对这套组合有效性的一个外部验证。

三、Real-ESRGAN:当输入不再是"实验室里的干净低清图"

ESRGAN 的训练假设非常"实验室化":低分辨率图像是由高分辨率图像经过 双三次下采样 得到的。在 benchmark 上这个假设成立,但在真实使用场景里几乎从来不成立。一张网上下载的 1024×1024 图片,可能在到达你手里之前已经经历了:相机镜头的模糊、传感器的噪声、第一次 JPEG 压缩、上传时的第二次压缩、平台转码时的缩放,甚至截图和二次编辑。这一连串退化过程,远不是一次干净的下采样能描述的。拿纯粹在双三次退化上训练的 ESRGAN 去处理这种"包浆"图片,模型会把它当成"加了噪的干净图",结果要么噪点被当成纹理放大,要么纹理被过度平滑。

Real-ESRGAN 的核心思路是把退化过程本身"做脏"。它提出了 高阶退化建模(High-Order Degradation Modeling):把经典的退化链条------模糊、下采样、加噪声、JPEG 压缩------递归地应用两次,用来模拟图片被多次压缩、多次传输、多次编辑后的状态。模糊核也不再局限于简单的高斯核,而是混入各向异性核和 sinc 核,其中 sinc 核专门用来合成真实图像中常见的振铃和过冲伪影。退化参数的采样范围也刻意拉宽:模糊核大小 7 到 21,高斯核 σ 在 0.2 到 1.5,JPEG 质量因子 30 到 95。这样训练出来的模型,见到一张"脏"的输入时,不会惊讶,因为它已经在训练中见过各种程度的"脏"。

在架构上,Real-ESRGAN 的生成器基本沿用 ESRGAN 的 RRDB 结构,但判别器换成了带 谱归一化(Spectral Normalization)的 U-Net。U-Net 判别器输出的不再是一个全局标量,而是一张逐像素的真/假图,能给生成器提供更细粒度的局部反馈,这对恢复局部纹理合成质量很重要;谱归一化则用来稳定 GAN 训练,并抑制对抗训练中常见的过度锐化伪影。损失函数是 L1 损失、感知损失和对抗损失的加权组合,其中对抗损失的权重相对较小(约 0.1),目的不是让判别器主导训练,而是在保证像素级保真度的前提下,把感知质量往上推一把。

从使用者的角度看,Real-ESRGAN 的意义在于:它把"超分模型只能处理干净低清图"这个隐含前提打破了。你今天手头那张来源不明、压缩痕迹明显的 1024×1024 图片,用 Real-ESRGAN 系模型来放大,通常比用纯 ESRGAN 系模型更稳妥。

四、工程落地:UltimateSDUpscale 如何把 4K/8K 放大变成可运行的工作流

理解了模型原理,下一个问题是:1024×1024 放大到 4K 是 4 倍面积,放大到 8K 是 16 倍面积。如果直接把整张目标分辨率的图像塞进显存做推理或扩散重绘,消费级显卡几乎必然 OOM。UltimateSDUpscale 节点解决这个问题的办法不是"更省显存的模型",而是 分块(tiling) :把图像切成若干小块,逐块处理,再通过重叠区域的重绘融合把块与块之间的接缝"抹平"。这样做显存占用的峰值不再由输出分辨率决定,而是由 tile_width × tile_height 决定,你完全可以在 8GB 显存的卡上用 512×512 的块去处理一张最终 8192×8192 的输出。

4.1 安装与模型放置

UltimateSDUpscale 是 A1111 社区中 Coyote-A 的 Ultimate SD Upscale 脚本在 ComfyUI 中的封装,安装命令如下:

bash 复制代码
cd ComfyUI/custom_nodes
git clone https://github.com/ssitu/ComfyUI_UltimateSDUpscale --recursive

重启 ComfyUI 后,节点出现在菜单的 image/upscaling 分类下。超分模型(.pth 文件)统一放在 ComfyUI/models/upscale_models/ 目录,ComfyUI 的 Load Upscale Model 节点会自动扫描该路径。模型可以从 OpenModelDB 下载,该站点支持按图像类型筛选,并标注每个模型的放大倍数。

4.2 核心参数:upscale_by、tile 与 denoise 的配合

upscale_by 是宽高的倍率。如果想把 1024×1024 放大到 4K(通常指长边约 3840),4 倍放大得到 4096×4096,已经进入 4K 范畴;如果想精确控制到某个长边,应使用 UltimateSDUpscale (No Upscale) 变体,先在外部分别做 ImageUpscaleWithModel → ImageScale,再把已经插值到目标尺寸的图像送入节点做分块重绘。

tile_width / tile_height 默认 512×512。显存紧张时调小,但调得过小会增加块的数量,接缝风险和时间开销都会上升。社区中一个常见的平衡点是 512 到 768;如果卡有 12GB 以上显存,768 或 1024 的块可以明显减少分块数量,缩短总时间,但显存占用也会相应上升。

force_uniform_tiles 建议开启。图像边缘往往不能被 tile 尺寸整除,如果关闭该选项,边缘块会变小,采样更快,但不规则尺寸可能引入轻微伪影;开启后,边缘块会借用图像内部区域扩展到统一尺寸,接缝行为更一致。

最容易被误解的参数是 denoise。在 UltimateSDUpscale 中,它控制的是重叠区域融合时的重绘强度。默认建议落在 0.2--0.4 之间:太低(接近 0.1)接缝过渡生硬,块与块之间能看到"拼接感";太高(超过 0.5)扩散模型开始"重新生成"内容而不是"融合放大",块与块之间可能出现纹理逻辑不一致,甚至局部"换脸"或"换图案"。社区实践中,4K 放大的常用起点是 denoise = 0.35 左右,配合 tile = 768、padding = 55、mask_blur = 20,然后再根据图像类型微调。

4.3 完整工作流示意与节点连接

一个从生成末端接入的"生成 → 超分 → 保存"工作流,在 ComfyUI 中的连接关系可以用下面的流程图表示:
#mermaid-svg-C2XhclXAxlvOravb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-C2XhclXAxlvOravb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-C2XhclXAxlvOravb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-C2XhclXAxlvOravb .error-icon{fill:#552222;}#mermaid-svg-C2XhclXAxlvOravb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-C2XhclXAxlvOravb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-C2XhclXAxlvOravb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-C2XhclXAxlvOravb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-C2XhclXAxlvOravb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-C2XhclXAxlvOravb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-C2XhclXAxlvOravb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-C2XhclXAxlvOravb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-C2XhclXAxlvOravb .marker.cross{stroke:#333333;}#mermaid-svg-C2XhclXAxlvOravb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-C2XhclXAxlvOravb p{margin:0;}#mermaid-svg-C2XhclXAxlvOravb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-C2XhclXAxlvOravb .cluster-label text{fill:#333;}#mermaid-svg-C2XhclXAxlvOravb .cluster-label span{color:#333;}#mermaid-svg-C2XhclXAxlvOravb .cluster-label span p{background-color:transparent;}#mermaid-svg-C2XhclXAxlvOravb .label text,#mermaid-svg-C2XhclXAxlvOravb span{fill:#333;color:#333;}#mermaid-svg-C2XhclXAxlvOravb .node rect,#mermaid-svg-C2XhclXAxlvOravb .node circle,#mermaid-svg-C2XhclXAxlvOravb .node ellipse,#mermaid-svg-C2XhclXAxlvOravb .node polygon,#mermaid-svg-C2XhclXAxlvOravb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-C2XhclXAxlvOravb .rough-node .label text,#mermaid-svg-C2XhclXAxlvOravb .node .label text,#mermaid-svg-C2XhclXAxlvOravb .image-shape .label,#mermaid-svg-C2XhclXAxlvOravb .icon-shape .label{text-anchor:middle;}#mermaid-svg-C2XhclXAxlvOravb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-C2XhclXAxlvOravb .rough-node .label,#mermaid-svg-C2XhclXAxlvOravb .node .label,#mermaid-svg-C2XhclXAxlvOravb .image-shape .label,#mermaid-svg-C2XhclXAxlvOravb .icon-shape .label{text-align:center;}#mermaid-svg-C2XhclXAxlvOravb .node.clickable{cursor:pointer;}#mermaid-svg-C2XhclXAxlvOravb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-C2XhclXAxlvOravb .arrowheadPath{fill:#333333;}#mermaid-svg-C2XhclXAxlvOravb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-C2XhclXAxlvOravb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-C2XhclXAxlvOravb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C2XhclXAxlvOravb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-C2XhclXAxlvOravb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C2XhclXAxlvOravb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-C2XhclXAxlvOravb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-C2XhclXAxlvOravb .cluster text{fill:#333;}#mermaid-svg-C2XhclXAxlvOravb .cluster span{color:#333;}#mermaid-svg-C2XhclXAxlvOravb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-C2XhclXAxlvOravb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-C2XhclXAxlvOravb rect.text{fill:none;stroke-width:0;}#mermaid-svg-C2XhclXAxlvOravb .icon-shape,#mermaid-svg-C2XhclXAxlvOravb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C2XhclXAxlvOravb .icon-shape p,#mermaid-svg-C2XhclXAxlvOravb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-C2XhclXAxlvOravb .icon-shape .label rect,#mermaid-svg-C2XhclXAxlvOravb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C2XhclXAxlvOravb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-C2XhclXAxlvOravb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-C2XhclXAxlvOravb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
KSampler 输出

1024×1024 潜空间/图像
VAE Decode
UltimateSDUpscale 主节点
Load Upscale Model

4x-UltraSharp.pth 或 RealESRGAN_x4plus.pth
Load Checkpoint

用于重绘的扩散模型
Save Image

4096×4096
需要 8K?
链式第二个放大节点

或直接调大 upscale_by

如果只想做纯模型放大(不引入扩散重绘),可以走更轻量的路径:Load Upscale Model + Upscale Image (Using Model)。这条路径速度最快,显存需求最低,适合输入本身质量较好、只需要"干净地变大"的场景;代价是没有扩散模型参与修补,如果原图有轻微噪点或压缩痕迹,模型放大可能会把这些问题一起放大。UltimateSDUpscale 的复杂之处,恰恰在于它把"放大"和"重绘修补"合并到了一个节点里:分块提供的是显存可行性,denoise 提供的是接缝融合和局部细节调整的抓手。

4.4 直接用 Python 调用 .pth 做推理

并不是所有流程都需要跑完整的 ComfyUI。如果你只想批量对一批图片做纯 ESRGAN/Real-ESRGAN 放大,可以直接用 RealESRGANer 加载 .pth 权重:

python 复制代码
import cv2
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer

# 声明与权重匹配的网络结构
model = RRDBNet(
    num_in_ch=3, num_out_ch=3,
    num_feat=64, num_block=23, num_grow_ch=32,
    scale=4
)

upsampler = RealESRGANer(
    scale=4,
    model_path="weights/RealESRGAN_x4plus.pth",
    model=model,
    tile=512,          # 分块尺寸,0 表示不分块
    tile_pad=10,       # 块与块之间的重叠 padding
    pre_pad=0,
    half=True          # FP16 推理,显存减半,多数现代显卡可用
)

img = cv2.imread("input.png", cv2.IMREAD_UNCHANGED)
output, _ = upsampler.enhance(img, outscale=4)
cv2.imwrite("output_4x.png", output)

这里的 tile 和 tile_pad 是显存控制的关键:tile=512 意味着峰值显存大致对应 512×512 的中间特征,而不是整张输出图;tile_pad 给相邻块留出重叠边距,避免接缝处因为上下文不足而出现亮度跳变。half=True 在支持 FP16 的 NVIDIA 显卡上可以把显存占用进一步压低,速度通常也有提升。

如果希望从脚本侧触发 ComfyUI 的工作流,则需要先在 ComfyUI 界面中把工作流导出为 API 格式 ,然后用 HTTP 请求提交 prompt。下面是一段使用标准库构造请求的示意(省略了具体节点 ID,因为每张工作流导出的节点编号不同):

python 复制代码
import json, urllib.request, uuid

with open("upscale_api_workflow.json", "r", encoding="utf-8") as f:
    workflow = json.load(f)

# 假设工作流中 UltimateSDUpscale 节点的 id 为 "12"
workflow["12"]["inputs"]["upscale_by"] = 4
workflow["12"]["inputs"]["tile_width"] = 512
workflow["12"]["inputs"]["tile_height"] = 512
workflow["12"]["inputs"]["denoise"] = 0.35

payload = {
    "prompt": workflow,
    "client_id": str(uuid.uuid4())
}

req = urllib.request.Request(
    "http://127.0.0.1:8188/prompt",
    data=json.dumps(payload).encode("utf-8"),
    headers={"Content-Type": "application/json"}
)
urllib.request.urlopen(req)

这段代码的意义在于把超分从"手动点按钮"变成可批量、可调度的生产环节。导出 API 格式的方法是:在 ComfyUI 菜单中选择"文件 → 导出工作流(API)",得到的就是上面这种以节点 ID 为键的字典结构。

五、模型选择与评价:没有"最好",只有"匹配输入"

社区里常被拿来对比的两个 4 倍模型是 RealESRGAN_x4plus 和 4x-UltraSharp。前者训练数据以真实世界照片为主,风格偏向 平衡和稳健 ,对压缩痕迹、噪声、来源不明的图片容忍度高,是不知道选什么时的安全起点;代价是对插画或动漫风格的内容有时会 过度平滑 ,把本来干净的色块和线条处理得偏"肉"。4x-UltraSharp 则在细节恢复上更激进,倾向于保留更多高频纹理,在 AI 生成图、建筑、人像等需要"锐利感"的场景里表现突出;但它会把噪点和压缩痕迹也一起放大,画面有时显得"脏",在高对比边缘和非常平滑的渐变(比如天空、皮肤)上还可能引入轻微锐化伪影。对于动漫内容,Real-ESRGAN 提供了专门的 RealESRGAN_x4plus_anime_6B,模型体积更小,线条和色块的处理针对性更强。

评价超分质量时,单一指标容易产生误导。PSNR 衡量像素级均方误差,数值越高代表像素越接近参考图;SSIM 从亮度、对比度和结构相似性出发,更接近人眼对"结构是否保留"的判断;LPIPS 则用深度特征距离来度量感知相似度,数值越低表示感知上越接近参考图。这三个指标的组合逻辑是:PSNR 和 SSIM 高,说明保真度好;LPIPS 低,说明感知质量好;但 GAN 类模型恰恰可能在 PSNR 上不占优,却在 LPIPS 和主观视觉上明显更好------因为它选择了"生成合理纹理"而不是"平滑地接近像素均值"。所以在实际选型时,更可靠的做法是:固定同一张输入,用 2--3 个候选模型各跑一次,并排对比边缘锐度、纹理自然度和伪影情况,再结合客观指标做辅助判断。

六、几个容易踩坑的认知,以及参数速查

第一,放大不等于修复。 如果原图本身的信息量已经很低------比如一张 200×200 的严重模糊人脸------直接放大到 4K 得到的是一张"清晰但空洞"的图:皮肤纹理看起来合理,但那是模型根据先验"猜"出来的,不是原图里真实存在的。人脸尤其容易暴露出这种"过度生成"的蜡像感。遇到信息量过低的输入,更合理的策略是先做修复或重绘,再放大,而不是指望一次超分解决所有问题。

第二,显存和时间的权衡是真实的,分块是在用时间换空间。 一张 1024×1024 放大到 4K,在 RTX 3060 级别显卡上,tile=512 通常需要数分钟量级;把 tile 调到 1024 会减少分块数量、缩短总时间,但峰值显存大致翻倍。如果显存只有 8GB,不要尝试 1024 的 tile 去直接跑 4K 输出,从 512 甚至 384 起步更稳妥。

第三,"先生成小图,再放大"是效率最高的策略。 扩散模型在 1024×1024 甚至 768×768 上的构图控制力和注意力集中度,通常好于直接生成 4K。正确的流程是:用小尺寸生成满意的构图和内容,再把超分作为流程末端的一个独立阶段。这样做不仅显存友好,最终细节质量往往也更好,因为放大阶段只负责它最擅长的事------补充高频信息,而不是同时承担构图和纹理生成的双重压力。

下表汇总了 UltimateSDUpscale 在 4K 放大场景下的常用参数起点,可以根据显存和图像类型在此基础上微调:

参数 建议起点 作用与调整方向
upscale_by 4(1024→4096) 宽高倍率;需要精确尺寸时改用 No Upscale 变体
tile_width/height 512(8GB 显存)/ 768(12GB+) 峰值显存的主要决定因素;越小越省显存,但接缝风险上升
denoise 0.3--0.4 重叠区域重绘强度;过高会"重新生成",过低接缝生硬
force_uniform_tiles 开启 统一边缘块尺寸,减少不规则接缝伪影
tile_pad 10--16 块间重叠边距,给融合留上下文
模型 RealESRGAN_x4plus(通用/老图) 4x-UltraSharp(AI 图/建筑/人像) 按输入类型选择,不要指望一个模型通吃

七、写在最后

从 ESRGAN 的 RRDB 到 Real-ESRGAN 的高阶退化,再到 UltimateSDUpscale 的 tiling 工程封装,这条链路回答的其实是同一个问题的三个层次:怎样让模型"知道"缺失的细节应该长什么样(网络与训练),怎样让模型"适应"真实世界中脏的输入(退化建模),以及怎样在有限显存里把这种能力用到 4K 甚至 8K 的输出上(分块与流程设计) 。1024×1024 到 4K 不是简单地把 upscale_by 设成 4 就结束了;模型选择决定了纹理的"性格",tile 决定了能不能跑得动,denoise 决定了接缝是消失还是变成另一种瑕疵。把这几个变量之间的关系理清楚,你手里的超分节点就不再是一个黑盒按钮,而是一个可以按图像类型、显存预算和输出目标来调节的工程工具。

相关推荐
武子康1 小时前
H3-World 的动作没按时发生,先检查时间线变成了什么
人工智能·后端·agent
jimmyleeee1 小时前
Agent安全之五:当语言变成执行------深入理解Unexpected Code Execution (ASI05)
人工智能·安全
谁在黄金彼岸1 小时前
Ollama 还是 LM Studio:在 24GB 的 M6 Mac 上,我把 Ollama 卸载了
人工智能
方方洛1 小时前
ray教程-18-性能调优与反模式
人工智能·分布式·机器学习
人工智能培训1 小时前
知识图谱:结构化表达实体与关系的知识组织技术
大数据·人工智能·算法·重构·生活·知识图谱
GIS数据转换器2 小时前
基于CIM的城市智慧供热监管服务平台
java·大数据·开发语言·人工智能·后端·物联网·struts
专注仿真2 小时前
动态规划算法详解
人工智能·python·大模型训练·控制算法
xingyuzhisuan2 小时前
不止文生图:StarverseAI 商业视觉解锁视频、展台全场景创作
人工智能
编程见习者2 小时前
AI应用开发之LangGraph解析
人工智能·知识图谱