function histdemo(action, varargin) %HISTDEMO - It demonstrates histogram mapping experiments % % % % Author: J.Yang 4/99 % Copyright: Professor Jan Allebach at Purdue ECE % $ Revision: 1.01 $ $ Date : 5/10/99 %-------<< Sub functions >>--------- % 1. init % 2. Open % 3. Popup % 3-1. Active load image % 4. close % 5. MapBtn % 5-1. stretch % 5-2. equalize % 5-3. reset_hist % 6. EditBtn % 6-1. histmap_add % 6-2. histmap_drop % 6-3. histmap_move % 7. disp_new, disp_orig % 8. disp_hist % 9. show_value %----------------------------------- %{ if nargin < 1 action = 'init' ; end ; feval(action,varargin{:}) ; return ; %} function init() %{{ % Parameter sharing by global cell array % 'Gparm' = [ ... ] ; % If demo is already running, bring it to the foreground. h = findobj(allchild(0), 'tag', 'Hist convert'); if ~isempty(h) figure(h(1)) return end global GParm ; %------------------------------------------ % Set up for screen frame and attributes % FIGURE GParm.figure = figure('Color',[0.7 0.7 0.7], ... 'MenuBar','none', ... 'Colormap',gray(256), ... 'Name','Histogram convert Demo', ... 'Visible','on', 'Resize','off', ... % initially not drawn 'NumberTitle','off', ... 'ShareColor','off', ... 'RendererMode','manual','Renderer','painters', ... 'Units','pixels', ... 'Position',[5 5 790 595], ... 'Pointer','watch', ... 'Tag','Hist convert'); xdim = 256 ; ydim = 256; xs1 = 8 ; xs2 = 261 + xs1 ; xs3 = xs2 + 261 ; yb = 20 ; yu = yb + ydim + 39 ; %----------------- % button layout % BUTTON posbox = [xs1 yb 200 100] ; h = uicontrol('Style','frame', ... 'Parent',GParm.figure, ... 'Background',[.45 .45 .45], ... 'Units','pixels', ... 'Position',posbox) ; %% File radio button posbox = [xs1+50 yb+70 100 20] ; GParm.open = uicontrol( ... 'Parent',GParm.figure, ... 'Style','Pushbutton', ... 'Units','pixel', ... 'BusyAction','queue','Interruptible','off', ... 'Position',posbox, ... 'String','Open File', ... 'Callback','histdemo(''Open'');'... ) ; %% Image list button posbox = [xs1+50 yb+40 100 20] ; GParm.popup = uicontrol( ... 'Parent',GParm.figure, ... 'Style','popupmenu', ... 'Units','pixel', ... 'BusyAction','queue','Interruptible','off', ... 'Position',posbox, ... 'value',2, ... 'String','Blur|Dark|House|Narrow|Race|Yacht' , ... 'Callback','histdemo(''Popup'');' ... ) ; %% Close button posbox = [xs1+50 yb+10 100 20] ; GParm.close = uicontrol( ... 'Parent',GParm.figure, ... 'Style','Pushbutton', ... 'BusyAction','queue','Interruptible','off', ... 'Units','pixel', ... 'String','Close', ... 'Position',posbox, ... 'Callback','histdemo(''Close'');' ... ) ; %% histogram manuipulation button GParm.histMapText = uicontrol(... 'Parent',GParm.figure, ... 'Style','text', ... 'Units','pixels', ... 'Position',[xs3+4 yu-29 40 20], ... 'Clipping','off', ... 'String','MAP ' ... ) ; posbox = [xs3+42 yu-25 83 20] ; GParm.histMapBtn = uicontrol(... 'Parent',GParm.figure, ... 'Style','popupmenu', ... 'BusyAction','queue','Interruptible','off', ... 'Units','pixel', ... 'String','Invert|Normal|Equalize|Stretch|Reset', ... 'value',5, ... 'Position',posbox, ... 'Callback','histdemo(''MapBtn'');' ... ); GParm.histMapText = uicontrol(... 'Parent',GParm.figure, ... 'Style','text', ... 'Units','pixels', ... 'Position',[xs3+132 yu-29 40 20], ... 'String','EDIT' ... ) ; posbox(1) = posbox(1) + 128 ; GParm.histEditBtn = uicontrol(... 'Parent',GParm.figure, ... 'Style','popupmenu', ... 'BusyAction','queue','Interruptible','off', ... 'Units','pixel', ... 'String','Add|Drop|Move|Done', ... 'value',4, ... 'Position',posbox,... 'Callback','histdemo(''EditBtn'');' ... ) ; %% Show value box --- shared by histogram and image value display GParm.padBox = uicontrol( ... 'Parent',GParm.figure, ... 'Style','frame', ... 'Background','k', ... 'Visible','off', ... 'Position',[0 0 100 20]... ) ; GParm.padText = uicontrol( ... 'Parent',GParm.figure, ... 'Units','pixel', ... 'Style','text', ... 'Visible','off', ... 'HitTest','off', ... 'BackgroundColor','w', ... 'Position',[0 0 98 18], ... 'String','', ... 'tag','Pad' ... ) ; %------------------< screen layout >--------------------------------- % To fit 6 image axes into 800x600 screen, allocate 256x256 images % to each axes. % % | new | | % | orig hist | | reconstruct 256 % +------ ---+ +------- % (8,336) (430,336) % +------------------+ | orig hist 64 % | button box | +-------- % | | (430,266) % | | | orig img 256 % +------------------+ +------- % (8,5) (400,5) (430,5) %-------------------------------------------------------------------- % axes, image, surface % AXES % --- orig axes and images --- GParm.origimgAxe = axes( ... 'Parent',GParm.figure, ... 'Units','pixels', ... 'BusyAction','Queue','Interruptible','off',... 'ydir', 'reverse', ... 'Layer', 'top', ... 'Nextplot','add', ... 'Drawmode','fast', ... 'XLim', [-1 xdim+2], ... 'YLim', [-1 ydim+2],... 'CLim', [0 1], ... 'Position',[xs2 yb xdim ydim], ... 'Box','on', ... 'XTick',[],'YTick',[], ... 'Tag','origimgaxes' ... ) ; GParm.origimg = image( ... % sampling grid interactive 'Parent',GParm.origimgAxe, ... 'Visible', 'off', ... 'CData', [], ... 'BusyAction','Queue','Interruptible','off',... 'XData', [1:xdim],... 'YData', [1:ydim],... 'ButtonDownFcn','histdemo(''show_value'',20);', ... 'EraseMode', 'normal' ... ); title('Original image') ; % orig histogram axes and plot GParm.orighistAxe = axes( ... 'Parent',GParm.figure, ... 'Units','pixels', ... 'BusyAction','Queue','Interruptible','off',... 'XLim', [-1 xdim+2], ... 'YLim', [-1 ydim+2],... 'CLim', [0 1], ... 'Position',[xs3 yb xdim ydim], ... 'Drawmode','fast', ... 'Box','on', ... 'Layer','top', ... % show grid line above image 'XGrid','on','YGrid','on', ... 'XTick',[128],'YTick',[10000], ... 'Tag','orighistaxes' ... ); title('Given Histogram','units','pixels','position',[110 257]) ; GParm.origHist = line( ... 'Parent',GParm.orighistAxe, ... 'Visible','off', ... 'Color','blue', ... 'Linewidth',2, ... 'EraseMode','xor', ... 'ButtonDownFcn','histdemo(''show_value'',0);' , ... 'XData',[],'YData',[] ... ); % histogram conversion plot & axes GParm.histmapAxe = axes( ... 'Parent',GParm.figure, ... 'Units','pixels', ... 'BusyAction','Queue','Interruptible','off',... 'XLim', [-.5 xdim-.5], ... 'YLim', [-.5 ydim-.5],... 'CLim', [0 1], ... 'Position',[xs3 yu xdim ydim], ... 'Drawmode','fast', ... 'Box','on', ... 'Layer','top', ... % show grid line above image 'XGrid','on','YGrid','on', ... 'XTick',[128],'YTick',[128], ... 'XTickLabel','','YTickLabel','', ... 'Tag','orighistaxes' ... ); GParm.vertex = [[0;0],[255;255]] ; GParm.histMap = line( ... 'Parent',GParm.histmapAxe, ... 'Visible','on', ... 'Color','blue', ... 'Marker','diamond', ... 'MarkerFaceColor','red', ... 'EraseMode','normal', ... 'XData',[0,255],'YData',[0,255] ... ); %% 'ButtonDownFcn','histdemo(''map_start'');',... title('Mapping Curve') ; % new mapped histogram axes and plot GParm.newhistAxe = axes( ... 'Parent',GParm.figure, ... 'Units','pixels', ... 'BusyAction','Queue','Interruptible','off',... 'xdir', 'reverse', ... 'XLim', [-1 xdim+2], ... 'YLim', [-1 ydim+2],... 'CLim', [0 1], ... 'Position',[xs2 yu xdim ydim], ... 'Drawmode','fast', ... 'Box','on', ... 'Layer','top', ... % show grid line above image 'XGrid','on','YGrid','on', ... 'XTick',[10000],'YTick',[128], ... 'Tag','orighistaxes' ... ); GParm.newHist = line( ... 'Parent',GParm.newhistAxe, ... 'Visible','off', ... 'Color','blue', ... 'Linewidth',2, ... 'EraseMode','xor', ... 'ButtonDownFcn','histdemo(''show_value'',10);', ... 'XData',[],'YData',[] ... ); title('New Histogram') ; % new image with histogram transformed GParm.newimgAxe = axes( ... 'Parent',GParm.figure, ... 'Units','pixels', ... 'BusyAction','Queue','Interruptible','off',... 'ydir', 'reverse', ... 'Layer', 'top', ... 'Nextplot','add', ... 'Drawmode','fast', ... 'XLim', [-1 xdim+2], ... 'YLim', [-1 ydim+2],... 'CLim', [0 1], ... 'Position',[xs1 yu xdim ydim], ... 'Box','on', ... 'XTick',[],'YTick',[], ... 'Tag','origimgaxes' ... ) ; GParm.newimg = image( ... % sampling grid interactive 'Parent',GParm.newimgAxe, ... 'Visible', 'off', ... 'CData', [], ... 'BusyAction','Queue','Interruptible','off',... 'XData', [1:xdim],... 'YData', [1:ydim],... 'ButtonDownFcn', 'histdemo(''show_value'',30);', ... 'EraseMode', 'normal' ... ); title('Transformed image') ; %------------------------------ %GParm.mapstart = 1 ; GParm.veridx = 1 ; GParm.imgData = [] ; GParm.newimgData = [] ; GParm.orighistData = [] ; GParm.newhistData = [] ; GParm.menuval = 1 ; %------------------------------ Popup ; drawnow ; return ; %}} %--- Menu button function ---- function Open() %{{ global GParm ; pos = get(GParm.open,'position') ; x = pos(1) + pos(3) + 10 ; y = pos(2) + pos(4) + 10 ; [fname,path] = uigetfile('*.tif','Open an image file :',x,y) ; if fname ~= 0 fname = strcat(path, fname) ; Action_loadimage(fname) ; disp_orig ; draw_hist(1) ; set(GParm.figure,'pointer','arrow') ; end ; return; %}} function Popup() %{{ global GParm ; h = GParm.popup ; % get handle for popup list button v = get(h,{'value','String'}) ; name = deblank(v{2}(v{1},:)) ; set(GParm.figure,'pointer','watch') ; switch name case 'Blur' load demo2 Blur ; GParm.imgData = Blur ; Blur = [] ; case 'Dark' load demo2 Dark ; GParm.imgData = Dark ; Dark = [] ; case 'House' load demo2 House ; GParm.imgData = House ; House = [] ; case 'Narrow' load demo2 Narrow ; GParm.imgData = Narrow ; Narrow = [] ; case 'Race' load demo2 Race ; GParm.imgData = Race ; Race = [] ; case 'Yacht' load demo2 Yacht ; GParm.imgData = Yacht ; Yacht = [] ; end ; disp_orig ; draw_hist(1) ; set(GParm.figure,'pointer','arrow') ; return ; %}} function Close() %{{ global GParm ; %% close(gcf) ; close(GParm.figure) ; clear global GParm ; return ; %}} %-------------------- sub functions -----------% function disp_orig() %{{{ global GParm ; im = GParm.imgData ; set(GParm.origimg,'CData',double(im),'visible','on') ; draw_hist(0) ; return ; %}}} function disp_new() %{{{ global GParm ; im = GParm.newimgData ; set(GParm.newimg,'CData',double(im),'visible','on' ); drawnow ; return ; %}}} function nd = draw_hist(mode) %{{ draw normalized histogram of orig and mapped image global GParm ; switch mode case 0 x = GParm.imgData ; GParm.newimgData = GParm.imgData ; % just allocate mem for k=0:255 ; d(k+1) = sum(sum( x==uint8(k) )) ; end ; GParm.orighistData = d ; % for show_val max_hist = max(255,max(d(:))) ; set(GParm.orighistAxe,'Ylim',[-0.01 max_hist+10]); %,'yscale','log'); set(GParm.origHist,'Xdata',[0:255], 'YData',d,'color','b','visible','on') ; case 1 ax = GParm.newhistAxe ; h = GParm.newHist; x = [GParm.vertex(1,:)] ; y = [GParm.vertex(2,:)] ; len = length(x) ; d = zeros(1,256) ; ng = zeros(1,256) ; for m = 1:len-1 ; dx = x(m+1) - x(m) ; dy = y(m+1) - y(m) ; sy = y(m) * dx + 0.5; if( x(m) ~= x(m+1) ) for k=x(m)+(m>1)+1:x(m+1)+1 ; % to prevent divide zero [0,255] -> [ 1,256] cnt = sum(sum(GParm.imgData == uint8(k-1) ) ); sy = sy + dy ; ng(k) = min(255,max( floor(sy / dx),0)) ; % new gray value d(ng(k)+1) = d(ng(k)+1) + cnt ; end ; end ; end ; GParm.newimgData = uint8(ng(double(GParm.imgData(:,:))+1)) ; GParm.newhistData = d ; % for show_val max_hist = max(255,max(d(:))) ; set(GParm.newhistAxe,'xlim',[-0.01 max_hist+10]) ; % ,'xscale','log'); set(GParm.newHist,'ydata',[0:255], 'xData',d,'color','b','visible','on') ; disp_new ; end ; return ; %}} %------- histogram map button functions ---------------- function MapBtn() %{{ global GParm ; % get name and value for popup list button v = get(GParm.histMapBtn,{'value','String'}) ; name = deblank(v{2}(v{1},:)) ; switch ( v{1} ) case 5, % Reset reset_hist ; case {1, 2} %Invert,Normal if( v{1} ~= GParm.menuval ) return ; end ; GParm.menuval = 3 - v{1} ; GParm.vertex(2,:) = 255 - GParm.vertex(2,:) ; set(GParm.histMap,'Xdata',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)]) ; draw_hist(1) ; case 3, %Equalize equalize ; case 4, %Stretch stretch ; end ; return ; %}} function stretch() %{{ ignore below 2% occurrence global GParm ; d = GParm.orighistData ; % for show_val max_hist = max(255,max(d(:))) ; k = GParm.menuval ; % holds Invert/Normal status yb = [0 255] ; big = find( d > max_hist * 0.02 ) ; % over than 2 percent GParm.vertex = [ 0 big(1) big(length(big)) 255 ; ... yb(k) yb(k) yb(3-k) yb(3-k)] ; set(GParm.histMap,'XData',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)]) ; draw_hist(1) ; drawnow ; return ; %}} function equalize() %{{ use 'histeq' matlab function global GParm ; set(GParm.figure,'Pointer','watch') ; [GParm.newimgData ng] = histeq(GParm.imgData,64) ; GParm.vertex(1,1:256) = 0:255 ; GParm.vertex(2,1:256) = floor(ng(:)*255+.5) ; set(GParm.histMap,'XData',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)],... 'Erasemode','xor') ; drawnow ; d = zeros(1,256) ; for k=1:256 ; cnt = sum(sum(GParm.newimgData == uint8(k-1) ) ); d(k) = cnt ; end ; GParm.newimgData = uint8(GParm.newimgData) ; GParm.newhistData = d ; % for show_val max_hist = max(255,max(d(:))) ; set(GParm.newhistAxe,'xlim',[-0.01 max_hist+10]) ; % ,'xscale','log'); set(GParm.newHist,'ydata',[0:255], 'xData',d,'color','b','visible','on') ; set(GParm.figure,'Pointer','arrow') ; disp_new ; return ; %}} function reset_hist() %{{ back to normal mapping global GParm ; GParm.menuval = 1 ; set(GParm.histMap,'XData',[0,255],'YData',[0,255]) ; GParm.vertex = [[0;0],[255;255]] ; draw_hist(1) ; drawnow ; return ; %}} %------- histogram Edit button functions ---------------- function EditBtn() %{{ global GParm ; % get name and value for popup list button v = get(GParm.histEditBtn,{'value','String'}) ; name = deblank(v{2}(v{1},:)) ; GParm.editmode = v{1} ; set(GParm.histmapAxe,'ButtonDownFcn','') ; switch ( v{1} ) case 1, % Add set([GParm.histmapAxe GParm.histMap],'ButtonDownFcn','histdemo(''histmap_add'')' ) ; case 2, % Drop set(GParm.histMap,'ButtonDownFcn','histdemo(''histmap_drop'')' ) ; case 3, % Move set(GParm.histMap,'ButtonDownFcn','histdemo(''histmap_move'',0)' ) ; case 4, % Done set(GParm.histMap,'ButtonDownFcn','' ) ; set(GParm.figure,'pointer','watch') ; drawnow ; draw_hist(1) ; disp_new ; set(GParm.figure,'pointer','arrow') ; drawnow ; end ; return ; %}} function histmap_add() %{{{ global GParm ; pt = get(GParm.histmapAxe,'currentpoint') ; pos = floor(pt(1,1:2) + [.5 .5]) ; % insert point and sort by column -- x increasing order a = sort([256 1] * [GParm.vertex,[pos(1,1);pos(1,2)]]) ; GParm.vertex = [floor(a/256) ; mod(a,256)] ; % GParm.vertex = [x1 x2 x3 .. xe ; % y1 y2 y3 .. ye ] ; set(GParm.histMap,'Erasemode','xor', ... 'Xdata',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)]) ; return ; %}}} function histmap_drop() %{{{ global GParm ; pt = get(GParm.histmapAxe,'currentpoint') ; % distance from cursor to vetice. dist = round([GParm.vertex(1,:)-pt(1,1); GParm.vertex(2,:)-pt(1,2)] ) ; dist = sum(dist.^2,1) ; [v idx] = min (dist) ; len = size(GParm.vertex,2) ; % preserve start, end point if( idx == 1 | idx == len ) return ; end ; a = GParm.vertex ; % delete picked vetex GParm.vertex = [ a(:,1:idx-1), a(:,idx+1:len) ]; set(GParm.histMap,'Erasemode','xor', ... 'Xdata',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)]) ; return ; %}}} function histmap_move(mode) %{{{ global GParm ; pt = get(GParm.histmapAxe,'currentpoint') ; x = floor( pt(1,1)+.5) ; y = (pt(1,2)+.5) ; x = min(255,max(x,0)) ; y = min(255,max(y,0)) ; len = size(GParm.vertex,2) ; % column length % check if new vertex added % GParm.vertex = [x1 x2 x3 .. xe ; % y1 y2 y3 .. ye ] ; switch( mode ) case 0, % initial pick % distance from cursor to vetice. dist = round([GParm.vertex(1,:)-x; GParm.vertex(2,:)-y] ) ; dist = sum(dist.^2,1) ; [v idx] = min (dist) ; % preserve start, end point if( idx == 1 | idx == len ) return ; end ; GParm.veridx = idx ; set(GParm.figure,'WindowButtonMotionFcn','histdemo(''histmap_move'',1)' ); set(GParm.figure,'WindowButtonUpFcn','histdemo(''histmap_move'',2)' ); set(GParm.histMap,'Erasemode','xor','XData',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)]) ; set(GParm.figure,'pointer','cross') ; drawnow ; case {1,2} % button and motion % bounding x segment bsx = max(1,GParm.veridx-1) ; bex = min(len,GParm.veridx+1) ; bsx = GParm.vertex(1,bsx) ; bex = GParm.vertex(1,bex) ; GParm.vertex(1,GParm.veridx) = max( bsx, min(x, bex) ) ; GParm.vertex(2,GParm.veridx) = max( 0,min(y,255) ) ; if mode == 2 , % button release a = GParm.vertex ; % eliminate duplicated pts b = [] ; % start from null for i=1:len-1 ; if( abs(a(1,i)-a(1,i+1))>2 | abs(a(2,i)-a(2,i+1))>2 ) b = [b, [a(:,i);]] ; end ; end ; GParm.vertex = [b, [a(:,len);]] ; % append last set(GParm.figure,'WindowButtonMotionFcn','','WindowButtonUpFcn','','pointer','watch') ; set(GParm.histMap,'Erasemode','normal','visible','on') ; set(GParm.figure,'pointer','arrow') ; end ; set(GParm.histMap,'XData',[GParm.vertex(1,:)],'YData',[GParm.vertex(2,:)]) ; drawnow ; end ; return ; %}}} %----------- auxiliary functions --------------------------- function show_value(mode) %{{{ global GParm ; if rem(mode,10) , set([GParm.padBox GParm.padText],'visible','off') ; set(GParm.figure,'WindowButtonUpFcn','') ; drawnow ; return ; end ; pt = get(GParm.figure,'currentpoint') ; if( pt(1,1) > 690 ) pt(1,1) = pt(1,1) - 100 ; end ; if( pt(1,2) > 540 ) pt(1,2) = pt(1,2) - 40 ; end ; pos = [pt(1,1)+2 pt(1,2)+4 98 36] ; pos1 = [pt(1,1)+1 pt(1,2)+3 100 38] ; hax = get(GParm.figure,'currentAxes') ; pt = get(hax,'currentpoint') ; idx = min(255, max(floor( pt(1,1) + .5 ),1) ); idy = min(255, max(floor( pt(1,2) + .5 ),1) ); switch (mode/10) , case 0, % orighist s = sprintf(' gray : %d\n count : %ld ',idx-1, GParm.orighistData(idx)) ; case 1, % newhist s = sprintf(' gray : %d\n count : %ld ',idy-1, GParm.newhistData(idy)) ; case 2, % origimg disp([idx,256-idy]) ; s = sprintf('pixel: (%d,%d)\ngray : %d',idx,256-idy,... double( GParm.imgData(idy,idx)) ); case 3, % newimg s = sprintf('pixel: (%d,%d)\ngray : %d',idx,256-idy,... double( GParm.newimgData(idy,idx)) ); end ; set(GParm.padBox,'Visible','on','position',pos1) ; set(GParm.padText,'visible','on','position',pos,'String',s) ; set(GParm.figure,'WindowButtonUpFcn','histdemo(''show_value'',1);' ) ; drawnow ; return ; %}}} function Action_loadimage(fname) % {{{{ global GParm ; set(GParm.figure,'Pointer','watch') ; drawnow try if exist(fname) % fname ~= [] or '' x = imread(fname) ; else set(GParm.figure,'Pointer','arrow') ; errordlg(LASTERR,'No such File','modal') ; return ; end catch errordlg(LASTERR,'MATLAB Error Message','modal') ; set(GParm.figure,'Pointer','arrow') ; drawnow return ; end % crop center area [xs ys] = size(x) ; if( xs>256 & ys>=256) | (ys>256 & xs>=256) xs = ceil( (xs-255)/2 ) ; ys = ceil( (ys-255)/2 ) ; y = x(xs:xs+255,ys:ys+255) ; x = y ; else if( xs<256 | ys<256 ) errmsg = ['The "' fname '" file has smaller image size than 256 x 256.' ... '\n Image Load operation aborted.'] ; errordlg(errmsg,'Image size error','modal') ; set(GParm.figure,'Pointer','arrow') ; drawnow return ; end ; end ; GParm.imgData = x ; drawnow return ; % }}}} end of Action_loadimage