Implementing perspective transform using vips

281 views Asked by At

I am trying to implement a perspective transform using vips in Go. I am using opencv's GetPerspectiveTransform method to calculate a transform matrix then computing a map image as follows

func DistortPerspective(ref *vips.ImageRef, tiepoints []float64) {
    T := createTransform(tiepoints)

    // make an index image where pixels have the value of their (x, y) coordinates
    index, err := vips.XYZ(ref.Width(), ref.Height())
    if err != nil {
        logger.Debug(nil, err)
    }

    i0, err := index.Copy()
    if err != nil {
        logger.Debug(nil, err)
    }
    i1, err := index.Copy()
    if err != nil {
        logger.Debug(nil, err)
    }
    err = i0.ExtractBand(0, 1)
    if err != nil {
        logger.Debug(nil, err)
    }
    err = i1.ExtractBand(1, 1)
    if err != nil {
        logger.Debug(nil, err)
    }

    T00 := float64(T.GetFloatAt(0, 0))
    T01 := float64(T.GetFloatAt(0, 1))
    T02 := float64(T.GetFloatAt(0, 2))

    T10 := float64(T.GetFloatAt(1, 0))
    T11 := float64(T.GetFloatAt(1, 1))
    T12 := float64(T.GetFloatAt(1, 2))

    T20 := float64(T.GetFloatAt(2, 0))
    T21 := float64(T.GetFloatAt(2, 1))
    T22 := float64(T.GetFloatAt(2, 2))

    // i0 * T[0,0]
    i0xT00 := linear(i0, T00, 0)

    // i1 * T[0,1] + T[0,2]
    i1xT01_T02 := linear(i1, T01, T02)

    // i0 * T[1,0]
    i0xT10 := linear(i0, T10, 0)

    // i1 * T[1,1] + T[1,2]
    i1xT11_T12 := linear(i1, T11, T12)

    //i[0] * T[0,0] + i[1] * T[0,1] + T[0,2]
    i0xT00_i1xT01_T02 := add(i0xT00, i1xT01_T02)

    //i[0] * T[1,0] + i[1] * T[1,1] + T[1,2]
    i0xT10_i1xT11_T12 := add(i0xT10, i1xT11_T12)

    //i[0] * T[2,0]
    i0xT20 := linear(i0, T20, 0)

    //i[1] * T[2,1] + T[2,2]
    i1xT21_T22 := linear(i1, T21, T22)

    //i[0] * T[2,0] + i[1] * T[2,1] + T[2,2]
    i0xT20_i1xT21_T22 := add(i0xT20, i1xT21_T22)

    //x = (i[0] * T[0,0] + i[1] * T[0,1] + T[0,2]) / (i[0] * T[2,0] + i[1] * T[2,1] + T[2,2])
    x := divide(i0xT00_i1xT01_T02, i0xT20_i1xT21_T22)

    //y = (i[0] * T[1,0] + i[1] * T[1,1] + T[1,2]) / (i[0] * T[2,0] + i[1] * T[2,1] + T[2,2])
    y := divide(i0xT10_i1xT11_T12, i0xT20_i1xT21_T22)

    //# join up x and y as a map image
    mapimage := bandjoin(x, y)

    //transform the original image
    err = ref.Mapim(mapimage)
    if err != nil {
        logger.Debug(nil, err)
    }
}

However the image that comes as a result is wrong. I was using this Stack Overflow answer as a reference How to perform perspective distort transformation in VIPS? and it appeared to me that opencv WarpPerspective method does something similar so I thought that using opencv to calculate transform coefficients would work.

1

There are 1 answers

0
axkirillov On

The answer was two-fold.

First of all, use GetDoubleAt(row int, col int) method that returns proper float64 instead of GetFloatAt(row int, col int), that returns float32. Somehow the conversion was messing up the numbers.

Second of all, in the method for calculating the transform matrix, in the GetPerspectiveTransform call, source and destination sets should be flipped. That is because opencv's WarpPerspective method implicitly inverts the matrix, while this implementation does not.

func createTransform(distortion []float64) gocv.Mat {
    srcSet := []image.Point{
        {X: int(distortion[0]), Y: int(distortion[1])},
        {X: int(distortion[4]), Y: int(distortion[5])},
        {X: int(distortion[8]), Y: int(distortion[9])},
        {X: int(distortion[12]), Y: int(distortion[13])},
    }
    dstSet := []image.Point{
        {X: int(distortion[2]), Y: int(distortion[3])},
        {X: int(distortion[6]), Y: int(distortion[7])},
        {X: int(distortion[10]), Y: int(distortion[11])},
        {X: int(distortion[14]), Y: int(distortion[15])},
    }
    return gocv.GetPerspectiveTransform(dstSet, srcSet)
}