diff --git a/components/rgbd-sources/CMakeLists.txt b/components/rgbd-sources/CMakeLists.txt
index 5983d1d940b8b420aa5fb8b6653da65f7f904b0a..e0a925616c522522342d0032b5cae7bc684698b9 100644
--- a/components/rgbd-sources/CMakeLists.txt
+++ b/components/rgbd-sources/CMakeLists.txt
@@ -14,6 +14,7 @@ set(RGBDSRC
 #	src/algorithms/opencv_sgbm.cpp
 #	src/algorithms/opencv_bm.cpp
 	src/cb_segmentation.cpp
+	src/offilter.cpp
 )
 
 if (HAVE_REALSENSE)
diff --git a/components/rgbd-sources/include/ftl/offilter.hpp b/components/rgbd-sources/include/ftl/offilter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7775de411b952e83d431843c829d56cf8c868fce
--- /dev/null
+++ b/components/rgbd-sources/include/ftl/offilter.hpp
@@ -0,0 +1,31 @@
+#pragma once
+#include <opencv2/core.hpp>
+#include <opencv2/core/cuda.hpp>
+#include <opencv2/cudaoptflow.hpp>
+
+namespace ftl {
+namespace rgbd {
+
+class OFDisparityFilter {
+public:
+	OFDisparityFilter() : n_max_(0), threshold_(0.0), size_(0, 0) {} // TODO: invalid state
+	OFDisparityFilter(cv::Size size, int n_frames, float threshold);
+	void filter(cv::Mat &disp, const cv::Mat &rgb);
+
+private:
+	int n_;
+	int n_max_;
+	float threshold_;
+	cv::Size size_;
+
+	cv::Mat disp_;
+	cv::Mat gray_;
+
+	cv::Mat flowxy_;
+	cv::Mat flowxy_up_;
+
+	cv::Ptr<cv::cuda::NvidiaOpticalFlow_1_0> nvof_;
+};
+
+}
+}
\ No newline at end of file
diff --git a/components/rgbd-sources/src/algorithms/fixstars_sgm.cpp b/components/rgbd-sources/src/algorithms/fixstars_sgm.cpp
index b6a860db28fa4f9c11d4ec89eb55e0d8f87b4c2c..33bc92a177eee1370b6fd228a7f509ce38ca3b51 100644
--- a/components/rgbd-sources/src/algorithms/fixstars_sgm.cpp
+++ b/components/rgbd-sources/src/algorithms/fixstars_sgm.cpp
@@ -36,6 +36,15 @@ FixstarsSGM::FixstarsSGM(nlohmann::json &config) : Disparity(config) {
 
 		filter_ = cv::cuda::createDisparityBilateralFilter(max_disp_ << 4, radius, iter);
 	}
+	
+	bool use_off_ = value("use_off", false);
+
+	if (use_off_)
+	{
+		int off_size = value("off_size", 9);
+		double off_threshold = value("off_threshold", 0.9);
+		off_ = ftl::rgbd::OFDisparityFilter(size_, off_size, off_threshold);
+	}
 
 	init(size_);
 }
@@ -95,6 +104,12 @@ void FixstarsSGM::compute(const cv::cuda::GpuMat &l, const cv::cuda::GpuMat &r,
 	}
 
 	dispt_full_res_.convertTo(disp, CV_32F, 1.0f / 16.0f, stream);
+
+	if (use_off_) {
+		Mat disp_host(disp);
+		off_.filter(disp_host, Mat(lbw_));
+		disp.upload(disp_host);
+	}
 }
 
 void FixstarsSGM::setMask(Mat &mask) {
diff --git a/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp b/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp
index 08d01a535494d10469c99e8f627c8c92571753dd..521241a70843a8e5668e223f5be7b0e7050e4311 100644
--- a/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp
+++ b/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp
@@ -12,7 +12,7 @@
 #include <opencv2/cudastereo.hpp>
 #include <ftl/configuration.hpp>
 
-#include "ftl/cb_segmentation.hpp"
+#include "ftl/offilter.hpp"
 
 namespace ftl {
 	namespace algorithms {
@@ -44,6 +44,7 @@ namespace ftl {
 			int P2_;
 			cv::Size size_;
 			bool use_filter_;
+			bool use_off_;
 			cv::Ptr<cv::cuda::DisparityBilateralFilter> filter_;
 			sgm::StereoSGM *ssgm_;
 			cv::cuda::GpuMat lbw_;
@@ -52,6 +53,7 @@ namespace ftl {
 
 			cv::cuda::GpuMat l_downscaled_;
 			cv::cuda::GpuMat dispt_full_res_;
+			ftl::rgbd::OFDisparityFilter off_;
 		};
 	};
 };
diff --git a/components/rgbd-sources/src/offilter.cpp b/components/rgbd-sources/src/offilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b74ff3ce32d8e5400692f73c09b906fb048d4920
--- /dev/null
+++ b/components/rgbd-sources/src/offilter.cpp
@@ -0,0 +1,109 @@
+#include <loguru.hpp>
+
+#include "ftl/offilter.hpp"
+
+using namespace ftl::rgbd;
+
+using cv::Mat;
+using cv::Size;
+
+using std::vector;
+
+template<typename T> static bool inline isValidDisparity(T d) { return (0.0 < d) && (d < 256.0); } // TODO
+
+OFDisparityFilter::OFDisparityFilter(Size size, int n_frames, float threshold) :
+	n_(0), n_max_(n_frames), threshold_(threshold), size_(size)
+{
+	disp_ = Mat::zeros(size, CV_64FC(n_frames));
+	gray_ = Mat::zeros(size, CV_8UC1);
+
+	nvof_ = cv::cuda::NvidiaOpticalFlow_1_0::create(size.width, size.height,
+													cv::cuda::NvidiaOpticalFlow_1_0::NV_OF_PERF_LEVEL_SLOW,
+													true, false, false, 0);
+	
+}
+
+void OFDisparityFilter::filter(Mat &disp, const Mat &gray)
+{
+
+	const int n = n_;
+	n_ = (n_ + 1) % n_max_;
+	
+	nvof_->calc(gray, gray_, flowxy_);
+	nvof_->upSampler(	flowxy_, size_.width, size_.height,
+						nvof_->getGridSize(), flowxy_up_);
+
+	CHECK(disp.type() == CV_32FC1);
+	CHECK(gray.type() == CV_8UC1);
+	CHECK(flowxy_up_.type() == CV_32FC2);
+
+	gray.copyTo(gray_);
+
+	vector<float> values(n_max_);
+
+	for (int y = 0; y < size_.height; y++)
+	{
+		float *D = disp_.ptr<float>(y);
+		float *d = disp.ptr<float>(y);
+		float *flow = flowxy_up_.ptr<float>(y);
+
+		for (int x = 0; x < size_.width; x++)
+		{
+			const float flow_l1 = abs(flow[2*x]) + abs(flow[2*x + 1]);
+
+			if (flow_l1 < threshold_)
+			{
+				values.clear();
+
+				if (isValidDisparity(d[x]))
+				{
+					bool updated = false;
+					for (int i = 0; i < n_max_; i++)
+					{
+						float &val = D[n_max_ * x + (n_max_ - i + n) % n_max_];
+						if (!isValidDisparity(val))
+						{
+							val = d[x];
+							updated = true;
+						}
+					}
+					if (!updated) { D[n_max_ * x + n] = d[x]; }
+				}
+
+				for (int i = 0; i < n_max_; i++)
+				{
+					float &val = D[n_max_ * x + i];
+					if (isValidDisparity(val)) { values.push_back(val); }
+				}
+
+				if (values.size() > 0) {
+					const auto median_it = values.begin() + values.size() / 2;
+					std::nth_element(values.begin(), median_it , values.end());
+					d[x] = *median_it;
+				}
+
+				/*
+				if (isValidDepth(d[x]) && isValidDepth(D[x]))
+				{
+					D[x] = D[x] * 0.66 + d[x] * (1.0 - 0.66);
+				}
+				if (isValidDepth(D[x]))
+				{
+					d[x] = D[x];
+				}
+				else
+				{
+					D[x] = d[x];
+				}
+				*/
+			}
+			else
+			{
+				for (int i = 0; i < n_max_; i++)
+				{
+					D[n_max_ * x + i] = 0.0;
+				}
+			}
+		}
+	}
+}