pythonarraysnumpyopencvmemory-layout

Error in cv2.rectangle after flipping image with slicing: 'Layout of the output array img is incompatible with cv::Mat'


I am using OpenCV to draw a rectangle on a binary image. My goal is to draw the rectangle on both the original image and its vertically flipped version. However, I encounter an issue when flipping the image using the slicing method. Here’s the code snippet I am using:

import cv2
import numpy as np

# Create a binary image
binary_image = np.zeros((100, 100), dtype=np.uint8)
cv2.rectangle(binary_image, (10, 10), (30, 30), (255), -1)

# Flip the image using slicing
flipped_slicing = binary_image[::-1]
# Try to draw a rectangle on the flipped image
cv2.rectangle(flipped_slicing, (10, 10), (30, 30), (255), -1)

# Flip the image using cv2.flip
flipped_cv2 = cv2.flip(binary_image, 0)
# Draw a rectangle on the flipped image
cv2.rectangle(flipped_cv2, (10, 10), (30, 30), (255), -1)

The issue is that cv2.rectangle works perfectly on the image flipped with cv2.flip, but it fails when using the slicing method [::-1]. I expected both methods to produce similar results. Can anyone explain why this difference occurs and how to resolve it?

flipped_slicing_method = binary_image[::-1]
flipped_cv = cv2.flip(binary_image, 0)
print(np.all(flipped_slicing_method==flipped_cv))
>> True

But when I run this code, I get an error.

cv2.rectangle(flipped_slicing_method, (0,0), (400, 400), 255,-1)

the Error is:

error: OpenCV(4.9.0) :-1: error: (-5:Bad argument) in function 'rectangle'
> Overload resolution failed:
>  - Layout of the output array img is incompatible with cv::Mat
>  - Expected Ptr<cv::UMat> for argument 'img'
>  - Layout of the output array img is incompatible with cv::Mat
>  - Expected Ptr<cv::UMat> for argument 'img'

Solution

  • A look at the flags attribute of the Numpy arrays, shows that slicing gives non contiguous data, and that the result does not own this data:

    >>> flipped_slicing.flags
      C_CONTIGUOUS : False
      F_CONTIGUOUS : False
      OWNDATA : False
      WRITEABLE : True
      ALIGNED : True
      WRITEBACKIFCOPY : False
    
    >>> flipped_cv2.flags
      C_CONTIGUOUS : True
      F_CONTIGUOUS : False
      OWNDATA : True
      WRITEABLE : True
      ALIGNED : True
      WRITEBACKIFCOPY : False
    

    This is confirmed by the strides attributes:

    >>> flipped_slicing.strides
    (-100, 1)
    
    >>> flipped_cv2.strides
    (100, 1)
    

    OpenCV can operate on non-contiguous arrays, but it requires the stride (called "step" in the documentation) to be non negative. So your code will work with positive slicing, but not with negative one, as you need for your flipping operation.