📄 ice.m
字号:
function varargout = ice(varargin)
%ICE Interactive Color Editor.
%
% OUT = ICE('Property Name', 'Property Value', ...) transforms an
% image's color components based on interactively specified mapping
% functions. Inputs are Property Name/Property Value pairs:
%
% Name Value
% ------------ -----------------------------------------------
% 'image' An RGB or monochrome input image to be
% transformed by interactively specified
% mappings.
% 'space' The color space of the components to be
% modified. Possible values are 'rgb', 'cmy',
% 'hsi', 'hsv', 'ntsc' (or 'yiq'), 'ycbcr'. When
% omitted, the RGB color space is assumed.
% 'wait' If 'on' (the default), OUT is the mapped input
% image and ICE returns to the calling function
% or workspace when closed. If 'off', OUT is the
% handle of the mapped input image and ICE
% returns immediately.
%
% EXAMPLES:
% ice OR ice('wait', 'off') % Demo user interface
% ice('image', f) % Map RGB or mono image
% ice('image', f, 'space', 'hsv') % Map HSV of RGB image
% g = ice('image', f) % Return mapped image
% g = ice('image', f, 'wait', 'off'); % Return its handle
%
% ICE displays one popup menu selectable mapping function at a
% time. Each image component is mapped by a dedicated curve (e.g.,
% R, G, or B) and then by an all-component curve (e.g., RGB). Each
% curve's control points are depicted as circles that can be moved,
% added, or deleted with a two- or three-button mouse:
%
% Mouse Button Editing Operation
% ------------ -----------------------------------------------
% Left Move control point by pressing and dragging.
% Middle Add and position a control point by pressing
% and dragging. (Optionally Shift-Left)
% Right Delete a control point. (Optionally
% Control-Left)
%
% Checkboxes determine how mapping functions are computed, whether
% the input image and reference pseudo- and full-color bars are
% mapped, and the displayed reference curve information (e.g.,
% PDF):
%
% Checkbox Function
% ------------ -----------------------------------------------
% Smooth Checked for cubic spline (smooth curve)
% interpolation. If unchecked, piecewise linear.
% Clamp Ends Checked to force the starting and ending curve
% slopes in cubic spline interpolation to 0. No
% effect on piecewise linear.
% Show PDF Display probability density function(s) [i.e.,
% histogram(s)] of the image components affected
% by the mapping function.
% Show CDF Display cumulative distributions function(s)
% instead of PDFs.
% <Note: Show PDF/CDF are mutually exclusive.>
% Map Image If checked, image mapping is enabled; else
% not.
% Map Bars If checked, pseudo- and full-color bar mapping
% is enabled; else display the unmapped bars (a
% gray wedge and hue wedge, respectively).
%
% Mapping functions can be initialized via pushbuttons:
%
% Button Function
% ------------ -----------------------------------------------
% Reset Init the currently displayed mapping function
% and uncheck all curve parameters.
% Reset All Initialize all mapping functions.
% Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
% Digital Image Processing Using MATLAB, Prentice-Hall, 2004
% $Revision: 1.5 $ $Date: 2003/11/21 14:16:10 $
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @ice_OpeningFcn, ...
'gui_OutputFcn', @ice_OutputFcn, ...
'gui_LayoutFcn', [], ...
'gui_Callback', []);
if nargin & ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
%-------------------------------------------------------------------%
function ice_OpeningFcn(hObject, eventdata, handles, varargin)
% When ICE is opened, perform basic initialization (e.g., setup
% globals, ...) before it is made visible.
% Set ICE globals to defaults.
handles.updown = 'none'; % Mouse updown state
handles.plotbox = [0 0 1 1]; % Plot area parameters in pixels
handles.set1 = [0 0; 1 1]; % Curve 1 control points
handles.set2 = [0 0; 1 1]; % Curve 2 control points
handles.set3 = [0 0; 1 1]; % Curve 3 control points
handles.set4 = [0 0; 1 1]; % Curve 4 control points
handles.curve = 'set1'; % Structure name of selected curve
handles.cindex = 1; % Index of selected curve
handles.node = 0; % Index of selected control point
handles.below = 1; % Index of node below control point
handles.above = 2; % Index of node above control point
handles.smooth = [0; 0; 0; 0]; % Curve smoothing states
handles.slope = [0; 0; 0; 0]; % Curve end slope control states
handles.cdf = [0; 0; 0; 0]; % Curve CDF states
handles.pdf = [0; 0; 0; 0]; % Curve PDF states
handles.output = []; % Output image handle
handles.df = []; % Input PDFs and CDFs
handles.colortype = 'rgb'; % Input image color space
handles.input = []; % Input image data
handles.imagemap = 1; % Image map enable
handles.barmap = 1; % Bar map enable
handles.graybar = []; % Pseudo (gray) bar image
handles.colorbar = []; % Color (hue) bar image
% Process Property Name/Property Value input argument pairs.
wait = 'on';
if (nargin > 3)
for i = 1:2:(nargin - 3)
if nargin - 3 == i
break;
end
switch lower(varargin{i})
case 'image'
if ndims(varargin{i + 1}) == 3
handles.input = varargin{i + 1};
elseif ndims(varargin{i + 1}) == 2
handles.input = cat(3, varargin{i + 1}, ...
varargin{i + 1}, varargin{i + 1});
end
handles.input = double(handles.input);
inputmax = max(handles.input(:));
if inputmax > 255
handles.input = handles.input / 65535;
elseif inputmax > 1
handles.input = handles.input / 255;
end
case 'space'
handles.colortype = lower(varargin{i + 1});
switch handles.colortype
case 'cmy'
list = {'CMY' 'Cyan' 'Magenta' 'Yellow'};
case {'ntsc', 'yiq'}
list = {'YIQ' 'Luminance' 'Hue' 'Saturation'};
handles.colortype = 'ntsc';
case 'ycbcr'
list = {'YCbCr' 'Luminance' 'Blue' ...
'Difference' 'Red Difference'};
case 'hsv'
list = {'HSV' 'Hue' 'Saturation' 'Value'};
case 'hsi'
list = {'HSI' 'Hue' 'Saturation' 'Intensity'};
otherwise
list = {'RGB' 'Red' 'Green' 'Blue'};
handles.colortype = 'rgb';
end
set(handles.component_popup, 'String', list);
case 'wait'
wait = lower(varargin{i + 1});
end
end
end
% Create pseudo- and full-color mapping bars (grays and hues). Store
% a color space converted 1x128x3 line of each bar for mapping.
xi = 0:1/127:1; x = 0:1/6:1; x = x';
y = [1 1 0 0 0 1 1; 0 1 1 1 0 0 0; 0 0 0 1 1 1 0]';
gb = repmat(xi, [1 1 3]); cb = interp1q(x, y, xi');
cb = reshape(cb, [1 128 3]);
if ~strcmp(handles.colortype, 'rgb')
gb = eval(['rgb2' handles.colortype '(gb)']);
cb = eval(['rgb2' handles.colortype '(cb)']);
end
gb = round(255 * gb); gb = max(0, gb); gb = min(255, gb);
cb = round(255 * cb); cb = max(0, cb); cb = min(255, cb);
handles.graybar = gb; handles.colorbar = cb;
% Do color space transforms, clamp to [0, 255], compute histograms
% and cumulative distribution functions, and create output figure.
if size(handles.input, 1)
if ~strcmp(handles.colortype, 'rgb')
handles.input = eval(['rgb2' handles.colortype ...
'(handles.input)']);
end
handles.input = round(255 * handles.input);
handles.input = max(0, handles.input);
handles.input = min(255, handles.input);
for i = 1:3
color = handles.input(:, :, i);
df = hist(color(:), 0:255);
handles.df = [handles.df; df / max(df(:))];
df = df / sum(df(:)); df = cumsum(df);
handles.df = [handles.df; df];
end
figure; handles.output = gcf;
end
% Compute ICE's screen position and display image/graph.
set(0, 'Units', 'pixels'); ssz = get(0, 'Screensize');
set(handles.ice, 'Units', 'pixels');
uisz = get(handles.ice, 'Position');
if size(handles.input, 1)
fsz = get(handles.output, 'Position');
bc = (fsz(4) - uisz(4)) / 3;
if bc > 0
bc = bc + fsz(2);
else
bc = fsz(2) + fsz(4) - uisz(4) - 10;
end
lc = fsz(1) + (size(handles.input, 2) / 4) + (3 * fsz(3) / 4);
lc = min(lc, ssz(3) - uisz(3) - 10);
set(handles.ice, 'Position', [lc bc 463 391]);
else
bc = round((ssz(4) - uisz(4)) / 2) - 10;
lc = round((ssz(3) - uisz(3)) / 2) - 10;
set(handles.ice, 'Position', [lc bc uisz(3) uisz(4)]);
end
set(handles.ice, 'Units', 'normalized');
graph(handles); render(handles);
% Update handles and make ICE wait before exit if required.
guidata(hObject, handles);
if strcmpi(wait, 'on')
uiwait(handles.ice);
end
%-------------------------------------------------------------------%
function varargout = ice_OutputFcn(hObject, eventdata, handles)
% After ICE is closed, get the image data of the current figure
% for the output. If 'handles' exists, ICE isn抰 closed (there was
% no 'uiwait') so output figure handle.
if max(size(handles)) == 0
figh = get(gcf);
imageh = get(figh.Children);
if max(size(imageh)) > 0
image = get(imageh.Children);
varargout{1} = image.CData;
end
else
varargout{1} = hObject;
end
%-------------------------------------------------------------------%
function ice_WindowButtonDownFcn(hObject, eventdata, handles)
% Start mapping function control point editing. Do move, add, or
% delete for left, middle, and right button mouse clicks ('normal',
% 'extend', and 'alt' cases) over plot area.
set(handles.curve_axes, 'Units', 'pixels');
handles.plotbox = get(handles.curve_axes, 'Position');
set(handles.curve_axes, 'Units', 'normalized');
[inplot, x, y] = cursor(hObject, handles);
if inplot
nodes = getfield(handles, handles.curve);
i = find(x >= nodes(:, 1)); below = max(i);
above = min(below + 1, size(nodes, 1));
if (x - nodes(below, 1)) > (nodes(above, 1) - x)
node = above;
else
node = below;
end
deletednode = 0;
switch get(hObject, 'SelectionType')
case 'normal'
if node == above
above = min(above + 1, size(nodes, 1));
elseif node == below
below = max(below - 1, 1);
end
if node == size(nodes, 1)
below = above;
elseif node == 1
above = below;
end
if x > nodes(above, 1)
x = nodes(above, 1);
elseif x < nodes(below, 1)
x = nodes(below, 1);
end
handles.node = node; handles.updown = 'down';
handles.below = below; handles.above = above;
nodes(node, :) = [x y];
case 'extend'
if ~length(find(nodes(:, 1) == x))
nodes = [nodes(1:below, :); [x y]; nodes(above:end, :)];
handles.node = above; handles.updown = 'down';
handles.below = below; handles.above = above + 1;
end
case 'alt'
if (node ~= 1) & (node ~= size(nodes, 1))
nodes(node, :) = []; deletednode = 1;
end
handles.node = 0;
set(handles.input_text, 'String', '');
set(handles.output_text, 'String', '');
end
handles = setfield(handles, handles.curve, nodes);
guidata(hObject, handles);
graph(handles);
if deletednode
render(handles);
end
end
%-------------------------------------------------------------------%
function ice_WindowButtonMotionFcn(hObject, eventdata, handles)
% Do nothing unless a mouse 'down' event has occurred. If it has,
% modify control point and make new mapping function.
if ~strcmpi(handles.updown, 'down')
return;
end
[inplot, x, y] = cursor(hObject, handles);
if inplot
nodes = getfield(handles, handles.curve);
nudge = handles.smooth(handles.cindex) / 256;
if (handles.node ~= 1) & (handles.node ~= size(nodes, 1))
if x >= nodes(handles.above, 1)
x = nodes(handles.above, 1) - nudge;
elseif x <= nodes(handles.below, 1)
x = nodes(handles.below, 1) + nudge;
end
else
if x > nodes(handles.above, 1)
x = nodes(handles.above, 1);
elseif x < nodes(handles.below, 1)
x = nodes(handles.below, 1);
end
end
nodes(handles.node, :) = [x y];
handles = setfield(handles, handles.curve, nodes);
guidata(hObject, handles);
graph(handles);
end
%-------------------------------------------------------------------%
function ice_WindowButtonUpFcn(hObject, eventdata, handles)
% Terminate ongoing control point move or add operation. Clear
% coordinate text below plot and update display.
update = strcmpi(handles.updown, 'down');
handles.updown = 'up'; handles.node = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -