Skip to content

Commit

Permalink
Merge pull request #40 from gadget-man/main
Browse files Browse the repository at this point in the history
Add On-Premise SDK server, detection_rule, region_strict
  • Loading branch information
robmarkcole authored Jul 20, 2021
2 parents b425f3f + ea28b3c commit 5137d26
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 5 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# HASS-plate-recognizer
Read vehicle license plates with https://platerecognizer.com/ which offers free processing of 2500 images per month. You will need to create an account and get your API token.
Read vehicle license plates with [Plate Recognizer ALPR](https://platerecognizer.com/) which offers free processing of 2500 images per month. You will need to create an account and get your API token.

This integration adds an image processing entity where the state of the entity is the number of license plates found in a processed image. Information about the vehicle which has the license plate is provided in the entity attributes, and includes the license plate number, [region/country](http://docs.platerecognizer.com/#countries), vehicle type, and confidence (in a scale 0 to 1) in this prediction. For each vehicle an `platerecognizer.vehicle_detected` event is fired, containing the same information just listed. Additionally, statistics about your account usage are given in the `Statistics` attribute, including the number of `calls_remaining` out of your 2500 monthly available.

If you have a paid plan that includes MMC (Make/Model/Colour) data you can received the orientation of the vehicle in the entity attributes.

You can also forward the LPR results straight to [ParkPow](https://parkpow.com), a parking management software and sister-company to Plate Recognizer.

If you have a local SDK licence, you can optionally specify the server address.

**Note** this integration does NOT automatically process images, it is necessary to call the `image_processing.scan` service to trigger processing.

## Home Assistant setup
Expand All @@ -24,6 +28,10 @@ image_processing:
save_timestamped_file: True
always_save_latest_file: True
mmc: True
detection_rule: strict
region: strict
server: http://yoururl:8080/v1/plate-reader/

source:
- entity_id: camera.yours
```
Expand All @@ -36,6 +44,11 @@ Configuration variables:
- **save_timestamped_file**: (Optional, default `False`, requires `save_file_folder` to be configured) Save the processed image with the time of detection in the filename.
- **always_save_latest_file**: (Optional, default `False`, requires `save_file_folder` to be configured) Always save the last processed image, no matter there were detections or not.
- **mmc**: (Optional, default `False`, requires a [paid plan](https://platerecognizer.com/pricing/) with the MMC (Make, Model, Colour) feature enabled.) If enabled returns the orientation of the vehicle as a separate attribute containing Front/Rear/Unknown.
- **detection_rule**: (Optional) If set to `strict`, the license plates that are detected outside a vehicle will be discarded.
- **region**: (Optional) If set to `strict`, only accept the results that exactly match the templates of the specified region. For example, if the license plate of a region is 3 letters and 3 numbers, the value abc1234 will be discarded. For regions with vanity license plates (e.g. in us-ca), we do not recommend the use of Strict Mode. Otherwise, the engine will discard the vanity plates.
- **server**: (Optional, requires a [paid plan](https://platerecognizer.com/pricing/) Provide a local server address to use [On-Premise SDK](https://docs.platerecognizer.com/#on-premise-sdk)


- **source**: Must be a camera.

<p align="center">
Expand Down
40 changes: 36 additions & 4 deletions custom_components/platerecognizer/image_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import re
import io
from typing import List, Dict
import json

from PIL import Image, ImageDraw, UnidentifiedImageError
from pathlib import Path
Expand Down Expand Up @@ -42,6 +43,9 @@
CONF_ALWAYS_SAVE_LATEST_FILE = "always_save_latest_file"
CONF_WATCHED_PLATES = "watched_plates"
CONF_MMC = "mmc"
CONF_SERVER = "server"
CONF_DETECTION_RULE = "detection_rule"
CONF_REGION_STRICT = "region"

DATETIME_FORMAT = "%Y-%m-%d_%H-%M-%S"
RED = (255, 0, 0) # For objects within the ROI
Expand All @@ -60,6 +64,9 @@
vol.Optional(CONF_WATCHED_PLATES): vol.All(
cv.ensure_list, [cv.string]
),
vol.Optional(CONF_SERVER, default=PLATE_READER_URL): cv.string,
vol.Optional(CONF_DETECTION_RULE, default=False): cv.string,
vol.Optional(CONF_REGION_STRICT, default=False): cv.string,
}
)

Expand Down Expand Up @@ -112,6 +119,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
camera_entity=camera[CONF_ENTITY_ID],
name=camera.get(CONF_NAME),
mmc=config.get(CONF_MMC),
server=config.get(CONF_SERVER),
detection_rule = config.get(CONF_DETECTION_RULE),
region_strict = config.get(CONF_REGION_STRICT),

)
entities.append(platerecognizer)
add_entities(entities)
Expand All @@ -131,6 +142,9 @@ def __init__(
camera_entity,
name,
mmc,
server,
detection_rule,
region_strict,
):
"""Init."""
self._headers = {"Authorization": f"Token {api_token}"}
Expand All @@ -146,6 +160,9 @@ def __init__(
self._always_save_latest_file = always_save_latest_file
self._watched_plates = watched_plates
self._mmc = mmc
self._server = server
self._detection_rule = detection_rule
self._region_strict = region_strict
self._state = None
self._results = {}
self._vehicles = [{}]
Expand All @@ -156,6 +173,7 @@ def __init__(
self._image_width = None
self._image_height = None
self._image = None
self._config = {}
self.get_statistics()

def process_image(self, image):
Expand All @@ -167,14 +185,20 @@ def process_image(self, image):
self._orientations = []
self._image = Image.open(io.BytesIO(bytearray(image)))
self._image_width, self._image_height = self._image.size

if self._regions == DEFAULT_REGIONS:
regions = None
else:
regions = self._regions
if self._detection_rule:
self._config.update({"detection_rule" : self._detection_rule})
if self._region_strict:
self._config.update({"region": self._region_strict})
try:
_LOGGER.debug("Config: " + str(json.dumps(self._config)))
response = requests.post(
PLATE_READER_URL,
data=dict(regions=regions, camera_id=self.name, mmc=self._mmc),
self._server,
data=dict(regions=regions, camera_id=self.name, mmc=self._mmc, config=json.dumps(self._config)),
files={"upload": image},
headers=self._headers
).json()
Expand Down Expand Up @@ -202,7 +226,13 @@ def process_image(self, image):
if self._save_file_folder:
if self._state > 0 or self._always_save_latest_file:
self.save_image()
self.get_statistics()
if self._server == PLATE_READER_URL:
self.get_statistics()
else:
stats = response["usage"]
calls_remaining = stats["max_calls"] - stats["calls"]
stats.update({"calls_remaining": calls_remaining})
self._statistics = stats

def get_statistics(self):
try:
Expand Down Expand Up @@ -290,8 +320,10 @@ def device_state_attributes(self):
attr.update({"statistics": self._statistics})
if self._regions != DEFAULT_REGIONS:
attr[CONF_REGIONS] = self._regions
if self._server != PLATE_READER_URL:
attr[CONF_SERVER] = str(self._server)
if self._save_file_folder:
attr[CONF_SAVE_FILE_FOLDER] = str(self._save_file_folder)
attr[CONF_SAVE_TIMESTAMPTED_FILE] = self._save_timestamped_file
attr[CONF_ALWAYS_SAVE_LATEST_FILE] = self._always_save_latest_file
return attr
return attr

0 comments on commit 5137d26

Please sign in to comment.