2019-07-23 16:29:14 +00:00
|
|
|
// Copyright 2019, Collabora, Ltd.
|
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/*!
|
|
|
|
* @file
|
|
|
|
* @brief HSV Picker Debugging code.
|
|
|
|
* @author Pete Black <pblack@collabora.com>
|
|
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
2019-10-12 11:24:19 +00:00
|
|
|
* @ingroup aux_tracking
|
2019-07-23 16:29:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "util/u_misc.h"
|
|
|
|
#include "util/u_debug.h"
|
|
|
|
#include "util/u_format.h"
|
|
|
|
|
|
|
|
#include "tracking/t_tracking.h"
|
|
|
|
|
|
|
|
#include <opencv2/opencv.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Defines and structs
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PICK_WIN "HSV Picker Debugger"
|
|
|
|
|
|
|
|
#define max(a, b) (a > b ? a : b)
|
|
|
|
#define min(a, b) (a < b ? a : b)
|
|
|
|
|
|
|
|
class DebugHSVPicker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
struct xrt_frame_sink base = {};
|
2019-08-22 13:15:41 +00:00
|
|
|
struct xrt_frame_node node = {};
|
2019-07-23 16:29:14 +00:00
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
cv::Mat hsv = {};
|
|
|
|
cv::Mat threshold = {};
|
|
|
|
} debug;
|
|
|
|
|
|
|
|
struct xrt_frame_sink *passthrough;
|
|
|
|
|
|
|
|
struct t_convert_table yuv_to_hsv;
|
|
|
|
};
|
|
|
|
|
|
|
|
const int max_value_H = 360 / 2;
|
|
|
|
const int max_value = 256;
|
|
|
|
static int low_H = 0, low_S = 0, low_V = 0;
|
|
|
|
static int high_H = max_value_H, high_S = max_value, high_V = max_value;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Debug functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
ensure_debug_is_allocated(class DebugHSVPicker &d, int rows, int cols)
|
|
|
|
{
|
|
|
|
if (d.debug.hsv.cols == cols && d.debug.hsv.rows == rows) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
d.debug.threshold = cv::Mat(rows, cols, CV_8UC1);
|
|
|
|
d.debug.hsv = cv::Mat(rows, cols, CV_8UC3);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_frame_yuv(class DebugHSVPicker &d, struct xrt_frame *xf)
|
|
|
|
{
|
|
|
|
for (uint32_t y = 0; y < xf->height; y++) {
|
|
|
|
uint8_t *src = (uint8_t *)xf->data + y * xf->stride;
|
|
|
|
auto hsv = d.debug.hsv.ptr<uint8_t>(y);
|
|
|
|
for (uint32_t x = 0; x < xf->width; x++) {
|
|
|
|
uint8_t y = src[0];
|
|
|
|
uint8_t cb = src[1];
|
|
|
|
uint8_t cr = src[2];
|
|
|
|
|
|
|
|
uint8_t *hsv1 = d.yuv_to_hsv.v[y][cb][cr];
|
|
|
|
|
|
|
|
hsv[0] = hsv1[0];
|
|
|
|
hsv[1] = hsv1[1];
|
|
|
|
hsv[2] = hsv1[2];
|
|
|
|
|
|
|
|
hsv += 3;
|
|
|
|
src += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::inRange(d.debug.hsv, cv::Scalar(low_H, low_S, low_V),
|
|
|
|
cv::Scalar(high_H, high_S, high_V), d.debug.threshold);
|
|
|
|
cv::imshow(PICK_WIN, d.debug.threshold);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_frame_yuyv(class DebugHSVPicker &d, struct xrt_frame *xf)
|
|
|
|
{
|
|
|
|
for (uint32_t y = 0; y < xf->height; y++) {
|
|
|
|
uint8_t *src = (uint8_t *)xf->data + y * xf->stride;
|
|
|
|
auto hsv = d.debug.hsv.ptr<uint8_t>(y);
|
|
|
|
for (uint32_t x = 0; x < xf->width; x += 2) {
|
|
|
|
uint8_t y1 = src[0];
|
|
|
|
uint8_t cb = src[1];
|
|
|
|
uint8_t y2 = src[2];
|
|
|
|
uint8_t cr = src[3];
|
|
|
|
|
|
|
|
uint8_t *hsv1 = d.yuv_to_hsv.v[y1][cb][cr];
|
|
|
|
uint8_t *hsv2 = d.yuv_to_hsv.v[y2][cb][cr];
|
|
|
|
|
|
|
|
hsv[0] = hsv1[0];
|
|
|
|
hsv[1] = hsv1[1];
|
|
|
|
hsv[2] = hsv1[2];
|
|
|
|
hsv[3] = hsv2[0];
|
|
|
|
hsv[4] = hsv2[1];
|
|
|
|
hsv[5] = hsv2[2];
|
|
|
|
|
|
|
|
hsv += 6;
|
|
|
|
src += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::inRange(d.debug.hsv, cv::Scalar(low_H, low_S, low_V),
|
|
|
|
cv::Scalar(high_H, high_S, high_V), d.debug.threshold);
|
|
|
|
cv::imshow(PICK_WIN, d.debug.threshold);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_frame(class DebugHSVPicker &d, struct xrt_frame *xf)
|
|
|
|
{
|
|
|
|
ensure_debug_is_allocated(d, xf->height, xf->width);
|
|
|
|
|
|
|
|
switch (xf->format) {
|
|
|
|
case XRT_FORMAT_YUV888: process_frame_yuv(d, xf); break;
|
|
|
|
case XRT_FORMAT_YUV422: process_frame_yuyv(d, xf); break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "ERROR: Bad format '%s'",
|
|
|
|
u_format_str(xf->format));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_low_H_thresh_trackbar(int, void *)
|
|
|
|
{
|
|
|
|
low_H = min(high_H - 1, low_H);
|
|
|
|
cv::setTrackbarPos("Low H", PICK_WIN, low_H);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_high_H_thresh_trackbar(int, void *)
|
|
|
|
{
|
|
|
|
high_H = max(high_H, low_H + 1);
|
|
|
|
cv::setTrackbarPos("High H", PICK_WIN, high_H);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_low_S_thresh_trackbar(int, void *)
|
|
|
|
{
|
|
|
|
low_S = min(high_S - 1, low_S);
|
|
|
|
cv::setTrackbarPos("Low S", PICK_WIN, low_S);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_high_S_thresh_trackbar(int, void *)
|
|
|
|
{
|
|
|
|
high_S = max(high_S, low_S + 1);
|
|
|
|
cv::setTrackbarPos("High S", PICK_WIN, high_S);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_low_V_thresh_trackbar(int, void *)
|
|
|
|
{
|
|
|
|
low_V = min(high_V - 1, low_V);
|
|
|
|
cv::setTrackbarPos("Low V", PICK_WIN, low_V);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_high_V_thresh_trackbar(int, void *)
|
|
|
|
{
|
|
|
|
high_V = max(high_V, low_V + 1);
|
|
|
|
cv::setTrackbarPos("High V", PICK_WIN, high_V);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Exported functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" void
|
|
|
|
t_debug_hsv_picker_frame(struct xrt_frame_sink *xsink, struct xrt_frame *xf)
|
|
|
|
{
|
2019-08-21 19:29:37 +00:00
|
|
|
auto &d = *(class DebugHSVPicker *)xsink;
|
2019-07-23 16:29:14 +00:00
|
|
|
|
|
|
|
process_frame(d, xf);
|
|
|
|
|
|
|
|
d.passthrough->push_frame(d.passthrough, xf);
|
|
|
|
}
|
|
|
|
|
2019-08-22 13:15:41 +00:00
|
|
|
extern "C" void
|
|
|
|
t_debug_hsv_picker_break_apart(struct xrt_frame_node *node)
|
|
|
|
{}
|
|
|
|
|
|
|
|
extern "C" void
|
|
|
|
t_debug_hsv_picker_destroy(struct xrt_frame_node *node)
|
|
|
|
{
|
|
|
|
auto d = container_of(node, DebugHSVPicker, node);
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
2019-07-23 16:29:14 +00:00
|
|
|
extern "C" int
|
2019-08-22 13:15:41 +00:00
|
|
|
t_debug_hsv_picker_create(struct xrt_frame_context *xfctx,
|
|
|
|
struct xrt_frame_sink *passthrough,
|
2019-07-23 16:29:14 +00:00
|
|
|
struct xrt_frame_sink **out_sink)
|
|
|
|
{
|
|
|
|
auto &d = *(new DebugHSVPicker());
|
|
|
|
|
|
|
|
cv::namedWindow(PICK_WIN);
|
|
|
|
|
2019-08-22 13:15:41 +00:00
|
|
|
d.base.push_frame = t_debug_hsv_picker_frame;
|
|
|
|
d.node.break_apart = t_debug_hsv_picker_break_apart;
|
|
|
|
d.node.destroy = t_debug_hsv_picker_destroy;
|
|
|
|
d.passthrough = passthrough;
|
|
|
|
|
2019-07-23 16:29:14 +00:00
|
|
|
// Trackbars to set thresholds for HSV values
|
|
|
|
cv::createTrackbar("Low H", PICK_WIN, &low_H, max_value_H,
|
|
|
|
on_low_H_thresh_trackbar);
|
|
|
|
cv::createTrackbar("High H", PICK_WIN, &high_H, max_value_H,
|
|
|
|
on_high_H_thresh_trackbar);
|
|
|
|
cv::createTrackbar("Low S", PICK_WIN, &low_S, max_value,
|
|
|
|
on_low_S_thresh_trackbar);
|
|
|
|
cv::createTrackbar("High S", PICK_WIN, &high_S, max_value,
|
|
|
|
on_high_S_thresh_trackbar);
|
|
|
|
cv::createTrackbar("Low V", PICK_WIN, &low_V, max_value,
|
|
|
|
on_low_V_thresh_trackbar);
|
|
|
|
cv::createTrackbar("High V", PICK_WIN, &high_V, max_value,
|
|
|
|
on_high_V_thresh_trackbar);
|
|
|
|
|
|
|
|
cv::startWindowThread();
|
|
|
|
|
|
|
|
t_convert_make_y8u8v8_to_h8s8v8(&d.yuv_to_hsv);
|
|
|
|
|
2019-08-22 13:15:41 +00:00
|
|
|
xrt_frame_context_add(xfctx, &d.node);
|
2019-07-23 16:29:14 +00:00
|
|
|
|
|
|
|
*out_sink = &d.base;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|