aaronsnoswell
aaronsnoswell

Reputation: 6261

How is Matlab's imrotate so fast?

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

Upvotes: 1

Views: 1602

Answers (2)

Royi
Royi

Reputation: 4963

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

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

Upvotes: 0

yhenon
yhenon

Reputation: 4301

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

Upvotes: 3

Related Questions