MATLAB环境下基于隐马尔可夫模型-高斯混合模型-期望最大化的图像分割算法 算法运行环境为MATLAB R2021b,使用期望最大化(EM)算法进行图像分割,并同时依赖于高斯混合模型(GMM)和马尔可夫随机场(MRF)模型。 压缩包=程序+数据+参考。
图像分割总给人一种"道理都懂,实操抓瞎"的体验。传统方法像k-means简单粗暴,但对复杂纹理就歇菜。今天咱们折腾的这个HMM-GMM-EM组合拳,算是把概率建模玩出花来了。别被名字吓到,本质上就是让计算机自己学着怎么把图片切成不同区域。
先看核心代码架构:
matlab
function [labels, mu, sigma] = hmm_gmm_em_seg(img, K, max_iter)
[h, w] = size(img);
data = double(img(:));
% 初始化GMM参数
mu = linspace(min(data), max(data), K)';
sigma = ones(K,1)*var(data)/K;
alpha = ones(K,1)/K;
% MRF参数
beta = 0.5; % 空间平滑系数
neighbors = get_8neighbors(h,w); % 预计算邻域关系
for iter = 1:max_iter
% E步:计算后验概率
[gamma, loglik] = expectation(data, mu, sigma, alpha, beta, neighbors);
% M步:更新参数
[mu, sigma, alpha] = maximization(data, gamma);
fprintf('Iter %d: LogLik=%.2f\n', iter, loglik);
end
[~, labels] = max(gamma, [], 2);
labels = reshape(labels, h, w);
end
这里藏着三个关键点:GMM负责像素强度建模,MRF处理空间连续性,EM算法当粘合剂。初始化时用线性间隔的均值,比随机初始化稳定得多,避免算法开局就翻车。
MRF的实现是魔鬼细节:
matlab
function energy = mrf_energy(gamma, neighbors, beta)
K = size(gamma,2);
energy = zeros(size(gamma));
for k = 1:K
% 计算邻域内同类别的概率和
neighbor_sum = accumarray(neighbors(:), gamma(:,k), [numel(gamma(:,k)),1]);
energy(:,k) = beta * neighbor_sum;
end
energy = exp(energy - logsumexp(energy,2)); % 数值稳定处理
end
这个邻域能量计算相当于给每个像素的类别选择施加"从众压力",beta参数控制着空间平滑力度。实测中beta=0.3~0.7效果较好,太小分割结果碎得像马赛克,太大又会模糊边缘。
看看参数更新怎么玩:
matlab
function [mu_new, sigma_new, alpha_new] = maximization(data, gamma)
K = size(gamma,2);
gamma_sum = sum(gamma,1);
mu_new = (gamma' * data) ./ gamma_sum';
sigma_new = zeros(K,1);
for k = 1:K
diff = data - mu_new(k);
sigma_new(k) = (gamma(:,k)' * (diff.^2)) / gamma_sum(k);
end
alpha_new = gamma_sum' / size(data,1);
end
这里有个坑------协方差矩阵可能奇异的处理没写出来,实践中需要加个epsilon=1e-6的对角项。更新公式看着像加权平均,实际上gamma在这里扮演软分配的角色,比硬分配的k-means温柔多了。
跑个脑部MRI分割试试:
matlab
img = imread('brain_slice.png');
[labels, ~, ~] = hmm_gmm_em_seg(img, 3, 20);
imshow(label2rgb(labels))
20次迭代后,白质、灰质、脑脊液基本能分开。不过注意GMM对初始值敏感,遇到极端情况还是得祭出人工干预大法。
这算法在工业质检中贼好用,比如检测手机屏幕亮点缺陷。但别指望它通吃所有场景------计算复杂度是硬伤,512x512的图迭代20次就得喝杯咖啡等着。下次咱们可以聊聊怎么用多尺度技巧加速,或者上GPU飙车。
