justadudewhohacks / opencv4nodejs

Nodejs bindings to OpenCV 3 and OpenCV 4
MIT License
4.97k stars 827 forks source link

imread vs fs.readFile + new cv.Mat(buffer.. #7

Closed YuriGor closed 7 years ago

YuriGor commented 7 years ago

Hi, could you please direct me, how to determine correct constant to use in Mat constructor?

In readme there is an example:

const matFromArray = new cv.Mat(new Buffer.from(charData), rows, cols, cv.CV_8UC3);

in my case I has a buffer with PNG, but I don't know its parameters, only size.

When I save it to the file with fs.writeFile and then read it into matrix with imread, everything is ok. (I am doing 'norm' with another Mat and it works)

But I cant put this buffer directly into matrix correctly, if I do new cv.Mat(buffer, rows, cols); without third parameter, I cant later use it in norm, getting this error

OpenCV Error: Assertion failed (_src1.sameSize(_src2) && _src1.type() == _src2.type()) in norm, file /home/gor/opencv-master/modules/core/src/stat.cpp, line 3628

I used to get this error before in node-opencv + OpenCV v2.4.9 but it happened in both cases, with cv.readImage(path) and cv.readImage(buffer)

With your module imread works fine. Is there any way to extract from imread and reuse its PNG channels and type detector? I cant perform excessive read/write operations to file system, because it's too slow in my case (I generate and compare millions of PNG buffers runtime)

I found no implementation for OpenCV Mat methods such as size(), type(), channels(), depth() in your module, is there some way to use them? I suspect there is no good way to determine a constant I am looking for without this methods?

YuriGor commented 7 years ago

I found imdecode method and it works! Asking a question often makes to think clearer and inspires to research)

justadudewhohacks commented 7 years ago

Hello YuriGor, nice that you figured out how to do it. I just recently added imdecode and imencode, as I was facing the same issue as you when I was implementing the opencv-expressjs example. When you are reading a .jpg or .png image with fs or FileReader, you will get the data as a base64 string. Basically you have to do something like this: cv.imdecode(Buffer.from(base64Data, 'base64')), where base64Data is the string with the prefix removed. Creating a new Mat from a Buffer with charData does only work, if you have the raw unencoded data, such as you will receive from mat.getData() for example.

By the way you can retrieve a Mat's metadata by simply calling mat.cols, mat.rows, mat.type, mat.channels, mat.depth etc. . Alternatively you can simply console.log(mat).

The type will be the integer value of the code. You can look up the codes here: http://ninghang.blogspot.de/2012/11/list-of-mat-type-in-opencv.html.

YuriGor commented 7 years ago

As I can see there is no base64 encoding when I read png image from file. Look at my code, it works well withoud any base64-related stuff:

var Promise = require("bluebird")
  , fs = Promise.promisifyAll(require('fs'))
  , cv = require('opencv4nodejs');

var imgdir = __dirname+'/img/';
var img_o = imgdir + 'src/walken.png';
var img_d = imgdir + 'dst/walken.png';

var first = cv.imread(img_o);
fs.readFileAsync(img_d)
.then(buffer=>{
  var second = cv.imdecode(buffer);
  console.log(first.norm(second, cv.NORM_L1));
})

In real program I use my lib to receive base64-encoded string from PhantomJS and do exactly the same thing as you suggest, but not in case of reading file.

Do you have any plans to implement addWeighted method? I really miss it to be able to blend source images with my SVG

justadudewhohacks commented 7 years ago

Okay I see, interesting. I can add the bindings to addWeighted if you need it. Let me know if you miss any further methods and I will add them tomorrow.

YuriGor commented 7 years ago

I am not familiar with your module (and OpenCV itself) so I am not sure is something implemented or not before I tried to use it. I have plans to use this methods not listed in your doc

Also I have another question: most of the methods implemented as synchronous, while in node-opencv most of them are asynchronous. Does it mean we will face a performance issues? Is it possible to call opencv method in non-blocking way, to be able fully load all the processor cores?

justadudewhohacks commented 7 years ago

'isContourConvex' is a property of Contour, e.g. 'contour.isConvex'. Partition is implemented for Point2d only at the moment. I will create a PR for the methods you listed.

Regarding your other question:

Node-opencv uses callbacks, however the methods are not asynchronous. They are implemented synchronously, so imo there is no reason to use callbacks. The only async implementations I can see right now are CascadeClassifier, FaceRecognizer and AsyncImageSimilarity.

And yes it is possible to implement them async or make use of multithreading. I did not run in performance issues yet, as OpenCV is pretty fast for most of the stuff already. But I can see, why you would want to make your method calls non-blocking. I didn't make up my mind about provding an async API yet, but it is possible.

YuriGor commented 7 years ago

I suspected it will be too good to be really async, but even on single thread opencv MUCH faster than load pixels and iterate an array to calculate Norm in js manually as i tried at first.

justadudewhohacks commented 7 years ago

Yeah, JS is not very performant for CV tasks. Fortunately we can use compiled C++ libs such as OpenCV with Node.js.