最近一周都在做文通车号识别的调研和测试工作,这里除了文通SDK集成相关的东西外,主要都是android摄像头相关的知识,包括获取当前手机支持的预览和拍照尺寸,实现Camera的预览,以及相应数据的采集和转换
1.Camera所对应的属性(支持的预览和拍照尺寸):
Camera camera = Camera.open();
if(camera !=null) {
Camera.Parameters parameters = camera.getParameters();
List<Camer.size> previewSizes = parameters.getSupportedPreviewSizes();
List<Camera.size> PictureSizes = parameters.getSupportedPictureSizes();
Camera.Size size;}
previewSizes获取的就是当前支持的预览尺寸PictureSize就是支持的拍照尺寸,这里要注意预览尺寸和照片尺寸都不止一个而且手机不通支持的尺寸也是不一样的
** 2.实现摄像预览:**
** 摄像预览主要是用到了SurfaceView来进行展示Camera捕获的图像**
** 涉及到的类SurfaceHolder**
SurfaceHolder holder=surfaceView.getHolder(); //帮定 surfaceview的holder
holder.addCallback(context);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//需要实现的几个方法
public voidsurfaceCreated(SurfaceHolder holder) //surfaceview创建时会回调的方法
public voidsurfaceChanged(finalSurfaceHolder holder, intformat, intwidth, intheight)//当surfaceview发生变化时的回调
public voidsurfaceDestroyed(SurfaceHolder holder)//surface销毁
//识别sdk中的代码,以后做个参考
@Override
public voidsurfaceCreated(SurfaceHolder holder) {
if(camera==null) {
try{
camera= Camera.open();
}catch(Exception e) {
e.printStackTrace();
return;
}
}
try{
camera.setPreviewDisplay(holder);//可以理解 为将surfaceview和 camera绑定到一起,这样camera的数据就会显示到绑定的holder的 surfaceview上,并且相应的数据通过 onPreviewFrame(byte[] data,Camera camera)的回调就能够获取到
initCamera(holder,rotation);
re.addView(myview);
if(timer==null) {
timer=newTimerTask() {
public voidrun() {
// isSuccess=false;
if(camera!=null) {
try{
camera.autoFocus(newAutoFocusCallback() {
public voidonAutoFocus(booleansuccess,Camera camera) {
// isSuccess=success;
}
});
}catch(Exception e) {
e.printStackTrace();
}
}
}
;
};
}
time.schedule(timer,500,2500);//延迟0.5秒,2.5秒一次的自动对焦
}catch(IOException e) {
e.printStackTrace();
}
}
@Override
public voidsurfaceChanged(finalSurfaceHolder holder, intformat, intwidth, intheight) {
if(camera!=null) {
try{
camera.autoFocus(newAutoFocusCallback() {
@Override
public voidonAutoFocus(booleansuccess,Camera camera) {
if(success) {
synchronized(camera) {
newThread() {
public voidrun() {
initCamera(holder,rotation);
super.run();
}
}.start();
}
}
}
});
}catch(Exception e) {
//TODO: handle exception
}
}
}
@Override
public voidsurfaceDestroyed(SurfaceHolder holder) {
try{
if(camera!=null) {
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera=null;
}
}catch(Exception e) {
}
}
Camera.Parameters parameters =camera.getParameters();//参数设置
parameters.setPictureFormat(PixelFormat.JPEG);//设置图片格式
parameters.setPreviewSize(preWidth,preHeight);//设置预览的尺寸,这里就用到之前获取的支持预览的尺寸,根据自己的需求来进行选择
camera.setParameters(parameters);
camera.setDisplayOrientation(r);//这里有一个小坑,我这里当时只想验证竖屏情况下的识别,所以直接设置角度为90度,然后相机第一次怎么连接都是失败,然后再重新打开就没有问题了...后来是做了camera.stopPreview();
//camera.setPreviewCallback(null);然后在从新设置PreviewCallback就可以直接打来了
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(MemoryCameraActivity.this);//这里就是设置预览数据的回调
camera.startPreview();//开启预览
//这里摄像头还是使用的旧Api,主要是为了兼容嘛,新的Api为Camera2
@Override
public voidonPreviewFrame(byte[] data,Camera camera) { //Camera的Preview的回调
//实时监听屏幕旋转角度
intuiRot = getWindowManager().getDefaultDisplay().getRotation();//获取屏幕旋转的角度
if(uiRot !=tempUiRot) {
System.err.println("uiRot:"+ uiRot);
Message mesg =newMessage();
mesg.what= uiRot;
handler.sendMessage(mesg);
tempUiRot= uiRot;
}
if(setRecogArgs) {
Intent authIntent =newIntent(MemoryCameraActivity.this,RecogService.class);
bindService(authIntent,recogConn,Service.BIND_AUTO_CREATE);
setRecogArgs=false;
}
if(iInitPlateIDSDK==0) {
nums++;
if(nums==1) {
nums= -1;
prp.height=preHeight;//
prp.width=preWidth;//
prp.picByte= data;
picData= data;
// prp.isCheckDevType = true;
//开发码
prp.devCode= Devcode.DEVCODE;
if(rotation==0) {
//通知识别核心,识别前图像应先旋转的角度
prp.plateIDCfg.bRotate=0;
setHorizontalRegion();
}else if(rotation==90) {
prp.plateIDCfg.bRotate=1;
setLinearRegion();
}else if(rotation==180) {
prp.plateIDCfg.bRotate=2;
setHorizontalRegion();
}else if(rotation==270) {
prp.plateIDCfg.bRotate=3;
setLinearRegion();
}
// System.out.println("实际区域" + preWidth +
// " " + preHeight);
// System.out.println("边长:"+ViewfinderView.length*2);
// System.out.println("敏感区域:" +
// prp.plateIDCfg.left + " "
// + prp.plateIDCfg.right + " " +
// prp.plateIDCfg.top
// + " " + prp.plateIDCfg.bottom);
if(isCamera) {
//进行授权验证 并开始识别
fieldvalue=recogBinder.doRecogDetail(prp);
nRet=recogBinder.getnRet();
if(nRet!=0) {
String[] str = {""+nRet};
getResult(str);
}else{
getResult(fieldvalue);
intentNV21data= data;
}
}
}
}
}
3.获取的数据转换:
//android摄像头获取的数据为NV21的格式,我们要使用的话,需要将这个数据转为jpeg,bitmap啊之类的,这里用到的是Android的提供的YuvImage类
ByteArrayOutputStream baos =newByteArrayOutputStream();
YuvImage yuvimage =newYuvImage(tempData,ImageFormat.NV21,Width,Height, null);//预览时获取的数据,数据的格式,宽,高
yuvimage.compressToJpeg(newRect(0,0,Width,Height),100,baos);//转为jpeg的流
bitmap= BitmapFactory.decodeByteArray(baos.toByteArray(),0,baos.size(),options);
//流到bitmap
4.这里在加一个闪光灯的操作:
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {//没有闪光灯
Toast.makeText(MemoryCameraActivity.this,
getResources().getString(
getResources().getIdentifier("no_flash","string",
getPackageName())),Toast.LENGTH_LONG).show();
}else{
if(camera!=null) {
Camera.Parameters parameters =camera.getParameters();
String flashMode = parameters.getFlashMode();
if(flashMode.equals(Camera.Parameters.FLASH_MODE_TORCH)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
parameters.setExposureCompensation(0);
}else{
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//闪光灯常亮
parameters.setExposureCompensation(-1);
}
try{
camera.setParameters(parameters);
}catch(Exception e) {
Toast.makeText(MemoryCameraActivity.this,
getResources().getString(
getResources().getIdentifier("no_flash",
"string",getPackageName())),
Toast.LENGTH_LONG).show();
}
camera.startPreview();
}
}
```