[Interest] Understanding QImage::transformed()

Samuel Rødal srodal at gmail.com
Sat Dec 15 16:32:51 CET 2018


Accidentally replied off-list, replying again here.

On Fri, Dec 14, 2018 at 11:56 PM Jason H <jhihn at gmx.com> wrote:
>
> I have an image. I have identified 4 control points in in image (1920x1080).
> I want to map the points to a square image, of 1080 on a side.
>
> QPolygonF fromPoly(QVector<QPointF> { ... });
> QPolygonF toPoly(QVector<QPointF> {QPoint(squareDimension/2, 0), QPoint(squareDimension, squareDimension/2), QPoint(squareDimension/2, squareDimension), QPoint(0, squareDimension/2)});
>
> Where the toPoly maps to [(960,0), (1080,960), (960,1080), [0, 960)]

Hmm? Surely toPoly is something like:

QPolygonF toPoly(QVector<QPointF> { QPointF(540, 0), QPointF(1080,
540), QPointF(540, 1080), QPointF(0, 540) });

How can squareDimension == 1080 and squareDimension/2 == 960?

> QTransform tx;
> if (QTransform::quadToQuad(fromPoly, toPoly, tx)) {
>         out = image.transformed(QImage::trueMatrix(tx, image.width(), image.height()));
>         qDebug() << out.save("sdsd1.jpg");
>         out = out.copy ((out.width() - squareDimension)/2,(out.height() - squareDimension)/2, squareDimension, squareDimension);
>         qDebug() << out.save("sdsd2.jpg");
> }
>
> But out is (3270x2179);
> The control points match up a square in that out image of 1600x1600, which is not right. No matter what I do, using trueMatrix() or not, when I crop the image to 1080x1080, it is too zoomed in.
>
> What do I need to go to get all the control points to fit into an image of 1080x1080? Imagine an image (1920x1080) of a clock with some perspective skew. I identify 12,3, 6, and 9 hour positions. I want to create an image of the clock without perspective skew, that is to say, a face-on approximation of the clock.

The result of QImage::trueMatrix() isn't meant to be passed to
QImage::transformed(), since QImage::transformed() will _always_ do
the compensation to ensure all the points of the original image are
included in the transformed image. So using tx or trueMatrix(tx) will
produce the same result. Instead, you want to use QImage::trueMatrix()
to figure out where the (0, 0) origin in the toPoly's coordinate
system gets mapped, in order to cut the (0, 0, 1080, 1080) rectangle
from the transformed image:

    QTransform tx;
   if (QTransform::quadToQuad(fromPoly, toPoly, tx)) {
       QTransform trueMatrix = QImage::trueMatrix(tx, image.width(),
image.height());

       QPoint delta = trueMatrix.map(tx.inverted().map(QPointF(0,
0))).toPoint();
       QImage out = image.transformed(tx,
Qt::SmoothTransformation).copy(delta.x(), delta.y(), 1080, 1080);

     out.save("out.jpg");
   }

We map (0, 0) in the toPoly's coordinate system to the fromPoly and
source image's coordinate system by using the inverse of tx, and then
we map this point to the target image by using the result of
trueMatrix().

trueMatrix docs: "This function returns the modified matrix, which
maps points correctly from the original image into the new image."

Regards,

Samuel



More information about the Interest mailing list