在自定义Camera2 API上显示矩形边界框(覆盖),以便仅捕获该框内的图像 [英] Display a rectangular bounding box (overlay) on custom Camera2 API so only the image inside the box is captured
问题描述
我正在使用以下代码通过自定义Camera2 API捕获图像.我是一个初学者,我不知道如何在相机预览中显示矩形叠加层.我确实尝试过这里提供的解决方案,但无法弄清楚如何使其适合我的代码.
I'm using the following code to capture an image using the custom Camera2 API. I am a beginner and I have no idea how to display a rectangular overlay on the camera preview. I did try the solutions available here, but I can't figure out how to fit it in my code.
我需要实现这样的功能以捕获A4纸.
I need to achieve something like this to capture A4 sheets.
只有框中的图像部分必须被捕获并显示在下一个活动中.
Only the part of the image which is in the box must be captured and displayed in the next activity.
MainActivity
MainActivity
package com.example.customcamera;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static
{
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
TextureView textureView;
private CameraDevice cameraDevice;
private String cameraId;
private Size imageDimensions;
private CameraCaptureSession cameraCaptureSession;
private CaptureRequest.Builder captureRequestBuilder;
private ImageReader imageReader;
private boolean flashSupported;
private HandlerThread backgroundThread;
private Handler backgroundHandler;
CameraDevice.StateCallback stateCallback;
File file;
final int MY_PERMISSIONS_REQUEST_CAMERA=102;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
MainActivity.this.cameraDevice.close();
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
MainActivity.this.cameraDevice.close();
MainActivity.this.cameraDevice = null; // Change to global
}
};
textureView = findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(textureListener);
Button btn_camera = findViewById(R.id.btn_camera);
btn_camera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takePicture();
}
});
}
public void takePicture()
{
if(cameraDevice == null)
return;
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try
{
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraDevice.getId());
Size[] jpegSizes = null;
if(cameraCharacteristics == null)
jpegSizes = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
// Capture image with custom size
int width = 640;
int height = 480;
if(jpegSizes != null && jpegSizes.length > 0)
{
width = jpegSizes[0].getWidth();
height = jpegSizes[0].getHeight();
}
imageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
final List<Surface> outputSurface = new ArrayList<>(2);
outputSurface.add(imageReader.getSurface());
outputSurface.add(new Surface(textureView.getSurfaceTexture()));
final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
// Check orientation base on device
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader imageReader) {
file = new File(Environment.getExternalStorageDirectory() + "/" + UUID.randomUUID().toString() + ".jpg");
Image image = null;
try
{
image = imageReader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
buffer.rewind();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
save(bytes);
startActivity(new Intent(MainActivity.this, ImageDisplay.class).putExtra("FILE", file.getPath()));
}
finally {
if(image != null)
image.close();
}
}
public void save(byte[] bytes)
{
OutputStream outputStream = null;
try
{
outputStream = new FileOutputStream(file);
outputStream.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(outputStream == null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
imageReader.setOnImageAvailableListener(readerListener, backgroundHandler);
final CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
Toast.makeText(MainActivity.this, "Saved", Toast.LENGTH_SHORT).show();
createCameraPreview();
}
};
cameraDevice.createCaptureSession(outputSurface, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
try {
cameraCaptureSession.capture(captureBuilder.build(), captureCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
}
}, backgroundHandler);
}
catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void createCameraPreview() {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(imageDimensions.getWidth(), imageDimensions.getHeight());
Surface surface = new Surface(texture);
try {
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if(cameraDevice == null)
return;
cameraCaptureSession = session;
updatePreview();
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Changed", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void updatePreview() {
if(cameraDevice == null)
Toast.makeText(MainActivity.this, "Error!", Toast.LENGTH_SHORT).show();
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
try {
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void openCamera()
{
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try
{
cameraId = cameraManager.getCameraIdList()[0];
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
assert map != null;
imageDimensions = map.getOutputSizes(SurfaceTexture.class)[0];
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Permission to Camera is not granted, request for permission
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_CAMERA);
return;
}
cameraManager.openCamera(cameraId, stateCallback, null);
}
catch (CameraAccessException e) {
e.printStackTrace();
}
}
TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
Log.e("Surfacetexturedestroyed", "called");
if(cameraDevice != null)
{
Log.e("Camera not null", "make null");
cameraDevice.close();
cameraDevice = null;
}
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
};
@Override
protected void onResume() {
super.onResume();
startBackgroundThread();
if(textureView.isAvailable())
openCamera();
else
textureView.setSurfaceTextureListener(textureListener);
}
@Override
protected void onPause() {
stopBackgroundThread();
super.onPause();
}
private void stopBackgroundThread() {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
private void startBackgroundThread() {
backgroundThread = new HandlerThread("Camera Background");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
/**
* Handler for handling the user event after requesting permission.
*/
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
{
switch (requestCode)
{
case 102: //MY_PERMISSIONS_REQUEST_CAMERA=102 Use Camera
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
// permission was granted, yay!
openCamera();
}
else
{
// permission denied, boo!
Toast.makeText(this, "Permission to use device Camera denied! Cannot proceed ahead!", Toast.LENGTH_SHORT).show();
finish();
}
break;
default: Toast.makeText(this, "Failed to handle permissions response!", Toast.LENGTH_SHORT).show();
}
}
}
activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextureView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/textureView"
android:layout_alignParentTop="true"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Take Picture"
android:textSize="30sp"
android:id="@+id/btn_camera"
android:layout_alignParentBottom="true"
android:layout_margin="8dp"/>
</RelativeLayout>
ImageDisplay活动
ImageDisplay Activity
package com.example.customcamera;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Path;
import android.media.Image;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import java.io.File;
public class ImageDisplay extends AppCompatActivity {
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_display);
imageView = findViewById(R.id.imageView);
String imgFile = getIntent().getStringExtra("FILE");
Bitmap bitmap = BitmapFactory.decodeFile(imgFile);
imageView.setImageBitmap(bitmap);
}
}
image_display.xml
image_display.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ImageDisplay">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
推荐答案
我刚刚在Xamarin iOS App上做到了这一点.如果发现有帮助,可以看看它.
I just did this on Xamarin iOS App. You can take a lookg at it incase if your found it helpful.
这篇关于在自定义Camera2 API上显示矩形边界框(覆盖),以便仅捕获该框内的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!