np.rot90() corrupts an opencv image

Viewed 1805

When trying to rotate a landscape image to portrait, after applying the rotation, I cannot draw on the image.

img1 = cv2.imread('a.jpg')
cv2.circle(img1, tuple([10,10]),radius = 3, color = (255,0,0))

works fine.

Then I try:

img2 = np.rot90(img1,3)
cv2.circle(img2, tuple([10,10]),radius = 3, color = (255,0,0))

and I get the error:

TypeError: Layout of the output array img is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)

Looking at type(img2) and img2.dtype it seems identical to img1. the dimensions also seem fine (the first two dimensions are flipped, the third stays "3")

BTW: this seems to work. (why?):

img2 = np.rot90(img1,3)
img3 = img2.copy()
cv2.circle(img3, tuple([10,10]),radius = 3, color = (255,0,0))
2 Answers

np.rot90 isn't quite corrupting the image per se. The reason behind this is because OpenCV cannot work with "views" of numpy arrays. np.rot90, as well as several other numpy operations do not actually modify the underlying array, but instead return a modified "view" to the array. This will typically throw the following error with OpenCV ops:

Layout of the output array img is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)
Expected Ptr<cv::UMat> for argument 'img'

Operations like np.rot90(img1), np.fliplr(img1) , np.flipud(img1), np.transpose(img1, (1, 0, 2)), etc all are simply returning views, and will all throw the same above error.

The reason why adding .copy() resolves the error is because, when .copy() is called on a view, it becomes stored in memory as a new array that is different from the original array. You can actually verify this quite simply:

import numpy as np

img = np.random.choice(256, (50, 100, 3)).astype(np.uint8)
img_r = np.rot90(img)

print(img_r == img)
# False

print(np.shares_memory(img, img_r))
# True

img_r = img_r.copy()
print(np.shares_memory(img, img_r))
# False

As you can see, even though img_r == img shows False, they still share the same memory -- as img_r is a view of img. But after calling .copy(), the view img_r becomes its own array and no longer shares memory with the original img.

Related