一、Matlab安裝及題目
最近有一個數字圖像處理的實驗課,我們組準備選一個有關圖像分割的題目。這裏把我們組的一些思路及首先記錄下。
首先自然是matlab的安裝了,網上有很多的安裝包,但是大部分都是百度網盤的,我超級不喜歡百度網盤。所以我直接給一個matlab7.0瀏覽器下載的鏈接:
https://dl.pconline.com.cn/download/360588.html
解壓安裝後,對於window10操作系統,請右鍵Matlab.exe文件,選擇屬性,兼容性,以兼容模式運行該程序:
Matlab7.0是比較老的版本了,沒有自動補全等功能,如果是長期使用還是推薦取網上下載新版本。
然後題目如下:
二、使用HSV色彩空間進行顏色閾值分割
一種簡單的方法是使用顏色的數值不同進行劃分,顏色相近的點分到一張圖片裏面,那麼我們直接用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
23 46
53 64
66 77
81 360
第四步、根據上面的結果繪製分割圖。代碼如下:
function []=Step2(hsv,res,flag)
temp_hsv = hsv;
% 1.根據設定的頻率係數,分類,獲取區間傳入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
res2(i,1) = 0;
res2(i,2) = round((res2(i,2)+res2(i+1,1))/2);
elseif i==length(res2(:,1))
res2(i,1) = round((res2(i-1,2)+res2(i,1))/2);
res2(i,2) = 360;
else
res2(i,1) = round((res2(i-1,2)+res2(i,1))/2);
res2(i,2) = round((res2(i,2)+res2(i+1,1))/2);
end
end
% 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
結果如下,雖然還是不太理想,但是還闊以:
那有沒有更好的方法呢?答案是有的,下一篇文章講解OpenCv方法實現圖像分割,用到的技術是超像素SLIC方法、kmean分類,不要被嚇到了,實現可能會難一點,但是超級容易理解。