| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "edge-impulse-sdk/dsp/image/processing.hpp" |
| #include "edge-impulse-sdk/dsp/ei_utils.h" |
| #include "edge-impulse-sdk/dsp/returntypes.hpp" |
| #include "edge-impulse-sdk/porting/ei_classifier_porting.h" |
| #include "edge-impulse-sdk/porting/ei_logging.h" |
| #include "edge-impulse-sdk/classifier/ei_constants.h" |
| #include <string.h> |
| #include <stddef.h> |
|
|
| namespace ei { |
| namespace image { |
| namespace processing { |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| 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 = 0; |
| 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 = 0; |
| 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); |
| } |
|
|
| int resize_image_using_mode( |
| const uint8_t *srcImage, |
| int srcWidth, |
| int srcHeight, |
| uint8_t *dstImage, |
| int dstWidth, |
| int dstHeight, |
| int pixel_size_B, |
| int mode) |
| { |
|
|
| if (srcWidth == dstWidth && srcHeight == dstHeight) { |
| EI_LOGI("Source and destination sizes are the same. No resizing needed.\n"); |
| if (srcImage != dstImage) { |
| memcpy(dstImage, srcImage, srcWidth * srcHeight * pixel_size_B); |
| } |
| return -1; |
| } |
|
|
| if (srcImage == dstImage && mode == EI_CLASSIFIER_RESIZE_FIT_LONGEST) { |
| EI_LOGI("FIT LONGEST in place, make sure source is oversized to fit destination size\n"); |
| } |
|
|
| if (mode == EI_CLASSIFIER_RESIZE_FIT_SHORTEST) { |
| int res = crop_and_interpolate_image( |
| srcImage, |
| srcWidth, |
| srcHeight, |
| dstImage, |
| dstWidth, |
| dstHeight, |
| pixel_size_B); |
|
|
| if (res != 0) { |
| EI_LOGE("Error in crop_and_interpolate_image: %d\n", res); |
| return res; |
| } |
| return 0; |
| } |
|
|
| if (mode == EI_CLASSIFIER_RESIZE_SQUASH) { |
| int res = |
| resize_image(srcImage, srcWidth, srcHeight, dstImage, dstWidth, dstHeight, pixel_size_B); |
|
|
| if (res != 0) { |
| EI_LOGE("Error in resize_image: %d\n", res); |
| return res; |
| } |
| return 0; |
| } |
|
|
| if (mode == EI_CLASSIFIER_RESIZE_FIT_LONGEST) { |
| |
| float srcAspect = static_cast<float>(srcWidth) / srcHeight; |
| float dstAspect = static_cast<float>(dstWidth) / dstHeight; |
|
|
| |
| int resizeWidth, resizeHeight; |
| if (srcAspect > dstAspect) { |
| resizeWidth = dstWidth; |
| resizeHeight = static_cast<int>(dstWidth / srcAspect); |
| } |
| else { |
| resizeHeight = dstHeight; |
| resizeWidth = static_cast<int>(dstHeight * srcAspect); |
| } |
|
|
| |
| int startX = (dstWidth - resizeWidth) / 2; |
| int startY = (dstHeight - resizeHeight) / 2; |
|
|
| |
| int res = resize_image( |
| srcImage, |
| srcWidth, |
| srcHeight, |
| dstImage, |
| resizeWidth, |
| resizeHeight, |
| pixel_size_B); |
|
|
| if (res != 0) { |
| EI_LOGE("Error in resize_image: %d\n", res); |
| return res; |
| } |
| |
| if (resizeWidth == dstWidth) { |
| |
| size_t dstStart = (startY * dstWidth + startX) * pixel_size_B; |
| memmove( |
| dstImage + dstStart, |
| dstImage, |
| resizeWidth * resizeHeight * pixel_size_B); |
| |
| memset(dstImage, 0, dstStart); |
| |
| |
| memset( |
| dstImage + dstStart + resizeWidth * resizeHeight * pixel_size_B, |
| 0, |
| dstStart); |
| } |
| |
| else { |
| size_t resizeIndex = (dstWidth * dstHeight - resizeWidth * resizeHeight) * pixel_size_B; |
|
|
| |
| memmove(dstImage + resizeIndex, dstImage, resizeWidth * resizeHeight * pixel_size_B); |
|
|
| |
| for (int y = 0; y < resizeHeight; y++) { |
| memmove( |
| dstImage + ((startY + y) * dstWidth + startX) * pixel_size_B, |
| dstImage + resizeIndex, |
| resizeWidth * pixel_size_B); |
| resizeIndex += resizeWidth * pixel_size_B; |
| |
| memset(dstImage + (startY + y) * dstWidth * pixel_size_B, 0, startX * pixel_size_B); |
| memset(dstImage + ((startY + y) * dstWidth + startX + resizeWidth) * pixel_size_B, 0, (dstWidth - startX - resizeWidth) * pixel_size_B); |
| } |
| } |
| return 0; |
| } |
|
|
| |
| return -2; |
| } |
| } |
| } |
| } |
|
|