Merge branch 'develop' into feature/transformation-custom-image-support
This commit is contained in:
Коммит
1a1da9e5be
|
@ -48,10 +48,10 @@ extern "C" {
|
|||
*/
|
||||
K4A_EXPORT uint32_t k4a_device_get_installed_count(void);
|
||||
|
||||
/** Sets and clears the callback function to recieve debug messages from the Azure Kinect device.
|
||||
/** Sets and clears the callback function to receive debug messages from the Azure Kinect device.
|
||||
*
|
||||
* \param message_cb
|
||||
* The callback function to recieve messages from. Set to NULL to unregister the callback function.
|
||||
* The callback function to receive messages from. Set to NULL to unregister the callback function.
|
||||
*
|
||||
* \param message_cb_context
|
||||
* The callback functions context.
|
||||
|
@ -96,6 +96,42 @@ K4A_EXPORT k4a_result_t k4a_set_debug_message_handler(k4a_logging_message_cb_t *
|
|||
void *message_cb_context,
|
||||
k4a_log_level_t min_level);
|
||||
|
||||
/** Sets the callback functions for the SDK allocator
|
||||
*
|
||||
* \param allocate
|
||||
* The callback function to allocate memory. When the SDK requires memory allocation this callback will be
|
||||
* called and the application can provide a buffer and a context.
|
||||
*
|
||||
* \param free
|
||||
* The callback function to free memory. The SDK will call this function when memory allocated by \p allocate
|
||||
* is no longer needed.
|
||||
*
|
||||
* \return ::K4A_RESULT_SUCCEEDED if the callback function was set or cleared successfully. ::K4A_RESULT_FAILED if an
|
||||
* error is encountered or the callback function has already been set.
|
||||
*
|
||||
* \remarks
|
||||
* Call this function to hook memory allocation by the SDK. Calling with both \p allocate and \p free as NULL will
|
||||
* clear the hook and reset to the default allocator.
|
||||
*
|
||||
* \remarks
|
||||
* If this function is called after memory has been allocated, the previous version of \p free function may still be
|
||||
* called in the future. The SDK will always call the \p free function that was set at the time that the memory
|
||||
* was allocated.
|
||||
*
|
||||
* \remarks
|
||||
* Not all memory allocation by the SDK is performed by this allocate function. Small allocations or allocations
|
||||
* from special pools may come from other sources.
|
||||
*
|
||||
* \xmlonly
|
||||
* <requirements>
|
||||
* <requirement name="Header">k4a.h (include k4a/k4a.h)</requirement>
|
||||
* <requirement name="Library">k4a.lib</requirement>
|
||||
* <requirement name="DLL">k4a.dll</requirement>
|
||||
* </requirements>
|
||||
* \endxmlonly
|
||||
*/
|
||||
K4A_EXPORT k4a_result_t k4a_set_allocator(k4a_memory_allocate_cb_t allocate, k4a_memory_destroy_cb_t free);
|
||||
|
||||
/** Open an Azure Kinect device.
|
||||
*
|
||||
* \param index
|
||||
|
|
|
@ -286,6 +286,7 @@ typedef enum
|
|||
* </requirements>
|
||||
* \endxmlonly
|
||||
*/
|
||||
// Be sure to update k4a_depth_mode_to_string in k4a.c if enum values are added.
|
||||
typedef enum
|
||||
{
|
||||
K4A_DEPTH_MODE_OFF = 0, /**< Depth sensor will be turned off with this setting. */
|
||||
|
@ -304,6 +305,7 @@ typedef enum
|
|||
* </requirements>
|
||||
* \endxmlonly
|
||||
*/
|
||||
// Be sure to update k4a_color_resolution_to_string in k4a.c if enum values are added.
|
||||
typedef enum
|
||||
{
|
||||
K4A_COLOR_RESOLUTION_OFF = 0, /**< Color camera will be turned off with this setting */
|
||||
|
@ -326,6 +328,7 @@ typedef enum
|
|||
* </requirements>
|
||||
* \endxmlonly
|
||||
*/
|
||||
// Be sure to update k4a_image_format_to_string in k4a.c if enum values are added.
|
||||
typedef enum
|
||||
{
|
||||
/** Color image type MJPG.
|
||||
|
@ -465,13 +468,13 @@ typedef enum
|
|||
* This enumeration is used to select the desired frame rate to operate the cameras. The actual
|
||||
* frame rate may vary slightly due to dropped data, synchronization variation between devices,
|
||||
* clock accuracy, or if the camera exposure priority mode causes reduced frame rate.
|
||||
*
|
||||
* \xmlonly
|
||||
* <requirements>
|
||||
* <requirement name="Header">k4atypes.h (include k4a/k4a.h)</requirement>
|
||||
* </requirements>
|
||||
* \endxmlonly
|
||||
*/
|
||||
// Be sure to update k4a_fps_to_string in k4a.c if enum values are added.
|
||||
typedef enum
|
||||
{
|
||||
K4A_FRAMES_PER_SECOND_5 = 0, /**< 5 FPS */
|
||||
|
@ -861,6 +864,30 @@ typedef void(k4a_logging_message_cb_t)(void *context,
|
|||
*/
|
||||
typedef void(k4a_memory_destroy_cb_t)(void *buffer, void *context);
|
||||
|
||||
/** Callback function for a memory allocation.
|
||||
*
|
||||
* \param size
|
||||
* Minimum size in bytes needed for the buffer.
|
||||
*
|
||||
* \param context
|
||||
* Output parameter for a context that will be provided in the subsequent call to the \ref k4a_memory_destroy_cb_t
|
||||
* callback.
|
||||
*
|
||||
* \return
|
||||
* A pointer to the newly allocated memory.
|
||||
*
|
||||
* \remarks
|
||||
* A callback of this type is provided when there is an application defined allocator.
|
||||
*
|
||||
* \xmlonly
|
||||
* <requirements>
|
||||
* <requirement name="Header">k4atypes.h (include k4a/k4a.h)</requirement>
|
||||
* </requirements>
|
||||
* \endxmlonly
|
||||
*
|
||||
*/
|
||||
typedef uint8_t *(k4a_memory_allocate_cb_t)(int size, void **context);
|
||||
|
||||
/**
|
||||
*
|
||||
* @}
|
||||
|
@ -970,8 +997,8 @@ typedef struct _k4a_device_configuration_t
|
|||
*/
|
||||
typedef struct _k4a_calibration_extrinsics_t
|
||||
{
|
||||
float rotation[9]; /**< Rotation matrix*/
|
||||
float translation[3]; /**< Translation vector*/
|
||||
float rotation[9]; /**< 3x3 Rotation matrix stored in row major order */
|
||||
float translation[3]; /**< Translation vector, x,y,z (in millimeters) */
|
||||
} k4a_calibration_extrinsics_t;
|
||||
|
||||
/** Camera intrinsic calibration data.
|
||||
|
|
|
@ -43,6 +43,42 @@ void allocator_initialize(void);
|
|||
*/
|
||||
void allocator_deinitialize(void);
|
||||
|
||||
/** Sets the callback functions for the SDK allocator
|
||||
*
|
||||
* \param allocate
|
||||
* The callback function to allocate memory. When the SDK requires memory allocation this callback will be
|
||||
* called and the application can provide a buffer and a context.
|
||||
*
|
||||
* \param free
|
||||
* The callback function to free memory. The SDK will call this function when memory allocated by \p allocate
|
||||
* is no longer needed.
|
||||
*
|
||||
* \return ::K4A_RESULT_SUCCEEDED if the callback function was set or cleared successfully. ::K4A_RESULT_FAILED if an
|
||||
* error is encountered or the callback function has already been set.
|
||||
*
|
||||
* \remarks
|
||||
* Call this function to hook memory allocation by the SDK. Calling with both \p allocate and \p free as NULL will
|
||||
* clear the hook and reset to the default allocator.
|
||||
*
|
||||
* \remarks
|
||||
* If this function is called after memory has been allocated, the previous version of \p free function may still be
|
||||
* called in the future. The SDK will always call the \p free function that was set at the time that the memory
|
||||
* was allocated.
|
||||
*
|
||||
* \remarks
|
||||
* Not all memory allocation by the SDK is performed by this allocate function. Small allocations or allocations
|
||||
* from special pools may come from other sources.
|
||||
*
|
||||
* \xmlonly
|
||||
* <requirements>
|
||||
* <requirement name="Header">k4a.h (include k4a/k4a.h)</requirement>
|
||||
* <requirement name="Library">k4a.lib</requirement>
|
||||
* <requirement name="DLL">k4a.dll</requirement>
|
||||
* </requirements>
|
||||
* \endxmlonly
|
||||
*/
|
||||
k4a_result_t allocator_set_allocator(k4a_memory_allocate_cb_t allocate, k4a_memory_destroy_cb_t free);
|
||||
|
||||
/** Allocates memory from the allocator
|
||||
*
|
||||
* \param source
|
||||
|
@ -51,18 +87,28 @@ void allocator_deinitialize(void);
|
|||
* \param alloc_size
|
||||
* size of the memory to allocate
|
||||
*
|
||||
* \param context [OUT]
|
||||
* context to go with the allocation be used when freed
|
||||
*
|
||||
* This call only cleans up the allocator handle.
|
||||
* This function should not be called until all outstanding \ref k4a_capture_t objects are freed.
|
||||
*/
|
||||
uint8_t *allocator_alloc(allocation_source_t source, size_t alloc_size);
|
||||
|
||||
/** Returns a buffer to the allocator
|
||||
*
|
||||
* \param buffer
|
||||
* Buffer to free
|
||||
*
|
||||
* \remarks
|
||||
* This should only be called with a buffer allocated by allocator_alloc()
|
||||
*/
|
||||
void allocator_free(void *buffer);
|
||||
|
||||
/** Verifies there are no outstanding allocations
|
||||
*
|
||||
* \remarks
|
||||
* This function should return 0 to indicate the number of outstanding allocations. Consider this the
|
||||
* number of leaked allocations.
|
||||
*
|
||||
* This function should not be called until all outstanding \ref k4a_capture_t objects are freed.
|
||||
*/
|
||||
uint8_t *allocator_alloc(allocation_source_t source, size_t alloc_size, void **context);
|
||||
void allocator_free(void *buffer, void *context);
|
||||
|
||||
long allocator_test_for_leaks(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -39,7 +39,7 @@ void global_init_once(k4a_init_once_t *init_once, k4a_init_once_function_t *init
|
|||
#define K4A_DECLARE_GLOBAL(_global_type_, _init_function_) \
|
||||
static k4a_init_once_t g_##_global_type_##_init_once = K4A_INIT_ONCE; \
|
||||
static _global_type_ _##_global_type_##_private; \
|
||||
static void fn_##_global_type_##_init_function() \
|
||||
static void fn_##_global_type_##_init_function(void) \
|
||||
{ \
|
||||
memset(&_##_global_type_##_private, 0, sizeof(_##_global_type_##_private)); \
|
||||
_init_function_(&_##_global_type_##_private); \
|
||||
|
@ -47,7 +47,7 @@ void global_init_once(k4a_init_once_t *init_once, k4a_init_once_function_t *init
|
|||
} \
|
||||
static _global_type_ *_global_type_##_get() \
|
||||
{ \
|
||||
global_init_once(&g_##_global_type_##_init_once, fn_##_global_type_##_init_function); \
|
||||
global_init_once(&g_##_global_type_##_init_once, &fn_##_global_type_##_init_function); \
|
||||
return &_##_global_type_##_private; \
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ used with CPP and destroy being used with C, or vise-vesa, the types get c or cp
|
|||
static inline _internal_context_type_ *_public_handle_name_##_create(_public_handle_name_ *handle) \
|
||||
{ \
|
||||
PUB_HANDLE_TYPE(_public_handle_name_) * pContextWrapper; \
|
||||
*handle = NULL; \
|
||||
pContextWrapper = ALLOCATE(PUB_HANDLE_TYPE(_public_handle_name_)); \
|
||||
if (pContextWrapper == NULL) \
|
||||
{ \
|
||||
|
|
|
@ -31,6 +31,9 @@ typedef void(image_destroy_cb_t)(void *buffer, void *context);
|
|||
* \param stride_bytes [IN]
|
||||
* stride of the image being created
|
||||
*
|
||||
* \param source
|
||||
* source of the image for allocation accounting
|
||||
*
|
||||
* \return NULL if failed, otherwise a k4a_image_t handle
|
||||
*
|
||||
* If successful, \ref image_create will return an image_t handle. This function will allocate a function of size
|
||||
|
@ -38,8 +41,12 @@ typedef void(image_destroy_cb_t)(void *buffer, void *context);
|
|||
*
|
||||
* When done with the device, close the handle with \ref image_release
|
||||
*/
|
||||
k4a_result_t
|
||||
image_create(k4a_image_format_t format, int width_pixels, int height_pixels, int stride_bytes, k4a_image_t *image);
|
||||
k4a_result_t image_create(k4a_image_format_t format,
|
||||
int width_pixels,
|
||||
int height_pixels,
|
||||
int stride_bytes,
|
||||
allocation_source_t source,
|
||||
k4a_image_t *image);
|
||||
|
||||
/** Create a handle to an image object.
|
||||
* internal function to allocate an image object and memory blob of 'size'. Used for USB layer where we need counted
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
// Dependent libraries
|
||||
#include <k4ainternal/capture.h>
|
||||
#include <azure_c_shared_utility/lock.h>
|
||||
#include <k4ainternal/global.h>
|
||||
#include <k4ainternal/rwlock.h>
|
||||
#include <azure_c_shared_utility/refcount.h>
|
||||
|
||||
// System dependencies
|
||||
|
@ -24,6 +25,65 @@ typedef enum
|
|||
IMAGE_TYPE_COUNT,
|
||||
} image_type_index_t;
|
||||
|
||||
// Global properties of the allocator
|
||||
typedef struct
|
||||
{
|
||||
k4a_rwlock_t lock;
|
||||
|
||||
// Access to these function pointers may only occur
|
||||
// while holding lock
|
||||
k4a_memory_allocate_cb_t *alloc;
|
||||
k4a_memory_destroy_cb_t *free;
|
||||
} allocator_global_t;
|
||||
|
||||
// This allocator implementation is used by default
|
||||
static uint8_t *default_alloc(int size, void **context)
|
||||
{
|
||||
*context = NULL;
|
||||
if (size < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return (uint8_t *)malloc((size_t)size);
|
||||
}
|
||||
|
||||
// This is the free function for the default allocator
|
||||
static void default_free(void *buffer, void *context)
|
||||
{
|
||||
(void)context;
|
||||
assert(context == NULL);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
// This is a one time initialization of the global state for the allocator
|
||||
static void allocator_global_init(allocator_global_t *g_allocator)
|
||||
{
|
||||
rwlock_init(&g_allocator->lock);
|
||||
|
||||
g_allocator->alloc = default_alloc;
|
||||
g_allocator->free = default_free;
|
||||
}
|
||||
|
||||
// The allocation context is pre-pended to memory returned by the allocator
|
||||
// This state is used to track the freeing of the allocation
|
||||
typedef struct _allocation_context_t
|
||||
{
|
||||
union _allocator_context
|
||||
{
|
||||
struct _context
|
||||
{
|
||||
allocation_source_t source;
|
||||
k4a_memory_destroy_cb_t *free;
|
||||
void *free_context;
|
||||
} context;
|
||||
|
||||
// Keep 16 byte alignment so that allocations may be used with SSE
|
||||
char alignment[32];
|
||||
} u;
|
||||
} allocation_context_t;
|
||||
|
||||
K4A_DECLARE_GLOBAL(allocator_global_t, allocator_global_init);
|
||||
|
||||
//
|
||||
// Simple counts of memory allocations for the purpose of detecting leaks of the K4A SDK's larger memory objects.
|
||||
//
|
||||
|
@ -48,7 +108,7 @@ static volatile long g_allocator_sessions = 0;
|
|||
typedef struct _capture_context_t
|
||||
{
|
||||
volatile long ref_count;
|
||||
LOCK_HANDLE lock;
|
||||
k4a_rwlock_t lock;
|
||||
|
||||
k4a_image_t image[IMAGE_TYPE_COUNT];
|
||||
|
||||
|
@ -67,11 +127,31 @@ void allocator_deinitialize(void)
|
|||
DEC_REF_VAR(g_allocator_sessions);
|
||||
}
|
||||
|
||||
uint8_t *allocator_alloc(allocation_source_t source, size_t alloc_size, void **context)
|
||||
k4a_result_t allocator_set_allocator(k4a_memory_allocate_cb_t allocate, k4a_memory_destroy_cb_t free)
|
||||
{
|
||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, allocate == NULL && free != NULL);
|
||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, allocate != NULL && free == NULL);
|
||||
|
||||
allocator_global_t *g_allocator = allocator_global_t_get();
|
||||
rwlock_acquire_write(&g_allocator->lock);
|
||||
|
||||
g_allocator->alloc = allocate ? allocate : default_alloc;
|
||||
g_allocator->free = free ? free : default_free;
|
||||
|
||||
rwlock_release_write(&g_allocator->lock);
|
||||
|
||||
return K4A_RESULT_SUCCEEDED;
|
||||
}
|
||||
|
||||
uint8_t *allocator_alloc(allocation_source_t source, size_t alloc_size)
|
||||
{
|
||||
allocator_global_t *g_allocator = allocator_global_t_get();
|
||||
|
||||
RETURN_VALUE_IF_ARG(NULL, source < ALLOCATION_SOURCE_USER || source > ALLOCATION_SOURCE_USB_IMU);
|
||||
RETURN_VALUE_IF_ARG(NULL, alloc_size == 0);
|
||||
RETURN_VALUE_IF_ARG(NULL, context == NULL);
|
||||
|
||||
size_t required_bytes = alloc_size + sizeof(allocation_context_t);
|
||||
RETURN_VALUE_IF_ARG(NULL, required_bytes > INT32_MAX);
|
||||
|
||||
volatile long *ref = NULL;
|
||||
switch (source)
|
||||
|
@ -101,15 +181,44 @@ uint8_t *allocator_alloc(allocation_source_t source, size_t alloc_size, void **c
|
|||
|
||||
INC_REF_VAR(*ref);
|
||||
|
||||
memcpy((uint8_t *)context, &source, sizeof(allocation_source_t));
|
||||
rwlock_acquire_read(&g_allocator->lock);
|
||||
|
||||
return malloc(alloc_size);
|
||||
void *user_context;
|
||||
|
||||
void *full_buffer = g_allocator->alloc((int)required_bytes, &user_context);
|
||||
|
||||
// Store information about the allocation that we will need during free.
|
||||
allocation_context_t allocation_context;
|
||||
|
||||
allocation_context.u.context.source = source;
|
||||
allocation_context.u.context.free = g_allocator->free;
|
||||
allocation_context.u.context.free_context = user_context;
|
||||
|
||||
rwlock_release_read(&g_allocator->lock);
|
||||
|
||||
if (full_buffer == NULL)
|
||||
{
|
||||
LOG_ERROR("User allocation function for %d bytes failed", required_bytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Memcpy the context information to the header of the full buffer.
|
||||
// Don't cast the buffer directly since there is no alignment constraint
|
||||
memcpy(full_buffer, &allocation_context, sizeof(allocation_context));
|
||||
|
||||
// Provide the caller with the buffer after the allocation context header.
|
||||
uint8_t *buffer = (uint8_t *)full_buffer + sizeof(allocation_context_t);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void allocator_free(void *buffer, void *context)
|
||||
void allocator_free(void *buffer)
|
||||
{
|
||||
allocation_source_t source;
|
||||
memcpy(&source, (uint8_t *)&context, sizeof(allocation_source_t));
|
||||
void *full_buffer = (uint8_t *)buffer - sizeof(allocation_context_t);
|
||||
allocation_context_t allocation_context;
|
||||
memcpy(&allocation_context, full_buffer, sizeof(allocation_context));
|
||||
|
||||
allocation_source_t source = allocation_context.u.context.source;
|
||||
|
||||
RETURN_VALUE_IF_ARG(VOID_VALUE, source < ALLOCATION_SOURCE_USER || source > ALLOCATION_SOURCE_USB_IMU);
|
||||
RETURN_VALUE_IF_ARG(VOID_VALUE, buffer == NULL);
|
||||
|
@ -142,7 +251,9 @@ void allocator_free(void *buffer, void *context)
|
|||
}
|
||||
|
||||
DEC_REF_VAR(*ref);
|
||||
free(buffer);
|
||||
|
||||
allocation_context.u.context.free(full_buffer, allocation_context.u.context.free_context);
|
||||
full_buffer = NULL;
|
||||
}
|
||||
|
||||
long allocator_test_for_leaks(void)
|
||||
|
@ -189,7 +300,7 @@ void capture_dec_ref(k4a_capture_t capture_handle)
|
|||
|
||||
if (new_count == 0)
|
||||
{
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_write(&capture->lock);
|
||||
for (int x = 0; x < IMAGE_TYPE_COUNT; x++)
|
||||
{
|
||||
if (capture->image[x])
|
||||
|
@ -197,8 +308,8 @@ void capture_dec_ref(k4a_capture_t capture_handle)
|
|||
image_dec_ref(capture->image[x]);
|
||||
}
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
Lock_Deinit(capture->lock);
|
||||
rwlock_release_write(&capture->lock);
|
||||
rwlock_deinit(&capture->lock);
|
||||
k4a_capture_t_destroy(capture_handle);
|
||||
}
|
||||
}
|
||||
|
@ -215,22 +326,14 @@ k4a_result_t capture_create(k4a_capture_t *capture_handle)
|
|||
{
|
||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, capture_handle == NULL);
|
||||
|
||||
k4a_result_t result;
|
||||
capture_context_t *capture = k4a_capture_t_create(capture_handle);
|
||||
result = K4A_RESULT_FROM_BOOL(capture != NULL);
|
||||
k4a_result_t result = K4A_RESULT_FROM_BOOL(capture != NULL);
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
capture->ref_count = 1;
|
||||
capture->temperature_c = NAN;
|
||||
capture->lock = Lock_Init();
|
||||
result = K4A_RESULT_FROM_BOOL(capture->lock != NULL);
|
||||
}
|
||||
|
||||
if (K4A_FAILED(result) && capture)
|
||||
{
|
||||
capture_dec_ref(*capture_handle);
|
||||
capture_handle = NULL;
|
||||
rwlock_init(&capture->lock);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -242,13 +345,13 @@ k4a_image_t capture_get_color_image(k4a_capture_t capture_handle)
|
|||
|
||||
capture_context_t *capture = k4a_capture_t_get_context(capture_handle);
|
||||
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_read(&capture->lock);
|
||||
k4a_image_t *image = &capture->image[IMAGE_TYPE_COLOR];
|
||||
if (*image)
|
||||
{
|
||||
image_inc_ref(*image);
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
rwlock_release_read(&capture->lock);
|
||||
return *image;
|
||||
}
|
||||
k4a_image_t capture_get_depth_image(k4a_capture_t capture_handle)
|
||||
|
@ -257,13 +360,13 @@ k4a_image_t capture_get_depth_image(k4a_capture_t capture_handle)
|
|||
|
||||
capture_context_t *capture = k4a_capture_t_get_context(capture_handle);
|
||||
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_read(&capture->lock);
|
||||
k4a_image_t *image = &capture->image[IMAGE_TYPE_DEPTH];
|
||||
if (*image)
|
||||
{
|
||||
image_inc_ref(*image);
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
rwlock_release_read(&capture->lock);
|
||||
return *image;
|
||||
}
|
||||
|
||||
|
@ -273,13 +376,13 @@ k4a_image_t capture_get_ir_image(k4a_capture_t capture_handle)
|
|||
|
||||
capture_context_t *capture = k4a_capture_t_get_context(capture_handle);
|
||||
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_read(&capture->lock);
|
||||
k4a_image_t *image = &capture->image[IMAGE_TYPE_IR];
|
||||
if (*image)
|
||||
{
|
||||
image_inc_ref(*image);
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
rwlock_release_read(&capture->lock);
|
||||
return *image;
|
||||
}
|
||||
|
||||
|
@ -295,7 +398,7 @@ void capture_set_color_image(k4a_capture_t capture_handle, k4a_image_t image_han
|
|||
|
||||
capture_context_t *capture = k4a_capture_t_get_context(capture_handle);
|
||||
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_write(&capture->lock);
|
||||
k4a_image_t *image = &capture->image[IMAGE_TYPE_COLOR];
|
||||
if (*image)
|
||||
{
|
||||
|
@ -306,7 +409,7 @@ void capture_set_color_image(k4a_capture_t capture_handle, k4a_image_t image_han
|
|||
{
|
||||
image_inc_ref(*image);
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
rwlock_release_write(&capture->lock);
|
||||
}
|
||||
void capture_set_depth_image(k4a_capture_t capture_handle, k4a_image_t image_handle)
|
||||
{
|
||||
|
@ -314,7 +417,7 @@ void capture_set_depth_image(k4a_capture_t capture_handle, k4a_image_t image_han
|
|||
|
||||
capture_context_t *capture = k4a_capture_t_get_context(capture_handle);
|
||||
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_write(&capture->lock);
|
||||
k4a_image_t *image = &capture->image[IMAGE_TYPE_DEPTH];
|
||||
if (*image)
|
||||
{
|
||||
|
@ -325,14 +428,14 @@ void capture_set_depth_image(k4a_capture_t capture_handle, k4a_image_t image_han
|
|||
{
|
||||
image_inc_ref(*image);
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
rwlock_release_write(&capture->lock);
|
||||
}
|
||||
void capture_set_ir_image(k4a_capture_t capture_handle, k4a_image_t image_handle)
|
||||
{
|
||||
RETURN_VALUE_IF_HANDLE_INVALID(VOID_VALUE, k4a_capture_t, capture_handle);
|
||||
|
||||
capture_context_t *capture = k4a_capture_t_get_context(capture_handle);
|
||||
Lock(capture->lock);
|
||||
rwlock_acquire_write(&capture->lock);
|
||||
k4a_image_t *image = &capture->image[IMAGE_TYPE_IR];
|
||||
if (*image)
|
||||
{
|
||||
|
@ -343,7 +446,7 @@ void capture_set_ir_image(k4a_capture_t capture_handle, k4a_image_t image_handle
|
|||
{
|
||||
image_inc_ref(*image);
|
||||
}
|
||||
Unlock(capture->lock);
|
||||
rwlock_release_write(&capture->lock);
|
||||
}
|
||||
void capture_set_imu_image(k4a_capture_t capture_handle, k4a_image_t image_handle)
|
||||
{
|
||||
|
|
|
@ -164,19 +164,12 @@ CFrameContext::~CFrameContext()
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct _mf_buffer_wrapper_t
|
||||
{
|
||||
void *allocator_context;
|
||||
CFrameContext *pFrameContext;
|
||||
} mf_buffer_wrapper_t;
|
||||
|
||||
void FrameDestroyCallback(void *frame, void *context)
|
||||
{
|
||||
(void)frame;
|
||||
mf_buffer_wrapper_t *wrapper = (mf_buffer_wrapper_t *)context;
|
||||
CFrameContext *pFrameContext = (CFrameContext *)context;
|
||||
|
||||
delete wrapper->pFrameContext;
|
||||
allocator_free(wrapper, wrapper->allocator_context);
|
||||
delete pFrameContext;
|
||||
}
|
||||
|
||||
CMFCameraReader::CMFCameraReader()
|
||||
|
@ -953,17 +946,6 @@ int CMFCameraReader::GetStride()
|
|||
|
||||
k4a_result_t CMFCameraReader::CreateImage(CFrameContext *pFrameContext, k4a_image_t *image)
|
||||
{
|
||||
void *context;
|
||||
mf_buffer_wrapper_t *wrapper;
|
||||
|
||||
// We use a wrapper here so that we can keep track of the MF frame buffers in use; ALLOCATION_SOURCE_COLOR will
|
||||
// count outstanding allocations. If too many are used then MF stops receiving images over USB until the captures
|
||||
// are released.
|
||||
wrapper = (mf_buffer_wrapper_t *)allocator_alloc(ALLOCATION_SOURCE_COLOR, sizeof(mf_buffer_wrapper_t), &context);
|
||||
|
||||
wrapper->allocator_context = context;
|
||||
wrapper->pFrameContext = pFrameContext;
|
||||
|
||||
return TRACE_CALL(image_create_from_buffer(m_image_format,
|
||||
m_width_pixels,
|
||||
m_height_pixels,
|
||||
|
@ -971,38 +953,52 @@ k4a_result_t CMFCameraReader::CreateImage(CFrameContext *pFrameContext, k4a_imag
|
|||
pFrameContext->GetBuffer(),
|
||||
pFrameContext->GetFrameSize(),
|
||||
FrameDestroyCallback,
|
||||
wrapper,
|
||||
pFrameContext,
|
||||
image));
|
||||
}
|
||||
|
||||
static void mfcamerareader_free_allocation(void *buffer, void *context)
|
||||
{
|
||||
(void)context;
|
||||
allocator_free(buffer);
|
||||
}
|
||||
|
||||
k4a_result_t CMFCameraReader::CreateImageCopy(CFrameContext *pFrameContext, k4a_image_t *image)
|
||||
{
|
||||
|
||||
size_t size = pFrameContext->GetFrameSize();
|
||||
void *context;
|
||||
|
||||
k4a_result_t result;
|
||||
uint8_t *buffer = allocator_alloc(ALLOCATION_SOURCE_COLOR, size, &context);
|
||||
uint8_t *buffer = allocator_alloc(ALLOCATION_SOURCE_COLOR, size);
|
||||
result = K4A_RESULT_FROM_BOOL(buffer != NULL);
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
memcpy(buffer, pFrameContext->GetBuffer(), size);
|
||||
|
||||
result = TRACE_CALL(image_create_from_buffer(m_image_format,
|
||||
m_width_pixels,
|
||||
m_height_pixels,
|
||||
GetStride(),
|
||||
buffer,
|
||||
size,
|
||||
allocator_free,
|
||||
context,
|
||||
mfcamerareader_free_allocation,
|
||||
NULL,
|
||||
image));
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
assert(image_get_size(*image) == size);
|
||||
memcpy(image_get_buffer(*image), pFrameContext->GetBuffer(), size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cleanup if there was an error
|
||||
allocator_free(buffer, context);
|
||||
if (buffer != NULL)
|
||||
{
|
||||
allocator_free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1098,6 +1098,13 @@ k4a_result_t UVCCameraReader::SetCameraControl(const k4a_color_control_command_t
|
|||
return K4A_RESULT_SUCCEEDED;
|
||||
}
|
||||
|
||||
// Callback function for when image objects are destroyed
|
||||
static void uvc_camerareader_free_allocation(void *buffer, void *context)
|
||||
{
|
||||
(void)context;
|
||||
allocator_free(buffer);
|
||||
}
|
||||
|
||||
void UVCCameraReader::Callback(uvc_frame_t *frame)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
@ -1188,7 +1195,7 @@ void UVCCameraReader::Callback(uvc_frame_t *frame)
|
|||
}
|
||||
|
||||
// Allocate K4A Color buffer
|
||||
buffer = allocator_alloc(ALLOCATION_SOURCE_COLOR, buffer_size, &context);
|
||||
buffer = allocator_alloc(ALLOCATION_SOURCE_COLOR, buffer_size);
|
||||
k4a_result_t result = K4A_RESULT_FROM_BOOL(buffer != NULL);
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
|
@ -1207,20 +1214,22 @@ void UVCCameraReader::Callback(uvc_frame_t *frame)
|
|||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
// The buffer size may be larger than the height * stride for some formats
|
||||
// so we must use image_create_from_buffer rather than image_create
|
||||
result = TRACE_CALL(image_create_from_buffer(m_output_image_format,
|
||||
(int)m_width_pixels,
|
||||
(int)m_height_pixels,
|
||||
stride,
|
||||
buffer,
|
||||
buffer_size,
|
||||
allocator_free,
|
||||
uvc_camerareader_free_allocation,
|
||||
context,
|
||||
&image));
|
||||
}
|
||||
else
|
||||
{
|
||||
// cleanup if there was an error
|
||||
allocator_free(buffer, context);
|
||||
allocator_free(buffer);
|
||||
}
|
||||
|
||||
k4a_capture_t capture = NULL;
|
||||
|
|
|
@ -28,8 +28,8 @@ dotnet_style_qualification_for_method = true:warning
|
|||
dotnet_style_qualification_for_property = true:warning
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = false:warning
|
||||
dotnet_style_predefined_type_for_member_access = false:warning
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
|
||||
dotnet_style_predefined_type_for_member_access = true:warning
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
|
|
@ -11,8 +11,14 @@
|
|||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image x:Name="colorImageViewPane" />
|
||||
<Image x:Name="depthImageViewPane" Grid.Column="1"/>
|
||||
<Image x:Name="inputColorImageViewPane" />
|
||||
<Image x:Name="outputColorImageViewPane" Grid.Column="1"/>
|
||||
|
||||
<Label x:Name="fps" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1"></Label>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
|
|
@ -8,6 +8,7 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Azure.Kinect.Sensor.WPF;
|
||||
|
||||
namespace Microsoft.Azure.Kinect.Sensor.Examples.WPFViewer
|
||||
|
@ -46,61 +47,93 @@ namespace Microsoft.Azure.Kinect.Sensor.Examples.WPFViewer
|
|||
ColorResolution = ColorResolution.r1440p,
|
||||
DepthMode = DepthMode.WFOV_2x2Binned,
|
||||
SynchronizedImagesOnly = true,
|
||||
CameraFPS = FPS.fps30,
|
||||
});
|
||||
|
||||
int colorWidth = device.GetCalibration().color_camera_calibration.resolution_width;
|
||||
int colorHeight = device.GetCalibration().color_camera_calibration.resolution_height;
|
||||
|
||||
DateTime start = DateTime.Now;
|
||||
int frameCount = 0;
|
||||
|
||||
// Allocate image buffers for us to manipulate
|
||||
using (ArrayImage<BGRA> modifiedColor = new ArrayImage<BGRA>(ImageFormat.ColorBGRA32, colorWidth, colorHeight))
|
||||
using (ArrayImage<ushort> transformedDepth = new ArrayImage<ushort>(ImageFormat.Depth16, colorWidth, colorHeight))
|
||||
using (Image transformedDepth = new Image(ImageFormat.Depth16, colorWidth, colorHeight))
|
||||
using (Image outputColorImage = new Image(ImageFormat.ColorBGRA32, colorWidth, colorHeight))
|
||||
using (Transformation transform = device.GetCalibration().CreateTransformation())
|
||||
{
|
||||
Stopwatch sw = new Stopwatch();
|
||||
int frameCount = 0;
|
||||
sw.Start();
|
||||
|
||||
while (this.running)
|
||||
{
|
||||
// Wait for a capture on a thread pool thread
|
||||
using (Capture capture = await Task.Run(() => { return device.GetCapture(); }))
|
||||
using (Capture capture = await Task.Run(() => { return device.GetCapture(); }).ConfigureAwait(true))
|
||||
{
|
||||
// Update the color image preview
|
||||
colorImageViewPane.Source = capture.Color.CreateBitmapSource();
|
||||
|
||||
// Compute the depth preview on a thread pool thread
|
||||
depthImageViewPane.Source = (await Task.Run(() =>
|
||||
// Create a BitmapSource for the unmodified color image.
|
||||
// Creating the BitmapSource is slow, so do it asynchronously on another thread
|
||||
Task<BitmapSource> createInputColorBitmapTask = Task.Run(() =>
|
||||
{
|
||||
// Transform the depth image
|
||||
BitmapSource source = capture.Color.CreateBitmapSource();
|
||||
|
||||
// Allow the bitmap to move threads
|
||||
source.Freeze();
|
||||
return source;
|
||||
});
|
||||
|
||||
// Compute the colorized output bitmap on a thread pool thread
|
||||
Task<BitmapSource> createOutputColorBitmapTask = Task.Run(() =>
|
||||
{
|
||||
// Transform the depth image to the perspective of the color camera
|
||||
transform.DepthImageToColorCamera(capture, transformedDepth);
|
||||
// Copy the color image
|
||||
capture.Color.CopyBytesTo(modifiedColor, 0, 0, (int)modifiedColor.Size);
|
||||
|
||||
ushort[] depthBuffer = transformedDepth.Buffer;
|
||||
BGRA[] colorBuffer = modifiedColor.Buffer;
|
||||
// Get Span<T> references to the pixel buffers for fast pixel access.
|
||||
Span<ushort> depthBuffer = transformedDepth.GetPixels<ushort>().Span;
|
||||
Span<BGRA> colorBuffer = capture.Color.GetPixels<BGRA>().Span;
|
||||
Span<BGRA> outputColorBuffer = outputColorImage.GetPixels<BGRA>().Span;
|
||||
|
||||
// Modify the color image with data from the depth image
|
||||
// Create an output color image with data from the depth image
|
||||
for (int i = 0; i < colorBuffer.Length; i++)
|
||||
{
|
||||
// The output image will be the same as the input color image,
|
||||
// but colorized with Red where there is no depth data, and Green
|
||||
// where there is depth data at more than 1.5 meters
|
||||
outputColorBuffer[i] = colorBuffer[i];
|
||||
|
||||
if (depthBuffer[i] == 0)
|
||||
{
|
||||
colorBuffer[i].R = 255;
|
||||
outputColorBuffer[i].R = 255;
|
||||
}
|
||||
else if (depthBuffer[i] > 1500)
|
||||
{
|
||||
colorBuffer[i].G = 255;
|
||||
outputColorBuffer[i].G = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return modifiedColor;
|
||||
})).CreateBitmapSource();
|
||||
}
|
||||
BitmapSource source = outputColorImage.CreateBitmapSource();
|
||||
|
||||
if (++frameCount >= 30)
|
||||
{
|
||||
Console.WriteLine("{0}ms => {1} FPS", sw.Elapsed.TotalMilliseconds, frameCount / sw.Elapsed.TotalSeconds);
|
||||
sw.Restart();
|
||||
frameCount = 0;
|
||||
// Allow the bitmap to move threads
|
||||
source.Freeze();
|
||||
|
||||
return source;
|
||||
});
|
||||
|
||||
// Wait for both bitmaps to be ready and assign them.
|
||||
BitmapSource inputColorBitmap = await createInputColorBitmapTask.ConfigureAwait(true);
|
||||
BitmapSource outputColorBitmap = await createOutputColorBitmapTask.ConfigureAwait(true);
|
||||
|
||||
this.inputColorImageViewPane.Source = inputColorBitmap;
|
||||
this.outputColorImageViewPane.Source = outputColorBitmap;
|
||||
|
||||
frameCount++;
|
||||
|
||||
TimeSpan timeSpan = DateTime.Now - start;
|
||||
if (timeSpan > TimeSpan.FromSeconds(2))
|
||||
{
|
||||
double framesPerSecond = (double)frameCount / timeSpan.TotalSeconds;
|
||||
|
||||
this.fps.Content = $"{framesPerSecond:F2} FPS";
|
||||
|
||||
frameCount = 0;
|
||||
start = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,20 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
|
|
|
@ -6,4 +6,8 @@
|
|||
<package id="Microsoft.NetCore.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="Microsoft.NetFramework.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="StyleCop.Analyzers" version="1.1.118" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="System.Buffers" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Memory" version="4.5.3" targetFramework="net461" />
|
||||
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
|
||||
</packages>
|
|
@ -66,9 +66,8 @@ namespace Microsoft.Azure.Kinect.Sensor.Examples.WinForms
|
|||
// TODO: Lock the Bitmap and access the bytes directly?
|
||||
////BitmapData d = depthVisualization.LockBits(new Rectangle(0, 0, depthVisualization.Width, depthVisualization.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
|
||||
ushort[] depthValues = new ushort[capture.Depth.WidthPixels * capture.Depth.HeightPixels];
|
||||
ushort[] depthValues = capture.Depth.GetPixels<ushort>().ToArray();
|
||||
|
||||
capture.Depth.CopyTo(depthValues, 0, 0, depthValues.Length);
|
||||
for (int y = 0; y < capture.Depth.HeightPixels; y++)
|
||||
{
|
||||
for (int x = 0; x < capture.Depth.WidthPixels; x++)
|
||||
|
|
|
@ -58,7 +58,20 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
|
|
@ -6,4 +6,8 @@
|
|||
<package id="Microsoft.NetCore.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="Microsoft.NetFramework.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="StyleCop.Analyzers" version="1.1.118" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="System.Buffers" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Memory" version="4.5.3" targetFramework="net461" />
|
||||
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
|
||||
</packages>
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
|
@ -35,16 +36,20 @@ namespace Microsoft.Azure.Kinect.Sensor.WPF
|
|||
|
||||
unsafe
|
||||
{
|
||||
return BitmapSource.Create(
|
||||
reference.WidthPixels,
|
||||
reference.HeightPixels,
|
||||
dpiX,
|
||||
dpiY,
|
||||
pixelFormat,
|
||||
/* palette: */ null,
|
||||
(IntPtr)reference.Buffer,
|
||||
checked((int)reference.Size),
|
||||
reference.StrideBytes);
|
||||
using (var pin = reference.Memory.Pin())
|
||||
{
|
||||
BitmapSource source = BitmapSource.Create(
|
||||
reference.WidthPixels,
|
||||
reference.HeightPixels,
|
||||
dpiX,
|
||||
dpiY,
|
||||
pixelFormat,
|
||||
/* palette: */ null,
|
||||
(IntPtr)pin.Pointer,
|
||||
checked((int)reference.Size),
|
||||
reference.StrideBytes);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,20 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
|
|
@ -6,4 +6,8 @@
|
|||
<package id="Microsoft.NetCore.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="Microsoft.NetFramework.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="StyleCop.Analyzers" version="1.1.118" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="System.Buffers" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Memory" version="4.5.3" targetFramework="net461" />
|
||||
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
|
||||
</packages>
|
|
@ -30,11 +30,14 @@ namespace Microsoft.Azure.Kinect.Sensor.WinForms
|
|||
throw new AzureKinectException($"Pixel format {reference.Format} cannot be converted to a BitmapSource");
|
||||
}
|
||||
|
||||
return new Bitmap(image.WidthPixels,
|
||||
image.HeightPixels,
|
||||
image.StrideBytes,
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb,
|
||||
(IntPtr)image.Buffer);
|
||||
using (var pin = image.Memory.Pin())
|
||||
{
|
||||
return new Bitmap(image.WidthPixels,
|
||||
image.HeightPixels,
|
||||
image.StrideBytes,
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb,
|
||||
(IntPtr)pin.Pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,21 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
|
|
@ -6,4 +6,8 @@
|
|||
<package id="Microsoft.NetCore.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="Microsoft.NetFramework.Analyzers" version="2.9.3" targetFramework="net461" />
|
||||
<package id="StyleCop.Analyzers" version="1.1.118" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="System.Buffers" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Memory" version="4.5.3" targetFramework="net461" />
|
||||
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
|
||||
</packages>
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.572
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29111.141
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sensor", "SDK\Microsoft.Azure.Kinect.Sensor.csproj", "{847B31D5-C253-4766-BF81-032F4670589D}"
|
||||
EndProject
|
||||
|
@ -27,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{21E41070
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{118BF8E1-C662-4852-955F-22DC68BB95CC}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
AzureKinectSensorSDK.ruleset = AzureKinectSensorSDK.ruleset
|
||||
stylecop.json = stylecop.json
|
||||
VersionInfo.cs = VersionInfo.cs
|
||||
|
|
|
@ -0,0 +1,430 @@
|
|||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly:InternalsVisibleTo("Microsoft.Azure.Kinect.Sensor.UnitTests")]
|
||||
|
||||
namespace Microsoft.Azure.Kinect.Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages buffer allocation.
|
||||
/// </summary>
|
||||
internal class Allocator
|
||||
{
|
||||
// Objects we are tracking for disposal prior to CLR shutdown
|
||||
private readonly HashSet<WeakReference<IDisposable>> disposables = new HashSet<WeakReference<IDisposable>>();
|
||||
|
||||
// Allocations made by the managed code for the native library
|
||||
private readonly SortedDictionary<long, AllocationContext> allocations = new SortedDictionary<long, AllocationContext>();
|
||||
|
||||
// A recycleable large array pool to prevent unnecissary managed allocationsns and clearing of memory
|
||||
private readonly ArrayPool<byte> pool = new LargeArrayPool();
|
||||
|
||||
// Managed buffers used as caches for the native memory.
|
||||
private readonly Dictionary<IntPtr, BufferCacheEntry> bufferCache = new Dictionary<IntPtr, BufferCacheEntry>();
|
||||
|
||||
// Native delegates
|
||||
private readonly NativeMethods.k4a_memory_allocate_cb_t allocateDelegate;
|
||||
private readonly NativeMethods.k4a_memory_destroy_cb_t freeDelegate;
|
||||
|
||||
// Native allocator hook state
|
||||
private bool hooked = false;
|
||||
|
||||
// This is set when the CLR is shutting down, causing an exception during
|
||||
// any new object registrations.
|
||||
private bool noMoreDisposalRegistrations = false;
|
||||
|
||||
private Allocator()
|
||||
{
|
||||
this.allocateDelegate = new NativeMethods.k4a_memory_allocate_cb_t(this.AllocateFunction);
|
||||
this.freeDelegate = new NativeMethods.k4a_memory_destroy_cb_t(this.FreeFunction);
|
||||
|
||||
// Register for ProcessExit and DomainUnload to allow us to unhook the native layer before
|
||||
// native to managed callbacks are no longer allowed.
|
||||
AppDomain.CurrentDomain.DomainUnload += this.ApplicationExit;
|
||||
AppDomain.CurrentDomain.ProcessExit += this.ApplicationExit;
|
||||
|
||||
// Default to the safe and performant configuration
|
||||
|
||||
// Use Managed Allocator will cause the native layer to allocate from managed byte[] arrays when possible
|
||||
// these can then be safely referenced with a Memory<T> and exposed to user code. This should have fairly
|
||||
// minimal performance impact, but provides strong memory safety.
|
||||
this.UseManagedAllocator = true;
|
||||
|
||||
// When managed code needs to provide access to memory that didn't originate from the managed allocator
|
||||
// the SafeCopyNativeBuffers options causes the managed code to make a safe cache copy of the native buffer
|
||||
// in a managed byte[] array. This has a more significant performance impact, but generally is only needed for
|
||||
// media foundation (color image) or potentially custom buffers. When set to true, the Memory<T> objects are safe
|
||||
// copies of the native buffers. When set to false, the Memory<T> objects are direct pointers to the native buffers
|
||||
// and can therefore cause native memory corruption if a Memory<T> (or Span<T>) is used after the Image is disposed
|
||||
// or garbage collected.
|
||||
this.SafeCopyNativeBuffers = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Allocator.
|
||||
/// </summary>
|
||||
public static Allocator Singleton { get; } = new Allocator();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to have the native library use the managed allocator.
|
||||
/// </summary>
|
||||
public bool UseManagedAllocator
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.hooked;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (value && !this.hooked)
|
||||
{
|
||||
try
|
||||
{
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_set_allocator(this.allocateDelegate, this.freeDelegate));
|
||||
this.hooked = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Don't fail if we can't set the allocator since this code path is called during the global type
|
||||
// initialization. A failure to set the allocator is also not fatal, but will only cause a performance
|
||||
// issue.
|
||||
System.Diagnostics.Debug.WriteLine("Unable to hook native allocator");
|
||||
}
|
||||
}
|
||||
|
||||
if (!value && this.hooked)
|
||||
{
|
||||
// Disabling the hook once it has been enabled should not catch the exception
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_set_allocator(null, null));
|
||||
this.hooked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to make a safe copy of native buffers.
|
||||
/// </summary>
|
||||
public bool SafeCopyNativeBuffers { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Register the object for disposal when the CLR shuts down.
|
||||
/// </summary>
|
||||
/// <param name="disposable">Object to dispose before native hooks are disconnected.</param>
|
||||
/// <remarks>
|
||||
/// When the CLR shuts down, native callbacks in to the CLR result in an application crash. The allocator free method
|
||||
/// is a native callback to the managed layer that is called whenever the hooked native API needs to free memory.
|
||||
///
|
||||
/// To avoid this callback after the CLR shuts down, the native library must be completly cleaned up prior CLR shutdown.
|
||||
///
|
||||
/// Any object that may hold references to the native library (and will therefore generate native to manged callbacks when it
|
||||
/// gets cleaned up) should register with the RegisterForDisposal method to ensure it is cleaned up in the correct order.
|
||||
/// during shutdown.
|
||||
/// </remarks>
|
||||
public void RegisterForDisposal(IDisposable disposable)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.noMoreDisposalRegistrations)
|
||||
{
|
||||
throw new InvalidOperationException("New objects may not be registered during shutdown.");
|
||||
}
|
||||
|
||||
// Track the object as one we may need to dispose during shutdown.
|
||||
// Use a weak reference to allow the object to be garbage collected earliler if possible.
|
||||
_ = this.disposables.Add(new WeakReference<IDisposable>(disposable));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister the object for disposal.
|
||||
/// </summary>
|
||||
/// <param name="disposable">Object to unregister.</param>
|
||||
/// <remarks>
|
||||
/// This does not unhook the native allocator, but only unregisters the object for
|
||||
/// disposal.
|
||||
/// </remarks>
|
||||
public void UnregisterForDisposal(IDisposable disposable)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// Remove the object and clean up any dead weak references.
|
||||
_ = this.disposables.RemoveWhere((r) =>
|
||||
{
|
||||
bool alive = r.TryGetTarget(out IDisposable target);
|
||||
|
||||
return !alive || target == disposable;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Memory reference to the managed memory that was used by the hooked native
|
||||
/// allocator.
|
||||
/// </summary>
|
||||
/// <param name="address">Native address of the memory.</param>
|
||||
/// <param name="size">Size of the memory region.</param>
|
||||
/// <returns>Reference to the memory, or an empty memory reference.</returns>
|
||||
/// <remarks>
|
||||
/// If the address originally came from a managed array that was provided to the native
|
||||
/// API through the allocator hook, this function will return a Memory reference to the managed
|
||||
/// memory. Since this is a reference to the managed memory and not the native pointer, it
|
||||
/// is safe and not subject to use after free bugs.
|
||||
///
|
||||
/// The address and size do not need to reference the exact pointer provided to the native layer
|
||||
/// by the allocator, but can refer to any region in the allocated memory.
|
||||
/// </remarks>
|
||||
public Memory<byte> GetManagedAllocatedMemory(IntPtr address, long size)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
AllocationContext allocation = this.FindNearestContext(address);
|
||||
if (allocation == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
long offset = (long)address - (long)allocation.BufferAddress;
|
||||
|
||||
// Check that the beginning of the memory is in this allocation
|
||||
if (offset > allocation.Buffer.LongLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check that the end of the memory is in this allocation
|
||||
if (checked(offset + size) > allocation.Buffer.LongLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return a reference to this memory
|
||||
return new Memory<byte>(allocation.Buffer, checked((int)offset), checked((int)size));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a managed array to cache the contents of a native buffer.
|
||||
/// </summary>
|
||||
/// <param name="nativeAddress">Native buffer to mirror.</param>
|
||||
/// <param name="size">Size of the native memory.</param>
|
||||
/// <returns>A managed array populated with the content of the native buffer.</returns>
|
||||
/// <remarks>Multiple callers asking for the same address will get the same buffer.
|
||||
/// When done with the buffer the caller must call <seealso cref="ReturnBufferCache(IntPtr)"/>.
|
||||
/// </remarks>
|
||||
public byte[] GetBufferCache(IntPtr nativeAddress, int size)
|
||||
{
|
||||
BufferCacheEntry entry;
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (this.bufferCache.ContainsKey(nativeAddress))
|
||||
{
|
||||
entry = this.bufferCache[nativeAddress];
|
||||
entry.ReferenceCount++;
|
||||
|
||||
if (entry.UsedSize != size)
|
||||
{
|
||||
throw new Exception("Multiple image buffers sharing the same address cannot have the same size");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = new BufferCacheEntry
|
||||
{
|
||||
ManagedBufferCache = this.pool.Rent(size),
|
||||
UsedSize = size,
|
||||
ReferenceCount = 1,
|
||||
Initialized = false,
|
||||
};
|
||||
|
||||
this.bufferCache.Add(nativeAddress, entry);
|
||||
}
|
||||
}
|
||||
|
||||
lock (entry)
|
||||
{
|
||||
if (!entry.Initialized)
|
||||
{
|
||||
Marshal.Copy(nativeAddress, entry.ManagedBufferCache, 0, entry.UsedSize);
|
||||
entry.Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return entry.ManagedBufferCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the buffer cache.
|
||||
/// </summary>
|
||||
/// <param name="nativeAddress">Address of the native buffer.</param>
|
||||
/// <remarks>Must be called exactly once for each buffer provided by <see cref="GetBufferCache(IntPtr, int)"/>.</remarks>
|
||||
public void ReturnBufferCache(IntPtr nativeAddress)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
BufferCacheEntry entry = this.bufferCache[nativeAddress];
|
||||
entry.ReferenceCount--;
|
||||
if (entry.ReferenceCount == 0)
|
||||
{
|
||||
this.pool.Return(entry.ManagedBufferCache);
|
||||
_ = this.bufferCache.Remove(nativeAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the allocation context who's address is closest but not
|
||||
// greater than the search address
|
||||
private AllocationContext FindNearestContext(IntPtr address)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
long[] keys = new long[this.allocations.Count];
|
||||
this.allocations.Keys.CopyTo(keys, 0);
|
||||
|
||||
int searchIndex = Array.BinarySearch(keys, (long)address);
|
||||
if (searchIndex >= 0)
|
||||
{
|
||||
return this.allocations[keys[searchIndex]];
|
||||
}
|
||||
else
|
||||
{
|
||||
int nextLowestIndex = ~searchIndex - 1;
|
||||
if (nextLowestIndex < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
AllocationContext allocation = this.allocations[keys[nextLowestIndex]];
|
||||
|
||||
return allocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called by the native layer to allocate memory
|
||||
private IntPtr AllocateFunction(int size, out IntPtr context)
|
||||
{
|
||||
byte[] buffer = this.pool.Rent(size);
|
||||
AllocationContext allocationContext = new AllocationContext()
|
||||
{
|
||||
Buffer = buffer,
|
||||
BufferPin = GCHandle.Alloc(buffer, GCHandleType.Pinned),
|
||||
CallbackDelegate = this.freeDelegate,
|
||||
};
|
||||
|
||||
allocationContext.BufferAddress = allocationContext.BufferPin.AddrOfPinnedObject();
|
||||
|
||||
context = (IntPtr)GCHandle.Alloc(allocationContext);
|
||||
|
||||
lock (this)
|
||||
{
|
||||
this.allocations.Add((long)allocationContext.BufferAddress, allocationContext);
|
||||
}
|
||||
|
||||
return allocationContext.BufferAddress;
|
||||
}
|
||||
|
||||
// This function is called by the native layer to free memory
|
||||
private void FreeFunction(IntPtr buffer, IntPtr context)
|
||||
{
|
||||
GCHandle contextPin = (GCHandle)context;
|
||||
AllocationContext allocationContext = (AllocationContext)contextPin.Target;
|
||||
|
||||
lock (this)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(object.ReferenceEquals(this.allocations[(long)buffer], allocationContext), "Allocation context does not match expected value");
|
||||
_ = this.allocations.Remove((long)buffer);
|
||||
}
|
||||
|
||||
allocationContext.BufferPin.Free();
|
||||
this.pool.Return(allocationContext.Buffer);
|
||||
contextPin.Free();
|
||||
}
|
||||
|
||||
// Called when the AppDomain is unloaded or the application exits
|
||||
private void ApplicationExit(object sender, EventArgs e)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// Disable the managed allocator hook to ensure no new allocations
|
||||
this.UseManagedAllocator = false;
|
||||
|
||||
// Prevent more dispsal registrations while we are cleaning up
|
||||
this.noMoreDisposalRegistrations = true;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"Disposable count {this.disposables.Count} (Allocation Count {this.allocations.Count})");
|
||||
|
||||
// First dispose of all the registered objects
|
||||
|
||||
// Don't dispose of the objects during this loop since a
|
||||
// side effect of disposing of an object may be the object unregistering itself
|
||||
// and causing the collection to be modified.
|
||||
List<IDisposable> disposeList = new List<IDisposable>();
|
||||
foreach (WeakReference<IDisposable> r in this.disposables)
|
||||
{
|
||||
if (r.TryGetTarget(out IDisposable disposable))
|
||||
{
|
||||
disposeList.Add(disposable);
|
||||
}
|
||||
}
|
||||
|
||||
this.disposables.Clear();
|
||||
|
||||
foreach (IDisposable disposable in disposeList)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Disposed {disposable.GetType().FullName} (Allocation Count {this.allocations.Count})");
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
// If the allocation count is not zero, we will be called again with a free function
|
||||
// if this happens after the CLR has entered shutdown, the CLR will generate an exception.
|
||||
if (this.allocations.Count > 0)
|
||||
{
|
||||
// TODO: Update exception type
|
||||
throw new Exception("Not all native allocations have been freed before managed shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AllocationContext
|
||||
{
|
||||
public byte[] Buffer { get; set; }
|
||||
|
||||
public IntPtr BufferAddress { get; set; }
|
||||
|
||||
public GCHandle BufferPin { get; set; }
|
||||
|
||||
public NativeMethods.k4a_memory_destroy_cb_t CallbackDelegate { get; set; }
|
||||
}
|
||||
|
||||
private class BufferCacheEntry
|
||||
{
|
||||
public byte[] ManagedBufferCache { get; set; }
|
||||
|
||||
public int UsedSize { get; set; }
|
||||
|
||||
public int ReferenceCount { get; set; }
|
||||
|
||||
public bool Initialized { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Azure.Kinect.Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper to cast from one Memory type to another.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFrom">Element type of the original Memory.</typeparam>
|
||||
/// <typeparam name="TTo">Element type of the new Memory.</typeparam>
|
||||
/// <remarks>
|
||||
/// This type does not take ownership of the Memory, so Dispose does nothing.
|
||||
/// The resultant Memory object derived from this type has the same useful lifetime as
|
||||
/// the source Memory object.
|
||||
/// </remarks>
|
||||
internal class AzureKinectMemoryCast<TFrom, TTo> : MemoryManager<TTo>
|
||||
where TFrom : unmanaged
|
||||
where TTo : unmanaged
|
||||
{
|
||||
private Memory<TFrom> Source { get; }
|
||||
public AzureKinectMemoryCast(Memory<TFrom> memory)
|
||||
{
|
||||
this.Source = memory;
|
||||
}
|
||||
public override Span<TTo> GetSpan()
|
||||
{
|
||||
return MemoryMarshal.Cast<TFrom, TTo>(this.Source.Span);
|
||||
}
|
||||
|
||||
public override MemoryHandle Pin(int elementIndex = 0)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Unpin()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Azure.Kinect.Sensor
|
||||
{
|
||||
|
||||
|
||||
class AzureKinectMemoryManager : MemoryManager<byte>
|
||||
{
|
||||
private Image image;
|
||||
|
||||
internal AzureKinectMemoryManager(Image image)
|
||||
{
|
||||
this.image = image.Reference();
|
||||
|
||||
memoryPressure = image.Size;
|
||||
}
|
||||
|
||||
long memoryPressure;
|
||||
|
||||
public unsafe override Span<byte> GetSpan()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(AzureKinectMemoryManager));
|
||||
}
|
||||
return new Span<byte>(image.GetUnsafeBuffer(), checked((int)image.Size));
|
||||
}
|
||||
}
|
||||
|
||||
private int pinCount = 0;
|
||||
|
||||
public unsafe override MemoryHandle Pin(int elementIndex = 0)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(AzureKinectMemoryManager));
|
||||
}
|
||||
Interlocked.Increment(ref pinCount);
|
||||
return new MemoryHandle(Unsafe.Add<byte>(image.GetUnsafeBuffer(), elementIndex), pinnable: this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Unpin()
|
||||
{
|
||||
Interlocked.Decrement(ref pinCount);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (image != null)
|
||||
{
|
||||
image.Dispose();
|
||||
image = null;
|
||||
}
|
||||
|
||||
if (pinCount != 0)
|
||||
{
|
||||
throw new Exception("Buffer disposed while pinned");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,10 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
{
|
||||
internal Capture(NativeMethods.k4a_capture_t handle)
|
||||
{
|
||||
// Hook the native allocator and register this object.
|
||||
// .Dispose() will be called on this object when the allocator is shut down.
|
||||
Allocator.Singleton.RegisterForDisposal(this);
|
||||
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
|
@ -217,58 +221,45 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
// TODO: dispose managed state (managed objects).
|
||||
if (_Color != null)
|
||||
{
|
||||
// TODO: dispose managed state (managed objects).
|
||||
if (_Color != null)
|
||||
{
|
||||
_Color.Dispose();
|
||||
_Color = null;
|
||||
}
|
||||
if (_Depth != null)
|
||||
{
|
||||
_Depth.Dispose();
|
||||
_Depth = null;
|
||||
}
|
||||
if (_IR != null)
|
||||
{
|
||||
_IR.Dispose();
|
||||
_IR = null;
|
||||
}
|
||||
_Color.Dispose();
|
||||
_Color = null;
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
handle.Close();
|
||||
handle = null;
|
||||
|
||||
disposedValue = true;
|
||||
if (_Depth != null)
|
||||
{
|
||||
_Depth.Dispose();
|
||||
_Depth = null;
|
||||
}
|
||||
if (_IR != null)
|
||||
{
|
||||
_IR.Dispose();
|
||||
_IR = null;
|
||||
}
|
||||
if (handle != null)
|
||||
{
|
||||
handle.Close();
|
||||
handle = null;
|
||||
}
|
||||
Allocator.Singleton.UnregisterForDisposal(this);
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||
~Capture()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
|
|
@ -7,44 +7,54 @@ using System.Text;
|
|||
|
||||
namespace Microsoft.Azure.Kinect.Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an Azure Kinect device.
|
||||
/// </summary>
|
||||
public class Device : IDisposable
|
||||
{
|
||||
// Cache these valuses so we don't need to re-marshal them for each
|
||||
// access since they are immutable.
|
||||
private string serialNum = null;
|
||||
private HardwareVersion version = null;
|
||||
|
||||
// The native handle to the device.
|
||||
private NativeMethods.k4a_device_t handle;
|
||||
|
||||
// To detect redundant calls to Dispose
|
||||
private bool disposedValue = false;
|
||||
|
||||
private Device(NativeMethods.k4a_device_t handle)
|
||||
{
|
||||
// Hook the native allocator and register this object.
|
||||
// .Dispose() will be called on this object when the allocator is shut down.
|
||||
Allocator.Singleton.RegisterForDisposal(this);
|
||||
|
||||
this.handle = handle;
|
||||
}
|
||||
public static int GetInstalledCount()
|
||||
{
|
||||
return (int)NativeMethods.k4a_device_get_installed_count();
|
||||
}
|
||||
|
||||
public static Device Open(int index = 0)
|
||||
{
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_open((uint)index, out NativeMethods.k4a_device_t handle));
|
||||
return new Device(handle);
|
||||
}
|
||||
|
||||
|
||||
private string serialNum = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the devices serial number.
|
||||
/// </summary>
|
||||
public string SerialNum
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
|
||||
if (serialNum != null)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
return serialNum;
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
if (this.serialNum != null)
|
||||
{
|
||||
return this.serialNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine the required string size
|
||||
UIntPtr size = new UIntPtr(0);
|
||||
if (NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL != NativeMethods.k4a_device_get_serialnum(handle, null, ref size))
|
||||
if (NativeMethods.k4a_device_get_serialnum(this.handle, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
|
||||
{
|
||||
throw new System.InvalidOperationException($"Unexpected internal state calling { nameof(NativeMethods.k4a_device_get_serialnum) }");
|
||||
}
|
||||
|
@ -53,7 +63,7 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
StringBuilder serialno = new StringBuilder((int)size.ToUInt32());
|
||||
|
||||
// Get the serial number
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_serialnum(handle, serialno, ref size));
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_serialnum(this.handle, serialno, ref size));
|
||||
|
||||
this.serialNum = serialno.ToString();
|
||||
|
||||
|
@ -63,64 +73,195 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the depth mode the device is currently set to.
|
||||
/// </summary>
|
||||
public DepthMode CurrentDepthMode { get; private set; } = DepthMode.Off;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color resolution the device is currently set to.
|
||||
/// </summary>
|
||||
public ColorResolution CurrentColorResolution { get; private set; } = ColorResolution.Off;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether gets the Sync In jack is connected.
|
||||
/// </summary>
|
||||
public bool SyncInJackConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_sync_jack(
|
||||
this.handle,
|
||||
out bool sync_in,
|
||||
out bool sync_out));
|
||||
return sync_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether gets the Sync Out jack is connected.
|
||||
/// </summary>
|
||||
public bool SyncOutJackConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_sync_jack(
|
||||
this.handle,
|
||||
out bool sync_in,
|
||||
out bool sync_out));
|
||||
return sync_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hardware version of the device.
|
||||
/// </summary>
|
||||
public HardwareVersion Version
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
if (this.version != null)
|
||||
{
|
||||
return this.version;
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_version(
|
||||
this.handle,
|
||||
out NativeMethods.k4a_hardware_version_t nativeVersion));
|
||||
|
||||
this.version = nativeVersion.ToHardwareVersion();
|
||||
return this.version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of currently connected devices.
|
||||
/// </summary>
|
||||
/// <returns>The number of connected devices.</returns>
|
||||
public static int GetInstalledCount()
|
||||
{
|
||||
return (int)NativeMethods.k4a_device_get_installed_count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an Azure Kinect device.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the device to open if there are multiple connected.</param>
|
||||
/// <returns>A Device object representing that device.</returns>
|
||||
/// <remarks>The device will remain opened for exclusive access until the Device object is disposed.</remarks>
|
||||
public static Device Open(int index = 0)
|
||||
{
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_open((uint)index, out NativeMethods.k4a_device_t handle));
|
||||
return new Device(handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the calibration of the device.
|
||||
/// </summary>
|
||||
/// <param name="depthMode">Depth mode for the calibration.</param>
|
||||
/// <param name="colorResolution">Color camera resolution for the calibration.</param>
|
||||
/// <returns>Calibration object.</returns>
|
||||
public Calibration GetCalibration(DepthMode depthMode, ColorResolution colorResolution)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_calibration(handle, depthMode, colorResolution, out Calibration calibration));
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_calibration(this.handle, depthMode, colorResolution, out Calibration calibration));
|
||||
return calibration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DepthMode CurrentDepthMode { get; private set; } = DepthMode.Off;
|
||||
public ColorResolution CurrentColorResolution { get; private set; } = ColorResolution.Off;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the calibration of the device for the current operating mode.
|
||||
/// </summary>
|
||||
/// <returns>Calibration object.</returns>
|
||||
public Calibration GetCalibration()
|
||||
{
|
||||
if (CurrentColorResolution == ColorResolution.Off && CurrentDepthMode == DepthMode.Off)
|
||||
if (this.CurrentColorResolution == ColorResolution.Off && this.CurrentDepthMode == DepthMode.Off)
|
||||
{
|
||||
throw new AzureKinectException("Cameras not started");
|
||||
}
|
||||
|
||||
return GetCalibration(CurrentDepthMode, CurrentColorResolution);
|
||||
return this.GetCalibration(this.CurrentDepthMode, this.CurrentColorResolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device raw calibration data.
|
||||
/// </summary>
|
||||
/// <returns>The raw data can be stored offline for future use.</returns>
|
||||
public byte[] GetRawCalibration()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
// Determine the required calibration size
|
||||
UIntPtr size = new UIntPtr(0);
|
||||
if (NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL != NativeMethods.k4a_device_get_raw_calibration(handle, null, ref size))
|
||||
if (NativeMethods.k4a_device_get_raw_calibration(this.handle, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
|
||||
{
|
||||
throw new AzureKinectException($"Unexpected result calling { nameof(NativeMethods.k4a_device_get_raw_calibration) }");
|
||||
throw new AzureKinectException($"Unexpected result calling {nameof(NativeMethods.k4a_device_get_raw_calibration)}");
|
||||
}
|
||||
|
||||
// Allocate a string buffer
|
||||
byte[] raw = new byte[size.ToUInt32()];
|
||||
|
||||
// Get the raw calibration
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_raw_calibration(handle, raw, ref size));
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_raw_calibration(this.handle, raw, ref size));
|
||||
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
public Capture GetCapture(int timeoutInMS = -1)
|
||||
/// <summary>
|
||||
/// Reads a sensor capture.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to wait for a capture.</param>
|
||||
/// <returns>A Capture object holding image data.</returns>
|
||||
/// <remarks>Gets the next capture in the streamed sequence of captures from the camera.
|
||||
/// If a new capture is not currently available, this function will block until the timeout is reached.
|
||||
/// The SDK will buffer at least two captures worth of data before dropping the oldest capture.
|
||||
/// Callers needing to capture all data need to ensure they read the data as fast as the data is being produced on average.</remarks>
|
||||
public Capture GetCapture(TimeSpan timeout)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_capture(handle, out NativeMethods.k4a_capture_t capture, timeoutInMS);
|
||||
NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_capture(this.handle, out NativeMethods.k4a_capture_t capture, (int)timeout.TotalMilliseconds);
|
||||
|
||||
if (result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_TIMEOUT)
|
||||
{
|
||||
|
@ -138,15 +279,39 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
}
|
||||
}
|
||||
|
||||
public ImuSample GetImuSample(int timeoutInMS = -1)
|
||||
/// <summary>
|
||||
/// Reads a sensor capture.
|
||||
/// </summary>
|
||||
/// <returns>A Capture object holding image data.</returns>
|
||||
/// <remarks>Gets the next capture in the streamed sequence of captures from the camera.
|
||||
/// If a new capture is not currently available, this function will block until one is available.
|
||||
/// The SDK will buffer at least two captures worth of data before dropping the oldest capture.
|
||||
/// Callers needing to capture all data need to ensure they read the data as fast as the data is being produced on average.</remarks>
|
||||
public Capture GetCapture()
|
||||
{
|
||||
return this.GetCapture(TimeSpan.FromMilliseconds(-1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an IMU sample from the device.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to wait for an IMU sample.</param>
|
||||
/// <returns>The next unread IMU sample from the device.</returns>
|
||||
/// <remarks>Gets the next sample in the streamed sequence of IMU samples from the device.
|
||||
/// If a new sample is not currently available, this function will block until the timeout is reached.
|
||||
/// The API will buffer at least two camera capture intervals worth of samples before dropping the oldest sample. Callers needing to capture all data need to ensure they read the data as fast as the data is being produced on average.
|
||||
/// </remarks>
|
||||
public ImuSample GetImuSample(TimeSpan timeout)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
ImuSample sample = new ImuSample();
|
||||
NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_imu_sample(handle, sample, timeoutInMS);
|
||||
NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_imu_sample(this.handle, sample, (int)timeout.TotalMilliseconds);
|
||||
|
||||
if (result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_TIMEOUT)
|
||||
{
|
||||
|
@ -159,191 +324,179 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an IMU sample from the device.
|
||||
/// </summary>
|
||||
/// <returns>The next unread IMU sample from the device.</returns>
|
||||
/// <remarks>Gets the next sample in the streamed sequence of IMU samples from the device.
|
||||
/// If a new sample is not currently available, this function will block until one is available.
|
||||
/// The API will buffer at least two camera capture intervals worth of samples before dropping the oldest sample. Callers needing to capture all data need to ensure they read the data as fast as the data is being produced on average.
|
||||
/// </remarks>
|
||||
public ImuSample GetImuSample()
|
||||
{
|
||||
return this.GetImuSample(TimeSpan.FromMilliseconds(-1));
|
||||
}
|
||||
|
||||
public Int32 GetColorControl(ColorControlCommand command)
|
||||
/// <summary>
|
||||
/// Get the Azure Kinect color sensor control value.
|
||||
/// </summary>
|
||||
/// <param name="command">Color sensor control command.</param>
|
||||
/// <returns>The value of the color control option.</returns>
|
||||
public int GetColorControl(ColorControlCommand command)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
return this.GetColorControl(command, out ColorControlMode mode);
|
||||
}
|
||||
}
|
||||
|
||||
public Int32 GetColorControl(ColorControlCommand command, out ColorControlMode mode)
|
||||
/// <summary>
|
||||
/// Get the Azure Kinect color sensor control value.
|
||||
/// </summary>
|
||||
/// <param name="command">Color sensor control command.</param>
|
||||
/// <param name="mode">The mode of the color control option.</param>
|
||||
/// <returns>The value of the color control option.</returns>
|
||||
public int GetColorControl(ColorControlCommand command, out ColorControlMode mode)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_color_control(handle, command, out mode, out int value));
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_color_control(this.handle, command, out mode, out int value));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetColorControl(ColorControlCommand command, ColorControlMode mode, Int32 value)
|
||||
/// <summary>
|
||||
/// Sets the Azure Kinect color sensor control value.
|
||||
/// </summary>
|
||||
/// <param name="command">Color sensor control command.</param>
|
||||
/// <param name="mode">The mode of the color control option.</param>
|
||||
/// <param name="value">The value of the color control option.</param>
|
||||
public void SetColorControl(ColorControlCommand command, ColorControlMode mode, int value)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_set_color_control(handle, command, mode, value));
|
||||
}
|
||||
}
|
||||
|
||||
public bool SyncInJackConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_sync_jack(handle,
|
||||
out bool sync_in,
|
||||
out bool sync_out));
|
||||
return sync_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SyncOutJackConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_sync_jack(handle,
|
||||
out bool sync_in,
|
||||
out bool sync_out));
|
||||
return sync_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the version information so we don't need to re-marshal it for each
|
||||
// access since it is not allowed to change
|
||||
private HardwareVersion version = null;
|
||||
|
||||
public HardwareVersion Version
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
|
||||
if (version != null)
|
||||
return version;
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_get_version(handle,
|
||||
out NativeMethods.k4a_hardware_version_t nativeVersion));
|
||||
|
||||
version = nativeVersion.ToHardwareVersion();
|
||||
return version;
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_set_color_control(this.handle, command, mode, value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts color and depth camera capture.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration we want to run the device in.</param>
|
||||
public void StartCameras(DeviceConfiguration configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
NativeMethods.k4a_device_configuration_t nativeConfig = configuration.GetNativeConfiguration();
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_start_cameras(handle, ref nativeConfig));
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_start_cameras(this.handle, ref nativeConfig));
|
||||
|
||||
this.CurrentDepthMode = configuration.DepthMode;
|
||||
this.CurrentColorResolution = configuration.ColorResolution;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the color and depth camera capture.
|
||||
/// </summary>
|
||||
public void StopCameras()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
NativeMethods.k4a_device_stop_cameras(handle);
|
||||
NativeMethods.k4a_device_stop_cameras(this.handle);
|
||||
|
||||
this.CurrentDepthMode = DepthMode.Off;
|
||||
this.CurrentColorResolution = ColorResolution.Off;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the IMU sample stream.
|
||||
/// </summary>
|
||||
public void StartImu()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_start_imu(handle));
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_start_imu(this.handle));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the IMU sample stream.
|
||||
/// </summary>
|
||||
public void StopImu()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposedValue)
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
|
||||
NativeMethods.k4a_device_stop_imu(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private NativeMethods.k4a_device_t handle;
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
if (this.disposedValue)
|
||||
{
|
||||
// TODO: dispose managed state (managed objects).
|
||||
throw new ObjectDisposedException(nameof(Device));
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
handle.Close();
|
||||
handle = null;
|
||||
|
||||
disposedValue = true;
|
||||
NativeMethods.k4a_device_stop_imu(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||
~Device() {
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Handle the Dispose pattern.
|
||||
/// </summary>
|
||||
/// <param name="disposing">True if called by Dispose</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Allocator.Singleton.UnregisterForDisposal(this);
|
||||
|
||||
this.handle.Close();
|
||||
this.handle = null;
|
||||
|
||||
this.disposedValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Azure.Kinect.Sensor
|
||||
{
|
||||
internal class LargeArrayPool : ArrayPool<byte>
|
||||
{
|
||||
private readonly List<WeakReference> pool = new List<WeakReference>();
|
||||
|
||||
public override byte[] Rent(int minimumLength)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
byte[] buffer;
|
||||
foreach (WeakReference x in this.pool)
|
||||
{
|
||||
if (x.IsAlive)
|
||||
{
|
||||
buffer = (byte[])x.Target;
|
||||
if (buffer.Length >= minimumLength)
|
||||
{
|
||||
_ = this.pool.Remove(x);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new byte[minimumLength];
|
||||
}
|
||||
}
|
||||
|
||||
public override void Return(byte[] array, bool clearArray = false)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (clearArray)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
this.pool.Add(new WeakReference(array));
|
||||
|
||||
int count = this.pool.RemoveAll((x) => !x.IsAlive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Memory" Version="4.5.3" />
|
||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -64,14 +64,24 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
{
|
||||
}
|
||||
|
||||
private k4a_image_t(k4a_image_t original) : base(true)
|
||||
{
|
||||
NativeMethods.k4a_image_reference(original.handle);
|
||||
this.handle = original.handle;
|
||||
}
|
||||
|
||||
public k4a_image_t DuplicateReference()
|
||||
{
|
||||
return new k4a_image_t(this);
|
||||
/*
|
||||
k4a_image_t duplicate = new k4a_image_t();
|
||||
|
||||
NativeMethods.k4a_image_reference(handle);
|
||||
|
||||
duplicate.handle = this.handle;
|
||||
return duplicate;
|
||||
*/
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
NativeMethods.k4a_image_release(handle);
|
||||
|
@ -189,6 +199,13 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
|
||||
#region Functions
|
||||
|
||||
[DllImport("k4a", CallingConvention = CallingConvention.Cdecl)]
|
||||
[NativeReference]
|
||||
public static extern k4a_result_t k4a_set_allocator(
|
||||
k4a_memory_allocate_cb_t allocate,
|
||||
k4a_memory_destroy_cb_t free
|
||||
);
|
||||
|
||||
[DllImport("k4a", CallingConvention = CallingConvention.Cdecl)]
|
||||
[NativeReference]
|
||||
public static extern k4a_result_t k4a_calibration_2d_to_2d(
|
||||
|
@ -311,9 +328,7 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
[DllImport("k4a", CallingConvention = CallingConvention.Cdecl)]
|
||||
[NativeReference]
|
||||
public static extern void k4a_capture_set_temperature_c(k4a_capture_t capture_handle, float temperature_c);
|
||||
|
||||
|
||||
|
||||
|
||||
[DllImport("k4a", CallingConvention = CallingConvention.Cdecl)]
|
||||
[NativeReference]
|
||||
public static extern void k4a_capture_reference(IntPtr capture_handle);
|
||||
|
@ -330,6 +345,7 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
int stride_bytes,
|
||||
out k4a_image_t image_handle);
|
||||
|
||||
public delegate IntPtr k4a_memory_allocate_cb_t(int size, out IntPtr context);
|
||||
public delegate void k4a_memory_destroy_cb_t(IntPtr buffer, IntPtr context);
|
||||
|
||||
[DllImport("k4a", CallingConvention = CallingConvention.Cdecl)]
|
||||
|
|
|
@ -19,14 +19,14 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
}
|
||||
}
|
||||
|
||||
public ArrayImage<ushort> DepthImageToColorCamera(Capture capture)
|
||||
public Image DepthImageToColorCamera(Capture capture)
|
||||
{
|
||||
return DepthImageToColorCamera(capture.Depth);
|
||||
}
|
||||
|
||||
public ArrayImage<ushort> DepthImageToColorCamera(Image depth)
|
||||
public Image DepthImageToColorCamera(Image depth)
|
||||
{
|
||||
ArrayImage<ushort> image = new ArrayImage<ushort>(ImageFormat.Depth16,
|
||||
Image image = new Image(ImageFormat.Depth16,
|
||||
calibration.color_camera_calibration.resolution_width,
|
||||
calibration.color_camera_calibration.resolution_height)
|
||||
{
|
||||
|
@ -55,23 +55,29 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
using (Image depthReference = depth.Reference())
|
||||
using (Image transformedReference = transformed.Reference())
|
||||
{
|
||||
// Ensure changes made to the managed memory are visible to the native layer
|
||||
depthReference.FlushMemory();
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_transformation_depth_image_to_color_camera(
|
||||
handle,
|
||||
depthReference.DangerousGetHandle(),
|
||||
transformedReference.DangerousGetHandle()
|
||||
));
|
||||
|
||||
// Copy the native memory back to managed memory if required
|
||||
transformedReference.InvalidateMemory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayImage<BGRA> ColorImageToDepthCamera(Capture capture)
|
||||
public Image ColorImageToDepthCamera(Capture capture)
|
||||
{
|
||||
return ColorImageToDepthCamera(capture.Depth, capture.Color);
|
||||
}
|
||||
|
||||
public ArrayImage<BGRA> ColorImageToDepthCamera(Image depth, Image color)
|
||||
public Image ColorImageToDepthCamera(Image depth, Image color)
|
||||
{
|
||||
ArrayImage<BGRA> transformed = new ArrayImage<BGRA>(ImageFormat.ColorBGRA32,
|
||||
Image transformed = new Image(ImageFormat.ColorBGRA32,
|
||||
calibration.depth_camera_calibration.resolution_width,
|
||||
calibration.depth_camera_calibration.resolution_height)
|
||||
{
|
||||
|
@ -80,7 +86,6 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
Timestamp = color.Timestamp,
|
||||
WhiteBalance = color.WhiteBalance
|
||||
};
|
||||
|
||||
|
||||
ColorImageToDepthCamera(depth, color, transformed);
|
||||
|
||||
|
@ -105,18 +110,25 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
using (Image colorReference = color.Reference())
|
||||
using (Image transformedReference = transformed.Reference())
|
||||
{
|
||||
// Ensure changes made to the managed memory are visible to the native layer
|
||||
depthReference.FlushMemory();
|
||||
colorReference.FlushMemory();
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_transformation_color_image_to_depth_camera(
|
||||
handle,
|
||||
depthReference.DangerousGetHandle(),
|
||||
colorReference.DangerousGetHandle(),
|
||||
transformedReference.DangerousGetHandle()));
|
||||
|
||||
// Copy the native memory back to managed memory if required
|
||||
transformedReference.InvalidateMemory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayImage<Short3> DepthImageToPointCloud(Image depth, Calibration.DeviceType camera = Calibration.DeviceType.Depth)
|
||||
public Image DepthImageToPointCloud(Image depth, Calibration.DeviceType camera = Calibration.DeviceType.Depth)
|
||||
{
|
||||
ArrayImage<Short3> pointCloud = new ArrayImage<Short3>(ImageFormat.Custom,
|
||||
Image pointCloud = new Image(ImageFormat.Custom,
|
||||
depth.WidthPixels, depth.HeightPixels);
|
||||
|
||||
DepthImageToPointCloud(depth, pointCloud, camera);
|
||||
|
@ -136,11 +148,17 @@ namespace Microsoft.Azure.Kinect.Sensor
|
|||
using (Image depthReference = depth.Reference())
|
||||
using (Image pointCloudReference = pointCloud.Reference())
|
||||
{
|
||||
// Ensure changes made to the managed memory are visible to the native layer
|
||||
depthReference.FlushMemory();
|
||||
|
||||
AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_transformation_depth_image_to_point_cloud(
|
||||
handle,
|
||||
depthReference.DangerousGetHandle(),
|
||||
camera,
|
||||
pointCloudReference.DangerousGetHandle()));
|
||||
|
||||
// Copy the native memory back to managed memory if required
|
||||
pointCloudReference.InvalidateMemory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,16 @@ void Stub_SetErrorFunction(RaiseError *pfnErrorHandler)
|
|||
|
||||
inline void Stub_Assert(char *szFile, int line, bool expressionValue, char *expression)
|
||||
{
|
||||
if (!expressionValue && g_ErrorHandler)
|
||||
if (!expressionValue)
|
||||
{
|
||||
g_ErrorHandler(szFile, line, expression);
|
||||
if (g_ErrorHandler)
|
||||
{
|
||||
g_ErrorHandler(szFile, line, expression);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace Microsoft.Azure.Kinect.Sensor.UnitTests
|
|||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
|
||||
// Don't hook the native allocator
|
||||
Microsoft.Azure.Kinect.Sensor.Allocator.Singleton.UseManagedAllocator = false;
|
||||
}
|
||||
|
||||
// Helper function to implement basic open/close behavior
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace Microsoft.Azure.Kinect.Sensor.UnitTests
|
|||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
|
||||
// Don't hook the native allocator
|
||||
Microsoft.Azure.Kinect.Sensor.Allocator.Singleton.UseManagedAllocator = false;
|
||||
}
|
||||
|
||||
// Helper function to implement basic open/close behavior
|
||||
|
@ -616,7 +617,7 @@ void k4a_capture_release(k4a_capture_t capture_handle)
|
|||
{
|
||||
Assert.AreEqual(0, count.Calls("k4a_device_get_capture"));
|
||||
Assert.AreEqual(0, count.Calls("k4a_capture_release"));
|
||||
using (Capture capture = device.GetCapture(2345))
|
||||
using (Capture capture = device.GetCapture(System.TimeSpan.FromMilliseconds(2345)))
|
||||
{
|
||||
Assert.AreEqual(1, count.Calls("k4a_device_get_capture"));
|
||||
Assert.AreEqual(0, count.Calls("k4a_capture_release"));
|
||||
|
@ -635,7 +636,7 @@ void k4a_capture_release(k4a_capture_t capture_handle)
|
|||
Assert.AreEqual(0, count.Calls("k4a_device_get_capture"));
|
||||
Assert.AreEqual(0, count.Calls("k4a_capture_release"));
|
||||
|
||||
capture = device.GetCapture(2345);
|
||||
capture = device.GetCapture(System.TimeSpan.FromMilliseconds(2345));
|
||||
|
||||
Assert.AreEqual(1, count.Calls("k4a_device_get_capture"));
|
||||
Assert.AreEqual(0, count.Calls("k4a_capture_release"));
|
||||
|
@ -683,7 +684,7 @@ k4a_wait_result_t k4a_device_get_capture(k4a_device_t device_handle, k4a_capture
|
|||
|
||||
Assert.Throws(typeof(System.TimeoutException), () =>
|
||||
{
|
||||
using (Capture capture = device.GetCapture(2345))
|
||||
using (Capture capture = device.GetCapture(System.TimeSpan.FromMilliseconds(2345)))
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -735,7 +736,7 @@ k4a_wait_result_t k4a_device_get_capture(k4a_device_t device_handle, k4a_capture
|
|||
|
||||
Assert.Throws(typeof(Microsoft.Azure.Kinect.Sensor.AzureKinectException), () =>
|
||||
{
|
||||
using (Capture capture = device.GetCapture(2345))
|
||||
using (Capture capture = device.GetCapture(System.TimeSpan.FromMilliseconds(2345)))
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -769,7 +770,7 @@ k4a_wait_result_t k4a_device_get_capture(k4a_device_t device_handle, k4a_capture
|
|||
|
||||
Assert.Throws(typeof(Microsoft.Azure.Kinect.Sensor.AzureKinectException), () =>
|
||||
{
|
||||
using (Capture capture = device.GetCapture(2345))
|
||||
using (Capture capture = device.GetCapture(System.TimeSpan.FromMilliseconds(2345)))
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -968,7 +969,7 @@ k4a_wait_result_t k4a_device_get_imu_sample(k4a_device_t device_handle, k4a_imu_
|
|||
using (Device device = Device.Open(0))
|
||||
{
|
||||
Assert.AreEqual(0, count.Calls("k4a_device_get_imu_sample"));
|
||||
ImuSample sample = device.GetImuSample(2345);
|
||||
ImuSample sample = device.GetImuSample(System.TimeSpan.FromMilliseconds(2345));
|
||||
|
||||
Assert.AreEqual(0.123f, sample.Temperature);
|
||||
|
||||
|
@ -987,7 +988,7 @@ k4a_wait_result_t k4a_device_get_imu_sample(k4a_device_t device_handle, k4a_imu_
|
|||
device.Dispose();
|
||||
Assert.Throws(typeof(System.ObjectDisposedException), () =>
|
||||
{
|
||||
device.GetImuSample(2345);
|
||||
device.GetImuSample(System.TimeSpan.FromMilliseconds(2345));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1019,7 +1020,7 @@ k4a_wait_result_t k4a_device_get_imu_sample(k4a_device_t device_handle, k4a_imu_
|
|||
|
||||
Assert.Throws(typeof(System.TimeoutException), () =>
|
||||
{
|
||||
ImuSample sample = device.GetImuSample(2345);
|
||||
ImuSample sample = device.GetImuSample(System.TimeSpan.FromMilliseconds(2345));
|
||||
});
|
||||
|
||||
Assert.AreEqual(1, count.Calls("k4a_device_get_imu_sample"));
|
||||
|
@ -1050,7 +1051,7 @@ k4a_wait_result_t k4a_device_get_imu_sample(k4a_device_t device_handle, k4a_imu_
|
|||
|
||||
Assert.Throws(typeof(Microsoft.Azure.Kinect.Sensor.AzureKinectException), () =>
|
||||
{
|
||||
ImuSample sample = device.GetImuSample(2345);
|
||||
ImuSample sample = device.GetImuSample(System.TimeSpan.FromMilliseconds(2345));
|
||||
});
|
||||
|
||||
Assert.AreEqual(1, count.Calls("k4a_device_get_imu_sample"));
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
// Licensed under the MIT License.
|
||||
using Microsoft.Azure.Kinect.Sensor.Test.StubGenerator;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Azure.Kinect.Sensor.UnitTests
|
||||
{
|
||||
|
@ -28,13 +33,20 @@ namespace Microsoft.Azure.Kinect.Sensor.UnitTests
|
|||
// Force garbage collection
|
||||
System.GC.Collect(0, System.GCCollectionMode.Forced, true);
|
||||
System.GC.WaitForPendingFinalizers();
|
||||
|
||||
// Don't hook the native allocator
|
||||
Microsoft.Azure.Kinect.Sensor.Allocator.Singleton.UseManagedAllocator = false;
|
||||
}
|
||||
|
||||
// Helper function to implement basic create/release behavior
|
||||
private void SetCreateReleaseImplementation()
|
||||
private void SetImageStubImplementation()
|
||||
{
|
||||
NativeK4a.SetImplementation(@"
|
||||
|
||||
uint16_t dummybuffer[640*480];
|
||||
|
||||
int referenceCount = 0;
|
||||
|
||||
k4a_result_t k4a_image_create(k4a_image_format_t format, int width_pixels, int height_pixels, int stride_bytes, k4a_image_t* image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle != NULL);
|
||||
|
@ -45,12 +57,16 @@ k4a_result_t k4a_image_create(k4a_image_format_t format, int width_pixels, int h
|
|||
STUB_ASSERT(stride_bytes == (640*2));
|
||||
|
||||
*image_handle = (k4a_image_t)0x0D001234;
|
||||
return K4A_RESULT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void k4a_image_release(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
for (int i = 0; i < 640 * 480; i++)
|
||||
{
|
||||
dummybuffer[i] = (uint16_t)i;
|
||||
}
|
||||
|
||||
//STUB_ASSERT(referenceCount == 0);
|
||||
referenceCount = 1;
|
||||
|
||||
return K4A_RESULT_SUCCEEDED;
|
||||
}
|
||||
|
||||
size_t k4a_image_get_size(k4a_image_t image_handle)
|
||||
|
@ -60,6 +76,51 @@ size_t k4a_image_get_size(k4a_image_t image_handle)
|
|||
return 640*2*480;
|
||||
}
|
||||
|
||||
uint8_t* k4a_image_get_buffer(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return (uint8_t*)dummybuffer;
|
||||
}
|
||||
|
||||
int k4a_image_get_stride_bytes(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return 640*2;
|
||||
}
|
||||
|
||||
int k4a_image_get_width_pixels(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return 640;
|
||||
}
|
||||
|
||||
int k4a_image_get_height_pixels(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return 480;
|
||||
}
|
||||
|
||||
void k4a_image_reference(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
referenceCount++;
|
||||
}
|
||||
|
||||
void k4a_image_release(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
referenceCount--;
|
||||
if (referenceCount == 0)
|
||||
{
|
||||
memset(dummybuffer, 0, sizeof(dummybuffer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
");
|
||||
}
|
||||
|
||||
|
@ -72,7 +133,7 @@ size_t k4a_image_get_size(k4a_image_t image_handle)
|
|||
[Test]
|
||||
public void ImageGarbageCollection()
|
||||
{
|
||||
SetCreateReleaseImplementation();
|
||||
SetImageStubImplementation();
|
||||
|
||||
CallCount count = NativeK4a.CountCalls();
|
||||
|
||||
|
@ -83,12 +144,15 @@ size_t k4a_image_get_size(k4a_image_t image_handle)
|
|||
{
|
||||
Image i = new Image(ImageFormat.Custom, 640, 480, 640 * 2);
|
||||
|
||||
var memory = i.Memory;
|
||||
|
||||
// The reference should still exist and we should have not seen close called
|
||||
Assert.AreEqual(1, count.Calls("k4a_image_create"));
|
||||
Assert.AreEqual(0, count.Calls("k4a_image_release"));
|
||||
|
||||
return i;
|
||||
});
|
||||
|
||||
// The reference to the Device object is no longer on the stack, and therefore is free to be garbage collected
|
||||
// At this point capture.IsAlive is likely to be true, but not garanteed to be
|
||||
|
||||
|
@ -101,135 +165,38 @@ size_t k4a_image_get_size(k4a_image_t image_handle)
|
|||
|
||||
// k4a_device_close should have been called automatically
|
||||
Assert.AreEqual(1, count.Calls("k4a_image_create"));
|
||||
Assert.AreEqual(1, count.Calls("k4a_image_release"));
|
||||
Assert.AreEqual(count.Calls("k4a_image_reference") + 1, count.Calls("k4a_image_release"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBufferCopyTest()
|
||||
public void ImageMemoryTest()
|
||||
{
|
||||
SetCreateReleaseImplementation();
|
||||
|
||||
NativeK4a.SetImplementation(@"
|
||||
|
||||
uint16_t dummybuffer[640*480];
|
||||
|
||||
|
||||
size_t k4a_image_get_size(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return 640*2*480;
|
||||
}
|
||||
|
||||
uint8_t* k4a_image_get_buffer(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
for (int i = 0; i < 640 * 480; i++)
|
||||
{
|
||||
dummybuffer[i] = (uint16_t)i;
|
||||
}
|
||||
|
||||
return (uint8_t*)dummybuffer;
|
||||
}
|
||||
|
||||
");
|
||||
SetImageStubImplementation();
|
||||
|
||||
|
||||
CallCount count = NativeK4a.CountCalls();
|
||||
|
||||
Assert.AreEqual(0, count.Calls("k4a_image_create"));
|
||||
Assert.AreEqual(0, count.Calls("k4a_image_release"));
|
||||
|
||||
using (Image image = new Image(ImageFormat.Custom, 640, 480, 640 * 2))
|
||||
Task.Run(() =>
|
||||
{
|
||||
|
||||
byte[] buffer = image.GetBufferCopy();
|
||||
Assert.AreEqual(640 * 480 * 2, image.Size);
|
||||
|
||||
// k4a_image_create should have been called once
|
||||
Assert.AreEqual(1, count.Calls("k4a_image_create"));
|
||||
|
||||
for (int i = 0; i < buffer.Length / 2; i++)
|
||||
using (Image image = new Image(ImageFormat.Custom, 640, 480, 640 * 2))
|
||||
{
|
||||
Assert.AreEqual((ushort)i, System.BitConverter.ToUInt16(buffer, i * 2));
|
||||
Memory<byte> memory = image.Memory;
|
||||
System.Span<byte> memorySpan = memory.Span;
|
||||
|
||||
System.Span<short> shortSpan = MemoryMarshal.Cast<byte, short>(memorySpan);
|
||||
|
||||
Assert.AreEqual(0, shortSpan[0]);
|
||||
Assert.AreEqual(1, shortSpan[1]);
|
||||
image.Dispose();
|
||||
}
|
||||
}).Wait();
|
||||
|
||||
// Verify that writes to the buffer are to a copy and don't impact future callers
|
||||
buffer[0] = 99;
|
||||
|
||||
byte[] buffer2 = image.GetBufferCopy();
|
||||
Assert.AreEqual(0, buffer2[0]);
|
||||
}
|
||||
|
||||
|
||||
NativeK4a.SetImplementation(@"
|
||||
|
||||
size_t k4a_image_get_size(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return 640*2*480;
|
||||
}
|
||||
|
||||
uint8_t* k4a_image_get_buffer(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return (uint8_t*)NULL;
|
||||
}
|
||||
|
||||
");
|
||||
using (Image image = new Image(ImageFormat.Custom, 640, 480, 640 * 2))
|
||||
{
|
||||
|
||||
Assert.Throws(typeof(AzureKinectException), () => {
|
||||
byte[] buffer = image.GetBufferCopy();
|
||||
});
|
||||
}
|
||||
|
||||
NativeK4a.SetImplementation(@"
|
||||
|
||||
uint16_t dummybuffer[640*480];
|
||||
|
||||
|
||||
size_t k4a_image_get_size(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* k4a_image_get_buffer(k4a_image_t image_handle)
|
||||
{
|
||||
STUB_ASSERT(image_handle == (k4a_image_t)0x0D001234);
|
||||
|
||||
for (int i = 0; i < 640 * 480; i++)
|
||||
{
|
||||
dummybuffer[i] = (uint16_t)i;
|
||||
}
|
||||
|
||||
return (uint8_t*)dummybuffer;
|
||||
}
|
||||
|
||||
");
|
||||
|
||||
using (Image image = new Image(ImageFormat.Custom, 640, 480, 640 * 2))
|
||||
{
|
||||
|
||||
byte[] buffer = image.GetBufferCopy();
|
||||
|
||||
Assert.AreEqual(0, buffer.Length);
|
||||
|
||||
image.Dispose();
|
||||
|
||||
Assert.Throws(typeof(System.ObjectDisposedException), ()=>
|
||||
{
|
||||
image.GetBufferCopy();
|
||||
});
|
||||
}
|
||||
GC.Collect(0, GCCollectionMode.Forced, true, true);
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
Assert.AreEqual(count.Calls("k4a_image_reference") + 1, count.Calls("k4a_image_release"), "References not zero");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -49,10 +49,8 @@ typedef struct _dewrapper_context_t
|
|||
|
||||
typedef struct _shared_image_context_t
|
||||
{
|
||||
LOCK_HANDLE lock;
|
||||
// Overall shared buffer
|
||||
uint8_t *buffer;
|
||||
image_destroy_cb_t *memory_free_cb;
|
||||
void *memory_free_cb_context;
|
||||
volatile long ref;
|
||||
} shared_image_context_t;
|
||||
|
||||
|
@ -108,15 +106,17 @@ static void free_shared_depth_image(void *buffer, void *context)
|
|||
RETURN_VALUE_IF_ARG(VOID_VALUE, buffer == NULL);
|
||||
RETURN_VALUE_IF_ARG(VOID_VALUE, context == NULL);
|
||||
|
||||
// The buffer being passed in may be the beginning or the middle of the
|
||||
// overall shared buffer
|
||||
(void)buffer;
|
||||
|
||||
shared_image_context_t *shared_context = context;
|
||||
shared_image_context_t *shared_context = (shared_image_context_t *)context;
|
||||
|
||||
long count = DEC_REF_VAR(shared_context->ref);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
shared_context->memory_free_cb(shared_context->buffer, shared_context->memory_free_cb_context);
|
||||
allocator_free(shared_context->buffer);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +214,6 @@ static int depth_engine_thread(void *param)
|
|||
shared_image_context_t *shared_image_context = NULL;
|
||||
uint8_t *raw_image_buffer = NULL;
|
||||
size_t raw_image_buffer_size = 0;
|
||||
void *allocator_context = NULL;
|
||||
bool dropped = false;
|
||||
|
||||
k4a_wait_result_t wresult = queue_pop(dewrapper->queue, K4A_WAIT_INFINITE, &capture_raw);
|
||||
|
@ -236,9 +235,7 @@ static int depth_engine_thread(void *param)
|
|||
|
||||
// Allocate 1 buffer for depth engine to write depth and IR images to
|
||||
assert(depth_engine_output_buffer_size != 0);
|
||||
capture_byte_ptr = allocator_alloc(ALLOCATION_SOURCE_DEPTH,
|
||||
depth_engine_output_buffer_size,
|
||||
&allocator_context);
|
||||
capture_byte_ptr = allocator_alloc(ALLOCATION_SOURCE_DEPTH, depth_engine_output_buffer_size);
|
||||
if (capture_byte_ptr == NULL)
|
||||
{
|
||||
LOG_ERROR("Depth streaming callback failed to allocate output buffer", 0);
|
||||
|
@ -297,8 +294,6 @@ static int depth_engine_thread(void *param)
|
|||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
shared_image_context->memory_free_cb = allocator_free;
|
||||
shared_image_context->memory_free_cb_context = allocator_context;
|
||||
shared_image_context->ref = 0;
|
||||
shared_image_context->buffer = capture_byte_ptr;
|
||||
|
||||
|
@ -397,7 +392,7 @@ static int depth_engine_thread(void *param)
|
|||
|
||||
if (capture_byte_ptr && cleanup_capture_byte_ptr)
|
||||
{
|
||||
allocator_free(capture_byte_ptr, allocator_context);
|
||||
allocator_free(capture_byte_ptr);
|
||||
}
|
||||
|
||||
if (dropped)
|
||||
|
|
|
@ -105,6 +105,12 @@ k4a_result_t image_create_from_buffer(k4a_image_format_t format,
|
|||
return result;
|
||||
}
|
||||
|
||||
static void image_default_free_function(void *buffer, void *context)
|
||||
{
|
||||
(void)context;
|
||||
allocator_free(buffer);
|
||||
}
|
||||
|
||||
static k4a_result_t image_create_empty_image(allocation_source_t source, size_t size, k4a_image_t *image_handle)
|
||||
{
|
||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, image_handle == NULL);
|
||||
|
@ -113,21 +119,20 @@ static k4a_result_t image_create_empty_image(allocation_source_t source, size_t
|
|||
|
||||
k4a_result_t result;
|
||||
image_context_t *image = NULL;
|
||||
void *alloc_context = NULL;
|
||||
|
||||
result = K4A_RESULT_FROM_BOOL((image = k4a_image_t_create(image_handle)) != NULL);
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL((image->buffer = allocator_alloc(source, size, &alloc_context)) != NULL);
|
||||
result = K4A_RESULT_FROM_BOOL((image->buffer = allocator_alloc(source, size)) != NULL);
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
image->ref_count = 1;
|
||||
image->buffer_size = size;
|
||||
image->memory_free_cb = allocator_free;
|
||||
image->memory_free_cb_context = alloc_context;
|
||||
image->memory_free_cb = image_default_free_function;
|
||||
image->memory_free_cb_context = NULL;
|
||||
image->lock = Lock_Init();
|
||||
result = K4A_RESULT_FROM_BOOL(image->lock != NULL);
|
||||
}
|
||||
|
@ -154,6 +159,7 @@ k4a_result_t image_create(k4a_image_format_t format,
|
|||
int width_pixels,
|
||||
int height_pixels,
|
||||
int stride_bytes,
|
||||
allocation_source_t source,
|
||||
k4a_image_t *image_handle)
|
||||
{
|
||||
// User is special and only allowed to be used by the user through a public API.
|
||||
|
@ -168,7 +174,7 @@ k4a_result_t image_create(k4a_image_format_t format,
|
|||
size_t size = (size_t)height_pixels * (size_t)stride_bytes;
|
||||
|
||||
*image_handle = NULL;
|
||||
result = TRACE_CALL(image_create_empty_image(ALLOCATION_SOURCE_USER, size, image_handle));
|
||||
result = TRACE_CALL(image_create_empty_image(source, size, image_handle));
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
|
|
428
src/sdk/k4a.c
428
src/sdk/k4a.c
|
@ -54,6 +54,18 @@ K4A_DECLARE_CONTEXT(k4a_device_t, k4a_context_t);
|
|||
#define DEPTH_CAPTURE (false)
|
||||
#define COLOR_CAPTURE (true)
|
||||
#define TRANSFORM_ENABLE_GPU_OPTIMIZATION (true)
|
||||
#define K4A_DEPTH_MODE_TO_STRING_CASE(depth_mode) \
|
||||
case depth_mode: \
|
||||
return #depth_mode
|
||||
#define K4A_COLOR_RESOLUTION_TO_STRING_CASE(color_resolution) \
|
||||
case color_resolution: \
|
||||
return #color_resolution
|
||||
#define K4A_IMAGE_FORMAT_TO_STRING_CASE(image_format) \
|
||||
case image_format: \
|
||||
return #image_format
|
||||
#define K4A_FPS_TO_STRING_CASE(fps) \
|
||||
case fps: \
|
||||
return #fps
|
||||
|
||||
uint32_t k4a_device_get_installed_count(void)
|
||||
{
|
||||
|
@ -69,6 +81,11 @@ k4a_result_t k4a_set_debug_message_handler(k4a_logging_message_cb_t *message_cb,
|
|||
return logger_register_message_callback(message_cb, message_cb_context, min_level);
|
||||
}
|
||||
|
||||
k4a_result_t k4a_set_allocator(k4a_memory_allocate_cb_t allocate, k4a_memory_destroy_cb_t free)
|
||||
{
|
||||
return allocator_set_allocator(allocate, free);
|
||||
}
|
||||
|
||||
depth_cb_streaming_capture_t depth_capture_ready;
|
||||
color_cb_streaming_capture_t color_capture_ready;
|
||||
|
||||
|
@ -258,7 +275,6 @@ void k4a_device_close(k4a_device_t device_handle)
|
|||
}
|
||||
k4a_device_t_destroy(device_handle);
|
||||
allocator_deinitialize();
|
||||
assert(allocator_test_for_leaks() == 0);
|
||||
}
|
||||
|
||||
k4a_wait_result_t k4a_device_get_capture(k4a_device_t device_handle,
|
||||
|
@ -391,7 +407,7 @@ k4a_result_t k4a_image_create(k4a_image_format_t format,
|
|||
int stride_bytes,
|
||||
k4a_image_t *image_handle)
|
||||
{
|
||||
return image_create(format, width_pixels, height_pixels, stride_bytes, image_handle);
|
||||
return image_create(format, width_pixels, height_pixels, stride_bytes, ALLOCATION_SOURCE_USER, image_handle);
|
||||
}
|
||||
|
||||
k4a_result_t k4a_image_create_from_buffer(k4a_image_format_t format,
|
||||
|
@ -522,175 +538,319 @@ void k4a_image_release(k4a_image_t image_handle)
|
|||
image_dec_ref(image_handle);
|
||||
}
|
||||
|
||||
static const char *k4a_depth_mode_to_string(k4a_depth_mode_t depth_mode)
|
||||
{
|
||||
switch (depth_mode)
|
||||
{
|
||||
K4A_DEPTH_MODE_TO_STRING_CASE(K4A_DEPTH_MODE_OFF);
|
||||
K4A_DEPTH_MODE_TO_STRING_CASE(K4A_DEPTH_MODE_NFOV_2X2BINNED);
|
||||
K4A_DEPTH_MODE_TO_STRING_CASE(K4A_DEPTH_MODE_NFOV_UNBINNED);
|
||||
K4A_DEPTH_MODE_TO_STRING_CASE(K4A_DEPTH_MODE_WFOV_2X2BINNED);
|
||||
K4A_DEPTH_MODE_TO_STRING_CASE(K4A_DEPTH_MODE_WFOV_UNBINNED);
|
||||
K4A_DEPTH_MODE_TO_STRING_CASE(K4A_DEPTH_MODE_PASSIVE_IR);
|
||||
}
|
||||
return "Unexpected k4a_depth_mode_t value.";
|
||||
}
|
||||
|
||||
static const char *k4a_color_resolution_to_string(k4a_color_resolution_t resolution)
|
||||
{
|
||||
switch (resolution)
|
||||
{
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_OFF);
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_720P);
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_1080P);
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_1440P);
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_1536P);
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_2160P);
|
||||
K4A_COLOR_RESOLUTION_TO_STRING_CASE(K4A_COLOR_RESOLUTION_3072P);
|
||||
}
|
||||
return "Unexpected k4a_color_resolution_t value.";
|
||||
}
|
||||
|
||||
static const char *k4a_image_format_to_string(k4a_image_format_t image_format)
|
||||
{
|
||||
switch (image_format)
|
||||
{
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_COLOR_MJPG);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_COLOR_NV12);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_COLOR_YUY2);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_COLOR_BGRA32);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_DEPTH16);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_IR16);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_MONO8);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_MONO16);
|
||||
K4A_IMAGE_FORMAT_TO_STRING_CASE(K4A_IMAGE_FORMAT_CUSTOM);
|
||||
}
|
||||
return "Unexpected k4a_image_format_t value.";
|
||||
}
|
||||
|
||||
static const char *k4a_fps_to_string(k4a_fps_t fps)
|
||||
{
|
||||
switch (fps)
|
||||
{
|
||||
K4A_FPS_TO_STRING_CASE(K4A_FRAMES_PER_SECOND_5);
|
||||
K4A_FPS_TO_STRING_CASE(K4A_FRAMES_PER_SECOND_15);
|
||||
K4A_FPS_TO_STRING_CASE(K4A_FRAMES_PER_SECOND_30);
|
||||
}
|
||||
return "Unexpected k4a_fps_t value.";
|
||||
}
|
||||
|
||||
static k4a_result_t validate_configuration(k4a_context_t *device, const k4a_device_configuration_t *config)
|
||||
{
|
||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, config == NULL);
|
||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, device == NULL);
|
||||
k4a_result_t result;
|
||||
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
||||
bool depth_enabled = false;
|
||||
bool color_enabled = false;
|
||||
|
||||
result = K4A_RESULT_FROM_BOOL(
|
||||
config->color_format == K4A_IMAGE_FORMAT_COLOR_MJPG || config->color_format == K4A_IMAGE_FORMAT_COLOR_YUY2 ||
|
||||
config->color_format == K4A_IMAGE_FORMAT_COLOR_NV12 || config->color_format == K4A_IMAGE_FORMAT_COLOR_BGRA32);
|
||||
if (K4A_SUCCEEDED(result))
|
||||
if (config->color_format != K4A_IMAGE_FORMAT_COLOR_MJPG && config->color_format != K4A_IMAGE_FORMAT_COLOR_YUY2 &&
|
||||
config->color_format != K4A_IMAGE_FORMAT_COLOR_NV12 && config->color_format != K4A_IMAGE_FORMAT_COLOR_BGRA32)
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->color_resolution >= K4A_COLOR_RESOLUTION_OFF &&
|
||||
config->color_resolution <= K4A_COLOR_RESOLUTION_3072P);
|
||||
}
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->depth_mode >= K4A_DEPTH_MODE_OFF &&
|
||||
config->depth_mode <= K4A_DEPTH_MODE_PASSIVE_IR);
|
||||
}
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->camera_fps == K4A_FRAMES_PER_SECOND_5 ||
|
||||
config->camera_fps == K4A_FRAMES_PER_SECOND_15 ||
|
||||
config->camera_fps == K4A_FRAMES_PER_SECOND_30);
|
||||
}
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->wired_sync_mode >= K4A_WIRED_SYNC_MODE_STANDALONE &&
|
||||
config->wired_sync_mode <= K4A_WIRED_SYNC_MODE_SUBORDINATE);
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured color_format is not a valid k4a_color_format_t value.", 0);
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result) && (config->wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE ||
|
||||
config->wired_sync_mode == K4A_WIRED_SYNC_MODE_MASTER))
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
bool sync_in_cable_present;
|
||||
bool sync_out_cable_present;
|
||||
result = TRACE_CALL(
|
||||
colormcu_get_external_sync_jack_state(device->colormcu, &sync_in_cable_present, &sync_out_cable_present));
|
||||
|
||||
if (K4A_SUCCEEDED(result) && config->wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE)
|
||||
if (config->color_resolution < K4A_COLOR_RESOLUTION_OFF ||
|
||||
config->color_resolution > K4A_COLOR_RESOLUTION_3072P)
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(sync_in_cable_present == true);
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured color_resolution is not a valid k4a_color_resolution_t value.", 0);
|
||||
}
|
||||
if (K4A_SUCCEEDED(result) && config->wired_sync_mode == K4A_WIRED_SYNC_MODE_MASTER)
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(sync_out_cable_present == true);
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result) && config->color_resolution == K4A_COLOR_RESOLUTION_OFF)
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (config->depth_mode < K4A_DEPTH_MODE_OFF || config->depth_mode > K4A_DEPTH_MODE_PASSIVE_IR)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured depth_mode is not a valid k4a_depth_mode_t value.", 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (config->camera_fps != K4A_FRAMES_PER_SECOND_5 && config->camera_fps != K4A_FRAMES_PER_SECOND_15 &&
|
||||
config->camera_fps != K4A_FRAMES_PER_SECOND_30)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured camera_fps is not a valid k4a_fps_t value.", 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (config->wired_sync_mode < K4A_WIRED_SYNC_MODE_STANDALONE ||
|
||||
config->wired_sync_mode > K4A_WIRED_SYNC_MODE_SUBORDINATE)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured wired_sync_mode is not a valid k4a_wired_sync_mode_t value.", 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (config->wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE ||
|
||||
config->wired_sync_mode == K4A_WIRED_SYNC_MODE_MASTER)
|
||||
{
|
||||
bool sync_in_cable_present;
|
||||
bool sync_out_cable_present;
|
||||
|
||||
result = colormcu_get_external_sync_jack_state(device->colormcu,
|
||||
&sync_in_cable_present,
|
||||
&sync_out_cable_present);
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
LOG_ERROR("Device wired_sync_mode is set to K4A_WIRED_SYNC_MODE_MASTER, so color camera must be used "
|
||||
"on master device. Color_resolution can not be set to K4A_COLOR_RESOLUTION_OFF.",
|
||||
0);
|
||||
return K4A_RESULT_FAILED;
|
||||
if (config->wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE && !sync_in_cable_present)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("Failure to detect presence of sync in cable with wired sync mode "
|
||||
"K4A_WIRED_SYNC_MODE_SUBORDINATE.",
|
||||
0);
|
||||
}
|
||||
|
||||
if (config->wired_sync_mode == K4A_WIRED_SYNC_MODE_MASTER)
|
||||
{
|
||||
if (!sync_out_cable_present)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("Failure to detect presence of sync out cable with wired sync mode "
|
||||
"K4A_WIRED_SYNC_MODE_MASTER.",
|
||||
0);
|
||||
}
|
||||
|
||||
if (config->color_resolution == K4A_COLOR_RESOLUTION_OFF)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR(
|
||||
"Device wired_sync_mode is set to K4A_WIRED_SYNC_MODE_MASTER, so color camera must be used "
|
||||
"on master device. Color_resolution can not be set to K4A_COLOR_RESOLUTION_OFF.",
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result) && config->subordinate_delay_off_master_usec != 0)
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (config->wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE)
|
||||
if (config->wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE &&
|
||||
config->subordinate_delay_off_master_usec != 0)
|
||||
{
|
||||
uint32_t fps_in_usec = 1000000 / k4a_convert_fps_to_uint(config->camera_fps);
|
||||
result = K4A_RESULT_FROM_BOOL(config->subordinate_delay_off_master_usec < fps_in_usec);
|
||||
if (config->subordinate_delay_off_master_usec > fps_in_usec)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR(
|
||||
"The configured subordinate device delay from the master device cannot exceed one frame interval.",
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
if (config->wired_sync_mode != K4A_WIRED_SYNC_MODE_SUBORDINATE &&
|
||||
config->subordinate_delay_off_master_usec != 0)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("When wired_sync_mode is K4A_WIRED_SYNC_MODE_STANDALONE or K4A_WIRED_SYNC_MODE_MASTER, the "
|
||||
"subordinate_delay_off_master_usec must be 0.",
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (config->depth_mode != K4A_DEPTH_MODE_OFF)
|
||||
{
|
||||
depth_enabled = true;
|
||||
}
|
||||
|
||||
if (config->color_resolution != K4A_COLOR_RESOLUTION_OFF)
|
||||
{
|
||||
color_enabled = true;
|
||||
}
|
||||
|
||||
if (depth_enabled && color_enabled)
|
||||
{
|
||||
int64_t fps = 1000000 / k4a_convert_fps_to_uint(config->camera_fps);
|
||||
if (config->depth_delay_off_color_usec < -fps || config->depth_delay_off_color_usec > fps)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured depth_delay_off_color_usec must be within +/- one frame interval.", 0);
|
||||
}
|
||||
}
|
||||
else if (!depth_enabled && !color_enabled)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("Neither depth camera nor color camera are enabled in the configuration, at least one needs to "
|
||||
"be enabled.",
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->subordinate_delay_off_master_usec == 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (config->depth_mode != K4A_DEPTH_MODE_OFF)
|
||||
{
|
||||
depth_enabled = true;
|
||||
}
|
||||
|
||||
if (config->color_resolution != K4A_COLOR_RESOLUTION_OFF)
|
||||
{
|
||||
color_enabled = true;
|
||||
}
|
||||
|
||||
if (depth_enabled && color_enabled)
|
||||
{
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
int64_t fps = 1000000 / k4a_convert_fps_to_uint(config->camera_fps);
|
||||
result = K4A_RESULT_FROM_BOOL((config->depth_delay_off_color_usec >= -fps) &&
|
||||
(config->depth_delay_off_color_usec <= fps));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->depth_delay_off_color_usec == 0);
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
result = K4A_RESULT_FROM_BOOL(config->synchronized_images_only == false);
|
||||
}
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result) && depth_enabled)
|
||||
{
|
||||
struct _depth_configuration
|
||||
{
|
||||
k4a_depth_mode_t mode;
|
||||
k4a_fps_t max_fps;
|
||||
} supported_depth_configs[] = {
|
||||
{ K4A_DEPTH_MODE_NFOV_2X2BINNED, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_DEPTH_MODE_NFOV_UNBINNED, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_DEPTH_MODE_WFOV_2X2BINNED, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_DEPTH_MODE_WFOV_UNBINNED, K4A_FRAMES_PER_SECOND_15 },
|
||||
{ K4A_DEPTH_MODE_PASSIVE_IR, K4A_FRAMES_PER_SECOND_30 },
|
||||
};
|
||||
|
||||
bool depth_fps_and_mode_supported = false;
|
||||
for (unsigned int x = 0; x < COUNTOF(supported_depth_configs); x++)
|
||||
{
|
||||
if (supported_depth_configs[x].mode == config->depth_mode &&
|
||||
supported_depth_configs[x].max_fps >= config->camera_fps)
|
||||
if (config->depth_delay_off_color_usec != 0)
|
||||
{
|
||||
depth_fps_and_mode_supported = true;
|
||||
break;
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("If depth_delay_off_color_usec is not 0, both depth camera and color camera must be enabled.",
|
||||
0);
|
||||
}
|
||||
|
||||
if (config->synchronized_images_only)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR(
|
||||
"To enable synchronized_images_only, both depth camera and color camera must also be enabled.", 0);
|
||||
}
|
||||
}
|
||||
result = K4A_RESULT_FROM_BOOL(depth_fps_and_mode_supported == true);
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result) && color_enabled)
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
struct _color_configuration
|
||||
if (depth_enabled)
|
||||
{
|
||||
k4a_color_resolution_t res;
|
||||
k4a_image_format_t format;
|
||||
k4a_fps_t max_fps;
|
||||
} supported_color_configs[] = {
|
||||
{ K4A_COLOR_RESOLUTION_2160P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1440P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1080P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_YUY2, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_NV12, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_3072P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_15 },
|
||||
{ K4A_COLOR_RESOLUTION_1536P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_2160P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1440P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1080P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_3072P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_15 },
|
||||
{ K4A_COLOR_RESOLUTION_1536P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
};
|
||||
|
||||
bool color_fps_and_res_and_format_supported = false;
|
||||
for (unsigned int x = 0; x < COUNTOF(supported_color_configs); x++)
|
||||
{
|
||||
if (supported_color_configs[x].res == config->color_resolution &&
|
||||
supported_color_configs[x].max_fps >= config->camera_fps &&
|
||||
supported_color_configs[x].format == config->color_format)
|
||||
struct _depth_configuration
|
||||
{
|
||||
color_fps_and_res_and_format_supported = true;
|
||||
break;
|
||||
k4a_depth_mode_t mode;
|
||||
k4a_fps_t max_fps;
|
||||
} supported_depth_configs[] = {
|
||||
{ K4A_DEPTH_MODE_NFOV_2X2BINNED, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_DEPTH_MODE_NFOV_UNBINNED, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_DEPTH_MODE_WFOV_2X2BINNED, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_DEPTH_MODE_WFOV_UNBINNED, K4A_FRAMES_PER_SECOND_15 },
|
||||
{ K4A_DEPTH_MODE_PASSIVE_IR, K4A_FRAMES_PER_SECOND_30 },
|
||||
};
|
||||
|
||||
bool depth_fps_and_mode_supported = false;
|
||||
for (unsigned int x = 0; x < COUNTOF(supported_depth_configs); x++)
|
||||
{
|
||||
if (supported_depth_configs[x].mode == config->depth_mode &&
|
||||
supported_depth_configs[x].max_fps >= config->camera_fps)
|
||||
{
|
||||
depth_fps_and_mode_supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!depth_fps_and_mode_supported)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The configured depth_mode %s does not support the configured camera_fps %s.",
|
||||
k4a_depth_mode_to_string(config->depth_mode),
|
||||
k4a_fps_to_string(config->camera_fps));
|
||||
}
|
||||
}
|
||||
result = K4A_RESULT_FROM_BOOL(color_fps_and_res_and_format_supported == true);
|
||||
}
|
||||
|
||||
if (K4A_SUCCEEDED(result))
|
||||
{
|
||||
if (color_enabled)
|
||||
{
|
||||
struct _color_configuration
|
||||
{
|
||||
k4a_color_resolution_t res;
|
||||
k4a_image_format_t format;
|
||||
k4a_fps_t max_fps;
|
||||
} supported_color_configs[] = {
|
||||
{ K4A_COLOR_RESOLUTION_2160P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1440P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1080P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_YUY2, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_NV12, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_3072P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_15 },
|
||||
{ K4A_COLOR_RESOLUTION_1536P, K4A_IMAGE_FORMAT_COLOR_MJPG, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_2160P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1440P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_1080P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_720P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
{ K4A_COLOR_RESOLUTION_3072P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_15 },
|
||||
{ K4A_COLOR_RESOLUTION_1536P, K4A_IMAGE_FORMAT_COLOR_BGRA32, K4A_FRAMES_PER_SECOND_30 },
|
||||
};
|
||||
|
||||
bool color_fps_and_res_and_format_supported = false;
|
||||
for (unsigned int x = 0; x < COUNTOF(supported_color_configs); x++)
|
||||
{
|
||||
if (supported_color_configs[x].res == config->color_resolution &&
|
||||
supported_color_configs[x].max_fps >= config->camera_fps &&
|
||||
supported_color_configs[x].format == config->color_format)
|
||||
{
|
||||
color_fps_and_res_and_format_supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!color_fps_and_res_and_format_supported)
|
||||
{
|
||||
result = K4A_RESULT_FAILED;
|
||||
LOG_ERROR("The combination of color_resolution at %s, color_format at %s, and camera_fps at %s is not "
|
||||
"supported.",
|
||||
k4a_color_resolution_to_string(config->color_resolution),
|
||||
k4a_image_format_to_string(config->color_format),
|
||||
k4a_fps_to_string(config->camera_fps));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -319,7 +319,12 @@ TEST_F(transformation_ut, transformation_depth_image_to_point_cloud)
|
|||
int width = m_calibration.depth_camera_calibration.resolution_width;
|
||||
int height = m_calibration.depth_camera_calibration.resolution_height;
|
||||
k4a_image_t depth_image = NULL;
|
||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16, width, height, width * (int)sizeof(uint16_t), &depth_image),
|
||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
|
||||
width,
|
||||
height,
|
||||
width * (int)sizeof(uint16_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&depth_image),
|
||||
K4A_RESULT_SUCCEEDED);
|
||||
ASSERT_NE(depth_image, (k4a_image_t)NULL);
|
||||
k4a_transformation_image_descriptor_t depth_image_descriptor = image_get_descriptor(depth_image);
|
||||
|
@ -331,7 +336,12 @@ TEST_F(transformation_ut, transformation_depth_image_to_point_cloud)
|
|||
}
|
||||
|
||||
k4a_image_t xyz_image = NULL;
|
||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM, width, height, width * 3 * (int)sizeof(int16_t), &xyz_image),
|
||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM,
|
||||
width,
|
||||
height,
|
||||
width * 3 * (int)sizeof(int16_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&xyz_image),
|
||||
K4A_RESULT_SUCCEEDED);
|
||||
ASSERT_NE(xyz_image, (k4a_image_t)NULL);
|
||||
k4a_transformation_image_descriptor_t xyz_image_descriptor = image_get_descriptor(xyz_image);
|
||||
|
@ -374,6 +384,7 @@ TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
|||
depth_image_width_pixels,
|
||||
depth_image_height_pixels,
|
||||
depth_image_width_pixels * 1 * (int)sizeof(uint16_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&depth_image),
|
||||
K4A_WAIT_RESULT_SUCCEEDED);
|
||||
|
||||
|
@ -400,6 +411,7 @@ TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
|||
color_image_width_pixels,
|
||||
color_image_height_pixels,
|
||||
color_image_width_pixels * 4 * (int)sizeof(uint8_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&color_image),
|
||||
K4A_WAIT_RESULT_SUCCEEDED);
|
||||
|
||||
|
@ -408,6 +420,7 @@ TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
|||
depth_image_width_pixels,
|
||||
depth_image_height_pixels,
|
||||
depth_image_width_pixels * 4 * (int)sizeof(uint8_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&transformed_color_image),
|
||||
K4A_WAIT_RESULT_SUCCEEDED);
|
||||
|
||||
|
@ -416,6 +429,7 @@ TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
|||
color_image_width_pixels,
|
||||
color_image_height_pixels,
|
||||
color_image_width_pixels * 1 * (int)sizeof(uint16_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&transformed_depth_image),
|
||||
K4A_WAIT_RESULT_SUCCEEDED);
|
||||
|
||||
|
@ -440,6 +454,7 @@ TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
|||
depth_image_width_pixels,
|
||||
depth_image_height_pixels,
|
||||
depth_image_width_pixels * 3 * (int)sizeof(int16_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&xyz_depth_image),
|
||||
K4A_RESULT_SUCCEEDED);
|
||||
|
||||
|
@ -448,6 +463,7 @@ TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
|||
color_image_width_pixels,
|
||||
color_image_height_pixels,
|
||||
color_image_width_pixels * 3 * (int)sizeof(int16_t),
|
||||
ALLOCATION_SOURCE_USER,
|
||||
&xyz_color_image),
|
||||
K4A_RESULT_SUCCEEDED);
|
||||
|
||||
|
|
|
@ -150,12 +150,18 @@ TEST(allocator_ut, allocator_api_validation)
|
|||
ASSERT_EQ(allocator_test_for_leaks(), 0);
|
||||
}
|
||||
|
||||
static void image_free_function(void *buffer, void *context)
|
||||
{
|
||||
(void)context;
|
||||
allocator_free(buffer);
|
||||
}
|
||||
|
||||
TEST(allocator_ut, image_api_validation)
|
||||
{
|
||||
uint8_t *buffer = NULL;
|
||||
k4a_image_t image = NULL;
|
||||
const int IMAGE_SIZE = 128;
|
||||
void *context;
|
||||
void *context = NULL;
|
||||
|
||||
{
|
||||
allocation_source_t source_depth = ALLOCATION_SOURCE_DEPTH;
|
||||
|
@ -176,42 +182,52 @@ TEST(allocator_ut, image_api_validation)
|
|||
{
|
||||
// Unsupported formats for this creation API.
|
||||
k4a_image_format_t bad_format = (k4a_image_format_t)99;
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 1, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 0, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 0, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 10, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 10, 0, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 10, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 1, ALLOCATION_SOURCE_USER, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 0, ALLOCATION_SOURCE_USER, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create(bad_format, 0, 0, 0, ALLOCATION_SOURCE_USER, NULL));
|
||||
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, &image));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||
image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, ALLOCATION_SOURCE_USER, &image));
|
||||
image_dec_ref(image);
|
||||
ASSERT_EQ(allocator_test_for_leaks(), 0);
|
||||
}
|
||||
|
@ -220,87 +236,94 @@ TEST(allocator_ut, image_api_validation)
|
|||
k4a_image_format_t bad_format = (k4a_image_format_t)99;
|
||||
|
||||
// clang-format off
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
|
||||
//ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 1, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 0, buffer, IMAGE_SIZE, allocator_free, context, NULL));
|
||||
//ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 0, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 10, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 10, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 1, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(bad_format, 0, 0, 0, buffer, IMAGE_SIZE, image_free_function, context, NULL));
|
||||
|
||||
// No buffer
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, NULL, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, NULL, IMAGE_SIZE, image_free_function, context, &image));
|
||||
// bad size
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, 0, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_FAILED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, 0, image_free_function, context, &image));
|
||||
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_MJPG, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_MJPG, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_YUY2, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_YUY2, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_BGRA32, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_BGRA32, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_DEPTH16, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_DEPTH16, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_IR16, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_IR16, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_CUSTOM, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_CUSTOM, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
image_dec_ref(image);
|
||||
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_NE((uint8_t*)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_MJPG, 10, 10, 1, buffer, IMAGE_SIZE, NULL, NULL, &image));
|
||||
image_dec_ref(image);
|
||||
allocator_free(buffer, context);
|
||||
allocator_free(buffer);
|
||||
ASSERT_EQ(allocator_test_for_leaks(), 0);
|
||||
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||
image_create_from_buffer(
|
||||
K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12,
|
||||
10,
|
||||
10,
|
||||
1,
|
||||
buffer,
|
||||
IMAGE_SIZE,
|
||||
image_free_function,
|
||||
context,
|
||||
&image));
|
||||
|
||||
ASSERT_EQ((uint8_t *)NULL, image_get_buffer(NULL));
|
||||
ASSERT_EQ(buffer, image_get_buffer(image));
|
||||
|
@ -359,10 +382,17 @@ TEST(allocator_ut, image_api_validation)
|
|||
}
|
||||
|
||||
{
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||
image_create_from_buffer(
|
||||
K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12,
|
||||
10,
|
||||
10,
|
||||
1,
|
||||
buffer,
|
||||
IMAGE_SIZE,
|
||||
image_free_function,
|
||||
context,
|
||||
&image));
|
||||
|
||||
image_inc_ref(NULL);
|
||||
image_inc_ref(NULL);
|
||||
|
@ -392,10 +422,17 @@ TEST(allocator_ut, image_api_validation)
|
|||
ASSERT_EQ(0, image_get_system_timestamp_nsec(nullptr));
|
||||
image_set_system_timestamp_nsec(nullptr, 0);
|
||||
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||
image_create_from_buffer(
|
||||
K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
image_create_from_buffer(K4A_IMAGE_FORMAT_COLOR_NV12,
|
||||
10,
|
||||
10,
|
||||
1,
|
||||
buffer,
|
||||
IMAGE_SIZE,
|
||||
image_free_function,
|
||||
context,
|
||||
&image));
|
||||
|
||||
ASSERT_EQ(0, image_get_system_timestamp_nsec(image));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_apply_system_timestamp(image));
|
||||
|
@ -418,13 +455,13 @@ TEST(allocator_ut, manual_image_system_time)
|
|||
uint8_t *buffer = NULL;
|
||||
k4a_image_t image;
|
||||
const int IMAGE_SIZE = 128;
|
||||
void *context;
|
||||
void *context = NULL;
|
||||
uint64_t ts_last;
|
||||
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE, &context));
|
||||
ASSERT_NE((uint8_t *)NULL, buffer = allocator_alloc(ALLOCATION_SOURCE_USER, IMAGE_SIZE));
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||
image_create_from_buffer(
|
||||
K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, allocator_free, context, &image));
|
||||
K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, buffer, IMAGE_SIZE, image_free_function, context, &image));
|
||||
|
||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, image_apply_system_timestamp(image));
|
||||
ASSERT_NE((ts_last = image_get_system_timestamp_nsec(image)), 0);
|
||||
|
|
Загрузка…
Ссылка в новой задаче