0、簡介
這個小作品是我的數字圖像處理課程設計,應用基本的圖像處理知識與MATLAB的基本腳本編程,實現一個簡單的找茬軟件。軟件實現過程中使用到的找茬游戲圖片來源有“夢幻找茬”、“圖圖找茬”。如有興趣的人可以親自玩下這兩個游戲。本程序主要目的是練習數字圖像處理的基本知識、數學形態學以及MATLAB語言。由於時間與能力的有限,並沒有做的很好,各位大神小踩,謝謝。
1、所需知識
matlab基本語言;
matlab GUI實現;
數字圖像處理基本知識;
數學形態學基本知識;
2、算法函數實現過程
1)圖像獲取分割
原本的課程設計要求就是要求使用的是游戲界面截圖,使用的圖片就是存在兩邊的圖,兩邊的圖是存在差異區域的,首先要做的就是將圖像簡單進行豎直平分分割。
2)計算圖像偏移裁剪
由於一般的截圖左右兩邊的圖並不能保證對稱,所以我們需要計算利用圖片的25%中心區域來計算左右兩邊圖的偏移量,再使用這偏移量來裁剪使得兩邊的圖片盡可能重合。這樣查找差異區域的時候就不會出現額外因偏移出現的錯誤標定。由於我測試的圖像使用的是800*500的圖,逐個像素點計算,共需要比較的像素點個數大概是800*500*0.25*20=2000 000次,所以一般的電腦都需要3分鍾。
3)三通道不相同區域提取
原本課程設計的要求是用彩色轉灰度后再提取出不同的區域,但是轉換得到的灰度的茬的差異有些存在的不明顯,所以最后我選擇使用紅藍綠三通道來分離出茬的區域。利用原有的顏色差異來分離出茬的區域成效明顯好於灰度圖像提取。我這里是直接使用MATLAB內置有的imabsdiff()函數來直接獲取各個通道不同的區域。然后將各個通道不同的區域相加起來,再使用imopen(),imclose()函數進行開閉運算。實際的圖像看起來是一致的顏色,其實存在有一定的差值,甚至是較大的毛刺,所以必須是使用開閉運算來消除毛刺與噪聲。
4)矩形框標定顯示
為了更明顯的顯示出茬的位置,最后使用一個矩形標定的函數來繪制出茬的區域。首先是使用MATLAB自帶的工具函數bwlabel()獲取茬區域的連通區域,獲得到數據有標定標定好的連通區域與連通區域個數。 再用regionprops()獲取連通區域信息,得到的信息有面積"area"、矩形框位置"boundingbox"、連通區域中心"central"。得到這些數據,就可以很好的使用矩形框定函數框定茬的位置。
5)MATLAB GUI實現
MATLAB的GUI還是比較簡單的,初學者需要注意的就只有
a.初始化函數
b.使用句柄傳參、或者設置全局變量傳參
c.按鍵響應事件,實際就是調用的函數
MATLAB的所有顯示都可以是控件顯示的,如 :set(handles.radiobutton1,'visible','off'); 就是設置單選框的隱藏不顯示,涉及到的參數鍵與值都可以在GUI繪制的界面的屬性中找到。最后一件事情就是MATLAB的GUI實現之后可以打包成可執行文件,但是需要使用的電腦需要安裝編譯電腦上的MCRInstaller.exe程序。所以MATLAB的GUI程序幾乎是不可能脫離MATLAB運行,因為GUI打包幾乎都涉及到MATLAB內置的畫圖函數,所以目的主機必須要擁有MATLAB整個運行庫才可以運行GUI。
3、找茬算法源碼
主文件:
clear close all; %%讀取圖片,並顯示 fileName = 'test14.png'; img = imread(fileName); % figure();imshow(img);title('原圖'); %獲取圖片信息 [image_h,image_w,image_c] = size(img); image_w2 = ceil(image_w/2); %獲取灰度圖像 img_gray = rgb2gray(img); %%對源圖片對半豎直分割 img_M1 = zeros(image_h,image_w2,3); img_M2 = zeros(image_h,image_w2,3); img_M1 = imcrop(img,[0,0,image_w2,image_h]); img_M1 = uint8(img_M1); img_M1_gray = rgb2gray(img_M1); figure(); imshow(img_M1);title('原左圖'); img_M2 = imcrop(img,[image_w2+1,0,image_w2,image_h]); img_M2 = uint8(img_M2); img_M2_gray = rgb2gray(img_M2); figure(); imshow(img_M2);title('原右圖'); %%判斷是否需要一鍵找茬 % yijian_flag = 0; % char = input('是否一鍵找茬,y/n:','s'); % if char=='y' % yijian_flag = 1; % end % num_fine = 0; % %輸入偏移量 % if yijian_flag == 0 % while(~num_fine) % pic = input('請確定要偏移的圖,l/r:','s'); % pic_dx = input('請輸入豎直偏移量(0~8):'); % pic_dy = input('請輸入豎直偏移量(0~8):'); % % if( ((pic=='l')| (pic=='r'))&(pic_dx>=0)& (pic_dx<=8)&(pic_dy>=0)& (pic_dy<=8) ) % num_fine = 1; % else % fprintf('輸入錯誤,請重新輸入!!!\n'); % end % end % else % pic = 'l'; % pic_dx = 0; % pic_dy = 0; % end %針對test01.jpg默認值 pic = 'l'; pic_dx = 0; pic_dy = 0; %%進行圖片偏移 img_M1_O = zeros(image_h+pic_dx,image_w2+pic_dy,3); img_M2_O = zeros(image_h+pic_dx,image_w2+pic_dy,3); img_M1_O = uint8(img_M1_O); img_M2_O = uint8(img_M2_O); img_M1_O(:,:,:) = 255; img_M2_O(:,:,:) = 255; if pic==1 for i = 1:image_h for j = 1:image_w2 img_M1_O(i+pic_dx,j+pic_dy,:) = img_M1(i,j,:); end end for i = 1:image_h for j = 1:image_w2 img_M2_O(i,j,:) = img_M2(i,j,:); end end else for i = 1:image_h for j = 1:image_w2 img_M2_O(i+pic_dx,j+pic_dy,:) = img_M2(i,j,:); end end for i = 1:image_h for j = 1:image_w2 img_M1_O(i,j,:) = img_M1(i,j,:); end end end figure();imshow(img_M1_O);title('左圖偏移后'); figure();imshow(img_M2_O);title('右圖偏移后'); %%計算出左右圖片偏差值 img_M1_gray = rgb2gray(img_M1_O); img_M2_gray = rgb2gray(img_M2_O); % 根據圖片中心鎖定圖片中心 image_h41 = ceil(image_h/4); image_w41 = ceil(image_w2/4); image_h43 = image_h41*3; image_w43 = image_w41*3; differ = 255*200*200; differ = uint32(differ); dx_min = 0; dy_min = 0; tmp = 0; count = 0; count = uint32(count); qiuhe = zeros(600); qiuhe = uint32(qiuhe); z = 1; for dx=-(10+pic_dx):10+pic_dx for dy=-(10+pic_dy):10+pic_dy for i=image_h41:image_h43 for j=image_w41:image_w43 tmp = uint32(abs(img_M1_gray(i,j)-img_M2_gray(i+dx,j+dy))); count = count + tmp; end end qiuhe(z) = count; z = z+1; if count<differ differ = count dx_min = dx dy_min = dy end count = 0; %count計數清零 end end % %針對test01.jpg默認值 % dx_min = 0 % dy_min = 1 %%根據偏移量裁剪左右圖 image_Nh = image_h-abs(dx_min); image_Nw = image_w2-abs(dy_min); img_N1 = zeros(image_Nh,image_Nw); img_N2 = img_N1; %判斷裁剪所需值 if dx_min<=0 dx1 = -dx_min; dx2 = 0; else dx2 = dx_min; dx1 = 0; end if dy_min<=0 dy1 = -dy_min; dy2 = 0; else dy2 = dy_min; dy1 = 0; end img_N1 = zeros(image_Nh,image_Nw,3); img_N2 = img_N1; for i=1:image_Nh for j=1:image_Nw img_N1(i,j,:) = img_M1_O(i+dx1,j+dy1,:); end end img_N1 = uint8(img_N1); figure(); imshow(img_N1);title('裁剪好后的左圖'); for i=1:image_Nh for j=1:image_Nw img_N2(i,j,:) = img_M2_O(i+dx2,j+dy2,:); end end img_N2 = uint8(img_N2); figure(); imshow(img_N2);title('裁剪好后的右圖'); %%通過三個通道分別找茬后再綜合 img_N2_R = img_N2(:,:,1); img_N2_G = img_N2(:,:,2); img_N2_B = img_N2(:,:,3); % figure(); imshow(img_N2_R); title('紅色通道'); img_N1_R = img_N1(:,:,1); img_N1_G = img_N1(:,:,2); img_N1_B = img_N1(:,:,3); % figure(); imshow(img_N2_R); title('紅色通道'); %獲取各個通道的不同區域 img_T_R = imabsdiff(img_N2_R,img_N1_R); T1 = graythresh(img_T_R); img_D_R = im2bw(img_T_R,T1);%Otus閾值進行分割 img_T_G = imabsdiff(img_N2_G,img_N1_G); T2 = graythresh(img_T_G); img_D_G = im2bw(img_T_G,T2); img_T_B = imabsdiff(img_N2_B,img_N1_B); T3 = graythresh(img_T_B); img_D_B = im2bw(img_T_B,T3); img_D = img_D_R + img_D_G +img_D_B; se1=strel('disk',1); se2=strel('disk',3);%disk其實就是一個八邊形 img_N5=imclose(img_D,se2);%經過閉運算 img_N5=imopen(img_D,se1);%經過開運算 K=imclose(img_N5,se2); %獲取到茬所在區域 figure(); imshow(K);title('獲取茬區域塊'); %%繪制標定茬的對比圖 [L, n]=bwlabel(K,8); %獲取茬連通區域 img_reg = regionprops(L,'area','boundingbox'); areas = [img_reg.Area]; rects = cat(1,img_reg.BoundingBox); rects = round(rects); img_L1_T = img_N1; img_L2_T = img_N2; %輸出判定圖框,並在圖框上繪制茬的矩形 %標定左圖茬 for i=1:size(rects,1); [state_L1,img_L1_T] = draw_rect(img_L1_T,[rects(i,2),rects(i,1)],[rects(i,3),rects(i,4)]); end img_L1 = img_L1_T; for i=1:size(rects,1); [state_L2,img_L2_T] = draw_rect(img_L2_T,[rects(i,2),rects(i,1)],[rects(i,3),rects(i,4)]); end img_L2 = img_L2_T; figure(); imshow(img_L1); figure(); imshow(img_L2); % figure('visible','off'); imshow(img_L1_T); % for i=1:size(rects,1); % rectangle('position',rects(i,:),'EdgeColor','r','linewidth',2); % end % frame1=getframe(gcf); % img_L1=frame2im(frame1); % imwrite(img_L1,'L1.png','png');%可以修改保存的格式 % % % figure('visible','off'); imshow(img_L2_T); % for i=1:size(rects,1); % rectangle('position',rects(i,:),'EdgeColor','r','linewidth',2); % end % frame2=getframe(gcf); % img_L2=frame2im(frame2); % imwrite(img_L2,'L2.png','png');%可以修改保存的格式 % figure(); imshow(img_L1); % figure(); imshow(img_L2); if n>0 str = sprintf('有茬,茬個數:%d',n); title(str, 'Color', 'r'); else str = sprintf('沒有茬'); title(str, 'Color', 'g'); end
標定矩形框:
function [state,result]=draw_rect(data,pointAll,windSize,showOrNot) % 函數調用:[state,result]=draw_rect(data,pointAll,windSize,showOrNot) % 函數功能:在圖像畫個長方形框 % 函數輸入:data為原始的大圖,可為灰度圖,可為彩色圖 % pointAll 框的左上角在大圖中的坐標(每行代表一個坐標), % 注意:在圖中的坐標系為第一列為y,第二列為x(很奇怪的) % windSize 框的大小 分別表示長寬 % showOrNot 是否要顯示,默認為顯示出來 % 函數輸出:state -- 表示程序結果狀態 % result - 結果圖像數據 if nargin < 4 showOrNot = 0; end rgb = [255 0 0]; % 邊框顏色 lineSize = 2; % 邊框大小,取1,2,3 windSize(1,1)=windSize(1,1); windSize(1,2) = windSize(1,2); if windSize(1,1) > size(data,1) ||... windSize(1,2) > size(data,2) state = -1; % 說明窗口太大,圖像太小,沒必要獲取 disp('the window size is larger then image...'); return; end result = data; if size(data,3) == 3 for k=1:3 for i=1:size(pointAll,1) %畫邊框順序為:上右下左的原則 result(pointAll(i,1),pointAll(i,2):pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1):pointAll(i,1)+windSize(i,2),pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1)+windSize(i,2),pointAll(i,2):pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1):pointAll(i,1)+windSize(i,2),pointAll(i,2),k) = rgb(1,k); if lineSize == 2 || lineSize == 3 result(pointAll(i,1)+1,pointAll(i,2):pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1):pointAll(i,1)+windSize(i,2),pointAll(i,2)+windSize(i,1)-1,k) = rgb(1,k); result(pointAll(i,1)+windSize(i,2)-1,pointAll(i,2):pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1):pointAll(i,1)+windSize(i,2),pointAll(i,2)+1,k) = rgb(1,k); if lineSize == 3 result(pointAll(i,1)+1,pointAll(i,2):pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1):pointAll(i,1)+windSize(i,2),pointAll(i,2)+windSize(i,1)+1,k) = rgb(1,k); result(pointAll(i,1)+windSize(i,2)+1,pointAll(i,2):pointAll(i,2)+windSize(i,1),k) = rgb(1,k); result(pointAll(i,1):pointAll(i,1)+windSize(i,2),pointAll(i,2)+1,k) = rgb(1,k); end end end end end state = 1; if showOrNot == 1 figure; imshow(result); end %%這個我是直接使用了網上大神寫好的代碼修改一下適應找茬實現 %https://blog.csdn.net/loveaborn/article/details/8545809
由於GUI的代碼、GUI界面以及源文件過大,我無法直接使用博客園分享,所以稍后給出其他方式分享:
最后我推薦一下博客園里面非常好的數字圖像處理的數學形態學處理算法基礎的帖子:
http://www.cnblogs.com/tornadomeet/archive/2012/03/20/2408086.html
最后,本人初次寫博客,不妥之處望指出,謝謝!