Commit ad40cf69 authored by 姜天宇's avatar 姜天宇

feat(v1.0.1): 增加Camerax

parent 2e307acd
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android'
} }
android { android {
...@@ -10,8 +11,8 @@ android { ...@@ -10,8 +11,8 @@ android {
applicationId "com.wmdigit.cateringdetect" applicationId "com.wmdigit.cateringdetect"
minSdk 24 minSdk 24
targetSdk 33 targetSdk 33
versionCode 1000000 versionCode 1000100
versionName "1.0.0" versionName "1.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...@@ -37,8 +38,11 @@ android { ...@@ -37,8 +38,11 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
} }
buildFeatures { buildFeatures {
viewBinding true viewBinding true
......
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
plugins { plugins {
id 'com.android.application' version '8.1.0-beta01' apply false id 'com.android.application' version '8.1.0-beta01' apply false
id 'com.android.library' version '8.1.0-beta01' apply false id 'com.android.library' version '8.1.0-beta01' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
} }
\ No newline at end of file
...@@ -33,7 +33,7 @@ dependencies { ...@@ -33,7 +33,7 @@ dependencies {
implementation project(path: ":common") implementation project(path: ":common")
// CameraX core library using the camera2 implementation // CameraX core library using the camera2 implementation
def camerax_version = "1.4.0-beta02" def camerax_version = "1.2.0-beta02"
// The following line is optional, as the core library is included indirectly by camera-camera2 // The following line is optional, as the core library is included indirectly by camera-camera2
implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}"
......
package com.wmdigit.camera;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Size;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import com.elvishew.xlog.XLog;
import com.google.common.util.concurrent.ListenableFuture;
import com.wmdigit.camera.listener.OnImageAnalyzeListener;
import com.wmdigit.common.utils.YuvToRgbConverter;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 相机管理类
* @author dizi
*/
public class CameraxController {
/**
* 摄像头预览分辨率
*/
private final Size CAMERA_PREVIEW_SIZE = new Size(640, 480);
/**
* 帧处理的时间间隔
*/
private final long INTERVAL_FRAMES_ANALYZE = 100L;
private static CameraxController instance;
private Context context;
/**
* 获取相机服务provider
*/
private ListenableFuture<ProcessCameraProvider> cameraProviderFutureListener;
/**
* 相机的生命周期
*/
private LifecycleOwner lifecycleOwner;
/**
* 图片分析线程
*/
private final ExecutorService executors = Executors.newFixedThreadPool(1);
/**
* YUV转RGB转化器
*/
private YuvToRgbConverter yuvToRgbConverter;
/**
* 图片分析回调
*/
private OnImageAnalyzeListener onImageAnalyzeListener;
public static CameraxController getInstance(Context context) {
if (instance == null){
synchronized (CameraxController.class){
if (instance == null){
instance = new CameraxController(context);
}
}
}
return instance;
}
public static CameraxController getInstance() {
return instance;
}
public CameraxController(Context context) {
this.context = context;
this.cameraProviderFutureListener = ProcessCameraProvider.getInstance(context);
this.yuvToRgbConverter = new YuvToRgbConverter(context);
}
/**
* 配置生命周期
* @param lifecycleOwner
* @return
*/
public CameraxController setLifecycleOwner(LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
return this;
}
/**
* 注册图片分析回调
* @param onImageAnalyzeListener
* @return
*/
public CameraxController setOnImageAnalyzeListener(OnImageAnalyzeListener onImageAnalyzeListener) {
this.onImageAnalyzeListener = onImageAnalyzeListener;
return this;
}
/**
* 相机初始化
*/
public void apply(){
cameraProviderFutureListener.addListener(() -> {
try{
// 获取cameraProvider
ProcessCameraProvider cameraProvider = cameraProviderFutureListener.get();
// 创建图像分析器
ImageAnalysis imageAnalysis = createImageAnalysis();
// 绑定相机
Camera camera = bindCamera(cameraProvider, imageAnalysis);
if (camera != null) {
// 观察相机状态
observeCameraLifecycle(camera);
}
}
catch (ExecutionException | InterruptedException e){
XLog.e(e.toString());
}
}, ContextCompat.getMainExecutor(context));
}
/**
* 创建图片分析器
* @return
*/
private ImageAnalysis createImageAnalysis() {
// 构建分析器
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
.setTargetResolution(CAMERA_PREVIEW_SIZE)
// 非阻塞
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
// 实现分析回调
imageAnalysis.setAnalyzer(executors, imageProxy->{
long startTime = System.currentTimeMillis();
// 处理图片
analyzeImage(imageProxy);
imageProxy.close();
// 休眠
long costTime = System.currentTimeMillis() - startTime;
System.out.println("处理时间:" + costTime);
long leftTime = INTERVAL_FRAMES_ANALYZE - costTime;
if (leftTime <= INTERVAL_FRAMES_ANALYZE && leftTime >= 0){
try {
Thread.sleep(leftTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
return imageAnalysis;
}
/**
* 处理图像
* @param imageProxy
*/
private void analyzeImage(ImageProxy imageProxy) {
try {
// 生成Bitmap
Bitmap bitmap = Bitmap.createBitmap(imageProxy.getWidth(), imageProxy.getHeight(), Bitmap.Config.ARGB_8888);
yuvToRgbConverter.yuvToRgb(imageProxy.getImage(), bitmap);
// 回调
if (onImageAnalyzeListener != null){
onImageAnalyzeListener.onAnalyzed(bitmap);
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 绑定相机
* @param cameraProvider
* @param imageAnalysis
*/
private Camera bindCamera(ProcessCameraProvider cameraProvider, ImageAnalysis imageAnalysis) {
// 防止重复绑定
cameraProvider.unbindAll();
// 获取所有相机
List<CameraInfo> cameraList = cameraProvider.getAvailableCameraInfos();
if (cameraList.size() == 0){
XLog.e("无可用相机!");
return null;
}
// 相机下标
int index = 0;
if (cameraList.size() >= 2){
index = 1;
}
// 根据下标获取相机
CameraInfo cameraInfo = cameraProvider.getAvailableCameraInfos().get(index);
@SuppressLint("RestrictedApi") CameraSelector cameraSelector = CameraSelector.Builder
.fromSelector(cameraInfo.getCameraSelector())
.build();
// 绑定相机、生命周期和图片分析器
return cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis);
}
/**
* 观察相机生命周期
* @param camera
*/
private void observeCameraLifecycle(Camera camera){
camera.getCameraInfo().getCameraState().removeObservers(lifecycleOwner);
camera.getCameraInfo().getCameraState().observe(lifecycleOwner, cameraState -> {
XLog.i("相机状态:" + cameraState.getType());
switch (cameraState.getType()){
case CLOSED:
// 相机关闭
break;
default:
break;
}
if (cameraState.getError() != null){
XLog.i("相机异常:" + cameraState.getType());
}
});
}
}
package com.wmdigit.camera.listener;
import android.graphics.Bitmap;
/**
* Camerax的图片回调
* @author dizi
*/
public interface OnImageAnalyzeListener {
/**
* 图片处理完成后,通过此接口回调
* @param bitmap
*/
void onAnalyzed(Bitmap bitmap);
}
package com.wmdigit.camera.model;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
* 相机的视频帧
* @author dizi
*/
public class CameraFrame implements Parcelable {
private long timestamp;
private Bitmap bitmap;
public CameraFrame(Bitmap bitmap) {
this.bitmap = bitmap;
this.timestamp = System.currentTimeMillis();
}
protected CameraFrame(Parcel in) {
timestamp = in.readLong();
bitmap = in.readParcelable(Bitmap.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(timestamp);
dest.writeParcelable(bitmap, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<CameraFrame> CREATOR = new Creator<CameraFrame>() {
@Override
public CameraFrame createFromParcel(Parcel in) {
return new CameraFrame(in);
}
@Override
public CameraFrame[] newArray(int size) {
return new CameraFrame[size];
}
};
}
plugins { plugins {
id 'com.android.library' id 'com.android.library'
id 'org.jetbrains.kotlin.android'
} }
android { android {
...@@ -26,8 +27,11 @@ android { ...@@ -26,8 +27,11 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
} }
buildFeatures { buildFeatures {
......
v1.0.0 2024/07/18 1.演示Demo v1.0.0 2024/07/18 1.演示Demo
\ No newline at end of file v1.0.1 2024/08/01 1.增加camerax
2.todo 增加3568视频流算法
\ No newline at end of file
...@@ -49,6 +49,7 @@ dependencies { ...@@ -49,6 +49,7 @@ dependencies {
implementation project(path: ":common") implementation project(path: ":common")
implementation project(path: ":core") implementation project(path: ":core")
implementation project(path: ":data-local") implementation project(path: ":data-local")
implementation project(path: ":camera")
implementation 'com.alibaba:arouter-api:1.5.2' implementation 'com.alibaba:arouter-api:1.5.2'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.2' annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
......
package com.wmdigit.cateringdetect.demo.ui.fragment; package com.wmdigit.cateringdetect.demo.ui.fragment;
import androidx.lifecycle.Observer; import android.graphics.Bitmap;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import com.wmdigit.camera.CameraxController;
import com.wmdigit.camera.listener.OnImageAnalyzeListener;
import com.wmdigit.cateringdetect.demo.R; import com.wmdigit.cateringdetect.demo.R;
import com.wmdigit.cateringdetect.demo.databinding.FragmentDemoHomeBinding; import com.wmdigit.cateringdetect.demo.databinding.FragmentDemoHomeBinding;
import com.wmdigit.cateringdetect.demo.ui.adapter.DemoImagesAdapter; import com.wmdigit.cateringdetect.demo.ui.adapter.DemoImagesAdapter;
...@@ -80,6 +80,16 @@ public class DemoHomeFragment extends BaseMvvmFragment<DemoHomeViewModel, Fragme ...@@ -80,6 +80,16 @@ public class DemoHomeFragment extends BaseMvvmFragment<DemoHomeViewModel, Fragme
} }
}); });
mDataBinding.rvShoppingCart.setAdapter(shoppingCartAdapter); mDataBinding.rvShoppingCart.setAdapter(shoppingCartAdapter);
// todo 测试,开相机
CameraxController.getInstance(requireContext())
.setLifecycleOwner(this)
.setOnImageAnalyzeListener(this::processBitmap)
.apply();
}
private void processBitmap(Bitmap bitmap){
} }
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment