# cv2.remap height must be multiple of 8?

I am using the cv2.remap function (OpenCV 2.4.8.2 on OS X 10.9.4) to warp rectangular images with an alpha channel into arbitrary quads. I am encountering an issue where along the bottom of the image, pixels of a uniform color sometimes appear behind my rotated image. As a test case for my code, I warp a red square. An example result for a 100x100 output square:

Notice the aliasing at the bottom corner; the strip along the bottom is replaced where the rotated square intersects it. Thus the strip is 'behind' the imagery.

The test case has allowed me to play with numbers, and I have discovered that this issue doesn't occur if new image's height is a multiple of 8. For example, if I change the output image to be 104 pixels high, the bar disappears (but now I have undesirable padding at the bottom). The size of the bar varies with the remainder of H/8 (i.e. a height of 103 creates the thickest bar).

Am I correct in suspecting this is a bug? I have not directly played with the C/C++(?) API for this functionality to see if the issue exists there too.

A simpler, modified version of my code and test is below. The use of griddata along with remap is adapted from this answer on Stack Overflow. Assuming one has the various python packages used, (Pillow, scipy, cv2, numpy), this script should work to produce the above images. The later is created if you change ysize to 104.

from PIL import Image
import cv2
import numpy as np
from scipy.interpolate import griddata

def remap_image_from_pts(img, oldmap, newmap, maxx, maxy):
"""Remaps img using sparse maps.

Parameters
----------
img : cv2.Image
RGBA
oldmap : 2D array-like
Sparse map of points corresponding to original image.
newmap : 2D array-like
Sparse map of points corresponding to warped image.
maxx, maxy : int
Dimensions of resulting dense mappings. (Can be inferred from newmap)

Returns
-------
PIL.Image
Remapped image

"""
grid_x, grid_y = np.mgrid[0:maxx:maxx*1j, 0:maxy:maxy*1j]
grid_z = griddata(newmap, oldmap, (grid_x, grid_y), method='linear')
print grid_z.shape
map_x = np.append([], [ar[:,0] for ar in grid_z]).reshape(maxx, maxy)
map_y = np.append([], [ar[:,1] for ar in grid_z]).reshape(maxx, maxy)
map_x_32 = map_x.astype('float32')
map_y_32 = map_y.astype('float32')

remappedpts = cv2.transpose(
cv2.remap(img, map_x_32, map_y_32, cv2.INTER_CUBIC, borderValue=0, borderMode=cv2.BORDER_CONSTANT))

pil_im = Image.fromarray(remappedpts, mode='RGBA')
return pil_im

def test_remap_image_from_pts():
# Should create a red square and output a smaller rotated red square.

size = 100
imgPIL = Image.new('RGB', (size,size), 'red')

# convert to cv2 compatible format and add alpha channel
img = np.array(imgPIL, dtype=np.uint8)
img_rgb = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA)

oldmap = np.array([np.array(i) for i in [[0,100], [0,0], [100,0],[100,100]]])
newmap = np.array([np.array(i) for i in [ [0,80],[20,0],[100,20], [80,100]]])

ysize = 100
maxx, maxy = (size, ysize)

outputPIL = remap_image_from_pts ...
edit retag close merge delete