콜백함수 사용법..

var cv = require(‘opencv’);
var fs = require(‘fs’)
, path = require(‘path’)
, async = require(‘async’)
, Controller = require(‘node-pid-controller’)
, events = require(‘events’)
, EventEmitter = new events.EventEmitter()
;

var DT = 150; // time between faces detection

var client, io, lastPng;
var tracking = false;
var debug = true;
var processingImage = false;
var face_cascade = new cv.CascadeClassifier(path.join(__dirname,'node_modules','opencv','data','haarcascade_frontalface_alt2.xml'));


/** 
 *  Controllers initialization.
 */
var ver_ctrl = new Controller(0.3, 0.01, 0.1)
  , hor_ctrl = new Controller(0.4, 0.01, 0.1)
  ;

function log(string) {
	if (debug) {
		console.log(string);
	}
}

var times = [];

function detectFaces() {
  if(tracking && (!processingImage) && lastPng) {
	processingImage = true;

	async.waterfall([
	  function(cb) {
		// 1. Stop the Drone before taking picture
		client.stop();
		setTimeout(function() { // wait the drone stabilization for a new image
		  EventEmitter.once('newPng', function() {
			cb();
		  });
		}, 200);
	  },
	  function(cb) {
		// 2. Read picture (takes between 60 and 100 ms)
		cv.readImage( lastPng, function(err, im) {
		  cb(err,im);
		});
	  },
	  function(im, cb) {
		// 3. Detect faces (takes between 200 and 250 ms)
		var opts = {};
		face_cascade.detectMultiScale(im, function(err, faces) {
		  cb(err, faces, im);
		}, opts.scale, opts.neighbors
		 , opts.min && opts.min[0], opts.min && opts.min[1]);
	  },
	  function(faces, im, cb) {
		// 4. Analyze faces
		var face;
		var biggestFace;
		var dt = DT; // minimum time for the next detection

		for(var k = 0; k < faces.length; k++) {
		  face = faces[k];
		  if( !biggestFace || biggestFace.width < face.width ) biggestFace = face;
		}

		if( biggestFace ) {
		  face = biggestFace;
		  io.sockets.emit('face', { x: face.x, y: face.y, w: face.width, h: face.height, iw: im.width(), ih: im.height() });

		  face.centerX = face.x + face.width * 0.5;
		  face.centerY = face.y + face.height * 0.5;

		  var centerX = im.width() * 0.5;
		  var centerY = im.height() * 0.5;

		  var heightAmount = -( face.centerY - centerY ) / centerY;
		  var turnAmount = -( face.centerX - centerX ) / centerX;

		  heightAmount = ver_ctrl.update(-heightAmount); // pid
		  turnAmount   = hor_ctrl.update(-turnAmount);   // pid

		  var lim = 0.1;
		  if( Math.abs( turnAmount ) > lim || Math.abs( heightAmount ) > lim ){
			log( "  turning " + turnAmount );
			if (debug) io.sockets.emit('/message', 'turnAmount : ' + turnAmount);
			if( turnAmount < 0 ) client.clockwise( Math.abs( turnAmount ) );
			else client.counterClockwise( turnAmount );

			log( "  going vertical " + heightAmount );
			if (debug) io.sockets.emit('/message', 'heightAmount : ' + heightAmount);
			if(  heightAmount < 0 ) client.down( Math.abs(heightAmount) );
			else client.up( heightAmount );
		  }
		  else {
			if (debug) io.sockets.emit('/message', 'pause!');
			client.stop();
		  }

		  // to determine how much time the drone will move, we use the lower of the changes [-1,1], and multiply by a reference time.
		  dt = Math.min(Math.abs(turnAmount), Math.abs(heightAmount));
		  dt = dt * 2000;
		}
		
		processingImage = false;
		cb(null, dt);
	  }
	], function(err, dt) {
	  dt = Math.max(dt, DT);
	  setTimeout(detectFaces, dt);
	});
  } else {
	if (tracking) setTimeout(detectFaces, DT);
  };
};

function copterface(name, deps) {
	debug = deps.debug || false;
	io = deps.io;
	io.sockets.on('connection', function (socket) {
		socket.on('/copterface', function (cmd) {
			console.log("copterface", cmd);
			if (cmd == "toggle") {
			  client.stop(); // make sure to stop the helicopter if stop copterface
			  tracking = tracking ? false : true;
			  if (tracking) detectFaces();
			} 
		});
	});

	client = deps.client;
	client.createPngStream()
	  .on('error', console.log)
	  .on('data', function(pngBuffer) {
	  lastPng = pngBuffer;
	  EventEmitter.emit('newPng');
	});

}

module.exports = copterface;

↑ index.js

	(function (window, undefined) {
  'use strict';

  var CF;

  CF = function CopterFace(cockpit) {
	console.log("Loading Copterface plugin.");

	// Instance variables
	this.cockpit = cockpit;
	this.tracking = false;

	// Add required UI elements
	$("#cockpit").append('<canvas id="copterface" width="640" height="360"></canvas>');
	$("#cockpit").append('<div id="copterface-label" style="display:none;">Face Tracking ON</div>');
	this.ctx = $('#copterface').get(0).getContext('2d');

	// Bind to navdata events on websockets
	var self = this;
	this.cockpit.socket.on('face', function(data) {
	  if (self.tracking && !jQuery.isEmptyObject(data)) {
		requestAnimationFrame(function() {
		  self.render(data);
		});
	  }
	});

	// Bind on window events to resize
	$(window).resize(function(event) {
	  self.draw();
	});

	$(document).keypress(function(ev) {
	  self.keyPress(ev);
	});
  };

  CF.prototype.keyPress = function(ev) {
	console.log("Keypress: " + ev.keyCode);
	if (ev.keyCode != 102) {
	  return;
	}

	ev.preventDefault();
	this.tracking = this.tracking ? false : true;
	this.cockpit.socket.emit("/copterface", "toggle");
	this.clear();
	if (this.tracking) {
		$("#copterface-label").show();
	} else {
		$("#copterface-label").hide();
	}
  }

  CF.prototype.render = function(data) {
	this.ctx.canvas.width = $('#cockpit').innerWidth();
	this.ctx.canvas.height = $('#cockpit').innerHeight();
	  
	var cw = this.ctx.canvas.width;
	var ch = this.ctx.canvas.height;

	var x = (data.x/data.iw) * cw;
	var y = (data.y/data.ih) * ch;
	var w = (data.w/data.iw) * cw;
	var h = (data.h/data.ih) * ch;
	
	this.ctx.clearRect(0, 0, cw, ch);
	this.ctx.save();
	this.ctx.strokeStyle = 'green';
	this.ctx.lineWidth = 2;
	
	this.ctx.strokeRect(x,y,w,h);
	this.ctx.restore();
  }

  CF.prototype.clear = function() {
	this.ctx.canvas.width = $('#cockpit').innerWidth();
	this.ctx.canvas.height = $('#cockpit').innerHeight();
	  
	var cw = this.ctx.canvas.width;
	var ch = this.ctx.canvas.height;
	
	this.ctx.clearRect(0, 0, cw, ch);
  }

  window.Cockpit.plugins.push(CF);

}(window, undefined));

↑ copterface.js

↓ 디렉토리 구조
image

  1. index.js의 copterface 함수 안에 있는
    socket.on(’/copterface’, function (cmd)의 동작 과정을 잘 모르겠습니다.

  2. 저의 예상으로는 디렉토리 구조에서 노란색 형광펜으로 표시한
    copterface.js를 호출하는 것 같은데,
    (function (window, undefined) 안에 있는 console.log(“Loading Copterface plugin.”);이 출력이 안됩니다.

  3. copterface.js파일을 index.js에서 호출하는 방법이 있을까요…?

음… 주어진 코드만 보면 index.js에서 detectFaces를 실행하고 있는데요.
detectFaces는 정의된적이 없으니 찾을수 없는 상태입니다.

그리고 copterface.js를 require하는 구문 자체가 없어서 copterface.js를 어떻게 실행한다는 것인지 알 수가 없어요.

1개의 좋아요

detectFaces는 index.js에 정의되어있는데 너무 길어서 제가 생략했었습니다…
index.js에서 require(“copterface”)하니까
copterface.js의 copterface안에서
if(typeof faceCallback !== ‘function’) throw new Error(‘missing callback, expected as second argument to copterface’);
이 부분에서 에러가 납니다…

모듈을 호출하는 부분에서 코드가 잘못되었겠지요.

require한 copterface가 function(pngStream,options,faceCallback) {} 이니,

인자 3개를 주어서 실행시켜주셔야 하는데 그 부분 코드가 없네요.

질문 수정했는데, 죄송하지만 혹시 다시 한번 봐주실수있나요?!

API를 보시면 사용법이 socket.on(eventName, callback) 입니다.

socket.on(’/copterface’, function (cmd)의 ’/copterface’는 require가 아니라 그냥 이벤트 네임일 뿐이에요.

connection 이벤트를 수신한 다음에, 앞으로 /copterface 이벤트를 수신한다는 의미의 API 입니다.

socket.io를 공부하셔야 겠어요.

1개의 좋아요