OCR

FREEMIUM
By API 4 AI | Updated il y a un mois | Visual Recognition
Popularity

9.6 / 10

Latency

1,846ms

Service Level

100%

Health Check

100%

Back to All Tutorials (8)

How to recognize VIN from a photo with OCR API.

There are several compelling reasons to extract the Vehicle Identification Number (VIN) from a car windshield photo. Whether you’re an insurance agent evaluating a claim, a law enforcement officer investigating a case, or a car enthusiast decoding vehicle details, the efficient extraction of VINs is invaluable. However, this process has traditionally been time-consuming and labor-intensive.

Fortunately, modern technology offers a solution to expedite VIN extraction from car windshield photos: a simple Python script combined with an Optical Character Recognition (OCR) API. OCR technology comprehensively recognizes and extracts text from images, making it ideal for this task.

Leveraging an OCR API enables us to employ advanced algorithms trained to accurately detect and recognize text within images. The API’s output conveniently provides essential information. Each detected VIN text block comes with its corresponding bounding box coordinates and recognized text.

To further simplify the process, we will develop a Python script automating the VIN search across a designated folder of car windshield photos. The script methodically processes each image, extracts VINs using the OCR API, and stores the results in CSV (Comma-Separated Values) files. This structured output format ensures organized extraction, facilitating seamless integration into other applications or systems for analysis.

With the script in place, simply provide the path to the car windshield photo folder. The script iterates through each image, utilizing the OCR API to detect and extract VIN information. Extracted VINs, along with their bounding box coordinates, are written into separate CSV files for reference and use.

This efficient, automated approach significantly reduces time and effort needed for reading VINs from car windshield photos. Whether dealing with a few images or a large batch, the Python script coupled with an OCR API streamlines the workflow. Now, you can swiftly and accurately extract essential vehicle information, empowering effective and efficient task completion.

Python implementation

Before we start, you should know that you can find OCR API documentation at https://api4.ai/docs/ocr and OCR API code examples at https://gitlab.com/api4ai/examples/ocr.
Also, you need to install the required packages: pip install requests

VIN verification

Allow us to commence by introducing a VIN verification function, designed to discriminate against extraneous text that may be present in the image.
There are two distinct approaches to check whether the text is a VIN, and whether it is valid.
According to Wikipedia, the initial method involves computing the check digit within the VIN itself.
However, it is crucial to note that this technique exclusively applies to automobiles originating from North America.

transliteration_map = {
    'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8,
    'J': 1, 'K': 2, 'L': 3, 'M': 4, 'N': 5, 'P': 7,'R': 9,
    'S': 2, 'T': 3, 'U': 4, 'V': 5, 'W': 6, 'X': 7, 'Y': 8, 'Z': 9
}
weight_factor = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2]

def verify_vin(vin: str):
    if len(vin) != 17:
        return False
    transliterated = [int(char) if char.isnumeric()
                      else transliteration_map[char]
                      for char in vin]
    products = [transliterated[i] * weight_factor[i] for i in range(17)]
    num = sum(products) % 11
    checkdigit = 'X' if num == 10 else str(num)
    return checkdigit == vin[8]

Whilst the first method presented may not be universally applicable, fear not, for I shall unveil to you the second method, a beacon of hope for those who seek an alternative approach.
VINs is a 17-character word with a certain structure that does not include the letters O (o), I (i), and Q (q).
So the second method is based on testing these rules with regular expressions.
In this tutorial, the second one will be used, but you can use any of them or even combine them.

def verify_vin(vin: str):
    """Verify that string is VIN."""
    pattern = r'^[A-HJ-NPR-Z\d]{11}\d{6}$'
    return bool(re.match(pattern, vin))

Text recognition

As it was decided before, the OCR API will be used to find a VIN on photos.
All you need to do is send an image to the API, get text in response, and filter out the text that does not contain the VIN.

API_URL = 'https://ocr43.p.rapidapi.com'

def get_vin(photo_path: Path, api_key: str):
    # We strongly recommend you use exponential backoff.
    error_statuses = (408, 409, 429, 500, 502, 503, 504)
    s = requests.Session()
    retries = Retry(backoff_factor=1.5, status_forcelist=error_statuses)

    s.mount('https://', HTTPAdapter(max_retries=retries))

    url = f'{API_URL}/v1/results'
    with photo_path.open('rb') as f:
        api_res = s.post(url, files={'image': f},
                         headers={'X-RapidAPI-Key': api_key}, timeout=20)
    api_res_json = api_res.json()

    # Handle processing failure.
    if (api_res.status_code != 200 or
            api_res_json['results'][0]['status']['code'] == 'failure'):
        print('Image processing failed.')
        sys.exit(1)

    # Find VIN and return it.
    try:
        text = api_res_json['results'][0]['entities'][0]['objects'][0]['entities'][0]['text']
    except IndexError:
        return None

    for line in text.split():
        if verify_vin(line.upper()):
            return line
    return None

Command line arguments parsing

A photo directory and API key from RAPID API will be passed as command-line arguments.

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('--api-key', help='Rapid API token.', required=True)
    parser.add_argument('photos_dir', type=Path,
                        help='Path to a directory with photos.')
    return parser.parse_args()

Main function

The last thing left is the main function, where photos from the directory will be processed and the results recorded.

def main():
    args = parse_args()
    handled_f = (args.photos_dir / 'vins.csv').open('w')
    unrecognizable_f = (args.photos_dir / 'unrecognizable.csv').open('w')
    csv_handled = csv.writer(handled_f)
    csv_unrecognizable = csv.writer(unrecognizable_f)
    extensions = ['.png', '.jpg', '.jpeg']
    files = itertools.chain.from_iterable(
        [args.photos_dir.glob(f'*{ext}') for ext in extensions]
    )
    for photo in files:
        vin = get_vin(photo, args.api_key)
        if vin:
            csv_handled.writerow([photo, vin])
        else:
            csv_unrecognizable.writerow([photo])

    handled_f.close()
    unrecognizable_f.close()


if __name__ == '__main__':
    main()

Full script

"""
Get VINs from each image in directory and write results to a csv file.
Run the script:
`python3 main.py --api-key <RAPID API TOKEN> <PATH TO DIRECTORY WITH IMAGES>`
"""
import argparse
import csv
import itertools
import re
import sys
from pathlib import Path

import requests
from requests.adapters import Retry, HTTPAdapter

API_URL = 'https://ocr43.p.rapidapi.com'


def parse_args():
    """Parse command line arguments."""
    parser = argparse.ArgumentParser()
    parser.add_argument('--api-key', help='Rapid API token.', required=True)  # Get your token at https://rapidapi.com/api4ai-api4ai-default/api/ocr43/pricing
    parser.add_argument('photos_dir', type=Path,
                        help='Path to a directory with photos.')
    return parser.parse_args()


# transliteration_map = {
#     'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8,
#     'J': 1, 'K': 2, 'L': 3, 'M': 4, 'N': 5, 'P': 7,'R': 9,
#     'S': 2, 'T': 3, 'U': 4, 'V': 5, 'W': 6, 'X': 7, 'Y': 8, 'Z': 9
# }
#
# weight_factor = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2]
#
# def verify_vin(vin: str):
#     if len(vin) != 17:
#         return False
#     transliterated = [int(char) if char.isnumeric()
#                       else transliteration_map[char]
#                       for char in vin]
#     products = [transliterated[i] * weight_factor[i] for i in range(17)]
#     num = sum(products) % 11
#     checkdigit = 'X' if num == 10 else str(num)
#     return checkdigit == vin[8]


def verify_vin(vin: str):
    """Verify that string is VIN."""
    pattern = r'^[A-HJ-NPR-Z\d]{11}\d{6}$'
    return bool(re.match(pattern, vin))


def get_vin(photo_path: Path, api_key: str):
    """Get a VIN from a photo."""
    # We strongly recommend you use exponential backoff.
    error_statuses = (408, 409, 429, 500, 502, 503, 504)
    s = requests.Session()
    retries = Retry(backoff_factor=1.5, status_forcelist=error_statuses)

    s.mount('https://', HTTPAdapter(max_retries=retries))

    url = f'{API_URL}/v1/results'
    with photo_path.open('rb') as f:
        api_res = s.post(url, files={'image': f},
                         headers={'X-RapidAPI-Key': api_key}, timeout=20)
    api_res_json = api_res.json()

    # Handle processing failure.
    if (api_res.status_code != 200 or
            api_res_json['results'][0]['status']['code'] == 'failure'):
        print('Image processing failed.')
        sys.exit(1)

    # Find VIN and return it.
    try:
        text = api_res_json['results'][0]['entities'][0]['objects'][0]['entities'][0]['text']
    except IndexError:
        return None

    for line in text.split():
        if verify_vin(line.upper()):
            return line
    return None


def main():
    """
    Script entry point.

    Write recognized VINs in folder to a csv file.
    """
    args = parse_args()
    handled_f = (args.photos_dir / 'vins.csv').open('w')
    unrecognizable_f = (args.photos_dir / 'unrecognizable.csv').open('w')
    csv_handled = csv.writer(handled_f)
    csv_unrecognizable = csv.writer(unrecognizable_f)
    extensions = ['.png', '.jpg', '.jpeg']
    files = itertools.chain.from_iterable(
        [args.photos_dir.glob(f'*{ext}') for ext in extensions]
    )
    for photo in files:
        vin = get_vin(photo, args.api_key)
        if vin:
            csv_handled.writerow([photo, vin])
        else:
            csv_unrecognizable.writerow([photo])

    handled_f.close()
    unrecognizable_f.close()


if __name__ == '__main__':
    main()

Test the script

Let’s run the script: python3 main.py --api-key "YOUR_API_KEY" "PATH/TO/FOLDER/WITH/VINS"
The script will handle all the photos in directory and write down results in vins.csv and unrecognizable.csv files.
As the result, in vins.csv will be pairs of path to a photo and VIN in this photo.

Conclusion

In conclusion, the recognition of VINs from windshield images plays a pivotal role across multiple domains. The OCR API emerges as a transformative force, simplifying and expediting this vital task, opening the door to heightened productivity and accuracy. By embracing the OCR API, we embark on a seamless journey of discovery, effortlessly unraveling the mysteries concealed within windshield images.