去年在开发健康管理App时,我遇到一个棘手问题:用户上传的饮食照片往往包含多种菜品混合,传统图像识别技术只能识别单一主体,导致营养分析误差率高达60%。这促使我研发了一套细颗粒度多菜品识别系统,实测将混合餐食的营养计算准确率提升到89%。
这套系统的核心突破在于三点:首先,采用改进的YOLOv8模型实现像素级菜品分割;其次,建立包含1200种中式菜品的3D营养数据库;最后,独创了基于视觉体积估算的份量算法。现在用户只需拍摄一张餐桌照片,系统就能自动识别出宫保鸡丁、麻婆豆腐、米饭等所有可见食物,并生成完整的蛋白质、碳水、脂肪等16项营养数据。
基础模型选用YOLOv8n-cls版本进行改造,主要优化包括:
python复制# 模型结构关键修改示例
class DualHead(nn.Module):
def __init__(self, nc=80):
super().__init__()
self.cls_head = nn.Sequential(
nn.Conv2d(256, 512, 3, padding=1),
nn.ReLU(),
nn.Conv2d(512, nc, 1))
self.seg_head = nn.Sequential(
nn.Conv2d(256, 128, 3, padding=1),
nn.Upsample(scale_factor=2),
nn.Conv2d(128, 1, 1))
为解决市面营养数据不适用于中餐的问题,我们通过三种途径建立数据库:
数据库采用树形结构存储,例如:
code复制川菜 -> 宫保鸡丁 ->
主料: 鸡胸肉(200g)、花生(50g)
辅料: 干辣椒(10g)、花椒(5g)
营养成分:
热量: 285kcal
蛋白质: 23.4g
碳水: 12.7g
...
传统方案依赖参照物(如硬币)进行比例换算,误差较大。我们开发了立体视觉算法:
python复制def calculate_volume(mask, plate_diameter):
# mask: 菜品分割二值图
# plate_diameter: 识别出的餐盘直径(cm)
area_pixels = np.sum(mask)
px2cm = plate_diameter / plate_diameter_pixels
base_area = area_pixels * (px2cm**2)
# 阴影分析获取高度
height = shadow_analysis(mask)
# 根据菜品类型选择密度
density = get_density(dish_class)
return base_area * height * density
采用成分叠加法处理混合菜品:
关键点:对于无法识别的菜品,系统会标记"未知成分"并基于视觉特征估算宏观营养,避免完全丢失数据。
实测发现餐厅暖光会导致颜色失真,我们开发了动态白平衡:
python复制def auto_white_balance(img):
gray_world = np.mean(img, axis=(0,1))
scale = gray_world.max() / gray_world
balanced = img * scale[None,None,:]
return np.clip(balanced, 0, 255).astype('uint8')
在iPhone 13上实测发现原生模型推理需1200ms,通过以下优化降至280ms:
上线后持续收集用户反馈构建数据飞轮:
测试图片:包含米饭、红烧肉、清炒西兰花的餐盘
处理流程:
误差分析:与人工称重对比,份量估算误差±15%,营养数据误差±8%(主要来自酱汁成分不确定)
除健康管理外,该系统还可用于:
当前限制:对完全混合的炒饭类识别率仅65%,下一步计划引入红外摄像头获取深度信息。实际部署中发现,在沙县小吃这类高反光环境中,识别准确率会下降约20%,临时解决方案是提示用户调整拍摄角度。