# Too much information about the size function 16

Posted by **Steve Eddins**,

Today's post is proof that this old MATLAB dog can learn new tricks. The subject is this: Given an image array, how can you most easily determine the number of rows and columns it has? (In code, that is; I don't mean by looking at the Workspace Browser!)

I'm going to take an indirect path to get there:

1. I'll explain a general principle of MATLAB function behavior.

2. I'll show the that the `size` function violates this principle.

3. I'll show how a common use of `size` can lead you astray.

4. I'll show the code I used to use.

5. Finally I'll show how to do it a new "trick" (that Loren taught me) which uses a fairly recent addition to the MATLAB language.

There's nothing profound here, but there are some subtle issues about using `size` with multidimensional arrays that image processing users should probably be aware of. (I'll warn you here --- if you're already
bored with this topic, the rest of the post doesn't get better. You should probably just go read one of the other MATLAB Central blogs instead.)

The general principle of MATLAB function behavior is about functions with multiple output arguments. For example, the Image
Processing Toolbox function `bwlabel` has two output arguments. A sample call using both output arguments is:

[L,n] = bwlabel(BW);

The first output argument is the label matrix, and the second argument is the number of labeled objects.

For most MATLAB functions, the meaning of each output argument does not depend on how many output arguments are specified
when the function is called. In the example above, if you call `bwlabel` with only one output argument, that output is still the label matrix. The second output argument is silently dropped by
MATLAB. And if you call the function with no output argument, the first output is still the label matrix and is used to set
the automatic workspace variable `ans`.

I can think of three kinds of exceptions to this behavior. In the first case, common to many graphics functions, a function
returns something (like a handle) if called with an output argument, but it returns nothing if called without an output argument.
Examples in this category include `plot` and `bar`.

The second kind of exception is for functions that "pretty-print" something to the Command Window if called without an output
argument and return a value if called with an output argument. Examples include `dir` and `whos`.

The third category is (unfortunately) the "everything else" exceptions. And `size` is in this group.

You can see the exceptional behavior with your plain ol' two-dimensional matrices. Consider the following two calls to size:

A = rand(3,4); % A is 3-by-4 m = size(A); [m,n] = size(A);

`m` is not the same in both cases. In the first case, `size` returns the size vector, `[3 4]`. In the second case, `m` is the number of rows (3) and `n` is the number of columns (4).

For higher dimensional arrays it gets more interesting. The rule is this: If you call `size` with two or more output arguments, the last output argument is the number of elements in that array dimension *and all the remaining dimensions of the array*.

It's probably easier to just show you. Let's look at a 3-by-4-by-5 array and calls to `size` with a different number of output arguments.

B = rand(3,4,5); [m,n,p] = size(B)

m = 3 n = 4 p = 5

Not surprising. Now just call it with just two output arguments:

[m,n] = size(B)

m = 3 n = 20

Now the second output is 20 instead of 4! That's because 4*5 is 20. For fun, call size with 4 outputs:

[m,n,p,q] = size(B)

m = 3 n = 4 p = 5 q = 1

MATLAB is perfectly happy to treat `B` as a four-dimensional array whose last dimension has size 1. That's actually pretty useful, even essential, for writing reasonable-looking
multidimensional code in MATLAB.

Now let me connect this back to the problem I posed at the very beginning: find the number of rows and columns in an image array. Let's make it a concrete example:

neb = imread('ngc6543a.jpg'); imshow(neb, 'InitialMagnification', 50) title('Obligatory image') xlabel('This blog is supposed to be about image processing after all')

Here's the naive attempt to ask for the number of rows and columns in our image:

[nrows,ncols] = size(neb)

nrows = 650 ncols = 1800

But unfortunately that's not right because `neb` is a 3-D array. Therefore the second output is the number of columns times 3, the number of color planes. There are two ways
I've always used to make sure I get the right answer. The first is to call `size` with three output arguments, even though I only need two:

[nrows,ncols,junk] = size(neb)

nrows = 650 ncols = 600 junk = 3

I always found this vaguely unsatisfactory because I don't like unused variables cluttering up my code. The second way requires
two separate calls to `size`:

nrows = size(neb,1)

nrows = 650

ncols = size(neb,2)

ncols = 600

Loren pointed out to me recently that a feature added to R2009b, using the tilde character in input and output argument lists, can be used here. Used in an output argument list, the tilde character says to MATLAB, "Hey, I'm not going to need this output argument and don't want to even give it a variable name, so please just clean it up automatically for me." Loren and I both posted about this feature (hers and mine) if you want to go read more about it. As I recall, not everyone was a fan. Oh, well. I like it.

Anyway, here's how you can use it in the call to `size`:

[nrows,ncols,~] = size(neb)

nrows = 650 ncols = 600

Maybe this was more than you really wanted to know about `size`. However, I think that if you regularly write code that handles multidimensional arrays, eventually you'll be happy that
you made it all the way to the end.

Get the MATLAB code

Published with MATLAB® 7.11

## 16 CommentsOldest to Newest

**1**of 16

Here’s something I’ve recently hit a snag with regarding extra dimensions.

Suppose you have a function that should take in an n-by-3 array, and return, say, the sum of the 1st and 3rd columns. Pretty easy, right?

function out = simpleFunction(a) out = a(:,1) + a(:,3);

Now, what about if you wanted the function to work on an n-by-3-by-m array. Still pretty easy, just add a colon for the 3rd dim:

function out = simpleFunction(a) out = a(:,1,:) + a(:,3,:);

But is there a nice way to handle *any* subsequent dimensions such an an n-by-3-by-m-by-p-by-etc… array?

I think that just adding 20 colons will suffice at least for the first 20 dimensions, but it hardly seems elegant (or robust, if someone happens to provide an array with 21 dimensions).

Is there a nice solution, a not-so-nice solution, or no solution?

**2**of 16

Oh, and here’s a method using reshape() and size() that should work for the question I posed…

function out = simpleFunction(a) sz_orig = size(a); [sz_1, sz_2, sz_rest] = size(a); a_flat = reshape(a, [sz_1, sz_2, sz_rest]); out_flat = sum(a_flat(:,[1 3],:), 2); out = reshape(out_flat, a_sz);

I just wonder if there’s a more elegant answer (that doesn’t need to duplicate the input and reshape twice), such as the answer if the need was to sum *all* columns in the 2nd dimension, rather than columns 1 and 3. When *that* question is posed, it gets very simple and still handles multiple dimensions by default:

out = sum(a,2);

**3**of 16

Tildes are fantastic, until you go to give your code to someone running a version prior to 2009b. In my opinion, the following is ideal for both readable and portable code.

nRows = size(im,1); nCols = size(im,2);

**4**of 16

I think that the simplest syntax for ‘size’ is the best:

sz = size(a); nd = length(sz); for ii=1:nd ... sz(ii) ... end

@Sven:

I like to use the this to extract a slice from an n-D array:

dim = 2; % the dimension along which to slice plane = 3; % the index along dimension 'dim' nd = ndims(in); % make sure dim<=nd ! s = repmat({':'},1,nd); s{dim} = plane; out = in(s{:}); % Is equivalent to: out = in(:,3,:);

**5**of 16

Actually size’s behavior is consistent with regular indexing in Matlab, which conveniently solves Sven’s problem!

Sven, Cris, you can simply index 3 dimensions, and all dimensions after your last “:” will be treated as one.

Try this:

x=zeros([3 3 3 3]); %4D array

x(:) = 1:numel(x); %populate it with consecutive numbers

x(:,1,:) %notice that the virtual “third dimension” here actually has 9 elements, so it represents dimensions 3 and 4

**6**of 16

Regarding the non-intuitive behavior Steve pointed out (which is convenient for N-D arrays, but for the simple case seems unnecessarily error-prone). Size already accepts a scalar dimension as input:

m = size(A,1);

How about if it accepted a vector of dimensions? Then you could write the (IMO) intuitive code:

[m,n] = size(A,1:2);

Which in the case of a 3D (or N-D) array wouldn’t return all the remaining dimensions, so you could use it for both a grayscale and a color image.

**7**of 16

Sven-

I think you can get what you want by preallocating out or reshaping it after. Something like this.

sz = size(a);

sz(2) = 1;

out = zeros(sz);

out(:) = a(:,1,:) + a(:,3,:);

But I haven’t tried it.

–Loren

**8**of 16

Hey, thanks all for the input! It showed some tricks I was unaware of. I used it to write a more dimensionally-robust cross-product calculator. Turns out João/Loren’s method with preallocation was the quickest, followed close by Cris’s use of repeated ‘:’ characters, and my reshape/unreshape was slowest of all. I would use the same ordering for “elegantness” too (although being able to send ‘:’ as a string surprised me).

function c = vectorCross3d(a,b) sza = size(a); szb = size(b); % Initialise c to the size of a or b, whichever has more dimensions. If % they have the same dimensions, initialise to the larger of the two switch sign(numel(sza) - numel(szb)) case 1, c = zeros(sza); case -1, c = zeros(szb); otherwise, c = zeros(max(sza,szb)); end c(:) = bsxfun(@times, a(:,[2 3 1],:), b(:,[3 1 2],:)) - ... bsxfun(@times, b(:,[2 3 1],:), a(:,[3 1 2],:)); end

Now I can cross product a 1-by-3 vector with any arbitrarily sized n-by-3-by-m-by-etc set of vectors:

>> out = vectorCross3d(rand(1,3), rand(40,3,12,29)); >> size(out) ans = 40 3 12 29

**9**of 16

Hi Steve,

Sorry for the off-topic, but I was wondering whether you would consider a post on symmetry measurement of objects in an image. I think it has a lot of potential :)

I haven’t found anything in the Mathworks site – sorry if it has been covered.

And thank you for the blog. For a biologist like me, with a very shallow background in algebra and calculus and a low proficiency in programming, your blog is both helpful and inspiring.

Cheers,

Joaquin

**10**of 16

I sometimes find this useful for those times when SIZE could mess me up on an unknown array:

A = rand(3,3,4,5,6); [SZ(1:ndims(A))] = size(A)

**11**of 16

Oops, I meant to use {}, not ()!

**12**of 16

Joaquin—Thanks for the suggestion. Can you be a bit more specific about what kind of measurement you’re looking for?

**13**of 16

Matt—I don’t see how that is different from:

sz = size(A);

Can you clarify?

**14**of 16

Steve,

Thanks for your answer.

I am trying to segment nuclei. Undersegmentation of nearby nuclei is a common problem. Most of them can be separated with marker-controlled watershedding. However sometimes even that fails. I tried solidity, eccentricity and compactness, alone and combined, to identify these cases by shape, but it is not stringent enough and I pick many correctly segmented nuclei that just happen to be not that roundish. However the overlapping nuclei tend to have an eight-ish shape, that is not shared by the other eccentric, low-solidity, low-compactness well-segmented nuclei.

So it would be very useful to be able to get, for a given object, how far is the shape from having, for instance, a (double) mirror symmetry, and obtain the direction/s of the symmetry axis/es.

My knowledge of maths is just very far away to be able to implement that myself. And there isn’t seem to be an obvious place where to get examples of these problems tackled with MATLAB. So I thought of suggesting it to you, because even this would only improve the performance of my little project by ~1%, I think it is an interesting topic nonetheless.

Best,

Joaquin

**15**of 16

Steve,

Please do allow me some editing:

[…] it would be very useful to be able to get, for a given object, A MEASURE OF how far […]

[…] even THOUGH this would only improve […]

Joaquin

**16**of 16

Joaquin—Thanks for the suggestion.

## Recent Comments