Dynamic Variables in Loops

I'm sure this has been answered before, but I can't seem to find the answer anywhere... Pretty simple problem for seasoned MATLAB users...
I have a series of images in a folder. I am trying to average these images. I start off by prompting the user for a folder to work from, then count the number of .jpg's in the folder. I run a loop to create a variable A for each image in the folder (A1, A2, A3, ....., A(i))
Now I need to assign those images to A(i) for them to processed.
Here is the code, thanks in advance. It is NOT recognizing A(i) (I think) and the error message is
Error in Average_Code (line 21) A(i)=imread(Files(i).name);
clc;
clear all;
close all;
%User selecting image directory
cd(uigetdir);
%Counting number of .JPG's in folder
Files = dir('*.jpg')
b=numel(Files);
%Create Dynamic Variables
for i=1:b
eval(['A' num2str(i) '= i']);
end
fusion = 0;
%Assign Images to Dynamic Variables
for i=1:b
A(i)=imread(Files(i).name);
A(i)=double(A(i));
fusion = fusion + A(i);
end
%Average Images
average_image = fusion/b;
imshow(average_image);
Thoughts?

1 Comment

The answer is simple: do not do this. In MATLAB it is (almost always) better to not create variable names on the fly:
Alternatives usually involve accumulating data in an array (which could be a struct or cell), of course with array preallocation. This page gives excellent examples about how you can do this:

Sign in to comment.

 Accepted Answer

Matt Tearle
Matt Tearle on 27 Oct 2014
Edited: Matt Tearle on 28 Oct 2014
Yes, this has been answered before, and the answer is: generally, don't!
The specific problem you're having is that you created a bunch of variables called A1, A2, etc., but in the loop you're trying to reference A(2), which is not the same thing as A2.
You could keep everything in a single array A. How you do that depends on the files. If they are all B&W images of the same size, you could keep them in a 3-D double array:
A(:,:,i) = double(imread...);
Then, at the end,
average_image = mean(A,3);
Given that you're trying to do averaging, I assume your images are at least the same size. If they're color images, things get a bit gnarlier, but you could still do it with 4-D arrays (depending on what you mean by averaging the images).
EDIT TO ADD: OK, given your comment, if you're dealing with true-color images, imread will return them as 3-D arrays (m-by-n-by-3), so you could store the b different images in a 4-D array (m-by-n-by-3-by-b):
for i = 1:b
A(:,:,:,i) = double(imread(...));
end
If the "average" image is just the mean of each of the 3 color planes individually, then you can do
average_image = mean(A,4);
imshow(average_image)
If you want something more complicated than that, then... well, I'm sure it's possible, but I don't know what it would look like.

6 Comments

@Shant: This is the most frequently asked question and the answer is always the same.
I think the notion of high dimension arrays can be troublesome to novice users and young students. The reason I bet this question is popular is that its probably much easier to think about the solution in the sense of a dynamic naming convention rather than a 4D matrix, even though they accomplish the same thing.
I am most certainly a novice with programming, and you are correct... In terms of thought process, it is easier for me to grasp utilizing a dynamic variable to store the images for averaging. However, I did get the code posted by Matt to function as a 4D array. The output did not truly average the pixel values within the image (at least it didn't appear to do so. I took a picture of a circle colored red, then the same image, same size, colored yellow. Theoretically the output would be an orange circle, but it did not do this. It output a yellow circle. These images are being utilized to test the code, but in actuality I am going to be using this code to average a series of 10 images that are black and white speckle patterns (not true B&W, the samples happen to be black with white spray paint to apply the speckle and it is taken as a color image by a digital camera.) The averaging is meant to eliminate or reduce noise. The averaged image will then be used in digital image correlation on a sub-pixel level.
Hmm, I wonder what the problem is with the averaging. You're testing it with a directory that just has the two jpgs (red circle and yellow circle)? I'd check the two images separately to make sure they're looking as you expect -- imshow(A(:,:,:,1)) -- then also check the individual color planes -- imshow(A(:,:,1,1)). Then finally take a pixel in the middle and compare the two input images and the averaged image
x = 123; % horizontal pixel number
y = 234; % vertical pixel number
red = squeeze(A(y,x,:,1))
yellow = squeeze(A(y,x,:,2))
orange = squeeze(average_image(y,x,:))
See if orange really is the mean of red and yellow.
It seems like all the pixel values are right...
I just tried a second method of coding to do what I am trying. It works like a charm, except the output image is stark white even though the pixels HAVE a value. I'm in over my head as usual.
clc;
clear all;
close all;
%User selecting image directory
cd(uigetdir);
%Counting number of .JPG's in folder
Files = dir('*.jpg');
b=numel(Files);
I0 = imread('IMG_1.jpg')
sumImage = double(I0); % Initialize to first image.
for i=2:b % Read in remaining images.
rgbImage = imread(['IMG_',num2str(i),'.jpg']);
sumImage = sumImage + double(rgbImage);
end;
meanImage = sumImage / b
imshow(meanImage);
imshow(meanImage,'Border','Tight');
set(gcf, 'PaperPositionMode', 'auto');
h = gcf;
saveas(h, [cd '\' 'IMG_Average'], 'jpg');
I checked the value of meanImage, and it does have pixel values.... but as you can see in the attached image, nothing.
Oh waitwaitwaitwaitwait. Dammit, sorry, I've been an idiot. I forgot about the conversion you have to make when you go from uint8 to double. In uint8 the values go from 0 to 255. Going to double keeps the same values. But to represent an image as a double, you should use values in the range 0 to 1. So
A(:,:,:,i) = double(imread(...))/255;
or
rgbImage = imread(['IMG_',num2str(i),'.jpg']);
sumImage = sumImage + double(rgbImage)/255;
But a better way to do it is to use im2double instead of just double (this takes care of the necessary scaling for you):
A(:,:,:,i) = im2double(imread(...));
or
rgbImage = imread(['IMG_',num2str(i),'.jpg']);
sumImage = sumImage + im2double(rgbImage);
This was probably what was causing the earlier issue you were having (with red/yellow/orange).

Sign in to comment.

More Answers (0)

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Asked:

on 27 Oct 2014

Commented:

on 30 Oct 2014

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!