基于K-Means的图像分割区域降噪优化方法咨询
解决K-Means图像分割后的噪声与边缘细节问题
针对你用K-Means做图像调色板简化时出现的分割噪声、边缘/文字细节丢失问题,以下是几个针对性的改进方案,兼顾去噪与细节保留:
一、替换预处理滤波:用双边滤波替代中值滤波
中值滤波是全局均匀模糊,会损失边缘细节。双边滤波能在去除小噪声的同时保留图像边缘,更适合分割前的预处理:
def segment_image(image_path): data = cv2.imread(image_path) o_image = cv2.cvtColor(data, cv2.COLOR_BGR2RGB) # 替换原medianBlur为双边滤波 image = cv2.bilateralFilter(o_image, d=9, sigmaColor=75, sigmaSpace=75) arr = image.reshape((-1, 3)) values = np.float32(arr) k = elbow(arr) print('Using {} clusters.'.format(k)) kmeans = KMeans(n_clusters = k, random_state=11, init = 'k-means++', n_init='auto').fit(arr) labels = kmeans.labels_.flatten() centers = np.uint8(kmeans.cluster_centers_) less_colors = centers[labels.flatten()].reshape(data.shape) cv2.imshow('Segmented Result', less_colors) cv2.waitKey(0) cv2.destroyAllWindows()
- 参数说明:
d是滤波核直径,sigmaColor控制颜色相似度权重,sigmaSpace控制空间距离权重,可根据你的图像调整(细节较多时减小d)。
二、对分割结果做形态学后处理
针对K-Means输出的标签掩码,用形态学操作去除小噪声点、填补区域空洞,且不会过度模糊边缘:
# 假设你已经得到K-Means的labels和原始图像o_image # 1. 提取目标区域的掩码(比如柠檬对应的标签为label_lemon) mask = (labels.reshape(o_image.shape[:2]) == label_lemon).astype(np.uint8) * 255 # 2. 创建结构元素(椭圆核比矩形更贴合物体边缘) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) # 3. 开运算:先腐蚀去小噪声,再膨胀恢复区域大小 clean_mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 4. 闭运算:填补区域内的小空洞 clean_mask = cv2.morphologyEx(clean_mask, cv2.MORPH_CLOSE, kernel) # 5. 用干净的掩码提取目标区域 segmented_lemon = cv2.bitwise_and(o_image, o_image, mask=clean_mask) cv2.imshow('Clean Lemon Segment', segmented_lemon)
- 可根据噪声大小调整核的尺寸(噪声点较大时用
(5,5)核)。
三、基于超像素的K-Means聚类
先将图像分割为超像素(颜色、纹理相似的像素组),再对超像素的平均颜色做聚类,避免单个像素噪声干扰,同时保留边缘:
from skimage.segmentation import slic from skimage.util import img_as_float from skimage.measure import regionprops def segment_image(image_path): data = cv2.imread(image_path) o_image = cv2.cvtColor(data, cv2.COLOR_BGR2RGB) img_float = img_as_float(o_image) # 生成超像素:n_segments控制超像素数量,compactness平衡空间紧凑度与颜色相似度 segments = slic(img_float, n_segments=200, compactness=10, sigma=1) # 计算每个超像素的平均颜色 region_colors = [] for prop in regionprops(segments): mean_color = np.mean(img_float[prop.coords[:,0], prop.coords[:,1]], axis=0) region_colors.append(mean_color) # 对超像素颜色做K-Means k = elbow(np.array(region_colors)) print('Using {} clusters.'.format(k)) kmeans = KMeans(n_clusters=k, random_state=11, init='k-means++', n_init='auto').fit(region_colors) # 将超像素映射到聚类标签,生成最终图像 segment_labels = kmeans.labels_[segments] centers = np.uint8(kmeans.cluster_centers_ * 255) # skimage转float后颜色值在0-1之间 less_colors = centers[segment_labels].reshape(o_image.shape) cv2.imshow('Superpixel-based Segmentation', less_colors) cv2.waitKey(0) cv2.destroyAllWindows()
四、扩展K-Means的特征空间
仅用RGB颜色特征容易受亮度干扰,加入HSV的色调(H)通道能提升颜色聚类的稳定性,减少因亮度波动产生的噪声:
def segment_image(image_path): data = cv2.imread(image_path) o_image = cv2.cvtColor(data, cv2.COLOR_BGR2RGB) image = cv2.bilateralFilter(o_image, d=9, sigmaColor=75, sigmaSpace=75) # 转换为HSV,提取H通道 hsv_image = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) h_channel = hsv_image[:, :, 0:1] # 取H通道,保持维度一致 # 合并RGB与H通道作为K-Means的输入特征 feature_array = np.concatenate([image, h_channel], axis=2).reshape(-1, 4) feature_array = np.float32(feature_array) k = elbow(feature_array) print('Using {} clusters.'.format(k)) kmeans = KMeans(n_clusters=k, random_state=11, init='k-means++', n_init='auto').fit(feature_array) labels = kmeans.labels_.flatten() # 仅取RGB中心值(H通道用于聚类,最终输出用RGB) centers_rgb = np.uint8(kmeans.cluster_centers_[:, :3]) less_colors = centers_rgb[labels.flatten()].reshape(data.shape) cv2.imshow('RGB+H Segmentation', less_colors) cv2.waitKey(0) cv2.destroyAllWindows()
组合建议
推荐采用「双边滤波预处理 + RGB+H特征K-Means + 形态学后处理」的组合,既能有效抑制噪声,又能最大程度保留边缘和文字细节。
内容的提问来源于stack exchange,提问作者Johnny
相关产品推荐
相关产品推荐

