I agree it would be more natural if something this worked:
img1_bg = cv2.bitwise_and(roi, mask_inv) # will not work
#raises cv2.error: /modules/core/src/arithm.cpp:225: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and type), nor 'array op scalar', nor 'scalar op array' in function 'binary_op'
And the first question is why doesn't it. The thing is, the roi
and mask_inv
arrays have different shapes: (rows, cols, channels)
vs (rows, cols, 1)
. I guess, the authors of the OpenCV library wanted to prevent silent shape casting, which may be unsafe and introduce hard to catch bugs, and decided to implement the check, that the first two arguments are of the same shape.
The second question is why this works:
img1_bg = cv2.bitwise_and(roi, roi, mask_inv)
In short, the answer is that for any array of integers (like roi
) it is always true that
roi == cv2.bitwise_and(roi, roi)
This holds due to the fact that 0 & 0 == 0
and 1 & 1 == 1
for the bits. But the cv2.bitwise_and
function is actually a combination of two:
- Apply bitwise and to the images from the first two arguments
- Apply mask to the result, i.e. make equal to zeroes (== black) any pixel, where mask is zero.
In this typical usage you present, the first part of this function is not used, the two arguments make this part work like identity, it doesn't change the array. So you kind of bypass the first part with a trick of putting the first two arguments equal. The second part is the only one, which is applied in this typical usage.
P.S. Please, see the answers, linked in the comments for more info.