1、布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/textureView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
2、在AndroidManifest.xml中增加权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera2.full" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
3、MainActivity的详细代码,包括设置分辨率、横竖屏旋转适配(此适配方案需根据不同设备调整),每五帧自动保存一张图片到相册(此需求需要增加保存图片的权限),如需点击按钮拍照,可根据自动拍照的逻辑进行一些修改。
package com.kgzn.aicamera;
import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
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.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import android.view.Display;
import android.view.TextureView;
//import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int REQUEST_CAMERA_PERMISSION = 200;
private TextureView textureView;
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSession;
private CaptureRequest.Builder captureRequestBuilder;
private String cameraId;
private Handler backgroundHandler;
private HandlerThread backgroundThread;
private ImageReader imageReader;
private int sensorOrientation;
private Size targetSize;
private int frameCounter = 0; // 帧计数器
private NcnnDetectHand ncnnDetectHand;
private int current_cpugpu = 0;
private boolean use_gpu = false;
private TextView gestureText;
private Boolean isInit = false;
private List<String> resultList = Collections.synchronizedList(new ArrayList<>());
private Handler mHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull android.os.Message msg) {
if (msg.what == 0) {
// 清空 gestureText 的文本
gestureText.setText("");
}
}
};
private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.d(TAG, "onSurfaceTextureAvailable: textureView = " + textureView + ", cameraDevice = " + cameraDevice);
configureTransform(width, height);
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.d(TAG, "onSurfaceTextureAvailable: textureView = " + textureView + ", cameraDevice = " + cameraDevice);
// 可以在这里处理纹理视图大小改变的情况
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// 纹理视图更新时调用
}
};
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d(TAG, "onOpened: ");
cameraDevice = camera;
createCameraPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
Log.d(TAG, "onDisconnected: ");
cameraDevice.close();
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
Log.d(TAG, "onError: ");
cameraDevice.close();
cameraDevice = null;
}
};
private final ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
byte[] data = new byte[buffer.capacity()];
buffer.get(data);
// 将 byte[] 数据转换为 Bitmap
// Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
frameCounter++;
if (frameCounter % 5 == 0) {
Log.d(TAG, "onImageAvailable: frameCounter = " + frameCounter);
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if (bitmap != null) {
// 获取设备方向
int rotation = getDeviceRotation();
int rotationDegrees = getBitmapOrientation(rotation);
// 处理镜像和旋转
Bitmap processedBitmap = processBitmap(bitmap, rotationDegrees);
saveBitmapToGallery(processedBitmap);
}
}
// 在这里可以对每一帧图片数据进行操作
// 例如进行图像处理、特征提取等
image.close();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 隐藏状态栏和导航栏
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
textureView = findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(textureListener);
gestureText = findViewById(R.id.gesture_text);
}
@Override
protected void onResume() {
super.onResume();
startBackgroundThread();
if (textureView.isAvailable()) {
openCamera(textureView.getWidth(), textureView.getHeight());
} else {
textureView.setSurfaceTextureListener(textureListener);
}
}
@Override
protected void onPause() {
releaseCamera();
stopBackgroundThread();
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
ncnnDetectHand.unloadModel();
}
private void openCamera(int width, int height) {
CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
this.cameraId = cameraId;
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map != null) {
// Size largest = getLargestSize(map.getOutputSizes(ImageFormat.JPEG));
// imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG,
// 10);
// 假设目标分辨率为 800x600
int targetWidth = 800;
int targetHeight = 600;
// 传入 JPEG 格式支持的尺寸列表
targetSize = chooseOptimalSize(map.getOutputSizes(ImageFormat.JPEG), targetWidth, targetHeight);
imageReader = ImageReader.newInstance(targetSize.getWidth(), targetSize.getHeight(), ImageFormat.JPEG, 10);
imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler);
}
break;
}
}
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
return;
}
manager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
texture.setDefaultBufferSize(textureView.getWidth(), textureView.getHeight());
Surface surface = new Surface(texture);
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
captureRequestBuilder.addTarget(imageReader.getSurface());
// // 设置目标帧率范围为 30fps
// android.util.Range<Integer> fpsRange = new android.util.Range<>(30, 30);
// captureRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if (cameraDevice == null) {
return;
}
cameraCaptureSession = session;
try {
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Toast.makeText(MainActivity.this, "配置失败", Toast.LENGTH_SHORT).show();
}
}, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void startBackgroundThread() {
backgroundThread = new HandlerThread("CameraBackground");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
private void stopBackgroundThread() {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void configureTransform(int viewWidth, int viewHeight) {
if (null == textureView) {
return;
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Log.d(TAG, "configureTransform: rotation = " + rotation);
android.graphics.Matrix matrix = new android.graphics.Matrix();
android.graphics.RectF viewRect = new android.graphics.RectF(0, 0, viewWidth, viewHeight);
android.graphics.RectF bufferRect = new android.graphics.RectF(0, 0, textureView.getHeight(), textureView.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, android.graphics.Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / textureView.getHeight(),
(float) viewWidth / textureView.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
textureView.setTransform(matrix);
}
private Size getLargestSize(Size[] sizes) {
Size largest = sizes[0];
for (Size size : sizes) {
if (size.getWidth() * size.getHeight() > largest.getWidth() * largest.getHeight()) {
largest = size;
}
}
return largest;
}
/**
* 选择最接近目标分辨率的尺寸
* @param sizes 相机支持的尺寸列表
* @param targetWidth 目标宽度
* @param targetHeight 目标高度
* @return 最接近目标分辨率的尺寸
*/
private Size chooseOptimalSize(Size[] sizes, int targetWidth, int targetHeight) {
Size optimalSize = null;
int minDiff = Integer.MAX_VALUE;
for (Size size : sizes) {
int widthDiff = Math.abs(size.getWidth() - targetWidth);
int heightDiff = Math.abs(size.getHeight() - targetHeight);
int diff = widthDiff + heightDiff;
if (diff < minDiff) {
minDiff = diff;
optimalSize = size;
}
}
return optimalSize;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "你拒绝了相机权限", Toast.LENGTH_SHORT).show();
finish();
}
}
}
private void saveBitmapToGallery(Bitmap bitmap) {
ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "CameraFrame_" + System.currentTimeMillis() + ".jpg");
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
Uri imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
if (imageUri != null) {
try {
OutputStream outputStream = contentResolver.openOutputStream(imageUri);
if (outputStream != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
Toast.makeText(this, "图片已保存到图库", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "保存图片失败", Toast.LENGTH_SHORT).show();
}
}
}
private void releaseCamera() {
try {
if (null != cameraCaptureSession) {
cameraCaptureSession.close();
cameraCaptureSession = null;
}
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != imageReader) {
imageReader.close();
imageReader = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private int getDeviceRotation() {
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.getRotation();
// LogUtil.i(TAG,"getDeviceRotation rotation:"+rotation);
switch (rotation) {
case Surface.ROTATION_0:
return 270;
case Surface.ROTATION_180:
return 90;
case Surface.ROTATION_270:
return 180;
default:
return 0;
}
}
public int getBitmapOrientation(int deviceOrientation){
if (deviceOrientation == android.view.OrientationEventListener.ORIENTATION_UNKNOWN) return sensorOrientation;
deviceOrientation = (deviceOrientation + 45) / 90 * 90;
int jpegOrientation = (sensorOrientation - deviceOrientation + 360) % 360 + 90;
return jpegOrientation;
}
private Bitmap processBitmap(Bitmap bitmap, int rotation) {
Matrix matrix = new Matrix();
// 镜像处理(水平镜像)
matrix.preScale(-1, 1);
// 旋转处理
matrix.postRotate(rotation);
// 创建新的 Bitmap
// return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// 创建新的 Bitmap
Bitmap processedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// 压缩图片质量
Bitmap compressedBitmap = compressBitmap(processedBitmap, 50); // 50 表示压缩质量为 50%
// 回收原始的 processedBitmap
processedBitmap.recycle();
return compressedBitmap;
}
private Bitmap compressBitmap(Bitmap bitmap, int quality) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
}
}