资讯动态

Welsh算法灰度图上色:原理、Python实现与优化

发布时间:2026/10/18 19:29:23 来源:尧图企业网站定制
简介这套基于Welsh算法的灰度图像彩色化处理及优化Python实现包含完整源码、项目说明与详细注释面向计算机相关专业学生、教师或开发者适用于毕业设计、课程设计、课题初期演示及算法学习进阶。压缩包共16个文件含4个Python源码、3个pyc编译文件、8张彩色/灰度/处理效果图像和1份项目说明文档整体仅533KB结构清晰便于查阅其中Python源码均带详细注释项目说明文档对算法原理和实验流程作了梳理。从颜色统计匹配到边缘保持滤波代码覆盖Welsh颜色转移彩色化、导向滤波去噪优化以及图像自然感处理等核心环节所有代码均测试运行成功可在此基础上修改扩展帮助深入理解灰度图像彩色化中的颜色匹配、边缘保持与噪声抑制思路。目前已有432人浏览学习适合作为计算机视觉方向入门实践或实际项目借鉴参考。1. 灰度图上色不是加滤镜Welsh算法解决的是“猜颜色分布”上周帮朋友处理一张黑白全家福扫描件脸部噪点很重。我第一反应是丢给深度学习上色模型跑一版结果同一个输入跑了三次出了三种肤色其中一次把窗帘染成荧光绿。换回Welsh算法后一张参考彩色图加一套近邻匹配十分钟出稳定结果同一个输入永远同一个输出。很多人觉得“黑白上色不重要”但对老照片修复、老电影着色、界面原型快速出彩图这类场景来说稳定、可复现、不依赖训练权重的方案反而比“抽卡式”的深度学习上色更实用。Welsh算法的核心思路很朴素灰度图里每个像素只有亮度可用那就把目标像素邻域的亮度统计特征拿到参考彩色图里做最近邻匹配再把参考像素的色度通道借过来。它不需要GPU、不需要数据集适合想快速落地一个可靠彩色化工具的开发者。2. 先看懂Welsh再写代码亮度重映射与邻域匹配是根2.1 颜色迁移的两条路线为什么Welsh选择“局域匹配”灰度图彩色化在图像处理里属于颜色迁移color transfer问题。这个方向有两条经典路线一条是全局统计迁移另一条就是Welsh代表的局域匹配迁移。全局统计迁移的思路是把目标图和参考图都转到某个颜色空间分别计算每个通道的均值和标准差然后把目标图的均值和标准差强行掰成和参考图一致。这条路线实现极其简单几十行就能跑通处理偏色校正比如把一张发黄的照片调回正常色调非常有效。但放到灰度图上色场景它会立刻暴露问题一张灰度图里可能同时有人脸、天空、草地、衣服全局统计迁移会把参考图的整体色调强加给所有区域结果就是天空带上了草地的绿人脸带上了天空的蓝整张图颜色发闷、粘成一团。原因在于全局统计只描述“整张图”的颜色分布丢失了空间位置和局部结构信息。Welsh走的则是另一条路局域匹配。它假设“亮度结构相似的地方颜色也应该相似”。算法逐个像素或采样点分析邻域内的亮度均值和标准差在参考图里找到结构最相似的像素把那个像素的α、β色度通道搬过来。正因为匹配发生在局部天空的平滑区域会去参考图里找平滑区域草地的纹理区域会去参考图里找纹理区域空间对应关系被保留下来。这就是Welsh在处理多区域复杂灰度图时明显优于全局统计迁移的原因。两条路线的对比如下表对比项全局统计迁移Welsh局域匹配匹配粒度整张图一个统计量每个像素/采样点独立匹配适合场景偏色校正、单色调图灰度图上色、多区域复杂场景空间一致性无整体色调统一局部保持边缘相对清晰计算成本低O(HW)高O(HW×N)需要优化输出稳定性稳定稳定采样固定时2.2 Welsh主流程五步与三个关键参数Welsh算法的落地流程可以分为五步理解了这五步后面的代码就是按顺序填空。第一步把参考彩色图和灰度图都从RGB转到lαβ颜色空间。为什么要用lαβ而不是RGB因为lαβ的l通道代表亮度α和β代表色度三个通道的相关性被大幅降低。在灰度图里我们只有l匹配时只需要比较亮度相关特征最后再把参考像素的α、β注入目标像素。这个颜色空间是经典论文里反复验证过适合做颜色迁移的。第二步亮度重映射。把灰度图的亮度l的均值和标准差线性映射到和参考图一致。这一步不是必须的但强烈建议保留。原因很实在最近邻匹配对特征尺度非常敏感如果两幅图的亮度分布差异过大匹配出来的参考像素结构上根本不相似颜色自然离谱。第三步计算邻域统计特征。对每个像素在窗口内计算邻域亮度的均值和标准差。窗口内的均值描述了局部亮度水平标准差描述了局部纹理强度。平滑的天空区域标准差小纹理丰富的草地标准差大这两个特征足以区分绝大多数结构。第四步最近邻匹配。把目标像素的邻域特征拿到参考图的采样点集合里找距离最近的参考像素记录它的α、β值。这一步是整算法的核心也是性能瓶颈。第五步写回色度并转回RGB。用匹配到的α、β替换目标像素的α、βl通道仍然用原灰度图的值再逆变换回RGB输出。这里有三个关键参数直接决定最终效果第一个是邻域窗口大小win。win3时噪声大win15时颜色溢出严重常见取值是5到9。窗口太小标准差特征不稳定平滑区域容易匹配到随机位置产生椒盐噪点窗口太大边缘两侧的结构会被混在一个窗口里导致颜色越过边界。我一般先用5x5起步观察平坦区域是否有噪点再逐步调大。第二个是采样点数量sample_num。原论文的加速思路是不必让每个目标像素在全图所有参考像素里搜索而是先从参考图中随机采样一批像素只在这批像素里做最近邻。采样点过少比如50个候选颜色不够输出会出现明显色块感采样点过多计算量线性上升。400到1000是一个比较稳的区间。第三个是特征权重w。标准Welsh用邻域均值和邻域标准差两个特征默认权重可以给(1.0, 0.5)。亮度均值权重偏高是因为亮度水平是判断“天空vs草地”这类大类别的最强信号标准差权重用来修正纹理差异不需要给太高给高了会造成平坦区域和纹理区域之间匹配过于敏感。3. 从零实现Welsh算法核心匹配代码与参数设置3.1 颜色空间转换与亮度重映射先搭好lαβ通道我把灰度图约定为三通道灰度RGB每个通道数值相同参考图为普通彩色RGB。第一步先把两者统一转到lαβ空间。这里直接给出论文里的变换矩阵RGB取值归一化到0~1。import numpy as np import cv2 _RGB2LMS np.array([ [0.3811, 0.5783, 0.0402], [0.1967, 0.7244, 0.0782], [0.0241, 0.1288, 0.8444], ]) _LMS2LAB np.array([ [1/np.sqrt(3), 1/np.sqrt(3), 1/np.sqrt(3)], [1/np.sqrt(6), 1/np.sqrt(6), -2/np.sqrt(6)], [1/np.sqrt(2), -1/np.sqrt(2), 0], ]) def rgb_to_lab(rgb): # rgb: float32, 取值 [0,1], 形状 HxWx3 lms rgb _RGB2LMS.T lms np.log10(np.maximum(lms, 1e-8)) lab lms _LMS2LAB.T return lab def lab_to_rgb(lab): # 逆变换lαβ - LMS - RGB结果裁剪到 [0,1] lms lab np.linalg.inv(_LMS2LAB).T lms np.power(10.0, lms) rgb lms np.linalg.inv(_RGB2LMS).T return np.clip(rgb, 0, 1)这段代码里有两个细节要留意。首先是np.maximum(lms, 1e-8)LMS通道经过矩阵乘法后可能产生接近零甚至负的数值直接取对数会得到-inf或nan所以需要先做下限保护。其次是逆变换用了np.linalg.inv去求两个变换矩阵的逆避免我手写逆变换公式出错代价是每次调用多一点点矩阵运算对单张图像完全可接受。亮度重映射的代码更短但它是容易被跳过的一步。def remap_luminance(src_l, ref_l): # 把src的亮度均值/方差映射到和ref一致 sm, ss src_l.mean(), src_l.std() rm, rs ref_l.mean(), ref_l.std() return (src_l - sm) / (ss 1e-8) * rs rm这个线性映射的直觉是参考图的亮度分布决定了它有哪些亮度层次目标图的亮度如果整体偏暗匹配时所有像素都会去找参考图里暗部的颜色亮部颜色全部浪费。映射之后两幅图的亮度均值相同、方差相同匹配的候选分布更均匀。1e-8是为了防除零。这里注意重映射改变的是目标图的l通道在最后输出时这个被修改过的l仍然会被保留——Welsh算法允许对亮度做调整因为亮度本来就是原图的信息只是做了统计对齐。3.2 邻域特征提取与最近邻匹配核心代码与参数含义特征提取我用OpenCV的盒式滤波来实现比用双层for循环快两个数量级而且代码更短。def compute_stat_features(l_channel, win5): # l_channel: HxW float32这里是lαβ空间的亮度通道 k np.ones((win, win), dtypenp.float32) / (win * win) mean cv2.filter2D(l_channel, -1, k, borderTypecv2.BORDER_REFLECT) sq cv2.filter2D(l_channel * l_channel, -1, k, borderTypecv2.BORDER_REFLECT) std np.sqrt(np.maximum(sq - mean * mean, 0)) return np.stack([mean, std], axis-1)盒式滤波计算邻域均值的原理是卷积每个像素的输出是周围win×win区域的加权平均。标准差用“窗口内平方的均值减去均值的平方”来算这是E[X²]-E[X]²的恒等变形能一次滤波搞定不用先求均值再逐像素减。BORDER_REFLECT表示边缘用镜像填充比默认的补零更稳——补零会让图像边缘窗口内混入大量零值导致边缘像素的均值和标准差被低估上色后四周一圈颜色明显发暗。最近邻匹配是全文最核心的代码块。第一步先做参考图采样def sample_ref(ref_feat, ref_ab, sample_num, seed42): # 随机采样参考图中的像素返回采样后的特征和色度 h, w ref_feat.shape[:2] rng np.random.default_rng(seed) idx rng.choice(h * w, sizesample_num, replaceFalse) return ref_feat.reshape(-1, 2)[idx], ref_ab.reshape(-1, 2)[idx]seed固定很重要。Welsh算法本身是确定性算法但随机采样这一步引入了随机性。如果seed不固定同一张图每次跑出来的颜色会有细微差异。固定seed之后输出完全可复现排障时才能对比不同参数的影响。然后是朴素的最近邻匹配def nearest_match_naive(target_feat, ref_feat_s, ref_ab_s, w(1.0, 0.5)): # target_feat: HxWx2, ref_feat_s: Nx2, ref_ab_s: Nx2 h, w target_feat.shape[:2] out np.zeros((h, w, 2), dtypenp.float32) block 64 for y in range(0, h, block): for x in range(0, w, block): tf target_feat[y:yblock, x:xblock] d (w[0] * (tf[..., None, 0] - ref_feat_s[None, None, :, 0]) ** 2 w[1] * (tf[..., None, 1] - ref_feat_s[None, None, :, 1]) ** 2) best np.argmin(d, axis-1) out[y:yblock, x:xblock] ref_ab_s[best] return out这里距离计算用的是特征差平方加权和等价于加权欧氏距离。块大小block64是为了控制中间张量的规模每个块产生的距离矩阵形状是64×64×NN取500时约2000万个浮点数内存占用几十MB可以接受。如果直接对整个H×W图像做广播距离计算一张800×600的图会产生800×600×500的中间矩阵约2.4亿个浮点数内存直接爆掉。这是初写Welsh最容易翻车的地方后面优化章节会彻底解决。3.3 把流程串成主函数从灰度图到彩色图的完整闭合有了前面的转换、重映射、特征提取、匹配四个函数主函数就是把它们按顺序串起来。def colorize(gray_rgb, ref_rgb, win5, sample_num400, w(1.0, 0.5), remapTrue): # gray_rgb: 三通道灰度图uint8ref_rgb: 参考彩色图uint8 target_lab rgb_to_lab(gray_rgb.astype(np.float32) / 255.0) ref_lab rgb_to_lab(ref_rgb.astype(np.float32) / 255.0) if remap: target_lab[..., 0] remap_luminance(target_lab[..., 0], ref_lab[..., 0]) t_feat compute_stat_features(target_lab[..., 0], win) r_feat compute_stat_features(ref_lab[..., 0], win) ref_ab ref_lab[..., 1:] # 取α、β通道 ref_f_s, ref_ab_s sample_ref(r_feat, ref_ab, sample_num) ab nearest_match_naive(t_feat, ref_f_s, ref_ab_s, w) target_lab[..., 1:] ab out lab_to_rgb(target_lab) return (out * 255).astype(np.uint8)调用示例gray cv2.imread(gray_photo.jpg, cv2.IMREAD_GRAYSCALE) gray_rgb cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB) ref cv2.imread(reference.jpg) out colorize(gray_rgb, ref, win5, sample_num400) cv2.imwrite(result.jpg, out)主函数里有几个易错点。第一rgb_to_lab内部要求输入是0到1的浮点数uint8图像必须除以255否则矩阵乘法结果量级不对输出整个偏色。第二ref_ab取的是参考图色度目标图的α、β被赋值覆盖但l通道保持重映射后的值不变不能把参考图的l通道也搬过来。第三lab_to_rgb返回的已经是0到1的浮点RGB最后要乘255再转uint8。到这一步一个可运行的Welsh彩色化流程就完整了。接下来要解决性能问题这张800×600的图在sample_num400时朴素的最近邻匹配大约需要几秒到十几秒图像越大越慢优化的空间非常明显。4. Welsh彩色化的三条优化路线随机采样、多尺度与KDTree加速4.1 优化1亮度分层采样把随机采样从“碰运气”变成“按需取色”朴素的随机采样在参考图像素分布极不均匀时有个隐患如果参考图里暗部占80%、亮部占20%随机采400个点亮部可能只分到80个候选点结果目标图里原本该匹配亮色的区域因为候选不够最后都匹配到了附近的暗部颜色整体发闷。解决方法是亮度分层采样。先把参考图所有像素按亮度分成K个桶每个桶内固定采N_K个点保证每个亮度层次都有足够候选。def stratified_sample(l_channel, ref_feat, ref_ab, sample_num, bins8, seed0): # 按亮度直方图分层采样每个亮度桶采样 sample_num//bins 个点 h, w l_channel.shape[:2] flat_l l_channel.reshape(-1) flat_feat ref_feat.reshape(-1, 2) flat_ab ref_ab.reshape(-1, 2) rng np.random.default_rng(seed) selected [] edges np.percentile(flat_l, np.linspace(0, 100, bins 1)) for i in range(bins): mask (flat_l edges[i]) (flat_l edges[i 1]) idx np.where(mask)[0] if len(idx) 0: continue n max(1, sample_num // bins) pick rng.choice(idx, sizemin(n, len(idx)), replaceFalse) selected.append(pick) sel np.concatenate(selected) return flat_feat[sel], flat_ab[sel]分桶用np.percentile算亮度边界保证每个桶的像素数量大致相等而不是在亮度值上等距切分。这样即便参考图整体偏暗亮部桶也能分到足够的候选。参数上bins取8到12比较合适太小不起作用太大每个桶候选过少。这个采样的计算量比全图扫描小得多但比纯随机采样多了一次分桶和掩码选择对总耗时影响可以忽略。4.2 优化2用KDTree把匹配从线性扫描变成对数查询朴素匹配的时间复杂度是O(HW×N)N是采样点数量。图像越大N越大耗时线性增长。KDTree可以把每次查询的复杂度降到O(log N)整张图降到O(HW log N)。这正是Welsh算法在工程落地里最值得做的一步优化。from scipy.spatial import cKDTree def nearest_match_kdtree(target_feat, ref_feat_s, ref_ab_s, w(1.0, 0.5)): # 把特征先乘权重这样KDTree里的欧氏距离就等于加权距离 w_arr np.array(w, dtypenp.float32) ref_scaled ref_feat_s * w_arr # 建立KDTreequery时返回最近邻的索引 tree cKDTree(ref_scaled) h, w_img target_feat.shape[:2] t_scaled target_feat.reshape(-1, 2) * w_arr _, best_idx tree.query(t_scaled, k1) return ref_ab_s[best_idx].reshape(h, w_img, 2)这里有个容易忽略的细节加权距离不能直接丢给KDTree因为cKDTree内部算的是标准欧氏距离。正确的做法是先把特征乘上权重让加权距离等价于权重空间里的欧氏距离。注意如果特征各维度的尺度差异很大KDTree的构建和查询效率会下降但这里只有两维完全不用担心。替换掉nearest_match_naive后同样参数下耗时通常能从十几秒降到一两秒图像越大收益越明显。4.3 优化3多尺度由粗到细匹配消除平坦区域的匹配跳变朴素匹配和KDTree匹配都是逐像素独立寻优存在一个视觉上的隐患在天空、墙面这类大范围平坦区域很多像素的特征非常接近最近邻结果可能在不同参考位置之间跳变。反映到输出上就是同一片天空出现几块深浅不一的颜色过渡。多尺度匹配是解决这类空间不一致问题的常用手法。思路是先在缩小后的图像上做匹配得到一个“参考坐标”的粗估计然后把坐标图上采样回原分辨率只在参考坐标附近的小邻域里重新搜索。def colorize_multiscale(gray_rgb, ref_rgb, win5, sample_num600): # 由粗到细的匹配核心是记录参考坐标而不是直接记录颜色 def match_with_guide(scale): g cv2.resize(gray_rgb, None, fxscale, fyscale, interpolationcv2.INTER_AREA) r cv2.resize(ref_rgb, None, fxscale, fyscale, interpolationcv2.INTER_AREA) # 正常计算 lαβ、特征、采样、KDTree匹配 # 得到匹配结果后把每个目标像素命中的参考像素坐标存成coord_map return coord_map, g coord, _ match_with_guide(0.25) for scale in [0.5, 1.0]: coord cv2.resize(coord, None, fx2, fy2, interpolationcv2.INTER_LINEAR) # 在coord附近半径r5的窗口内搜索最优匹配更新coord # 最后一层根据最终coord从参考图取色 return out_rgb这个方案比直接对原图跑一次匹配多耗一半时间但平坦区域的色块跳变明显减少。实践中的参数经验是初始尺度取0.25每往上一层半径从8像素减到4像素再减到2像素。注意多尺度匹配里记录的是“匹配到的参考坐标”而不是参考像素的α、β值因为只有坐标才能在尺度间传递和插值。如果你只想让流程简单可以先跳过这一节先跑通4.1和4.2等观察到明显的颜色跳变再回来加这一层。5. 避坑指南Welsh彩色化的5个翻车现场5.1 参考图亮度结构与目标图差距太大整体偏色、发灰现象输出图所有颜色都灰蒙蒙的饱和度很低像是给灰度图蒙了一层彩色的纱。原因亮度重映射只对齐了均值和标准差但两幅图的亮度直方图形状可能完全不同。比如目标图是逆光人像暗部像素极多参考图是明亮风景亮部像素极多。匹配时目标图的暗部像素找不到结构相似的参考候选只能勉强匹配到最近的暗部颜色颜色自然发闷。解决先做亮度直方图匹配再进Welsh或者直接换一张与目标图亮度分布接近的参考图。直方图匹配可以用cv2.calcHist配合cv2.LUT实现把目标图l通道的直方图映射到参考图l通道的累积分布上效果比重映射更精细。如果直方图匹配后仍不理想还有一个笨办法把参考图也转成灰度观察它的灰度图和目标图的灰度图在视觉上是否接近如果明显一暗一亮先对参考图做亮度调整。5.2 邻域窗口设错要么椒盐噪点要么颜色糊成一片现象win3时平滑区域出现密密麻麻的小色斑像是彩色噪点win15时人物衣服和背景墙的颜色互相渗透边缘模糊。原因窗口太小标准差特征在平坦区域极度不稳定几个像素的微小灰度波动就会显著改变标准差导致匹配坐标随机跳动窗口太大窗口内混入了边缘两侧的不同结构特征失去了区分能力。解决从win5开始跑如果输出有椒盐感逐步加到7或9。经验上5x5适合人像和中等纹理图像7x7适合大块天空、水面这类平滑区域较多的图像9x9以上一般只在超大画幅降噪时用。注意win增大时计算量也会增大因为滤波核变大但开销远小于匹配阶段不用太担心。5.3 αβ通道写回后越界颜色断层与暗部发黑现象输出图的暗部区域出现明显的色带断裂颜色从深蓝直接跳到黑色过渡极不自然。原因lαβ空间逆变换回RGB后部分像素的RGB值超出0到1范围简单的np.clip会把超出部分截断。大面积截断会让暗部细节同时丢亮度又丢色度。解决在lab_to_rgb的clip之前先做百分位截断。取RGB三通道各自1%和99%分位点把低于1%的分位点的值压到1%分位点高于99%的压到99%分位点再clip到0~1。这样既避免了极端离群点带来的整体偏色又不破坏正常灰度梯度。如果用了百分位截断后暗部仍然发黑检查一下输入灰度图的暗部是否有大量纯黑像素这类像素在lαβ空间里log(0)会被保护成很小但非零的数逆变换回来时会偏离原位置这是颜色空间的固有问题建议对输入灰度图先做一次轻微的对比度拉伸。5.4 采样点数量被调太低色块感严重现象整张图像被分割成一块块颜色不同的区域同一片区域的内部颜色不连续像是低精度量化效果。原因sample_num太小参考候选点太少相邻目标像素经常会匹配到同一个或零散的几个参考点颜色在空间上无法连续过渡。解决把sample_num提到600以上再看效果。如果你的参考图内容本身比较单一比如只有天空和人脸400可能够用参考图内容越丰富需要的采样点越多。我之前踩过一次为了追求速度把采样点压到100整张脸成了三块色斑后来分层采样加800个点问题立刻消失。这里不用盲目追求1000以上采样点到一定数量后收益递减还会拖慢KDTree构建。5.5 只拿亮度做匹配不看邻域统计纹理错配现象水面波纹区域被匹配上了草地的颜色砖墙区域被匹配上了树叶的颜色输出图局部颜色和原物体完全对不上。原因如果特征只包含当前像素的亮度l匹配对“亮度相同但纹理完全不同”的区域毫无分辨力。灰度图里水波纹和草地叶片可能亮度接近但纹理差异巨大邻域标准差正是用来区分这两类结构的。解决确保特征向量里包含邻域标准差并把标准差权重w[1]从0.5适当提高到0.8左右。代价是匹配会更挑剔如果参考图和目标图的结构相似度不够高匹配失败率反而上升。另一个补充手段是在特征里加入灰度梯度方向直方图但特征维度从2维变成更高维后KDTree的查询效率会下降除非对纹理匹配质量有硬性要求否则我一般不动。6. 把Welsh结果变好用回环验证与参数微调技巧灰度图上色一个天然的难点是它没有唯一正确的真值。一张黑白人脸肤色可以偏白也可以偏黄谈不上谁对谁错。所以调参时不能直接用“像不像原图”来评判我习惯用回环验证法先取一张彩色参考图把它转成灰度再拿这张灰度图和原彩色图分别作为目标和参考跑Welsh最后与原图算PSNR、SSIM和色差。因为已知原图的颜色分布这一步能客观评估算法本身是否把结构带歪了。color_img cv2.imread(ground_truth.jpg) gray cv2.cvtColor(color_img, cv2.COLOR_RGB2GRAY) gray_rgb cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB) out colorize(gray_rgb, color_img, win5, sample_num600) psnr cv2.PSNR(color_img, out) ssim_val ssim(color_img, out, channel_axis2) print(fPSNR{psnr:.2f} dB, SSIM{ssim_val:.4f})注意指标的含义边界PSNR和SSIM只衡量“结构还原度”不能代表“语义正确性”。真实黑白照片上色没有标准答案这两个指标只用来在参数迭代时判断改动是变好还是变坏而不是追求一个绝对高分。参数微调顺序上我习惯先固定sample_num600、w(1.0, 0.5)只调win。用win5、7、9三档各跑一次回环验证选PSNR最高或视觉上色块最少的那档。win定了之后再调sample_num从400到1000每次翻倍观察平坦区域是否还有跳变。最后才动特征权重通常只在纹理错配严重时把w[1]调到0.8。一个小技巧如果参考图里包含多个亮度层次差异巨大的区域可以在strata_sample里把bins从8提高到12。这样暗部、中间调、亮部各分到更多候选。但bins过高会让每个桶的候选过少反而失去意义。我自己最早调参时把采样压到100还怪算法不行后来发现问题在候选数量不够用800点加分层采样后效果天差地别。遇到结果不理想先检查候选量和窗口再怀疑算法本身。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑