You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
7.0 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.
#include "reflectivity.h"
#include <vector>
#include <numeric>
#include <algorithm>
#include <stdexcept>
#include <cmath>
#include <limits>
using namespace rs2;
static const size_t N_STD_FRAMES = 100;
static const int NINETY_FIVE_PERCENT_OF_STD_PERIOD = static_cast< int >( 0.95 * N_STD_FRAMES );
static const float MAX_RANGE_IN_UNIT = 65536.0f;
static const float LONG_THERMAL = 74.5f;
static const float INDOOR_MAX_RANGE = 9.0f;
// TODO try to read from LRS
static const float FOV_H = 0.610865f;
static const float FOV_V = 0.479966f;
static const int VGA_WIDTH = 640;
static const int VGA_HEIGHT = 480;
static const int VGA_HALF_WIDTH = VGA_WIDTH / 2;
static const int VGA_HALF_HEIGHT = VGA_HEIGHT / 2;
static bool is_close_to_zero( float x )
{
return ( std::abs( x ) < std::numeric_limits< float >::epsilon() );
}
reflectivity::reflectivity()
: _history_size( 0 )
{
_dist_queue.assign( N_STD_FRAMES,
0 ); // Allocate size for all samples in advance to minimize runtime.
}
// Reflectivity analysis is based on STD and normalized IR.Final reflectivity values are weighted
// average between them
float reflectivity::get_reflectivity( float raw_noise_estimation,
float max_usable_range,
float ir_val ) const
{
// Calculating STD over time(temporal noise), when there are more than 5 % invalid return high
// value
std::vector< float > _filt_dist_arr( _dist_queue.size() );
// Copy non zero (or close to zero) elements in history
auto new_end = std::copy_if( _dist_queue.begin(),
_dist_queue.end(),
_filt_dist_arr.begin(),
[]( float elem ) { return ! is_close_to_zero( elem ); } );
_filt_dist_arr.resize( new_end - _filt_dist_arr.begin() );
if( 0 == _filt_dist_arr.size() )
throw std::logic_error( "reflectivity N/A, not enough data was collected" );
float sum = std::accumulate( _filt_dist_arr.begin(), _filt_dist_arr.end(), 0.0f );
float mean = sum / _filt_dist_arr.size();
float standard_deviation = 9999.0f;
if( _filt_dist_arr.size() >= NINETY_FIVE_PERCENT_OF_STD_PERIOD )
{
float variance = 0.0f;
for( auto val : _filt_dist_arr )
variance += std::pow( val - mean, 2.0f );
variance = variance / _filt_dist_arr.size();
standard_deviation = std::sqrt( variance );
}
// Range is calculated based on position in map(assuming 0 tilt) Distance is just based on plane distance
auto range = mean / 1000.0f;
// Normalized IR instead of IR is used to be robust to high ambient, RSS is assumed between echo to noise
auto nest = raw_noise_estimation / 16.0f;
auto normalized_ir = 0.0f;
if( ir_val > nest )
normalized_ir = std::pow( std::pow( ir_val, 2.0f ) - std::pow( nest, 2.0f ), 0.5f );
auto ref_from_ir = 0.0f;
auto ref_from_std = 0.0f;
auto i_ref = 0.0f;
auto s_ref = 0.0f;
auto i_dist_85 = 0.0f;
auto s_dist_85 = 0.0f;
auto ref = 0.0f;
if( nest <= LONG_THERMAL * 2.0f )
{
// We can hold reflectivity information from IR
if( normalized_ir < 70.0f )
ref_from_ir = 0.5f; // Low confidence as IR values are low
else
ref_from_ir = 1.0f; // High confidence as IR values hold good SNR
// Analyzing reflectivity based on 85 % reflectivity data
i_dist_85 = 6.75f * std::exp( -0.012f * normalized_ir );
i_dist_85 = i_dist_85 * max_usable_range / INDOOR_MAX_RANGE;
if( 0.0f < i_dist_85 ) // protect devision by zero
{
i_ref = static_cast<float>(0.85f * std::pow( ( range / i_dist_85 ), 2 ));
if( i_ref > 0.95f )
{
ref_from_ir = 0.75f;
i_ref = 0.95f;
}
}
else
ref_from_ir = 0.0f;
}
// STD based analysis
// STD values are at the edges, hard to get data
if( standard_deviation >= 1.5f && standard_deviation <= 10.f )
{
if( standard_deviation > 2.5f )
ref_from_std = 0.5f; // Low confidence data due to low STD values
else
ref_from_std = 1.0f; // High confidence data
// STD based analysis based on 85% reflectivity data
if( range > 0.0f )
{
s_dist_85 = ( 2.25f * ( std::log( standard_deviation ) ) + 1.56f ) * max_usable_range
/ INDOOR_MAX_RANGE;
if( 0 < s_dist_85 ) // protect devision by zero
{
s_ref = 0.85f * std::pow( ( range / s_dist_85 ), 2.0f );
if( s_ref > 0.95f )
{
ref_from_std = ref_from_std * 0.75f;
s_ref = 0.95f;
}
}
else
ref_from_std = 0.0f;
}
else
{
ref_from_std = 0.0f;
}
}
// Calculating Final reflectivity
if( is_close_to_zero( ref_from_ir + ref_from_std ) )
throw std::logic_error( "reflectivity N/A" );
ref = ( i_ref * ref_from_ir + s_ref * ref_from_std ) / ( ref_from_ir + ref_from_std );
// Force 15% resolution
if( ref >= 0.85f )
ref = 0.85f;
else if( ref >= 0.7f )
ref = 0.7f;
else if( ref >= 0.55f )
ref = 0.55f;
else if( ref >= 0.4f )
ref = 0.4f;
else if( ref >= 0.25f )
ref = 0.25f;
else
ref = 0.1f;
return ref;
}
void reflectivity::add_depth_sample( float depth_val, int x_in_image, int y_in_image )
{
if( x_in_image >= 0 && x_in_image < VGA_WIDTH && y_in_image >= 0 && y_in_image < VGA_HEIGHT )
{
auto dist_z = round( depth_val * 16000.f / MAX_RANGE_IN_UNIT ); // convert to mm units
float x_ang = FOV_H * std::abs( VGA_HALF_WIDTH - x_in_image ) / VGA_HALF_WIDTH;
float y_ang = FOV_V * std::abs( VGA_HALF_HEIGHT - y_in_image ) / VGA_HALF_HEIGHT;
auto dist_r = dist_z * std::sqrt( 1.0f + (std::pow( 2.0f * std::tan( x_ang ), 2.0f ) + std::pow( 2.0f * std::tan( y_ang ), 2.0f ) ) / 4.0f );
if( _dist_queue.size() >= N_STD_FRAMES ) // Keep queue as N_STD_FRAMES size queue
_dist_queue.pop_front();
_dist_queue.push_back( dist_r );
if( _history_size < N_STD_FRAMES )
_history_size++;
}
}
void rs2::reflectivity::reset_history()
{
if( _history_size > 0 )
{
_dist_queue.assign( N_STD_FRAMES, 0 );
_history_size = 0;
}
}
float rs2::reflectivity::get_samples_ratio() const
{
return static_cast< float >( history_size() ) / history_capacity();
}
bool rs2::reflectivity::is_history_full() const
{
return history_size() == history_capacity();
}
// Return the history queue capacity
size_t rs2::reflectivity::history_capacity() const
{
return N_STD_FRAMES;
}
// Return the history queue current size
size_t rs2::reflectivity::history_size() const
{
return _history_size;
}