mrousavy / react-native-vision-camera

📸 A powerful, high-performance React Native Camera library.
https://react-native-vision-camera.com
MIT License
7.31k stars 1.07k forks source link

💭 How to get a 9/16 aspect ratio for photos and videos? #3083

Closed VictorioMolina closed 2 months ago

VictorioMolina commented 2 months ago

Question

I want to record videos and take photos on Instagram Stories aspect ratio -> with portrait orientation for both the camera and the output.

I have tried with the Templates, but as a result I get horizontal photos

const SnapchatResolution = { width: 1920, height: 1080 }
const InstagramResolution = { width: 3840, height: 2160 }
const ScreenAspectRatio = Dimensions.get('window').height / Dimensions.get('window').width

How could we achieve this? I couldn't find any example where height > width in the documentation for camera format.

What I tried

const format = useCameraFormat(device, [
    { videoResolution: "max" },
    { videoAspectRatio: 9 / 16 },
    { photoResolution: "max" },
    { photoAspectRatio: 9 / 16 },
  ]);

But the resulting aspect ratio, in the format is 4032/3024:

  {
  "minFps": 1,
  "videoWidth": 4032, <-----------------
  "minISO": 32,
  "photoHeight": 3024, <-----------------
  "maxFps": 30,
  "fieldOfView": 102.90327453613281,
  "videoHeight": 3024, <-----------------
  "autoFocusSystem": "phase-detection",
  "supportsVideoHdr": false,
  "supportsPhotoHdr": false,
  "photoWidth": 4032, <-----------------
  "supportsDepthCapture": false,
  "maxISO": 3072,
  "pixelFormats": [
    "yuv",
    "yuv",
    "rgb"
  ],
  "maxZoom": 189,
  "videoStabilizationModes": [
    "auto",
    "off"
  ]
}

VisionCamera Version

^3.9.2 (also tried with ^4.5.0)

Additional information

maintenance-hans[bot] commented 2 months ago

Guten Tag, Hans here.

[!NOTE] New features, bugfixes, updates and other improvements are all handled mostly by @mrousavy in his free time. To support @mrousavy, please consider 💖 sponsoring him on GitHub 💖. Sponsored issues will be prioritized.

VictorioMolina commented 2 months ago

Also tried with last version 4.5.0 and outputOrientation = “device” and “preview”.

The photo orientation is always landscape-right on my iPhone 11 Pro.

Not using frame processors.

 LOG  outputOrientation prop = "device"
 LOG  onOutputOrientationChanged: portrait
 LOG  Format filters: {"photoAspectRatio": 2.1653333333333333} {"photoResolution": {"height": 2160, "width": 3840}} {"videoAspectRatio": 2.1653333333333333} {"videoResolution": {"height": 2160, "width": 3840}}
 LOG  Captured photo orientation: landscape-right
VictorioMolina commented 2 months ago

Camera preview:

WhatsApp Image 2024-07-16 at 13 09 57

Result:

WhatsApp Image 2024-07-16 at 13 09 58

mrousavy commented 2 months ago

Don't use V3, use V4.

Also, formats are always in horizontal (or device native) orientation, this is mentioned in the Format docs.

Please read the Format documentation - there is also a format selector for aspect ratio

VictorioMolina commented 2 months ago

@mrousavy Hey, thank you for your answer, I really appreciate it and the work you are doing with this library.

I read documentation some months ago, while implementing the camera system. Have checked the formats docs again, and yes, it is working as expected.

The error I was having was related to the photo.width always being bigger than photo.height (after mapping my formats, I detected there were no vertical ones).

_1. When rendering with react native image, with absolute fill, the image is correctly visualized.

  1. When saving in the camera roll (I suppose because of Exif metadata), it is rendered correctly in the device´s native gallery._ 3. When rendering the image without absolute fill, it seems we need to switch the properties when snap orientation !== camera preview orientation??

For example, here, using Skia Images:

    const renderCanvas = () => (
      <SkiaCanvas ref={ref} style={[globalStyles.flexCenteredContainer, style]}>
        <Group transform={transformation}>
          <Group matrix={matrix}>
            <Image
              image={image}
              width={width} <---------- 4224px
              height={height} <----------  2376px (the image orientation is not correct, is it safe to switch the properties?).
              fit="cover"
            >
              {filter && <ColorMatrix matrix={filter.data} />}
            </Image>
          </Group>
        </Group>
      </SkiaCanvas>
    );

When I say switching the properties, I mean something like:

image = { // Do this when required, based on Exif metadata Orientation?
  ...image,
  width: image.height,
  height: image.width,
};

OR

let image = await cameraRef.current.takePhoto(settings);

const asset = await cameraRoll.createAsset(image.path);

image = { // The asset returned by the camera roll asset always reflects correct dimensions
  path: image.path,
  width: asset.width,
  height: asset.height,
};

When I do this, image is rendered 100% correct on the UI, after responsifying the dimensions.

Aspect ratio is good. Problem is with dimensions, making images look horizontal when they were taken vertically.

mrousavy commented 2 months ago

The error I was having was related to the photo.width always being bigger than photo.height (after mapping my formats, I detected there were no vertical ones).

Exactly - this is because Camera sensors are always in a fixed native orientation, which is landscape in most of the cases.

In VisionCamera, I use transform matrixes, rotations and EXIF tags to rotate the images to the desired output rotation (portrait in your case).

This is expected.

mrousavy commented 2 months ago

Wait sorry I thought you were referring to format.photoWidth, but you actually said photo.width?

Let me try this to see if I can reproduce this.

mrousavy commented 2 months ago

To be honest I would probably rely on <Image> or some other component that actually loads the image to give you width/height properties - those are already accounting for pixel ratio and orientation. VisionCamera gives you the true native dimensions, while Image loaders give you the dimensions after loading the image.

VictorioMolina commented 2 months ago

To be honest I would probably rely on or some other component that actually loads the image to give you width/height properties - those are already accounting for pixel ratio and orientation.

My use case requires Skia image and canvas, with explicit width and height animated properties 😥. For now, as a workaround, I am saving to camera roll and swapping the photo's file dimensions with the ones included in the camera roll asset, ignoring Exif metadata to prevent incorrect behaviour because of that swap.