1. 要求
基于图像的灰度直方图,计算分割双峰的阈值,实现灰度图像前景和背景的分离。分离后的图像矩阵中,前景和背景用0和1表示。
2. 显示灰度图像
对于有3通道的RGB图像,需要预先使用rgb2gray函数将其转换为单通道的灰度图像。对于灰度图像,通过给定的算法也可以将其转换成为RGB图像,如
Matlab实现伪彩色处理:灰度图像转换为彩色图像_ 一只博客-
clear, close all
% RGB图像需要使用rgb2gray处理
% I_rgb = imread('peppers.png');
% I = rgb2gray(I_rgb);
% 灰度图像不需要处理
I = imread('cameraman.tif');
figure, imshow(I), title('原图');
3. 绘制灰度直方图
Maltab提供了imhist函数,可以绘制灰度图像的直方图
figure, imhist(I), title('灰度直方图');
可以看到,该图像的灰度直方图大致有两个波峰,分别在15和160附近,表示这个图像的大多数像素点的灰度值都分布在15和160附近。
4. 手工寻找阈值
在两个波峰之间,我们可以手工寻找波谷,发现波谷对应的灰度值为73(或70)
gray_min = 73; % 分割阈值
I(I < gray_min) = 0; % 低于阈值的置0
I(I >= gray_min) = 1; % 大于等于阈值的置1
figure, imshow(I, []), title('前景背景分割图');
5. 自动搜索阈值
5.1 现实问题
当图片数量较少的时候,我们可以通过手工寻找两个波峰之间的波谷的方法,确定一个较合适的阈值,但是当图片数量成百上千时,就需要一个自动寻找阈值的方法。
5.2 搜索算法
这里我们基于一个理想化条件:图像的灰度直方图有且仅有两个波峰,不存在多峰或单峰的情况。
于是,当灰度直方图有且仅有两个波峰时,可以将自动搜索阈值的算法简化成如下几步:
Step 1:获取每个灰度值对应的像素点个数,由此组成数组counts,counts(1)表示灰度值为0的像素点的个数;
Step 2:在counts数组中搜索,确定两个波峰counts(a)和counts(b)以及它们的位置a和b;
Step 3: 在counts中,从a到b搜索最小值counts(c)(对应波谷),将其对应的灰度值c-1作为最佳阈值。
5.3 代码实现
对于Step1,我们可以使用imhist函数来获取每个灰度值对于的像素点个数
[counts, ~] = imhist(I); % 获取每个灰度值对应的像素点个数
对于Step2,单纯搜索counts中的前2个最大值往往无法奏效。对于当前的灰度直方图来说,波峰1附近的峰仍然高于波峰2
于是我想到可以使用maltab自带的函数maxk,在counts中选出前K个最大值,记录下他们对应的最小灰度值和最大灰度值,分别作为搜索的起点search_start和终点search_end。
[~, index] = maxk(counts, 50); % 选取前50个最大值的索引
search_start = min(index); % 搜索起点
search_end = max(index); % 搜索终点
为了更加方便快速的获取最佳阈值,我选择在不改动counts数组大小的基础上搜索最小值。
% 生成无效范围
index_range = [1:search_start search_end:256];
counts(index_range) = inf; % 将无效范围的灰度值数量设置为最大,即inf
[~, index_min] = min(counts); % 搜索最小counts对应的索引
gray_min = index_min - 1; % 索引-1即为灰度值(分割阈值)
在counts数组中,只需要将范围以外的其他数设置为无穷大,接着在counts数组中寻找最小值对应的索引即可。matlab中正无穷大的符号是INF,min函数可以返回数组中的最小值和其对应的索引,这里我们不需要最小值(该灰度值对应的像素点个数),所以用~来表示不需要返回最小值。
I(I < gray_min) = 0; % 低于阈值的置0
I(I >= gray_min) = 1; % 大于等于阈值的置1
figure, imshow(I, []), title('前景背景分割图');
5.4 稳定性检验
为了检验这个自动搜索阈值的算法是否具有普适性,我将图像更换为了matlab自带的彩色辣椒图,并将其转换为灰度图,分割结果如下。