cameraFrameToBgrMat function

Mat cameraFrameToBgrMat(
  1. CameraFrame frame, {
  2. int? maxDim,
})

Decodes a CameraFrame into a 3-channel BGR cv.Mat.

The layout and safe operation order come from flutter_litert (CameraFrame.decodePlan); this only maps that backend-neutral plan onto OpenCV primitives. Equivalent to the previous inline conversion: for 4-channel frames it resizes/rotates before dropping alpha; for packed YUV it colour-converts first (the packed layout can't be resized).

Implementation

cv.Mat cameraFrameToBgrMat(CameraFrame frame, {int? maxDim}) {
  final CameraFrameDecodePlan plan = frame.decodePlan();
  final cv.Mat source = matFromPackedLayout(
    plan.sourceLayout,
    frame.bytes,
    plan.sourceLayout.channels == 4 ? cv.MatType.CV_8UC4 : cv.MatType.CV_8UC1,
  );

  cv.Mat maybeResize(cv.Mat m) {
    if (maxDim == null || (m.cols <= maxDim && m.rows <= maxDim)) return m;
    final double scale = maxDim / (m.cols > m.rows ? m.cols : m.rows);
    final cv.Mat resized = cv.resize(m, (
      (m.cols * scale).toInt(),
      (m.rows * scale).toInt(),
    ), interpolation: cv.INTER_LINEAR);
    m.dispose();
    return resized;
  }

  int? rotateFlag() {
    return switch (plan.rotation) {
      CameraFrameRotation.cw90 => cv.ROTATE_90_CLOCKWISE,
      CameraFrameRotation.cw180 => cv.ROTATE_180,
      CameraFrameRotation.cw270 => cv.ROTATE_90_COUNTERCLOCKWISE,
      null => null,
    };
  }

  cv.Mat maybeRotate(cv.Mat m) {
    final int? flag = rotateFlag();
    if (flag == null) return m;
    final cv.Mat rotated = cv.rotate(m, flag);
    m.dispose();
    return rotated;
  }

  final int cvtCode = switch (plan.conversion) {
    CameraFrameConversion.bgra2bgr => cv.COLOR_BGRA2BGR,
    CameraFrameConversion.rgba2bgr => cv.COLOR_RGBA2BGR,
    CameraFrameConversion.yuv2bgrNv12 => cv.COLOR_YUV2BGR_NV12,
    CameraFrameConversion.yuv2bgrNv21 => cv.COLOR_YUV2BGR_NV21,
    CameraFrameConversion.yuv2bgrI420 => cv.COLOR_YUV2BGR_I420,
  };

  switch (plan.order) {
    case CameraFrameDecodeOrder.resizeRotateThenColorConvert:
      cv.Mat current = plan.hasStridePadding
          ? source.region(cv.Rect(0, 0, plan.visibleWidth, plan.visibleHeight))
          : source;

      if (maxDim != null && (current.cols > maxDim || current.rows > maxDim)) {
        final double scale =
            maxDim /
            (current.cols > current.rows ? current.cols : current.rows);
        final cv.Mat resized = cv.resize(current, (
          (current.cols * scale).toInt(),
          (current.rows * scale).toInt(),
        ), interpolation: cv.INTER_LINEAR);
        if (!identical(current, source)) current.dispose();
        current = resized;
      }

      final int? flag = rotateFlag();
      if (flag != null) {
        final cv.Mat rotated = cv.rotate(current, flag);
        if (!identical(current, source)) current.dispose();
        current = rotated;
      }

      final cv.Mat bgr = cv.cvtColor(current, cvtCode);
      if (!identical(current, source)) current.dispose();
      source.dispose();
      return bgr;

    case CameraFrameDecodeOrder.colorConvertThenResizeRotate:
      cv.Mat current = cv.cvtColor(source, cvtCode);
      source.dispose();
      current = maybeResize(current);
      current = maybeRotate(current);
      return current;
  }
}