<jupyter_start><jupyter_text>To run this code You need a Google Vision API Credentials and AWS S3 Acess Key and Secret Access Key<jupyter_code>import os, io
from google.cloud import vision_v1 as vision
from google.cloud.vision_v1 import types
import pandas as pd
import cv2
import numpy as np
import json
import re
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont
import imagehash
from enum import Enum
from termcolor import colored
import boto3
from botocore.client import Config
import uuid
%matplotlib inline

os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = r'Demz_vision_API_token.json'  # GOOGLE API Credentials
# Path to the two License
Driver_License = 'test2.jpg'
PCO_License = 'test1.jpg'
FOLDER_PATH = 'C:\\Users\\USER\\Desktop\\NEW_DEMZ\\OCR\\OCR_Works\\Google\\images'
# Google Client
client = vision.ImageAnnotatorClient()
crop_hints_params = vision.CropHintsParams(aspect_ratios=[1.77])<jupyter_output><empty_output><jupyter_text>AWS S3 Client<jupyter_code># You can replace these fields with your own
with open('configfile.json') as f:
    data = json.load(f)

ACCESS_KEY_ID = data['demz-license-user-access-key'] # AWS Secret Key ID
ACCESS_SECRET_KEY = data['demz-license-user-secret-access-key'] #AWS Access Secret Key

# Bucket Names
pco_BUCKET_NAME = 'pco-license'
driver_BUCKET_NAME = 'drivers-license'
user_BUCKET_NAME = 'license-user'<jupyter_output><empty_output><jupyter_text>Get License Response from GOOGLE API<jupyter_code># Drivers License response
with io.open(os.path.join(FOLDER_PATH, Driver_License), 'rb') as image_file1:
        content = image_file1.read()
content_image = types.Image(content=content) # Reading the Image Content
Drivers_response = client.text_detection(image=content_image) # Text Detection 
Drivers_response_face = client.face_detection(image=content_image) # Face Detection 
Drivers_texts = Drivers_response.text_annotations # Text Response
Drivers_faceAnnotations = Drivers_response_face.face_annotations # Face Response
# PCO License response
with io.open(os.path.join(FOLDER_PATH, PCO_License), 'rb') as image_file1:
        content = image_file1.read()
content_image = types.Image(content=content) # Reading the Image Content
PCO_response = client.text_detection(image=content_image) # Text Detection 
PCO_response_face = client.face_detection(image=content_image) # Face Detection 
PCO_texts = PCO_response.text_annotations # Text Response
PCO_faceAnnotations = PCO_response_face.face_annotations # Face Response<jupyter_output><empty_output><jupyter_text>Crop Both License Images<jupyter_code>def get_crop_hint(crop_hints):
    """Detect crop hints on a single image and return the first result."""
    with io.open(crop_hints, 'rb') as image_file1:
        content = image_file1.read()
        
    image = vision.Image(content=content) # Read the content from the image
    
    image_context = vision.ImageContext(crop_hints_params=crop_hints_params)

    response = client.crop_hints(image=image, image_context=image_context)
    hints = response.crop_hints_annotation.crop_hints

    # Get bounds for the first crop hint using an aspect ratio of 1.77.
    vertices = hints[0].bounding_poly.vertices

    return vertices
def crop_Driver_hint(image_file):
    """Crop the image using the hints in the vector list."""
    vects = get_crop_hint(image_file)
#     response_face = Drivers_response_face
    faceAnnotations = Drivers_faceAnnotations
    try:
        face_bounds = []
        # Getting the vertices of the face detected
        for face in faceAnnotations:
            face_vertices = (['({0},{1})'.format(vertex.x, vertex.y) for vertex in face.bounding_poly.vertices])
            face_bounds.append(face_vertices)
        bound_1 = face_bounds[0][0]
        bound_2 = face_bounds[0][2]
        bound_1 = eval(bound_1)
        bound_2 = eval(bound_2)

        im = Image.open(image_file)
        
        # Crop out the face
        im2 = im.crop([bound_1[0], bound_1[1],
                      bound_2[0] - 1, bound_2[1] - 1])
    
        print('Saved new image to output-crop.jpg')
        return im2
    except:
        print("No Image Detected")
    
def crop_PCO_hint(image_file):
    """Crop the image using the hints in the vector list."""
    vects = get_crop_hint(image_file)
    faceAnnotations = PCO_faceAnnotations
    try:
        face_bounds = []
        for face in faceAnnotations:
            face_vertices = (['({0},{1})'.format(vertex.x, vertex.y) for vertex in face.bounding_poly.vertices])
            face_bounds.append(face_vertices)
        bound_1 = face_bounds[0][0]
        bound_2 = face_bounds[0][2]
        bound_1 = eval(bound_1)
        bound_2 = eval(bound_2)

        im = Image.open(image_file)

        im2 = im.crop([bound_1[0], bound_1[1],
                      bound_2[0] - 1, bound_2[1] - 1])
        print('Saved new image to output-crop.jpg')
        return im2
    except:
        print("No Image Detected")
    
# Croped Images
Driver_Image = crop_Driver_hint(os.path.join(FOLDER_PATH, Driver_License))
PCO_IMage = crop_PCO_hint(os.path.join(FOLDER_PATH, PCO_License))
Driver_Image
PCO_IMage<jupyter_output><empty_output><jupyter_text>Processing License Text<jupyter_code>def Processor(Drivers_texts, PCO_texts):
    Drivers_contents = [] # Driver contents
    PCO_contents = [] # PCO Contents
    
    # Extract Driver License Contents
    for text in Drivers_texts:
        Drivers_contents.append(text.description)
    
    # Extract PCO License Contents
    for text in PCO_texts:
        PCO_contents.append(text.description)
    # The first content in the Text response is a string of all the texts seperated by '\n'
    Driver_String_Content = Drivers_contents[0] # Get the first content 
    PCO_String_Content = PCO_contents[0] # Get the first content
    
#     print(PCO_contents)

    # Process the PCO License
    def Process_PCO_License(PCO_String_Content):
        # Using the 'Expiry Date' as a divider
        if 'Expire Date' in PCO_String_Content: 
            new_pco_list = PCO_String_Content.split('Expire Date:')
        elif 'Expiry Date:' in PCO_String_Content:
            new_pco_list = PCO_String_Content.split('Expiry Date:')
        else:
            new_pco_list = PCO_String_Content.split('Expiry Date')
        # Getting the Expiry date
        new_pco = new_pco_list[-1]
        Expiry_date = new_pco_list[-1].split('\n')
        Expiry_date = [date for date in Expiry_date if date != ''][0]
        new_pco_content = new_pco_list[0]

        def get_pco_license_NO(new_pco_content):
            license_NO = re.findall(r'\d',new_pco_content)
            license_NO = ''.join(license_NO)
            return license_NO
        # Get the License Number
        PCO_license_NO = get_pco_license_NO(new_pco_content)
        new_pco_content = new_pco_content.split(PCO_license_NO)

        new_pco_content = new_pco_content[-1]
        new_pco_content = new_pco_content.split('\n') # FirstName and Lastname in a list

        new_pco_content = [name for name in new_pco_content if name != ''] # Cleanup the list
        First_Name = new_pco_content[0]
        Other_Name = new_pco_content[-1]
        Full_Name = First_Name + ' '+ Other_Name
        Full_Name = Full_Name.split(' ')
        Full_Name = sorted(Full_Name)
        Full_Name = ' '.join(Full_Name)
        print(Full_Name)

        PCO_CONTENT = {}

        PCO_CONTENT['Full_Name'] = Full_Name
        PCO_CONTENT['License Number'] = PCO_license_NO
        PCO_CONTENT['Expiry Date'] = Expiry_date
        
        return PCO_CONTENT
    

    def Process_Driver_License(Driver_String_Content):
        
        if 'DRIVING LICENCE' in Driver_String_Content.upper():
            if 'DRIVING LICENCE' in Driver_String_Content.upper():
                Driver_contents = Driver_String_Content.split('DRIVING LICENCE')[-1]
            elif '1.' in Driver_String_Content:
                Driver_contents = re.split('1.', Driver_String_Content, 1)[-1]
            else:
                Driver_contents = Driver_String_Content
                print('No Split Found')
    #       Dates
            # Match dates using regex
            dates = re.findall(r"([1-9]|0[1-9]|1[0-9]|2[0-9]|3[0-1])(\.|-|/)([1-9]|0[1-9]|1[0-2])(\.|-|/)(19[0-9][0-9]|20[0-9][0-9]|[0-9][0-9])", Driver_contents)
            new_dates = [] # all the dates
            for date in dates:
                dat = ''.join(date)
                new_dates.append(dat) # append the dates
            # Check if Dates is matched
            if len(new_dates) >= 3:
                date_and_POB = re.search(f'{new_dates[0]}.*\n', Driver_contents).group(0).split('\n')[0] # Date and place of birth
                DOB = new_dates[0] # Date o birth
                POB = date_and_POB.split(DOB)[-1] # PLace of Birth
                issued_date = new_dates[1] # Issued Date
                Renew_due_date = new_dates[2] # Renew Date
            else:
                # Search for dates in the content
                dates = re.findall("\d\d.*\d\d",Driver_contents) 
                date_and_POB = re.search(f'{dates[0]}.*\n', Driver_contents).group(0).split('\n')[0]
                DOB = dates[0]
                POB = date_and_POB.split(DOB)[-1]
                issued_date = dates[1]
                Renew_due_date = dates[2]
            print(DOB)
            print(POB)

    #       Names
            Names = Driver_contents.split(date_and_POB)[0] # Split the content from date and place of birth, get the first element
            Names = re.split('\d.',Names) # Split the names on the digit
            Names = ' '.join(Names) # Join the Names
            Names = Names.split('\n') # Split the Names at '\n'
            Names = [name.strip() for name in Names if name] # Cleanup
            Names = [name for name in Names if name] # Cleanup
            if Names[0] == 'UK': # Check if UK is in the names
                Names = Names[1:] # if UK Slice the List
            if len(Names) > 2: # if length of Names is greater than 2
                First_Name = Names[0] # First Name
                if not 'UK' in Names:
                    Other_Name = ' '.join(Names[1:]) 
                else:
                    Other_Name = Names[1]
                # Get the Full Names
                Full_Name = First_Name + ' '+ Other_Name
                Full_Name = Full_Name.split(' ')
                Full_Name = sorted(Full_Name)
                Full_Name = ' '.join(Full_Name)
                print(Full_Name)
            else:
                First_Name = Names[0]
                Other_Name = Names[1]
                Full_Name = First_Name + ' '+ Other_Name
                Full_Name = Full_Name.split(' ')
                Full_Name = sorted(Full_Name)
                Full_Name = ' '.join(Full_Name)
                print(Full_Name)
                
    #       Authority
            Authority = re.search(f'{issued_date}.*\n', Driver_contents).group(0) # Split the Text from Issued Date
            Authority_EX = re.findall("\d\d.*\d\d", Authority)
            authority_date = []
            for date in Authority_EX:
                dat = ''.join(date)
                authority_date.append(dat)
            Authority = Authority.split(authority_date[-1])[-1]
            Authority = Authority.split(' ')[-1]
            Authority = Authority.split('\n')[0]

    #       LICENSE NUMBER
            License_No = Driver_contents.split(Renew_due_date)[1] # Split the Text from 
            License_No = License_No.split('\n')
            License_No = [license.strip() for license in License_No if license]
            match_license = []
            for license in License_No:
                if re.search("\w.*\d", license):
                    match_license.append(license)
            if len(match_license) > 0:
                License_No = match_license[0]
            else:
                License_No = 'None'

    #       Address
            Address = Driver_contents.split(License_No)[-1]
            Address_list = Address.split('\n')

            # Merge the Address if it continues in another line
            output = []
            for i in range(len(Address_list)-1):
                if str(Address_list[i]).endswith(','):
                    new_word = Address_list[i] +' '+ Address_list[i+1]
                    output.append(new_word)
                    next_word = Address_list[i+1]
                    Address_list.remove(next_word)
                elif Address_list[i] == max(Address_list, key=len) and str(Address_list[i]).endswith('.'):
                    new_word = Address_list[i] +' '+ Address_list[i+1]
                    output.append(new_word)
                    next_word = Address_list[i+1]
                    Address_list.remove(next_word)
                else:
                    new_word = Address_list[i]
                    output.append(new_word)

            Address_list = output  
            Address = max(Address_list, key=len)
            if Address.isupper():
                Address = Address
            elif max(Address_list, key=len):
                Address = Address
            else:
                Address = [address for address in Address_list if address.isupper()][0]

            # Entitlement categoory
            Entitlement_categoory = Driver_contents.split(License_No)[-1]
            Entitlement_categoory = Entitlement_categoory.split('\n')
            Entitlement_categoory = [category for category in Entitlement_categoory if category][-1]
            if Entitlement_categoory == max(Address_list, key=len):
                Entitlement_categoory = 'None'
            elif re.search(f"{Entitlement_categoory}", Address) :
                Entitlement_categoory = 'None'
            else:
                Entitlement_categoory = Entitlement_categoory.split(' ')[-1]
                Entitlement_categoory  = Entitlement_categoory


            DRIVER_CONTENT = {}

            DRIVER_CONTENT['Full_Name'] = Full_Name
#             DRIVER_CONTENT['Other Name'] = Other_Name
            DRIVER_CONTENT['Date of birth'] = DOB
            DRIVER_CONTENT['Place of birth'] = POB
            DRIVER_CONTENT['Issued Date'] = issued_date
            DRIVER_CONTENT['Renew Due Date'] = Renew_due_date
            DRIVER_CONTENT['Issuing authority'] = Authority
            DRIVER_CONTENT['License Number'] = License_No
            DRIVER_CONTENT['Address'] = Address
            DRIVER_CONTENT['Entitlement categories'] = Entitlement_categoory

            return DRIVER_CONTENT
        else:
            print('Document is Not a Driving License')
    
    PCO_CONTENT = Process_PCO_License(PCO_String_Content)
    DRIVER_CONTENT = Process_Driver_License(Driver_String_Content)
        
    return PCO_CONTENT, DRIVER_CONTENT 
PCO_CONTENT, DRIVER_CONTENT = Processor(Drivers_texts, PCO_texts)
# print(pco_and_driver_license)
# PCO_CONTENT
for x, y in PCO_CONTENT.items():
    print(x +":  " + colored(y, 'green'))
#     print(f"{x}\x1b[31m{':  ' + y}\x1b[0m")
# DRIVER_CONTENT
for x, y in DRIVER_CONTENT.items():
    print(x +":  " + colored(y, 'blue'))
#     print(f"{x}\x1b[31m{':  ' + y}\x1b[0m")<jupyter_output>Full_Name:  [34m<NAME>[0m
Date of birth:  [34m03.05. 1984[0m
Place of birth:  [34m SOMALIA[0m
Issued Date:  [34m21.08.2014[0m
Renew Due Date:  [34m20.06.2024[0m
Issuing authority:  [34mDVLA[0m
License Number:  [34mSHEIKBO5034Y9AD 16[0m
Address:  [34ma. 5a UPPER WALTHAMSTOW ROAD, WALTHAMSTOW. LONDON, E17 00[0m
Entitlement categories:  [34mAM/A/B1/B//k/p/q[0m<jupyter_text>Compare both License Informations<jupyter_code>def Compare_License(Driver_Content, PCO_Content, Driver_Image, PCO_IMage):
    
    def check_Match_Names(Driver_Content, PCO_Content):
        Driver_Full_Name = Driver_Content['Full_Name'].upper()  # Get the Driver FullName      
        PCO_Full_Name = PCO_Content["Full_Name"].upper() # Get PCO FullName
        
        # Compare the FullNames
        Names_Match = {}
        if Driver_Full_Name == PCO_Full_Name:
            Names_Match["Full_Name_Matched"] = True
        else:
            Names_Match["Full_Name_Matched"] = False
            
        return Names_Match
    
    # Check the similarities in the Images Using ImageHash (Python Library)
    def check_Image_Similarities(Driver_Image, PCO_IMage):
        
        hash0 = imagehash.average_hash(Driver_Image) # Driver Image Hash
        hash1 = imagehash.average_hash(PCO_IMage)  # PCO Image Hash
        cutoff = 5

        Image_Similar = {}
        # Compute the difference between the Both Images
        if hash0 - hash1 < cutoff:
            Image_Similar["images_similar"] = True
            print('images_are_similar')
            print('')
        else:
            Image_Similar["images_similar"] = False
            print('images are not similar')
            print('')
        
        return Image_Similar
            
    # Compare Names and Images
    compared_names = check_Match_Names(Driver_Content, PCO_Content)
    compared_images = check_Image_Similarities(Driver_Image, PCO_IMage)
    
    result = {**compared_names, **compared_images}
    
    return result
# Compare Both License
result = Compare_License(DRIVER_CONTENT, PCO_CONTENT, Driver_Image, PCO_IMage)
# Display Results
for x,y in result.items():
    if y == True:
        print(x +":  " + colored(y, 'blue'))
    else:
        print(x +":  " + colored(y, 'red'))<jupyter_output>Full_Name_Matched:  [34mTrue[0m
images_similar:  [31mFalse[0m<jupyter_text>Save Data to s3<jupyter_code># Create a Unique ID for Every Item to be Uploaded
unique_id = uuid.uuid4().hex
print(unique_id)
# Arrange All the data

pco_original_image = open(os.path.join(FOLDER_PATH, PCO_License), 'rb')
driver_original_image = open(os.path.join(FOLDER_PATH, Driver_License), 'rb')

pco_cropped_image = PCO_IMage
driver_cropped_image = Driver_Image

#Convert The cropped Image to bytes and save in memory
buf = io.BytesIO()
pco_cropped_image.save(buf, format='PNG')
pco_cropped_image = buf.getvalue()

buf = io.BytesIO()
driver_cropped_image.save(buf, format='PNG')
driver_cropped_image = buf.getvalue()

# Convert the Text to Json for AWS
pco_details = json.dumps(PCO_CONTENT)
driver_details = json.dumps(DRIVER_CONTENT)
pco_and_driver_license = {'PCO_LICENSE':unique_id, 'DRIVER_LICENSE':unique_id}
pco_and_driver_license = json.dumps(pco_and_driver_license)

if result['Full_Name_Matched'] == True:
    username = DRIVER_CONTENT['Full_Name'].split(' ')
    username = '-'.join(username)
else:
    username = None

# Upload to PCO-license bucket

s3 = boto3.resource(
    's3',
    aws_access_key_id=ACCESS_KEY_ID,
    aws_secret_access_key=ACCESS_SECRET_KEY,
    config=Config(signature_version='s3v4')
)

s3.Bucket(pco_BUCKET_NAME).put_object(Key=f'{unique_id}/user_image.png', Body=pco_cropped_image, Tagging=f'unique_id={unique_id}')
s3.Bucket(pco_BUCKET_NAME).put_object(Key=f'{unique_id}/user_detail.json', Body=pco_details, Tagging=f'unique_id={unique_id}')

print ("Done")
# Upload to Driver-license bucket

s3 = boto3.resource(
    's3',
    aws_access_key_id=ACCESS_KEY_ID,
    aws_secret_access_key=ACCESS_SECRET_KEY,
    config=Config(signature_version='s3v4')
)

s3.Bucket(driver_BUCKET_NAME).put_object(Key=f'{unique_id}/user_image.png', Body=driver_cropped_image, Tagging=f'unique_id={unique_id}')
s3.Bucket(driver_BUCKET_NAME).put_object(Key=f'{unique_id}/user_detail.json', Body=driver_details, Tagging=f'unique_id={unique_id}')

print ("Done")
# Upload to user-license bucket

s3 = boto3.resource(
    's3',
    aws_access_key_id=ACCESS_KEY_ID,
    aws_secret_access_key=ACCESS_SECRET_KEY,
    config=Config(signature_version='s3v4')
)

s3.Bucket(user_BUCKET_NAME).put_object(Key=f'{unique_id}/pco_user_image.png', Body=pco_original_image, Tagging=f'unique_id={unique_id}')
s3.Bucket(user_BUCKET_NAME).put_object(Key=f'{unique_id}/driver_user_image.png', Body=driver_original_image, Tagging=f'unique_id={unique_id}')
s3.Bucket(user_BUCKET_NAME).put_object(Key=f'{unique_id}/user_info.json', Body=pco_and_driver_license, Tagging=f'unique_id={unique_id}')

print ("Done")<jupyter_output>Done
{'fraction_non_alphanumeric': 0.10406431442608308, 'fraction_numerical': 0.013547714753610243, 'mean_word_length': 2.7965335342878674, 'pattern_counts': {'":': 4, '<': 28, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '584740', 'license': 'BSD-Source-Code', 'n_tokens_mistral': 6814, 'n_tokens_neox': 6566, 'n_words': 1342, 'path': 'License Processor .ipynb', 'repo_name': 'Bruno-Odinukweze/License-Parser', 'size': '20162'}