Download ei-cpp-export/edge-impulse-sdk/dsp/image/processing.cpp from luisomoreau/fomo-face-detection: direct link, hf CLI and curl.
- Browser
- Download file 13.5 kB
-
https://huggingface.co/luisomoreau/fomo-face-detection/resolve/main/ei-cpp-export/edge-impulse-sdk/dsp/image/processing.cpp
- Command line
-
hf download hf://luisomoreau/fomo-face-detection/ei-cpp-export/edge-impulse-sdk/dsp/image/processing.cpp
-
curl -L -o processing.cpp https://huggingface.co/luisomoreau/fomo-face-detection/resolve/main/ei-cpp-export/edge-impulse-sdk/dsp/image/processing.cpp
13.5 kB
| /* | |
| * Copyright (c) 2022 EdgeImpulse Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an "AS | |
| * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | |
| * express or implied. See the License for the specific language | |
| * governing permissions and limitations under the License. | |
| * | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| namespace ei { namespace image { namespace processing { | |
| enum YUV_OPTIONS | |
| { | |
| BIG_ENDIAN_ORDER = 1, //RGB reading from low to high memory. Otherwise, uses native encoding | |
| PAD_4B = 2, // pad 0x00 on the high B. ie 0x00RRGGBB | |
| }; | |
| /** | |
| * @brief Convert YUV to RGB | |
| * | |
| * @param rgb_out Output buffer (can be the same as yuv_in if big enough) | |
| * @param yuv_in Input buffer | |
| * @param in_size_B Size of input image in B | |
| * @param opts Note, only BIG_ENDIAN_ORDER supported presently | |
| */ | |
| int yuv422_to_rgb888( | |
| unsigned char *rgb_out, | |
| unsigned const char *yuv_in, | |
| unsigned int in_size_B, | |
| YUV_OPTIONS opts) | |
| { | |
| // Clamp out of range values | |
| // Color space conversion for RGB | |
| 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; | |
| // Going backwards probably looks strange, but | |
| // This allows us to do the algorithm in place! | |
| // User needs to put the YUV image into a larger buffer than necessary | |
| // But going backwards means we don't overwrite the YUV bytes | |
| // until we don't need them anymore | |
| 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 { | |
| // not yet supported | |
| return EIDSP_NOT_SUPPORTED; | |
| } | |
| } | |
| return EIDSP_OK; | |
| } | |
| /** | |
| * @brief Crops an image. Can be in-place. 4B alignment for best performance | |
| * (Alignment is tested, will fall back to B by B movement) | |
| * | |
| * @param srcWidth X dimension in pixels | |
| * @param srcHeight Y dimension in pixels | |
| * @param srcImage Input buffer | |
| * @param startX X coord of first pixel to keep | |
| * @param startY Y coord of the first pixel to keep | |
| * @param dstWidth Desired X dimension in pixels (should be smaller than srcWidth) | |
| * @param dstHeight Desired Y dimension in pixels (should be smaller than srcHeight) | |
| * @param dstImage Output buffer, can be the same as srcImage | |
| * @param iBpp 8 or 16 for bits per pixel | |
| */ | |
| 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; // invalid parameters | |
| } | |
| 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) { // either src or dst pointer is not aligned | |
| for (; x < dstWidth; x++) { | |
| *d++ = *s++; // have to do it byte-by-byte | |
| } | |
| } | |
| else { | |
| // move 4 bytes at a time if aligned or alignment not enforced | |
| s32 = (uint32_t *)s; | |
| d32 = (uint32_t *)d; | |
| for (; x < dstWidth - 3; x += 4) { | |
| *d32++ = *s32++; | |
| } | |
| // any remaining stragglers? | |
| s = (uint8_t *)s32; | |
| d = (uint8_t *)d32; | |
| for (; x < dstWidth; x++) { | |
| *d++ = *s++; | |
| } | |
| } | |
| } // for y | |
| } // 8-bpp | |
| 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) { // either src or dst pointer is not aligned | |
| for (; x < dstWidth; x++) { | |
| *d++ = *s++; // have to do it 16-bits at a time | |
| } | |
| } | |
| else { | |
| // move 4 bytes at a time if aligned or alignment no enforced | |
| s32 = (uint32_t *)s; | |
| d32 = (uint32_t *)d; | |
| for (; x < dstWidth - 1; x += 2) { // we can move 2 pixels at a time | |
| *d32++ = *s32++; | |
| } | |
| // any remaining stragglers? | |
| s = (uint16_t *)s32; | |
| d = (uint16_t *)d32; | |
| for (; x < dstWidth; x++) { | |
| *d++ = *s++; | |
| } | |
| } | |
| } // for y | |
| } // 16-bpp case | |
| return EIDSP_OK; | |
| } /* cropImage() */ | |
| /** | |
| * @copydoc cropImage( | |
| int srcWidth, | |
| int srcHeight, | |
| const uint8_t *srcImage, | |
| int startX, | |
| int startY, | |
| int dstWidth, | |
| int dstHeight, | |
| uint8_t *dstImage, | |
| int iBpp) | |
| */ | |
| int crop_image_rgb888_packed( | |
| const uint8_t *srcImage, | |
| int srcWidth, | |
| int srcHeight, | |
| int startX, | |
| int startY, | |
| uint8_t *dstImage, | |
| int dstWidth, | |
| int dstHeight) | |
| { | |
| // use 8 bpp mode, but do everything *3 for RGB | |
| return cropImage( | |
| srcImage, | |
| srcWidth * 3, | |
| srcHeight, | |
| startX * 3, | |
| startY, | |
| dstImage, | |
| dstWidth * 3, | |
| dstHeight, | |
| 8); | |
| } | |
| /** | |
| * @brief Resize an image using interpolation | |
| * Can be used to resize the image smaller or larger | |
| * If resizing much smaller than 1/3 size, then a more rubust algorithm should average all of the pixels | |
| * This algorithm uses bilinear interpolation - averages a 2x2 region to generate each new pixel | |
| * | |
| * @param srcWidth Input image width in pixels | |
| * @param srcHeight Input image height in pixels | |
| * @param srcImage Input buffer | |
| * @param dstWidth Output image width in pixels | |
| * @param dstHeight Output image height in pixels | |
| * @param dstImage Output buffer, can be same as input buffer | |
| * @param pixel_size_B Size of pixels in Bytes. 3 for RGB, 1 for mono | |
| */ | |
| int resize_image( | |
| const uint8_t *srcImage, | |
| int srcWidth, | |
| int srcHeight, | |
| uint8_t *dstImage, | |
| int dstWidth, | |
| int dstHeight, | |
| int pixel_size_B) | |
| { | |
| // Copied from ei_camera.cpp in firmware-eta-compute | |
| // Modified for RGB888 | |
| // This needs to be < 16 or it won't fit. Cortex-M4 only has SIMD for signed multiplies | |
| 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; // accumulators and fractions for scaling the image | |
| uint32_t x_frac, nx_frac, y_frac, ny_frac; | |
| int x, y, ty; | |
| if (srcHeight < 2) { | |
| return EIDSP_PARAMETER_INVALID; | |
| } | |
| // start at 1/2 pixel in to account for integer downsampling which might miss pixels | |
| 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; | |
| //from here out, *3 b/c RGB | |
| srcWidth *= pixel_size_B; | |
| //srcHeight not used for indexing | |
| //dstWidth still needed as is | |
| //dstHeight shouldn't be scaled | |
| const uint8_t *s; | |
| uint8_t *d; | |
| for (y = 0; y < dstHeight; y++) { | |
| // do indexing computations | |
| ty = src_y_accum >> FRAC_BITS; // src y | |
| y_frac = src_y_accum & FRAC_MASK; | |
| src_y_accum += src_y_frac; | |
| ny_frac = FRAC_VAL - y_frac; // y fraction and 1.0 - y fraction | |
| s = &srcImage[ty * srcWidth]; | |
| d = &dstImage[y * dstWidth * pixel_size_B]; //not scaled above | |
| // start at 1/2 pixel in to account for integer downsampling which might miss pixels | |
| src_x_accum = FRAC_VAL / 2; | |
| for (x = 0; x < dstWidth; x++) { | |
| uint32_t tx, p00, p01, p10, p11; | |
| // do indexing computations | |
| tx = (src_x_accum >> FRAC_BITS) * pixel_size_B; | |
| x_frac = src_x_accum & FRAC_MASK; | |
| nx_frac = FRAC_VAL - x_frac; // x fraction and 1.0 - x fraction | |
| src_x_accum += src_x_frac; | |
| //interpolate and write out | |
| for (int color = 0; color < pixel_size_B; | |
| color++) // do pixel_size_B times for pixel_size_B colors | |
| { | |
| 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; // top line | |
| p01 = ((p01 * nx_frac) + (p11 * x_frac) + FRAC_VAL / 2) >> FRAC_BITS; // bottom line | |
| p00 = ((p00 * ny_frac) + (p01 * y_frac) + FRAC_VAL / 2) >> FRAC_BITS; //top + bottom | |
| *d++ = (uint8_t)p00; // store new pixel | |
| //ready next loop | |
| tx++; | |
| } | |
| } // for x | |
| } // for y | |
| return EIDSP_OK; | |
| } // resizeImage() | |
| /** | |
| * @brief Calculate new dims that match the aspect ratio of destination | |
| * This prevents a squashed look | |
| * The smallest axis is held constant | |
| * | |
| * @param srcWidth Input width in pixels | |
| * @param srcHeight Input height in pixels | |
| * @param dstWidth Ultimate width in pixels | |
| * @param dstHeight Ultimate height in pixels | |
| * @param[out] cropWidth Width in pixels that matches the aspect ratio | |
| * @param[out] cropHeight Height in pixels that matches the aspect ratio | |
| */ | |
| void calculate_crop_dims( | |
| int srcWidth, | |
| int srcHeight, | |
| int dstWidth, | |
| int dstHeight, | |
| int &cropWidth, | |
| int &cropHeight) | |
| { | |
| //first, trim the largest axis to match destination aspect ratio | |
| //calculate by fixing the smaller axis | |
| if (srcWidth > srcHeight) { | |
| cropWidth = (uint32_t)(dstWidth * srcHeight) / dstHeight; //cast in case int is small | |
| 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; | |
| // What are dimensions that maintain aspect ratio? | |
| calculate_crop_dims(srcWidth, srcHeight, dstWidth, dstHeight, cropWidth, cropHeight); | |
| // Now crop to that dimension | |
| int res = crop_image_rgb888_packed( | |
| srcImage, | |
| srcWidth, | |
| srcHeight, | |
| (srcWidth - cropWidth) / 2, | |
| (srcHeight - cropHeight) / 2, | |
| dstImage, | |
| cropWidth, | |
| cropHeight); | |
| if( res != EIDSP_OK) { return res; } | |
| // Finally, interpolate down to desired dimensions, in place | |
| 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; | |
| // What are dimensions that maintain aspect ratio? | |
| calculate_crop_dims(srcWidth, srcHeight, dstWidth, dstHeight, cropWidth, cropHeight); | |
| // Now crop to that dimension | |
| 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; } | |
| // Finally, interpolate down to desired dimensions, in place | |
| return resize_image(dstImage, cropWidth, cropHeight, dstImage, dstWidth, dstHeight, pixel_size_B); | |
| } | |
| }}} //namespaces | |