演示视频
开发环境
IDE: Android Studio 4.1.1
minSdkVersion: 19
targetSdkVersion: 30
JavaVersion:1.8
项目地址
先把项目放在这,懒得看的可以直接去Git Clone,所需的东西都已经在上面了,下载之后直接Android Studio打开即可。
(以下所叙述仅叙述部分内容,代码需要直接去Github下载,重要部分均有注释)
Github: https://github.com/iMacroC/imgclassifier
所需权限声明
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
一些坑
- 手机锁屏后解锁 preview就会黑屏(锁屏需要释放相关资源 以及隐藏preview),需要写一个广播来监听屏幕开关事件。
- 通过Intent 选择相关图片文件的话,如果来源是相册 将会被旋转90度(可能和系统有关系),我是通过判断来源URL路径来进行旋转图片解决的。
- 相机预览界面可能会被旋转90度(看官方说好像是部分机型 具体不了解)
- 不拍照保存文件,直接从预览画面获取预览帧的图片是Yuv类型的(我觉得直接拍照的话可能会消耗更多一些性能 所以就采取了从预览帧获取的方式 而且直接获取预览帧的话可以做成实时和别)
- 摄像头权限声明后还需动态申请
- setPreviewCallBackWithBuffer 回调速度等将会优于 PreviewCallBack 具体访问:https://developer.android.com/reference/android/hardware/Camera#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback)
调用摄像头
调用相机主要是参考了官方文档: https://developer.android.com/guide/topics/media/camera?hl=zh-cn
(这个学期有Android开发的课程,我也是事后才知道原来书上有写...不过我本来就是打算把书卖掉的,现在都没拆封哈哈哈哈@(中指))
需要注意的一个地方就是当手机锁屏后再开屏的话会crash,因此需要写一个BroadcastReceiver来监听屏幕开关事件,锁屏后就释放相关资源并隐藏preview,解锁后便重新实例化相关对象。
可能于官方这个提示有关系:
值得注意的是在Android 6.0后实例化Camera之前需要动态申请权限(防止权限滥用,Google就采取了一些措施)
private void requestPermission() {
// 判断当前Activity是否已经获得了该权限
if (ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// 如果App的权限申请曾经被用户拒绝过,就需要在这里跟用户做出解释
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)) {
Toast.makeText(MainActivity.this,"请进入设置-应用管理-打开相机权限",Toast.LENGTH_SHORT).show();
} else {
// 进行权限请求
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},CAMERA_REQ_CODE);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
if(requestCode==CAMERA_REQ_CODE) {
// 如果请求被拒绝,那么通常grantResults数组为空
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 申请成功,进行相应操作
} else {
// 申请失败,可以继续向用户解释。
Toast.makeText(MainActivity.this, "没有相机权限,您可能无法正常使用本应用", Toast.LENGTH_LONG).show();
}
}
}
获取一个Camera的实例化对象(且是后置摄像头)
public static Camera getCameraInstance(){
Camera c = null;
try {
// attempt to get a Camera instance
int numberOfCameras = Camera.getNumberOfCameras(); //获取总共的摄像头数量
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();//获取摄像头信息
for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { //循环输出后置摄像头ID 并执行open函数 返回该对象
if (checkCameraHardware(context)){
System.out.println("Has Camera\n Open Camera");
System.out.println("Total Cameras: " + String.valueOf(Camera.getNumberOfCameras()));
System.out.println("Openning Camera");
c = Camera.open(i);
}else{
System.out.println("Does not has Camera");
}
return c;
}
}
System.out.println("Opened Succeed");
}
catch (Exception e){
System.out.println("Camera is not available");
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
你可以在实例化之前 检测一下设备是否有摄像头组件
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
在onCreate事件中进行相关操作,现在就可以在APP上看到相机的预览画面了。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
int screenWidth = display.getWidth();
requestPermission();//请求摄像头权限
c = getCameraInstance(); // 实例化一个摄像头对象
parameters = c.getParameters();
//实例化preview显示 摄像头内容
mPreview = new CameraPreview(this, c);
preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.getLayoutParams().height = (int) (screenHeight*0.6);//设置摄像头预览显示区域高度
c.setDisplayOrientation(90);//设置旋转90° 不然是横屏
preview.addView(mPreview);// 将mPreview添加到preview上显示
}
CameraPreview 类
package com.julym.camera;
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private String TAG;
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
调用TFlite模型
在Android Studio 4.1后可以直接在文件中导入模型,并自动生成相关类文件,且会自动帮你进行图片相关的预处理。
导入后双击该模型即可看到相关信息以及Sample Code
模型最好在onCreate事件中加载好(减少二次加载耗时),然后封装一个函数方便调用。
由于该模型将返回所有可能性,所以需要写一个冒泡排序获取最大可能。
public Map efficientnet(Bitmap bitmap){
Map result = new HashMap();
TensorImage image = TensorImage.fromBitmap(bitmap);
// Runs model inference and gets result.
efficientnetLite4Fp322.Outputs outputs = model.process(image);
List<Category> probability = outputs.getProbabilityAsCategoryList();
double temp = 0;
String predict = null;
for( int i = 0 ; i < probability.size() ; i++) {
if (temp>probability.get(i).getScore()){
}else{
temp=probability.get(i).getScore();
predict = probability.get(i).getLabel();
}
}
System.out.println(probability);
Log.e("Predict", predict);
Log.e("Score", String.valueOf(temp));
result.put("predict", predict);
result.put("score", String.valueOf(temp));
return result;
}
您好,请问一下,github的代码里real-time的功能怎么没有了?
@QQ昵称获取失败 之前重新上传了一次 可能上传到之前的代码了, real-time就直接用timer循环就好了
@QQ昵称获取失败 死循环也可以,但是调用间隔不能太快,不然会溢出
请问您有空把最新的上传一下吗?感谢
@QQ昵称获取失败 啊 项目已经不知道丢哪里去了
@QQ昵称获取失败 抱歉哈…
@Macro 没事儿,十分感谢
Ads
《霍格沃茨之遗·豪华版》破解补丁+完整游戏下载
蚂蚁加速器 支持安卓、Windows、IOS、MAC 多条线路稳定使用
2月17日更新 丨2019最新丨Google Youtube hosts丨持续更新 文末下载自动替换脚本
官方Office 2019正式版
【转】百分百过所有游戏机器码教程
当前访客