Python
This section describes how to use Intrada ALPR from your Python application.
The use of the library is explained by an array of available methods and a code snippet on how to do a single detection.
Before using the Python binding, please make sure you have the following:
- An Intrada fully function software package with valid license
- Basic knowledge of Python and C
- Make sure you have the `ctypes` library (part of the standard Python library since Python 2.5)
To start using the Intrada Python binding, store your Intrada software package (`intrada.dll, intrada.lic, intrada_cm_XX.dll, setting.txt, etc`) in the target folder.
To start using the Intrada ALPR library in python, first we need to include load the library in python.
import ctypes from enum import IntEnum intrada = ctypes.CDLL("./intrada.dll")
Intrada return codes
Every function of Intrada returns an integer which corresponds to the result of the function. These can be used in Python as well to improve the readability of the code, and improve error handling and debugging. The list is kept short for demonstration purposes, but the full list can be obtained by looking in the Intrada header.
# Define a bunch of return codes. More can be found in the intrada header. # The list is kept short for demonstration purposes class ReturnCode(IntEnum): INTRADA_RETURN_OK = 1 INTRADA_RETURN_ILLEGAL_LICENSE_KEY = 2 INTRADA_RETURN_LIB_NOT_INITIALIZED = 3 INTRADA_RETURN_ILLEGAL_IMAGE = 4 INTRADA_RETURN_ILLEGAL_DATA = 5 INTRADA_RETURN_ZERO_PLATES_IN_DATA = 6 INTRADA_RETURN_CANNOT_OPEN_FILE = 7
Intrada structs
In the Intrada functions there are 3 required structs, which can be defined as follows:
# Custom Intrada structs Data = ctypes.c_void_p Image = ctypes.c_void_p Settings = ctypes.c_void_p
Intrada functions
In this section some core functions will be shown. With these functions you’re able to get a detection on an image. The original C function name is provided as a comment. Not all functions are mentioned here because there are too many. Based on these examples and the accompanying Intrada User manual, the other functions should be easy to implement.
# Create empty Intrada Data struct # extern int IntradaCreateData(IntradaData *data); IntradaCreateData = intrada.IntradaCreateData IntradaCreateData.argtypes = [Data] IntradaCreateData.restype = ctypes.c_int # Free and destroy Intrada Data struct # extern int IntradaDestroyData(IntradaData data); IntradaDestroyData = intrada.IntradaDestroyData IntradaDestroyData.argtypes = [Data, Image] IntradaDestroyData.restype = ctypes.c_int # Load the Intrada image into the Intrada data struct # extern int IntradaDataSetImage(IntradaData data, IntradaImage image); IntradaDataSetImage = intrada.IntradaDataSetImage IntradaDataSetImage.argtypes = [Data, Image] IntradaDataSetImage.restype = ctypes.c_int # Create a Intrada Image struct from a filename # extern int IntradaLoadImage(const char *filename, int to_gray, IntradaImage *image); IntradaLoadImage = intrada.IntradaLoadImage IntradaLoadImage.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(Image)] IntradaLoadImage.restype = ctypes.c_int # Create a new Intrada Settings object # extern int IntradaCreateSettings(const char *name, IntradaSettings *settings); IntradaCreateSettings = intrada.IntradaCreateSettings IntradaCreateSettings.argtypes = [ctypes.c_char_p, Settings] IntradaCreateSettings.restype = ctypes.c_int # Perform a recognition action on the provided image #extern int IntradaRecognize(IntradaSettings settings, IntradaData data); IntradaRecognize = intrada.IntradaRecognize IntradaRecognize.argtypes = [Settings, Data] IntradaRecognize.restype = ctypes.c_int # Several functions to retreive requested information for the recognition action # extern int IntradaGetPlateRegistrationNumber(IntradaData data, int plate_index, char *char_buffer, int char_buffer_size); IntradaGetPlateRegistrationNumber = intrada.IntradaGetPlateRegistrationNumber IntradaGetPlateRegistrationNumber.argtypes = [Data, ctypes.c_int, ctypes.c_char_p, ctypes.c_int] IntradaGetPlateRegistrationNumber.restype = ctypes.c_int # extern int IntradaGetPlateConfidence(IntradaData data, int plate_index, int *confidence); IntradaGetPlateConfidence = intrada.IntradaGetPlateConfidence IntradaGetPlateConfidence.argtypes = [Data, ctypes.c_int, ctypes.POINTER(ctypes.c_int)] IntradaGetPlateConfidence.restype = ctypes.c_int # extern int IntradaGetPlateCountry(IntradaData data, int plate, const char **country); IntradaGetPlateCountry = intrada.IntradaGetPlateCountry IntradaGetPlateCountry.argtypes = [Data, ctypes.c_int, ctypes.POINTER(ctypes.POINTER(ctypes.c_char))] IntradaGetPlateCountry.restype = ctypes.c_int
One important function is still missing, and that’s IntradaInitLibrary. This function logically initializes the Intrada library. This function is mentioned separately to give a tip on how to make it easier to use the function.
When passing Python strings to C functions that expect null-terminated strings (C-style strings), it’s necessary to encode the strings as ASCII or UTF-8. \This is because the default encoding for Python strings is Unicode, while C-style strings are typically encoded using ASCII or UTF-8. By encoding Python strings as ASCII before passing them to C functions, you ensure that the strings are properly converted into a format that is supported by Intrada. This encoding ensures that characters outside the ASCII range are represented correctly, preventing potential issues such as data loss or unexpected behavior when working with non-ASCII characters. Therefore, ASCII encoding is essential for seamlessly interfacing Python code with C code when dealing with string data.
# Function to initialize the Intrada ALPR library # extern int IntradaInitLibrary(const char *company_name, # const char *options, # const char *license_key); IntradaInitLibraryPure = intrada.IntradaInitLibrary IntradaInitLibraryPure.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p] IntradaInitLibraryPure.restype = ctypes.c_int # A function to make the usage of the IntradaInitLibrary easier. This function handles the python Unicode string encoding for intrada def IntradaInitLibrary(string1, string2, string3): return IntradaInitLibraryPure(string1.encode('ascii'), string2.encode('ascii'), string3.encode('ascii'))
In this example the function: “IntradaInitLibraryPure” is directly using the C function. The IntradaInitLibrary function simplifies the interaction with the C library by abstracting away the encoding details from the user. By handling the encoding internally, users can pass regular Python strings as arguments, without needing to worry about the specific encoding requirements of the underlying C library. This concept can be used in other functions as well, such as the IntradaLoadImage function.
Intrada returns C-style arrays and such. Working with C-style character arrays in Python, especially when using the ctypes module, can introduce a lot of errors due to differences in string representations between Python and C. To simplify this process, two helper functions have been provided. By encapsulating the necessary steps within these functions, the users can focus on the logic of their code without the need of the low-level details.
# Return the length of the given array. def GetCharArraySize(ctypes_array): size = 0 try: while ctypes_array[size] != b'\0': size += 1 return size except Exception as e: ## Most likely a NULL array # Handle the exception raise RuntimeError("An error occurred, and it was caught") from e # Function to create a string from a character array def GetStringFromCharArray(ctypes_array): size = GetCharArraySize(ctypes_array) try: char_array = [ctypes_array[i].decode('ascii') for i in range(size)] except Exception as e: # Handle the exception raise RuntimeError("An error occurred") from e try: return "".join(char_array) except Exception as e: # Handle the exception raise RuntimeError("An error occurred") from e
Fully working Intrada ALPR application:
if __name__ == "__main__": result = IntradaInitLibrary("Company name", "Options", "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX") if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) filename = "filename.jpg" confidence = ctypes.c_int() image = Image() data = Data() the_settings = Settings() result = IntradaCreateSettings("Settings".encode('ascii'), ctypes.byref(the_settings)) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) result = IntradaLoadImage(filename.encode('ascii'), 1, ctypes.byref(image)) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) result = IntradaCreateData(ctypes.byref(data)) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) result = IntradaDataSetImage(data, image) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) result = IntradaRecognize(the_settings, data) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) char_buffer = ctypes.create_string_buffer(INTRADA_MAX_CHARS_REGISTRATION_NUMBER) result = IntradaGetPlateRegistrationNumber(data, 0, char_buffer , INTRADA_MAX_CHARS_REGISTRATION_NUMBER) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) registration_number = GetStringFromCharArray(char_buffer) result = IntradaGetPlateConfidence(data, 0, ctypes.byref(confidence)) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) country = ctypes.POINTER(ctypes.c_char)() result = IntradaGetPlateCountry(data, 0, ctypes.byref(country)) if result != ReturnCode.INTRADA_RETURN_OK: print(ReturnCode(result).name) country_name = GetStringFromCharArray(country) print(f'{filename},{registration_number}, {confidence.value}, {country_name}')