Baunsgaard commented on code in PR #1843:
URL: https://github.com/apache/systemds/pull/1843#discussion_r1327427076


##########
.github/workflows/javaTests.yml:
##########
@@ -81,39 +82,39 @@ jobs:
         ]
     name: ${{ matrix.tests }}
     steps:
-    - name: Checkout Repository
-      uses: actions/checkout@v4
-
-
-    - name: ${{ matrix.tests }}
-      uses: ./.github/action/
-      id: test
-      with:
-        test-to-run: ${{ matrix.tests }}
-
-    - name: Clean Github Artifact Name of Asterisks
-      run: |
-        ARTIFACT_NAME="transient_jacoco"
-        ARTIFACT_NAME+="-${{ matrix.os }}"
-        ARTIFACT_NAME+="-java-${{ matrix.java }}"
-        ARTIFACT_NAME+="-${{ matrix.javadist }}"
-        ARTIFACT_NAME+="-${{ matrix.tests }}"
-        ARTIFACT_NAME=${ARTIFACT_NAME//\*/x} # replace * with x
-        echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
-
-    - name: Save Java Test Coverage as Artifact
-      uses: actions/upload-artifact@v3
-      with:
-        name: ${{ env.ARTIFACT_NAME }}
-        path: target/jacoco.exec
-        retention-days: 1
+      - name: Checkout Repository
+        uses: actions/checkout@v4

Review Comment:
   we need to revert here, since the change is only syntax.



##########
src/main/cpp/imgUtils.cpp:
##########
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <iostream>
+#include <cmath>
+#include <cstring>
+#include "common.h"
+
+using namespace std;
+
+void printImage(const double* image, int rows, int cols) {
+    for (int i = 0; i < rows; i++) {
+        for (int j = 0; j < cols; j++) {
+            cout << image[i * cols + j] << " ";
+        }
+        cout <<  endl;
+    }
+    cout << "\n"<< endl;
+}
+
+void img_transform(const double* img_in, int orig_w, int orig_h, int out_w, 
int out_h, double a, double b, double c, double d,
+                     double e, double f, double fill_value, double* img_out) {
+    double divisor = a * e - b * d;
+    if (divisor == 0) {
+        std::cout << "Inverse matrix does not exist! Returning input." << 
std::endl;
+        for (int i = 0; i < orig_h; i++) {
+            for (int j = 0; j < orig_w; j++) {
+                img_out[i * orig_w + j] = img_in[i * orig_w + j];
+            }
+        }
+    } else {
+        // Create the inverted transformation matrix
+        double T_inv[9];
+        T_inv[0] = e / divisor;
+        T_inv[1] = -b / divisor;
+        T_inv[2] = (b * f - c * e) / divisor;
+        T_inv[3] = -d / divisor;
+        T_inv[4] = a / divisor;
+        T_inv[5] = (c * d - a * f) / divisor;
+        T_inv[6] = 0.0;
+        T_inv[7] = 0.0;
+        T_inv[8] = 1.0;
+
+        // Create the coordinates of output pixel centers linearized in 
row-major order
+        double* coords = new double[2 * out_w * out_h];
+        for (int i = 0; i < out_h; i++) {
+            for (int j = 0; j < out_w; j++) {
+                coords[2 * (i * out_w + j)] = j + 0.5;
+                coords[2 * (i * out_w + j) + 1] = i + 0.5;
+            }
+        }
+
+        // Perform matrix multiplication to compute sampling pixel indices
+        double* transformed_coords = new double[2 * out_w * out_h];
+        for (int i = 0; i < out_w * out_h; i++) {
+            double x = coords[2 * i];
+            double y = coords[2 * i + 1];
+            transformed_coords[2 * i] = std::floor(T_inv[0] * x + T_inv[1] * y 
+ T_inv[2]) + 1;
+            transformed_coords[2 * i + 1] = std::floor(T_inv[3] * x + T_inv[4] 
* y + T_inv[5]) + 1;
+        }
+
+        // Fill output image
+        for (int i = 0; i < out_h; i++) {
+            for (int j = 0; j < out_w; j++) {
+                int inx = static_cast<int>(transformed_coords[2 * (i * out_w + 
j)]) - 1;
+                int iny = static_cast<int>(transformed_coords[2 * (i * out_w + 
j) + 1]) - 1;
+                if (inx >= 0 && inx < orig_w && iny >= 0 && iny < orig_h) {
+                    img_out[i * out_w + j] = img_in[iny * orig_w + inx];
+                } else {
+                    img_out[i * out_w + j] = fill_value;
+                }
+            }
+        }
+
+        delete[] coords;
+        delete[] transformed_coords;
+    }
+}
+
+void imageRotate(double* img_in, int rows, int cols, double radians, double 
fill_value, double* img_out) {
+
+    // Translation matrix for moving the origin to the center of the image
+    double t1_data[] = {
+            1, 0, static_cast<double>(-cols)/2,
+            0, 1, static_cast<double>(-rows)/2,
+            0, 0, 1
+    };
+    double* t1 = t1_data;
+    // Translation matrix for moving the origin back to the top left corner
+    double t2_data[] = {
+            1, 0, static_cast<double>(cols)/2,
+            0, 1, static_cast<double>(rows)/2,
+            0, 0, 1
+    };
+    double* t2 = t2_data;
+    // The rotation matrix around the origin
+    double rot_data[] = {
+            cos(radians), sin(radians), 0,
+            -sin(radians), cos(radians), 0,
+            0, 0, 1
+    };
+    double* rot = rot_data;
+
+    // Combined transformation matrix
+    double m_data1[3*3];
+    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0, t2, 
3, rot, 3, 0.0, m_data1, 3);
+    double m_data2[3*3];
+    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0, 
m_data1, 3, t1, 3, 0.0, m_data2, 3);
+    double* m = m_data2;
+    // Transform image
+    img_transform(img_in,rows,cols,rows,cols,m[0], m[1], m[2], m[3], m[4], 
m[5], fill_value, img_out);
+}
+
+double* imageCutout(double* img_in, int rows, int cols, int x, int y, int 
width, int height, double fill_value) {
+    // Allocate memory for the output image using MKL
+   double* img_out = new double[rows * cols];
+
+    if (width < 1 || height < 1) {
+        // Invalid width or height, return the input image as it is
+        cblas_dcopy(rows * cols, img_in, 1, img_out, 1);
+    } else {
+        int end_x = x + width - 1;
+        int end_y = y + height - 1;
+
+        int start_x = std::max(1, x);
+        int start_y = std::max(1, y);
+        end_x = std::min(cols, end_x);
+        end_y = std::min(rows, end_y);
+
+        // Copy the input image to the output image using MKL
+        cblas_dcopy(rows * cols, img_in, 1, img_out, 1);
+
+        // Fill the cutout region with the fill_value
+        for (int i = start_y - 1; i < end_y; ++i) {
+            for (int j = start_x - 1; j < end_x; ++j) {
+                img_out[i * cols + j] = fill_value;
+            }
+        }
+    }
+
+    return img_out;
+}
+
+double* imageCrop(double* img_in, int orig_w, int orig_h, int w, int h, int 
x_offset, int y_offset) {
+    // Allocate memory for the output image
+    double* img_out = new double[w * h];
+
+    int start_h = (std::ceil((orig_h - h) / 2)) + y_offset - 1 ;
+    int end_h = (start_h + h - 1);
+    int start_w = (std::ceil((orig_w - w) / 2)) + x_offset - 1;
+    int end_w = (start_w + w - 1);
+
+    // Create a mask to identify the cropped region
+    double* mask = new double[orig_w * orig_h];
+    double* temp_mask = new double[w * h];
+
+    // Set mask elements to 0 outside the cropped region and 1 inside
+    memset(mask, 0, orig_w * orig_h * sizeof(double));
+    for(int i = 0; i < h * w; i++) {
+     temp_mask[i] = 1;
+    }
+
+    for (int i = start_h; i <= end_h; ++i) {
+        for (int j = start_w; j <= end_w; ++j) {
+            mask[i * orig_w + j] = temp_mask[(i - start_h) * w + (j - 
start_w)];
+        }
+    }
+
+    // Apply the mask to crop the image
+    for (int i = 0; i < h; ++i) {
+        for (int j = 0; j < w; ++j) {
+            img_out[i * w + j] = img_in[(start_h + i) * orig_w + (start_w + 
j)] * mask[(start_h + i) * orig_w + (start_w + j)];
+        }
+    }
+
+    // Free memory for the mask
+    delete[] mask;
+    delete[] temp_mask;
+
+    return img_out;
+}
+
+void img_translate(double* img_in, double offset_x, double offset_y,
+                   int in_w, int in_h, int out_w, int out_h, double 
fill_value, double* img_out) {
+    int w = out_w;
+    int h = out_h;
+
+    offset_x = round(offset_x);
+    offset_y = round(offset_y);
+
+
+    int start_x = 0 - static_cast<int>(offset_x);
+    int start_y = 0 - static_cast<int>(offset_y);
+    int end_x = std::max(w, out_w) - static_cast<int>(offset_x);
+    int end_y = std::max(h, out_h) - static_cast<int>(offset_y);
+
+    if (start_x < 0)
+        start_x = 0;
+    if (start_y < 0)
+        start_y = 0;
+
+    if (w < end_x)
+        end_x = w;
+    if (h < end_y)
+        end_y = h;
+
+    if (out_w < end_x + static_cast<int>(offset_x))
+        end_x = out_w - static_cast<int>(offset_x);
+    if (out_h < end_y + static_cast<int>(offset_y))
+        end_y = out_h - static_cast<int>(offset_y);
+
+    for (int y = 0; y < out_h; ++y) {
+        for (int x = 0; x < out_w; ++x) {
+            img_out[y * out_w + x] = fill_value;
+        }
+    }
+
+   if (start_x < end_x && start_y < end_y) {
+           for (int y = start_y + static_cast<int>(offset_y); y < end_y + 
static_cast<int>(offset_y); ++y) {
+                int x_in = (start_x > 0) ? start_x + 
static_cast<int>(offset_x): start_x;
+                int y_in = (start_y > 0 ) ? y : y - static_cast<int>(offset_y);
+               cblas_dcopy(end_x - start_x, &img_in[(x_in) + (y_in) * in_w],
+                           1, &img_out[y * out_w + start_x + 
static_cast<int>(offset_x)], 1);
+           }
+       }

Review Comment:
   indentation.



##########
src/main/cpp/imgUtils.h:
##########
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef IMAGE_UTILS_H
+#define IMAGE_UTILS_H
+#include <cmath>
+
+void printImage(const double* image, int rows, int cols);

Review Comment:
   consider removing



##########
src/main/cpp/imgUtils.cpp:
##########
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <iostream>
+#include <cmath>
+#include <cstring>
+#include "common.h"
+
+using namespace std;
+
+void printImage(const double* image, int rows, int cols) {
+    for (int i = 0; i < rows; i++) {
+        for (int j = 0; j < cols; j++) {
+            cout << image[i * cols + j] << " ";
+        }
+        cout <<  endl;
+    }
+    cout << "\n"<< endl;
+}
+
+void img_transform(const double* img_in, int orig_w, int orig_h, int out_w, 
int out_h, double a, double b, double c, double d,
+                     double e, double f, double fill_value, double* img_out) {
+    double divisor = a * e - b * d;
+    if (divisor == 0) {
+        std::cout << "Inverse matrix does not exist! Returning input." << 
std::endl;
+        for (int i = 0; i < orig_h; i++) {
+            for (int j = 0; j < orig_w; j++) {
+                img_out[i * orig_w + j] = img_in[i * orig_w + j];
+            }
+        }
+    } else {
+        // Create the inverted transformation matrix
+        double T_inv[9];
+        T_inv[0] = e / divisor;
+        T_inv[1] = -b / divisor;
+        T_inv[2] = (b * f - c * e) / divisor;
+        T_inv[3] = -d / divisor;
+        T_inv[4] = a / divisor;
+        T_inv[5] = (c * d - a * f) / divisor;
+        T_inv[6] = 0.0;
+        T_inv[7] = 0.0;
+        T_inv[8] = 1.0;
+
+        // Create the coordinates of output pixel centers linearized in 
row-major order
+        double* coords = new double[2 * out_w * out_h];
+        for (int i = 0; i < out_h; i++) {
+            for (int j = 0; j < out_w; j++) {
+                coords[2 * (i * out_w + j)] = j + 0.5;
+                coords[2 * (i * out_w + j) + 1] = i + 0.5;
+            }
+        }
+
+        // Perform matrix multiplication to compute sampling pixel indices
+        double* transformed_coords = new double[2 * out_w * out_h];

Review Comment:
   i do not know, but 
   
   can this be allocated as int?



##########
src/test/java/org/apache/sysds/test/functions/nativ/PerformanceComparison.java:
##########
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.functions.nativ;
+
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.test.TestUtils;
+import org.apache.sysds.utils.ImgNativeHelper;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.apache.sysds.utils.ImgNativeHelper.*;

Review Comment:
   no wildcard imports allowed.



##########
src/test/java/org/apache/sysds/test/functions/nativ/ImgUtilsTest.java:
##########
@@ -0,0 +1,521 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.functions.nativ;
+
+import org.apache.sysds.utils.ImgNativeHelper;
+import org.junit.Test;
+import static org.junit.Assert.assertArrayEquals;
+
+public class ImgUtilsTest {
+
+    @Test
+    public void runTestsWithOpenBlas() {
+        runTests("openblas");
+    }
+
+    //TODO configure intel mkl in test docker container to run this test
+    public void runTestsWithMKL() {
+        runTests("mkl");
+    }
+
+
+
+    public static void testImageRotation90And45(ImgNativeHelper 
imgNativeHelper) {
+
+        // Input image dimensions
+        int rows = 3;
+        int cols = 3;
+        // Input image
+        double[] img_in = {
+                1,2,3,
+                4,5,6,
+                7,8,9
+        };
+        // Rotation angle in radians
+        double radians = Math.PI / 2;
+        // Fill value for the output image
+        double fill_value = 0.0;
+        // Expected output image
+        double[] expected_img_out_90 = {
+                3,6,9,
+                2,5,8,
+                1,4,7
+        };
+
+        double[] expected_img_out_45 = {
+                2,3,6,
+                1,5,9,
+                4,7,8
+        };
+
+        // Create the output image array
+        double[] img_out = new double[rows * cols];
+        imgNativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, 
img_out);
+        assertArrayEquals(expected_img_out_90, img_out, 0.0001);
+        //rotate by 45
+        imgNativeHelper.imageRotate(img_in, rows, cols, radians/2, fill_value, 
img_out);
+        assertArrayEquals(expected_img_out_45, img_out, 0.0001);
+    }
+
+
+
+    public static void testImageRotation90And45_4x4(ImgNativeHelper 
imgNativeHelper) {
+
+        // Input image dimensions
+        int rows = 4;
+        int cols = 4;
+        // Input image
+        double[] img_in = {
+                1,2,3,5,
+                4,5,6,5,
+                7,8,9,5,
+                5,5,5,5,
+        };
+        // Rotation angle in radians
+        double radians = Math.PI / 2;
+        // Fill value for the output image
+        double fill_value = 0.0;
+        // Expected output image
+        double[] expected_img_out_90 = {
+                5,5,5,5,
+                3,6,9,5,
+                2,5,8,5,
+                1,4,7,5
+        };
+
+
+        double[] expected_img_out_45 = {
+                0,3,5,0,
+                2,5,6,5,
+                4,5,8,5,
+                0,7,5,0
+        };
+
+        // Create the output image array
+        double[] img_out = new double[rows * cols];
+        imgNativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, 
img_out);
+        assertArrayEquals(expected_img_out_90, img_out, 0.0001);
+        //rotate by 45
+        imgNativeHelper.imageRotate(img_in, rows, cols, radians/2, fill_value, 
img_out);
+        assertArrayEquals(expected_img_out_45, img_out, 0.0001);
+    }
+
+
+    public static void testImageRotation45(ImgNativeHelper imgNativeHelper) {
+        // Input image dimensions
+        int rows = 6;
+        int cols = 6;
+
+        // Input image
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,5.0,6.0,
+                5.0, 6.0, 7.0, 8.0,9.0,10.0,
+                9.0, 10.0, 11.0, 12.0,13.0,14.0,
+                13.0, 14.0, 15.0, 16.0,17.0,18.0,
+                17.0, 18.0, 19.0, 20.0, 21.0, 22.0,
+                21.0,22.0,23.0,24.0,25.0,26.0
+        };
+
+        // Rotation angle in radians
+        double radians = Math.PI / 4;
+
+        // Fill value for the output image
+        double fill_value = 0.0;
+
+        // Expected output image
+        double[] expected_img_out = {
+                0, 4, 5, 10 ,14, 0,
+                3, 4, 8 ,13 ,18 ,18,
+                2, 7, 12 ,16 ,17 ,22,
+                5, 10, 15 ,16, 20 ,25,
+                9, 13, 14, 19, 24 ,24,
+                0, 13, 17 ,22, 23 ,0
+        };
+
+        // Create the output image array
+        double[] img_out = new double[rows * cols];
+
+        // Rotate the image
+        imgNativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, 
img_out);
+
+        // Compare the output image with the expected image
+        assertArrayEquals(expected_img_out, img_out, 0.0001);
+    }
+
+
+    public static void testImageRotation180(ImgNativeHelper imgNativeHelper) {
+        // Input image dimensions
+        int rows = 4;
+        int cols = 4;
+
+        // Input image
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        // Rotation angle in radians
+        double radians = Math.PI;
+
+        // Fill value for the output image
+        double fill_value = 0.0;
+
+        // Expected output image
+        double[] expected_img_out = {
+                16.0, 15.0, 14.0, 13.0,
+                12.0, 11.0, 10.0, 9.0,
+                8.0, 7.0, 6.0, 5.0,
+                4.0, 3.0, 2.0, 1.0
+        };
+
+        // Create the output image array
+        double[] img_out = new double[rows * cols];
+
+        // Rotate the image
+        imgNativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, 
img_out);
+
+        // Compare the output image with the expected image
+        assertArrayEquals(expected_img_out, img_out, 0.0001);
+    }
+
+
+    public static void testCutoutImage(ImgNativeHelper imgNativeHelper) {
+        // Example input 2D matrix
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        int rows = 4;
+        int cols = 4;
+        int x = 2;
+        int y = 2;
+        int width = 3;
+        int height = 3;
+        double fill_value = 0.0;
+
+        // Perform image cutout using JNI
+        double[] img_out = imgNativeHelper.imageCutout(img_in, rows, cols, x, 
y, width, height, fill_value);
+
+        // Expected output image after cutout
+        double[] expectedOutput = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 0.0, 0.0, 0.0,
+                9.0, 0.0, 0.0, 0.0,
+                13.0, 0.0, 0.0, 0.0
+        };
+
+        // Check if the output image matches the expected output
+        assertArrayEquals(expectedOutput, img_out,0.0001);
+    }
+
+
+    public static void testCutoutImageNonSquare(ImgNativeHelper 
imgNativeHelper) {
+        // Example input 2D matrix
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0
+        };
+
+        int rows = 3;
+        int cols = 4;
+        int x = 1;
+        int y = 1;
+        int width = 3;
+        int height = 2;
+        double fill_value = 0.0;
+
+        // Perform image cutout using JNI
+        double[] img_out = imgNativeHelper.imageCutout(img_in, rows, cols, x, 
y, width, height, fill_value);
+
+        // Expected output image after cutout
+        double[] expectedOutput = {
+                0.0, 0.0, 0.0, 4.0,
+                0.0, 0.0, 0.0, 8.0,
+                9.0, 10.0, 11.0, 12.0
+        };
+
+        // Check if the output image matches the expected output
+        assertArrayEquals(expectedOutput, img_out, 0.0001);
+    }
+
+
+    public static void testImageCutoutInvalidCutout(ImgNativeHelper 
imgNativeHelper) {
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        int rows = 4;
+        int cols = 4;
+        int x = 3;
+        int y = 3;
+        int width = -2;
+        int height = 0;
+        double fill_value = 0.0;
+
+        double[] expectedOutput = img_in; // Expect no change since the cutout 
is invalid
+
+        double[] img_out = imgNativeHelper.imageCutout(img_in, rows, cols, x, 
y, width, height, fill_value);
+        assertArrayEquals(expectedOutput, img_out,0.0001);
+    }
+
+
+    public static void testImageCutoutNoCutout(ImgNativeHelper 
imgNativeHelper) {
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        int rows = 4;
+        int cols = 4;
+        int x = 3;
+        int y = 3;
+        int width = 1;
+        int height = 1;
+        double fill_value = 0.0;
+
+        double[] expectedOutput = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 0.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        double[] img_out = imgNativeHelper.imageCutout(img_in, rows, cols, x, 
y, width, height, fill_value);
+        assertArrayEquals(expectedOutput, img_out,0.0001);
+    }
+
+
+    public static void testImageCropValidCrop(ImgNativeHelper imgNativeHelper) 
{
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        int orig_w = 4;
+        int orig_h = 4;
+        int w = 2;
+        int h = 2;
+        int x_offset = 1;
+        int y_offset = 1;
+
+        double[] expectedOutput = {
+                6.0, 7.0,
+                10.0, 11.0
+        };
+
+        double[] img_out = imgNativeHelper.cropImage(img_in, orig_w, orig_h, 
w, h, x_offset, y_offset);
+
+        assertArrayEquals(expectedOutput, img_out,0.0001);     }
+
+
+    public static void testImageCropInvalidCrop(ImgNativeHelper 
imgNativeHelper) {
+        double[] img_in = {
+                1.0, 2.0, 3.0, 4.0,
+                5.0, 6.0, 7.0, 8.0,
+                9.0, 10.0, 11.0, 12.0,
+                13.0, 14.0, 15.0, 16.0
+        };
+
+        int orig_w = 4;
+        int orig_h = 4;
+        int w = 5;
+        int h = 5;
+        int x_offset = 1;
+        int y_offset = 1;
+
+        double[] expectedOutput = img_in; // Expect no change since the crop 
is invalid
+
+        double[] img_out = imgNativeHelper.cropImage(img_in, orig_w, orig_h, 
w, h, x_offset, y_offset);
+        assertArrayEquals(expectedOutput, img_out,0.0001);
+    }
+
+
+    public void testImgTranslate(ImgNativeHelper imgNativeHelper) {
+        int in_w = 5;
+        int in_h = 5;
+        int out_w = 7;
+        int out_h = 7;
+        double fill_value = 0.0;
+
+        double[] img_in = new double[in_w * in_h];
+        for (int i = 0; i < in_w * in_h; ++i) {
+            img_in[i] = i + 1; // Filling input image with sequential values
+        }
+
+        double[] img_out = new double[out_w * out_h];
+
+        double offset_x = 1.5;
+        double offset_y = 1.5;
+
+        imgNativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, 
out_w, out_h, fill_value, img_out);
+
+        // Expected output based on the given offsets and fill value
+        double[] expectedOutput = {
+                0,0,0,0,0,0,0,
+                0,0,0,0,0,0,0,
+                0,0,1,2,3,4,5,
+                0,0,6,7,8,9,10,
+                0,0,11,12,13,14,15,
+                0,0,16,17,18,19,20,
+                0,0,21,22,23,24,25
+        };
+
+        assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays 
with a small epsilon
+    }
+
+
+    public static void testImgTranslateNegativeOffsets(ImgNativeHelper 
imgNativeHelper) {
+        int in_w = 5;
+        int in_h = 5;
+        int out_w = 6;
+        int out_h = 6;
+        double fill_value = 0.0;
+
+        double[] img_in = new double[in_w * in_h];
+        for (int i = 0; i < in_w * in_h; ++i) {
+            img_in[i] = i + 1; // Filling input image with sequential values
+        }
+
+        double[] img_out = new double[out_w * out_h];
+
+        double offset_x = -0.5; // Negative offset in X direction
+        double offset_y = -0.5; // Negative offset in Y direction
+
+        imgNativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, 
out_w, out_h, fill_value, img_out);
+
+        // Expected output based on the given offsets and fill value
+        double[] expectedOutput = {
+                1,2,3,4,5,0,
+                6,7,8,9,10,0,
+                11,12,13,14,15,0,
+                16,17,18,19,20,0,
+                21,22,23,24,25,0,
+                0,0,0,0,0,0,
+        };
+
+        assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays 
with a small epsilon
+    }
+
+
+    public static void 
testImgTranslatePositiveAndNegativeOffsets(ImgNativeHelper imgNativeHelper) {
+        int in_w = 5;
+        int in_h = 5;
+        int out_w = 6;
+        int out_h = 6;
+        double fill_value = 0.0;
+
+        double[] img_in = new double[in_w * in_h];
+        for (int i = 0; i < in_w * in_h; ++i) {
+            img_in[i] = i + 1; // Filling input image with sequential values
+        }
+
+        double[] img_out = new double[out_w * out_h];
+
+        double offset_x = -0.5; // Negative offset in X direction
+        double offset_y = 0.5; // Negative offset in Y direction
+
+        imgNativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, 
out_w, out_h, fill_value, img_out);
+
+        // Expected output based on the given offsets and fill value
+        double[] expectedOutput = {
+                0,0,0,0,0,0,
+                1,2,3,4,5,0,
+                6,7,8,9,10,0,
+                11,12,13,14,15,0,
+                16,17,18,19,20,0,
+                21,22,23,24,25,0,
+        };
+
+        assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays 
with a small epsilon
+    }
+
+
+    public static void 
testImgTranslatePositiveAndNegativeOffsets2(ImgNativeHelper imgNativeHelper) {
+        int in_w = 5;
+        int in_h = 5;
+        int out_w = 6;
+        int out_h = 6;
+        double fill_value = 0.0;
+
+        double[] img_in = new double[in_w * in_h];
+        for (int i = 0; i < in_w * in_h; ++i) {
+            img_in[i] = i + 1; // Filling input image with sequential values
+        }
+
+        double[] img_out = new double[out_w * out_h];
+
+        double offset_x = 0.5; // Negative offset in X direction
+        double offset_y = -0.5; // Negative offset in Y direction
+
+        imgNativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, 
out_w, out_h, fill_value, img_out);
+
+        // Expected output based on the given offsets and fill value
+        double[] expectedOutput = {
+                0,1,2,3,4,5,
+                0,6,7,8,9,10,
+                0,11,12,13,14,15,
+                0,16,17,18,19,20,
+                0,21,22,23,24,25,
+                0,0,0,0,0,0,
+        };
+
+        assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays 
with a small epsilon
+    }
+
+    public void runTests(String blasType) {

Review Comment:
   i understand why you designed it this way, but please make the test 
integrate with JUnit,
   
   To do this remove the `runTests` method and make all your static calls 
annotated with @ Test.
   Then make the imgNativeHelper a field in this class that the constructor 
instantiate.
   Then finally the class needs to be changed into a parameterized class, that 
calls the constructor selecting either BLAS or MKL.
   



##########
src/main/cpp/systemds_img.cpp:
##########
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include "common.h"
+#include "libmatrixdnn.h"
+#include "libmatrixmult.h"
+#include "systemds.h"
+#include "imgUtils.h"
+
+JNIEXPORT void JNICALL Java_org_apache_sysds_utils_ImgNativeHelper_imageRotate
+    (JNIEnv* env, jclass clazz, jdoubleArray img_in, jint rows, jint cols, 
jdouble radians, jdouble fill_value, jdoubleArray img_out) {
+    
+    if (rows != cols) {
+        jclass exceptionClass = 
env->FindClass("java/lang/IllegalArgumentException");
+        if (exceptionClass != NULL) {
+            env->ThrowNew(exceptionClass, "Image dimensions must be equal");
+            return;
+        }
+    }
+
+    jsize num_pixels = env->GetArrayLength(img_in);
+    jdouble* img_in_data = env->GetDoubleArrayElements(img_in, NULL);
+    jdouble* img_out_data = new jdouble[num_pixels];
+    imageRotate(img_in_data, rows, cols, radians, fill_value, img_out_data);
+    env->SetDoubleArrayRegion(img_out, 0, num_pixels, img_out_data);
+    delete[] img_out_data;
+    env->ReleaseDoubleArrayElements(img_in, img_in_data, JNI_ABORT);
+}
+
+JNIEXPORT jdoubleArray JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_imageCutout
+    (JNIEnv* env, jclass cls, jdoubleArray img_in, jint rows, jint cols, jint 
x, jint y, jint width, jint height, jdouble fill_value) {
+   
+    jdouble* img_in_arr = env->GetDoubleArrayElements(img_in, nullptr);
+    jdouble* img_out_arr = imageCutout(img_in_arr, rows, cols, x, y, width, 
height, fill_value);
+    jdoubleArray img_out = env->NewDoubleArray(rows * cols);
+
+    env->SetDoubleArrayRegion(img_out, 0, rows * cols, img_out_arr);
+    env->ReleaseDoubleArrayElements(img_in, img_in_arr, JNI_ABORT);
+    delete[] img_out_arr;
+
+    return img_out;
+}
+
+JNIEXPORT jdoubleArray JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_cropImage(JNIEnv *env, jclass,
+    jdoubleArray img_in, jint orig_w, jint orig_h, jint w, jint h, jint 
x_offset, jint y_offset) {
+    jsize length = env->GetArrayLength(img_in);
+    double *img_in_array = env->GetDoubleArrayElements(img_in, 0);
+
+    int start_h = (ceil((orig_h - h) / 2)) + y_offset - 1;
+    int end_h = (start_h + h - 1);
+    int start_w = (ceil((orig_w - w) / 2)) + x_offset - 1;
+    int end_w = (start_w + w - 1);
+
+    jdoubleArray img_out_java;
+    if (start_h < 0 || end_h >= orig_h || start_w < 0 || end_w >= orig_w) {
+      img_out_java = env->NewDoubleArray(orig_w * orig_h);
+      env->SetDoubleArrayRegion(img_out_java, 0, orig_w * orig_h, 
img_in_array);
+
+    }else {
+        double *img_out = imageCrop(img_in_array, orig_w, orig_h, w, h, 
x_offset, y_offset);
+
+         if (img_out == nullptr) {
+                 return nullptr;
+             }
+
+         img_out_java = env->NewDoubleArray(w * h);
+         env->SetDoubleArrayRegion(img_out_java, 0, w * h, img_out);
+         delete[] img_out;
+     }
+
+     env->ReleaseDoubleArrayElements(img_in, img_in_array, 0);
+
+    return img_out_java;
+}
+
+/*JNIEXPORT jdoubleArray JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_shearImage(JNIEnv *env, jclass,
+    jdoubleArray img_in, jint width, jint height, jdouble shear_x, jdouble 
shear_y, jdouble fill_value) {
+
+   
+    jsize length = env->GetArrayLength(img_in);
+    double *img_in_array = env->GetDoubleArrayElements(img_in, 0);
+
+   
+    double* img_out = img_transform(img_in_array, width, height, shear_x, 
shear_y, fill_value);
+
+   
+    env->ReleaseDoubleArrayElements(img_in, img_in_array, 0);
+
+    if (img_out == nullptr) {
+        return nullptr;
+    }
+
+   
+    jdoubleArray img_out_java = env->NewDoubleArray(width * height);
+    env->SetDoubleArrayRegion(img_out_java, 0, width * height, img_out);
+
+   
+    delete[] img_out;
+
+    return img_out_java;
+}*/
+
+JNIEXPORT void JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_imgTranslate(JNIEnv *env, jclass 
cls,
+                                                          jdoubleArray img_in, 
jdouble offset_x, jdouble offset_y,
+                                                          jint in_w, jint 
in_h, jint out_w, jint out_h,
+                                                          jdouble fill_value, 
jdoubleArray img_out) {
+   
+    jdouble* j_img_in = env->GetDoubleArrayElements(img_in, nullptr);
+    jdouble* j_img_out = env->GetDoubleArrayElements(img_out, nullptr);
+    img_translate(j_img_in, offset_x, offset_y, in_w, in_h, out_w, out_h, 
fill_value, j_img_out);
+    env->ReleaseDoubleArrayElements(img_in, j_img_in, 0);
+    env->ReleaseDoubleArrayElements(img_out, j_img_out, 0);
+}

Review Comment:
   newline in the end.



##########
src/main/java/org/apache/sysds/utils/ImgNativeHelper.java:
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.utils;
+
+import org.apache.commons.lang.SystemUtils;
+
+import java.io.File;
+
+public class ImgNativeHelper extends NativeHelper {

Review Comment:
   you can add doc on the Helper class itself.
   i would suggest a hint at where to find the CPP files.



##########
src/main/cpp/imgUtils.cpp:
##########
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <iostream>
+#include <cmath>
+#include <cstring>
+#include "common.h"
+
+using namespace std;
+
+void printImage(const double* image, int rows, int cols) {
+    for (int i = 0; i < rows; i++) {
+        for (int j = 0; j < cols; j++) {
+            cout << image[i * cols + j] << " ";
+        }
+        cout <<  endl;
+    }
+    cout << "\n"<< endl;
+}
+
+void img_transform(const double* img_in, int orig_w, int orig_h, int out_w, 
int out_h, double a, double b, double c, double d,
+                     double e, double f, double fill_value, double* img_out) {
+    double divisor = a * e - b * d;
+    if (divisor == 0) {
+        std::cout << "Inverse matrix does not exist! Returning input." << 
std::endl;
+        for (int i = 0; i < orig_h; i++) {
+            for (int j = 0; j < orig_w; j++) {
+                img_out[i * orig_w + j] = img_in[i * orig_w + j];
+            }
+        }
+    } else {
+        // Create the inverted transformation matrix
+        double T_inv[9];
+        T_inv[0] = e / divisor;
+        T_inv[1] = -b / divisor;
+        T_inv[2] = (b * f - c * e) / divisor;
+        T_inv[3] = -d / divisor;
+        T_inv[4] = a / divisor;
+        T_inv[5] = (c * d - a * f) / divisor;
+        T_inv[6] = 0.0;
+        T_inv[7] = 0.0;
+        T_inv[8] = 1.0;
+
+        // Create the coordinates of output pixel centers linearized in 
row-major order
+        double* coords = new double[2 * out_w * out_h];
+        for (int i = 0; i < out_h; i++) {
+            for (int j = 0; j < out_w; j++) {
+                coords[2 * (i * out_w + j)] = j + 0.5;
+                coords[2 * (i * out_w + j) + 1] = i + 0.5;
+            }
+        }
+
+        // Perform matrix multiplication to compute sampling pixel indices
+        double* transformed_coords = new double[2 * out_w * out_h];
+        for (int i = 0; i < out_w * out_h; i++) {
+            double x = coords[2 * i];
+            double y = coords[2 * i + 1];
+            transformed_coords[2 * i] = std::floor(T_inv[0] * x + T_inv[1] * y 
+ T_inv[2]) + 1;
+            transformed_coords[2 * i + 1] = std::floor(T_inv[3] * x + T_inv[4] 
* y + T_inv[5]) + 1;
+        }
+
+        // Fill output image
+        for (int i = 0; i < out_h; i++) {
+            for (int j = 0; j < out_w; j++) {
+                int inx = static_cast<int>(transformed_coords[2 * (i * out_w + 
j)]) - 1;
+                int iny = static_cast<int>(transformed_coords[2 * (i * out_w + 
j) + 1]) - 1;
+                if (inx >= 0 && inx < orig_w && iny >= 0 && iny < orig_h) {
+                    img_out[i * out_w + j] = img_in[iny * orig_w + inx];
+                } else {
+                    img_out[i * out_w + j] = fill_value;
+                }
+            }
+        }
+
+        delete[] coords;
+        delete[] transformed_coords;
+    }
+}
+
+void imageRotate(double* img_in, int rows, int cols, double radians, double 
fill_value, double* img_out) {
+
+    // Translation matrix for moving the origin to the center of the image
+    double t1_data[] = {
+            1, 0, static_cast<double>(-cols)/2,
+            0, 1, static_cast<double>(-rows)/2,
+            0, 0, 1
+    };
+    double* t1 = t1_data;
+    // Translation matrix for moving the origin back to the top left corner
+    double t2_data[] = {
+            1, 0, static_cast<double>(cols)/2,
+            0, 1, static_cast<double>(rows)/2,
+            0, 0, 1
+    };
+    double* t2 = t2_data;
+    // The rotation matrix around the origin
+    double rot_data[] = {
+            cos(radians), sin(radians), 0,
+            -sin(radians), cos(radians), 0,
+            0, 0, 1
+    };
+    double* rot = rot_data;
+
+    // Combined transformation matrix
+    double m_data1[3*3];
+    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0, t2, 
3, rot, 3, 0.0, m_data1, 3);
+    double m_data2[3*3];
+    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0, 
m_data1, 3, t1, 3, 0.0, m_data2, 3);
+    double* m = m_data2;
+    // Transform image
+    img_transform(img_in,rows,cols,rows,cols,m[0], m[1], m[2], m[3], m[4], 
m[5], fill_value, img_out);
+}
+
+double* imageCutout(double* img_in, int rows, int cols, int x, int y, int 
width, int height, double fill_value) {
+    // Allocate memory for the output image using MKL
+   double* img_out = new double[rows * cols];
+
+    if (width < 1 || height < 1) {
+        // Invalid width or height, return the input image as it is
+        cblas_dcopy(rows * cols, img_in, 1, img_out, 1);
+    } else {
+        int end_x = x + width - 1;
+        int end_y = y + height - 1;
+
+        int start_x = std::max(1, x);
+        int start_y = std::max(1, y);
+        end_x = std::min(cols, end_x);
+        end_y = std::min(rows, end_y);
+
+        // Copy the input image to the output image using MKL
+        cblas_dcopy(rows * cols, img_in, 1, img_out, 1);
+
+        // Fill the cutout region with the fill_value
+        for (int i = start_y - 1; i < end_y; ++i) {
+            for (int j = start_x - 1; j < end_x; ++j) {
+                img_out[i * cols + j] = fill_value;
+            }
+        }
+    }
+
+    return img_out;
+}
+
+double* imageCrop(double* img_in, int orig_w, int orig_h, int w, int h, int 
x_offset, int y_offset) {
+    // Allocate memory for the output image
+    double* img_out = new double[w * h];
+
+    int start_h = (std::ceil((orig_h - h) / 2)) + y_offset - 1 ;
+    int end_h = (start_h + h - 1);
+    int start_w = (std::ceil((orig_w - w) / 2)) + x_offset - 1;
+    int end_w = (start_w + w - 1);
+
+    // Create a mask to identify the cropped region
+    double* mask = new double[orig_w * orig_h];
+    double* temp_mask = new double[w * h];
+
+    // Set mask elements to 0 outside the cropped region and 1 inside
+    memset(mask, 0, orig_w * orig_h * sizeof(double));
+    for(int i = 0; i < h * w; i++) {
+     temp_mask[i] = 1;
+    }
+
+    for (int i = start_h; i <= end_h; ++i) {
+        for (int j = start_w; j <= end_w; ++j) {
+            mask[i * orig_w + j] = temp_mask[(i - start_h) * w + (j - 
start_w)];
+        }
+    }
+
+    // Apply the mask to crop the image
+    for (int i = 0; i < h; ++i) {
+        for (int j = 0; j < w; ++j) {
+            img_out[i * w + j] = img_in[(start_h + i) * orig_w + (start_w + 
j)] * mask[(start_h + i) * orig_w + (start_w + j)];
+        }
+    }
+
+    // Free memory for the mask
+    delete[] mask;
+    delete[] temp_mask;
+
+    return img_out;
+}
+
+void img_translate(double* img_in, double offset_x, double offset_y,

Review Comment:
   cast the offsets in the first line rather than every call to offset_x and y, 
and maybe consider changing the API to use int offsets.



##########
src/main/cpp/systemds_img.cpp:
##########
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include "common.h"
+#include "libmatrixdnn.h"
+#include "libmatrixmult.h"
+#include "systemds.h"
+#include "imgUtils.h"
+
+JNIEXPORT void JNICALL Java_org_apache_sysds_utils_ImgNativeHelper_imageRotate
+    (JNIEnv* env, jclass clazz, jdoubleArray img_in, jint rows, jint cols, 
jdouble radians, jdouble fill_value, jdoubleArray img_out) {
+    
+    if (rows != cols) {
+        jclass exceptionClass = 
env->FindClass("java/lang/IllegalArgumentException");
+        if (exceptionClass != NULL) {
+            env->ThrowNew(exceptionClass, "Image dimensions must be equal");
+            return;
+        }
+    }
+
+    jsize num_pixels = env->GetArrayLength(img_in);
+    jdouble* img_in_data = env->GetDoubleArrayElements(img_in, NULL);
+    jdouble* img_out_data = new jdouble[num_pixels];
+    imageRotate(img_in_data, rows, cols, radians, fill_value, img_out_data);
+    env->SetDoubleArrayRegion(img_out, 0, num_pixels, img_out_data);
+    delete[] img_out_data;
+    env->ReleaseDoubleArrayElements(img_in, img_in_data, JNI_ABORT);
+}
+
+JNIEXPORT jdoubleArray JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_imageCutout
+    (JNIEnv* env, jclass cls, jdoubleArray img_in, jint rows, jint cols, jint 
x, jint y, jint width, jint height, jdouble fill_value) {
+   
+    jdouble* img_in_arr = env->GetDoubleArrayElements(img_in, nullptr);
+    jdouble* img_out_arr = imageCutout(img_in_arr, rows, cols, x, y, width, 
height, fill_value);
+    jdoubleArray img_out = env->NewDoubleArray(rows * cols);
+
+    env->SetDoubleArrayRegion(img_out, 0, rows * cols, img_out_arr);
+    env->ReleaseDoubleArrayElements(img_in, img_in_arr, JNI_ABORT);
+    delete[] img_out_arr;
+
+    return img_out;
+}
+
+JNIEXPORT jdoubleArray JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_cropImage(JNIEnv *env, jclass,
+    jdoubleArray img_in, jint orig_w, jint orig_h, jint w, jint h, jint 
x_offset, jint y_offset) {
+    jsize length = env->GetArrayLength(img_in);
+    double *img_in_array = env->GetDoubleArrayElements(img_in, 0);
+
+    int start_h = (ceil((orig_h - h) / 2)) + y_offset - 1;
+    int end_h = (start_h + h - 1);
+    int start_w = (ceil((orig_w - w) / 2)) + x_offset - 1;
+    int end_w = (start_w + w - 1);
+
+    jdoubleArray img_out_java;
+    if (start_h < 0 || end_h >= orig_h || start_w < 0 || end_w >= orig_w) {
+      img_out_java = env->NewDoubleArray(orig_w * orig_h);
+      env->SetDoubleArrayRegion(img_out_java, 0, orig_w * orig_h, 
img_in_array);
+
+    }else {
+        double *img_out = imageCrop(img_in_array, orig_w, orig_h, w, h, 
x_offset, y_offset);
+
+         if (img_out == nullptr) {
+                 return nullptr;
+             }
+
+         img_out_java = env->NewDoubleArray(w * h);
+         env->SetDoubleArrayRegion(img_out_java, 0, w * h, img_out);
+         delete[] img_out;
+     }
+
+     env->ReleaseDoubleArrayElements(img_in, img_in_array, 0);
+
+    return img_out_java;
+}
+
+/*JNIEXPORT jdoubleArray JNICALL 
Java_org_apache_sysds_utils_ImgNativeHelper_shearImage(JNIEnv *env, jclass,
+    jdoubleArray img_in, jint width, jint height, jdouble shear_x, jdouble 
shear_y, jdouble fill_value) {
+
+   
+    jsize length = env->GetArrayLength(img_in);
+    double *img_in_array = env->GetDoubleArrayElements(img_in, 0);
+
+   
+    double* img_out = img_transform(img_in_array, width, height, shear_x, 
shear_y, fill_value);
+
+   
+    env->ReleaseDoubleArrayElements(img_in, img_in_array, 0);
+
+    if (img_out == nullptr) {
+        return nullptr;
+    }
+
+   
+    jdoubleArray img_out_java = env->NewDoubleArray(width * height);
+    env->SetDoubleArrayRegion(img_out_java, 0, width * height, img_out);
+
+   
+    delete[] img_out;
+
+    return img_out_java;
+}*/

Review Comment:
   remove commented code.



##########
src/main/cpp/build.sh:
##########
@@ -46,6 +47,14 @@ if ! [ -x "$(command -v patchelf)" ]; then
   exit 1
 fi
 
+# Check if OpenBLAS is installed
+if ! ldconfig -p | grep -q libopenblas; then
+  echo "OpenBLAS not found. Installing OpenBLAS..."
+
+  apt-get update
+  apt-get install libopenblas-dev -y
+fi

Review Comment:
   add comment on MKL install.



##########
src/main/cpp/lib/libsystemds_mkl-Linux-x86_64.so:
##########


Review Comment:
   maybe not add these?



##########
src/main/cpp/imgUtils.cpp:
##########
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <iostream>
+#include <cmath>
+#include <cstring>
+#include "common.h"
+
+using namespace std;
+
+void printImage(const double* image, int rows, int cols) {
+    for (int i = 0; i < rows; i++) {
+        for (int j = 0; j < cols; j++) {
+            cout << image[i * cols + j] << " ";
+        }
+        cout <<  endl;
+    }
+    cout << "\n"<< endl;
+}

Review Comment:
   i suggest removing this debugging printImage.



##########
src/main/java/org/apache/sysds/utils/ImgNativeHelper.java:
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.utils;
+
+import org.apache.commons.lang.SystemUtils;
+
+import java.io.File;
+
+public class ImgNativeHelper extends NativeHelper {
+    private final String blasType;
+
+    public ImgNativeHelper(String blasType) {
+        if(blasType != null) this.blasType = blasType;
+        else this.blasType = "openblas";
+        loadNativeLibrary();
+    }
+
+    // Method to load the native library based on the specified BLAS type
+    private void loadNativeLibrary() {
+        try {
+            if (SystemUtils.IS_OS_LINUX) {
+                String libname = blasType + "-Linux-x86_64.so";
+                
System.load(System.getProperty("user.dir")+"/src/main/cpp/lib/libsystemds_" + 
libname);
+                
//System.load("/home/runner/work/systemds/systemds/src/main/cpp/lib/libsystemds_openblas-Linux-x86_64.so");
+            } else if (SystemUtils.IS_OS_WINDOWS) {
+                String libname = blasType + "-Windows-x86_64.dll";
+                System.load("/src/main/cpp/lib/".replace("/", File.separator) 
+ "libsystemds_" + libname);
+            } else if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX) {
+               // String libname = "systemds_" + blasType + "-Darwin-x86_64";
+                
System.load(System.getProperty("user.dir")+"/src/main/cpp/lib/libsystemds_" + 
blasType + "-Darwin-x86_64.dylib");

Review Comment:
   same here, remove commented code, 
   



##########
src/main/java/org/apache/sysds/utils/ImgNativeHelper.java:
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.utils;
+
+import org.apache.commons.lang.SystemUtils;
+
+import java.io.File;
+
+public class ImgNativeHelper extends NativeHelper {
+    private final String blasType;
+
+    public ImgNativeHelper(String blasType) {
+        if(blasType != null) this.blasType = blasType;
+        else this.blasType = "openblas";
+        loadNativeLibrary();
+    }
+
+    // Method to load the native library based on the specified BLAS type
+    private void loadNativeLibrary() {
+        try {
+            if (SystemUtils.IS_OS_LINUX) {
+                String libname = blasType + "-Linux-x86_64.so";
+                
System.load(System.getProperty("user.dir")+"/src/main/cpp/lib/libsystemds_" + 
libname);
+                
//System.load("/home/runner/work/systemds/systemds/src/main/cpp/lib/libsystemds_openblas-Linux-x86_64.so");
+            } else if (SystemUtils.IS_OS_WINDOWS) {
+                String libname = blasType + "-Windows-x86_64.dll";
+                System.load("/src/main/cpp/lib/".replace("/", File.separator) 
+ "libsystemds_" + libname);
+            } else if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX) {
+               // String libname = "systemds_" + blasType + "-Darwin-x86_64";
+                
System.load(System.getProperty("user.dir")+"/src/main/cpp/lib/libsystemds_" + 
blasType + "-Darwin-x86_64.dylib");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();

Review Comment:
   i would like this to throw the exception, not just print stack trace.
   
   you can wrap the exception in a `throw new RuntimeException(e);`



##########
src/test/java/org/apache/sysds/test/functions/nativ/PerformanceComparison.java:
##########
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.functions.nativ;
+
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.test.TestUtils;
+import org.apache.sysds.utils.ImgNativeHelper;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.apache.sysds.utils.ImgNativeHelper.*;
+
+public class PerformanceComparison extends AutomatedTestBase {
+
+    private final static String TEST_DIR = "functions/builtin/";
+    private final static String TEST_CLASS_DIR = TEST_DIR + 
PerformanceComparison.class.getSimpleName() + "/";
+
+    private final static double eps = 1e-10;
+    private final static int rows = 512;
+    private final static int cols = 512;
+    private final static double spSparse = 0.1;
+    private final static double spDense = 0.9;
+    private final static int x_offset = 12;
+    private final static int y_offset = 24;
+    private final static float size = 0.8f;
+
+    private final ImgNativeHelper imgNativeHelper = new 
ImgNativeHelper("openblas");
+
+    @Override
+    public void setUp() {
+      //  addTestConfiguration(TEST_NAME,new TestConfiguration(TEST_CLASS_DIR, 
TEST_NAME,new String[]{"B"}));
+    }
+
+    private static double randomDouble(double min, double max) {
+        return min + (max - min) * new Random().nextDouble();
+    }
+
+    // Function to generate a random square matrix of size n x n
+    public static double[] generateRandomMatrix(int rows, int cols, double 
min, double max, double sparsity, long seed) {
+        double[] matrix = new double[rows * cols];
+        Random random = (seed == -1) ? TestUtils.random : new Random(seed);
+
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < cols; j++) {
+                int index = i * cols + j; // Calculate the index for the 1D 
array
+                if (random.nextDouble() > sparsity) {
+                    continue;
+                }
+                matrix[index] = (random.nextDouble() * (max - min) + min);
+            }
+        }
+
+        return matrix;
+    }
+
+    // Function to print a square matrix
+    private static void printMatrix(double[] matrix, int n) {
+        for (int i = 0; i < n; i++) {
+            for (int j = 0; j < n; j++) {
+                System.out.printf("%.4f\t", matrix[i * n + j]);
+            }
+            System.out.println();
+        }
+    }
+
+    public void runBlasTests(boolean sparse,int n, int seed) {
+    double spSparse = 0.1;

Review Comment:
   indentation



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to