You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于OpenCV/Python实现餐盘食物图像分割(非深度学习方法)

问题描述

我希望不使用深度学习技术,仅通过OpenCV/Python库将餐盘里的三种食物分割为独立图像。原始图像为一个盛放三种食物的餐盘。

我先尝试对OpenCV读取的BGR格式图像应用掩码:

lower_blue = np.array([10, 50, 100])
upper_blue = np.array([110, 225, 225])
mask = cv2.inRange(orig, lower_blue, upper_blue)

result = cv2.bitwise_and(orig, orig, mask = mask)

#cv2_imshow(result)
plt.imshow(result)

得到的结果是大部分餐盘区域为黑色,仅食物被高亮。随后我将其转换为RGB格式,并尝试提取轮廓:

lowerthresh = np.array([60,60,60])
higherthresh = np.array([250,250,250])
mask = cv2.inRange(convertedimage,lowerthresh,higherthresh)
mask.shape

提取的轮廓图像能看到食物大致轮廓,但无法将三种食物分别提取为独立裁剪图像。

后续我尝试使用HSV色彩空间的色调通道掩码背景:

from skimage.color import rgb2hsv
sample_h= rgb2hsv(rgb)
fig, ax = plt.subplots(1, 3, figsize=(15,5))
ax[0].imshow(sample_h[:,:,0], cmap='hsv')
ax[0].set_title('Hue',fontsize=15)
ax[1].imshow(sample_h[:,:,1], cmap='hsv')
ax[1].set_title('Saturation',fontsize=15)
ax[2].imshow(sample_h[:,:,2], cmap='hsv')
ax[2].set_title('Value',fontsize=15);
plt.show()

对应的H/S/V通道可视化图像中,色调通道能区分部分背景和食物,但后续处理:

fig, ax = plt.subplots(1,3,figsize=(15,5))
im = ax[0].imshow(sample_h[:,:,0],cmap='hsv')
fig.colorbar(im,ax=ax[0])
ax[0].set_title('Hue Graph',fontsize=15)

lower_mask = sample_h[:,:,0] > 0
upper_mask = sample_h[:,:,0] < 0.15
mask = upper_mask*lower_mask

red = rgb[:,:,0]*mask
green = rgb[:,:,1]*mask
blue = rgb[:,:,2]*mask

mask2 = np.dstack((red,green,blue))

ax[1].imshow(mask)
ax[2].imshow(mask2)
ax[1].set_title('Mask',fontsize=15)
ax[2].set_title('Final Image',fontsize=15)
plt.tight_layout()
plt.show()

得到的结果还是无法有效分离三种食物,请问是否有其他可行的解决方案?

可行解决方案

结合色彩分割+轮廓筛选+形态学操作,可以实现三种食物的分离提取,步骤如下:

1. 预处理:精准去除背景

利用HSV色彩空间分离餐盘背景和食物,通过形态学操作消除噪点:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取图像并转换为HSV格式
img = cv2.imread('food_plate.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 定义餐盘背景的HSV范围(根据实际图像微调)
lower_white = np.array([0, 0, 200])
upper_white = np.array([180, 30, 255])

# 创建背景掩码并反转,得到食物区域掩码
bg_mask = cv2.inRange(hsv, lower_white, upper_white)
food_mask = cv2.bitwise_not(bg_mask)

# 形态学操作去除噪点、填补缝隙
kernel = np.ones((5,5), np.uint8)
food_mask = cv2.morphologyEx(food_mask, cv2.MORPH_OPEN, kernel)
food_mask = cv2.morphologyEx(food_mask, cv2.MORPH_CLOSE, kernel)

# 提取食物区域
food_region = cv2.bitwise_and(img, img, mask=food_mask)

2. 提取并筛选食物轮廓

提取食物的外接轮廓,过滤掉小噪点,保留三个主要食物的轮廓:

# 转换为灰度图并提取轮廓
gray = cv2.cvtColor(food_region, cv2.COLOR_BGR2GRAY)
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 筛选轮廓:过滤面积过小的噪点
filtered_contours = []
for cnt in contours:
    area = cv2.contourArea(cnt)
    if area > 1000:  # 阈值根据图像大小调整
        filtered_contours.append(cnt)

# 保留面积最大的3个轮廓(对应三种食物)
filtered_contours = sorted(filtered_contours, key=cv2.contourArea, reverse=True)[:3]

3. 裁剪单个食物图像

对每个轮廓生成独立掩码,裁剪出单种食物的图像:

# 遍历每个轮廓,生成独立食物图像
for i, cnt in enumerate(filtered_contours):
    # 创建单食物掩码
    single_mask = np.zeros_like(food_mask)
    cv2.drawContours(single_mask, [cnt], -1, 255, thickness=cv2.FILLED)
    
    # 提取单食物区域
    single_food = cv2.bitwise_and(img, img, mask=single_mask)
    
    # 获取外接矩形并裁剪
    x, y, w, h = cv2.boundingRect(cnt)
    cropped_food = single_food[y:y+h, x:x+w]
    
    # 保存或显示结果
    cv2.imwrite(f'food_{i+1}.jpg', cropped_food)
    plt.imshow(cv2.cvtColor(cropped_food, cv2.COLOR_BGR2RGB))
    plt.title(f'食物 {i+1}')
    plt.show()

4. 优化方案:色彩聚类(可选)

如果轮廓筛选效果不佳,可使用K-Means聚类对食物区域做色彩分割:

from sklearn.cluster import KMeans

# 提取食物区域的非黑像素
pixels = food_region.reshape(-1, 3)
pixels = pixels[np.all(pixels != 0, axis=1)]

# K-Means聚类(3类对应三种食物)
kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(pixels)

# 生成每个类别的掩码并裁剪
kernel = np.ones((5,5), np.uint8)
for cluster in range(3):
    cluster_mask = np.zeros_like(food_mask)
    idx = np.where(labels == cluster)[0]
    # 标记对应像素位置
    for i in idx:
        y = i // img.shape[1]
        x = i % img.shape[1]
        cluster_mask[y, x] = 255
    # 形态学优化掩码
    cluster_mask = cv2.morphologyEx(cluster_mask, cv2.MORPH_CLOSE, kernel)
    # 裁剪并保存
    cluster_food = cv2.bitwise_and(img, img, mask=cluster_mask)
    x, y, w, h = cv2.boundingRect(cluster_mask)
    cropped = cluster_food[y:y+h, x:x+w]
    cv2.imwrite(f'cluster_food_{cluster+1}.jpg', cropped)

内容的提问来源于stack exchange,提问作者Rjj123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 19:55:18