React Native安卓相机2纵横比问题

3
我创建了一个库(请查看此处获取更多信息),用于在Android上实现Android Camera2功能,一切都运行良好,但是全屏相机的纵横比存在问题。

请查看下面的图片,预览中的外观和真实图片不同(仅预览存在问题,拍摄后的真实图片没有问题)

水平方向的拍摄图像:

enter image description here

水平方向的预览图像:

enter image description here

垂直方向的拍摄图像:

enter image description here

垂直方向的预览图像:

enter image description here

Google的Camera2 API示例有类似的问题并已解决here

但是相同的代码在我的React Native库代码中不起作用。可能我正在使用一个代码来捕获图像和视频,并添加了额外的代码来捕获视频。


我记得我有一段时间前看到过这些截图,但是我找不到它们出现的问题了。你当时删除了它吗? - Alex Cohn
2个回答

2

我在这里对一个类似的问题作了详细回答,内容涉及编程理论。

简而言之,在 cameraReady 事件中,你需要获取屏幕大小、支持的摄像头宽高比,并找到最接近屏幕比例但不过度高的比例。

一个示例应用程序如下所示:

import React, { useEffect, useState } from 'react';
import {StyleSheet, View, Text, Dimensions, Platform } from 'react-native';
import { Camera } from 'expo-camera';
import * as Permissions from 'expo-permissions';

export default function App() {
  //  camera permissions
  const [hasCameraPermission, setHasCameraPermission] = useState(null);
  const [camera, setCamera] = useState(null);

  // Screen Ratio and image padding
  const [imagePadding, setImagePadding] = useState(0);
  const [ratio, setRatio] = useState('4:3');  // default is 4:3
  const { height, width } = Dimensions.get('window');
  const screenRatio = height / width;
  const [isRatioSet, setIsRatioSet] =  useState(false);

  // on screen  load, ask for permission to use the camera
  useEffect(() => {
    async function getCameraStatus() {
      const { status } = await Permissions.askAsync(Permissions.CAMERA);
      setHasCameraPermission(status == 'granted');
    }
    getCameraStatus();
  }, []);

  // set the camera ratio and padding.
  // this code assumes a portrait mode screen
  const prepareRatio = async () => {
    let desiredRatio = '4:3';  // Start with the system default
    // This issue only affects Android
    if (Platform.OS === 'android') {
      const ratios = await camera.getSupportedRatiosAsync();

      // Calculate the width/height of each of the supported camera ratios
      // These width/height are measured in landscape mode
      // find the ratio that is closest to the screen ratio without going over
      let distances = {};
      let realRatios = {};
      let minDistance = null;
      for (const ratio of ratios) {
        const parts = ratio.split(':');
        const realRatio = parseInt(parts[0]) / parseInt(parts[1]);
        realRatios[ratio] = realRatio;
        // ratio can't be taller than screen, so we don't want an abs()
        const distance = screenRatio - realRatio; 
        distances[ratio] = realRatio;
        if (minDistance == null) {
          minDistance = ratio;
        } else {
          if (distance >= 0 && distance < distances[minDistance]) {
            minDistance = ratio;
          }
        }
      }
      // set the best match
      desiredRatio = minDistance;
      //  calculate the difference between the camera width and the screen height
      const remainder = Math.floor(
        (height - realRatios[desiredRatio] * width) / 2
      );
      // set the preview padding and preview ratio
      setImagePadding(remainder / 2);
      setRatio(desiredRatio);
      // Set a flag so we don't do this 
      // calculation each time the screen refreshes
      setIsRatioSet(true);
    }
  };

  // the camera must be loaded in order to access the supported ratios
  const setCameraReady = async() => {
    if (!isRatioSet) {
      await prepareRatio();
    }
  };

  if (hasCameraPermission === null) {
    return (
      <View style={styles.information}>
        <Text>Waiting for camera permissions</Text>
      </View>
    );
  } else if (hasCameraPermission === false) {
    return (
      <View style={styles.information}>
        <Text>No access to camera</Text>
      </View>
    );
  } else {
    return (
      <View style={styles.container}>
        {/* 
        We created a Camera height by adding margins to the top and bottom, 
        but we could set the width/height instead 
        since we know the screen dimensions
        */}
        <Camera
          style={[styles.cameraPreview, {marginTop: imagePadding, marginBottom: imagePadding}]}
          onCameraReady={setCameraReady}
          ratio={ratio}
          ref={(ref) => {
            setCamera(ref);
          }}>
        </Camera>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  information: { 
    flex: 1,
    justifyContent: 'center',
    alignContent: 'center',
    alignItems: 'center',
  },
  container: {
    flex: 1,
    backgroundColor: '#000',
    justifyContent: 'center',
  },
  cameraPreview: {
    flex: 1,
  }
});

你可以 在Expo Snack上在线或在Android上尝试此代码

2
往往情况下,相机传感器的长宽比与屏幕的长宽比不匹配。你正在经历的就是这种结果。
由于这种长宽比不匹配,你无法强制预览全屏。你必须选择“有效”的尺寸。那么该怎么做呢?
我们无法改变传感器的尺寸。我们需要回答的问题是,我的预览高度应该是多少?(假设是竖屏模式)
以下是一个例子:
传感器(假设为竖屏):
- 宽度:50 - 高度:100 - 长宽比:(宽度/高度)=0.5
屏幕(假设为竖屏):
- 宽度:400 - 高度:1000 - 长宽比:(宽度/高度)=0.4
根据上述数值,你的预览图像将会被“拉伸”。
以下是如何解决这个问题:
我们知道我们想要的长宽比:0.5 宽度/高度=0.5
我们知道屏幕的宽度(竖屏):400 400/高度=0.5 高度=400/0.5=800 为了在x或y方向上没有拉伸,高度(假设为竖屏)应为:预览宽度/所需长宽比

尽管针对旧相机API,但是此答案可能也相关。 - Alex Cohn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接