diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4bf0f37db31f35fe4c97c09f25a6add778d80c90..5d348360078b5ab4986a3f0f84084672ab72e995 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -477,7 +477,7 @@ if (WITH_SDK)
 	if (NOT WIN32)
 		add_subdirectory(SDK/C)
 	endif()
-	add_subdirectory(SDK/C++)
+	add_subdirectory(SDK/CPP)
 endif()
 
 if (HAVE_AVFORMAT)
diff --git a/SDK/C++/private/feed_impl.cpp b/SDK/C++/private/feed_impl.cpp
deleted file mode 100644
index 001b2974f802d5d46091c9b8f820fdba72729ad3..0000000000000000000000000000000000000000
--- a/SDK/C++/private/feed_impl.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "feed_impl.hpp"
-
-using voltu::internal::FeedImpl;
-
-FeedImpl::FeedImpl(ftl::stream::Feed* feed, uint32_t fsid)
- : feed_(feed)
-{
-	fsids_.insert(fsid);
-}
-
-FeedImpl::~FeedImpl()
-{
-	remove();
-}
-	
-std::string FeedImpl::getURI()
-{
-	return feed_->getURI(*fsids_.begin());
-}
-
-void FeedImpl::remove()
-{
-
-}
-
diff --git a/SDK/C++/private/feed_impl.hpp b/SDK/C++/private/feed_impl.hpp
deleted file mode 100644
index 6eae89a31f769385770854475c416b74df5abdaa..0000000000000000000000000000000000000000
--- a/SDK/C++/private/feed_impl.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <voltu/feed.hpp>
-#include <ftl/streams/feed.hpp>
-#include <unordered_set>
-
-namespace voltu
-{
-namespace internal
-{
-
-class FeedImpl : public voltu::Feed
-{
-public:
-	FeedImpl(ftl::stream::Feed*, uint32_t fsid);
-	~FeedImpl();
-	
-	std::string getURI() override;
-
-	void remove() override;
-
-private:
-	ftl::stream::Feed *feed_;
-	std::unordered_set<uint32_t> fsids_;
-};
-
-}
-}
diff --git a/SDK/C++/public/ext/pybind11 b/SDK/C++/public/ext/pybind11
deleted file mode 160000
index 06a54018c8a9fd9a7be5f5b56414b5da9259f637..0000000000000000000000000000000000000000
--- a/SDK/C++/public/ext/pybind11
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 06a54018c8a9fd9a7be5f5b56414b5da9259f637
diff --git a/SDK/C++/public/include/voltu/feed.hpp b/SDK/C++/public/include/voltu/feed.hpp
deleted file mode 100644
index 7a58301960f8b065955528d32d309198b85ad96e..0000000000000000000000000000000000000000
--- a/SDK/C++/public/include/voltu/feed.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include "defines.hpp"
-
-#include <memory>
-#include <string>
-
-namespace voltu
-{
-
-enum class FeedType
-{
-	kInvalid = 0,
-	kMonoCamera = 1,
-	kStereoCamera = 2,
-	kDepthCamera = 3,
-	kVirtual = 4,
-	kScreen = 5,
-	kRoom = 6,
-	kRooms = 7
-};
-
-class Feed
-{
-public:
-	PY_API virtual std::string getURI() = 0;
-
-	PY_API virtual void remove() = 0;
-
-	// Get rooms
-};
-
-typedef std::shared_ptr<voltu::Feed> FeedPtr;
-
-}
diff --git a/SDK/C++/public/include/voltu/initialise.hpp b/SDK/C++/public/include/voltu/initialise.hpp
deleted file mode 100644
index e44799a67837ad543664b6c81316c82c98f9747e..0000000000000000000000000000000000000000
--- a/SDK/C++/public/include/voltu/initialise.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-#include <memory>
-#include <voltu/system.hpp>
-#include "defines.hpp"
-
-namespace voltu
-{
-	PY_API std::shared_ptr<voltu::System> instance();
-}
diff --git a/SDK/C++/public/include/voltu/room.hpp b/SDK/C++/public/include/voltu/room.hpp
deleted file mode 100644
index da09776227d4e25aeef9f763a0f26270941e090b..0000000000000000000000000000000000000000
--- a/SDK/C++/public/include/voltu/room.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include "defines.hpp"
-#include <voltu/types/frame.hpp>
-#include <memory>
-
-namespace voltu
-{
-
-enum class RoomType
-{
-	kInvalid = 0,
-	kPhysical = 1,
-	kComposite = 2
-};
-
-typedef unsigned int RoomId;
-
-class Room
-{
-public:
-	PY_API virtual bool waitNextFrame(int64_t) = 0;
-
-	PY_API inline bool hasNextFrame() { return waitNextFrame(0); };
-
-	PY_API virtual voltu::FramePtr getFrame() = 0;
-
-	PY_API virtual std::string getName() = 0;
-
-	PY_API virtual bool active() = 0;
-};
-
-typedef std::shared_ptr<Room> RoomPtr;
-
-}
diff --git a/SDK/C++/public/include/voltu/system.hpp b/SDK/C++/public/include/voltu/system.hpp
deleted file mode 100644
index cf927eeba5cb61615ab598a1af61da9aaec2a997..0000000000000000000000000000000000000000
--- a/SDK/C++/public/include/voltu/system.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-#include "defines.hpp"
-
-#include <voltu/room.hpp>
-#include <voltu/observer.hpp>
-#include <voltu/feed.hpp>
-#include <list>
-
-namespace voltu
-{
-
-struct Version
-{
-	int major;
-	int minor;
-	int patch;
-};
-
-class System
-{
-public:
-	virtual voltu::Version getVersion() const = 0;
-
-	PY_API virtual voltu::RoomPtr createRoom() = 0;
-
-	PY_API virtual voltu::ObserverPtr createObserver() = 0;
-
-	PY_API virtual voltu::FeedPtr open(const std::string&) = 0;
-
-	PY_API virtual std::list<voltu::RoomId> listRooms() = 0;
-
-	PY_API virtual voltu::RoomPtr getRoom(voltu::RoomId) = 0;
-};
-
-}
diff --git a/SDK/C++/public/include/voltu/types/errors.hpp b/SDK/C++/public/include/voltu/types/errors.hpp
deleted file mode 100644
index 9426428014094370c26160ab2f4ec2c893205a97..0000000000000000000000000000000000000000
--- a/SDK/C++/public/include/voltu/types/errors.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include <exception>
-
-namespace voltu
-{
-namespace exceptions
-{
-
-struct Exception : public std::exception
-{
-	virtual const char* what() const noexcept { return "VolTu General Error"; }
-};
-
-}
-}
-
-#define VOLTU_EXCEPTION(NAME,BASE,MSG) struct NAME : public voltu::exceptions::BASE { virtual const char* what() const noexcept { return MSG; } };
-
-namespace voltu
-{
-namespace exceptions
-{
-
-VOLTU_EXCEPTION(BadImageChannel, Exception, "Invalid image channel");
-VOLTU_EXCEPTION(NoFrame, Exception, "No frame available");
-VOLTU_EXCEPTION(AlreadyInit, Exception, "VolTu already initialised");
-VOLTU_EXCEPTION(LibraryLoadFailed, Exception, "Could not load VolTu library");
-VOLTU_EXCEPTION(LibraryVersionMismatch, Exception, "Wrong version of library found");
-VOLTU_EXCEPTION(BadSourceURI, Exception, "Bad source URI");
-VOLTU_EXCEPTION(InvalidFrameObject, Exception, "Invalid Frame object");
-VOLTU_EXCEPTION(InternalRenderError, Exception, "Internal renderer error");
-VOLTU_EXCEPTION(InvalidProperty, Exception, "Unknown property enum");
-VOLTU_EXCEPTION(BadParameterValue, Exception, "Method parameter is not valid");
-
-}
-}
diff --git a/SDK/C++/CMakeLists.txt b/SDK/CPP/CMakeLists.txt
similarity index 88%
rename from SDK/C++/CMakeLists.txt
rename to SDK/CPP/CMakeLists.txt
index 8bec8ee83108171e91209a80092813a0915d8466..b64eae3d9a119df248378b8b1e41f2b271187ba1 100644
--- a/SDK/C++/CMakeLists.txt
+++ b/SDK/CPP/CMakeLists.txt
@@ -6,6 +6,9 @@ add_library(voltu SHARED
 	private/image_impl.cpp
 	private/observer_impl.cpp
 	private/pointcloud_impl.cpp
+	private/pipeline_impl.cpp
+	private/operator_impl.cpp
+	private/property_impl.cpp
 )
 
 target_include_directories(voltu
diff --git a/SDK/CPP/private/feed_impl.cpp b/SDK/CPP/private/feed_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..edbb1d884f35dc5e4db0a4cf3aff9ac5b0370cd1
--- /dev/null
+++ b/SDK/CPP/private/feed_impl.cpp
@@ -0,0 +1,82 @@
+#include "feed_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+using voltu::internal::InputFeedImpl;
+using voltu::internal::OutputFeedImpl;
+
+// ==== Input ==================================================================
+
+InputFeedImpl::InputFeedImpl(ftl::stream::Feed* feed, uint32_t fsid)
+ : feed_(feed)
+{
+	fsids_.insert(fsid);
+}
+
+InputFeedImpl::~InputFeedImpl()
+{
+	//remove();
+}
+	
+std::string InputFeedImpl::getURI()
+{
+	return feed_->getURI(*fsids_.begin());
+}
+
+void InputFeedImpl::remove()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+void InputFeedImpl::submit(const voltu::FramePtr &frame)
+{
+	throw voltu::exceptions::ReadOnly();
+}
+
+voltu::FeedType InputFeedImpl::type()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+voltu::PropertyPtr InputFeedImpl::property(voltu::FeedProperty)
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+// ==== Output =================================================================
+
+OutputFeedImpl::OutputFeedImpl(ftl::stream::Feed* feed, const std::string &uri)
+ : feed_(feed), uri_(uri)
+{
+
+}
+
+OutputFeedImpl::~OutputFeedImpl()
+{
+	//remove();
+}
+	
+std::string OutputFeedImpl::getURI()
+{
+	return uri_;
+}
+
+void OutputFeedImpl::remove()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+void OutputFeedImpl::submit(const voltu::FramePtr &frame)
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+voltu::FeedType OutputFeedImpl::type()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+voltu::PropertyPtr OutputFeedImpl::property(voltu::FeedProperty)
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
diff --git a/SDK/CPP/private/feed_impl.hpp b/SDK/CPP/private/feed_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba0ab72d7d43eebaa03c5794b76d2b762e507706
--- /dev/null
+++ b/SDK/CPP/private/feed_impl.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <voltu/feed.hpp>
+#include <ftl/streams/feed.hpp>
+#include <unordered_set>
+
+namespace voltu
+{
+namespace internal
+{
+
+class InputFeedImpl : public voltu::Feed
+{
+public:
+	InputFeedImpl(ftl::stream::Feed*, uint32_t fsid);
+	~InputFeedImpl() override;
+	
+	std::string getURI() override;
+
+	void remove() override;
+
+	void submit(const voltu::FramePtr &frame) override;
+
+	voltu::FeedType type() override;
+
+	voltu::PropertyPtr property(voltu::FeedProperty) override;
+
+private:
+	ftl::stream::Feed *feed_;
+	std::unordered_set<uint32_t> fsids_;
+};
+
+class OutputFeedImpl : public voltu::Feed
+{
+public:
+	OutputFeedImpl(ftl::stream::Feed*, const std::string &uri);
+	~OutputFeedImpl() override;
+	
+	std::string getURI() override;
+
+	void remove() override;
+
+	void submit(const voltu::FramePtr &frame) override;
+
+	voltu::FeedType type() override;
+
+	voltu::PropertyPtr property(voltu::FeedProperty) override;
+
+private:
+	ftl::stream::Feed *feed_;
+	std::string uri_;
+};
+
+}
+}
diff --git a/SDK/C++/private/frame_impl.cpp b/SDK/CPP/private/frame_impl.cpp
similarity index 71%
rename from SDK/C++/private/frame_impl.cpp
rename to SDK/CPP/private/frame_impl.cpp
index a6057f093bc149f9877e09cb096a6f661f8e39fe..0b0779b6a2a2d23a3250a0cb917e8486e88af4b9 100644
--- a/SDK/C++/private/frame_impl.cpp
+++ b/SDK/CPP/private/frame_impl.cpp
@@ -24,6 +24,7 @@ std::list<voltu::ImagePtr> FrameImpl::getImageSet(voltu::Channel c)
 	{
 	case voltu::Channel::kColour	: channel = ftl::codecs::Channel::Colour; break;
 	case voltu::Channel::kDepth		: channel = ftl::codecs::Channel::Depth; break;
+	case voltu::Channel::kNormals	: channel = ftl::codecs::Channel::Normals; break;
 	default: throw voltu::exceptions::BadImageChannel();
 	}
 
@@ -49,6 +50,25 @@ voltu::PointCloudPtr FrameImpl::getPointCloud(voltu::PointCloudFormat cloudfmt,
 	return nullptr;
 }
 
+std::vector<std::string> FrameImpl::getMessages()
+{
+	std::vector<std::string> msgs;
+
+	for (const auto &fs : framesets_)
+	{
+		for (const auto &f : fs->frames)
+		{
+			if (f.hasChannel(ftl::codecs::Channel::Messages))
+			{
+				const auto &m = f.get<std::vector<std::string>>(ftl::codecs::Channel::Messages);
+				msgs.insert(msgs.end(), m.begin(), m.end());
+			}
+		}
+	}
+
+	return msgs;
+}
+
 void FrameImpl::pushFrameSet(const std::shared_ptr<ftl::data::FrameSet> &fs)
 {
 	framesets_.push_back(fs);
diff --git a/SDK/C++/private/frame_impl.hpp b/SDK/CPP/private/frame_impl.hpp
similarity index 90%
rename from SDK/C++/private/frame_impl.hpp
rename to SDK/CPP/private/frame_impl.hpp
index ee6aec4a7da48c086df4c6475125ab7cc36efc95..1b6074a8a6cc1c23dd9e317c09cf1a5596bd467c 100644
--- a/SDK/C++/private/frame_impl.hpp
+++ b/SDK/CPP/private/frame_impl.hpp
@@ -14,12 +14,14 @@ class FrameImpl : public voltu::Frame
 {
 public:
 	FrameImpl();
-	~FrameImpl();
+	~FrameImpl() override;
 
 	std::list<voltu::ImagePtr> getImageSet(voltu::Channel) override;
 
 	voltu::PointCloudPtr getPointCloud(voltu::PointCloudFormat cloudfmt, voltu::PointFormat pointfmt) override;
 
+	std::vector<std::string> getMessages() override;
+
 	int64_t getTimestamp() override;
 
 	void pushFrameSet(const std::shared_ptr<ftl::data::FrameSet> &fs);
diff --git a/SDK/C++/private/image_impl.cpp b/SDK/CPP/private/image_impl.cpp
similarity index 90%
rename from SDK/C++/private/image_impl.cpp
rename to SDK/CPP/private/image_impl.cpp
index f8bf63a27001f0540f008eed4a5f4bb5bd8dfb75..76dea9a2234933e6dad53bdaecd7e83f52e257fd 100644
--- a/SDK/C++/private/image_impl.cpp
+++ b/SDK/CPP/private/image_impl.cpp
@@ -30,6 +30,10 @@ voltu::ImageData ImageImpl::getHost()
 	{
 		r.format = voltu::ImageFormat::kFloat32;
 	}
+	else if (m.type() == CV_16FC4)
+	{
+		r.format = voltu::ImageFormat::kFloat16_4;
+	}
 
 	return r;
 }
@@ -51,6 +55,10 @@ voltu::ImageData ImageImpl::getDevice()
 	{
 		r.format = voltu::ImageFormat::kFloat32;
 	}
+	else if (m.type() == CV_16FC4)
+	{
+		r.format = voltu::ImageFormat::kFloat16_4;
+	}
 
 	return r;
 }
@@ -66,6 +74,7 @@ voltu::Channel ImageImpl::getChannel()
 	{
 	case ftl::codecs::Channel::Colour		: return voltu::Channel::kColour;
 	case ftl::codecs::Channel::Depth		: return voltu::Channel::kDepth;
+	case ftl::codecs::Channel::Normals		: return voltu::Channel::kNormals;
 	default: return voltu::Channel::kInvalid;
 	}
 }
diff --git a/SDK/C++/private/image_impl.hpp b/SDK/CPP/private/image_impl.hpp
similarity index 97%
rename from SDK/C++/private/image_impl.hpp
rename to SDK/CPP/private/image_impl.hpp
index 189a1c19fd13366e8728a50494a6ee9b1e2c4c5f..ac9a33c46cfb9211ab7d9896b65971b6a550e952 100644
--- a/SDK/C++/private/image_impl.hpp
+++ b/SDK/CPP/private/image_impl.hpp
@@ -12,7 +12,7 @@ class ImageImpl : public voltu::Image
 {
 public:
 	ImageImpl(const ftl::rgbd::Frame&, ftl::codecs::Channel c);
-	~ImageImpl();
+	~ImageImpl() override;
 
 	voltu::ImageData getHost() override;
 
diff --git a/SDK/C++/private/observer_impl.cpp b/SDK/CPP/private/observer_impl.cpp
similarity index 95%
rename from SDK/C++/private/observer_impl.cpp
rename to SDK/CPP/private/observer_impl.cpp
index 325d24cf39eb8a0a32e5390f6ef0c84a5ab4a7f8..8315df3d229a86b3b0024d1e739d79c2e9c04592 100644
--- a/SDK/C++/private/observer_impl.cpp
+++ b/SDK/CPP/private/observer_impl.cpp
@@ -9,9 +9,10 @@ using voltu::internal::ObserverImpl;
 using ftl::rgbd::Capability;
 
 ObserverImpl::ObserverImpl(ftl::Configurable *base)
+ : id_(254)  // FIXME: Allocate this
 {
 	pool_ = new ftl::data::Pool(2,5);
-	rend_ = ftl::create<ftl::render::CUDARender>(base, "camN");
+	rend_ = ftl::create<ftl::render::CUDARender>(base, "camN");  // FIXME: Generate name properly
 
 	intrinsics_.fx = 700.0f;
 	intrinsics_.fy = 700.0f;
@@ -28,7 +29,9 @@ ObserverImpl::ObserverImpl(ftl::Configurable *base)
 
 ObserverImpl::~ObserverImpl()
 {
-
+	frameset_.reset();
+	delete rend_;
+	delete pool_;
 }
 
 void ObserverImpl::setResolution(uint32_t w, uint32_t h)
@@ -163,7 +166,7 @@ voltu::FramePtr ObserverImpl::getFrame()
 
 voltu::PropertyPtr ObserverImpl::property(voltu::ObserverProperty)
 {
-	throw voltu::exceptions::InvalidProperty();
+	throw voltu::exceptions::NotImplemented();
 }
 
 std::shared_ptr<ftl::data::FrameSet> ObserverImpl::_makeFrameSet()
diff --git a/SDK/C++/private/observer_impl.hpp b/SDK/CPP/private/observer_impl.hpp
similarity index 97%
rename from SDK/C++/private/observer_impl.hpp
rename to SDK/CPP/private/observer_impl.hpp
index 817e2babdb45b2beb23380f7b6e4ee20ca7e87c2..dfccf7e59476ffd405e3a222b3e41e5d30d1f58a 100644
--- a/SDK/C++/private/observer_impl.hpp
+++ b/SDK/CPP/private/observer_impl.hpp
@@ -17,7 +17,7 @@ class ObserverImpl : public voltu::Observer
 public:
 	explicit ObserverImpl(ftl::Configurable *base);
 
-	~ObserverImpl();
+	~ObserverImpl() override;
 
 	void setResolution(uint32_t w, uint32_t h) override;
 
diff --git a/SDK/CPP/private/operator_impl.cpp b/SDK/CPP/private/operator_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..41f376c209d9d8ba7b5bfbd1c05a155890355643
--- /dev/null
+++ b/SDK/CPP/private/operator_impl.cpp
@@ -0,0 +1,22 @@
+#include "operator_impl.hpp"
+#include "property_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+using voltu::internal::OperatorImpl;
+
+OperatorImpl::OperatorImpl(ftl::Configurable *cfg)
+ : cfg_(cfg)
+{
+	
+}
+
+OperatorImpl::~OperatorImpl()
+{
+
+}
+
+voltu::PropertyPtr OperatorImpl::property(const std::string &name)
+{
+	if (!cfg_->has(name)) throw voltu::exceptions::BadPropertyName();
+	return std::make_shared<voltu::internal::CfgPropertyImpl>(cfg_, name);
+}
diff --git a/SDK/CPP/private/operator_impl.hpp b/SDK/CPP/private/operator_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfe5d26ab0f24d1dafeb8fec2f0c1d4ea67b5fac
--- /dev/null
+++ b/SDK/CPP/private/operator_impl.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <voltu/operator.hpp>
+#include <ftl/configurable.hpp>
+
+#include <string>
+
+namespace voltu
+{
+namespace internal
+{
+
+class OperatorImpl : public voltu::Operator
+{
+public:
+	OperatorImpl(ftl::Configurable*);
+	~OperatorImpl() override;
+
+	voltu::PropertyPtr property(const std::string &name) override;
+
+private:
+	ftl::Configurable *cfg_;
+};
+
+}
+}
\ No newline at end of file
diff --git a/SDK/CPP/private/pipeline_impl.cpp b/SDK/CPP/private/pipeline_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..114f73ad4b19a0bf8db3053bdc25b6260e63351c
--- /dev/null
+++ b/SDK/CPP/private/pipeline_impl.cpp
@@ -0,0 +1,60 @@
+#include "pipeline_impl.hpp"
+#include "frame_impl.hpp"
+#include "operator_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+#include <ftl/operators/fusion.hpp>
+#include <ftl/operators/gt_analysis.hpp>
+
+using voltu::internal::PipelineImpl;
+
+PipelineImpl::PipelineImpl(ftl::Configurable *root)
+{
+	graph_ = ftl::create<ftl::operators::Graph>(root, "pipe1");
+}
+
+PipelineImpl::~PipelineImpl()
+{
+	delete graph_;
+}
+
+void PipelineImpl::submit(const voltu::FramePtr &frame)
+{
+	auto *fimp = dynamic_cast<voltu::internal::FrameImpl*>(frame.get());
+	if (!fimp)
+	{
+		throw voltu::exceptions::InvalidFrameObject();
+	}
+
+	const auto &sets = fimp->getInternalFrameSets();
+
+	if (sets.size() > 1) throw voltu::exceptions::IncompatibleOperation();
+
+	for (const auto &fs : sets)
+	{
+		ready_ = false;
+		graph_->queue(fs, [this]()
+		{
+			ready_ = true;
+		});
+	}
+}
+
+bool PipelineImpl::waitCompletion(int timeout)
+{
+	int count = timeout / 5;
+	while (!ready_ && --count >= 0) std::this_thread::sleep_for(std::chrono::milliseconds(5));
+	return ready_;
+}
+
+voltu::OperatorPtr PipelineImpl::appendOperator(voltu::OperatorId id)
+{
+	if (static_cast<int>(id) <= 0) throw voltu::exceptions::BadParameterValue();
+	
+	switch (id)
+	{
+	case voltu::OperatorId::kFusion			: return std::make_shared<voltu::internal::OperatorImpl>(graph_->append<ftl::operators::Fusion>("fusion"));
+	case voltu::OperatorId::kGTEvaluator	: return std::make_shared<voltu::internal::OperatorImpl>(graph_->append<ftl::operators::GTAnalysis>("gtanal"));
+	default: throw voltu::exceptions::NotImplemented();
+	}
+}
\ No newline at end of file
diff --git a/SDK/CPP/private/pipeline_impl.hpp b/SDK/CPP/private/pipeline_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..98fe41c9018fe328c1cb296e3819d052a8808a73
--- /dev/null
+++ b/SDK/CPP/private/pipeline_impl.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <voltu/pipeline.hpp>
+#include <ftl/operators/operator.hpp>
+
+namespace voltu
+{
+namespace internal
+{
+
+class PipelineImpl : public voltu::Pipeline
+{
+public:
+	PipelineImpl(ftl::Configurable *root);
+	~PipelineImpl() override;
+
+	void submit(const voltu::FramePtr &frame) override;
+
+	bool waitCompletion(int timeout) override;
+
+	voltu::OperatorPtr appendOperator(voltu::OperatorId id) override;
+
+private:
+	ftl::operators::Graph *graph_;
+	bool ready_ = false;
+};
+
+}
+}
\ No newline at end of file
diff --git a/SDK/C++/private/pointcloud_impl.cpp b/SDK/CPP/private/pointcloud_impl.cpp
similarity index 100%
rename from SDK/C++/private/pointcloud_impl.cpp
rename to SDK/CPP/private/pointcloud_impl.cpp
diff --git a/SDK/C++/private/pointcloud_impl.hpp b/SDK/CPP/private/pointcloud_impl.hpp
similarity index 100%
rename from SDK/C++/private/pointcloud_impl.hpp
rename to SDK/CPP/private/pointcloud_impl.hpp
diff --git a/SDK/CPP/private/property_impl.cpp b/SDK/CPP/private/property_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb3cd8dcd0364d458b7c6dd23be94719c7031f02
--- /dev/null
+++ b/SDK/CPP/private/property_impl.cpp
@@ -0,0 +1,135 @@
+#include "property_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+using voltu::internal::CfgPropertyImpl;
+//using voltu::internal::FloatCfgProperty;
+
+CfgPropertyImpl::CfgPropertyImpl(ftl::Configurable *cfg, const std::string &name)
+ : cfg_(cfg), name_(name)
+{
+
+}
+
+CfgPropertyImpl::~CfgPropertyImpl()
+{
+
+}
+
+void CfgPropertyImpl::setInt(int value)
+{
+	if (cfg_->is<int>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+void CfgPropertyImpl::setFloat(float value)
+{
+	if (cfg_->is<float>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+void CfgPropertyImpl::setString(const std::string &value)
+{
+	if (cfg_->is<std::string>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+void CfgPropertyImpl::setBool(bool value)
+{
+	if (cfg_->is<bool>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+int CfgPropertyImpl::getInt()
+{
+	if (cfg_->is<int>(name_))
+	{
+		return *cfg_->get<int>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+float CfgPropertyImpl::getFloat()
+{
+	if (cfg_->is<float>(name_))
+	{
+		return *cfg_->get<float>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+std::string CfgPropertyImpl::getString()
+{
+	if (cfg_->is<std::string>(name_))
+	{
+		return *cfg_->get<std::string>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+bool CfgPropertyImpl::getBool()
+{
+	if (cfg_->is<bool>(name_))
+	{
+		return *cfg_->get<bool>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+// ==== Float ====
+
+/*FloatCfgProperty::FloatCfgProperty(ftl::Configurable *cfg, const std::string &name)
+ : CfgPropertyImpl(cfg, name)
+{
+
+}
+
+FloatCfgProperty::~FloatCfgProperty()
+{
+
+}
+
+void FloatCfgProperty::setFloat(float)
+{
+
+}
+
+float FloatCfgProperty::getFloat()
+{
+
+}*/
diff --git a/SDK/CPP/private/property_impl.hpp b/SDK/CPP/private/property_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2adf96de3f172d07357a13cfd568ea7304dbd00d
--- /dev/null
+++ b/SDK/CPP/private/property_impl.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <ftl/configurable.hpp>
+#include <voltu/types/property.hpp>
+#include <string>
+
+namespace voltu
+{
+namespace internal
+{
+
+class CfgPropertyImpl : public voltu::Property
+{
+public:
+	CfgPropertyImpl(ftl::Configurable *cfg, const std::string &name);
+	virtual ~CfgPropertyImpl() override;
+
+	virtual void setInt(int) override;
+
+	virtual void setFloat(float) override;
+
+	virtual void setString(const std::string &) override;
+
+	virtual void setBool(bool) override;
+
+	virtual int getInt() override;
+
+	virtual float getFloat() override;
+
+	virtual std::string getString() override;
+
+	virtual bool getBool() override;
+
+private:
+	ftl::Configurable *cfg_;
+	std::string name_;
+};
+
+/*class FloatCfgProperty : public CfgPropertyImpl
+{
+public:
+	FloatCfgProperty(ftl::Configurable *cfg, const std::string &name);
+	~FloatCfgProperty() override;
+
+	void setFloat(float) override;
+
+	float getFloat() override;
+};*/
+
+}
+}
diff --git a/SDK/C++/private/room_impl.cpp b/SDK/CPP/private/room_impl.cpp
similarity index 96%
rename from SDK/C++/private/room_impl.cpp
rename to SDK/CPP/private/room_impl.cpp
index 9df583c86610ecfeb4c0140ee659201ba76a1f29..76ab1b92bad65d098c8a0f2a34685edc9219e458 100644
--- a/SDK/C++/private/room_impl.cpp
+++ b/SDK/CPP/private/room_impl.cpp
@@ -11,6 +11,11 @@ RoomImpl::RoomImpl(ftl::stream::Feed* feed)
 
 }
 
+RoomImpl::~RoomImpl()
+{
+	if (filter_) filter_->remove();
+}
+
 bool RoomImpl::waitNextFrame(int64_t timeout)
 {
 	if (!filter_)
diff --git a/SDK/C++/private/room_impl.hpp b/SDK/CPP/private/room_impl.hpp
similarity index 96%
rename from SDK/C++/private/room_impl.hpp
rename to SDK/CPP/private/room_impl.hpp
index 6c57dfcbb499c32f487d952c7b021da3ce148faa..be8516a637b316c9a3964b14952f11fcadd23459 100644
--- a/SDK/C++/private/room_impl.hpp
+++ b/SDK/CPP/private/room_impl.hpp
@@ -13,6 +13,8 @@ class RoomImpl : public voltu::Room
 {
 public:
 	explicit RoomImpl(ftl::stream::Feed*);
+	
+	~RoomImpl() override;
 
 	bool waitNextFrame(int64_t) override;
 
diff --git a/SDK/C++/private/system.cpp b/SDK/CPP/private/system.cpp
similarity index 77%
rename from SDK/C++/private/system.cpp
rename to SDK/CPP/private/system.cpp
index dd18eef256b5d16dba15f00d9f5170380b286e48..84bdbcd406793b0a1b51300b2f7ef0e860e67287 100644
--- a/SDK/C++/private/system.cpp
+++ b/SDK/CPP/private/system.cpp
@@ -2,6 +2,7 @@
 #include "feed_impl.hpp"
 #include "room_impl.hpp"
 #include "observer_impl.hpp"
+#include "pipeline_impl.hpp"
 #include <voltu/voltu.hpp>
 #include <voltu/types/errors.hpp>
 #include <ftl/timer.hpp>
@@ -44,7 +45,14 @@ SystemImpl::SystemImpl()
 SystemImpl::~SystemImpl()
 {
 	ftl::timer::stop(true);
+	net_->shutdown();
 	ftl::pool.stop(true);
+	delete feed_;
+	delete net_;
+	delete root_;
+
+	// FIXME: Check this actually works, can it be restarted? Pool issues?
+	g_isinit = false;
 }
 
 voltu::Version SystemImpl::getVersion() const
@@ -61,7 +69,7 @@ voltu::FeedPtr SystemImpl::open(const std::string& uri)
 	try
 	{
 		uint32_t fsid = feed_->add(uri);
-		return std::make_shared<voltu::internal::FeedImpl>(feed_, fsid);
+		return std::make_shared<voltu::internal::InputFeedImpl>(feed_, fsid);
 	}
 	catch(const std::exception &e)
 	{
@@ -88,3 +96,13 @@ voltu::ObserverPtr SystemImpl::createObserver()
 {
 	return std::make_shared<voltu::internal::ObserverImpl>(root_);
 }
+
+voltu::FeedPtr SystemImpl::createFeed(const std::string &uri)
+{
+	return std::make_shared<voltu::internal::OutputFeedImpl>(feed_, uri);
+}
+
+voltu::PipelinePtr SystemImpl::createPipeline()
+{
+	return std::make_shared<voltu::internal::PipelineImpl>(root_);
+}
diff --git a/SDK/C++/private/system_impl.hpp b/SDK/CPP/private/system_impl.hpp
similarity index 83%
rename from SDK/C++/private/system_impl.hpp
rename to SDK/CPP/private/system_impl.hpp
index f11f48891d7a3b838303abdda47f240c63d60da7..dfc2fae09a7b8fd2dafaa5161e49ba0964af092c 100644
--- a/SDK/C++/private/system_impl.hpp
+++ b/SDK/CPP/private/system_impl.hpp
@@ -14,7 +14,7 @@ class SystemImpl : public voltu::System
 {
 public:
 	SystemImpl();
-	~SystemImpl();
+	~SystemImpl() override;
 
 	voltu::Version getVersion() const override;
 
@@ -28,6 +28,10 @@ public:
 
 	voltu::RoomPtr getRoom(voltu::RoomId) override;
 
+	voltu::FeedPtr createFeed(const std::string &uri) override;
+
+	voltu::PipelinePtr createPipeline() override;
+
 private:
 	ftl::Configurable* root_;
 	ftl::stream::Feed* feed_;
diff --git a/SDK/C++/public/CMakeLists.txt b/SDK/CPP/public/CMakeLists.txt
similarity index 93%
rename from SDK/C++/public/CMakeLists.txt
rename to SDK/CPP/public/CMakeLists.txt
index ae8c7da08bfad909195d334e699fdbba5655e0bf..a0eb7eaf58262395b4232cba3a0421cb144ccfca 100644
--- a/SDK/C++/public/CMakeLists.txt
+++ b/SDK/CPP/public/CMakeLists.txt
@@ -62,6 +62,11 @@ add_executable(voltu_basic_virtual_cam
 )
 target_link_libraries(voltu_basic_virtual_cam voltu_sdk)
 
+add_executable(voltu_fusion_evaluator
+	samples/fusion_evaluator/main.cpp
+)
+target_link_libraries(voltu_fusion_evaluator voltu_sdk)
+
 if (WITH_PYTHON)
 	add_subdirectory(ext/pybind11)
 	add_subdirectory(python)
diff --git a/SDK/CPP/public/LICENSE b/SDK/CPP/public/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..2e5d48af4dd87df06b5a99c9605b19f9d3303324
--- /dev/null
+++ b/SDK/CPP/public/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2020 Nicolas Pope
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+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.
\ No newline at end of file
diff --git a/SDK/CPP/public/README.md b/SDK/CPP/public/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..980ab1abd49a40b37cdb51b146be1421706564d1
--- /dev/null
+++ b/SDK/CPP/public/README.md
@@ -0,0 +1 @@
+# VolTu - Volumetric Capture SDK
\ No newline at end of file
diff --git a/SDK/C++/public/include/voltu/defines.hpp b/SDK/CPP/public/include/voltu/defines.hpp
similarity index 74%
rename from SDK/C++/public/include/voltu/defines.hpp
rename to SDK/CPP/public/include/voltu/defines.hpp
index 4f5ded24e0c89cfdbb5b002d1e2d830c1f90aeb2..19f2416f2812cc41b69b6c2f73a7a7f3bd4f7588 100644
--- a/SDK/C++/public/include/voltu/defines.hpp
+++ b/SDK/CPP/public/include/voltu/defines.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file system.hpp
+ * @copyright Copyright (c) 2020 Sebastian Hahta, MIT License
+ * @author Sebastian Hahta
+ */
+
 #pragma once
 
 #ifndef PY_API
diff --git a/SDK/CPP/public/include/voltu/feed.hpp b/SDK/CPP/public/include/voltu/feed.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d863cf8940cf70cde9c41e3b4576877e9fe2ee7f
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/feed.hpp
@@ -0,0 +1,61 @@
+/**
+ * @file feed.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+
+#include "defines.hpp"
+#include <voltu/types/frame.hpp>
+#include <voltu/types/property.hpp>
+
+#include <memory>
+#include <string>
+
+namespace voltu
+{
+
+enum class FeedType
+{
+	kInvalid = 0,
+	kDevice = 1,
+	kFile = 2,
+	kStream = 3,
+	kVirtual = 4,
+	kMultiple = 5
+};
+
+enum class FeedProperty
+{
+	kInvalid			= 0,
+	kColourCodec		= 2001,
+	kDepthCodec			= 2002,
+	kFPSLimit			= 2003,
+	kColourBitrate		= 2004,
+	kDepthBitrate		= 2005,
+	kFileLooping		= 2006,
+	kFileSpeed			= 2007
+};
+
+class Feed
+{
+public:
+	virtual ~Feed() = default;
+	
+	PY_API virtual std::string getURI() = 0;
+
+	PY_API virtual void remove() = 0;
+
+	PY_API virtual void submit(const voltu::FramePtr &frame) = 0;
+
+	PY_API virtual voltu::FeedType type() = 0;
+
+	PY_API virtual voltu::PropertyPtr property(voltu::FeedProperty) = 0;
+
+	// Get rooms
+};
+
+typedef std::shared_ptr<voltu::Feed> FeedPtr;
+
+}
diff --git a/SDK/CPP/public/include/voltu/initialise.hpp b/SDK/CPP/public/include/voltu/initialise.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a6e5b3ffc2c2326b5934d2577d0458605ddc51c
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/initialise.hpp
@@ -0,0 +1,46 @@
+/**
+ * @file initialise.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+#include <memory>
+#include <voltu/system.hpp>
+#include "defines.hpp"
+
+namespace voltu
+{
+	/**
+	 * @brief Get core VolTu instance.
+	 * 
+	 * This method returns a smart pointer to a singleton VolTu runtime
+	 * instance and must be the first VolTu call. On any given machine it is
+	 * only sensible and possible to have one runtime instance of VolTu due to
+	 * its use of hardware devices. Multiple real instances are not possible.
+	 * 
+	 * @code
+	 * int main(int argc, char** argv) {
+	 *     auto vtu = voltu::instance();
+	 * 
+	 *     vtu->open("device:camera");
+	 *     ...
+	 * }
+	 * @endcode
+	 * 
+	 * @note
+	 * This method must only be called once.
+	 * 
+	 * @throw voltu::exceptions::LibraryLoadFailed
+	 * If runtime not found or is invalid.
+	 *
+	 * @throw voltu::exceptions::RuntimeVersionMismatch
+	 * If major or minor version does not match the SDK headers.
+	 * 
+	 * @throw voltu::exceptions::RuntimeAlreadyInUse
+	 * If a runtime instance is in use by another application.
+	 * 
+	 * @return Singleton VolTu runtime instance.
+	 */
+	PY_API std::shared_ptr<voltu::System> instance();
+}
diff --git a/SDK/C++/public/include/voltu/observer.hpp b/SDK/CPP/public/include/voltu/observer.hpp
similarity index 81%
rename from SDK/C++/public/include/voltu/observer.hpp
rename to SDK/CPP/public/include/voltu/observer.hpp
index 03323b0793212d14bcc4f45f6112c1af33b30363..e5d99325f01df39a58e1f8eb0ba4c7572e53f044 100644
--- a/SDK/C++/public/include/voltu/observer.hpp
+++ b/SDK/CPP/public/include/voltu/observer.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file observer.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 #include "defines.hpp"
@@ -25,6 +31,8 @@ enum class ObserverProperty
 class Observer
 {
 public:
+	virtual ~Observer() = default;
+	
 	PY_API virtual void setResolution(uint32_t w, uint32_t h) = 0;
 
 	PY_API virtual void setFocalLength(uint32_t f) = 0;
@@ -40,6 +48,8 @@ public:
 	PY_API PY_RV_LIFETIME_PARENT virtual voltu::FramePtr getFrame() = 0;
 
 	PY_API virtual voltu::PropertyPtr property(voltu::ObserverProperty) = 0;
+
+	//PY_API virtual voltu::PropertyPtr property(const std::string &name) = 0;
 };
 
 typedef std::shared_ptr<Observer> ObserverPtr;
diff --git a/SDK/C++/public/include/voltu/opencv.hpp b/SDK/CPP/public/include/voltu/opencv.hpp
similarity index 74%
rename from SDK/C++/public/include/voltu/opencv.hpp
rename to SDK/CPP/public/include/voltu/opencv.hpp
index 467a793e8ecff579444b8afede9a1dd740aac05c..7119cbc87097ce1609a7e93696059db38b7097e3 100644
--- a/SDK/C++/public/include/voltu/opencv.hpp
+++ b/SDK/CPP/public/include/voltu/opencv.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file opencv.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 #include <opencv2/core/mat.hpp>
diff --git a/SDK/CPP/public/include/voltu/operator.hpp b/SDK/CPP/public/include/voltu/operator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..432e7b63568ee8d5bd62d1649c98bf30f79e0f3e
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/operator.hpp
@@ -0,0 +1,51 @@
+/**
+ * @file operator.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+
+#include <voltu/types/property.hpp>
+#include <memory>
+
+namespace voltu
+{
+
+
+/**
+ * @brief Operation type identifier.
+ */
+enum class OperatorId
+{
+	kInvalid		= 0,
+	kDepth			= 1,	///< Calculate depth map from stereo images
+	kGTEvaluator	= 2,	///< Ground truth quality evaluation
+	kFusion			= 3,	///< Fuse multiple depth maps together
+	kClipping		= 4,	///< Use a clip box to remove unwanted data
+	kAruco			= 5,	///< Aruco tag detector
+	kPixelAnalysis	= 6		// TODO: Change this
+};
+
+/**
+ * @brief Manage operator settings.
+ */
+class Operator
+{
+public:
+	virtual ~Operator() = default;
+	
+	/**
+	 * @brief Get a named operator property.
+	 * 
+	 * @throw voltu::exceptions::BadPropertyName If name not valid.
+	 * @return Property accessor object.
+	 */
+	PY_API virtual voltu::PropertyPtr property(const std::string &name) = 0;
+
+	// TODO: Get list of properties supported
+};
+
+typedef std::shared_ptr<Operator> OperatorPtr;
+
+}
\ No newline at end of file
diff --git a/SDK/CPP/public/include/voltu/pipeline.hpp b/SDK/CPP/public/include/voltu/pipeline.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8a201bac088192866886505050b2bce07b51acd
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/pipeline.hpp
@@ -0,0 +1,89 @@
+/**
+ * @file pipeline.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+
+#include <voltu/defines.hpp>
+#include <voltu/types/frame.hpp>
+#include <voltu/operator.hpp>
+#include <memory>
+
+namespace voltu
+{
+
+/**
+ * @brief Manage a pipeline of frame procesing operations.
+ * 
+ * A frame processing pipeline can be constructed from a range of different
+ * operators. One example is the fusion operator that merges all input data
+ * together with a model. The pipeline of operators runs asynchonously but in
+ * sequential order and modifies the frames internal data.
+ * 
+ * @see voltu::Frame
+ * @see voltu::Operator
+ */
+class Pipeline
+{
+public:
+	virtual ~Pipeline() = default;
+	
+	/**
+	 * @brief Send a frame for processing.
+	 * 
+	 * This method is non-blocking and will therefore return before operator
+	 * processing has been completed. The data inside the frame object is
+	 * modified during this process, so the data should not be accessed until
+	 * the pipeline is completed.
+	 * 
+	 * @note
+	 * You must not submit multiple frames to the same pipeline without
+	 * ensuring completion. You must also not access frame data during
+	 * processing. The room object associated with the frame must remain valid
+	 * for the duration of processing.
+	 * 
+	 * @param frame A room data frame.
+	 * 
+	 * @throw voltu::exceptions::InvalidFrameObject If the frame is invalid
+	 * @throw voltu::exceptions::IncompatibleOperation If the pipeline given
+	 *     cannot be applied to the frame for some reason.
+	 */
+	PY_API virtual void submit(const voltu::FramePtr &frame) = 0;
+
+	/**
+	 * @brief Block until all processing is completed.
+	 * 
+	 * If a frame has been submitted, this can be used to block until all
+	 * processing has finished. If no frame has been submitted then this
+	 * method will currently block until timeout.
+	 * 
+	 * @todo Allow timeout of -1 and exception on no frame.
+	 * 
+	 * @param timeout Millisecond timeout, or 0 for non-blocking check.
+	 * @return True if completed
+	 */
+	PY_API virtual bool waitCompletion(int timeout) = 0;
+
+	/**
+	 * @brief Add an operator to this pipeline.
+	 * 
+	 * Each operator is appended to the pipeline in the order added, and
+	 * therefore will be processed sequentially in that same order. One type
+	 * of operator can be appended multiple times, useful if different settings
+	 * are to be used each time. Settings can be accessed from the returned
+	 * operator management instance.
+	 * 
+	 * @see voltu::Operator
+	 * 
+	 * @throw voltu::exceptions::BadParameterValue For invalid operators
+	 * @throw voltu::exceptions::NotImplemented If operator not implemented for SDK
+	 * @return Operator management instance for settings.
+	 */
+	PY_API virtual voltu::OperatorPtr appendOperator(voltu::OperatorId id) = 0;
+};
+
+typedef std::shared_ptr<Pipeline> PipelinePtr;
+
+}
diff --git a/SDK/CPP/public/include/voltu/room.hpp b/SDK/CPP/public/include/voltu/room.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..87f0f070cc3fae52bd52a6046c6cd4ae299680ea
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/room.hpp
@@ -0,0 +1,121 @@
+/**
+ * @file room.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+
+#include "defines.hpp"
+#include <voltu/types/frame.hpp>
+#include <memory>
+#include <string>
+
+namespace voltu
+{
+
+/**
+ * @brief Room classification.
+ */
+enum class RoomType
+{
+	kInvalid = 0,
+	kPhysical = 1,	///< Physically captured space
+	kComposite = 2	///< Virtual combination of other rooms
+};
+
+/**
+ * @brief Unique room identifier.
+ */
+typedef unsigned int RoomId;
+
+/**
+ * @brief Volumetric captured room data.
+ * 
+ * An instance of this class provides access to discrete data frames for a
+ * volumetrically captured room, or combination of rooms. The room data is
+ * made available in discrete timestamped frames, but only the most recent
+ * frame is kept inside the room class. Blocking and polling functions can be
+ * used to wait for new frames, or the most recent frame can always be accessed.
+ * If you wish to keep a history of frames, it will be up to you to maintain a
+ * copy of the frame objects obtained from the room instance as they arrive.
+ * 
+ * For composite virtual rooms, a frame constitutes all data from all sub-rooms.
+ * Timestamps for these sub-rooms may not match each other and are not expected
+ * to be synchronised.
+ * 
+ * @todo A callback option for receiving new frames.
+ * @see voltu::Frame
+ */
+class Room
+{
+public:
+	virtual ~Room() = default;
+	
+	/**
+	 * @brief Allow blocking until a new frame is received.
+	 * 
+	 * Depending upon the timeout value, this function does the following:
+	 * 1) If `timeout` = 0 then it returns immediately.
+	 * 2) If `timeout` < 0 then it blocks without timeout.
+	 * 3) If `timeout` > 0 then it blocks for maximum `timeout` milliseconds.
+	 * 
+	 * @note
+	 * Note that for composite rooms with multiple physical rooms, a new
+	 * frame occurs whenever any of the physical rooms provides a new frame,
+	 * even if other rooms do not.
+	 * 
+	 * @todo Allow option to wait for new frames from all sub-rooms.
+	 * 
+	 * @param timeout Millisecond timeout, or 0 or -1.
+	 * @return True if a new unseen frame is available.
+	 */
+	PY_API virtual bool waitNextFrame(int64_t timeout) = 0;
+
+	/**
+	 * @brief Check if a new frame is available.
+	 * 
+	 * Equivalent to `waitNextFrame(0)`.
+	 * 
+	 * @return True if a new unseen frame is available.
+	 */
+	PY_API inline bool hasNextFrame() { return waitNextFrame(0); };
+
+	/**
+	 * @brief Retrieve the most recently available frame.
+	 * 
+	 * This method can be called any number of times and may return the same
+	 * frame data if no new data has arrived between calls. Calling this once
+	 * marks the data as seen, and therefore causes a subsequent call to
+	 * `waitNextFrame` to block until more data arrives.
+	 * 
+	 * @note
+	 * Each call to `getFrame` can return a different smart pointer for the same
+	 * frame data, this is valid. The room object must remain in existence for
+	 * as long as any frame objects are held.
+	 * 
+	 * @throw voltu::exceptions::NoFrame If no frames have yet been received.
+	 * @return Timestamped frame data instance.
+	 */
+	PY_API virtual voltu::FramePtr getFrame() = 0;
+
+	/**
+	 * @brief Get a human readable room name.
+	 * @return A room name string.
+	 */
+	PY_API virtual std::string getName() = 0;
+
+	/**
+	 * @brief Check if the room is actively receiving data.
+	 * 
+	 * It is possible that the underlying data source for a room finished or
+	 * is otherwise terminated, in which case the room is marked inactive.
+	 * 
+	 * @return True if room is expected to continue receiving data.
+	 */
+	PY_API virtual bool active() = 0;
+};
+
+typedef std::shared_ptr<Room> RoomPtr;
+
+}
diff --git a/SDK/C++/public/include/voltu/source.hpp b/SDK/CPP/public/include/voltu/source.hpp
similarity index 100%
rename from SDK/C++/public/include/voltu/source.hpp
rename to SDK/CPP/public/include/voltu/source.hpp
diff --git a/SDK/CPP/public/include/voltu/system.hpp b/SDK/CPP/public/include/voltu/system.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a7e4cd96387a6e50b4933dad6892eb52104755d
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/system.hpp
@@ -0,0 +1,143 @@
+/**
+ * @file system.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+#include "defines.hpp"
+
+#include <voltu/room.hpp>
+#include <voltu/observer.hpp>
+#include <voltu/feed.hpp>
+#include <voltu/pipeline.hpp>
+#include <list>
+#include <string>
+
+namespace voltu
+{
+
+/**
+ * @brief Voltu semantic versioning information.
+ */
+struct Version
+{
+	int major;  ///< API Incompatible change
+	int minor;	///< Possible binary incompatible, extensions
+	int patch;	///< Internal only fixes.
+};
+
+/**
+ * @brief Singleton Voltu system instance.
+ * 
+ * Provides access to the key components such as opening streams or files and
+ * creating virtual cameras. Use `voltu::instance()` to obtain the object. All
+ * object instances in VolTu are managed by shared smart pointers.
+ */
+class System
+{
+public:
+	virtual ~System() = default;
+	
+	/**
+	 * @brief Get the runtime version information.
+	 * 
+	 * This method gets the VolTu version of the runtime shared library, which
+	 * may not be the same as the version of the SDK here.
+	 * 
+	 * @see voltu.hpp
+	 * 
+	 * @return Always returns semantic versioning structure.
+	 */
+	virtual voltu::Version getVersion() const = 0;
+
+	/**
+	 * @brief Make a virtual room or composite room.
+	 * 
+	 * @return A new virtual room instance.
+	 */
+	PY_API virtual voltu::RoomPtr createRoom() = 0;
+
+	/**
+	 * @brief Create a virtual observer.
+	 * 
+	 * An observer renderers virtual camera views, audio and other data from
+	 * submitted framesets. It is possible and recommended that a single
+	 * observer instance be used to renderer multiple different views, rather
+	 * than creating lots of observers. This saves memory resources.
+	 * 
+	 * @return A new observer instance.
+	 */
+	PY_API virtual voltu::ObserverPtr createObserver() = 0;
+
+	/**
+	 * @brief Open a file, device or network stream using a URI.
+	 * 
+	 * All data sources in VolTu are represented by Universal Resource
+	 * Identifiers (URIs), with some non-standard additions. A few examples
+	 * are:
+	 * * `file:///home/user/file.ftl`
+	 * * `tcp://localhost:9001/*`
+	 * * `ftl://my.stream.name/room1`
+	 * * `ws://ftlab.utu.fi/lab/`
+	 * * `./file.ftl`
+	 * * `device:camera`
+	 * * `device:screen`
+	 * 
+	 * Note that returning from this call does not guarantee that the source
+	 * is fully open and operational, this depends on network handshakes or
+	 * file processing that occurs asynchronously.
+	 * 
+	 * @throw voltu::exceptions::BadSourceURI If an unrecognised URI is given.
+	 * @return A feed management object for the data source.
+	 */
+	PY_API virtual voltu::FeedPtr open(const std::string &uri) = 0;
+
+	/**
+	 * @brief Get a list of all available rooms.
+	 * 
+	 * A room is a 3D captured physical space, or a combination of such spaces,
+	 * and is represented by a unique identifier within the local system. This
+	 * method obtains a list of all available rooms from all sources. To obtain
+	 * rooms, either use `open` or `createRoom`.
+	 * 
+	 * @return A list of room ids, which can be empty.
+	 * 
+	 * @see getRoom
+	 * @see open
+	 */
+	PY_API virtual std::list<voltu::RoomId> listRooms() = 0;
+
+	/**
+	 * @brief Get a room instance from identifier.
+	 * 
+	 * A room instance enables access to all data frames associated with that
+	 * room, including image data. Calling `getRoom` with the same ID
+	 * multiple times will return different smart pointers to room instances
+	 * but provides access to the same data regardless and is valid. An invalid
+	 * room ID will throw an exception.
+	 * 
+	 * @throw voltu::exceptions::InvalidRoomId If the ID does not exist.
+	 * @return Room instance or accessing room data.
+	 */
+	PY_API virtual voltu::RoomPtr getRoom(voltu::RoomId room) = 0;
+
+	/** Make a file or streaming feed, to which you can send frames. */
+	PY_API virtual voltu::FeedPtr createFeed(const std::string &uri) = 0;
+
+	/**
+	 * @brief Make an empty operator pipeline for frame processing.
+	 * 
+	 * A pipeline allows a sequence of processing operations to be applied to
+	 * a data frame. These operations include stereo correspondence, fusion,
+	 * data evaluation and various image processing operations. Only some
+	 * of these operators are exposed in the SDK. Once a pipeline instance
+	 * is obtained, you can add specific operators to it, configure them and
+	 * then submit frames from processing.
+	 * 
+	 * @return A unique pipeline instance.
+	 */
+	PY_API virtual voltu::PipelinePtr createPipeline() = 0;
+};
+
+}  // namespace voltu
diff --git a/SDK/C++/public/include/voltu/types/channel.hpp b/SDK/CPP/public/include/voltu/types/channel.hpp
similarity index 51%
rename from SDK/C++/public/include/voltu/types/channel.hpp
rename to SDK/CPP/public/include/voltu/types/channel.hpp
index 07b7c9abadf3caaa36e1fcd8d01f08d496d1a5b0..e9e86324e565c22b65f31e41753c5c80113d70aa 100644
--- a/SDK/C++/public/include/voltu/types/channel.hpp
+++ b/SDK/CPP/public/include/voltu/types/channel.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file channel.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 namespace voltu {
diff --git a/SDK/CPP/public/include/voltu/types/errors.hpp b/SDK/CPP/public/include/voltu/types/errors.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b1e77accc50f9975698db4f61be11a5dd7b1c330
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/types/errors.hpp
@@ -0,0 +1,52 @@
+/**
+ * @file errors.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+
+#include <exception>
+
+namespace voltu
+{
+namespace exceptions
+{
+
+struct Exception : public std::exception
+{
+	virtual const char* what() const noexcept { return "VolTu General Error"; }
+};
+
+}
+}
+
+#define VOLTU_EXCEPTION(NAME,BASE,MSG) struct NAME : public voltu::exceptions::BASE { virtual const char* what() const noexcept { return MSG; } };
+
+namespace voltu
+{
+namespace exceptions
+{
+
+VOLTU_EXCEPTION(BadImageChannel, Exception, "Invalid image channel");
+VOLTU_EXCEPTION(NoFrame, Exception, "No frame available");
+VOLTU_EXCEPTION(AlreadyInit, Exception, "VolTu already initialised");
+VOLTU_EXCEPTION(LibraryLoadFailed, Exception, "Could not load VolTu library");
+VOLTU_EXCEPTION(RuntimeVersionMismatch, Exception, "Wrong version of runtime found");
+VOLTU_EXCEPTION(RuntimeAlreadyInUse, Exception, "VolTu runtime already in use");
+VOLTU_EXCEPTION(BadSourceURI, Exception, "Bad source URI");
+VOLTU_EXCEPTION(InvalidFrameObject, Exception, "Invalid Frame object");
+VOLTU_EXCEPTION(InternalRenderError, Exception, "Internal renderer error");
+VOLTU_EXCEPTION(InvalidRoomId, Exception, "Room identifier does not exist");
+VOLTU_EXCEPTION(PropertyUnavailable, Exception, "Property currently not available");
+VOLTU_EXCEPTION(BadPropertyName, Exception, "Not a valid property name");
+VOLTU_EXCEPTION(BadPropertyType, Exception, "Incorrect property data type");
+VOLTU_EXCEPTION(BadPropertyValue, Exception, "Property value out of allowed range");
+VOLTU_EXCEPTION(BadParameterValue, Exception, "Method parameter is not valid");
+VOLTU_EXCEPTION(NotImplemented, Exception, "Functionality not implemented");
+VOLTU_EXCEPTION(ReadOnly, Exception, "Read only, write not allowed");
+VOLTU_EXCEPTION(WriteOnly, Exception, "Write only, read not allowed");
+VOLTU_EXCEPTION(IncompatibleOperation, Exception, "The input data and operator are incompatible");
+
+}
+}
diff --git a/SDK/C++/public/include/voltu/types/frame.hpp b/SDK/CPP/public/include/voltu/types/frame.hpp
similarity index 74%
rename from SDK/C++/public/include/voltu/types/frame.hpp
rename to SDK/CPP/public/include/voltu/types/frame.hpp
index adbd84c260984a52e43e25d185120341da5aa093..4209cc80c2d1af4e46c4c1d7227ad0e3a9918c79 100644
--- a/SDK/C++/public/include/voltu/types/frame.hpp
+++ b/SDK/CPP/public/include/voltu/types/frame.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file frame.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 #include "../defines.hpp"
 
@@ -14,10 +20,14 @@ namespace voltu
 class Frame
 {
 public:
+	virtual ~Frame() = default;
+	
 	PY_API PY_RV_LIFETIME_PARENT virtual std::list<voltu::ImagePtr> getImageSet(voltu::Channel channel) = 0;
 
 	PY_API PY_RV_LIFETIME_PARENT virtual voltu::PointCloudPtr getPointCloud(voltu::PointCloudFormat cloudfmt, voltu::PointFormat pointfmt) = 0;
 
+	PY_API virtual std::vector<std::string> getMessages() = 0;
+
 	PY_API virtual int64_t getTimestamp() = 0;
 };
 
diff --git a/SDK/C++/public/include/voltu/types/image.hpp b/SDK/CPP/public/include/voltu/types/image.hpp
similarity index 86%
rename from SDK/C++/public/include/voltu/types/image.hpp
rename to SDK/CPP/public/include/voltu/types/image.hpp
index 76d5ec5f099223e36d49cdc6dce4f9435d64d8aa..c9c342f27c13cbc595bcd56f89ca18676dba0689 100644
--- a/SDK/C++/public/include/voltu/types/image.hpp
+++ b/SDK/CPP/public/include/voltu/types/image.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file image.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 #include "../defines.hpp"
@@ -15,7 +21,8 @@ enum class ImageFormat
 {
 	kInvalid = 0,
 	kFloat32 = 1,
-	kBGRA8 = 2
+	kBGRA8 = 2,
+	kFloat16_4 = 3
 };
 
 PY_NO_SHARED_PTR struct ImageData
@@ -30,6 +37,8 @@ PY_NO_SHARED_PTR struct ImageData
 class Image
 {
 public:
+	virtual ~Image() = default;
+
 	PY_API PY_RV_LIFETIME_PARENT virtual ImageData getHost() = 0;
 
 	virtual ImageData getDevice() = 0;
diff --git a/SDK/C++/public/include/voltu/types/intrinsics.hpp b/SDK/CPP/public/include/voltu/types/intrinsics.hpp
similarity index 71%
rename from SDK/C++/public/include/voltu/types/intrinsics.hpp
rename to SDK/CPP/public/include/voltu/types/intrinsics.hpp
index 70d1dfe2c2f3ba6f8138e9c4dbb1ac618ae25187..cc4ee4caa188064a72ae0aad8a631ebfd4227890 100644
--- a/SDK/C++/public/include/voltu/types/intrinsics.hpp
+++ b/SDK/CPP/public/include/voltu/types/intrinsics.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file intrinsics.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 namespace voltu
diff --git a/SDK/C++/public/include/voltu/types/pointcloud.hpp b/SDK/CPP/public/include/voltu/types/pointcloud.hpp
similarity index 84%
rename from SDK/C++/public/include/voltu/types/pointcloud.hpp
rename to SDK/CPP/public/include/voltu/types/pointcloud.hpp
index c602a4f6a007b2b2b123bc263f766665e35b8af7..8ea53712af2771ccffcf9d608d74b9e97a25c27e 100644
--- a/SDK/C++/public/include/voltu/types/pointcloud.hpp
+++ b/SDK/CPP/public/include/voltu/types/pointcloud.hpp
@@ -1,3 +1,9 @@
+/**
+ * @file pointcloud.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 #include <memory>
diff --git a/SDK/C++/public/include/voltu/types/property.hpp b/SDK/CPP/public/include/voltu/types/property.hpp
similarity index 80%
rename from SDK/C++/public/include/voltu/types/property.hpp
rename to SDK/CPP/public/include/voltu/types/property.hpp
index 2fa3c43b44ab95503b1cca587ef4aab8cd1cf66b..f2f781b9486092cf7d602850155bbb1602f022d4 100644
--- a/SDK/C++/public/include/voltu/types/property.hpp
+++ b/SDK/CPP/public/include/voltu/types/property.hpp
@@ -1,7 +1,14 @@
+/**
+ * @file property.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 #include "../defines.hpp"
 #include <memory>
+#include <string>
 
 namespace voltu
 {
@@ -9,6 +16,8 @@ namespace voltu
 class Property
 {
 public:
+	virtual ~Property() = default;
+
 	PY_API virtual void setInt(int) = 0;
 
 	PY_API virtual void setFloat(float) = 0;
diff --git a/SDK/C++/public/include/voltu/voltu.hpp b/SDK/CPP/public/include/voltu/voltu.hpp
similarity index 66%
rename from SDK/C++/public/include/voltu/voltu.hpp
rename to SDK/CPP/public/include/voltu/voltu.hpp
index acb2836f45b5c643465bf20a999795555d8225de..a2c4c0a142341a9c145999bc1cadd530271c25b7 100644
--- a/SDK/C++/public/include/voltu/voltu.hpp
+++ b/SDK/CPP/public/include/voltu/voltu.hpp
@@ -1,8 +1,14 @@
+/**
+ * @file voltu.hpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #pragma once
 
 // Bump these for each release
 #define VOLTU_VERSION_MAJOR 0    // For API incompatible changes
-#define VOLTU_VERSION_MINOR 1    // For binary compatibility and extensions
+#define VOLTU_VERSION_MINOR 2    // For binary compatibility and extensions
 #define VOLTU_VERSION_PATCH 0    // Binary compatible internal fixes
 
 #define VOLTU_VERSION ((VOLTU_VERSION_MAJOR*10000) + (VOLTU_VERSION_MINOR*100) + VOLTU_VERSION_PATCH)
diff --git a/SDK/C++/public/python/CMakeLists.txt b/SDK/CPP/public/python/CMakeLists.txt
similarity index 100%
rename from SDK/C++/public/python/CMakeLists.txt
rename to SDK/CPP/public/python/CMakeLists.txt
diff --git a/SDK/C++/public/python/CppHeaderParser/CppHeaderParser.py b/SDK/CPP/public/python/CppHeaderParser/CppHeaderParser.py
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/CppHeaderParser.py
rename to SDK/CPP/public/python/CppHeaderParser/CppHeaderParser.py
diff --git a/SDK/C++/public/python/CppHeaderParser/LICENSE.txt b/SDK/CPP/public/python/CppHeaderParser/LICENSE.txt
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/LICENSE.txt
rename to SDK/CPP/public/python/CppHeaderParser/LICENSE.txt
diff --git a/SDK/C++/public/python/CppHeaderParser/README.rst b/SDK/CPP/public/python/CppHeaderParser/README.rst
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/README.rst
rename to SDK/CPP/public/python/CppHeaderParser/README.rst
diff --git a/SDK/C++/public/python/CppHeaderParser/__init__.py b/SDK/CPP/public/python/CppHeaderParser/__init__.py
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/__init__.py
rename to SDK/CPP/public/python/CppHeaderParser/__init__.py
diff --git a/SDK/C++/public/python/CppHeaderParser/doxygen.py b/SDK/CPP/public/python/CppHeaderParser/doxygen.py
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/doxygen.py
rename to SDK/CPP/public/python/CppHeaderParser/doxygen.py
diff --git a/SDK/C++/public/python/CppHeaderParser/lexer.py b/SDK/CPP/public/python/CppHeaderParser/lexer.py
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/lexer.py
rename to SDK/CPP/public/python/CppHeaderParser/lexer.py
diff --git a/SDK/C++/public/python/CppHeaderParser/tojson.py b/SDK/CPP/public/python/CppHeaderParser/tojson.py
similarity index 100%
rename from SDK/C++/public/python/CppHeaderParser/tojson.py
rename to SDK/CPP/public/python/CppHeaderParser/tojson.py
diff --git a/SDK/C++/public/python/README.md b/SDK/CPP/public/python/README.md
similarity index 100%
rename from SDK/C++/public/python/README.md
rename to SDK/CPP/public/python/README.md
diff --git a/SDK/C++/public/python/automatic_bindings.cpp.in b/SDK/CPP/public/python/automatic_bindings.cpp.in
similarity index 100%
rename from SDK/C++/public/python/automatic_bindings.cpp.in
rename to SDK/CPP/public/python/automatic_bindings.cpp.in
diff --git a/SDK/C++/public/python/examples/example1.py b/SDK/CPP/public/python/examples/example1.py
similarity index 100%
rename from SDK/C++/public/python/examples/example1.py
rename to SDK/CPP/public/python/examples/example1.py
diff --git a/SDK/C++/public/python/examples/example2.py b/SDK/CPP/public/python/examples/example2.py
similarity index 100%
rename from SDK/C++/public/python/examples/example2.py
rename to SDK/CPP/public/python/examples/example2.py
diff --git a/SDK/C++/public/python/gen.py b/SDK/CPP/public/python/gen.py
similarity index 100%
rename from SDK/C++/public/python/gen.py
rename to SDK/CPP/public/python/gen.py
diff --git a/SDK/C++/public/python/module.cpp b/SDK/CPP/public/python/module.cpp
similarity index 100%
rename from SDK/C++/public/python/module.cpp
rename to SDK/CPP/public/python/module.cpp
diff --git a/SDK/C++/public/python/module.hpp b/SDK/CPP/public/python/module.hpp
similarity index 100%
rename from SDK/C++/public/python/module.hpp
rename to SDK/CPP/public/python/module.hpp
diff --git a/SDK/C++/public/python/tests/__init__.py b/SDK/CPP/public/python/tests/__init__.py
similarity index 100%
rename from SDK/C++/public/python/tests/__init__.py
rename to SDK/CPP/public/python/tests/__init__.py
diff --git a/SDK/C++/public/python/tests/test_load.py b/SDK/CPP/public/python/tests/test_load.py
similarity index 100%
rename from SDK/C++/public/python/tests/test_load.py
rename to SDK/CPP/public/python/tests/test_load.py
diff --git a/SDK/C++/public/python/types/image.hpp b/SDK/CPP/public/python/types/image.hpp
similarity index 100%
rename from SDK/C++/public/python/types/image.hpp
rename to SDK/CPP/public/python/types/image.hpp
diff --git a/SDK/C++/public/samples/basic_file/main.cpp b/SDK/CPP/public/samples/basic_file/main.cpp
similarity index 100%
rename from SDK/C++/public/samples/basic_file/main.cpp
rename to SDK/CPP/public/samples/basic_file/main.cpp
diff --git a/SDK/C++/public/samples/basic_test/main.cpp b/SDK/CPP/public/samples/basic_test/main.cpp
similarity index 100%
rename from SDK/C++/public/samples/basic_test/main.cpp
rename to SDK/CPP/public/samples/basic_test/main.cpp
diff --git a/SDK/C++/public/samples/basic_virtual_cam/main.cpp b/SDK/CPP/public/samples/basic_virtual_cam/main.cpp
similarity index 100%
rename from SDK/C++/public/samples/basic_virtual_cam/main.cpp
rename to SDK/CPP/public/samples/basic_virtual_cam/main.cpp
diff --git a/SDK/CPP/public/samples/fusion_evaluator/main.cpp b/SDK/CPP/public/samples/fusion_evaluator/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..908d0925ed80bf5d4d125d6f184e6dc0d7a9f906
--- /dev/null
+++ b/SDK/CPP/public/samples/fusion_evaluator/main.cpp
@@ -0,0 +1,67 @@
+#include <voltu/voltu.hpp>
+#include <voltu/opencv.hpp>
+#include <iostream>
+#include <thread>
+#include <chrono>
+
+#include <opencv2/highgui.hpp>
+
+using std::cout;
+using std::endl;
+using std::string;
+
+int main(int argc, char **argv)
+{
+	if (argc != 2) return -1;
+
+	auto vtu = voltu::instance();
+
+	if (!vtu->open(argv[1]))
+	{
+		cout << "Could not open source" << endl;
+		return -1;
+	}
+
+	while (vtu->listRooms().size() == 0)
+	{
+		std::this_thread::sleep_for(std::chrono::milliseconds(100));
+	}
+
+	auto room = vtu->getRoom(vtu->listRooms().front());
+	if (!room)
+	{
+		cout << "Could not get room" << endl;
+		return -1;
+	}
+
+	auto frame = room->getFrame();
+
+	auto pipe = vtu->createPipeline();
+	auto op1 = pipe->appendOperator(voltu::OperatorId::kFusion);
+	auto op2 = pipe->appendOperator(voltu::OperatorId::kGTEvaluator);
+
+	op2->property("show_colour")->setBool(true);
+
+	pipe->submit(frame);
+	pipe->waitCompletion(1000);
+
+	auto imgset = frame->getImageSet(voltu::Channel::kColour);
+
+	for (auto img : imgset)
+	{
+		cv::Mat m;
+		voltu::cv::visualise(img, m);
+		cv::imshow(string("Image-") + img->getName(), m);
+		break;
+	}
+
+	std::vector<std::string> msgs = frame->getMessages();
+	for (const auto &s : msgs)
+	{
+		cout << s << endl;
+	}
+
+	cv::waitKey(-1);
+
+	return 0;
+}
diff --git a/SDK/C++/public/voltu.cpp b/SDK/CPP/public/voltu.cpp
similarity index 82%
rename from SDK/C++/public/voltu.cpp
rename to SDK/CPP/public/voltu.cpp
index 25b0f4019ccd8c5df7c3090b7c7ea3198162baef..bf63ed6b4c1f145f1a36547030e97c898359f78f 100644
--- a/SDK/C++/public/voltu.cpp
+++ b/SDK/CPP/public/voltu.cpp
@@ -1,3 +1,9 @@
+/**
+ * @file voltu.cpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #include <voltu/initialise.hpp>
 #include <voltu/types/errors.hpp>
 #include <voltu/voltu.hpp>
@@ -44,6 +50,7 @@ static void unloadLibrary(Library lib)
 
 static std::string locateLibrary()
 {
+	// TODO: Use full paths and find correct versions
 #if defined(WIN32)
 	return "voltu.dll";
 #else
@@ -70,13 +77,14 @@ std::shared_ptr<voltu::System> voltu::instance()
 
 			if (!instance)
 			{
-				throw voltu::exceptions::LibraryLoadFailed();
+				throw voltu::exceptions::RuntimeAlreadyInUse();
 			}
 
+			// FIXME: Perhaps use a C method instead for safety?
 			auto ver = instance->getVersion();
 			if (ver.major != VOLTU_VERSION_MAJOR || ver.minor != VOLTU_VERSION_MINOR)
 			{
-				throw voltu::exceptions::LibraryVersionMismatch();
+				throw voltu::exceptions::RuntimeVersionMismatch();
 			}
 
 			return instance;
diff --git a/SDK/C++/public/voltu_cv.cpp b/SDK/CPP/public/voltu_cv.cpp
similarity index 67%
rename from SDK/C++/public/voltu_cv.cpp
rename to SDK/CPP/public/voltu_cv.cpp
index 3b77155b6450560eb7808f7294013926fea382e7..31cd29aa6f85a77ee3baf23541d918ea5046b01a 100644
--- a/SDK/C++/public/voltu_cv.cpp
+++ b/SDK/CPP/public/voltu_cv.cpp
@@ -1,4 +1,11 @@
+/**
+ * @file voltu_cv.cpp
+ * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
+ * @author Nicolas Pope
+ */
+
 #include <voltu/opencv.hpp>
+#include <voltu/types/errors.hpp>
 
 #include <opencv2/imgproc.hpp>
 
@@ -14,6 +21,10 @@ void voltu::cv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
 	{
 		mat = ::cv::Mat(data.height, data.width, CV_32FC1, data.data);
 	}
+	else if (data.format == voltu::ImageFormat::kFloat16_4)
+	{
+		mat = ::cv::Mat(data.height, data.width, CV_16FC4, data.data);
+	}
 	else
 	{
 		mat = ::cv::Mat();
@@ -22,7 +33,7 @@ void voltu::cv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
 
 void voltu::cv::convert(voltu::ImagePtr img, ::cv::cuda::GpuMat &mat)
 {
-
+	throw voltu::exceptions::NotImplemented();
 }
 
 void voltu::cv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
@@ -47,4 +58,13 @@ void voltu::cv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
 		::cv::applyColorMap(tmp, mat, ::cv::COLORMAP_INFERNO);
 		//#endif
 	}
+	else if (data.format == voltu::ImageFormat::kFloat16_4)
+	{
+		::cv::Mat tmp;
+		voltu::cv::convert(img, tmp);
+		tmp.convertTo(tmp, CV_32FC4);
+		tmp += 1.0f;
+		tmp *= 127.0f;
+		tmp.convertTo(mat, CV_8UC4);
+	}
 }
diff --git a/components/codecs/include/ftl/codecs/packet.hpp b/components/codecs/include/ftl/codecs/packet.hpp
index 1b37d88b4a07c0351bfe9bf92f1d0e427f9eed2e..8f2359d5be4b77cf166ab797700831d17e730588 100644
--- a/components/codecs/include/ftl/codecs/packet.hpp
+++ b/components/codecs/include/ftl/codecs/packet.hpp
@@ -13,12 +13,14 @@ namespace codecs {
 static constexpr uint8_t kAllFrames = 255;
 static constexpr uint8_t kAllFramesets = 255;
 
+static constexpr uint8_t kCurrentFTLVersion = 5;
+
 /**
  * First bytes of our file format.
  */
 struct Header {
 	const char magic[4] = {'F','T','L','F'};
-	uint8_t version = 5;
+	uint8_t version = kCurrentFTLVersion;
 };
 
 /**
@@ -56,7 +58,7 @@ static constexpr unsigned int kStreamCap_NewConnection = 0x04;
 
 /** V4 packets have no stream flags field */
 struct StreamPacketV4 {
-	int version;			// FTL version, Not encoded into stream
+	int version=4;			// FTL version, Not encoded into stream
 
 	int64_t timestamp;
 	uint8_t streamID;  		// Source number [or v4 frameset id]
@@ -81,7 +83,7 @@ struct StreamPacketV4 {
  * or included before a frame packet structure.
  */
 struct StreamPacket {
-	int version;			// FTL version, Not encoded into stream
+	int version = kCurrentFTLVersion;	// FTL version, Not encoded into stream
 
 	int64_t timestamp;
 	uint8_t streamID;  		// Source number [or v4 frameset id]
diff --git a/components/codecs/src/opencv_decoder.cpp b/components/codecs/src/opencv_decoder.cpp
index b7f1a600ff2887330f9266cf8198b048d1770e73..6e094a0e5b1fe35af04eb651b327cf3c79d5bc3e 100644
--- a/components/codecs/src/opencv_decoder.cpp
+++ b/components/codecs/src/opencv_decoder.cpp
@@ -37,7 +37,11 @@ bool OpenCVDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out
 	if (tmp2_.type() == CV_8UC3) {
 		cv::cvtColor(tmp2_, tmp_, cv::COLOR_RGB2BGRA);
 	} else {
-		tmp_ = tmp2_;
+		if (pkt.flags & ftl::codecs::kFlagFlipRGB) {
+			cv::cvtColor(tmp2_, tmp_, cv::COLOR_RGBA2BGRA);
+		} else {
+			tmp_ = tmp2_;
+		}
 	}
 
 	// Apply colour correction to chunk
diff --git a/components/common/cpp/include/ftl/configurable.hpp b/components/common/cpp/include/ftl/configurable.hpp
index b2a66943d35e7cba6a86d6842e037d0068d15feb..3d2a115e670e977fc45e4b8fdb32ff055789dd6c 100644
--- a/components/common/cpp/include/ftl/configurable.hpp
+++ b/components/common/cpp/include/ftl/configurable.hpp
@@ -67,6 +67,11 @@ class Configurable {
 
 	std::string getID() { return *get<std::string>("$id"); }
 
+	template <typename T>
+	inline bool is(const std::string &name) { return false; }
+
+	bool has(const std::string &name) const;
+
 	/**
 	 * Get a configuration property, but return a default if not found.
 	 */
@@ -191,6 +196,20 @@ extern template void ftl::Configurable::set<std::string>(const std::string &name
 extern template void ftl::Configurable::set<std::vector<std::string>>(const std::string &name, std::vector<std::string> value);
 extern template void ftl::Configurable::set<nlohmann::json>(const std::string &name, nlohmann::json value);
 
+template <> bool ftl::Configurable::is<float>(const std::string &name);
+template <> bool ftl::Configurable::is<int>(const std::string &name);
+template <> bool ftl::Configurable::is<unsigned int>(const std::string &name);
+template <> bool ftl::Configurable::is<std::string>(const std::string &name);
+template <> bool ftl::Configurable::is<bool>(const std::string &name);
+template <> bool ftl::Configurable::is<double>(const std::string &name);
+
+extern template bool ftl::Configurable::is<float>(const std::string &name);
+extern template bool ftl::Configurable::is<int>(const std::string &name);
+extern template bool ftl::Configurable::is<unsigned int>(const std::string &name);
+extern template bool ftl::Configurable::is<std::string>(const std::string &name);
+extern template bool ftl::Configurable::is<bool>(const std::string &name);
+extern template bool ftl::Configurable::is<double>(const std::string &name);
+
 template <typename T, typename... ARGS>
 T *ftl::Configurable::create(const std::string &name, ARGS ...args) {
 	return ftl::config::create<T>(this, name, args...);
diff --git a/components/common/cpp/src/configurable.cpp b/components/common/cpp/src/configurable.cpp
index 1f377fc5185cdc533d8c5823514a3fb4bfda3e24..c5f6d9d868806dbe3dc87f64f1069d887bf26b01 100644
--- a/components/common/cpp/src/configurable.cpp
+++ b/components/common/cpp/src/configurable.cpp
@@ -121,6 +121,40 @@ template void ftl::Configurable::set<std::string>(const std::string &name, std::
 template void ftl::Configurable::set<std::vector<std::string>>(const std::string &name, std::vector<std::string> value);
 template void ftl::Configurable::set<nlohmann::json>(const std::string &name, nlohmann::json value);
 
+template <>
+bool ftl::Configurable::is<float>(const std::string &name) {
+	return getConfig()[name].is_number_float();
+}
+
+template <>
+bool ftl::Configurable::is<int>(const std::string &name) {
+	return getConfig()[name].is_number_integer();
+}
+
+template <>
+bool ftl::Configurable::is<unsigned int>(const std::string &name) {
+	return getConfig()[name].is_number_unsigned();
+}
+
+template <>
+bool ftl::Configurable::is<std::string>(const std::string &name) {
+	return getConfig()[name].is_string();
+}
+
+template <>
+bool ftl::Configurable::is<bool>(const std::string &name) {
+	return getConfig()[name].is_boolean();
+}
+
+template <>
+bool ftl::Configurable::is<double>(const std::string &name) {
+	return getConfig()[name].is_number_float();
+}
+
+bool ftl::Configurable::has(const std::string &name) const {
+	return (config_) ? config_->contains(name) : false;
+}
+
 void Configurable::required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r) {
 	bool diderror = false;
 	for (auto i : r) {
diff --git a/components/operators/include/ftl/operators/antialiasing.hpp b/components/operators/include/ftl/operators/antialiasing.hpp
index 5548c08578e96879f36f2c3a11b281b78ca4bf43..9c249f3d82a89aae0c4cdadba74e0079d69121d9 100644
--- a/components/operators/include/ftl/operators/antialiasing.hpp
+++ b/components/operators/include/ftl/operators/antialiasing.hpp
@@ -19,6 +19,8 @@ class FXAA : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/clipping.hpp b/components/operators/include/ftl/operators/clipping.hpp
index 25d8b76ff7bdbfc99717b01652497395e5ff7ea1..a45ba760ad2f444d1091a56d96e69acb7e921b38 100644
--- a/components/operators/include/ftl/operators/clipping.hpp
+++ b/components/operators/include/ftl/operators/clipping.hpp
@@ -19,6 +19,8 @@ class ClipScene : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/colours.hpp b/components/operators/include/ftl/operators/colours.hpp
index a54539a67f829a7d1e9aa6c6306bb76a707081fa..d6745d17c58f01e1db5ecffae723aa7d863d1165 100644
--- a/components/operators/include/ftl/operators/colours.hpp
+++ b/components/operators/include/ftl/operators/colours.hpp
@@ -15,6 +15,8 @@ class ColourChannels : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
     private:
     cv::cuda::GpuMat temp_;
 	cv::cuda::GpuMat rbuf_;
diff --git a/components/operators/include/ftl/operators/depth.hpp b/components/operators/include/ftl/operators/depth.hpp
index 14f46b12d12b3adf933bd3ea7bd67463b36f19f2..38596dcc012e8395020d390762ebae913c0b0762 100644
--- a/components/operators/include/ftl/operators/depth.hpp
+++ b/components/operators/include/ftl/operators/depth.hpp
@@ -18,6 +18,8 @@ class DepthBilateralFilter : public::ftl::operators::Operator {
 	inline ftl::operators::Operator::Type type() const override { return ftl::operators::Operator::Type::OneToOne; }
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	cv::cuda::GpuMat disp_int_;
 	cv::cuda::GpuMat disp_int_result_;
diff --git a/components/operators/include/ftl/operators/detectandtrack.hpp b/components/operators/include/ftl/operators/detectandtrack.hpp
index 0d8b063d34cc2a9059d6cabe54d6c25484015891..2341d2d40bf544621950743ebcadb89676ce4809 100644
--- a/components/operators/include/ftl/operators/detectandtrack.hpp
+++ b/components/operators/include/ftl/operators/detectandtrack.hpp
@@ -48,6 +48,8 @@ class DetectAndTrack : public ftl::operators::Operator {
 
 	void wait(cudaStream_t) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	protected:
 	bool init();
 
@@ -126,6 +128,8 @@ class ArUco : public ftl::operators::Operator {
 	ftl::codecs::Channel channel_in_;
 	ftl::codecs::Channel channel_out_;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	bool estimate_pose_;
 	float marker_size_;
diff --git a/components/operators/include/ftl/operators/disparity.hpp b/components/operators/include/ftl/operators/disparity.hpp
index 17993f24c5e1778957cc7f3f29762ccaa611b0cc..2e4365d8ce361d186aba42fcafeaebcb3ebc7dec 100644
--- a/components/operators/include/ftl/operators/disparity.hpp
+++ b/components/operators/include/ftl/operators/disparity.hpp
@@ -29,6 +29,8 @@ public:
 
 	bool isMemoryHeavy() const override { return true; }
 
+	static void configuration(ftl::Configurable*) {}
+
 private:
 	bool init();
 
@@ -54,6 +56,8 @@ class FixstarsSGM : public ftl::operators::Operator {
 
 	bool isMemoryHeavy() const override { return true; }
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	bool init();
 	bool updateParameters();
@@ -99,6 +103,8 @@ class DisparityBilateralFilter : public::ftl::operators::Operator {
 	inline Operator::Type type() const override { return Operator::Type::OneToOne; }
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	cv::Ptr<cv::cuda::DisparityBilateralFilter> filter_;
 	cv::cuda::GpuMat disp_int_;
@@ -121,6 +127,8 @@ class DisparityToDepth : public ftl::operators::Operator {
 	~DisparityToDepth() {};
 	inline Operator::Type type() const override { return Operator::Type::OneToOne; }
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
+
+	static void configuration(ftl::Configurable*) {}
 };
 
 /**
@@ -138,6 +146,8 @@ class DepthChannel : public ftl::operators::Operator {
 	bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::operators::Graph *pipe_;
 	std::vector<cv::cuda::GpuMat> rbuf_;
@@ -160,6 +170,8 @@ class OpticalFlowTemporalSmoothing : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	void _init(ftl::Configurable* cfg);
 	bool init();
diff --git a/components/operators/include/ftl/operators/filling.hpp b/components/operators/include/ftl/operators/filling.hpp
index ed2b3f39a20a949fa28dfe61232d44d15f56affe..c26b9ef80e7aa0705fb6877f06a1154ee8fc0078 100644
--- a/components/operators/include/ftl/operators/filling.hpp
+++ b/components/operators/include/ftl/operators/filling.hpp
@@ -19,6 +19,8 @@ class ScanFieldFill : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 class CrossSupportFill : public ftl::operators::Operator {
@@ -30,6 +32,8 @@ class CrossSupportFill : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/fusion.hpp b/components/operators/include/ftl/operators/fusion.hpp
index d9f2c76c1d707b764bf32e4ab668b688c314e175..0062f1b648f37c6fbd21a9068e8f6f6da35ab017 100644
--- a/components/operators/include/ftl/operators/fusion.hpp
+++ b/components/operators/include/ftl/operators/fusion.hpp
@@ -17,6 +17,8 @@ class Fusion : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::MLSMultiIntensity mls_;
 	std::vector<cv::cuda::GpuMat> weights_;
diff --git a/components/operators/include/ftl/operators/gt_analysis.hpp b/components/operators/include/ftl/operators/gt_analysis.hpp
index cea34c9dba5477012bd2a620ebbbe0e079fdea51..efe0b64e63b27b6b999f79baa10ecf18f68ad3a9 100644
--- a/components/operators/include/ftl/operators/gt_analysis.hpp
+++ b/components/operators/include/ftl/operators/gt_analysis.hpp
@@ -21,6 +21,8 @@ class GTAnalysis : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*);
+
 	private:
 	ftl::cuda::GTAnalysisData *output_;
 };
diff --git a/components/operators/include/ftl/operators/mask.hpp b/components/operators/include/ftl/operators/mask.hpp
index 7d877d6579c329f7af513840b7140f187dbf7eed..863e085c2518700954a0aaac365edd3e37f418c6 100644
--- a/components/operators/include/ftl/operators/mask.hpp
+++ b/components/operators/include/ftl/operators/mask.hpp
@@ -21,6 +21,8 @@ class DiscontinuityMask : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -35,6 +37,8 @@ class BorderMask : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -49,6 +53,8 @@ class DisplayMask : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -63,6 +69,8 @@ class CullDiscontinuity : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/mvmls.hpp b/components/operators/include/ftl/operators/mvmls.hpp
index 0c0d6f4afb9715814199304ca0b430ac094071c4..56aafac1fe779832f9f7b6e857a1d67063113fdd 100644
--- a/components/operators/include/ftl/operators/mvmls.hpp
+++ b/components/operators/include/ftl/operators/mvmls.hpp
@@ -15,6 +15,8 @@ class MultiViewMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	std::vector<ftl::cuda::TextureObject<float4>*> centroid_horiz_;
 	std::vector<ftl::cuda::TextureObject<float4>*> centroid_vert_;
diff --git a/components/operators/include/ftl/operators/normals.hpp b/components/operators/include/ftl/operators/normals.hpp
index c4d1a0190d27e9f307474eeed3f2234ac99e2e23..398b006c7e6286575bd9ed986b02f5b8d5a7fc73 100644
--- a/components/operators/include/ftl/operators/normals.hpp
+++ b/components/operators/include/ftl/operators/normals.hpp
@@ -19,6 +19,8 @@ class Normals : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -36,6 +38,8 @@ class NormalDot : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -51,6 +55,8 @@ class SmoothNormals : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::TextureObject<half4> temp_;
 
diff --git a/components/operators/include/ftl/operators/operator.hpp b/components/operators/include/ftl/operators/operator.hpp
index 3f77b6900e4456898076031b3d97ba5bae190f90..938f5e781ab8bd4435f7a80c7bca6c9e0afcceca 100644
--- a/components/operators/include/ftl/operators/operator.hpp
+++ b/components/operators/include/ftl/operators/operator.hpp
@@ -81,7 +81,7 @@ struct ConstructionHelperBase {
 
 template <typename T>
 struct ConstructionHelper : public ConstructionHelperBase {
-	explicit ConstructionHelper(ftl::Configurable *cfg) : ConstructionHelperBase(cfg) {}
+	explicit ConstructionHelper(ftl::Configurable *cfg) : ConstructionHelperBase(cfg) { T::configuration(cfg); }
 	~ConstructionHelper() {}
 	ftl::operators::Operator *make(Graph *g) override {
 		return new T(g, config);
@@ -92,6 +92,7 @@ template <typename T, typename... ARGS>
 struct ConstructionHelper2 : public ConstructionHelperBase {
 	explicit ConstructionHelper2(ftl::Configurable *cfg, ARGS... args) : ConstructionHelperBase(cfg) {
 		arguments_ = std::make_tuple(args...);
+		T::configuration(cfg);
 	}
 	~ConstructionHelper2() {}
 	ftl::operators::Operator *make(Graph *g) override {
diff --git a/components/operators/include/ftl/operators/opticalflow.hpp b/components/operators/include/ftl/operators/opticalflow.hpp
index 21d8bbb200383b85a4f0c99a1f35e5e2e7b51385..cea1e7ba44f6f7b2f670cef318eb1ced4fcf7b7b 100644
--- a/components/operators/include/ftl/operators/opticalflow.hpp
+++ b/components/operators/include/ftl/operators/opticalflow.hpp
@@ -20,6 +20,8 @@ class NVOpticalFlow : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	protected:
 	bool init();
 
diff --git a/components/operators/include/ftl/operators/poser.hpp b/components/operators/include/ftl/operators/poser.hpp
index 8d762f8ba2ee371342adb8e20b07d359211d081e..2a6a4d6eb276471b6791e5efd55248a02dfb1db0 100644
--- a/components/operators/include/ftl/operators/poser.hpp
+++ b/components/operators/include/ftl/operators/poser.hpp
@@ -24,6 +24,8 @@ class Poser : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	static bool get(const std::string &name, Eigen::Matrix4d &pose);
 	static bool get(const std::string &name, ftl::codecs::Shape3D &shape);
 	static std::list<ftl::codecs::Shape3D*> getAll(int32_t fsid);
diff --git a/components/operators/include/ftl/operators/segmentation.hpp b/components/operators/include/ftl/operators/segmentation.hpp
index ec2f9e9a6d3ea8d683a6139a237849c82b8fe44a..0c3a4fff7337876d3d1a005acb12a8193ab958d9 100644
--- a/components/operators/include/ftl/operators/segmentation.hpp
+++ b/components/operators/include/ftl/operators/segmentation.hpp
@@ -18,6 +18,8 @@ class CrossSupport : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -32,6 +34,8 @@ class VisCrossSupport : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/smoothing.hpp b/components/operators/include/ftl/operators/smoothing.hpp
index db231cdd714a786df1dedf7ca54de06d2005ae4e..d035d399a74a2ecec1fd0dd06e118b20a54f6802 100644
--- a/components/operators/include/ftl/operators/smoothing.hpp
+++ b/components/operators/include/ftl/operators/smoothing.hpp
@@ -21,6 +21,8 @@ class HFSmoother : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	cv::cuda::GpuMat temp_;
 	//ftl::rgbd::Frame frames_[4];
@@ -42,6 +44,8 @@ class SmoothChannel : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::TextureObject<uchar4> temp_[6];
 };
@@ -60,6 +64,8 @@ class SimpleMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::data::Frame temp_;
 };
@@ -77,6 +83,8 @@ class ColourMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::data::Frame temp_;
 };
@@ -120,6 +128,8 @@ class AggreMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::TextureObject<float4> centroid_horiz_;
 	ftl::cuda::TextureObject<float4> centroid_vert_;
@@ -144,6 +154,8 @@ class AdaptiveMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::data::Frame temp_;
 };
diff --git a/components/operators/include/ftl/operators/weighting.hpp b/components/operators/include/ftl/operators/weighting.hpp
index 8256a08cb23d03d42b9ef18a7107a858a34ebb8b..d829de145e7ac42f1c99d356fa2abf60c869b6d4 100644
--- a/components/operators/include/ftl/operators/weighting.hpp
+++ b/components/operators/include/ftl/operators/weighting.hpp
@@ -29,6 +29,8 @@ class PixelWeights : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 class CullWeight : public ftl::operators::Operator {
@@ -40,6 +42,8 @@ class CullWeight : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 class DegradeWeight : public ftl::operators::Operator {
@@ -51,6 +55,8 @@ class DegradeWeight : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/src/analysis/evaluation/gt_analysis.cpp b/components/operators/src/analysis/evaluation/gt_analysis.cpp
index d7f94d39946d58a877050c73371c178436a9e6f9..c9559084eb53da55e13c8c4b22ccb221fcd9cb3f 100644
--- a/components/operators/src/analysis/evaluation/gt_analysis.cpp
+++ b/components/operators/src/analysis/evaluation/gt_analysis.cpp
@@ -9,6 +9,11 @@ GTAnalysis::GTAnalysis(ftl::operators::Graph *g, ftl::Configurable *cfg) : ftl::
 	cudaMalloc(&output_, sizeof(ftl::cuda::GTAnalysisData));
 }
 
+void GTAnalysis::configuration(ftl::Configurable *cfg) {
+	cfg->value("use_disparity", true);
+	cfg->value("show_colour", false);
+}
+
 GTAnalysis::~GTAnalysis() {
 	cudaFree(output_);
 }
diff --git a/components/streams/src/filestream.cpp b/components/streams/src/filestream.cpp
index 96159a54475f4863a011bb360a2903ed01977c0b..cddf769b5f0528ac285973292842a214cdadfc4a 100644
--- a/components/streams/src/filestream.cpp
+++ b/components/streams/src/filestream.cpp
@@ -174,6 +174,7 @@ bool File::readPacket(std::tuple<ftl::codecs::StreamPacket,ftl::codecs::Packet>
 
 				auto &spkt = std::get<0>(data);
 				auto &spktv4 = std::get<0>(datav4);
+				spkt.version = 4;
 				spkt.streamID = spktv4.streamID;
 				spkt.channel = spktv4.channel;
 				spkt.frame_number = spktv4.frame_number;
@@ -213,7 +214,7 @@ void File::_patchPackets(ftl::codecs::StreamPacket &spkt, ftl::codecs::Packet &p
 		if (codec == ftl::codecs::codec_t::HEVC) pkt.codec = ftl::codecs::codec_t::HEVC_LOSSLESS;
 	}
 
-	spkt.version = 5;
+	spkt.version = ftl::codecs::kCurrentFTLVersion;
 
 	// Fix for flags corruption
 	if (pkt.data.size() == 0) {