news 2026/10/5 10:03:02

YOLOv5行人检测数据集构建指南:格式规范与实战避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5行人检测数据集构建指南:格式规范与实战避坑

简介:本资源是一份开箱即用的行人目标检测专用数据集,严格遵循YOLOv5目录结构规范,面向计算机视觉初学者、算法工程师及模型训练实践者,解决小规模场景下人形目标快速标注、训练与验证的刚需。压缩包共2000个文件,主体为1999个YOLO格式txt标签文件(含训练集3000对图片/标签、测试集300对)及1个可视化脚本show.py,所有文件按images/train、images/val、labels/train、labels/val等标准路径组织,无需重排目录即可直连YOLOv5训练流程;另附classes.txt类别字典,确保类别一致性。资源包大小523MB,结构清晰、即下即用。目前已有356人学习下载。用户可直接运行show.py对任意图像加载预测框并保存可视化结果,大幅降低数据校验门槛;同时提供完整train/val划分与统一person单类定义,适配入门级目标检测实验、课程设计及轻量模型微调任务。

1. 行人检测不是“换个数据集就能跑通”:YOLOv5目录格式的行人数据集,本质是让模型在真实街景里不漏检、不误判、不飘移

你手头有一段监控视频,想用YOLOv5自动框出行人——结果模型把广告牌上的人形剪影当真目标,把遮阳伞下模糊的腿脚漏掉,甚至对穿深色衣服、戴帽子、背光站立的人完全“视而不见”。这不是模型不行,而是你喂给它的数据,根本没覆盖这些高频翻车场景。标题里的“行人目标检测数据集(YOLOv5目录格式)”,说的不是随便找几张带人图、改个文件名就完事;它是一套结构化、可复现、带现实约束的数据交付标准:images/和labels/严格配对,每张图对应一个.txt标签文件,每行按class_id center_x center_y width height归一化坐标写死,连空格数、小数位、换行符都影响训练收敛。它解决的不是“能不能训”,而是“训出来的模型敢不敢部署到路口摄像头里”。适合两类人:一是刚跑通train.py但一换实测场景就崩的新手,需要从数据源头建立鲁棒性意识;二是已有业务系统但检测率卡在82%上不去的工程师,得靠高质量行人数据集做定向增强。别再拿COCO里抠出来的几百张人图凑数了——行人检测的难点从来不在算法,而在数据是否真的“见过”凌晨三点的城中村巷口、暴雨中的公交站台、强逆光下的斑马线。


2. 为什么必须是YOLOv5目录格式?不是VOC、COCO或自定义JSON

2.1 YOLOv5目录格式的底层逻辑:牺牲灵活性,换训练确定性

YOLOv5官方训练脚本(train.py)根本不读XML或JSON。它硬编码了数据加载路径:

  • --data参数指向一个.yaml配置文件,该文件里明确定义train: ./images/train和val: ./images/val;
  • 加载器datasets.py会扫描images/下所有.jpg/.png,自动匹配同名.txt在labels/目录;
  • 每个.txt必须是纯文本,每行<class_id> <x_center> <y_center> <width> <height>,全部归一化到[0,1]区间,小数点后6位(Ultralytics默认精度)。

这不是设计缺陷,而是工程取舍。VOC的XML要解析DOM树,COCO的JSON要遍历嵌套字典,而YOLOv5直接np.loadtxt()读txt,单图加载耗时降低47%(实测1080p图像,XML平均32ms,txt仅17ms)。更重要的是,归一化坐标+固定格式彻底规避了坐标系混乱:VOC用左上角(x,y)+宽高,COCO用左上角(x,y)+宽高但可能含segmentation,而YOLOv5强制中心点+宽高,且所有值相对图像尺寸缩放——这使得数据增强(如mosaic、random affine)能直接对数值做线性变换,无需反复重算bbox顶点。我曾把同一组行人图片转成VOC格式喂进YOLOv5,因XML里<bndbox>坐标未按PIL.Image.open().size校验,导致mosaic拼接时bbox错位,loss在第3轮突然飙升到inf。

2.2 行人数据集的特殊性:为什么不能直接套用COCO或Pascal VOC

COCO的“person”类别包含17万张图,但其中62%是室内场景(办公室、卧室、健身房),23%是艺术照/模特图(高饱和滤镜、夸张姿态),真正符合交通监控视角的不足5%。Pascal VOC的“person”更少,仅4322张,且标注极度简略——很多图里只标了全身框,但实际部署时需识别半身、侧影、遮挡状态。而专业行人数据集(如CrowdHuman、CityPersons)的核心价值在于标注粒度与现实任务对齐:

  • CrowdHuman明确区分visible body(可见躯干)、full body(完整身体)、head(头部)三类框,解决“雨衣遮住下半身但头可见”这类场景;
  • CityPersons定义reasonable(合理尺度)、small(<50px)、occluded(遮挡>60%)、truncated(截断)四类属性,训练时可加权loss;
  • 所有框均按监控视角拍摄,分辨率集中在1920×1080,长宽比接近16:9,而非COCO常见的4:3或1:1。

直接把COCO的person子集转YOLOv5格式,会丢失这些关键属性。Ultralytics的dataset.yaml不支持属性字段,但你可以把occluded样本的class_id设为101(原person=0),在models/yolo.py里修改loss计算逻辑——这是进阶玩法,新手先确保基础格式正确。

2.3 从零构建YOLOv5行人数据集:三步落地流程

步骤1:目录骨架初始化(必须严格)
# 创建标准目录结构(注意:images和labels必须同级,且train/val/test子目录名不可改) mkdir -p pedestrian_dataset/{images/{train,val,test},labels/{train,val,test}} # 验证结构(输出应为6行,无多余空格) find pedestrian_dataset -type d | sort

提示:Ultralytics v8.0+支持--data指向单个dataset.yaml,但该文件里train/val路径仍需指向images/train等子目录。若你把图片全放在images/根目录,训练会报错No images found——因为loader默认只扫images/train。

步骤2:图像与标签文件名严格配对

行人数据集常见错误:

  • 图片名含中文或空格(如路口_行人_001.jpg)→ YOLOv5读取时路径解析失败;
  • 标签文件名大小写不一致(IMG_001.jpgvsimg_001.txt)→ Windows下可能侥幸通过,Linux直接404;
  • 图像格式混用(.JPG和.jpg共存)→glob.glob("*.jpg")漏掉大写后缀。

血泪经验:统一用小写字母+数字+下划线,批量重命名:

# 进入images/train目录执行(其他子目录同理) cd pedestrian_dataset/images/train for file in *.JPG; do mv "$file" "${file%.JPG}.jpg"; done for file in *; do mv "$file" "$(echo $file | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]/_/g')"; done # 同步处理labels/train(确保txt名与jpg名完全一致) cd ../.. cd labels/train for file in *; do base=$(basename "$file" .txt) if [ -f "../images/train/${base}.jpg" ]; then mv "$file" "${base}.txt" else echo "WARNING: no image match for $file" fi done
步骤3:标签文件内容校验(关键!)

每个.txt必须满足:

  • 每行5个数值,用空格分隔;
  • class_id为0(行人唯一类别);
  • x_center,y_center,width,height均∈[0,1];
  • width和height>0(否则训练时iou=0导致梯度爆炸)。

写个校验脚本防翻车:

# validate_labels.py import os import numpy as np from pathlib import Path def check_label_file(txt_path, img_size): h, w = img_size try: data = np.loadtxt(txt_path) if data.size == 0: return False, "Empty file" if data.ndim == 1: data = data.reshape(1, -1) if data.shape[1] != 5: return False, f"Expected 5 columns, got {data.shape[1]}" for i, row in enumerate(data): cls, cx, cy, bw, bh = row if not (0 <= cls <= 1 and cls == int(cls)): return False, f"Line {i}: class_id {cls} not integer or out of range" if not (0 <= cx <= 1 and 0 <= cy <= 1): return False, f"Line {i}: center ({cx},{cy}) out of [0,1]" if not (0 < bw <= 1 and 0 < bh <= 1): return False, f"Line {i}: width/height ({bw},{bh}) invalid" # 可选:检查是否超出图像边界(虽归一化后理论上不会,但原始标注可能错) x1 = max(0, cx - bw/2) y1 = max(0, cy - bh/2) x2 = min(1, cx + bw/2) y2 = min(1, cy + bh/2) if x1 >= x2 or y1 >= y2: return False, f"Line {i}: invalid bbox after clamp" return True, "OK" except Exception as e: return False, f"Parse error: {e}" # 批量校验 label_dir = Path("pedestrian_dataset/labels/train") img_dir = Path("pedestrian_dataset/images/train") for txt_path in label_dir.glob("*.txt"): img_path = img_dir / f"{txt_path.stem}.jpg" if not img_path.exists(): print(f"MISSING IMAGE: {txt_path}") continue from PIL import Image try: img = Image.open(img_path) ok, msg = check_label_file(txt_path, img.size) if not ok: print(f"{txt_path}: {msg}") except Exception as e: print(f"{txt_path}: image open failed - {e}")

运行后若无输出,说明标签格式合规。这一步省不得——我曾因一个.txt里多了一个空行,导致训练第10轮loss突增,debug三天才发现np.loadtxt()把空行读成[nan nan nan nan nan],参与loss计算后梯度爆炸。


3. 行人数据集怎么来?开源资源筛选、清洗与合成增强实战

3.1 开源数据集选型:避开“高分低质”陷阱

网络热词里常提CrowdHuman、CityPersons、WIDER Pedestrian,但直接下载就用会踩坑:

数据集原始格式行人密度关键缺陷是否推荐用于YOLOv5行人训练
CrowdHumanCOCO JSON极高(平均150人/图)标注含ignore区域(非遮挡,是背景干扰物),YOLOv5无法识别此字段,需转为负样本或裁剪✅ 强推,但必须清洗ignore
CityPersonsPascal VOC XML中等(10-30人/图)occluded/truncated属性需映射为不同class_id,否则loss权重失衡✅ 推荐,需修改训练代码
WIDER Pedestrian自定义TXT低(常单人)坐标为绝对像素值,未归一化,且部分图分辨率<640px,YOLOv5默认resize会失真⚠️ 需重标注,慎用
Caltech Pedestrian自定义MATLAB极低(视频帧序列)每帧标注为矩形框,但大量帧无行人(负样本缺失),直接转YOLOv5会导致正负样本严重不均衡❌ 不推荐,除非补全负样本

实操建议:

  • 首选CrowdHuman:下载CrowdHuman_train01.zip等4个分卷,解压后得到Images/和Annotations/;
  • 跳过CityPersons的train_extra子集:该部分标注质量差,误标率高达18%(论文Table 3);
  • 绝对不用WIDER的val集:其验证集标注错误率32%,YOLOv5训练时会学错。

3.2 CrowdHuman转YOLOv5格式:清洗ignore区域+生成负样本

CrowdHuman的annotation_train.json里,每个bbox带is_ignore字段(1=忽略,0=有效)。YOLOv5不认这个,必须处理:

# crowdhuman_to_yolo.py import json import numpy as np from pathlib import Path from PIL import Image # 加载JSON with open("CrowdHuman/Annotations/annotation_train.json") as f: annos = json.load(f) # 创建YOLOv5目录 Path("pedestrian_dataset/images/train").mkdir(exist_ok=True) Path("pedestrian_dataset/labels/train").mkdir(exist_ok=True) for anno in annos: img_id = anno["ID"] img_path = f"CrowdHuman/Images/{img_id}.jpg" if not Path(img_path).exists(): continue # 读取图像获取尺寸 img = Image.open(img_path) w, h = img.size # 筛选有效bbox(is_ignore==0)且面积>200px(过滤噪点) bboxes = [] for box in anno["gtboxes"]: if box.get("is_ignore", 0) == 1: continue x, y, bw, bh = box["fbox"] # fbox=full body box if bw * bh < 200: continue # 归一化 cx = (x + bw/2) / w cy = (y + bh/2) / h nw = bw / w nh = bh / h bboxes.append([0, cx, cy, nw, nh]) # class_id=0 for person # 写入labels label_path = f"pedestrian_dataset/labels/train/{img_id}.txt" if bboxes: np.savetxt(label_path, bboxes, fmt="%.6f") else: # 关键!生成空标签文件(YOLOv5要求每个图都有对应txt) with open(label_path, "w") as f: pass # 复制图像(硬链接节省空间) dst_img = f"pedestrian_dataset/images/train/{img_id}.jpg" Path(dst_img).hardlink_to(Path(img_path))

注意:fbox是完整身体框,vbox是可见身体框。行人检测任务中,fbox更稳定(遮挡时vbox可能消失),故优先用fbox。若需强化遮挡鲁棒性,可将vbox作为第二标签(class_id=1),但需修改模型head输出通道数。

3.3 合成增强:用Diffusion生成“没见过”的行人场景

开源数据集难覆盖极端场景:

  • 凌晨4点路灯下的蓝光行人(色温3500K,信噪比<10dB);
  • 雨天玻璃幕墙反射造成的虚影行人;
  • 无人机俯拍视角(pitch>45°)的微小行人(<30px)。

用Stable Diffusion+ControlNet生成补充数据(需GPU):

# 安装依赖(conda环境) conda install -c conda-forge opencv pytorch torchvision torchaudio pytorch-cuda=11.8 -c nvidia pip install diffusers transformers accelerate safetensors # 生成雨天行人(prompt需精确控制) from diffusers import StableDiffusionControlNetPipeline, ControlNetModel from PIL import Image import torch controlnet = ControlNetModel.from_pretrained( "lllyasviel/sd-controlnet-canny", torch_dtype=torch.float16 ) pipe = StableDiffusionControlNetPipeline.from_pretrained( "runwayml/stable-diffusion-v1-5", controlnet=controlnet, torch_dtype=torch.float16 ).to("cuda") # 输入一张雨天街道草图(Canny边缘图) canny_image = Image.open("rainy_street_canny.png") prompt = "photorealistic pedestrian walking on rainy street at night, wet pavement reflection, cinematic lighting, 8k" image = pipe( prompt, image=canny_image, num_inference_steps=30, guidance_scale=12.0, generator=torch.Generator(device="cuda").manual_seed(42) ).images[0] # 保存并手动标注(生成图需人工校验,不可全自动) image.save("pedestrian_dataset/images/train/rainy_001.jpg") # 生成对应txt(用labelImg工具标注后导出)

玄学经验:Diffusion生成的行人,腿部关节常扭曲。用OpenPose预估关键点,过滤掉膝盖角度>160°或<10°的样本——这能筛掉92%的伪影。


4. 避坑:YOLOv5行人数据集的5个致命错误与修复方案

4.1 现象:训练loss正常下降,但验证mAP@0.5始终为0

原因:dataset.yaml里nc: 1写成了nc: 0,或names: ["person"]被注释掉,导致模型输出层只有0个类别,预测时所有bbox被丢弃。
解决:检查dataset.yaml必须包含:

train: ./images/train val: ./images/val nc: 1 names: ["person"]

注意:nc必须是整数,names必须是列表,字符串用双引号。若写names: person(无方括号),Ultralytics会报错TypeError: list indices must be integers。

4.2 现象:训练时GPU显存暴涨,batch_size=8就OOM

原因:图像尺寸不统一。YOLOv5默认imgsz=640,但若数据集中有4K图(3840×2160),letterbox预处理会将其缩放到640×360,但原始图加载到内存仍占3840×2160×3≈24MB/张,16张图就超384MB。
解决:预处理时统一缩放+裁剪:

# 批量调整图像尺寸(保持宽高比,短边=640) mogrify -path ./images/train_resized -resize '640x640^' -gravity center -extent 640x640 ./images/train/*.jpg # 更新dataset.yaml中的imgsz为640

4.3 现象:验证时大量漏检,尤其小行人(<40px)

原因:YOLOv5默认anchor尺寸(models/yolov5s.yaml中anchors:)针对COCO优化,最小anchor为10×13,而行人小目标常<20px。
解决:用utils/autoanchor.py重新聚类:

python utils/autoanchor.py -f ./pedestrian_dataset/labels/train/ -n 9 -r 0.98 -t 0.2

参数说明:-n 9生成9个anchor(YOLOv5默认),-r 0.98要求98%的bbox能被覆盖,-t 0.2是IOU阈值。输出新anchor替换yaml中anchors:字段。

4.4 现象:模型对戴口罩行人检测率骤降35%

原因:训练数据中口罩覆盖率<5%,模型未学习口罩特征。
解决:用albumentations做在线增强:

# 在datasets.py的__getitem__中插入 import albumentations as A transform = A.Compose([ A.RandomShadow(p=0.3), # 模拟口罩阴影 A.RandomBrightnessContrast(p=0.2), A.OneOf([ A.Cutout(num_holes=1, max_h_size=30, max_w_size=30, p=0.5), # 模拟口罩遮挡 A.CoarseDropout(max_holes=1, max_height=40, max_width=40, p=0.5) ], p=0.7) ], bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))

4.5 现象:部署到Jetson Xavier后FPS从32降到8

原因:训练时用了--rect参数(矩形推理),但部署时输入图尺寸≠训练时imgsz,导致动态resize耗时激增。
解决:训练时禁用--rect,强制所有图resize到固定尺寸:

python train.py --data dataset.yaml --weights yolov5s.pt --img 640 --batch-size 16 --rect False

血泪经验:--rect在训练时提速15%,但会让模型失去对任意尺寸的泛化能力。边缘设备务必关掉。


5. 让行人检测真正落地:验证、调参与工业级部署技巧

5.1 验证不是看mAP,而是看“业务漏检率”

mAP@0.5在COCO上达55%的模型,在真实路口可能漏检37%的闯红灯行人。必须构建业务导向验证集:

  • 采集200段10秒监控视频(覆盖早晚高峰、雨天、夜间);
  • 人工标注每帧的“关键行人”:
    • crossing(正在过斑马线);
    • waiting(在停止线后等待);
    • obstructing(站在车道内阻碍通行)。
  • 用metrics.py计算三类的单独召回率(Recall),而非整体mAP。
# business_recall.py from utils.metrics import ap_per_class import numpy as np # 加载预测结果(preds.npy)和真值(labels.npy) preds = np.load("preds.npy") # shape: (N, 6) [x1,y1,x2,y2,conf,class] labels = np.load("labels.npy") # shape: (M, 5) [class,x_center,y_center,w,h] # 定义业务类别映射(此处简化,实际需按视频帧提取) business_types = { "crossing": [0.3, 0.7, 0.2, 0.8], # 斑马线区域坐标(归一化) "waiting": [0.4, 0.9, 0.1, 0.3], "obstructing": [0.1, 0.6, 0.5, 0.9] } for biz_type, roi in business_types.items(): # 提取ROI内真值和预测 mask_labels = (labels[:,1] > roi[0]) & (labels[:,1] < roi[1]) & \ (labels[:,2] > roi[2]) & (labels[:,2] < roi[3]) mask_preds = (preds[:,0] > roi[0]*640) & (preds[:,0] < roi[1]*640) & \ (preds[:,1] > roi[2]*640) & (preds[:,1] < roi[3]*640) recall = calculate_recall(labels[mask_labels], preds[mask_preds]) print(f"{biz_type} Recall: {recall:.3f}")

关键指标:crossing召回率必须≥95%,否则无法触发预警;obstructing召回率≥80%即可,因其发生频次低。

5.2 超参数调优:行人检测的3个必调参数

YOLOv5默认超参(data/hyp.scratch-low.yaml)针对通用目标,行人需针对性调整:

参数默认值行人检测推荐值原因
lr0(初始学习率)0.010.005行人特征细微(如衣着纹理),过大学习率易破坏底层特征提取
lrf(学习率终值)0.10.05保持一定学习率避免陷入局部最优,尤其对小目标
fl_gamma(Focal Loss γ)0.01.5行人正负样本极不均衡(1:1000),γ=1.5提升难例权重

修改方式:在train.py中传参:

python train.py --data dataset.yaml --weights yolov5s.pt --hyp hyp.person.yaml

其中hyp.person.yaml内容:

lr0: 0.005 lrf: 0.05 fl_gamma: 1.5

5.3 工业部署:TensorRT加速下的行人检测流水线

在Jetson AGX Orin上,FP16 TensorRT引擎比PyTorch快4.2倍。但直接转换YOLOv5会失败——因其PostProcess(NMS)在Python端,TRT无法优化。必须用models/export.py导出ONNX,再用onnx-simplifier清理冗余节点:

# 导出ONNX(禁用training ops) python models/export.py --weights yolov5s.pt --include onnx --img 640 --batch 1 # 简化ONNX(关键!否则TRT报错) pip install onnx-simplifier python -m onnxsim yolov5s.onnx yolov5s_sim.onnx # 转TensorRT(需安装tensorrt>=8.5) trtexec --onnx=yolov5s_sim.onnx --saveEngine=yolov5s.trt --fp16 --workspace=4096

部署时的黑匣子技巧:

  • 输入预处理必须与训练一致:cv2.cvtColor(cv2.resize(img, (640,640)), cv2.COLOR_BGR2RGB);
  • 输出解析用CUDA kernel加速NMS(Ultralytics提供utils/nms_cuda.cu);
  • 对连续帧做track_id关联:用IoU+外观特征(ReID)避免同一行人被重复计数。

我一般会在TRT推理后加一层轻量级卡尔曼滤波,对行人中心点轨迹平滑——这能让路口统计误差从±3.2人/分钟降到±0.7人/分钟。

最后说句实在话:花两周时间打磨一个高质量行人数据集,比调参三天提升的mAP更可靠。因为模型永远只能看到你给它的世界,而行人检测的真实战场,从来不在实验室的测试集里,而在凌晨三点的监控画面上。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 10:02:46

【行业前沿报告】DAgger:让智能体在自己走到的状态上学习

摘要&#xff1a;本文解读 AISTATS 2011 论文 A Reduction of Imitation Learning and Structured Prediction to No-Regret Online Learning。行为克隆在专家数据上训练&#xff0c;但部署时模型会走到自己造成的状态&#xff0c;离线准确率无法直接换算成自主运行可靠性。DAg…

作者头像 李华
网站建设 2026/10/5 10:02:25

MRAM+8位MCU实现高频掉电安全存储方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 10:01:31

Apache POI实战:Java生成Word文档标题、表格、图片与目录全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 10:00:58

工业嵌入式存储选型:MRAM与PIC18F86J11驱动实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 10:00:58

Excel VBA按列拆分数据到多个文件:完整代码与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 9:59:46

一个编程小白的计划

哈啰&#xff0c;我是一名大一新生&#xff0c;现在就读于西南交通大学的信息安全专业。今天是我开始学习C语言的第一天&#xff0c;我的目标是熟练掌握C语言&#xff0c;并在学校期末考试中获得前5%的成绩&#xff0c;并且能对计算机系统有更深入的了解。我通过在网上找的C语言…

作者头像 李华