Search code examples
matlabimage-processingvectorizationevaluation

How can I vectorize these while and nested for loops in MATLAB?


I am trying to vectorize the following code. My program is taking quite a long time to execute the result. For that reason i want to vectorized my while loop. Is it possible to vectorize it or do you have a different idea on what I should do?

Here I am taking the average of the neighboring pixels and iterating the loop until I get an approximate result:

n = 500;
Mat_new = rand(n); 
error = 1;
while error > 0.000001
    Mat_Old = Mat_new;
    for i = 2:n-1
        for j = 2:n-1
            Mat_new(i,j) =abs((2+((Mat_Old(i+1,j)+Mat_Old(i-1,j)+Mat_Old(i,j+1)+Mat_Old(i,j-1))))/(4));
        end
    end
    error =max(max(abs(Mat_Old-Mat_new)));
end

Solution

  • What you are doing is essentially a convolution of a 2D matrix. You just have to specify the input filter that you want. In your case, you want find a weighted sum of the base cardinal directions: North, East, South, West, given a particular location in the matrix as the output. As such, create a 3 x 3 filter that encompasses these directions, then use imfilter, or conv2 to create your output matrix. Seeing as how you want to create an image that has 1 element border surrounding the result, we should use conv2 instead. Though you have a lot of parentheses (yuck), what (I believe) you are doing is adding up all of the values in the cardinal directions, add this sum with 2, take the absolute value, then divide by 4.

    As such, do this:

    n = 500;
    Mat_new = rand(n); 
    error = 1;
    h = [0 1 0; 1 0 1; 0 1 0]; % // Define filter here
    while error > 0.000001
        Mat_Old = Mat_new;
    
        Mat_new = conv2(Mat_Old, h, 'valid');
        Mat_new = abs(Mat_new + 2) / 4; %//Take the output, add 2, absolute then divide by 4
        %// Pad border with zeroes
        Mat_new = padarray(Mat_new, [1 1]);
    
        error = max(abs(Mat_Old(:) - Mat_new(:))); %// Calculate maximum error
    end
    

    What the above code is doing is basically what you're doing with the double for loop. Except now, we are doing it with conv2. The first line of Mat_new computes the sum of the four cardinal directions for each location in your matrix, ignoring the border as you have shown in your for loop. Once we do this, we take the absolute value of each entry in the matrix through abs after we add 2 to each entry, then divide by 4. After, we pad the border of the output matrix with all zeroes using padarray. By the way, I've restructured your error statement so that it doesn't use a nested call to max. I never liked the way that looked.

    Unfortunately, the while loop (I don't think...) can be vectorized. You are computing a new output at each iteration, and want to calculate the error between the previous iteration and the current one. In this case, there is no way to vectorize something like this with a strong recurrence relation.... so you're stuck with the while loop for now. However, you can definitely vectorize the double for loop, and that's what we have just accomplished.

    This should hopefully achieve what you want!