bartbutenaers / node-red-contrib-plate-recognizer

A Node-RED node for license plate recognizing via platerecognizer.com
Apache License 2.0
4 stars 5 forks source link

node-red-contrib-plate-recognizer

A Node-RED node for license plate recognizing via platerecognizer.com

Install

Run the following npm command in your Node-RED user directory (typically ~/.node-red):

npm install node-red-contrib-plate-recognizer

Note that you need to signup for an account on platerecognizer.com, and paste your private token into this node's config screen. With a free account there is a limit to recognize 2500 images per month, but they also offer various paid license models.

Support my Node-RED developments

Please buy my wife a coffee to keep her happy, while I am busy developing Node-RED stuff for you ...

Buy my wife a coffee

Node usage

This node will detect and recognise license plates in an image, using a deep learning (cloud) service. The AI (cloud) service has been trained for license plates for more than 100 countries. They also offer an SDK for local setups, which can easily be installed as a Docker container. See also our blog for an introduction.

:warning: When you have an image with an incorrect recognition result, don't hesitate to contact the people of platerecognizer.com! They offer great support. When you provide them the image, they will analyse it and try to solve the problem. This way the system can become better and better ...

Send an image (as buffer or base64 encoded string) via an input message, to start a recognition:

Basic flow

[{"id":"38586517.a5bf9a","type":"plate-recognizer","z":"d9a54719.b13a88","name":"","inputField":"payload","inputFieldType":"msg","outputField":"payload","outputFieldType":"msg","url":"https://api.platerecognizer.com/v1/plate-reader/","ignoreDuring":false,"makeAndModel":false,"statusText":"none","cameraId":"","regionFilter":false,"timestamp":false,"regionList":"","regionListType":"json","x":880,"y":660,"wires":[["30cb89da.c35546"],[]]},{"id":"e4699284.081c7","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":500,"y":660,"wires":[["9e2c9295.7f9a9"]]},{"id":"30cb89da.c35546","type":"debug","z":"d9a54719.b13a88","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1060,"y":660,"wires":[]},{"id":"9e2c9295.7f9a9","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"https://www.mercedes-benz.com/en/classic/history/h-number-plate-2020/_jcr_content/root/paragraph/paragraph-right/paragraphimage/image/MQ6-8-image-20191205151927/02-mercedes-benz-classic-h-number-plate-2020-2560x1440.jpeg","tls":"","persist":false,"proxy":"","authType":"","x":680,"y":660,"wires":[["38586517.a5bf9a"]]}]

The output message will contain the recognition results (in json format):

Basic output

The "results" will be a json array, containing a separate element for each license plate that has been recognised. When the image contains N cars, then the array will contain N elements.

For each recognised license plate, some basic information will be delivered:

Node properties

The node can be configured via a series of settings on the config screen:

Input field

The field of the input message which will need to contain the input image. By default msg.payload will be used. The image should be a binary Buffer or a base64 encoded string.

Output field

The field of the output message where the recognition result will be stored (in JSON format). By default msg.payload will be used.

API token

Create an account at platerecogniser.com and enter your private API token here.

URL

Specify the URL of the recognition service, to allow different kind of setups:

Camera ID

Optionally a camera id can be specified, which will be sent to the recognition service.

Status text

Specify how the recognition result needs to be displayed in the node status label:

Ignore images arriving during recognition

When selected images will automatically be skipped, when the previous image is still being recognized. When deselected multiple images can be recognized simultaneously.

Predict vehicle make and model (MMC)

When selected not only the plate will be recognized, but there will also be a prediction of the vehicle brand and type.

CAUTION: this is only supported for some paid account types!

Send separate message for each plate:

When selected a separate output message will be send for each recognized license plate. If not selected a single output message will be send containing an array of ALL recognized license plates. See the section "Split output messages" below for more information.

Specify one or more regions

When selected, an array of region codes can be specified (see supported regions.

Example flow (different cases)

The following flow explains some different use cases:

Note that the node-red-contrib-image-output node needs to be installed also!

image

[{"id":"fff72ad.6f8c7d8","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":920,"y":460,"wires":[["ee55dd94.02b2e"]]},{"id":"28523886.f82428","type":"debug","z":"d9a54719.b13a88","name":"Normal recognitions","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"recognition","targetType":"msg","x":1560,"y":580,"wires":[]},{"id":"ee55dd94.02b2e","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"https://www.mercedes-benz.com/en/classic/history/h-number-plate-2020/_jcr_content/root/paragraph/paragraph-right/paragraphimage/image/MQ6-8-image-20191205151927/02-mercedes-benz-classic-h-number-plate-2020-2560x1440.jpeg","tls":"","persist":false,"proxy":"","authType":"","x":1100,"y":460,"wires":[["231d72b4.5233ae"]]},{"id":"10f189ae.db2d76","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":920,"y":660,"wires":[["24464bb6.a07b64"]]},{"id":"24464bb6.a07b64","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"https://image.freepik.com/free-photo/empty-parking-lot_1127-3298.jpg","tls":"","persist":false,"proxy":"","authType":"","x":1100,"y":660,"wires":[["231d72b4.5233ae"]]},{"id":"5ee1acd5.4e0784","type":"comment","z":"d9a54719.b13a88","name":"No cars","info":"","x":890,"y":620,"wires":[]},{"id":"231d72b4.5233ae","type":"plate-recognizer","z":"d9a54719.b13a88","name":"","inputField":"payload","inputFieldType":"msg","outputField":"recognition","outputFieldType":"msg","url":"https://api.platerecognizer.com/v1/plate-reader/","ignoreDuring":false,"makeAndModel":false,"statusText":"scores","cameraId":"","regionFilter":false,"timestamp":false,"regionList":"","regionListType":"json","x":1320,"y":660,"wires":[["28523886.f82428","37b1b695.62fa4a"],["100a66ab.4607f9"]]},{"id":"37b1b695.62fa4a","type":"image","z":"d9a54719.b13a88","name":"Show analyzed image","width":"400","data":"payload","dataType":"msg","thumbnail":false,"active":true,"x":1740,"y":640,"wires":[]},{"id":"869cd924.d991d8","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":920,"y":960,"wires":[["f6b6ac8f.40806"]]},{"id":"f6b6ac8f.40806","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"https://static.nieuwsblad.be/Assets/Images_Upload/2015/05/24/patton_drivers_2015_1.jpg?maxheight=460&maxwidth=638&scale=both","tls":"","persist":false,"proxy":"","authType":"","x":1100,"y":960,"wires":[["231d72b4.5233ae"]]},{"id":"6ac3b436.692fac","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":920,"y":760,"wires":[["d2c4249d.901fd8"]]},{"id":"d2c4249d.901fd8","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"http://newscrane.com/wp-content/uploads/2019/09/Car-insurance-Newscrane-02.jpg","tls":"","persist":false,"proxy":"","authType":"","x":1100,"y":760,"wires":[["231d72b4.5233ae"]]},{"id":"9a21e444.b22fc8","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":920,"y":560,"wires":[["8e81e18d.39882"]]},{"id":"8e81e18d.39882","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"http://www.piepenbroek.nl/foto2010/baltisch/IMG_1499.JPG","tls":"","persist":false,"proxy":"","authType":"","x":1100,"y":560,"wires":[["231d72b4.5233ae"]]},{"id":"5899f099.1b645","type":"comment","z":"d9a54719.b13a88","name":"Two cars","info":"","x":900,"y":520,"wires":[]},{"id":"1db8bd98.23d672","type":"comment","z":"d9a54719.b13a88","name":"One car","info":"","x":890,"y":420,"wires":[]},{"id":"100a66ab.4607f9","type":"debug","z":"d9a54719.b13a88","name":"Errors","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"recognition","targetType":"msg","x":1510,"y":680,"wires":[]},{"id":"cdfaa780.c2fa18","type":"comment","z":"d9a54719.b13a88","name":"Truck with labels","info":"","x":920,"y":920,"wires":[]},{"id":"c1d2f038.82ecc","type":"comment","z":"d9a54719.b13a88","name":"Car at angle","info":"","x":910,"y":720,"wires":[]},{"id":"8c4f0d01.426a2","type":"inject","z":"d9a54719.b13a88","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":920,"y":860,"wires":[["7b580c6e.105764"]]},{"id":"7b580c6e.105764","type":"http request","z":"d9a54719.b13a88","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"https://askautoexperts.com/wp-content/uploads/1931-Dodge-1024x768.jpg","tls":"","persist":false,"proxy":"","authType":"","x":1100,"y":860,"wires":[["231d72b4.5233ae"]]},{"id":"da494e5a.e21ff","type":"comment","z":"d9a54719.b13a88","name":"Another car at angle","info":"","x":930,"y":820,"wires":[]}]

Recognition status

The output message will contain the recognition status (and statusText):

status

Plate statistics

Since the number of recognitions per month is limited (e.g. 2500 for a free account), it is very useful to determine from time to time how many recognitions are left. This way you can avoid situations where you are not aware that you have run out of recognitions...

A second node ("Plate statistics") has been provided to get the statistics of your account (with 'URL' and 'API token' settings identical as described above):

Statistics flow

[{"id":"9b354f46.f081d","type":"plate-statistics","z":"d9a54719.b13a88","name":"","outputField":"payload","outputFieldType":"msg","url":"https://api.platerecognizer.com/v1/statistics/","x":980,"y":480,"wires":[["54063a77.493ae4"]]},{"id":"5653b69.13c4648","type":"inject","z":"d9a54719.b13a88","name":"Get statistics","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":770,"y":480,"wires":[["9b354f46.f081d"]]},{"id":"54063a77.493ae4","type":"debug","z":"d9a54719.b13a88","name":"Plate statistics","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1190,"y":480,"wires":[]}]

The resulting statistics (in json format) contain the maximum number of statistics, and also the used number of statistics of the current month:

Statistics output

Split output messages

When the input message contains multiple license plates, then the output message will contain an array of license plates. Since not all Node-RED nodes can handle arrays as input, it might be required to split the array into separate items. In other words the single output message (containing an array of N license plates) need to be split into N separate output messages (each one containing a single license plate).

CAUTION: to avoid conflicts, the original input message will be cloned N times. But since the output message also contains the input image, that input image will also be cloned N times. As a result extra system resources (CPU and memory) will be used!

Using a Split node

The Split node is a Node-RED core node that can be used to split a single message into multiple messages:

Split node flow

[{"id":"ef944a38.bbef38","type":"plate-recognizer","z":"c8a948fc.76ade8","name":"","inputField":"payload","inputFieldType":"msg","outputField":"payload","outputFieldType":"msg","url":"https://api.platerecognizer.com/v1/plate-reader/","ignoreDuring":true,"makeAndModel":false,"statusText":"count","cameraId":"","regionFilter":false,"regionList":"[]","regionListType":"json","x":780,"y":500,"wires":[["78a775a2.7e37cc","60071b7.6a1cae4"],[]]},{"id":"2e44447f.4e42dc","type":"split","z":"c8a948fc.76ade8","name":"Split array","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":1220,"y":500,"wires":[["745a5d4a.ff4e34"]]},{"id":"5a39a5a9.55c97c","type":"http request","z":"c8a948fc.76ade8","name":"Get video stream","method":"GET","ret":"bin","paytoqs":false,"url":"http://www.piepenbroek.nl/foto2010/baltisch/IMG_1499.JPG","tls":"","persist":false,"proxy":"","authType":"","x":570,"y":500,"wires":[["ef944a38.bbef38","dad27e21.9ab27"]]},{"id":"dad27e21.9ab27","type":"image","z":"c8a948fc.76ade8","name":"","width":"400","data":"payload","dataType":"msg","thumbnail":false,"active":true,"x":780,"y":580,"wires":[]},{"id":"745a5d4a.ff4e34","type":"debug","z":"c8a948fc.76ade8","name":"Show messages","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1380,"y":500,"wires":[]},{"id":"15ee9680.b977ea","type":"inject","z":"c8a948fc.76ade8","name":"Start the test","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":500,"wires":[["5a39a5a9.55c97c"]]},{"id":"78a775a2.7e37cc","type":"change","z":"c8a948fc.76ade8","name":"payload = payload.results","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.results","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":500,"wires":[["2e44447f.4e42dc"]]},{"id":"60071b7.6a1cae4","type":"debug","z":"c8a948fc.76ade8","name":"Show messages","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":980,"y":440,"wires":[]}]

The flow explained step by step:

  1. Start the flow by pressing the button on the Inject-node
  2. The second node gets an image (e.g. via a http request from an ip camera)
  3. In the image-preview node you can see that the image contains two license plates
  4. The plate recognizer node detects two plates
  5. Via a debug node you can see the json output: it is a single message containing an array of two license plates.
  6. I move the payloads.result field to the payload field (because the next node expects the array in the payload field).
  7. The split node splits the array in the payload, which means the single message will be splitted in two separate messages.
  8. With a debug node you will see that we now have two separate messages, each one containing a single license plate (which can now be handled easily by other nodes in the flow...).

Using the build-in splitter

Since the Split node will cause our flow to become a bit more complex, this node offers a build-in split functionality. When the "Send separate message for each plate" checkbox is activated, an input image (containing N license plates) will result in N output messages (each one containing a single license plate).

Edge case: when the input message contains NO license plate, then a single output message will be sent containing an empty result:

Empty result

Remark: in the latter case, we could have decided to send no output message (since no license plate has been detected). But when somebody sends a picture to the input, he will expect something back ...