I did not exactly understand what the "bitwise_and" operator does when used in openCV. I would also like to know about it's parameters.
bitwise_and() function is used to Compute the bit-wise AND of two array element-wise. This function computes the bit-wise AND of the underlying binary representation of the integers in the input arrays. Parameters : arr1 : [array_like] Input array.
You can add two images with the OpenCV function, cv. add(), or simply by the numpy operation res = img1 + img2. Both images should be of same depth and type, or the second image can just be a scalar value.
Performing masking on color images: You can mask out a certain region of a given color image using the same function as well. Consider the following image: and consider Screen 1 (given above) to be the mask fin = cv2. bitwise_and(image, image, mask = screen1) cv2. imwrite("Masked image.jpg", fin)
bitwise_not function. Essentially, the bitwise NOT function flips pixel values. All pixels that are greater than zero are set to zero, and all pixels that are equal to zero are set to 255 : Figure 6: Applying a bitwise NOT with 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 0
1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
You could produce the above image with Python with:
import numpy as np
mask = 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 1
0 1 0 1 0 0 0 0
1 0 1 0 1 1 1 1
0 1 0 1 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 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] = False
imageBars = 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 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
And an image of bars:
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 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 0
0 1 0 1 0 0 0 0
1 0 1 0 0 0 0 0
0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 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 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 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 1
0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 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 1
0 1 0 1 0 0 0 0
1 0 1 0 1 1 1 1
0 1 0 1 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 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:
import numpy as np
import cv2
import matplotlib.pyplot as plt
images = 'images/'
# Read image 330 x 379 x 3 uint8
img = cv2.imread(images + 'sample.jpg')
# Reorder channels as display is done with pyplot
img[:,:,[0,1,2]] = img[:,:,[2,1,0]]
# Create white image, 3 channels 330 x 379 x 3 uint8
w_3c = np.full_like(img, fill_value=(255,255,255))
# Define disk elements
center = (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 uint8
d_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 channel
masked = cv2.bitwise_and(img, w_3c, mask=d_1c)
# Add white background
d_3c_i = ~d_3c
final = cv2.bitwise_or(img, d_3c_i)
# Prepare to plot
to_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 everthing
fig,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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With