classdef warholer < handle
%WARHOLER GUI for creating 8-color images by thresholding R, G, B values
% GUI properties
properties
img % Original image (data)
img8 % Thresholded image
fname % Filename for image
handles % GUI control handles
end
methods
% Constructor method (creates the GUI)
function obj = warholer(fname)
% Blank image (size chosen to make nice initial window arrangement)
obj.img = zeros(100,89);
% Make window
obj.handles.fig = figure('Toolbar','none','Menubar','none');
% Make GUI controls
% Note: positions are left as default; everything will be
% adjusted by the adjustsize callback (which is called
% immediately)
% Get background color
bgc = get(obj.handles.fig,'color');
% Make pushbutton to get file (this will become the axes where
% image is displayed, after the file is selected)
obj.handles.ax = uicontrol(obj.handles.fig,...
'units','normalized','style','pushbutton',...
'string','Click to choose image','fontweight','bold',...
'fontsize',10,'callback',@obj.getimg);
% Make slider controls -- currently all disabled
% Add text below sliders (to show current slider value)
obj.handles.rval = uicontrol(obj.handles.fig,...
'units','normalized','style','text','string','128',...
'fontsize',10,'fontweight','bold',...
'backgroundcolor',bgc,'Enable','off');
obj.handles.gval = uicontrol(obj.handles.fig,...
'units','normalized','style','text','string','128',...
'fontsize',10,'fontweight','bold',...
'backgroundcolor',bgc,'Enable','off');
obj.handles.bval = uicontrol(obj.handles.fig,...
'units','normalized','style','text','string','128',...
'fontsize',10,'fontweight','bold',...
'backgroundcolor',bgc,'Enable','off');
% Add static labels above sliders
obj.handles.rtxt = uicontrol(obj.handles.fig,...
'units','normalized','style','text','string','R',...
'fontsize',10,'fontweight','bold',...
'backgroundcolor',bgc,'Enable','off');
obj.handles.gtxt = uicontrol(obj.handles.fig,...
'units','normalized','style','text','string','G',...
'fontsize',10,'fontweight','bold',...
'backgroundcolor',bgc,'Enable','off');
obj.handles.btxt = uicontrol(obj.handles.fig,...
'units','normalized','style','text','string','B',...
'fontsize',10,'fontweight','bold',...
'backgroundcolor',bgc,'Enable','off');
% Make sliders
obj.handles.rs = uicontrol(obj.handles.fig,...
'units','normalized','style','slider','min',0,'max',255,...
'value',128,'SliderStep',[1 10]/255,...
'callback',@obj.changer,'Enable','off');
obj.handles.gs = uicontrol(obj.handles.fig,...
'units','normalized','style','slider','min',0,'max',255,...
'value',128,'SliderStep',[1 10]/255,...
'callback',@obj.changeg,'Enable','off');
obj.handles.bs = uicontrol(obj.handles.fig,...
'units','normalized','style','slider','min',0,'max',255,...
'value',128,'SliderStep',[1 10]/255,...
'callback',@obj.changeb,'Enable','off');
% Add "randomize" button
obj.handles.rnd = uicontrol(obj.handles.fig,...
'units','normalized','style','pushbutton',...
'string','Randomize','fontweight','bold','fontsize',10,...
'callback',@obj.rndm,'Enable','off');
% Was the constructor called with an input (filename)?
if nargin
% If so, use that as the image
obj.fname = fname;
% Use the image
getimg(obj,obj.handles.ax)
else
% If not, leave filename property empty
obj.fname = '';
% Adjust UI control locations
adjustsize(obj)
end
end % constructor
%%%% UTILITIES %%%%
% Set positions of UI window elements
function adjustsize(obj)
% Get dimensions
ar = get(0,'ScreenSize');
is = size(obj.img);
hpos = get(obj.handles.fig,'Position');
sw = 30; % Slider width
% Do clever fiddling to arrange image and sliders nicely
y = sw/hpos(4);
hpos(3) = hpos(3)*ar(4)*is(2)/(ar(3)*is(1));
x = hpos(3)/(hpos(3)+7*sw);
hpos(3) = hpos(3) + 7*sw;
hpos(1) = ar(1) + 0.5*(ar(3)-hpos(3));
sw = sw/hpos(3);
% Adjust window position
set(obj.handles.fig,'Position',hpos)
% Adjust axes position
if isempty(obj.fname)
% No image => "axes" are actually pushbutton
set(obj.handles.ax,'Position',[0.05,0.4,x-0.05,0.2])
else
% Image exists
set(obj.handles.ax,'Position',[0.05,0,x-0.05,1])
end
% Slider dimensions (more or less -- used to position UI
% elements related to the sliders)
sldht = 1-5*y;
sldst = 3*y;
% Set positions of text below sliders (showing current values)
set(obj.handles.rval,'position',[x+sw,sldst,sw,y/2]);
set(obj.handles.gval,'position',[x+3*sw,sldst,sw,y/2]);
set(obj.handles.bval,'position',[x+5*sw,sldst,sw,y/2]);
% Set positions of static text
sldst = 3.5*y + sldht;
set(obj.handles.rtxt,'position',[x+sw,sldst,sw,y/2]);
set(obj.handles.gtxt,'position',[x+3*sw,sldst,sw,y/2]);
set(obj.handles.btxt,'position',[x+5*sw,sldst,sw,y/2]);
% Set positions of sliders
sldst = 3.5*y;
set(obj.handles.rs,'position',[x+sw,sldst,sw,sldht]);
set(obj.handles.gs,'position',[x+3*sw,sldst,sw,sldht]);
set(obj.handles.bs,'position',[x+5*sw,sldst,sw,sldht]);
% Set position of randomize button
set(obj.handles.rnd,'position',[x+2*sw,y,3*sw,y]);
end
%%%% CALLBACKS %%%%
% Get and display image
function getimg(obj,h,~)
% Retrieve current filename (either empty or given when the
% constructor was called)
fname = obj.fname; %#ok<*PROP>
% Empty filename => let user choose file
if isempty(fname)
[fname,pathname] = uigetfile({'*.jpg;*.tif;*.png;*.gif','All Image Files'});
end
% If user pressed "cancel", do nothing.
% Otherwise, load and display image
if ~isequal(fname,0)
% Full path not given if user provides filename when calling the constructor
if exist('pathname','var')
fname = fullfile(pathname, fname);
end
% Now (finally!) we have the filename of the image
obj.fname = fname;
% Read the image from file
obj.img = imread(fname);
% Get the image size
is = size(obj.img);
% Check if image is b/w, rather than color
if numel(is)<3
% Make sure image is 3-D array
obj.img = repmat(obj.img,[1,1,3]);
end
% Do thresholding (with default value)
obj.img8 = (obj.img>=128);
% Delete and replace current axes (current "axes" are
% actually the pushbutton)
x = get(h,'position');
delete(h)
obj.handles.ax = axes('position',x,'Parent',obj.handles.fig);
% Display image in new axes
obj.handles.him = image(obj.img8,'Parent',obj.handles.ax);
axis image
axis off
% Enable all the slider controls & randomize button
set(obj.handles.rval,'Enable','on')
set(obj.handles.gval,'Enable','on')
set(obj.handles.bval,'Enable','on')
set(obj.handles.rtxt,'Enable','on')
set(obj.handles.gtxt,'Enable','on')
set(obj.handles.btxt,'Enable','on')
set(obj.handles.rs,'Enable','on')
set(obj.handles.gs,'Enable','on')
set(obj.handles.bs,'Enable','on')
set(obj.handles.rnd,'Enable','on')
% Resize everything
adjustsize(obj)
end
end
% Change red value slider
function changer(obj,h,~)
% Get the value and round off
val = round(get(h,'value'));
% Set the value back to the rounded value
set(h,'value',val);
% Update the string
set(obj.handles.rval,'string',num2str(val));
% Update the image (red channel)
obj.img8(:,:,1) = (obj.img(:,:,1)>=val);
set(obj.handles.him,'CData',obj.img8)
end
% Change green value slider
function changeg(obj,h,~)
val = round(get(h,'value'));
set(h,'value',val);
set(obj.handles.gval,'string',num2str(val));
% Update the image (green channel)
obj.img8(:,:,2) = (obj.img(:,:,2)>=val);
set(obj.handles.him,'CData',obj.img8)
end
% Change blue value slider
function changeb(obj,h,~)
val = round(get(h,'value'));
set(h,'value',val);
set(obj.handles.bval,'string',num2str(val));
% Update the image (blue channel)
obj.img8(:,:,3) = (obj.img(:,:,3)>=val);
set(obj.handles.him,'CData',obj.img8)
end
% Randomize button
function rndm(obj,~,~)
% Set the sliders to random integer values from 0 to 255
set(obj.handles.rs,'value',randi([0,255]))
set(obj.handles.gs,'value',randi([0,255]))
set(obj.handles.bs,'value',randi([0,255]))
% Call the updating callbacks
changer(obj,obj.handles.rs,[])
changeg(obj,obj.handles.gs,[])
changeb(obj,obj.handles.bs,[])
end
end
end