Download src/runtime/preprocess.cpp from Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5: direct link, hf CLI and curl.
- Browser
- Download file 6.11 kB
-
https://huggingface.co/Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5/resolve/main/src/runtime/preprocess.cpp
- Command line
-
hf download hf://Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5/src/runtime/preprocess.cpp
-
curl -L -o preprocess.cpp https://huggingface.co/Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5/resolve/main/src/runtime/preprocess.cpp
6.11 kB
| namespace { | |
| // Preprocessing preserves image pixels and pads the shorter dimension with the | |
| // same value used by YOLO letterboxing. Source bytes are normalized from | |
| // [0, 255] to [0, 1] while packed RGB/BGR channels are separated into the | |
| // planar red, green, and blue tensors consumed by the runtime. | |
| constexpr uint8_t kLetterboxValue = 114; | |
| constexpr float kByteScale = 1.0F / 255.0F; | |
| // Scalar pixels and padding use a lookup table so every byte value receives | |
| // the same floating-point conversion without repeating division in the loops. | |
| const std::array<float, 256>& NormalizedValues() { | |
| static const std::array<float, 256> values = []() { | |
| std::array<float, 256> result{}; | |
| for (size_t index = 0; index < result.size(); ++index) { | |
| result[index] = static_cast<float>(index) / 255.0F; | |
| } | |
| return result; | |
| }(); | |
| return values; | |
| } | |
| // Normalize eight values at once for the vectorized body of each image row. | |
| void StoreNormalized8(uint8x8_t bytes, float* output) { | |
| const uint16x8_t values = vmovl_u8(bytes); | |
| const float32x4_t scale = vdupq_n_f32(kByteScale); | |
| vst1q_f32(output, vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(values))), scale)); | |
| vst1q_f32(output + 4, vmulq_f32(vcvtq_f32_u32(vmovl_high_u16(values)), scale)); | |
| } | |
| } // namespace | |
| extern "C" y26_native_status y26_native_preprocess_image_no_resize( | |
| const uint8_t* image, size_t image_bytes, size_t height, size_t width, size_t row_stride_bytes, | |
| y26_native_pixel_format pixel_format, float* output, size_t output_bytes) { | |
| // Reject missing buffers, empty images, and output sizes that cannot contain | |
| // three equal float planes. | |
| if (!image || !output || !height || !width || output_bytes % (3 * sizeof(float))) { | |
| return Y26_NATIVE_INVALID_ARGUMENT; | |
| } | |
| // Derive the square model resolution from the caller's output allocation. | |
| // This no-resize entry point accepts only supported resolutions and images | |
| // whose longer side already equals that resolution. | |
| const size_t input_plane = output_bytes / (3 * sizeof(float)); | |
| const size_t input_size = static_cast<size_t>(std::sqrt(static_cast<double>(input_plane))); | |
| if (input_size * input_size != input_plane || input_size > std::numeric_limits<uint32_t>::max() || | |
| !yolo26::IsSupportedResolution(static_cast<uint32_t>(input_size)) || height > input_size || | |
| width > input_size || std::max(height, width) != input_size || | |
| pixel_format < Y26_NATIVE_BGR || pixel_format > Y26_NATIVE_BGRX || | |
| output_bytes != 3 * input_plane * sizeof(float)) { | |
| return Y26_NATIVE_INVALID_ARGUMENT; | |
| } | |
| // Validate the packed source layout before calculating its final byte. A row | |
| // may include trailing stride padding, but it must contain every image pixel. | |
| const size_t pixel_stride = pixel_format == Y26_NATIVE_BGRX ? 4 : 3; | |
| if (width > std::numeric_limits<size_t>::max() / pixel_stride || | |
| row_stride_bytes < width * pixel_stride) { | |
| return Y26_NATIVE_INVALID_ARGUMENT; | |
| } | |
| const size_t final_row_bytes = width * pixel_stride; | |
| if ((height - 1) > (std::numeric_limits<size_t>::max() - final_row_bytes) / row_stride_bytes) { | |
| return Y26_NATIVE_INVALID_ARGUMENT; | |
| } | |
| const size_t required_image_bytes = (height - 1) * row_stride_bytes + final_row_bytes; | |
| if (image_bytes < required_image_bytes || | |
| yolo26::BuffersOverlap(image, required_image_bytes, output, output_bytes)) { | |
| return Y26_NATIVE_INVALID_ARGUMENT; | |
| } | |
| const auto& normalized = NormalizedValues(); | |
| // Centered padding implements the no-resize half of letterboxing. The ABI | |
| // requires one image side to match the model, so only one axis is padded. | |
| const size_t top = (input_size - height) / 2; | |
| const size_t left = (input_size - width) / 2; | |
| float* red_plane = output; | |
| float* green_plane = output + input_plane; | |
| float* blue_plane = output + 2 * input_plane; | |
| for (float* plane : {red_plane, green_plane, blue_plane}) { | |
| std::fill(plane, plane + top * input_size, normalized[kLetterboxValue]); | |
| std::fill(plane + (top + height) * input_size, plane + input_plane, | |
| normalized[kLetterboxValue]); | |
| if (left) { | |
| for (size_t y = top; y < top + height; ++y) { | |
| float* row = plane + y * input_size; | |
| std::fill(row, row + left, normalized[kLetterboxValue]); | |
| std::fill(row + left + width, row + input_size, normalized[kLetterboxValue]); | |
| } | |
| } | |
| } | |
| const size_t red_index = pixel_format == Y26_NATIVE_RGB ? 0 : 2; | |
| const size_t blue_index = 2 - red_index; | |
| // Separate packed source channels into planar output. Complete groups of | |
| // eight pixels use NEON; the scalar loop handles the remaining row tail. | |
| for (size_t y = 0; y < height; ++y) { | |
| const uint8_t* source = image + y * row_stride_bytes; | |
| const size_t offset = (top + y) * input_size + left; | |
| float* red = red_plane + offset; | |
| float* green = green_plane + offset; | |
| float* blue = blue_plane + offset; | |
| size_t x = 0; | |
| for (; x + 8 <= width; x += 8) { | |
| if (pixel_format == Y26_NATIVE_BGRX) { | |
| const uint8x8x4_t pixels = vld4_u8(source + 4 * x); | |
| StoreNormalized8(pixels.val[2], red + x); | |
| StoreNormalized8(pixels.val[1], green + x); | |
| StoreNormalized8(pixels.val[0], blue + x); | |
| } else { | |
| const uint8x8x3_t pixels = vld3_u8(source + 3 * x); | |
| StoreNormalized8(pixels.val[red_index], red + x); | |
| StoreNormalized8(pixels.val[1], green + x); | |
| StoreNormalized8(pixels.val[blue_index], blue + x); | |
| } | |
| } | |
| for (; x < width; ++x) { | |
| const uint8_t* pixel = source + pixel_stride * x; | |
| if (pixel_format == Y26_NATIVE_RGB) { | |
| red[x] = normalized[pixel[0]]; | |
| green[x] = normalized[pixel[1]]; | |
| blue[x] = normalized[pixel[2]]; | |
| } else { | |
| blue[x] = normalized[pixel[0]]; | |
| green[x] = normalized[pixel[1]]; | |
| red[x] = normalized[pixel[2]]; | |
| } | |
| } | |
| } | |
| return Y26_NATIVE_OK; | |
| } | |