This is an automated email from the ASF dual-hosted git repository.
baunsgaard pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/systemds.git
The following commit(s) were added to refs/heads/main by this push:
new 04b5048b17 [SYSTEMDS-3545] Image Transform Linearized
04b5048b17 is described below
commit 04b5048b171c021d8e98323cdb700d49ab66cd67
Author: Sebastian Baunsgaard <[email protected]>
AuthorDate: Mon Oct 16 14:34:09 2023 +0200
[SYSTEMDS-3545] Image Transform Linearized
This commit adds support for linearized transformations of images.
This is a fundamental building block for many of the different
transformations, and will in the future be build upon for efficient
image transformations.
It rewire the transformation matrix, to construct a sparse Matrix
that when matrix multiplied with a linearized input transforms the
linearized representation.
LDE Project SoSe 2023
Closes #1911
---
scripts/builtin/img_brightness_linearized.dml | 2 +-
scripts/builtin/img_transform.dml | 41 +++---
..._transform.dml => img_transform_linearized.dml} | 55 ++++----
.../java/org/apache/sysds/common/Builtins.java | 1 +
.../pipelines/BuiltinImageTransformLinTest.java | 146 +++++++++++++++++++++
.../pipelines/BuiltinImageTransformTest.java | 26 ++--
.../resources/expected/ImageTransformLinInput.csv | 1 +
.../expected/ImageTransformLinTransformed.csv | 1 +
.../functions/builtin/image_transform_linearized.R | 77 +++++++++++
.../builtin/image_transform_linearized.dml | 40 ++++++
10 files changed, 330 insertions(+), 60 deletions(-)
diff --git a/scripts/builtin/img_brightness_linearized.dml
b/scripts/builtin/img_brightness_linearized.dml
index 1b1433bfa8..8c5e72d13f 100644
--- a/scripts/builtin/img_brightness_linearized.dml
+++ b/scripts/builtin/img_brightness_linearized.dml
@@ -35,4 +35,4 @@
m_img_brightness_linearized = function(Matrix[Double] img_in, Double value,
Integer channel_max) return (Matrix[Double] img_out) {
img_out = img_brightness(img_in,value,channel_max)
-}
\ No newline at end of file
+}
diff --git a/scripts/builtin/img_transform.dml
b/scripts/builtin/img_transform.dml
index 84eee6379e..c6a3d3e176 100644
--- a/scripts/builtin/img_transform.dml
+++ b/scripts/builtin/img_transform.dml
@@ -37,8 +37,9 @@
# img_out Output image as 2D matrix with top left corner at [1, 1]
#
---------------------------------------------------------------------------------------
-m_img_transform = function(Matrix[Double] img_in, Integer out_w, Integer
out_h, Double a, Double b, Double c, Double d,
- Double e, Double f, Double fill_value) return (Matrix[Double] img_out) {
+m_img_transform = function(Matrix[Double] img_in, Integer out_w, Integer
out_h, Double a,
+ Double b, Double c, Double d, Double e, Double f, Double fill_value)
+ return (Matrix[Double] img_out) {
divisor = a * e - b * d
if(divisor == 0) {
print("Inverse matrix does not exist! Returning input.")
@@ -67,25 +68,27 @@ m_img_transform = function(Matrix[Double] img_in, Integer
out_w, Integer out_h,
# compute sampling pixel indices
coords = floor(T_inv %*% coords) + 1
- # fill output image
- img_out = matrix(fill_value, rows=out_w*out_h, cols=1)
- parfor (cell in 1:(out_w*out_h)) {
- inx = as.scalar(coords[1, cell])
- iny = as.scalar(coords[2, cell])
- if ((0 < inx) & (inx <= orig_w) & (0 < iny) & (iny <= orig_h))
- img_out[cell] = img_in[iny, inx]
+ img_out = matrix(fill_value, rows=out_h, cols=out_w)
+
+ inx = t(coords[1,])
+ iny = t(coords[2,])
+
+ # any out-of-range pixels, if present, correspond to an extra pixel with
fill_value at the end of the input
+ index_vector = (orig_w *(iny-1) + inx) * ((0<inx) & (inx<=orig_w) &
(0<iny) & (iny<=orig_h))
+ index_vector = t(index_vector)
+ xs = ((index_vector == 0)*(orig_w*orig_h +1)) + index_vector
+
+ y = matrix(img_in, 1, orig_w*orig_h)
+ if(min(index_vector) == 0){
+ ys= cbind(y, matrix(fill_value,1, 1))
+ }else{
+ ys = y
}
- # TODO replace above loop with following vectorized code
- # but additional size mismatch / fill handling necessary
- # ---
- # P = order(target=t(coords), by=matrix("1 2",1,2), index.return=TRUE);
- # inx = t(coords[1,]);
- # iny = t(coords[2,]);
- # vals = P %*% matrix(img_in, out_w*out_h, 1);
- # img_out = ((0<inx) & (inx<=orig_w) & (0<iny) & (iny<=orig_h)) * vals;
+ ind= matrix(seq(1,ncol(xs),1),1,ncol(xs))
+ z = table(xs, ind)
+ output = ys%*%z
- # reshape output
- img_out = matrix(img_out, rows=out_h, cols=out_w)
+ img_out = matrix(output, rows=out_h, cols=out_w)
}
}
diff --git a/scripts/builtin/img_transform.dml
b/scripts/builtin/img_transform_linearized.dml
similarity index 62%
copy from scripts/builtin/img_transform.dml
copy to scripts/builtin/img_transform_linearized.dml
index 84eee6379e..e351c4cf7c 100644
--- a/scripts/builtin/img_transform.dml
+++ b/scripts/builtin/img_transform_linearized.dml
@@ -19,34 +19,35 @@
#
#-------------------------------------------------------------
-# The Image Transform function applies an affine transformation to an image.
+# The Linearized Image Transform function applies an affine transformation to
linearized images.
# Optionally resizes the image (without scaling).
# Uses nearest neighbor sampling.
#
# INPUT:
#
-------------------------------------------------------------------------------------------
-# img_in Input image as 2D matrix with top left corner at [1, 1]
-# out_w Width of the output image
-# out_h Height of the output image
+# img_in Linearized input images as 2D matrix with top left corner at
[1, 1]
+# out_w Width of the output matrix
+# out_h Height of the output matrix
# a,b,c,d,e,f The first two rows of the affine matrix in row-major order
-# fill_value The background of the image
+# fill_value The background of an image
#
-------------------------------------------------------------------------------------------
#
# OUTPUT:
#
---------------------------------------------------------------------------------------
-# img_out Output image as 2D matrix with top left corner at [1, 1]
+# img_out Output images in linearized form as 2D matrix with top left corner
at [1, 1]
#
---------------------------------------------------------------------------------------
-m_img_transform = function(Matrix[Double] img_in, Integer out_w, Integer
out_h, Double a, Double b, Double c, Double d,
- Double e, Double f, Double fill_value) return (Matrix[Double] img_out) {
+m_img_transform_linearized = function(Matrix[Double] img_in, Integer out_w,
Integer out_h, Double a, Double b, Double c, Double d,
+ Double e, Double f, Double fill_value, Integer s_cols, Integer s_rows) return
(Matrix[Double] img_out) {
+ # size of a single image is s_cols : s_rows
divisor = a * e - b * d
if(divisor == 0) {
print("Inverse matrix does not exist! Returning input.")
img_out = img_in
}
else {
- orig_w = ncol(img_in)
- orig_h = nrow(img_in)
+ orig_w = s_cols
+ orig_h = s_rows
# inverted transformation matrix
# inversion is necessary because we compute the sampling position of
pixels in the output image
# and not the output coordinates of input pixels
@@ -68,24 +69,26 @@ m_img_transform = function(Matrix[Double] img_in, Integer
out_w, Integer out_h,
coords = floor(T_inv %*% coords) + 1
# fill output image
- img_out = matrix(fill_value, rows=out_w*out_h, cols=1)
- parfor (cell in 1:(out_w*out_h)) {
- inx = as.scalar(coords[1, cell])
- iny = as.scalar(coords[2, cell])
- if ((0 < inx) & (inx <= orig_w) & (0 < iny) & (iny <= orig_h))
- img_out[cell] = img_in[iny, inx]
+ img_out = matrix(fill_value, rows=nrow(img_in), cols=out_w*out_h)
+
+ inx = t(coords[1,])
+ iny = t(coords[2,])
+
+ # any out-of-range pixels, if present, correspond to an extra pixel with
fill_value at the end of the input
+ index_vector = (orig_w *(iny-1) + inx) * ((0<inx) & (inx<=orig_w) &
(0<iny) & (iny<=orig_h))
+ index_vector = t(index_vector)
+ xs = ((index_vector == 0)*(orig_w*orig_h +1)) + index_vector
+
+ if(min(index_vector) == 0){
+ ys=cbind(img_in, matrix(fill_value,nrow(img_in), 1))
+ }else{
+ ys = img_in
}
- # TODO replace above loop with following vectorized code
- # but additional size mismatch / fill handling necessary
- # ---
- # P = order(target=t(coords), by=matrix("1 2",1,2), index.return=TRUE);
- # inx = t(coords[1,]);
- # iny = t(coords[2,]);
- # vals = P %*% matrix(img_in, out_w*out_h, 1);
- # img_out = ((0<inx) & (inx<=orig_w) & (0<iny) & (iny<=orig_h)) * vals;
+ ind= matrix(seq(1,ncol(xs),1),1,ncol(xs))
+ z = table(xs, ind)
+ output = ys%*%z
- # reshape output
- img_out = matrix(img_out, rows=out_h, cols=out_w)
+ img_out = matrix(output, rows=nrow(img_in), cols=out_w*out_h)
}
}
diff --git a/src/main/java/org/apache/sysds/common/Builtins.java
b/src/main/java/org/apache/sysds/common/Builtins.java
index 14c5d8437c..fa6ddfc4eb 100644
--- a/src/main/java/org/apache/sysds/common/Builtins.java
+++ b/src/main/java/org/apache/sysds/common/Builtins.java
@@ -159,6 +159,7 @@ public enum Builtins {
IMG_BRIGHTNESS_LINEARIZED("img_brightness_linearized", true),
IMG_CROP("img_crop", true),
IMG_TRANSFORM("img_transform", true),
+ IMG_TRANSFORM_LINEARIZED("img_transform_linearized", true),
IMG_TRANSLATE("img_translate", true),
IMG_ROTATE("img_rotate", true),
IMG_SHEAR("img_shear", true),
diff --git
a/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformLinTest.java
b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformLinTest.java
new file mode 100644
index 0000000000..e240b8e6fa
--- /dev/null
+++
b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformLinTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.pipelines;
+
+import org.apache.sysds.common.Types.ExecMode;
+import org.apache.sysds.common.Types.ExecType;
+import org.apache.sysds.runtime.matrix.data.MatrixValue;
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Arrays;
+
+@RunWith(Parameterized.class)
[email protected]
+
+public class BuiltinImageTransformLinTest extends AutomatedTestBase {
+ private final static String TEST_NAME = "image_transform_linearized";
+ private final static String TEST_DIR = "functions/builtin/";
+ private final static String TEST_CLASS_DIR = TEST_DIR +
BuiltinImageTransformLinTest.class.getSimpleName()
+ + "/";
+
+ private final static double eps = 1e-10;
+ private final static double spSparse = 0.1;
+ private final static double spDense = 0.9;
+ private final static double fill_value = 0.0;
+
+ @Parameterized.Parameter(0)
+ public int s_rows;
+ @Parameterized.Parameter(1)
+ public int s_cols;
+ @Parameterized.Parameter(2)
+ public int n_imgs;
+
+ public double a;
+ public double b;
+ public double c;
+ public double d;
+ public double e;
+ public double f;
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ { 16, 15, 50 },
+ { 32, 31, 100 },
+ { 64, 64, 200 },
+ { 127, 128, 100 },
+ { 256, 256, 200 },
+ { 500, 135, 100 }
+ });
+ }
+
+ @Override
+ public void setUp() {
+ // rotate 30 degrees around the center
+ a = Math.sqrt(3) / 2;
+ b = -1.0 / 2.0;
+ c = s_cols / 4.0 * (3 - Math.sqrt(3));
+ d = 1.0 / 2.0;
+ e = Math.sqrt(3) / 2;
+ f = s_rows / 4.0 * (1 - Math.sqrt(3));
+
+ addTestConfiguration(TEST_NAME, new TestConfiguration(TEST_CLASS_DIR,
TEST_NAME, new String[] { "B" }));
+ }
+
+ @Test
+ public void testImageTransformLinMatrixDenseCP() {
+ runImageTransformLinTest(false, ExecType.CP);
+ }
+
+ @Test
+ public void testImageTransformLinMatrixSparseCP() {
+ runImageTransformLinTest(true, ExecType.CP);
+ }
+
+ @Test
+ public void testImageTransformLinMatrixDenseSP() {
+ runImageTransformLinTest(false, ExecType.SPARK);
+ }
+
+ @Test
+ public void testImageTransformLinMatrixSparseSP() {
+ runImageTransformLinTest(true, ExecType.SPARK);
+ }
+
+ private void runImageTransformLinTest(boolean sparse, ExecType instType) {
+ ExecMode platformOld = setExecMode(instType);
+ disableOutAndExpectedDeletion();
+ setOutputBuffering(true);
+
+ try {
+ loadTestConfiguration(getTestConfiguration(TEST_NAME));
+ double sparsity = sparse ? spSparse : spDense;
+
+ String HOME = SCRIPT_DIR + TEST_DIR;
+ fullDMLScriptName = HOME + TEST_NAME + ".dml";
+ programArgs = new String[] { "-nvargs", "in_file=" + input("A"),
"out_file=" + output("B"),
+ "width=" + s_cols * s_rows,
+ "height=" + n_imgs, "out_w=" + s_cols, "out_h=" + s_rows *
1.2,
+ "a=" + a, "b=" + b, "c=" + c, "d=" + d, "e=" + e, "f=" +
f, "fill_value=" + fill_value,
+ "s_cols=" + s_cols,
+ "s_rows=" + s_rows };
+
+ fullRScriptName = HOME + TEST_NAME + ".R";
+ rCmd = "Rscript" + " " + fullRScriptName + " " + inputDir() + " "
+ expectedDir() + " " + s_cols * s_rows
+ + " " + n_imgs + " " + s_cols + " " + (s_rows * 1.2) + " "
+ a + " " + b + " " + c + " " + d + " "
+ + e + " " + f + " " + fill_value + " " + s_cols + " " +
s_rows;
+
+ // generate actual dataset
+ double[][] A = getRandomMatrix(n_imgs, s_cols * s_rows, 0, 255,
sparsity, 7);
+ writeInputMatrixWithMTD("A", A, true);
+
+ runTest(true, false, null, -1);
+ runRScript(true);
+
+ HashMap<MatrixValue.CellIndex, Double> dmlfile =
readDMLMatrixFromOutputDir("B");
+ HashMap<MatrixValue.CellIndex, Double> rfile =
readRMatrixFromExpectedDir("B");
+ TestUtils.compareMatrices(dmlfile, rfile, eps, "Stat-DML",
"Stat-R");
+ } finally {
+ rtplatform = platformOld;
+ }
+ }
+}
diff --git
a/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformTest.java
b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformTest.java
index 73012e5347..327fc9d981 100644
---
a/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformTest.java
+++
b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformTest.java
@@ -24,7 +24,7 @@ import org.apache.sysds.runtime.matrix.data.MatrixValue;
import org.apache.sysds.test.AutomatedTestBase;
import org.apache.sysds.test.TestConfiguration;
import org.apache.sysds.test.TestUtils;
-import org.junit.Ignore;
+//import org.junit.Ignore;
import org.junit.Test;
import java.util.HashMap;
@@ -49,7 +49,7 @@ public class BuiltinImageTransformTest extends
AutomatedTestBase {
@Override
public void setUp() {
- addTestConfiguration(TEST_NAME, new
TestConfiguration(TEST_CLASS_DIR, TEST_NAME, new String[] {"B"}));
+ addTestConfiguration(TEST_NAME, new
TestConfiguration(TEST_CLASS_DIR, TEST_NAME, new String[] { "B" }));
}
@Test
@@ -62,13 +62,11 @@ public class BuiltinImageTransformTest extends
AutomatedTestBase {
runImageTransformTest(true, ExecType.CP);
}
- @Ignore
@Test
public void testImageTransformMatrixDenseSP() {
runImageTransformTest(false, ExecType.SPARK);
}
- @Ignore
@Test
public void testImageTransformMatrixSparseSP() {
runImageTransformTest(false, ExecType.SPARK);
@@ -84,9 +82,9 @@ public class BuiltinImageTransformTest extends
AutomatedTestBase {
double[][] reference =
TestUtils.readExpectedResource("ImageTransformTransformed.csv", out_h, out_w);
String HOME = SCRIPT_DIR + TEST_DIR;
fullDMLScriptName = HOME + TEST_NAME + ".dml";
- programArgs = new String[] {"-nvargs", "in_file=" + input("A"),
"out_file=" + output("B"), "width=" + cols,
+ programArgs = new String[] { "-nvargs", "in_file=" +
input("A"), "out_file=" + output("B"), "width=" + cols,
"height=" + rows, "out_w=" + out_w, "out_h=" +
out_h,
- "a=" + a, "b=" + b, "c=" + c, "d=" + d, "e=" +
e, "f=" + f, "fill_value=" + fill_value};
+ "a=" + a, "b=" + b, "c=" + c, "d=" + d, "e=" +
e, "f=" + f, "fill_value=" + fill_value };
writeInputMatrixWithMTD("A", input, true);
runTest(true, false, null, -1);
@@ -106,26 +104,26 @@ public class BuiltinImageTransformTest extends
AutomatedTestBase {
String HOME = SCRIPT_DIR + TEST_DIR;
fullDMLScriptName = HOME + TEST_NAME + ".dml";
- programArgs = new String[] {"-nvargs", "in_file=" +
input("A"), "out_file=" + output("B"), "width=" + cols,
- "height=" + rows, "out_w=" + cols, "out_h=" +
(rows * 1.2),
- "a=" + a, "b=" + b, "c=" + c, "d=" + d, "e=" +
e, "f=" + f};
+ programArgs = new String[] { "-nvargs", "in_file=" +
input("A"), "out_file=" + output("B"), "width=" + cols,
+ "height=" + rows, "out_w=" + cols,
"out_h=" + (rows * 1.2),
+ "a=" + a, "b=" + b, "c=" + c, "d=" + d,
"e=" + e, "f=" + f };
fullRScriptName = HOME + TEST_NAME + ".R";
- rCmd = "Rscript" + " " + fullRScriptName + " " +
inputDir() + " " + expectedDir() + " " + cols + " " + rows + " " + cols + " " +
(rows * 1.2) + " " + a + " " + b + " " + c + " " + d + " " + e + " " + f;
+ rCmd = "Rscript" + " " + fullRScriptName + " " +
inputDir() + " " + expectedDir() + " " + cols + " " + rows
+ + " " + cols + " " + (rows * 1.2) + " "
+ a + " " + b + " " + c + " " + d + " " + e + " " + f;
- //generate actual dataset
+ // generate actual dataset
double[][] A = getRandomMatrix(rows, cols, 0, 255,
sparsity, 7);
writeInputMatrixWithMTD("A", A, true);
runTest(true, false, null, -1);
runRScript(true);
- //compare matrices
+ // compare matrices
HashMap<MatrixValue.CellIndex, Double> dmlfile =
readDMLMatrixFromOutputDir("B");
HashMap<MatrixValue.CellIndex, Double> rfile =
readRMatrixFromExpectedDir("B");
TestUtils.compareMatrices(dmlfile, rfile, eps,
"Stat-DML", "Stat-R");
- }
- finally {
+ } finally {
rtplatform = platformOld;
}
}
diff --git a/src/test/resources/expected/ImageTransformLinInput.csv
b/src/test/resources/expected/ImageTransformLinInput.csv
new file mode 100644
index 0000000000..2969b5d9dd
--- /dev/null
+++ b/src/test/resources/expected/ImageTransformLinInput.csv
@@ -0,0 +1 @@
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 [...]
diff --git a/src/test/resources/expected/ImageTransformLinTransformed.csv
b/src/test/resources/expected/ImageTransformLinTransformed.csv
new file mode 100644
index 0000000000..02b5bdb87d
--- /dev/null
+++ b/src/test/resources/expected/ImageTransformLinTransformed.csv
@@ -0,0 +1 @@
+128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128
128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128
128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128
128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128
128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128
128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128
128 128 128 128 [...]
diff --git a/src/test/scripts/functions/builtin/image_transform_linearized.R
b/src/test/scripts/functions/builtin/image_transform_linearized.R
new file mode 100644
index 0000000000..2b2498d8c9
--- /dev/null
+++ b/src/test/scripts/functions/builtin/image_transform_linearized.R
@@ -0,0 +1,77 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+args = commandArgs(TRUE)
+options(digits=22)
+library("Matrix")
+
+image_transform_linearized = function(img_in, out_w, out_h, a, b, c, d, e, f,
fill_value, s_cols, s_rows) {
+ divisor = a * e - b * d
+ if (divisor == 0) {
+ print("Inverse matrix does not exist! Returning input.")
+ img_out = img_in
+ }
+ else {
+ orig_w = s_cols
+ orig_h = s_rows
+ T.inv = matrix(0, nrow=3, ncol=3)
+ T.inv[1, 1] = e / divisor
+ T.inv[1, 2] = -b / divisor
+ T.inv[1, 3] = (b * f - c * e) / divisor
+ T.inv[2, 1] = -d / divisor
+ T.inv[2, 2] = a / divisor
+ T.inv[2, 3] = (c * d - a * f) / divisor
+ T.inv[3, 3] = 1
+
+ img_out = matrix(fill_value, nrow=nrow(img_in), ncol=out_w*out_h)
+
+ coords = matrix(1, nrow=3, ncol=out_w*out_h)
+ coords[1,] = t((seq(0, out_w*out_h-1) %% out_w) + 0.5)
+ coords[2,] = t((seq(0, out_w*out_h-1) %/% out_w) + 0.5)
+
+ coords = floor(T.inv %*% coords) + 1
+
+ inx = coords[1,]
+ iny = coords[2,]
+ ind = (iny-1)*orig_w + inx
+
+ for (cell in 1:(out_w*out_h)) {
+ inxi = coords[1, cell]
+ inyi = coords[2, cell]
+ indi = ind[cell]
+ if ((0 < inxi) & (inxi <= orig_w) & (0 < inyi) & (inyi <= orig_h)) {
+ img_out[,cell] = img_in[,indi]
+ }
+ }
+
+ img_out = matrix(img_out, nrow=nrow(img_in), ncol=out_w*out_h)
+ }
+
+ img_out
+}
+
+input = as.matrix(readMM(paste(args[1], "A.mtx", sep="")))
+input = matrix(input, ncol=as.integer(args[3]), nrow=as.integer(args[4]))
+
+transformed = image_transform_linearized(input, as.integer(args[5]),
as.integer(args[6]), as.double(args[7]),
+as.double(args[8]), as.double(args[9]), as.double(args[10]),
as.double(args[11]), as.double(args[12]),
+as.double(args[13]), as.integer(args[14]),as.integer(args[15]))
+writeMM(as(transformed, "CsparseMatrix"), paste(args[2], "B", sep=""))
diff --git a/src/test/scripts/functions/builtin/image_transform_linearized.dml
b/src/test/scripts/functions/builtin/image_transform_linearized.dml
new file mode 100644
index 0000000000..e53440b6a1
--- /dev/null
+++ b/src/test/scripts/functions/builtin/image_transform_linearized.dml
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+input = read($in_file)
+width = ifdef($width, 512)
+height = ifdef($height, 512)
+out_w = ifdef($out_w, 512)
+out_h = ifdef($out_h, 512)
+a = ifdef($a, 1)
+b = ifdef($b, 0)
+c = ifdef($c, 0)
+d = ifdef($d, 0)
+e = ifdef($e, 1)
+f = ifdef($f, 0)
+fill_value = ifdef($fill_value, 0)
+s_cols = ifdef($s_cols, 512)
+s_rows = ifdef($s_rows, 512)
+
+input = matrix(input, rows=height, cols=width)
+
+transformed = img_transform_linearized(input, out_w, out_h, a, b, c, d, e, f,
fill_value, s_cols, s_rows)
+write(transformed, $out_file)