質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.63%

Kinectでの関節の座標取得

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,014

kinect-jongen

score 13

 前提・実現したいこと

C++でKinectの開発を行っています。
主に、秀和システムの「KINECT for Windows SDKプログラミング」という本を参考にして開発をしています。

私はKinectを用いて、人の手や足のピンポイントの座標を取得してその座標を配列に格納したいと思っています。

 発生している問題・エラーメッセージ

身体の各関節の座標を個別で取得する方法が分からなくて困っています。
本やインターネットで調べても詳しい方法が出てこないです。

ほかにこのような開発を行ったことがある方など助けていただけたら嬉しいです。

 該当のソースコード

#include <iostream>
#include <sstream>

#include <Kinect.h>
#include <opencv2\opencv.hpp>

#include <atlbase.h>

#define ERROR_CHECK( ret )  \
    if ( (ret) != S_OK ) {    \
        std::stringstream ss;    \
        ss << "failed " #ret " " << std::hex << ret << std::endl;            \
        throw std::runtime_error( ss.str().c_str() );            \
    }

//Kinect動作用

class KinectApp{

private:

    CComPtr<IKinectSensor> kinect = nullptr;

    //カラー画像用
    CComPtr<IColorFrameReader> colorFrameReader = nullptr;

    int colorWidth;
    int colorHeight;
    unsigned int colorBytesPerPixel;

    ColorImageFormat colorFormat = ColorImageFormat::ColorImageFormat_Bgra;

    std::vector<BYTE> colorBuffer;



    //body用
    CComPtr<IBodyFrameReader> bodyFrameReader = nullptr;
    IBody* bodies[6];

public:

    ~KinectApp(){
        // Kinectの動作を終了する
        if ( kinect != nullptr ){
            kinect->Close();
        }
    }

    // 初期化
    void initialize(){
        // デフォルトのKinectを取得する
        ERROR_CHECK( ::GetDefaultKinectSensor( &kinect ) );
        ERROR_CHECK( kinect->Open() );

        //カラーリーダーを取得する
        CComPtr<IColorFrameSource> colorFrameSource;
        ERROR_CHECK(kinect->get_ColorFrameSource(&colorFrameSource));
        ERROR_CHECK(colorFrameSource->OpenReader(&colorFrameReader));

        // デフォルトのカラー画像のサイズを取得する
        CComPtr<IFrameDescription> defaultColorFrameDescription;
        ERROR_CHECK(colorFrameSource->get_FrameDescription(&defaultColorFrameDescription));
        ERROR_CHECK(defaultColorFrameDescription->get_Width(&colorWidth));
        ERROR_CHECK(defaultColorFrameDescription->get_Height(&colorHeight));
        ERROR_CHECK(defaultColorFrameDescription->get_BytesPerPixel(&colorBytesPerPixel));
        std::cout << "default : " << colorWidth << ", " << colorHeight << ", " << colorBytesPerPixel << std::endl;

        // カラー画像のサイズを取得する
        CComPtr<IFrameDescription> colorFrameDescription;
        ERROR_CHECK(colorFrameSource->CreateFrameDescription(colorFormat, &colorFrameDescription));
        ERROR_CHECK(colorFrameDescription->get_Width(&colorWidth));
        ERROR_CHECK(colorFrameDescription->get_Height(&colorHeight));
        ERROR_CHECK(colorFrameDescription->get_BytesPerPixel(&colorBytesPerPixel));
        std::cout << "create  : " << colorWidth << ", " << colorHeight << ", " << colorBytesPerPixel << std::endl;

        // バッファーを作成する
        colorBuffer.resize(colorWidth * colorHeight * colorBytesPerPixel);

        // ボディリーダーを取得する
        CComPtr<IBodyFrameSource> bodyFrameSource;
        ERROR_CHECK( kinect->get_BodyFrameSource( &bodyFrameSource ) );
        ERROR_CHECK( bodyFrameSource->OpenReader( &bodyFrameReader ) );

        for ( auto& body : bodies ){
            body = nullptr;
        }
    }

    void run(){
        while ( 1 ) {
            update();
            draw();

            auto key = cv::waitKey( 10 );
            if ( key == 'q' ){
                break;
            }
        }
    }

private:

    // データの更新処理
    void update(){
        updateBodyFrame();
        updateColorFrame();
    }

    // ボディフレームの更新
    void updateBodyFrame(){
        // フレームを取得する
        CComPtr<IBodyFrame> bodyFrame;
        auto ret = bodyFrameReader->AcquireLatestFrame( &bodyFrame );
        if ( FAILED( ret ) ){
            return;
        }

        // 前回のBodyを解放する
        for ( auto& body : bodies ){
            if ( body != nullptr ){
                body->Release();
                body = nullptr;
            }
        }

        // データを取得する
        ERROR_CHECK( bodyFrame->GetAndRefreshBodyData( 6, &bodies[0] ) );
    }

    void draw(){
        drawBodyFrame();
        drawColorFrame();
    }

    void drawBodyFrame(){
        // 関節の座標をDepth座標系で表示する
        cv::Mat bodyImage = cv::Mat::zeros( 424, 512, CV_8UC4 );

        for ( auto body : bodies ){
            if ( body == nullptr ){
                continue;
            }

            BOOLEAN isTracked = false;
            ERROR_CHECK( body->get_IsTracked( &isTracked ) );
            if ( !isTracked ) {
                continue;
            }

            // 関節の位置を表示する
            Joint joints[JointType::JointType_Count];
            body->GetJoints( JointType::JointType_Count, joints );
            for ( auto joint : joints ) {
                // 手の位置が追跡状態
                if ( joint.TrackingState == TrackingState::TrackingState_Tracked ) {
                    drawEllipse( bodyImage, joint, 10, cv::Scalar( 255, 0, 0 ) );
                }
                // 手の位置が推測状態
                else if ( joint.TrackingState == TrackingState::TrackingState_Inferred ) {
                    drawEllipse( bodyImage, joint, 10, cv::Scalar( 255, 255, 0 ) );
                }
            }
        }

        cv::imshow( "Body Image", bodyImage );
    }

    void drawEllipse( cv::Mat& bodyImage, const Joint& joint, int r, const cv::Scalar& color ){
        // カメラ座標系をDepth座標系に変換する
        CComPtr<ICoordinateMapper> mapper;
        ERROR_CHECK( kinect->get_CoordinateMapper( &mapper ) );

        DepthSpacePoint point;
        mapper->MapCameraPointToDepthSpace( joint.Position, &point );

        cv::circle( bodyImage, cv::Point( point.X, point.Y ), r, color, -1 );
    }

    // カラーフレームの更新
    void updateColorFrame(){
        // フレームを取得する
        CComPtr<IColorFrame> colorFrame;
        auto ret = colorFrameReader->AcquireLatestFrame(&colorFrame);
        if (FAILED(ret)) {
            return;
        }

        // 指定の形式でデータを取得する
        ERROR_CHECK(colorFrame->CopyConvertedFrameDataToArray(colorBuffer.size(), &colorBuffer[0], colorFormat));
    }

    // カラーデータの表示処理
    void drawColorFrame(){

#if 0
        // カラーデータを表示する
        cv::Mat colorImage(colorHeight, colorWidth, CV_8UC4, &colorBuffer[0]);
        cv::imshow("Color Image", colorImage);

#else
        cv::Mat colorImage(colorHeight, colorWidth, CV_8UC4, &colorBuffer[0]);
        cv::Mat harfImage;
        cv::resize(colorImage, harfImage, cv::Size(), 0.5, 0.5);
        cv::imshow("Harf Image", harfImage);

#endif

    }
};

void main(){
    try {
        KinectApp app;
        app.initialize();
        app.run();
    }
    catch ( std::exception& ex ){
        std::cout << ex.what() << std::endl;
    }
}

 試したこと

void drawEllipse中のjointをJointTypeの中のそれぞれの関節に対応するものにあてはめたりしたのですが成功しませんでした。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

以下の部分を改造すると良いです。

// 関節の位置を表示する
Joint joints[JointType::JointType_Count];
body->GetJoints( JointType::JointType_Count, joints );
for ( auto joint : joints ) {
    ...
}

リファレンスを見ると、Joint構造体のメンバを知ることができます。

未検証ですが、次のようにループを書いていけば良いでしょう。

for(const auto& joint: joints) {  // 無駄なコピーはしない方が良いです
    switch(joint.JointType) {
    case JointType.HandLeft:
        座標を記録する
        break;
    ...
    }
}

座標値の取得方法・解釈の仕方については、次の記事が参考になりそうです。
Kinect開発備忘録(1) | Kinect | アプリ関連ニュース | ギガスジャパン

座標の記録については、std::arrayを使うのが安直で簡単だと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/06 11:16

    回答ありがとうございます!
    参考にさせていただきます。

    回答の中のswitch文の中に
    case JointType_HandRight:
    mapper->MapCameraPointToDepthSpace(joint.Position, &HandRightPoint);
    HandRightRecord[j] = HandRightPoint;
    j++;
    break;
    }
    という文を書いてみましたが、HandRightPointのところに「"DepthSpacePoint"から"double"への適切な変換関数が存在しません」と表示されてしまいます。
    HandRightRecordは
    std::array<double, 50000> HandRightRecord;
    と宣言しました。

    エラーの文の1つ上の文でやっているつもりですがどのようにこのエラーを消せばよろしいでしょうか。

    このエラーを消したところで値の保存はできているでしょうか。

    初心者でもうしわけございません

    キャンセル

  • 2018/12/06 13:30

    とりあえずエラーを消すだけなら、HandRightRecordの型をstd::array<DepthSpacePoint, 50000>にしてみるとか。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る