diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp
index 4deb6256e622a1cd9283aeefe2e3520fc72023e3..18539c4e952d4cd77d22ffbfed18ce65eec9c4e6 100644
--- a/applications/reconstruct/src/main.cpp
+++ b/applications/reconstruct/src/main.cpp
@@ -309,9 +309,9 @@ static void run(ftl::Configurable *root) {
 	pipeline1->append<ftl::operators::Normals>("normals");  // Estimate surface normals
 	//pipeline1->append<ftl::operators::SmoothChannel>("smoothing");  // Generate a smoothing channel
 	//pipeline1->append<ftl::operators::ScanFieldFill>("filling");  // Generate a smoothing channel
-	pipeline1->append<ftl::operators::CrossSupport>("cross")->set("enabled", false);
+	pipeline1->append<ftl::operators::CrossSupport>("cross");
 	pipeline1->append<ftl::operators::ColourMLS>("mls");  // Perform MLS (using smoothing channel)
-	pipeline1->append<ftl::operators::VisCrossSupport>("viscross")->set("enabled", false);
+	pipeline1->append<ftl::operators::VisCrossSupport>("viscross");
 	// Alignment
 
 
diff --git a/components/codecs/include/ftl/codecs/channels.hpp b/components/codecs/include/ftl/codecs/channels.hpp
index 85cd9fffc45ddcda1b6cc13a160c74c30797b804..3a486cb32cb1c220737818affefbe9d0328ac20e 100644
--- a/components/codecs/include/ftl/codecs/channels.hpp
+++ b/components/codecs/include/ftl/codecs/channels.hpp
@@ -19,7 +19,7 @@ enum struct Channel : int {
     Deviation		= 4,
     Screen          = 4,
     Normals			= 5,	// 32FC4
-    Points			= 6,	// 32FC4
+    Points			= 6,	// 32FC4 (should be deprecated)
     Confidence		= 7,	// 32F
     Contribution	= 7,	// 32F
     EnergyVector	= 8,	// 32FC4
@@ -28,9 +28,9 @@ enum struct Channel : int {
     Energy			= 10,	// 32F
 	Mask			= 11,	// 32U
 	Density			= 12,	// 32F
-    LeftGray		= 13,	// Deprecated
-    RightGray		= 14,	// Deprecated
-    Overlay1		= 15,	// Unused
+	Support1		= 13,	// 8UC4 (currently)
+	Support2		= 14,	// 8UC4 (currently)
+    Segmentation	= 15,	// 32S?
 
 	AudioLeft		= 32,
 	AudioRight		= 33,
diff --git a/components/codecs/src/channels.cpp b/components/codecs/src/channels.cpp
index 25b06b533b5c95d16bd60365ccbcc4373e09bd45..effaf9eb261dfe460f2d6f5bf6f08849e4f93538 100644
--- a/components/codecs/src/channels.cpp
+++ b/components/codecs/src/channels.cpp
@@ -21,9 +21,9 @@ static ChannelInfo info[] = {
     "Energy", CV_32F,
 	"Mask", CV_32S,
 	"Density", CV_32F,
-    "LeftGray", CV_8U,
-    "RightGray", CV_8U,
-    "Overlay1", CV_8UC3,
+    "Support1", CV_8UC4,
+    "Support2", CV_8UC4,
+    "Segmentation", CV_32S,
 
 	"NoName", 0,
 	"NoName", 0,
diff --git a/components/common/cpp/include/ftl/configurable.hpp b/components/common/cpp/include/ftl/configurable.hpp
index 244b548285b92902e645436891b04acb182ffd08..219bd171edf4b7997cc35f97fa7787a0c4bb4559 100644
--- a/components/common/cpp/include/ftl/configurable.hpp
+++ b/components/common/cpp/include/ftl/configurable.hpp
@@ -104,7 +104,7 @@ class Configurable {
 	protected:
 	nlohmann::json *config_;
 
-	virtual void inject(const std::string name, nlohmann::json &value) {}
+	virtual void inject(const std::string &name, nlohmann::json &value) {}
 
 	private:
 	std::map<std::string, std::list<std::function<void(const config::Event&)>>> observers_; 
diff --git a/components/net/cpp/include/ftl/net_configurable.hpp b/components/net/cpp/include/ftl/net_configurable.hpp
index 3a31ecc27675f2e37b72bc42ee83da0dee5b7e4c..bdd21c4700e7664cff996672585054c30b6e8e6a 100644
--- a/components/net/cpp/include/ftl/net_configurable.hpp
+++ b/components/net/cpp/include/ftl/net_configurable.hpp
@@ -13,7 +13,7 @@ namespace ftl {
 	~NetConfigurable();
 
 	protected:
-	void inject(const std::string name, nlohmann::json &value);
+	void inject(const std::string &name, nlohmann::json &value);
 
     private:
 	ftl::UUID peer;
diff --git a/components/net/cpp/src/net_configurable.cpp b/components/net/cpp/src/net_configurable.cpp
index edcec65b8c4ba452472e30efd29b672b7a6e2c55..be98cf7edbb221c61e9732a9e59d8546f1185644 100644
--- a/components/net/cpp/src/net_configurable.cpp
+++ b/components/net/cpp/src/net_configurable.cpp
@@ -7,6 +7,6 @@ ftl::NetConfigurable::NetConfigurable(ftl::UUID peer, const std::string &suri, f
 
 ftl::NetConfigurable::~NetConfigurable(){}
 
-void ftl::NetConfigurable::inject(const std::string name, nlohmann::json &value) {
+void ftl::NetConfigurable::inject(const std::string &name, nlohmann::json &value) {
     ctrl.set(peer, suri + std::string("/") + name, value);
 }
diff --git a/components/operators/CMakeLists.txt b/components/operators/CMakeLists.txt
index 76e67de0209533bead6c6b065eb197e4eba3a858..ae282147dbbd3038d3dbe78981835748de0a81a3 100644
--- a/components/operators/CMakeLists.txt
+++ b/components/operators/CMakeLists.txt
@@ -17,7 +17,8 @@ set(OPERSRC
 )
 
 if (HAVE_OPTFLOW)
-	list(APPEND OPERSRC src/nvopticalflow.cpp
+	list(APPEND OPERSRC
+		src/nvopticalflow.cpp
 		src/disparity/optflow_smoothing.cu
 		src/disparity/optflow_smoothing.cpp)
 endif()
diff --git a/components/operators/include/ftl/operators/filling.hpp b/components/operators/include/ftl/operators/filling.hpp
index 1c06074e060dc7ed329829e7894b271f07256773..2825dde5f6840658d7c49dd374792e80952e9391 100644
--- a/components/operators/include/ftl/operators/filling.hpp
+++ b/components/operators/include/ftl/operators/filling.hpp
@@ -21,6 +21,17 @@ class ScanFieldFill : public ftl::operators::Operator {
 
 };
 
+class CrossSupportFill : public ftl::operators::Operator {
+	public:
+    explicit CrossSupportFill(ftl::Configurable*);
+    ~CrossSupportFill();
+
+	inline Operator::Type type() const override { return Operator::Type::OneToOne; }
+
+    bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *src, cudaStream_t stream) override;
+
+};
+
 }
 }
 
diff --git a/components/operators/include/ftl/operators/operator.hpp b/components/operators/include/ftl/operators/operator.hpp
index 04f542b3b1b088b53adfdb79cb012d9fb3e89e9c..729092dd10a96418e6ec4b3cf0073f82238b455d 100644
--- a/components/operators/include/ftl/operators/operator.hpp
+++ b/components/operators/include/ftl/operators/operator.hpp
@@ -56,7 +56,7 @@ class Operator {
 namespace detail {
 
 struct ConstructionHelperBase {
-	ConstructionHelperBase(ftl::Configurable *cfg) : config(cfg) {}
+	explicit ConstructionHelperBase(ftl::Configurable *cfg) : config(cfg) {}
 	virtual ~ConstructionHelperBase() {}
 	virtual ftl::operators::Operator *make()=0;
 
@@ -65,7 +65,7 @@ struct ConstructionHelperBase {
 
 template <typename T>
 struct ConstructionHelper : public ConstructionHelperBase {
-	ConstructionHelper(ftl::Configurable *cfg) : ConstructionHelperBase(cfg) {}
+	explicit ConstructionHelper(ftl::Configurable *cfg) : ConstructionHelperBase(cfg) {}
 	~ConstructionHelper() {}
 	ftl::operators::Operator *make() override {
 		return new T(config);
diff --git a/components/operators/src/filling.cpp b/components/operators/src/filling.cpp
index c0e6bc997cbead6c480b7dedc3f8661a36b84681..b1e68b4d7e3c1dd79fc70e7b7879a820ec708ec6 100644
--- a/components/operators/src/filling.cpp
+++ b/components/operators/src/filling.cpp
@@ -3,6 +3,7 @@
 #include "filling_cuda.hpp"
 
 using ftl::operators::ScanFieldFill;
+using ftl::operators::CrossSupportFill;
 using ftl::codecs::Channel;
 
 ScanFieldFill::ScanFieldFill(ftl::Configurable *cfg) : ftl::operators::Operator(cfg) {
@@ -26,3 +27,26 @@ bool ScanFieldFill::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd
 
 	return true;
 }
+
+
+CrossSupportFill::CrossSupportFill(ftl::Configurable *cfg) : ftl::operators::Operator(cfg) {
+
+}
+
+CrossSupportFill::~CrossSupportFill() {
+
+}
+
+bool CrossSupportFill::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *s, cudaStream_t stream) {
+
+	/*ftl::cuda::filling_csr(
+		in.createTexture<uchar4>(Channel::Colour2),
+		in.createTexture<float4>(Channel::Normals),
+		in.createTexture<float>(Channel::Depth),
+		in.createTexture<float>(Channel::Depth),
+		in.createTexture<uchar4>(Channel::Colour),
+		s->parameters(), 0
+	);*/
+
+	return true;
+}
diff --git a/components/operators/src/filling.cu b/components/operators/src/filling.cu
index e0b6cf978bc2b974324f8f88501db61ff6993f79..7780530ff69ab178c46803a38f05de85d037f033 100644
--- a/components/operators/src/filling.cu
+++ b/components/operators/src/filling.cu
@@ -1,5 +1,6 @@
 #include "filling_cuda.hpp"
 
+#define T_PER_BLOCK 8
 #define WARP_SIZE 32
 #define MAX_EDGES 128
 
@@ -128,3 +129,73 @@ void ftl::cuda::scan_field_fill(
 	scan_field_fill_kernel<<<gridSize, blockSize, 0, stream>>>(depth_in, depth_out, smoothing, thresh, camera);
 	cudaSafeCall( cudaGetLastError() );
 }
+
+
+//===== Cross Support Region Filling ===========================================
+
+__global__ void filling_csr_kernel(
+		TextureObject<uchar4> region,
+		TextureObject<float4> normals_in,
+		TextureObject<float> depth_in,        // Virtual depth map
+		TextureObject<float> depth_out,   // Accumulated output
+		TextureObject<uchar4> colour_in,
+		ftl::rgbd::Camera camera) {
+	
+	const int x = blockIdx.x*blockDim.x + threadIdx.x;
+	const int y = blockIdx.y*blockDim.y + threadIdx.y;
+
+	if (x < 0 || y < 0 || x >= depth_in.width() || y >= depth_in.height()) return;
+
+	float3 aX = make_float3(0.0f,0.0f,0.0f);
+	float3 nX = make_float3(0.0f,0.0f,0.0f);
+	float contrib = 0.0f;
+
+	float d0 = depth_in.tex2D(x, y);
+	//depth_out(x,y) = d0;
+	//normals_out(x,y) = normals_in(x,y);
+	if (d0 < camera.minDepth || d0 > camera.maxDepth) return;
+	float3 X = camera.screenToCam((int)(x),(int)(y),d0);
+
+	uchar4 c0 = colour_in.tex2D(x, y);
+
+	// Neighbourhood
+	uchar4 base = region.tex2D(x,y);
+
+	for (int v=-base.z; v<=base.w; ++v) {
+	uchar4 baseY = region.tex2D(x,y+v);
+
+	for (int u=-baseY.x; u<=baseY.y; ++u) {
+		const float d = depth_in.tex2D(x+u, y+v);
+		if (d < camera.minDepth || d > camera.maxDepth) continue;
+
+		// Point and normal of neighbour
+		const float3 Xi = camera.screenToCam((int)(x)+u,(int)(y)+v,d);
+		const float3 Ni = make_float3(normals_in.tex2D((int)(x)+u, (int)(y)+v));
+
+		
+	}
+	}
+
+	
+}
+
+void ftl::cuda::filling_csr(
+		ftl::cuda::TextureObject<uchar4> &region,
+		ftl::cuda::TextureObject<float4> &normals_in,
+		ftl::cuda::TextureObject<float> &depth_in,
+		ftl::cuda::TextureObject<float> &depth_out,
+		ftl::cuda::TextureObject<uchar4> &colour_in,
+		const ftl::rgbd::Camera &camera,
+		cudaStream_t stream) {
+
+	const dim3 gridSize((depth_out.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (depth_out.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
+	const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
+
+	filling_csr_kernel<<<gridSize, blockSize, 0, stream>>>(region, normals_in, depth_in, depth_out, colour_in, camera);
+	cudaSafeCall( cudaGetLastError() );
+
+
+	#ifdef _DEBUG
+	cudaSafeCall(cudaDeviceSynchronize());
+	#endif
+}
diff --git a/components/operators/src/filling_cuda.hpp b/components/operators/src/filling_cuda.hpp
index c89423fe2b3984694d16c0ef2c5d69735fe5c712..e47781ac7fdd3656809de496dad21d1bb3a48862 100644
--- a/components/operators/src/filling_cuda.hpp
+++ b/components/operators/src/filling_cuda.hpp
@@ -15,6 +15,15 @@ void scan_field_fill(
 		const ftl::rgbd::Camera &camera,
 		cudaStream_t stream);
 
+void filling_csr(
+		ftl::cuda::TextureObject<uchar4> &region,
+		ftl::cuda::TextureObject<float4> &normals_in,
+		ftl::cuda::TextureObject<float> &depth_in,
+		ftl::cuda::TextureObject<float> &depth_out,
+		ftl::cuda::TextureObject<uchar4> &colour_in,
+		const ftl::rgbd::Camera &camera,
+		cudaStream_t stream);
+
 }
 }
 
diff --git a/components/operators/src/mls.cu b/components/operators/src/mls.cu
index e189bf0e502845a18f565d3df539f8560930b8e1..d7770f1345a9545b67e3534ebaf637ab42f73370 100644
--- a/components/operators/src/mls.cu
+++ b/components/operators/src/mls.cu
@@ -214,6 +214,14 @@ void ftl::cuda::colour_mls_smooth(
 
 // ===== Colour MLS using cross support region =================================
 
+__device__ inline int segmentID(int u, int v) {
+	if (u < 0 && v < 0) return 0x001;
+	if (u > 0 && v < 0) return 0x002;
+	if (u > 0 && v > 0) return 0x004;
+	if (u < 0 && v > 0) return 0x008;
+	return 0;
+}
+
 /*
  * Smooth depth map using Moving Least Squares. This version uses colour
  * similarity weights to adjust the spatial smoothing factor. It is naive in
@@ -221,6 +229,7 @@ void ftl::cuda::colour_mls_smooth(
  * level of single pixels can be subject to noise. Colour noise should first
  * be removed from the image.
  */
+ template <bool FILLING>
  __global__ void colour_mls_smooth_csr_kernel(
 	 	TextureObject<uchar4> region,
 		TextureObject<float4> normals_in,
@@ -244,14 +253,21 @@ void ftl::cuda::colour_mls_smooth(
 	float d0 = depth_in.tex2D(x, y);
 	depth_out(x,y) = d0;
 	normals_out(x,y) = normals_in(x,y);
-	if (d0 < camera.minDepth || d0 > camera.maxDepth) return;
+	if (d0 < camera.minDepth || d0 > camera.maxDepth) {
+		if(FILLING) d0 = 0.0f;
+		else return;
+	}
 	float3 X = camera.screenToCam((int)(x),(int)(y),d0);
 
 	uchar4 c0 = colour_in.tex2D(x, y);
 
     // Neighbourhood
 	uchar4 base = region.tex2D(x,y);
+	int segment_check = 0;
 
+	// TODO: Does using a fixed loop size with range test work better?
+	// Or with warp per pixel version, this would be less of a problem...
+	// TODO: Do a separate vote fill step?
 	for (int v=-base.z; v<=base.w; ++v) {
 		uchar4 baseY = region.tex2D(x,y+v);
 
@@ -268,12 +284,17 @@ void ftl::cuda::colour_mls_smooth(
 			const uchar4 c = colour_in.tex2D(x+u, y+v);
 			const float cw = ftl::cuda::colourWeighting(c0,c,colour_smoothing);
 
+			// Allow missing point to borrow z value
+			// TODO: This is a bad choice of Z! Perhaps try histogram vote approach
+			if (FILLING && d0 == 0.0f) X = camera.screenToCam((int)(x),(int)(y),Xi.z);
+
 			// Gauss approx weighting function using point distance
 			const float w = ftl::cuda::spatialWeighting(X,Xi,smoothing*cw);
 
 			aX += Xi*w;
 			nX += Ni*w;
 			contrib += w;
+			if (FILLING && w > 0.0f && v > -base.z+1 && v < base.w-1 && u > -baseY.x+1 && u < baseY.y-1) segment_check |= segmentID(u,v);
 		}
 	}
 
@@ -282,6 +303,11 @@ void ftl::cuda::colour_mls_smooth(
 	nX /= contrib;  // Weighted average normal
 	aX /= contrib;  // Weighted average point (centroid)
 
+	if (FILLING && d0 == 0.0f) {
+		if (__popc(segment_check) < 3) return;
+		X = camera.screenToCam((int)(x),(int)(y),aX.z);
+	}
+
 	// Signed-Distance Field function
 	float fX = nX.x * (X.x - aX.x) + nX.y * (X.y - aX.y) + nX.z * (X.z - aX.z);
 
@@ -306,13 +332,18 @@ void ftl::cuda::colour_mls_smooth_csr(
 		ftl::cuda::TextureObject<uchar4> &colour_in,
 		float smoothing,
 		float colour_smoothing,
+		bool filling,
 		const ftl::rgbd::Camera &camera,
 		cudaStream_t stream) {
 
 	const dim3 gridSize((depth_out.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (depth_out.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
 	const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
 
-	colour_mls_smooth_csr_kernel<<<gridSize, blockSize, 0, stream>>>(region, normals_in, normals_out, depth_in, depth_out, colour_in, smoothing, colour_smoothing, camera);
+	if (filling) {
+		colour_mls_smooth_csr_kernel<true><<<gridSize, blockSize, 0, stream>>>(region, normals_in, normals_out, depth_in, depth_out, colour_in, smoothing, colour_smoothing, camera);
+	} else {
+		colour_mls_smooth_csr_kernel<false><<<gridSize, blockSize, 0, stream>>>(region, normals_in, normals_out, depth_in, depth_out, colour_in, smoothing, colour_smoothing, camera);
+	}
 		
 	cudaSafeCall( cudaGetLastError() );
 
diff --git a/components/operators/src/segmentation.cpp b/components/operators/src/segmentation.cpp
index 24405005a90efa68093d9f746ada0f5c070b8f5f..a4bb9b0f7ebb717ee40cbf42015b45629d01ae36 100644
--- a/components/operators/src/segmentation.cpp
+++ b/components/operators/src/segmentation.cpp
@@ -14,13 +14,25 @@ CrossSupport::~CrossSupport() {
 }
 
 bool CrossSupport::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *s, cudaStream_t stream) {
-	ftl::cuda::support_region(
-        in.createTexture<uchar4>(Channel::Colour),
-		out.createTexture<uchar4>(Channel::Colour2, ftl::rgbd::Format<uchar4>(in.get<cv::cuda::GpuMat>(Channel::Colour).size())),
-		config()->value("tau", 5),
-        config()->value("v_max", 5),
-        config()->value("h_max", 5), 0
-	);
+	bool use_depth = config()->value("depth_region", false);
+
+	if (use_depth) {
+		ftl::cuda::support_region(
+			in.createTexture<float>(Channel::Depth),
+			out.createTexture<uchar4>(Channel::Support2, ftl::rgbd::Format<uchar4>(in.get<cv::cuda::GpuMat>(Channel::Colour).size())),
+			config()->value("depth_tau", 0.04f),
+			config()->value("v_max", 5),
+			config()->value("h_max", 5), 0
+		);
+	} //else {
+		ftl::cuda::support_region(
+			in.createTexture<uchar4>(Channel::Colour),
+			out.createTexture<uchar4>(Channel::Support1, ftl::rgbd::Format<uchar4>(in.get<cv::cuda::GpuMat>(Channel::Colour).size())),
+			config()->value("tau", 5.0f),
+			config()->value("v_max", 5),
+			config()->value("h_max", 5), 0
+		);
+	//}
 
 	return true;
 }
@@ -37,11 +49,48 @@ VisCrossSupport::~VisCrossSupport() {
 }
 
 bool VisCrossSupport::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *s, cudaStream_t stream) {
-	ftl::cuda::vis_support_region(
-        in.createTexture<uchar4>(Channel::Colour),
-		in.createTexture<uchar4>(Channel::Colour2),
-		0
-	);
+	bool show_depth = false;
+	if (in.hasChannel(Channel::Support2) && config()->value("show_depth_support", false)) {
+		show_depth = true;
+	}
+
+	bool show_bad = config()->value("show_bad", false) && in.hasChannel(Channel::Support2);
+
+	if (show_bad) {
+		ftl::cuda::vis_bad_region(
+			in.createTexture<uchar4>(Channel::Colour),
+			in.createTexture<float>(Channel::Depth),
+			in.createTexture<uchar4>(Channel::Support1),
+			in.createTexture<uchar4>(Channel::Support2),
+			0
+		);
+	} else {
+		ftl::cuda::vis_support_region(
+			in.createTexture<uchar4>(Channel::Colour),
+			in.createTexture<uchar4>(Channel::Support1),
+			make_uchar4(0,0,255,0),
+			make_uchar4(255,0,0,0),
+			config()->value("offset_x", 0),
+			config()->value("offset_y", 0),
+			config()->value("spacing_x", 50),
+			config()->value("spacing_y", 50),
+			0
+		);
+
+		if (show_depth) {
+			ftl::cuda::vis_support_region(
+				in.createTexture<uchar4>(Channel::Colour),
+				in.createTexture<uchar4>(Channel::Support2),
+				make_uchar4(0,0,255,0),
+				make_uchar4(0,255,0,0),
+				config()->value("offset_x", 0),
+				config()->value("offset_y", 0),
+				config()->value("spacing_x", 50),
+				config()->value("spacing_y", 50),
+				0
+			);
+		}
+	}
 
 	return true;
 }
\ No newline at end of file
diff --git a/components/operators/src/segmentation.cu b/components/operators/src/segmentation.cu
index c5c337edecd79173c327ae7816d892893b269096..baeda1023b99610396e7388b2ec2b5bae3c3cdbf 100644
--- a/components/operators/src/segmentation.cu
+++ b/components/operators/src/segmentation.cu
@@ -4,65 +4,93 @@
 
 using ftl::cuda::TextureObject;
 
+template <typename T>
+__device__ inline float cross(T p1, T p2);
 
-__device__ inline int cross(uchar4 p1, uchar4 p2) {
+template <>
+__device__ inline float cross<uchar4>(uchar4 p1, uchar4 p2) {
     return max(max(__sad(p1.x,p2.x,0),__sad(p1.y,p2.y,0)), __sad(p1.z,p2.z,0));
 }
 
-__device__ uchar4 calculate_support_region(const TextureObject<uchar4> &img, int x, int y, int tau, int v_max, int h_max) {
+template <>
+__device__ inline float cross<float>(float p1, float p2) {
+    return fabs(p1-p2);
+}
+
+template <typename T>
+__device__ uchar4 calculate_support_region(const TextureObject<T> &img, int x, int y, float tau, int v_max, int h_max) {
     int x_min = max(0, x - h_max);
     int x_max = min(img.width()-1, x + h_max);
     int y_min = max(0, y - v_max);
     int y_max = min(img.height()-1, y + v_max);
 
-	uchar4 result = make_uchar4(x - x_min, x_max - x, y - y_min, y_max - y);
+	uchar4 result = make_uchar4(0, 0, 0, 0);
 
-    uchar4 colour = img.tex2D(x,y);
+	T colour = img.tex2D(x,y);
+	T prev_colour = colour;
 
-    for (int u=x-1; u >= x_min; --u) {
-        if (cross(colour, img.tex2D(u,y)) > tau) {
-            result.x = x - u;
+	int u;
+    for (u=x-1; u >= x_min; --u) {
+		T next_colour = img.tex2D(u,y);
+        if (cross(prev_colour, next_colour) > tau) {
+            result.x = x - u - 1;
             break;
-        }
-    }
-    
-    for (int u=x+1; u <= x_max; ++u) {
-        if (cross(colour, img.tex2D(u,y)) > tau) {
-            result.y = u - x;
+		}
+		prev_colour = next_colour;
+	}
+	if (u < x_min) result.x = x - x_min;
+	
+	prev_colour = colour;
+    for (u=x+1; u <= x_max; ++u) {
+		T next_colour = img.tex2D(u,y);
+        if (cross(prev_colour, next_colour) > tau) {
+            result.y = u - x - 1;
             break;
-        }
-    }
-
-    for (int v=y-1; v >= y_min; --v) {
-        if (cross(colour, img.tex2D(x,v)) > tau) {
-            result.z = y - v;
+		}
+		prev_colour = next_colour;
+	}
+	if (u > x_max) result.y = x_max - x;
+
+	int v;
+	prev_colour = colour;
+    for (v=y-1; v >= y_min; --v) {
+		T next_colour = img.tex2D(x,v);
+        if (cross(prev_colour, next_colour) > tau) {
+            result.z = y - v - 1;
             break;
-        }
-    }
+		}
+		prev_colour = next_colour;
+	}
+	if (v < y_min) result.z = y - y_min;
 
-    for (int v=y+1; v <= y_max; ++v) {
-        if (cross(colour, img.tex2D(x,v)) > tau) {
-            result.w = v - y;
+	prev_colour = colour;
+    for (v=y+1; v <= y_max; ++v) {
+		T next_colour = img.tex2D(x,v);
+        if (cross(prev_colour, next_colour) > tau) {
+            result.w = v - y - 1;
             break;
-        }
-    }
+		}
+		prev_colour = next_colour;
+	}
+	if (v > y_max) result.w = y_max - y;
 
     return result;
 }
 
-__global__ void support_region_kernel(TextureObject<uchar4> colour, TextureObject<uchar4> region, int tau, int v_max, int h_max) {
+template <typename T>
+__global__ void support_region_kernel(TextureObject<T> img, TextureObject<uchar4> region, float tau, int v_max, int h_max) {
     const int x = blockIdx.x*blockDim.x + threadIdx.x;
     const int y = blockIdx.y*blockDim.y + threadIdx.y;
 
-    if (x < 0 || y < 0 || x >= colour.width() || y >= colour.height()) return;
+    if (x < 0 || y < 0 || x >= img.width() || y >= img.height()) return;
 
-    region(x,y) = calculate_support_region(colour, x, y, tau, v_max, h_max);
+    region(x,y) = calculate_support_region(img, x, y, tau, v_max, h_max);
 }
 
 void ftl::cuda::support_region(
         ftl::cuda::TextureObject<uchar4> &colour,
         ftl::cuda::TextureObject<uchar4> &region,
-        int tau,
+        float tau,
         int v_max,
         int h_max,
         cudaStream_t stream) {
@@ -79,16 +107,40 @@ void ftl::cuda::support_region(
     #endif
 }
 
-__global__ void vis_support_region_kernel(TextureObject<uchar4> colour, TextureObject<uchar4> region) {
+void ftl::cuda::support_region(
+		ftl::cuda::TextureObject<float> &depth,
+		ftl::cuda::TextureObject<uchar4> &region,
+		float tau,
+		int v_max,
+		int h_max,
+		cudaStream_t stream) {
+
+	const dim3 gridSize((region.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (region.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
+	const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
+
+	support_region_kernel<<<gridSize, blockSize, 0, stream>>>(depth, region, tau, v_max, h_max);
+	cudaSafeCall( cudaGetLastError() );
+
+
+	#ifdef _DEBUG
+	cudaSafeCall(cudaDeviceSynchronize());
+	#endif
+}
+
+__global__ void vis_support_region_kernel(TextureObject<uchar4> colour, TextureObject<uchar4> region, uchar4 bcolour, uchar4 acolour,
+		int ox, int oy, int dx, int dy) {
     const int x = blockIdx.x*blockDim.x + threadIdx.x;
     const int y = blockIdx.y*blockDim.y + threadIdx.y;
 
 	if (x < 0 || y < 0 || x >= colour.width() || y >= colour.height()) return;
 	
 	// Grid pattern
-	if (x % 50 != 0 || y % 50 != 0) return;
+	if (x % dx != ox || y % dy != oy) return;
 
 	uchar4 base = region.tex2D(x,y);
+
+	// Edge pattern
+	//if (base.x != 1) return;
 	
 	for (int v=-base.z; v<=base.w; ++v) {
 		uchar4 baseY = region.tex2D(x,y+v);
@@ -96,20 +148,87 @@ __global__ void vis_support_region_kernel(TextureObject<uchar4> colour, TextureO
 		for (int u=-baseY.x; u<=baseY.y; ++u) {
 			if (x+u < 0 || y+v < 0 || x+u >= colour.width() || y+v >= colour.height()) continue;
 			uchar4 col = colour.tex2D(x+u, y+v);
-			colour(x+u, y+v) = make_uchar4(255,col.y,col.z,0);
+			colour(x+u, y+v) = (u==0 || v == 0) ?
+					make_uchar4(max(bcolour.x, col.x), max(bcolour.y, col.y), max(bcolour.z, col.z), 0) :
+					make_uchar4(max(acolour.x, col.x), max(acolour.y, col.y), max(acolour.z, col.z), 0);
 		}
 	}
 }
 
 void ftl::cuda::vis_support_region(
         ftl::cuda::TextureObject<uchar4> &colour,
-        ftl::cuda::TextureObject<uchar4> &region,
+		ftl::cuda::TextureObject<uchar4> &region,
+		uchar4 bar_colour,
+		uchar4 area_colour,
+		int ox, int oy, int dx, int dy,
+        cudaStream_t stream) {
+
+    const dim3 gridSize((region.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (region.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
+    const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
+
+    vis_support_region_kernel<<<gridSize, blockSize, 0, stream>>>(
+		colour,
+		region,
+		bar_colour,
+		area_colour,
+		ox,oy,dx,dy
+	);
+    cudaSafeCall( cudaGetLastError() );
+
+
+    #ifdef _DEBUG
+    cudaSafeCall(cudaDeviceSynchronize());
+    #endif
+}
+
+// ===== Vis bad edges =========================================================
+
+__global__ void vis_bad_region_kernel(
+		TextureObject<uchar4> colour,
+		TextureObject<float> depth,
+		TextureObject<uchar4> region,
+		TextureObject<uchar4> dregion) {
+    const int x = blockIdx.x*blockDim.x + threadIdx.x;
+    const int y = blockIdx.y*blockDim.y + threadIdx.y;
+
+	if (x < 0 || y < 0 || x >= colour.width() || y >= colour.height()) return;
+	
+	// Grid pattern
+	//if (x % 50 != 0 || y % 50 != 0) return;
+
+	uchar4 base = region.tex2D(x,y);
+	uchar4 baseD = dregion.tex2D(x,y);
+	uchar4 col = colour.tex2D(x,y);
+	float d = depth.tex2D(x,y);
+
+	if (baseD.x > base.x && baseD.y < base.y) {
+		uchar4 baseR = region.tex2D(x+baseD.y+1, y);
+		float dR = depth.tex2D(x+baseD.y+1, y);
+		//if (x+baseD.y+1-baseR.x <= x) {
+			if (d > 0.0f && d < 30.0f && (dR <= 0.0f || dR >= 30.0f)) {
+				colour(x,y) = make_uchar4(col.x, col.y, 255, 0);
+				depth(x,y) = 0.0f;
+			}
+		//}
+	}
+}
+
+void ftl::cuda::vis_bad_region(
+		ftl::cuda::TextureObject<uchar4> &colour,
+		ftl::cuda::TextureObject<float> &depth,
+		ftl::cuda::TextureObject<uchar4> &region,
+		ftl::cuda::TextureObject<uchar4> &dregion,
         cudaStream_t stream) {
 
     const dim3 gridSize((region.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (region.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
     const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
 
-    vis_support_region_kernel<<<gridSize, blockSize, 0, stream>>>(colour, region);
+    vis_bad_region_kernel<<<gridSize, blockSize, 0, stream>>>(
+		colour,
+		depth,
+		region,
+		dregion
+	);
     cudaSafeCall( cudaGetLastError() );
 
 
diff --git a/components/operators/src/segmentation_cuda.hpp b/components/operators/src/segmentation_cuda.hpp
index 566b9d033a77935798e294f6126ede330963be4c..5ef55abb10a0d61aa83f900761b26352a3ae4a87 100644
--- a/components/operators/src/segmentation_cuda.hpp
+++ b/components/operators/src/segmentation_cuda.hpp
@@ -9,14 +9,31 @@ namespace cuda {
 void support_region(
 		ftl::cuda::TextureObject<uchar4> &colour,
 		ftl::cuda::TextureObject<uchar4> &region,
-		int tau, int v_max, int h_max,
+		float tau, int v_max, int h_max,
+		cudaStream_t stream);
+
+void support_region(
+		ftl::cuda::TextureObject<float> &depth,
+		ftl::cuda::TextureObject<uchar4> &region,
+		float tau, int v_max, int h_max,
 		cudaStream_t stream);
 
 void vis_support_region(
         ftl::cuda::TextureObject<uchar4> &colour,
         ftl::cuda::TextureObject<uchar4> &region,
+		uchar4 bar_colour,
+		uchar4 area_colour,
+		int ox, int oy, int dx, int dy,
         cudaStream_t stream);
 
+void vis_bad_region(
+		ftl::cuda::TextureObject<uchar4> &colour,
+		ftl::cuda::TextureObject<float> &depth,
+		ftl::cuda::TextureObject<uchar4> &region,
+		ftl::cuda::TextureObject<uchar4> &dregion,
+        cudaStream_t stream);
+
+
 }
 }
 
diff --git a/components/operators/src/smoothing.cpp b/components/operators/src/smoothing.cpp
index c23ba0c5f3711ab1e044e4dcaa162b32de562367..a9041087ad2a3432e04957bfc983c8d29a2ae525 100644
--- a/components/operators/src/smoothing.cpp
+++ b/components/operators/src/smoothing.cpp
@@ -184,6 +184,7 @@ bool ColourMLS::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::So
 	int iters = config()->value("mls_iterations", 10);
 	int radius = config()->value("mls_radius",3);
 	bool crosssup = config()->value("cross_support", false);
+	bool filling = config()->value("filling", false);
 
 	if (!in.hasChannel(Channel::Normals)) {
 		LOG(ERROR) << "Required normals channel missing for MLS";
@@ -207,7 +208,7 @@ bool ColourMLS::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::So
 			);
 		} else {
 			ftl::cuda::colour_mls_smooth_csr(
-				in.createTexture<uchar4>(Channel::Colour2),
+				in.createTexture<uchar4>(Channel::Support1),
 				in.createTexture<float4>(Channel::Normals),
 				in.createTexture<float4>(Channel::Points, ftl::rgbd::Format<float4>(in.get<cv::cuda::GpuMat>(Channel::Depth).size())),
 				in.createTexture<float>(Channel::Depth),
@@ -215,6 +216,7 @@ bool ColourMLS::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::So
 				in.createTexture<uchar4>(Channel::Colour),
 				thresh,
 				col_smooth,
+				filling,
 				s->parameters(),
 				0
 			);
diff --git a/components/operators/src/smoothing_cuda.hpp b/components/operators/src/smoothing_cuda.hpp
index 729c962218c48d204c175f6a338bc797878d70d0..a92a49260a48dcabe115768b347dfca5cc2283ab 100644
--- a/components/operators/src/smoothing_cuda.hpp
+++ b/components/operators/src/smoothing_cuda.hpp
@@ -38,6 +38,7 @@ void colour_mls_smooth_csr(
 		ftl::cuda::TextureObject<uchar4> &colour_in,
 		float smoothing,
 		float colour_smoothing,
+		bool filling,
 		const ftl::rgbd::Camera &camera,
 		cudaStream_t stream);
 
diff --git a/components/rgbd-sources/src/sources/ftlfile/file_source.cpp b/components/rgbd-sources/src/sources/ftlfile/file_source.cpp
index 9597bde796f80ac67f38a5a569190001e13d7065..0962c1886dc199e50530343c0d01edf4e74e37f0 100644
--- a/components/rgbd-sources/src/sources/ftlfile/file_source.cpp
+++ b/components/rgbd-sources/src/sources/ftlfile/file_source.cpp
@@ -30,6 +30,13 @@ FileSource::FileSource(ftl::rgbd::Source *s, ftl::rgbd::Player *r, int sid) : ft
 	realtime_ = host_->value("realtime", true);
 	timestamp_ = r->getStartTime();
 	sourceid_ = sid;
+	freeze_ = host_->value("freeze", false);
+	have_frozen_ = false;
+
+	host_->on("freeze", [this](const ftl::config::Event &e) {
+		have_frozen_ = false;
+		freeze_ = host_->value("freeze", false);
+	});
 
     r->onPacket(sid, [this](const ftl::codecs::StreamPacket &spkt, ftl::codecs::Packet &pkt) {
 		host_->notifyRaw(spkt, pkt);
@@ -133,18 +140,28 @@ bool FileSource::capture(int64_t ts) {
 }
 
 bool FileSource::retrieve() {
-	if (!reader_->read(timestamp_)) {
+	if (!have_frozen_ && !reader_->read(timestamp_)) {
 		cache_write_ = -1;
 	}
     return true;
 }
 
 void FileSource::swap() {
+	if (have_frozen_) return;
 	cache_read_ = cache_write_;
 	cache_write_ = (cache_write_ == 0) ? 1 : 0;
 }
 
 bool FileSource::compute(int n, int b) {
+	// Freeze frame requires a copy to be made each time...
+	if (have_frozen_) {
+		cv::cuda::GpuMat t1, t2;
+		if (!rgb_.empty()) rgb_.copyTo(t1);
+		if (!depth_.empty()) depth_.copyTo(t2);
+		host_->notify(timestamp_, t1, t2);
+		return true;
+	}
+
 	if (cache_read_ < 0) return false;
 	if (cache_[cache_read_].size() == 0) return false;
 
@@ -191,7 +208,11 @@ bool FileSource::compute(int n, int b) {
 	if (rgb_.empty() || depth_.empty()) return false;
 
 	// Inform about a decoded frame pair
-	host_->notify(timestamp_, rgb_, depth_);
+	if (freeze_ && !have_frozen_) {
+		have_frozen_ = true;
+	} else {
+		host_->notify(timestamp_, rgb_, depth_);
+	}
     return true;
 }
 
diff --git a/components/rgbd-sources/src/sources/ftlfile/file_source.hpp b/components/rgbd-sources/src/sources/ftlfile/file_source.hpp
index 2d59b68cb1e07d10aade07b33780af2497736a12..f8a26da37802e0aa2aae4681b420e6061c230fd2 100644
--- a/components/rgbd-sources/src/sources/ftlfile/file_source.hpp
+++ b/components/rgbd-sources/src/sources/ftlfile/file_source.hpp
@@ -43,6 +43,8 @@ class FileSource : public detail::Source {
 	ftl::codecs::Decoder *decoders_[2];
 
 	bool realtime_;
+	bool freeze_;
+	bool have_frozen_;
 
 	void _processCalibration(ftl::codecs::Packet &pkt);
 	void _processPose(ftl::codecs::Packet &pkt);