• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Perspective correct images within AI... Possible?

Engaged ,
Oct 05, 2017 Oct 05, 2017

Copy link to clipboard

Copied

An image might better explain what I'd like to do:

Screen Shot 10-05-17 at 01.54 PM.PNG

Currently, I do a TON of perspective correction by correcting the image in PS and then placing into AI.  It would be nice to be able to do this completely within AI.  Perhaps through envelope distort? Are envelopes scriptable?  I can take care of the maths of the transform I'm just looking for some direction on where to start, or if it's even possible?

TOPICS
Scripting

Views

1.0K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Valorous Hero , Oct 05, 2017 Oct 05, 2017

Some of the stuff is not scriptable, but luckily, this command works to make an envelope with a top object:
app.executeMenuCommand("Make Envelope");

Thus, you can with script change the shape of your path you want to be used as a 4-corner perspective plane and then do the above line to make the envelope. Come to think of it, this could be used to say, map a new entire facade on to a building, or something.

Votes

Translate

Translate
Adobe
Valorous Hero ,
Oct 05, 2017 Oct 05, 2017

Copy link to clipboard

Copied

Some of the stuff is not scriptable, but luckily, this command works to make an envelope with a top object:
app.executeMenuCommand("Make Envelope");

Thus, you can with script change the shape of your path you want to be used as a 4-corner perspective plane and then do the above line to make the envelope. Come to think of it, this could be used to say, map a new entire facade on to a building, or something.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 06, 2017 Oct 06, 2017

Copy link to clipboard

Copied

Perfect! Thank you so much!  Does anyone know a slim library I can include to work with matrices?  I only need the following matrix functions:

multiplication, determinant and inverse.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Oct 06, 2017 Oct 06, 2017

Copy link to clipboard

Copied

This is the kind of thing I would use more knowledge in. Let me know if you end up finding such a suitable library?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 08, 2017 Oct 08, 2017

Copy link to clipboard

Copied

one of the most useful Matrix explanations I've found

senocular.com

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 12, 2017 Oct 12, 2017

Copy link to clipboard

Copied

I think I may just roll my own... Thanks!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 13, 2017 Oct 13, 2017

Copy link to clipboard

Copied

If anyone is interested, here's my Matrix class. It's by no means complete nor tested extensively, but it's working for what I'm using it for (see 1st post in this thread).

Methods available:

  • solve (static):Solves a system of linear equations using matrices.
  • toArray: Converts a matrix object to a 1-dimensional array.
  • toString: Returns a multi-line string representation of the  Matrix.
  • getEntry: Returns the value stored in the Matrix object at a specific row and column.
  • setEntry: Set the value stored in the Matrix object at a specific row and column.
  • getRows: The number of rows in the Matrix.
  • getColumn: The number of columns in the Matrix.
  • copy: Returns a copy of the matrix.
  • add: Adds matrices together.
  • subtract: Subtracts matrices.
  • multiply: Returns the product of two matrices or the product of a matrix and a scalar.
  • determinate: Return the determinate of a matrix. (There is also a static version).
  • inverse: Return the inverse of a matrix.

Enjoy:

// ***********************************************************************

// **********                            MATRIX CLASS                            **********

// ***********************************************************************

//      Represents a mathematical Matrix of values and

//      performs various operations on Matrix object.

//

//  Author:      Jeremy Wilcock

//  Version:    1.787

//  Date:         2017.13.10

//

//  Determinate code borrowed and modified from:

//      http://professorjava.weebly.com/matrix-determinant.html

//      Author: Unknown

//

//  Inverse code borrowed and modified from:

//      http://blog.acipo.com/matrix-inversion-in-javascript/

//      Author: Andrew Ippoliti

// ***********************************************************************

// ***********************************************************************

// Constructor.

//      Creates a new Matrix Object.

//

// Parameters:

//      rows            Number of rows to create in the matrix

//      columns     Number of rows to create in the matrix

//      values         A 1-dimensional array representing the values

//                           in the matrix in row followed by column format

//                           e.g. [r1c1, r1c2, r2c1, r2c2].

// ***********************************************************************

function Matrix(rows, columns, values)

{

    // Create an array to hold the rows.

    this.values = new Array(rows);

    for (var i = 0; i <rows; i++)

    {

        // In each row, create an array to hold the columns.

        this.values = new Array(columns);

        for (var j = 0; j < columns; j++)

        {

            // In each cell, copy the values from the input array.

            this.values = new Array(rows);

            var inputIndex = j + (i * columns);

           

            // If there are not enough values passed to fill the matrix, use zeros.

            if (values == undefined || values[inputIndex] == undefined)

                this.values = 0;

            else

                this.values = values[inputIndex];

        }

    }

};

// ***********************************************************************

// Static method.

//      Solves a system of linear equations using Cramer's Rule where

//      m is an nxn matrix of coefficients with a non-zero determinate

//      (has a unique solution) and v is a vertical vector with n elements

//      holding the answers (nx1 matrix).

//

// Parameters:

//      m     A square Matrix object representing the coefficients

//               in the equations.

//      v       A Matrix object representing a vertical vector with the

//               same number of rows as m.

//

// Returns:

//      A verical vector (nx1 matrix) containing the solutions to

//      the variables.

// ***********************************************************************

Matrix.solve = function (m, v)

{

    // Determine dimension of matrix.

    var n = m.getRows();

   

    // Throw if not square.

    if (m.getColumns() != n)

        throw "m must be a square matrix.";

   

    // Throw if v is not a vertical vector.

    if (v.getColumns() != 1)

        throw "v must be a vertical vector.";

       

    // Throw if vector is not the same height at the matrix.

    if (v.getRows() != n)

        throw "Vertical vector v's height must match the height of the matrix m.";

       

    // Determinate D of m.

    var D = m.determinate();

   

    // Throw if D = 0.

    if (D == 0)

        throw "Determinate of m is zero. m does not have a unique solution.";

   

    // Determinates D_1 through D_n

    var D_n = new Array(n);

   

    for (var i = 0; i < n; i++)

    {

        D_n = m.copy();

       

        for (var j = 0; j < n; j++)

        {

            // colum = i, row = j

            D_n.setEntry(j, i, v.getEntry(j, 0));

        }

   

        D_n = D_n.determinate();

       

    }

   

    // Create new vector containing solutions S_n = D_n/D

    var S_n = new Array(n);

   

    for (var i = 0; i < n; i++)

    {

        S_n = D_n/D;

    }

    // Create results Matrix

    var results = new Matrix(n, 1, S_n);

       

    return results;

}

// ***********************************************************************

// Method.

//      Returns a 1-dimensional array representing this Matrix in

//      row followed by column format, e.g. [r1c1, r1c2, r2c1, r2c2].

//

// Returns:

//      An array.

// ***********************************************************************

Matrix.prototype.toArray = function()

{

    var result = new Array(this.values.length * this.values[0].length);

    for (var i = 0; i <this.getRows(); i++)

    {

        for (var j = 0; j < this.getColumns(); j++)

        {

            var inputIndex = j + (i*this.getColumns());

            result[inputIndex] = this.values;

        }

    }

    return result;

};

// ***********************************************************************

// Method.

//      Returns a multi-line string representation of this Matrix.

//

// Returns:

//      A String.

// ***********************************************************************

Matrix.prototype.toString = function()

{

    var result = "";

    for (var i = 0; i < this.getRows(); i++)

    {

        result += "¦\t";

        for (var j = 0; j < this.getColumns(); j++)

        {

            result += this.values + "\t";

        }

        result += "¦\r";

    }

    return result;

};

// ***********************************************************************

// Method.

//      Returns the value stored in the Matrix object at a specific

//      row and column.

//

// Parameters:

//      row         The row of the entry.

//      column  The column of the entry.

//

// Returns:

//      The value stored at the row and column specified.

// ***********************************************************************

Matrix.prototype.getEntry = function(row, column)

{

    if (row >= this.getRows() || column >= this.getColumns())

        throw "Invalid Index";

       

    return this.values[row][column];

};

// ***********************************************************************

// Method.

//      Sets the value stored in the Matrix object at a specific

//      row and column.

//

// Parameters:

//      row         The row of the entry.

//      column  The column of the entry.

//      value       The value to store.

// ***********************************************************************

Matrix.prototype.setEntry = function(row, column, value)

{

    if (row >= this.getRows() || column >= this.getColumns())

        throw "Invalid Index";

       

    this.values[row][column] = value;

};

// ***********************************************************************

// Method.

//      Returns the number of rows in this Matrix.

//

// Returns:

//      The number of rows in this Matrix.

// ***********************************************************************

Matrix.prototype.getRows = function()

{

    return this.values.length;     

};

// ***********************************************************************

// Method.

//      Returns the number of columns in this Matrix.

//

// Returns:

//      The number of columns in this Matrix.

// ***********************************************************************

Matrix.prototype.getColumns = function()

{

    return this.values[0].length;     

};

// ***********************************************************************

// Method.

//      Returns a duplicate of this Matrix.

//

// Returns:

//      A Matrix object.

// ***********************************************************************

Matrix.prototype.copy = function()

{

    var result = new Matrix(this.getRows(), this.getColumns(), this.toArray());

    return result;

};

// ***********************************************************************

// Method.

//      Adds this matrix to another and returns a new Matrix

//      representing the result.

//

// Parameters:

//      other       The other matrix to add this matrix to.

//

// Returns:

//      A Matrix object.

// ***********************************************************************

Matrix.prototype.add = function(other)

{

    if (this.getRows() != other.getRows() || this.getColumns() != other.getColumns())    

        throw "Matrices of different dimensions cannot be added";

   

    var result = new Matrix(this.getRows(), this.getColumns());

   

    for (var i = 0; i < this.getRows(); i++)

    {

        for (var j = 0; j < this.getColumns(); j++)

        {

            result.setEntry(i, j, this.getEntry(i, j) + other.getEntry(i, j));

        }

    }

    return result;

};

// ***********************************************************************

// Method.

//      Subtracts another Matrix from this Matrix and returns a new

//      Matrix representing the result.

//

// Parameters:

//      other       The other matrix to subtract from this Matrix.

//

// Returns:

//      A Matrix object.

// ***********************************************************************

Matrix.prototype.subtract = function(other)

{

    if (this.getRows() != other.getRows() || this.getColumns() != other.getColumns())    

        throw "Matrices of different dimensions cannot be added";

   

    var result = new Matrix(this.getRows() , this.getColumns());

   

    for (var i = 0; i < this.getRows(); i++)

    {

        for (var j = 0; j < this.getColumns(); j++)

        {

            result.setEntry(i, j, this.getEntry(i, j) - other.getEntry(i, j));

        }

    }

    return result;

};

// ***********************************************************************

// Method.

//      Multiplies this Matrix with another Matrix or a scalar value and

//      returns a new Matrix representing the result. Matrix by matrix

//      multiplication is done in this order: THIS MATRIX * OTHER.

//

// Parameters:

//      other       The other matrix to multiply with this Matrix or a scalar to multipy with.

//

// Returns:

//      A Matrix object.

// ***********************************************************************

Matrix.prototype.multiply = function(other)

{

    // Scalar

    if (typeof other == "number")

    {

        var result = this.copy();

        for (var i = 0; i < this.getRows(); i++)

        {

            for (var j = 0; j < this.getColumns(); j++)

            {

                result.setEntry(i, j, result.getEntry(i, j) * other);

            }

        }

        return result;

    }

    // Test for invalid object.

    if (!other instanceof Matrix)

         throw "Can only multiply by a matrix or scalar";

        

    // Test for valid matrices size.

    if (this.getColumns() != other.getRows())

        throw "Matrices sizes are not compatable with multiplication.";

       

    // Matrices

    var result = new Matrix(this.getRows(), other.getColumns());

   

    for (var r = 0; r < this.getRows(); r++)

    {

        for (var c = 0; c < other.getColumns(); c++)

        {

            var dotResult = 0;

            for (var i = 0; i < this.getColumns(); i++)

            {

                dotResult += this.getEntry(r, i) * other.getEntry(i, c);      

            }

            result.setEntry(r,c, dotResult);

        }

    }

   

    return result;  

}

// ***********************************************************************

// Method.

//      Returns the determinate of this Matrix.

//

// Returns:

//      The determinate of this Matrix.

// ***********************************************************************

Matrix.prototype.determinate = function()

{

    // Throw if Matrix is not square.

    if (this.getRows() != this.getColumns())

        throw "Not a square matrix.";

   

    // Return determinate using recursive static method.

    return Matrix.determinate(this);   

}

// ***********************************************************************

// Static method.

//      Returns the determinate of the Matrix passed as a parameter

//      using recursion.

//

// Parameters:

//      matrix       The Matrix to calculate the determinate of.

//

// Returns:

//      The determinate of the Matrix.

// ***********************************************************************

Matrix.determinate = function(matrix)

{

    var sum=0;

    var s;

    if (matrix.getRows() == 1)

    {

        return(matrix.getEntry(0, 0));

    }

    if (matrix.getRows() == 2)

    {

        return((matrix.getEntry(0, 0)*matrix.getEntry(1, 1))-(matrix.getEntry(1, 0)*matrix.getEntry(0, 1)));

    }

    if (matrix.getRows() == 3)

    {

        return((matrix.getEntry(0, 0) * matrix.getEntry(1, 1) * matrix.getEntry(2, 2))

                +   (matrix.getEntry(0, 1) * matrix.getEntry(1, 2) * matrix.getEntry(2, 0))

                +   (matrix.getEntry(0, 2) * matrix.getEntry(1, 0) * matrix.getEntry(2, 1))

                -   (matrix.getEntry(0, 0) * matrix.getEntry(1, 2) * matrix.getEntry(2, 1))

                -   (matrix.getEntry(0, 1) * matrix.getEntry(1, 0) * matrix.getEntry(2, 2))

                -   (matrix.getEntry(0, 2) * matrix.getEntry(1, 1) * matrix.getEntry(2, 0)));

    }

    for (var i=0; i < matrix.getRows(); i++)

    {

        var smallerMatrix = new Matrix(matrix.getRows() - 1, matrix.getRows() - 1);

        for (var r=1; r < matrix.getRows(); r++)

        {

            for(var c=0; c < matrix.getRows(); c++)

            {

                if (c < i)

                {

                    smallerMatrix.setEntry(r-1, c, matrix.getEntry(r, c));

                }

                else if (c > i)

                {

                    smallerMatrix.setEntry(r-1, c-1, matrix.getEntry(r, c));

                }

            }

        }

        s = (i%2 == 0) ? 1 : -1;

          

        sum += s * matrix.getEntry(0, i) * smallerMatrix.determinate();

    }

    return(sum);

}

// ***********************************************************************

// Method.

//      Returns the inverse of this Matrix using guassian elimination.

//

// Returns:

//      A Matrix Object.

// ***********************************************************************

Matrix.prototype.inverse = function()

{

    // Throw if the matrix isn't square.

    if (this.getRows() != this.getColumns())

        throw "Not a square matrix.";

   

    var dim = this.getRows();

    // Create an identity matrix.

    var I = new Matrix(dim, dim);

    //Place a 1 on each diagonal to create the identity matrix.

    for (var i=0; i < dim; i++)

        I.setEntry(i, i, 1);

   

    // Copy the original Matrix

    var C = this.copy();

   

    // Perform elementary row operations

    var e = 0;

    for (var i=0; i < dim; i++){

        // Retreive the element e on the diagonal

        e = C.getEntry(i, i);

       

        // if we have a 0 on the diagonal we need to swap with a lower row.

        if (e == 0)

        {

            // Loop through every row below the i'th row

            for (var ii = i+1; ii < dim; ii ++)

            {

                // If the ii'th row has a non-zero in the i'th column it would

                // make the diagonal have a non-zero, so swap it

                if (C.getEntry(ii, i) != 0){

                    for (var j=0; j < dim; j++){

                        // Perform the swap in the copy.

                        e = C.getEntry(i, j)

                        C.setEntry(i, j, C.getEntry(ii, j));

                        C.setEntry(ii, j, e);

                       

                        // Perform the swap in the identity matrix.

                        e = I.getEntry(i, j);

                        I.setEntry(i, j, I.getEntry(ii, j));

                        I.setEntry(ii, j, e);

                    }

                    // We can break out since a swap happened. No need to check other rows.

                    break;

                }

            }

       

            // Get the updated diagonal

            e = C.getEntry(i, i);

           

            // If it's still 0 the matrix is not invertable.

            if (e == 0)

                throw "Singlular Matrix, not invertable";

        }

       

        // Scale this row down by e (so we have a 1 on the diagonal).

        for (var j=0; j < dim; j++)

        {

            // Apply to both the copy and the identity matrices.

            C.setEntry(i, j, C.getEntry(i, j)/e);

            I.setEntry(i, j, I.getEntry(i, j)/e);

        }

       

        // Subtract this row (scaled appropriately for each row) from ALL of

        // the other rows so that there will be 0's in this column in the

        // rows above and below this one.

        for (var ii=0; ii < dim; ii++)

        {

            // Only apply to other rows because we want a 1 on the diagonal.

            if (ii == i)

                continue;

           

            // We want to change this element to 0.

            e = C.getEntry(ii, i);

           

            // Subtract (the row above(or below) scaled by e) from (the

            // current row) but start at the i'th column and assume all the

            // stuff left of diagonal is 0 (which it should be if we made this

            // algorithm correctly).

            for (var j=0; j < dim; j++)

            {

                 // Apply to both the copy and the identity matrices.

                C.setEntry(ii, j, C.getEntry(ii, j) - C.getEntry(i, j) * e);

                I.setEntry(ii, j, I.getEntry(ii, j) - I.getEntry(i, j) * e);

            }

        }

    }

   

    // Done! Return the resulting inverse Matrix.

    return I;

}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Oct 14, 2017 Oct 14, 2017

Copy link to clipboard

Copied

Wow, please keep coming back to the forum? This is really helpful and is helpful toward building of my own understanding of the Matix.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Oct 20, 2017 Oct 20, 2017

Copy link to clipboard

Copied

LATEST

   Anyone know if there's a way to get the envelope distort to act like the free distort tool, or if there's a way to script the free distort tool?  I've got the correct shape being generated for the distort, but for some reason when I apply the envelope to the image some straight lines become curved  (even though my top object is not curved). I'm assuming it has something to do with AI adding handles to the anchors, but I've tried retracting all the handles and the distortions become worse.

Screen Shot 10-20-17 at 11.29 AM.PNG

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines