diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca8b5b291db1eb9dc2e6e7ec8297d16d52a1ce14..ef3b984099eeedcc3ce0d98c94e5266bd3239099 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,7 @@ check_language(CUDA)
 if (CUDA_TOOLKIT_ROOT_DIR)
 enable_language(CUDA)
 set(CMAKE_CUDA_FLAGS "")
-set(CMAKE_CUDA_FLAGS_DEBUG "-g -DDEBUG -D_DEBUG -Wall")
+set(CMAKE_CUDA_FLAGS_DEBUG "-g -DDEBUG -D_DEBUG")
 set(CMAKE_CUDA_FLAGS_RELEASE "")
 set(HAVE_CUDA TRUE)
 include_directories(${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
@@ -114,6 +114,7 @@ endif()
 
 SET(CMAKE_USE_RELATIVE_PATHS ON)
 
+add_subdirectory(common/cpp)
 add_subdirectory(renderer)
 add_subdirectory(net)
 add_subdirectory(vision)
diff --git a/common/config/config.json b/common/config/config.json
index bafcaf0b4fedcfaa3aa154fe2afd9ac0ee0be026..80d6589a63910e414ff02d2db35e46291eb69130 100644
--- a/common/config/config.json
+++ b/common/config/config.json
@@ -1,66 +1,68 @@
 {
-	"middlebury": {
-		"dataset": "",
-		"threshold": 10.0,
-		"scale": 0.25
-	},
-	"source": {
-		"flip": false,
-		"nostereo": false,
-		"scale": 1.0,
-		"flip_vert": false
-	},
-	"calibrate": false,
-	"calibration": {
-		"board_size": [9,6],
-		"square_size": 50,
-		"frame_delay": 1.0,
-		"num_frames": 35,
-		"assume_zero_tangential_distortion": true,
-		"fix_aspect_ratio": true,
-		"fix_principal_point_at_center": true,
-		"use_fisheye_model": false,
-		"fix_k1": false,
-		"fix_k2": false,
-		"fix_k3": false,
-		"fix_k4": true,
-		"fix_k5": true,
-		"save": true,
-		"use_intrinsics": true,
-		"use_extrinsics": true,
-		"flip_vertical": false
-	},
-	"camera": {
-		"name": "Panasonic Lumix DMC-FZ300",
-		"focal_length": 25,
-		"sensor_width": 6.17,
-		"base_line": 0.1
-	},
-	"disparity": {
-		"algorithm": "rtcensus",
-		"use_cuda": true,
-		"minimum": 0,
-		"maximum": 208,
-		"tau": 0.0,
-		"gamma": 0.0,
-		"window_size": 5,
-		"sigma": 1.5,
-		"lambda": 8000.0
-	},
-	"display": {
-		"flip_vert": false,
-		"disparity": false,
-		"points": true,
-		"depth": false,
-		"left": false,
-		"right": false
-	},
-	"net": {
-		"listen": "tcp://*:9001",
-		"peers": []
-	},
-	"stream": {
-		"name": "dummy"
+	"vision": {
+		"middlebury": {
+			"dataset": "",
+			"threshold": 10.0,
+			"scale": 0.25
+		},
+		"source": {
+			"flip": false,
+			"nostereo": false,
+			"scale": 1.0,
+			"flip_vert": false
+		},
+		"calibrate": false,
+		"calibration": {
+			"board_size": [9,6],
+			"square_size": 50,
+			"frame_delay": 1.0,
+			"num_frames": 35,
+			"assume_zero_tangential_distortion": true,
+			"fix_aspect_ratio": true,
+			"fix_principal_point_at_center": true,
+			"use_fisheye_model": false,
+			"fix_k1": false,
+			"fix_k2": false,
+			"fix_k3": false,
+			"fix_k4": true,
+			"fix_k5": true,
+			"save": true,
+			"use_intrinsics": true,
+			"use_extrinsics": true,
+			"flip_vertical": false
+		},
+		"camera": {
+			"name": "Panasonic Lumix DMC-FZ300",
+			"focal_length": 25,
+			"sensor_width": 6.17,
+			"base_line": 0.1
+		},
+		"disparity": {
+			"algorithm": "rtcensus",
+			"use_cuda": true,
+			"minimum": 0,
+			"maximum": 208,
+			"tau": 0.0,
+			"gamma": 0.0,
+			"window_size": 5,
+			"sigma": 1.5,
+			"lambda": 8000.0
+		},
+		"display": {
+			"flip_vert": false,
+			"disparity": false,
+			"points": true,
+			"depth": false,
+			"left": false,
+			"right": false
+		},
+		"net": {
+			"listen": "tcp://*:9001",
+			"peers": []
+		},
+		"stream": {
+			"name": "dummy"
+		}
 	},
 	"representation": {
 		"net": {
diff --git a/common/cpp/CMakeLists.txt b/common/cpp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2a9be0f3b3f32963d088d6af06809db57281d3b7
--- /dev/null
+++ b/common/cpp/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(COMMONSRC
+	src/config.cpp
+	src/configuration.cpp
+)
+
+add_library(ftlcommon ${COMMONSRC})
+
+target_include_directories(ftlcommon PUBLIC
+	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+	$<INSTALL_INTERFACE:include>
+	PRIVATE src)
+target_link_libraries(ftlcommon glog::glog)
+
diff --git a/common/cpp/include/ftl/configuration.hpp b/common/cpp/include/ftl/configuration.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..685c9b4ba30e08268d4a48c2b50ad852e84cf7b8
--- /dev/null
+++ b/common/cpp/include/ftl/configuration.hpp
@@ -0,0 +1,26 @@
+#ifndef _FTL_COMMON_CONFIGURATION_HPP_
+#define _FTL_COMMON_CONFIGURATION_HPP_
+
+#include <nlohmann/json.hpp>
+#include <string>
+#include <vector>
+#include <optional>
+
+namespace ftl {
+
+extern nlohmann::json config;
+
+bool is_directory(const std::string &path);
+bool is_file(const std::string &path);
+bool create_directory(const std::string &path);
+
+bool is_video(const std::string &file);
+
+std::optional<std::string> locateFile(const std::string &name);
+
+std::vector<std::string> configure(int argc, char **argv, const std::string &app);
+
+};
+
+#endif  // _FTL_COMMON_CONFIGURATION_HPP_
+
diff --git a/common/cpp/src/configuration.cpp b/common/cpp/src/configuration.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a7df835da6b6b2ad8c914de3a8c0ee95528c219
--- /dev/null
+++ b/common/cpp/src/configuration.cpp
@@ -0,0 +1,233 @@
+#include <glog/logging.h>
+#include <ftl/config.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include <nlohmann/json.hpp>
+#include <ftl/configuration.hpp>
+
+#include <fstream>
+#include <string>
+#include <map>
+#include <iostream>
+
+using nlohmann::json;
+using std::ifstream;
+using std::string;
+using std::map;
+using std::vector;
+using std::optional;
+using ftl::is_file;
+using ftl::is_directory;
+
+// Store loaded configuration
+namespace ftl {
+json config;
+};
+
+using ftl::config;
+
+bool ftl::is_directory(const std::string &path) {
+#ifdef WIN32
+	DWORD attrib = GetFileAttributesA(path.c_str());
+	if (attrib == INVALID_FILE_ATTRIBUTES) return false;
+	else return (attrib & FILE_ATTRIBUTE_DIRECTORY);
+#else
+	struct stat s;
+	if (::stat(path.c_str(), &s) == 0) {
+		return S_ISDIR(s.st_mode);
+	} else {
+		return false;
+	}
+#endif
+}
+
+bool ftl::is_file(const std::string &path) {
+#ifdef WIN32
+	DWORD attrib = GetFileAttributesA(path.c_str());
+	if (attrib == INVALID_FILE_ATTRIBUTES) return false;
+	else return !(attrib & FILE_ATTRIBUTE_DIRECTORY);
+#else
+	struct stat s;
+	if (::stat(path.c_str(), &s) == 0) {
+		return S_ISREG(s.st_mode);
+	} else {
+		return false;
+	}
+#endif
+}
+
+static bool endsWith(const string &s, const string &e) {
+	return s.size() >= e.size() && 
+				s.compare(s.size() - e.size(), e.size(), e) == 0;
+}
+
+bool ftl::is_video(const string &file) {
+	return endsWith(file, ".mp4");
+}
+
+bool ftl::create_directory(const std::string &path) {
+#ifdef WIN32
+	// TODO(nick)
+#else
+	if (!is_directory(path)) {
+		int err = ::mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+		return err != -1;
+	}
+	return true;
+#endif
+}
+
+optional<string> ftl::locateFile(const string &name) {
+	auto paths = config["paths"];
+	
+	if (!paths.is_null()) {
+		for (string p : paths) {
+			if (is_directory(p)) {
+				if (is_file(p+"/"+name)) {
+					return p+"/"+name;
+				}
+			} else if (p.size() >= name.size() && 
+					p.compare(p.size() - name.size(), name.size(), name) == 0 &&
+					is_file(p)) {
+				return p;
+			}
+		}
+	}
+	
+	if (is_file("./"+name)) return "./"+name;
+	if (is_file(string(FTL_LOCAL_CONFIG_ROOT) +"/"+ name)) return string(FTL_LOCAL_CONFIG_ROOT) +"/"+ name;
+	if (is_file(string(FTL_GLOBAL_CONFIG_ROOT) +"/"+ name)) return string(FTL_GLOBAL_CONFIG_ROOT) +"/"+ name;
+	return {};
+}
+
+/**
+ * Combine one json config with another patch json config.
+ */
+static bool mergeConfig(const string &path) {
+	ifstream i;
+	i.open(path);
+	if (i.is_open()) {
+		json t;
+		i >> t;
+		config.merge_patch(t);
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Find and load a JSON configuration file
+ */
+static bool findConfiguration(const string &file, const vector<string> &paths,
+		const std::string &app) {
+	bool found = false;
+	
+	found |= mergeConfig(FTL_GLOBAL_CONFIG_ROOT "/config.json");
+	found |= mergeConfig(FTL_LOCAL_CONFIG_ROOT "/config.json");
+	found |= mergeConfig("./config.json");
+	
+	for (auto p : paths) {
+		if (is_directory(p)) {
+			found |= mergeConfig(p+"/config.json");
+		}
+	}
+	
+	if (file != "") {
+		found |= mergeConfig(file);
+	}
+
+	if (found) {
+		config = config[app];
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Generate a map from command line option to value
+ */
+static map<string, string> read_options(char ***argv, int *argc) {
+	map<string, string> opts;
+
+	while (*argc > 0) {
+		string cmd((*argv)[0]);
+		if (cmd[0] != '-') break;
+
+		size_t p;
+		if ((p = cmd.find("=")) == string::npos) {
+			opts[cmd.substr(2)] = "true";
+		} else {
+			auto val = cmd.substr(p+1);
+			if (std::isdigit(val[0]) || val == "true" || val == "false" || val == "null") {
+				opts[cmd.substr(2, p-2)] = val;
+			} else {
+				if (val[0] == '\\') opts[cmd.substr(2, p-2)] = val;
+				else opts[cmd.substr(2, p-2)] = "\""+val+"\"";
+			}
+		}
+
+		(*argc)--;
+		(*argv)++;
+	}
+
+	return opts;
+}
+
+/**
+ * Put command line options into json config. If config element does not exist
+ * or is of a different type then report an error.
+ */
+static void process_options(const map<string, string> &opts) {
+	for (auto opt : opts) {
+		if (opt.first == "config") continue;
+
+		if (opt.first == "version") {
+			std::cout << "FTL Vision Node - v" << FTL_VERSION << std::endl;
+			std::cout << FTL_VERSION_LONG << std::endl;
+			exit(0);
+		}
+
+		try {
+			auto ptr = json::json_pointer("/"+opt.first);
+			// TODO(nick) Allow strings without quotes
+			auto v = json::parse(opt.second);
+			if (v.type() != config.at(ptr).type()) {
+				LOG(ERROR) << "Incorrect type for argument " << opt.first;
+				continue;
+			}
+			config.at(ptr) = v;
+		} catch(...) {
+			LOG(ERROR) << "Unrecognised option: " << opt.first;
+		}
+	}
+}
+
+vector<string> ftl::configure(int argc, char **argv, const std::string &app) {
+	argc--;
+	argv++;
+
+	// Process Arguments
+	auto options = read_options(&argv, &argc);
+	
+	vector<string> paths;
+	while (argc-- > 0) {
+		paths.push_back(argv[0]);
+	}
+	
+	if (!findConfiguration(options["config"], paths, app)) {
+		LOG(FATAL) << "Could not find any configuration!";
+	}
+	process_options(options);
+
+	return paths;
+}
+
diff --git a/net/cpp/include/ftl/net/peer.hpp b/net/cpp/include/ftl/net/peer.hpp
index cd0fe67d027a2803d450c842b49e23de8069dad0..fb789a46b65b9f892f093b9131f4f066b903d55e 100644
--- a/net/cpp/include/ftl/net/peer.hpp
+++ b/net/cpp/include/ftl/net/peer.hpp
@@ -289,6 +289,7 @@ int Peer::asyncCall(
 	
 	LOG(INFO) << "RPC " << name << "() -> " << uri_;
 	
+	std::unique_lock<std::mutex> lk(send_mtx_);
 	msgpack::pack(send_buf_, call_obj);
 	
 	// Register the CB
diff --git a/net/cpp/src/dispatcher.cpp b/net/cpp/src/dispatcher.cpp
index 34861a04fcd08044f8f2b579f739911bbf723dbd..269db7a6d38de8be0ac8fe25d82b222db63f0c04 100644
--- a/net/cpp/src/dispatcher.cpp
+++ b/net/cpp/src/dispatcher.cpp
@@ -129,7 +129,7 @@ void ftl::net::Dispatcher::dispatch_notification(Peer &s, msgpack::object const
     auto &&name = std::get<1>(the_call);
     auto &&args = std::get<2>(the_call);
     
-    //LOG(INFO) << "NOTIFICATION " << name << "() <- " << s.getURI();
+    // LOG(INFO) << "NOTIFICATION " << name << "() <- " << s.getURI();
 
     auto binding = _locateHandler(name);
 
diff --git a/net/cpp/src/peer.cpp b/net/cpp/src/peer.cpp
index a782bc67c1c249767e06c1797f2e952f1f3aca18..5c29ace9a708fcee6737b38976afc85b444ffd43 100644
--- a/net/cpp/src/peer.cpp
+++ b/net/cpp/src/peer.cpp
@@ -122,9 +122,9 @@ static int tcpConnect(URI &uri) {
 	}
 
 	// Make blocking again
-	/*rg = fcntl(csocket, F_GETFL, NULL));
+	/*long arg = fcntl(csocket, F_GETFL, NULL);
 	arg &= (~O_NONBLOCK);
-	fcntl(csocket, F_SETFL, arg) < 0)*/
+	fcntl(csocket, F_SETFL, arg);*/
 
 	return csocket;
 }
@@ -153,8 +153,6 @@ Peer::Peer(int s, Dispatcher *d) : sock_(s) {
 				_trigger(open_handlers_);
 			}
 		});
-	
-		//ftl::UUID uuid;
 
 		send("__handshake__", ftl::net::kMagic, ftl::net::kVersion, ftl::net::this_peer); 
 	}
@@ -171,14 +169,6 @@ Peer::Peer(const char *pUri, Dispatcher *d) : uri_(pUri) {
 	scheme_ = uri.getProtocol();
 	if (uri.getProtocol() == URI::SCHEME_TCP) {
 		sock_ = tcpConnect(uri);
-		
-#ifdef WIN32
-		u_long on = 1;
-		ioctlsocket(sock_, FIONBIO, &on);
-#else
-		fcntl(sock_, F_SETFL, O_NONBLOCK);
-#endif
-		
 		status_ = kConnecting;
 	} else if (uri.getProtocol() == URI::SCHEME_WS) {
 		LOG(INFO) << "Websocket connect " << uri.getPath();
@@ -191,13 +181,6 @@ Peer::Peer(const char *pUri, Dispatcher *d) : uri_(pUri) {
 		} else {
 			LOG(ERROR) << "Connection refused to " << uri.getHost() << ":" << uri.getPort();
 		}
-		
-#ifdef WIN32
-		u_long on = 1;
-		ioctlsocket(sock_, FIONBIO, &on);
-#else
-		fcntl(sock_, F_SETFL, O_NONBLOCK);
-#endif
 
 		status_ = kConnecting;
 	} else {
@@ -315,7 +298,7 @@ void Peer::data() {
 }
 
 bool Peer::_data() {
-	//std::unique_lock<std::mutex> lk(recv_mtx_);
+	// std::unique_lock<std::mutex> lk(recv_mtx_);
 
 	recv_buf_.reserve_buffer(kMaxMessage);
 	int rc = ftl::net::internal::recv(sock_, recv_buf_.buffer(), kMaxMessage, 0);
@@ -483,6 +466,7 @@ void Peer::cancelCall(int id) {
 
 void Peer::_sendResponse(uint32_t id, const msgpack::object &res) {
 	Dispatcher::response_t res_obj = std::make_tuple(1,id,std::string(""),res);
+	std::unique_lock<std::mutex> lk(send_mtx_);
 	msgpack::pack(send_buf_, res_obj);
 	_send();
 }
@@ -535,6 +519,13 @@ int Peer::_send() {
 	int c = ftl::net::internal::writev(sock_, send_buf_.vector(), send_buf_.vector_size());
 #endif
 	send_buf_.clear();
+	
+	// We are blocking, so -1 should mean actual error
+	if (c == -1) {
+		socketError();
+		close();
+	}
+	
 	return c;
 }
 
diff --git a/reconstruct/src/main.cpp b/reconstruct/src/main.cpp
index e4385633ab87aff3380476ba3449fb4a50f52c0d..1885d8c397e9fa69456ffb2722dc5796d62bc490 100644
--- a/reconstruct/src/main.cpp
+++ b/reconstruct/src/main.cpp
@@ -7,6 +7,7 @@
 #include <glog/logging.h>
 #include <ftl/config.h>
 #include <zlib.h>
+// #include <lz4.h>
 
 #include <string>
 #include <map>
@@ -131,9 +132,9 @@ static void run(const string &file) {
 		cv::imdecode(jpg, cv::IMREAD_COLOR, &rgb);
 		//LOG(INFO) << "Received JPG : " << rgb.cols;
 		
-		depth = Mat(rgb.size(), CV_32FC1);
+		depth = Mat(rgb.size(), CV_16UC1);
 		
-		z_stream infstream;
+		/*z_stream infstream;
 		infstream.zalloc = Z_NULL;
 		infstream.zfree = Z_NULL;
 		infstream.opaque = Z_NULL;
@@ -146,7 +147,12 @@ static void run(const string &file) {
 		// the actual DE-compression work.
 		inflateInit(&infstream);
 		inflate(&infstream, Z_NO_FLUSH);
-		inflateEnd(&infstream);
+		inflateEnd(&infstream);*/
+		
+		//LZ4_decompress_safe((char*)d.data(), (char*)depth.data, d.size(), depth.step*depth.rows);
+		
+		cv::imdecode(d, cv::IMREAD_UNCHANGED, &depth);
+		depth.convertTo(depth, CV_32FC1, 1.0f/16.0f);
 	});
 	
 	while (disp.active()) {
@@ -156,7 +162,10 @@ static void run(const string &file) {
 		if (depth.cols > 0) {
 			// If no calibration data then get it from the remote machine
 			if (Q.rows == 0) {
+				// Must unlock before calling findOne to prevent net block!!
+				lk.unlock();
 				auto buf = net.findOne<vector<unsigned char>>((string)config["source"]+"/calibration");
+				lk.lock();
 				if (buf) {
 					Q = Mat(cv::Size(4,4), CV_32F);
 					memcpy(Q.data, (*buf).data(), (*buf).size());
diff --git a/vision/CMakeLists.txt b/vision/CMakeLists.txt
index 47a39a6e4764d466876874994a8ae1d098e3df6c..a0cc3f6b9f57fb1d43d5c56ed035c17b11b0da44 100644
--- a/vision/CMakeLists.txt
+++ b/vision/CMakeLists.txt
@@ -5,7 +5,6 @@ include_directories(${PROJECT_SOURCE_DIR}/vision/include)
 add_subdirectory(lib)
 
 set(CVNODESRC
-	../common/cpp/src/config.cpp
 	src/main.cpp
 	src/calibrate.cpp
 	src/local.cpp
@@ -38,6 +37,7 @@ if (CUDA_FOUND)
 endif (CUDA_FOUND)
 
 add_executable(ftl-vision ${CVNODESRC})
+add_dependencies(ftl-vision ftlcommon)
 add_dependencies(ftl-vision ftlnet)
 add_dependencies(ftl-vision ftlrender)
 add_dependencies(ftl-vision libelas)
@@ -47,6 +47,6 @@ set_property(TARGET ftl-vision PROPERTY CUDA_SEPARABLE_COMPILATION ON)
 endif()
 
 #target_include_directories(cv-node PUBLIC ${PROJECT_SOURCE_DIR}/include)
-target_link_libraries(ftl-vision ftlrender Threads::Threads ZLIB::ZLIB libelas ${OpenCV_LIBS} ${LIBSGM_LIBRARIES} ${CUDA_LIBRARIES} glog::glog ftlnet)
+target_link_libraries(ftl-vision ftlcommon ftlrender Threads::Threads ZLIB::ZLIB libelas ${OpenCV_LIBS} ${LIBSGM_LIBRARIES} ${CUDA_LIBRARIES} glog::glog ftlnet)
 
 
diff --git a/vision/src/algorithms/opencv_bm.cpp b/vision/src/algorithms/opencv_bm.cpp
index acb9793e601f82edcfaac3e9e5b6088a49e25936..203ac37e221e5b086fcf32295dfbc9e09d1e1655 100644
--- a/vision/src/algorithms/opencv_bm.cpp
+++ b/vision/src/algorithms/opencv_bm.cpp
@@ -22,9 +22,12 @@ OpenCVBM::OpenCVBM(nlohmann::json &config) : Disparity(config) {
 void OpenCVBM::compute(const cv::Mat &l, const cv::Mat &r, cv::Mat &disp) {
 	Mat left_disp;
 	Mat right_disp;
-	left_matcher_-> compute(l, r,left_disp);
-    right_matcher_->compute(r,l, right_disp);
-    wls_filter_->filter(left_disp,l,disp,right_disp);
+    Mat lg, rg;
+	cv::cvtColor(l, lg, cv::COLOR_BGR2GRAY);
+	cv::cvtColor(r, rg, cv::COLOR_BGR2GRAY);
+	left_matcher_-> compute(lg, rg,left_disp);
+    right_matcher_->compute(rg, lg, right_disp);
+    wls_filter_->filter(left_disp, l, disp, right_disp);
 }
 
 
diff --git a/vision/src/calibrate.cpp b/vision/src/calibrate.cpp
index 46dadb8ab33f574fd1ce7df941f5293ea1270378..092f5015d9371d44e3ab9163c14ed5d3921e4563 100644
--- a/vision/src/calibrate.cpp
+++ b/vision/src/calibrate.cpp
@@ -4,6 +4,7 @@
 
 #include <glog/logging.h>
 #include <ftl/config.h>
+#include <ftl/configuration.hpp>
 
 #include <iostream>
 #include <sstream>
@@ -214,12 +215,21 @@ bool Calibrate::_loadCalibration() {
 	float scale = 1.0f;
 
     Rect roi1, roi2;
+    FileStorage fs;
 
     // reading intrinsic parameters
-    FileStorage fs(FTL_LOCAL_CONFIG_ROOT "/intrinsics.yml", FileStorage::READ);
-    if (!fs.isOpened()) {
-        LOG(WARNING) << "Calibration file not found";
-        return false;
+    auto ifile = ftl::locateFile("intrinsics.yml");
+    if (ifile) {
+		fs.open((*ifile).c_str(), FileStorage::READ);
+		if (!fs.isOpened()) {
+		    LOG(WARNING) << "Could not open intrinsics file";
+		    return false;
+		}
+		
+		LOG(INFO) << "Intrinsics from: " << *ifile;
+    } else {
+    	LOG(WARNING) << "Calibration intrinsics file not found";
+		return false;
     }
 
     Mat M1, D1, M2, D2;
@@ -231,10 +241,18 @@ bool Calibrate::_loadCalibration() {
     M1 *= scale;
     M2 *= scale;
 
-    fs.open(FTL_LOCAL_CONFIG_ROOT "/extrinsics.yml", FileStorage::READ);
-    if (!fs.isOpened()) {
-        LOG(WARNING) << "Calibration file not found";
-        return false;
+	auto efile = ftl::locateFile("extrinsics.yml");
+	if (efile) {
+		fs.open((*efile).c_str(), FileStorage::READ);
+		if (!fs.isOpened()) {
+		    LOG(WARNING) << "Could not open extrinsics file";
+		    return false;
+		}
+		
+		LOG(INFO) << "Extrinsics from: " << *efile;
+    } else {
+    	LOG(WARNING) << "Calibration extrinsics file not found";
+		return false;
     }
 
     Mat R, T, R1, P1, R2, P2;
diff --git a/vision/src/main.cpp b/vision/src/main.cpp
index e7f4b9fe35a4c8f31abe1d43dc0cd3538b2173c2..59b61bae91e02c781a00edbce2d1a252081a33be 100644
--- a/vision/src/main.cpp
+++ b/vision/src/main.cpp
@@ -5,7 +5,7 @@
  */
 
 #include <glog/logging.h>
-#include <ftl/config.h>
+#include <ftl/configuration.hpp>
 #include <ctpl_stl.h>
 #include <zlib.h>
 
@@ -48,91 +48,30 @@ using std::string;
 using std::vector;
 using std::map;
 using std::condition_variable;
+using std::this_thread::sleep_for;
+using std::chrono::milliseconds;
 using std::mutex;
 using std::unique_lock;
 using cv::Mat;
 using json = nlohmann::json;
-using std::ifstream;
+using ftl::config;
 
-// Store loaded configuration
-static json config;
-
-/**
- * Find and load a JSON configuration file
- */
-static bool findConfiguration(const string &file) {
-	ifstream i;
-	
-	if (file != "") i.open(file);
-	if (!i.is_open()) i.open("./config.json");
-	if (!i.is_open()) i.open(FTL_LOCAL_CONFIG_ROOT "/config.json");
-	if (!i.is_open()) i.open(FTL_GLOBAL_CONFIG_ROOT "/config.json");
-	if (!i.is_open()) return false;
-	i >> config;
-	return true;
-}
-
-/**
- * Generate a map from command line option to value
- */
-map<string, string> read_options(char ***argv, int *argc) {
-	map<string, string> opts;
-
-	while (*argc > 0) {
-		string cmd((*argv)[0]);
-		if (cmd[0] != '-') break;
-
-		size_t p;
-		if ((p = cmd.find("=")) == string::npos) {
-			opts[cmd.substr(2)] = "true";
-		} else {
-			opts[cmd.substr(2, p-2)] = cmd.substr(p+1);
-		}
-
-		(*argc)--;
-		(*argv)++;
-	}
-
-	return opts;
-}
-
-/**
- * Put command line options into json config. If config element does not exist
- * or is of a different type then report an error.
- */
-static void process_options(const map<string, string> &opts) {
-	for (auto opt : opts) {
-		if (opt.first == "config") continue;
-
-		if (opt.first == "version") {
-			std::cout << "FTL Vision Node - v" << FTL_VERSION << std::endl;
-			std::cout << FTL_VERSION_LONG << std::endl;
-			exit(0);
-		}
-
-		try {
-			auto ptr = json::json_pointer("/"+opt.first);
-			// TODO(nick) Allow strings without quotes
-			auto v = json::parse(opt.second);
-			if (v.type() != config.at(ptr).type()) {
-				LOG(ERROR) << "Incorrect type for argument " << opt.first;
-				continue;
-			}
-			config.at(ptr) = v;
-		} catch(...) {
-			LOG(ERROR) << "Unrecognised option: " << opt.first;
-		}
-	}
-}
 
 static void run(const string &file) {
 	ctpl::thread_pool pool(2);
 	Universe net(config["net"]);
 
 	LocalSource *lsrc;
-	if (file != "") {
+	if (ftl::is_video(file)) {
 		// Load video file
 		lsrc = new LocalSource(file, config["source"]);
+	} else if (file != "") {
+		auto vid = ftl::locateFile("video.mp4");
+		if (!vid) {
+			LOG(FATAL) << "No video.mp4 file found in provided paths";
+		} else {
+			lsrc = new LocalSource(*vid, config["source"]);
+		}
 	} else {
 		// Use cameras
 		lsrc = new LocalSource(config["source"]);
@@ -153,7 +92,7 @@ static void run(const string &file) {
 	calibrate.getQ().convertTo(Q_32F, CV_32F);
 	
 	// Allow remote users to access camera calibration matrix
-	net.bind(string("ftl://utu.fi/")+(string)config["stream"]["name"]+string("/rgb-d/calibration"), [&calibrate,Q_32F]() -> vector<unsigned char> {
+	net.bind(string("ftl://utu.fi/")+(string)config["stream"]["name"]+string("/rgb-d/calibration"), [Q_32F]() -> vector<unsigned char> {
 		vector<unsigned char> buf;
 		buf.resize(Q_32F.step*Q_32F.rows);
 		LOG(INFO) << "Calib buf size = " << buf.size();
@@ -188,14 +127,13 @@ static void run(const string &file) {
 			calibrate.rectified(l, r);
 
 			// Feed into sync buffer and network forward
-			sync->feed(ftl::LEFT, l, lsrc->getTimestamp());
-			sync->feed(ftl::RIGHT, r, lsrc->getTimestamp());
+			//sync->feed(ftl::LEFT, l, lsrc->getTimestamp());
+			//sync->feed(ftl::RIGHT, r, lsrc->getTimestamp());
 
 			// Read back from buffer
-			sync->get(ftl::LEFT, l);
-			sync->get(ftl::RIGHT, r);
+			//sync->get(ftl::LEFT, l);
+			//sync->get(ftl::RIGHT, r);
 
-			// TODO(nick) Pipeline this
 		    disparity->compute(l, r, disp);
 
 			unique_lock<mutex> lk(m);
@@ -266,7 +204,8 @@ static void run(const string &file) {
 			LOG(INFO) << "Stream in " << elapsed.count() << "s";
 		});
 
-		display.render(l, disp);
+		// Send RGB+Depth images for local rendering
+		if (pl.rows > 0) display.render(pl, pdisp);
 		display.wait(1);
 
 		// Wait for both pipelines to complete
@@ -282,19 +221,13 @@ static void run(const string &file) {
 }
 
 int main(int argc, char **argv) {
-	argc--;
-	argv++;
-
-	// Process Arguments
-	auto options = read_options(&argv, &argc);
-	if (!findConfiguration(options["config"])) {
-		LOG(FATAL) << "Could not find any configuration!";
-	}
-	process_options(options);
+	auto paths = ftl::configure(argc, argv, "vision");
+	
+	config["paths"] = paths;
 
 	// Choose normal or middlebury modes
 	if (config["middlebury"]["dataset"] == "") {
-		run((argc > 0) ? argv[0] : "");
+		run((paths.size() > 0) ? paths[0] : "");
 	} else {
 		ftl::middlebury::test(config);
 	}
diff --git a/vision/src/middlebury.cpp b/vision/src/middlebury.cpp
index 9252a4374dfeaeb7efae1e1c87e26776c9f9d0e4..494e6a9ff6f0703750afdafece30f0d157e65667 100644
--- a/vision/src/middlebury.cpp
+++ b/vision/src/middlebury.cpp
@@ -244,9 +244,7 @@ void ftl::middlebury::test(nlohmann::json &config) {
 	
 	// Run algorithm
 	auto disparity = ftl::Disparity::create(config["disparity"]);
-	cvtColor(l,  l, cv::COLOR_BGR2GRAY);
-    cvtColor(r, r, cv::COLOR_BGR2GRAY);
-        
+    
     Mat disp;
     disparity->compute(l,r,disp);
 	disp.convertTo(disp, CV_32F);
diff --git a/vision/src/streamer.cpp b/vision/src/streamer.cpp
index ac732684691656ea9a2811666b4612814cd8d8be..7527735cb6b6d207892b0bb2b8488ae3c12d16bf 100644
--- a/vision/src/streamer.cpp
+++ b/vision/src/streamer.cpp
@@ -2,6 +2,7 @@
 #include <ftl/streamer.hpp>
 #include <vector>
 #include <zlib.h>
+// #include <lz4.h>
 
 using ftl::Streamer;
 using ftl::net::Universe;
@@ -24,23 +25,35 @@ void Streamer::send(const Mat &rgb, const Mat &depth) {
 	vector<unsigned char> rgb_buf;
 	cv::imencode(".jpg", rgb, rgb_buf);
 	
+	Mat d2;
+    depth.convertTo(d2, CV_16UC1, 16);
+	
 	vector<unsigned char> d_buf;
-	d_buf.resize(depth.step*depth.rows);
+	/*d_buf.resize(d2.step*d2.rows);
 	z_stream defstream;
     defstream.zalloc = Z_NULL;
     defstream.zfree = Z_NULL;
     defstream.opaque = Z_NULL;
-    defstream.avail_in = depth.step*depth.rows;
-    defstream.next_in = (Bytef *)depth.data; // input char array
-    defstream.avail_out = (uInt)depth.step*depth.rows; // size of output
+    defstream.avail_in = d2.step*d2.rows;
+    defstream.next_in = (Bytef *)d2.data; // input char array
+    defstream.avail_out = (uInt)d2.step*d2.rows; // size of output
     defstream.next_out = (Bytef *)d_buf.data(); // output char array
     
     deflateInit(&defstream, Z_DEFAULT_COMPRESSION);
     deflate(&defstream, Z_FINISH);
     deflateEnd(&defstream);
     
-    d_buf.resize(defstream.total_out);
-    //LOG(INFO) << "Depth Size = " << ((float)d_buf.size() / (1024.0f*1024.0f));
+    d2.copyTo(last);
+    
+    d_buf.resize(defstream.total_out);*/
+    
+    // LZ4 Version
+    // d_buf.resize(LZ4_compressBound(depth.step*depth.rows));
+    // int s = LZ4_compress_default((char*)depth.data, (char*)d_buf.data(), depth.step*depth.rows, d_buf.size());
+    // d_buf.resize(s);
+
+    cv::imencode(".png", d2, d_buf);
+    LOG(INFO) << "Depth Size = " << ((float)d_buf.size() / (1024.0f*1024.0f));
     
     net_.publish(uri_, rgb_buf, d_buf);
 }