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.

175 lines
6.5 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2019 Intel Corporation. All Rights Reserved.
#include <librealsense2/rs.hpp> // Include RealSense Cross Platform API
#include "cv-helpers.hpp" // frame_to_mat
#include <rs-vino/object-detection.h>
#include <rs-vino/detected-object.h>
#include <rsutils/easylogging/easyloggingpp.h>
#ifdef BUILD_SHARED_LIBS
// With static linkage, ELPP is initialized by librealsense, so doing it here will
// create errors. When we're using the shared .so/.dll, the two are separate and we have
// to initialize ours if we want to use the APIs!
INITIALIZE_EASYLOGGINGPP
#endif
namespace openvino = InferenceEngine;
int main(int argc, char * argv[]) try
{
el::Configurations conf;
conf.set( el::Level::Global, el::ConfigurationType::Format, "[%level] %msg" );
//conf.set( el::Level::Debug, el::ConfigurationType::Enabled, "false" );
el::Loggers::reconfigureLogger( "default", conf );
rs2::log_to_console( RS2_LOG_SEVERITY_WARN ); // only warnings (and above) should come through
// Declare RealSense pipeline, encapsulating the actual device and sensors
rs2::pipeline pipe;
pipe.start();
rs2::align align_to( RS2_STREAM_COLOR );
// Start the inference engine, needed to accomplish anything. We also add a CPU extension, allowing
// us to run the inference on the CPU. A GPU solution may be possible but, at least without a GPU,
// a CPU-bound process is faster. To change to GPU, use "GPU" instead (and remove AddExtension()):
openvino::Core engine;
#ifdef OPENVINO2019
openvino_helpers::error_listener error_listener;
engine.SetLogCallback( error_listener );
#endif
std::string const device_name { "CPU" };
// Cpu extensions library was removed in OpenVINO >= 2020.1, extensions were merged into the cpu plugin.
#ifdef OPENVINO2019
engine.AddExtension( std::make_shared< openvino::Extensions::Cpu::CpuExtensions >(), device_name );
#endif
openvino_helpers::object_detection faceDetector(
"face-detection-adas-0001.xml",
0.5 // Probability threshold -- anything with less confidence will be thrown out
);
try
{
faceDetector.load_into( engine, device_name );
}
catch( const std::exception & e )
{
// The model files should have been downloaded automatically by CMake into build/wrappers/openvino/face,
// which is also where Visual Studio runs the sample from. However, you may need to copy these two files:
// face-detection-adas-0001.bin
// face-detection-adas-0001.xml
// Into the local directory where you run from (or change the path given in the ctor above)
LOG(ERROR) << "Failed to load model files:\n " << e.what();
LOG(INFO) << "Please copy the model files into the working directory from which this sample is run";
return EXIT_FAILURE;
}
const auto window_name = "OpenVINO face detection sample";
cv::namedWindow( window_name, cv::WINDOW_AUTOSIZE );
bool first_frame = true;
cv::Mat prev_image;
openvino_helpers::detected_objects faces;
size_t id = 0;
while( cv::getWindowProperty( window_name, cv::WND_PROP_AUTOSIZE ) >= 0 )
{
// Wait for the next set of frames
auto data = pipe.wait_for_frames();
// Make sure the frames are spatially aligned
data = align_to.process( data );
auto color_frame = data.get_color_frame();
auto depth_frame = data.get_depth_frame();
// If we only received a new depth frame, but the color did not update, continue
static uint64 last_frame_number = 0;
if( color_frame.get_frame_number() == last_frame_number )
continue;
last_frame_number = color_frame.get_frame_number();
auto image = frame_to_mat( color_frame );
// We process the previous frame so if this is our first then queue it and continue
if( first_frame )
{
faceDetector.enqueue( image );
faceDetector.submit_request();
first_frame = false;
prev_image = image;
continue;
}
// Wait for the results of the previous frame we enqueued: we're going to process these
faceDetector.wait();
auto const results = faceDetector.fetch_results();
// Enqueue the current frame so we'd get the results when the next frame comes along!
faceDetector.enqueue( image );
faceDetector.submit_request();
openvino_helpers::detected_objects prev_faces { std::move( faces ) };
faces.clear();
for( auto const & result : results )
{
cv::Rect rect = result.location;
rect = rect & cv::Rect( 0, 0, image.cols, image.rows );
auto face_ptr = openvino_helpers::find_object( rect, prev_faces );
if( !face_ptr )
// New face
face_ptr = std::make_shared< openvino_helpers::detected_object >( id++, std::string(), rect );
else
// Existing face; just update its parameters
face_ptr->move( rect );
faces.push_back( face_ptr );
}
// Keep this image so we can actually process pieces of it once we have the results
prev_image = image;
// Display the results (from the last frame) as rectangles on top (of the current frame)
for( auto && face : faces )
{
cv::Scalar color( 255, 255, 255 ); // BGR
auto r = face->get_location();
cv::rectangle( image, r, color );
// Get a very crude approximation (not aligned) of the center in the depth frame, and output the distance to it
auto center_x = r.x + r.width / 2;
auto center_y = r.y + r.height / 2;
auto d = depth_frame.get_distance( center_x, center_y );
if( d )
{
std::ostringstream ss;
ss << std::setprecision( 2 ) << d;
ss << " m";
cv::putText( image, ss.str(), cv::Point( r.x + 5, r.y + r.height - 5 ), cv::FONT_HERSHEY_SIMPLEX, 0.3, color );
}
}
imshow( window_name, image );
if( cv::waitKey( 1 ) >= 0 )
break;
}
return EXIT_SUCCESS;
}
catch (const rs2::error & e)
{
LOG(ERROR) << "Caught RealSense exception from " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
LOG(ERROR) << "Unknown exception caught: " << e.what();
return EXIT_FAILURE;
}