Skip to content

v0.37.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 02:31
· 42 commits to main since this release
f26b13f

This release breaks the API: the preprocessing types move to edgefirst-tensor, "letterbox" now only means a fit with a pad colour, interpolation becomes a convert option, and several old APIs are removed. Consumers must move as a set; the C ABI versions are bumped.

Added

  • Model configuration discovery (edgefirst-decoder model_config, EDGEAI-2357). resolve_model_config(path, signals, overrides) returns a ResolvedModelConfig: schema, labels, model family, task, semantic decode mode, a PreprocessSpec, and where each value came from (provenance, describe()).
    • Sources, highest first, each filling only unset fields:
      1. embedded edgefirst.json, then its sidecar plus labels.txt (the legacy validation.normalization / validation.preprocessing now back input.preprocessing); an edgefirst.json without outputs leaves the schema to a later source;
      2. embedded, then sidecar, Hugging Face config.json + preprocessor_config.json;
      3. Ultralytics metadata;
      4. Hugging Face tensor names (pixel_values → logits), as guesses;
      5. defaults.
    • Containers are read without a runtime. ONNX metadata_props (edgefirst, legacy edgefirst.json, labels, hf_config, hf_preprocessor_config) are read by a protobuf field walk that skips the graph. ZIP trailers (TFLite associated files, HEF, DVM, TensorRT) are read behind the opt-in model-files feature, which the C library and Python wheel enable; only the file's tail, the central directory and the text entries are read.
    • A config.json is used only when it is a Hugging Face config. *ForSemanticSegmentation architectures map to the semantic decoder; other architectures are UnsupportedArchitecture.
    • ModelOverrides (task, normalize, resize, decode, labels) parse one grammar built on the shared vocabulary names (normalize=unsigned|signed|raw|imagenet|R,G,B/R,G,B, resize=stretch|letterbox|letterbox-center|letterbox-topleft), and describe() prints those keys in it. A bare letterbox follows the model family's convention.
    • Class names from every source (edgefirst.json, labels.txt, ONNX labels, Hugging Face id2label/label2id, Ultralytics names, the labels override) have leading and trailing whitespace trimmed, so ADE20K's "bed " resolves to "bed". A discovered source whose class names are unusable (a name empty after trimming, a malformed id2label, or a count that disagrees with num_labels or with the class count the outputs state or imply) is ignored with a warning and a note naming the source and index (e.g. id2label[7]), and the next source supplies the labels; when none does, the labels are empty with a warning. The count is the one the decoder builder derives, for every model type whose outputs carry one (semantic, Ultralytics and ModelPack detection and segmentation); a ModelPack segmentation output counts its channels less the trailing background channel, and end-to-end detections and class maps carry none. A labels override whose count disagrees is a ModelConfigError::LabelCountMismatch { expected, found } for every such model type, no longer only for semantic models; in Python it is a LabelCountMismatchError (an OverrideError with expected and found), and in C ef_model_config_resolve sets ERANGE. End-to-end detections and class maps state no class count, so there an override of any length is accepted and the caller checks it. Ultralytics names with the wrong count no longer stop resolution: the schema is laid out from the labels override's count, or from the one output whose class count fits, and the names are ignored with a note; when two outputs could hold the detections, resolution fails with an ambiguous-layout error naming those two remedies. Names repeated ignoring case add a note.
    • ModelSources::new().with_embedded(..).with_sidecar(..).with_signals(..) feeds resolve_sources from already-read files.
    • Python: resolve_model_config, ModelSignals, ModelOverrides, ResolvedModelConfig, PreprocessSpec, ConfigSource, ModelFamily, ModelTask, ConfigField, and the ModelConfigError (ValueError) exceptions OverrideError, UnresolvedConfigError and UnsupportedModelError (also NotImplementedError). C: ef_model_config_*, ef_model_overrides_*, ef_preprocess_spec and ef_preprocess_spec_default(); ef_model_config_resolve sets ENODATA when nothing supplies a required piece.
  • Shared preprocessing values (edgefirst-tensor). PreprocessSpec, ContentRegion, the ContentRegionSource trait, letterbox_rect, broadcast_rgb, Normalization::unit_mean_std, PadSpace and Interpolation::{Bicubic, Area}. ConvertOptions::from_spec/with_spec build convert options from a spec, interpolation included. Python: ConvertOptions.from_spec, Interpolation, PadSpace. C: ef_convert_options_from_spec, EF_INTERPOLATION_*, EF_PAD_SPACE_*.
  • Per-convert interpolation. ConvertOptions::interpolation overrides the processor's setting for one convert, so models sharing a processor keep their own resampling. Bicubic and area log one warning per mode and run bilinear. Python: ConvertOptions(interpolation=). C: ef_convert_options_set_interpolation.
  • Content region. ConvertOptions::content_region reports where a convert puts the image, computed by the code that paints it, and SemanticOptions::with_convert takes it for semantic decode. Python: ConvertOptions.content_region, ContentRegion. C: ef_convert_options_content_region.
  • Tiling with preprocessing. TilingConfig::with_options converts every tile with the given options. Python: TilingConfig(options=). C: plan_tiles, tile_into and tile_one take an ef_convert_options *.

Changed

  • Preprocessing types live in edgefirst-tensor. Normalization, NormalizationPreset, LetterboxPlacement, Fit, FitMode and Interpolation moved there; edgefirst-image re-exports them at the same paths. They are #[non_exhaustive], like ConfigSource, ModelFamily, ModelTask, ConfigField and DecodeMode. Normalization::mean_std returns the tensor error and requires a positive std.
  • Decoder schema. schema::Normalization is renamed NormalizationConfig; schema::ResizeMode is replaced by the tensor FitMode; DecodeMode gains as_str/from_str_code. The schema uses the shared NormalizationPreset, LetterboxPlacement, Interpolation and PadSpace.
  • "Letterbox" means the fit and its pad; the image rectangle is a content region. ConvertOptions::letterbox(pad) and ef_convert_options_set_letterbox keep their names and select the fit.
    • Rust: SemanticOptions::with_letterbox → with_content_region; MaskOverlay::letterbox, TilePlacement::letterbox and the materialize_masks argument → content_region.
    • Python: decode_semantic, the mask-drawing APIs and TilePlacement take content_region=; the old letterbox= keyword is not accepted there (convert(letterbox=<pad colour>) is unchanged).
    • C: ef_semantic_options_set_content_region, ef_tile_placement.has_content_region/content_region, and the draw APIs' content_region parameter.
  • C ABI versions. ef_decoder_abi_version 4, ef_image_abi_version 4. ef_tiling_config is now {tile_w, tile_h, overlap_ratio}; callers move fit, pad and placement into an ef_convert_options passed to plan_tiles, tile_into and tile_one.
  • Letterbox placement is spelled topleft. LetterboxPlacement::TopLeft reads and writes as topleft in edgefirst.json (input.preprocessing.letterbox.placement), the resize override (letterbox-topleft) and Python string arguments. top_left is no longer accepted, so edgefirst.json files written by 0.36.0 with a top-left placement must be updated, and files written by this release do not load in 0.36.0. Numeric codes and the C EF_LETTERBOX_PLACEMENT_TOP_LEFT constant are unchanged.
  • Python placements. Placement arguments accept "center"/"topleft" and their codes, and set_interpolation accepts all four interpolations.

Removed

  • ConvertOptions::with_preprocessing (feature decode); use ConvertOptions::from_spec with a PreprocessSpec.
  • TilingConfig::fit, pad and with_fit (Python TilingConfig(fit=, pad=), C ef_tiling_config.fit/pad); use with_options.
  • Python edgefirst.image.Normalization. Tensor.normalize_to_numpy takes a NormalizationPreset, and ConvertOptions(normalization=) and convert(normalization=) take a NormalizationPreset, its name, or (mean, std).

Fixed

  • Tiling lost the content region and ignored placement. A letterboxed tile away from the frame origin had no content region recorded, because its crop was checked against the tile size instead of the frame, so its boxes lifted to the wrong place. Tile content regions were also always computed centred.
  • V4L2 H.264 encoder ignored the requested profile on the i.MX 8M Plus. edgefirst-codec now applies the encoder controls before setting the CAPTURE (coded) format, because the vsi_v4l2 driver reset the profile to Constrained Baseline when that format was set. The profile and level are read back after the format is set and applied again where a driver changed them. The encoder also turns on frame- and macroblock-level rate control alongside constant bitrate; vsi_v4l2 left both off and encoded every frame at a fixed QP, so the requested bitrate was not held. Bitrate::Auto, the default, now targets 0.16 bits per pixel within 1 to 100 Mbps (about 10 Mbps at 1080p30) with the same rate control, where it used to leave the device's defaults; EncoderConfig::target_bitrate() returns the target either variant resolves to. An unset profile now selects High, and an unset level the lowest level of ITU-T H.264 Annex A that covers the frame size, each frame dimension, macroblock rate and target bitrate, where the device used its own defaults (Constrained Baseline on vsi_v4l2); both are best effort, so a device without those controls keeps its defaults. EncoderConfig::target_profile(), EncoderConfig::target_level() and H264Level::lowest_for() expose the choice.