Warping an image with cv2.warpAffine() results in an image shifted by 0.5 pixel.
Is this behaviour expected? If so, what's the reasons behind?
Code
Below is a simple example where an image is scaled with affine transform.
import cv2
import numpy as np
def warpScale(im, scale):
affine = np.array([
[scale, 0, 0],
[0, scale, 0],
[0, 0, 1],
], np.float32)
return cv2.warpAffine(im, affine[:2, :], (int(im.shape[1]*scale), int(im.shape[0]*scale)))
im = cv2.imread("0-in.png") # 2x2
scale = 8
# Scale up
im1 = warpScale(im, scale)
cv2.imwrite("1.png", im1) # 8x8
# Scale down
im2 = warpScale(im1, 1. / scale)
cv2.imwrite("2.png", im2) # 2x2
Input 2x2 image:
Output-1 (scaled up by a factor of 8):
Output-2 (scaled down back to the original):
Workaround
When I rewrite the warpScale()
function as blow, it becomes as I expected. It's like doing the following operations in order:
- Shift the original image by (+0.5, +0.5)
- Apply the intended affine transform
- Shift the transformed image by (-0.5, -0.5)
:
def warpScale(im, scale):
affine = np.dot(
np.array([
[1, 0, -0.5],
[0, 1, -0.5],
[0, 0, 1],
], np.float32),
np.dot(
np.array([
[scale, 0, 0],
[0, scale, 0],
[0, 0, 1],
], np.float32),
np.array([
[1, 0, 0.5],
[0, 1, 0.5],
[0, 0, 1],
], np.float32)
)
)
return cv2.warpAffine(im, affine[:2, :], (int(im.shape[1]*scale), int(im.shape[0]*scale)))
Output-1 (scaled up by a factor of 8):
Output-2 (scaled down back to the original):