|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef __EIDSP_IMAGE_PROCESSING__H__ |
|
|
#define __EIDSP_IMAGE_PROCESSING__H__ |
|
|
|
|
|
#include "edge-impulse-sdk/dsp/ei_utils.h" |
|
|
#include "edge-impulse-sdk/porting/ei_classifier_porting.h" |
|
|
#include "edge-impulse-sdk/dsp/returntypes.hpp" |
|
|
#include "edge-impulse-sdk/dsp/image/processing.hpp" |
|
|
|
|
|
namespace ei { namespace image { namespace processing { |
|
|
|
|
|
enum YUV_OPTIONS |
|
|
{ |
|
|
BIG_ENDIAN_ORDER = 1, |
|
|
PAD_4B = 2, |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int yuv422_to_rgb888( |
|
|
unsigned char *rgb_out, |
|
|
unsigned const char *yuv_in, |
|
|
unsigned int in_size_B, |
|
|
YUV_OPTIONS opts) |
|
|
{ |
|
|
|
|
|
|
|
|
#define EI_CLAMP(t) (((t) > 255) ? 255 : (((t) < 0) ? 0 : (t))) |
|
|
|
|
|
|
|
|
#define EI_GET_R_FROM_YUV(y, u, v) ((298 * y + 409 * v + 128) >> 8) |
|
|
#define EI_GET_G_FROM_YUV(y, u, v) ((298 * y - 100 * u - 208 * v + 128) >> 8) |
|
|
#define EI_GET_B_FROM_YUV(y, u, v) ((298 * y + 516 * u + 128) >> 8) |
|
|
|
|
|
unsigned int in_size_pixels = in_size_B / 4; |
|
|
yuv_in += in_size_B - 1; |
|
|
|
|
|
int rgb_end = TEST_BIT_MASK(opts, PAD_4B) ? 2 * in_size_B : (6 * in_size_B) / 4; |
|
|
rgb_out += rgb_end - 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < in_size_pixels; ++i) { |
|
|
int y2 = *yuv_in-- - 16; |
|
|
int v = *yuv_in-- - 128; |
|
|
int y0 = *yuv_in-- - 16; |
|
|
int u0 = *yuv_in-- - 128; |
|
|
|
|
|
if (TEST_BIT_MASK(opts, BIG_ENDIAN_ORDER)) { |
|
|
*rgb_out-- = EI_CLAMP(EI_GET_B_FROM_YUV(y2, u0, v)); |
|
|
*rgb_out-- = EI_CLAMP(EI_GET_G_FROM_YUV(y2, u0, v)); |
|
|
*rgb_out-- = EI_CLAMP(EI_GET_R_FROM_YUV(y2, u0, v)); |
|
|
if (TEST_BIT_MASK(opts, PAD_4B)) { |
|
|
*rgb_out-- = 0; |
|
|
} |
|
|
|
|
|
*rgb_out-- = EI_CLAMP(EI_GET_B_FROM_YUV(y0, u0, v)); |
|
|
*rgb_out-- = EI_CLAMP(EI_GET_G_FROM_YUV(y0, u0, v)); |
|
|
*rgb_out-- = EI_CLAMP(EI_GET_R_FROM_YUV(y0, u0, v)); |
|
|
if (TEST_BIT_MASK(opts, PAD_4B)) { |
|
|
*rgb_out-- = 0; |
|
|
} |
|
|
} |
|
|
else { |
|
|
|
|
|
return EIDSP_NOT_SUPPORTED; |
|
|
} |
|
|
} |
|
|
return EIDSP_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int cropImage( |
|
|
const uint8_t *srcImage, |
|
|
int srcWidth, |
|
|
int srcHeight, |
|
|
int startX, |
|
|
int startY, |
|
|
uint8_t *dstImage, |
|
|
int dstWidth, |
|
|
int dstHeight, |
|
|
int iBpp) |
|
|
{ |
|
|
uint32_t *s32, *d32; |
|
|
int x, y; |
|
|
|
|
|
if (startX < 0 || startX >= srcWidth || startY < 0 || startY >= srcHeight || |
|
|
(startX + dstWidth) > srcWidth || (startY + dstHeight) > srcHeight) { |
|
|
return EIDSP_PARAMETER_INVALID; |
|
|
} |
|
|
if (iBpp != 8 && iBpp != 16) { |
|
|
return EIDSP_PARAMETER_INVALID; |
|
|
} |
|
|
|
|
|
if (iBpp == 8) { |
|
|
const uint8_t *s; |
|
|
uint8_t *d; |
|
|
for (y = 0; y < dstHeight; y++) { |
|
|
s = &srcImage[srcWidth * (y + startY) + startX]; |
|
|
d = &dstImage[(dstWidth * y)]; |
|
|
x = 0; |
|
|
if ((intptr_t)s & 3 || (intptr_t)d & 3) { |
|
|
for (; x < dstWidth; x++) { |
|
|
*d++ = *s++; |
|
|
} |
|
|
} |
|
|
else { |
|
|
|
|
|
s32 = (uint32_t *)s; |
|
|
d32 = (uint32_t *)d; |
|
|
for (; x < dstWidth - 3; x += 4) { |
|
|
*d32++ = *s32++; |
|
|
} |
|
|
|
|
|
s = (uint8_t *)s32; |
|
|
d = (uint8_t *)d32; |
|
|
for (; x < dstWidth; x++) { |
|
|
*d++ = *s++; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else { |
|
|
uint16_t *s, *d; |
|
|
for (y = 0; y < dstHeight; y++) { |
|
|
s = (uint16_t *)&srcImage[2 * srcWidth * (y + startY) + startX * 2]; |
|
|
d = (uint16_t *)&dstImage[(dstWidth * y * 2)]; |
|
|
x = 0; |
|
|
if ((intptr_t)s & 2 || (intptr_t)d & 2) { |
|
|
for (; x < dstWidth; x++) { |
|
|
*d++ = *s++; |
|
|
} |
|
|
} |
|
|
else { |
|
|
|
|
|
s32 = (uint32_t *)s; |
|
|
d32 = (uint32_t *)d; |
|
|
for (; x < dstWidth - 1; x += 2) { |
|
|
*d32++ = *s32++; |
|
|
} |
|
|
|
|
|
s = (uint16_t *)s32; |
|
|
d = (uint16_t *)d32; |
|
|
for (; x < dstWidth; x++) { |
|
|
*d++ = *s++; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return EIDSP_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int crop_image_rgb888_packed( |
|
|
const uint8_t *srcImage, |
|
|
int srcWidth, |
|
|
int srcHeight, |
|
|
int startX, |
|
|
int startY, |
|
|
uint8_t *dstImage, |
|
|
int dstWidth, |
|
|
int dstHeight) |
|
|
{ |
|
|
|
|
|
return cropImage( |
|
|
srcImage, |
|
|
srcWidth * 3, |
|
|
srcHeight, |
|
|
startX * 3, |
|
|
startY, |
|
|
dstImage, |
|
|
dstWidth * 3, |
|
|
dstHeight, |
|
|
8); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int resize_image( |
|
|
const uint8_t *srcImage, |
|
|
int srcWidth, |
|
|
int srcHeight, |
|
|
uint8_t *dstImage, |
|
|
int dstWidth, |
|
|
int dstHeight, |
|
|
int pixel_size_B) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
constexpr int FRAC_BITS = 14; |
|
|
constexpr int FRAC_VAL = (1 << FRAC_BITS); |
|
|
constexpr int FRAC_MASK = (FRAC_VAL - 1); |
|
|
|
|
|
uint32_t src_x_accum, src_y_accum; |
|
|
uint32_t x_frac, nx_frac, y_frac, ny_frac; |
|
|
int x, y, ty; |
|
|
|
|
|
if (srcHeight < 2) { |
|
|
return EIDSP_PARAMETER_INVALID; |
|
|
} |
|
|
|
|
|
|
|
|
src_y_accum = FRAC_VAL / 2; |
|
|
const uint32_t src_x_frac = (srcWidth * FRAC_VAL) / dstWidth; |
|
|
const uint32_t src_y_frac = (srcHeight * FRAC_VAL) / dstHeight; |
|
|
|
|
|
|
|
|
srcWidth *= pixel_size_B; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uint8_t *s; |
|
|
uint8_t *d; |
|
|
|
|
|
for (y = 0; y < dstHeight; y++) { |
|
|
|
|
|
ty = src_y_accum >> FRAC_BITS; |
|
|
y_frac = src_y_accum & FRAC_MASK; |
|
|
src_y_accum += src_y_frac; |
|
|
ny_frac = FRAC_VAL - y_frac; |
|
|
|
|
|
s = &srcImage[ty * srcWidth]; |
|
|
d = &dstImage[y * dstWidth * pixel_size_B]; |
|
|
|
|
|
src_x_accum = FRAC_VAL / 2; |
|
|
for (x = 0; x < dstWidth; x++) { |
|
|
uint32_t tx, p00, p01, p10, p11; |
|
|
|
|
|
tx = (src_x_accum >> FRAC_BITS) * pixel_size_B; |
|
|
x_frac = src_x_accum & FRAC_MASK; |
|
|
nx_frac = FRAC_VAL - x_frac; |
|
|
src_x_accum += src_x_frac; |
|
|
|
|
|
|
|
|
for (int color = 0; color < pixel_size_B; |
|
|
color++) |
|
|
{ |
|
|
p00 = s[tx]; |
|
|
p10 = s[tx + pixel_size_B]; |
|
|
p01 = s[tx + srcWidth]; |
|
|
p11 = s[tx + srcWidth + pixel_size_B]; |
|
|
p00 = ((p00 * nx_frac) + (p10 * x_frac) + FRAC_VAL / 2) >> FRAC_BITS; |
|
|
p01 = ((p01 * nx_frac) + (p11 * x_frac) + FRAC_VAL / 2) >> FRAC_BITS; |
|
|
p00 = ((p00 * ny_frac) + (p01 * y_frac) + FRAC_VAL / 2) >> FRAC_BITS; |
|
|
*d++ = (uint8_t)p00; |
|
|
|
|
|
tx++; |
|
|
} |
|
|
} |
|
|
} |
|
|
return EIDSP_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void calculate_crop_dims( |
|
|
int srcWidth, |
|
|
int srcHeight, |
|
|
int dstWidth, |
|
|
int dstHeight, |
|
|
int &cropWidth, |
|
|
int &cropHeight) |
|
|
{ |
|
|
|
|
|
|
|
|
if (srcWidth > srcHeight) { |
|
|
cropWidth = (uint32_t)(dstWidth * srcHeight) / dstHeight; |
|
|
cropHeight = srcHeight; |
|
|
} |
|
|
else { |
|
|
cropHeight = (uint32_t)(dstHeight * srcWidth) / dstWidth; |
|
|
cropWidth = srcWidth; |
|
|
} |
|
|
} |
|
|
|
|
|
int crop_and_interpolate_rgb888( |
|
|
const uint8_t *srcImage, |
|
|
int srcWidth, |
|
|
int srcHeight, |
|
|
uint8_t *dstImage, |
|
|
int dstWidth, |
|
|
int dstHeight) |
|
|
{ |
|
|
int cropWidth, cropHeight; |
|
|
|
|
|
calculate_crop_dims(srcWidth, srcHeight, dstWidth, dstHeight, cropWidth, cropHeight); |
|
|
|
|
|
int res = crop_image_rgb888_packed( |
|
|
srcImage, |
|
|
srcWidth, |
|
|
srcHeight, |
|
|
(srcWidth - cropWidth) / 2, |
|
|
(srcHeight - cropHeight) / 2, |
|
|
dstImage, |
|
|
cropWidth, |
|
|
cropHeight); |
|
|
|
|
|
if( res != EIDSP_OK) { return res; } |
|
|
|
|
|
return resize_image(dstImage, cropWidth, cropHeight, dstImage, dstWidth, dstHeight, 3); |
|
|
} |
|
|
|
|
|
int crop_and_interpolate_image( |
|
|
const uint8_t *srcImage, |
|
|
int srcWidth, |
|
|
int srcHeight, |
|
|
uint8_t *dstImage, |
|
|
int dstWidth, |
|
|
int dstHeight, |
|
|
int pixel_size_B) |
|
|
{ |
|
|
int cropWidth, cropHeight; |
|
|
|
|
|
calculate_crop_dims(srcWidth, srcHeight, dstWidth, dstHeight, cropWidth, cropHeight); |
|
|
|
|
|
|
|
|
int res = cropImage( |
|
|
srcImage, |
|
|
srcWidth * pixel_size_B, |
|
|
srcHeight, |
|
|
((srcWidth - cropWidth) / 2) * pixel_size_B, |
|
|
(srcHeight - cropHeight) / 2, |
|
|
dstImage, |
|
|
cropWidth * pixel_size_B, |
|
|
cropHeight, |
|
|
8); |
|
|
|
|
|
if( res != EIDSP_OK) { return res; } |
|
|
|
|
|
|
|
|
return resize_image(dstImage, cropWidth, cropHeight, dstImage, dstWidth, dstHeight, pixel_size_B); |
|
|
} |
|
|
|
|
|
}}} |
|
|
#endif |