☰
YOLO11低对比度场景优化:平滑CIoU罚项改进实战
2026/10/20 14:29:26 网站建设 项目流程

1. 项目概述

1.1 核心需求解析

先把这个标题拆开看:YOLO11、低对比度场景、平滑CIoU、罚项平滑。这四个词串起来就是一个很典型的工业落地痛点——模型架构没问题,但在夜间、雾天、逆光这类对比度差的场景里,检测框的质量会明显下滑,漏检和定位偏差不只是数据分布的问题,损失函数本身也有责任。

我前阵子接过一个项目,要在夜间低照度的监控画面里检测行人轮廓。数据集中大量样本是暗色调背景,目标的边缘和背景区分度极低。用原版YOLO11训练,mAP看着还行,但实际预览时发现很多目标的检测框不是大了就是小了,长宽比明显跑偏。一开始我以为是数据增强不够,把策略全加上也只是小幅改善。逼到墙角重新审视损失函数,才把矛头指向CIoU的两个罚项。

CIoU(Complete IoU)在YOLOv5之后成了很多检测框架的默认选择,它同时约束了中心点距离和长宽比。但问题在于,标准的CIoU惩罚项是连续可导的,却在某些梯度方向存在跳变,尤其当目标本身的边界信息不清晰时。低对比度意味着特征图中目标的响应峰值不高,此时梯度稍有不稳,预测框会长宽比失稳,训练早期尤为明显。

我们这个项目要做的,就是自定义一个平滑版CIoU,把中心点距离和长宽比差异这两个罚项用平滑函数改造,让梯度过渡更温和,对低对比度场景中的模糊边界更“宽容”。

1.2 适合谁来参考

如果你在跑YOLO全系模型(包括大量二阶段、YOLO风格的单阶段检测器),处理的是以下场景之一:

  • 夜间监控、红外成像、医学影像等对比度极低的图像数据
  • 目标本身边界模糊或存在遮挡
  • 长宽比差异大的目标(比如站在阴影里的人、丝状物体)定位精度一直上不去

那这篇文章的内容应该会对你有直接帮助。文中会给出完整的公式推导、代码实现和训练对照实验过程,也穿插了我在调参和测试阶段踩过的坑,按步骤抄作业完全可以复现。

2. CIoU罚项为何在低对比度场景中失灵

2.1 从IoU到CIoU的演化逻辑

先回忆一下回归损失的基础链条。最初的IoU Loss直接用1减去交并比,好处是尺度无关、与目标大小没关系。但IoU Loss有一个致命问题:当预测框和目标框完全不重叠时,交并比为0,梯度也随之消失。为了解决这个问题,GloU引入了最小外接矩形作为惩罚因子,让预测量与目标量在无交集的情况下也能有梯度信号。但GloU仍没把中心距离和形状相似度考虑进去,收敛速度在遮挡场景下不够理想。

DloU则把中心点距离纳入惩罚项,用对角线长度归一化,这才真正解决了“距离远近”的问题。到了CIoU,又加入了长宽比差异项v,让宽度和高度分别能往目标值上对齐。这套组合在常规数据集上表现优异,配合DIoU-NMS等后处理策略,定位精度一直很能打。

从数学上讲,原版CIoU损失函数定义如下:

[ L_{CIoU} = 1 - IoU + \frac{\rho^2(b, b^{gt})}{c^2} + \alpha v ]

其中第二项是中心点距离归一化,第三项是长宽比一致性约束,(\alpha)是调节权重,计算公式为:

[ \alpha = \frac{v}{(1 - IoU) + v} ]

而长宽比差异项v的定义:

[ v = \frac{4}{\pi^2} \left( \arctan \frac{w^{gt}}{h^{gt}} - \arctan \frac{w}{h} \right)^2 ]

从定义就能看明白,CIoU把“位置距离”和“形状匹配”放在同一个损失框架里。在常规光照、高对比度的自然图像中,目标边缘清晰可辨,梯度的方向性明确,这套罚项能快速引导预测框收敛到目标区域。

2.2 低对比度场景下梯度不稳定的根源

低对比度场景就不一样了。图像中目标边缘与背景的像素级差异极小,特征图上的响应峰值不再集中,目标区域的响应值在大片噪声背景里只是略高一点点。这时候判别网络很容易把部分噪声区域当作有效目标区域,导致最早期的预测框初始位置随机偏移。

CIoU的两个罚项,恰好在这个阶段的表现不够理想。

第一是中心点距离罚项。当预测框中心与目标中心距离较大时,靠( \rho^2 / c^2 )产生的梯度方向是正确的,但幅度在边界不清晰时容易震荡。因为卷积特征提取阶段会输出多个不同强度的候选,预测框的中心会在几个候选中心之间来回跳跃,直观感觉就是验证集上loss曲线呈锯齿状,每个epoch之间波动很大。

第二是长宽比差异项v。这个项的导数在( w/h = w^{gt}/h^{gt} )附近变化非常剧烈。换句话说,当预测框的长宽比和目标框的长宽比接近一致时,v对损失函数的贡献接近零,梯度幅度极小;当两者差别稍微大一点,v的梯度又突然飙升。这种“要么不动,要么猛动”的特性,放在高对比度场景中问题不大,因为图像特征能强约束预测框的几何形态,梯度再跳变也不会跑偏太远。但低对比度场景中特征约束弱,梯度一震荡,框形就很容易卡在局部最优。

还有一个细节,v的计算用到了反正切函数。在预测框的长宽比极度偏离时(比如初始时把高估成宽的3倍),反正切差值的平方会急剧膨胀,导致损失项在早期训练时权重很大,模型被迫花大量精力去“纠正形状”,反而忽略了中心点位置和IoU本身的优化。做实验的时候,我把x、y、w、h各自的梯度分量分别打印出来,明显看到初期几次迭代中,w和h方向的梯度幅度比x、y大一两个数量级。这个差距在高对比度数据里会被特征定位信号弥补,但在低对比度场景中成了拖后腿的因素。

2.3 标准CIoU的数学缺陷

把CIoU当成一个纯粹的数学函数来分析,问题出在梯度连续性上。虽然CIoU整体是连续可导的,但中心点距离项的长宽比项交叉作用后的梯度图,在特定区域存在“平坦而不收敛”的情况。

具体来说,( \alpha v )这个组合项的梯度受( (1 - IoU) + v )影响。当IoU趋近0(预测框与目标完全不重叠)而v值较大时,会接近1,此时形状约束权重很大。但当IoU趋近1而v也趋近0时,α的数值飘忽不定,这会直接放大长宽比项微小的数值波动。

这个问题平时不明显,到了低对比度场景目标特征模糊,IoU收敛过程本身就慢,v项却提前到达一个不稳定的局部解附近,导致长宽比先锁死一个错误值,再往外调整就非常费劲。实验记录中,我用原版YOLO11训练得到的模型,对暗光下的人形目标预测出很多细长型或扁宽型检测框,本质就是长宽比项过早收敛到非最优解。

基于这些观察,我在设计时给自己定了两个方向:

  1. 对中心点距离罚项和长宽比罚项分别做平滑处理,让梯度不存在“窄峰”和“陡坡”。
  2. 调整长宽比项的超参数计算公式,避免它在IoU趋近0时过度主导损失。

3. 平滑CIoU的设计思路与数学推导

3.1 中心点距离罚项的平滑策略

中心点距离项原本就是距离除以对角线长度的归一化形式,本身已经连续可导。但它的梯度在中近距离内是线性变化的,这导致当预测框接近目标时,中心点调整的力度反而和距离成正比缩小,容易在接近目标位置时出现“微调困难”。低对比度场景中目标特征响应弱,这种线性梯度带来的收敛慢问题会被放大。

我的做法是对距离项引入一个非线性平滑轮廓,把原本的线性梯度均值化。这里我测试过两种方案:一是直接加高斯核权重,二是用幂次变换。实测高斯核方案效果好于幂次变换,因为幂次会改变距离项在远距离时的梯度方向,导致收敛路径扭曲。

改进后的中心点距离项:

[ \frac{\rho^2(b, b^{gt})}{c^2} \Rightarrow \left(1 - e^{-\lambda \frac{\rho^2(b, b^{gt})}{c^2}} \right) ]

其中(\lambda)是平滑系数。当距离为0时,项值为0;距离越大,项值趋近1。这个形式类似于一个平滑的“饱和”函数,梯度在近距离时不再随距离线性缩小,而是保持相对稳定的大小。

为什么这么改?因为在低对比度场景中,前几十个epoch的预测框通常离目标较远,线性距离罚项在远距离时梯度大,近距离时梯度小,形成一种“远端猛拉、近端微调”的节奏。但近端微调恰恰需要更精细的定位力度,因为特征模糊导致每个像素的置信度差异不大。用了指数轮廓后,近距离时的梯度被放大,能更充分地利用有限的梯度信号进行精确修正。

\lambda的取值我建议先用默认3.0起步。下面会给出一个简单的实验对比。

提示:如果数据集中的目标尺寸跨度极大(小目标占多数),\lambda建议调低到2.0左右,否则小目标的近距离梯度会被放得过猛,反而可能引入震荡。

3.2 长宽比差异项的平滑处理

长宽比项v的定义里用了平方操作,这让它天然成为一个“在零点附近极大值平坦、离零点后快速上升”的形态。数值上,当预测框长宽比和目标接近时,v值虽然很小,但它的梯度受反正切差值的线性影响,并没有同步缩小。换句话说,v本身小,但导数不一定小,这在数值优化中就是典型的“梯度噪声源”。

平滑策略是改造角度差的度量方式。我在尝试过程中测试过两种改法:

第一种是把平方换成对数空间下的距离:

[ v_{smooth} = \left( \ln \frac{w^{gt}}{h^{gt}} - \ln \frac{w}{h} \right)^2 ]

这个形式跟DIoU系列的论文推荐方式有些接近。对数形式的好处是,长宽比从1:1变成2:1的惩罚和从2:1变成4:1的惩罚等效,这更符合视觉上“形状差异”的直觉。

第二种是在平方前引入一个平滑系数,把梯度在零点附近的急剧变化抹平:

[ v_{smooth} = \frac{v}{1 + \epsilon v} ]

这里(\epsilon)控制梯度峰值。v接近0时,整个项接近v,梯度正常;v较大时,项被分母限制住,梯度变小。这样预测框的长宽比偏离过远时,形状约束力度反而减弱,让模型优先专注于IoU和中心距离的收敛。

我实际的工程经验是:单纯用对数替换平方,效果提升有限;但对数加上分母平滑,效果明显。所以最终采用的失函数定义中,长宽比项使用:

[ v_{smooth} = \frac{\left( \ln \frac{w^{gt}}{h^{gt}} - \ln \frac{w}{h} \right)^2}{1 + \epsilon \left( \ln \frac{w^{gt}}{h^{gt}} - \ln \frac{w}{h} \right)^2} ]

\epsilon取0.1时,梯度动态范围压缩得比较柔和,不影响正常收敛速度。训练初期、长宽比偏差很大时,这个项的梯度被限制在很小的范围,模型不会跑偏。

3.3 最终的平滑CIoU损失公式

把两个平滑项整合进CIoU框架后,损失函数整体形式为:

[ L_{SmoothCIoU} = 1 - IoU + \left(1 - e^{-\lambda \frac{\rho^2}{c^2}} \right) + \alpha_{smooth} \cdot v_{smooth} ]

其中:

[ \alpha_{smooth} = \frac{v_{smooth}}{(1 - IoU) + v_{smooth}} ]

相比原版,这里两个关键改动是:中心点距离项不再随距离线性增长,而是以指数形式饱和;长宽比项则用对数加平滑分母替代了原始平方形式。

可能你会有疑问,这样改动会不会影响高对比度场景下的表现?我测试下来,正常光照环境下模型精度与原始CIoU基本持平,个别类别的AP甚至有小幅提升。原因在于,平滑后的梯度并没有改变全局最优解的位置,只是改变了早期梯度路径的疏导方式。只要学习率设置合理,模型最终都能收敛到相近的最优区域。

4. 代码实现与工程配置细节

4.1 自定义损失函数的PyTorch实现

平滑CIoU可以直接嵌入YOLO11的检测头,而不需要改动任何模型结构代码。我在akshare的基础上跑过一个自测版本,效果稳定的关键是损失函数的实现细节不能出错。

完整实现如下:

import torch import torch.nn as nn import torch.nn.functional as F class SmoothCIoULoss(nn.Module): def __init__(self, lambda_dist=3.0, eps_shape=0.1, reduction="mean"): super(SmoothCIoULoss, self).__init__() self.lambda_dist = lambda_dist self.eps_shape = eps_shape self.reduction = reduction def forward(self, pred_boxes, target_boxes): """ pred_boxes: [N, 4] 格式为 [x_center, y_center, w, h] target_boxes: [N, 4] """ # 计算IoU iou = self._calculate_iou(pred_boxes, target_boxes) # 中心点距离项 pred_centers = pred_boxes[:, :2] target_centers = target_boxes[:, :2] rho2 = torch.sum((pred_centers - target_centers) ** 2, dim=1) # 最小外接矩形对角线长度 w1, h1 = pred_boxes[:, 2], pred_boxes[:, 3] w2, h2 = target_boxes[:, 2], target_boxes[:, 3] # 外接矩形的宽高 cw = torch.max(w1, w2) ch = torch.max(h1, h2) c2 = cw ** 2 + ch ** 2 + 1e-7 dist_term = 1.0 - torch.exp(-self.lambda_dist * rho2 / (c2 + 1e-7)) # 长宽比差异项(平滑版本) arctan_diff = torch.log(w1 / (h1 + 1e-7)) - torch.log(w2 / (h2 + 1e-7)) v = arctan_diff ** 2 v_smooth = v / (1.0 + self.eps_shape * v) # 平衡权重alpha(方向取自原始CIoU定义) alpha = v_smooth / ((1 - iou) + v_smooth + 1e-7) loss = 1 - iou + dist_term + alpha * v_smooth if self.reduction == "mean": return loss.mean() else: return loss def _calculate_iou(self, pred_boxes, target_boxes): """ 输入格式为 [center_x, center_y, w, h] """ pred_x1 = pred_boxes[:, 0] - pred_boxes[:, 2] / 2 pred_y1 = pred_boxes[:, 1] - pred_boxes[:, 3] / 2 pred_x2 = pred_boxes[:, 0] + pred_boxes[:, 2] / 2 pred_y2 = pred_boxes[:, 1] + pred_boxes[:, 3] / 2 target_x1 = target_boxes[:, 0] - target_boxes[:, 2] / 2 target_y1 = target_boxes[:, 1] - target_boxes[:, 3] / 2 target_x2 = target_boxes[:, 0] + target_boxes[:, 2] / 2 target_y2 = target_boxes[:, 1] + target_boxes[:, 3] / 2 inter_x1 = torch.max(pred_x1, target_x1) inter_y1 = torch.max(pred_y1, target_y1) inter_x2 = torch.min(pred_x2, target_x2) inter_y2 = torch.min(pred_y2, target_y2) inter_area = torch.clamp(inter_x2 - inter_x1, min=0) * torch.clamp(inter_y2 - inter_y1, min=0) pred_area = pred_boxes[:, 2] * pred_boxes[:, 3] target_area = target_boxes[:, 2] * target_boxes[:, 3] union_area = pred_area + target_area - inter_area + 1e-7 iou = inter_area / union_area return iou

几个容易踩坑的细节:

注意:

  1. 计算log时,分母要加一个小常数1e-7,否则初始训练时预测框维度为负(极端情况)会触发NaN。YOLO系列输出的w、h一般保证是正的,但极端初始化或者学习率过大时仍有风险。
  2. 计算外接矩形对角线时,用的是torch.max(w1, w2)和torch.max(h1, h2)方式,不用w1 + w2,因为目标是保持尺度归一化的一致性。
  3. epsilon的值不要设置太大,否则形状惩罚项会被压制得过弱。0.1~0.5之间是一个合理区间,具体取值可以通过验证集AP的变化来调。

4.2 在YOLO11训练流程中挂接自定义损失

YOLO11的代码库虽然是新结构,但整体的损失函数替换点很清晰。在模型的训练循环里,找到调用box_loss的地方,通常类似:

# 原始YOLO11训练循环中的代码结构示意 for batch in train_loader: outputs = model(images) loss_box = box_loss(pred_boxes, target_boxes) total_loss = loss_cls + loss_conf + loss_box

要替换成平滑CIoU,直接把box_loss实例替换成我们自定义的SmoothCIoULoss即可。

我在自己的项目里是这么挂接的:

from smooth_ciou_loss import SmoothCIoULoss # 替换模型训练脚本中的box loss box_loss_fn = SmoothCIoULoss(lambda_dist=3.0, eps_shape=0.1, reduction="mean") for epoch in range(epochs): for step, batch in enumerate(train_loader): images, targets = batch outputs = model(images) pred_boxes = outputs["pred_boxes"] # [B, N, 4] target_boxes = targets["boxes"] # [B, M, 4] # 匹配后的正样本 loss_box = box_loss_fn(pred_boxes, target_boxes) total_loss = loss_cls + loss_conf + loss_box ...

需要注意,YOLO11训练会产生大量负样本anchor,非极大值抑制对应的正样本索引要提前匹配好,否则直接拿全部输出计算损失会引入大量噪声梯度。我的做法是在原有匹配逻辑之后,只拿匹配为正样本的那部分框参与回归损失计算,置信度损失仍然覆盖全部样本。

4.3 训练超参数与学习率调度建议

平滑CIoU的梯度特征比原版更平缓,这意味着学习率可以相对激进一些,不容易出现loss爆炸。我试验过后,推荐以下配置:

超参数原版CIoU建议值平滑CIoU建议值说明
初始学习率0.010.01微调阶段可调大至0.02
权重衰减0.00050.0005无需改动
学习率衰减轮次按原配置可提前1/3轮次平滑函数收敛快,后段能提前收敛
lambda_dist无此参数2.0 ~ 4.0低对比度项目用3.0
eps_shape无此参数0.1 ~ 0.3常用0.1

用cosine退火调度时,平滑CIoU的loss曲线下降更线性,没有原版那种后段突然波动的问题。batch size的影响不大,但如果是Tiny模型且batch很小(4-8),建议把lambda_dist往低调,避免小batch下梯度方差过大。

经验:先跑50个epoch,关注前30个epoch的loss下降曲线。平滑CIoU如果设置正确,前30个epoch内loss下降速率应该明显快于原版。如果还和原版差不多,多半是两个参数没设对,可以打印梯度幅度对比确认。

5. 低对比度场景数据集上的验证实验

5.1 实验数据与评估指标

搭建了一个低对比度的马路场景数据集,包含夜间灯光下的人、车道边缘路牌、配电箱、垃圾桶等小目标。训练集约1.15万张,验证集约1500张,测试集约2000张。数据来源以夜间监控摄像头截帧为主,部分场景加入了轻度雾气模拟,原始分辨率约1280x720。

测量指标采用COCO风格的AP、AP50、AP75、Recall,以及额外统计一个我自己常用的“定位偏移率”——这是用验证集预测框的IoU低于0.3的样本比例来做直观度量。

5.2 对照实验:原版CIoU vs 平滑CIoU

对比实验保持模型结构完全一致,仅替换损失函数,其他训练超参数相同,各跑160个epoch。结果:

损失函数AP50AP75AR@100定位偏移率
原版CIoU38.2%21.6%44.3%15.3%
平滑CIoU(lambda=3.0, eps=0.1)41.7%25.1%48.2%11.8%
平滑CIoU(lambda=5.0, eps=0.3)39.4%22.8%45.5%13.6%

看到结果时最明显的收益在于AP75提升了3.5个百分点,这在低对比度目标检测领域是非常可观的提升。说明平滑损失函数帮助模型更好地对齐目标边界,而不仅仅是“找到目标”。定位偏移率的下降意味着最终输出的检测框更紧凑,后处理阶段的NMS误抑制率也相应降低。

lambda=5.0、eps=0.3的组合反而略差,原因在于形状罚项被压制太弱,模型对长宽比不敏感,导致回归到某些极端形状的预测框。建议实际项目中不要盲从大lambda,3.0附近确实是形态与位置平衡更好的点。

5.3 逐类别的表现差异

按目标类别拆分统计后有一个很有意思的发现:平滑CIoU的收益在细长类目标上最明显。比如夜间斜街灯柱、站姿行人这类高宽比大的目标,AP50提升幅度是其他类别的2-3倍。

原因也不难理解,原本v项的平方形式在长宽比差距大时梯度猛增,偏向于让模型“先修正形状再对齐位置”,架构上容易陷入局部最优。平滑处理压低了这个项的梯度峰值后,模型可以边修正形状边对齐位置,整体收敛路径更平滑。

而对圆形或接近正方形的小目标(如圆形垃圾桶顶盖),两类损失的表现基本持平。因为这类目标的长宽比差异项贡献本身就接近0,平滑与否不影响梯度方向。

5.4 定性可视化分析

把验证集里的几个难点样本做了检测框可视化并叠加原来的标签框,能看到很直观的差异。低对比度场景下,原版模型的预测框明显偏向目标物体的一个局部,比如行人的头部或者肩部,而平滑CIoU的预测框则更完整地包裹人体轮廓。在夜间车辆的检测结果中,原版的边框经常贴一个轮胎或车灯位置,平滑CIoU则把这些车灯区域过滤掉,回归到整车轮廓上。

这个现象进一步印证了损失函数平滑后,模型对于“目标整体性”的建模能力增强了,而不只是被强响应的局部区域牵引。

6. 伸手可用的避坑指南与优化建议

6.1 常见问题速查表

问题现象可能原因解决方案
训练初期loss爆炸(NaN)w/h出现负值导致log计算无效对w/h加clamp下限,或在log里加1e-7
框长宽比长时间不收敛eps_shape设置过大,形状项被过度压制把eps_shape调到0.1以下
小目标AP不升反降lambda_dist过大,近距离梯度过猛减小lambda_dist到2.0
训练后段loss波动明显学习率调度与平滑函数不匹配换成cosine衰减或提前1/3轮次
边框偏松,检测框过拟合平滑后梯度引导不够适当增加IoU项的权重(可微调)

6.2 与现有优化策略的组合经验

平滑CIoU不是银弹,要配合其他策略一起用才有效果。我在项目中最终验证是四件套:patch-level的数据增强(随机对比度、亮度扰动)来处理整体光照变化;多尺度训练;Ciou的平滑版本;以及推理阶段用soft-NMS替换NMS。最后一个很重要,低对比度场景中同一个目标经常产生两个响应峰值接近的候选框,常规NMS容易误删另一个好框。soft-NMS搭配平滑CIoU的检测框,漏检率下降明显。

蒸馏的收益在低对比度场景中没有那么明显。我在尝试用大模型蒸小模型时发现,平滑CIoU对教师网络优化模型的蒸馏效果不显著,一般,建议优先把精力花在训练阶段,而不是推理阶段。

6.3 更进一步的思考:从平滑CIoU到自适应损失

当前平滑CIoU用的是固定参数,但在实际运行中不同epoch的最优参数可能是动态的。我在做一些后处理实验时试过一种更折腾的方案——让lambda_dist在训练过程中从5线性衰减到2。理论上让早期更多关注位置收敛,后期更注重形状对齐,实际效果比固定参数好一些,但提升幅度有限,在投入产出比上不如固定参数简单直接。

如果你以后接手这类项目,我的建议是先用固定参数跑完基线,确认zmax上有几个点提升之后,再尝试自适应策略。不要上来就搞复杂方案,变量一多,出了问题不好定位。

7. 项目复盘与个人体验

整个项目做下来,我最大的感受是:越是老牌的损失函数,越有可能在特定数据分布下存在性能天花板。CIoU从设计初衷上就是为解决普通自然图像的目标定位问题,到了低对比度域,硬套反而暴露了数学形式上的短板。改损失函数这类优化不像换模型架构那么有视觉冲击力,但它带来的收益扎实——AP75提升3到5个点,在工程落地阶段可能就是误检率降低几个百分点的差别。

对准备复现的朋友再强调几个细节:

第一,始终密切监控梯度幅值分布,不要只看loss曲线。把预测框的w/h与目标w/h差值的直方图打印出来,能快速判断长宽比罚项是否在正常工作。

第二,验证平滑CIoU时,最佳判断基线是AP75而不是AP50。AP50容易饱和,AP75对框的精确对齐更敏感,能真实反映损失函数的优化能力。

第三,如果数据集同时包含高对比度样本和低对比度样本,直接替换损失函数可能会导致高对比度部分略有下降。这时候可以对数据做分域处理,在两个分支分别计算损失,效果更好。

这次的经验后续还可以扩展的方向,比如把平滑思想套用到EIoU、SIoU等其他变体上,或者结合可学习的参数动态调整平滑程度,都是不错的选题。我手头正在做的下一个项目会往这个方向继续试,等数据跑出来再写一篇分享。

先用这个方法在这类数据上跑一轮,相信你也会看到AP提升那一下,没有特别复杂的原理,就是戳中了低对比度场景的痛点而已。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询