Home > Back-end >  How to calculate x/y rotation from corner points?
How to calculate x/y rotation from corner points?

Time:10-12

I am using the camera of a mobile phone to get a stream of images, and then analyze the images to find QR codes in them (using Googles ML Kit). Once a QR code has been found, I get the four corner-points of the QR code in the image. I want to overlay this with a rectangle with content in it ("Container" with children in Flutter).

However, usually the camera isn't aligned perfectly to the QR code, so the corner points don't form a rectangle. So in order to overlay the QR code with my rectangle, I need to transform my rectangle so it fits to the corner points.

My question is, given the four corner points, how do I find out the transformation matrix that I need to apply to my content?

CodePudding user response:

this is a quick port of skia's SkMatrix::setPolyToPoly method - i am not sure about 3 places so check TODO in the code below:

Matrix4? setPolyToPoly(List<Offset> src, List<Offset> dst) {
  Matrix4 tempMap = Matrix4.identity();
  Matrix4 result = Matrix4.identity();

  if (!_poly4Proc(src, tempMap)) {
    return null;
  }

  // TODO no idea what to do here...
  // copyInverse returns some double, but what is it???
  // docs say nothing...
  // original code checks if matrix can be inverted:
  // if (!tempMap.invert(&result)) {
  //     return false;
  // }
  result.copyInverse(tempMap);

  if (!_poly4Proc(dst, tempMap)) {
    return null;
  }
  return tempMap * result;
}

bool _poly4Proc(List<Offset> src, Matrix4 matrix) {
  double a1, a2;
  double x0, y0, x1, y1, x2, y2;

  x0 = src[2].dx - src[0].dx;
  y0 = src[2].dy - src[0].dy;
  x1 = src[2].dx - src[1].dx;
  y1 = src[2].dy - src[1].dy;
  x2 = src[2].dx - src[3].dx;
  y2 = src[2].dy - src[3].dy;

  /* check if abs(x2) > abs(y2) */
  if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
    double denom = _ieeeFloatDivide(x1 * y2, x2) - y1;
    if (_checkForZero(denom)) {
      return false;
    }
    a1 = (((x0 - x1) * y2 / x2) - y0   y1) / denom;
  } else {
    double denom = x1 - _ieeeFloatDivide(y1 * x2, y2);
    if (_checkForZero(denom)) {
      return false;
    }
    a1 = (x0 - x1 - _ieeeFloatDivide((y0 - y1) * x2, y2)) / denom;
  }

  /* check if abs(x1) > abs(y1) */
  if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
    double denom = y2 - _ieeeFloatDivide(x2 * y1, x1);
    if (_checkForZero(denom)) {
      return false;
    }
    a2 = (y0 - y2 - _ieeeFloatDivide((x0 - x2) * y1, x1)) / denom;
  } else {
    double denom = _ieeeFloatDivide(y2 * x1, y1) - x2;
    if (_checkForZero(denom)) {
      return false;
    }
    a2 = (_ieeeFloatDivide((y0 - y2) * x1, y1) - x0   x2) / denom;
  }

  final array = <double>[
    a2 * src[3].dx   src[3].dx - src[0].dx, a2 * src[3].dy   src[3].dy - src[0].dy, 0, a2,
    a1 * src[1].dx   src[1].dx - src[0].dx, a1 * src[1].dy   src[1].dy - src[0].dy, 0, a1,
    0, 0, 1, 0,
    src[0].dx, src[0].dy, 0, 1,
  ];
  matrix.copyFromArray(array);
  return true;
}

// TODO not sure if its ok
double _ieeeFloatDivide(double d0, double d1) => d0 / d1;

// TODO not sure if its ok
bool _checkForZero(double d) => d == 0;

sample test widget:

class FooPerspective extends StatelessWidget {
  static const src = [
    Offset(0, 0),
    Offset(100, 0),
    Offset(100, 100),
    Offset(0, 100),
  ];
  static const dst = [
    Offset(100, 100),
    Offset(200, 120),
    Offset(220, 190),
    Offset(120, 200),
  ];

  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: Stack(
        children: [
          Transform(
            transform: setPolyToPoly(src, dst)!,
            child: Container(
              width: 100,
              height: 100,
              padding: const EdgeInsets.all(4),
              decoration: const BoxDecoration(
                color: Colors.orange,
                boxShadow: [BoxShadow(spreadRadius: 1, blurRadius: 6, offset: Offset(3, 3))],
              ),
              child: const Text('Cillum non minim officia excepteur in qui.', textScaleFactor: 1.2),
            ),
          ),
          ...dst.map((o) => Positioned(
            left: o.dx - 8,
            top: o.dy - 8,
            width: 16,
            height: 16,
            child: Container(
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                border: Border.all(width: 1),
              ),
            ),
          ))
        ],
      ),
    );
  }
}

the result:

enter image description here

  • Related