2019-09-23 10:46:06 +00:00
|
|
|
// Copyright 2019, Collabora, Ltd.
|
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/*!
|
|
|
|
* @file
|
|
|
|
* @brief Calibration gui scene.
|
|
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
|
|
* @ingroup gui
|
|
|
|
*/
|
|
|
|
|
2020-03-02 15:44:00 +00:00
|
|
|
#include "xrt/xrt_config_have.h"
|
2019-09-23 10:46:06 +00:00
|
|
|
#include "util/u_var.h"
|
|
|
|
#include "util/u_misc.h"
|
|
|
|
#include "util/u_sink.h"
|
|
|
|
|
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
#include "tracking/t_tracking.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "xrt/xrt_frame.h"
|
|
|
|
#include "xrt/xrt_prober.h"
|
|
|
|
#include "xrt/xrt_tracking.h"
|
|
|
|
#include "xrt/xrt_frameserver.h"
|
|
|
|
|
|
|
|
#include "gui_common.h"
|
|
|
|
#include "gui_imgui.h"
|
|
|
|
|
2019-11-22 11:41:36 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
2020-03-02 15:44:00 +00:00
|
|
|
|
2020-01-18 21:05:08 +00:00
|
|
|
enum camera_type
|
|
|
|
{
|
|
|
|
CAM_REGULAR,
|
|
|
|
CAM_PS4,
|
|
|
|
CAM_LEAP_MOTION,
|
|
|
|
};
|
2019-09-23 10:46:06 +00:00
|
|
|
|
|
|
|
struct calibration_scene
|
|
|
|
{
|
|
|
|
struct gui_scene base;
|
2019-09-27 19:11:17 +00:00
|
|
|
|
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
struct t_calibration_params params;
|
2020-01-14 20:39:12 +00:00
|
|
|
struct t_calibration_status status;
|
2019-09-27 19:11:17 +00:00
|
|
|
#endif
|
|
|
|
|
2019-09-23 10:46:06 +00:00
|
|
|
struct xrt_frame_context *xfctx;
|
2019-09-27 19:11:17 +00:00
|
|
|
struct xrt_fs *xfs;
|
|
|
|
size_t mode;
|
2020-01-17 14:21:58 +00:00
|
|
|
|
2020-01-18 21:05:08 +00:00
|
|
|
int camera_type;
|
2019-09-23 10:46:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Internal functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
draw_texture(struct gui_ogl_texture *tex, bool header)
|
|
|
|
{
|
|
|
|
if (tex == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_None;
|
|
|
|
if (header && !igCollapsingHeader(tex->name, flags)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gui_ogl_sink_update(tex);
|
|
|
|
|
|
|
|
int w = tex->w / (tex->half ? 2 : 1);
|
|
|
|
int h = tex->h / (tex->half ? 2 : 1);
|
|
|
|
|
2019-11-14 17:39:13 +00:00
|
|
|
ImVec2 size = {(float)w, (float)h};
|
2019-09-23 10:46:06 +00:00
|
|
|
ImVec2 uv0 = {0, 0};
|
|
|
|
ImVec2 uv1 = {1, 1};
|
|
|
|
ImVec4 white = {1, 1, 1, 1};
|
|
|
|
ImTextureID id = (ImTextureID)(intptr_t)tex->id;
|
|
|
|
igImage(id, size, uv0, uv1, white, white);
|
|
|
|
igText("Sequence %u", (uint32_t)tex->seq);
|
|
|
|
|
|
|
|
char temp[512];
|
|
|
|
snprintf(temp, 512, "Half (%s)", tex->name);
|
|
|
|
igCheckbox(temp, &tex->half);
|
|
|
|
}
|
|
|
|
|
2020-01-14 20:39:12 +00:00
|
|
|
static void
|
|
|
|
render_progress(struct calibration_scene *cs)
|
|
|
|
{
|
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
if (cs->status.finished) {
|
|
|
|
|
|
|
|
igText(
|
|
|
|
"Calibration complete - showing preview of undistortion.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const ImVec2 progress_dims = {150, 0};
|
|
|
|
if (cs->status.cooldown > 0) {
|
|
|
|
// This progress bar intentionally counts down to 0.
|
|
|
|
float cooldown = (float)(cs->status.cooldown) /
|
|
|
|
(float)cs->params.num_cooldown_frames;
|
|
|
|
igText("Move to a new position");
|
|
|
|
igProgressBar(cooldown, progress_dims, "Move to new position");
|
|
|
|
} else if (!cs->status.found) {
|
|
|
|
// This progress bar is always zero:
|
|
|
|
// comes before "hold still"
|
|
|
|
igText("Show board");
|
|
|
|
igProgressBar(0.0f, progress_dims, "Show board");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// This progress bar counts up from zero before
|
|
|
|
// capturing.
|
|
|
|
int waits_complete =
|
|
|
|
cs->params.num_wait_for - cs->status.waits_remaining;
|
|
|
|
float hold_completion =
|
|
|
|
(float)waits_complete / (float)cs->params.num_wait_for;
|
|
|
|
if (cs->status.waits_remaining == 0) {
|
|
|
|
igText("Capturing and processing!");
|
|
|
|
} else {
|
|
|
|
igText("Hold still! (%i/%i)", waits_complete,
|
|
|
|
cs->params.num_wait_for);
|
|
|
|
}
|
|
|
|
igProgressBar(hold_completion, progress_dims, "Hold still!");
|
|
|
|
}
|
|
|
|
float capture_completion = ((float)cs->status.num_collected) /
|
|
|
|
(float)cs->params.num_collect_total;
|
|
|
|
igText("Overall progress: %i of %i frames captured",
|
|
|
|
cs->status.num_collected, cs->params.num_collect_total);
|
|
|
|
igProgressBar(capture_completion, progress_dims, NULL);
|
|
|
|
|
|
|
|
#else
|
|
|
|
// Unused
|
|
|
|
(void)cs;
|
|
|
|
#endif // XRT_HAVE_OPENCV
|
|
|
|
}
|
|
|
|
|
2020-03-03 23:35:14 +00:00
|
|
|
XRT_MAYBE_UNUSED static void
|
2019-10-09 15:22:34 +00:00
|
|
|
scene_render_video(struct gui_scene *scene, struct gui_program *p)
|
2019-09-23 10:46:06 +00:00
|
|
|
{
|
|
|
|
struct calibration_scene *cs = (struct calibration_scene *)scene;
|
|
|
|
|
|
|
|
igBegin("Calibration", NULL, 0);
|
|
|
|
|
2020-01-14 20:39:12 +00:00
|
|
|
// Manipulated textures
|
2019-09-23 10:46:06 +00:00
|
|
|
draw_texture(p->texs[0], false);
|
2020-01-14 20:39:12 +00:00
|
|
|
|
|
|
|
// Progress widgets
|
|
|
|
render_progress(cs);
|
|
|
|
|
|
|
|
// Raw textures
|
2019-09-23 10:46:06 +00:00
|
|
|
draw_texture(p->texs[1], true);
|
|
|
|
|
|
|
|
for (size_t i = 2; i < ARRAY_SIZE(p->texs); i++) {
|
|
|
|
draw_texture(p->texs[i], true);
|
|
|
|
}
|
|
|
|
|
|
|
|
igSeparator();
|
|
|
|
|
|
|
|
static ImVec2 button_dims = {0, 0};
|
|
|
|
if (igButton("Exit", button_dims)) {
|
|
|
|
gui_scene_delete_me(p, &cs->base);
|
|
|
|
}
|
|
|
|
|
|
|
|
igEnd();
|
|
|
|
}
|
|
|
|
|
2019-09-27 19:11:17 +00:00
|
|
|
static void
|
2019-10-09 15:22:34 +00:00
|
|
|
scene_render_select(struct gui_scene *scene, struct gui_program *p)
|
2019-09-27 19:11:17 +00:00
|
|
|
{
|
|
|
|
struct calibration_scene *cs = (struct calibration_scene *)scene;
|
|
|
|
|
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
igBegin("Params", NULL, 0);
|
|
|
|
|
2019-11-20 23:48:54 +00:00
|
|
|
// clang-format off
|
2020-01-18 21:05:08 +00:00
|
|
|
igComboStr("Type", &cs->camera_type, "Regular\0PS4\0Leap Motion Controller\0\0", -1);
|
|
|
|
|
|
|
|
switch (cs->camera_type) {
|
|
|
|
case CAM_REGULAR:
|
2020-01-17 14:21:58 +00:00
|
|
|
igCheckbox("Fisheye Camera", &cs->params.use_fisheye);
|
|
|
|
igCheckbox("Stereo (Side-By-Side) Camera", &cs->params.stereo_sbs);
|
2020-01-18 21:05:08 +00:00
|
|
|
break;
|
|
|
|
case CAM_PS4:
|
2020-01-17 14:21:58 +00:00
|
|
|
cs->params.use_fisheye = false;
|
|
|
|
cs->params.stereo_sbs = true;
|
2020-01-18 21:05:08 +00:00
|
|
|
break;
|
|
|
|
case CAM_LEAP_MOTION:
|
|
|
|
cs->params.use_fisheye = true;
|
|
|
|
cs->params.stereo_sbs = true;
|
|
|
|
break;
|
2020-01-17 14:21:58 +00:00
|
|
|
}
|
2019-11-20 23:46:42 +00:00
|
|
|
|
|
|
|
igSeparator();
|
2020-01-14 20:31:53 +00:00
|
|
|
igCheckbox("Mirror on-screen preview", &cs->params.mirror_rgb_image);
|
2020-01-16 23:04:14 +00:00
|
|
|
igCheckbox("Save images", &cs->params.save_images);
|
2019-11-20 21:58:28 +00:00
|
|
|
|
2019-11-22 14:20:19 +00:00
|
|
|
igSeparator();
|
2020-01-17 14:21:58 +00:00
|
|
|
igCheckbox("Load images", &cs->params.load.enabled);
|
2019-11-22 14:20:19 +00:00
|
|
|
if (cs->params.load.enabled) {
|
|
|
|
igInputInt("# images", &cs->params.load.num_images, 1, 5, 0);
|
|
|
|
}
|
|
|
|
|
2019-11-20 21:58:28 +00:00
|
|
|
igSeparator();
|
2019-11-22 12:20:53 +00:00
|
|
|
igInputInt("Cooldown for # frames", &cs->params.num_cooldown_frames, 1, 5, 0);
|
|
|
|
igInputInt("Wait for # frames (steady)", &cs->params.num_wait_for, 1, 5, 0);
|
2019-11-20 23:48:54 +00:00
|
|
|
igInputInt("Collect # measurements", &cs->params.num_collect_total, 1, 5, 0);
|
|
|
|
igInputInt("Collect in groups of #", &cs->params.num_collect_restart, 1, 5, 0);
|
2019-09-27 19:11:17 +00:00
|
|
|
|
2019-11-20 21:58:28 +00:00
|
|
|
igSeparator();
|
2019-11-22 11:41:36 +00:00
|
|
|
igComboStr("Board type", (int*)&cs->params.pattern, "Checkers\0Circles\0Asymetric Circles\0\0", 3);
|
|
|
|
switch (cs->params.pattern) {
|
|
|
|
case T_BOARD_CHECKERS:
|
|
|
|
igInputInt("Checkerboard Rows", &cs->params.checkers.rows, 1, 5, 0);
|
|
|
|
igInputInt("Checkerboard Columns", &cs->params.checkers.cols, 1, 5, 0);
|
|
|
|
igInputFloat("Checker Size (m)", &cs->params.checkers.size_meters, 0.0005, 0.001, NULL, 0);
|
|
|
|
igCheckbox("Subpixel", &cs->params.checkers.subpixel_enable);
|
|
|
|
igInputInt("Subpixel Search Size", &cs->params.checkers.subpixel_size, 1, 5, 0);
|
|
|
|
break;
|
|
|
|
case T_BOARD_CIRCLES:
|
|
|
|
igInputInt("Circle Rows", &cs->params.circles.rows, 1, 5, 0);
|
|
|
|
igInputInt("Circle Columns", &cs->params.circles.cols, 1, 5, 0);
|
|
|
|
igInputFloat("Spacing (m)", &cs->params.circles.distance_meters, 0.0005, 0.001, NULL, 0);
|
|
|
|
break;
|
|
|
|
case T_BOARD_ASYMMETRIC_CIRCLES:
|
|
|
|
igInputInt("Circle Rows", &cs->params.asymmetric_circles.rows, 1, 5, 0);
|
|
|
|
igInputInt("Circle Columns", &cs->params.asymmetric_circles.cols, 1, 5, 0);
|
|
|
|
igInputFloat("Diagonal spacing (m)", &cs->params.asymmetric_circles.diagonal_distance_meters, 0.0005, 0.001, NULL, 0);
|
|
|
|
break;
|
|
|
|
default: assert(false);
|
|
|
|
}
|
2019-11-20 23:48:54 +00:00
|
|
|
// clang-format on
|
2019-11-20 21:58:28 +00:00
|
|
|
|
2019-09-27 19:11:17 +00:00
|
|
|
static ImVec2 button_dims = {0, 0};
|
2019-11-20 21:58:28 +00:00
|
|
|
igSeparator();
|
2019-09-27 19:11:17 +00:00
|
|
|
bool pressed = igButton("Done", button_dims);
|
|
|
|
igEnd();
|
|
|
|
|
|
|
|
if (!pressed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cs->base.render = scene_render_video;
|
|
|
|
|
|
|
|
struct xrt_frame_sink *rgb = NULL;
|
|
|
|
struct xrt_frame_sink *raw = NULL;
|
|
|
|
struct xrt_frame_sink *cali = NULL;
|
|
|
|
|
|
|
|
p->texs[p->num_texs++] =
|
|
|
|
gui_ogl_sink_create("Calibration", cs->xfctx, &rgb);
|
2020-01-17 22:02:21 +00:00
|
|
|
u_sink_create_to_r8g8b8_or_l8(cs->xfctx, rgb, &rgb);
|
2019-09-27 19:11:17 +00:00
|
|
|
u_sink_queue_create(cs->xfctx, rgb, &rgb);
|
|
|
|
|
|
|
|
p->texs[p->num_texs++] = gui_ogl_sink_create("Raw", cs->xfctx, &raw);
|
2020-01-17 22:02:21 +00:00
|
|
|
u_sink_create_to_r8g8b8_or_l8(cs->xfctx, raw, &raw);
|
2019-09-27 19:11:17 +00:00
|
|
|
u_sink_queue_create(cs->xfctx, raw, &raw);
|
|
|
|
|
2020-01-14 20:39:12 +00:00
|
|
|
t_calibration_stereo_create(cs->xfctx, &cs->params, &cs->status, rgb,
|
|
|
|
&cali);
|
2019-09-27 19:11:17 +00:00
|
|
|
u_sink_split_create(cs->xfctx, raw, cali, &cali);
|
2020-01-18 21:05:08 +00:00
|
|
|
u_sink_deinterleaver_create(cs->xfctx, cali, &cali);
|
|
|
|
u_sink_queue_create(cs->xfctx, cali, &cali);
|
2020-01-17 21:50:43 +00:00
|
|
|
|
|
|
|
// Just after the camera create a quirk stream.
|
|
|
|
struct u_sink_quirk_params qp;
|
|
|
|
U_ZERO(&qp);
|
|
|
|
qp.stereo_sbs = cs->params.stereo_sbs;
|
2020-01-18 21:05:08 +00:00
|
|
|
qp.ps4_cam = cs->camera_type == CAM_PS4;
|
|
|
|
qp.leap_motion = cs->camera_type == CAM_LEAP_MOTION;
|
2020-01-17 21:50:43 +00:00
|
|
|
u_sink_quirk_create(cs->xfctx, cali, &qp, &cali);
|
2019-09-27 19:11:17 +00:00
|
|
|
|
|
|
|
// Now that we have setup a node graph, start it.
|
|
|
|
xrt_fs_stream_start(cs->xfs, cali, cs->mode);
|
|
|
|
#else
|
|
|
|
gui_scene_delete_me(p, &cs->base);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-09-23 10:46:06 +00:00
|
|
|
static void
|
2019-10-09 15:22:34 +00:00
|
|
|
scene_destroy(struct gui_scene *scene, struct gui_program *p)
|
2019-09-23 10:46:06 +00:00
|
|
|
{
|
|
|
|
struct calibration_scene *cs = (struct calibration_scene *)scene;
|
|
|
|
|
|
|
|
if (cs->xfctx != NULL) {
|
|
|
|
xrt_frame_context_destroy_nodes(cs->xfctx);
|
|
|
|
cs->xfctx = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 'Exported' functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2019-10-09 15:22:34 +00:00
|
|
|
gui_scene_calibrate(struct gui_program *p,
|
2019-09-23 10:46:06 +00:00
|
|
|
struct xrt_frame_context *xfctx,
|
|
|
|
struct xrt_fs *xfs,
|
|
|
|
size_t mode)
|
|
|
|
{
|
|
|
|
struct calibration_scene *cs = U_TYPED_CALLOC(struct calibration_scene);
|
|
|
|
|
2019-09-27 19:11:17 +00:00
|
|
|
cs->base.render = scene_render_select;
|
2019-09-23 10:46:06 +00:00
|
|
|
cs->base.destroy = scene_destroy;
|
|
|
|
cs->xfctx = xfctx;
|
2019-09-27 19:11:17 +00:00
|
|
|
cs->xfs = xfs;
|
|
|
|
cs->mode = mode;
|
2019-09-23 10:46:06 +00:00
|
|
|
|
2020-01-17 12:05:58 +00:00
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
t_calibration_params_default(&cs->params);
|
2020-01-17 14:21:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pre-quirk some known cameras.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// PS4 Camera.
|
|
|
|
if (strcmp(xfs->name, "USB Camera-OV580: USB Camera-OV") == 0) {
|
|
|
|
// It's one speedy camera. :)
|
|
|
|
cs->params.num_cooldown_frames = 240;
|
|
|
|
cs->params.num_wait_for = 10;
|
|
|
|
cs->params.stereo_sbs = true;
|
2020-01-18 21:05:08 +00:00
|
|
|
cs->camera_type = CAM_PS4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leap Motion.
|
|
|
|
if (strcmp(xfs->name, "Leap Motion Controller") == 0) {
|
|
|
|
cs->params.use_fisheye = true;
|
|
|
|
cs->params.stereo_sbs = true;
|
|
|
|
cs->camera_type = CAM_LEAP_MOTION;
|
2020-01-17 14:21:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Valve Index and ELP Stereo Camera.
|
|
|
|
if (strcmp(xfs->name, "3D Camera: eTronVideo") == 0 ||
|
|
|
|
strcmp(xfs->name, "3D USB Camera: 3D USB Camera") == 0) {
|
|
|
|
cs->params.use_fisheye = true;
|
2020-01-18 21:05:08 +00:00
|
|
|
cs->params.stereo_sbs = true;
|
|
|
|
cs->camera_type = CAM_REGULAR;
|
2020-01-17 14:21:58 +00:00
|
|
|
}
|
2020-01-18 21:05:08 +00:00
|
|
|
|
|
|
|
|
2020-01-17 12:05:58 +00:00
|
|
|
#endif
|
2019-09-23 10:46:06 +00:00
|
|
|
gui_scene_push_front(p, &cs->base);
|
|
|
|
}
|