diff --git a/scripts/builtin/img_transform_linearized.dml b/scripts/builtin/img_transform_linearized.dml index 06867d61b2e..296e5e513c7 100644 --- a/scripts/builtin/img_transform_linearized.dml +++ b/scripts/builtin/img_transform_linearized.dml @@ -22,6 +22,7 @@ # The Linearized Image Transform function applies an affine transformation to linearized images. # Optionally resizes the image (without scaling). # Uses nearest neighbor sampling. +# Is implemented as a builtin function as img_transform_matrix in image_transform_linearized. # # INPUT: # ------------------------------------------------------------------------------------------- @@ -75,17 +76,15 @@ m_img_transform_linearized = function(Matrix[Double] img_in, Integer out_w, Inte index_vector = (orig_w *(iny-1) + inx) * ((0"); + BinaryOperator op_less_equal = InstructionUtils.parseExtendedBinaryOperator("<="); + BinaryOperator op_and = InstructionUtils.parseExtendedBinaryOperator("&&"); + MatrixBlock helper_one; //(0=1 && dimMat.get(0,1)>=1 && dimMat.get(1,0)>=1 && dimMat.get(1,1)>=1){ + //check if the double values of the dimension matrix are actually positive natural numbers + //i.e. that they can be cast to int without loss of information for matrix generation + if(!(dimMat.get(0,0)== floor(dimMat.get(0,0))) || !(dimMat.get(0,1) == floor(dimMat.get(0,1))) + || !(dimMat.get(1,0)== floor(dimMat.get(1,0))) || !(dimMat.get(1,1) == floor(dimMat.get(1,1)))){ + throw new RuntimeException("Image dimensions are not positive natural numbers! Check input and output image dimensions!"); + } + }else{ + throw new RuntimeException("Wrong values! Image dimensions cannot be zero or negative! Check input and output image dimensions!"); + } + } +} diff --git a/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinImageTransformLinearizedTest.java b/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinImageTransformLinearizedTest.java new file mode 100644 index 00000000000..29f08f108a2 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinImageTransformLinearizedTest.java @@ -0,0 +1,157 @@ +/* + * 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.builtin.part1; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.apache.sysds.common.Types.ExecMode; +import org.apache.sysds.common.Types.ExecType; +import org.apache.sysds.test.AutomatedTestBase; +import org.apache.sysds.test.TestConfiguration; + +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@net.jcip.annotations.NotThreadSafe + +public class BuiltinImageTransformLinearizedTest extends AutomatedTestBase { + private final static String TEST_NAME_LINEARIZED = "image_transform_linearized"; + private final static String TEST_DIR = "functions/builtin/"; + private final static String TEST_CLASS_DIR = TEST_DIR + BuiltinImageTransformLinearizedTest.class.getSimpleName() + "/"; + + private final static double eps = 1e-10; + private final static double spSparse = 0.05; + private final static double spDense = 0.5; + + @Parameterized.Parameter(0) + public int rows; //number of linearized images + @Parameterized.Parameter(1) + public int width; + @Parameterized.Parameter(2) + public int height; + @Parameterized.Parameter(3) + public int out_w; + @Parameterized.Parameter(4) + public int out_h; + @Parameterized.Parameter(5) + public int a; + @Parameterized.Parameter(6) + public int b; + @Parameterized.Parameter(7) + public int c; + @Parameterized.Parameter(8) + public int d; + @Parameterized.Parameter(9) + public int e; + @Parameterized.Parameter(10) + public int f; + @Parameterized.Parameter(11) + public int fill_value; + @Parameterized.Parameter(12) + public int s_cols; + @Parameterized.Parameter(13) + public int s_rows; + + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] {{4, 512, 512, 512, 512, 2,1,0,0,1,0,0, 512, 512}}); + } + + @Override + public void setUp() { + addTestConfiguration(TEST_NAME_LINEARIZED, + new TestConfiguration(TEST_CLASS_DIR, TEST_NAME_LINEARIZED, new String[] {"B_x"})); + } + + @Test + public void testImageTransformLinearized() { + runImageTransformLinearizedTest(false, ExecType.CP); + } + + @Test + public void testImageTransformLinearizedEmpty() { + runImageTransformLinearizedTestEmpty(false, ExecType.CP); + } + + @Test + @Ignore + public void testImageTransformLinearizedSP() { + runImageTransformLinearizedTest(true, ExecType.SPARK); + } + + private void runImageTransformLinearizedTest(boolean sparse, ExecType instType) { + ExecMode platformOld = setExecMode(instType); + disableOutAndExpectedDeletion(); + + try { + loadTestConfiguration(getTestConfiguration(TEST_NAME_LINEARIZED)); + + double sparsity = sparse ? spSparse : spDense; + String HOME = SCRIPT_DIR + TEST_DIR; + + fullDMLScriptName = HOME + TEST_NAME_LINEARIZED + ".dml"; + programArgs = new String[] {"-nvargs", + "in_file=" + input("A"), + "width=" + width*height, "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, "s_cols=" + s_cols, "s_rows=" + s_rows, + "out_file=" + output("B_x")}; + + double[][] A = getRandomMatrix(rows, height*width, 0, 255, sparsity, 7); + writeInputMatrixWithMTD("A", A, true); + + runTest(true, false, null, -1); + + } + finally { + rtplatform = platformOld; + } + } + private void runImageTransformLinearizedTestEmpty(boolean sparse, ExecType instType) { + ExecMode platformOld = setExecMode(instType); + disableOutAndExpectedDeletion(); + + try { + loadTestConfiguration(getTestConfiguration(TEST_NAME_LINEARIZED)); + + double sparsity = sparse ? spSparse : spDense; + String HOME = SCRIPT_DIR + TEST_DIR; + + fullDMLScriptName = HOME + TEST_NAME_LINEARIZED + ".dml"; + programArgs = new String[]{"-nvargs", "in_file=" + input("A"), "width=", "height=", + "out_w=", "out_h=", "a=", "b=", "c=", "d=", "e=", "f=", + "fill_value=", "s_cols=", "s_rows=", + "out_file=" + output("B_x")}; + + int dims = width * height; + double[][] A = getRandomMatrix(rows, dims, 0, 255, sparsity, 7); + writeInputMatrixWithMTD("A", A, true); + + runTest(true, false, null, -1); + + } finally { + rtplatform = platformOld; + } + } +} diff --git a/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinImageTransformMatrixTest.java b/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinImageTransformMatrixTest.java new file mode 100644 index 00000000000..8ed152f2fe9 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinImageTransformMatrixTest.java @@ -0,0 +1,150 @@ +/* + * 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.builtin.part1; + +import org.apache.sysds.runtime.matrix.data.MatrixValue; +import org.apache.sysds.test.AutomatedTestBase; +import org.apache.sysds.test.TestUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.apache.sysds.common.Types.ExecMode; +import org.apache.sysds.common.Types.ExecType; +import org.apache.sysds.test.TestConfiguration; + +import java.util.*; + +@RunWith(Parameterized.class) +@net.jcip.annotations.NotThreadSafe + +public class BuiltinImageTransformMatrixTest extends AutomatedTestBase { + private final static String TEST_NAME_LINEARIZED = "image_transform_matrix"; + private final static String TEST_DIR = "functions/builtin/"; + private final static String TEST_CLASS_DIR = TEST_DIR + BuiltinImageTransformMatrixTest.class.getSimpleName() + "/"; + + @Parameterized.Parameter(0) + public double[][] transMat; + @Parameterized.Parameter(1) + public double[][] dimMat; + @Parameterized.Parameter(2) + public boolean fails; + + private final static double [][] t1 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; //initial test for warmup + private final static double [][] d1 = new double[][] {{10, 10},{15,15}}; + private final static double [][] t2 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d2 = new double[][] {{100, 100},{100,100}}; //test1: 100x100 + private final static double [][] t3 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d3 = new double[][] {{640, 480},{640,480}}; //test2: 640x480 + private final static double [][] t4 = new double[][] {{4,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d4 = new double[][] {{1280, 720},{1280, 720}};//test3: 1280x720 + private final static double [][] t5 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d5 = new double[][] {{1920, 1080},{1920, 1080}};//test4 1920x1080 + private final static double [][] t6 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d6 = new double[][] {{2560, 1440},{2560, 1440}};//test5 2560x1440 + private final static double [][] t7 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d7 = new double[][] {{3840, 2160},{3840, 2160}}; //test6 3840x2160 + private final static double [][] t8 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d8 = new double[][] {{3840, 2160},{1980, 1080}}; + private final static double [][] t9 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d9 = new double[][] {{20.10, 200},{200, 200}}; + private final static double [][] t10 = new double[][] {{0,0,0},{0,0,0},{0,0,0}}; + private final static double [][] d10 = new double[][] {{20.10, 200},{200, 200}}; + private final static double [][] t11 = new double[][] {{0,0,0},{0,1,0},{0,0,0}}; + private final static double [][] d11 = new double[][] {{20.10, 200},{200, 200}}; + private final static double [][] t12 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d12 = new double[][] {{0, 0},{0, 0}}; + private final static double [][] t13 = new double[][] {{2,0,0},{0,1,0},{0,0,1}}; + private final static double [][] d13 = new double[][] {{0.10, 200},{200, 200}}; + + public double internal = 1.0; + public boolean compareResults = false; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] {{t1, d1, false},{t2, d2, false},{t3, d3, false},{t4, d4, false}, + {t5, d5,false},{t6, d6, false},{t7, d7,false},{t8, d8,false}, + {t9, d9,true},{t10,d10,true},{t11, d11,true},{t12, d12,true},{t13, d13,true}}); + } + + @Override + public void setUp() { + addTestConfiguration(TEST_NAME_LINEARIZED, + new TestConfiguration(TEST_CLASS_DIR, TEST_NAME_LINEARIZED, new String[] {"B_x"})); + } + + //test for using the internal implementation only + @Test + public void testImageTransformMatrix() { + runImageTransformMatrixTest(ExecType.CP); + } + + //test for using the script implementation only + @Test + public void testImageTransformMatrixScript() { + internal = 0; + runImageTransformMatrixTest(ExecType.CP); + } + + //test for comparing the script and internal implementations for correctness + //presumably due to caching it should not be used for benchmarks + @Test + public void testImageTransformMatrixCompare() { + internal = 1; + compareResults = true; + runImageTransformMatrixTest(ExecType.CP); + } + + private void runImageTransformMatrixTest(ExecType instType) { + ExecMode platformOld = setExecMode(instType); + disableOutAndExpectedDeletion(); + + try { + loadTestConfiguration(getTestConfiguration(TEST_NAME_LINEARIZED)); + + String HOME = SCRIPT_DIR + TEST_DIR; + + writeInputMatrixWithMTD("transMat", transMat, true); + writeInputMatrixWithMTD("dimMat", dimMat, true); + writeInputMatrixWithMTD("internal", new double[][] {{internal}}, true); + + fullDMLScriptName = HOME + TEST_NAME_LINEARIZED + ".dml"; + programArgs = new String[]{"-nvargs", "transMat=" + input("transMat"), "dimMat=" + input("dimMat"), "out_file=" + output("B_internal"),"internal=" + input("internal"), "--debug"}; + runTest(true, fails, null, -1); + if (compareResults && !fails) { + internal = 0; + writeInputMatrixWithMTD("internal", new double[][] {{internal}}, true); + programArgs = new String[]{"-nvargs", "transMat=" + input("transMat"), "dimMat=" + input("dimMat"), "out_file=" + output("B_script"),"internal=" + input("internal"), "--debug"}; + runTest(true, fails, null, -1); + + HashMap internalfile = readDMLMatrixFromOutputDir("B_internal"); + HashMap scriptfile = readDMLMatrixFromOutputDir("B_script"); + TestUtils.compareMatrices(internalfile, scriptfile, 1e-10, "Stat-DML", "Stat-R"); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + rtplatform = platformOld; + internal = 1.0; + } + } + + +} \ No newline at end of file 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 index 0f83938578c..4c3445fe641 100644 --- a/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformLinTest.java +++ b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinImageTransformLinTest.java @@ -62,8 +62,7 @@ public class BuiltinImageTransformLinTest extends AutomatedTestBase { @Parameterized.Parameters public static Collection data() { - return Arrays.asList(new Object[][] {{16, 15, 50}, {32, 31, 100}, {64, 64, 200}, {127, 128, 100}, {256, 256, 200}, - {500, 135, 100}}); + return Arrays.asList(new Object[][] {{16, 15, 50}, {32, 31, 100}, {64, 64, 200},{127, 128, 100}, {256, 256, 200}, {500, 135, 100}}); } @Override @@ -113,13 +112,13 @@ private void runImageTransformLinTest(boolean sparse, ExecType instType) { 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, + "width=" + s_cols * s_rows, "height=" + n_imgs, "out_w=" + s_cols, "out_h=" + Math.floor(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 + + n_imgs + " " + s_cols + " " + Math.floor(s_rows * 1.2) + " " + a + " " + b + " " + c + " " + d + " " + e + " " + f + " " + fill_value + " " + s_cols + " " + s_rows; // generate actual dataset diff --git a/src/test/scripts/functions/builtin/image_transform_linearized.dml b/src/test/scripts/functions/builtin/image_transform_linearized.dml index e53440b6a17..2dbbd2590e1 100644 --- a/src/test/scripts/functions/builtin/image_transform_linearized.dml +++ b/src/test/scripts/functions/builtin/image_transform_linearized.dml @@ -18,15 +18,38 @@ # under the License. # #------------------------------------------------------------- +# INPUT: +# -input: linearized picture with pixel_width*pixel_height columns +# and number of pictures as rows (one image per row) +# -width: pixel_width*pixel_height of the original images +# -height: number ob images +# -out_w: width of the output images +# -out_h: height of the output images +# -a: entry at [0,0] of the affine matrix +# -b: entry at [0,1] of the affine matrix +# -c: entry at [0,2] of the affine matrix +# -d: entry at [1,0] of the affine matrix +# -e: entry at [1,1] of the affine matrix +# -f: entry at [1,2] of the affine matrix +# -fill_value colorvalue for empty spaces +# after transformation +# -s_cols: original number of pixel width +# before linearization +# -s_rows: original number of pixel height +# before linearization +#------------------------------------------------------------- +# OUTPUT: +# -img_out: transformed image +#------------------------------------------------------------- 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) +a = ifdef($a, 2) +b = ifdef($b, 1) +c = ifdef($c, 1) d = ifdef($d, 0) e = ifdef($e, 1) f = ifdef($f, 0) @@ -34,7 +57,36 @@ 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) +#method using dml scripts +if(FALSE){ + 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) +#method using builtin function +}else{ + affineMat = matrix(0,rows=3, cols=3) + affineMat[1,1] = a + affineMat[1,2] = b + affineMat[1,3] = c + affineMat[2,1] = d + affineMat[2,2] = e + affineMat[2,3] = f + affineMat[3,3] = 1 + dimMat = matrix(0,rows=2, cols=2) + dimMat[1,1] = s_cols + dimMat[1,2] = s_rows + dimMat[2,1] = out_w + dimMat[2,2] = out_h + + [zMat, isFillable] = img_transform_matrix(affineMat, dimMat) + + if(as.logical(as.scalar(isFillable))){ + ys=cbind(input, matrix(fill_value,nrow(input), 1)) + }else{ + ys = input + } -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) + output = ys%*%zMat + img_out = matrix(output, rows=nrow(input), cols=out_w*out_h) + write(img_out, $out_file) +} diff --git a/src/test/scripts/functions/builtin/image_transform_matrix.dml b/src/test/scripts/functions/builtin/image_transform_matrix.dml new file mode 100644 index 00000000000..c66463bcc2a --- /dev/null +++ b/src/test/scripts/functions/builtin/image_transform_matrix.dml @@ -0,0 +1,47 @@ +#------------------------------------------------------------- +# +# 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. +# +#------------------------------------------------------------- + +#get the transformation matrix (3x3) as an input +transformationMatrix = read($transMat) + +# get a matrix with the original image dimensions and the target dimensions (2x2) +# -----original width----original height +# -----target width------target height +dimensionMatrix = read($dimMat) + +input = matrix(transformationMatrix, rows=3, cols=3) + +# by setting the boolean the calculation of the transformation matrix can +# be done internally or via script +builtin = TRUE +builtin = as.logical(as.scalar(read($internal))) +if(builtin){ + # internal execution + [zMat, isFillable] = img_transform_matrix(input, dimensionMatrix) +}else{ + # execution via script + [zMat, isFillable] = img_transform_test(input, dimensionMatrix) +} + +# function for calculating the matrix which will be matrix multiplied with +# the image for matrix multiplication +isFillable = as.logical(as.scalar(isFillable)) +write(zMat, $out_file) diff --git a/src/test/scripts/functions/builtin/img_transform_test.dml b/src/test/scripts/functions/builtin/img_transform_test.dml new file mode 100644 index 00000000000..bcccfd3a0e4 --- /dev/null +++ b/src/test/scripts/functions/builtin/img_transform_test.dml @@ -0,0 +1,70 @@ +#------------------------------------------------------------- +# +# 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. +# +#------------------------------------------------------------- +# Generates the z matrix for the new linearized image transformation function in order to apply +# affine transformation to linearized images. +# +# INPUT: +# ------------------------------------------------------------------------------------------- +# transMat 3x3 matrix for affine transformation (transformation Matrix) +# dimMat 2x2 matrix containing the original size of the image in the first row as follows +# [1,1] original height; [1,2] original width +# and the target size of the new image in the second row as follows +# [2,1] target height; [2,2] target width +# (dimensionMatrix) +# ------------------------------------------------------------------------------------------- +# OUTPUT: +# --------------------------------------------------------------------------------------- +# zMat transformation matrix to be multiplied with for the linearized image +# transformation function (arbitrary naming) +# isFillable returns a boolean which indicates if cells need to be filled (with fillvalue), +# in this case the image is extended, otherwise the original image is used +# --------------------------------------------------------------------------------------- + + +m_img_transform_test = function(Matrix[Double] transMat, Matrix[Double] dimensionMatrix) + return (Matrix[Double] zMat, Matrix[Double] isFillable){ + T_inv = inv(transMat) + + orig_w = as.scalar(dimensionMatrix[1,1]) + orig_h = as.scalar(dimensionMatrix[1,2]) + out_w = as.scalar(dimensionMatrix[2,1]) + out_h = as.scalar(dimensionMatrix[2,2]) + + ## coordinates of output pixel-centers linearized in row-major order + coords = matrix(1, rows=3, cols=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) + + # compute sampling pixel indices + coords = floor(T_inv %*% coords) + 1 + + 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