Fetching contributors… Cannot retrieve contributors at this time
150 lines (123 sloc) 5.87 KB

⬅️

# Can you rotate a meme?

### Hey!

Image rotation is the very basic operation in image (pre)processing. We are so often using this procedure, that I even asked myself, do I really know how to rotate an image without built-in function? That's how it all started.
The first and the most important math technique to refresh to accomplish this task is Polar Coordinate system. Usually, since image is matrix, we usually work with it in Cartesian coordinate system (translations, flipping, etc). However, rotation operation is much easier in polar coordinate system, where radius and ANGLE are already how we can represent any point (pixel). Here is a brief result:

Rotation ### 1. Code

Here is the code. The important part is commented out:

```%% 1-D Vector of original image size
x1 = zeros([size(A,1)*size(A,2) 1]);
x2 = zeros([size(A,1)*size(A,2) 1]);

% Empty template of zeros
C = uint8(zeros([size(A,1) size(A,2)]));

% Specified angle
deg = 45;

% Midpoint (axis of rotation)
midx = ceil((size(C,1)+1)/2);
midy = ceil((size(C,2)+1)/2);

% Main loop
m = 1;
for i = 1:size(A,1)
i1 = i-midx;
for j = 1:size(A,2)
% Converting Cartesian to polar from central point
[t,r] = cart2pol(i1,j-midy);
% Converting back degrees to radians
% Convertin back polar to Cartesian
[x,y] = pol2cart(t,r);
% Floats to integers replacing with new values
x1(m) = round(x+midx);
x2(m) = round(y+midy);
m = m+1;
end
end```

`x1` and `x2` represent values of Cartesian system coordinates. Sometimes, their value may go negative. This happens when rotated pixels are out of bounds. We can prevent it by shifting pixels to the exact absolute value of that negative:

```% Checking for negative values (Out of image size range)
min_x1 = min(x1);
min_x2 = min(x2);

%% Shifting them
if min_x1 <= 0
x1 = abs(min_x1) + x1 + 1;
end
if min_x2 <= 0
x2 = abs(min_x2) + x2 + 1;
end

n=1;

%% Putting old pixels to zero template
for i = 1:size(A,1)
for j = 1:size(A,2)
C(x1(n),x2(n)) = A(i,j);
n = n+1;
end
end

Output = C;```

Done. But let's inspect the image:

Black dots (empty pixels) We can notice here of black empty dots. These mean that due to rotation, size rotated image is different from original image, and so, there are extra pixels at the places. If we do not assign any value to these pixels, they will appear as black (0). So, how do we manage them? There are several techniques, actually, such as nearest neighbour, bilinear, bicubic interpolation. We will consider first and second in this post.
In essence, nearest neighbour methods simply reads values of the nearest pixel and puts it into the one, which is empty. Here is how it works in terms of code:

```% Fill pixels with nearest neighbour value
for i = 2:size(C,1)-1
for j = 2:size(C,2)-1
% 3-3 Neighbourhood
temp = C(i-1:i+1,j-1:j+1);
% If center point is zero and neighbourhood sum is not zero
if(temp(5)==0 && sum(temp(:))~=0)
% Find non-zero values
pt = find(temp~=0);
% Sort them according to distance
[val, ind] = sort(abs(pt-5));
% Replace central point with new nearest neighbour value
Output(i,j) = temp(pt(ind(1)));
end
end
end

figure()
imshow(Output)```

Let's see the zoomed result:

Nearest neighbour result (Zoomed) Good. No black dots anymore. However, the edge parts in the image are rough. In original image, they are more smooth. So, this is the drawback of nearest neighbour technique. Since, it does not care about neighbourhood information in general, and takes only nearest neighbour value, such effects (roughness) can take place in the result. So, let's inspect the bilinear interpolation.
Bilinear interpolation reads whole 3x3 neighbourhood around the empty pixels and calculates weighted average, where weights are distances from center point. These values are combined to get weighted average value. This value is then replaced to the empty pixel:

```% Fill pixels with bilinear interpolation
for i = 2:size(C,1)-1
for j = 2:size(C,2)-1
% 3-3 Neighbourhood
temp = C(i-1:i+1,j-1:j+1);
% If center point is zero and neighbourhood sum is not zero
if(temp(5)==0 && sum(temp(:))~=0)
% Pixel values of neighbors
ns = [temp(1:4),temp(6:9)];
% Weight values (distances) of neighbours
ws = [sqrt(2) 1 sqrt(2) 1 1 sqrt(2) 1 sqrt(2)];
% Weighted average
wal = round(mean(double(ns).*ws));
% Replacing central pixel with weighted average value
Output(i,j) = wal;
end
end
end

figure
imshow(Output);```

Bilinear interpolation (Zoomed) Now it gets smoother. We can go even more, if we use bicubic interpolation. The difference between bilinear and bicubic interpolation is in the size of considered neigbourhood. For example, if bilinear uses 3x3 neighbourhood, bicubic considers 4x4 neighbourhood. That's it. We can rotate a meme without MATLAB's imrotate and OpenCV's cv2.rotate().

-Have fun!

You can’t perform that action at this time.