{"id":1063,"date":"2018-11-03T14:48:02","date_gmt":"2018-11-03T13:48:02","guid":{"rendered":"http:\/\/www.calvert.ch\/maurice\/?p=1063"},"modified":"2018-11-04T13:13:25","modified_gmt":"2018-11-04T12:13:25","slug":"improving-the-depth-map-accuracy-of-realsense-cameras-by-an-order-of-magnitude","status":"publish","type":"post","link":"https:\/\/www.calvert.ch\/maurice\/improving-the-depth-map-accuracy-of-realsense-cameras-by-an-order-of-magnitude\/","title":{"rendered":"Improving the depth map accuracy of RealSense cameras &#8211; by an order of magnitude"},"content":{"rendered":"<p>Intel\u2019s RealSense cameras are astonishingly <a href=\"https:\/\/en.wikipedia.org\/wiki\/Accuracy_and_precision\">precise but not as accurate<\/a>. By optimising the calibration of the depth stream and correcting for non-linearity, the accuracy can be improved by an order of magnitude at 2.5 metres and becomes almost linear in the depth:<\/p>\n<p><a href=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/averagezerror.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-1092\" src=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/averagezerror-1024x631.png\" alt=\"\" width=\"695\" height=\"428\" srcset=\"https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/averagezerror-1024x631.png 1024w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/averagezerror-300x185.png 300w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/averagezerror-768x474.png 768w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/averagezerror.png 1140w\" sizes=\"auto, (max-width: 695px) 100vw, 695px\" \/><\/a><\/p>\n<p><a href=\"#solution\">Skip to solution<\/a><\/p>\n<h1>Sources of error<\/h1>\n<p>Calculating the coordinates of the 3D point corresponding to a depth reading is straightforward trigonometry \u2013 <a href=\"http:\/\/www.calvert.ch\/maurice\/2018\/11\/01\/realsense-cameras-calculating-3d-coordinates-from-depth-row-and-column\/\">here<\/a>&#8216;s a quick refresher \u2013 but the accuracy of the results depends on several factors.<\/p>\n<h2>Accuracy of the intrinsics<\/h2>\n<p>The supplied <a href=\"https:\/\/www.intel.com\/content\/www\/us\/en\/support\/articles\/000026725\/emerging-technologies\/intel-realsense-technology.html\">Intel\u00ae RealSense&#x2122; Depth Module D400 Series Custom Calibration<\/a> program uses the traditional method, displaying a chequerboard to the camera in various poses and solving for the intrinsics. There are several issues with this methodology:<\/p>\n<ol>\n<li>This method establishes the intrinsics solely for the colour camera.<\/li>\n<li>Although it resolves to sub-pixel accuracy, it does so on a single frame, which is imprecise. The results of 3D calculations are extremely sensitive to errors in the field-of-view: A one-degree error in the vertical field of view translates into &gt;11mm error at 1 metre. Concomitant errors in the horizontal field of view make matters worse and they are quadratic in the depth.<\/li>\n<li>The depth stream is synthesised by the stereo depth module and the vision processor. Imperfections anywhere in the chain (unforeseen distortion, varying refraction at different wavelengths, heuristics in the algorithms, depth filtering) may negatively affect the accuracy. One cannot assume that an apparently perfect colour image will produce ideal results in the depth map.<\/li>\n<\/ol>\n<h2>Non-linearity<\/h2>\n<p>This is readily observed with the supplied DepthQuality tool. When viewing a target at a measured distance of 1\u2019000 from the glass, the instantaneous reported depth is out by ~22mm:<\/p>\n<p><a href=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/depthquality.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-1088\" src=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/depthquality-1024x829.png\" alt=\"\" width=\"695\" height=\"563\" srcset=\"https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/depthquality-1024x829.png 1024w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/depthquality-300x243.png 300w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/depthquality-768x622.png 768w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/depthquality.png 1026w\" sizes=\"auto, (max-width: 695px) 100vw, 695px\" \/><\/a><\/p>\n<p>By averaging depth measurements over a period, errors in precision can be eliminated. Averaged over 1\u2019000\u2019000 measurements, my out-of-the-box D435 reports a range of 980.70mm \u2013 an error of 19.3mm. This is within the specified accuracy 2%=20mm but increases quadratically, as is to be expected. Fortunately, this non-linearity appears to be constant for a given camera and once determined, can be eliminated.<\/p>\n<h2>Focal Point<\/h2>\n<p>The focal point of the depth map is supplied in the <a href=\"https:\/\/software.intel.com\/en-us\/realsense\/d400\/intel-realsense-depth-camera-d400-series-datasheet\">Intel RealSense D400 Series Datasheet<\/a>, for a D435 it is defined as being 3.2mm behind of the glass. Presumably due to the manufacturing tolerances of \u00b13%, the focal point may in reality be tens of mm away.<\/p>\n<h2>Mounting<\/h2>\n<p>No matter how precisely the camera is mounted, there will be errors between the mounting and the camera\u2019s true central axis. Knowing them improves the accuracy when translating from the camera frame to the parent (vehicle or world) frame<a id=\"solution\"><\/a>.<\/p>\n<h1>Solution<\/h1>\n<p>I have written a program that calibrates a camera based solely on measurements in the depth stream. It derives all the parameters discussed above by making several observations of a target with known dimensions. A much higher degree of accuracy is obtained by averaging over a large number of measurements. The optimal parameter values are then calculated, as a single problem, with a non-linear solver.<\/p>\n<p>It is open-source, available on GitHub\u00a0<a href=\"https:\/\/github.com\/smirkingman\/RealSense-Calibrator\">https:\/\/github.com\/smirkingman\/RealSense-Calibrator<\/a><\/p>\n<p>Screenshot of an optimiser output:<\/p>\n<p><a href=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/CameraBCalibration.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-1115\" src=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/CameraBCalibration-1024x553.png\" alt=\"\" width=\"695\" height=\"375\" srcset=\"https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/CameraBCalibration-1024x553.png 1024w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/CameraBCalibration-300x162.png 300w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/CameraBCalibration-768x415.png 768w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/CameraBCalibration.png 2000w\" sizes=\"auto, (max-width: 695px) 100vw, 695px\" \/><\/a><\/p>\n<h1>Discussion<\/h1>\n<p>The comparisons presented above use the Z-range as the metric, as this is the metric in the reference documentation. The Z measure alone is only part of the answer, a more realistic metric is the 3D error of the point: the vector between the truth and the 3D point determined by the camera and software. Futhermore, just supplying a number doesn&#8217;t tell the whole story. Traditional error analysis supplies descriptive statistics, which give a value and a confidence known as the <a href=\"https:\/\/en.wikipedia.org\/wiki\/68%E2%80%9395%E2%80%9399.7_rule\">68\u201395\u201399.7 rule<\/a>, which allows us to make statements like &#8220;The error will be no more than Xmm 99.7% of the time&#8221; (3-sigma, or 3<i>\u03c3).\u00a0<\/i><\/p>\n<p>The 3D error &#8211; the length of the vector between the true coordinates of the point and what the camera+software reported is:<\/p>\n<p><a href=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/average3derror.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-1103\" src=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/average3derror-1024x653.png\" alt=\"\" width=\"695\" height=\"443\" srcset=\"https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/average3derror-1024x653.png 1024w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/average3derror-300x191.png 300w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/average3derror-768x490.png 768w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/average3derror.png 1129w\" sizes=\"auto, (max-width: 695px) 100vw, 695px\" \/><\/a><\/p>\n<p>The 3-sigma error is:<\/p>\n<p><a href=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/3dsigma.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-1104\" src=\"http:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/3dsigma-1024x651.png\" alt=\"\" width=\"695\" height=\"442\" srcset=\"https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/3dsigma-1024x651.png 1024w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/3dsigma-300x191.png 300w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/3dsigma-768x489.png 768w, https:\/\/www.calvert.ch\/maurice\/files\/2018\/11\/3dsigma.png 1138w\" sizes=\"auto, (max-width: 695px) 100vw, 695px\" \/><\/a><\/p>\n<p>what this shows is that at 1.5 metres, the coordinates of the 3D point will be between 1&#8217;447 and 1&#8217;553 from the camera 99.7% of the time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Intel\u2019s RealSense cameras are astonishingly precise but not as accurate. By optimising the calibration of the depth stream and correcting for non-linearity, the accuracy can be improved by an order of magnitude at 2.5 metres and becomes almost linear in <a href='https:\/\/www.calvert.ch\/maurice\/improving-the-depth-map-accuracy-of-realsense-cameras-by-an-order-of-magnitude\/' class='excerpt-more'>[&#8230;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[67,47,11],"tags":[],"class_list":["post-1063","post","type-post","status-publish","format-standard","hentry","category-3d","category-programming","category-technology","category-67-id","category-47-id","category-11-id","post-seq-1","post-parity-odd","meta-position-corners","fix"],"_links":{"self":[{"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/posts\/1063","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/comments?post=1063"}],"version-history":[{"count":20,"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/posts\/1063\/revisions"}],"predecessor-version":[{"id":1116,"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/posts\/1063\/revisions\/1116"}],"wp:attachment":[{"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/media?parent=1063"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/categories?post=1063"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.calvert.ch\/maurice\/wp-json\/wp\/v2\/tags?post=1063"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}