Node-red-contrib-image-tools - combining images

Hi folks,

the node red project I'm currently working on in my day job is at the point of being usable but I have to make an adjustment to my label (printed on a Dymo 450 thermal printer). The label is attached to a PCB and these new PCBs are just too small for our current 51mm x 19mm labels. So I'm reworking the layout for a 25mm x 25mm label, this doesn't lend itself to using a barcode so I'm switching to a QR code. I only need to put 68 characters into it so it's not very big.

So I have a template for my label and I'm able to add some man readable text to it, that all works fine on the current label. I can also make the QR code and save it to a flow.qr variable. The problem I'm having is combining my template (read from a png file) with the QR saved to the flow.qr I get no output from the node. I thought I had to use composite but then realised I probably should be using blit . . .

My template label png is 304 x 304 pixels and the QR code is approx 84 x 84 so there should be no problem with it fitting.

My test flow . . .

[{"id":"1778f6d3.186309","type":"image viewer","z":"b7065b03.85e128","name":"","width":160,"data":"payload","dataType":"msg","active":true,"x":1090,"y":1400,"wires":[[]]},{"id":"9d9012a6.2a615","type":"Barcode Generator","z":"b7065b03.85e128","name":"QR Code","data":"serial","dataType":"msg","barcode":"qrcode","barcodeType":"barcode","options":"","optionsType":"ui","sendProperty":"payload","props":[],"x":360,"y":1340,"wires":[["55f46c87.3663f4","d1aad62a.7addc8","5b0e0657.5e6698"]]},{"id":"f1bcfcf6.b6fea","type":"inject","z":"b7065b03.85e128","name":"Serial","props":[{"p":"serial","v":"00001066","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":210,"y":1340,"wires":[["9d9012a6.2a615"]]},{"id":"dfe6eb84.8d2a88","type":"jimp-image","z":"b7065b03.85e128","name":"QR Merge","data":"payload","dataType":"msg","ret":"img","parameter1":"qr","parameter1Type":"flow","parameter2":"5","parameter2Type":"num","parameter3":"70","parameter3Type":"num","parameter4":"BLEND_SOURCE_OVER","parameter4Type":"none","parameter5":"","parameter5Type":"none","parameter6":"","parameter6Type":"none","parameter7":"","parameter7Type":"none","parameter8":"","parameter8Type":"msg","sendProperty":"payload","sendPropertyType":"msg","parameterCount":7,"jimpFunction":"blit","selectedJimpFunction":{"name":"blit","fn":"blit","description":"blit the image with another Jimp image at x, y, optionally cropped","parameters":[{"name":"src","type":"","required":true,"hint":"the source image (a Jimp instance)","defaultType":"msg","defaultValue":"payload"},{"name":"x","type":"num","required":true,"hint":"the x position to blit the image"},{"name":"y","type":"num","required":true,"hint":"the y position to blit the image"},{"name":"srcx","type":"num","required":false,"hint":"the x position from which to crop the source image"},{"name":"srcy","type":"num","required":false,"hint":"the y position from which to crop the source image"},{"name":"srcw","type":"num","required":false,"hint":"the width to which to crop the source image"},{"name":"srch","type":"num","required":false,"hint":"the height to which to crop the source image"}]},"x":870,"y":1340,"wires":[["1778f6d3.186309"]]},{"id":"55f46c87.3663f4","type":"change","z":"b7065b03.85e128","name":"> flow.qr","rules":[{"t":"set","p":"qr","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":1340,"wires":[["857dc975.6722e8"]]},{"id":"857dc975.6722e8","type":"file in","z":"b7065b03.85e128","name":"Open label","filename":"/home/pi/dymo/1inchSqLabel.png","format":"","chunk":false,"sendError":false,"x":690,"y":1340,"wires":[["dfe6eb84.8d2a88","98f87b98.c5a4b8","4d68fd7d.2e4bb4"]]},{"id":"d1aad62a.7addc8","type":"image viewer","z":"b7065b03.85e128","name":"","width":160,"data":"payload","dataType":"msg","active":true,"x":530,"y":1400,"wires":[[]]},{"id":"98f87b98.c5a4b8","type":"image viewer","z":"b7065b03.85e128","name":"","width":160,"data":"payload","dataType":"msg","active":true,"x":830,"y":1400,"wires":[[]]},{"id":"4d68fd7d.2e4bb4","type":"function","z":"b7065b03.85e128","name":"check qr file","func":"msg.payload = flow.get(\"qr\");\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1070,"y":1260,"wires":[["5c25d5fc.fc9f2c"]]},{"id":"5c25d5fc.fc9f2c","type":"image viewer","z":"b7065b03.85e128","name":"","width":160,"data":"payload","dataType":"msg","active":true,"x":1310,"y":1400,"wires":[[]]},{"id":"5b0e0657.5e6698","type":"jimp-image","z":"b7065b03.85e128","name":"QR > file","data":"payload","dataType":"msg","ret":"img","parameter1":"qr.png","parameter1Type":"str","parameter2":"5","parameter2Type":"num","parameter3":"170","parameter3Type":"num","parameter4":"BLEND_OVERLAY","parameter4Type":"Blend","parameter5":"0.8","parameter5Type":"num","parameter6":"0.8","parameter6Type":"num","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","sendProperty":"payload","sendPropertyType":"msg","parameterCount":1,"jimpFunction":"write","selectedJimpFunction":{"name":"write","fn":"write","description":"Write to file. NOTE: You can specify an alternative file extension type to change the type. Currently support types are jpg, png, bmp.","parameters":[{"name":"filename","type":"str","required":true,"hint":"Name of the file","defaultType":"str"}]},"x":660,"y":1260,"wires":[[]]}]

[{"id":"1778f6d3.186309","type":"image viewer","z":"af952aeaa20f4f97","name":"","width":160,"data":"merged","dataType":"msg","active":true,"x":1370,"y":1500,"wires":[[]]},{"id":"9d9012a6.2a615","type":"Barcode Generator","z":"af952aeaa20f4f97","name":"QR Code","data":"serial","dataType":"msg","barcode":"qrcode","barcodeType":"barcode","options":"","optionsType":"ui","sendProperty":"qrBuffer","props":[],"x":680,"y":1420,"wires":[["d9581a7e1b3a7366"]]},{"id":"f1bcfcf6.b6fea","type":"inject","z":"af952aeaa20f4f97","name":"Serial","props":[{"p":"serial","v":"00001066","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1420,"wires":[["9d9012a6.2a615"]]},{"id":"dfe6eb84.8d2a88","type":"jimp-image","z":"af952aeaa20f4f97","name":"QR Merge","data":"template","dataType":"msg","ret":"img","parameter1":"qr","parameter1Type":"msg","parameter2":"qrx","parameter2Type":"msg","parameter3":"qry","parameter3Type":"msg","parameter4":"0","parameter4Type":"num","parameter5":"0","parameter5Type":"num","parameter6":"qrw","parameter6Type":"msg","parameter7":"qrh","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","sendProperty":"merged","sendPropertyType":"msg","parameterCount":7,"jimpFunction":"blit","selectedJimpFunction":{"name":"blit","fn":"blit","description":"blit the image with another Jimp image at x, y, optionally cropped","parameters":[{"name":"src","type":"","required":true,"hint":"the source image (a Jimp instance)","defaultType":"msg","defaultValue":"payload"},{"name":"x","type":"num","required":true,"hint":"the x position to blit the image"},{"name":"y","type":"num","required":true,"hint":"the y position to blit the image"},{"name":"srcx","type":"num","required":false,"hint":"the x position from which to crop the source image"},{"name":"srcy","type":"num","required":false,"hint":"the y position from which to crop the source image"},{"name":"srcw","type":"num","required":false,"hint":"the width to which to crop the source image"},{"name":"srch","type":"num","required":false,"hint":"the height to which to crop the source image"}]},"x":1210,"y":1500,"wires":[["1778f6d3.186309"]]},{"id":"d1aad62a.7addc8","type":"image viewer","z":"af952aeaa20f4f97","name":"","width":160,"data":"qr","dataType":"msg","active":true,"x":510,"y":1500,"wires":[["274df8a7b5fe670f"]]},{"id":"98f87b98.c5a4b8","type":"image viewer","z":"af952aeaa20f4f97","name":"","width":160,"data":"template","dataType":"msg","active":true,"x":1030,"y":1500,"wires":[["dfe6eb84.8d2a88"]]},{"id":"274df8a7b5fe670f","type":"jimp-image","z":"af952aeaa20f4f97","name":"open template","data":"c:/temp/label_template_1.png","dataType":"str","ret":"img","parameter1":"","parameter1Type":"msg","parameter2":"","parameter2Type":"msg","parameter3":"","parameter3Type":"msg","parameter4":"","parameter4Type":"msg","parameter5":"","parameter5Type":"msg","parameter6":"","parameter6Type":"msg","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","sendProperty":"template","sendPropertyType":"msg","parameterCount":0,"jimpFunction":"none","selectedJimpFunction":{"name":"none","fn":"none","description":"Just loads the image.","parameters":[]},"x":700,"y":1500,"wires":[["05aaba7fe1e31eb1"]]},{"id":"d9581a7e1b3a7366","type":"jimp-image","z":"af952aeaa20f4f97","name":"","data":"qrBuffer","dataType":"msg","ret":"img","parameter1":"","parameter1Type":"msg","parameter2":"","parameter2Type":"msg","parameter3":"","parameter3Type":"msg","parameter4":"","parameter4Type":"msg","parameter5":"","parameter5Type":"msg","parameter6":"","parameter6Type":"msg","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","sendProperty":"qr","sendPropertyType":"msg","parameterCount":0,"jimpFunction":"none","selectedJimpFunction":{"name":"none","fn":"none","description":"Just loads the image.","parameters":[]},"x":850,"y":1420,"wires":[["d1aad62a.7addc8"]]},{"id":"05aaba7fe1e31eb1","type":"function","z":"af952aeaa20f4f97","name":"calc center","func":"\nmsg.qrh = msg.qr.bitmap.height;\nmsg.qrw = msg.qr.bitmap.width;\n\nmsg.qrx = (msg.template.bitmap.height / 2) - (msg.qrh / 2);\nmsg.qry = (msg.template.bitmap.width / 2) - (msg.qrw / 2);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":870,"y":1500,"wires":[["98f87b98.c5a4b8"]]}]

Why it didnt work for you...

The blit src requires the incoming image to be a jimp image object.

  • The QR code is generated as a buffer - so an image node is required to convert the QR to a jimp image
  • The file node loads a buffer - so an image node is required to convert the template to a jimp image - OR - like i did, use an image node to load the template

PS, avoid flow context where possible as you can get into issues with concurrency when the flow runs faster that the images can be processed. Instead of flow, use msg props to carry the loaded image through the flow. (check the output property names I used)

PS 2 - avoid branching as much as possible when working with images - try to keep image msgs serial. This avoids unnecessary msg cloning (images are large when decoded into bitmaps in memory)

2 Likes

Hi Steve,

thanks for the help and explanation, I'll take on board your advice about best practice too. My Node Red is getting better, it's only my 2nd project but is much much better compared to my first from 2 years ago.

thanks again,

Simon

1 Like

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