1 minute read

0. Import Libraries

import cv2
import numpy as np
import matplotlib.pyplot as plt

1. Load the image file

img = cv2.imread('car.jpg')
height, width, channel = img.shape # height x width x color channel, (960, 1280, 3)
plt.figure(figsize=(12,12))
plt.imshow(img)

2. Image Processing

(1) Convert color to gray
(2) Threshold for image binarization (change the pixel to black or white)
(3) Gaussian Blurring, Gaussian smoothing filtering

imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

imgThreshold = cv2.adaptiveThreshold(imgGray,
                                  maxValue=255.0,
                                  adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                  thresholdType=cv2.THRESH_BINARY_INV,
                                  blockSize=19,
                                  C=9)
                                  
imgThresholdBlur = cv2.GaussianBlur(imgThreshold, ksize=(5,5), sigmaX=1)                         

*adaptiveMethod, T(x,y) is related to the blockSizeXblockSize - C
-> ADAPTIVE_THRESH_MEAN_C or ADAPTIVE_THRESH_GAUSSIAN_C

*threshholdType, THRESH_BINARY or THRESH_BINACRY_INV

*GaussianBlur is the kernel consists of pixel values of Gaussian distribution.

Build a mask (0, 0, 0, …, 0) and extract the contours.
It retrieves all the contours using the image of binary pixels.
then draw all the contour as a box from saved contours in findContours.

imgMask = np.zeros((height, width, channel), dtype=np.uint8)

contours, _ = cv2.findContours(imgThresholdBlur,
                                  mode=cv2.RETR_LIST,
                                  method=cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(imgMask, contours=contours, contourIdx=-1, color=(255, 255, 255))

plt.imshow(imgMask)

We now have a many contours,
and save all the contour to contours {} for box processing.

imgMask = np.zeros((height, width, channel), dtype=np.uint8)
contours = []

for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(imgMask, pt1=(x, y), pt2=(x+width, y+height), color=(255, 255, 255), thickness=1)
    
    contours.append({
        'contour':contour,
        'w':w,
        'h':h,
        'x':x,
        'y':y,
        'cx':(w/2)+x,
        'cy':(h/2)+y
    })
    
plt.imshow(imgMask)

It’s time to clean the boxes up.
Think of size of box,

minWidth, minHeight = 1, 12
minRatio, maxRatio = 0.5, 1.0

possibleContours = []

cnt = 0
for c in contours:
    area = c['w'] * c['h']
    ratio = c['w'] / c['h']
    
    if (c['w'] > minWidth) and (c['h'] > minHeight) and (minRatio < ratio < maxRatio):
        c['idx'] = cnt
        cnt += 1
        possibleContours.append(c)
        
imgMask = np.zeros((height, width, channel), dtype=np.uint8)

for c in possibleContours:
    cv2.rectangle(imgMask, pt1=(c['x'], c['y']), pt2=(c['w']+c['x'], c['h']+c['y']), color=(255, 255, 255), thickness=1)

plt.imshow(imgMask)

But we have many unecessary boxes…contours.
Let’s consider what a character looks like…about number plate.
It consists of “### ####” in Korea.

maxTheta = 20.0
def matchPlate(contourList):
    matchedIdx = []
    for c1 in contourList:
        matchedTempIdx = []
        for c2 in contourList:
            if c1['idx'] == c2['idx']:
                continue
            else:
                dx = abs(c1['cx'] - c2['cx'])
                dy = abs(c1['cy'] - c2['cy'])
                
                diagonal = np.sqrt(c1['w']*c1['w'] + c1['h']*c1['h'])
                distance = np.linalg.norm(np.array([c1['cx'], c1['cy']]) - np.array([c2['cx'], c2['cy']]))
                if dx == 0:
                    theta = -1
                else:
                    theta = np.degrees(np.arctan(dy/dx))
                    
                if theta == -1 or theta > maxTheta:
                    continue
                    #matchedTempIdx.append(c2['idx'])
                    
        matchedTempIdx.append(c1['idx'])