mirror of
https://github.com/peterbraden/node-opencv.git
synced 2025-12-08 19:45:55 +00:00
Merge branch 'master' of github.com:peterbraden/node-opencv
Conflicts: src/Matrix.cc src/Matrix.h
This commit is contained in:
commit
7548d39616
180
README.md
180
README.md
@ -13,31 +13,31 @@ You'll need OpenCV 2.3.1 installed.
|
||||
|
||||
Then:
|
||||
|
||||
|
||||
npm install opencv
|
||||
|
||||
```bash
|
||||
$ npm install opencv
|
||||
```
|
||||
|
||||
Or to build the repo:
|
||||
|
||||
|
||||
node-gyp rebuild
|
||||
|
||||
```bash
|
||||
$ node-gyp rebuild
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Face Detection
|
||||
|
||||
|
||||
cv.readImage("./examples/test.jpg", function(err, im){
|
||||
im.detectObject(cv.FACE_CASCADE, {}, function(err, faces){
|
||||
for (var i=0;i<faces.length; i++){
|
||||
var x = faces[i]
|
||||
im.ellipse(x.x + x.width/2, x.y + x.height/2, x.width/2, x.height/2);
|
||||
}
|
||||
im.save('./out.jpg');
|
||||
});
|
||||
})
|
||||
|
||||
```javascript
|
||||
cv.readImage("./examples/test.jpg", function(err, im){
|
||||
im.detectObject(cv.FACE_CASCADE, {}, function(err, faces){
|
||||
for (var i=0;i<faces.length; i++){
|
||||
var x = faces[i]
|
||||
im.ellipse(x.x + x.width/2, x.y + x.height/2, x.width/2, x.height/2);
|
||||
}
|
||||
im.save('./out.jpg');
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## API Documentation
|
||||
@ -49,42 +49,52 @@ base datastructure in OpenCV. Things like images are just matrices of pixels.
|
||||
|
||||
#### Creation
|
||||
|
||||
new Matrix(rows, cols)
|
||||
```javascript
|
||||
new Matrix(rows, cols)
|
||||
```
|
||||
|
||||
Or if you're thinking of a Matrix as an image:
|
||||
|
||||
new Matrix(height, width)
|
||||
```javascript
|
||||
new Matrix(height, width)
|
||||
```
|
||||
|
||||
Or you can use opencv to read in image files. Supported formats are in the OpenCV docs, but jpgs etc are supported.
|
||||
|
||||
cv.readImage(filename, function(mat){
|
||||
...
|
||||
})
|
||||
```javascript
|
||||
cv.readImage(filename, function(mat){
|
||||
...
|
||||
})
|
||||
|
||||
cv.readImage(buffer, function(mat){
|
||||
...
|
||||
})
|
||||
cv.readImage(buffer, function(mat){
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
If you need to pipe data into an image, you can use an ImageDataStream:
|
||||
|
||||
var s = new cv.ImageDataStream()
|
||||
```javascript
|
||||
var s = new cv.ImageDataStream()
|
||||
|
||||
s.on('load', function(matrix){
|
||||
...
|
||||
})
|
||||
s.on('load', function(matrix){
|
||||
...
|
||||
})
|
||||
|
||||
fs.createReadStream('./examples/test.jpg').pipe(s);
|
||||
fs.createReadStream('./examples/test.jpg').pipe(s);
|
||||
```
|
||||
|
||||
If however, you have a series of images, and you wish to stream them into a
|
||||
stream of Matrices, you can use an ImageStream. Thus:
|
||||
|
||||
var s = new cv.ImageStream()
|
||||
```javascript
|
||||
var s = new cv.ImageStream()
|
||||
|
||||
s.on('data', function(matrix){
|
||||
...
|
||||
})
|
||||
s.on('data', function(matrix){
|
||||
...
|
||||
})
|
||||
|
||||
ardrone.createPngStream().pipe(s);
|
||||
ardrone.createPngStream().pipe(s);
|
||||
```
|
||||
|
||||
Note: Each 'data' event into the ImageStream should be a complete image buffer.
|
||||
|
||||
@ -92,36 +102,42 @@ Note: Each 'data' event into the ImageStream should be a complete image buffer.
|
||||
|
||||
#### Accessing Data
|
||||
|
||||
var mat = new cv.Matrix.Eye(4,4); // Create identity matrix
|
||||
```javascript
|
||||
var mat = new cv.Matrix.Eye(4,4); // Create identity matrix
|
||||
|
||||
mat.get(0,0) // 1
|
||||
|
||||
mat.row(0) // [1,0,0,0]
|
||||
mat.col(4) // [0,0,0,1]
|
||||
mat.get(0,0) // 1
|
||||
|
||||
mat.row(0) // [1,0,0,0]
|
||||
mat.col(4) // [0,0,0,1]
|
||||
```
|
||||
|
||||
##### Save
|
||||
|
||||
mat.save('./pic.jpg')
|
||||
```javascript
|
||||
mat.save('./pic.jpg')
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
var buff = mat.toBuffer()
|
||||
|
||||
```javascript
|
||||
var buff = mat.toBuffer()
|
||||
```
|
||||
|
||||
#### Image Processing
|
||||
|
||||
im.convertGrayscale()
|
||||
im.canny(5, 300)
|
||||
im.houghLinesP()
|
||||
|
||||
```javascript
|
||||
im.convertGrayscale()
|
||||
im.canny(5, 300)
|
||||
im.houghLinesP()
|
||||
```
|
||||
|
||||
|
||||
#### Simple Drawing
|
||||
|
||||
im.ellipse(x, y)
|
||||
im.line([x1,y1], [x2, y2])
|
||||
|
||||
```javascript
|
||||
im.ellipse(x, y)
|
||||
im.line([x1,y1], [x2, y2])
|
||||
```
|
||||
|
||||
#### Object Detection
|
||||
|
||||
@ -129,21 +145,25 @@ There is a shortcut method for
|
||||
[Viola-Jones Haar Cascade](http://www.cognotics.com/opencv/servo_2007_series/part_2/sidebar.html) object
|
||||
detection. This can be used for face detection etc.
|
||||
|
||||
|
||||
mat.detectObject(haar_cascade_xml, opts, function(err, matches){})
|
||||
```javascript
|
||||
mat.detectObject(haar_cascade_xml, opts, function(err, matches){})
|
||||
```
|
||||
|
||||
For convenience in face recognition, cv.FACE_CASCADE is a cascade that can be used for frontal face recognition.
|
||||
|
||||
Also:
|
||||
|
||||
mat.goodFeaturesToTrack
|
||||
|
||||
```javascript
|
||||
mat.goodFeaturesToTrack
|
||||
```
|
||||
|
||||
#### Contours
|
||||
|
||||
mat.findCountours
|
||||
mat.drawContour
|
||||
mat.drawAllContours
|
||||
```javascript
|
||||
mat.findCountours
|
||||
mat.drawContour
|
||||
mat.drawAllContours
|
||||
```
|
||||
|
||||
### Using Contours
|
||||
|
||||
@ -151,33 +171,35 @@ Also:
|
||||
functions for accessing, computing with, and altering the contours contained in it.
|
||||
See [relevant source code](src/Contours.cc) and [examples](examples/)
|
||||
|
||||
var contours = im.findContours;
|
||||
```javascript
|
||||
var contours = im.findContours;
|
||||
|
||||
# Count of contours in the Contours object
|
||||
contours.size();
|
||||
# Count of contours in the Contours object
|
||||
contours.size();
|
||||
|
||||
# Count of corners(verticies) of contour `index`
|
||||
contours.cornerCount(index);
|
||||
# Count of corners(verticies) of contour `index`
|
||||
contours.cornerCount(index);
|
||||
|
||||
# Access vertex data of contours
|
||||
for(var c = 0; c < contours.size(); ++c) {
|
||||
console.log("Contour " + c);
|
||||
for(var i = 0; i < contours.cornerCount(c); ++i) {
|
||||
var point = contours.point(c, i);
|
||||
console.log("(" + point.x + "," + point.y + ")");"
|
||||
}
|
||||
}
|
||||
# Access vertex data of contours
|
||||
for(var c = 0; c < contours.size(); ++c) {
|
||||
console.log("Contour " + c);
|
||||
for(var i = 0; i < contours.cornerCount(c); ++i) {
|
||||
var point = contours.point(c, i);
|
||||
console.log("(" + point.x + "," + point.y + ")");"
|
||||
}
|
||||
}
|
||||
|
||||
# Computations of contour `index`
|
||||
contours.area(index);
|
||||
contours.arcLength(index, isClosed);
|
||||
contours.boundingRect(index);
|
||||
contours.minAreaRect(index);
|
||||
contours.isConvex(index);
|
||||
# Computations of contour `index`
|
||||
contours.area(index);
|
||||
contours.arcLength(index, isClosed);
|
||||
contours.boundingRect(index);
|
||||
contours.minAreaRect(index);
|
||||
contours.isConvex(index);
|
||||
|
||||
# Destructively alter contour `index`
|
||||
contours.approxPolyDP(index, epsilon, isClosed);
|
||||
contours.convexHull(index, clockwise);
|
||||
# Destructively alter contour `index`
|
||||
contours.approxPolyDP(index, epsilon, isClosed);
|
||||
contours.convexHull(index, clockwise);
|
||||
```
|
||||
|
||||
## MIT License
|
||||
The library is distributed under the MIT License - if for some reason that
|
||||
|
||||
@ -3,13 +3,13 @@ var cv = require('../lib/opencv')
|
||||
|
||||
var vid = new cv.VideoCapture("/Users/peterbraden/Desktop/repos/node-opencv/examples/motion.avi")
|
||||
|
||||
vid.read(function(mat){
|
||||
vid.read(function(err, mat){
|
||||
var track = new cv.TrackedObject(mat, [420, 110, 490, 170], {channel: "value"});
|
||||
var x = 0;
|
||||
var iter = function(){
|
||||
vid.read(function(m2){
|
||||
x++;
|
||||
var rec = track.track(m2)
|
||||
var rec = track.track(err, m2)
|
||||
console.log(">>", x, ":" , rec)
|
||||
if (x % 10 == 0){
|
||||
m2.rectangle([rec[0], rec[1]], [rec[2], rec[3]])
|
||||
|
||||
@ -4,7 +4,7 @@ var vid = new cv.VideoCapture(0)
|
||||
|
||||
|
||||
var snap = function(){
|
||||
vid.read(function(im){
|
||||
vid.read(function(err, im){
|
||||
im.detectObject(cv.FACE_CASCADE, {}, function(err, faces){
|
||||
if (!faces){
|
||||
console.log("No Faces")
|
||||
|
||||
@ -50,14 +50,14 @@ util.inherits(cv.ImageDataStream, Stream);
|
||||
var imagedatastream = cv.ImageDataStream.prototype;
|
||||
|
||||
imagedatastream.write = function(buf){
|
||||
this.data.push(buf)
|
||||
this.data.push(buf)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
imagedatastream.end = function(b){
|
||||
var self = this;
|
||||
|
||||
|
||||
if (b)
|
||||
imagestream.write.call(this,b);
|
||||
|
||||
@ -98,7 +98,7 @@ var ods = cv.ObjectDetectionStream.prototype;
|
||||
ods.write = function(m){
|
||||
var self = this;
|
||||
|
||||
this.classifier.detectMultiScale(m,
|
||||
this.classifier.detectMultiScale(m,
|
||||
function(e, objs){
|
||||
if (e) { throw e }
|
||||
self.emit('data', objs, m);
|
||||
@ -111,7 +111,7 @@ ods.write = function(m){
|
||||
cv.VideoStream = function(src){
|
||||
if (src instanceof cv.VideoCapture){
|
||||
this.video = src
|
||||
} else {
|
||||
} else {
|
||||
this.video = new cv.VideoCapture(src);
|
||||
}
|
||||
this.readable = true;
|
||||
@ -128,7 +128,7 @@ videostream.read = function(){
|
||||
var self = this;
|
||||
|
||||
var frame = function(){
|
||||
self.video.read(function(mat){
|
||||
self.video.read(function(err, mat){
|
||||
self.emit('data', mat)
|
||||
if (!self.paused){
|
||||
process.nextTick(frame)
|
||||
|
||||
@ -46,6 +46,7 @@ Matrix::Init(Handle<Object> target) {
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "save", Save);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "saveAsync", SaveAsync);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "resize", Resize);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "rotate", Rotate);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "pyrDown", PyrDown);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "pyrUp", PyrUp);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "channels", Channels);
|
||||
@ -54,6 +55,8 @@ Matrix::Init(Handle<Object> target) {
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "convertHSVscale", ConvertHSVscale);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "gaussianBlur", GaussianBlur);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "copy", Copy);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "flip", Flip);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "roi", ROI);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "ptr", Ptr);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "addWeighted", AddWeighted);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "split", Split);
|
||||
@ -73,6 +76,7 @@ Matrix::Init(Handle<Object> target) {
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "locateROI", LocateROI);
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "threshold", Threshold);
|
||||
NODE_SET_PROTOTYPE_METHOD(constructor, "meanStdDev", MeanStdDev);
|
||||
|
||||
NODE_SET_METHOD(constructor, "Eye", Eye);
|
||||
|
||||
@ -696,6 +700,54 @@ Matrix::Copy(const v8::Arguments& args) {
|
||||
}
|
||||
|
||||
|
||||
Handle<Value>
|
||||
Matrix::Flip(const v8::Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
|
||||
|
||||
if ( args.Length() < 1 || !args[0]->IsInt32() ) {
|
||||
return v8::ThrowException(Exception::TypeError(String::New(
|
||||
"Flip requires an integer flipCode argument (0 = X axis, positive = Y axis, negative = both axis)")));
|
||||
}
|
||||
|
||||
int flipCode = args[0]->ToInt32()->Value();
|
||||
|
||||
Local<Object> img_to_return = Matrix::constructor->GetFunction()->NewInstance();
|
||||
Matrix *img = ObjectWrap::Unwrap<Matrix>(img_to_return);
|
||||
cv::flip(self->mat, img->mat, flipCode);
|
||||
|
||||
return scope.Close(img_to_return);
|
||||
}
|
||||
|
||||
|
||||
Handle<Value>
|
||||
Matrix::ROI(const v8::Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
|
||||
|
||||
if ( args.Length() != 4 ) {
|
||||
return v8::ThrowException(Exception::TypeError(String::New(
|
||||
"ROI requires x,y,w,h arguments")));
|
||||
}
|
||||
|
||||
// although it's an image to return, it is in fact a pointer to ROI of parent matrix
|
||||
Local<Object> img_to_return = Matrix::constructor->GetFunction()->NewInstance();
|
||||
Matrix *img = ObjectWrap::Unwrap<Matrix>(img_to_return);
|
||||
|
||||
int x = args[0]->IntegerValue();
|
||||
int y = args[1]->IntegerValue();
|
||||
int w = args[2]->IntegerValue();
|
||||
int h = args[3]->IntegerValue();
|
||||
|
||||
cv::Mat roi(self->mat, cv::Rect(x,y,w,h));
|
||||
img->mat = roi;
|
||||
|
||||
return scope.Close(img_to_return);
|
||||
}
|
||||
|
||||
|
||||
Handle<Value>
|
||||
Matrix::Ptr(const v8::Arguments& args) {
|
||||
HandleScope scope;
|
||||
@ -927,6 +979,30 @@ Matrix::Resize(const v8::Arguments& args){
|
||||
return scope.Close(Undefined());
|
||||
}
|
||||
|
||||
|
||||
Handle<Value>
|
||||
Matrix::Rotate(const v8::Arguments& args){
|
||||
HandleScope scope;
|
||||
|
||||
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
|
||||
cv::Mat rotMatrix(2, 3, CV_32FC1);
|
||||
cv::Mat res;
|
||||
|
||||
float angle = args[0]->ToNumber()->Value();
|
||||
int x = args[1]->IsUndefined() ? round(self->mat.size().width / 2) : args[1]->Uint32Value();
|
||||
int y = args[1]->IsUndefined() ? round(self->mat.size().height / 2) : args[2]->Uint32Value();
|
||||
|
||||
cv::Point center = cv::Point(x,y);
|
||||
|
||||
rotMatrix = getRotationMatrix2D(center, angle, 1.0);
|
||||
|
||||
cv::warpAffine(self->mat, res, rotMatrix, self->mat.size());
|
||||
~self->mat;
|
||||
self->mat = res;
|
||||
|
||||
return scope.Close(Undefined());
|
||||
}
|
||||
|
||||
Handle<Value>
|
||||
Matrix::PyrDown(const v8::Arguments& args){
|
||||
SETUP_FUNCTION(Matrix)
|
||||
@ -1040,3 +1116,22 @@ Matrix::Threshold(const v8::Arguments& args) {
|
||||
|
||||
return scope.Close(img_to_return);
|
||||
}
|
||||
|
||||
Handle<Value>
|
||||
Matrix::MeanStdDev(const v8::Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
Matrix *self = ObjectWrap::Unwrap<Matrix>(args.This());
|
||||
|
||||
Local<Object> mean = Matrix::constructor->GetFunction()->NewInstance();
|
||||
Matrix *m_mean = ObjectWrap::Unwrap<Matrix>(mean);
|
||||
Local<Object> stddev = Matrix::constructor->GetFunction()->NewInstance();
|
||||
Matrix *m_stddev = ObjectWrap::Unwrap<Matrix>(stddev);
|
||||
|
||||
cv::meanStdDev(self->mat, m_mean->mat, m_stddev->mat);
|
||||
|
||||
Local<Object> data = Object::New();
|
||||
data->Set(String::NewSymbol("mean"), mean);
|
||||
data->Set(String::NewSymbol("stddev"), stddev);
|
||||
return scope.Close(data);
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ class Matrix: public node::ObjectWrap {
|
||||
JSFUNC(ToBufferAsync)
|
||||
|
||||
JSFUNC(Resize)
|
||||
JSFUNC(Rotate)
|
||||
JSFUNC(PyrDown)
|
||||
JSFUNC(PyrUp)
|
||||
|
||||
@ -49,6 +50,8 @@ class Matrix: public node::ObjectWrap {
|
||||
JSFUNC(ConvertHSVscale)
|
||||
JSFUNC(GaussianBlur)
|
||||
JSFUNC(Copy)
|
||||
JSFUNC(Flip)
|
||||
JSFUNC(ROI)
|
||||
JSFUNC(Ptr)
|
||||
JSFUNC(AddWeighted)
|
||||
JSFUNC(Split)
|
||||
@ -70,6 +73,7 @@ class Matrix: public node::ObjectWrap {
|
||||
JSFUNC(AdjustROI)
|
||||
|
||||
JSFUNC(Threshold)
|
||||
JSFUNC(MeanStdDev)
|
||||
/*
|
||||
static Handle<Value> Val(const Arguments& args);
|
||||
static Handle<Value> RowRange(const Arguments& args);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user