Skip to content
Browse files

Merge pull request #2 from bransorem/master

Version 0.1 - or something
  • Loading branch information...
2 parents 06baeb6 + 771d8f3 commit 8b9b98a33b77da29d7814b9b9c213bc0b434a0f5 @estackpole estackpole committed Mar 23, 2012
Showing with 536 additions and 0 deletions.
  1. +34 −0 .gitignore
  2. 0 README
  3. +96 −0 README.md
  4. +34 −0 src/.gitignore
  5. +217 −0 src/OpenROV.js
  6. +69 −0 src/app.js
  7. +54 −0 src/capture.cpp
  8. +32 −0 src/index.html
View
34 .gitignore
@@ -0,0 +1,34 @@
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+
+# OS generated files #
+######################
+.DS_Store*
+ehthumbs.db
+Icon?
+Thumbs.db
View
0 README
No changes.
View
96 README.md
@@ -0,0 +1,96 @@
+ROVision
+========
+
+"[OpenROV](http://openrov.com/) is a DIY telerobotics community centered around underwater exploration & adventure." One goal of OpenROV is to have onboard video for live viewing as the user operates the ROV. Enter: ROVision.
+
+Introduction
+------------
+
+We designed the onboard video platform using several key technologies:
+
+- [OpenCV](http://opencv.willowgarage.com/)
+- [Node.js](http://nodejs.org/)
+- [Socket.io](http://socket.io/)
+
+Combining these great technologies provides a lot of power and room for future growth. But is also provides well documented means to extend OpenROV. With Node.js and Socket.io, not only are we able to stream video to a web browser by updating an image, but we are also able to control the ROV and view valuable sensor information. This is just the beginning. Ultimately, we would like to move to a WebM (or similar) video stream from OpenCV, but we need your help. We're hoping that you, the community, are as excited as we are to explore the many uncharted areas underwater. We have many exciting ideas for the future
+
+
+Requirements
+------------
+- BeagleBone: [http://beagleboard.org/bone](http://beagleboard.org/bone)
+- USB webcam: we're using the Microsoft LifeCam HD-5000
+- Ubuntu 11.10 for BeagleBone: [http://elinux.org/BeagleBoardUbuntu#Demo_Image](http://elinux.org/BeagleBoardUbuntu#Demo_Image)
+- OpenCV 2.3: [http://opencv.willowgarage.com/](http://opencv.willowgarage.com/)
+- Node.js: [http://nodejs.org/](http://nodejs.org/)
+- Socket.io: [http://socket.io/](http://socket.io/)
+
+###For installation
+(pre-OpenROV-custom-image):
+
+- g++
+- make
+- cmake
+- svn
+- pkg-config
+- npm
+
+
+Installation
+------------
+
+*We WILL be making a custom image to bypass this difficult process in the future.*
+
+I will warn you that this was a hairy process.
+
+First, download and install Ubuntu (link above) onto a microSD card. Then, put it in the BeagleBone and plug the BeagleBone into a router. You need to SSH into the BeagleBone once it has booted:
+
+ $ ssh -l ubuntu 192.168.1.100
+
+Where the IP address corresponds to the BeagleBone's current IP address. The default password is listed on the Ubuntu download page (linked above). It should be: `temppwd`
+
+Make sure the BeagleBone has an Internet connection. Install dependencies:
+
+`$ sudo apt-get install g++`
+`$ sudo apt-get install make`
+`$ sudo apt-get install cmake`
+`$ sudo apt-get install subversion`
+`$ wget http://sourceforge.net/projects/numpy/files/NumPy/1.6.1/numpy-1.6.1.tar.gz` - untar then install
+`$ sudo apt-get install pkg-config`
+
+OpenCV:
+- [http://opencv.willowgarage.com/wiki/InstallGuide](http://opencv.willowgarage.com/wiki/InstallGuide)
+- [http://thebitbangtheory.wordpress.com/2011/10/23/how-to-install-opencv-2-3-1-in-ubuntu-11-10-oneiric-ocelot-with-python-support/](http://thebitbangtheory.wordpress.com/2011/10/23/how-to-install-opencv-2-3-1-in-ubuntu-11-10-oneiric-ocelot-with-python-support/)
+
+Install Node.js from source (we used v0.6.12):
+[https://github.com/joyent/node/wiki/Installation](https://github.com/joyent/node/wiki/Installation)
+
+`$ sudo apt-get install curl`
+
+NPM: [http://npmjs.org/](http://npmjs.org/)
+
+`$ sudo npm install -g socket.io`
+
+Finally, download ROVision, then compile the C++ program using this command:
+
+ $ g++ capture.cpp -o capture `pkg-config opencv --cflags --libs`
+
+To run,
+
+ $ node app.js
+
+
+
+Future
+------
+
+As I mentioned earlier, one of our top future goals is getting a WebM (or maybe h.264) stream output from OpenCV. However, we have so much more planned. Imagine being able to track a fish, or map underwater environments in 3D, or coordinate multiple ROV's using vision, or whatever you can imagine! Please fork & play. We'd love to see what you create.
+
+
+License
+-------
+
+This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this license, visit <http://creativecommons.org/licenses/by-sa/3.0/> or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
+
+*Some portions of code may be subject to different licensing agreements.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
34 src/.gitignore
@@ -0,0 +1,34 @@
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+
+# OS generated files #
+######################
+.DS_Store*
+ehthumbs.db
+Icon?
+Thumbs.db
View
217 src/OpenROV.js
@@ -0,0 +1,217 @@
+/*
+ * Created for OpenROV: www.openrov.com
+ * Author: Bran Sorem (www.bransorem.com)
+ * Date: 03/19/12
+ *
+ * Description:
+ * This script creates a directory and sends that as an argument to a spawned process (capture.cpp).
+ * Then, it sends a request to capture a frame with file name of current time at a given interval.
+ * Lastly, when (capture.cpp) responds with the file name (meaning save completed), it reads the file
+ * and then emits the content to the Node.js server in base64 (string) format.
+ *
+ * Special thanks to pdeschen (blog.rassemblr.com):
+ * https://github.com/pdeschen/camelot
+ *
+ * License
+ * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
+ * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a
+ * letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
+ *
+ */
+
+var socket = require('socket.io'),
+ spawn = require('child_process').spawn,
+ sys = require('util'),
+ events = require('events'),
+ fs = require('fs'),
+ path = require('path');
+
+// Create dir (current date) to save images into
+var time = new Date();
+var location = time.getFullYear() +
+ '-' + time.getMonth() +
+ '-' + time.getDate() + '/';
+
+// Open capture app as a child process
+var cmd = './capture'; // rename to correspond with your C++ compilation
+var capture_process = spawn(cmd, [location]);
+var default_opts = { device : '/dev/video0' };
+
+
+/* =====================================
+ =========== OpenROV class ===========
+ ===================================== */
+var OpenROV = function (options){
+ events.EventEmitter.call(this);
+ this.emitter = events;
+ this.opts = mixin(options, default_opts);
+ return this;
+};
+sys.inherits(OpenROV, events.EventEmitter);
+
+// ============ init() ============
+// create directory to store photos
+// ================================
+OpenROV.prototype.init = function(){ fs.mkdir(location, 0755); };
+
+// ============ reset() ===============
+// reset the device to default settings
+// ====================================
+OpenROV.prototype.reset = function (){
+ this.opts = default_opts;
+ return default_opts;
+};
+
+// ============ update(options) ============
+// options (obj)
+// update options of rov instance
+// =========================================
+OpenROV.prototype.update = function (options){
+ this.opts = mixin(options, this.opts)
+ return this.opts;
+};
+
+// ============ close() =============
+// close the camera and child process
+// ==================================
+OpenROV.prototype.close = function(){
+ capture_process.stdin.write('exit');
+ process.kill(capture_process.pid, 'SIGHUP');
+};
+
+// ============ capture(options, callback) ============
+// options (obj) - options for controlling camera
+// callback (function) - callback function
+// Captures a frame from camera and emits it as 'frame'
+// ====================================================
+OpenROV.prototype.capture = function (options, callback){
+
+ this.opts = mixin(options, this.opts);
+ var rov = this;
+
+ // when ./capture responds, image has been saved
+ capture_process.stdout.on('data', function(response){
+
+ try {
+ // remove any trailing newline chars
+ var file = response.toString().replace(/(\r\n|\n|\r)/gm, '');
+ var imgFile = location + file;
+
+ console.log('Sending: ' + file);
+
+ // open file from system, then emit to server
+ fs.readFile(imgFile, 'base64', function(err, img){
+ if (!err) rov.emit('frame', img);
+ fs.unlink(imgFile); // comment out this line to store footage on ROV (warning: takes up lots of space)
+ });
+ }
+ catch (captureError){ console.log(captureError); }
+ });
+
+ // this function loops, 'grabs' frame from camera (sends stdin to child process ./capture)
+ var grab = function (){
+ try{
+
+ var self = this;
+ self.emitter = this.emitter;
+ self.arguments = [];
+ self.format = ".jpg";
+ format = ".jpg";
+
+ path.exists(this.opts.device, function (exists){
+
+ // only call this function if camera is connected
+ var alive = function (){
+ var file = getTime() + format + '\n\r';
+ capture_process.stdin.write(file);
+ }
+
+ // uh-oh, no camera connected!
+ if (!exists){
+ var message = 'no device (' + self.opts.device + ').';
+ var err = new Error(message);
+ self.emit('error.device', err);
+ if (callback){ callback.call(err); }
+ // hope it changes
+ fs.watchFile(self.opts.device, function (curr, prev){ alive.apply(self); });
+ return;
+ }
+ alive.apply(self);
+
+ });
+ } catch (capError){ console.write(capError); }
+ };
+
+ // loop based on delay (if set) in milliseconds
+ if (this.opts.delay){
+ setInterval(function (self){
+ grab.apply(self);
+ }, this.opts.delay, this);
+ } else { grab.apply(this); }
+};
+
+
+
+// ================================================
+// get current time in image name format
+function getTime(){
+ var t = new Date(),
+ hrs = t.getHours(),
+ min = t.getMinutes(),
+ sec = t.getSeconds(),
+ millisec = t.getMilliseconds();
+ // Make text width consistent
+ if (min < 10) min = '0' + min;
+ if (sec < 10) sec = '0' + sec;
+ if (millisec < 100){
+ if (millisec.length < 10){ millisec = '0' + millisec; }
+ millisec = '0' + millisec;
+ }
+ var id = hrs + '.' + min + '.' + sec + '.' + millisec;
+
+ return id;
+}
+
+// ============ mixin (source, destination) ============
+// -source (obj)
+// -destination (obj)
+// combines parameters from source and destination
+// ============
+// special thanks to pdeschen for this function
+// originally created for Camelot:
+// https://github.com/pdeschen/camelot
+// =====================================================
+var mixin = function (source, destination){
+
+ if (typeof (source) == "object"){
+ for ( var prop in source){
+ if ((typeof (source[prop]) == "object") && (source[prop] instanceof Array)){
+ if (destination[prop] === undefined){
+ destination[prop] = [];
+ }
+ for ( var index = 0; index < source[prop].length; index += 1){
+ if (typeof (source[prop][index]) == "object"){
+ if (destination[prop][index] === undefined){
+ destination[prop][index] = {};
+ }
+ destination[prop].push(mixin(source[prop][index], destination[prop][index]));
+ } else {
+ destination[prop].push(source[prop][index]);
+ }
+ }
+ } else if (typeof (source[prop]) == "object"){
+ if (destination[prop] === undefined){
+ destination[prop] = {};
+ }
+ mixin(source[prop], destination[prop]);
+ } else {
+ destination[prop] = source[prop];
+ }
+ }
+ }
+
+ return destination;
+};
+
+module.exports = OpenROV;
+
View
69 src/app.js
@@ -0,0 +1,69 @@
+/*
+ * Created for OpenROV: www.openrov.com
+ * Author: Bran Sorem (www.bransorem.com)
+ * Date: 03/19/12
+ *
+ * Description:
+ * This script is the Node.js server for OpenROV. It creates a server and instantiates an OpenROV
+ * and sets the interval to grab frames. The interval is set with the DELAY variable which is in
+ * milliseconds.
+ *
+ * License
+ * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
+ * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a
+ * letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
+ */
+
+var app = require('http').createServer(handler)
+ , io = require('socket.io').listen(app)
+ , fs = require('fs')
+ , OpenROV = require('./OpenROV.js');
+
+// Globals =================
+var mutex = 0;
+var rov = new OpenROV();
+var DELAY = 200; // delay between frame grabs in milliseconds (roughly)
+var PORT = 8080;
+
+// no debug messages
+io.configure(function(){ io.set('log level', 1); });
+
+app.listen(PORT);
+
+// HANDLER ==============================
+function handler (req, res) {
+ fs.readFile(__dirname + '/index.html',
+ function (err, data) {
+ if (err) {
+ res.writeHead(500);
+ return res.end('Error loading index.html');
+ }
+ res.writeHead(200);
+ res.end(data);
+ });
+}
+
+// SOCKET connection ==============================
+io.sockets.on('connection', function (socket) {
+ socket.send('initialize'); // opens socket with client
+
+ // when frame emits, send it over socket
+ rov.on('frame', function(img){
+ socket.emit('frame', img);
+ });
+
+ // only run one instance
+ if (mutex == 0){
+ rov.init();
+ rov.capture({'delay' : DELAY});
+ }
+ mutex++;
+});
+
+// SOCKET disconnection ==============================
+io.sockets.on('disconnect', function(socket){
+ mutex--;
+ if (mutex == 0){
+ rov.close(); // no need to keep running the process, no one is watching
+ }
+});
View
54 src/capture.cpp
@@ -0,0 +1,54 @@
+/*
+ * Created for OpenROV: www.openrov.com
+ * Author: Bran Sorem (www.bransorem.com)
+ * Date: 03/19/12
+ *
+ * Description:
+ * This program takes a folder (with trailing /) as an argument to determine where to store images.
+ * Then, it enters an infinite loop, until it receives 'exit' as input, where the input is a file
+ * name including extension (i.e.: 03.15.39.106.jpg). Then it captures an image and saves it with
+ * the given file name in the given location.
+ *
+ * License
+ * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
+ * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a
+ * letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
+ */
+
+#include "cv.h"
+#include "highgui.h"
+#include <iostream>
+
+using namespace cv;
+
+int main(int argc, char* argv[]){
+
+ // Initialize with folder to save to as argument
+ // ./capture 01-01-12/
+ string file = "";
+ if (argc > 1) file = argv[1];
+ else return -1;
+
+ VideoCapture cap(0);
+
+ if (!cap.isOpened()) return -1;
+
+ // respond with success
+ std::cout << file << std::endl;
+
+ std::string input;
+ Mat frame;
+ // run until input -> 'exit'
+ for (;;){
+ std::cin >> input; // file name (including extension)
+
+ if (input == "exit") break;
+
+ cap >> frame;
+ string output = file + input;
+ imwrite(output, frame);
+ std::cout << input; // respond with file name, signifies save finished (causes emitter to respond)
+ }
+
+ return 0;
+}
View
32 src/index.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head><title>OpenROV</title>
+
+<script src="/socket.io/socket.io.js"></script>
+
+<script>
+var socket = io.connect();
+
+socket.on('frame', function(data) {
+ document.getElementById('video').src = 'data:image/jpeg;base64,' + data;
+});
+
+</script>
+<style type="text/css">
+body { text-align: center; background-color: #000; }
+#video {
+ width: 640px;
+ height: 480px;
+ margin: auto;
+ margin-top: 50px;
+}
+</style>
+
+</head>
+<body>
+
+<div id="frm"></div>
+<img src="" alt="OpenROV" id="video" />
+
+</body>
+</html>

0 comments on commit 8b9b98a

Please sign in to comment.
Something went wrong with that request. Please try again.