React Native android camera2纵横比问题

时间:2017-11-29 11:38:13

标签: android react-native android-camera2

我已经创建了一个库(请查看this以获取更多信息),因为Android for Android Camera2功能在Android中运行良好,但是在全屏相机的宽高比问题上。

请检查下面的图像,它在预览和真实图片中的外观(只有预览的问题不是拍摄后的真实照片)

捕获的图像水平:

enter image description here

水平预览图片:

enter image description here

垂直捕获的图像:

enter image description here

垂直预览图片:

enter image description here

Google的Camera2 API样本有类似的问题并已解决here

但是同样的代码在我的本机库代码中没有工作。可能是我正在使用一个代码用于图像和视频,并添加额外的视频捕获代码。

2 个答案:

答案 0 :(得分:1)

通常,相机传感器的宽高比与屏幕宽高比不匹配。结果就是您目前正在经历的事情。

由于纵横比不匹配。您实际上无法强制预览为全屏。您选择“有效”尺寸。那你怎么做的?

我们无法对传感器的尺寸做些什么。我们要回答的问题是我的预览高度应该是多少? (假设肖像模式)

以下是一个例子:

传感器(假设人像):

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

屏幕(假设肖像):

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

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

以下是修复方法:

我们知道我们想要的宽高比: 0.5

宽度/高度= 0.5

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

400 /身高= 0.5

身高= 400 / 0.5 = 800

为了不在x或y方向拉伸,高度(假设为肖像)需要为:预览宽度/所需宽高比

答案 1 :(得分:0)

我为想要更多理论的任何人详细回答了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