Skip to content

File plugin.cpp

File List > depthai > plugin.cpp

Go to the documentation of this file

// ILLIXR includes
#include "plugin.hpp"

#include 
#include 
#include 
#include 
#include 
#include 

using namespace ILLIXR;
using namespace ILLIXR::data_format;

[[maybe_unused]] depthai::depthai(const std::string& name, phonebook* pb)
    : plugin{name, pb}
    , switchboard_{phonebook_->lookup_impl<switchboard>()}
    , clock_{phonebook_->lookup_impl<relative_clock>()}
    , imu_writer_{switchboard_->get_writer<imu_type>("imu")}
    , cam_writer_{switchboard_->get_writer<binocular_cam_type>("cam")}
    , rgb_depth_{switchboard_->get_writer<rgb_depth_type>("rgb_depth")} // Initialize DepthAI pipeline and device
    , device_{create_camera_pipeline()} {
    spdlogger(switchboard_->get_env_char("DEPTHAI_LOG_LEVEL"));
#ifndef NDEBUG
    spdlog::get(name)->debug("pipeline started");
#endif
    color_queue_                           = device_.getOutputQueue("preview", 1, false);
    depth_queue_                           = device_.getOutputQueue("depth", 1, false);
    rectif_left_queue_                     = device_.getOutputQueue("rectified_left", 1, false);
    rectif_right_queue_                    = device_.getOutputQueue("rectified_right", 1, false);
    imu_queue_                             = device_.getOutputQueue("imu", 1, false);
    std::function<void(void)> imu_callback = [&]() {
        callback();
    };
    imu_queue_->addCallback(imu_callback);
    test_time_point_ = std::chrono::steady_clock::now();
}

void depthai::callback() {
    std::lock_guard<std::mutex> lock(mutex_);
    // Check for available data
    bool color_go      = color_queue_->has<dai::ImgFrame>();
    bool depth_go      = depth_queue_->has<dai::ImgFrame>();
    bool rectifL_go    = rectif_left_queue_->has<dai::ImgFrame>();
    bool rectifR_go    = rectif_right_queue_->has<dai::ImgFrame>();
    bool imu_packet_go = imu_queue_->has<dai::IMUData>();

#ifndef NDEBUG
    if (color_go) {
        rgb_count_++;
    }
    if (depth_go) {
        depth_count_++;
    }
    if (rectifL_go) {
        left_count_++;
    }
    if (rectifR_go) {
        right_count_++;
    }
#endif

    if (rectifR_go && rectifL_go && depth_go && color_go) {
#ifndef NDEBUG
        all_count_++;
#endif
        auto color_frame = color_queue_->tryGet<dai::ImgFrame>();
        auto depth_frame = depth_queue_->tryGet<dai::ImgFrame>();
        auto rectifL     = rectif_left_queue_->tryGet<dai::ImgFrame>();
        auto rectifR     = rectif_right_queue_->tryGet<dai::ImgFrame>();

        ullong cam_time = static_cast<ullong>(
            std::chrono::time_point_cast<std::chrono::nanoseconds>(color_frame->getTimestamp()).time_since_epoch().count());
        if (!first_cam_time_) {
            first_cam_time_      = cam_time;
            first_real_cam_time_ = clock_->now();
        }

        time_point cam_time_point{*first_real_cam_time_ + std::chrono::nanoseconds(cam_time - *first_cam_time_)};

        cv::Mat color = cv::Mat(static_cast<int>(color_frame->getHeight()), static_cast<int>(color_frame->getWidth()), CV_8UC3,
                                color_frame->getData().data());
        cv::Mat rgb_out{color.clone()};
        cv::Mat rectified_left_frame = cv::Mat(static_cast<int>(rectifL->getHeight()), static_cast<int>(rectifL->getWidth()),
                                               CV_8UC1, rectifL->getData().data());
        cv::Mat left_out{rectified_left_frame.clone()};
        cv::flip(left_out, left_out, 1);
        cv::Mat rectified_right_frame = cv::Mat(static_cast<int>(rectifR->getHeight()), static_cast<int>(rectifR->getWidth()),
                                                CV_8UC1, rectifR->getData().data());
        cv::Mat right_out{rectified_right_frame.clone()};
        cv::flip(right_out, right_out, 1);

        cv::Mat depth = cv::Mat(static_cast<int>(depth_frame->getHeight()), static_cast<int>(depth_frame->getWidth()), CV_16UC1,
                                depth_frame->getData().data());
        cv::Mat converted_depth;
        depth.convertTo(converted_depth, CV_32FC1, 1000.f);

        cam_writer_.put(cam_writer_.allocate<binocular_cam_type>({cam_time_point, cv::Mat{left_out}, cv::Mat{right_out}}));
        rgb_depth_.put(rgb_depth_.allocate<rgb_depth_type>({cam_time_point, cv::Mat{rgb_out}, cv::Mat{converted_depth}}));
    }

    std::chrono::time_point<std::chrono::steady_clock, std::chrono::steady_clock::duration> gyro_ts;
    Eigen::Vector3d                                                                         la;
    Eigen::Vector3d                                                                         av;

    if (imu_packet_go) {
        auto imu_packet = imu_queue_->tryGet<dai::IMUData>();
#ifndef NDEBUG
        if (imu_packet_ == 0) {
            first_packet_time_ = std::chrono::steady_clock::now();
        }
        imu_packet_++;
#endif

        auto imu_data = imu_packet->packets;
        for (auto& imu_datum : imu_data) {
            gyro_ts = std::chrono::time_point_cast<std::chrono::nanoseconds>(imu_datum.gyroscope.timestamp.get());
            if (gyro_ts <= test_time_point_) {
                return;
            }
            test_time_point_ = gyro_ts;
            la               = {imu_datum.acceleroMeter.x, imu_datum.acceleroMeter.y, imu_datum.acceleroMeter.z};
            av               = {imu_datum.gyroscope.x, imu_datum.gyroscope.y, imu_datum.gyroscope.z};
        }

        // Time as ullong (nanoseconds)
        ullong imu_time = static_cast<ullong>(gyro_ts.time_since_epoch().count());
        if (!first_imu_time_) {
            first_imu_time_      = imu_time;
            first_real_imu_time_ = clock_->now();
        }

        time_point imu_time_point{*first_real_imu_time_ + std::chrono::nanoseconds(imu_time - *first_imu_time_)};

// Submit to switchboard
#ifndef NDEBUG
        imu_pub_++;
#endif
        imu_writer_.put(imu_writer_.allocate<imu_type>({
            imu_time_point,
            av,
            la,
        }));
    }
}

depthai::~depthai() {
#ifndef NDEBUG
    spdlog::get(name_)->debug("Destructor: Packets Received {} Published: IMU: {} RGB-D: {}", imu_packet_, imu_pub_, rgbd_pub_);
    auto dur = std::chrono::steady_clock::now() - first_packet_time_;
    spdlog::get(name_)->debug("Time since first packet: {} ms",
                              std::chrono::duration_cast<std::chrono::milliseconds>(dur).count());
    spdlog::get(name_)->debug("RGB: {} Left: {} Right: {} Depth: {} All: {}", rgb_count_, left_count_, right_count_,
                              depth_count_, all_count_);
#endif
}

dai::Pipeline depthai::create_camera_pipeline() const {
#ifndef NDEBUG
    spdlog::get(name_)->debug("creating pipeline");
#endif
    dai::Pipeline p;

    // IMU
    auto imu      = p.create<dai::node::IMU>();
    auto xout_imu = p.create<dai::node::XLinkOut>();
    xout_imu->setStreamName("imu");

    // Enable raw readings at 500Hz for accel and gyro
    if (use_raw_) {
        imu->enableIMUSensor({dai::IMUSensor::ACCELEROMETER_RAW, dai::IMUSensor::GYROSCOPE_RAW}, 400);
    } else {
        imu->enableIMUSensor({dai::IMUSensor::ACCELEROMETER, dai::IMUSensor::GYROSCOPE_CALIBRATED}, 400);
    }

    // above this threshold packets will be sent in batch of X, if the host is not blocked
    imu->setBatchReportThreshold(1);
    // maximum number of IMU packets in a batch, if it's reached device will block sending until host can receive it
    // if lower or equal to batchReportThreshold then the sending is always blocking on device
    imu->setMaxBatchReports(1);
    // WARNING, temporarily 6 is the max

    // Link plugins CAM -> XLINK
    imu->out.link(xout_imu->input);

    // Color Camera, default 30 FPS
    auto color_cam = p.create<dai::node::ColorCamera>();
    auto xlink_out = p.create<dai::node::XLinkOut>();
    xlink_out->setStreamName("preview");

    color_cam->setPreviewSize(640, 480);
    color_cam->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1080_P);
    color_cam->setInterleaved(true);

    // Link plugins CAM -> XLINK
    color_cam->preview.link(xlink_out->input);

    // Mono Cameras
    auto mono_left  = p.create<dai::node::MonoCamera>();
    auto mono_right = p.create<dai::node::MonoCamera>();
    // MonoCamera
    mono_left->setResolution(dai::MonoCameraProperties::SensorResolution::THE_400_P);
    mono_left->setBoardSocket(dai::CameraBoardSocket::LEFT);
    mono_left->setFps(30.0);
    mono_right->setResolution(dai::MonoCameraProperties::SensorResolution::THE_400_P);
    mono_right->setBoardSocket(dai::CameraBoardSocket::RIGHT);
    mono_right->setFps(30.0);

    // Stereo Setup
    //  Better handling for occlusions:
    bool lrcheck = true;
    // Closer-in minimum depth, disparity range is doubled (from 95 to 190):
    bool extended = false;
    // Better accuracy for longer distance, fractional disparity 32-levels:
    bool subpixel = false;

    /*
    int max_disp = 96;
    if (extended)
        max_disp *= 2;
    if (subpixel)
        max_disp *= 32; // 5 bits fractional disparity
    */
    // StereoDepth
    auto stereo       = p.create<dai::node::StereoDepth>();
    auto xout_rectifL = p.create<dai::node::XLinkOut>();
    auto xout_rectifR = p.create<dai::node::XLinkOut>();
    auto xout_depth   = p.create<dai::node::XLinkOut>();

    stereo->initialConfig.setConfidenceThreshold(200);
    stereo->setLeftRightCheck(lrcheck);
    stereo->setExtendedDisparity(extended);
    stereo->setSubpixel(subpixel);

    xout_depth->setStreamName("depth");
    xout_rectifL->setStreamName("rectified_left");
    xout_rectifR->setStreamName("rectified_right");

    stereo->rectifiedLeft.link(xout_rectifL->input);
    stereo->rectifiedRight.link(xout_rectifR->input);

    stereo->depth.link(xout_depth->input);
    // Link plugins CAM -> STEREO -> XLINK
    mono_left->out.link(stereo->left);
    mono_right->out.link(stereo->right);

    return p;
}

// This line makes the plugin importable by Spindle
PLUGIN_MAIN(depthai)