%SEGMENTGUI GUI to do simple segmentation % OUT = SEGMENTGUI(IN) presents a GUI with a view of IN and % its histogram, and allows the user to select a threshold % for segmentation. OUT will contain the thresholded image % when the user is finished. % % The threshold value can be changed by clicking on the % histogram. This will update the display. The image on % the left shows the grey-value image, the one on the % right the thresholded one. % % In case of a 3D image, it is possible to change the view % by selecting one of the radiobuttons near the top. Also, % you can change the slice by clicking with the left and % right mouse buttons on one of the images. %(c) by Cris Luengo, August 2001 % If you don't want either or to do what they do % now, comment out the section in the function 'KeyPress' that % handles these keys. This section is commented. function out = segmentgui(in) % Handle Callback functions if ischar(in) switch(in) case 'DIP_GetParamList' out = struct('menu','Point',... 'display','Segment GUI',... 'inparams',struct('name', {'in'},... 'description',{'Input image'},... 'type', {'image'},... 'dim_check', {0},... 'range_check',{[]},... 'required', {1},... 'default', {'a'}... ),... 'outparams',struct('name', {'out'},... 'description',{'Output image'},... 'type', {'image'}... )... ); case 'imageclick' ImageClick; case 'histclick' HistogramClick; case 'chngorient' ChangeOrientation; case 'sliceselect' SliceSelect; case 'keypress' KeyPress; otherwise error('Input must be an image!') end return end % Parse input image if ~isa(in,'dip_image') in = dip_image(in); end in = squeeze(in); if ndims(in)<2 | ndims(in)>3 error('Only 2D and 3D images supported.') end if isa(dip_array(in),'double') in = dip_image(in,'sfloat'); end % Create figure window figh = figure; dtu = get(0,'units'); set(0,'units','pixels'); dts = get(0,'ScreenSize'); dts = dts(3:4); set(0,'units',dtu); sz = [880,400]; set(figh,'units','pixels','position',[dts-sz-[10,100],sz],... 'colormap',[0,0,0;1,0,0;gray(128)],... 'color',get(0,'defaultuicontrolbackground'),... 'KeyPressFcn','segmentgui(''keypress'')'); % Create image display axhb = axes('parent',figh,'units','pixels',... 'position',[sz(1)-256-20,60,256,256],... 'visible','off','ydir','reverse','DataAspectRatio',[1,1,1]); axhg = axes('parent',figh,'units','pixels',... 'position',[sz(1)-512-40,60,256,256],... 'visible','off','ydir','reverse','DataAspectRatio',[1,1,1]); udata.img = in; imsz = size(in); if ndims(in)==3 udata.orientation = 3; % axis along which we look: 3 = x-y slices. % 1 = y-z slices. udata.slice = 0; imsz(udata.orientation) = []; else udata.orientation = 0; % meaning 2D. end udata.imghb = image('parent',axhb); udata.imghg = image('parent',axhg); set(axhb,'xlim',[0,imsz(1)]+0.5,'ylim',[0,imsz(2)]+0.5); set(axhg,'xlim',[0,imsz(1)]+0.5,'ylim',[0,imsz(2)]+0.5); if ndims(in)==3 set(udata.imghb,'ButtonDownFcn','segmentgui(''imageclick'')'); set(udata.imghg,'ButtonDownFcn','segmentgui(''imageclick'')'); % For 3D display: create buttons uicontrol('parent',figh,'style','text',... 'position',[sz(1)-256-60-100,60+256+5,100,20],... 'horizontalalignment','right',... 'string','slice number: '); udata.slicetxt = uicontrol('parent',figh,'style','edit',... 'position',[sz(1)-256-60,60+256+5,60,20],... 'string',num2str(udata.slice),... 'horizontalalignment','left',... 'callback','segmentgui(''sliceselect'')'); udata.mxslc = uicontrol('parent',figh,'style','text',... 'position',[sz(1)-256,60+256+5,100,20],... 'string',[' (out of ',num2str(size(udata.img,udata.orientation)),')']); uicontrol('parent',figh,'style','radiobutton','string','X-Y',... 'position',[sz(1)-512+130,60+256+30,50,20],'tag','3',... 'callback','segmentgui(''chngorient'')','value',1); uicontrol('parent',figh,'style','radiobutton','string','X-Z',... 'position',[sz(1)-512+130+70,60+256+30,50,20],'tag','2',... 'callback','segmentgui(''chngorient'')','value',0); uicontrol('parent',figh,'style','radiobutton','string','Y-Z',... 'position',[sz(1)-512+130+140,60+256+30,50,20],'tag','1',... 'callback','segmentgui(''chngorient'')','value',0); end % Create histogram display udata.histh = axes('parent',figh,'units','pixels',... 'position',[600-512-40,60,256,256],... 'box','on'); [hist,bins] = diphist(in,[]); line('parent',udata.histh,'xdata',bins,'ydata',hist,... 'ButtonDownFcn','segmentgui(''histclick'')'); yrange = [0,max(hist(2:end-1))]; set(udata.histh,'xlim',bins([1,end]),'ylim',yrange,'ytick',yrange); % I do this because max and min values are too frequent. udata.threshv = IsoDataThreshold(hist,bins); udata.threshh = line('parent',udata.histh,... 'xdata',[udata.threshv,udata.threshv],... 'ydata',yrange,'color',[1,0,0],'EraseMode','xor',... 'ButtonDownFcn','segmentgui(''histclick'')'); set(figh,'ButtonDownFcn','segmentgui(''histclick'')'); set(udata.histh,'ButtonDownFcn','segmentgui(''histclick'')'); % Display image DisplayImage(udata); % Create control buttons uicontrol('parent',figh,'String','Ok',... 'position',[sz(1)-100,20,80,30],... 'Callback','set(gcbf,''tag'',''OK'')'); uicontrol('parent',figh,'String','Cancel',... 'position',[sz(1)-200,20,80,30],... 'Callback','set(gcbf,''tag'',''CANCEL'')'); % Save data into figure window set(figh,'userdata',[]); set(figh,'userdata',udata); % This avoids a very slow update MATLAB sometimes does. % Wait for user to finish waitfor(figh,'tag'); ok = 0; if ishandle(figh) if strcmp(get(figh,'tag'),'OK') ok = 1; udata = get(figh,'userdata'); end delete(figh); end % Perform segmentation if ok out = in>=udata.threshv; else error('Operation cancelled'); end % Done! % Send a new image to the image display (update both) function DisplayImage(udata) switch udata.orientation case 0 data = udata.img; case 1 data = squeeze(udata.img(udata.slice,:,:)); case 2 data = squeeze(udata.img(:,udata.slice,:)); case 3 data = squeeze(udata.img(:,:,udata.slice)); end g = uint8(stretch(data,0,100,129,2)); b = uint8(data >= udata.threshv); set(udata.imghg,'cdata',g); set(udata.imghb,'cdata',b); % Send a new image to the binary image display function DisplayBinaryImage(udata) switch udata.orientation case 0 data = udata.img; case 1 data = squeeze(udata.img(udata.slice,:,:)); case 2 data = squeeze(udata.img(:,udata.slice,:)); case 3 data = squeeze(udata.img(:,:,udata.slice)); end b = uint8(data >= udata.threshv); set(udata.imghb,'cdata',b); % Callback for clicking on the image function ImageClick figh = gcbf; if ~isempty(figh) udata = get(figh,'userdata'); imsz = size(udata.img); switch get(figh,'selectiontype') case 'normal' % left mouse click if udata.slice>=imsz(udata.orientation)-1 return end udata.slice = udata.slice+1; case 'alt' % right mouse click if udata.slice<=0 return end udata.slice = udata.slice-1; end DisplayImage(udata); set(udata.slicetxt,'string',num2str(udata.slice)); set(figh,'userdata',[]); set(figh,'userdata',udata); % This avoids a very slow update MATLAB sometimes does. end % Callback for pressing a key function KeyPress figh = gcbf; if ~isempty(figh) udata = get(figh,'userdata'); imsz = size(udata.img); switch get(figh,'currentcharacter') case {'n','N'} % next if udata.orientation==0 | udata.slice>=imsz(udata.orientation)-1 return end udata.slice = udata.slice+1; case {'p','P'} % previous if udata.orientation==0 | udata.slice<=0 return end udata.slice = udata.slice-1; %--- Handles escape and enter keys that terminate the dialog box. case 27 % set(gcbf,'tag','CANCEL'); return case 13 % set(gcbf,'tag','OK'); return %--- end DisplayImage(udata); set(udata.slicetxt,'string',num2str(udata.slice)); set(figh,'userdata',[]); set(figh,'userdata',udata); % This avoids a very slow update MATLAB sometimes does. end % Callback for the edit box (selecting a slice number) function SliceSelect figh = gcbf; if ~isempty(figh) udata = get(figh,'userdata'); imsz = size(udata.img); udata.slice = str2num(get(udata.slicetxt,'string')); if udata.slice >= imsz(udata.orientation) udata.slice = imsz(udata.orientation)-1; elseif udata.slice <= 0 udata.slice = 0; end DisplayImage(udata); set(udata.slicetxt,'string',num2str(udata.slice)); set(figh,'userdata',[]); set(figh,'userdata',udata); % This avoids a very slow update MATLAB sometimes does. end % Callback for the orientation checkboxes function ChangeOrientation [box,figh] = gcbo; if ~isempty(figh) set(findobj(figh,'type','uicontrol','style','radiobutton'),'value',0); set(box,'value',1); udata = get(figh,'userdata'); udata.orientation = str2num(get(box,'tag')); imsz = size(udata.img); if udata.slice >= imsz(udata.orientation) udata.slice = imsz(udata.orientation)-1; end imsz(udata.orientation) = []; set(get(udata.imghb,'parent'),'xlim',[0,imsz(1)]+0.5,'ylim',[0,imsz(2)]+0.5); set(get(udata.imghg,'parent'),'xlim',[0,imsz(1)]+0.5,'ylim',[0,imsz(2)]+0.5); DisplayImage(udata); set(udata.slicetxt,'string',num2str(udata.slice)); set(udata.mxslc,'string',[' (out of ',num2str(size(udata.img,udata.orientation)),')']); set(figh,'userdata',[]); set(figh,'userdata',udata); % This avoids a very slow update MATLAB sometimes does. end % Callback for clicking on the histogram function HistogramClick figh = gcbf; if ~isempty(figh) udata = get(figh,'userdata'); pt = get(udata.histh,'CurrentPoint'); xlim = get(udata.histh,'xlim'); if pt(1,1)>=xlim(1) & pt(1,1)<=xlim(2) udata.threshv = pt(1,1); set(udata.threshh,'xdata',[pt(1,1),pt(1,1)]); DisplayBinaryImage(udata); set(figh,'userdata',[]); set(figh,'userdata',udata); % This avoids a very slow update MATLAB sometimes does. end end % Isodata threshold algorithm function threshold_new = IsoDataThreshold(histogram,bins) min_val = bins(1); max_val = bins(end); range = max_val-min_val; interval = bins(2)-bins(1); threshold_old = min_val; threshold_new = min_val + range/2; while (threshold_new - threshold_old < interval/10) mask = (bins < threshold_new); mean1 = sum(bins(mask).*histogram(mask)) ./ sum(histogram(mask)); mask = (bins >= threshold_new); mean2 = sum(bins(mask).*histogram(mask)) ./ sum(histogram(mask)); threshold_old = threshold_new; threshold_new = (mean1 + mean2) ./ 2; end