Fix/optimize Android put/get functions
authorGiles Payne <gilespayne@gmail.com>
Fri, 19 Feb 2021 08:10:11 +0000 (17:10 +0900)
committerGiles Payne <gilespayne@gmail.com>
Fri, 19 Feb 2021 08:10:11 +0000 (17:10 +0900)
modules/core/misc/java/test/MatTest.java
modules/java/generator/src/cpp/Mat.cpp
modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java
modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java

index 039aa39929ee9824ded4f40e5fb2fcd78fc8e7bf..00e7b7cb32038a7667f714cf6dbec264b7df5521 100644 (file)
@@ -455,6 +455,27 @@ public class MatTest extends OpenCVTestCase {
         bytesNum = sm.get(1, 1, buff11);
         assertEquals(4, bytesNum);
         assertTrue(Arrays.equals(new short[] {340, 341, 0, 0}, buff11));
+
+        Mat m2 = new Mat(new int[]{ 5, 6, 8 }, CvType.CV_16S);
+        short[] data = new short[(int)m2.total()];
+        for (int i = 0; i < data.length; i++ ) {
+            data[i] = (short)i;
+        }
+        m2.put(new int[] {0, 0, 0}, data);
+        Mat matNonContinuous = m2.submat(new Range[]{new Range(1,4), new Range(2,5), new Range(3,6)});
+        Mat matContinuous = matNonContinuous.clone();
+        short[] outNonContinuous = new short[(int)matNonContinuous.total()];
+        matNonContinuous.get(new int[] { 0, 0, 0 }, outNonContinuous);
+        short[] outContinuous = new short[(int)matNonContinuous.total()];
+        matContinuous.get(new int[] { 0, 0, 0 }, outContinuous);
+        assertArrayEquals(outNonContinuous, outContinuous);
+        Mat subMat2 = m2.submat(new Range[]{new Range(1,4), new Range(1,5), new Range(0,8)});
+        Mat subMatClone2 = subMat2.clone();
+        short[] outNonContinuous2 = new short[(int)subMat2.total()];
+        subMat2.get(new int[] { 0, 1, 1 }, outNonContinuous2);
+        short[] outContinuous2 = new short[(int)subMat2.total()];
+        subMatClone2.get(new int[] { 0, 1, 1 }, outContinuous2);
+        assertArrayEquals(outNonContinuous2, outContinuous2);
     }
 
     public void testGetNativeObjAddr() {
index 5203413ae4231b9bc53953cc0efc4c8ed0f2938e..d59fe4a5065ac7db8c8572ad2ed23dfc62f3257a 100644 (file)
@@ -2129,80 +2129,83 @@ namespace {
 #undef JOCvT
 }
 
-template<typename T> 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;
-
-    count *= sizeof(T);
-    int rest = ((m->rows - row) * m->cols - col) * (int)m->elemSize();
-    if(count>rest) count = rest;
-    int res = count;
+static size_t idx2Offset(cv::Mat* mat, std::vector<int>& indices) {
+    size_t offset = indices[0];
+    for (int dim=1; dim < mat->dims; dim++) {
+        offset = offset*mat->size[dim] + indices[dim];
+    }
+    return offset;
+}
 
-    if( m->isContinuous() )
-    {
-        memcpy(m->ptr(row, col), buff + offset, count);
-    } else {
-        // row by row
-        int num = (m->cols - col) * (int)m->elemSize(); // 1st partial row
-        if(count<num) num = count;
-        uchar* data = m->ptr(row++, col);
-        while(count>0){
-            memcpy(data, buff + offset, num);
-            count -= num;
-            buff += num;
-            num = m->cols * (int)m->elemSize();
-            if(count<num) num = count;
-            data = m->ptr(row++, 0);
-        }
+static void offset2Idx(cv::Mat* mat, size_t offset, std::vector<int>& indices) {
+    for (int dim=mat->dims-1; dim>=0; dim--) {
+        indices[dim] = offset % mat->size[dim];
+        offset = (offset - indices[dim]) / mat->size[dim];
     }
-    return res;
 }
 
 // returns true if final index was reached
-static bool updateIdx(cv::Mat* m, std::vector<int>& idx, int inc) {
-    for (int i=m->dims-1; i>=0; i--) {
-        if (inc == 0) return false;
-        idx[i] = (idx[i] + 1) % m->size[i];
-        inc--;
-    }
-    return true;
+static bool updateIdx(cv::Mat* mat, std::vector<int>& indices, size_t inc) {
+    size_t currentOffset = idx2Offset(mat, indices);
+    size_t newOffset = currentOffset + inc;
+    bool reachedEnd = newOffset>=(size_t)mat->total();
+    offset2Idx(mat, reachedEnd?0:newOffset, indices);
+    return reachedEnd;
 }
 
-template<typename T> static int mat_put_idx(cv::Mat* m, std::vector<int>& idx, int count, int offset, char* buff)
-{
+template<typename T> static int mat_copy_data(cv::Mat* m, std::vector<int>& idx, int count, char* buff, bool isPut) {
     if(! m) return 0;
     if(! buff) return 0;
 
-    count *= sizeof(T);
-    int rest = (int)m->elemSize();
-    for (int i = 0; i < m->dims; i++) {
-        rest *= (m->size[i] - idx[i]);
-    }
-    if(count>rest) count = rest;
-    int res = count;
+    size_t countBytes = count * sizeof(T);
+    size_t remainingBytes = (size_t)(m->total() - idx2Offset(m, idx))*m->elemSize();
+    countBytes = (countBytes>remainingBytes)?remainingBytes:countBytes;
+    int res = (int)countBytes;
 
     if( m->isContinuous() )
     {
-        memcpy(m->ptr(idx.data()), buff + offset, count);
+        if (isPut) {
+            memcpy(m->ptr(idx.data()), buff, countBytes);
+        } else {
+            memcpy(buff, m->ptr(idx.data()), countBytes);
+        }
     } else {
-        // dim by dim
-        int num = (m->size[m->dims-1] - idx[m->dims-1]) * (int)m->elemSize(); // 1st partial row
-        if(count<num) num = count;
+        size_t blockSize = m->size[m->dims-1] * m->elemSize();
+        size_t firstPartialBlockSize = (m->size[m->dims-1] - idx[m->dims-1]) * m->step[m->dims-1];;
+        for (int dim=m->dims-2; dim>=0 && blockSize == m->step[dim]; dim--) {
+            blockSize *= m->size[dim];
+            firstPartialBlockSize += (m->size[dim] - (idx[dim]+1)) * m->step[dim];
+        }
+        size_t copyCount = (countBytes<firstPartialBlockSize)?countBytes:firstPartialBlockSize;
         uchar* data = m->ptr(idx.data());
-        while(count>0){
-            memcpy(data, buff + offset, num);
-            updateIdx(m, idx, num / (int)m->elemSize());
-            count -= num;
-            buff += num;
-            num = m->size[m->dims-1] * (int)m->elemSize();
-            if(count<num) num = count;
+        while(countBytes>0){
+            if (isPut) {
+                memcpy(data, buff, copyCount);
+            } else {
+                memcpy(buff, data, copyCount);
+            }
+            updateIdx(m, idx, copyCount / m->elemSize());
+            countBytes -= copyCount;
+            buff += copyCount;
+            copyCount = countBytes<blockSize?countBytes:blockSize;
             data = m->ptr(idx.data());
         }
     }
     return res;
 }
 
+template<typename T> static int mat_put_idx(cv::Mat* m, std::vector<int>& idx, int count, int offset, char* buff)
+{
+    return mat_copy_data<T>(m, idx, count, buff + offset, true);
+}
+
+template<typename T> static int mat_put(cv::Mat* m, int row, int col, int count, int offset, char* buff)
+{
+    int indicesArray[] = { row, col };
+    std::vector<int> indices(indicesArray, indicesArray+2);
+    return mat_put_idx<T>(m, indices, count, offset, buff);
+}
+
 template<class ARRAY> 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<ARRAY>::put;
@@ -2455,68 +2458,16 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutDIdx
 
 } // extern "C"
 
-template<typename T> static int mat_get(cv::Mat* m, int row, int col, int count, char* buff)
+template<typename T> static int mat_get_idx(cv::Mat* m, std::vector<int>& idx, int count, char* buff)
 {
-    if(! m) return 0;
-    if(! buff) return 0;
-
-    int bytesToCopy = count * sizeof(T);
-    int bytesRestInMat = ((m->rows - row) * m->cols - col) * (int)m->elemSize();
-    if(bytesToCopy > bytesRestInMat) bytesToCopy = bytesRestInMat;
-    int res = bytesToCopy;
-
-    if( m->isContinuous() )
-    {
-        memcpy(buff, m->ptr(row, col), bytesToCopy);
-    } else {
-        // row by row
-        int bytesInRow = (m->cols - col) * (int)m->elemSize(); // 1st partial row
-        while(bytesToCopy > 0)
-        {
-            int len = std::min(bytesToCopy, bytesInRow);
-            memcpy(buff, m->ptr(row, col), len);
-            bytesToCopy -= len;
-            buff += len;
-            row++;
-            col = 0;
-            bytesInRow = m->cols * (int)m->elemSize();
-        }
-    }
-    return res;
+    return mat_copy_data<T>(m, idx, count, buff, false);
 }
 
-template<typename T> static int mat_get_idx(cv::Mat* m, std::vector<int>& idx, int count, char* buff)
+template<typename T> static int mat_get(cv::Mat* m, int row, int col, int count, char* buff)
 {
-    if(! m) return 0;
-    if(! buff) return 0;
-
-    count *= sizeof(T);
-    int rest = (int)m->elemSize();
-    for (int i = 0; i < m->dims; i++) {
-        rest *= (m->size[i] - idx[i]);
-    }
-    if(count>rest) count = rest;
-    int res = count;
-
-    if( m->isContinuous() )
-    {
-        memcpy(buff, m->ptr(idx.data()), count);
-    } else {
-        // dim by dim
-        int num = (m->size[m->dims-1] - idx[m->dims-1]) * (int)m->elemSize(); // 1st partial row
-        if(count<num) num = count;
-        uchar* data = m->ptr(idx.data());
-        while(count>0){
-            memcpy(buff, data, num);
-            updateIdx(m, idx, num / (int)m->elemSize());
-            count -= num;
-            buff += num;
-            num = m->size[m->dims-1] * (int)m->elemSize();
-            if(count<num) num = count;
-            data = m->ptr(idx.data());
-        }
-    }
-    return res;
+    int indicesArray[] = { row, col };
+    std::vector<int> indices(indicesArray, indicesArray+2);
+    return mat_get_idx<T>(m, indices, count, buff);
 }
 
 template<class ARRAY> static jint java_mat_get(JNIEnv* env, jlong self, jint row, jint col, jint count, ARRAY vals) {
index 5c6432c9f2a1f92fe096309c8d2d95a8000dd030..802bb2daa4ce6acf373a5d2d7a7a0f1801c80968 100644 (file)
@@ -279,19 +279,23 @@ public class OpenCVTestCase extends TestCase {
     }
 
     public static <E extends Number> void assertArrayEquals(E[] ar1, E[] ar2, double epsilon) {
-        if (ar1.length != ar2.length) {
-            fail("Arrays have different sizes.");
-        }
+        assertEquals(ar1.length, ar2.length);
 
         for (int i = 0; i < ar1.length; i++)
             assertEquals(ar1[i].doubleValue(), ar2[i].doubleValue(), epsilon);
             //assertTrue(Math.abs(ar1[i].doubleValue() - ar2[i].doubleValue()) <= epsilon);
     }
 
+    public static void assertArrayEquals(short[] ar1, short[] ar2) {
+        assertEquals(ar1.length, ar2.length);
+
+        for (int i = 0; i < ar1.length; i++)
+            assertEquals(ar1[i], ar2[i]);
+            //assertTrue(Math.abs(ar1[i].doubleValue() - ar2[i].doubleValue()) <= epsilon);
+    }
+
     public static void assertArrayEquals(double[] ar1, double[] ar2, double epsilon) {
-        if (ar1.length != ar2.length) {
-            fail("Arrays have different sizes.");
-        }
+        assertEquals(ar1.length, ar2.length);
 
         for (int i = 0; i < ar1.length; i++)
             assertEquals(ar1[i], ar2[i], epsilon);
index 5b303f38369490b0b587e52cbf68e686e8f3034f..3fd918dbfe93f6dbc99266c9e58c79d8a8277121 100644 (file)
@@ -305,19 +305,22 @@ public class OpenCVTestCase extends TestCase {
     }
 
     public static <E extends Number> void assertArrayEquals(E[] ar1, E[] ar2, double epsilon) {
-        if (ar1.length != ar2.length) {
-            fail("Arrays have different sizes.");
-        }
+        assertEquals(ar1.length, ar2.length);
 
         for (int i = 0; i < ar1.length; i++)
             assertEquals(ar1[i].doubleValue(), ar2[i].doubleValue(), epsilon);
             //assertTrue(Math.abs(ar1[i].doubleValue() - ar2[i].doubleValue()) <= epsilon);
     }
 
+    public static void assertArrayEquals(short[] ar1, short[] ar2) {
+        assertEquals(ar1.length, ar2.length);
+
+        for (int i = 0; i < ar1.length; i++)
+            assertEquals(ar1[i], ar2[i]);
+    }
+
     public static void assertArrayEquals(double[] ar1, double[] ar2, double epsilon) {
-        if (ar1.length != ar2.length) {
-            fail("Arrays have different sizes.");
-        }
+        assertEquals(ar1.length, ar2.length);
 
         for (int i = 0; i < ar1.length; i++)
             assertEquals(ar1[i], ar2[i], epsilon);