[英]OpenCV - How to set a Full Screen Camera view in Android?
Background背景
As a novice in software, I am currently aiming to format my camerapreview to full screen, that would be identical to the camera preview on Snapchat.作为软件新手,我目前的目标是将我的相机预览格式化为全屏,这与 Snapchat 上的相机预览相同。 Right now, I am able to showcase my camera preview in a 1:1 box format that I was able to set by following this tutorial .
现在,我可以按照本教程设置的 1:1 框格式展示我的相机预览。 Other potential solutions that I had encountered in other questions either stretched/distorted the previewed image, or didn't launch the application altogether.
我在其他问题中遇到的其他潜在解决方案要么拉伸/扭曲了预览图像,要么完全没有启动应用程序。 How would I be able to do this while still maintaining portrait mode?
我怎样才能在保持纵向模式的同时做到这一点? Code provided below
下面提供的代码
Additional device specifications include the fact that the device I aim to launch the application on is a OnePlus Six, and its aspect ratio is 19:9.其他设备规格包括我打算在其上启动应用程序的设备是 OnePlus Six,其纵横比为 19:9。 This is what the camera on my application currently looks like this.
这就是我的应用程序上的相机目前的样子。
I want to eliminate the black borders above and below the preview and allow the camera to take up the entirety of the screen.我想消除预览上方和下方的黑色边框,并让相机占据整个屏幕。
MainActivity.java主活动.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活动_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_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<org.opencv.android.JavaCameraView
android:id="@+id/my_camera_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</RelativeLayout>
AndroidManifest.xml 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>
UPDATE : I have come across solutions that aim to change orientation and/or eliminate the action bar.更新:我遇到了旨在改变方向和/或消除操作栏的解决方案。 Applying
setMaxFrameSize()
does not work aside from stretching the resolution of the CameraPreview.除了拉伸 CameraPreview 的分辨率之外,应用
setMaxFrameSize()
不起作用。 Another answer I have seen is getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
我看到的另一个答案是
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
which is no longer valid as FLAG_KEEP_SCREEN_ON
has been deprecated.这不再有效,因为
FLAG_KEEP_SCREEN_ON
已被弃用。 If anyone can offer the slightest bit of solution as to how I can fix this, I would be eternally grateful.如果有人能就我如何解决这个问题提供一点点解决方案,我将永远感激不尽。
UPDATE 2 : I have attempted to modify layout_height
in my activity_main.xml
file, only to have it push the preview further down the screen, while it still retains it's 1:1 box format.更新 2 :我试图在我的
activity_main.xml
文件中修改layout_height
,只是让它将预览进一步向下推到屏幕,同时它仍然保留它的 1:1 框格式。 Additionally, I have also considered implementing javaCameraView.getLayoutParams().height=
in my MainActivity only to have it distort/stretch the camera preview and not achieve my intended desire.此外,我还考虑
javaCameraView.getLayoutParams().height=
在我的 MainActivity 中实现javaCameraView.getLayoutParams().height=
只是为了让它扭曲/拉伸相机预览而不实现我的预期愿望。
Ciao,再见,
Short answer简答
I'm leaving at the end the full solution for the MainActivity
class - both for the back and front cameras - after the code explanations在代码解释之后,我将在最后留下
MainActivity
类的完整解决方案 - 无论是后置摄像头还是前置摄像头
Full Details完整详情
I joined a few answers on this website and modified onCameraFrame()
.我在这个网站上加入了一些答案并修改了
onCameraFrame()
。 Now I can open the app in portrait mode, as the native Android camera or Snapchat view现在我可以在纵向模式下打开应用程序,作为原生 Android 相机或 Snapchat 视图
This solution is really lightweight, I didn't change any OpenCV file as in other answers on this website [eg this one ].这个解决方案真的很轻量级,我没有像本网站上的其他答案那样更改任何 OpenCV 文件[例如这个]。 Depending on the
activeCamera
you want to use [ie back or front one] there is a little modification required inside onCameraFrame()
, which I will list below根据您要使用的
activeCamera
[即背面或正面],在onCameraFrame()
内部需要进行一些修改,我将在下面列出
Common base共同基地
So I first followed the 4 steps in this answer , exactly as they are presented there and they really put me on the right track.所以我首先遵循了这个答案中的 4 个步骤,正如他们在那里展示的那样,他们真的让我走上了正确的轨道。 It is worth to notice that with these changes you will get immediately a good looking landscape view but as soon as you put your phone in a portrait position the upper and lower black bands will come back.
值得注意的是,通过这些更改,您将立即获得漂亮的横向视图,但是一旦您将手机置于纵向位置,上下黑带就会回来。 Changing the line at step 3 into:
将步骤 3 中的行更改为:
android:screenOrientation="portrait"
didn't solve for me.没有为我解决。 Also, if you want to go full screen you need to remove the title bar and in order to do so I incorporated this answer too.
另外,如果你想全屏显示,你需要删除标题栏,为此我也加入了这个答案。 This means that you will need to modify also this line in the
AndroidManifest.xml
from the previous step 2这意味着您还需要修改上一步 2 中
AndroidManifest.xml
这一行
android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"
Since you initialize the camera in two places and you need to change it [the current snippet works with the front camera, you need the back one] you can clean-up the code and start defining the camera of interest so that you can modify the input source in one point only:由于您在两个地方初始化相机并且需要更改它[当前代码段适用于前置相机,您需要后置相机]您可以清理代码并开始定义感兴趣的相机,以便您可以修改仅在一点输入源:
public class MainActivity extends AppCompatActivity implements
CameraBridgeViewBase.CvCameraViewListener2
{
...
// back camera
int activeCamera = CameraBridgeViewBase.CAMERA_ID_BACK;
// front camera
// int activeCamera = CameraBridgeViewBase.CAMERA_ID_FRONT;
then pass it to a new initializeCamera()
method然后将其传递给新的
initializeCamera()
方法
private void initializeCamera(JavaCameraView javaCameraView, int activeCamera){
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(activeCamera);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
}
which you have to call as soon as Android detects that the user has given the CAMERA
permission:一旦 Android 检测到用户已授予
CAMERA
权限,您必须立即调用它:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Permissions granted");
initializeCamera(javaCameraView, activeCamera);
and:和:
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);
Now it's the moment to distinguish between front and back camera现在是区分前后摄像头的时候了
Back camera后置摄像头
In this case you won't need to manipulate the frames at all and the code goes as follows:在这种情况下,您根本不需要操作帧,代码如下:
@Override
public void onCameraViewStarted(int width, int height){
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
{
mRGBA = inputFrame.rgba();
return mRGBA;
}
Front camera前置摄像头
In this case you need to slightly manipulate the frames flipping them, otherwise your portrait mode will show upside down在这种情况下,您需要稍微操纵翻转它们的框架,否则您的肖像模式将颠倒显示
@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;
}
Finally, be sure that the frame manipulations you add to your code are really required.最后,确保您添加到代码中的帧操作确实是必需的。 Their processing time lowers performances and can even expose you to unrequired troubles.
它们的处理时间会降低性能,甚至会让您遇到不必要的麻烦。 I didn't check accurately but I'm quite sure that transposing the matrices in your
onCameraFrame()
was the root cause for distortion我没有准确检查,但我很确定转置
onCameraFrame()
的矩阵是失真的根本原因
MainActivity for back camera后置摄像头的 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 for front camera前置摄像头的 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);
}
}
}
Have a good day,祝你有美好的一天,
Antonino安东尼诺
Try adding this to your MainActivity.java尝试将此添加到您的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) {
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.