基于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
相关产品推荐
相关产品推荐

