Creating a node for License Plate Recognition

I am using the following service: http://platerecognizer.com
And while I successfully created a flow that will use the curl in the exec node, and then the JSON node to read the plates from the image I am just not satisfied with how it all looks together. And would love to create a node.
But from where should I start, the only setting the node should have is an API field and a name field, and the only input should be the image itself, and the output is a simple JSON, and all of the output content is useful for someone building software for an example public parking.
Could somebody help me out here? Could I somehow use the existing code for javascript from their website?
I have created the package.json and the lpr.html
Tried reading the documentation, but I still have a lot of question.

<script type="text/javascript">
    RED.nodes.registerType('license-plate-recognition',{
        category: 'recognition',
        color: '#349eeb',
        inputs:1,
        outputs:1,
        icon: "icons/icon.png",
        label: function() {
            return this.name||"license-plate-recognition";
        }
    });
</script>

<script type="text/html" data-template-name="license-plate-recognition">
    <div class="form-row">
        <label for="node-input-name"><i class="icon-tag"></i> Name</label>
        <input type="text" id="node-input-name" placeholder="Name">
         <label for="pwd">API Token:</label>
        <input type="password" id="API" name="API"> 
    </div>
</script>


<script type="text/html" data-help-name="lower-case">
    <p>Automatic Number Plate Recognition software that works in all environments, optimized for your location.</p>
</script>

Hi @lizzardguki,

That would be a nice node! By coincidence I have been reading about the plate-recognizer last weekend. Don't forget to mention in the info panel that the number of free images per month is limited to 2500 recognitions...

You can always have a look at my (old) node-red-contrib-openalpr node as an example. That one uses the OpenAlpr cloud service, but it also produces a json output. But my json is a bit more complex, since it also recognizes the vehicle itself (brand ...).

I assume you can implement the http POST request with authorization header, by using this code snippet.

Bart

Thanks Bart for your help , will checkout your contribution , it was really easy for me to write functions inside node red, but writing a node itself is a little problem , and i hope openalpr will help me.

You are welcome!
Some quick tips to get you started:

  • 1 input port where you receive images as binary buffers or base64 string, to make it widely usable. Since you need to send it base64 encoded to the cloud service, you can use our code snippet from the node-red-contrib-image-output node as an example:
    if (Buffer.isBuffer(image)) {
        image = image.toString("base64");
    }
    
  • 1 output port. Don't know if your output messages will only contain the json? Personally I would prefer to have both the image AND the json in the output. Then (in the next nodes in the flow) the output message could be used e.g. to mail the image when a number plate is not in some list of known number plates ...
  • perhaps also avoid parallel recognitions? E.g. keep a boolean isRecognizing: when you the previous image is still underway from the cloud service, then isRecognizing is true. When a new image arrives meanwhile, it could be rejected (since isRecognizing is still true).

Looking forward to test it!

Are the Stream and buffer same? While reading the code on ALPR I see a

fs.createReadStream(image_path)

personally I just want the following javascript code working as a self node-red node

So image as input and json as output.

const fetch = require('node-fetch');
const FormData = require('form-data');
const fs = require('fs');

let image_path = '/path/to/car.jpg'
let body = new FormData();
body.append('upload', fs.createReadStream(image_path));
// Or body.append('upload', base64Image);
body.append('regions', 'gb');
fetch("https://api.platerecognizer.com/v1/plate-reader/", {
        method: 'POST',
        headers: {
            "Authorization": "Token API_TOKEN"
        },
        body: body
    }).then(res => res.json())
    .then(json => console.log(json))
    .catch((err) => {
        console.log(err);
    });

Personally I wouldn't do it like that. Now you have to write all your images to disc and read them from there again (via the read stream).
Normally previous nodes in your flow will read an image from an ip camera and send the image to your node via messages (e.g. in the msg.payload). So just get the images from the input messages:

node.on("input", function(msg) {
   // payload should contain an image as buffer or base64 encoded string
   var image = msg.payload;

   // make sure all images are base64 encoded
   if (Buffer.isBuffer(image)) {
       image = image.toString("base64");
   }
   
   var authorizationHeader = "Authorization": "Basic " + new Buffer("Token:" + node.credentials.token, "utf8").toString("base64")

   // Pass the base64 encoded image to the cloud service 
   ...
}

You should keep the images in memory, and avoid any disc io!!! And for use cases where the images are stored on disc, you can add a File-In node just before your plate-recognizer node (to read the images from disc).

Caution: your private token really should be stored in the node credentials!!! Don't use a plain text input field! Otherwise all other nodes can read it, and when somebody export his flow (to share with others) you will also export his token as plain text, and make it visible to everybody.

It would also be nice if you could add an URL field on your node's config screen (which would be default filled with https://api.platerecognizer.com/v1/plate-reader/). Because they also offer a Docker container to run the whole recognition offline on a local server. In that case you should be able to enter the local url into your config screen, to send the images to your own local recognition service ...

My time is up for today. Good luck !!

Visual studio code gives me an error that an comma is needed after "Authorization": should i worry about it ?

Ah perhaps it should be "Authorization: Basic ".
Have not tested that code. Just quick typing on my smartphone...

And I see now that you got your code snippet from here, where you can also find:

So to me it seems that you don't even need to base64 encode the token, i.e. something like this:

node.on("input", function(msg) {
   // payload should contain an image as buffer or base64 encoded string
   var image = msg.payload;

   // make sure all images are base64 encoded
   if (Buffer.isBuffer(image)) {
       image = image.toString("base64");
   }
   
   var body = new FormData();
   body.append('upload', image);

   // Pass the base64 encoded image to the cloud (or docker) service 
   fetch(node.url, {
        method: 'POST',
        headers: {
            "Authorization": "Token " + node.credentials.token
        },
        body: body
    }).then(res => res.json())
    .then(json => {
         // Don't override the original msg.payload, which contains the input image
         msg.recognition = json;
         node.send(msg);
    })
    .catch((err) => {
        console.log(err);
    });
}

P.S. None of my code is tested, it is just to give you a quick idea!!!!

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.