Can you rotate a meme?
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:
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 from radians to degree with new added value t1 = radtodeg(t)+deg; % Converting back degrees to radians t = degtorad(t1); % 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
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().