OpenCV - 如何在 Android 中设置全屏相机视图?
Posted
技术标签:
【中文标题】OpenCV - 如何在 Android 中设置全屏相机视图?【英文标题】:OpenCV - How to set a Full Screen Camera view in Android? 【发布时间】:2021-02-11 02:01:38 【问题描述】:背景
作为软件新手,我目前的目标是将我的相机预览格式化为全屏,这与 Snapchat 上的相机预览相同。现在,我可以通过关注this tutorial 以 1:1 框格式展示我的相机预览。我在其他问题中遇到的其他潜在解决方案要么拉伸/扭曲了预览图像,要么完全没有启动应用程序。我怎样才能在保持纵向模式的同时做到这一点?下面提供的代码
其他设备规格包括我打算在其上启动应用程序的设备是 OnePlus 6,其纵横比为 19:9。这就是我的应用程序上的相机目前的样子。
.
我想消除预览上方和下方的黑色边框,让相机占据整个屏幕。
MainActivity.java
package com.example.cv;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.Toast;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2
private static String TAG = "MainActivity";
JavaCameraView javaCameraView;
Mat mRGBA, mRGBAT, dst;
private static final int MY_CAMERA_REQUEST_CODE = 100;
BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(MainActivity.this)
@Override
public void onManagerConnected(int status)
if (status == BaseLoaderCallback.SUCCESS)
javaCameraView.enableView();
else
super.onManagerConnected(status);
;
static
if (OpenCVLoader.initDebug())
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
else
Log.d(TAG, "OpenCV not Working or Loaded.");
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "Permissions granted");
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_BACK);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
else
Log.d(TAG, "Permission prompt");
ActivityCompat.requestPermissions(this, new String[]Manifest.permission.CAMERA, MY_CAMERA_REQUEST_CODE);
@Override
public void onCameraViewStarted(int width, int height)
mRGBAT = new Mat();
dst = new Mat();
@Override
public void onCameraViewStopped()
mRGBA.release();
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
mRGBA = inputFrame.rgba();
Core.transpose(mRGBA, mRGBAT);
Core.flip(mRGBAT, mRGBAT, 1);
Imgproc.resize(mRGBAT, dst, mRGBA.size());
mRGBA.release();
mRGBAT.release();
return dst;
@Override
public void onPointerCaptureChanged(boolean hasCapture)
@Override
protected void onDestroy()
super.onDestroy();
if (javaCameraView != null)
javaCameraView.disableView();
@Override
protected void onPause()
super.onPause();
if (javaCameraView != null)
javaCameraView.disableView();
@Override
protected void onResume()
super.onResume();
if (OpenCVLoader.initDebug())
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
else
Log.d(TAG, "OpenCV not Working or Loaded.");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_CAMERA_REQUEST_CODE)
// camera can be turned on
Toast.makeText(this, "camera permission granted", Toast.LENGTH_LONG).show();
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_FRONT);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
else
//camera will stay off
Toast.makeText(this, "camera permission denied", Toast.LENGTH_LONG).show();
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:context=".MainActivity">
<org.opencv.android.JavaCameraView
android:id="@+id/my_camera_view"
android:layout_
android:layout_
/>
</RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cv">
<supports-screens android:resizeable="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-feature android:name="android.hardware.camera.front"/>
<uses-feature android:name="android.hardware.camera.front.autofocus"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
更新:我遇到了旨在改变方向和/或消除操作栏的解决方案。除了拉伸 CameraPreview 的分辨率之外,应用 setMaxFrameSize()
不起作用。
我看到的另一个答案是getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
,它不再有效,因为FLAG_KEEP_SCREEN_ON
已被弃用。如果有人能就我如何解决这个问题提供一点解决方案,我将永远感激不尽。
更新 2:我试图在我的 activity_main.xml
文件中修改 layout_height
,只是让它将预览推到屏幕下方,而它仍然保留它的 1:1 框格式.此外,我还考虑在我的 MainActivity 中实现 javaCameraView.getLayoutParams().height=
只是为了让它扭曲/拉伸相机预览,而不是达到我的预期。
【问题讨论】:
android studio 只是一个 IDE,您尝试实现的结果不会因为您使用 android studio 开发它而改变,所以我已将其从您的问题中删除.就像android-studio
标签所说,当您的问题实际上涉及 IDE 本身时,最好使用它,如果您觉得您的问题特别涉及 IDE,欢迎您恢复我的更改 :)
感谢您的更改!我不知道。
没关系,这是一个常见的错误,很遗憾我无法帮助您解决问题,但我相信您很快就会找到答案
我当然希望如此!这是我在继续下一步之前最想知道的最后一件事!
老实说,我不确定我是否完全明白你想要全屏显示的内容。您指的是 Snapchat 预览 [没有帧更改],然后在 onCameraFrame()
内转置。如果用户打开您的应用程序,他们应该看到什么?一个快照视图?还是自己旋转?我可能错了,但使用当前代码看起来你选择了一个内容[例如一张脸] 例如在纵向模式下,然后您要求 opencv 转置此内容,即将原始纵向“捕获”调整为完整横向,这将扭曲输入
【参考方案1】:
喏,
简答
在代码解释之后,我将在最后留下 MainActivity
类的完整解决方案 - 用于后置和前置摄像头
详细信息
我在这个网站上加入了几个答案并修改了onCameraFrame()
。现在我可以以纵向模式打开应用,作为原生 Android 相机或 Snapchat 视图
这个解决方案真的很轻量级,我没有像这个网站上的其他答案一样更改任何 OpenCV 文件 [例如。 this one]。根据您要使用的activeCamera
[即back or front one]onCameraFrame()
里面需要做一点修改,下面会列出来
共同基础
所以我首先按照this answer 中的 4 个步骤进行操作,正如它们在那里呈现的那样,它们确实让我走上了正确的轨道。值得注意的是,通过这些更改,您将立即获得漂亮的横向视图,但是一旦您将手机置于纵向位置,上下黑色条纹就会回来。将步骤 3 中的行更改为:
android:screenOrientation="portrait"
没有为我解决。此外,如果您想全屏显示,您需要删除标题栏,为此我也合并了this answer。这意味着您还需要在上一步 2 中修改 AndroidManifest.xml
中的这一行
android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"
由于您在两个地方初始化了相机并且需要更改它[当前的sn-p适用于前置相机,您需要后置一个]您可以清理代码并开始定义感兴趣的相机所以您只能在一处修改输入源:
public class MainActivity extends AppCompatActivity implements
CameraBridgeViewBase.CvCameraViewListener2
...
// back camera
int activeCamera = CameraBridgeViewBase.CAMERA_ID_BACK;
// front camera
// int activeCamera = CameraBridgeViewBase.CAMERA_ID_FRONT;
然后将其传递给新的initializeCamera()
方法
private void initializeCamera(JavaCameraView javaCameraView, int activeCamera)
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(activeCamera);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
Android 检测到用户已授予CAMERA
权限后,您必须立即调用:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "Permissions granted");
initializeCamera(javaCameraView, activeCamera);
和:
if (requestCode == MY_CAMERA_REQUEST_CODE)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
Toast.makeText(this, "Camera Permission granted", Toast.LENGTH_LONG).show();
initializeCamera(javaCameraView, activeCamera);
现在是区分前置摄像头和后置摄像头的时候了
后置摄像头
在这种情况下,您根本不需要操作帧,代码如下:
@Override
public void onCameraViewStarted(int width, int height)
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
mRGBA = inputFrame.rgba();
return mRGBA;
前置摄像头
在这种情况下,您需要稍微操纵翻转它们的框架,否则您的纵向模式将显示颠倒
@Override
public void onCameraViewStarted(int width, int height)
mRGBAT = new Mat();
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
mRGBA = inputFrame.rgba();
// flipping to show portrait mode properly
Core.flip(mRGBA, mRGBAT, 1);
// releasing what's not anymore needed
mRGBA.release();
return mRGBAT;
最后,确保您添加到代码中的帧操作确实是必需的。它们的处理时间会降低性能,甚至会使您面临不必要的麻烦。我没有准确检查,但我很确定转置 onCameraFrame()
中的矩阵是失真的根本原因
后置摄像头的 MainActivity
package com.change.package.name;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.Mat;
public class MainActivity extends AppCompatActivity implements
CameraBridgeViewBase.CvCameraViewListener2
private static String TAG = "MainActivity";
JavaCameraView javaCameraView;
Mat mRGBA;
private static final int MY_CAMERA_REQUEST_CODE = 100;
int activeCamera = CameraBridgeViewBase.CAMERA_ID_BACK;
BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(MainActivity.this)
@Override
public void onManagerConnected(int status)
if (status == BaseLoaderCallback.SUCCESS)
javaCameraView.enableView();
else
super.onManagerConnected(status);
;
static
if (OpenCVLoader.initDebug())
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
else
Log.d(TAG, "OpenCV not Working or Loaded.");
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "Permissions granted");
initializeCamera(javaCameraView, activeCamera);
else
Log.d(TAG, "Troubles");
ActivityCompat.requestPermissions(this, new String[]Manifest.permission.CAMERA, MY_CAMERA_REQUEST_CODE);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_CAMERA_REQUEST_CODE)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
Toast.makeText(this, "Camera Permission granted", Toast.LENGTH_LONG).show();
initializeCamera(javaCameraView, activeCamera);
else
Toast.makeText(this, "Camera Permission denied", Toast.LENGTH_LONG).show();
private void initializeCamera(JavaCameraView javaCameraView, int activeCamera)
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(activeCamera);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
@Override
public void onCameraViewStarted(int width, int height)
@Override
public void onCameraViewStopped()
mRGBA.release();
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
// code for the back camera
mRGBA = inputFrame.rgba();
return mRGBA;
@Override
public void onPointerCaptureChanged(boolean hasCapture)
@Override
protected void onDestroy()
super.onDestroy();
if (javaCameraView != null)
javaCameraView.disableView();
@Override
protected void onPause()
super.onPause();
if (javaCameraView != null)
javaCameraView.disableView();
@Override
protected void onResume()
super.onResume();
if (OpenCVLoader.initDebug())
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
else
Log.d(TAG, "OpenCV not Working or Loaded.");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
前置摄像头的 MainActivity
package com.change.package.name;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.Mat;
public class MainActivity extends AppCompatActivity implements
CameraBridgeViewBase.CvCameraViewListener2
private static String TAG = "MainActivity";
JavaCameraView javaCameraView;
Mat mRGBA, mRGBAT;
private static final int MY_CAMERA_REQUEST_CODE = 100;
int activeCamera = CameraBridgeViewBase.CAMERA_ID_FRONT;
BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(MainActivity.this)
@Override
public void onManagerConnected(int status)
if (status == BaseLoaderCallback.SUCCESS)
javaCameraView.enableView();
else
super.onManagerConnected(status);
;
static
if (OpenCVLoader.initDebug())
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
else
Log.d(TAG, "OpenCV not Working or Loaded.");
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "Permissions granted");
initializeCamera(javaCameraView, activeCamera);
else
Log.d(TAG, "Troubles");
ActivityCompat.requestPermissions(this, new String[]Manifest.permission.CAMERA, MY_CAMERA_REQUEST_CODE);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_CAMERA_REQUEST_CODE)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
Toast.makeText(this, "Camera Permission granted", Toast.LENGTH_LONG).show();
initializeCamera(javaCameraView, activeCamera);
else
Toast.makeText(this, "Camera Permission denied", Toast.LENGTH_LONG).show();
private void initializeCamera(JavaCameraView javaCameraView, int activeCamera)
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(activeCamera);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
@Override
public void onCameraViewStarted(int width, int height)
mRGBAT = new Mat();
@Override
public void onCameraViewStopped()
mRGBA.release();
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
// code for the front camera
mRGBA = inputFrame.rgba();
// flipping to show portrait mode properly
Core.flip(mRGBA, mRGBAT, 1);
mRGBA.release();
return mRGBAT;
@Override
public void onPointerCaptureChanged(boolean hasCapture)
@Override
protected void onDestroy()
super.onDestroy();
if (javaCameraView != null)
javaCameraView.disableView();
@Override
protected void onPause()
super.onPause();
if (javaCameraView != null)
javaCameraView.disableView();
@Override
protected void onResume()
super.onResume();
if (OpenCVLoader.initDebug())
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
else
Log.d(TAG, "OpenCV not Working or Loaded.");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
祝你有美好的一天, 安东尼诺
【讨论】:
我必须Pull method `initializeCamera' to 'CvCameraViewListener2
才能实施该新方法吗?如果是这样,它会运行错误错误:CameraBridgeViewBase.CvCameraViewListenerAdapter is not abstract and does not override abstract method initializeCamera(JavaCameraView,int) in CvCameraViewListener2
。如果没有,则 Gradle Build 将不会启动。此外,requestCode
和 grantResults
在输入时会变成红色,作为错误出现。除此之外,我非常感谢您的努力!
@RunMildew 我不确定我是否理解您的问题,但initializeCamera()
可以在onRequestPermissionsResult()
和onCameraViewStarted()
之间添加。将MY_CAMERA_REQUEST_CODE
的声明保留在现在的位置,并在下面添加int activeCamera = CameraBridgeViewBase.CAMERA_ID_BACK;
您已经在正确的位置拥有 private static final int MY_CAMERA_REQUEST_CODE = 100;
并且可见 [= 没有红色标志]。您需要在之后添加int activeCamera = CameraBridgeViewBase.CAMERA_ID_BACK;
。检查 MainActivity 类中的括号,确保每个方法在声明新方法之前关闭
伙伴,添加.FullScreen
应该是可行的。这是my screenshot。我猜你放错了this answer 的第一部分,它必须插入res/values/styles.xml
而不是AndroidManifest.xml。如果您注意到style name
正好是Theme.AppCompat.Light.NoActionBar.FullScreen
。该应用程序有效,我测试了它
这是必须输入"android:screenOrientation="landscape"
的结果。如前所述,如果您将其更改为纵向,它将不再是全屏。在这个问题上有一个github thread,你可以尝试看看那里发布的解决方案是否有帮助。或者您可以解决该问题,例如,您可以使用横向启动屏幕启动应用程序,用户将了解该应用程序默认以该方向工作【参考方案2】:
尝试将其添加到您的 MainActivity.java
@Override
public void onPreviewFrame(byte[] data, Camera camera)
try
Data = data;
if(flag == true)
fheight = camera.getParameters().getPreviewSize().height;
fwidth = camera.getParameters().getPreviewSize().width;
flag = false;
catch (Exception e)
【讨论】:
我无法实现 onPreviewFrame() 方法,因为它是灰色的。有什么办法可以解决这个问题?此外,即使手动输入所有这些,许多代码也会变成红色。无论如何,我非常感谢您的回复!以上是关于OpenCV - 如何在 Android 中设置全屏相机视图?的主要内容,如果未能解决你的问题,请参考以下文章