Files
opencv-MIRROR/modules/java
utibenkei 9d40b84107 Merge pull request #29851 from utibenkei:fix-java-mat-get-put
java: fix Mat get/put JNI for 0D/1D/ND Mats in OpenCV 5 - #29851#29851

## Summary

Fix incorrect behavior of Java `Mat.get()` / `Mat.put()` JNI bindings for
0D (scalar), 1D, and ND `cv::Mat` objects.
The Java JNI layer in `modules/java/generator/src/cpp/Mat.cpp` still assumed
essentially 2D row/col indexing in several get/put code paths. This caused
wrong results or failed access for scalar and 1D Mats, and incorrect bounds
handling for ND Mats accessed via index arrays.
This PR updates the native get/put helpers in `Mat.cpp` and adds regression
tests in `modules/core/misc/java/test/MatTest.java`.

## Problem (before this patch)
### 1. 0D (scalar) Mats
- `Mat::ptr(const int*)` requires `dims >= 1`, so idx-based paths that called
  `m->ptr(idx.data())` directly (e.g. `nGetIdx`, `mat_copy_data`, `PUT_ITEM_IDX`)
  could not access a true 0D Mat correctly.
- `idx2Offset()` always read `indices[0]` without checking `dims <= 0`.
- Row/col helpers always built a 2-element index `{row, col}` instead of an
  empty index vector for 0D Mats.
Although OpenCV 5 supports 0D Mats at the C++ level, the Java wrapper did not
correctly implement idx-based access with an empty index array (`new int[]{}`).
This PR adds explicit JNI support for that case via `mat_elem_ptr()` and
related helpers.
**Example:** For `Mat m = new Mat(new int[]{}, CvType.CV_64F)` (`dims()==0`),
`m.put(new int[]{}, 42.0)` and `m.get(new int[]{}, buf)` could fail or access
the wrong memory.
### 2. 1D Mats (`dims() == 1`)
- Row/col get/put always converted `(row, col)` to `{row, col}` (length 2),
  while a 1D Mat requires a single-element index vector.
- For a 1D Mat, OpenCV 5 represents the shape as `dims == rows == 1` and
  `cols == total() == N`. The Java row/col access path must therefore interpret
  `(row, col)` as the 1D element index represented by `col` (with `row == 0`),
  converting internally to `{col}` via `row_col_to_idx()`.
**Example:** For `Mat m = new Mat(new int[]{10}, CvType.CV_32F)`, calling
`m.put(0, 3, values)` could behave incorrectly because native code treated
the access as `{0, 3}` instead of `{3}`.
### 3. ND Mats (`dims() > 2`) via row/col API
- Row/col get/put only checked `rows` and `cols`, without rejecting Mats with
  `dims > 2`.
- ND Mats must be accessed via idx-based APIs; using row/col on them could
  produce silently wrong results instead of failing early.
### 4. Idx-based API bounds checking
- Old code checked `size[i] <= idx[i]` but did not validate:
  - `idx.length == dims`
  - negative index values
- `nPutDIdx` computed the number of remaining elements with a per-dimension
  product loop (`channels * (size[i] - idx[i]) * ...`). For ND Mats, this did
  not correctly account for the current linear index position when determining
  how many elements remain from the selected subarray onward. The fix uses
  `(total() - idx2Offset(m, idx)) * channels()`, which matches the linear
  layout used by `mat_copy_data()`.

## Changes
- Add `mat_elem_ptr()` to support 0D scalar Mats (`Mat::ptr(const int*)`
  requires `dims >= 1`).
- Add `check_row_col_access()`, `check_idx_access()`, and `row_col_to_idx()`
  to validate access and convert row/col indices according to OpenCV semantics.
- Update `idx2Offset()`, `offset2Idx()`, and `mat_copy_data()` for 0D/1D Mats.
- Route row/col get/put through `row_col_to_idx()` (1D: element index in `col`,
  `row == 0`).
- Restrict row/col APIs to Mats with `dims <= 2`; ND Mats must use idx-based
  APIs (`nGet*Idx`, `nPut*Idx`, `nGetIdx`, `nPutDIdx`).
- Fix remaining-element calculation in `nPutDIdx` to use `idx2Offset()`.

## Behavior notes
| API | 0D Mat (`dims==0`) | 1D Mat (`dims==1`) | 2D Mat | ND Mat (`dims>2`) |
|-----|-------------------|-------------------|--------|-------------------|
| `get/put(int[] idx, ...)` | supported (`idx.length == 0`; added by this PR) | supported (`idx.length == 1`) | supported | supported |
| `get/put(row, col, ...)` | supported (`row=0`, `col=0`) | supported (index in `col`, `row=0`) | supported | rejected at JNI layer return 0; |
| `get(row, col)` | supported | supported | supported | rejected at JNI layer return 0; |
| `get(int[] idx)` | supported | supported | supported | supported |

## Test plan
Added regression tests in `MatTest.java`:
- `testGetPut0DMat` — idx and row/col get/put on a scalar Mat (`new int[]{}`)
- `testGetPut1DMat` — idx and row/col get/put on `new Mat(new int[]{5}, ...)`
- `testGetPut1DMatDoubleArray` — bulk double put/get via idx on a 1D Mat
- `testGetPut3DMat` — row/col API rejected for 3D Mat; idx API works

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-10-30 10:45:13 +03:00
..
2025-02-05 09:28:27 +03:00
2026-06-22 20:38:16 -04:00