camera2扫描获取高清图
先看下google提供的demo https://github.com/googlesamples/android-Camera2Basic
扫描获取高清图也是基于这个demo做了一些修改,遇到一些问题,这边讲述下自己的想法,大佬勿喷。
- demo中自定义的AutoFitTextureView 我建议是不要用,它是通过手机的屏幕分辨率来选择的分辨率,现在国产手机分辨率有很多不是16:9,4:3的,所以它这套算法下来,会选取比较低的分辨率,就导致预览的时候模糊看不清。
我个人建议是先选定自己想要的分辨率,(我用的是1080*1920)然后设置使用TextureView设置宽为match_parent,最后动态设置高度为 textureView高度 = textureView宽 * 预览高 / 预览宽
2.Camera2功能支持情况 我做的是扫描软件,同时又要求要高清图,为了保证预览无卡顿,所以要做到拍摄图片时,预览不能暂停,但是发现oppo手机不行,取一张图就卡一次,用户体验非常不好,我也傻到联系他们客服(当然客服跟你讲的都是官方话术)这里要感谢一位大佬 这是他写 https://www.jianshu.com/p/23e8789fbc10 以下是摘抄
查询Camera2功能支持情况
上面说过, 不是所以手机都支持完整的Camera2功能, 现在都2018了, Camera2出来都有4年左右了, 但估计还有些中低端手机使用的HAL1, 使用HAL1就会导致Camera2一些高级功能都没法使用了, 下面讲一下如何查询设备对应Camera2的支持情况.
INFO_SUPPORTED_HARDWARE_LEVEL
硬件层面支持的Camera2功能等级, 主要分为5个等级:
INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
INFO_SUPPORTED_HARDWARE_LEVEL_FULL
INFO_SUPPORTED_HARDWARE_LEVEL_3
INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
LEVEL_LEGACY: 向后兼容模式, 如果是此等级, 基本没有额外功能, HAL层大概率就是HAL1(我遇到过的都是)
LEVEL_LIMITED: 有最基本的功能, 还支持一些额外的高级功能, 这些高级功能是LEVEL_FULL的子集
LEVEL_FULL: 支持对每一帧数据进行控制,还支持高速率的图片拍摄
LEVEL_3: 支持YUV后处理和Raw格式图片拍摄, 还支持额外的输出流配置
LEVEL_EXTERNAL: API28中加入的, 应该是外接的摄像头, 功能和LIMITED类似
各个等级从支持的功能多少排序为: LEGACY < LIMITED < FULL < LEVEL_3
获取等级相关代码
private int isHardwareSupported(CameraCharacteristics characteristics) {
Integer deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (deviceLevel == null) {
Log.e(TAG, "can not get INFO_SUPPORTED_HARDWARE_LEVEL");
return -1;
}
switch (deviceLevel) {
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL:
Log.w(TAG, "hardware supported level:LEVEL_FULL");
break;
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:
Log.w(TAG, "hardware supported level:LEVEL_LEGACY");
break;
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3:
Log.w(TAG, "hardware supported level:LEVEL_3");
break;
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:
Log.w(TAG, "hardware supported level:LEVEL_LIMITED");
break;
}
return deviceLevel;
}
. 测试了几款手机 oppo手机的等级就是LEVEL_LEGACY,也就是个five。所以oppo用camera2的话,预览的时候就不要想拿高分辨的图了,也不要拍照,直接获取低分辨率预览图,也是可以使用的。
开始贴代码
public class CameraActivity2 extends BaseActivity
implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private static final int REQUEST_CAMERA_PERMISSION = 1;
private static final int TIME_OUT = 1010;//鉴别超时
private ImageView ivOutJb,iv_tishi,iv_light,iv_biaozhi;
private ViewfinderView viewfinderView;
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);
}
/**
* Tag for the {@link Log}.
*/
private CameraManager manager;
/**
* Camera state: Showing camera preview.
*/
private static final int STATE_PREVIEW = 0;
/**
* Camera state: Waiting for the focus to be locked.
*/
private static final int STATE_WAITING_LOCK = 1;
/**
* Camera state: Waiting for the exposure to be precapture state.
*/
private static final int STATE_WAITING_PRECAPTURE = 2;
/**
* Camera state: Waiting for the exposure state to be something other than precapture.
*/
private static final int STATE_WAITING_NON_PRECAPTURE = 3;
/**
* Camera state: Picture was taken.
*/
private static final int STATE_PICTURE_TAKEN = 4;
/**
* Max preview width that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_WIDTH = 1920;
/**
* Max preview height that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_HEIGHT = 1080;
private int picWidth;
private int picHeight;
/**
* {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
*/
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
/**
* ID of the current {@link CameraDevice}.
*/
private String mCameraId;
/**
* An {@link AutoFitTextureView} for camera preview.
*/
private TextureView mTextureView;
/**
* A {@link CameraCaptureSession } for camera preview.
*/
private CameraCaptureSession mCaptureSession;
/**
* A reference to the opened {@link CameraDevice}.
*/
private CameraDevice mCameraDevice;
/**
* 预览
*/
private Size mPreviewSize;
//JPEG格式下支持拍摄图片的数组集合
private Size [] pic;
/**
* {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
*/
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
finish();
}
};
/**
* An additional thread for running tasks that shouldn't block the UI.
*/
private HandlerThread mBackgroundThread;
/**
* A {@link Handler} for running tasks in the background.
*/
private Handler mBackgroundHandler;
/**
* An {@link ImageReader} that handles still image capture.
*/
private ImageReader mImageReader;
/**
* This is the output file for our picture.
*/
private File mFile;
/**
* This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
* still image is ready to be saved.
*/
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
mBackgroundHandler.post(new ImageSaver(reader.acquireLatestImage(), mFile));
}
};
/**
* {@link CaptureRequest.Builder} for the camera preview
*/
private CaptureRequest.Builder mPreviewRequestBuilder;
/**
* {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
*/
private CaptureRequest mPreviewRequest;
/**
* The current state of camera state for taking pictures.
*
* @see #mCaptureCallback
*/
private int mState = STATE_PREVIEW;
/**
* A {@link Semaphore} to prevent the app from exiting before closing the camera.
*/
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
/**
* Whether the current camera device supports Flash or not.
*/
private boolean mFlashSupported;
/**
* Orientation of the camera sensor
*/
private int mSensorOrientation;
/**
* A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
*/
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
Log.d("stateinfo","STATE_PREVIEW");
// We have nothing to do when the camera preview is working normally.
break;
}
case STATE_WAITING_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
captureStillPicture();
} else if (CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState) {
mState = STATE_PREVIEW;
captureStillPicture();
}
break;
}
}
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
process(result);
}
};
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(CameraActivity2.this, text, Toast.LENGTH_SHORT).show();
}
});
}
//这边做了修改 选择比较合适的分辨率
private Size chooseOptimalSize(Size[] choices, int textureViewWidth,
int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
List<Size> bigEnough = new ArrayList<>();
List<Size> bestBigEnough = new ArrayList<>();
List<Size> notBigEnough = new ArrayList<>();
List<Size> bestSize = new ArrayList<>();
//以1080p为基准高于1080p的则视为高分辨率相反则视为低分辨率
for (Size option : choices) {
int w = option.getWidth();
int h = option.getHeight();
double rate = (double) w/h;
String rateStr = String.valueOf(rate);
if (w > 1920 && h > 1080){
bigEnough.add(option);
if (rateStr.startsWith("1.7")){
bestBigEnough.add(option);
}
}else {
if (rateStr.startsWith("1.7")){
bestSize.add(option);
}
notBigEnough.add(option);
}
}
//choose 16:9
if (bestSize.size() > 0){
return Collections.max(bestSize,new CompareSizesByArea());
}else if (notBigEnough.size() > 0){
return Collections.max(notBigEnough, new CompareSizesByArea());
}else if (bestBigEnough.size()> 0){
return Collections.min(bestBigEnough, new CompareSizesByArea());
}else if (bigEnough.size() > 0){
return Collections.min(bigEnough, new CompareSizesByArea());
} else {
return choices[0];
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.capture1);
AppManager.getAppManager().addActivity(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setScreenBrightness(200);
mTextureView = findViewById(R.id.preview_view);
viewfinderView = findViewById(R.id.scanner_view);
mFile = new File(Environment.getExternalStorageDirectory(), "自己取名字" +"Pic.jpg");
mCurrentState = AppManager.getAppManager().getCurrentLightState();
if (mCurrentState == 0){
iv_light.setImageResource(R.drawable.a31);
}else {
iv_light.setImageResource(R.drawable.a32);
}
Toast.makeText(this, R.string.mingliang, Toast.LENGTH_SHORT).show();
getScreenInfo();
}
/**
* 开始计时
*/
private void startTimeCount() {
time = 0;
start();
}
private int screenWidth;
private int screenHeight;
private void getScreenInfo(){
//获取内屏分辨率(华为手机有bar这个值不会变)
// WindowManager windowManager = getWindowManager();
// Display d = windowManager.getDefaultDisplay();
// DisplayMetrics realDis = new DisplayMetrics();
// d.getRealMetrics(realDis);
// screenHeight = realDis.heightPixels;
// screenWidth = realDis.widthPixels;
Point screenPoint = Camera2Manager.getCamera2Manager().getScreenPoint();
screenWidth = screenPoint.x;
screenHeight = screenPoint.y;
}
@Override
public void onResume() {
super.onResume();
Log.d("decodeMessage","onResume");
startBackgroundThread();
isF