简介:面向计算机相关专业学生及实践者的一套灰度图像彩色化处理与优化实现资源,适合毕业设计、课程设计、算法进阶及实际项目借鉴。基于Welsh颜色转移算法完成灰度图自动着色,再引入导向滤波进行去噪与边缘保留优化,解决传统滤波在降噪时易丢失细节、增强时易放大噪声的问题,能直观改善着色结果的自然度与真实感。压缩包内共16个文件,以8张JPG测试图像、4个Python脚本为主体,另含3个编译缓存文件与1份Markdown项目说明,整体大小约533KB,结构简洁、便于对照实验。源码经过运行验证,包含较为详细的中文注释,对Welsh采样匹配、导向滤波参数调整等关键步骤均有说明,可供读者从原理到实现完整理解,并在此基础上扩展修改。目前已有430人学习,对于希望快速上手Welsh颜色转移图像彩色化及相关滤波优化的读者,是一份实用的参考。
1. 给灰度图像彩色化,Welsh算法是性价比最高的起点
给灰度图像彩色化,最笨的办法是用PS一点一点涂,一张人像磨一两个小时还不一定自然。Welsh算法解决的就是这个问题:给定一张参考彩色图,算法自动把参考图的颜色“迁移”到灰度图上,几分钟得到一张观感自然的彩色结果。它不需要训练数据、不需要GPU,一台普通笔记本就能跑,核心代码量不大,特别适合做图像处理课设、老照片修复预处理,或者给想理解传统颜色迁移方法的人当入门项目。这篇笔记从算法原理、Python实现、三个实测有效的优化方向,一直写到五个高频踩坑点,尽量让新手按步骤能跑通,让熟手直接避开性价比最低的那几个坑。
2. Welsh算法为什么能上色:亮度匹配加邻域统计的样本迁移逻辑
2.1 从参考图“借”颜色的完整链路
Welsh算法的核心思想非常直白:灰度图只有亮度信息,彩色图有亮度加色度信息,那就在参考彩色图里找一块“长得像”目标区域的样本,把它的颜色搬过来。整条链路分四步走:先在参考图上按固定间隔采样,得到一批带有Y、Cb、Cr三通道的候选样本;然后计算目标灰度图每个像素邻域的亮度均值和标准差;再在参考样本中找出亮度最接近的候选;最后把候选的Cb和Cr赋给目标像素。
你会发现整个过程中算法完全没有“学习”任何东西,它做的是匹配和搬运。这和深度学习彩色化方案有本质区别:基于GAN或CNN的方法需要大量成对数据训练,而Welsh算法零训练,效果完全取决于参考图选得好不好。在做老照片修复这类单张图片任务时,传统算法的可控性反而是优势——结果不满意,换一张参考图重新跑一次就行,不用重新训练模型。
2.2 颜色空间为什么选YCbCr而不是RGB
RGB空间里三个通道的关联性太强,直接对R、G、B做差值和匹配会产生明显的偏色。比如你在RGB空间里把两个像素的欧氏距离算得很小,但人眼感知到的颜色可能差得很远。YCbCr把亮度(Y)和色度(Cb、Cr)拆开,亮度通道正好能和灰度图的亮度直接对应,色度通道独立处理也不会互相污染。
OpenCV里的cv2.COLOR_BGR2YCrCb就是做这件事的,注意它的通道顺序是Y、Cr、Cb,和理论文档里常写的YCbCr顺序略有差别。我最早在这里翻过车:按Y、Cb、Cr的顺序去拆通道,结果cv2.merge出来全是诡异的品红色调,排查了半天才发现是通道顺序写反了。如果你看到自己的输出图偏红偏蓝,先检查这一行,不要急着调算法参数。
import cv2 import numpy as np gray = cv2.imread("old_photo.png", cv2.IMREAD_GRAYSCALE) ref = cv2.imread("reference_color.jpg") # BGR通道顺序 ref_ycrcb = cv2.cvtColor(ref, cv2.COLOR_BGR2YCrCb) ref_y, ref_cr, ref_cb = cv2.split(ref_ycrcb)逻辑说明:ref_y是参考图的亮度通道,直接参与匹配;ref_cr和ref_cb是后续要“借”给灰度图的色度信息。参数说明:IMREAD_GRAYSCALE强制以单通道读入,避免某些图片带Alpha通道导致后续维度对不上;如果参考图是RGBA格式,先用cv2.cvtColor(ref, cv2.COLOR_BGRA2BGR)去掉Alpha通道再转YCbCr。
2.3 邻域标准差:抑制错误匹配的关键约束
如果只按单个像素的亮度匹配,会出现大量错误匹配。想象一下:一张白墙照片和一片白云,单像素亮度值可能完全相同,但人眼知道墙是白的、云是蓝灰的。单像素匹配会把云的蓝色搬到墙上。Welsh算法的关键改进在于引入了邻域统计量——对每个像素取一个patch窗口(常见是5×5或7×7),计算窗口内亮度的均值和标准差。均值描述局部明暗,标准差描述局部纹理复杂度。平坦区域标准差小,边缘区域标准差大,这两个特征联合起来就能很好地区分“墙”和“云”。
匹配时的代价函数通常写成:
score = (target_mean - sample_mean)^2 + lambda * (target_std - sample_std)^2lambda是标准差项的权重,一般取0.1到0.5之间。取太小,纹理约束失效,容易产生色块;取太大,亮度匹配被弱化,整体色调会偏。我习惯从0.3起步,先看输出图有没有明显色斑,再决定调大还是调小,这个参数比你想的更敏感。
2.4 原论文没展开但你绕不开的三个参数
第一个是采样间隔sample_step。Welsh原论文的做法是在参考图中按扫描线顺序采样,实际工程实现常用固定步长均匀采样。步长取4到16之间,参考图如果是高清的,取8就能拿到足够多的候选样本;步长太小候选样本过多,匹配时计算量翻倍但效果提升有限。
第二个是亮度分桶bucket。直接把每个目标像素和全部候选样本做匹配,速度极慢。常见做法是按亮度值分桶,比如桶大小为32,就把整个亮度范围分成8段,目标像素只在自己的亮度段内找候选。桶太大失去加速意义,桶太小容易在某一段找不到样本。我记得某次把桶调到64,结果人脸区域全部偏绿,因为那个亮度段里恰好全是草地样本,这就是“闭着眼睛挑了个最近的”导致的翻车。
第三个是邻域窗口patch。窗口太小,均值方差都不可靠;窗口太大,边缘细节被抹平。人像图用5,风景图用7,超过9之后彩色化结果会明显变糊,边缘处理起来也不好看。
3. 用Python把Welsh算法跑起来:核心代码与最小复现流程
3.1 环境准备:OpenCV加NumPy就够了
先确认环境。Python版本3.8以上就行,关键依赖只有两个:numpy和opencv-python。如果你是在VSCode里配环境,建议直接建一个虚拟环境,不要往全局环境装,后面换项目版本冲突的时候会头疼。装完验证一下:
pip install numpy opencv-python python -c "import cv2, numpy; print(cv2.__version__, numpy.__version__)"逻辑说明:这行命令同时验证两个核心库能否正常导入,并打印版本号。参数说明:OpenCV 4.x以上都支持COLOR_BGR2YCrCb,如果打印出版本信息说明环境没问题;如果报错ModuleNotFoundError,先检查当前终端是不是激活了正确的虚拟环境——这个问题在Windows下尤其常见。
3.2 读取灰度图与参考图:尺寸和通道对齐
这一步看起来简单,但尺寸不匹配是后续一切怪问题的源头。灰度图和参考图不需要严格等尺寸,但参考图的长宽比最好和目标图接近。原因很实在:Welsh算法是逐像素迁移颜色,如果参考图是竖构图而目标图是横构图,候选样本的邻域统计分布会整体偏移,结果就是天空和草地的颜色互相错位。
gray = cv2.imread("old_photo.png", cv2.IMREAD_GRAYSCALE) if gray is None: raise FileNotFoundError("目标灰度图读取失败,检查路径") ref = cv2.imread("reference_color.jpg") if ref is None: raise FileNotFoundError("参考图读取失败,检查路径") # 参考图尺寸随意,但建议不要比目标图小太多 h, w = gray.shape ref = cv2.resize(ref, (w, h), interpolation=cv2.INTER_LINEAR)逻辑说明:gray.shape拿到的是(h, w),而cv2.resize需要的是(w, h),这个顺序错了图片会直接变形。把参考图缩放到和目标图完全一样大,是为了后面逐像素操作时维度对齐,少一个if判断。参数说明:插值方式用INTER_LINEAR就行,速度适中、质量够用;如果参考图是大幅缩小,用INTER_AREA能减少锯齿,但这一步对最终着色效果影响很小,不必纠结。
3.3 核心匹配逻辑:按亮度分桶加邻域标准差匹配
这一节是算法的核心实现。先提前算好参考图和目标图的邻域均值和标准差,避免在双循环里重复计算,这是能跑和不能跑的分水岭。
def welsh_colorize(gray, ref_bgr, sample_step=8, patch=5, bucket=32, std_weight=0.3): """Welsh灰度图彩色化核心实现。 Args: gray: 单通道uint8灰度图 ref_bgr: 已resize到相同尺寸的BGR彩色参考图 sample_step: 参考图像素采样间隔 patch: 邻域窗口大小,必须为奇数 bucket: 亮度分桶大小 std_weight: 邻域标准差在匹配代价中的权重 """ ref_ycrcb = cv2.cvtColor(ref_bgr, cv2.COLOR_BGR2YCrCb) ref_y, ref_cr, ref_cb = cv2.split(ref_ycrcb) h, w = gray.shape # 1. 计算参考图的邻域均值和标准差 k = patch // 2 ref_mean = cv2.blur(ref_y.astype(np.float32), (patch, patch)) ref_sq = cv2.blur(ref_y.astype(np.float32) ** 2, (patch, patch)) ref_std = np.sqrt(np.maximum(ref_sq - ref_mean ** 2, 0)) # 2. 按sample_step采样参考图的候选样本 sample_mask = np.zeros_like(ref_y, dtype=bool) sample_mask[::sample_step, ::sample_step] = True s_y = ref_y[sample_mask].astype(np.float32) s_cr = ref_cr[sample_mask].astype(np.float32) s_cb = ref_cb[sample_mask].astype(np.float32) s_mean = ref_mean[sample_mask] s_std = ref_std[sample_mask] # 3. 计算目标灰度图的邻域均值和标准差 g_mean = cv2.blur(gray.astype(np.float32), (patch, patch)) g_sq = cv2.blur(gray.astype(np.float32) ** 2, (patch, patch)) g_std = np.sqrt(np.maximum(g_sq - g_mean ** 2, 0)) # 4. 逐像素分桶匹配 out_cr = np.zeros_like(gray, dtype=np.float32) out_cb = np.zeros_like(gray, dtype=np.float32) for y in range(h): for x in range(w): center_val = gray[y, x] low = max(0, int(center_val) - bucket // 2) high = min(255, int(center_val) + bucket // 2) # 在当前亮度桶内找候选 idx = np.where((s_y >= low) & (s_y <= high))[0] if len(idx) == 0: idx = np.arange(len(s_y)) # 计算代价并取最小 d = (g_mean[y, x] - s_mean[idx]) ** 2 + \ std_weight * (g_std[y, x] - s_std[idx]) ** 2 best = idx[np.argmin(d)] out_cr[y, x] = s_cr[best] out_cb[y, x] = s_cb[best] # 5. 合并通道并转回BGR result = cv2.merge([gray.astype(np.float32), out_cr, out_cb]) result = np.clip(result, 0, 255).astype(np.uint8) return cv2.cvtColor(result, cv2.COLOR_YCrCb2BGR) gray = cv2.imread("old_photo.png", cv2.IMREAD_GRAYSCALE) ref = cv2.imread("reference_color.jpg") ref = cv2.resize(ref, (gray.shape[1], gray.shape[0])) colorized = welsh_colorize(gray, ref, sample_step=8, patch=5, bucket=32, std_weight=0.3) cv2.imwrite("colorized_result.jpg", colorized)逻辑说明:分成五步走,第一步用cv2.blur计算邻域均值,再用平方均值减均值平方的方式算标准差,这是图像处理里标准的快速方差计算方法,避开显式的双循环卷积。第二步用布尔掩码采样,只保留sample_step间隔上的像素作为候选,大幅压缩匹配空间。第四步是核心匹配,对每个目标像素限制在亮度附近的一个小桶里找候选,桶内取代价最小的样本,把它的色度赋给目标像素。
参数说明:sample_step=8意味着参考图大约只有1/64的像素参与匹配,对一张1080P的图来说仍有约3万个候选,足够覆盖大多数情况;bucket=32把亮度分成8段,是一个兼顾速度和质量的起始值;std_weight=0.3是标准差约束的强度,调到0就是纯亮度匹配,色斑会明显变多。
3.4 跑通后先别急着优化:用最朴素的结果评估效果
第一次跑通后,输出的图片大概率会有明显的色块和噪点,这是正常的。Welsh算法在论文里的效果图都经过后处理,原始输出几乎不可能直接商用。你先用cv2.imshow或者直接打开输出的JPG,看三件事:整体色调是否符合参考图的氛围;人脸或主体区域是否出现明显的颜色跳变;边缘是否“渗色”,比如深色衣服边缘染上了背景的颜色。
这三点决定了你下一步往哪个方向优化。如果整体色调偏了,问题在参考图选择;如果局部花斑,问题在bucket和std_weight;如果边缘渗色,你得考虑在匹配代价里加入梯度约束,或者对输出色度通道做引导滤波。别一上来就堆优化手段,先定位问题比什么都重要。
4. 三个亲测有效的优化方向:从分钟级到秒级,从花脸到自然
4.1 优化一:图像金字塔把复杂度降一个量级
上面那段代码在720P的图上跑一遍大概要一两分钟,主要耗时在双循环里的np.where。最有效的加速方式是金字塔:先把图缩到1/4甚至1/8尺寸跑匹配,再把匹配结果的色度通道放大回原尺寸。亮度细节在放大时从原图直接取,因为灰度图本身的边缘信息完全保留。
def welsh_colorize_pyramid(gray, ref_bgr, levels=2, **kwargs): """金字塔加速版Welsh。先在低分辨率匹配,再上采样恢复色度。""" h, w = gray.shape # 逐级缩小 small_gray = gray.copy() small_ref = ref_bgr.copy() for _ in range(levels): small_gray = cv2.pyrDown(small_gray) small_ref = cv2.pyrDown(small_ref) # 在低分辨率上跑匹配 small_ref = cv2.resize(small_ref, (small_gray.shape[1], small_gray.shape[0])) small_cr, small_cb = welsh_colorize_core(small_gray, small_ref, **kwargs) # 双线性上采样回原尺寸 cr_full = cv2.resize(small_cr, (w, h), interpolation=cv2.INTER_LINEAR) cb_full = cv2.resize(small_cb, (w, h), interpolation=cv2.INTER_LINEAR) result = cv2.merge([gray.astype(np.float32), cr_full, cb_full]) result = np.clip(result, 0, 255).astype(np.uint8) return cv2.cvtColor(result, cv2.COLOR_YCrCb2BGR)逻辑说明:pyrDown每次把图像长宽各缩小一半,两层金字塔意味着匹配的像素量减少到原来的1/16,耗时基本也能降到原来的1/10以上。色度通道是低频信息,用双线性插值放大不会造成明显损失。参数说明:levels=2是推荐值,再往上加到3,速度提升开始变缓,而色度细节的丢失开始肉眼可见,尤其在发丝和树叶这类高频区域。
4.2 优化二:用随机搜索替换全像素匹配
第二个优化思路是打破“每个目标像素都要匹配”的惯性。相邻像素的亮度邻域高度相似,匹配结果也高度相似,没必要逐像素计算。做法是先按sample_step对目标图也做一次采样,在这些采样点上跑匹配,得到色度后对整个色度通道做插值。配合金字塔使用,速度能再翻一倍以上。
def welsh_colorize_sampled(gray, ref_bgr, target_step=4, **kwargs): h, w = gray.shape # 对目标图像素抽样匹配 sampled_gray = gray[::target_step, ::target_step] sampled_ref = cv2.resize(ref_bgr, (sampled_gray.shape[1], sampled_gray.shape[0])) cr_small, cb_small = welsh_colorize_core(sampled_gray, sampled_ref, **kwargs) # 插值回全尺寸 cr_full = cv2.resize(cr_small, (w, h), interpolation=cv2.INTER_CUBIC) cb_full = cv2.resize(cb_small, (w, h), interpolation=cv2.INTER_CUBIC) ...逻辑说明:target_step=4表示每4×4像素块里只匹配一个代表点,色度通道天然是低频信息,相邻4像素内的颜色差异几乎不可见。这就是“用空间分辨率换时间”的典型取舍。参数说明:target_step最大不要超过8,超过后头发丝和物体边缘会出现明显的“彩色锯齿”,因为色度通道的信息密度跟不上亮度通道的细节。
4.3 优化三:参考图预处理比调算法参数更有效
这是我最想强调的一点。调参半天不如换一张参考图,Welsh算法的天花板由参考图决定。我维护过一个小的老照片修复工作流,里面最重要的步骤是参考图筛选:目标图是室内人像,参考图就选室内光照下的同色系人像;目标图是室外风景,参考图就选相似光线和季节的风景照。光照方向不一致会让整个画面的阴影区域染上奇怪的色调。另外,参考图先做一次高斯模糊再参与匹配,能有效抑制参考图本身的噪点被迁移到结果里。
ref = cv2.GaussianBlur(ref, (3, 3), 0) ref_ycrcb = cv2.cvtColor(ref, cv2.COLOR_BGR2YCrCb)逻辑说明:在转换颜色空间之前先对参考图做轻度平滑,代价是参考图略微变糊,收益是参考图的高频噪点不会通过样本迁移“复印”到目标图。参数说明:核大小(3, 3)和标准差0是轻量平滑,不要用(5, 5)以上,否则参考图的纹理特征丢失,匹配时的邻域标准差约束会失真。
4.4 优化后的完整调用与参数建议
三个优化叠加后,720P图的处理时间能从一两分钟降到3到5秒,效果上基本看不出差异。我常用的参数组合是:
| 参数 | 推荐值 | 适用场景 |
|---|---|---|
| sample_step | 8~12 | 参考图越清晰取值越大 |
| patch | 5 | 人像、细节多的图 |
| patch | 7 | 风景、大面积平滑区域 |
| bucket | 16~32 | 越小色斑越少但越慢 |
| std_weight | 0.2~0.5 | 纹理丰富场景取上限 |
| levels | 2 | 通用推荐,超过3慎用 |
| target_step | 4 | 配合金字塔使用 |
这套参数下,肤色、天空、植被这些常见元素的颜色迁移都比较稳。需要注意patch和bucket联动:patch变大时邻域均值更平滑,bucket可以适当调小来补回一部分区分度,这个规律调多了自然会有手感。
5. 避坑指南:灰度图彩色化最常见的5个问题与排查方法
5.1 问题一:输出大片色块,像是被人拿油漆桶泼过
现象:结果图整体色调是对的,但天空、墙壁、人脸这些大面积区域出现明显的颜色分块,一块浅蓝一块深蓝。
原因:亮度分桶bucket取值太大,比如设成64或128。分桶本质是在亮度维度上聚类,桶越粗,亮度相近但颜色差异大的参考样本会被归到一起,相邻目标像素分别匹配到桶内距离最近但颜色不同的样本,色块边界就出来了。
解决:把bucket从64降到16或32,同时把std_weight从0.3提到0.5。如果色块还在,检查参考图本身是不是有大量同亮度不同色的区域——比如同一片天空里有蓝天和白云,亮度接近但色度差异很大。这时候需要换参考图,或者对参考图做颜色聚类预处理,而不是继续调参。
5.2 问题二:换了图效果差别巨大,同一套参数不稳定
现象:同一套代码,参考图从A换成B,效果从“惊艳”变成“噩梦”,人脸发绿、天空发黄。
原因:Welsh算法严重依赖参考图与目标图的亮度分布相似度。参考图B如果整体偏暗或偏亮,它的亮度直方图与目标图的直方图错位,匹配时就只能“矮子里拔将军”。
解决:先对参考图做亮度直方图匹配,把参考图的亮度分布掰到和目标图一致。用cv2.calcHist分别统计两张图的亮度直方图,然后用cv2.LUT做映射。这一步能在不改变参考图色彩构成的前提下,把它的亮度分布对齐到目标图,大幅提升匹配质量。我现在的流程里这步是必做的,耗时不到50毫秒,效果提升却非常明显。
5.3 问题三:边缘严重渗色,深色衣服染上背景颜色
现象:物体的边缘轮廓外侧出现一圈背景色的光晕,尤其在人像的发际线和衣服边缘。
原因:邻域窗口patch在跨过边缘时,窗口内同时包含了前景和背景的像素,均值和标准差被“平均”掉了。此时计算的代价分数不再可靠,匹配到的样本可能是完全无关的区域。
解决:在匹配代价中加入梯度惩罚项。对每个像素先算Sobel梯度幅值,把梯度差异加到代价函数里。边缘像素的梯度大,就会优先匹配参考图中同样具有强边缘的样本,避免“平滑的墙”匹配上“锐利的云”。
g_grad = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3) r_grad = cv2.Sobel(ref_y, cv2.CV_32F, 1, 0, ksize=3) # 代价函数变为 # d = (mean_diff)^2 + std_weight * (std_diff)^2 + grad_weight * (grad_diff)^2逻辑说明:Sobel算子计算水平方向梯度,ksize=3是最常见的核大小。梯度权重grad_weight取值0.1到0.3,太大会让亮度匹配失效,太小等于没加。加了这行之后,边缘渗色的改善是肉眼可见的,代价函数从两项变三项的计算开销几乎可以忽略。
5.4 问题四:720P图跑了好几分钟,以为程序死循环了
现象:运行脚本后终端没有任何输出,也没有报错,就是卡住不动。
原因:双重for循环逐像素匹配,每个像素内部还有一次np.where全数组扫描。整体时间复杂度是O(H×W×N),N是候选样本数。720P的图有92万个像素,每个像素扫描3万个候选,这个计算量在纯Python里跑是灾难级的。
解决:先确认是不是死循环——在循环里加个计数器,每处理1万像素打印一次进度。然后必须走第四章的金字塔加速和目标采样加速。我在第一次实现时没有概念,直接拿1080P图测试,等了三分钟没出结果还以为内存爆了,后来加了进度条才发现是计算量问题,不是程序问题。
5.5 问题五:输出全黑或者全灰,完全看不到任何颜色
现象:程序不报错,输出的图片却是全黑的,或者是一张和原灰度图一模一样的灰图。
原因:这两种情况原因不同。全黑一般是通道合并时数据范围出了问题:YCbCr的通道在cv2.split之后是uint8,但经过浮点运算后变成了float32,直接cv2.merge再cvtColor时OpenCV会截断,导致亮度通道丢失。全灰则是色度通道的数值全部落在128附近——当匹配到的样本色度本身就是中性色,或者参考图本身饱和度极低时会出现这种情况。
解决:确保在cv2.merge前把三个通道统一成float32,合并后先np.clip(0, 255)再.astype(np.uint8)。检查参考图的饱和度,如果参考图本身就是低饱和风格的,输出自然偏灰。我现在的排查顺序是:先打印out_cr和out_cb的均值和标准差,如果标准差小于10,说明色度通道根本没有有效信息,问题出在参考图上。
6. 验证与进阶:用PSNR和闭环实验给结果一个交代
这个项目做完后,怎么证明它“有效”?灰度图本身没有标准彩色答案,你没法直接算误差。业界最常见的做法是闭环实验:拿一张彩色图,转成灰度,再跑一遍Welsh算法,然后把结果和原始彩色图对比,计算PSNR。这个指标能客观地告诉你算法在亮度分布相同的条件下能恢复多少颜色信息。
orig_color = cv2.imread("original.jpg") gray_version = cv2.cvtColor(orig_color, cv2.COLOR_BGR2GRAY) recovered = welsh_colorize(gray_version, ref) psnr = cv2.PSNR(orig_color, recovered) print(f"PSNR = {psnr:.2f} dB")逻辑说明:PSNR越高说明恢复的彩色图和原图越接近。30dB以上算不错,25dB以下说明参考图选得有严重问题。注意PSNR只衡量像素级差异,不完全等价于人眼观感,所以它只能作为筛选参考图的工具,不能替代人眼最终判断。我的习惯是:候选参考图先跑一版,PSNR低于27直接换,高于27再细看主观效果。
进阶的话,有三个方向值得做:一是给算法加一个简单的掩码界面,在灰度图上手动标记肤色区域,让算法优先从参考图的对应部位匹配——这个改动不大但效果立竿见影,适合人物特写;二是把金字塔层的色度结果做一次引导滤波,让色度边缘贴着亮度边缘走,能系统性改善边缘渗色问题;三是处理视频帧时加光流约束,相邻帧的色度保持一致,避免闪烁——这个跨到视频增强领域了,但基础还是Welsh算法这套匹配逻辑。
最后说一个我自己的教训。最早调试时,我为了追求速度把bucket设成128,出来的图远看还行,放大后全是色块,我还以为是算法本身垃圾。后来一档一档往下试,直到16才看到干净的结果。从那以后我养成了一个习惯:任何参数改动,先跑一张小图看效果,再跑大图验证速度。这套流程帮我少走了很多弯路,希望帮到你。
本文还有配套的精品资源,点击获取