'Quality of filtering with masks and contours cv2 for OCR car-plate recognition (HSV)

I have been trying for a while to read car-plates from a video collected from a camera. I have been successful insofar as building the Computer Vision algorithm to detect plates (I have used YOLOv5). Now the problem comes when I do have my bounding rectangle with the plate, and I do need to read the plate number through an OCR. I present as follows two different examples, highlighting the specific problem. I was wondering if there were any state-of-art methods to generalize the algorithm's ability to pick up plates with OCR. This HSV filter I am applying does not generalize well to all images (or at least my intuition says the problem should be that one).

In this one there is no problem whatsoever:

Image where the HSV filtering works

This one is the original one:

Problematic image not filtered

And this is the same algorithm as above applied in that:

Image where the HSV filtering does not work

Code follows (Using colab):

import numpy as np
from PIL import Image
import pytesseract
from google.colab.patches import cv2_imshow
import cv2

img_naples = np.array(Image.open(img_name))
x, y, widthbox, heightbox = bbox_plate
x_left = x
y_up = y
x_right = x+widthbox
y_down = y + heightbox
plate_naples_rgb = img_naples[y_up:y_down,x_left:x_right]
plate_naples_rgb = cv2.resize( 
        plate_naples_rgb, None, fx = 2, fy = 2, 
        interpolation = cv2.INTER_CUBIC)
result = np.zeros(plate_naples_rgb.shape, dtype=np.uint8)
#Application of HSV filtering
hsv = cv2.cvtColor(plate_naples_rgb, cv2.COLOR_BGR2HSV)
lower = np.array([0,13,21])
upper = np.array([176,111,110])
#Different upper bound filters trials
#(hMin = 0 , sMin = 0, vMin = 91), (hMax = 179 , sMax = 255, vMax = 255)
#(hMax = 131 , sMax = 126, vMax = 90)
mask = cv2.inRange(hsv, lower, upper)
# Perform morph close and merge for 3-channel ROI extraction
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=1)
extract = cv2.merge([close,close,close])
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    print('Area', area)
    #if 100 < area < 500:
    #    cv2.rectangle(plate_naples_rgb, (x, y), (x + w, y + h), (36,255,12), 3)
    #    result[y:y+h, x:x+w] = extract[y:y+h, x:x+w]
# Invert image and throw into Pytesseract
invert = 255 - result
data = pytesseract.image_to_string(invert, lang='eng',config='--psm 6')
print(data)

cv2_imshow(plate_naples_rgb)
cv2_imshow(close)
cv2_imshow(result)
cv2_imshow(invert)


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source