Skip to content

File plugin.cpp

File List > hand_tracking > viewer > plugin.cpp

Go to the documentation of this file

#include "plugin.hpp"

#include "illixr/data_format/opencv_data_types.hpp"
#include "illixr/data_format/shape.hpp"
#include "illixr/gl_util/obj.hpp"
#include "illixr/imgui/backends/imgui_impl_glfw.h"
#include "illixr/imgui/backends/imgui_impl_opengl3.h"
#include "illixr/shader_util.hpp"

#include 

using namespace ILLIXR;
using namespace ILLIXR::data_format;

static void glfw_error_callback(int error, const char* description) {
    spdlog::get("illixr")->error("|| glfw error_callback: {}\n|> {}", error, description);
    ILLIXR::abort();
}

[[maybe_unused]] std::string image_type_string(const image::image_type it) {
    switch (it) {
    case image::LEFT_EYE:
    case image::LEFT_EYE_PROCESSED:
        return "left";
    case image::RIGHT_EYE:
    case image::RIGHT_EYE_PROCESSED:
        return "right";
    case image::RGB:
    case image::RGB_PROCESSED:
        return "rgb";
    case image::DEPTH:
        return "depth";
    case image::CONFIDENCE:
        return "confidence";
    }
    return "";
}

viewer::viewer(const std::string& name_, phonebook* pb_)
    : plugin{name_, pb_}
    , clock_{phonebook_->lookup_impl<relative_clock>()}
    , switchboard_{pb_->lookup_impl<switchboard>()}
    , pose_{switchboard_->root_coordinates.position(), switchboard_->root_coordinates.orientation()}
    , current_frame_(nullptr) { }

void viewer::start() {
    plugin::start();
    if (!glfwInit()) {
        ILLIXR::abort("[viewer] Failed to initialize glfw");
    }

    glfwSetErrorCallback(glfw_error_callback);

    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
    glfwWindowHint(GLFW_VISIBLE, GL_TRUE);

    constexpr std::string_view glsl_version{"#version 330 core"};

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
    viewport_ = glfwCreateWindow(640 * 3 + 20, 1000, "ILLIXR Hand Tracking Viewer", nullptr, nullptr);
    if (viewport_ == nullptr) {
        spdlog::get(name_)->error("couldn't create window {}:{}", __FILE__, __LINE__);
        ILLIXR::abort();
    }

    glfwSetWindowSize(viewport_, 640 * 3 + 20, 480 * 3 + 20);

    glfwMakeContextCurrent(viewport_);

    glfwSwapInterval(1);

    const GLenum glew_err = glewInit();
    if (glew_err != GLEW_OK) {
        // spdlog::get(name)->error("GLEW Error: {}", glewGetErrorString(glew_err));
        glfwDestroyWindow(viewport_);
        ILLIXR::abort("[hand_tracking viewer] Failed to initialize GLEW");
    }

    // Initialize IMGUI context.
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();

    // Dark theme, of course.
    ImGui::StyleColorsDark();

    // Init IMGUI for OpenGL
    ImGui_ImplGlfw_InitForOpenGL(viewport_, true);
    ImGui_ImplOpenGL3_Init(glsl_version.data());

    glGenTextures(2, &(textures_[0]));
    for (unsigned int texture : textures_) {
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }

    // Construct a basic perspective projection
    // math_util::projection_fov(&basicProjection, 40.0f, 40.0f, 40.0f, 40.0f, 0.03f, 20.0f);

    glfwMakeContextCurrent(nullptr);
    switchboard_->schedule<ht::ht_frame>(id_, "ht", [this](const switchboard::ptr<const ht::ht_frame>& ht_frame, std::size_t) {
        this->make_gui(ht_frame);
    });
}

viewer::~viewer() {
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(viewport_);

    RAC_ERRNO_MSG("debugview during destructor");

    glfwTerminate();
}

void viewer::make_position_table() const {
    if (ImGui::BeginTable("Poses", 2, ImGuiTableFlags_Borders)) {
        ImGui::TableSetupColumn("Pose");
        ImGui::TableSetupColumn("x, y, z : w, x, y, z");
        ImGui::TableHeadersRow();
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0);
        ImGui::Text("Origin");
        ImGui::TableSetColumnIndex(1);
        ImGui::Text("%.2f, %.2f, %.2f : %.2f, %.2f, %.2f, %.2f", pose_.position.x(), pose_.position.y(), pose_.position.z(),
                    pose_.orientation.w(), pose_.orientation.x(), pose_.orientation.y(), pose_.orientation.z());
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0);
        ImGui::Text("Current");
        ImGui::TableSetColumnIndex(1);
        ImGui::Text("%.2f, %.2f, %.2f : %.2f, %.2f, %.2f, %.2f", current_frame_->wcs_offset.position.x(),
                    current_frame_->wcs_offset.position.y(), current_frame_->wcs_offset.position.z(),
                    current_frame_->wcs_offset.orientation.w(), current_frame_->wcs_offset.orientation.x(),
                    current_frame_->wcs_offset.orientation.y(), current_frame_->wcs_offset.orientation.z());
        ImGui::EndTable();
    }
    std::string label;
    if (ImGui::BeginTable("True Points", 2, ImGuiTableFlags_Borders)) {
        ImGui::TableNextRow();
        for (int idx = 0; idx < 2; idx++) {
            if (idx == 0) {
                label = "True Points Left Hand";
            } else {
                label = "True Points Right Hand";
            }

            ImGui::TableSetColumnIndex(idx);
            if (ImGui::BeginTable(label.c_str(), 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
                ImGui::TableSetupColumn("Point");
                ImGui::TableSetupColumn("x");
                ImGui::TableSetupColumn("y");
                ImGui::TableSetupColumn("z");
                ImGui::TableSetupColumn("Confidence");
                // ImGui::TableSetupColumn("Wx");
                // ImGui::TableSetupColumn("Wy");
                // ImGui::TableSetupColumn("Wz");
                ImGui::TableHeadersRow();
                auto points = current_frame_->hand_positions.at(static_cast<pose::side>(idx));
                bool skip   = points.points_.empty();
                for (int row = ht::WRIST; row < ht::PINKY_TIP; row++) {
                    ImGui::TableNextRow();
                    ImGui::TableSetColumnIndex(0);
                    ImGui::Text("%s", ht::point_str_map.at(row).c_str());
                    point::point pnt;
                    if (!skip) {
                        pnt = points.points_.at(row);
                    }
                    for (int i = 0; i < 3; i++) {
                        ImGui::TableSetColumnIndex(i + 1);
                        if (pnt.valid) {
                            ImGui::Text("%.3f", pnt[i]);
                        } else {
                            ImGui::Text("");
                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,
                                                   ImGui::GetColorU32(ImVec4(0.7f, 0.3f, 0.3f, 0.65f)));
                            // ImGui::TableSetColumnIndex(i + 1 + 4);
                            // ImGui::Text("");
                            // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,
                            //                        ImGui::GetColorU32(ImVec4(0.7f, 0.3f, 0.3f, 0.65f)));
                        }
                    }
                    ImGui::TableSetColumnIndex(4);
                    if (pnt.valid) {
                        ImGui::Text("%.2f", pnt.confidence);
                    } else {
                        ImGui::Text("");
                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(0.7f, 0.3f, 0.3f, 0.65f)));
                    }
                }
                ImGui::EndTable();
            }
        }
        ImGui::EndTable();
    }
}

void viewer::make_detection_table(pose::side eye, int idx, const std::string& label) const {
    if (ImGui::BeginTabItem(label.c_str())) {
        if (ImGui::BeginTable("first_det", 2, ImGuiTableFlags_Borders)) {
            ImGui::TableSetupColumn("first_det_info");
            ImGui::TableSetupColumn("first_det_img");
            ImGui::TableNextRow();

            ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
            ImGui::TableSetColumnIndex(0);

            if (ImGui::BeginTable("Palms", 6, flags)) {
                ImGui::TableSetupColumn("");
                ImGui::TableSetupColumn("Unit");
                ImGui::TableSetupColumn("Center");
                ImGui::TableSetupColumn("Width");
                ImGui::TableSetupColumn("Height");
                ImGui::TableSetupColumn("Rotation");
                ImGui::TableHeadersRow();
                for (auto i : ht::hand_map) {
                    shapes::rect current_rect = current_frame_->detections.at(eye).palms.at(i);
                    ImGui::TableNextRow();
                    ImGui::TableSetColumnIndex(0);
                    ImGui::Text("%s", (i == 0) ? "Left palm" : "Right palm");
                    ImGui::TableSetColumnIndex(1);
                    ImGui::Text("%s", "mm");
                    if (current_rect.valid) {
                        ImGui::TableSetColumnIndex(2);
                        ImGui::Text("%.2f, %.2f", current_rect.x_center, current_rect.y_center);
                        ImGui::TableSetColumnIndex(3);
                        ImGui::Text("%.2f", current_rect.width);
                        ImGui::TableSetColumnIndex(4);
                        ImGui::Text("%.2f", current_rect.height);
                        ImGui::TableSetColumnIndex(5);
                        ImGui::Text("%.2f", current_rect.rotation);
                    }
                }
                ImGui::EndTable();
            }
            if (ImGui::BeginTable("Hands", 6, flags)) {
                ImGui::TableSetupColumn("");
                ImGui::TableSetupColumn("Unit");
                ImGui::TableSetupColumn("Center");
                ImGui::TableSetupColumn("Width");
                ImGui::TableSetupColumn("Height");
                ImGui::TableSetupColumn("Rotation");
                ImGui::TableHeadersRow();
                for (auto i : ht::hand_map) {
                    shapes::rect current_rect = current_frame_->detections.at(eye).hands.at(i);
                    ImGui::TableNextRow();
                    ImGui::TableSetColumnIndex(0);
                    ImGui::Text("%s", (i == 0) ? "Left hand" : "Right hand");
                    ImGui::TableSetColumnIndex(1);
                    ImGui::Text("%s", "mm");
                    if (current_rect.valid) {
                        ImGui::TableSetColumnIndex(2);
                        ImGui::Text("%.2f, %.2f", current_rect.x_center, current_rect.y_center);
                        ImGui::TableSetColumnIndex(3);
                        ImGui::Text("%.2f", current_rect.width);
                        ImGui::TableSetColumnIndex(4);
                        ImGui::Text("%.2f", current_rect.height);
                        ImGui::TableSetColumnIndex(5);
                        ImGui::Text("%.2f", current_rect.rotation);
                    }
                }
                ImGui::EndTable();
            }
            if (ImGui::BeginTable("Hand Points", 3, flags)) {
                ImGui::TableSetupColumn("Point");
                ImGui::TableSetupColumn("Left");
                ImGui::TableSetupColumn("Right");
                ImGui::TableHeadersRow();
                ImGui::TableNextRow();
                ImGui::TableSetColumnIndex(0);
                ImGui::Text("%s", "Confidence");
                ImGui::TableSetColumnIndex(1);
                ImGui::Text("%.2f", current_frame_->detections.at(eye).confidence.at(pose::LEFT));
                ImGui::TableSetColumnIndex(2);
                ImGui::Text("%.2f", current_frame_->detections.at(eye).confidence.at(pose::RIGHT));
                for (int row = ht::WRIST; row <= ht::PINKY_TIP; row++) {
                    ImGui::TableNextRow();
                    ImGui::TableSetColumnIndex(0);
                    ImGui::Text("%s", ht::point_str_map.at(row).c_str());
                    ImGui::TableSetColumnIndex(1);
                    auto pnt = current_frame_->detections.at(eye).points.at(pose::LEFT).points_[row];
                    if (pnt.valid) {
                        ImGui::Text("%.2f, %.2f, %.2f", pnt.x(), pnt.y(), pnt.z());
                    } else {
                        ImGui::Text("%s", "");
                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(0.7f, 0.3f, 0.3f, 0.65f)));
                    }
                    ImGui::TableSetColumnIndex(2);
                    pnt = current_frame_->detections.at(eye).points.at(pose::RIGHT).points_[row];
                    if (pnt.valid) {
                        ImGui::Text("%.2f, %.2f, %.2f", pnt.x(), pnt.y(), pnt.z());
                    } else {
                        ImGui::Text("%s", "");
                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(0.7f, 0.3f, 0.3f, 0.65f)));
                    }
                }

                ImGui::EndTable();
            }
            ImGui::TableSetColumnIndex(1);
            ImGui::Image((void*) (intptr_t) textures_[idx], ImVec2(640, 480));
            ImGui::EndTable();
        }
        ImGui::EndTabItem();
    }
}

void viewer::make_gui(const switchboard::ptr<const ht::ht_frame>& frame) {
    glfwMakeContextCurrent(viewport_);
    glfwPollEvents();
    current_frame_ = frame.get();

    std::vector<image::image_type> found_types;
    cv::Mat                        raw_img[2];
    int                            last_idx = 0;

    if (current_frame_->find(image::LEFT_EYE_PROCESSED) != current_frame_->images.end()) {
        found_types.push_back(image::LEFT_EYE_PROCESSED);
        raw_img[0] = current_frame_->at(image::LEFT_EYE).clone();
        last_idx++;
    }

    if (current_frame_->find(image::RIGHT_EYE_PROCESSED) != current_frame_->images.end()) {
        found_types.push_back(image::RIGHT_EYE_PROCESSED);
        raw_img[last_idx] = current_frame_->at(image::RIGHT_EYE).clone();
    }

    if (found_types.empty() && current_frame_->find(image::RGB_PROCESSED) != current_frame_->images.end()) {
        found_types.push_back(image::RGB_PROCESSED);
        raw_img[0] = current_frame_->at(image::RGB).clone();
    }

    cv::Mat processed[2];
    cv::Mat combined[2], flattened[2];
    int     d_width, d_height;
    glfwGetFramebufferSize(viewport_, &d_width, &d_height);
    glViewport(0, 0, d_width, d_height);
    glClearColor(clear_color_.x * clear_color_.w, clear_color_.y * clear_color_.w, clear_color_.z * clear_color_.w,
                 clear_color_.w);
    glClear(GL_COLOR_BUFFER_BIT);

    ImGui_ImplOpenGL3_NewFrame();

    ImGui_ImplGlfw_NewFrame();

    ImGui::NewFrame();
    float proc_time;
    if (current_frame_->detections.count(pose::LEFT) == 1 && current_frame_->detections.count(pose::RIGHT) == 1) {
        raw_img[0]     = current_frame_->at(image::LEFT_EYE).clone();
        raw_img[1]     = current_frame_->at(image::RIGHT_EYE).clone();
        enabled_right_ = true;
        tab_label_     = "Left Eye Raw";
        single_eye_    = pose::LEFT;
        detections_    = {image::LEFT_EYE_PROCESSED, image::RIGHT_EYE_PROCESSED};
        proc_time = (float) std::max(current_frame_->detections.at(static_cast<const pose::side>(image::LEFT_EYE)).proc_time,
                                     current_frame_->detections.at(static_cast<const pose::side>(image::RIGHT_EYE)).proc_time) /
            (1000.f * 1000.f);
    } else {
        image::image_type img_typ;
        if (current_frame_->detections.count(pose::LEFT) == 1) {
            single_eye_ = pose::LEFT;
            detections_ = {image::LEFT_EYE_PROCESSED};
            img_typ     = image::LEFT_EYE;
            proc_time   = (float) current_frame_->detections.at(static_cast<const pose::side>(image::LEFT_EYE)).proc_time /
                (1000.f * 1000.f);
        } else if (current_frame_->detections.count(pose::RIGHT) == 1) {
            single_eye_ = pose::RIGHT;
            detections_ = {image::RIGHT_EYE_PROCESSED};
            img_typ     = image::RIGHT_EYE;
            proc_time   = (float) current_frame_->detections.at(static_cast<const pose::side>(image::RIGHT_EYE)).proc_time /
                (1000.f * 1000.f);
        } else {
            return;
        }
        raw_img[0] = current_frame_->at(img_typ).clone();
        raw_img[1].release();
        enabled_right_ = false;
        tab_label_     = "Raw";
    }

    for (size_t i = 0; i < 2; i++) {
        if (i == 1 && !enabled_right_)
            break;
        processed[i] = current_frame_->at(detections_[i]).clone();
        cv::Mat mask, inv, r;
        cv::cvtColor(processed[i], mask, cv::COLOR_RGBA2GRAY);
        cv::threshold(mask, mask, 10, 255, cv::THRESH_BINARY);
        cv::bitwise_not(mask, mask);
        cv::bitwise_and(raw_img[i], raw_img[i], r, mask);
        cv::cvtColor(processed[i], flattened[i], cv::COLOR_RGBA2RGB);
        combined[i] = flattened[i] + r;
        glBindTexture(GL_TEXTURE_2D, textures_[i]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, combined[i].cols, combined[i].rows, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     combined[i].ptr());
    }

    if (ImGui::Begin("Hand Detections")) {
        if (ImGui::BeginMenuBar()) {
            if (ImGui::BeginMenu("File")) {
                if (ImGui::MenuItem("Exit")) { }
                ImGui::EndMenu();
            }
            ImGui::EndMenuBar();
        }
        if (ImGui::BeginTable("times_table", 2, ImGuiTableFlags_Borders)) {
            ImGui::TableSetupColumn("title_label");
            ImGui::TableSetupColumn("time_label");
            ImGui::TableNextRow();
            ImGui::TableSetColumnIndex(0);
            ImGui::Text("%s", "Processing time (ms)");
            ImGui::TableSetColumnIndex(1);
            ImGui::Text("%f", proc_time);
            ImGui::EndTable();
        }
        ImGui::Separator();

        if (ImGui::BeginTabBar("TabBar")) {
            make_detection_table(single_eye_, 0, tab_label_);
            if (enabled_right_) {
                make_detection_table(pose::RIGHT, 1, "Right Eye Raw");
            }
            if (ImGui::BeginTabItem("True Position")) {
                make_position_table();
                ImGui::EndTabItem();
            }
            ImGui::EndTabBar();
        }
        ImGui::End();
    }
    ImGui::Render();

    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    glfwSwapBuffers(viewport_);
}

PLUGIN_MAIN(viewer)