From c6d9ce8fd3deb82d8e8bcc5efb44c74e51005572 Mon Sep 17 00:00:00 2001 From: SarenT Date: Fri, 12 Jan 2018 16:00:58 +0100 Subject: [PATCH] Merge pull request #10489 from SarenT:offset-mat_put Adding capability to parse subsections of a byte array in Java bindings (#10489) * Adding capability to parse subsections of a byte array in Java bindings. (Because Java lacks pointers. Therefore, reading images within a subsection of a byte array is impossible by Java's nature and limitations. Because of this, many IO functions in Java require additional parameters offset and length to define, which section of an array to be read.) * Corrected according to the review. Previous interfaces were restored, instead internal interfaces were modified to provide subsampling of java byte arrays. * Adding tests and test related files. * Adding missing files for the test. * Simplified the test * Check was corrected according to discussion. An OutOfRangeException will be thrown instead of returning. * java: update MatOfByte implementation checks / tests --- modules/core/misc/java/src/java/core+Mat.java | 17 +++++ .../misc/java/src/java/core+MatOfByte.java | 19 +++++ .../core/misc/java/test/MatOfByteTest.java | 70 +++++++++++++++++++ modules/java/generator/src/cpp/Mat.cpp | 27 ++++--- 4 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 modules/core/misc/java/test/MatOfByteTest.java diff --git a/modules/core/misc/java/src/java/core+Mat.java b/modules/core/misc/java/src/java/core+Mat.java index 98cdba9736..c975f0c44b 100644 --- a/modules/core/misc/java/src/java/core+Mat.java +++ b/modules/core/misc/java/src/java/core+Mat.java @@ -1015,6 +1015,21 @@ public class Mat { throw new java.lang.UnsupportedOperationException("Mat data type is not compatible: " + t); } + // javadoc:Mat::put(row,col,data,offset,length) + public int put(int row, int col, byte[] data, int offset, int length) { + int t = type(); + if (data == null || length % CvType.channels(t) != 0) + throw new java.lang.UnsupportedOperationException( + "Provided data element number (" + + (data == null ? 0 : data.length) + + ") should be multiple of the Mat channels count (" + + CvType.channels(t) + ")"); + if (CvType.depth(t) == CvType.CV_8U || CvType.depth(t) == CvType.CV_8S) { + return nPutBwOffset(nativeObj, row, col, length, offset, data); + } + throw new java.lang.UnsupportedOperationException("Mat data type is not compatible: " + t); + } + // javadoc:Mat::get(row,col,data) public int get(int row, int col, byte[] data) { int t = type(); @@ -1318,6 +1333,8 @@ public class Mat { private static native int nPutB(long self, int row, int col, int count, byte[] data); + private static native int nPutBwOffset(long self, int row, int col, int count, int offset, byte[] data); + private static native int nGetB(long self, int row, int col, int count, byte[] vals); private static native int nGetS(long self, int row, int col, int count, short[] vals); diff --git a/modules/core/misc/java/src/java/core+MatOfByte.java b/modules/core/misc/java/src/java/core+MatOfByte.java index 7756eb94f9..eb928fb0a9 100644 --- a/modules/core/misc/java/src/java/core+MatOfByte.java +++ b/modules/core/misc/java/src/java/core+MatOfByte.java @@ -35,6 +35,11 @@ public class MatOfByte extends Mat { fromArray(a); } + public MatOfByte(int offset, int length, byte...a) { + super(); + fromArray(offset, length, a); + } + public void alloc(int elemNumber) { if(elemNumber>0) super.create(elemNumber, 1, CvType.makeType(_depth, _channels)); @@ -48,6 +53,20 @@ public class MatOfByte extends Mat { put(0, 0, a); //TODO: check ret val! } + public void fromArray(int offset, int length, byte...a) { + if (offset < 0) + throw new IllegalArgumentException("offset < 0"); + if (a == null) + throw new NullPointerException(); + if (length < 0 || length + offset > a.length) + throw new IllegalArgumentException("invalid 'length' parameter: " + Integer.toString(length)); + if (a.length == 0) + return; + int num = length / _channels; + alloc(num); + put(0, 0, a, offset, length); //TODO: check ret val! + } + public byte[] toArray() { int num = checkVector(_channels, _depth); if(num < 0) diff --git a/modules/core/misc/java/test/MatOfByteTest.java b/modules/core/misc/java/test/MatOfByteTest.java new file mode 100644 index 0000000000..18f56e2010 --- /dev/null +++ b/modules/core/misc/java/test/MatOfByteTest.java @@ -0,0 +1,70 @@ +package org.opencv.test.core; + +import java.util.Arrays; + +import org.opencv.core.Core; +import org.opencv.core.CvException; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfDouble; +import org.opencv.test.OpenCVTestCase; +import org.opencv.imgcodecs.Imgcodecs; + +public class MatOfByteTest extends OpenCVTestCase { + + public void testMatOfSubByteArray() { + byte[] inputBytes = { 1,2,3,4,5 }; + + MatOfByte m0 = new MatOfByte(inputBytes); + MatOfByte m1 = new MatOfByte(0, inputBytes.length, inputBytes); + MatOfByte m2 = new MatOfByte(1, inputBytes.length - 2, inputBytes); + + assertEquals(5.0, m0.size().height); + assertEquals(1.0, m0.size().width); + + assertEquals(m0.get(0, 0)[0], m1.get(0, 0)[0]); + assertEquals(m0.get((int) m0.size().height - 1, 0)[0], m1.get((int) m1.size().height - 1, 0)[0]); + + assertEquals(3.0, m2.size().height); + assertEquals(1.0, m2.size().width); + + assertEquals(2.0, m2.get(0, 0)[0]); + assertEquals(3.0, m2.get(1, 0)[0]); + assertEquals(4.0, m2.get(2, 0)[0]); + } + + + public void testMatOfSubByteArray_BadArg() { + byte[] inputBytes = { 1,2,3,4,5 }; + + try { + MatOfByte m1 = new MatOfByte(-1, inputBytes.length, inputBytes); + fail("Missing check: offset < 0"); + } catch (IllegalArgumentException e) { + // pass + } + + try { + MatOfByte m1 = new MatOfByte(0, inputBytes.length, null); + fail("Missing check: NullPointerException"); + } catch (NullPointerException e) { + // pass + } + + try { + MatOfByte m1 = new MatOfByte(0, -1, inputBytes); + fail("Missing check: length < 0"); + } catch (IllegalArgumentException e) { + // pass + } + + try { + MatOfByte m1 = new MatOfByte(1, inputBytes.length, inputBytes); + fail("Missing check: buffer bounds"); + } catch (IllegalArgumentException e) { + // pass + } + } + +} diff --git a/modules/java/generator/src/cpp/Mat.cpp b/modules/java/generator/src/cpp/Mat.cpp index 6122a7e1e3..14f5497a1d 100644 --- a/modules/java/generator/src/cpp/Mat.cpp +++ b/modules/java/generator/src/cpp/Mat.cpp @@ -1863,7 +1863,7 @@ namespace { #undef JOCvT } -template static int mat_put(cv::Mat* m, int row, int col, int count, char* buff) +template static int mat_put(cv::Mat* m, int row, int col, int count, int offset, char* buff) { if(! m) return 0; if(! buff) return 0; @@ -1875,14 +1875,14 @@ template static int mat_put(cv::Mat* m, int row, int col, int count, if( m->isContinuous() ) { - memcpy(m->ptr(row, col), buff, count); + memcpy(m->ptr(row, col), buff + offset, count); } else { // row by row int num = (m->cols - col) * (int)m->elemSize(); // 1st partial row if(countptr(row++, col); while(count>0){ - memcpy(data, buff, num); + memcpy(data, buff + offset, num); count -= num; buff += num; num = m->cols * (int)m->elemSize(); @@ -1893,7 +1893,7 @@ template static int mat_put(cv::Mat* m, int row, int col, int count, return res; } -template static jint java_mat_put(JNIEnv* env, jlong self, jint row, jint col, jint count, ARRAY vals) +template static jint java_mat_put(JNIEnv* env, jlong self, jint row, jint col, jint count, jint offset, ARRAY vals) { static const char *method_name = JavaOpenCVTrait::put; try { @@ -1904,7 +1904,7 @@ template static jint java_mat_put(JNIEnv* env, jlong self, jint row if(me->rows<=row || me->cols<=col) return 0; // indexes out of range char* values = (char*)env->GetPrimitiveArrayCritical(vals, 0); - int res = mat_put::value_type>(me, row, col, count, values); + int res = mat_put::value_type>(me, row, col, count, offset, values); env->ReleasePrimitiveArrayCritical(vals, values, JNI_ABORT); return res; } catch(const std::exception &e) { @@ -1924,7 +1924,16 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutB JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutB (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jbyteArray vals) { - return java_mat_put(env, self, row, col, count, vals); + return java_mat_put(env, self, row, col, count, 0, vals); +} + +JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutBwOffset + (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jint offset, jbyteArray vals); + +JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutBwOffset + (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jint offset, jbyteArray vals) +{ + return java_mat_put(env, self, row, col, count, offset, vals); } JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutS @@ -1933,7 +1942,7 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutS JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutS (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jshortArray vals) { - return java_mat_put(env, self, row, col, count, vals); + return java_mat_put(env, self, row, col, count, 0, vals); } JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutI @@ -1942,7 +1951,7 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutI JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutI (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jintArray vals) { - return java_mat_put(env, self, row, col, count, vals); + return java_mat_put(env, self, row, col, count, 0, vals); } JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutF @@ -1951,7 +1960,7 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutF JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutF (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jfloatArray vals) { - return java_mat_put(env, self, row, col, count, vals); + return java_mat_put(env, self, row, col, count, 0, vals); } } // extern "C"