The open source OpenXR runtime
1// Copyright 2019, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief HSV Picker Debugging code.
6 * @author Pete Black <pblack@collabora.com>
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @ingroup aux_tracking
9 */
10
11#include "util/u_misc.h"
12#include "util/u_debug.h"
13#include "util/u_format.h"
14
15#include "tracking/t_tracking.h"
16
17#include <opencv2/opencv.hpp>
18
19
20/*
21 *
22 * Defines and structs
23 *
24 */
25
26#define PICK_WIN "HSV Picker Debugger"
27
28#define max(a, b) (a > b ? a : b)
29#define min(a, b) (a < b ? a : b)
30
31/*!
32 * An @ref xrt_frame_sink that can be used to select HSV thresholds
33 * interactively.
34 * @implements xrt_frame_sink
35 * @implements xrt_frame_node
36 */
37class DebugHSVPicker
38{
39public:
40 struct xrt_frame_sink base = {};
41 struct xrt_frame_node node = {};
42
43 struct
44 {
45 cv::Mat hsv = {};
46 cv::Mat threshold = {};
47 } debug;
48
49 struct xrt_frame_sink *passthrough;
50
51 struct t_convert_table yuv_to_hsv;
52};
53
54const int max_value_H = 360 / 2;
55const int max_value = 256;
56static int low_H = 0, low_S = 0, low_V = 0;
57static int high_H = max_value_H, high_S = max_value, high_V = max_value;
58
59
60/*
61 *
62 * Debug functions.
63 *
64 */
65
66static void
67ensure_debug_is_allocated(class DebugHSVPicker &d, int rows, int cols)
68{
69 if (d.debug.hsv.cols == cols && d.debug.hsv.rows == rows) {
70 return;
71 }
72
73 d.debug.threshold = cv::Mat(rows, cols, CV_8UC1);
74 d.debug.hsv = cv::Mat(rows, cols, CV_8UC3);
75}
76
77static void
78process_frame_yuv(class DebugHSVPicker &d, struct xrt_frame *xf)
79{
80 for (uint32_t y = 0; y < xf->height; y++) {
81 uint8_t *src = (uint8_t *)xf->data + y * xf->stride;
82 auto *hsv = d.debug.hsv.ptr<uint8_t>(y);
83 for (uint32_t x = 0; x < xf->width; x++) {
84 uint8_t y = src[0];
85 uint8_t cb = src[1];
86 uint8_t cr = src[2];
87
88 uint8_t *hsv1 = d.yuv_to_hsv.v[y][cb][cr];
89
90 hsv[0] = hsv1[0];
91 hsv[1] = hsv1[1];
92 hsv[2] = hsv1[2];
93
94 hsv += 3;
95 src += 3;
96 }
97 }
98
99 cv::inRange(d.debug.hsv, cv::Scalar(low_H, low_S, low_V), cv::Scalar(high_H, high_S, high_V),
100 d.debug.threshold);
101 cv::imshow(PICK_WIN, d.debug.threshold);
102}
103
104static void
105process_frame_yuyv(class DebugHSVPicker &d, struct xrt_frame *xf)
106{
107 for (uint32_t y = 0; y < xf->height; y++) {
108 uint8_t *src = (uint8_t *)xf->data + y * xf->stride;
109 auto *hsv = d.debug.hsv.ptr<uint8_t>(y);
110 for (uint32_t x = 0; x < xf->width; x += 2) {
111 uint8_t y1 = src[0];
112 uint8_t cb = src[1];
113 uint8_t y2 = src[2];
114 uint8_t cr = src[3];
115
116 uint8_t *hsv1 = d.yuv_to_hsv.v[y1][cb][cr];
117 uint8_t *hsv2 = d.yuv_to_hsv.v[y2][cb][cr];
118
119 hsv[0] = hsv1[0];
120 hsv[1] = hsv1[1];
121 hsv[2] = hsv1[2];
122 hsv[3] = hsv2[0];
123 hsv[4] = hsv2[1];
124 hsv[5] = hsv2[2];
125
126 hsv += 6;
127 src += 4;
128 }
129 }
130
131 cv::inRange(d.debug.hsv, cv::Scalar(low_H, low_S, low_V), cv::Scalar(high_H, high_S, high_V),
132 d.debug.threshold);
133 cv::imshow(PICK_WIN, d.debug.threshold);
134}
135
136static void
137process_frame(class DebugHSVPicker &d, struct xrt_frame *xf)
138{
139 ensure_debug_is_allocated(d, xf->height, xf->width);
140
141 switch (xf->format) {
142 case XRT_FORMAT_YUV888: process_frame_yuv(d, xf); break;
143 case XRT_FORMAT_YUYV422: process_frame_yuyv(d, xf); break;
144 default: U_LOG_E("Bad format '%s'", u_format_str(xf->format)); break;
145 }
146}
147
148static void
149on_low_H_thresh_trackbar(int /*unused*/, void * /*unused*/)
150{
151 low_H = min(high_H - 1, low_H);
152 cv::setTrackbarPos("Low H", PICK_WIN, low_H);
153}
154
155static void
156on_high_H_thresh_trackbar(int /*unused*/, void * /*unused*/)
157{
158 high_H = max(high_H, low_H + 1);
159 cv::setTrackbarPos("High H", PICK_WIN, high_H);
160}
161
162static void
163on_low_S_thresh_trackbar(int /*unused*/, void * /*unused*/)
164{
165 low_S = min(high_S - 1, low_S);
166 cv::setTrackbarPos("Low S", PICK_WIN, low_S);
167}
168
169static void
170on_high_S_thresh_trackbar(int /*unused*/, void * /*unused*/)
171{
172 high_S = max(high_S, low_S + 1);
173 cv::setTrackbarPos("High S", PICK_WIN, high_S);
174}
175
176static void
177on_low_V_thresh_trackbar(int /*unused*/, void * /*unused*/)
178{
179 low_V = min(high_V - 1, low_V);
180 cv::setTrackbarPos("Low V", PICK_WIN, low_V);
181}
182
183static void
184on_high_V_thresh_trackbar(int /*unused*/, void * /*unused*/)
185{
186 high_V = max(high_V, low_V + 1);
187 cv::setTrackbarPos("High V", PICK_WIN, high_V);
188}
189
190
191/*
192 *
193 * Exported functions.
194 *
195 */
196
197extern "C" void
198t_debug_hsv_picker_frame(struct xrt_frame_sink *xsink, struct xrt_frame *xf)
199{
200 auto &d = *(class DebugHSVPicker *)xsink;
201
202 process_frame(d, xf);
203
204 d.passthrough->push_frame(d.passthrough, xf);
205}
206
207extern "C" void
208t_debug_hsv_picker_break_apart(struct xrt_frame_node *node)
209{}
210
211extern "C" void
212t_debug_hsv_picker_destroy(struct xrt_frame_node *node)
213{
214 auto *d = container_of(node, DebugHSVPicker, node);
215 delete d;
216}
217
218extern "C" int
219t_debug_hsv_picker_create(struct xrt_frame_context *xfctx,
220 struct xrt_frame_sink *passthrough,
221 struct xrt_frame_sink **out_sink)
222{
223 auto &d = *(new DebugHSVPicker());
224
225 cv::namedWindow(PICK_WIN);
226
227 d.base.push_frame = t_debug_hsv_picker_frame;
228 d.node.break_apart = t_debug_hsv_picker_break_apart;
229 d.node.destroy = t_debug_hsv_picker_destroy;
230 d.passthrough = passthrough;
231
232 // Trackbars to set thresholds for HSV values
233 cv::createTrackbar("Low H", PICK_WIN, &low_H, max_value_H, on_low_H_thresh_trackbar);
234 cv::createTrackbar("High H", PICK_WIN, &high_H, max_value_H, on_high_H_thresh_trackbar);
235 cv::createTrackbar("Low S", PICK_WIN, &low_S, max_value, on_low_S_thresh_trackbar);
236 cv::createTrackbar("High S", PICK_WIN, &high_S, max_value, on_high_S_thresh_trackbar);
237 cv::createTrackbar("Low V", PICK_WIN, &low_V, max_value, on_low_V_thresh_trackbar);
238 cv::createTrackbar("High V", PICK_WIN, &high_V, max_value, on_high_V_thresh_trackbar);
239
240 cv::startWindowThread();
241
242 t_convert_make_y8u8v8_to_h8s8v8(&d.yuv_to_hsv);
243
244 xrt_frame_context_add(xfctx, &d.node);
245
246 *out_sink = &d.base;
247
248 return 0;
249}