ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零构建YOLO可用的脸部皮肤病检测数据集:VOC格式标注与实战指南

从零构建YOLO可用的脸部皮肤病检测数据集:VOC格式标注与实战指南 简介目标检测是计算机视觉的核心任务之一其原理是通过算法自动识别图像中特定目标的位置和类别。在医疗AI等垂直领域高质量、针对性强的数据集是模型取得优异性能的技术基石。一个设计良好的数据集能显著提升模型的泛化能力和实用价值尤其在皮肤病辅助诊断等应用场景中。本文聚焦于构建一个开箱即用的脸部皮肤病检测数据集详细阐述了从数据收集、清洗、标注到格式转换的完整工程化流程。针对YOLO这一流行框架深入解析了VOC格式与YOLO格式的差异与转换实战并融入了针对医学图像的数据增强策略与严格的数据划分原则旨在为CV初学者和医疗AI开发者提供一个可直接用于模型训练的、规范的解决方案。1. 项目概述从零构建一个可用的皮肤病检测数据集做计算机视觉项目尤其是目标检测最常听到的一句话就是“数据为王”。但当你真正开始动手特别是面对一个相对垂直的领域——比如脸部皮肤病检测时就会发现找到一个现成的、标注好的、格式对的数据集比想象中难得多。网上公开的数据集要么是通用的人脸数据集要么是医学影像但格式五花八门直接能喂给YOLO模型训练的VOC格式皮肤病数据集几乎是凤毛麟角。这个项目标题“脸部皮肤病检测数据集YOLOVOC格式”直指的就是这个痛点它不是一个简单的数据搬运而是一个从数据收集、清洗、标注到格式转换的完整解决方案目标是为后续的YOLO模型训练提供一个“开箱即用”的基础。这个数据集的核心价值在于其“针对性”和“可用性”。针对性是指它聚焦于脸部的皮肤病变如痤疮痘痘、色斑、痣、皮炎区域等这与检测整张人脸或人体有本质区别需要更精细的边界框和类别定义。可用性则直接体现在“YOLOVOC格式”上。YOLO是目前最流行、最易上手的目标检测算法之一而VOCVisual Object Classes格式是一种广泛支持的标注格式许多标注工具和训练框架都兼容它。将两者结合意味着你拿到这个数据集后几乎不需要任何预处理就可以直接用于YOLOv5、YOLOv8等模型的训练大大降低了入门和实验的门槛。对于谁有用如果你是计算机视觉的初学者想找一个具体的项目练手皮肤病检测是个很好的选择它比猫狗分类更有挑战性比工业缺陷检测更容易获取图像。如果你是医疗AI方向的研究者或开发者这个数据集可以作为一个可靠的基准baseline或预训练数据来源。甚至对于皮肤科医生或医学生它也可以作为一个辅助教学工具直观地展示算法如何定位皮肤病损。接下来我将详细拆解构建这样一个数据集的全过程包括思路、工具、实操步骤以及我踩过的那些坑。2. 数据集构建的核心思路与设计考量构建一个高质量的数据集远不止是把图片和标注文件丢进一个文件夹那么简单。它需要系统的设计和前瞻性的考量尤其是在医学相关领域数据的质量直接决定了模型性能的天花板。2.1 数据来源策略公开、合规与多样性平衡数据从哪里来这是第一个要解决的问题。完全从零拍摄是不现实的我们主要依赖公开资源但必须严格遵守伦理和版权。公开医学数据集这是质量相对最高的来源。例如ISIC国际皮肤影像协作组织档案库提供了大量的皮肤镜图像但主要是黑素瘤等且多为特写不一定符合“脸部”和“自然光”场景。PH²、DermNet等数据集也类似。我们的策略是从中筛选出明显位于脸部的图像并注意其授权协议确保允许用于研究和非商业用途。合规网络爬取在严格遵守网站Robots协议和版权的前提下可以从一些皮肤科科普网站、医学教育平台获取经过脱敏处理的案例图片。关键点在于必须确保图像已去除个人身份信息如眼睛、独特痣等且仅用于学术研究。我会使用Python的requests和BeautifulSoup库进行定向爬取并设置合理的请求间隔避免对目标服务器造成压力。数据增强与模拟为了增加数据多样性并解决某些类别样本少的问题在已有标注数据的基础上进行严格的数据增强。这不仅仅是简单的旋转、翻转。对于皮肤病检测我更加注重光度变化模拟不同肤色、不同光照条件强光、背光下的表现。局部遮挡模拟头发、眼镜、手部对病损区域的局部遮挡提升模型鲁棒性。背景替换将病损区域抠出粘贴到不同的干净脸部背景上需确保背景图像版权合规这是一种有效的“模拟生成”手段。注意伦理与版权是红线。所有数据尤其是涉及人脸的医学图像必须确保来源合法、用途合规。在项目文档中必须明确标注数据来源和使用的许可协议如CC BY-NC-SA 4.0。切勿使用任何未明确授权或可能侵犯隐私的图像。2.2 类别体系定义精准与实用的博弈定义“检测什么”至关重要。脸部皮肤病种类繁多是做成一个精细的几十类分类还是一个粗糙的几类检测这需要权衡。初版建议类别经过调研和实用性考量我建议初始版本定义4-5个类别在可管理性和实用性间取得平衡acne- 痤疮/痘痘通常是点状或小片状隆起最常见。spot- 色斑/斑块如雀斑、黄褐斑、日光斑表现为平坦的色素沉着区域。nevus- 痣通常边界清晰、凸起或平坦的深色点状或片状。rash- 皮疹/皮炎区域表现为片状红斑、脱屑边界可能模糊。other- 其他/疑似病损用于归类不明确或罕见的损伤避免模型强行将其归入上述类别导致错误。标注规范制定每个类别都需要明确的书面标注规范。例如acne标注单个痘痘的凸起边界。对于密集的痘痘群是标成一个大框还是多个小框我倾向于标注多个小框这能让模型学习更精细的位置信息对于计数等下游任务更有价值。spot标注整个斑块的轮廓即使边界模糊也需根据颜色和纹理变化进行最佳估计。rash由于边界高度模糊标注其核心可见区域并在规范中说明此类标注固有的不确定性。2.3 标注格式选择为什么是VOC目标检测的标注格式主要有VOC XML、COCO JSON、YOLO TXT等。选择VOC格式即PASCAL VOC使用的XML格式作为我们的存储格式是基于以下考量可读性与可编辑性强XML是明文格式人类可以直接阅读和修改这对于检查标注错误、进行小规模调整非常友好。相比之下YOLO TXT里的归一化坐标不够直观。信息承载丰富VOC XML可以方便地存储图片尺寸、标注对象类别、边界框坐标xmin, ymin, xmax, ymax还可以扩展添加难度、截断、姿态等属性虽然我们这个项目可能用不到但格式本身支持。广泛的工具链支持绝大多数标注工具如LabelImg、CVAT、MakeSense.ai都直接支持导出VOC格式。这简化了标注流程。到YOLO格式的转换成熟YOLO训练虽然需要自己的TXT格式但从VOC转换到YOLO有非常成熟、稳定的脚本例如YOLO官方仓库提供的scripts/voc_label.py或自写脚本。我们将数据存储为VOC格式相当于保留了一份“源文件”可以灵活地转换为YOLO格式或其他格式适应性更强。核心设计思路总结我们的数据集是一个以VOC XML为“源格式”以便于人工校验和工具处理通过自动化脚本一键转换为YOLO格式以供直接训练。这种两级结构兼顾了生产环节的友好性和消费环节的便捷性。3. 数据采集、清洗与标注全流程实操有了设计思路接下来就是动手环节。这个过程耗时最长也最考验耐心和细致程度。3.1 数据采集与初步清洗我通常使用Python脚本组织这一过程。创建一个项目目录结构如下skin_dataset/ ├── raw_images/ # 存放从各处下载的原始图片 ├── cleaned_images/ # 存放经过清洗后的图片 ├── annotation_voc/ # 存放VOC格式的XML标注文件 ├── annotation_yolo/ # 存放转换后的YOLO格式TXT文件 ├── ImageSets/Main/ # 存放train.txt, val.txt等划分文件 └── scripts/ # 存放各种工具脚本批量下载与重命名无论来自哪个源下载的图片先全部放入raw_images。使用脚本进行批量重命名采用有规律的命名方式如face_skin_000001.jpg避免中文和特殊字符。自动化初步清洗去除非图像文件检查文件头确保都是有效的JPEG或PNG。去除低质量图像使用OpenCV检查图像是否完全损坏无法解码。对于分辨率过低的图像如宽度300像素可以考虑剔除因为目标检测需要足够的像素信息。去重计算图像的哈希值如感知哈希pHash移除重复或高度相似的图像防止数据泄露。人工复审自动化清洗后必须进行人工快速浏览。剔除那些完全不相关非脸部、非皮肤病、图像质量极差严重模糊、过度曝光或包含敏感信息的图片。这一步无法省略。3.2 使用LabelImg进行精细标注LabelImg是开源且最常用的标注工具之一直接支持VOC格式。环境配置与启动通过pip安装(pip install labelImg)或下载可执行文件。启动后将Open Dir指向cleaned_images。标注操作核心技巧快捷键熟练使用W创建框、A上一张、D下一张、CtrlS保存能极大提升效率。框体精度对于边界清晰的病损如痣、单个痘痘务必让框紧贴边缘。对于边界模糊的如色斑、皮疹与团队其他标注者或自己多次标注保持一致是关键。可以定期抽取一些图片进行多人标注计算标注者间信度以确保标准统一。困难样本处理对于极小目标微小粉刺或严重遮挡目标在LabelImg中可以使用difficult标签进行标记。在VOC XML中这会记录为difficult1/difficult在训练时某些框架可以选择忽略这些困难样本。标签一致性确保同一种病损在不同图片中的标签一致。建立并随时查阅我们之前制定的《标注规范》文档。标注文件管理LabelImg保存的XML文件会自动与图片同名并存储在同一个目录或指定目录。我会将其统一保存到annotation_voc文件夹与图片分离便于管理。实操心得标注是体力活也是技术活。不要追求一次标注完所有图片。建议采用“分批标注-抽样检查-修正规范-继续标注”的迭代方式。标注200张后随机抽查20张检查是否存在标签误用、框体不准等问题及时调整规范或对已标数据进行修正。3.3 数据集划分策略不能把所有数据随机分成训练集和验证集就完事。对于医学图像需要更严谨的划分防止数据泄露data leakage。患者级别的划分这是最重要的原则如果同一个患者的多张图片不同角度、不同时期被分别放入训练集和验证集模型可能会因为“记住”了患者的特定特征如肤色、痣的独特 pattern而在验证集上表现出虚高的性能这称为数据泄露。因此划分必须在“患者ID”级别进行。如果数据没有患者ID则需要根据人脸特征在去除病损区域后进行粗略聚类确保同一个人的人脸图像都在同一个集合中。比例分配采用经典的70-15-15或80-10-10比例划分训练集train、验证集val和测试集test。测试集在最终模型评估前应绝对封存不参与任何调参。类别平衡确保每个集合中各个类别的样本数量大致平衡。如果acne图片有1000张rash只有50张就需要对少样本类别进行过采样如复制、增强或在划分时使用分层抽样Stratified Split。生成划分文件编写一个Python脚本根据上述原则生成train.txtval.txttest.txt每个文件内部是图片的文件名不含后缀列表如下# train.txt face_skin_000001 face_skin_000003 face_skin_000005 ...这些文件将存放在ImageSets/Main/目录下是VOC格式数据集的标准组成部分也是后续格式转换脚本的输入。4. VOC格式详解与YOLO格式转换实战理解数据格式的细节是进行有效处理和调试的基础。4.1 VOC格式XML文件结构解析一个典型的VOC标注XML文件如下所示annotation foldercleaned_images/folder filenameface_skin_000001.jpg/filename path/full/path/to/face_skin_000001.jpg/path source databaseUnknown/database /source size width800/width height600/height depth3/depth /size segmented0/segmented object nameacne/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin245/xmin ymin163/ymin xmax259/xmax ymax178/ymax /bndbox /object object namespot/name poseUnspecified/pose truncated1/truncated difficult0/difficult bndbox xmin420/xmin ymin310/ymin xmax480/xmax ymax345/ymax /bndbox /object /annotationsize: 记录了图像的宽、高和通道数至关重要因为坐标转换依赖于此。object: 每个检测目标对应一个object节点。一张图可以有多个。name: 类别名称与我们定义的acnespot等对应。bndbox: 边界框的绝对像素坐标。truncated: 目标是否被截断部分在图像外。对于脸部边缘的病损应标记为1。difficult: 是否为难样本。4.2 编写VOC转YOLO格式脚本YOLO格式的标注文件是一个与图片同名的.txt文件每行代表一个目标格式为class_id x_center y_center width height其中坐标和宽高都是相对于图片宽度和高度的归一化值范围0-1。转换脚本的核心逻辑如下import xml.etree.ElementTree as ET import os # 类别到ID的映射字典必须与后续YOLO训练的data.yaml文件一致 class_dict {acne: 0, spot: 1, nevus: 2, rash: 3, other: 4} def convert_voc_to_yolo(voc_xml_path, output_txt_path): tree ET.parse(voc_xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) with open(output_txt_path, w) as f: for obj in root.iter(object): cls_name obj.find(name).text # 如果遇到未定义的类别可以跳过或报错 if cls_name not in class_dict: continue cls_id class_dict[cls_name] xmlbox obj.find(bndbox) xmin float(xmlbox.find(xmin).text) ymin float(xmlbox.find(ymin).text) xmax float(xmlbox.find(xmax).text) ymax float(xmlbox.find(ymax).text) # 计算归一化中心坐标和宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 写入TXT文件 f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) # 批量转换 voc_anno_dir annotation_voc yolo_anno_dir annotation_yolo os.makedirs(yolo_anno_dir, exist_okTrue) for xml_file in os.listdir(voc_anno_dir): if xml_file.endswith(.xml): xml_path os.path.join(voc_anno_dir, xml_file) txt_name os.path.splitext(xml_file)[0] .txt txt_path os.path.join(yolo_anno_dir, txt_name) convert_voc_to_yolo(xml_path, txt_path) print(fConverted {xml_file} to {txt_name})关键检查点转换后务必随机抽样检查。打开几张图片用OpenCV或简单的绘图脚本将YOLO格式的归一化坐标还原成绝对坐标画在图上看框是否与目标对齐。确保class_dict的ID从0开始连续并与后续的data.yaml配置文件完全一致。4.3 准备YOLO训练配置文件YOLO以Ultralytics YOLOv8为例需要一个数据集配置文件data.yaml其内容如下# data.yaml path: /absolute/path/to/skin_dataset # 数据集根目录 train: images/train # 训练集图片相对路径相对于path val: images/val # 验证集图片相对路径 test: images/test # 测试集图片相对路径可选 # 类别列表顺序必须与转换脚本中的class_dict一致 names: 0: acne 1: spot 2: nevus 3: rash 4: other # 类别数量 nc: 5你需要根据之前生成的train.txt等文件将对应的图片和标注文件YOLO格式组织到images/train/,labels/train/等目录中。通常的YOLO数据集目录结构如下skin_dataset_yolo/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── face_skin_000001.jpg │ │ └── ... │ ├── val/ │ └── test/ └── labels/ ├── train/ │ ├── face_skin_000001.txt │ └── ... ├── val/ └── test/编写一个脚本根据ImageSets/Main下的划分文件将cleaned_images中的图片和annotation_yolo中的标注文件分别复制到上述对应目录即可完成最终数据集的组装。5. 数据质量评估与常见问题排查数据集构建完成后在投入训练前必须进行系统性的质量评估否则“垃圾进垃圾出”Garbage In, Garbage Out的法则会在模型性能上应验。5.1 自动化质量检查脚本编写检查脚本批量发现潜在问题标注文件与图像匹配检查确保每个图片文件都有对应的标注文件VOC XML或YOLO TXT反之亦然。标注格式合法性检查对于YOLO格式检查每行是否有5个数值类别ID是否为整数且在[0, nc-1]范围内归一化坐标是否在[0, 1]区间内允许极小的负值或略大于1的值对应截断目标但需审查。对于VOC格式检查XML语法是否良好xmin是否小于xmaxymin是否小于ymax坐标是否超出图像边界。标注完整性检查空标注文件有些图片可能没有病损是否需要保留为负样本在目标检测中通常不保留负样本图片或者单独处理。如果一个图片对应的TXT文件为空需要确认是否符合预期。极小目标检查计算边界框的像素面积width * height。如果面积小于某个阈值例如 20像素可能标注的是噪声或极难识别的目标需要人工复核是否应该保留。类别分布统计统计每个类别的实例数量生成直方图。严重的类别不平衡需要在训练时通过数据增强或损失函数如Focal Loss进行调整。5.2 可视化审查发现隐藏问题自动化检查能发现格式错误但逻辑错误如标错类别、框体不准需要人眼判断。批量可视化脚本使用OpenCV或Matplotlib编写脚本随机抽取100-200张图片将标注框和类别名称绘制在图像上保存为一张大图或PDF文件进行快速浏览。重点关注框体是否紧密贴合目标类别标签是否正确例如把一个大色斑标成了rash是否有漏标的目标特别是小目标或模糊目标对于truncated1的目标框体是否合理难点样本审查专门审查那些被标记为difficult1或目标极小的样本决定是修正标注、保留还是剔除。5.3 常见问题与解决方案实录在构建过程中我遇到了以下典型问题及解决方法问题现象可能原因解决方案YOLO训练时Loss为NaN或突然爆炸1. 标注坐标异常如归一化后1。2. 图片路径错误导致读取到空或损坏图像。3. 类别ID错误如ID5但nc5。1. 运行上述质量检查脚本修复异常坐标。2. 检查data.yaml中的路径确保是绝对路径并确认图片文件存在且可读。3. 确认class_dict、data.yaml中的names以及标注文件中的ID完全一致。模型预测时框总是偏向图像某侧或尺寸异常标注坐标归一化计算错误。最常见的是用(x_center, y_center, width, height)除以了错误的图像尺寸如用了固定值而非该图片的实际宽高。复查VOC转YOLO的脚本确保对每一张图片都从其XML中读取了正确的width和height进行计算。某个类别如rash的AP平均精度始终极低1. 该类别样本数量太少。2. 该类别标注一致性差边界模糊导致不同标注者差异大。3. 类别定义本身模糊与spot等类别易混淆。1. 对该类别进行过采样和数据增强。2. 重新审查并统一该类别的标注规范对已有标注进行修正。3. 考虑合并易混淆的类别如将spot和rash合并为pigmentation或引入“疑似”标签。训练集精度很高但验证集精度极低数据泄露训练集和验证集包含了同一患者的不同图像。回退到数据集划分步骤。确保以患者为单位进行划分。如果无患者ID尝试用人脸识别模型提取特征进行聚类再划分。标注框在可视化时发生偏移图像显示时可能进行了缩放或保持宽高比的操作但绘制框时使用的是原始坐标。在可视化脚本中确保绘制前将归一化坐标根据当前显示图像的尺寸正确还原为像素坐标。一个关键的避坑技巧在正式启动大规模训练前先用一个极小的子集比如50张图跑1-2个epoch的快速训练。如果这个小实验能正常跑通loss下降且模型在验证子集上能给出看似合理的预测哪怕不准说明你的数据管道基本是通的。这能帮你提前发现大部分格式和配置问题避免浪费几天时间训练才发现数据根本不对。6. 数据增强策略与高级处理一个鲁棒的模型需要见过各种情况的数据。对于皮肤病检测有针对性的数据增强能显著提升模型泛化能力。6.1 针对皮肤病检测的增强方法除了通用的旋转、平移、缩放、裁剪、色彩抖动外以下增强对皮肤病检测特别有效光度模拟Photometric Simulation肤色变化在HSV或LAB颜色空间对亮度L/V和色度a/b通道进行轻微扰动模拟不同种族、不同日晒程度的肤色。光照不均添加渐变阴影或模拟点光源效果使模型不依赖于均匀光照。局部遮挡增强随机在图像上放置模拟物如头发、眼镜、手部、创可贴的贴图遮挡部分病损区域。这能强迫模型学习通过局部特征进行识别提升在真实遮挡场景下的表现。混合MixUp与马赛克MosaicMixUp将两张图像线性混合其标注框也相应混合。能鼓励模型学习更平滑的决策边界。Mosaic将四张图像拼接为一张同时缩放和排列它们的标注框。这是YOLOv4/v5引入的强大增强能极大地增加batch内的上下文多样性让小目标在更大的相对尺度下被学习。对于检测脸上多个小目标如痘痘非常有益。仿射变换与弹性形变轻微的仿射变换可以模拟头部姿态变化。极轻微的弹性形变可以模拟皮肤纹理的自然波动。6.2 实现方式与集成不建议在数据准备阶段就生成大量增强后的静态图像这会急剧膨胀存储空间。最佳实践是在训练时在线on-the-fly进行增强。使用YOLO内置增强Ultralytics YOLOv8的配置文件default.yaml或训练命令参数提供了丰富的增强选项如hsv_h,hsv_s,hsv_v,translate,scale,mosaic,mixup等。你只需要在训练命令中调整这些参数即可。yolo train datadata.yaml modelyolov8n.pt epochs100 imgsz640 augmentTrue hsv_h0.015 hsv_s0.7 hsv_v0.4 translate0.2 scale0.9 mosaic1.0 mixup0.15自定义增强管道如果内置增强不满足需求例如想添加特定的局部遮挡可以使用Albumentations或imgaug这样的专业增强库自定义一个变换管道然后集成到YOLO的数据加载器中。这需要修改源代码门槛较高但最灵活。经验之谈增强不是越多越好、强度越大越好。过强的增强可能会破坏图像中病损的语义信息例如把痘痘的颜色变得完全不像痘痘。建议从默认的增强强度开始通过观察验证集精度和损失曲线来调整。如果模型在训练集上过拟合训练损失持续下降验证损失先降后升则可以适当增加增强强度如果模型学习困难训练损失下降很慢则可能增强太强需要减弱。7. 数据集维护、版本管理与分享建议数据集不是一劳永逸的产物需要迭代和维护。7.1 版本控制使用Git LFS大文件存储或DVC数据版本控制工具来管理数据集的版本。每次对数据有重大更新如新增类别、修正大量标注、增加数据量都打上一个版本标签如v1.0,v1.1。在README.md中详细记录每个版本的变更日志Changelogv1.0初始版本包含5个类别2000张图像。v1.1修正了rash类别的标注规范重新标注了500张相关图像新增了scar疤痕类别补充300张图像。7.2 文档与元数据一个专业的数据集必须包含完善的文档。创建一个清晰的README.md文件内容应包括数据集概览名称、描述、统计信息图像数、实例数、类别分布图。目录结构详细说明每个文件夹的作用。数据格式详细说明VOC XML和YOLO TXT的具体格式并附上示例。数据划分说明划分原则如患者级划分和具体的文件列表。标注指南提供标注规范的详细说明最好附上带示例的图示。来源与许可明确列出所有数据来源并注明其使用的许可协议。声明数据集整体的许可协议如CC BY-NC-SA 4.0。使用方式如何用于YOLO训练的详细步骤。联系方式提供反馈问题的渠道。7.3 分享与协作如果计划开源数据集选择合适的平台GitHub Git LFS适合中小规模数据集便于代码和文档协同。Kaggle Datasets拥有庞大的AI社区易于发现和引用提供版本管理和讨论区。Hugging Face Datasets新兴的AI社区平台对数据集的支持很好易于与模型Hub集成。云存储元数据对于超大数据集可以将原始数据放在稳定的云存储如AWS S3, Google Cloud Storage在GitHub上仅存放元数据、文档和加载脚本。在分享时务必提供数据加载的示例脚本Python降低使用者的入门门槛。例如提供一个load_dataset.py脚本演示如何读取图像和标注并将其转换为PyTorch DataLoader。构建“脸部皮肤病检测数据集”是一个融合了医学知识、数据工程和机器学习实践的综合性项目。它没有太多高深的理论但极其考验执行者的细心、耐心和工程化能力。当你按照上述流程一步步从零搭建起一个干净、规范、可用的数据集并看到它成功训练出第一个能准确定位脸上痘痘的YOLO模型时那种成就感是无可替代的。这个过程中积累的数据处理、质量控制和问题排查经验会让你在后续的任何CV项目中都受益匪浅。本文还有配套的精品资源点击获取
返回列表