diff options
| -rw-r--r-- | tracker-kinect-face/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | tracker-kinect-face/camera_kinect_ir.cpp | 270 | ||||
| -rw-r--r-- | tracker-kinect-face/camera_kinect_ir.h | 74 | 
3 files changed, 355 insertions, 0 deletions
| diff --git a/tracker-kinect-face/CMakeLists.txt b/tracker-kinect-face/CMakeLists.txt index 8eb064e7..7d8f828a 100644 --- a/tracker-kinect-face/CMakeLists.txt +++ b/tracker-kinect-face/CMakeLists.txt @@ -40,5 +40,16 @@ if (WIN32 AND opentrack-intel)  		  DESTINATION "${opentrack-hier-pfx}"  		  PERMISSIONS ${opentrack-perms-exec}  		) + +        # Optional OpenCV support +        # Needed for Point Tracker to support Kinect V2 IR Sensor +        find_package(OpenCV QUIET) +        if(OpenCV_FOUND) +            add_definitions(-DOTR_HAVE_OPENCV) +            target_include_directories(${self} SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) +            target_link_libraries(${self} opencv_imgproc opentrack-cv opencv_core opentrack-video) +        endif() + +  	endif()  endif() diff --git a/tracker-kinect-face/camera_kinect_ir.cpp b/tracker-kinect-face/camera_kinect_ir.cpp new file mode 100644 index 00000000..edae6289 --- /dev/null +++ b/tracker-kinect-face/camera_kinect_ir.cpp @@ -0,0 +1,270 @@ +/* Copyright (c) 2019 Stéphane Lenclud + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include "camera_kinect_ir.h" + +#ifdef OTR_HAVE_OPENCV + +//#include "frame.hpp" + +#include "compat/sleep.hpp" +#include "compat/math-imports.hpp" + +#include <opencv2/imgproc.hpp> +#include <cstdlib> + +namespace Kinect { + +    static const char KKinectIRSensor[] = "Kinect V2 IR Sensor"; + +    CamerasProvider::CamerasProvider() = default; + +    std::unique_ptr<video::impl::camera> CamerasProvider::make_camera(const QString& name) +    { +        if (name.compare(KKinectIRSensor) == 0) +        { +            return std::make_unique<CameraKinectIr>(); +        } + +        return nullptr; +    } + +    std::vector<QString> CamerasProvider::camera_names() const +    { +        std::vector<QString> cameras; +        cameras.push_back(KKinectIRSensor); +        return cameras; +    } + +    bool CamerasProvider::can_show_dialog(const QString& camera_name) +    { +        return false; +    } + +    bool CamerasProvider::show_dialog(const QString& camera_name) +    { +        return false; +    } + +// Register our camera provider thus making sure Point Tracker can use Kinect V2 IR Sensor +OTR_REGISTER_CAMERA(CamerasProvider) + + +CameraKinectIr::CameraKinectIr()  +{ +} + + +CameraKinectIr::~CameraKinectIr() +{ +    stop(); +} + +bool CameraKinectIr::show_dialog() +{ +    return false; +} + +bool CameraKinectIr::is_open() +{ +    return iInfraredFrameReader!=nullptr; +} + +/// +/// Wait until we get a first frame +/// +void CameraKinectIr::WaitForFirstFrame() +{ +    bool new_frame = false; +    int attempts = 200; // Kinect cold start can take a while +    while (!new_frame && attempts>0) +    { +        new_frame = get_frame_(iMatFrame); +        portable::sleep(100); +        --attempts; +    } +} + + + +std::tuple<const video::impl::frame&, bool> CameraKinectIr::get_frame() +{ +    bool new_frame = false; +    new_frame = get_frame_(iMatFrame); +         +    iFrame.data = iMatFrame.ptr(); +    iFrame.width = 512; +    iFrame.height = 424; +    iFrame.stride = 0; // Auto step +    iFrame.channels = 3; +    return { iFrame, new_frame }; +} + +// Safe release for interfaces +template<class Interface> +inline void SafeRelease(Interface *& pInterfaceToRelease) +{ +    if (pInterfaceToRelease != NULL) +    { +        pInterfaceToRelease->Release(); +        pInterfaceToRelease = NULL; +    } +} + +bool CameraKinectIr::start(const info& args) +{ +    stop(); + +    HRESULT hr; + +    // Get and open Kinect sensor +    hr = GetDefaultKinectSensor(&iKinectSensor); +    if (SUCCEEDED(hr)) +    { +        hr = iKinectSensor->Open(); +    } + +    // Create infrared frame reader	 +    if (SUCCEEDED(hr)) +    { +        // Initialize the Kinect and get the infrared reader +        IInfraredFrameSource* pInfraredFrameSource = NULL; + +        hr = iKinectSensor->Open(); + +        if (SUCCEEDED(hr)) +        { +            hr = iKinectSensor->get_InfraredFrameSource(&pInfraredFrameSource); +        } + +        if (SUCCEEDED(hr)) +        { +            hr = pInfraredFrameSource->OpenReader(&iInfraredFrameReader); +        } + +        SafeRelease(pInfraredFrameSource); +    } + + +    if (SUCCEEDED(hr)) +    { +        WaitForFirstFrame(); +        bool success = iMatFrame.ptr() != nullptr; +        return success; +    } + +    stop(); +    return false; +} + +void CameraKinectIr::stop() +{ +    // done with infrared frame reader +    SafeRelease(iInfraredFrame); +    SafeRelease(iInfraredFrameReader); + +    // close the Kinect Sensor +    if (iKinectSensor) +    { +        iKinectSensor->Close(); +    } + +    SafeRelease(iKinectSensor); + +    // Free up our memory buffer if any +    iMatFrame = {};   +} + +bool CameraKinectIr::get_frame_(cv::Mat& frame) +{ + +    if (!iInfraredFrameReader) +    { +        return false; +    } + +    bool success = false; + +    // Release previous frame if any +    SafeRelease(iInfraredFrame); + +    HRESULT hr = iInfraredFrameReader->AcquireLatestFrame(&iInfraredFrame); + +    if (SUCCEEDED(hr)) +    { +        INT64 nTime = 0; +        IFrameDescription* frameDescription = NULL; +        int nWidth = 0; +        int nHeight = 0; +        float diagonalFieldOfView = 0.0f; +        UINT nBufferSize = 0; +        UINT16 *pBuffer = NULL; + +        hr = iInfraredFrame->get_RelativeTime(&nTime); + +        if (SUCCEEDED(hr)) +        { +            hr = iInfraredFrame->get_FrameDescription(&frameDescription); +        } + +        // TODO: should not request those info for a every frame really +        if (SUCCEEDED(hr)) +        { +            hr = frameDescription->get_Width(&nWidth); +        } + +        if (SUCCEEDED(hr)) +        { +            hr = frameDescription->get_Height(&nHeight); +        } + +        if (SUCCEEDED(hr)) +        { +            hr = frameDescription->get_DiagonalFieldOfView(&diagonalFieldOfView); +        } +         +        if (SUCCEEDED(hr)) +        { +            hr = iInfraredFrame->AccessUnderlyingBuffer(&nBufferSize, &pBuffer); +        } + +        if (SUCCEEDED(hr)) +        { +            //ProcessInfrared(nTime, pBuffer, nWidth, nHeight); + +            // Create an OpenCV matrix with our 16-bits IR buffer +            cv::Mat raw = cv::Mat(nHeight, nWidth, CV_16UC1, pBuffer); + +            // Convert that OpenCV matrix to an RGB one as this is what is expected by our point extractor +            // TODO: Ideally we should implement a point extractors that works with our native buffer +            // First resample to 8-bits             +            double min = std::numeric_limits<uint16_t>::min(); +            double max = std::numeric_limits<uint16_t>::max(); +            //cv::minMaxLoc(raw, &min, &max); // Should we use 16bit min and max instead? +            // For scalling to have more precission in the range we are interrested in +            min = max - 255;             +            cv::Mat raw8; +            // See: https://stackoverflow.com/questions/14539498/change-type-of-mat-object-from-cv-32f-to-cv-8u/14539652 +            raw.convertTo(raw8, CV_8U, 255.0 / (max - min), -255.0*min / (max - min)); +            // Second convert to RGB +            cv::cvtColor(raw8, frame, cv::COLOR_GRAY2BGR); +            // +            success = true; +        } + +        SafeRelease(frameDescription); +    } + + +    return success; +} + + + +} + +#endif diff --git a/tracker-kinect-face/camera_kinect_ir.h b/tracker-kinect-face/camera_kinect_ir.h new file mode 100644 index 00000000..f38c342a --- /dev/null +++ b/tracker-kinect-face/camera_kinect_ir.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2019 Stéphane Lenclud + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#pragma once + +#ifdef OTR_HAVE_OPENCV + +#include <Kinect.h> + +//#include "pt-api.hpp" +#include "compat/timer.hpp" +#include "video/camera.hpp" + + +#include <memory> + +#include <opencv2/core.hpp> +#include <opencv2/videoio.hpp> + +#include <QString> + +namespace Kinect { + +    struct CamerasProvider : video::impl::camera_ +    { +        CamerasProvider(); +        std::vector<QString> camera_names() const override; +        std::unique_ptr<video::impl::camera> make_camera(const QString& name) override; +        bool can_show_dialog(const QString& camera_name) override; +        bool show_dialog(const QString& camera_name) override; +    }; + + +/// +/// Implement our camera interface using Kinect V2 SDK IR Sensor. +/// +struct CameraKinectIr final : video::impl::camera +{ +    CameraKinectIr(); +    virtual ~CameraKinectIr(); + + +    [[nodiscard]] bool start(const info& args) override; +    void stop() override; +    bool is_open() override; +    std::tuple<const video::impl::frame&, bool> get_frame() override; +    [[nodiscard]] bool show_dialog() override; + +private: +    bool get_frame_(cv::Mat& frame); +    void WaitForFirstFrame(); + +private: +    // Current Kinect +    IKinectSensor* iKinectSensor = nullptr; + +    // Infrared reader +    IInfraredFrameReader*  iInfraredFrameReader = nullptr; + +    // Frame needs to stay alive while we access the data buffer +    IInfraredFrame* iInfraredFrame = nullptr; + +    video::frame iFrame; +    cv::Mat iMatFrame; +}; + +} + + +#endif | 
