Commit 508ebfd0 authored by Éric Thiébaut's avatar Éric Thiébaut
Browse files

Add common functions for preprocessing/conversion of raw images

parent e76ab981
......@@ -72,8 +72,9 @@ libtao_la_SOURCES = \
errors.c \
locks.c \
logmsg.c \
options.c \
mirrors.c \
options.c \
pixels.c \
semaphores.c \
sharedarrays.c \
sharedobjects.c \
......
......@@ -1143,43 +1143,3 @@ tao_shmid_t tao_get_next_image_shmid(
#define NAME shared_camera
#define MAGIC TAO_SHARED_CAMERA
#include "./sharedgeneric.c"
#define ENCODE(FUNC, INP, OUT) \
void FUNC( \
OUT* restrict dat, \
OUT* restrict wgt, \
const INP* restrict raw, \
long n, \
const OUT* restrict a, \
const OUT* restrict b, \
const OUT* restrict q, \
const OUT* restrict r) \
{ \
const OUT zero = 0; \
const OUT one = 1; \
if (wgt != NULL) { \
if (q != NULL && r != NULL) { \
for (long i = 0; i < n; ++i) { \
OUT x = ((OUT)raw[i] - b[i])*a[i]; \
dat[i] = x; \
wgt[i] = q[i]/((x > zero ? x : zero) + r[i]); \
} \
} else { \
for (long i = 0; i < n; ++i) { \
dat[i] = ((OUT)raw[i] - b[i])*a[i]; \
wgt[i] = one; \
} \
} \
} else { \
for (long i = 0; i < n; ++i) { \
dat[i] = ((OUT)raw[i] - b[i])*a[i]; \
} \
} \
}
ENCODE(tao_preprocess_image_u8_to_flt, uint8_t, float)
ENCODE(tao_preprocess_image_u8_to_dbl, uint8_t, double)
ENCODE(tao_preprocess_image_u16_to_flt, uint16_t, float)
ENCODE(tao_preprocess_image_u16_to_dbl, uint16_t, double)
#undef ENCODE
// pixels.c --
//
// Conversion and preprocessing of raw image pixels in TAO.
//
//-----------------------------------------------------------------------------
//
// This file if part of TAO real-time software licensed under the MIT license
// (https://git-cral.univ-lyon1.fr/tao/tao-rt).
//
// Copyright (C) 2018-2021, Éric Thiébaut.
#ifndef PIXELS_C_
#define PIXELS_C_ 1
#include "common.h"
#include <math.h>
#include <errno.h>
#include <tao-shared.h>
#include <tao-cameras.h>
#include <tao-private.h>
// Optimizer and loop vectorization make the following macros efficient.
#define raw_(x,y) raw[(x) + (y)*stride]
#define arr_(x,y) arr[(x) + (y)*width]
#define dat_(x,y) dat[(x) + (y)*width]
#define wgt_(x,y) wgt[(x) + (y)*width]
#define a_(x,y) a[(x) + (y)*width]
#define b_(x,y) b[(x) + (y)*width]
#define q_(x,y) q[(x) + (y)*width]
#define r_(x,y) r[(x) + (y)*width]
static inline float max_flt(float a, float b) { return a >= b ? a : b; }
static inline double max_dbl(double a, double b) { return a >= b ? a : b; }
#define max_(a, b) \
_Generic((a) + (b), \
float: max_flt, \
double: max_dbl)((a), (b))
// FIXME: faster with uint32_t in some cases?
typedef uint16_t unpacked_t;
static inline unpacked_t unpack_p12_lo(
unpacked_t b0,
unpacked_t b1)
{
return (b0 << 4) | (b1 & (unpacked_t)0x000F);
}
static inline unpacked_t unpack_p12_hi(
unpacked_t b1,
unpacked_t b2)
{
return (b2 << 4) | (b1 >> 4);
}
#ifdef PACKED_PIXELS
#error PACKED_PIXELS must not be defined
#endif
#define INP uint8_t
#define OUT uint8_t
#define CONVERT_FUNC tao_convert_u8_pixels_to_u8
#include __FILE__
#define OUT uint16_t
#define CONVERT_FUNC tao_convert_u8_pixels_to_u16
#include __FILE__
#define OUT uint32_t
#define CONVERT_FUNC tao_convert_u8_pixels_to_u32
#include __FILE__
#define OUT float
#define CONVERT_FUNC tao_convert_u8_pixels_to_flt
#define PREPROCESS_FUNC tao_preprocess_u8_pixels_to_flt
#include __FILE__
#define OUT double
#define CONVERT_FUNC tao_convert_u8_pixels_to_dbl
#define PREPROCESS_FUNC tao_preprocess_u8_pixels_to_dbl
#include __FILE__
#undef INP
#define INP uint16_t
#define OUT uint16_t
#define CONVERT_FUNC tao_convert_u16_pixels_to_u16
#include __FILE__
#define OUT uint32_t
#define CONVERT_FUNC tao_convert_u16_pixels_to_u32
#include __FILE__
#define OUT float
#define CONVERT_FUNC tao_convert_u16_pixels_to_flt
#define PREPROCESS_FUNC tao_preprocess_u16_pixels_to_flt
#include __FILE__
#define OUT double
#define CONVERT_FUNC tao_convert_u16_pixels_to_dbl
#define PREPROCESS_FUNC tao_preprocess_u16_pixels_to_dbl
#include __FILE__
#undef INP
#define INP uint32_t
#define OUT uint32_t
#define CONVERT_FUNC tao_convert_u32_pixels_to_u32
#include __FILE__
#define OUT float
#define CONVERT_FUNC tao_convert_u32_pixels_to_flt
#define PREPROCESS_FUNC tao_preprocess_u32_pixels_to_flt
#include __FILE__
#define OUT double
#define CONVERT_FUNC tao_convert_u32_pixels_to_dbl
#define PREPROCESS_FUNC tao_preprocess_u32_pixels_to_dbl
#include __FILE__
#undef INP
#define PACKED_PIXELS 12
#define INP uint8_t // buffer of bytes for packed pixels
#define OUT uint16_t
#define CONVERT_FUNC tao_convert_p12_pixels_to_u16
#include __FILE__
#define OUT uint32_t
#define CONVERT_FUNC tao_convert_p12_pixels_to_u32
#include __FILE__
#define OUT float
#define CONVERT_FUNC tao_convert_p12_pixels_to_flt
#define PREPROCESS_FUNC tao_preprocess_p12_pixels_to_flt
#include __FILE__
#define OUT double
#define CONVERT_FUNC tao_convert_p12_pixels_to_dbl
#define PREPROCESS_FUNC tao_preprocess_p12_pixels_to_dbl
#include __FILE__
#undef INP
#undef PACKED_PIXELS
#include __FILE__
#else // PIXELS_C_
#ifdef CONVERT_FUNC
tao_status_t CONVERT_FUNC(
OUT* restrict arr,
const INP* restrict raw,
long width,
long height,
long bytes_per_line)
{
if (arr == NULL || raw == NULL) {
tao_push_error(__func__, TAO_BAD_ADDRESS);
return TAO_ERROR;
}
if (width < 0 || height < 0 || bytes_per_line%sizeof(INP) != 0) {
tao_push_error(__func__, TAO_BAD_SIZE);
return TAO_ERROR;
}
#ifndef PACKED_PIXELS
long stride = bytes_per_line/sizeof(INP);
for (long y = 0; y < height; ++y) {
for (long x = 0; x < width; ++x) {
arr_(x,y) = (OUT)raw_(x,y);
}
}
#elif PACKED_PIXELS == 12
long xm = width - 1;
bool isodd = ((width&1) != 0);
for (long y = 0; y < height; ++y) {
const uint8_t* restrict ptr = raw + bytes_per_line*y;
for (long x = 0; x < xm; x += 2) {
unpacked_t b0 = ptr[0];
unpacked_t b1 = ptr[1];
unpacked_t b2 = ptr[2];
ptr += 3;
arr_(x,y) = unpack_p12_lo(b0, b1);
arr_(x+1,y) = unpack_p12_hi(b1, b2);
}
if (isodd) {
unpacked_t b0 = ptr[0];
unpacked_t b1 = ptr[1];
arr_(xm,y) = unpack_p12_lo(b0, b1);
}
}
#else
# error unexpected PACKED_PIXELS value
#endif // PACKED_PIXELS
return TAO_OK;
}
#endif // CONVERTS_FUNC
#ifdef PREPROCESS_FUNC
tao_status_t PREPROCESS_FUNC(
OUT* restrict dat,
OUT* restrict wgt,
const INP* restrict raw,
long width,
long height,
long bytes_per_line,
const OUT* restrict a,
const OUT* restrict b,
const OUT* restrict q,
const OUT* restrict r)
{
if (dat == NULL || raw == NULL || a == NULL || b == NULL) {
tao_push_error(__func__, TAO_BAD_ADDRESS);
return TAO_ERROR;
}
if (width < 0 || height < 0 || bytes_per_line%sizeof(INP) != 0) {
tao_push_error(__func__, TAO_BAD_SIZE);
return TAO_ERROR;
}
#ifndef PACKED_PIXELS
long stride = bytes_per_line/sizeof(INP);
for (long y = 0; y < height; ++y) {
for (long x = 0; x < width; ++x) {
dat_(x,y) = ((OUT)raw_(x,y) - b_(x,y))*a_(x,y);
}
if (wgt != NULL) {
if (q != NULL && r != NULL) {
for (long x = 0; x < width; ++x) {
wgt_(x,y) = q_(x,y)/max_(r_(x,y), r_(x,y) + dat_(x,y));
}
} else {
for (long x = 0; x < width; ++x) {
wgt_(x,y) = (OUT)1;
}
}
}
}
#elif PACKED_PIXELS == 12
long xm = width - 1;
bool isodd = ((width&1) != 0);
for (long y = 0; y < height; ++y) {
const uint8_t* restrict ptr = raw + bytes_per_line*y;
for (long x = 0; x < xm; x += 2) {
unpacked_t b0 = ptr[0];
unpacked_t b1 = ptr[1];
unpacked_t b2 = ptr[2];
ptr += 3;
dat_(x,y) = ((OUT)unpack_p12_lo(b0, b1) - b_(x,y))*a_(x,y);
dat_(x+1,y) = ((OUT)unpack_p12_hi(b1, b2) - b_(x+1,y))*a_(x+1,y);
}
if (isodd) {
unpacked_t b0 = ptr[0];
unpacked_t b1 = ptr[1];
dat_(xm,y) = ((OUT)unpack_p12_lo(b0, b1) - b_(xm,y))*a_(xm,y);
}
if (wgt != NULL) {
if (q != NULL && r != NULL) {
for (long x = 0; x < width; ++x) {
wgt_(x,y) = q_(x,y)/max_(r_(x,y), r_(x,y) + dat_(x,y));
}
} else {
for (long x = 0; x < width; ++x) {
wgt_(x,y) = (OUT)1;
}
}
}
}
#else
# error unexpected PACKED_PIXELS value
#endif // PACKED_PIXELS
return TAO_OK;
}
#endif // PREPROCESS_FUNC
#undef CONVERT_FUNC
#undef PREPROCESS_FUNC
#undef OUT
#endif // PIXELS_C_
......@@ -266,7 +266,7 @@ typedef enum _tao_error_code {
TAO_BAD_ENCODING = -15, ///< Bad encoding
TAO_BAD_ESCAPE = -16, ///< Unknown escape sequence
TAO_BAD_EXPOSURETIME = -17, ///< Invalid exposure time
TAO_BAD_FRAMERATE = -18, ///< Invalid acquistion frame rate
TAO_BAD_FRAMERATE = -18, ///< Invalid acquisition frame rate
TAO_BAD_GAIN = -19, ///< Invalid detector gain
TAO_BAD_MAGIC = -20, ///< Invalid magic number
TAO_BAD_NAME = -21, ///< Bad parameter name
......@@ -5723,69 +5723,319 @@ extern tao_status_t tao_unlock_shared_array(
//-----------------------------------------------------------------------------
/**
* @addtogroup RealTimeProcessing
* @addtogroup RealTimeProcessing
*
* @{
*/
/**
* Apply image pre-processing.
* @brief Convert acquired image.
*
* This function converts the pixel values from an acquisition buffer to an
* array. Lines of pixels may be strided in the acquisition buffer but pixels
* are assumed contiguous in the output arrays.
*
* In this function, raw pixels have unsigned 8-bit integer values and output
* arrays have single precision floating-point elements.
*
* @param arr Ouput image array.
* @param raw Input raw image buffer.
* @param width Number of pixels per image line.
* @param height Number of lines in image.
* @param bytes_per_line Number of bytes between successive lines in image
* buffer @a raw.
*
* @return @ref TAO_OK on success: @ref TAO_ERROR in case of failure.
*/
extern tao_status_t tao_convert_u8_pixels_to_u8(
uint8_t* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* output array has unsigned 16-bit integer elements.
*/
extern tao_status_t tao_convert_u8_pixels_to_u16(
uint16_t* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* output array has unsigned 32-bit integer elements.
*/
extern tao_status_t tao_convert_u8_pixels_to_u32(
uint32_t* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* output array has single precision floating-point elements.
*/
extern tao_status_t tao_convert_u8_pixels_to_flt(
float* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* output array has double precision floating-point elements.
*/
extern tao_status_t tao_convert_u8_pixels_to_dbl(
double* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* input buffer has unsigned 16-bit integer pixels and output array has
* unsigned 16-bit integer elements. The @a bytes_per_line argument must be a
* multiple of 2.
*/
extern tao_status_t tao_convert_u16_pixels_to_u16(
uint16_t* restrict arr,
const uint16_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u16_pixels_to_u16 except that
* output array has unsigned 32-bit integer elements.
*/
extern tao_status_t tao_convert_u16_pixels_to_u32(
uint32_t* restrict arr,
const uint16_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* The following formula is applied for each pixel index `i` to compute the
* pixel value and its weight:
* This function is similar to @ref tao_convert_u16_pixels_to_u16 except that
* output array has single precision floating-point elements.
*/
extern tao_status_t tao_convert_u16_pixels_to_flt(
float* restrict arr,
const uint16_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u16_pixels_to_u16 except that
* output array has double precision floating-point elements.
*/
extern tao_status_t tao_convert_u16_pixels_to_dbl(
double* restrict arr,
const uint16_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* input buffer has unsigned 32-bit integer pixels and output array has
* unsigned 32-bit integer elements. The @a bytes_per_line argument must be a
* multiple of 4.
*/
extern tao_status_t tao_convert_u32_pixels_to_u32(
uint32_t* restrict arr,
const uint32_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u32_pixels_to_u32 except that
* output array has single precision floating-point elements.
*/
extern tao_status_t tao_convert_u32_pixels_to_flt(
float* restrict arr,
const uint32_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u32_pixels_to_u32 except that
* output array has double precision floating-point elements.
*/
extern tao_status_t tao_convert_u32_pixels_to_dbl(
double* restrict arr,
const uint32_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_u8_pixels_to_u8 except that
* input buffer has packed unsigned 12-bit integer pixels(that is 3 consecutive
* bytes for 2 pixels as done by some Andor cameras) and output array has
* unsigned 16-bit integer elements. The @a bytes_per_line argument must be a
* multiple of 2.
*/
extern tao_status_t tao_convert_p12_pixels_to_u16(
uint16_t* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_p12_pixels_to_u16 except that
* output array has unsigned 32-bit integer elements.
*/
extern tao_status_t tao_convert_p12_pixels_to_u32(
uint32_t* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_p12_pixels_to_u16 except that
* output array has single precision floating-point elements.
*/
extern tao_status_t tao_convert_p12_pixels_to_flt(
float* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Convert acquired image.
*
* This function is similar to @ref tao_convert_p12_pixels_to_u16 except that
* output array has double precision floating-point elements.
*/
extern tao_status_t tao_convert_p12_pixels_to_dbl(
double* restrict arr,
const uint8_t* restrict raw,
long width,
long height,
long bytes_per_line);
/**
* @brief Apply image pre-processing.
*
* This function performs conversion and pre-processing of the pixel values
* from an acquisition buffer to an array of floating-point values and,
* optionally, of weights. Lines of pixels may be strided in the acquisition
* buffer but pixels are assumed contiguous in the output arrays.
*
* In this function, raw pixels have unsigned 8-bit integer values and output
* arrays have single precision floating-point elements.
*
* The following formula is applied for each pixel at position `(x,y)` to
* compute the preprocessed pixel value and its weight:
*
* ```
* dat[i] = (raw[i] - b[i])*a[i];
* wgt[i] = q[i]/(max(dat[i],0) + r[i]);
* dat[x,y] = (raw[x,y] - b[x,y])*a[x,y];
* wgt[x,y] = q[x,y]/(max(dat[x,y],0) + r[x,y]);
* ```
*
* @param dat Ouput image data.
* @param wgt Ouput image weights.
* @param raw Input raw image data.
* @param n Number of pixels.
* @param a Gain correction term.
* @param b Bias correction term.
* @param q Numerator for weights.
* @param r Denominator for weights.
* @param dat Ouput image array.
* @param wgt Ouput image weights.
* @param raw Input raw image buffer.
* @param width Number of pixels per image line.
* @param height Number of lines in image.
* @param bytes_per_line Number of bytes between successive lines in image
* buffer @a raw.
* @param a Gain correction term.
* @param b Bias correction term.
* @param q Numerator for weights.
* @param r Denominator for weights.
*
* If @a wgt is non `NULL` but @a q or @a r is `NULL`, all weights are set to
* `1`. If @a wgt is `NULL`, no weights are computed.
*
* @return @ref TAO_OK on success: @ref TAO_ERROR in case of failure.
*/
extern void tao_preprocess_image_u8_to_flt(
extern tao_status_t tao_preprocess_u8_pixels_to_flt(
float* restrict dat,
float* restrict wgt,
const uint8_t* restrict raw,
long n,
long width,
long height,
long bytes_per_line,
const float* restrict a,
const float* restrict b,
const float* restrict q,
const float* restrict r);
/**
* Apply image pre-processing.
* @brief Apply image pre-processing.