news 2026/10/11 20:26:27

番茄实例分割数据集实操:从COCO转换到YOLOv8-seg训练与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
番茄实例分割数据集实操:从COCO转换到YOLOv8-seg训练与避坑指南

简介:一份面向智慧农业与计算机视觉的番茄实例分割数据集,采用YOLO格式的多边形标注,覆盖坏番茄、好番茄、绿番茄与茎四个类别,适用于农业AI监控、自动化采摘、作物质量评估及教学研究等场景。压缩包共2000个文件,核心为1286个TXT标注文件与712张JPG原图,另有1个YAML配置文件和1个DOCX说明文档,整包约961.94MB。目前已有83人学习下载。数据集中每个番茄实例均经过精细多边形标注,边界准确,类别反映果实成熟度与茎部结构,可直接用于YOLO系列模型的实例分割训练;图片采集自实际生产环境,能帮助模型更好地应对田间光照、遮挡等复杂情况。配套的YAML和文档也便于快速配置环境与加载数据,适合算法工程师、农业科研人员及高校学生用于实验验证或项目落地。

1. 番茄实例分割数据集:先看清这一包 zip 里装的到底是什么

温室里数番茄看起来是个笨活,真做起来比想象中麻烦:叶子挡、果实叠、青果和枝干同色。我接过不少这类项目,最后都落到同一个东西上——一份番茄实例分割数据集。这份带20251122-173819时间戳的 zip 包,就是某个批次导出的带掩码标注数据:每一颗番茄都被多边形圈出来,用于训练模型做像素级识别。它适用的场景很具体:温室产量预估、采摘机械臂视觉、以及成熟度分级的前置检测。适合谁用?刚买了标注工具想跑通实例分割全流程的工程师,或者想让现有检测模型升级成像素级 mask 的团队。接下来的内容,我会按实际动手顺序讲:先拆包验数据,再转换格式喂给 YOLOv8-seg,最后把常见坑一次性说清。

2. 拆开数据集先做“体检”:目录结构、标注格式与合法性校验

拿到任何实例分割数据集,第一件事不是急着训练,而是把这包数据从头到尾验一遍。番茄这种目标形态不规则、遮挡严重的对象,标注文件里藏着的小毛病特别多:坐标越界、多边形自交、类别 ID 错位,任何一个都会在训练时变成幽灵报错。我一般会花半小时做一次完整性体检,后面能省出好几天调参时间。

2.1 先看 zip 里装了什么:目录结构与两种主流标注格式

解压前先用unzip -l看一眼包内结构,不要直接双击解压。命令行能让你在没有图形界面的服务器上也能完成检查,而且输出更紧凑:

unzip -l tomato_seg_dataset_20251122-173819.zip | head -50

这条命令只列目录清单,不解压全部内容。head -50是防止文件太多时刷屏。常见做法是里面同时带images/和annotations/两个顶层目录,图像按train/val分好;标注则可能是 COCO JSON,也可能是 YOLO-seg 的 txt。两种格式对应完全不同的后续处理路径。

COCO 格式在实例分割数据集里最通用。它把整个数据集的标注塞进一个annotations.json,里面有三个核心数组:images记录每张图的宽高和文件名,annotations存每个实例的多边形坐标、bbox 和类别,categories定义类别名和 ID。它的好处是自描述能力强,坏处是文件大、随机读取慢。

YOLO-seg 格式则是一张图对应一个 txt 文件,每行是一个实例,格式为class_id x1 y1 x2 y2 ...,坐标全部归一化到 0~1。它读取快、和 YOLO 系列训练器无缝衔接,但可读性差,用文本编辑器打开基本看不出对错。判断数据集属于哪种格式,一条命令就够了:

unzip -l tomato_seg_dataset_20251122-173819.zip | grep -E "(json|txt)$" | head -20

如果输出里出现annotations.json,优先按 COCO 走;如果看到一堆和图片同名的 txt,就是 YOLO-seg。两种格式我都遇到过,建议先确定再动手,别凭文件名猜。

2.2 用脚本把数据集“体检”一遍:图像解码、标注合法性、类别分布

解压后我会跑一个检查脚本,逐张验证图像能否正常解码、多边形坐标是否越界、类别分布是否合理。图像文件损坏在数据集中不少见,可能是采集设备写入中断或压缩传输丢包,这类坏图会在训练时直接中断进程。

import json import cv2 import glob import os # 路径按实际解压位置改 img_dir = "tomato_seg_dataset/images/train" json_path = "tomato_seg_dataset/annotations/instances_train.json" # 1. 检查图像完整性 imgs = glob.glob(os.path.join(img_dir, "*.jpg")) + glob.glob(os.path.join(img_dir, "*.png")) bad_imgs = [] for p in imgs: img = cv2.imread(p) if img is None: bad_imgs.append(p) print(f"图像总数: {len(imgs)}, 坏图数: {len(bad_imgs)}") # 2. 检查标注合法性 with open(json_path, "r") as f: coco = json.load(f) img_info = {item["id"]: item for item in coco["images"]} cat_info = {item["id"]: item["name"] for item in coco["categories"]} invalid = 0 for ann in coco["annotations"]: seg = ann["segmentation"] if isinstance(seg, dict): # RLE 格式跳过,具体检查在下一节说明 continue for poly in seg: xs = poly[0::2] ys = poly[1::2] img = img_info[ann["image_id"]] w, h = img["width"], img["height"] if max(xs) > w or max(ys) > h or min(xs) < 0 or min(ys) < 0: invalid += 1 print(f"非法标注: image_id={ann['image_id']}, ann_id={ann['id']}") print(f"越界多边形数量: {invalid}") # 3. 类别分布 from collections import Counter cat_counter = Counter() for ann in coco["annotations"]: cat_counter[cat_info[ann["category_id"]]] += 1 print("类别分布:", dict(cat_counter))

这段脚本三个用途。第一段用cv2.imread读图,返回None就是坏图,直接删掉或重下。第二段把多边形的 x 坐标和 y 坐标拆开,分别和图像宽高比较,找出越界标注;这类标注在训练时轻则 loss 异常,重则直接 NaN。第三段统计类别分布,番茄项目常见的是unripe_tomato和ripe_tomato两类,如果其中一类数量只有另一类的十分之一,说明采样偏了,需要补数据。

提示:边界情况要留意,坐标等于图像宽高是合法的(像素从 0 开始),只有大于宽高才越界。脚本里我用的是严格大于判断。

3. 把番茄数据集跑进 YOLOv8-seg:标注读取、yaml 配置与训练命令

体检完数据,下一步就是把标注喂给模型。这里我选择 YOLOv8-seg 作为主力训练框架,不是因为它是最新的,而是因为它的实例分割链路最稳:支持 COCO 和 YOLO-seg 两种标注读取、训练命令短、验证指标全、部署导出也方便。番茄这种边缘模糊、目标密集的场景,YOLOv8-seg 的精度和速度平衡在实测里是够用的。

3.1 为什么要选 YOLOv8-seg:番茄实例分割的任务特点与模型选型

番茄实例分割和通用分割任务最大的差别在于目标形态——果实近似椭圆但又不对称,绿色果实的边缘和叶片颜色接近,遮挡导致轮廓不完整。这种场景下,Mask R-CNN 那种两阶段方法精度上限高但速度慢;YOLOv8-seg 把检测框和 mask 分支放在同一个单阶段网络里,从训练到部署的压力都小一截。我做过的温室项目里,YOLOv8-seg 在 640 分辨率下能达到 30~45 FPS,mask 边缘虽然不如 Mask R-CNN 精细,但对产量统计来说完全够用。

选型还要看数据集本身给的是什么标注。如果数据是 COCO 格式,YOLOv8-seg 原生支持segment训练模式直接读取;如果数据是 YOLO-seg txt,则不需要任何转换。番茄这类小目标偏多的数据处理原则上优先保证召回率,模型规模从中杯开始,不要一上来就上最大模型,显存和时间成本都会翻倍。

3.2 COCO 转 YOLO-seg:多边形归一化脚本与参数说明

如果数据集是 COCO 格式,需要先转成 YOLO-seg 的 txt。转换的核心是按图片 ID 聚合该图的所有实例,把多边形坐标按图像宽高归一化,并写入同名 txt 文件:

import json import os def coco_to_yolo_seg(json_path, output_dir): with open(json_path, "r") as f: coco = json.load(f) img_info = {item["id"]: item for item in coco["images"]} ann_by_img = {} for ann in coco["annotations"]: img_id = ann["image_id"] if img_id not in ann_by_img: ann_by_img[img_id] = [] ann_by_img[img_id].append(ann) os.makedirs(output_dir, exist_ok=True) for img_id, anns in ann_by_img.items(): img = img_info[img_id] w, h = img["width"], img["height"] filename = os.path.splitext(img["file_name"])[0] + ".txt" lines = [] for ann in anns: # 只处理多边形,跳过 RLE if isinstance(ann["segmentation"], dict): continue # 取多边形外环,多个环只取第一个(常见做法) poly = ann["segmentation"][0] norm_coords = [] # 坐标归一化,并强制截断到 [0, 1] for i in range(0, len(poly), 2): x = min(max(poly[i] / w, 0.0), 1.0) y = min(max(poly[i + 1] / h, 0.0), 1.0) norm_coords.append(f"{x:.6f}") norm_coords.append(f"{y:.6f}") # 注意:COCO 的 category_id 从 1 开始,YOLO 从 0 开始 cls_id = ann["category_id"] - 1 lines.append(f"{cls_id} " + " ".join(norm_coords)) with open(os.path.join(output_dir, filename), "w") as f: f.write("\n".join(lines) + "\n") coco_to_yolo_seg("annotations/instances_train.json", "labels/train")

这段脚本有几个关键参数点。segmentation可能是多边形列表也可能是 RLE 编码字典,脚本对 RLE 直接跳过——如果你的数据集标注用了 RLE,需要额外用pycocotools解码成边界点,否则这部分实例会丢失。category_id - 1是个高频错误源:COCO 类别从 1 编号,YOLO 从 0 编号,直接复制代码忘记减一,训练时所有类别错位,验证 mAP 直接崩到 0。归一化后强制截断到 0~1 是防御性写法,避免浮点误差导致坐标越界。

3.3 数据划分与 yaml 配置:train/val 比例、类别名、路径写法

转换完标注,接下来是目录整理和 yaml 配置。我的习惯是把数据组织成 YOLO 标准目录结构,这样后续换模型、换框架都不用重新整理。images/和labels/保持同级,train和val子目录一一对应。

tomato_seg_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── tomato.yaml

划分比例方面,番茄数据集通常几百到几千张图,按 8:2 划分是基线设置。关键是划分前要确认没有同一株番茄的连续帧照片被随机分到两个集合里。我在后面避坑章节会详细说这个泄漏问题,这里先提示:按时间或按植株分组划分比按随机划分更可靠。

yaml 配置是训练器读取数据和类别的入口,写错路径或类别名会导致训练直接失败:

# tomato.yaml path: /home/user/datasets/tomato_seg_dataset # 绝对路径,相对路径容易踩坑 train: images/train val: images/val names: 0: unripe_tomato 1: ripe_tomato

path字段建议用绝对路径。YOLO 训练时工作目录可能切换,相对路径会导致找不到图片。names的顺序必须和转换脚本里的cls_id对应,否则类别标签就换位了。这里还容易犯一个错:类别名里用中文或带空格,YOLO 解析 yaml 时对特殊字符处理不友好,统一用英文小写加下划线最稳妥。

3.4 跑通训练命令:关键超参数与显存预算

配置写好后,训练命令本身很简单:

yolo segment train \ data=tomato.yaml \ model=yolov8s-seg.pt \ epochs=200 \ imgsz=640 \ batch=8 \ workers=4 \ project=tomato_runs \ name=seg_baseline

model=yolov8s-seg.pt会从官方权重加载预训练参数做迁移学习,这对番茄这种自定义数据集至关重要——从头训练 200 个 epoch 的收敛效果远不如预训练微调。imgsz=640是速度和精度的折中;番茄小目标多的话,我建议直接试imgsz=960,显存充足时精度提升明显。batch=8在 8GB 显存的 GPU 上接近上限,如果你用 24GB 显存可以提到 16 或 32,收敛更稳。

训练中的关键观察点是 loss 曲线的下降形态。正常情况box_loss和seg_loss在头 20 个 epoch 内快速下降,后面缓慢收敛;如果seg_loss震荡不降,大概率是标注质量问题,不是超参数问题。训练完成后,val 目录下的mask AP指标会输出到终端,这个指标的解读放在第五章节细讲。

4. 番茄实例分割常见问题:五条能省一周的避坑记录

这部分是我最想写的。实例分割数据集项目里,数据本身的坑远多于模型结构的坑。下面五条全是真实踩过的,每一条都按“现象 → 原因 → 解决”写清楚,对照排查能省下一周调参时间。

4.1 训练 loss 直接变 NaN:多边形自交或坐标越界

训练跑到第 20 个 epoch,seg_loss突然变成nan,然后整个 loss 曲线炸掉。重启训一次,在同样的位置又炸。这种情况基本上不是学习率问题,而是标注数据里有毒实例——多边形坐标越界、出现自交、或者顶点数少于 4 个。YOLO 在计算 mask 损失时对这类非法多边形没有足够防御,直接产生非法梯度。

解决方法是回到体检脚本,加一段几何校验:计算多边形的外接矩形和面积,面积小于 1 像素的实例直接剔除;坐标越界的做截断而不是跳过,因为跳过会让一张图少一个实例,训练信号缺失。自交多边形的判断用shapely一行搞定:

from shapely.geometry import Polygon def check_polygon(coords): poly = Polygon(coords) return poly.is_valid and poly.area > 1.0

4.2 指标很漂亮但看 mask 全是错的:数据泄漏

训练完 mAP@50 高达 0.95,可视化验证时发现模型对训练集里出现过的植株识别得极好,对没见过的温室区域几乎瞎猜。这是典型的数据泄漏——同一个番茄植株的多次拍摄被随机分到了 train 和 val,验证集和训练集的图像内容高度重叠,指标虚高。

解决方法是按采集批次或植株个体分组划分数据。比如每次采集 50 张连拍,就按组切分而不是按单张切分;或者按日期切分,前 70% 日期做训练,后 30% 日期做验证。这个划分策略应该在数据集处理的最开始就定好,等训练完再改划分,等于全部重训一遍。

4.3 小番茄全漏检:Mosaic 增广与小目标丢失

可视化验证时发现,图片里占面积不到 1% 的小番茄(刚坐果的幼果)几乎全部漏检。YOLO 的默认mosaic=1.0增广会把四张图缩在一起训练,番茄果实本来就不大,缩完之后变得只有十几个像素。另外下采样 32 倍后,小目标的特征图响应非常弱。

解决思路是双管齐下。第一,把imgsz从 640 提到 960,小目标的像素数接近翻倍;第二,把mosaic从 1.0 下调到 0.3,给小目标更多原始尺度曝光。还可以用 YOLOv8 自带的copy_paste增广,把小番茄实例复制粘贴到其他位置,增加正样本数。这三个调整搭配起来,小目标召回率能提升好几个点。

4.4 绿色番茄召回率低:标注一致性问题

训练结果里,红色番茄的 mAP 在 0.92,绿色番茄只有 0.71。看可视化结果,模型把所有绿色物体都倾向于预测为叶子,或者掩码边缘大片糊到叶子上。原因出在标注环节——绿色番茄和叶子的颜色太接近,标注员在勾勒轮廓时光靠肉眼很难分清边界,导致不同标注员(甚至同一个人不同时间)画出的掩码差异很大。

解决方法是制定标注规范,不给标注员自由发挥空间。我一般要求的规范是:果实可见轮廓必须完整闭合,边缘落在颜色突变处,不通过推理补齐被叶子遮挡的部分。另外可以做一次标注一致性审查,随机抽 5% 的图片让两位标注员分别标,计算 mask IoU,低于 0.8 的重新标。这个步骤虽然费时间,但对绿色目标的训练效果影响是决定性的。

4.5 转换脚本把背景也写进掩码:类别 ID 从 0 还是从 1

COCO 转 YOLO-seg 训练出来的模型,验证时发现预测掩码全部偏移到背景区域,或者出现整张图都被预测为目标的情况。检查转换脚本,发现category_id没有减 1——COCO 的类别号从 1 开始(0 往往是背景),YOLO 从 0 开始。如果数据集里只有番茄这一类,COCO 里番茄的category_id=1,转换后cls_id=0才是正确的;不减 1 就会变成cls_id=1,而 yaml 里names只有 0 号类别有定义,训练器就把 1 号当成不存在或映射错乱。

这个坑的隐蔽之处在于,如果 yaml 里定义了两个类别(比如 green 和 red),减 1 错误不会立刻让训练崩溃,只会让两个类别的标签互换或错位,最终的 mask AP 掉一半。排查方法是在转换后随机挑几个 txt 文件,用脚本读回并画在原图上,人工确认每个实例的类别标签是否和图像内容一致,这一步 5 分钟就能避免一个周末的无效训练。

5. 结果怎么看:用 mask 可视化、mask AP 与业务指标确认番茄分割真实水平

训练跑完,终端输出一长串指标,但“指标好”和“模型能用”是两回事。番茄实例分割的落地评价有两层:模型层面的 mask AP 和业务层面的计数误差率、漏检率。这一章讲清楚怎么看、怎么算、怎么用。

5.1 视觉验证优先:把预测掩码画回原图

任何量化指标都没法替代“亲眼看图”。我每次训练完的第一件事是随机抽 20 张验证集图片,把预测的掩码画回原图,重点看三个地方:绿色小果实的召回、遮挡果实的掩码连续性、以及背景区域有没有被误分割。

from ultralytics import YOLO import cv2 model = YOLO("tomato_runs/seg_baseline/weights/best.pt") img = cv2.imread("tomato_seg_dataset/images/val/IMG_0042.jpg") results = model.predict(img, conf=0.25, iou=0.5) for r in results: if r.masks is not None: for mask, box in zip(r.masks.data, r.boxes): # mask 是 640x640 的浮点张量,需要缩放到原图尺寸 mask_np = mask.cpu().numpy() mask_resized = cv2.resize(mask_np, (img.shape[1], img.shape[0])) # 将掩码区域着色叠加到原图上 color = (0, 255, 0) # 绿色 img[mask_resized > 0.5] = img[mask_resized > 0.5] * 0.5 + \ np.array(color) * 0.5 cv2.imwrite("visual_check/IMG_0042_pred.jpg", img)

conf=0.25是可视化时的低阈值设置,目的是看模型“有没有能力检测到”,而不是在最优阈值下表现如何。iou=0.5用于 NMS 去重。mask_resized > 0.5是关键的判断阈值,模型输出的 mask 是 0~1 的概率图,0.5 是常用的二值化分界;对番茄这种目标,我建议在验证时分别试 0.3 和 0.7,看掩码轮廓是否后缩或外扩,找到最适合业务的概率阈值。

5.2 量化指标:mask AP、mAP@50、mAP@50-95 怎么算怎么读

YOLOv8-seg 验证时输出的核心指标是mAP@50和mAP@50-95。mAP@50是预测掩码和真实掩码的 IoU 超过 0.5 即算正确,对番茄分割来说,这个指标合格线在 0.85 以上;mAP@50-95是把阈值从 0.5 到 0.95 每隔 0.05 算一次再取平均,更严格,番茄分割这种边缘模糊的目标在 0.7 以上就算不错。

业务侧还要算两个指标,用验证集即可。第一个是计数误差率——用预测 mask 的连通域数量除以真实实例数量,这个指标直接反映“数番茄”这个核心诉求的准确度;第二个是召回率在低置信度下的表现——把conf从 0.5 降到 0.1,如果召回率提升超过 10%,说明模型有能力检测只是阈值设高了,业务系统可以通过调阈值来平衡漏检和误检。

提示:mask AP 和检测框 AP 的差别在于 mask IoU 对边缘质量更敏感。番茄边缘模糊、遮挡多,不要强求 mask AP 和检测 AP 完全一致,差 2~3 个点是正常的。

6. 把这份番茄实例分割数据集变成自己的:伪标签、增广与跨场景扩展

数据集的价值不在于训练完一次就结束,而在于它能不能变成后续工作的起点。一个完整的数据集标注成本从几天到几周不等,用好了能省下大量重复劳动。

伪标签半自动扩充是最容易见效的进阶操作。用训练好的模型对未标注的温室新照片跑预测,置信度高于 0.9 的预测直接写入 YOLO-seg 格式的 txt,当成伪标注数据加入训练集。这样可以把训练数据从几百张扩到几千张,成本趋近于零。注意伪标签只用来扩大训练集,不要混进验证集,否则指标会虚高到失真。

数据增强策略要按实际场景调整。温室光照相对均匀,色彩增强hsv_h保持默认就行;如果是露天种植,建议hsv_s和hsv_v加码到 1.2 以上,模拟不同时段的光照变化。对于果实重叠严重的场景,fliplr=0.5的左右翻转是安全增强,但不要开上下翻转——番茄不会倒着长在藤上,这个细节反映了对任务语义的理解。

跨场景扩展是数据集的最终考验。从这份数据集训练出的模型,迁移到另一个品种(比如串收小番茄)或者不同光照条件的大棚,一般会掉 10~20 个点的 mAP。常见做法是把新场景的少量数据(30~50 张)做人工标注,和原数据集混合做二次微调,比完全重训的成本低得多。我在类似项目里试过,30 张新标注能把掉点拉回一半以上;而且迁移后的模型对边缘、遮挡的表现往往比原模型更稳,因为新样本引入了原数据集缺失的形态变化。

这个方向值不值得投入?如果业务需要做产量预估或采摘规划,像素级掩码提供的果实大小、重叠度信息是检测框给不了的。建议从这份数据集的基线模型起步,跑通全流程后把精力优先放在标注质量审查和数据划分上,这两个环节的回报率永远比调模型结构高。以上是我自己的习惯,每次拿到新数据集都是这套流程,希望对你有帮助。

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

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

影刀RPA流程并行执行:同时跑多个任务提升效率

影刀RPA流程并行执行&#xff1a;同时跑多个任务提升效率 作者&#xff1a;林焱默认情况下影刀流程是顺序执行的——一个任务跑完才跑下一个。如果有10个独立任务&#xff0c;一个个排队跑&#xff0c;总时间就是10个任务时间之和。 但如果这10个任务相互独立&#xff0c;完全可…

作者头像 李华
网站建设 2026/10/11 20:23:01

DB2临时表空间告急排查与调优实战指南

简介&#xff1a;这份资源是面向DB2数据库运维DBA与性能调优人员的真实案例文档&#xff0c;聚焦银行DB2系统因临时表空间TEMPSPACE1异常膨胀至10GB而引发的SQL执行变慢问题。资源包内含1个doc文件&#xff0c;约607KB&#xff0c;完整记录了从ACTIVE SESSION异常升高入手&…

作者头像 李华
网站建设 2026/10/11 20:21:51

ANSYS有限元分析从入门到实战:Workbench与APDL核心全解

简介&#xff1a;这是一份面向机械、航空航天、汽车等工业领域工程师及初学者的ANSYS有限元分析软件入门介绍PPT&#xff0c;系统梳理了这款CAE工具的核心功能、典型应用场景及基本分析流程。内容涵盖结构、热、电磁、流体与耦合场分析等主要模块&#xff0c;并介绍Mechanical、…

作者头像 李华
网站建设 2026/10/11 20:21:14

C# PDF 电子签章签名实战:坐标定位、图层透明度与批量盖章避坑指南

简介&#xff1a;这份资源是面向C#桌面开发者的PDF电子签章实践示例&#xff0c;基于WinForm框架并集成DevExpress控件库&#xff0c;用于解决在业务系统中为PDF文档添加合法电子签章、验证文档完整性与签署身份的问题。压缩包为rar格式&#xff0c;整体约68.81MB&#xff0c;包…

作者头像 李华
网站建设 2026/10/11 20:20:34

SRGAN图像超分辨率实战:从原理到TensorFlow训练避坑指南

简介&#xff1a;一套基于TensorFlow2.5与Keras实现的SRGAN超分辨率生成对抗网络项目&#xff0c;面向深度学习研究者与图像处理开发者&#xff0c;用于从低分辨率图像生成高分辨率图像&#xff0c;支持自定义数据集训练以提升特定场景的图像细节与真实感。资源包共17个文件&am…

作者头像 李华