Add ImageProcessor library to ExecuTorch (#19967)#19967
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/19967
Note: Links to docs will display an error until the docs builds have been completed. This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
@metascroy has exported this pull request. If you are a Meta employee, you can view the originating Diff in D106898421. |
This PR needs a
|
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Differential Revision: D106898421
1f6bc24 to
73aefa8
Compare
|
@claude review this code |
|
Claude finished @metascroy's task in 1m 35s —— View job Code Review: Add ImageProcessor library to ExecuTorch (#19967)
Overall this is a well-structured, well-tested library addition. The API is clean, the pImpl pattern is appropriate for the planned platform backends, and the test coverage is thorough. Below are my findings: Issues1. Redundant memory allocation in
The YUV→RGB conversion already produces RGB. You could either:
The current approach works correctly but doubles memory and adds an unnecessary O(n) copy for every YUV frame. 2.
// Line 424: hardcodes UP instead of forwarding the caller's value
auto err = process_into(
data, width, height, stride_bytes, input_format, *out,
Orientation::UP, // should be `orientation`
roi);3. Integer overflow potential in
Suggestions (non-blocking)4.
5.
6. Consider reserving the
Positive notes
|
73aefa8 to
8ac3b2f
Compare
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Differential Revision: D106898421
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Differential Revision: D106898421
8ac3b2f to
bd56f3f
Compare
Addressed 1, 2, 4 (with comment), and 5. 3 is incorrect. Elsewhere size_t is used for allocation, not indexing. height * stride does not come close to approaching INT32_MAX for images. |
|
@claude review this code |
|
Claude finished @metascroy's task in 3m 14s —— View job Code Review: Add ImageProcessor library to ExecuTorch (#19967)
Re-reviewed after the fixes for issues 1, 2, 4, and 5 from the previous review. The code is in good shape — the YUV path now writes RGBA directly (eliminating the double-buffer), orientation is forwarded correctly, the I have one remaining issue and a few minor suggestions: Issues1.
Since Suggestions (non-blocking)2. stb fetch pulls the full repo
3.
Positive notes
LGTM with the caveat on orientation behavior for future backends. |
Orientation is an enum and UP is only valid value, so will not address. Future diff in stack extends it. |
|
Note follow-up diffs will provide platform-accelerated implementations. Here is a preview of coming perf improvements for Apple: |
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Differential Revision: D106898421
bd56f3f to
990a220
Compare
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Differential Revision: D106898421
990a220 to
184bdf5
Compare
| const float val = | ||
| (resized_buf[src_idx + c] * norm.scale_factor - norm.mean[c]) / | ||
| norm.std_dev[c]; | ||
| const int32_t out_idx = c * final_w * final_h + dst_y * final_w + dst_x; |
There was a problem hiding this comment.
cast to size_t before the multiply ? Potential Integer overflow ?
| float* output = out.mutable_data_ptr<float>(); | ||
| std::fill( | ||
| output, | ||
| output + static_cast<size_t>(output_channels) * final_w * final_h, |
There was a problem hiding this comment.
Same potential issue with overflow ?
| for (int32_t c = 0; c < output_channels; ++c) { | ||
| const float val = | ||
| (resized_buf[src_idx + c] * norm.scale_factor - norm.mean[c]) / | ||
| norm.std_dev[c]; |
There was a problem hiding this comment.
nits: not sure if std_dev can be 0, maybe a defensive check for std_dev !=0
or aomething like
ET_CHECK_OR_RETURN_ERROR(norm.std_dev[c] != 0.0f, InvalidArgument,
"std_dev must be nonzero");
There was a problem hiding this comment.
Added check at process_into.
| return impl_->config; | ||
| } | ||
|
|
||
| Error ImageProcessor::process_into( |
There was a problem hiding this comment.
Nice to have: Looks like process_into allocates two scratch buffers on every call (lines 261, 279 — ~6 MB at 1080p). Since the API's purpose is to avoid per-call allocation, and the threading model is already one-instance-per-thread, these could be cached in Impl and reused across calls, unless I am missing something.
Same I guess for the rgba buffer in process_yuv_into (line 380).
There was a problem hiding this comment.
Caching in impl is an optimization in the Apple version. I don't want to spend too much optimizing these for-loop based implementations, though.
psiddh
left a comment
There was a problem hiding this comment.
looks great to me! Thanks for doing this including perf testings
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Differential Revision: D106898421
184bdf5 to
b7adfba
Compare
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Reviewed By: psiddh
Differential Revision: D106898421
b7adfba to
814ed10
Compare
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (`ImageProcessorConfig`):
- target_width / target_height: output spatial size.
- resize_mode: how the source is fit to the target.
- letterbox_anchor: placement of content when letterboxing.
- pad_value: fill value for letterbox padding.
- normalization: how uint8 [0,255] pixels map to floats.
- gpu_min_input_pixels: minimum source pixel count (width * height) at which
the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
- **ColorFormat** (`BGRA`, `RGBA`): interleaved 8-bit input layout.
- **YUVFormat** (`NV12`, `NV21`): semi-planar YUV input layout.
- **ResizeMode** (`STRETCH`, `LETTERBOX`): `STRETCH` fills the target, ignoring aspect ratio; `LETTERBOX` preserves aspect ratio and pads.
- **LetterboxAnchor** (`CENTER`, `TOP_LEFT`): where padded content is anchored.
- **Orientation** (`UP`): source orientation (upright).
- **Normalization** (`zeroToOne`, `imagenet`): `zeroToOne` scales to [0, 1]; `imagenet` additionally applies per-channel mean/std.
Example:
```
#include <executorch/extension/image/image_processor.h>
using namespace executorch::extension::image;
// Resize to 224x224, letterboxed, with ImageNet normalization.
ImageProcessorConfig config;
config.target_width = 224;
config.target_height = 224;
config.resize_mode = ResizeMode::LETTERBOX;
config.normalization = Normalization::imagenet();
ImageProcessor processor(config);
// RGBA/BGRA input -> normalized [1, 3, 224, 224] float tensor.
auto result = processor.process(
pixels, width, height, /*stride_bytes=*/width * 4, ColorFormat::RGBA);
if (result.ok()) {
TensorPtr tensor = std::move(result.get());
// feed `tensor` to the model...
}
// Semi-planar YUV (e.g. NV12 camera frame):
auto yuv = processor.process_yuv(
y_plane, y_stride, uv_plane, uv_stride, width, height, YUVFormat::NV12);
```
Reviewed By: psiddh
Differential Revision: D106898421
814ed10 to
fb092a0
Compare
Differential Revision: D106898421 Pull Request resolved: #19967
Summary:
Introduces an ImageProcessor library to ExecuTorch: a small, well-tested
core for turning camera/decoded image buffers into normalized model-input
tensors. This initial implementation is fully portable; performant
platform-specific backends (e.g. Apple Accelerate/Core Image) are added in
follow-up diffs behind the same API.
Given source pixels (BGRA/RGBA bytes or semi-planar YUV planes), the
processor crops/resizes to the configured target and emits a
[1, 3, target_height, target_width] float tensor, normalized per the
configured scheme.
Configuration (
ImageProcessorConfig):the GPU path is used; smaller inputs run on CPU. Sentinels: 0 = always GPU,
INT64_MAX = always CPU (defaults to ~1080p, i.e. 1920*1080 + 1). Platform
backends consult this to pick CPU vs GPU. (Note GPU is always platform optional; but GPU will never be routed when below gpu_min_input_pixels.)
Enum options:
Enum options:
BGRA,RGBA): interleaved 8-bit input layout.NV12,NV21): semi-planar YUV input layout.STRETCH,LETTERBOX):STRETCHfills the target, ignoring aspect ratio;LETTERBOXpreserves aspect ratio and pads.CENTER,TOP_LEFT): where padded content is anchored.UP): source orientation (upright).zeroToOne,imagenet):zeroToOnescales to [0, 1];imagenetadditionally applies per-channel mean/std.Example:
Reviewed By: psiddh
Differential Revision: D106898421