8

I'm reading in an image with OpenCV, and trying to do something with it in numpy (rotate 90deg). Viewing the result with imshow from matplotlib, it all seems to be working just fine - image is rotated. I can't use drawing methods from OpenCV on the new image, however. In the following code (I'm running this in a sagemath cloud worksheet):

%python
import cv2
import matplotlib.pyplot as plt
import numpy as np
import os, sys
image = np.array( cv2.imread('imagename.png') )
plt.imshow(image,cmap='gray')
image = np.array(np.rot90(image,3) ) # put it right side up
plt.imshow(image,cmap='gray')
cv2.rectangle(image,(0,0),(100,100),(255,0,0),2)
plt.imshow(image,cmap='gray')

I get the following error on the cv2.rectangle() command:

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

The error goes away if I use np.array(np.rot90(image,4) ) instead (i.e. rotate it 360). So it appears that the change in dimensions is messing it up. Does OpenCV store the dimensions somewhere internally that I need to update or something?

EDIT: Adding image = image.copy() after rot90() solved the problem. See rayryeng's answer below.

3 Answers 3

10

This is apparently a bug in the Python OpenCV wrapper. If you look at this question here: np.rot90() corrupts an opencv image, apparently doing a rotation that doesn't result back in the original dimensions corrupts the image and the OP in that post experiences the same error you are having. FWIW, I also experienced the same bug.... no idea why.

A way around this is to make a copy of the image after you rotate, and then show the image. This I can't really explain, but it seems to work. Also, make sure you call plt.show() at the end of your code to show the image:

import cv2
import matplotlib.pyplot as plt
import numpy as np
import os, sys
image = np.array( cv2.imread('imagename.png') )
plt.imshow(image,cmap='gray')
image = np.array(np.rot90(image,3) ) # put it right side up

image = image.copy() # Change

plt.imshow(image,cmap='gray')
cv2.rectangle(image,(0,0),(100,100),(255,0,0),2)
plt.imshow(image,cmap='gray')

plt.show() # Show image
Sign up to request clarification or add additional context in comments.

3 Comments

That's interesting cv2.imread() just returns an ndarray. The ndarray is independent of OpenCV, and isn't becoming corrupted in any way that I can see. The only thing that makes sense to me is that OpenCV keeps track of the dimensions of an 'image' (ndarray) separately or something. Although then it doesn't make sense why the copy workaround works.
I honestly have no Idea why it works either. I'm considering submitting a bug request to figure out why this is required to get the rotation to work... And I couldn't see any corruption between operations either. It just simply works if you make a copy!
I guess it has to do with how python stores arrays. numpy.rotate() doesn't actually alter the data, it just changes the view into it. If you do 'print image.flags' before and after the copy (after rotating), I think it makes sense what is going on. Since OpenCV is all C, the C routines don't properly handle the new view into the numpy array. So basically, this is a bug in how the OpenCV python wrapper passes information to the c routines. Not sure if there really is a solution, but it could at least throw an error if the data format is 'non-contiguous' or something that is not going to work.
2

I faced the same problem with numpy 1.11.2 and opencv 3.3.0. Not sure why, but this did the job for me. Before using cv2.rectangle, add the line below:

image1 = image1.transpose((1,0)).astype(np.uint8).copy()

Reference

1 Comment

This is the same answer as the accepted one, except you implemented the rotation as a transpose operation followed by a copy.
1

Convert data type works for my problem. The image is of type np.int64 before the convert.

image = image.astype(np.int32) # convert data type

2 Comments

Please elaborate - you do this before or after the rotate, etc? If it's after, then this is the same as the previous answer, since casting it causes numpy to create a new array.
I do this before cv2.putText. cv2.putText is the line raises the TypeError in my problem. 'image.copy()' not work for me. I guess the Layout means the memory or data layout inside the numpy array which is diff between np.float64 and float32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.