最近有一个数字图像处理的实验课,我们组准备选一个有关图像分割的题目。这里把我们组的一些思路及首先记录下。
首先自然是matlab的安装了,网上有很多的安装包,但是大部分都是百度网盘的,我超级不喜欢百度网盘。所以我直接给一个matlab7.0浏览器下载的链接:
https://dl.pconline.com.cn/download/360588.html
解压安装后,对于window10操作系统,请右键Matlab.exe文件,选择属性,兼容性,以兼容模式运行该程序:
Matlab7.0是比较老的版本了,没有自动补全等功能,如果是长期使用还是推荐取网上下载新版本。
一种简单的方法是使用颜色的数值不同进行划分,颜色相近的点分到一张图片里面,那么我们直接用RGB三通道划分吗?自然可以,但是我们先尽可能考虑简单情况吧(你也可以说我太菜 / W \)
三维的划分不容易,一维的容易不少,我们可以使用灰度图,然后弄出它的直方图再说。
不过可以注意到,我们要划分的图像,大都颜色近似。那么可以用一种可能更好的方式,这里我们就要介绍HSV色彩空间了。它如下图所示:
这个模型中颜色的参数分别是:色调(H),饱和度(S),亮度(V)。
H用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°, 蓝色为240°。它们的补色是:黄色为60°,青色为180°, 品红为300°;
因为HSV非常符合人的观察模式,S和V通道对颜色的归类影响不太大,所以可以直接用H通道。
因此我们将原本的灰度图的直方图变成H通道的直方图。
这里我们先实现一个函数(请无视函数名),传入参数为图像I,
第一步、读取图片,得到h通道。
第二步、绘制HSV彩带方便对比查看。(可选)
第三步、计算频率,准备绘制直方图。
第四步、特殊处理,作用就是放大一些凸点。(可选)
function [hsv,res]=Untitled(I)
% 1.读取图片并转换为HSV图像模型
hsv = rgb2hsv(I);%RGB转hsv
h = hsv(:,:,1);
h = h(:)*360; % 因为h通道为0-1,所以乘360
% 2.画hsv颜色分量
for i=1:360
tempx = 1-abs(mod(i/60,2)-1);
if i<60
plot(i,0,'*','color',[1 tempx 0],'MarkerSize',20);
elseif i<120
plot(i,0,'*','color',[tempx 1 0],'MarkerSize',20);
elseif i<180
plot(i,0,'*','color',[0 1 tempx],'MarkerSize',20);
elseif i<240
plot(i,0,'*','color',[0 tempx 1],'MarkerSize',20);
elseif i<300
plot(i,0,'*','color',[tempx 0 1],'MarkerSize',20);
else
plot(i,0,'*','color',[1 0 tempx],'MarkerSize',20);
end
hold all;
end
% 3.画hsv频率曲线
res = linspace(0,0,361);
for i=1:length(h)
if res(round(h(i))+1) < 10000 % 限制一下
res(round(h(i))+1) = res(round(h(i))+1) + 1;
end
end
% 4.特殊处理,放大odd像素
for i=2:360
if res(i) > res(i+1)+700 && res(i) > res(i-1)+700
res(i) = 5*res(i);
if res(i) > 10000
res(i) = 10000;
end
end
end
plot(res,'LineWidth', 2);
grid on;
记得文件名和函数名一致,然后我们来测试一下。
在command window输入下面的代码,选择图片,然后调用函数。
[fn,pn,fi]=uigetfile({'*.jpg;*.tif;*.png;*.gif;*.bmp','All Image Files';...
'*.*','All Files' },'mytitle',...
'C:\Work\myfile.jpg');
I = imread([pn fn]);
[hsv,res]=Untitled(I);
可以从直方图看到,明显看到这一张照片的绿色和蓝色还有红色比较多。我输入的图片如下:
之前的特殊处理的用处就是对于像蓝色这样的像素点比绿色少很多,所以对这样的像素放大。
这样我们就得到了一张直方图,怎么分呢?通过人眼,应该已经可以大致划几条竖线分割了吧。
比如这样(实际情况会有区别,虽然有够难看的),分成四部分,那么用matlab怎么实现呢?
一个简单的方法就是画一条横线,把超过这条横线的区间记录下来,根据这个区间进行划分。
我们再定义一个函数,传入上面函数得到的hsv,res以及一个阈值flag,我们那一条横线的值就是flag了。
第一步、根据设定的flag分类,获取区间传入res2中,就是计算那些我画的小红点了。
第二步、就是对区间相距太近的进行合并。
第三步、从左到右计算每一个区间右边界和下一个区间的左边界取中值。
比如:处理前的区间是0 1和30 31,那么就化为0 (1+30)/2和(1+30)/2 31,所以结果如下所示:
res2 =
0 1
30 31
60 61
67 68
85 90
res2 =
0 16
16 46
46 64
64 77
77 360
第四步、根据上面的结果绘制分割图。代码如下:
function []=Step2(hsv,res,flag)
temp_hsv = hsv;
% 1.根据设定的频率系数,分类
plot([0 360],[flag flag],'g--','LineWidth', 2);
% 2,获取区间传入res2中
left = 0;
flag_temp = 0; % 0表示还没有进入区间
res2 = [];
for i=1:length(res)
if res(i) > flag
if flag_temp == 0
left = i;
flag_temp = 1;
end
else
if flag_temp == 1
res2 = [res2;left-1,i-1];
flag_temp = 0;
end
end
end
% 间距小的合并
i = 1;
res3 = [];
while i<=length(res2(:,1))-1
% 小于12的间隔进行合并
temp_left = res2(i,1);
while i<=length(res2(:,1))-1 && res2(i+1,1) - res2(i,2) < 5
i=i+1;
end
res3 = [res3;temp_left,res2(i,2)];
i=i+1;
end
res2 = res3;
% 取中值划分
for i=1:length(res2(:,1))
if i==1
res3(i,1) = 0;
res3(i,2) = round((res2(i,2)+res2(i+1,1))/2);
elseif i==length(res2(:,1))
res3(i,1) = round((res2(i-1,2)+res2(i,1))/2);
res3(i,2) = 360;
else
res3(i,1) = round((res2(i-1,2)+res2(i,1))/2);
res3(i,2) = round((res2(i,2)+res2(i+1,1))/2);
end
end
length(res3(:,1))
res2 = res3;
% 3. 开始分割
figure;
for i=1:length(res2(:,1))
hsv = temp_hsv;
temp_interval = res2(i,:);
sum = 0;
for row=1:length(hsv(:,1,:))
for col=1:length(hsv(1,:,:))
temp_num = round(hsv(row,col,1)*360);
if temp_interval(1) <= temp_num && temp_num <= temp_interval(2)
; % 选中的像素处理
else
hsv(row,col,1) = 0; % 未选中像素处理,白色
hsv(row,col,2) = 0;
hsv(row,col,3) = 1;
end
end
end
NEW = hsv2rgb(hsv);
% subplot(1,length(res2(:,1)),i);
figure(i);
imshow(NEW);
end
结果如下,虽然还是不太理想,但是还阔以:
自己Matlab程序写得少,肯定上面的代码还有matlab相关的简单写法,但是思想是一样的(把Matla活生生用成了C),如果有更好的实现方法欢迎指正。
那有没有更好的方法呢?答案是有的,下一篇文章讲解OpenCv方法实现图像分割,用到的技术是超像素SLIC方法、kmean分类,不要被吓到了,实现可能会难一点,但是超级容易理解。
【OpenCv3】 VS C++ (五):SLIC超像素分割算法:
https://blog.csdn.net/qq_40515692/article/details/102750516