【问题标题】:React Native android camera2 aspect ratio issueReact Native android camera2纵横比问题
【发布时间】:2017-11-29 11:38:13
【问题描述】:

我已经为 Android 创建了一个库(查看this 了解更多信息),Android 中的 Android Camera2 功能一切正常,但全屏摄像头的纵横比问题。

请查看下图预览和实物图(只有预览有问题,拍摄后没有实物图)

水平拍摄的图像:

水平预览图像:

截取的图像垂直:

垂直预览图像:

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

但相同的代码在我的 react 本机库代码中不起作用。可能是我对图像和视频都使用了一个代码,并为视频捕获添加了其他代码。

【问题讨论】:

  • 我记得我前段时间看过这些截图,但我找不到它们出现的问题。那你删了吗?

标签: android react-native android-camera2


【解决方案1】:

通常情况下,相机传感器的纵横比与屏幕纵横比不匹配。结果就是您当前所经历的。

因为纵横比不匹配。您实际上不能强制预览全屏。您必须选择“有效”尺寸。那你是怎么做到的呢?

对于传感器的尺寸,我们无能为力。我们要回答的问题是我的预览高度应该是多少? (假设纵向模式)

这是一个例子:

传感器(假设纵向):

  • 宽度:50
  • 身高:100
  • 纵横比:(宽/高)=0.5

屏幕(假设纵向):

  • 宽度:400
  • 身高:1000
  • 纵横比:(宽/高)=0.4

使用上述值,您的预览图像将被“拉伸”。

解决方法如下:

我们知道我们想要的纵横比:0.5

宽度/高度 = 0.5

我们知道屏幕的宽度(纵向):400

400 / 高度 = 0.5

身高 = 400 / 0.5 = 800

为了在 x 或 y 方向没有拉伸,高度(假设纵向)需要为:预览宽度/所需纵横比

【讨论】:

  • This answer,虽然对于旧的相机 API,也可能是相关的。
【解决方案2】:

我为任何想要更多理论的人详细回答了similar question here

简而言之,在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,
  }
});

你也可以try this code out online or in your Android on Expo Snack

【讨论】:

    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-26
    • 2014-03-25
    • 2023-03-21
    相关资源
    最近更新 更多