Add a simple OpenGL-based viewer tool that viewer tool as an example so developers can quickly get started with their own viewer tool.

The simple viewer borrowed some code from the full K4AViewer tool; as part of this, cleaned up some of that code as well
This commit is contained in:
Billy Price 2019-03-21 14:49:38 -07:00 коммит произвёл GitHub
Родитель 8f45cd98e6
Коммит a6baff5846
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 1225 добавлений и 254 удалений

Просмотреть файл

@ -10,3 +10,4 @@ add_subdirectory(snipits)
add_subdirectory(streaming)
add_subdirectory(transformation)
add_subdirectory(undistort)
add_subdirectory(viewer)

Просмотреть файл

@ -0,0 +1,5 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
add_subdirectory(opengl)

Просмотреть файл

@ -0,0 +1,19 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
find_package(OpenGL REQUIRED)
include_directories(
.
${OPENGL_INCLUDE_DIRS}
)
add_executable(viewer_opengl
main.cpp
texture.cpp
viewerwindow.cpp)
target_link_libraries(viewer_opengl PRIVATE
k4a::k4a
glfw::glfw
imgui::imgui
${OPENGL_LIBRARIES})

Просмотреть файл

@ -0,0 +1,103 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4ADEPTHPIXELCOLORIZER_H
#define K4ADEPTHPIXELCOLORIZER_H
// System headers
//
#include <algorithm>
// Library headers
//
#include "k4aimgui_all.h"
// Project headers
//
#include "k4apixel.h"
namespace viewer
{
using DepthPixelVisualizationFunction = BgraPixel(const DepthPixel &value,
const DepthPixel &min,
const DepthPixel &max);
// Functions that provide ways to take depth images and turn them into color representations
// suitable for showing to humans.
//
class K4ADepthPixelColorizer
{
public:
// Computes a color representation of a depth pixel on the blue-red spectrum, using min
// as the value for blue and max as the value for red.
//
static inline BgraPixel ColorizeBlueToRed(const DepthPixel &depthPixel,
const DepthPixel &min,
const DepthPixel &max)
{
constexpr uint8_t PixelMax = std::numeric_limits<uint8_t>::max();
// Default to opaque black.
//
BgraPixel result = { 0, 0, 0, PixelMax };
// If the pixel is actual zero and not just below the min value, make it black
//
if (depthPixel == 0)
{
return result;
}
uint16_t clampedValue = depthPixel;
clampedValue = std::min(clampedValue, max);
clampedValue = std::max(clampedValue, min);
// Normalize to [0, 1]
//
float hue = (clampedValue - min) / static_cast<float>(max - min);
// The 'hue' coordinate in HSV is a polar coordinate, so it 'wraps'.
// Purple starts after blue and is close enough to red to be a bit unclear,
// so we want to go from blue to red. Purple starts around .6666667,
// so we want to normalize to [0, .6666667].
//
constexpr float range = 2.f / 3.f;
hue *= range;
// We want blue to be close and red to be far, so we need to reflect the
// hue across the middle of the range.
//
hue = range - hue;
float fRed = 0.f;
float fGreen = 0.f;
float fBlue = 0.f;
ImGui::ColorConvertHSVtoRGB(hue, 1.f, 1.f, fRed, fGreen, fBlue);
result.Red = static_cast<uint8_t>(fRed * PixelMax);
result.Green = static_cast<uint8_t>(fGreen * PixelMax);
result.Blue = static_cast<uint8_t>(fBlue * PixelMax);
return result;
}
// Computes a greyscale representation of a depth pixel.
//
static inline BgraPixel ColorizeGreyscale(const DepthPixel &value, const DepthPixel &min, const DepthPixel &max)
{
// Clamp to max
//
DepthPixel pixelValue = std::min(value, max);
constexpr uint8_t PixelMax = std::numeric_limits<uint8_t>::max();
const auto normalizedValue = static_cast<uint8_t>((pixelValue - min) * (double(PixelMax) / (max - min)));
// All color channels are set the same (image is greyscale)
//
return BgraPixel{ normalizedValue, normalizedValue, normalizedValue, PixelMax };
}
};
} // namespace viewer
#endif

Просмотреть файл

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4AIMGUI_ALL_H
#define K4AIMGUI_ALL_H
// Some of the dear imgui implementation headers must be included in a specific order, which can
// lead to really odd build breaks if you include one of those headers to get something out of it
// without including the rest in a header whose includes don't already include all of those
// dependencies from some other include they already have.
//
// This file includes all dear imgui-related headers that have ordering constraints; if you need
// access to symbols from any of these headers, include this header rather than directly including
// those headers.
//
// Some of these headers end up including windows.h, which defines the min/max macros, which conflict
// with std::min and std::max, which we're using because they're portable. This macro modifies the
// behavior of windows.h to not define those macros so we can avoid the conflict.
//
#define NOMINMAX
// Clang parses doxygen-style comments in your source and checks for doxygen syntax errors.
// Unfortunately, some of our external dependencies have doxygen syntax errors in their
// headers and clang looks at them when we include them here, so we need to shut off those
// warnings on these headers.
//
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#include <GL/gl3w.h>
#include <GLFW/glfw3.h>
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4APIXEL_H
#define K4APIXEL_H
// Helper structs/typedefs to cast buffers to
//
namespace viewer
{
struct BgraPixel
{
uint8_t Blue;
uint8_t Green;
uint8_t Red;
uint8_t Alpha;
};
using DepthPixel = uint16_t;
} // namespace viewer
#endif

Просмотреть файл

@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4AIMAGESIZES_H
#define K4AIMAGESIZES_H
#include <exception>
#include <utility>
#include <k4a/k4a.hpp>
namespace viewer
{
// Gets the dimensions of the color images that the color camera will produce for a
// given color resolution
//
inline std::pair<int, int> GetColorDimensions(const k4a_color_resolution_t resolution)
{
switch (resolution)
{
case K4A_COLOR_RESOLUTION_720P:
return { 1280, 720 };
case K4A_COLOR_RESOLUTION_2160P:
return { 3840, 2160 };
case K4A_COLOR_RESOLUTION_1440P:
return { 2560, 1440 };
case K4A_COLOR_RESOLUTION_1080P:
return { 1920, 1080 };
case K4A_COLOR_RESOLUTION_3072P:
return { 4096, 3072 };
case K4A_COLOR_RESOLUTION_1536P:
return { 2048, 1536 };
default:
throw std::logic_error("Invalid color dimensions value!");
}
}
// Gets the dimensions of the depth images that the depth camera will produce for a
// given depth mode
//
inline std::pair<int, int> GetDepthDimensions(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_NFOV_2X2BINNED:
return { 320, 288 };
case K4A_DEPTH_MODE_NFOV_UNBINNED:
return { 640, 576 };
case K4A_DEPTH_MODE_WFOV_2X2BINNED:
return { 512, 512 };
case K4A_DEPTH_MODE_WFOV_UNBINNED:
return { 1024, 1024 };
case K4A_DEPTH_MODE_PASSIVE_IR:
return { 1024, 1024 };
default:
throw std::logic_error("Invalid depth dimensions value!");
}
}
// Gets the range of values that we expect to see from the depth camera
// when using a given depth mode, in millimeters
//
inline std::pair<uint16_t, uint16_t> GetDepthModeRange(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_NFOV_2X2BINNED:
return { (uint16_t)500, (uint16_t)5800 };
case K4A_DEPTH_MODE_NFOV_UNBINNED:
return { (uint16_t)500, (uint16_t)4000 };
case K4A_DEPTH_MODE_WFOV_2X2BINNED:
return { (uint16_t)250, (uint16_t)3000 };
case K4A_DEPTH_MODE_WFOV_UNBINNED:
return { (uint16_t)250, (uint16_t)2500 };
case K4A_DEPTH_MODE_PASSIVE_IR:
default:
throw std::logic_error("Invalid depth mode!");
}
}
// Gets the expected min/max IR brightness levels that we expect to see
// from the IR camera when using a given depth mode
//
inline std::pair<uint16_t, uint16_t> GetIrLevels(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_PASSIVE_IR:
return { (uint16_t)0, (uint16_t)100 };
case K4A_DEPTH_MODE_OFF:
throw std::logic_error("Invalid depth mode!");
default:
return { (uint16_t)0, (uint16_t)1000 };
}
}
} // namespace viewer
#endif

Просмотреть файл

@ -0,0 +1,168 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <iostream>
#include <vector>
#include "k4adepthpixelcolorizer.h"
#include "k4apixel.h"
#include "k4astaticimageproperties.h"
#include "texture.h"
#include "viewerwindow.h"
using namespace viewer;
void ColorizeDepthImage(const k4a::image &depthImage,
DepthPixelVisualizationFunction visualizationFn,
std::pair<uint16_t, uint16_t> expectedValueRange,
std::vector<BgraPixel> *buffer);
int main()
{
try
{
// Check for devices
//
const uint32_t deviceCount = k4a::device::get_installed_count();
if (deviceCount == 0)
{
throw std::runtime_error("No Azure Kinect devices detected!");
}
// Start the device
//
k4a_device_configuration_t config = K4A_DEVICE_CONFIG_INIT_DISABLE_ALL;
config.camera_fps = K4A_FRAMES_PER_SECOND_30;
config.depth_mode = K4A_DEPTH_MODE_WFOV_2X2BINNED;
config.color_format = K4A_IMAGE_FORMAT_COLOR_BGRA32;
config.color_resolution = K4A_COLOR_RESOLUTION_720P;
// This means that we'll only get captures that have both color and
// depth images, so we don't need to check if the capture contains
// a particular type of image.
//
config.synchronized_images_only = true;
std::cout << "Started opening K4A device..." << std::endl;
k4a::device dev = k4a::device::open(K4A_DEVICE_DEFAULT);
dev.start_cameras(&config);
std::cout << "Finished opening K4A device." << std::endl;
// Create the viewer window.
//
ViewerWindow &window = ViewerWindow::Instance();
window.Initialize("Simple Azure Kinect Viewer", 1440, 900);
// Textures we can give to OpenGL / the viewer window to render.
//
Texture depthTexture = window.CreateTexture(GetDepthDimensions(config.depth_mode));
Texture colorTexture = window.CreateTexture(GetColorDimensions(config.color_resolution));
// A buffer containing a BGRA color representation of the depth image.
// This is what we'll end up giving to depthTexture as an image source.
// We don't need a similar buffer for the color image because the color
// image already comes to us in BGRA32 format.
//
std::vector<BgraPixel> depthTextureBuffer;
// viewer.BeginFrame() will start returning false when the user closes the window.
//
while (window.BeginFrame())
{
// Poll the device for new image data.
//
// We set the timeout to 0 so we don't block if there isn't an available frame.
//
// This works here because we're doing the work on the same thread that we're
// using for the UI, and the ViewerWindow class caps the framerate at the
// display's refresh rate (the EndFrame() call blocks on the display's sync).
//
// If we don't have new image data, we'll just reuse the textures we generated
// from the last time we got a capture.
//
k4a::capture capture;
if (dev.get_capture(&capture, std::chrono::milliseconds(0)))
{
const k4a::image depthImage = capture.get_depth_image();
const k4a::image colorImage = capture.get_color_image();
// If we hadn't set synchronized_images_only=true above, we'd need to do
// if (depthImage) { /* stuff */ } else { /* ignore the image */ } here.
//
// Depth data is in the form of uint16_t's representing the distance in
// millimeters of the pixel from the camera, so we need to convert it to
// a BGRA image before we can upload and show it.
//
ColorizeDepthImage(depthImage,
K4ADepthPixelColorizer::ColorizeBlueToRed,
GetDepthModeRange(config.depth_mode),
&depthTextureBuffer);
depthTexture.Update(&depthTextureBuffer[0]);
// Since we're using BGRA32 mode, we can just upload the color image directly.
// If you want to use one of the other modes, you have to do the conversion
// yourself.
//
colorTexture.Update(reinterpret_cast<const BgraPixel *>(colorImage.get_buffer()));
}
// Show the windows
//
const ImVec2 windowSize(window.GetWidth() / 2.f, static_cast<float>(window.GetHeight()));
window.ShowTexture("Depth", depthTexture, ImVec2(0.f, 0.f), windowSize);
window.ShowTexture("Color", colorTexture, ImVec2(window.GetWidth() / 2.f, 0.f), windowSize);
// This will tell ImGui/OpenGL to render the frame, and will block until the next vsync.
//
window.EndFrame();
}
}
catch (const std::exception &e)
{
std::cerr << e.what() << std::endl;
std::cerr << "Press [enter] to exit." << std::endl;
std::cin.get();
return 1;
}
return 0;
}
// Given a depth image, output a BGRA-formatted color image into buffer, using
// expectedValueRange to define what min/max values the depth image should have.
// Low values are blue, high values are red.
//
void ColorizeDepthImage(const k4a::image &depthImage,
DepthPixelVisualizationFunction visualizationFn,
std::pair<uint16_t, uint16_t> expectedValueRange,
std::vector<BgraPixel> *buffer)
{
// This function assumes that the image is made of depth pixels (i.e. uint16_t's),
// which is only true for IR/depth images.
//
const k4a_image_format_t imageFormat = depthImage.get_format();
if (imageFormat != K4A_IMAGE_FORMAT_DEPTH16 && imageFormat != K4A_IMAGE_FORMAT_IR16)
{
throw std::logic_error("Attempted to colorize a non-depth image!");
}
const int width = depthImage.get_width_pixels();
const int height = depthImage.get_height_pixels();
buffer->resize(static_cast<size_t>(width * height));
const uint16_t *depthData = reinterpret_cast<const uint16_t *>(depthImage.get_buffer());
for (int h = 0; h < height; ++h)
{
for (int w = 0; w < width; ++w)
{
const size_t currentPixel = static_cast<size_t>(h * width + w);
(*buffer)[currentPixel] = visualizationFn(depthData[currentPixel],
expectedValueRange.first,
expectedValueRange.second);
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
A no-frills viewer tool that just shows the depth image and color images from the default camera.
This tools takes no additional input.

Просмотреть файл

@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "texture.h"
#include "viewerutil.h"
using namespace viewer;
Texture::Texture(int width, int height) : m_width(width), m_height(height)
{
glGenTextures(1, &m_name);
glBindTexture(GL_TEXTURE_2D, m_name);
// Set filtering mode so the texture gets sampled properly
//
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Reserve storage for the texture
//
glTexStorage2D(GL_TEXTURE_2D, // target
1, // levels
GL_RGBA32F, // internalformat
m_width, // width
m_height); // height
CheckOpenGLErrors();
}
Texture::Texture(Texture &&other)
{
*this = std::move(other);
}
Texture &Texture::operator=(Texture &&other)
{
if (this == &other)
{
return *this;
}
DeleteTexture();
m_name = other.m_name;
m_height = other.m_height;
m_width = other.m_width;
other.m_name = InvalidTextureName;
other.m_height = 0;
other.m_width = 0;
return *this;
}
Texture::~Texture()
{
DeleteTexture();
}
void Texture::DeleteTexture()
{
glDeleteTextures(1, &m_name);
}
void Texture::Update(const BgraPixel *data)
{
glBindTexture(GL_TEXTURE_2D, m_name);
glTexSubImage2D(GL_TEXTURE_2D, // target
0, // level
0, // xoffset
0, // yoffset
m_width, // width
m_height, // height
GL_BGRA, // format
GL_UNSIGNED_BYTE, // type
reinterpret_cast<const GLvoid *>(data)); // pixels
CheckOpenGLErrors();
}

Просмотреть файл

@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef TEXTURE_H
#define TEXTURE_H
#include <utility>
#include "k4aimgui_all.h"
#include "k4apixel.h"
namespace viewer
{
// A simple wrapper for OpenGL textures.
// Textures are not resizeable, but can have their contents updated any number of times.
//
class Texture
{
public:
// Updates the image stored in the texture with data.
// The data is expected to be formatted as Width * Height pixels formatted
// as BGRA pixels represented by uint8_t's.
//
// The data you pass to it must be of the same size as the dimensions used
// to construct the texture.
//
void Update(const BgraPixel *data);
// Get the OpenGL texture name.
// OpenGL texture names are just uints that OpenGL uses as opaque pointers.
//
inline GLuint Name() const
{
return m_name;
}
// Gets the width of the texture.
//
inline int Width() const
{
return m_width;
}
// Gets the height of the texture.
//
inline int Height() const
{
return m_height;
}
~Texture();
// In the interest of simplicity, we don't refcount textures, so
// copying doesn't work.
//
Texture(const Texture &) = delete;
Texture &operator=(const Texture &) = delete;
// Textures are moveable.
//
Texture(Texture &&other);
Texture &operator=(Texture &&other);
private:
// Creating a texture before the viewer window has been initialized will fail,
// so we make the viewer responsible for creating texture instances.
//
// Creates a texture with the specified dimensions, including allocating space
// on the GPU. Use Update() to update the image stored in the texture.
//
friend class ViewerWindow;
Texture(int width, int height);
// Deletes the wrapped texture.
//
void DeleteTexture();
static constexpr GLuint InvalidTextureName = 0;
GLuint m_name = InvalidTextureName;
int m_width = 0;
int m_height = 0;
};
} // namespace viewer
#endif

Просмотреть файл

@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef VIEWERUTIL_H
#define VIEWERUTIL_H
#include <sstream>
#include "k4aimgui_all.h"
namespace viewer
{
// Throw an error if OpenGL has encountered an error.
//
inline void CheckOpenGLErrors()
{
const GLenum glStatus = glGetError();
if (glStatus != GL_NO_ERROR)
{
std::stringstream errorBuilder;
errorBuilder << "OpenGL error: " << static_cast<int>(glStatus);
throw std::runtime_error(errorBuilder.str().c_str());
}
}
// Gets the height of the title bar, in pixels
//
inline float GetTitleBarHeight()
{
return ImGui::GetFont()->FontSize + ImGui::GetStyle().FramePadding.y * 2;
}
// Gets the maximum dimensions that an image of size imageDimensions can be scaled to in order
// to fit in a window with imageMaxSize available space while maintaining its aspect ratio.
// imageMaxSize is expected to include space for window padding, but does not account for the
// title bar or any any potential other widgets in your window, so you'll need to subtract those
// from imageMaxSize, if applicable.
//
inline ImVec2 GetMaxImageSize(const ImVec2 imageDimensions, const ImVec2 imageMaxSize)
{
const float sourceAspectRatio = imageDimensions.x / imageDimensions.y;
const float verticalPadding = ImGui::GetStyle().WindowPadding.y * 2;
const float horizontalPadding = ImGui::GetStyle().WindowPadding.x * 2;
const float horizontalMaxSize = imageMaxSize.x - horizontalPadding;
const float verticalMaxSize = imageMaxSize.y - verticalPadding;
ImVec2 displayDimensions;
if (horizontalMaxSize / sourceAspectRatio <= verticalMaxSize)
{
displayDimensions.x = horizontalMaxSize;
displayDimensions.y = horizontalMaxSize / sourceAspectRatio;
}
else
{
displayDimensions.x = verticalMaxSize * sourceAspectRatio;
displayDimensions.y = verticalMaxSize;
}
return displayDimensions;
}
} // namespace viewer
#endif

Просмотреть файл

@ -0,0 +1,218 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "viewerwindow.h"
#include <iostream>
#include "viewerutil.h"
using namespace viewer;
namespace
{
// A callback that we give to OpenGL so we can get notified about any errors that might
// occur when they happen. Notifications relevant to performance tuning also come in
// through this callback.
//
void APIENTRY glDebugOutput(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
void *userParam)
{
(void)userParam;
// Some of the performance messages are a bit noisy, so we want to drop those
// to reduce noise in the log.
//
constexpr GLuint noisyMessages[] = {
131185, // Event that says a texture was loaded into memory
131169, // Event that says a buffer was allocated
};
for (GLuint noisyMessageId : noisyMessages)
{
if (id == noisyMessageId)
{
return;
}
}
std::cerr << "OpenGL debug message:" << std::endl
<< " source: " << source << std::endl
<< " type: " << type << std::endl
<< " id: " << id << std::endl
<< " sev: " << severity << std::endl
<< " len: " << length << std::endl
<< " msg: " << message << std::endl
<< "---------------------------" << std::endl;
}
} // namespace
ViewerWindow &ViewerWindow::Instance()
{
static ViewerWindow viewerWindow;
return viewerWindow;
}
void ViewerWindow::Initialize(const char *windowTitle, int defaultWidth, int defaultHeight)
{
std::cout << "Started initializing OpenGL..." << std::endl;
if (m_window != nullptr)
{
throw std::logic_error("Attempted to double-initialize the window!");
}
if (!glfwInit())
{
throw std::runtime_error("Failed to initialize GLFW!");
}
m_windowWidth = defaultWidth;
m_windowHeight = defaultHeight;
// Start the window
//
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
m_window = glfwCreateWindow(m_windowWidth, m_windowHeight, windowTitle, nullptr, nullptr);
glfwMakeContextCurrent(m_window);
// Enable vsync (cap framerate at the display's refresh rate)
//
glfwSwapInterval(1);
// Initialize OpenGL
//
if (gl3wInit())
{
throw std::runtime_error("Failed to initialize OpenGL!");
}
// Turn on OpenGL debugging. While not strictly necessary, this makes it much easier
// to track down OpenGL errors when they occur.
//
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(glDebugOutput, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
// Initialize ImGui
//
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplGlfw_InitForOpenGL(m_window, true);
ImGui_ImplOpenGL3_Init("#version 330");
// ImGui style settings
//
ImGui::StyleColorsDark();
ImGui::GetStyle().WindowRounding = 0.0f;
// By default, ImGui tries to save the previous window layout to disk.
// That doesn't really make sense for this application, so we want to
// disable saving the window layout.
//
ImGui::GetIO().IniFilename = nullptr;
CheckOpenGLErrors();
std::cout << "Finished initializing OpenGL." << std::endl;
}
ViewerWindow::~ViewerWindow()
{
// ImGui will assert if we throw without having called Render, which
// obscures the actual error message, so we call it here just in case.
//
ImGui::Render();
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(m_window);
glfwTerminate();
}
bool ViewerWindow::BeginFrame()
{
if (m_window == nullptr)
{
// You need to call ViewerWindow::Initialize first.
//
throw std::logic_error("Attempted to use uninitialized window!");
}
// glfwWindowShouldClose will start returning true when the user
// clicks the close button on the title bar.
//
if (glfwWindowShouldClose(m_window))
{
return false;
}
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
return true;
}
void ViewerWindow::EndFrame()
{
ImGui::Render();
glfwMakeContextCurrent(m_window);
glfwGetFramebufferSize(m_window, &m_windowWidth, &m_windowHeight);
glViewport(0, 0, m_windowWidth, m_windowHeight);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(m_window);
glfwPollEvents();
CheckOpenGLErrors();
}
Texture ViewerWindow::CreateTexture(int width, int height)
{
return Texture(width, height);
}
Texture ViewerWindow::CreateTexture(std::pair<int, int> dimensions)
{
return Texture(dimensions.first, dimensions.second);
}
void ViewerWindow::ShowTexture(const char *name, const Texture &texture, const ImVec2 &position, const ImVec2 &maxSize)
{
ImGui::SetNextWindowPos(position);
ImGui::SetNextWindowSize(maxSize);
if (ImGui::Begin(name, nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse))
{
// Figure out how big we can make the image
//
ImVec2 imageSize(static_cast<float>(texture.Width()), static_cast<float>(texture.Height()));
ImVec2 imageMaxSize = maxSize;
imageMaxSize.y -= GetTitleBarHeight();
imageSize = GetMaxImageSize(imageSize, imageMaxSize);
ImGui::Image(reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture.Name())), imageSize);
}
ImGui::End();
}

Просмотреть файл

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef VIEWERWINDOW_H
#define VIEWERWINDOW_H
#include "k4aimgui_all.h"
#include "texture.h"
namespace viewer
{
class ViewerWindow
{
public:
// ImGui's OpenGL/GLFW bindings use globals that make having multiple windows
// not work properly, so the ViewerWindow is a singleton.
//
static ViewerWindow &Instance();
// Initialize the window, setting its default title and dimensions.
// You must call Initialize() before attempting to render to the window.
//
void Initialize(const char *windowTitle, int defaultWidth, int defaultHeight);
// Tells the graphics framework to start a new frame.
// Returns false if the application has been closed and we should exit.
//
bool BeginFrame();
// Tells the graphics framework to finish rendering the current frame.
//
void EndFrame();
// Get the current width of the window
// Useful for figuring out how to size what you pass to ShowWindow().
//
inline int GetWidth() const
{
return m_windowWidth;
}
// Get the current height of the window.
// Useful for figuring out how to size what you pass to ShowWindow().
//
inline int GetHeight() const
{
return m_windowHeight;
}
// Create a Texture with the specified dimensions that you can use to show image data
//
Texture CreateTexture(int width, int height);
Texture CreateTexture(std::pair<int, int> dimensions);
// Show a sub-window within the main window with the given name that shows the image stored in
// texture at the provided position. The image will be stretched (maintaining aspect ratio) to
// fill maxSize.
//
void ShowTexture(const char *name, const Texture &texture, const ImVec2 &position, const ImVec2 &maxSize);
~ViewerWindow();
private:
ViewerWindow() = default;
GLFWwindow *m_window = nullptr;
int m_windowWidth;
int m_windowHeight;
};
} // namespace viewer
#endif

Просмотреть файл

@ -33,37 +33,12 @@
// Project headers
//
#include "assertionexception.h"
#include "k4astaticimageproperties.h"
#include "k4aviewererrormanager.h"
#include "perfcounter.h"
using namespace k4aviewer;
namespace
{
ImageDimensions GetDimensionsForColorResolution(const k4a_color_resolution_t resolution)
{
switch (resolution)
{
case K4A_COLOR_RESOLUTION_720P:
return { 1280, 720 };
case K4A_COLOR_RESOLUTION_2160P:
return { 3840, 2160 };
case K4A_COLOR_RESOLUTION_1440P:
return { 2560, 1440 };
case K4A_COLOR_RESOLUTION_1080P:
return { 1920, 1080 };
case K4A_COLOR_RESOLUTION_3072P:
return { 4096, 3072 };
case K4A_COLOR_RESOLUTION_1536P:
return { 2048, 1536 };
default:
throw AssertionException("Invalid color dimensions value");
}
}
} // namespace
class K4AColorFrameVisualizerBase
{
public:
@ -71,9 +46,9 @@ public:
protected:
explicit K4AColorFrameVisualizerBase(k4a_color_resolution_t colorResolution) :
m_dimensions(GetDimensionsForColorResolution(colorResolution))
m_dimensions(GetColorDimensions(colorResolution))
{
m_expectedBufferSize = sizeof(RgbaPixel) * static_cast<size_t>(m_dimensions.Width * m_dimensions.Height);
m_expectedBufferSize = sizeof(BgraPixel) * static_cast<size_t>(m_dimensions.Width * m_dimensions.Height);
}
size_t m_expectedBufferSize;
@ -122,7 +97,7 @@ public:
int result = libyuv::YUY2ToARGB(image.get_buffer(), // src_yuy2,
stride, // src_stride_yuy2,
&buffer.Data[0], // dst_argb,
m_dimensions.Width * static_cast<int>(sizeof(RgbaPixel)), // dst_stride_argb,
m_dimensions.Width * static_cast<int>(sizeof(BgraPixel)), // dst_stride_argb,
m_dimensions.Width, // width,
m_dimensions.Height // height
);
@ -189,7 +164,7 @@ public:
hueSatStart, // src_vu
hueSatStride, // src_stride_vu
&buffer.Data[0], // dst_argb
m_dimensions.Width * static_cast<int>(sizeof(RgbaPixel)), // dst_stride_argb
m_dimensions.Width * static_cast<int>(sizeof(BgraPixel)), // dst_stride_argb
m_dimensions.Width, // width
m_dimensions.Height // height
);
@ -232,7 +207,7 @@ public:
ImageVisualizationResult ConvertImage(const k4a::image &image,
K4ATextureBuffer<K4A_IMAGE_FORMAT_COLOR_BGRA32> &buffer) override
{
if (image.get_size() != static_cast<size_t>(m_dimensions.Height * m_dimensions.Width) * sizeof(RgbaPixel))
if (image.get_size() != static_cast<size_t>(m_dimensions.Height * m_dimensions.Width) * sizeof(BgraPixel))
{
return ImageVisualizationResult::InvalidBufferSizeError;
}

Просмотреть файл

@ -14,16 +14,17 @@
//
#include "k4adepthsensorframebasevisualizer.h"
#include "k4adepthpixelcolorizer.h"
#include "k4astaticimageproperties.h"
namespace k4aviewer
{
class K4ADepthFrameVisualizer
: public K4ADepthSensorFrameBaseVisualizer<K4A_IMAGE_FORMAT_DEPTH16, K4ADepthPixelColorizer::ColorizeRedToBlue>
: public K4ADepthSensorFrameBaseVisualizer<K4A_IMAGE_FORMAT_DEPTH16, K4ADepthPixelColorizer::ColorizeBlueToRed>
{
public:
explicit K4ADepthFrameVisualizer(k4a_depth_mode_t depthMode) :
K4ADepthSensorFrameBaseVisualizer(depthMode, GetRangeForDepthMode(depthMode))
K4ADepthSensorFrameBaseVisualizer(depthMode, GetDepthModeRange(depthMode))
{
}

Просмотреть файл

@ -10,114 +10,92 @@
// Library headers
//
#include "k4aimgui_all.h"
// Project headers
//
#include "k4apixel.h"
#include "k4aviewerutil.h"
namespace k4aviewer
{
using DepthPixelVisualizationFunction = RgbPixel(const ExpectedValueRange &validValueRange, const DepthPixel &value);
using DepthPixelVisualizationFunction = BgraPixel(const DepthPixel &value,
const DepthPixel &min,
const DepthPixel &max);
// Functions that provide ways to take depth images and turn them into color representations
// suitable for showing to humans.
//
class K4ADepthPixelColorizer
{
public:
// Colorizing strategy that uses a rainbow gradient where blue is near and red is far
// Computes a color representation of a depth pixel on the blue-red spectrum, using min
// as the value for blue and max as the value for red.
//
static inline RgbPixel ColorizeRedToBlue(const ExpectedValueRange &validValueRange, const DepthPixel &value)
static inline BgraPixel ColorizeBlueToRed(const DepthPixel &depthPixel,
const DepthPixel &min,
const DepthPixel &max)
{
float c[3] = { 1.0, 1.0, 1.0 }; // white
constexpr uint8_t PixelMax = std::numeric_limits<uint8_t>::max();
// Default to opaque black.
//
BgraPixel result = { 0, 0, 0, PixelMax };
// If the pixel is actual zero and not just below the min value, make it black
//
if (value == 0)
if (depthPixel == 0)
{
return RgbPixel{ 0, 0, 0 };
return result;
}
DepthPixel clampedValue = value;
clampedValue = std::min(clampedValue, validValueRange.Max);
clampedValue = std::max(clampedValue, validValueRange.Min);
uint16_t clampedValue = depthPixel;
clampedValue = std::min(clampedValue, max);
clampedValue = std::max(clampedValue, min);
const auto dv = static_cast<float>(validValueRange.Max - validValueRange.Min);
const float index = (clampedValue - validValueRange.Min) / dv; // [0, 1]
// If we're less than min, in the middle, or greater than max, then do B, G, R, respectively
if (index <= 0.0f)
{
c[0] = 0.0f;
c[1] = 0.0f;
c[2] = 1.0f; // Blue
}
else if (index == 0.5f)
{
c[0] = 0.0f;
c[1] = 1.0f; // Green
c[2] = 0.0f;
}
else if (index >= 1.0f)
{
c[0] = 1.0f; // Red
c[1] = 0.0f;
c[2] = 0.0f;
}
// The in between colors
else if (index < 0.5f)
{
// Adjust only Blue and Green
const float colorToSubtract = index - 0.25f; // [-.25, .25]
c[0] = 0.0f; // Red
if (colorToSubtract < 0.0f)
{
// If < 0, then subtract from Green
c[1] = 1.0f + (4.0f * colorToSubtract);
c[2] = 1.0f;
}
else
{
// If > 0, then subtract from Blue
c[1] = 1.0f;
c[2] = 1.0f - (4.0f * colorToSubtract);
}
}
else
{
// Adjust only Green and Red
const float colorToSubtract = index - 0.75f; // [-.25, .25]
c[2] = 0.0f; // Blue
if (colorToSubtract < 0.0f)
{
// If < 0, then subtract from Red
c[0] = 1.0f + (4.0f * colorToSubtract);
c[1] = 1.0f;
}
else
{
// If > 0, then subtract from Green
c[0] = 1.0f;
c[1] = 1.0f - (4.0f * colorToSubtract);
}
}
// Normalize to [0, 1]
//
float hue = (clampedValue - min) / static_cast<float>(max - min);
return RgbPixel{ static_cast<uint8_t>(c[0] * std::numeric_limits<uint8_t>::max()),
static_cast<uint8_t>(c[1] * std::numeric_limits<uint8_t>::max()),
static_cast<uint8_t>(c[2] * std::numeric_limits<uint8_t>::max()) };
// The 'hue' coordinate in HSV is a polar coordinate, so it 'wraps'.
// Purple starts after blue and is close enough to red to be a bit unclear,
// so we want to go from blue to red. Purple starts around .6666667,
// so we want to normalize to [0, .6666667].
//
constexpr float range = 2.f / 3.f;
hue *= range;
// We want blue to be close and red to be far, so we need to reflect the
// hue across the middle of the range.
//
hue = range - hue;
float fRed = 0.f;
float fGreen = 0.f;
float fBlue = 0.f;
ImGui::ColorConvertHSVtoRGB(hue, 1.f, 1.f, fRed, fGreen, fBlue);
result.Red = static_cast<uint8_t>(fRed * PixelMax);
result.Green = static_cast<uint8_t>(fGreen * PixelMax);
result.Blue = static_cast<uint8_t>(fBlue * PixelMax);
return result;
}
static inline RgbPixel ColorizeGreyscale(const ExpectedValueRange &expectedValueRange, const DepthPixel &value)
// Computes a greyscale representation of a depth pixel.
//
static inline BgraPixel ColorizeGreyscale(const DepthPixel &value, const DepthPixel &min, const DepthPixel &max)
{
// Clamp to max
//
DepthPixel pixelValue = std::min(value, expectedValueRange.Max);
DepthPixel pixelValue = std::min(value, max);
const auto normalizedValue = static_cast<uint8_t>(
(pixelValue - expectedValueRange.Min) *
(double(std::numeric_limits<uint8_t>::max()) / (expectedValueRange.Max - expectedValueRange.Min)));
constexpr uint8_t PixelMax = std::numeric_limits<uint8_t>::max();
const auto normalizedValue = static_cast<uint8_t>((pixelValue - min) * (double(PixelMax) / (max - min)));
// All color channels (RGB) are set the same (image is greyscale)
// All color channels are set the same (image is greyscale)
//
return RgbPixel{ normalizedValue, normalizedValue, normalizedValue };
return BgraPixel{ normalizedValue, normalizedValue, normalizedValue, PixelMax };
}
};
} // namespace k4aviewer

Просмотреть файл

@ -14,9 +14,9 @@
// Project headers
//
#include "assertionexception.h"
#include "ik4aframevisualizer.h"
#include "k4adepthpixelcolorizer.h"
#include "k4astaticimageproperties.h"
#include "k4aviewerutil.h"
#include "perfcounter.h"
@ -27,16 +27,16 @@ class K4ADepthSensorFrameBaseVisualizer : public IK4AFrameVisualizer<ImageFormat
{
public:
explicit K4ADepthSensorFrameBaseVisualizer(const k4a_depth_mode_t depthMode,
const ExpectedValueRange expectedValueRange) :
m_dimensions(GetImageDimensionsForDepthMode(depthMode)),
const std::pair<DepthPixel, DepthPixel> expectedValueRange) :
m_dimensions(GetDepthDimensions(depthMode)),
m_expectedValueRange(expectedValueRange),
m_expectedBufferSize(static_cast<size_t>(m_dimensions.Width * m_dimensions.Height) * sizeof(RgbPixel))
m_expectedBufferSize(static_cast<size_t>(m_dimensions.Width * m_dimensions.Height) * sizeof(BgraPixel))
{
}
GLenum InitializeTexture(std::shared_ptr<K4AViewerImage> &texture) override
{
return K4AViewerImage::Create(&texture, nullptr, m_dimensions, GL_RGB);
return K4AViewerImage::Create(&texture, nullptr, m_dimensions, GL_BGRA);
}
void InitializeBuffer(K4ATextureBuffer<ImageFormat> &buffer) override
@ -98,36 +98,16 @@ private:
{
const DepthPixel pixelValue = *reinterpret_cast<const DepthPixel *>(currentSrc);
RgbPixel *outputPixel = reinterpret_cast<RgbPixel *>(dst);
*outputPixel = VisualizationFunction(m_expectedValueRange, pixelValue);
BgraPixel *outputPixel = reinterpret_cast<BgraPixel *>(dst);
*outputPixel = VisualizationFunction(pixelValue, m_expectedValueRange.first, m_expectedValueRange.second);
dst += sizeof(RgbPixel);
dst += sizeof(BgraPixel);
currentSrc += sizeof(DepthPixel);
}
}
static ImageDimensions GetImageDimensionsForDepthMode(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_NFOV_2X2BINNED:
return { 320, 288 };
case K4A_DEPTH_MODE_NFOV_UNBINNED:
return { 640, 576 };
case K4A_DEPTH_MODE_WFOV_2X2BINNED:
return { 512, 512 };
case K4A_DEPTH_MODE_WFOV_UNBINNED:
return { 1024, 1024 };
case K4A_DEPTH_MODE_PASSIVE_IR:
return { 1024, 1024 };
default:
throw AssertionException("Invalid depth dimensions value");
}
}
const ImageDimensions m_dimensions;
const ExpectedValueRange m_expectedValueRange;
const std::pair<DepthPixel, DepthPixel> m_expectedValueRange;
const size_t m_expectedBufferSize;
};

Просмотреть файл

@ -21,8 +21,9 @@
#define NOMINMAX
// Clang parses doxygen-style comments in your source and checks for doxygen syntax errors.
// Unfortunately, some of our external dependencies have doxygen syntax errors in them, so
// we need to shut off that warning.
// Unfortunately, some of our external dependencies have doxygen syntax errors in their
// headers and clang looks at them when we include them here, so we need to shut off those
// warnings on these headers.
//
#ifdef __clang__
#pragma clang diagnostic push

Просмотреть файл

@ -18,27 +18,9 @@
using namespace k4aviewer;
namespace
{
ExpectedValueRange GetExpectedValueRangeForDepthMode(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_PASSIVE_IR:
return { 0, 100 };
case K4A_DEPTH_MODE_OFF:
throw AssertionException("Invalid depth mode!");
default:
return { 0, 1000 };
}
}
} // namespace
K4AInfraredFrameVisualizer::K4AInfraredFrameVisualizer(const k4a_depth_mode_t depthMode) :
K4ADepthSensorFrameBaseVisualizer<K4A_IMAGE_FORMAT_IR16, K4ADepthPixelColorizer::ColorizeGreyscale>(
depthMode,
GetExpectedValueRangeForDepthMode(depthMode))
K4ADepthSensorFrameBaseVisualizer<K4A_IMAGE_FORMAT_IR16, K4ADepthPixelColorizer::ColorizeGreyscale>(depthMode,
GetIrLevels(
depthMode))
{
}

Просмотреть файл

@ -4,35 +4,10 @@
#ifndef K4APIXEL_H
#define K4APIXEL_H
// Associated header
// Helper structs/typedefs to cast buffers to
//
// System headers
//
#include <memory>
// Library headers
//
// Project headers
//
namespace k4aviewer
{
struct RgbPixel
{
uint8_t Red;
uint8_t Green;
uint8_t Blue;
};
struct RgbaPixel
{
uint8_t Red;
uint8_t Green;
uint8_t Blue;
uint8_t Alpha;
};
struct BgraPixel
{

Просмотреть файл

@ -17,6 +17,7 @@
#include "assertionexception.h"
#include "k4acolorframevisualizer.h"
#include "k4adepthpixelcolorizer.h"
#include "k4astaticimageproperties.h"
#include "k4aviewerutil.h"
#include "perfcounter.h"
@ -177,7 +178,7 @@ K4APointCloudVisualizer::K4APointCloudVisualizer(const bool enableColorPointClou
m_enableColorPointCloud(enableColorPointCloud),
m_calibrationData(calibrationData)
{
m_expectedValueRange = GetRangeForDepthMode(m_calibrationData.depth_mode);
m_expectedValueRange = GetDepthModeRange(m_calibrationData.depth_mode);
m_transformation = k4a::transformation(m_calibrationData);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer.Id());
@ -249,11 +250,9 @@ PointCloudVisualizationResult K4APointCloudVisualizer::UpdatePointClouds(const k
while (dstPixel != endPixel)
{
const RgbPixel colorization = K4ADepthPixelColorizer::ColorizeRedToBlue(m_expectedValueRange, *srcPixel);
dstPixel->Red = colorization.Red;
dstPixel->Green = colorization.Green;
dstPixel->Blue = colorization.Blue;
dstPixel->Alpha = 0xFF;
*dstPixel = K4ADepthPixelColorizer::ColorizeBlueToRed(*srcPixel,
m_expectedValueRange.first,
m_expectedValueRange.second);
++dstPixel;
++srcPixel;

Просмотреть файл

@ -66,7 +66,7 @@ public:
private:
PointCloudVisualizationResult UpdatePointClouds(const k4a::capture &capture);
ExpectedValueRange m_expectedValueRange;
std::pair<DepthPixel, DepthPixel> m_expectedValueRange;
ImageDimensions m_dimensions;
PointCloudRenderer m_pointCloudRenderer;

Просмотреть файл

@ -63,7 +63,7 @@ void K4APointCloudWindow::Show(K4AWindowPlacementInfo placementInfo)
const ImVec2 sourceImageSize = ImVec2(static_cast<float>(m_texture->GetDimensions().Width),
static_cast<float>(m_texture->GetDimensions().Height));
const ImVec2 textureSize = GetImageSize(sourceImageSize, availableSize);
const ImVec2 textureSize = GetMaxImageSize(sourceImageSize, availableSize);
ImGui::Image(static_cast<ImTextureID>(*m_texture), textureSize);

Просмотреть файл

@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4AIMAGESIZES_H
#define K4AIMAGESIZES_H
#include <exception>
#include <utility>
#include <k4a/k4a.hpp>
namespace k4aviewer
{
// Gets the dimensions of the color images that the color camera will produce for a
// given color resolution
//
inline std::pair<int, int> GetColorDimensions(const k4a_color_resolution_t resolution)
{
switch (resolution)
{
case K4A_COLOR_RESOLUTION_720P:
return { 1280, 720 };
case K4A_COLOR_RESOLUTION_2160P:
return { 3840, 2160 };
case K4A_COLOR_RESOLUTION_1440P:
return { 2560, 1440 };
case K4A_COLOR_RESOLUTION_1080P:
return { 1920, 1080 };
case K4A_COLOR_RESOLUTION_3072P:
return { 4096, 3072 };
case K4A_COLOR_RESOLUTION_1536P:
return { 2048, 1536 };
default:
throw std::logic_error("Invalid color dimensions value!");
}
}
// Gets the dimensions of the depth images that the depth camera will produce for a
// given depth mode
//
inline std::pair<int, int> GetDepthDimensions(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_NFOV_2X2BINNED:
return { 320, 288 };
case K4A_DEPTH_MODE_NFOV_UNBINNED:
return { 640, 576 };
case K4A_DEPTH_MODE_WFOV_2X2BINNED:
return { 512, 512 };
case K4A_DEPTH_MODE_WFOV_UNBINNED:
return { 1024, 1024 };
case K4A_DEPTH_MODE_PASSIVE_IR:
return { 1024, 1024 };
default:
throw std::logic_error("Invalid depth dimensions value!");
}
}
// Gets the range of values that we expect to see from the depth camera
// when using a given depth mode, in millimeters
//
inline std::pair<uint16_t, uint16_t> GetDepthModeRange(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_NFOV_2X2BINNED:
return { (uint16_t)500, (uint16_t)5800 };
case K4A_DEPTH_MODE_NFOV_UNBINNED:
return { (uint16_t)500, (uint16_t)4000 };
case K4A_DEPTH_MODE_WFOV_2X2BINNED:
return { (uint16_t)250, (uint16_t)3000 };
case K4A_DEPTH_MODE_WFOV_UNBINNED:
return { (uint16_t)250, (uint16_t)2500 };
case K4A_DEPTH_MODE_PASSIVE_IR:
default:
throw std::logic_error("Invalid depth mode!");
}
}
// Gets the expected min/max IR brightness levels that we expect to see
// from the IR camera when using a given depth mode
//
inline std::pair<uint16_t, uint16_t> GetIrLevels(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_PASSIVE_IR:
return { (uint16_t)0, (uint16_t)100 };
case K4A_DEPTH_MODE_OFF:
throw std::logic_error("Invalid depth mode!");
default:
return { (uint16_t)0, (uint16_t)1000 };
}
}
} // namespace k4aviewer
#endif

Просмотреть файл

@ -94,7 +94,7 @@ private:
// Compute how big we can make the image
//
const ImVec2 displayDimensions = GetImageSize(sourceImageDimensions, maxSize);
const ImVec2 displayDimensions = GetMaxImageSize(sourceImageDimensions, maxSize);
ImGui::Image(static_cast<ImTextureID>(*m_currentTexture), displayDimensions);

Просмотреть файл

@ -59,25 +59,29 @@ void APIENTRY glDebugOutput(GLenum source,
{
(void)userParam;
// Really noisy event that just says a texture was loaded into memory; skip.
//
if (id == 131185)
return;
constexpr GLuint noisyMessages[] = {
131185, // Event that says a texture was loaded into memory
131169, // Event that says a buffer was allocated
};
// Info message saying a buffer was allocated. Happens during normal execution.
//
if (id == 131169)
return;
for (GLuint noisyMessageId : noisyMessages)
{
if (id == noisyMessageId)
{
return;
}
}
std::ofstream msgLogger;
msgLogger.open("k4aviewer.log", std::ofstream::out | std::ofstream::app);
msgLogger << "source: " << source << std::endl
<< "type: " << type << std::endl
<< "id: " << id << std::endl
<< "sev: " << severity << std::endl
<< "len: " << length << std::endl
<< "msg: " << message << std::endl
<< std::endl;
msgLogger << "OpenGL debug message:" << std::endl
<< " source: " << source << std::endl
<< " type: " << type << std::endl
<< " id: " << id << std::endl
<< " sev: " << severity << std::endl
<< " len: " << length << std::endl
<< " msg: " << message << std::endl
<< "---------------------------" << std::endl;
msgLogger.close();
}
@ -98,10 +102,6 @@ K4AViewer::K4AViewer(const K4AViewerOptions &args)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef K4AVIEWER_ENABLE_OPENGL_DEBUGGING
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
#endif
m_window = glfwCreateWindow(1440, 900, "Azure Kinect Viewer", nullptr, nullptr);
glfwMakeContextCurrent(m_window);
@ -109,6 +109,7 @@ K4AViewer::K4AViewer(const K4AViewerOptions &args)
gl3wInit();
#ifdef K4AVIEWER_ENABLE_OPENGL_DEBUGGING
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(glDebugOutput, nullptr);
@ -210,7 +211,6 @@ void K4AViewer::Run()
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwMakeContextCurrent(m_window);
glfwSwapBuffers(m_window);
}
}

Просмотреть файл

@ -22,6 +22,10 @@ namespace k4aviewer
{
struct ImageDimensions
{
ImageDimensions() = default;
constexpr ImageDimensions(int w, int h) : Width(w), Height(h) {}
constexpr ImageDimensions(const std::pair<int, int> &pair) : Width(pair.first), Height(pair.second) {}
int Width;
int Height;
};

Просмотреть файл

@ -15,7 +15,6 @@
// Project headers
//
#include "assertionexception.h"
namespace k4aviewer
{
@ -49,31 +48,6 @@ private:
std::function<void()> m_cleanupFunction;
};
struct ExpectedValueRange
{
uint16_t Min;
uint16_t Max;
};
static inline ExpectedValueRange GetRangeForDepthMode(const k4a_depth_mode_t depthMode)
{
switch (depthMode)
{
case K4A_DEPTH_MODE_NFOV_2X2BINNED:
return { 500, 5800 };
case K4A_DEPTH_MODE_NFOV_UNBINNED:
return { 500, 4000 };
case K4A_DEPTH_MODE_WFOV_2X2BINNED:
return { 250, 3000 };
case K4A_DEPTH_MODE_WFOV_UNBINNED:
return { 250, 2500 };
case K4A_DEPTH_MODE_PASSIVE_IR:
default:
throw AssertionException("Invalid depth mode");
}
}
} // namespace k4aviewer
// Reimplementations of utility functions from C++14 that were missed from C++11

Просмотреть файл

@ -149,6 +149,7 @@ void K4AWindowManager::ShowWindow(const ImVec2 windowAreaPosition,
K4AWindowPlacementInfo placementInfo;
placementInfo.Position = windowAreaPosition;
placementInfo.Size = windowAreaSize;
placementInfo.Size.y -= GetTitleBarHeight();
ImGui::SetNextWindowPos(windowAreaPosition);
ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), windowAreaSize);

Просмотреть файл

@ -20,31 +20,31 @@ inline float GetStandardVerticalSliderWidth()
{
// Width of the slider is 1 character, plus half the normal padding
//
return ImGui::GetDefaultFont()->FontSize + ImGui::GetStyle().FramePadding.x;
return ImGui::GetFont()->FontSize + ImGui::GetStyle().FramePadding.x;
}
inline float GetTitleBarHeight()
{
return ImGui::GetDefaultFont()->FontSize + ImGui::GetStyle().FramePadding.y * 2;
return ImGui::GetFont()->FontSize + ImGui::GetStyle().FramePadding.y * 2;
}
inline float GetDefaultButtonHeight()
{
return ImGui::GetDefaultFont()->FontSize + ImGui::GetStyle().FramePadding.y * 2 + ImGui::GetStyle().ItemSpacing.y;
return ImGui::GetFont()->FontSize + ImGui::GetStyle().FramePadding.y * 2 + ImGui::GetStyle().ItemSpacing.y;
}
// Gets the maximum dimensions that an image of size imageDimensions can be scaled to in order
// to fit in a window with imageMaxSize available space.
// imageMaxSize is expected to include space for the title bar and window padding, but does not
// account for any potential other widgets in your window, so you'll need to subtract those from
// imageMaxSize, if applicable.
// to fit in a window with imageMaxSize available space while maintaining its aspect ratio.
// imageMaxSize is expected to include space for window padding, but does not account for the
// title bar or any any potential other widgets in your window, so you'll need to subtract those
// from imageMaxSize, if applicable.
//
inline ImVec2 GetImageSize(const ImVec2 imageDimensions, const ImVec2 imageMaxSize)
inline ImVec2 GetMaxImageSize(const ImVec2 imageDimensions, const ImVec2 imageMaxSize)
{
const float sourceAspectRatio = imageDimensions.x / imageDimensions.y;
const float verticalPadding = GetTitleBarHeight() + ImGui::GetStyle().WindowPadding.y * 2;
const float horizontalPadding = ImGui::GetStyle().FramePadding.x * 2 + ImGui::GetStyle().WindowPadding.x * 2;
const float verticalPadding = ImGui::GetStyle().WindowPadding.y * 2;
const float horizontalPadding = ImGui::GetStyle().WindowPadding.x * 2;
const float horizontalMaxSize = imageMaxSize.x - horizontalPadding;
const float verticalMaxSize = imageMaxSize.y - verticalPadding;