Image Processing using OpenCV
Published: 2021-09-08 20:06:57

## Introduction
<p>
OpenCV is an open source and very powerful library when it comes to Image Processing. In the following post, I will do some basic Operations on Images using OpenCV
</p>

## Prerequisite
Before we start, we need to specify the prerequisites
<ol>
<li>Google Collab or Jupyter Notebook</li>
<li>Basic Knowledge of Python</li>
</ol>

## Required Imports
These are the imports we require to start our journey
```py
import sys
import numpy as np
import cv2
from google.colab.patches import cv2_imshow
from PIL import Image 
import math
```
The next step we need to do is Import our image
```py
img = cv2.imread("img1opencv.jpg", cv2.IMREAD_COLOR)
cv2_imshow(img)
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/929doe7dy65c60hf1z9v.png)
 
This would Import the Image on which we will perform the Operations

## 1. Topological Operations
We need to find out the distance b/w 2 pixels
We do the following by finding colors at pixels and selecting a color (Note - The image is 3 Channel RGB image)
```py
color1=np.array([169,169,169],dtype=np.uint8)
color1List=np.where(np.all((npimg==color1),axis=-1))
color1List
```
This selects pixels with the color code (169,169,169).
Now we need a second pixel, so we pick another color from the image from that.

```py
color2=np.array([34,  35,  39],dtype=np.uint8)
color2List=np.where(np.all((npimg==color2),axis=-1))
color2List
```

Finally, we use the Euclidean formula to find the distance b/w 2 pixels.
```py
dx2 = (color2List[0][0]-color1List[0][0])**2
dy2 = (color2List[1][0]-color1List[1][0])**2 
distance = math.sqrt(dx2 + dy2)
distance
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5j7s03hkxjw50v533jql.png)

##2. Point Operations
### a. Image Negatives
Finding Image Negatives is very easy with OpenCV. It can be done by a simple command of
```py
L = npimg.max()
imgNeg = L-img
cv2_imshow(imgNeg)
```
Which results in this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fgikyriizncxshgjtbt.png)

### b. Image Transform - Power Law
Power Law deals with Gamma levels of Images. It can be done by
```py
for gamma in [0.1, 0.5, 1.2, 2.2]:
  gamma_corrected_img = np.array(255*(npimg / 255) ** gamma, dtype = 'uint8')
  cv2_imshow(gamma_corrected_img)
```
We do this by changing the gamma levels to 0.1, 0.5, 1.2 and 2.2
which results in the following:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v5l7n0nftbjo3jvw457c.png)
**Gamma 0.1**
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sv9qv5jdbmph75hzpigh.png)
**Gamma 0.5**
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/28tjopumg0pzr5cpu4ru.png)
**Gamma 1.2**
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otqnd02oolh58zznu838.png)
**Gamma 2.2**

### c. Piece Wise Linear Transformation Functions: Grey Level slicing, Contrast Stretching

We will first start with **Grey level slicing**.
We do the following by first specifying a min and max threshold values. Let's assume our values to be 100 and 180. 
```py
T1 = 100

T2 = 180 

h,w,c = img.shape

img_thresh_back = np.zeros((h,w), dtype = int)
   
for i in range(h):
    for j in range(w):  
        if (T1 < npimg[i,j]).any() and (npimg[i,j] < T2).any():  
            img_thresh_back[i,j]= 255
        else:
            img_thresh_back[i-1,j-1] = img[i-1,j-1]

cv2_imshow(img_thresh_back)
```
Which results in:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sgti190eio5vkdhprw5k.png)

Now, we will move on to **Contrast Stretching**:
Contrast Stretching applies stretches the contrast across the image, which results in the image to look like this:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3dgw80ugodvwa9xieq3.png)

The code to do this is
```py
def pixelVal(pix, r1, s1, r2, s2):
    if (0 <= pix and pix <= r1):
        return (s1 / r1)*pix
    elif (r1 < pix and pix <= r2):
        return ((s2 - s1)/(r2 - r1)) * (pix - r1) + s1
    else:
        return ((255 - s2)/(255 - r2)) * (pix - r2) + s2

r1 = 66
s1 = 0
r2 = 152
s2 = 255

pixelVal_vec = np.vectorize(pixelVal)

contrast_stretched = pixelVal_vec(npimg, r1, s1, r2, s2)

cv2_imshow(contrast_stretched)
``` 
## 3. Neighborhood Operation (neighbor size n*n)
This operation first requires us to crop the image in n*n size
We do so by
```py
img.shape
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aoym95rcg3nist2460w0.png)

Now we create a cropped image using the shape to create a square matrix

```py
crop_img = img[0:2581, 0:2581]
```

There are a lot of operations that can be done using this, including using the Average neighbor value, but I was facing difficulty doing that, so we will proceed with using the Minimum neighbor.
This can be done using 
```py
for i in crop_img:
  for j in i:
    min = np.min(j)
    j[0] = min
    j[1] = min
    j[2] = min
cv2_imshow(crop_img)
```
which results in our cropped image to look like
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5bfgmcevmrcui5134kw2.png)

## 4. Operations on set of Images
Now we move on to the most fun operations that can be done using OpenCV.
### a. Arithmetic operations - Addition and Division
We will load a new image for this operation and resize both images to the same resolution.
I am going for 512 x 512
```py
img1_resize = cv2.resize(img, (512,512))
img2_resize = cv2.resize(img2, (512,512))
```
Here img2 is the new image we loaded.
Now we can perform **Addition** on this image
```py
imgAdd= cv2.addWeighted(img1_resize,0.5,img2_resize,0.5,0)
cv2_imshow(imgAdd)
```
Resulting in:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v22i0dwxl3jhso02uv1o.png)

Now we move on to **Division,** dividing image 1 from image 2 and then image 2 from image 1
```py
img1DivImg2 = cv2.divide(img1_resize, img2_resize)
cv2_imshow(img1DivImg2)
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0m0bkosstx3rkdabed6p.png)
```py
img2DivImg1 = cv2.divide(img2_resize, img1_resize)
cv2_imshow(img2DivImg1)
``` 
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyhotnq0abz75ky1upiy.png)

### b. Logical Operations: XOR, NOT
XOR and NOT operation can be performed easily on any image using OpenCV's inbuilt functions
```py
img_xor = cv2.bitwise_xor(img1_resize, img2_resize, mask = None)
cv2_imshow(img_xor)
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bfivt0t894d3soiz7yhi.png)
 
And NOT operation can be done by
```py
img_not = cv2.bitwise_not(img1_resize, img2_resize, mask = None)
cv2_imshow(img_not)
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ur82xnitij02uqr9ighn.png)

### Geometrical Operations - Rotation, affine transformation
**Rotation** is a very basic transformation which can easily be done by OpenCV.
```py
rows,cols, height = img.shape
M = cv2.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),90,1)
dst = cv2.warpAffine(img,M,(cols,rows))
cv2_imshow(dst)
``` 
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bfda2ekf0zmfygsmcxpu.png)

**Affine Transformation** is a little tedious task which requires some extra operations to be performed before on the image.
It results in changing the perspective of the image.
```py
srcTri = np.array( [[0, 0], [img.shape[1] - 1, 0], [0, img.shape[0] - 1]] ).astype(np.float32)
dstTri = np.array( [[0, img.shape[1]*0.33], [img.shape[1]*0.85, img.shape[0]*0.25], [img.shape[1]*0.15, img.shape[0]*0.7]] ).astype(np.float32)
warp_mat = cv2.getAffineTransform(srcTri, dstTri)
warp_dst = cv2.warpAffine(img, warp_mat, (img.shape[1], img.shape[0]))
cv2_imshow(warp_dst)
```
This is the result of this operation
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tfw037j015tnr2bikkfn.png)

### d. Statistical Operations - Mean and Variance
Finding mean and variance of an image is very easy since an Image is nothing but a NumPy array
Therefore, we can do this by
```py
mean = np.mean(img)
mean
variance = np.var(img)
variance
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hh4gdstqmamty0jerqoz.png)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8g3z9jk79mgfqi72l2y.png)

### e. Image Interpolation -  Down Sampling
We move on to our last operation for today, which is down sampling.
This is required to decrease the overall resolution of the image.
It can be done by
```py
print(img.shape)
img_down = cv2.pyrDown(img)
print(img_down.shape)
```  
The resultant image has a smaller shape, but the visible result won't be a noticeable change.

## Conclusion
This concludes my post about OpenCV. I hope you did enjoy going through it, although it being a bit lengthy.
Here is a Google Collab link for all the operations performed here.
[Google Collab Link](https://colab.research.google.com/drive/1597qwuQCFqfBhCTEhTQNB-Mma0Ao_ppo?usp=sharing)

#Thank You