diff --git a/imagescnan.m b/imagescnan.m new file mode 100644 index 0000000..e88523c --- /dev/null +++ b/imagescnan.m @@ -0,0 +1,477 @@ +function [H,HNAN] = imagescnan(varargin) +%IMAGESCNAN Scale data and display as image with uncolored NaNs. +% +% SYNTAX: +% imagescnan(U) +% imagescnan(U,...,'NanColor',CNAN) +% imagescnan(U,...,'NanMask',MNAN) +% imagescnan(U,...,IOPT) +% imagescnan(X,Y,U,...) +% [H,HNAN] = imagescnan(...); +% +% INPUT: +% U - 2 dimensional N-by-M image or N-by-M-by-3 RGB image. +% X - 2 extrema X-axis data; or the M values; or the N-by-M values +% as obtained from MESHGRID (see DESCRIPTION below). +% DEFAULT: [1 N] +% Y - 2 extrema X-axis data; or the N values; or the N-by-M values +% as obtained from MESHGRID (see DESCRIPTION below). +% DEFAULT: [1 M] +% CNAN - Color for the NaNs elements. May be a char specifier or an [R +% G B] triplet specifying the color. +% DEFAULT: invisible (axes background color) +% MNAN - Elements to be ignored besides not finite values. May be an +% scalar or a logical M-by-N matrix indicating the elements to +% be ignored. +% DEFAULT: [] +% IOPT - IMAGE function normal optional pair arguments like +% ('Parent',H) or/and CLIM like optional last argument as in +% IMAGESC. +% DEFAULT: none +% +% OUTPUT (all optional): +% H - Image handle +% HNAN - Handle of every ignored (NaN) value colored patch. +% +% DESCRIPTION: +% MATLAB function IMAGESC does not work properly with NaNs. This +% programs deals with this problem by including colored patches over +% this elements and maybe others specyfied by the user with MNAN. +% +% Besides, those functions does not work properly with X,Y values +% variable interval, but this functions does it by generating a whole +% new image of several rectangular patches, but whose centers may not +% lay in the specified coordinate (see NOTE below). This functionality +% is experimental and not recommended (see ADDITIONAL NOTES inside this +% program). +% +% In previous release, 2-dim input images were transformed into a +% 3-dim RGB image. This is not used anymore (see ADDITIONAL NOTES +% inside this file). +% +% NOTE: +% * Optional inputs use its DEFAULT value when not given or []. +% * Optional outputs may or not be called. +% * If X is a two element vector, min(X) will be the coordinate of the +% first column and max(X) of the last column. +% * If Y is a two element vector, min(Y) will be the coordinate of the +% first row and max(Y) of the last row. +% * If vector X-axis is decreasing U=fliplr(U) will be used. +% * If vector Y-axis is decreasing U=flipud(U) will be used. +% * When X or Y do not have a constant increasing/decreasing step, the +% vertices of the color rectangules are set in the middle of each +% pair of coordinates. For this reason its center may not lay on the +% specified coordinate, except on the coordinates at the edges where +% it always lays on the center. +% * To get a non-scaled image (IMAGE instead of IMAGESC) use: +% >> H = imagescnan(...); +% >> set(H,'CDataMapping','direct') +% * ADDITIONAL NOTES are included inside this file. +% +% EXAMPLE: +% % Compares with normal IMAGESC: +% N = 100; +% PNaNs = 0.10; +% U = peaks(N); +% U(round(1 + (N^2-1).*rand(N^2*PNaNs,1))) = NaN; % Adds NaNs +% subplot(221), imagesc(U) +% title('With IMAGESC: ugly NaNs') +% subplot(222), imagescnan(U) +% title('With IMAGESCNAN: uncolored NaNs') +% % Compares with SPY: +% subplot(223), spy(isnan(U)) +% title('SPY(isnan(U))') +% subplot(224), imagescnan(isnan(U),'NaNMask',0), axis equal tight +% title('SPY with IMAGESCNAN') +% +% SEE ALSO: +% IMAGE, IMAGESC, COLORBAR, IMREAD, IMWRITE +% and +% CMAPPING, CBFREEZE by Carlos Vargas +% at http://www.mathworks.com/matlabcentral/fileexchange +% +% +% --- +% MFILE: imagescnan.m +% VERSION: 2.1 (Aug 20, 2009) (download) +% MATLAB: 7.7.0.471 (R2008b) +% AUTHOR: Carlos Adrian Vargas Aguilera (MEXICO) +% CONTACT: nubeobscura@hotmail.com + +% ADDITIONAL NOTES: +% * I keep getting a kind of BUG with the edges of the patched NaNs. I +% added two NOTE inside this program that may fix this problem. +% Another way is to convert the intensity matrix U into RGB colors by +% using the CMAPPING function, as used by the first version of this +% program. +% * Besides, if the matrix is too large, sometimes there is an +% undocumented failure while drawing the patch NaNs. Is recommended +% to use U = cmapping(U,[],'k','discrete') instead, and change the +% CLIM to [min(U(:)) max(U(:))]. +% * The use of not homogeneous step interval X,Y axes is not +% recommended because the program tries to put its value in the +% middle of the colored rectangule (as IMAGESC does) and soetimes the +% result may not be what the user wants. So this is for experimental +% use only. + +% REVISIONS: +% 1.0 Released. (Jun 30, 2008) +% 1.1 Fixed bug when CAXIS used. Colorbar freezed colormap. Fixed +% bug in color vector input (Found by Greg King) and now +% accets RGB image as input. (Jul 14, 2008) +% 2.0 Totally rewritten code. Do not converts to RGB anymore. Do not +% freezes the colormap anymore. Do not output any colorbar. New +% X and Y variable steps accepted input. Now uses patches. (Jun +% 08, 2009) +% 2.1 Fixed bug with RGB input. Added a NOTE about the use of +% CMAPPING. (Aug 20, 2009) + +% DISCLAIMER: +% imagescnan.m is provided "as is" without warranty of any kind, under +% the revised BSD license. + +% Copyright (c) 2008,2009 Carlos Adrian Vargas Aguilera + + +% INPUTS CHECK-IN +% ------------------------------------------------------------------------- + +% Initializes: +X = []; +Y = []; +CNAN = []; +MNAN = []; +ha = []; + +% Checks number of inputs: +if nargin<1 + error('CVARGAS:imagescnan:notEnoughInputs',... + 'At least 1 input is required.') +elseif nargout>2 + error('CVARGAS:imagescnan:tooManyOutputs',... + 'At most 2 outputs are allowed.') +end + +% Gets X,Y,U: +if ((nargin==1) || (nargin==2)) + U = varargin{1}; + varargin(1) = []; +else + if (isnumeric(varargin{1}) && isnumeric(varargin{2}) && ... + isnumeric(varargin{3})) + X = varargin{1}; + Y = varargin{2}; + U = varargin{3}; + varargin(1:3) = []; + else + U = varargin{1}; + varargin(1) = []; + end +end + +% Check U: +ndim = ndims(U); +if (ndim==2) + [M,N] = size(U); + O = 1; +elseif (ndim==3) + [M,N,O] = size(U); + if (O~=3) + error('CVARGAS:imagescnan:incorrectRgbImage',... + 'RGB image must be of size M-by-N-by-3.') + end +else + error('CVARGAS:imagescnan:incorrectImageSize',... + 'Image must be 2-dimensional or a 3-dim RGB image.') +end + +% Check X: +aequal = true; % Equal intervals on x-axis? +dX = []; +if isempty(X) + X = [1 N]; +else + if (ndims(X)>2) + error('CVARGAS:imagescnan:incorrectXDims',... + 'X must be a vector or a matrix as a result of MESHGRID.') + end + if any(~isfinite(X(:))) + error('CVARGAS:imagescnan:incorrectXValue',... + 'X elements must be numeric and finite.') + end + [Mx,Nx] = size(X); + if ((Mx*Nx)==2) + if X(2)(eps*max(abs(dX(:)))*1000)) + error('CVARGAS:imagescnan:incorrectXMatrix',... + 'X matrix must be as generated by MESHGRID.') + end + X = X(1,:); + elseif (~any([Mx Nx]==1) && ~((Mx*Nx)==N)) + error('CVARGAS:imagescnan:incorrectXSize',... + 'X must be an scalar or a matrix.') + end + % Forces ascending x-axis: + [X,I] = sort(X(:).'); + for k = 1:O % Fixed bug Aug 2009 + U(:,:,k) = U(:,I,k); + end + clear I + % Checks equal intervals: + dX = diff(X); + if any(abs(dX(1)-dX(2:end))>(eps*max(dX)*1000)) + if aequal + aequal = false; + end + else + X = [X(1) X(end)]; + dX = []; + end + end +end + +% Check Y: +dY = []; +if isempty(Y) + Y = [1 M]; +else + if (ndims(Y)>2) + error('CVARGAS:imagescnan:incorrectYDims',... + 'Y must be a vector or a matrix as a result of MESHGRID.') + end + if any(~isfinite(Y(:))) + error('CVARGAS:imagescnan:incorrectYValue',... + 'Y elements must be numeric and finite.') + end + [My,Ny] = size(Y); + if ((My*Ny)==2) + if Y(2)(eps*max(abs(dY(:)))*1000)) + error('CVARGAS:imagescnan:incorrectYMatrix',... + 'Y matrix must be as generated by MESHGRID.') + end + Y = Y(:,1); + elseif (~any([My Ny]==1) && ~((My*Ny)==M)) + error('CVARGAS:imagescnan:incorrectYSize',... + 'Y must be an scalar or a matrix.') + end + % Forces ascending y-axis: + [Y,I] = sort(Y(:).'); + for k = 1:O % Fixed bug Aug 2009 + U(:,:,k) = U(I,:,k); + end + clear I + % Checks equal intervals: + dY = diff(Y); + if any(abs(dY(1)-dY(2:end))>(eps*max(dY)*1000)) + if aequal + aequal = false; + end + else + Y = [Y(1) Y(end)]; + dY = []; + end + end +end + +% Checks varargin: +ind = []; +Nopt = length(varargin); +for k = 1:Nopt-1 + if (~isempty(varargin{k}) && ischar(varargin{k})) + if strncmpi(varargin{k},'NanColor',4) + CNAN = varargin{k+1}; + ind = [ind k k+1]; + elseif strncmpi(varargin{k},'NanMask',4) + MNAN = varargin{k+1}; + ind = [ind k k+1]; + elseif (strncmpi(varargin{k},'Parent',2) && isempty(CNAN)) + try + CNAN = get(varargin{k+1},'Color'); + ha = varargin{k+1}; + catch + error('CVARGAS:imagescnan:incorrectParentHandle',... + '''Parent'' must be a valid axes handle.') + end + end + end +end +varargin(ind) = []; +Nargin = length(varargin); + +% Check ha: +if isempty(ha) + ha = gca; +end + +% Check CNAN: +if isempty(CNAN) + CNAN = get(ha,'Color'); +elseif ischar(CNAN) + switch lower(CNAN) + case 'y', CNAN = [1 1 0]; + case 'm', CNAN = [1 0 0]; + case 'c', CNAN = [0 1 1]; + case 'r', CNAN = [1 0 0]; + case 'g', CNAN = [0 1 0]; + case 'b', CNAN = [0 0 1]; + case 'w', CNAN = [1 1 1]; + case 'k', CNAN = [0 0 0]; + otherwise + error('CVARGAS:imagescnan:incorrectNancString',... + 'Color string must be a valid color identifier. One of ''ymcrgbwk''.') + end +elseif isnumeric(CNAN) && (length(CNAN)==3) + CNAN = CNAN(:).'; % Forces row vector. +else + error('CVARGAS:imagescnan:incorrectNancInput',... + 'Not recognized CNAN input.') +end + +% Check MNAN: +if isempty(MNAN) + MNAN = any(~isfinite(U),3); +else + if (ndims(MNAN)==2) + [Mm,Nm] = size(MNAN); + if ((Mm*Nm)==1) + MNAN = (any(~isfinite(U),3) | any(U==MNAN,3)); + elseif ((Mm==M) && (Nm==N) && islogical(MNAN)) + MNAN = (any(~isfinite(U),3) | MNAN); + else + error('CVARGAS:imagescnan:incorrectNanmSize',... + 'MNAN must be an scalar or a logical matrix of size M-by-N.') + end + else + error('CVARGAS:imagescnan:incorrectNanmDims',... + 'MNAN must be an scalar or a matrix.') + end +end + +% ------------------------------------------------------------------------- +% MAIN +% ------------------------------------------------------------------------- + +% Generates the image: +if aequal + % IMAGESC way. + H = imagesc(X,Y,U,varargin{:}); + +else + % PATCH way. + + % Check clim: + if (rem(Nargin,2)==1) + clim = varargin{end}; + varargin(end) = []; + if ((length(clim)~=2) || (clim(1)>clim(2))) + error('CVARGAS:imagescnan:incorrectClimInput',... + 'clim must be a 2 element increasing vector.') + end + else + clim = []; + end + + % Generates vertices between coordinates (coordinates may not be at the + % center of these vertices): + if (length(X)~=N) + X = (0:N-1)*((X(2)-X(1))/(N-1)) + X(1); + end + if (length(Y)~=M) + Y = (0:M-1)*((Y(2)-Y(1))/(M-1)) + Y(1); + end + if isempty(dX) + dX = diff(X); + end + if isempty(dY) + dY = diff(Y); + end + [X,Y] = meshgrid([X(1)-dX(1)/2 X+dX([1:N-1 N-1])/2],... + [Y(1)-dY(1)/2 Y+dY([1:M-1 M-1])/2]); + + % Generates faces: + ind = (1:(M+1)*N)'; + ind(M+1:M+1:end) = []; + + % Generates patches: + H = patch(... + 'Vertices' ,[X(:) Y(:)],... + 'Faces' ,[ind ind+1 ind+M+2 ind+M+1],... + 'FaceVertexCData',U(:),... + 'FaceColor' ,'flat',... + 'EdgeColor' ,'none',... % NOTE: Sometimes this is not required. + varargin{:}); + set(ha,... + 'YDir' ,'reverse',... + 'View' ,[0 90],... + 'Box' ,'on',... + 'Layer','top') + axis(ha,'tight') + + % Sets clim: + if ~isempty(clim) + set(ha,'CLim',clim) + else + set(ha,'CLimMode','auto') + end + +end + +% Adds NaNs patches: +if any(MNAN(:)) + if aequal + % dX and dY is constant: + [MNAN,NNAN] = ind2sub([M,N],find(MNAN)); + Nnan = length(MNAN); + dX = (X(2)-X(1))/(N-1)/2; + dY = (Y(2)-Y(1))/(M-1)/2; + HNAN = patch(repmat((X(1)+(NNAN(:)'-1)*(2*dX)),4,1) + ... + (repmat([-1 1 1 -1]'*dX,1,Nnan)),... + repmat((Y(1)+(MNAN(:)'-1)*(2*dY)),4,1) + ... + (repmat([1 1 -1 -1]'*dY,1,Nnan)),... + CNAN,... + 'EdgeColor',CNAN,... 'EdgeColor','none',... + varargin{1:Nargin-rem(Nargin,2)}); + else + % dX and/or dY is not constant: + MNAN = find(MNAN); + HNAN = patch(... + 'Vertices' ,[X(:) Y(:)],... + 'Faces' ,[ind(MNAN) ind(MNAN)+1 ind(MNAN)+M+2 ind(MNAN)+M+1],... + 'FaceColor' ,CNAN,... + 'EdgeColor' ,'none',... 'EdgeColor',CNAN,... % NOTE: may be better? + varargin{:}); + end +else + HNAN = []; +end + + +% OUTPUTS CHECK-OUT +% ------------------------------------------------------------------------- + +% Clears outputs?: +if (nargout==0) + clear H +end + + +% [EOF] imagescnan.m \ No newline at end of file