diff --git a/SDK/CPP/private/frame_impl.cpp b/SDK/CPP/private/frame_impl.cpp
index a91ad3e6a762cf18f9915299df616412e420e2b7..059b508fff3e7e7e3b40877685411ad890f0887f 100644
--- a/SDK/CPP/private/frame_impl.cpp
+++ b/SDK/CPP/private/frame_impl.cpp
@@ -15,9 +15,9 @@ FrameImpl::~FrameImpl()
 
 }
 
-std::list<voltu::ImagePtr> FrameImpl::getImageSet(voltu::Channel c)
+std::vector<voltu::ImagePtr> FrameImpl::getImageSet(voltu::Channel c)
 {
-	std::list<voltu::ImagePtr> result;
+	std::vector<voltu::ImagePtr> result;
 	ftl::codecs::Channel channel = ftl::codecs::Channel::Colour;
 
 	switch (c)
diff --git a/SDK/CPP/private/frame_impl.hpp b/SDK/CPP/private/frame_impl.hpp
index eb5caa5a65b873cc8f80718a1193051abb8fa26a..38750ce4269b9bdaf67df41ba76f4f07a3d15233 100644
--- a/SDK/CPP/private/frame_impl.hpp
+++ b/SDK/CPP/private/frame_impl.hpp
@@ -16,7 +16,7 @@ public:
 	FrameImpl();
 	~FrameImpl() override;
 
-	std::list<voltu::ImagePtr> getImageSet(voltu::Channel) override;
+	std::vector<voltu::ImagePtr> getImageSet(voltu::Channel) override;
 
 	voltu::PointCloudPtr getPointCloud(voltu::PointCloudFormat cloudfmt, voltu::PointFormat pointfmt) override;
 
diff --git a/SDK/CPP/public/CMakeLists.txt b/SDK/CPP/public/CMakeLists.txt
index f1ea62a4e8a428581f6a4a7f73915d7413ac5c66..e3d1c339a1e7852536c9a88a039a007f93e5cf77 100644
--- a/SDK/CPP/public/CMakeLists.txt
+++ b/SDK/CPP/public/CMakeLists.txt
@@ -43,6 +43,10 @@ endif()
 
 add_library(voltu_sdk STATIC ${VOLTU_SRCS})
 
+if (WITH_OPENCV)
+	target_compile_definitions(voltu_sdk PUBLIC WITH_OPENCV)
+endif()
+
 target_include_directories(voltu_sdk
 	PUBLIC include)
 target_link_libraries(voltu_sdk ${OS_LIBS} Threads::Threads ${OPTIONAL_DEPENDENCIES} Eigen3::Eigen)
diff --git a/SDK/CPP/public/include/voltu/cuda.hpp b/SDK/CPP/public/include/voltu/cuda.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..972f4964b839db3db3793d117e5fb80c0c7702ae
--- /dev/null
+++ b/SDK/CPP/public/include/voltu/cuda.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <voltu/types/image.hpp>
+#include 
+#include <memory>
+
+namespace voltu
+{
+
+/**
+ * @brief CUDA Processing Stream.
+ * 
+ * An instance of this class is mapped to a single CUDA stream, so any of the
+ * available operations will occur within that stream. It is therefore
+ * necessary to call `waitCompletion` after all steps have been finished.
+ */
+class CUDAProc
+{
+public:
+	virtual bool waitCompletion(int timeout, bool except=false) = 0;
+
+	virtual void* getInternalStream() = 0;
+
+	virtual void visualiseDepthEnhancement(const voltu::ImagePtr &gt, const voltu::ImagePtr &depth_old, const voltu::ImagePtr &depth_new, const voltu::ImagePtr &colour) = 0;
+};
+
+}
diff --git a/SDK/CPP/public/include/voltu/opencv.hpp b/SDK/CPP/public/include/voltu/opencv.hpp
index 7119cbc87097ce1609a7e93696059db38b7097e3..1e8e858ab0eef22dfccc892f0a1b2124056f482e 100644
--- a/SDK/CPP/public/include/voltu/opencv.hpp
+++ b/SDK/CPP/public/include/voltu/opencv.hpp
@@ -7,19 +7,29 @@
 #pragma once
 
 #include <opencv2/core/mat.hpp>
-#include <opencv2/core/cuda_types.hpp>
+#include <opencv2/core/cuda.hpp>
 #include <voltu/types/image.hpp>
 
 namespace voltu
 {
-namespace cv
+namespace opencv
 {
 
 void convert(voltu::ImagePtr img, ::cv::Mat &mat);
 
 void convert(voltu::ImagePtr img, ::cv::cuda::GpuMat &mat);
 
+::cv::cuda::GpuMat toGpuMat(voltu::ImagePtr img);
+
 void visualise(voltu::ImagePtr img, ::cv::Mat &mat);
 
 }
+
+struct GpuUtilities
+{
+	void (*visualiseDepthEnhancement)(const voltu::ImagePtr &gt, const voltu::ImagePtr &depth_old, const voltu::ImagePtr &depth_new, const voltu::ImagePtr &colour) = nullptr;
+};
+
+extern GpuUtilities gpu;
+
 }
\ No newline at end of file
diff --git a/SDK/CPP/public/include/voltu/system.hpp b/SDK/CPP/public/include/voltu/system.hpp
index 8fb9c744cdf3c7646531803f349aeb7455962c12..df7b4938ebd8103903b852f962fa6c23c1f5d255 100644
--- a/SDK/CPP/public/include/voltu/system.hpp
+++ b/SDK/CPP/public/include/voltu/system.hpp
@@ -78,7 +78,7 @@ public:
 	 * Identifiers (URIs), with some non-standard additions. A few examples
 	 * are:
 	 * * `file:///home/user/file.ftl`
-	 * * `tcp://localhost:9001/*`
+	 * * `tcp://localhost:9001/`
 	 * * `ftl://my.stream.name/room1`
 	 * * `ws://ftlab.utu.fi/lab/`
 	 * * `./file.ftl`
diff --git a/SDK/CPP/public/include/voltu/types/frame.hpp b/SDK/CPP/public/include/voltu/types/frame.hpp
index 30b7c4ddcf26c22256700311457abaf6755b8ef6..7f03fc6045ec8a378fb9feb174e956c59cc4adff 100644
--- a/SDK/CPP/public/include/voltu/types/frame.hpp
+++ b/SDK/CPP/public/include/voltu/types/frame.hpp
@@ -22,7 +22,7 @@ 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 std::vector<voltu::ImagePtr> getImageSet(voltu::Channel channel) = 0;
 
 	PY_API PY_RV_LIFETIME_PARENT virtual voltu::PointCloudPtr getPointCloud(voltu::PointCloudFormat cloudfmt, voltu::PointFormat pointfmt) = 0;
 
diff --git a/SDK/CPP/public/samples/basic_file/main.cpp b/SDK/CPP/public/samples/basic_file/main.cpp
index c983864275a217fc1d2cc6628012dc698f8ad19e..9af26c1c0859e609aaabf687dfeb119e9f625f80 100644
--- a/SDK/CPP/public/samples/basic_file/main.cpp
+++ b/SDK/CPP/public/samples/basic_file/main.cpp
@@ -45,7 +45,7 @@ int main(int argc, char **argv)
 		for (auto img : imgset)
 		{
 			cv::Mat m;
-			voltu::cv::visualise(img, m);
+			voltu::opencv::visualise(img, m);
 			cv::imshow(string("Image-") + img->getName(), m);
 		}
 
diff --git a/SDK/CPP/public/samples/basic_virtual_cam/main.cpp b/SDK/CPP/public/samples/basic_virtual_cam/main.cpp
index f1b2634b7135bfb3c303fdd6fd54e81da6a69a38..47dea0583709dbf339e32ca7e96f2eee8e7e78db 100644
--- a/SDK/CPP/public/samples/basic_virtual_cam/main.cpp
+++ b/SDK/CPP/public/samples/basic_virtual_cam/main.cpp
@@ -69,7 +69,7 @@ int main(int argc, char **argv)
 		for (auto img : imgset)
 		{
 			cv::Mat m;
-			voltu::cv::convert(img, m);
+			voltu::opencv::convert(img, m);
 			cv::imshow(string("Camera-") + img->getName(), m);
 		}
 
diff --git a/SDK/CPP/public/samples/fusion_evaluator/main.cpp b/SDK/CPP/public/samples/fusion_evaluator/main.cpp
index c4073baf4044d72a1346876047f8cfad317a7a06..f9b96468c292883118559a1542ca0b833d4a0e20 100644
--- a/SDK/CPP/public/samples/fusion_evaluator/main.cpp
+++ b/SDK/CPP/public/samples/fusion_evaluator/main.cpp
@@ -107,6 +107,10 @@ int main(int argc, char **argv)
 	op1->property("visibility_carving")->setBool(do_carving);
 	op1->property("mls_iterations")->setInt(iters);
 
+	cv::Mat old_depth;
+	auto oldimgset = frame->getImageSet(voltu::Channel::kDepth);
+	voltu::opencv::toGpuMat(oldimgset[sourceno]).download(old_depth);
+
 	pipe->submit(frame);
 	pipe->waitCompletion(3000, true);
 
@@ -123,7 +127,8 @@ int main(int argc, char **argv)
 	{
 		if (srccount++ < sourceno) continue;
 		cv::Mat m;
-		voltu::cv::visualise(img, m);
+		voltu::opencv::toGpuMat(img).download(m);
+		voltu::opencv::visualise(img, m);
 		cv::imshow(string("Image-") + img->getName(), m);
 		break;
 	}
diff --git a/SDK/CPP/public/voltu.cpp b/SDK/CPP/public/voltu.cpp
index 2c4d19712428be34af7d8fd04c93a7c615fdec7b..d558ff7b0897ab96cb58b55e115a21ebe42980d3 100644
--- a/SDK/CPP/public/voltu.cpp
+++ b/SDK/CPP/public/voltu.cpp
@@ -8,6 +8,10 @@
 #include <voltu/types/errors.hpp>
 #include <voltu/voltu.hpp>
 
+#ifdef WITH_OPENCV
+#include <voltu/opencv.hpp>
+#endif
+
 #if defined(WIN32)
 #include <windows.h>
 #pragma comment(lib, "User32.lib")
@@ -23,6 +27,10 @@
 
 static bool g_init = false;
 
+#ifdef WITH_OPENCV
+voltu::GpuUtilities voltu::gpu;
+#endif
+
 typedef void* Library;
 
 static Library loadLibrary(const char *file)
@@ -132,6 +140,18 @@ std::shared_ptr<voltu::System> voltu::instance()
 				throw voltu::exceptions::RuntimeVersionMismatch();
 			}
 
+#ifdef WITH_OPENCV
+			auto gpuinit = (voltu::GpuUtilities* (*)())getFunction(handle, "voltu_utilities_gpu");
+			if (gpuinit)
+			{
+				gpu = *gpuinit();
+			}
+			else
+			{
+				//throw voltu::exceptions::LibraryLoadFailed();	
+			}
+#endif
+
 			return instance;
 		}
 		else
diff --git a/SDK/CPP/public/voltu_cv.cpp b/SDK/CPP/public/voltu_cv.cpp
index e9951dc37959b0ea89b22c1d2f6432690619f4ef..5e55e79086a96dc599d209944f5c3ccff9170776 100644
--- a/SDK/CPP/public/voltu_cv.cpp
+++ b/SDK/CPP/public/voltu_cv.cpp
@@ -9,7 +9,7 @@
 
 #include <opencv2/imgproc.hpp>
 
-void voltu::cv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
+void voltu::opencv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
 {
 	voltu::ImageData data = img->getHost();
 
@@ -31,23 +31,44 @@ void voltu::cv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
 	}
 }
 
-void voltu::cv::convert(voltu::ImagePtr img, ::cv::cuda::GpuMat &mat)
+void voltu::opencv::convert(voltu::ImagePtr img, ::cv::cuda::GpuMat &mat)
 {
+	mat = voltu::opencv::toGpuMat(img);
+}
+
+cv::cuda::GpuMat voltu::opencv::toGpuMat(voltu::ImagePtr img)
+{
+	voltu::ImageData data = img->getDevice();
+
+	if (data.format == voltu::ImageFormat::kBGRA8)
+	{
+
+	}
+	else if (data.format == voltu::ImageFormat::kFloat32)
+	{
+		return ::cv::cuda::GpuMat(
+			data.height,
+			data.width,
+			CV_32F,
+			data.data
+		);
+	}
+
 	throw voltu::exceptions::NotImplemented();
 }
 
-void voltu::cv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
+void voltu::opencv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
 {
 	voltu::ImageData data = img->getHost();
 
 	if (data.format == voltu::ImageFormat::kBGRA8)
 	{
-		voltu::cv::convert(img, mat);
+		voltu::opencv::convert(img, mat);
 	}
 	else if (data.format == voltu::ImageFormat::kFloat32)
 	{
 		::cv::Mat tmp;
-		voltu::cv::convert(img, tmp);
+		voltu::opencv::convert(img, tmp);
 
 		float maxdepth = 8.0f;  // TODO: Get from intrinsics
 
@@ -65,7 +86,7 @@ void voltu::cv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
 	else if (data.format == voltu::ImageFormat::kFloat16_4)
 	{
 		::cv::Mat tmp;
-		voltu::cv::convert(img, tmp);
+		voltu::opencv::convert(img, tmp);
 		tmp.convertTo(tmp, CV_32FC4);
 		tmp += 1.0f;
 		tmp *= 127.0f;