1 |
function [H,HNAN] = imagescnan(varargin) |
2 |
%IMAGESCNAN Scale data and display as image with uncolored NaNs. |
3 |
% |
4 |
% SYNTAX: |
5 |
% imagescnan(U) |
6 |
% imagescnan(U,...,'NanColor',CNAN) |
7 |
% imagescnan(U,...,'NanMask',MNAN) |
8 |
% imagescnan(U,...,IOPT) |
9 |
% imagescnan(X,Y,U,...) |
10 |
% [H,HNAN] = imagescnan(...); |
11 |
% |
12 |
% INPUT: |
13 |
% U - 2 dimensional N-by-M image or N-by-M-by-3 RGB image. |
14 |
% X - 2 extrema X-axis data; or the M values; or the N-by-M values |
15 |
% as obtained from MESHGRID (see DESCRIPTION below). |
16 |
% DEFAULT: [1 N] |
17 |
% Y - 2 extrema X-axis data; or the N values; or the N-by-M values |
18 |
% as obtained from MESHGRID (see DESCRIPTION below). |
19 |
% DEFAULT: [1 M] |
20 |
% CNAN - Color for the NaNs elements. May be a char specifier or an [R |
21 |
% G B] triplet specifying the color. |
22 |
% DEFAULT: invisible (axes background color) |
23 |
% MNAN - Elements to be ignored besides not finite values. May be an |
24 |
% scalar or a logical M-by-N matrix indicating the elements to |
25 |
% be ignored. |
26 |
% DEFAULT: [] |
27 |
% IOPT - IMAGE function normal optional pair arguments like |
28 |
% ('Parent',H) or/and CLIM like optional last argument as in |
29 |
% IMAGESC. |
30 |
% DEFAULT: none |
31 |
% |
32 |
% OUTPUT (all optional): |
33 |
% H - Image handle |
34 |
% HNAN - Handle of every ignored (NaN) value colored patch. |
35 |
% |
36 |
% DESCRIPTION: |
37 |
% MATLAB function IMAGESC does not work properly with NaNs. This |
38 |
% programs deals with this problem by including colored patches over |
39 |
% this elements and maybe others specyfied by the user with MNAN. |
40 |
% |
41 |
% Besides, those functions does not work properly with X,Y values |
42 |
% variable interval, but this functions does it by generating a whole |
43 |
% new image of several rectangular patches, but whose centers may not |
44 |
% lay in the specified coordinate (see NOTE below). This functionality |
45 |
% is experimental and not recommended (see ADDITIONAL NOTES inside this |
46 |
% program). |
47 |
% |
48 |
% In previous release, 2-dim input images were transformed into a |
49 |
% 3-dim RGB image. This is not used anymore (see ADDITIONAL NOTES |
50 |
% inside this file). |
51 |
% |
52 |
% NOTE: |
53 |
% * Optional inputs use its DEFAULT value when not given or []. |
54 |
% * Optional outputs may or not be called. |
55 |
% * If X is a two element vector, min(X) will be the coordinate of the |
56 |
% first column and max(X) of the last column. |
57 |
% * If Y is a two element vector, min(Y) will be the coordinate of the |
58 |
% first row and max(Y) of the last row. |
59 |
% * If vector X-axis is decreasing U=fliplr(U) will be used. |
60 |
% * If vector Y-axis is decreasing U=flipud(U) will be used. |
61 |
% * When X or Y do not have a constant increasing/decreasing step, the |
62 |
% vertices of the color rectangules are set in the middle of each |
63 |
% pair of coordinates. For this reason its center may not lay on the |
64 |
% specified coordinate, except on the coordinates at the edges where |
65 |
% it always lays on the center. |
66 |
% * To get a non-scaled image (IMAGE instead of IMAGESC) use: |
67 |
% >> H = imagescnan(...); |
68 |
% >> set(H,'CDataMapping','direct') |
69 |
% * ADDITIONAL NOTES are included inside this file. |
70 |
% |
71 |
% EXAMPLE: |
72 |
% % Compares with normal IMAGESC: |
73 |
% N = 100; |
74 |
% PNaNs = 0.10; |
75 |
% U = peaks(N); |
76 |
% U(round(1 + (N^2-1).*rand(N^2*PNaNs,1))) = NaN; % Adds NaNs |
77 |
% subplot(221), imagesc(U) |
78 |
% title('With IMAGESC: ugly NaNs') |
79 |
% subplot(222), imagescnan(U) |
80 |
% title('With IMAGESCNAN: uncolored NaNs') |
81 |
% % Compares with SPY: |
82 |
% subplot(223), spy(isnan(U)) |
83 |
% title('SPY(isnan(U))') |
84 |
% subplot(224), imagescnan(isnan(U),'NaNMask',0), axis equal tight |
85 |
% title('SPY with IMAGESCNAN') |
86 |
% |
87 |
% SEE ALSO: |
88 |
% IMAGE, IMAGESC, COLORBAR, IMREAD, IMWRITE |
89 |
% and |
90 |
% CMAPPING, CBFREEZE by Carlos Vargas |
91 |
% at http://www.mathworks.com/matlabcentral/fileexchange |
92 |
% |
93 |
% |
94 |
% --- |
95 |
% MFILE: imagescnan.m |
96 |
% VERSION: 2.1 (Aug 20, 2009) (<a href="matlab:web('http://www.mathworks.com/matlabcentral/fileexchange/authors/11258')">download</a>) |
97 |
% MATLAB: 7.7.0.471 (R2008b) |
98 |
% AUTHOR: Carlos Adrian Vargas Aguilera (MEXICO) |
99 |
% CONTACT: nubeobscura@hotmail.com |
100 |
|
101 |
% ADDITIONAL NOTES: |
102 |
% * I keep getting a kind of BUG with the edges of the patched NaNs. I |
103 |
% added two NOTE inside this program that may fix this problem. |
104 |
% Another way is to convert the intensity matrix U into RGB colors by |
105 |
% using the CMAPPING function, as used by the first version of this |
106 |
% program. |
107 |
% * Besides, if the matrix is too large, sometimes there is an |
108 |
% undocumented failure while drawing the patch NaNs. Is recommended |
109 |
% to use U = cmapping(U,[],'k','discrete') instead, and change the |
110 |
% CLIM to [min(U(:)) max(U(:))]. |
111 |
% * The use of not homogeneous step interval X,Y axes is not |
112 |
% recommended because the program tries to put its value in the |
113 |
% middle of the colored rectangule (as IMAGESC does) and soetimes the |
114 |
% result may not be what the user wants. So this is for experimental |
115 |
% use only. |
116 |
|
117 |
% REVISIONS: |
118 |
% 1.0 Released. (Jun 30, 2008) |
119 |
% 1.1 Fixed bug when CAXIS used. Colorbar freezed colormap. Fixed |
120 |
% bug in color vector input (Found by Greg King) and now |
121 |
% accets RGB image as input. (Jul 14, 2008) |
122 |
% 2.0 Totally rewritten code. Do not converts to RGB anymore. Do not |
123 |
% freezes the colormap anymore. Do not output any colorbar. New |
124 |
% X and Y variable steps accepted input. Now uses patches. (Jun |
125 |
% 08, 2009) |
126 |
% 2.1 Fixed bug with RGB input. Added a NOTE about the use of |
127 |
% CMAPPING. (Aug 20, 2009) |
128 |
|
129 |
% DISCLAIMER: |
130 |
% imagescnan.m is provided "as is" without warranty of any kind, under |
131 |
% the revised BSD license. |
132 |
|
133 |
% Copyright (c) 2008,2009 Carlos Adrian Vargas Aguilera |
134 |
|
135 |
|
136 |
% INPUTS CHECK-IN |
137 |
% ------------------------------------------------------------------------- |
138 |
|
139 |
% Initializes: |
140 |
X = []; |
141 |
Y = []; |
142 |
CNAN = []; |
143 |
MNAN = []; |
144 |
ha = []; |
145 |
|
146 |
% Checks number of inputs: |
147 |
if nargin<1 |
148 |
error('CVARGAS:imagescnan:notEnoughInputs',... |
149 |
'At least 1 input is required.') |
150 |
elseif nargout>2 |
151 |
error('CVARGAS:imagescnan:tooManyOutputs',... |
152 |
'At most 2 outputs are allowed.') |
153 |
end |
154 |
|
155 |
% Gets X,Y,U: |
156 |
if ((nargin==1) || (nargin==2)) |
157 |
U = varargin{1}; |
158 |
varargin(1) = []; |
159 |
else |
160 |
if (isnumeric(varargin{1}) && isnumeric(varargin{2}) && ... |
161 |
isnumeric(varargin{3})) |
162 |
X = varargin{1}; |
163 |
Y = varargin{2}; |
164 |
U = varargin{3}; |
165 |
varargin(1:3) = []; |
166 |
else |
167 |
U = varargin{1}; |
168 |
varargin(1) = []; |
169 |
end |
170 |
end |
171 |
|
172 |
% Check U: |
173 |
ndim = ndims(U); |
174 |
if (ndim==2) |
175 |
[M,N] = size(U); |
176 |
O = 1; |
177 |
elseif (ndim==3) |
178 |
[M,N,O] = size(U); |
179 |
if (O~=3) |
180 |
error('CVARGAS:imagescnan:incorrectRgbImage',... |
181 |
'RGB image must be of size M-by-N-by-3.') |
182 |
end |
183 |
else |
184 |
error('CVARGAS:imagescnan:incorrectImageSize',... |
185 |
'Image must be 2-dimensional or a 3-dim RGB image.') |
186 |
end |
187 |
|
188 |
% Check X: |
189 |
aequal = true; % Equal intervals on x-axis? |
190 |
dX = []; |
191 |
if isempty(X) |
192 |
X = [1 N]; |
193 |
else |
194 |
if (ndims(X)>2) |
195 |
error('CVARGAS:imagescnan:incorrectXDims',... |
196 |
'X must be a vector or a matrix as a result of MESHGRID.') |
197 |
end |
198 |
if any(~isfinite(X(:))) |
199 |
error('CVARGAS:imagescnan:incorrectXValue',... |
200 |
'X elements must be numeric and finite.') |
201 |
end |
202 |
[Mx,Nx] = size(X); |
203 |
if ((Mx*Nx)==2) |
204 |
if X(2)<X(1) |
205 |
X = X([2 1]); |
206 |
for k = 1:O % Fixed bug Aug 2009 |
207 |
U(:,:,k) = fliplr(U(:,:,k)); |
208 |
end |
209 |
end |
210 |
else |
211 |
if ((Mx==M) && (Nx==N)) |
212 |
% Checks if generated with MESHGRID: |
213 |
dX = abs(X(2:M,:)-repmat(X(1,:),M-1,1)); |
214 |
if any(abs(dX(:))>(eps*max(abs(dX(:)))*1000)) |
215 |
error('CVARGAS:imagescnan:incorrectXMatrix',... |
216 |
'X matrix must be as generated by MESHGRID.') |
217 |
end |
218 |
X = X(1,:); |
219 |
elseif (~any([Mx Nx]==1) && ~((Mx*Nx)==N)) |
220 |
error('CVARGAS:imagescnan:incorrectXSize',... |
221 |
'X must be an scalar or a matrix.') |
222 |
end |
223 |
% Forces ascending x-axis: |
224 |
[X,I] = sort(X(:).'); |
225 |
for k = 1:O % Fixed bug Aug 2009 |
226 |
U(:,:,k) = U(:,I,k); |
227 |
end |
228 |
clear I |
229 |
% Checks equal intervals: |
230 |
dX = diff(X); |
231 |
if any(abs(dX(1)-dX(2:end))>(eps*max(dX)*1000)) |
232 |
if aequal |
233 |
aequal = false; |
234 |
end |
235 |
else |
236 |
X = [X(1) X(end)]; |
237 |
dX = []; |
238 |
end |
239 |
end |
240 |
end |
241 |
|
242 |
% Check Y: |
243 |
dY = []; |
244 |
if isempty(Y) |
245 |
Y = [1 M]; |
246 |
else |
247 |
if (ndims(Y)>2) |
248 |
error('CVARGAS:imagescnan:incorrectYDims',... |
249 |
'Y must be a vector or a matrix as a result of MESHGRID.') |
250 |
end |
251 |
if any(~isfinite(Y(:))) |
252 |
error('CVARGAS:imagescnan:incorrectYValue',... |
253 |
'Y elements must be numeric and finite.') |
254 |
end |
255 |
[My,Ny] = size(Y); |
256 |
if ((My*Ny)==2) |
257 |
if Y(2)<Y(1) |
258 |
Y = Y([2 1]); |
259 |
for k = 1:O % Fixed bug Aug 2009 |
260 |
U(:,:,k) = flipud(U(:,:,k)); |
261 |
end |
262 |
end |
263 |
else |
264 |
if ((My==M) && (Ny==N)) |
265 |
% Checks if generated with MESHGRID: |
266 |
dY = abs(Y(:,2:N)-repmat(Y(:,1),1,N-1)); |
267 |
if any(abs(dY(:))>(eps*max(abs(dY(:)))*1000)) |
268 |
error('CVARGAS:imagescnan:incorrectYMatrix',... |
269 |
'Y matrix must be as generated by MESHGRID.') |
270 |
end |
271 |
Y = Y(:,1); |
272 |
elseif (~any([My Ny]==1) && ~((My*Ny)==M)) |
273 |
error('CVARGAS:imagescnan:incorrectYSize',... |
274 |
'Y must be an scalar or a matrix.') |
275 |
end |
276 |
% Forces ascending y-axis: |
277 |
[Y,I] = sort(Y(:).'); |
278 |
for k = 1:O % Fixed bug Aug 2009 |
279 |
U(:,:,k) = U(I,:,k); |
280 |
end |
281 |
clear I |
282 |
% Checks equal intervals: |
283 |
dY = diff(Y); |
284 |
if any(abs(dY(1)-dY(2:end))>(eps*max(dY)*1000)) |
285 |
if aequal |
286 |
aequal = false; |
287 |
end |
288 |
else |
289 |
Y = [Y(1) Y(end)]; |
290 |
dY = []; |
291 |
end |
292 |
end |
293 |
end |
294 |
|
295 |
% Checks varargin: |
296 |
ind = []; |
297 |
Nopt = length(varargin); |
298 |
for k = 1:Nopt-1 |
299 |
if (~isempty(varargin{k}) && ischar(varargin{k})) |
300 |
if strncmpi(varargin{k},'NanColor',4) |
301 |
CNAN = varargin{k+1}; |
302 |
ind = [ind k k+1]; |
303 |
elseif strncmpi(varargin{k},'NanMask',4) |
304 |
MNAN = varargin{k+1}; |
305 |
ind = [ind k k+1]; |
306 |
elseif (strncmpi(varargin{k},'Parent',2) && isempty(CNAN)) |
307 |
try |
308 |
CNAN = get(varargin{k+1},'Color'); |
309 |
ha = varargin{k+1}; |
310 |
catch |
311 |
error('CVARGAS:imagescnan:incorrectParentHandle',... |
312 |
'''Parent'' must be a valid axes handle.') |
313 |
end |
314 |
end |
315 |
end |
316 |
end |
317 |
varargin(ind) = []; |
318 |
Nargin = length(varargin); |
319 |
|
320 |
% Check ha: |
321 |
if isempty(ha) |
322 |
ha = gca; |
323 |
end |
324 |
|
325 |
% Check CNAN: |
326 |
if isempty(CNAN) |
327 |
CNAN = get(ha,'Color'); |
328 |
elseif ischar(CNAN) |
329 |
switch lower(CNAN) |
330 |
case 'y', CNAN = [1 1 0]; |
331 |
case 'm', CNAN = [1 0 0]; |
332 |
case 'c', CNAN = [0 1 1]; |
333 |
case 'r', CNAN = [1 0 0]; |
334 |
case 'g', CNAN = [0 1 0]; |
335 |
case 'b', CNAN = [0 0 1]; |
336 |
case 'w', CNAN = [1 1 1]; |
337 |
case 'k', CNAN = [0 0 0]; |
338 |
otherwise |
339 |
error('CVARGAS:imagescnan:incorrectNancString',... |
340 |
'Color string must be a valid color identifier. One of ''ymcrgbwk''.') |
341 |
end |
342 |
elseif isnumeric(CNAN) && (length(CNAN)==3) |
343 |
CNAN = CNAN(:).'; % Forces row vector. |
344 |
else |
345 |
error('CVARGAS:imagescnan:incorrectNancInput',... |
346 |
'Not recognized CNAN input.') |
347 |
end |
348 |
|
349 |
% Check MNAN: |
350 |
if isempty(MNAN) |
351 |
MNAN = any(~isfinite(U),3); |
352 |
else |
353 |
if (ndims(MNAN)==2) |
354 |
[Mm,Nm] = size(MNAN); |
355 |
if ((Mm*Nm)==1) |
356 |
MNAN = (any(~isfinite(U),3) | any(U==MNAN,3)); |
357 |
elseif ((Mm==M) && (Nm==N) && islogical(MNAN)) |
358 |
MNAN = (any(~isfinite(U),3) | MNAN); |
359 |
else |
360 |
error('CVARGAS:imagescnan:incorrectNanmSize',... |
361 |
'MNAN must be an scalar or a logical matrix of size M-by-N.') |
362 |
end |
363 |
else |
364 |
error('CVARGAS:imagescnan:incorrectNanmDims',... |
365 |
'MNAN must be an scalar or a matrix.') |
366 |
end |
367 |
end |
368 |
|
369 |
% ------------------------------------------------------------------------- |
370 |
% MAIN |
371 |
% ------------------------------------------------------------------------- |
372 |
|
373 |
% Generates the image: |
374 |
if aequal |
375 |
% IMAGESC way. |
376 |
H = imagesc(X,Y,U,varargin{:}); |
377 |
|
378 |
else |
379 |
% PATCH way. |
380 |
|
381 |
% Check clim: |
382 |
if (rem(Nargin,2)==1) |
383 |
clim = varargin{end}; |
384 |
varargin(end) = []; |
385 |
if ((length(clim)~=2) || (clim(1)>clim(2))) |
386 |
error('CVARGAS:imagescnan:incorrectClimInput',... |
387 |
'clim must be a 2 element increasing vector.') |
388 |
end |
389 |
else |
390 |
clim = []; |
391 |
end |
392 |
|
393 |
% Generates vertices between coordinates (coordinates may not be at the |
394 |
% center of these vertices): |
395 |
if (length(X)~=N) |
396 |
X = (0:N-1)*((X(2)-X(1))/(N-1)) + X(1); |
397 |
end |
398 |
if (length(Y)~=M) |
399 |
Y = (0:M-1)*((Y(2)-Y(1))/(M-1)) + Y(1); |
400 |
end |
401 |
if isempty(dX) |
402 |
dX = diff(X); |
403 |
end |
404 |
if isempty(dY) |
405 |
dY = diff(Y); |
406 |
end |
407 |
[X,Y] = meshgrid([X(1)-dX(1)/2 X+dX([1:N-1 N-1])/2],... |
408 |
[Y(1)-dY(1)/2 Y+dY([1:M-1 M-1])/2]); |
409 |
|
410 |
% Generates faces: |
411 |
ind = (1:(M+1)*N)'; |
412 |
ind(M+1:M+1:end) = []; |
413 |
|
414 |
% Generates patches: |
415 |
H = patch(... |
416 |
'Vertices' ,[X(:) Y(:)],... |
417 |
'Faces' ,[ind ind+1 ind+M+2 ind+M+1],... |
418 |
'FaceVertexCData',U(:),... |
419 |
'FaceColor' ,'flat',... |
420 |
'EdgeColor' ,'none',... % NOTE: Sometimes this is not required. |
421 |
varargin{:}); |
422 |
set(ha,... |
423 |
'YDir' ,'reverse',... |
424 |
'View' ,[0 90],... |
425 |
'Box' ,'on',... |
426 |
'Layer','top') |
427 |
axis(ha,'tight') |
428 |
|
429 |
% Sets clim: |
430 |
if ~isempty(clim) |
431 |
set(ha,'CLim',clim) |
432 |
else |
433 |
set(ha,'CLimMode','auto') |
434 |
end |
435 |
|
436 |
end |
437 |
|
438 |
% Adds NaNs patches: |
439 |
if any(MNAN(:)) |
440 |
if aequal |
441 |
% dX and dY is constant: |
442 |
[MNAN,NNAN] = ind2sub([M,N],find(MNAN)); |
443 |
Nnan = length(MNAN); |
444 |
dX = (X(2)-X(1))/(N-1)/2; |
445 |
dY = (Y(2)-Y(1))/(M-1)/2; |
446 |
HNAN = patch(repmat((X(1)+(NNAN(:)'-1)*(2*dX)),4,1) + ... |
447 |
(repmat([-1 1 1 -1]'*dX,1,Nnan)),... |
448 |
repmat((Y(1)+(MNAN(:)'-1)*(2*dY)),4,1) + ... |
449 |
(repmat([1 1 -1 -1]'*dY,1,Nnan)),... |
450 |
CNAN,... |
451 |
'EdgeColor',CNAN,... 'EdgeColor','none',... |
452 |
varargin{1:Nargin-rem(Nargin,2)}); |
453 |
else |
454 |
% dX and/or dY is not constant: |
455 |
MNAN = find(MNAN); |
456 |
HNAN = patch(... |
457 |
'Vertices' ,[X(:) Y(:)],... |
458 |
'Faces' ,[ind(MNAN) ind(MNAN)+1 ind(MNAN)+M+2 ind(MNAN)+M+1],... |
459 |
'FaceColor' ,CNAN,... |
460 |
'EdgeColor' ,'none',... 'EdgeColor',CNAN,... % NOTE: may be better? |
461 |
varargin{:}); |
462 |
end |
463 |
else |
464 |
HNAN = []; |
465 |
end |
466 |
|
467 |
|
468 |
% OUTPUTS CHECK-OUT |
469 |
% ------------------------------------------------------------------------- |
470 |
|
471 |
% Clears outputs?: |
472 |
if (nargout==0) |
473 |
clear H |
474 |
end |
475 |
|
476 |
|
477 |
% [EOF] imagescnan.m |