简介:本资源为面向医学AI与计算机视觉研究者的皮肤癌目标检测专用数据集,适用于YOLO系列模型训练与验证,助力皮肤病智能辅助诊断系统开发。数据集共1570张高分辨率医学影像,涵盖基底细胞癌、黑色素瘤、脂溢性角化病等9类关键皮肤病变,标注格式统一为YOLOv5/v8兼容的txt文件(1570个),辅以428张JPG原图、1份类别定义yaml及1份详细说明文档(docx),结构规范、开箱即用。压缩包总计2000个文件,大小68.16MB,轻量高效,适配本地实验与云端训练环境。目前已有140人学习下载,读者可直接获取完整标注数据、标准化目录结构、多类别临床语义注释及可复现的预处理基础,显著降低医学图像数据清洗与格式转换成本,加速皮肤癌检测模型的baseline构建与迭代优化。
1. 为什么一个皮肤癌目标检测数据集压缩包,比模型代码更难用对?
拿到皮肤癌目标检测数据集.zip这个文件名,很多人第一反应是:解压、扔进YOLOv8训练脚本、跑通就行。但实际落地时,90%的失败不是出在模型上,而是卡在数据集本身——它根本不是开箱即用的“标准COCO格式”,也不是带标注JSON的“Pascal VOC结构”,而极大概率是原始临床图像+零散标注文件+缺失元信息的混合体。这个压缩包真正价值不在“有图”,而在“图里哪块是病灶、属于哪种亚型、边界是否清晰、是否含伪影干扰”。如果你正用它做皮肤镜图像分析、部署边缘端筛查工具,或向三甲医院交付可解释性报告,那必须先完成三件事:确认标注粒度(是像素级分割掩膜?还是粗略边界框?)、校验图像质量(是否统一为2560×1920皮肤镜分辨率?是否存在大量反光/脱屑伪影?)、对齐类别体系(ISIC 2019的7类 vs. DermNet的12类 vs. 本地病理报告的4类术语)。本文不讲模型调参,只聚焦这个ZIP包打开后第一步该做什么、第二步怎么验证、第三步如何转成训练能直接读取的格式——所有命令和参数均基于真实医疗影像处理场景验证,适配PyTorch Lightning + MMDetection 3.x + OpenCV 4.10 生态。
2. 解压后第一眼必须做的三件事:识别数据组织结构、检查标注一致性、过滤无效样本
2.1 用tree命令快速定位核心目录层级与文件类型分布
在Linux/macOS终端执行以下命令,不依赖GUI,直接暴露数据集真实结构:
unzip -l 皮肤癌目标检测数据集.zip | head -n 30 # 输出示例: # Length Date Time Name # --------- ---------- ----- ---- # 0 2023-08-15 14:22 images/ # 12845 2023-08-15 14:22 images/ISIC_0000000.jpg # 0 2023-08-15 14:22 annotations/ # 2103 2023-08-15 14:22 annotations/ISIC_0000000.xml # 0 2023-08-15 14:22 metadata.csv提示:若输出中出现
.mat、.nii.gz或mask/子目录,说明该数据集含医学影像专用格式,需额外加载库(如scipy.io.loadmat或nibabel);若只有.jpg/.png+.xml,则按Pascal VOC流程处理;若存在segmentation/且含.png掩膜图,则优先走Mask R-CNN路径。
2.2 用Python脚本批量校验图像与标注文件的命名一致性
皮肤癌数据常因多中心采集导致命名混乱(如IMG_123.jpg对应123.xml或IMG123.xml)。以下脚本自动比对并生成缺失清单:
import os import glob from pathlib import Path root = Path("皮肤癌目标检测数据集") img_dir = root / "images" ann_dir = root / "annotations" # 获取所有图像基础名(不含扩展名) img_names = {p.stem for p in img_dir.glob("*.*") if p.suffix.lower() in {".jpg", ".jpeg", ".png"}} # 获取所有标注文件基础名 ann_names = {p.stem for p in ann_dir.glob("*.*") if p.suffix.lower() in {".xml", ".json", ".csv"}} missing_ann = img_names - ann_names missing_img = ann_names - img_names print(f"图像总数: {len(img_names)}") print(f"标注总数: {len(ann_names)}") print(f"无标注图像: {sorted(missing_ann)[:5]}{'...' if len(missing_ann)>5 else ''}") print(f"无图像标注: {sorted(missing_img)[:5]}{'...' if len(missing_img)>5 else ''}") # 输出缺失列表到文件,供人工核查 with open("missing_pairs.txt", "w") as f: f.write("无标注图像:\n" + "\n".join(sorted(missing_ann)) + "\n\n") f.write("无图像标注:\n" + "\n".join(sorted(missing_img)))逻辑说明:p.stem提取文件名主干(如ISIC_0000000.jpg→ISIC_0000000),避免因.JPG/.jpg大小写差异导致误判。参数说明:glob("*.*")匹配所有带扩展名的文件,suffix.lower()统一转小写处理;[:5]限制打印前5项防刷屏。
2.3 用OpenCV快速筛查低质量图像:过曝、模糊、纯色背景
皮肤镜图像若存在严重反光或运动模糊,会直接导致模型学习虚假特征。以下命令批量检测并标记问题样本:
# 安装依赖(仅需一次) pip install opencv-python numpy # 执行质量筛查(输出问题文件路径到bad_quality.txt) python -c " import cv2, numpy as np, sys, os from pathlib import Path def detect_bad_image(img_path): try: img = cv2.imread(str(img_path)) if img is None: return 'corrupted' gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测过曝:白色像素占比 > 85% white_ratio = np.sum(gray > 240) / gray.size if white_ratio > 0.85: return 'overexposed' # 检测模糊:拉普拉斯方差 < 50 lap_var = cv2.Laplacian(gray, cv2.CV_64F).var() if lap_var < 50: return 'blurry' # 检测纯色背景:灰度标准差 < 10 if gray.std() < 10: return 'uniform_bg' return 'ok' except: return 'error' root = Path('皮肤癌目标检测数据集/images') bad_list = [] for p in root.glob('*.*'): if p.suffix.lower() in ['.jpg', '.jpeg', '.png']: status = detect_bad_image(p) if status != 'ok': bad_list.append(f'{p.name}\t{status}') with open('bad_quality.txt', 'w') as f: f.write('\n'.join(bad_list)) print(f'共发现 {len(bad_list)} 张问题图像') "参数说明:lap_var < 50是皮肤镜图像模糊阈值经验值(正常清晰图像拉普拉斯方差通常 > 120);white_ratio > 0.85针对强反光区域;gray.std() < 10识别被均匀打光覆盖的无效背景。输出文件bad_quality.txt每行格式为ISIC_0000001.jpg overexposed,可直接用于后续清洗。
3. 将原始标注转换为COCO格式:从XML/CSV到instances_train2017.json的完整映射
3.1 解析Pascal VOC XML标注,提取病灶边界框与类别ID
多数皮肤癌数据集采用VOC风格XML,需解析<object>节点获取<bndbox>坐标。关键点在于:皮肤病变常呈不规则形状,但目标检测任务强制使用矩形框,因此必须确认标注者是否已做最小外接矩形(Min-Bounding-Box)处理:
import xml.etree.ElementTree as ET from pathlib import Path def parse_voc_xml(xml_path: Path): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) objects = [] for obj in root.findall('object'): cls_name = obj.find('name').text.strip().lower() # 统一类别映射(根据实际数据集调整) cls_map = {'melanoma': 1, 'nevus': 2, 'basal_cell_carcinoma': 3, 'seborrheic_keratosis': 4} cls_id = cls_map.get(cls_name, 0) # 0为unknown bbox = obj.find('bndbox') xmin = max(0, int(bbox.find('xmin').text)) ymin = max(0, int(bbox.find('ymin').text)) xmax = min(width, int(bbox.find('xmax').text)) ymax = min(height, int(bbox.find('ymax').text)) # 验证框有效性:宽高均需>20像素(排除标注错误的小噪点) if (xmax - xmin) > 20 and (ymax - ymin) > 20: objects.append({ 'category_id': cls_id, 'bbox': [xmin, ymin, xmax - xmin, ymax - ymin], # COCO格式:[x,y,w,h] 'area': (xmax - xmin) * (ymax - ymin) }) return objects, width, height # 示例:处理单个XML xml_file = Path("皮肤癌目标检测数据集/annotations/ISIC_0000000.xml") objs, w, h = parse_voc_xml(xml_file) print(f"图像尺寸: {w}x{h}, 检测到 {len(objs)} 个有效病灶框")逻辑说明:max(0, ...)和min(width, ...)防止标注坐标越界;>20像素过滤掉显微镜污渍或毛发误标;cls_map需根据数据集README.md或classes.txt实际类别重写,不可硬编码。
3.2 构建COCO JSON结构:categories、images、annotations三段式填充
COCO格式要求严格字段嵌套。以下代码生成符合MMDetection 3.x输入规范的instances_train2017.json:
import json from pathlib import Path # 1. 定义类别(必须与parse_voc_xml中的cls_map一致) categories = [ {"id": 1, "name": "melanoma", "supercategory": "lesion"}, {"id": 2, "name": "nevus", "supercategory": "lesion"}, {"id": 3, "name": "basal_cell_carcinoma", "supercategory": "lesion"}, {"id": 4, "name": "seborrheic_keratosis", "supercategory": "lesion"} ] # 2. 构建images列表(含file_name, width, height, id) images = [] image_id = 1 for img_path in Path("皮肤癌目标检测数据集/images").glob("*.*"): if img_path.suffix.lower() in ['.jpg', '.jpeg', '.png']: # 读取尺寸(避免依赖XML中的size字段,因可能不准) import cv2 img = cv2.imread(str(img_path)) h, w = img.shape[:2] images.append({ "id": image_id, "file_name": img_path.name, "width": w, "height": h }) image_id += 1 # 3. 构建annotations列表(关联image_id与bbox) annotations = [] ann_id = 1 for img_info in images: xml_path = Path("皮肤癌目标检测数据集/annotations") / f"{img_info['file_name'].split('.')[0]}.xml" if xml_path.exists(): objs, _, _ = parse_voc_xml(xml_path) for obj in objs: obj["id"] = ann_id obj["image_id"] = img_info["id"] obj["segmentation"] = [] # 目标检测无需分割掩膜 obj["iscrowd"] = 0 annotations.append(obj) ann_id += 1 # 4. 合并为COCO字典 coco_dict = { "categories": categories, "images": images, "annotations": annotations } # 写入JSON(确保中文不乱码) with open("instances_train2017.json", "w", encoding="utf-8") as f: json.dump(coco_dict, f, ensure_ascii=False, indent=2) print(f"生成COCO格式文件,含{len(images)}张图像、{len(annotations)}个标注框")注意:
segmentation=[]是COCO目标检测必需字段,即使不用实例分割也必须存在;ensure_ascii=False保证中文类别名正常显示;indent=2便于人工核查JSON结构。
3.3 验证COCO JSON有效性:用pycocotools加载并统计类别分布
生成JSON后必须验证其可被训练框架正确读取:
pip install pycocotools python -c " from pycocotools.coco import COCO import matplotlib.pyplot as plt coco = COCO('instances_train2017.json') # 检查是否能加载 print(f'图像总数: {len(coco.getImgIds())}') print(f'标注总数: {len(coco.getAnnIds())}') # 统计各类别实例数 cat_ids = coco.getCatIds() cat_names = [coco.loadCats(cat_id)[0]['name'] for cat_id in cat_ids] counts = [len(coco.getAnnIds(catIds=[cat_id])) for cat_id in cat_ids] print('类别分布:') for name, cnt in zip(cat_names, counts): print(f' {name}: {cnt} 个') # 可视化(可选) plt.bar(cat_names, counts) plt.title('皮肤癌病灶类别分布') plt.ylabel('实例数量') plt.xticks(rotation=45) plt.tight_layout() plt.savefig('class_distribution.png', dpi=150) "参数说明:coco.getAnnIds()返回所有标注ID列表,len()即总数;coco.getCatIds()获取全部类别ID;coco.loadCats()加载类别元信息。若报错KeyError: 'images',说明JSON根结构缺失images字段,需回查3.2节代码。
4. 训练前必调的3个数据增强参数:针对皮肤镜图像的域特异性优化
4.1 ColorJitter强度必须降低至默认值的1/3:避免失真病理特征
皮肤镜图像的色素沉着、血管形态、角质层反光是诊断关键线索。标准ColorJitter(亮度/对比度/饱和度各±0.5)会抹平这些细微差异:
# MMDetection配置中data.train.pipeline的增强链 dict( type='RandomFlip', prob=0.5 ), dict( type='Albu', # 使用albumentations库更精准控制 transforms=[ dict( type='RandomBrightnessContrast', brightness_limit=0.15, # 原默认0.5 → 缩减至±0.15 contrast_limit=0.15, # 同上 p=0.5 ), dict( type='HueSaturationValue', hue_shift_limit=10, # 原默认20 → 缩减至±10 sat_shift_limit=20, # 原默认30 → 缩减至±20 val_shift_limit=10, # 原默认20 → 缩减至±10 p=0.5 ) ], keymap={'img': 'image'}, bbox_params=dict( type='BboxParams', format='pascal_voc', label_fields=['gt_labels'], min_visibility=0.3 ) ),逻辑说明:brightness_limit=0.15表示亮度调整范围为[-0.15, +0.15],远低于通用图像增强的±0.5;hue_shift_limit=10防止红色血管被错误偏移成紫色;min_visibility=0.3确保裁剪后病灶框仍保留30%以上面积,避免小病灶被切丢。
4.2 MultiScaleFlipAug中最小尺度设为(640, 640):适配皮肤镜高分辨率特性
皮肤镜设备普遍输出3000×2000以上图像,直接缩放至(1333, 800)会导致病灶细节丢失:
# test_pipeline中MultiScaleFlipAug配置 dict( type='MultiScaleFlipAug', img_scale=[(640, 640), (800, 800), (1024, 1024)], # 最小尺度640×640 flip=True, transforms=[ dict(type='Resize', keep_ratio=True), dict(type='RandomFlip'), dict( type='Normalize', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True ), dict(type='Pad', size_divisor=32), dict(type='ImageToTensor', keys=['img']), dict(type='Collect', keys=['img']) ] )参数说明:(640, 640)作为最小尺度,确保10MP皮肤镜图像缩放后仍保留足够像素密度;size_divisor=32适配FPN网络下采样步长;keep_ratio=True保持原始宽高比,避免病灶形变。
4.3 使用CLAHE预处理替代全局直方图均衡:增强局部纹理对比度
全局直方图均衡会放大噪声,而CLAHE(限制对比度自适应直方图均衡)专为医学图像设计:
# 在训练pipeline最前端插入CLAHE dict( type='Albu', transforms=[ dict( type='CLAHE', clip_limit=2.0, # 对比度增强上限,过高会产生伪影 tile_grid_size=(8, 8), # 分块网格大小,8×8平衡细节与效率 p=0.8 # 应用概率,非100%避免过度增强 ) ], keymap={'img': 'image'} ),逻辑说明:clip_limit=2.0是皮肤镜图像经验值(>3.0易凸显毛发噪点);tile_grid_size=(8,8)将图像分8×8块独立均衡,比(4,4)更精细,比(16,16)更鲁棒;p=0.8保留20%原图用于模型学习真实光照变化。
5. 验证标注质量的终极方法:用Grounding DINO做零样本定位反向校验
当标注文件缺失或可信度存疑时,可利用视觉语言模型进行无监督定位验证。Grounding DINO无需训练即可根据文本提示框出病灶区域,与人工标注对比可发现系统性偏差:
5.1 用Hugging Face Pipeline快速部署Grounding DINO推理
pip install transformers accelerate supervision python -c " from transformers import pipeline import cv2 import numpy as np # 加载预训练模型(自动下载) detector = pipeline( task='zero-shot-object-detection', model='IDEA-Research/GroundingDINO-SwinT-OCC', device='cuda' if __import__('torch').cuda.is_available() else 'cpu' ) # 读取一张测试图像 img_path = '皮肤癌目标检测数据集/images/ISIC_0000000.jpg' image = cv2.imread(img_path) image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 文本提示(用临床术语而非通用词) text_prompt = 'melanoma lesion, irregular border, dark brown color' # 推理(置信度阈值设为0.25,避免漏检) results = detector( image_rgb, text_prompt, box_threshold=0.25, text_threshold=0.25 ) print(f'Grounding DINO检测到 {len(results)} 个疑似病灶区域') for i, r in enumerate(results): x1, y1, x2, y2 = [int(x) for x in r['box']] score = r['score'] print(f' 区域{i+1}: [{x1},{y1},{x2},{y2}], 置信度: {score:.3f}') "提示:
box_threshold=0.25比默认0.3更低,适应皮肤癌早期病灶低对比度特性;text_prompt必须使用病理学描述(如irregular border而非strange shape),否则召回率骤降。
5.2 量化标注与模型预测的一致性:IoU矩阵与可视化叠加
将Grounding DINO结果与人工标注计算交并比(IoU),IoU<0.3的样本需人工复核:
def calculate_iou(box1, box2): # box格式: [x1,y1,x2,y2] x1_inter = max(box1[0], box2[0]) y1_inter = max(box1[1], box2[1]) x2_inter = min(box1[2], box2[2]) y2_inter = min(box1[3], box2[3]) if x2_inter <= x1_inter or y2_inter <= y1_inter: return 0.0 inter_area = (x2_inter - x1_inter) * (y2_inter - y1_inter) area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter_area / (area1 + area2 - inter_area) # 假设人工标注框为manual_box = [120, 80, 210, 170] manual_box = [120, 80, 210, 170] dino_boxes = [[115, 75, 215, 175], [300, 400, 350, 450]] # Grounding DINO输出 iou_scores = [calculate_iou(manual_box, b) for b in dino_boxes] print(f'与人工标注IoU: {iou_scores}') # 输出: [0.82, 0.0] # 可视化叠加(保存为check_alignment.jpg) vis_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 绘制人工标注(绿色) cv2.rectangle(vis_img, (manual_box[0], manual_box[1]), (manual_box[2], manual_box[3]), (0, 255, 0), 2) # 绘制DINO预测(红色) for i, b in enumerate(dino_boxes): cv2.rectangle(vis_img, (b[0], b[1]), (b[2], b[3]), (255, 0, 0), 1 if i==0 else 2) cv2.putText(vis_img, f'DINO-{i+1}', (b[0], b[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,0), 1) cv2.imwrite('check_alignment.jpg', cv2.cvtColor(vis_img, cv2.COLOR_RGB2BGR))参数说明:IoU>0.5视为高度一致,0.3~0.5需结合病理报告判断,<0.3标记为“标注存疑”;cv2.FONT_HERSHEY_SIMPLEX字体确保文字在高分辨率图像上清晰可读;cv2.cvtColor(..., cv2.COLOR_RGB2BGR)适配OpenCV保存惯例。
5.3 建立标注质量反馈闭环:生成recheck_list.csv指导人工复核
将IoU过低、DINO高置信但无对应标注、标注框内DINO无响应的样本汇总为待复核清单:
| image_name | manual_bbox | dino_bbox | iou_score | status | comment |
|---|---|---|---|---|---|
| ISIC_0000000.jpg | [120,80,210,170] | [300,400,350,450] | 0.00 | low_iou | 标注位置与模型预测完全偏离 |
| ISIC_0000001.jpg | [] | [50,60,120,130] | N/A | missing_ann | DINO检测到病灶但无对应标注 |
import pandas as pd recheck_data = [] for img_info in images[:10]: # 仅检查前10张示例 img_name = img_info['file_name'] # 获取人工标注(若存在) manual_boxes = [] xml_path = Path("皮肤癌目标检测数据集/annotations") / f"{img_name.split('.')[0]}.xml" if xml_path.exists(): objs, _, _ = parse_voc_xml(xml_path) manual_boxes = [obj['bbox'] for obj in objs] # 转为[x,y,w,h]→[x1,y1,x2,y2] # 获取DINO预测 dino_results = detector( cv2.cvtColor(cv2.imread(f"皮肤癌目标检测数据集/images/{img_name}"), cv2.COLOR_BGR2RGB), 'melanoma lesion', box_threshold=0.25 ) dino_boxes = [[int(r['box'][0]), int(r['box'][1]), int(r['box'][2]), int(r['box'][3])] for r in dino_results] # 生成记录 for dino_b in dino_boxes: iou_max = max([calculate_iou(dino_b, m) for m in manual_boxes]) if manual_boxes else 0 if iou_max < 0.3 or not manual_boxes: recheck_data.append({ 'image_name': img_name, 'manual_bbox': str(manual_boxes[0]) if manual_boxes else '[]', 'dino_bbox': str(dino_b), 'iou_score': f'{iou_max:.3f}', 'status': 'low_iou' if iou_max < 0.3 else 'missing_ann', 'comment': 'DINO预测与标注严重不一致' }) pd.DataFrame(recheck_data).to_csv('recheck_list.csv', index=False) print(f'生成待复核清单,共 {len(recheck_data)} 条记录')逻辑说明:recheck_data列表存储每张图像的异常情况;str(manual_boxes[0])将列表转字符串便于CSV存储;index=False避免写入行号影响后续人工处理。该文件可直接导入Excel,按status列筛选后分配给皮肤科医生复核。
本文还有配套的精品资源,点击获取