I want to use python3 with OpenCV packages to rotate an image in a degree 15 and rotate it back. However, the code below seem does not work but I think there no logic error in the codes. In the "NEAREST" window, the image is rotated correctly but in the "NEAREST-OK-RESTORE" window the image is not rotate back to its original position ( showed in "original" window ).
#! /usr/bin/env python3
#! -*- coding:utf-8 -*-
import cv2
import numpy as np
imgmat = cv2.imread('./lena.jpg',255)
print(hex(id(imgmat)))
cv2.imshow('original',imgmat)
rows,cols = imgmat.shape
M = cv2.getRotationMatrix2D((cols/2,rows/2),-15,1)
dst = cv2.warpAffine(src=imgmat,M=M,dsize=(cols,rows),flags=cv2.INTER_NEAREST)
print(hex(id(dst)))
cv2.imshow('NEAREST',dst)
OKMM = cv2.invertAffineTransform(M)
dst = cv2.warpAffine(dst,OKMM,(cols,rows),flags = cv2.INTER_NEAREST)
print(hex(id(dst)))
cv2.imshow('NEAREST-OK-RESTORE',dst)
cv2.waitKey(0)
The reason why this is happening is because when you are performing the rotation, this is doing it within the span of the original image dimensions, so this results in image clipping if you are rotating the image and the resulting corner points extend beyond the original image dimensions.
What you have to do is zero pad the image so that the rotated image is fully contained within it, rotate this image then when you rotate back, you will have to crop the result.
To figure out how much to zero pad your image by, note that the largest possible dimensions of your image will be when the image is at a 45 degree rotation. This means that the diagonal of the image would now be the number of rows in the image. Therefore, zero pad your image so that we can at least store the image when subject to a 45 degree rotation, rotate this image and when you rotate back you'll have to crop the result.
You can use numpy.pad
to do this for you and when you're done you can simply crop the result:
I've made the following changes to your code to do this:
import cv2
import numpy as np
imgmat = cv2.imread('./lena.jpg',255)
print(hex(id(imgmat)))
cv2.imshow('original',imgmat)
rows,cols = imgmat.shape
# NEW - Determine the diagonal length of the image
diagonal = int(np.ceil(np.sqrt(rows**2.0 + cols**2.0)))
# NEW - Determine how many pixels we need to pad to the top/bottom and left/right
pp_r, pp_c = (diagonal - rows) // 2, (diagonal - cols) // 2
# NEW - Pad the image
imgmat_copy = np.pad(imgmat, ((pp_r, pp_r), (pp_c, pp_c)), 'constant', constant_values=(0,0))
### Your code as before - note we are rotating the zero padded image
rows,cols = imgmat_copy.shape
M = cv2.getRotationMatrix2D((cols/2,rows/2),-15,1)
dst = cv2.warpAffine(src=imgmat_copy,M=M,dsize=imgmat_copy.shape,flags=cv2.INTER_NEAREST)
print(hex(id(dst)))
cv2.imshow('NEAREST',dst)
OKMM = cv2.invertAffineTransform(M)
dst = cv2.warpAffine(dst,OKMM,(cols,rows),flags = cv2.INTER_NEAREST)
print(hex(id(dst)))
# NEW - Crop the image
dst = dst[pp_r:-pp_r+1,pp_c:-pp_c+1]
cv2.imshow('NEAREST-OK-RESTORE',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
I now get: