...
 
Commits (11)
......@@ -28,6 +28,7 @@ find_package(OpenHMD)
find_package(OpenCV COMPONENTS core calib3d highgui imgproc imgcodecs features2d video)
find_package(Libusb1)
find_package(JPEG)
find_package(SDL2)
# @TODO Turn into a find_package LIBUVC file.
pkg_check_modules(LIBUVC libuvc)
......@@ -58,6 +59,7 @@ cmake_dependent_option(BUILD_WITH_LIBUVC "Enable libuvc video driver" ON "LIBUVC
cmake_dependent_option(BUILD_WITH_FFMPEG "Enable ffmpeg testing video driver" ON "FFMPEG_FOUND" OFF)
cmake_dependent_option(BUILD_WITH_HIDAPI "Enable HIDAPI-based driver(s) (OSVR HDK, PSVR)" ON "HIDAPI_FOUND" OFF)
cmake_dependent_option(BUILD_WITH_OPENHMD "Enable OpenHMD driver" ON "OPENHMD_FOUND" OFF)
cmake_dependent_option(BUILD_WITH_SDL2 "Enable SDL2 based test application" ON "SDL2_FOUND" OFF)
###
......@@ -83,6 +85,9 @@ endif()
if(BUILD_WITH_OPENCV)
add_definitions(-DXRT_HAVE_OPENCV)
# Tracking requires OpenCV
set(BUILD_TRACKING TRUE)
endif()
if(BUILD_WITH_JPEG)
......@@ -97,6 +102,18 @@ if(BUILD_WITH_FFMPEG)
add_definitions(-DXRT_HAVE_FFMPEG)
endif()
if(BUILD_WITH_SDL2)
add_definitions(-DXRT_HAVE_SDL2)
# Arch work around
if(NOT DEFINED SDL2_LIBRARIES)
set(SDL2_LIBRARIES SDL2::SDL2)
endif()
# SDL2 based gui
set(BUILD_TARGET_GUI TRUE)
endif()
if(BUILD_WITH_OPENHMD)
add_definitions(-DXRT_HAVE_OPENHMD)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Monado specific things
#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM "glad/gl.h"
//---- Define assertion handler. Defaults to calling assert().
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
// It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
//#define IMGUI_DISABLE_DEMO_WINDOWS
//#define IMGUI_DISABLE_METRICS_WINDOW
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices').
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Using 32-bits vertex indices (default is 16-bits) is one way to allow large meshes with more than 64K vertices.
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bits indices).
// Another way to allow large meshes while keeping 16-bits indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools
// Use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
// Have the Item Picker break in the ItemAdd() function instead of ItemHoverable() - which is earlier in the code, will catch a few extra items, allow picking items other than Hovered one.
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
// Specific OpenGL versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Set default OpenGL3 loader to be gl3w
#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#endif
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
This diff is collapsed.
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct SDL_Window;
typedef union SDL_Event SDL_Event;
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -16,6 +16,16 @@ set(OS_SOURCE_FILES
os/os_hid_hidraw.c
)
set(TRACKING_SOURCE_FILES
tracking/t_calibration.cpp
tracking/t_convert.cpp
tracking/t_debug_hsv_filter.cpp
tracking/t_debug_hsv_picker.cpp
tracking/t_debug_hsv_viewer.cpp
tracking/t_hsv_filter.c
tracking/t_tracking.h
)
set(UTIL_SOURCE_FILES
util/u_misc.c
util/u_misc.h
......@@ -30,10 +40,14 @@ set(UTIL_SOURCE_FILES
util/u_hashmap.h
util/u_hashset.cpp
util/u_hashset.h
util/u_sink.c
util/u_sink.h
util/u_sink_converter.c
util/u_sink_pool.c
util/u_sink_queue.c
util/u_time.cpp
util/u_time.h
util/u_var.cpp
util/u_var.h
)
# Common includes
......@@ -60,3 +74,18 @@ set_property(TARGET aux_math PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(aux_math SYSTEM
PRIVATE ${EIGEN3_INCLUDE_DIR}
)
if(BUILD_TRACKING)
# Tracking library.
# Use OBJECT to not create a archive, since it just gets in the way.
add_library(aux_tracking OBJECT ${TRACKING_SOURCE_FILES})
set_property(TARGET aux_tracking PROPERTY POSITION_INDEPENDENT_CODE ON)
# Math files has extra include(s).
target_include_directories(aux_tracking SYSTEM
PRIVATE
${EIGEN3_INCLUDE_DIR}
${OpenCV_INCLUDE_DIRS}
)
endif()
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Calibration code.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "util/u_sink.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_format.h"
#include "tracking/t_tracking.h"
#include <opencv2/opencv.hpp>
DEBUG_GET_ONCE_BOOL_OPTION(hsv_filter, "T_DEBUG_HSV_FILTER", false)
DEBUG_GET_ONCE_BOOL_OPTION(hsv_picker, "T_DEBUG_HSV_PICKER", false)
DEBUG_GET_ONCE_BOOL_OPTION(hsv_viewer, "T_DEBUG_HSV_VIEWER", false)
/*
*
* Structs
*
*/
class Calibration
{
public:
struct xrt_fs_sink base = {};
struct
{
cv::Mat rgb = {};
struct xrt_fs_sink *sink = {};
} gui;
cv::Mat grey;
char text[512];
};
/*!
* Holds `cv::Mat`s used during frame processing when processing a yuyv frame.
*/
struct t_frame_yuyv
{
public:
//! Full frame size, each block is split across two cols.
cv::Mat data_full = {};
//! Half horizontal width covering a complete block of two pixels.
cv::Mat data_half = {};
};
/*
*
* Small helpers.
*
*/
static void
send_rgb_frame(struct xrt_fs_sink *xsink, cv::Mat &rgb)
{
struct xrt_fs_frame xf = {};
xf.format = XRT_FORMAT_R8G8B8;
xf.width = rgb.cols;
xf.height = rgb.rows;
xf.data = rgb.data;
u_format_size_for_dimensions(xf.format, xf.width, xf.height, &xf.stride,
&xf.size);
xsink->push_frame(xsink, &xf);
}
static void
ensure_buffers_are_allocated(class Calibration &c, int rows, int cols)
{
if (c.gui.rgb.cols == cols && c.gui.rgb.rows == rows) {
return;
}
c.grey = cv::Mat(rows, cols, CV_8UC1, cv::Scalar(0));
c.gui.rgb = cv::Mat(rows, cols, CV_8UC3, cv::Scalar(0, 0, 0));
}
static void
print_txt(cv::Mat &rgb, const char *text, double fontScale)
{
int fontFace = 0;
int thickness = 2;
cv::Size textSize =
cv::getTextSize(text, fontFace, fontScale, thickness, NULL);
cv::Point textOrg((rgb.cols - textSize.width) / 2, textSize.height * 2);
cv::putText(rgb, text, textOrg, fontFace, fontScale,
cv::Scalar(192, 192, 192), thickness);
}
static void
make_gui_str(class Calibration &c)
{
auto &rgb = c.gui.rgb;
int cols = 800;
int rows = 100;
ensure_buffers_are_allocated(c, rows, cols);
cv::rectangle(rgb, cv::Point2f(0, 0), cv::Point2f(cols, rows),
cv::Scalar(0, 0, 0), -1, 0);
print_txt(rgb, c.text, 1.0);
send_rgb_frame(c.gui.sink, c.gui.rgb);
}
static void
make_calibration_frame(class Calibration &c)
{
auto &rgb = c.gui.rgb;
if (rgb.rows == 0 || rgb.cols == 0) {
ensure_buffers_are_allocated(c, 480, 640);
cv::rectangle(c.gui.rgb, cv::Point2f(0, 0),
cv::Point2f(rgb.cols, rgb.rows),
cv::Scalar(0, 0, 0), -1, 0);
}
/*
* Draw text
*/
print_txt(rgb, "CALIBRATION MODE", 1.5);
send_rgb_frame(c.gui.sink, rgb);
}
/*
*
* Main functions.
*
*/
static void
process_frame_yuv(class Calibration &c, struct xrt_fs_frame *xf)
{
int w = (int)xf->width;
int h = (int)xf->height;
cv::Mat data(h, w, CV_8UC3, xf->data, xf->stride);
ensure_buffers_are_allocated(c, data.rows, data.cols);
cv::cvtColor(data, c.gui.rgb, cv::COLOR_YUV2RGB);
cv::cvtColor(c.gui.rgb, c.grey, cv::COLOR_RGB2GRAY);
}
static void
process_frame_yuyv(class Calibration &c, struct xrt_fs_frame *xf)
{
/*
* Cleverly extract the different channels.
* Cr/Cb are extracted at half width.
*/
int w = (int)xf->width;
int half_w = w / 2;
int h = (int)xf->height;
class t_frame_yuyv f = {};
f.data_half = cv::Mat(h, half_w, CV_8UC4, xf->data, xf->stride);
f.data_full = cv::Mat(h, w, CV_8UC2, xf->data, xf->stride);
ensure_buffers_are_allocated(c, f.data_full.rows, f.data_full.cols);
cv::cvtColor(f.data_full, c.gui.rgb, cv::COLOR_YUV2RGB_YUYV);
cv::cvtColor(f.data_full, c.grey, cv::COLOR_YUV2GRAY_YUYV);
}
/*
*
* Interface functions.
*
*/
extern "C" void
t_calibration_frame(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
auto &c = *(struct Calibration *)xsink;
#if 0
if (xf->stereo_format != XRT_FS_STEREO_SBS) {
snprintf(c.text, sizeof(c.text),
"ERROR: Not side by side stereo!");
make_gui_str(c);
return;
}
#endif
// Fill both c.gui.rgb and c.grey with the data we got.
switch (xf->format) {
case XRT_FORMAT_YUV888: process_frame_yuv(c, xf); break;
case XRT_FORMAT_YUV422: process_frame_yuyv(c, xf); break;
default:
snprintf(c.text, sizeof(c.text), "ERROR: Bad format '%s'",
u_format_str(xf->format));
make_gui_str(c);
return;
}
make_calibration_frame(c);
}
/*
*
* Exported functions.
*
*/
extern "C" int
t_calibration_create(struct xrt_fs_sink *gui, struct xrt_fs_sink **out_sink)
{
auto &c = *(new Calibration());
c.gui.sink = gui;
c.base.push_frame = t_calibration_frame;
*out_sink = &c.base;
snprintf(c.text, sizeof(c.text), "Waiting for camera");
make_gui_str(c);
int ret = 0;
if (debug_get_bool_option_hsv_filter()) {
ret = t_debug_hsv_filter_create(*out_sink, out_sink);
}
if (debug_get_bool_option_hsv_picker()) {
ret = t_debug_hsv_picker_create(*out_sink, out_sink);
}
if (debug_get_bool_option_hsv_viewer()) {
ret = t_debug_hsv_viewer_create(*out_sink, out_sink);
}
// Ensure we only get yuv or yuyv frames.
u_sink_create_to_yuv_or_yuyv(*out_sink, out_sink);
return ret;
}
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Code to build conversion tables and convert images.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "tracking/t_tracking.h"
#include <opencv2/opencv.hpp>
/*
*
* 'Exported' functions.
*
*/
extern "C" void
t_convert_fill_table(struct t_convert_table *t)
{
for (int y = 0; y < 256; y++) {
for (int u = 0; u < 256; u++) {
uint8_t *dst = &t->v[y][u][0][0];
for (int v = 0; v < 256; v++) {
dst[0] = y;
dst[1] = u;
dst[2] = v;
dst += 3;
}
}
}
}
extern "C" void
t_convert_make_y8u8v8_to_r8g8b8(struct t_convert_table *t)
{
size_t size = 256 * 256 * 256;
t_convert_fill_table(t);
t_convert_in_place_y8u8v8_to_r8g8b8(size, 1, 0, t);
}
extern "C" void
t_convert_make_y8u8v8_to_h8s8v8(struct t_convert_table *t)
{
size_t size = 256 * 256 * 256;
t_convert_fill_table(t);
t_convert_in_place_y8u8v8_to_h8s8v8(size, 1, 0, &t->v);
}
extern "C" void
t_convert_make_h8s8v8_to_r8g8b8(struct t_convert_table *t)
{
size_t size = 256 * 256 * 256;
t_convert_fill_table(t);
t_convert_in_place_h8s8v8_to_r8g8b8(size, 1, 0, &t->v);
}
extern "C" void
t_convert_in_place_y8u8v8_to_r8g8b8(uint32_t width,
uint32_t height,
size_t stride,
void *data_ptr)
{
cv::Mat data(height, width, CV_8UC3, data_ptr, stride);
cv::cvtColor(data, data, cv::COLOR_YUV2RGB);
}
extern "C" void
t_convert_in_place_y8u8v8_to_h8s8v8(uint32_t width,
uint32_t height,
size_t stride,
void *data_ptr)
{
cv::Mat data(height, width, CV_8UC3, data_ptr, stride);
cv::Mat temp(height, width, CV_32FC3);
cv::cvtColor(data, temp, cv::COLOR_YUV2RGB);
cv::cvtColor(temp, data, cv::COLOR_RGB2HSV);
}
extern "C" void
t_convert_in_place_h8s8v8_to_r8g8b8(uint32_t width,
uint32_t height,
size_t stride,
void *data_ptr)
{
cv::Mat data(height, width, CV_8UC3, data_ptr, stride);
cv::cvtColor(data, data, cv::COLOR_YUV2RGB);
}
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief HSV filter debug code.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#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 HSV0_WIN "HSV Channel #1 (Red)"
#define HSV1_WIN "HSV Channel #2 (Purple)"
#define HSV2_WIN "HSV Channel #3 (Blue)"
#define HSV3_WIN "HSV Channel #4 (White)"
class DebugHSV
{
public:
struct xrt_fs_sink base = {};
struct xrt_fs_sink sinks[4] = {};
struct xrt_fs_sink *sink;
struct xrt_fs_sink *passthrough;
};
/*
*
* Exported functions.
*
*/
extern "C" void
t_debug_hsv_filter_frame0(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
cv::Mat tmp(xf->height, xf->width, CV_8UC1, xf->data, xf->stride);
cv::imshow(HSV0_WIN, tmp);
}
extern "C" void
t_debug_hsv_filter_frame1(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
cv::Mat tmp(xf->height, xf->width, CV_8UC1, xf->data, xf->stride);
cv::imshow(HSV1_WIN, tmp);
}
extern "C" void
t_debug_hsv_filter_frame2(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
cv::Mat tmp(xf->height, xf->width, CV_8UC1, xf->data, xf->stride);
cv::imshow(HSV2_WIN, tmp);
}
extern "C" void
t_debug_hsv_filter_frame3(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
cv::Mat tmp(xf->height, xf->width, CV_8UC1, xf->data, xf->stride);
cv::imshow(HSV3_WIN, tmp);
}
extern "C" void
t_debug_hsv_filter_frame(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
auto &d = *(struct DebugHSV *)xsink;
d.sink->push_frame(d.sink, xf);
d.passthrough->push_frame(d.passthrough, xf);
}
extern "C" int
t_debug_hsv_filter_create(struct xrt_fs_sink *passthrough,
struct xrt_fs_sink **out_sink)
{
auto &d = *(new DebugHSV());
cv::namedWindow(HSV0_WIN);
cv::namedWindow(HSV1_WIN);
cv::namedWindow(HSV2_WIN);
cv::namedWindow(HSV3_WIN);
cv::startWindowThread();
d.passthrough = passthrough;
d.sinks[0].push_frame = t_debug_hsv_filter_frame0;
d.sinks[1].push_frame = t_debug_hsv_filter_frame1;
d.sinks[2].push_frame = t_debug_hsv_filter_frame2;
d.sinks[3].push_frame = t_debug_hsv_filter_frame3;
d.base.push_frame = t_debug_hsv_filter_frame;
struct xrt_fs_sink *sinks[4] = {
&d.sinks[0],
&d.sinks[1],
&d.sinks[2],
&d.sinks[3],
};
*out_sink = &d.base;
t_hsv_filter_params params = T_HSV_DEFAULT_PARAMS();
t_hsv_filter_create(&params, sinks, &d.sink);
return 0;
}
// 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>
*/
#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_fs_sink base = {};
struct
{
cv::Mat hsv = {};
cv::Mat threshold = {};
} debug;
struct xrt_fs_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_fs_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_fs_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_fs_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_fs_sink *xsink, struct xrt_fs_frame *xf)
{
auto &d = *(struct DebugHSVPicker *)xsink;
process_frame(d, xf);
d.passthrough->push_frame(d.passthrough, xf);
}
extern "C" int
t_debug_hsv_picker_create(struct xrt_fs_sink *passthrough,
struct xrt_fs_sink **out_sink)
{
auto &d = *(new DebugHSVPicker());
cv::namedWindow(PICK_WIN);
// 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);
d.passthrough = passthrough;
d.base.push_frame = t_debug_hsv_picker_frame;
*out_sink = &d.base;
return 0;
}
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief HSV debug viewer code.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#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 HSV_WIN "HSV Filter Tester"
class DebugHSV
{
public:
struct xrt_fs_sink base = {};
struct xrt_fs_sink *passthrough;
cv::Mat bgr;
int lum_value = 0;
// No need to initialize these
struct t_convert_table yuv_to_rgb_table;
struct t_hsv_filter_large_table hsv_large;
struct t_hsv_filter_optimized_table hsv_opt;
};
/*
*
* Debug functions.
*
*/
static void
process_pixel(bool f1,
bool f1_diff,
uint8_t *hsv_cap,
uint8_t *hsv_opt,
uint8_t *hsv_diff,
uint8_t *rgb)
{
if (f1) {
hsv_cap[0] = rgb[2];
hsv_cap[1] = rgb[1];
hsv_cap[2] = rgb[0];
} else {
hsv_cap[0] = 0;
hsv_cap[1] = 0;
hsv_cap[2] = 0;
}
if (f1_diff) {
hsv_opt[0] = rgb[2];
hsv_opt[1] = rgb[1];
hsv_opt[2] = rgb[0];
} else {
hsv_opt[0] = 0;
hsv_opt[1] = 0;
hsv_opt[2] = 0;
}
if (f1 > f1_diff) {
hsv_diff[0] = 0xff;
hsv_diff[1] = 0;
hsv_diff[2] = 0;
} else if (f1 < f1_diff) {
hsv_diff[0] = 0;
hsv_diff[1] = 0;
hsv_diff[2] = 0xff;
} else {
hsv_diff[0] = 0;
hsv_diff[1] = 0;
hsv_diff[2] = 0;
}
}
#define SIZE 256
#define NUM_CHAN 4
static void
process_frame(DebugHSV &d, struct xrt_fs_frame *xf)
{
uint32_t width = SIZE * 3;
uint32_t height = SIZE * NUM_CHAN;
auto &bgr = d.bgr;
if (bgr.rows != (int)height || bgr.cols != (int)width) {
bgr = cv::Mat(height, width, CV_8UC3);
}
for (uint32_t yp = 0; yp < SIZE; yp++) {
for (int chan = 0; chan < NUM_CHAN; chan++) {
auto hsv_cap = bgr.ptr<uint8_t>(yp + SIZE * chan);
auto hsv_opt =
bgr.ptr<uint8_t>(yp + SIZE * chan) + 256 * 3;
auto hsv_diff =
bgr.ptr<uint8_t>(yp + SIZE * chan) + 512 * 3;
int mask = 1 << chan;
for (uint32_t xp = 0; xp < SIZE; xp++) {
int y = d.lum_value;
int u = yp;
int v = xp;
uint8_t *rgb = d.yuv_to_rgb_table.v[y][u][v];
uint8_t large = d.hsv_large.v[y][u][v];
uint8_t opt =
t_hsv_filter_sample(&d.hsv_opt, y, u, v);
large = (large & mask) != 0;
opt = (opt & mask) != 0;
process_pixel(large, opt, hsv_cap, hsv_opt,
hsv_diff, rgb);
hsv_cap += 3;
hsv_opt += 3;
hsv_diff += 3;
}
}
}
cv::imshow(HSV_WIN, bgr);
}
/*
*
* Exported functions.
*
*/
extern "C" void
t_debug_hsv_viewer_frame(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
auto &d = *(struct DebugHSV *)xsink;
process_frame(d, xf);
d.passthrough->push_frame(d.passthrough, xf);
}
extern "C" int
t_debug_hsv_viewer_create(struct xrt_fs_sink *passthrough,
struct xrt_fs_sink **out_sink)
{
auto &d = *(new DebugHSV());
cv::namedWindow(HSV_WIN);
cv::createTrackbar("Luma", HSV_WIN, &d.lum_value, 255, NULL);
cv::startWindowThread();
d.passthrough = passthrough;
d.base.push_frame = t_debug_hsv_viewer_frame;
t_convert_make_y8u8v8_to_r8g8b8(&d.yuv_to_rgb_table);
struct t_hsv_filter_params params = T_HSV_DEFAULT_PARAMS();
t_hsv_build_large_table(&params, &d.hsv_large);
t_hsv_build_optimized_table(&params, &d.hsv_opt);
*out_sink = &d.base;
return 0;
}
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief A simple HSV filter.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_format.h"
#include "tracking/t_tracking.h"
#include <stdio.h>
#define MOD_180(v) ((uint32_t)(v) % 180)
static inline bool
check_range(struct t_hsv_filter_color color, uint32_t h, uint32_t s, uint32_t v)
{
bool bad = false;
bad |= s < color.s_min;
bad |= v < color.v_min;
bad |= MOD_180(h + (360 - color.hue_min)) >= color.hue_range;
return !bad;
}
void
t_hsv_build_convert_table(struct t_hsv_filter_params *params,
struct t_convert_table *t)
{
struct t_hsv_filter_large_table *temp =
U_TYPED_CALLOC(struct t_hsv_filter_large_table);
t_hsv_build_large_table(params, temp);
uint8_t *dst = &t->v[0][0][0][0];
for (int y = 0; y < 256; y++) {
for (int u = 0; u < 256; u++) {
for (int v = 0; v < 256; v++) {
uint32_t mask = temp->v[y][u][v];
dst[0] = ((mask & 1) != 0) ? 0xff : 0x00;
dst[1] = ((mask & 2) != 0) ? 0xff : 0x00;
dst[2] = ((mask & 4) != 0) ? 0xff : 0x00;
dst += 3;
}
}
}
free(temp);
}
void
t_hsv_build_large_table(struct t_hsv_filter_params *params,
struct t_hsv_filter_large_table *t)
{
struct t_convert_table *temp = U_TYPED_CALLOC(struct t_convert_table);
t_convert_make_y8u8v8_to_h8s8v8(temp);
uint8_t *dst = &t->v[0][0][0];
for (int y = 0; y < 256; y++) {
for (int u = 0; u < 256; u++) {
for (int v = 0; v < 256; v++) {
uint32_t h = temp->v[y][u][v][0];
uint8_t s = temp->v[y][u][v][1];
uint8_t v2 = temp->v[y][u][v][2];
bool f0 =
check_range(params->color[0], h, s, v2);
bool f1 =
check_range(params->color[1], h, s, v2);
bool f2 =
check_range(params->color[2], h, s, v2);
bool f3 = s <= params->white.s_max &&
v2 >= params->white.v_min;
*dst = (f0 << 0) | (f1 << 1) | (f2 << 2) |
(f3 << 3);
dst += 1;
}
}
}
free(temp);
}
void
t_hsv_build_optimized_table(struct t_hsv_filter_params *params,
struct t_hsv_filter_optimized_table *t)
{
struct t_hsv_filter_large_table *temp =
U_TYPED_CALLOC(struct t_hsv_filter_large_table);
t_hsv_build_large_table(params, temp);
// Half of step, minues one
int offset = (T_HSV_STEP / 2) - 1;
for (int y = 0; y < T_HSV_SIZE; y++) {
int src_y = y * T_HSV_STEP + offset;
for (int u = 0; u < T_HSV_SIZE; u++) {
int src_u = u * T_HSV_STEP + offset;
int src_v = offset;
for (int v = 0; v < T_HSV_SIZE; v++) {
t->v[y][u][v] = temp->v[src_y][src_u][src_v];
src_v += T_HSV_STEP;
}
}
}
free(temp);
}
/*
*
* Sink filter
*
*/
#define NUM_CHANNELS 4
struct t_hsv_filter
{
struct xrt_fs_sink base;
struct xrt_fs_sink *sinks[NUM_CHANNELS];
struct t_hsv_filter_params params;
uint8_t *buf0;
uint8_t *buf1;
uint8_t *buf2;
uint8_t *buf3;
size_t buf_stride;
size_t buf_size;
uint32_t buf_width;
uint32_t buf_height;
struct t_hsv_filter_optimized_table table;
};
static void
process_sample(struct t_hsv_filter *f,
uint8_t y,
uint8_t cb,
uint8_t cr,
uint8_t *dst0,
uint8_t *dst1,
uint8_t *dst2,
uint8_t *dst3)
{
uint8_t bits = t_hsv_filter_sample(&f->table, y, cb, cr);
*dst0 = (bits & (1 << 0)) ? 0xff : 0x00;
*dst1 = (bits & (1 << 1)) ? 0xff : 0x00;
*dst2 = (bits & (1 << 2)) ? 0xff : 0x00;
*dst3 = (bits & (1 << 3)) ? 0xff : 0x00;
}
static void
process_frame_yuv(struct t_hsv_filter *f, struct xrt_fs_frame *xf)
{
for (uint32_t y = 0; y < xf->height; y++) {
uint8_t *src = (uint8_t *)xf->data + y * xf->stride;
uint8_t *dst0 = f->buf0 + y * f->buf_stride;
uint8_t *dst1 = f->buf1 + y * f->buf_stride;
uint8_t *dst2 = f->buf2 + y * f->buf_stride;
uint8_t *dst3 = f->buf3 + y * f->buf_stride;
for (uint32_t x = 0; x < xf->width; x += 1) {
uint8_t y = src[0];
uint8_t cb = src[1];
uint8_t cr = src[2];
src += 3;
process_sample(f, y, cb, cr, dst0, dst1, dst2, dst3);
dst0 += 1;
dst1 += 1;
dst2 += 1;
dst3 += 1;
}
}
}
static void
process_frame_yuyv(struct t_hsv_filter *f, struct xrt_fs_frame *xf)
{
for (uint32_t y = 0; y < xf->height; y++) {
uint8_t *src = (uint8_t *)xf->data + y * xf->stride;
uint8_t *dst0 = f->buf0 + y * f->buf_stride;
uint8_t *dst1 = f->buf1 + y * f->buf_stride;
uint8_t *dst2 = f->buf2 + y * f->buf_stride;
uint8_t *dst3 = f->buf3 + y * f->buf_stride;
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];
src += 4;
process_sample(f, y1, cb, cr, dst0, dst1, dst2, dst3);
dst0 += 1;
dst1 += 1;
dst2 += 1;
dst3 += 1;
process_sample(f, y2, cb, cr, dst0, dst1, dst2, dst3);
dst0 += 1;
dst1 += 1;
dst2 += 1;
dst3 += 1;
}
}
}
static void
ensure_buf_allocated(struct t_hsv_filter *f, struct xrt_fs_frame *xf)
{
if (xf->width == f->buf_width && xf->width == f->buf_height) {
return;
}
free(f->buf0);
free(f->buf1);
free(f->buf2);
free(f->buf3);
f->buf_width = xf->width;
f->buf_height = xf->height;
f->buf_stride = f->buf_width;
f->buf_size = f->buf_stride * f->buf_height;
f->buf0 = U_TYPED_ARRAY_CALLOC(uint8_t, f->buf_size);
f->buf1 = U_TYPED_ARRAY_CALLOC(uint8_t, f->buf_size);
f->buf2 = U_TYPED_ARRAY_CALLOC(uint8_t, f->buf_size);
f->buf3 = U_TYPED_ARRAY_CALLOC(uint8_t, f->buf_size);
}
static void
push_buf(struct t_hsv_filter *f, struct xrt_fs_sink *xsink, uint8_t *buf)
{
if (xsink == NULL) {
return;
}
struct xrt_fs_frame xf = {0};
xf.format = XRT_FORMAT_L8;
xf.width = f->buf_width;
xf.height = f->buf_height;
xf.stride = f->buf_stride;
xf.size = f->buf_size;
xf.data = buf;
xsink->push_frame(xsink, &xf);
}
static void
push_frame(struct xrt_fs_sink *xsink, struct xrt_fs_frame *xf)
{
struct t_hsv_filter *f = (struct t_hsv_filter *)xsink;
ensure_buf_allocated(f, xf);
switch (xf->format) {
case XRT_FORMAT_YUV888: process_frame_yuv(f, xf); break;
case XRT_FORMAT_YUV422: process_frame_yuyv(f, xf); break;
default:
fprintf(stderr, "ERROR: Bad format '%s'",
u_format_str(xf->format));
return;
}
push_buf(f, f->sinks[0], f->buf0);
push_buf(f, f->sinks[1], f->buf1);
push_buf(f, f->sinks[2], f->buf2);
push_buf(f, f->sinks[3], f->buf3);
}
int
t_hsv_filter_create(struct t_hsv_filter_params *params,
struct xrt_fs_sink *sinks[4],
struct xrt_fs_sink **out_sink)
{
struct t_hsv_filter *f = U_TYPED_CALLOC(struct t_hsv_filter);
f->params = *params;
f->base.push_frame = push_frame;
f->sinks[0] = sinks[0];
f->sinks[1] = sinks[1];
f->sinks[2] = sinks[2];
f->sinks[3] = sinks[3];
t_hsv_build_optimized_table(&f->params, &f->table);
*out_sink = &f->base;
return 0;
}
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Tracking API interface.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#pragma once
#include "xrt/xrt_frameserver.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
*
* Conversion functions.
*
*/
struct t_convert_table
{
uint8_t v[256][256][256][3];
};
void
t_convert_fill_table(struct t_convert_table *t);
void
t_convert_make_y8u8v8_to_r8g8b8(struct t_convert_table *t);
void
t_convert_make_y8u8v8_to_h8s8v8(struct t_convert_table *t);
void
t_convert_make_h8s8v8_to_r8g8b8(struct t_convert_table *t);
void
t_convert_in_place_y8u8v8_to_r8g8b8(uint32_t width,