In the last article we learnt about Color Spaces and Conversion. In this article, we are going to learn how to apply Arithmetic and Bitwise Operations on Image in Python using OpenCV library.
First, let’s go through the arithmetic operations.
Arithmetic Operations
Addition
This is the very basic and first topic of Arithmetic Operations on Image. The cv2.add() function is used to add two images or add a scalar to an image. It takes two images as arguments, or one image and a scalar value. Note that to add two images, both need to be of the same size.
You can add two images using the NumPy addition as well, i.e., result = img1 + img2. However, the results of the OpenCV function are much better.
We are going to add a football image to a football ground, as shown below.
But before adding them, we need to resize them to the same size. For that, we will use the cv2.resize() function. It takes the image that needs to be resized and the tuple containing the width and the height, i.e., (width, height). Let’s see.
import cv2 img1 = cv2.imread("ground.jpg") img2 = cv2.imread("football.jpg") img1 = cv2.resize(img1, (500, 400)) img2 = cv2.resize(img2, (500, 400)) cv2.imshow("Ground", img1) cv2.imshow("Football", img2) cv2.waitKey(0) cv2.destroyAllWindows()
Output
Both images have dimensions (400, 500, 3).
Now, let’s add them.
import cv2 img1 = cv2.imread("ground.jpg") img2 = cv2.imread("football.jpg") img1 = cv2.resize(img1, (500, 400)) img2 = cv2.resize(img2, (500, 400)) result_opencv = cv2.add(img1, img2) result_numpy = img1 + img2 cv2.imshow("Result OpenCv", result_opencv) cv2.imshow("Result Numpy", result_numpy) cv2.waitKey(0) cv2.destroyAllWindows()
Output
As you can see in the output, the result using the OpenCV function is a lot better than the manual addition. But the result is still not satisfactory. Most of the time, we do not want to add the exact values of both images. In other words, we want to have the effect of one image more than the other, i.e., add some transparency to images. Fortunately, OpenCV provides a function to do that. The cv2.addWeighted() function allows us to add weights to images. i.e.,
result = w1 × img1 + w2 × img2 + c
Where w1 is the weight applied to the img1, w2 is the weight applied to the img2, and c is a scalar value to increase (when c is positive) or decrease (when c is negative) brightness.
The function takes 5 arguments according to the above equation, i.e., cv2.addWeighted(img1, w1, img2, w2, c).
Let’s try to improve the above result using this function.
import cv2 img1 = cv2.imread("ground.jpg") img2 = cv2.imread("football.jpg") img1 = cv2.resize(img1, (500, 400)) img2 = cv2.resize(img2, (500, 400)) result= cv2.addWeighted(img1, 0.5, img2, 0.5, 0) cv2.imshow("Result OpenCv", result) cv2.waitKey(0) cv2.destroyAllWindows()
Output
As you can see, it has improved a lot.
Subtraction
The cv2.subtract() function is used to subtract one image from another or a scalar value from an image. Like, cv2.add(), both need to be of the same size.
Image subtraction is usually used to compare two images, i.e., highlight differences between two images.
Use the cv2.subtract() method instead of the manual subtraction, i.e., img1-img2, because it provides better results.
Let’s find the difference between the following two images.
import cv2 img1 = cv2.imread("dog1.jpg") img2 = cv2.imread("dog2.jpg") img1 = cv2.resize(img1, (500, 400)) img2 = cv2.resize(img2, (500, 400)) result= cv2.subtract(img1, img2) cv2.imshow("Result", result) cv2.waitKey(0) cv2.destroyAllWindows()
Output
In the above example, first, we read the above two images. Then, we resize them to the same size, i.e., (400, 500, 3). After that, we subtract img2 from img1 and display the result.
We have discussed the first part of the Arithmetic and Bitwise Operations on Image. Now we will discuss Bitwise operation.
Bitwise Operations
Before performing the bitwise operations, first, let’s create two binary images. We will apply bitwise operations on them. Consider the code below.
import cv2 import numpy as np img1 = np.zeros((400, 400, 3), dtype = np.uint8) img2 = np.zeros((400, 400, 3), dtype = np.uint8) img1 = cv2.rectangle(img1, (150, 150), (250, 250), (255,255, 255), -1) img2 = cv2.rectangle(img2, (0, 200), (400, 400), (255,255, 255), -1) cv2.imshow("img1", img1) cv2.imshow("img2", img2) cv2.waitKey(0) cv2.destroyAllWindows()
In the above example, first, we import numpy as np. Then, we use the np.zeros() function to create two images with dimensions (400, 400, 3). All the pixels in them are black, i.e., zero. Then, we use the cv2.rectangle() method to create a white region in them. It takes the image, (x, y) coordinates of the top left and bottom right rectangle, color, and thickness. In our case, the color is white, and the thickness is -1 because we want to fill the region with the white color. The resulting images are shown below.
Let’s now perform the bitwise AND, OR, XOR, and NOT operations on these images. The bitwise operators work similarly as the logical operators do. Let’s see each of them.
Bitwise AND Operation
In the AND operation, the resulting pixel is white (or 1 in the truth table given below) when the corresponding pixels in both the images are white. Otherwise, the resulting pixel has the black (0 in the truth table) color. The cv2.bitwise_and() function is used to perform the AND operation. Consider the table below to see how the AND operation works. Note that for images, 0 is black, and 1 is white.
A | B | AND |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
import cv2 import numpy as np img1 = np.zeros((400, 400, 3), dtype = np.uint8) img2 = np.zeros((400, 400, 3), dtype = np.uint8) img1 = cv2.rectangle(img1, (150, 150), (250, 250), (255,255, 255), -1) img2 = cv2.rectangle(img2, (0, 200), (400, 400), (255,255, 255), -1) bit_and = cv2.bitwise_and(img1,img2) cv2.imshow("Bitwise AND", bit_and) cv2.waitKey(0) cv2.destroyAllWindows()
Output
Bitwise OR Operation
In the OR operation, the resulting pixel has the black color (here, 0 in the truth table) when the corresponding pixels in both images are black. Otherwise, it is white (1 in the truth table). The cv2.bitwise_or() function is used for this.
A | B | OR |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
import cv2 import numpy as np img1 = np.zeros((400, 400, 3), dtype = np.uint8) img2 = np.zeros((400, 400, 3), dtype = np.uint8) img1 = cv2.rectangle(img1, (150, 150), (250, 250), (255,255, 255), -1) img2 = cv2.rectangle(img2, (0, 200), (400, 400), (255,255, 255), -1) bit_or = cv2.bitwise_or(img1,img2) cv2.imshow("Bitwise OR", bit_or) cv2.waitKey(0) cv2.destroyAllWindows()
Output
Bitwise XOR Operation
The cv2.bitwise_xor() function performs the XOR operation. It returns False when both the images have the same values, i.e., both are black, or both are white.
A | B | XOR |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
import cv2 import numpy as np img1 = np.zeros((400, 400, 3), dtype = np.uint8) img2 = np.zeros((400, 400, 3), dtype = np.uint8) img1 = cv2.rectangle(img1, (150, 150), (250, 250), (255,255, 255), -1) img2 = cv2.rectangle(img2, (0, 200), (400, 400), (255,255, 255), -1) bit_xor = cv2.bitwise_xor(img1,img2) cv2.imshow("Bitwise XOR", bit_xor) cv2.waitKey(0) cv2.destroyAllWindows()
Output
Bitwise NOT Operation
The NOT operation is quite straightforward. It just inverts the pixel value. If it is black (here, 0 in the truth table), the corresponding pixel in the resulting image will be white (1 in the truth table). The cv2.bitwise_not() performs the NOT operation.
A | NOT |
0 | 1 |
1 | 0 |
import cv2 import numpy as np img1 = np.zeros((400, 400, 3), dtype = np.uint8) img2 = np.zeros((400, 400, 3), dtype = np.uint8) img1 = cv2.rectangle(img1, (150, 150), (250, 250), (255,255, 255), -1) img2 = cv2.rectangle(img2, (0, 200), (400, 400), (255,255, 255), -1) cv2.imshow("img1", img1) cv2.imshow("img2", img2) bit_not_img1 = cv2.bitwise_not(img1) bit_not_img2 = cv2.bitwise_not(img2) cv2.imshow("Bitwise NOT on img1", bit_not_img1) cv2.imshow("Bitwise NOT on img2", bit_not_img2) cv2.waitKey(0) cv2.destroyAllWindows()
Output
Masking
Performing bitwise operations on images has many applications. For example, we can manipulate it and extract its desired part. Moreover, the bitwise AND operation can be used in image masking. Image masking allows us to focus on a specific region in an image. It can be done using the bitwise AND operation. Let’s extract the football from the above football.jpg image.
import cv2 import numpy as np import matplotlib.pyplot as plt img1 = cv2.imread("football.jpg") img1 = cv2.resize(img1, (500, 400)) mask = np.zeros(img1.shape, dtype = img1.dtype) mask = cv2.rectangle(mask, (243, 190), (323, 275), (255,255, 255), -1) result = cv2.bitwise_and(img1,mask) cv2.imshow("Result", result) cv2.waitKey(0) cv2.destroyAllWindows()
In the example above, we import NumPy to create a mask image using the np.zeros() function. It contains the same dimensions and the type as the img1, and all values are equal to zero. To get the desired region in the img1 (football), we need to make that region white in the mask image. For that, we use the cv2.rectangle() method. It is shown below.
Now that we have the mask image, let’s apply the bitwise AND operation between the img1 and the mask. The cv2.bitwise_and() chooses the smaller value from the given two values when performing the AND operation in a non-binary image. Therefore, all the pixels, excluding those in the football region, will become zero in the resulting image because the corresponding values in the mask image are zero. Moreover, in the football region, cv2.bitwise_and() will take values of the img1 because the mask image contains 255 (the maximum pixel value) everywhere in that region. Therefore, values in the img1 will either be smaller or equal to 255. The final result is shown below.
That’s all about Arithmetic and Bitwise Operations on Image. Cool, right?