What does bitwise_and operator exactly do in openCV? What does bitwise_and operator exactly do in openCV? python python

What does bitwise_and operator exactly do in openCV?


The general usage is that you want to get a subset of an image defined by another image, typically referred to as a "mask".

So suppose you want to "grab" the top left quadrant of an 8x8 image. You could form a mask that looks like:

1 1 1 1 0 0 0 01 1 1 1 0 0 0 01 1 1 1 0 0 0 01 1 1 1 0 0 0 00 0 0 0 0 0 0 00 0 0 0 0 0 0 00 0 0 0 0 0 0 00 0 0 0 0 0 0 0

You could produce the above image with Python with:

import numpy as npmask = np.zeros(shape=(8,8), dtype=bool)mask[0:4,0:4] = True

Then suppose you had an image like:

1 0 1 0 1 1 1 10 1 0 1 0 0 0 01 0 1 0 1 1 1 10 1 0 1 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 0

For concreteness, imagine that the above image is a simplified representation of the U.S.A. flag: stars in the top left, bars everywhere else. Suppose you wanted to form the above image. You could use the mask, and bitwise_and and bitwise_or to help you.

imageStars = np.ones(shape=(8,8), dtype=bool)for r, row in enumerate(imageStars):    for c, col in enumerate(row):        if r % 2 != c % 2: # even row, odd column, or odd row, even column            imageStars[r,c] = FalseimageBars = np.zeros(shape=(8,8), dtype=bool)for r, row in enumerate(imageStars):    if r % 2 == 0:        imageBars[r,:] = True

Now you have an image of stars:

1 0 1 0 1 0 1 00 1 0 1 0 1 0 1    1 0 1 0 1 0 1 00 1 0 1 0 1 0 1    1 0 1 0 1 0 1 00 1 0 1 0 1 0 1    1 0 1 0 1 0 1 00 1 0 1 0 1 0 1

And an image of bars:

1 1 1 1 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 0

And you want to combine them in a particular way, to form the flag, with the stars in the upper left quadrant and the bars everywhere else.

imageStarsCropped = cv2.bitwise_and(imageStars, mask)

imageStarsCropped will look like:

1 0 1 0 0 0 0 00 1 0 1 0 0 0 0    1 0 1 0 0 0 0 00 1 0 1 0 0 0 0  0 0 0 0 0 0 0 00 0 0 0 0 0 0 00 0 0 0 0 0 0 00 0 0 0 0 0 0 0

Do you see how it was formed? The bitwise_and returns 1 at every pixel where imageStars is 1 AND mask is 1; else, it returns 0.

Now let's get imageBarsCropped. First, let's reverse the mask:

maskReversed = cv2.bitwise_not(mask)

bitwise_not turns 1's into 0's and 0's into 1's. It "flips the bits". maskReversed will look like:

0 0 0 0 1 1 1 10 0 0 0 1 1 1 10 0 0 0 1 1 1 10 0 0 0 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 1

Now, we will use maskReversed to "grab" the portion of imageBars that we want.

imageBarsCropped = cv2.bitwise_and(imageBars, maskReversed)

imageBarsCropped will look like:

0 0 0 0 1 1 1 10 0 0 0 0 0 0 00 0 0 0 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 0

Now, let's combined the two "cropped" images to form the flag!

imageFlag = cv2.bitwise_or(imageStarsCropped, imageBarsCropped)

imageFlag will look like:

1 0 1 0 1 1 1 10 1 0 1 0 0 0 01 0 1 0 1 1 1 10 1 0 1 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 01 1 1 1 1 1 1 10 0 0 0 0 0 0 0

Do you see why? bitwise_or returns 1 whenever imageStarsCropped[r,c]==1 OR imageBarsCropped[r,c]==1.

Well, I hope this helps you to understand bitwise operations in OpenCV. These properties have a one-to-one correspondence with bitwise operations with binary numbers that the computer does to do arithmetic.


What does the operator do?

bitwise_and, bitwise_or and bitwise_xor perform a bitwise operation on elements taken from two arrays, src1 nd src2. bitwise_not is similar. Bitwise means the boolean operation is done between each bit of the values, bit per bit.

What is the mask parameter?

It's actually not a Boolean mask (and a Boolean array will be rejected). It's a uint8 array type where values are checked as being 0 or not. The "mask" has the same x,y shape than the images, but its elements are scalar, that is for an image 100 x 50 x 3 of uint8, the mask must be an array 100 x 50 of uint8.

How is the mask used?

This mask determines whether the operation will be performed on the pair of pixels at location x,y. If the element at position x,y in the mask is 0, no operation is performed, and the pixel in the resulting array is 0,0,0 (black). If the mask element at position x,y is not null then the bitwise operation determines the value in the resulting array.

Let's say we want to extract pixels within a circle and make other pixels white. This can be done using to bitwise operation with complementary masks:

openCV bitwise_and and bitwise_or

import numpy as npimport cv2import matplotlib.pyplot as pltimages = 'images/'# Read image 330 x 379 x 3 uint8img = cv2.imread(images + 'sample.jpg')# Reorder channels as display is done with pyplotimg[:,:,[0,1,2]] = img[:,:,[2,1,0]]# Create white image, 3 channels 330 x 379 x 3 uint8w_3c = np.full_like(img, fill_value=(255,255,255))# Define disk elementscenter = (img.shape[1]//2, img.shape[0]//2)radius = int(min(center) * .9)# Create white disk, 3 channels 330 x 379 x 3 uint8# and single channel 330 x 379 uint8d_3c = np.zeros_like (img[:,:], dtype='uint8')cv2.circle(d_3c, center, radius, [255]*3, thickness=cv2.FILLED)d_1c = d_3c[:,:,0]# Extract pixels disk using white disk single channelmasked = cv2.bitwise_and(img, w_3c, mask=d_1c)# Add white backgroundd_3c_i = ~d_3cfinal = cv2.bitwise_or(img, d_3c_i)# Prepare to plotto_plot = [[(img,'img'),            (w_3c,'w_3c')],           [(d_3c,'d_3c'),            (d_1c,'d_1c'),            (d_3c_i,'d_3c_i')],           [(masked,'img & w_3c mask d_1c'),            (final,'img | d_3c_i)')]]r = len(to_plot)c = max([len(l) for l in to_plot])# Show everthingfig,ax = plt.subplots(r,c, tight_layout=True)for i in range(r):    for j in range(c):        axij = ax[i,j]        if j < len(to_plot[i]):            dims = to_plot[i][j][0].ndim            if dims <= 3:                axij.imshow(to_plot[i][j][0], cmap='gray')            else:                axij.imshow(to_plot[i][j][0])            axij.set_title(to_plot[i][j][1])            axij.set_xticks([])            axij.set_yticks([])        else:            axij.set_axis_off()plt.ioff()plt.show()

The code can probably be improved.


Just an elaboration to the above answers - not a full answer. In real life image arrays will have values from 0 to 255, and not just 0s and 1s. What bitwise_or does in this case is to convert each corresponding number in the two images to its binary form, and then do an or or and or other operation.

Example:

Consider two values 233 and 180. A bitwise_or of these numbers gives us 253 (using cv2.bitwise_or(np.array([233]), np.array([180])). The binary equivalents of 233 and 180 are 11101001 and 10110100. Doing a bitwise_or gives us 11111101 which is the same as 253. This number was obtained by doing an or for each digit in the binary equivalents of 233 and 180 (you can verify)

bin(233) #0b in the output just means it is a binary number0b11101001bin(180)0b10110100bin(253)0b11111101cv2.bitwise_or(np.array([233]), np.array([180]))array([[253]], dtype=int32)

and, not and xor work similarly as or by doing the logic operations on the bits of the numbers.