# Julia version of cv.getPerspectiveTransform?

**URL:** <https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553>\
**Category:** Visualization\
**Created:** [September 21, 2021, 10:33pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553 "2021-09-21T22:33:56Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 21, 2021, 10:33pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/1 "2021-09-21T22:33:56Z")

</div>

I want to create and use a projective transform matrix between four points in one coordinate system to four points in another coordinate system. Is there a way to do this in Julia? In OpenCV I can call cv.[getPerspectiveTransform](https://theailearner.com/tag/cv2-getperspectivetransform/) and `cv.perspectiveTransform` to make and use the transformation.

For example:  
(0, 0) → (333, 333)  
(0, 100) → (345, 600)  
(100, 0) → (555, 346)  
(100, 100) → (610, 600)

So you can see that the shape may not be perfectly square from the four points.

It’s probably not affine, so the equation [here](https://en.wikipedia.org/wiki/Affine_transformation#Example_augmented_matrix) will not work. Couldn’t find a similar one for projective transformations.

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [September 22, 2021, 4:00am UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/2 "2021-09-22T04:00:22Z")

</div>

Perhaps you’ll find what you’re looking for here [https://github.com/JuliaGeometry/CoordinateTransformations.jl#perspective-transformations](https://github.com/JuliaGeometry/CoordinateTransformations.jl#perspective-transformations)

---

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 22, 2021, 10:10pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/3 "2021-09-22T22:10:40Z")

</div>

Can you show me how to setup the PerspectiveMap? I don’t see how to generate the matrix from the 4 points in that example …

---

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 22, 2021, 11:14pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/4 "2021-09-22T23:14:11Z")

</div>

Ok one solution is:

```julia
using OpenCV 

m = reshape(
  OpenCV.getPerspectiveTransform(
    reshape(Array{Float32}([0 0 0 100 100 0 100 100]), 1, 2 ,4), 
    reshape(Array{Float32}([333 333 345 600 345 550 550 625]), 1, 2, 4) , 
    Int32(0)), 
  3, 3)'

```

But I would prefer to not depend on OpenCV if possible. Also the matrix that OpenCV returns makes no sense to me - when I use it by taking `m * [1, 1, 1]` i get something outside the range of the coordinates in the new space. If I put in `m * [100, 100, 1]` I get something with the wrong signs altogether (the y coordinate is negative somehow).

Another option is to solve the system of equations myself and use the resulting formula … this is the 12 equations I’d need to reduce:

 ![55f0f76fea8aee0b9154a63468c4a23782f8e745fa16cfdb631e49a11cd1d9ec](https://global.discourse-cdn.com/julialang/original/3X/8/e/8edcb181fdbd5f20f0d520a16460bc0905127688.png)

Does Julia have some way of solving these large sets of symbolic equations?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [September 23, 2021, 3:37am UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/5 "2021-09-23T03:37:51Z")

</div>

> [@vshesh](#):
>
> Does Julia have some way of solving these large sets of symbolic equations?

The resulting symbolic formula could be nasty — it might be more efficient simply to construct the matrix and solve it numerically each time you change the parameters.

---

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 23, 2021, 4:40am UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/6 "2021-09-23T04:40:40Z")

</div>

Yeah I tried solving the full thing in a wolfram sandbox and the solver timed out.

Solving for converting from an arbitrary coordinate system **to** a square with side 100 (to simplify some of the equations) gave me this:

 ![Screen Shot 2021-09-22 at 9.35.30 PM](https://global.discourse-cdn.com/julialang/original/3X/2/5/255497d461eea2d2f806300352e36397161ca9f5.png)  
Yeah … no hope for implementing that in code.

This is for converting **from** a square with side = 100:

 ![Screen Shot 2021-09-22 at 9.25.42 PM](https://global.discourse-cdn.com/julialang/original/3X/a/f/af074c6fe60eb780ef8121b7912fca08cd62faf0.png)

Somehow this “side” isn’t so bad. Messy to implement directly though.

@stevengl is right - I will need to do this numerically when necessary.

In my application I may need to make and use ~10-20 perspective transforms at 20fps or so. Is there any approach that will be efficient enough to support this? I imagine trying to run rref on that many matrices will be quite slow…

---

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 23, 2021, 5:15am UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/7 "2021-09-23T05:15:51Z")

</div>

So this is what making it myself looks like:

```julia
using RowEchelon

function perspective_transform(
  ((x1, y1), (x2, y2), (x3, y3), (x4, y4)),  
  ((X1, Y1), (X2, Y2), (X3, Y3), (X4, Y4)))  

  m = [
    x1 y1 1 0 0 0 0 0 -X1 0 0 0 0
    x2 y2 1 0 0 0 0 0 0 -X2 0 0 0
    x3 y3 1 0 0 0 0 0 0 0 -X3 0 0
    x4 y4 1 0 0 0 0 0 0 0 0 -X4 0
    0 0 0 x1 y1 1 0 0 -Y1 0 0 0 0
    0 0 0 x2 y2 1 0 0 0 -Y2 0 0 0
    0 0 0 x3 y3 1 0 0 0 0 -Y3 0 0
    0 0 0 x4 y4 1 0 0 0 0 0 -Y4 0
    0 0 0 0 0 0 x1 y1 -1 0 0 0 -1
    0 0 0 0 0 0 x2 y2 0 -1 0 0 -1
    0 0 0 0 0 0 x3 y3 0 0 -1 0 -1
    0 0 0 0 0 0 x4 y4 0 0 0 -1 -1
  ]
  values = rref(m)[1:8, end]
  [
    values[1] values[2] values[3]
    values[4] values[5] values[6]
    values[7] values[8] 1
  ]
end

```

Is this the fastest way to set this up? that matrix is 75% zeros so maybe there’s a faster/cleaner way to do this? I’ve also heard that rref isn’t the most numerically stable, so if there’s a better option please tell me.

As for using it I get

```julia
function transform_perspective(m::Matrix, (x1, x2)) 
  t = m * [x1 x2 1]'
  [t[1] / t[3], t[2] / t[3]]
end

```

Of course both of these functions are specialized to the 2d case, but that’s all I need for now.

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [September 23, 2021, 5:37am UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/8 "2021-09-23T05:37:56Z")

</div>

> [@vshesh](#):
>
> In my application I may need to make and use ~10-20 perspective transforms at 20fps or so. Is there any approach that will be efficient enough to support this? I imagine trying to run rref on that many matrices will be quite slow

If you preallocate all the matrices such that you do not allocate new ones for each transform application there is a good chance you’ll make 20fps. For example, this

```julia
m = [
    x1 y1 ... 

```

allocates new memory, consider allocating that once outside the function (remember to use `const`) and just update the already allocated memory. Then make use of in-place multiplications using `mul!`.

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [September 23, 2021, 8:43am UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/9 "2021-09-23T08:43:07Z")

</div>

Do you know about [https://github.com/JuliaImages/ImageTransformations.jl](https://github.com/JuliaImages/ImageTransformations.jl)? It might have everything you need. And checkout this answer from Tim Holy: [Perspective warp an image in Julia - Stack Overflow](https://stackoverflow.com/a/45782741/2261957)

Hope this helps!

---

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 23, 2021, 2:56pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/10 "2021-09-23T14:56:35Z")

</div>

Seems like this resource allows someone to apply a transformation matrix (I don’t see a way to solve for one). So I can write the application function like:

```julia
using ImageTransformations

transform_perspective(m::Matrix, point::Vector) = PerspectiveMap() ∘ inv(LinearMap(m)) ∘ \x -> push(x, 1)

```

I’m not clear what I’m gaining compared to the simpler code above. Isn’t this a lot more operations to create all the transformation objects?

---

<div class="post-metadata">

**Author:** ![stillyslalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stillyslalom/32/45687_2.png) [@stillyslalom](https://discourse.julialang.org/u/stillyslalom)\
**Post date:** [September 23, 2021, 3:35pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/11 "2021-09-23T15:35:06Z")

</div>

Yes, ImageTransformations.jl currently just allows you to apply transformations, not fit them - this would be a nice addition to the package. Your function can be sped up with static arrays (and by rewriting the rref to use `\` to solve the linear equation):

```julia
using StaticArrays

function perspective_transform_SA(
         ((x1, y1), (x2, y2), (x3, y3), (x4, y4)),
         ((X1, Y1), (X2, Y2), (X3, Y3), (X4, Y4)))

    m = SA_F64[
        x1 y1 1 0 0 0 0 0 -X1 0 0 0
        x2 y2 1 0 0 0 0 0 0 -X2 0 0
        x3 y3 1 0 0 0 0 0 0 0 -X3 0
        x4 y4 1 0 0 0 0 0 0 0 0 -X4
        0 0 0 x1 y1 1 0 0 -Y1 0 0 0
        0 0 0 x2 y2 1 0 0 0 -Y2 0 0
        0 0 0 x3 y3 1 0 0 0 0 -Y3 0
        0 0 0 x4 y4 1 0 0 0 0 0 -Y4
        0 0 0 0 0 0 x1 y1 -1 0 0 0 
        0 0 0 0 0 0 x2 y2 0 -1 0 0 
        0 0 0 0 0 0 x3 y3 0 0 -1 0 
        0 0 0 0 0 0 x4 y4 0 0 0 -1 
    ]
    v = SA_F64[0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1]
    values = m \ v
    SA[
    values[1] values[2] values[3]
    values[4] values[5] values[6]
    values[7] values[8] 1
    ]
end

```

```julia
julia> @btime perspective_transform(((0, 0), (0, 100), (100, 0), (100, 100)),
                             ((333, 333), (345,600), (555, 346), (610, 600)))
  3.288 μs (59 allocations: 7.95 KiB)
3×3 Matrix{Float64}:
 2.50406 -0.476459 333.0
 0.307087 1.63268 333.0
 0.000511811 -0.00172887 1.0

julia> @btime perspective_transform_SA(((0, 0), (0, 100), (100, 0), (100, 100)),
                             ((333, 333), (345,600), (555, 346), (610, 600)))
  718.519 ns (0 allocations: 0 bytes)
3×3 SMatrix{3, 3, Float64, 9} with indices SOneTo(3)×SOneTo(3):
 2.50406 -0.476459 333.0
 0.307087 1.63268 333.0
 0.000511811 -0.00172887 1.0

```

At 718 ns per transform, that’s 1.4 million transforms per second. If you only need 400 transforms per second, this won’t be your limiting factor.

---

<div class="post-metadata">

**Author:** ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)\
**Post date:** [September 23, 2021, 4:34pm UTC](https://discourse.julialang.org/t/julia-version-of-cv-getperspectivetransform/68553/12 "2021-09-23T16:34:40Z")

</div>

Okay, that definitely solves the problem! Thank you.
