资讯动态

Python实现Welsh算法:灰度图像彩色化从原理到落地

发布时间:2026/10/18 20:49:41 来源:尧图企业网站定制
简介基于Welsh算法的灰度图像彩色化处理及优化python实现源码包面向计算机相关专业学生、毕业设计及图像处理爱好者解决从灰度图自动匹配彩色参考图并减少噪声伪影的问题。资源共16个文件包含4个python源文件、8张测试效果对比图、3个编译缓存文件及1份项目说明文档压缩包约533KB结构紧凑可直接运行。已有432人浏览学习。代码基于Welsh颜色转移算法完成灰度图彩色化并引入导向滤波代替常规滤波器去噪优化可保留边缘细节、抑制梯度反转伪影源文件附有详细注释说明文档梳理算法原理与实验流程配有灰度图、参考图、彩色化结果及优化效果图方便理解与二次修改适合课程设计或毕业设计参考也是学习颜色迁移与引导滤波的入门实例。1. 灰度图彩色化为什么第一反应该是 Welsh 算法看到“灰度图像彩色化”这个需求很多人第一反应是上深度学习。但如果你手里没有成对的彩色训练数据或者只想快速验证思路、跑通一条可复现的链路基于 Welsh 算法的传统方案反而是最稳的起点。它的核心思想不是“无中生有”而是“借色”找一张色彩参考图把它的像素按亮度映射到灰度图上让颜色跟着亮度走。这套方法不需要 GPU不需要训练只要一台普通电脑、一张参考图、一段 Python 代码就能出结果。本文就把这个实现过程拆开讲从颜色空间到逐像素匹配再到加速和踩坑一步一步让你能照着复现。2. 彩色化问题的本质灰度图到底缺了什么信息2.1 灰度图缺的不是亮度而是色度先验一张灰度图每个像素只有一个数值通常代表亮度Luminance。彩色化的任务是给它补上两个信息色相和饱和度合起来叫色度Chrominance。问题是同一个灰度值可能对应无数种颜色比如亮度都等于 128 的像素既可能是浅蓝色的天空也可能是灰色的水泥地。如果没有先验知识这个映射就是一个病态问题。常见的人工填色方案是交互式的用户先在图上画几笔颜色算法再把颜色扩散到整张图。这种方案效果好但依赖人工标注批量处理的时候不现实。另一种思路是用深度学习做端到端预测但这需要大量彩色训练数据而且模型的可解释性差出问题的时候很难定位。Welsh 算法的定位恰好介于两者之间它不需要人工标注也不需要训练只需要一张参考图。它的先验是“亮度相近的像素颜色也应该相近”。这个假设并不总是成立但对大多数自然图像来说它已经足够产生观感自然的结果。这种“用参考图代替模型”的思路让它在样本不足或快速 Demo 的场景下非常实用。2.2 颜色空间选型为什么不能用 RGB 直接配把彩色图像拆成亮度和色度第一步就是转换颜色空间。RGB 空间不适合做这件事因为 R、G、B 三个通道高度相关光照一变三个值一起变很难把亮度和色度干净地切开。Welsh 算法用的是 lαβ 颜色空间其中 l 通道表示亮度α 和 β 通道表示色度它的设计目标就是让三个通道尽可能去相关。lαβ 空间有一个关键性质通道之间的独立性比较好。这就意味着算法可以单独对 l 通道做亮度匹配匹配完成后直接把参考像素的 α、β 值拿过来贴到目标像素上而不用担心“亮度匹配准了但颜色通道互相污染”。相比之下如果直接在 RGB 空间按欧氏距离匹配匹配到的像素可能亮度接近但颜色完全偏掉。RGB 到 lαβ 的数学变换分两步先从 RGB 转到 LMS 锥响应空间再对 LMS 取对数后做一次去相关旋转。去相关旋转的目的是把三个通道的冗余信息压缩掉让第一维尽量承载亮度后两维尽量承载色差。第一步的变换矩阵是公开的不需要自己推导直接把矩阵写成 numpy 数组调用即可。2.3 参考图怎么选直接决定结果上限很多人在网上复现 Welsh 算法跑出来的图效果奇怪第一反应是代码写错了。实际上大部分情况下是参考图选得不对。参考图的亮度范围应该和目标灰度图接近色度分布应该尽量覆盖目标场景可能出现颜色。比如给一张夜景灰度图做彩色化你找一张阳光下的风景图做参考亮部会大量过曝色相也会偏移。反过来找一张夜景图效果会好很多。场景相似性比色彩鲜艳程度更重要。一张色彩单调的雾天参考图不可能给灰度图“脑补”出鲜艳的蓝绿色但一张色彩丰富的参考图如果场景不匹配出来的颜色又像贴纸一样生硬。实际上选参考图有点像调参数没有什么标准答案只能按目标图的内容多试几张。以我自己的经验先看亮度直方图是否大致重合再看主体色是否覆盖需求这两步比调任何代码参数都重要。3. 用 Python 从零实现 Welsh 算法最小可跑通版本3.1 颜色空间转换代码RGB ↔ lαβ正式动手之前先把颜色空间工具准备好。这里给出 RGB 到 lαβ 和反变换的两个函数使用的是公开的 LMS 矩阵和去相关旋转。注意输入输出都是 float 数组范围在 [0, 1] 之间建议先归一化再传进来。import numpy as np # RGB 转 LMS 的矩阵来源是颜色科学里的标准 LMS 锥响应模型 M_RGB2LMS np.array([ [0.3811, 0.5783, 0.0402], [0.1967, 0.7244, 0.0782], [0.0241, 0.1288, 0.8444] ]) # lαβ 空间中去相关旋转的矩阵 M_LMS2LAB np.array([ [1.0 / np.sqrt(3.0), 0.0, 0.0], [0.0, 1.0 / np.sqrt(6.0), 0.0], [0.0, 0.0, 1.0 / np.sqrt(2.0)] ]) np.array([ [1.0, 1.0, 1.0], [1.0, 1.0, -2.0], [1.0, -1.0, 0.0] ]) def rgb2lalphabeta(rgb): 输入 rgb: HxWx3 float 数组范围 [0,1]输出 l, alpha, beta 三个通道 # 防止取对数时出现 0 rgb np.maximum(rgb, 1e-6) lms np.dot(rgb, M_RGB2LMS.T) lms np.log10(lms) lab np.dot(lms, M_LMS2LAB.T) return lab[:, :, 0], lab[:, :, 1], lab[:, :, 2] def lalphabeta2rgb(l, alpha, beta): 输入 l, alpha, beta 三个 HxW 通道输出 rgb: HxWx3 float 数组范围 [0,1] h, w l.shape lab np.stack([l, alpha, beta], axis-1) lms np.dot(lab, np.linalg.inv(M_LMS2LAB).T) lms np.power(10.0, lms) rgb np.dot(lms, np.linalg.inv(M_RGB2LMS).T) return np.clip(rgb, 0.0, 1.0)这段代码里有两处需要注意。第一取对数前必须加一个很小的偏移量否则纯黑像素的 LMS 值为 0log10(0) 直接报错第二反变换时要对矩阵求逆不要自己手算乘回去。另外 OpenCV 读取的图像默认是 BGR 顺序喂给这个函数之前要用cv2.COLOR_BGR2RGB转一下或者把矩阵的输入通道顺序对调。3.2 核心匹配逻辑亮度索引 最近邻查找Welsh 算法最朴素的实现是双重循环遍历目标图的每个像素再从参考图里找一个亮度最接近的像素。这种写法在 100×100 的小图上跑起来都费劲2000×3000 的图直接卡死。所以工程实现必须做两件事第一把参考图像素按亮度排序第二用二分查找找最近邻替代线性扫描。下面的代码构建一个参考亮度索引这个索引可以复用灰度图的每个像素只需要一次np.searchsorted就能找到位置再比较左右两个候选的亮度差取更近的那个。这个实现的复杂度是 O(N log M)N 是目标像素数M 是参考像素数比 O(N*M) 的暴力版本快了几个数量级。def build_luminance_index(ref_l): 对参考图亮度通道建排序索引返回排序后的亮度数组和原像素下标 flat_l ref_l.flatten() sort_idx np.argsort(flat_l) return flat_l[sort_idx], sort_idx def match_pixels(target_l, sorted_ref_l, sort_idx): 对目标亮度图的每个像素在参考亮度数组中找最近邻返回对应像素下标 flat_t target_l.flatten() pos np.searchsorted(sorted_ref_l, flat_t) # searchsorted 返回插入位置需要检查左右两个候选 left np.clip(pos - 1, 0, len(sorted_ref_l) - 1) right np.clip(pos, 0, len(sorted_ref_l) - 1) left_diff np.abs(flat_t - sorted_ref_l[left]) right_diff np.abs(flat_t - sorted_ref_l[right]) chosen np.where(left_diff right_diff, left, right) return sort_idx[chosen]这里的searchsorted是 numpy 里的二分查找默认对升序数组有效所以参考亮度数组必须先排序。可能有人会问如果目标像素的亮度正好落在参考亮度数组两个值中间取左还是取右这个选择对结果影响不大因为相邻像素的亮度差通常很小色度也接近。如果实在不放心可以在左右差值相等的时候加一个随机扰动避免产生可察觉的人工条纹。另外一个容易忽略的点是如果灰度图里有大片纯白或纯黑区域它们的亮度值会落在参考亮度数组两端之外。这种情况下searchsorted会把所有像素推到同一侧选出来的参考像素可能来自参考图里少数几个极端亮/暗点。解决方法是先对两端的亮度做截断或者干脆把参考图的亮度范围拉伸到和目标一致。3.3 完整主流程从读图到保存结果现在把上面的工具函数串起来写一个完整的调用流程。这里的 ref_img 就是参考彩色图gray_img 是待上色的灰度图。主流程分四步读取图像、转 lαβ、亮度匹配、贴色并转回 RGB。import cv2 def colorize(ref_img, gray_img, sample_ratio1.0): # 1. 转换参考图到 lαβ ref_rgb cv2.cvtColor(ref_img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 ref_l, ref_a, ref_b rgb2lalphabeta(ref_rgb) # 2. 转换灰度图灰度图只有一个通道复制三份当作 RGB 再转 gray_rgb cv2.cvtColor(gray_img, cv2.COLOR_GRAY2RGB).astype(np.float32) / 255.0 gray_l, _, _ rgb2lalphabeta(gray_rgb) # 3. 建索引 匹配 sorted_l, sort_idx build_luminance_index(ref_l) matched_idx match_pixels(gray_l, sorted_l, sort_idx) h, w gray_l.shape flat_a ref_a.flatten()[matched_idx].reshape(h, w) flat_b ref_b.flatten()[matched_idx].reshape(h, w) # 4. 合并 l、a、b转回 RGB out_rgb lalphabeta2rgb(gray_l, flat_a, flat_b) out_bgr cv2.cvtColor((out_rgb * 255).astype(np.uint8), cv2.COLOR_RGB2BGR) return out_bgr # 调用示例 ref cv2.imread(ref.png) gray cv2.imread(gray.png, cv2.IMREAD_GRAYSCALE) result colorize(ref, gray) cv2.imwrite(result.png, result)这段代码里有一个小陷阱灰度图直接复制三份转 lαβ取出来的 l 通道和单通道灰度值并不一样。因为 RGB→LMS→l 的变换不是简单的取均值它经过了对数压缩和去相关旋转。不过没关系我们要的就是这个 l 域内的亮度值它比原始灰度值更接近人眼感知。如果你发现结果图亮度偏低可以对比一下 gray_l 和原始灰度直方图多半是参考图的亮度整体偏暗。sample_ratio参数在基础版本里先用默认值 1.0表示所有参考像素都参与索引构建。在下一章会用它做采样加速这里先留个口子。整个流程跑完应该能产出一张和你预期接近的彩色图。如果颜色完全不对先不要调代码而是换一张参考图试一次。4. 优化方向从“能出图”到“效率和质量都能打”4.1 加速优化像素采样 最近邻上采样基础版本虽然比双重循环快了不少但遇到 4000×3000 的大图逐像素匹配仍然要秒级甚至更久。而且很多像素的亮度值非常接近匹配结果几乎一样完全没必要逐像素计算。常见的加速方式是随机采样一部分目标像素做匹配其余像素的色度通过插值或复制获得。具体做法是对灰度图做网格状采样每隔 k 行 k 列取一个像素做匹配得到一个低分辨率的 a、b 通道图然后用升采样把它放大回原尺寸。插值方法选最近邻或双线性都可以如果采样间隔不大最近邻就够用。下面这段代码把colorize函数改造成可运行加速版本。def colorize_fast(ref_img, gray_img, sample_step4): h, w gray_img.shape # 1. 取参考图和灰度图的 l 通道 ref_rgb cv2.cvtColor(ref_img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 ref_l, ref_a, ref_b rgb2lalphabeta(ref_rgb) gray_rgb cv2.cvtColor(gray_img, cv2.COLOR_GRAY2RGB).astype(np.float32) / 255.0 gray_l, _, _ rgb2lalphabeta(gray_rgb) # 2. 建索引 sorted_l, sort_idx build_luminance_index(ref_l) # 3. 对采样点匹配 sample_y np.arange(0, h, sample_step) sample_x np.arange(0, w, sample_step) sy, sx np.meshgrid(sample_y, sample_x, indexingij) sample_l gray_l[sy, sx] matched match_pixels(sample_l, sorted_l, sort_idx) flat_a ref_a.flatten()[matched] flat_b ref_b.flatten()[matched] a_small flat_a.reshape(sy.shape[0], sx.shape[1]) b_small flat_b.reshape(sy.shape[0], sx.shape[1]) # 4. 升采样到原尺寸 a_full cv2.resize(a_small, (w, h), interpolationcv2.INTER_NEAREST) b_full cv2.resize(b_small, (w, h), interpolationcv2.INTER_NEAREST) out_rgb lalphabeta2rgb(gray_l, a_full, b_full) return cv2.cvtColor((out_rgb * 255).astype(np.uint8), cv2.COLOR_RGB2BGR)sample_step4表示每 4 个像素取一个总计算量降到原来的 1/16速度提升非常明显。具体间隔多大合适取决于图像内容和你的耐心。纹理细腻的人像建议 step 不超过 8否则眼睛和嘴唇的边界会糊掉大面积天空、墙面等平坦区域step 设为 16 影响也不大。有一个容易踩的坑是升采样之后 a、b 通道变平滑了但 l 通道保留了原始高频细节这会导致边缘处色度和亮度不匹配产生类似水彩画的渗色感。缓解办法是升采样用双线性插值并且在之后对 a、b 通道做一次轻微的高斯模糊把边缘的突兀感压下去。4.2 质量优化中值滤波消除彩色噪声和色斑Welsh 算法直接输出的结果经常布满细小的彩色噪点尤其在亮度平缓的区域比如天空、皮肤、墙面。原因是这些区域原本亮度很接近匹配到的参考像素却可能来自色彩差异很大的位置导致相邻像素的颜色跳变。这种噪点用高斯滤波能压下去但会把边缘颜色也抹掉更好的选择是中值滤波。中值滤波的核大小建议从 3 开始试最大不要超过 7。核太大会让颜色在物体边缘“溢出”比如红色衣服的边界渗到背景里。滤波应该只作用于 a、b 通道不要动 l 通道否则损失细节。下面这行代码用 scipy 实现简单粗暴。from scipy.ndimage import median_filter a_full median_filter(a_full, size5) b_full median_filter(b_full, size5)滤波核大小和采样间隔要搭配使用。采样间隔越大a、b 通道原本就越粗糙需要的滤波核就越大。我自己试过一组组合sample_step8配size5的效果比sample_step4配size3快一倍两者视觉质量接近。如果追求极致速度可以在这个基础上加大采样间隔和核大小但要注意观察边缘是否变色。4.3 亮度直方图匹配解决参考图明暗不匹配问题参考图亮度分布和目标灰度图差距过大时匹配结果经常出现一团一团的色块。比如参考图整体很暗而目标灰度图偏亮那么目标图亮部像素都会匹配到参考图最亮的几个像素上颜色单一且失真。这种情况下最先做的不是调匹配逻辑而是把参考图的亮度直方图对齐到目标图。直方图匹配的做法是分别计算两幅图的亮度累积分布函数CDF然后构造一个映射让参考图的每个亮度值映射到目标图对应分位数的亮度值。实现起来不复杂但需要先把 l 通道量化到 256 级灰度。加了这个预处理后参考图的亮度分布被强制贴合目标图色度匹配仍然按原始亮度进行但匹配结果会自然很多。def hist_match_lchannel(src_l, ref_l, bins256): 把 src_l 的亮度直方图匹配到 ref_l返回匹配后的亮度 src_flat src_l.flatten() ref_flat ref_l.flatten() src_hist, _ np.histogram(src_flat, binsbins, densityTrue) ref_hist, _ np.histogram(ref_flat, binsbins, densityTrue) src_cdf np.cumsum(src_hist) / np.sum(src_hist) ref_cdf np.cumsum(ref_hist) / np.sum(ref_hist) # 构造反向映射对每个源灰度级找参考 CDF 中最接近的位置 map_table np.zeros(bins, dtypenp.float32) for i in range(bins): diff np.abs(ref_cdf - src_cdf[i]) map_table[i] np.argmin(diff) / (bins - 1) # 量化源亮度到 [0, bins-1]查表 src_q np.floor(src_flat * (bins - 1)).astype(np.int64) return map_table[src_q].reshape(src_l.shape)这个函数的输入输出都是 float 数组范围在 [0, 1] 之间。注意np.histogram默认返回的密度直方图的 bin 宽度不是 1直接用累计和做 CDF 时需要除以总和归一化。映射表构造是 O(bins²) 的bins 取 256 完全没压力。需要注意的是直方图匹配改变了参考图的亮度通道但 a、b 通道还是原来的值。这就等于把彩色像素从亮度高的位置强行挪到亮度低的位置色度本身没有变但和场景的配合度会变。如果参考图和目标图内容差异太大这个操作也救不回来。所以它适合“参考图明暗不对”的场景不适合“参考图内容完全不对”的场景。4.4 可复现的参数组合参考表这几个优化措施之间不是互相独立的采样间隔、滤波核大小、匹配后是否平滑存在隐藏的联动关系。为了方便复现我把测试过的参数组合整理成了一张表。这里的图像尺寸假设在 2000×1500 左右如果分辨率翻倍采样间隔可以同步加大。使用场景sample_step滤波核 size直方图匹配耗时参考快速预览125开启约 0.5 秒标准出图63开启约 1 秒高质量人像23关闭约 3 秒耗时参考只是相对量级具体还取决于代码里是否把build_luminance_index挪到了循环外。这个索引只需要建一次如果有多张灰度图要批量上色提前把它存成 numpy 文件能省掉重复排序的时间。适配表里的数值不是死的图片内容干净、纹理少可以整体往上调细节密集则整体往下调。出图后如果颜色稀疏、像褪了色下调采样间隔如果噪声重先上调滤波核大小不要反过来。5. Welsh 算法避坑与常见问题5 条踩坑记录5.1 现象整张图颜色发灰像蒙了一层雾原因分析参考图和灰度图的亮度分布不一致且没有做直方图匹配。参考图整体偏亮时目标图的暗部像素会全部匹配到参考图最暗的一小部分像素上这些像素的色度本身不饱和导致暗部没颜色。反过来参考图偏暗则亮部发灰。这类问题的症状是全画面色彩层次丢失而不是局部块状发灰。解决办法对参考图和目标图的 l 通道做直方图匹配。匹配后如果颜色还是发灰检查代码里是否对 a、b 通道做了归一化。有些实现会把 a、b 通道缩放到 [-1, 1] 再贴回去但 lαβ 空间的 a、b 取值范围本来就很小一旦强归一化饱和色彩会被压平。我建议 a、b 通道保持原始范围只对 l 通道做对齐。5.2 现象人物皮肤变成绿色或蓝色原因分析这是色度在空间上不连续导致的。肤色区域的灰度值范围很窄参考图里这个亮度范围可能正好有天空、树叶等像素。如果匹配时只按亮度一个维度选择肤色区域就会随机挑选到蓝色或绿色像素形成斑点。这是 Welsh 算法最典型的翻车场景。解决办法给匹配加一个空间约束——目标像素的相邻像素如果颜色连续优先沿用相邻区域已确定的色度而不是各自独立匹配。在代码里可以在匹配完成之后对 a、b 通道做中值滤波强制让邻近像素的颜色趋向一致。如果还不满意就把参考图换成一张肤色占比更大的肖像图或者手动把参考图里蓝色和绿色像素剔除后再建索引。5.3 现象代码跑起来奇慢几分钟都没结果原因分析大概率是某个环节用了 Python 原生循环。比如逐像素调find或者对参考图做线性扫描复杂度直接爆表。另一种可能是在colorize_fast里每次调用都重新构建了参考亮度索引没有把它缓存下来重复计算拖慢了整体性能。解决办法检查代码里有没有for i in range(h): for j in range(w):这样的嵌套循环有就换成searchsorted向量化版本。第二步检查是否重复构建索引。如果用match_pixels对同一张参考图做多次,把sorted_l, sort_idx存到内存里不要每次匹配都重新argsort。最后确认sample_step没有被写成 1否则加速逻辑形同虚设。5.4 现象结果图出现大片纯色区域像贴了色块原因分析灰度图里如果有大面积的平坦区域所有像素亮度几乎一样它们会不约而同匹配到参考图亮度直方图里同一个峰值。如果参考图里这个亮度位置的像素来自一片纯色背景那目标图就会被贴上同一块颜色。这个问题和采样间隔无关是算法本身的统计偏差。解决办法在匹配时引入随机扰动也就是对目标像素的亮度值加上一个很小的噪声再做最近邻查找。这样平坦区域的像素会分散到参考图里多个亮度接近的位置打破色块。扰动幅度建议在参考图亮度标准差的 5% 到 10% 之间太大画质受损太小没效果。另外也可以把参考图里占比过大的背景区域降低权重但工程上稍麻烦随机扰动是最快的手段。5.5 现象转回 RGB 后颜色过饱和或者颜色溢出边界原因分析过饱和通常出在lalphabeta2rgb的反变换。lαβ 转 RGB 的矩阵逆运算对数值范围很敏感如果输入 l 通道值异常大反变换后 RGB 会被np.clip截断到 [0,1]但截断的同时颜色比例也变了导致色相偏移。颜色溢出边界则多半是滤波核太大或采样间隔过大色度在边缘处被平滑到了物体外。解决办法反变换前检查 l 通道是否在合理范围。lαβ 空间里 l 通道一般是 [0, 100] 或归一化后的 [0, 1]如果你用灰度图直接复制出的 RGB 计算 l它的范围可能比参考 l 大得多需要先做一次线性缩放到参考图范围。边界溢出问题把滤波核减小到 3或者对 a、b 通道的滤波改成“先按图像梯度加权”再做各向异性滤波能显著改善边缘渗色。6. 验证方法如何证明你的彩色化结果真的有效调参调到视觉满意是一回事但如果你要给领导或客户交付或者要批量处理一批图还需要一个客观验证手段。最常用的验证是拿一张彩色原图先把它转成灰度图再用算法恢复成彩色然后和原图做对比。对比指标选 PSNR 和 SSIM 两个就够PSNR 反映像素级误差SSIM 反映结构相似度两者结合能看出算法在哪类场景下表现好。import cv2 def evaluate_colorize(gt_color, ref_color, sample_step6): gray cv2.cvtColor(gt_color, cv2.COLOR_BGR2GRAY) result colorize_fast(ref_color, gray, sample_stepsample_step) psnr cv2.PSNR(gt_color, result) ssim cv2.compare_SSIM(cv2.cvtColor(gt_color, cv2.COLOR_BGR2GRAY), cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)) return psnr, ssim注意 SSIM 需要灰度图输入因为彩色图像的 SSIM 算法在 OpenCV 里支持并不稳定所以先转灰度再比结构。如果 PSNR 在 20 dB 上下说明颜色整体偏差明显先检查参考图是不是和原图场景差太远如果 PSNR 不高但 SSIM 很高说明结构保住了只是色度整体偏移可以尝试亮度对齐而不是改匹配逻辑。我习惯在调每个参数组合时都跑一遍这个验证并且把结果记录成一个 CSV 表按图像名、sample_step、滤波核、PSNR、SSIM 列出来。这样出现新的测试图时可以直接翻历史报告找最优参数不用重复试。有一次我为了让人像肤色更自然把中值滤波从 3 调到 5肉眼看着舒服了但 SSIM 反而掉了 0.02说明边缘结构被破坏最后又调回 3 并加了一点轻微高斯模糊才算稳住。验证这一步还有一个容易被忽略的功能它能暴露算法失败的模式。如果同一张图换不同参考图PSNR 波动超过 5 dB说明结果对参考图极其敏感这时候应该优先思考参考图选取策略而不是在代码里做文章。反过来如果换参考图结果几乎不变说明灰度图的亮度信息本身就限定了色彩范围算法已经尽力了。我的建议是把“参考图选择”也当作一个可调参数管理起来。我一般会给每个待处理的灰度图准备三张候选参考图一张场景相似的、一张亮度分布相似的、一张色彩饱和度高的先用快速采样跑一遍客观指标选出 PSNR/SSIM 最好的那张再做最终渲染。这个做法看起来多花了三倍时间实际上比在参数迷宫里来回试探要高效得多。希望这套验证方法能帮到你让你的彩色化结果不只是“看起来行”而是经得起对比测试。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑