1

I'm trying to write my own image rotation function that uses linear interpolation (see code below). Running my code on an example 256x256 image takes about 8 seconds, or ~0.12ms per pixel. Running Matlab's imrotate function using bilinear interpolation over the same image takes around 0.2 seconds, or ~0.003ms per pixel - around a hundred fold improvement.

I'm guessing there is some vectorisation optimisation that I'm missing, but I can't figure out where. Any suggestions are greatly appreciated.

Code below;

function [ output ] = rot_input_img_by_angle( input_img, angle )
%rot_input_img_by_angle Rotates the given image by angle about position
%   Given an image in the format [y, x, c], rotates it by the given angle
%   around the centre of the image

    if(nargin < 2)
        error('input_img and angle parameters are both required');
    end

    if(angle == 0)
        output = input_img;
        return;
    end

    position = [0 0];

    [height, width, channels] = size(input_img);
    num_pixels = height * width;
    half_width = width/2 - 0.5;
    half_height = height/2 - 0.5;

    % Compute the translation vector to move from a top-left origin to a
    % centred-origin
    T = [-half_width half_height]';

    % A lambda function for creating a 2D rotation matrix
    rotmat = @(th) [cos(th) -sin(th); sin(th) cos(th)];

    % Convert angle to radians and generate rotation matrix R for CR
    % rotation
    R = rotmat(deg2rad(angle));

    output = zeros(height, width, channels);

    for y=1:height

        for x=1:width

            loc = [x-1 y-1]';

            % Transform the current pixel location into the
            % origin-at-centre coordinate frame
            loc = loc .* [1; -1] + T;

            % Apply the inverse rotation mapping to this ouput pixel to
            % determine the location in the original input_img that this pixel
            % corresponds to
            loc = R * loc;

            % Transform back from the origin-at-centre coordinate frame to
            % the original input_img's origin-at-top-left frame
            loc = (loc - T) .* [1; -1] + [1; 1];


            if((loc(1) < 1) || (loc(1) > width) || (loc(2) < 1) || (loc(2) > height))
                % This pixel falls outside the input_img - leave it at 0
                continue;
            end

            % Linearly interpolate the nearest 4 pixels
            left_x = floor(loc(1));
            right_x = ceil(loc(1));
            top_y = floor(loc(2));
            bot_y = ceil(loc(2));

            if((left_x == right_x) & (top_y == bot_y))

                % The sample pixel lies directly on an original input_img pixel
                output(y, x, :) = input_img(y, x, :);

            else

                % The sample pixel lies inbetween several pixels

                % Location of the nearest 4 pixels
                px_locs = [left_x right_x left_x right_x; top_y top_y bot_y bot_y];

                px_dists = distance(loc, px_locs);
                px_dists = px_dists ./ sum(px_dists);

                % Take the linearly interpolated average of each color
                % channel's value
                for c=1:channels
                    output(y, x, c) = ...
                        px_dists(1) * input_img(px_locs(1, 1), px_locs(2, 1), c) + ...
                        px_dists(2) * input_img(px_locs(1, 2), px_locs(2, 2), c) + ...
                        px_dists(3) * input_img(px_locs(1, 3), px_locs(2, 3), c) + ...
                        px_dists(4) * input_img(px_locs(1, 4), px_locs(2, 4), c);
                end
            end

        end

    end

    output = cast(output, class(input_img));


end
aaronsnoswell
  • 6,051
  • 5
  • 47
  • 69
  • The slowness is most likely due to the nested for-loop with a lot of code to run in the inner loop (creating variables, computing matrices, evaluating functions, branching at if-statements, etc). I would guess that most of the speed of Matlab's `imrotate` is due to using optimized, compiled code (in a `mex` function). If you'd really like to improve your code, run the Matlab profiler to find out what is the slowest part. However, you'll probably be happiest just using the built-in `imrotate` if speed if your goal. – nibot Mar 30 '14 at 11:51

2 Answers2

3

You can see the function matlab uses by typing using

edit imrotate

Also, the documentation says:

%   Performance Note
%   ----------------
%   This function may take advantage of hardware optimization for datatypes
%   uint8, uint16, and single to run faster.

Matlab calls imrotatemex in this case, that is C code that has been compiled to be called from Matlab, and is generally faster. I don't know your image and system, so I can't say if this is happening.

You can still accelerate your code significantly by vectorising it. Instead of looping through each x and y value in the image, build arrays containing all combinations of x and y using meshgrid, and apply the operations to the array. This SO question contains an implementation of a nearest-neighbour interpolation rotation in matlab that is vectorised:

Image rotation by Matlab without using imrotate

Community
  • 1
  • 1
yhenon
  • 4,111
  • 1
  • 18
  • 34
0

I think the magic happens when MATLAB uses Intel's IPP library:

http://software.intel.com/en-us/intel-ipp

Royi
  • 4,640
  • 6
  • 46
  • 64