Reputation: 6261
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
Reputation: 4963
I think the magic happens when MATLAB uses Intel's IPP library:
http://software.intel.com/en-us/intel-ipp
Upvotes: 0
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