# Mathematica loop code not behaving as expected

getAvgs[A_, M_] := Module[{i, j, rArr, gArr, bArr},
rArr = {};
gArr = {};
bArr = {};
For[i = 1, i <= Length[A], i += 1,
For[j = 1, j <= Length[A[[1]]], j += 1,
If[M[[i, j]] == 255,
Append[rArr, A[[i, j, 1]]];
Append[gArr, A[[i, j, 2]]];
Append[bArr, A[[i, j, 3]]];
];
];
];
Return[{rArr, gArr, bArr}];
];


This code returned an array containing three blank arrays {{},{},{}}, meaning none of the Append functions worked. A_ and M_ are two images, A is 24-bit 3-channel colour, while M is black and white. They have the same dimensions, the idea is to use M as a mask to find the average colour of all pixels that are shown by the mask. However, none of the Append functions worked.

Append[{}, ImageData[,Byte][[30, 30, 3]]] correctly returned the value, so the syntax should be fine. When I made the for loop increment a counter, the counter correctly returned the number of masked pixels, so the for loop is working correctly too.

From what I know, Module basically declares the variables locally, preventing variable collisions. Is this correct as well?

-

## migrated from stackoverflow.comApr 6 '13 at 14:34

This question came from our site for professional and enthusiast programmers.

Thanks, will ask it there. –  March Ho Apr 3 '13 at 18:29
the direct fix is to use AppendTo, though of course you dont really want to use for loops at all. –  george2079 Apr 3 '13 at 20:14

You are trying to find the mean for each channel of an image after masking. So, your code could be much simpler:

image = ExampleData[{"TestImage", "Mandrill"}];
mask = DiskMatrix[#1/10, #2] & @@ ImageDimensions@image;
channels =  Pick[Flatten@#, Flatten@mask, 1] & /@ (ImageData /@ ColorSeparate[image]);
Mean /@ channels
(*
{0.846919, 0.435261, 0.408555}
*)


You may compare this result with the mean over the whole image

Mean /@ Flatten /@ ImageData /@ ColorSeparate[image]
(*
{0.53879, 0.505329, 0.443596}
*)


You can easily see that the result {R,G,B} behaves qualitatively as expected:

-

Append doesn't change the original list, it just returns a new one with one element added.

list = {1, 2, 3};
Append[list, 4]

(* {1, 2, 3, 4} *)

list

(* {1, 2, 3} *)


If you want to change the original list, use AppendTo.

AppendTo[list, 4]

(* {1, 2, 3, 4} *)

list

(* {1, 2, 3, 4} *)

-
Thanks for the old answer, in the 2 intervening years I have realised that this is a really common beginner mistake, hopefully I won't make this kind of mistake again :P –  March Ho Apr 20 at 18:33
Oops, I didn't even notice the date. For some reason this post showed up at the top of my 'active' list, so I just figured it was recent. –  rhennigan Apr 20 at 23:06

In case you really want a loop.. this is prefered over using AppendTo..

 getAvgs[A_, M_] := Module[{i, j, rArr, gArr, bArr},
Transpose[Reap[
For[i = 1, i <= Length[A], i += 1,
For[j = 1, j <= Length[A[[1]]], j += 1,
If[M[[i, j]] == 255,
Sow[A[[i, j]]]
;];];]][[2, 1]]]];


and another way more mathematica-esque..

getAvgs[A_, M_] :=
Transpose[ Last /@ Select [
Transpose[{Flatten[ M , 1] , Flatten[A, 1]}] , #[[1]] ==  255 & ]]


(not to disagree about using image tools, but it may be more instructive to learn the basics without that )

-