基於 Node-red實現opencv邊緣檢測節點

基於 Node-red實現opencv邊緣檢測節點

最近在學習如何將opencv的一些用例以節點的形式加入到Node-red供用戶使用,由於opencv邊緣檢測最簡單,所以用其練手,來熟悉下開發流程。node-red上opencv節點基本框架三部分模塊構成。

  • opencv邊緣檢測模塊

  • 基於V8的js調用C++模塊

  • nodejs邊緣檢測模塊


下面是opencv邊緣檢測模塊用例,共有三個函數分別供node-red圖像輸入節點、圖像檢測節點和圖像輸出節點調用,另外使用libuv定時器,用於在各node節點初始化後模擬圖像輸入,該模塊最後將編譯成靜態庫的形式使用。

#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <opencv2/opencv.hpp>
#include <string>
#include <uv.h>
#include "cv_edge.h"

using namespace std;
using namespace cv;

//global function and parameter called by timer
static sendMsgCb imageInMsgCb;
static Mat deliverImg;

//the timer to send the mat of image
uv_timer_t imageInTimer;


void timer_cb(uv_timer_t *handle)
{
    printDebugMsg("C++ canny", "globalImg: %x", &deliverImg);
    imageInMsgCb((unsigned long)&deliverImg);
}

int imageInInit(char *path, sendMsgCb cb){
    int ret;
    if(cb != NULL){
        imageInMsgCb = cb;
    }

    if(path == NULL){
        printDebugMsg("C++ canny", "the path is null");
        return ERR_UNKNOWN;
    }

    printDebugMsg("C++ canny", "image in path: %s", path);
    Mat img = imread(path, 0); 
    if(img.empty()){
        printDebugMsg("C++ canny", "the path doesn't exist:%s", path);
        return ERR_UNKNOWN;
    }
    deliverImg = img;

    //intialize and start timer
    ret = uv_timer_init(uv_default_loop(), &imageInTimer);
    uv_timer_start(&imageInTimer, timer_cb, 1000, 0);

    return ERR_NONE;
}

int imageEdgeCanny(unsigned long mat, sendMsgCb cb){
    Mat result;
    Canny(*(Mat *)mat, result, 50 ,150);
    printDebugMsg("C++ canny", "mat before:%x, after:%x", mat, &result);
    cb((unsigned long)&result);   
    return ERR_NONE;
}

int imageOutSave(unsigned long mat, char *path){
    imwrite(path, *(Mat *)mat);
    printDebugMsg("C++ canny","mat: %x, image out path: %s", mat, path);
    return ERR_NONE;
}

有了C++程序,那js如何去調用C++程序?這裏就要用到google的v8引擎,v8引擎採用C++開發,利用V8我們將C++庫打包成nodejs模塊的形式供js程序調用,這樣我們就可以很方便的使用js調用C++接口,下面是基於v8圖像檢測程序,其中imageEdgeCannyV8會調用上面寫的opencv用例接口,最後,通過node-gyp工具就可以把opencv庫和v8模塊打包成後綴名爲.node的庫供js程序調用。

#include "cv_edge_addon.h"
#include <stdlib.h>
#include <string.h>
#include "_globalvar.h"
using namespace v8;

Handle<Value> imageEdgeCannyV8(const Arguments &args){
    HandleScope scope;
    //convert the mat js pointer to C++ pointer
    V8_ASSERT(args[0]->IsUint32(), "args[0] parameters error!");
    int arg0 = (int)args[0]->IntegerValue();
    printDebugMsg("V8 image edge","arg0: %x", arg0);

    //get the function for sending message, convert js function to C++ function pointer
    V8_ASSERT(args[1]->IsFunction(), "arg[1] parameters error!");
    cbArray[0] = Persistent<Function>::New(Local<Function>::Cast(args[1]));
    sendMsgCb arg1;
    arg1 = cbFunc0;

    int ret = (int) imageEdgeCanny(arg0, arg1);
    printDebugMsg("V8 image edge","return value: %d", ret);
    //covert C++ int to js int
    Handle<Value> retV8 = Int32::New(ret);
    return scope.Close(retV8);
}

static void SetMemberFunc(Handle<Object> obj){
    //register the interface for upper js to call
    obj->Set(String::NewSymbol("imageEdgeCanny"), 
            FunctionTemplate::New(imageEdgeCannyV8)->GetFunction());
}

static void SetConst(Handle<Object> obj){
}



static void SetEnumConst(Handle<Object> obj){

}

static void SetGlobalVarFunc(Handle<Object> obj){

}

void Initcv_canny(Handle<Object> exports){

    SetMemberFunc(exports);

    SetConst(exports);

    SetEnumConst(exports);

    SetGlobalVarFunc(exports);
}

上述模塊編好之後,我們只需在js程序中使用require將模塊包含進來調用。在node-red中分別加入圖像輸入節點,圖像邊緣檢測節點和圖像輸出節點,下邊是圖像邊緣檢測節點的js程序

var IOLIB = require('../cv/addon');

var io = new IOLIB.IO({
    log: true,
    quickInit: false
});

var RedUtil = require('red-util');

module.exports = function(RED) {
    function edgeDetect(config) {
        console.log("edgeDetect intialize...");
        RED.nodes.createNode(this,config);
        var node = this;
        var redUtil = new RedUtil(node, config);
        var arg1 = function(){
            console.log("send edgeDetect message");
            node.send({
                'payload':arguments[0]
            });
        }
        node.on('input', function(msg){
        //check if the msg is valid
        if(!redUtil.isValid(msg))
            return;
        //covert json to int
        var args = redUtil.msgToArgs(msg);
        var arg0 = redUtil.jsonIntegerParse(args[0]);

        io.imageEdgeCanny(arg0, arg1);
        });

        node.close('close', function(){

        });
    }   
    RED.nodes.registerType("edgeDetect", edgeDetect);
}

最後,打開node-red界面,將新加入的imageIn,edgeDetect,imageOut拖動工作區並連到一起,設置imageIn節點和imageOut的圖像讀取路徑和處理後的保存路徑。
應用拓撲圖

設置輸入節點圖像路徑

設置輸出節點圖像路徑

點擊deploy,既可看到處理前和處理後的圖像。
這裏寫圖片描述

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章