数字图像处理、数学形态学、MATLAB GUI实现简单找茬软件
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
最后,本人初次写博客,不妥之处望指出,谢谢!