1. OpenCV with Python

1.1. OpenCV Basics

1.1.1. Introduction

OpenCV-3 is used in this tutorial which can be installed using below command,

pip install opencv-python==3.4.5.20

1.1.2. Load image

  • See comments for details,
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# load_save_image.py

import cv2

# read image
img = cv2.imread("images/shapes.jpg")

# show image
cv2.imshow("Shapes", img) # Window name -> Shapes

# wait for key before closing the window
cv2.waitKey(0)

# save image
cv2.imwrite("images/saved_by_opencv.jpg", img)
  • Run the code
$ python load_save_image.py
../_images/Screenshot_16.jpg

Fig. 1.1 Shapes

1.1.3. Load Video

  • cv2.VideoCapture(0) is use to show the video which is captured by webcam,
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# load_video.py

import numpy as np
import cv2

# load video
cap = cv2.VideoCapture("images/timer.mp4")

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()

    # Display the resulting frame
    cv2.imshow('frame',frame)
    if cv2.waitKey(30) & 0xFF == ord('q'): # press q to exit
        break

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
$ python load_video.py

1.1.4. Basic operations on images

1.1.4.1. Accessing and modifying pixel

  • In images, the pixel coordinates starts from (0, 0).
  • [B, G, R] format is used in OpenCV.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# access_modify_pixel.py


import cv2
import numpy as np


# read image
img = cv2.imread("images/shapes.jpg")


# pixel at point [10, 10] = white i.e. 255, 255, 255
px = img[10, 10]
print("original pixel: ", px) # [255 255 255]
cv2.imshow("Shapes", img)


# modify pixel to red : a dot can be seen in the image
img[10, 10] = (0, 0, 255)
px = img[10, 10]
print("Modified pixel: ", px) # [255 0 0]
cv2.imshow("Red dot at (10 10)", img)


# access the shape of the image
(h, w) = img.shape[:2] # height and width of image
print("height={}, width={}".format(h,w)) # height=360, width=640

print("Image size = ", img.size) # size of image = h*w*3 = 691200


cv2.waitKey(0)
../_images/Screenshot_21.jpg

Fig. 1.2 Red dot at (10 10)

1.1.4.2. Split and Merge

  • In this section, the color image is split and plotted into R, G and B color. Also, these R, G and B are merged together to get the original image.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# split_merge.py

# square is of red color: R = 255 (i.e. white), B & G = 0 (i.e. black)

# circle is of yellow color: R & G = 255 (i.e. white), B = 0 (i.e. black)

# triangle is purple: a mix of R & B with different ratio; therefore a different
# gray-shades for R and B (more of blue therefore lighter-gray shade) will be shown;
# whereas G = 0 (i.e. black)

import cv2
import numpy as np


# read image
img = cv2.imread("images/shapes.jpg")
(h, w) = img.shape[:2] # height and width of image

# split image into BGR
(B, G, R) = cv2.split(img)

# show B, G, R channels
cv2.imshow("Shapes", img)
cv2.imshow("Blue", B)
cv2.imshow("Green", G)
cv2.imshow("Red", R)

merge_img = cv2.merge([B, G, R])
cv2.imshow("Merged BGR", merge_img)


cv2.waitKey(0)
../_images/Screenshot_31.jpg

Fig. 1.3 Split and merge

1.1.4.3. Crop image

In this section, we will crop the image in 4 equal part and change the color of 2 parts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# crop_img.py

import cv2

# read image
img = cv2.imread("images/shapes.jpg")
cv2.imshow("Shapes", img) # display image

# Shape = (width, height, channel);  channel = 3 i.e. B, G, R
print("Image shape: ", img.shape) # Image shape:  (360, 640, 3)


# extract height and width i.e. first two values (360, 640)
(h, w) = img.shape[:2]
print("Heigth = {}, Width = {}".format(h, w)) # Heigth = 360, Width = 640

#### Pixel values
# print pixel value (B, G, R) at [0, 0]
print("(B G R) = ", img[0, 0]) # (B G R) =  [255 255 255] i.e. white
# print pixel value (B, G, R) at [40, 310]
print("(B G R) = ", img[40, 310]) # (B G R) =  [  0   0 254] i.e. red


### Crop image

# center point of image
# note that we will use the cX and cY as pixel location
# therefore these need to be an integer value, hence // is used
(cX, cY) = (w//2, h//2)

# top left i.e. 0-to-cY and 0-to-cX
top_left = img[0:cY, 0:cX]
cv2.imshow("Top Left", top_left) # display image

top_right = img[0:cY, cX:w]
cv2.imshow("Top Right", top_right) # display image

bottom_left = img[cY:h, 0:cX]
cv2.imshow("Bottom Left", bottom_left) # display image

bottom_right = img[cY:h, cX:w]
cv2.imshow("Bottom Right", bottom_right) # display image

cv2.waitKey(0)


### change color for cropped sections
img[cY:h, cX:w] = [255, 0, 0] # bottom right to Blue color
cv2.imshow("Bottom Right", bottom_right) # display image

# Green + Red = Yellow
img[0:cY, 0:cX] = [0, 255, 255] # Yellow color for top-left
cv2.imshow("Top Left", top_left) # display image


cv2.waitKey(0)
../_images/Screenshot_41.jpg

Fig. 1.4 Crop image

1.1.4.4. Image arithmetic

  • OpenCV sets the maximum and minimum as 255 and 0 respectively.
  • Numpy does the modulo addition.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# image_arith.py

import numpy as np
import cv2

x = np.uint8([250])
y = np.uint8([10])

print("OpenCV 250 + 10: ", cv2.add(x,y)) # 250+10 = 260 => 255
print("Numpy 250 + 10: ", x+y)          # 250+10 = 260 % 256 = 4


img = cv2.imread("images/shapes.jpg")
cv2.imshow("Shapes", img)


print("Initial pixel at [50, 50]\t: ", img[50, 50])
new_pixel =  90 * np.ones(img.shape, dtype = "uint8")

print("Add/subtract 90")

opencv_img = cv2.add(img, new_pixel)
print("OpenCV addition pixel at [50, 50]\t: ", opencv_img[50, 50])
cv2.imshow("OpenCV add", opencv_img)

opencv_img = cv2.subtract(img, new_pixel)
print("OpenCV subtract pixel at [50, 50]\t: ", opencv_img[50, 50])
cv2.imshow("OpenCV subtract", opencv_img)


numpy_img = img + new_pixel
print("Numpy addition pixel at [50, 50]\t: ", opencv_img[50, 50])
cv2.imshow("Numpy add", numpy_img)

numpy_img = img - new_pixel
print("Numpy subtract pixel at [50, 50]\t: ", opencv_img[50, 50])
cv2.imshow("Numpy subtract", numpy_img)

cv2.waitKey(0)
  • Output will be as below,
OpenCV 250 + 10:  [[255]]
Numpy 250 + 10:  [4]
Initial pixel at [50, 50]   :  [  1 255   0]
Add/subtract 90
OpenCV addition pixel at [50, 50]   :  [ 91 255  90]
OpenCV subtract pixel at [50, 50]   :  [  0 165   0]
Numpy addition pixel at [50, 50]    :  [  0 165   0]
Numpy subtract pixel at [50, 50]    :  [  0 165   0]
../_images/Screenshot_51.jpg

Fig. 1.5 Image arithmetic

1.1.4.5. Threshold

For every pixel, the same threshold value is applied. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# threshold_img.py

import cv2
import numpy as np


# read image
img = cv2.imread("images/rose.jpg")
cv2.imshow("Rose", img)

(T, thresh) = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)
cv2.imshow("Threshold Binary", thresh)


(T, thresh) = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("Threshold Binary Inverse", thresh)


cv2.waitKey(0)
../_images/Screenshot_61.jpg

Fig. 1.6 Threshold

1.1.5. Geometric Transformations

1.1.5.1. Scaling

Scaling is just resizing of the image. OpenCV comes with a function cv2.resize() for this purpose.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# scale_img.py

import cv2
import numpy as np


# read image
img = cv2.imread("images/shapes.jpg")
(h, w) = img.shape[:2] # height and width of image
cv2.imshow("Shapes", img) # display image

# scale by 0.5 in both x and y direction
scale_img = cv2.resize(img, (w//2, h//2), interpolation = cv2.INTER_CUBIC)
cv2.imshow("Resize Shapes", scale_img) # display image


cv2.waitKey(0)
../_images/Screenshot_71.jpg

Fig. 1.7 Resize or scaling

1.1.5.2. Flip

Three types of flips are possible,

  • 0 : Horizontal flip
  • 1 : Vertical flip
  • -1 : Both horizontal and vertical flip
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# flip_img.py

import cv2
import numpy as np


# read image
img = cv2.imread("images/shapes.jpg")
(h, w) = img.shape[:2] # height and width of image
cv2.imshow("Shapes", img) # display image

# flip horizontal
flip_horizontal = cv2.flip(img, 0)
cv2.imshow("Horizontal Flip", flip_horizontal) # display image

# flip vertical
flip_vertical = cv2.flip(img, 1)
cv2.imshow("Vertical Flip", flip_vertical) # display image

# flip vertical and horizontal both
flip_both = cv2.flip(img, -1)
cv2.imshow("Horizontal and Vertical Flip", flip_both) # display image


cv2.waitKey(0)
../_images/Screenshot_81.jpg

Fig. 1.8 Flip

1.1.5.3. Translation

Translation is the shifting of object’s location.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# translate_img.py

import cv2
import numpy as np

# translation matrix is defined as [1 0 t_x; 0 1 t_y]
# traslate/shift by t_x and t_y respectively

# shift by 30 (right) and 50 (down) in x and y direction respectively
# similarly -30 for left and -50 for upward shift
shift_matrix = np.float32([[1, 0, 30], [0, 1, 50]])


# read image
img = cv2.imread("images/shapes.jpg")
(h, w) = img.shape[:2] # height and width of image
cv2.imshow("Shapes", img) # display image


####### Now perform shift and rotate operation
shift_img = cv2.warpAffine(img, shift_matrix, (w, h))
cv2.imshow("Shifted Down and Right", shift_img)


cv2.waitKey(0)
../_images/Screenshot_91.jpg

Fig. 1.9 Translation

1
2
# shift by -30 and -50 in x and y direction respectively
shift_matrix = np.float32([[1, 0, 30], [0, 1, 50]])

1.1.5.4. Rotation

  • We need to define the rotation angle along with a point for rotation.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# rotate_img.py

import cv2
import numpy as np

# read image
img = cv2.imread("images/shapes.jpg")
(h, w) = img.shape[:2] # height and width of image
cv2.imshow("Shapes", img) # display image


# first define the point of rotation, e.g. (w/2, h/2) i.e. center of the image
(cX, cY) = (w/2, h/2)
# now define rotation matrix with 45 degree of rotation
rotation_matrix = cv2.getRotationMatrix2D((cX, cY), 45, 1.0)

# rotate and plot the image
rotated = cv2.warpAffine(img, rotation_matrix, (w, h))
cv2.imshow("Rotated by 45 Degrees", rotated)


cv2.waitKey(0)
../_images/Screenshot_101.jpg

Fig. 1.10 Rotation

1.1.6. Drawing

  • In this section, lines, rectangle, circle and ellipse are drawn using OpenCV.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# drawing_img.py

import cv2
import numpy as np


# read image
img = cv2.imread("images/shapes.jpg")
(h, w) = img.shape[:2] # height and width of image

# draw blue horizontal and vertical lines at the center of figure
# initial and final point are required to draw line
cv2.line(img,(0, h//2), (w, h//2), (255,0,0), 3) # horizontal line
cv2.line(img,(w//2, 0), (w//2, h), (255,0,0), 3) # vertical line

# draw rectangle
# top-left corner (5, 10) and bottom-right corner (200, 170) of rectangle
# points are calculated manually
cv2.rectangle(img, (5, 10), (200, 170),(0,0,250),3)


# draw circle
# center coordinates (w//2, h//2) and radius (50) are
# required to to draw circle. 10 is the line width
cv2.circle(img, (w//2, h//2), 50 , (0,0,0), 10) # black
cv2.circle(img, (w//2, h//2), 30, (0,0,255), -1) # -1 : filled circle


# draw ellipse
# center: (w//2, h//2)
# (major axis, minor axis): (100,50)
# direction of rotation: 0;  where 0 : anticlockwise, 1: clockwise
# start angle and end angle: 0, 360
# color: (0, 255, 0)
# width: 5 (-1 for filled)
cv2.ellipse(img, (w//2, h//2), (100,50), 0, 0, 360, (0, 255, 0), 5)


cv2.imshow("Shapes", img) # display image
cv2.waitKey(0)
../_images/Screenshot_112.jpg

Fig. 1.11 Drawing

1.1.7. Bitwise operation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# bitwise_img.py

import numpy as np
import cv2


# create and display frame of size 300
rectangle = np.zeros((300, 300), dtype = "uint8")
# display empty frame
cv2.imshow("Frame", rectangle)


# draw white rectangle
cv2.rectangle(rectangle, (20, 20), (280, 280), 255, -1)
cv2.imshow("Rectangle", rectangle)


# create rectangular frame of size 300x300 with name circle
circle = np.zeros((300, 300), dtype = "uint8")
# draw circle in rectangular frame
cv2.circle(circle, (150, 150), 150, 255, -1)
cv2.imshow("Circle", circle)


and_img = cv2.bitwise_and(circle, rectangle)
cv2.imshow("And", and_img)



# another example
rect1 = np.zeros((200, 400), dtype = "uint8")
rect2 = np.zeros((200, 400), dtype = "uint8")

rect1 = cv2.rectangle(rect1, (0, 200), (200, 0), 255, -1)
cv2.imshow("rect1", rect1);
rect2 = cv2.rectangle(rect2, (150, 100), (250, 150), 255, -1)
cv2.imshow("rect2", rect2);

result = cv2.bitwise_and(rect1, rect2);
cv2.imshow("AND", result);

result = cv2.bitwise_or(rect1, rect2);
cv2.imshow("OR", result);

result = cv2.bitwise_xor(rect1, rect2);
cv2.imshow("XOR", result);

result = cv2.bitwise_not(rect2);
cv2.imshow("rect2 NOT", result);

cv2.waitKey()
../_images/Screenshot_122.jpg

Fig. 1.12 Bitwise operation

1.1.8. Masking

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# mask_img.py

import cv2
import numpy as np


# Load two images
img = cv2.imread('images/shapes.jpg')
cv2.imshow("Shapes", img)


# create rectangular frame of size 300x300 with name circle
circle_mask = np.zeros(img.shape[:2], dtype="uint8")# draw circle in rectangular frame

# create a circle at (315, 265) to mask the Yellow circle
cv2.circle(circle_mask, (315, 265), 90, 255, -1)
cv2.imshow("Circle", circle_mask)

# mask the Yellow circle
masked_img = cv2.bitwise_and(img, img, mask=circle_mask)
cv2.imshow("Masked image", masked_img)

cv2.waitKey(0)
../_images/Screenshot_131.jpg

Fig. 1.13 Masking

1.1.9. Edge detection

1.1.9.1. Sobel edge detection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# sobel_img.py

import cv2
import numpy as np


# read image
img = cv2.imread("images/lego.jpg")
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("Lego", gray_img)

# compute gradients along the X and Y axis, respectively
gX = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0)
gY = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1)
#gX value after sobel conversion -52.0
print("gX value after sobel conversion", gX[100,200])

# gX and gY are decimal number with +/- values
# change these values to +ve integer format
gX = cv2.convertScaleAbs(gX)
# gX value after Absolute scaling 52
gY = cv2.convertScaleAbs(gY)
print("gX value after Absolute scaling", gX[100,200])

# combine the sobel X and Y in single image with equal amount
sobelCombined = cv2.addWeighted(gX, 0.5, gY, 0.5, 0)

# show the output images
cv2.imshow("Sobel X", gX)
cv2.imshow("Sobel Y", gY)
cv2.imshow("Sobel Combined", sobelCombined)


cv2.waitKey(0)
../_images/Screenshot_142.jpg

Fig. 1.14 Sobel edge detection

1.1.9.2. Canny edge detection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# canny_img.py

import cv2
import numpy as np


# read image
img = cv2.imread("images/lego.jpg")
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("Lego", gray_img)

# canny edge detection
# choice depends based on data
canny_wide = cv2.Canny(gray_img, 10, 200) # over detection
canny_medium = cv2.Canny(gray_img, 50, 150) # good detection
canny_narrow = cv2.Canny(gray_img, 200, 250) # missing detection

# show the output images
cv2.imshow("Canny (10, 200)", canny_wide)
cv2.imshow("Canny (50, 150)", canny_medium)
cv2.imshow("Canny (200, 200)", canny_narrow)


cv2.waitKey(0)
../_images/Screenshot_152.jpg

Fig. 1.15 Canny edge detection