Skip to content

[java_api] byte[] Tensor, PreProcessSteps normalization ops, indexed output getter + Android CheckJNI fix - #1124

Merged
allnes merged 9 commits into
openvinotoolkit:masterfrom
allnes:an/java-api-android
Aug 10, 2026
Merged

allnes merged 9 commits into
openvinotoolkit:masterfrom
allnes:an/java-api-android

Conversation

@allnes

@allnes allnes commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Details:

Small additions to the OpenVINO Java bindings so inference can be driven from Kotlin/Java without OpenCV, plus one correctness fix that Android's CheckJNI exposes. Each has JNI + a unit test.

  • Tensor(ElementType, int[] dims, byte[] data) — feed a u8 buffer (e.g. an NHWC camera frame) directly; previously only a native pointer (long) or float[]/int[]/long[] were accepted, so a u8 frame needed OpenCV's Mat.dataAddr() or a wasteful float copy. JNI TensorByte validates element type (u8/i8) and length.
  • PreProcessSteps.scale(float|float[]), mean(float|float[]), convert_layout(Layout), convert_element_type(ElementType) — previously only resize was exposed. Lets /255 normalization and NHWC→NCHW be fused into the compiled model via PrePostProcessor, so callers pass raw u8 pixels.
  • InferRequest.get_output_tensor(int index) — read multi-output models (e.g. detection + mask prototypes) by index; the API only had the single-output getter and get_tensor(String).
  • CheckJNI fix: ArrayList.add (returns boolean) was called via CallObjectMethod in six JNI helpers. Desktop JVMs tolerate it; Android's CheckJNI aborts the process (the return type of CallObjectMethodV does not match boolean java.util.ArrayList.add) the moment a list-returning API is used (Core.get_available_devices, CompiledModel.inputs/outputs, Model.outputs, Any.asList). Switched to CallBooleanMethod.

Tests added/updated under src/test/java/org/intel/openvino/: TensorTests, PrePostProcessorTests, InferRequestTests.

Motivated by a new Android YOLO demo (submitted separately) that uses these APIs, but they are general and useful on their own.

allnes and others added 2 commits August 3, 2026 19:39
…xed output getter

Additions needed to run inference from Kotlin/Java without OpenCV:

- Tensor(ElementType, int[] dims, byte[] data): feed a u8 buffer (e.g. an NHWC
  camera frame) directly, without a native Mat pointer. JNI TensorByte validates
  the element type (u8/i8) and length.
- PreProcessSteps.scale(float|float[]), mean(float|float[]), convert_layout(Layout),
  convert_element_type(ElementType): push /255 normalization and NHWC->NCHW into
  the compiled model via PrePostProcessor, so callers supply raw u8 pixels.
- InferRequest.get_output_tensor(int index): read multi-output models (e.g.
  detection + mask prototypes) by index.

JNI + hpp declarations included. Tests: TensorTests (byte[] ctor, wrong length/type),
PrePostProcessorTests (convert_element_type + scale + convert_layout; per-channel
mean/scale), InferRequestTests (indexed getter matches the no-arg getter; bad index).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oid CheckJNI abort)

ArrayList.add returns boolean, but the JNI helpers called it via CallObjectMethod.
Desktop JVMs tolerate the mismatch; Android's CheckJNI (default for debuggable apps)
aborts: 'JNI DETECTED ERROR IN APPLICATION: the return type of CallObjectMethodV
does not match boolean java.util.ArrayList.add'. It triggers as soon as a
list-returning API is used on-device (Core.get_available_devices,
CompiledModel.inputs/outputs, Model.outputs, Any.asList). Switch all six call sites
to CallBooleanMethod.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@allnes
allnes requested a review from a team as a code owner August 3, 2026 17:39
@github-actions github-actions Bot added the category: java API OpenVINO Runtime Java API label Aug 3, 2026
allnes and others added 5 commits August 3, 2026 20:05
- TensorTests.testGetTensorFromByte: a u8 tensor is not int/float pointer-
  representable, so as_int() throws; verify the byte-array constructor via
  shape/size/element-type instead (wrong-length/wrong-type cases still cover
  validation).
- PrePostProcessorTests.testScaleAndConvertLayout: derive the NHWC user-tensor
  spatial size from the model's actual input (net.input().get_shape() =
  [1,3,32,32]) so set_spatial_static_shape matches; the old hard-coded 2x2 was
  incompatible with the test model and failed the PrePostProcessor build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix the Code Style (google-java-format) CI check: two javadoc comments in the
new Tensor byte[] ctor and PreProcessSteps.scale needed rewrapping per the
formatter the CI uses.
…yte, strengthen tests

- GetOutputTensor1: allocate the wrapper only after the (throwing) lookup
  succeeds, so an out-of-range index no longer leaks an ov::Tensor while the
  exception unwinds through JNI_METHOD (TensorByte already followed this rule).
- Add Tensor.as_byte() (Java + JNI, mirroring asInt) so u8/i8 tensors, which
  are not readable via data()/as_int(), can be round-tripped; guarded to
  byte-wide element types.
- testGetTensorFromByte now round-trips the bytes via as_byte() instead of
  only checking metadata; add signed-i8 round-trip and as_byte wrong-type tests.
- testPerChannelMeanScale now runs inference and asserts the result differs
  from a no-preprocessing baseline, so a dropped/no-op mean/scale is caught.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two failures in the Build and Test job:

1. Core.java and PreProcessSteps.java had en/em-dash (U+2013 / U+2014)
   characters in javadoc. javac runs with -encoding US-ASCII in CI, so
   these raised "unmappable character" and compilation failed. Replace
   them with ASCII '-'.

2. testPerChannelMeanScale compared the model output with and without a
   folded per-channel mean/scale and required them to differ. The test
   model ends in a saturated one-hot classifier whose result is invariant
   to input normalization, so the outputs are identical and the assert
   always failed. Verify the steps are accepted and fused (successful
   build + infer, correct input element type and shape) instead, matching
   the approach of testScaleAndConvertLayout.

Verified on Windows against a real OpenVINO build: PrePostProcessorTests
and the full java_api suite pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@allnes

allnes commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@aobolensk please review

@aobolensk aobolensk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general LGTM

* @param model Model object acquired from {@link Core#read_model}.
* @param device Name of a device to load a model to.
* @param properties properties – Map of pairs: (property name, property value) relevant only
* @param properties properties - Map of pairs: (property name, property value) relevant only

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks fixed

@allnes
allnes enabled auto-merge (squash) August 10, 2026 10:39
@allnes
allnes merged commit f2b4037 into openvinotoolkit:master Aug 10, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: java API OpenVINO Runtime Java API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants