diff --git a/components/operators/include/ftl/operators/smoothing.hpp b/components/operators/include/ftl/operators/smoothing.hpp
index a35053d2a97be851ca9afe75938a57b357465a29..1f6961d812916bf8f9ecaa5c4e7629947230c0ac 100644
--- a/components/operators/include/ftl/operators/smoothing.hpp
+++ b/components/operators/include/ftl/operators/smoothing.hpp
@@ -42,6 +42,8 @@ class SmoothChannel : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *src, cudaStream_t stream) override;
 
+	private:
+	ftl::rgbd::Frame temp_[2];
 };
 
 /**
diff --git a/components/operators/src/smoothing.cpp b/components/operators/src/smoothing.cpp
index 01f7f7de677fa3d0165ddf0ed63b7ef478829d9e..79f37207756af7977d66c4c11d86c557ddd884c1 100644
--- a/components/operators/src/smoothing.cpp
+++ b/components/operators/src/smoothing.cpp
@@ -74,8 +74,11 @@ bool SmoothChannel::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd
 	int radius = config()->value("radius", 1);
 	float threshold = config()->value("threshold", 30.0f);
 
+	int width = s->parameters().width;
+	int height = s->parameters().height;
+
 	// Clear to max smoothing
-	out.create<GpuMat>(Channel::Smoothing, Format<float>(s->parameters().width, s->parameters().height)).setTo(cv::Scalar(1.0f));
+	out.create<GpuMat>(Channel::Smoothing, Format<float>(width, height)).setTo(cv::Scalar(1.0f));
 
 	// Reduce to nearest
 	ftl::cuda::smooth_channel(
@@ -88,6 +91,25 @@ bool SmoothChannel::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd
 		stream
 	);
 
+	width /= 2;
+	height /= 2;
+
+	ftl::rgbd::Camera scaledCam = s->parameters().scaled(width, height);
+
+	// Downscale images for next pass
+	cv::cuda::resize(in.get<GpuMat>(Channel::Colour), temp_[0].create<GpuMat>(Channel::Colour), cv::Size(width, height), 0.0, 0.0, cv::INTER_LINEAR);
+	cv::cuda::resize(in.get<GpuMat>(Channel::Depth), temp_[0].create<GpuMat>(Channel::Depth), cv::Size(width, height), 0.0, 0.0, cv::INTER_NEAREST);
+
+	ftl::cuda::smooth_channel(
+		temp_[0].createTexture<uchar4>(Channel::Colour),
+		temp_[0].createTexture<float>(Channel::Depth),
+		out.getTexture<float>(Channel::Smoothing),
+		scaledCam,
+		threshold,
+		radius,
+		stream
+	);
+
 	return true;
 }
 
diff --git a/components/rgbd-sources/include/ftl/rgbd/camera.hpp b/components/rgbd-sources/include/ftl/rgbd/camera.hpp
index 9fd6b98a026d9e3edf2ccd09d1a2a3011cbb71bb..2d36b2f7565620efe9962297dddf2a0e3a45393e 100644
--- a/components/rgbd-sources/include/ftl/rgbd/camera.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/camera.hpp
@@ -29,6 +29,8 @@ struct __align__(16) Camera {
 	double baseline;		// For stereo pair
 	double doffs;			// Disparity offset
 
+	Camera scaled(int width, int height) const;
+
 	/**
 	 * Convert camera coordinates into screen coordinates.
 	 */
diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp
index a7b6b2cb0e3081efa8371479a216d6dc21dce801..24629e13929983cbaeb1e13af31e68745d02f28b 100644
--- a/components/rgbd-sources/src/source.cpp
+++ b/components/rgbd-sources/src/source.cpp
@@ -280,7 +280,8 @@ void Source::notifyRaw(const ftl::codecs::StreamPacket &spkt, const ftl::codecs:
 /*
  * Scale camera parameters to match resolution.
  */
-static Camera scaled(Camera &cam, int width, int height) {
+Camera Camera::scaled(int width, int height) const {
+	const auto &cam = *this;
 	float scaleX = (float)width / (float)cam.width;
 	float scaleY = (float)height / (float)cam.height;
 
@@ -304,7 +305,7 @@ void Source::notify(int64_t ts, cv::cuda::GpuMat &c1, cv::cuda::GpuMat &c2) {
 
 	// Do we need to scale camera parameters
 	if (impl_->params_.width < max_width || impl_->params_.height < max_height) {
-		impl_->params_ = scaled(impl_->params_, max_width, max_height);
+		impl_->params_ = impl_->params_.scaled(max_width, max_height);
 	}
 
 	// Should channel 1 be scaled?