mirror of
https://github.com/node-red/node-red-nodes.git
synced 2025-03-01 10:37:43 +00:00
Add annotate-image node
This commit is contained in:
155
utility/annotate-image/annotate.js
Normal file
155
utility/annotate-image/annotate.js
Normal file
@@ -0,0 +1,155 @@
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
const pureimage = require("pureimage");
|
||||
const Readable = require("stream").Readable;
|
||||
const Writable = require("stream").Writable;
|
||||
const path = require("path");
|
||||
|
||||
let fontLoaded = false;
|
||||
function loadFont() {
|
||||
if (!fontLoaded) {
|
||||
const fnt = pureimage.registerFont(path.join(__dirname,'./SourceSansPro-Regular.ttf'),'Source Sans Pro');
|
||||
fnt.load();
|
||||
fontLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
function AnnotateNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
const defaultFill = n.fill || "";
|
||||
const defaultStroke = n.stroke || "#ffC000";
|
||||
const defaultLineWidth = parseInt(n.lineWidth) || 5;
|
||||
const defaultFontSize = n.fontSize || 24;
|
||||
const defaultFontColor = n.fontColor || "#ffC000"
|
||||
loadFont();
|
||||
|
||||
this.on("input", function(msg) {
|
||||
if (Buffer.isBuffer(msg.payload)) {
|
||||
if (msg.payload[0] !== 0xFF || msg.payload[1] !== 0xD8) {
|
||||
node.error("Not a JPEG image",msg);
|
||||
} else if (Array.isArray(msg.annotations) && msg.annotations.length > 0) {
|
||||
const stream = new Readable();
|
||||
stream.push(msg.payload);
|
||||
stream.push(null);
|
||||
pureimage.decodeJPEGFromStream(stream).then(img => {
|
||||
const c = pureimage.make(img.width, img.height);
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.drawImage(img,0,0,img.width,img.height);
|
||||
|
||||
ctx.lineJoin = 'bevel';
|
||||
|
||||
msg.annotations.forEach(function(annotation) {
|
||||
ctx.fillStyle = annotation.fill || defaultFill;
|
||||
ctx.strokeStyle = annotation.stroke || defaultStroke;
|
||||
ctx.lineWidth = annotation.lineWidth || defaultLineWidth;
|
||||
ctx.lineJoin = 'bevel';
|
||||
let x,y,r,w,h;
|
||||
switch(annotation.type) {
|
||||
case 'rect':
|
||||
if (annotation.bbox) {
|
||||
x = annotation.bbox[0]
|
||||
y = annotation.bbox[1]
|
||||
w = annotation.bbox[2]
|
||||
h = annotation.bbox[3]
|
||||
} else {
|
||||
x = annotation.x
|
||||
y = annotation.y
|
||||
w = annotation.w
|
||||
h = annotation.h
|
||||
}
|
||||
|
||||
if (x < 0) {
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
if (y < 0) {
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = annotation.lineWidth || defaultLineWidth;
|
||||
ctx.rect(x,y,w,h);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
||||
if (annotation.label) {
|
||||
ctx.font = `${annotation.fontSize || defaultFontSize}pt 'Source Sans Pro'`;
|
||||
ctx.fillStyle = annotation.fontColor || defaultFontColor;
|
||||
ctx.textBaseline = "top";
|
||||
ctx.textAlign = "left";
|
||||
ctx.fillText(annotation.label, x+2,y)
|
||||
}
|
||||
break;
|
||||
case 'circle':
|
||||
if (annotation.bbox) {
|
||||
x = annotation.bbox[0] + annotation.bbox[2]/2
|
||||
y = annotation.bbox[1] + annotation.bbox[3]/2
|
||||
r = Math.min(annotation.bbox[2],annotation.bbox[3])/2;
|
||||
} else {
|
||||
x = annotation.x
|
||||
y = annotation.y
|
||||
r = annotation.r;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = annotation.lineWidth || defaultLineWidth;
|
||||
ctx.arc(x,y,r,0,Math.PI*2);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
if (annotation.label) {
|
||||
ctx.font = `${annotation.fontSize || defaultFontSize}pt 'Source Sans Pro'`;
|
||||
ctx.fillStyle = annotation.fontColor || defaultFontColor;
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(annotation.label, x+2,y)
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
const bufferOutput = getWritableBuffer();
|
||||
pureimage.encodeJPEGToStream(c,bufferOutput.stream,90).then(() => {
|
||||
msg.payload = bufferOutput.getBuffer();
|
||||
node.send(msg);
|
||||
})
|
||||
}).catch(err => {
|
||||
node.error(err,msg);
|
||||
})
|
||||
} else {
|
||||
// No annotations to make - send the message on
|
||||
node.send(msg);
|
||||
}
|
||||
} else {
|
||||
node.error("Payload not a Buffer",msg)
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("annotate-image",AnnotateNode);
|
||||
|
||||
|
||||
|
||||
function getWritableBuffer() {
|
||||
var currentSize = 0;
|
||||
var buffer = null;
|
||||
const stream = new Writable({
|
||||
write(chunk, encoding, callback) {
|
||||
if (!buffer) {
|
||||
buffer = Buffer.from(chunk);
|
||||
} else {
|
||||
var newBuffer = Buffer.allocUnsafe(currentSize + chunk.length);
|
||||
buffer.copy(newBuffer);
|
||||
chunk.copy(newBuffer,currentSize);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
currentSize += chunk.length
|
||||
callback();
|
||||
}
|
||||
});
|
||||
return {
|
||||
stream: stream,
|
||||
getBuffer: function() {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user