package com.megvii.idcardlib; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.os.Bundle; import android.os.Vibrator; import android.view.TextureView; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.RelativeLayout; import android.widget.TextView; import com.megvii.idcardlib.util.DialogUtil; import com.megvii.idcardlib.util.ICamera; import com.megvii.idcardlib.util.RotaterUtil; import com.megvii.idcardlib.util.Util; import com.megvii.idcardlib.view.IDCardIndicator; import com.megvii.idcardlib.view.IDCardNewIndicator; import com.megvii.idcardquality.IDCardQualityAssessment; import com.megvii.idcardquality.IDCardQualityResult; import com.megvii.idcardquality.bean.IDCardAttr; import java.math.BigDecimal; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; public class IDCardScanActivity extends Activity implements TextureView.SurfaceTextureListener, Camera.PreviewCallback { private TextureView textureView; private DialogUtil mDialogUtil; private ICamera mICamera;// 照相机工具类 private IDCardQualityAssessment idCardQualityAssessment = null; private IDCardNewIndicator mNewIndicatorView; private IDCardIndicator mIdCardIndicator; private IDCardAttr.IDCardSide mSide; private DecodeThread mDecoder = null; private boolean mIsVertical = false; private TextView fps; private TextView errorType; private TextView horizontalTitle, verticalTitle; private TextView logInfo; private View debugRectangle; private boolean isDebugMode = false; int continuousClickCount = 0; long lastClickMillis = 0; private Vibrator vibrator; private float mInBound, mIsIDCard; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.idcardscan_layout); init(); initData(); } private void init() { vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); mSide = getIntent().getIntExtra("side", 0) == 0 ? IDCardAttr.IDCardSide.IDCARD_SIDE_FRONT : IDCardAttr.IDCardSide.IDCARD_SIDE_BACK; mIsVertical = getIntent().getBooleanExtra("isvertical", false); mICamera = new ICamera(mIsVertical); mDialogUtil = new DialogUtil(this); textureView = (TextureView) findViewById(R.id.idcardscan_layout_surface); textureView.setSurfaceTextureListener(this); textureView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mICamera.autoFocus(); } }); fps = (TextView) findViewById(R.id.idcardscan_layout_fps); logInfo = (TextView) findViewById(R.id.text_debug_info); errorType = (TextView) findViewById(R.id.idcardscan_layout_error_type); horizontalTitle = (TextView) findViewById(R.id.idcardscan_layout_horizontalTitle); verticalTitle = (TextView) findViewById(R.id.idcardscan_layout_verticalTitle); mFrameDataQueue = new LinkedBlockingDeque<byte[]>(1); mNewIndicatorView = (IDCardNewIndicator) findViewById(R.id.idcardscan_layout_newIndicator); mIdCardIndicator = (IDCardIndicator) findViewById(R.id.idcardscan_layout_indicator); debugRectangle = findViewById(R.id.debugRectangle); OnClickListener onClickListener = new OnClickListener() { @Override public void onClick(View v) { mICamera.autoFocus(); launchDebugMode(); } }; mNewIndicatorView.setOnClickListener(onClickListener); mIdCardIndicator.setOnClickListener(onClickListener); if (mIsVertical) { horizontalTitle.setVisibility(View.GONE); verticalTitle.setVisibility(View.VISIBLE); mIdCardIndicator.setVisibility(View.VISIBLE); mNewIndicatorView.setVisibility(View.GONE); mIdCardIndicator.setCardSideAndOrientation(mIsVertical, mSide); mNewIndicatorView.setCardSideAndOrientation(mIsVertical, mSide); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { horizontalTitle.setVisibility(View.VISIBLE); verticalTitle.setVisibility(View.GONE); mIdCardIndicator.setVisibility(View.GONE); mNewIndicatorView.setVisibility(View.VISIBLE); mIdCardIndicator.setCardSideAndOrientation(mIsVertical, mSide); mNewIndicatorView.setCardSideAndOrientation(mIsVertical, mSide); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } if (mDecoder == null) { mDecoder = new DecodeThread(); } if (!mDecoder.isAlive()) { mDecoder.start(); } } private void launchDebugMode() { long currentMillis = System.currentTimeMillis(); if (continuousClickCount == 0 || (continuousClickCount > 0 && currentMillis - lastClickMillis < 200)) { continuousClickCount++; } lastClickMillis = currentMillis; if (continuousClickCount == 6) { isDebugMode = true; continuousClickCount = 0; } } private void setDebugRectanglePosition() { Rect debugRoi; if (!mIsVertical) { debugRoi = mNewIndicatorView.getMargin(); } else { debugRoi = mIdCardIndicator.getMargin(); } final Rect fDebugRect = debugRoi; ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) debugRectangle.getLayoutParams(); params.setMargins(fDebugRect.left, fDebugRect.top, fDebugRect.right, fDebugRect.bottom); debugRectangle.setLayoutParams(params); } /** * 初始化数据 */ private void initData() { idCardQualityAssessment = new IDCardQualityAssessment.Builder().setIsIgnoreShadow(true).setIsIgnoreHighlight(false).build(); boolean initSuccess = idCardQualityAssessment.init(this, Util.readModel(this)); if (!initSuccess) { mDialogUtil.showDialog("检测器初始化失败"); } else { mInBound = idCardQualityAssessment.mInBound; mIsIDCard = idCardQualityAssessment.mIsIdcard; } } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { super.onPause(); } @Override protected void onDestroy() { super.onDestroy(); mDialogUtil.onDestory(); try { if (mDecoder != null) { mDecoder.interrupt(); mDecoder.join(); mDecoder = null; } } catch (InterruptedException e) { e.printStackTrace(); } idCardQualityAssessment.release(); idCardQualityAssessment = null; } private void doPreview() { if (!mHasSurface) return; mICamera.startPreview(textureView.getSurfaceTexture()); setDebugRectanglePosition(); } private boolean mHasSurface = false; @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { Camera mCamera = mICamera.openCamera(this); if (mCamera != null) { RelativeLayout.LayoutParams layout_params = mICamera.getLayoutParam(this); textureView.setLayoutParams(layout_params); mNewIndicatorView.setLayoutParams(layout_params); mIdCardIndicator.setLayoutParams(layout_params); } else { mDialogUtil.showDialog("打开摄像头失败"); return; } mHasSurface = true; doPreview(); mICamera.actionDetect(this); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mICamera.closeCamera(); mHasSurface = false; return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } @Override public void onPreviewFrame(final byte[] data, Camera camera) { mFrameDataQueue.offer(data); } private BlockingQueue<byte[]> mFrameDataQueue; private class DecodeThread extends Thread { boolean mHasSuccess = false; int mCount = 0; int mTimSum = 0; private IDCardQualityResult.IDCardFailedType mLstErrType; @Override public void run() { byte[] imgData = null; try { while ((imgData = mFrameDataQueue.take()) != null) { if (mHasSuccess) return; int imageWidth = mICamera.cameraWidth; int imageHeight = mICamera.cameraHeight; imgData = RotaterUtil.rotate(imgData, imageWidth, imageHeight, mICamera.getCameraAngle(IDCardScanActivity.this)); if (mIsVertical) { imageWidth = mICamera.cameraHeight; imageHeight = mICamera.cameraWidth; } long start = System.currentTimeMillis(); RectF rectF; if (!mIsVertical) { rectF = mNewIndicatorView.getPosition(); } else { rectF = mIdCardIndicator.getPosition(); } Rect roi = new Rect(); roi.left = (int) (rectF.left * imageWidth); roi.top = (int) (rectF.top * imageHeight); roi.right = (int) (rectF.right * imageWidth); roi.bottom = (int) (rectF.bottom * imageHeight); if (!isEven01(roi.left)) roi.left = roi.left + 1; if (!isEven01(roi.top)) roi.top = roi.top + 1; if (!isEven01(roi.right)) roi.right = roi.right - 1; if (!isEven01(roi.bottom)) roi.bottom = roi.bottom - 1; final IDCardQualityResult result = idCardQualityAssessment.getQuality(imgData, imageWidth, imageHeight, mSide, roi); long end = System.currentTimeMillis(); final long perFrameMillis = end - start; mCount++; mTimSum += (end - start); runOnUiThread(new Runnable() { @Override public void run() { if (isDebugMode) { if (result != null && result.attr != null) { String debugResult = "clear: " + new BigDecimal(result.attr.lowQuality).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue() + "\n" + "in_bound: " + new BigDecimal(result.attr.inBound).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue() + "\n" + "is_idcard: " + new BigDecimal(result.attr.isIdcard).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue() + "\n" + "flare: " + result.attr.specularHightlightCount + "\n" + "shadow: " + result.attr.shadowCount + "\n" + "millis: " + perFrameMillis; logInfo.setText(debugResult); } debugRectangle.setVisibility(View.VISIBLE); } else { logInfo.setText(""); debugRectangle.setVisibility(View.GONE); } } }); if (result != null) { if (result.attr != null) { float inBound = result.attr.inBound; float isIDCard = result.attr.isIdcard; if (isIDCard > mIsIDCard && inBound > 0) if (!mIsVertical) mNewIndicatorView.setBackColor(IDCardScanActivity.this, 0xaa000000); else mIdCardIndicator.setBackColor(IDCardScanActivity.this, 0xaa000000); else if (!mIsVertical) mNewIndicatorView.setBackColor(IDCardScanActivity.this, 0x00000000); else mIdCardIndicator.setBackColor(IDCardScanActivity.this, 0x00000000); } if (result.isValid()) { vibrator.vibrate(new long[]{0, 50, 50, 100, 50}, -1); mHasSuccess = true; handleSuccess(result); return; } else { if (!mIsVertical) mNewIndicatorView.setBackColor(IDCardScanActivity.this, 0x00000000); else mIdCardIndicator.setBackColor(IDCardScanActivity.this, 0x00000000); runOnUiThread(new Runnable() { @Override public void run() { List<IDCardQualityResult.IDCardFailedType> failTypes = (result == null) ? null : result.fails; if (failTypes != null) { StringBuilder stringBuilder = new StringBuilder(); IDCardQualityResult.IDCardFailedType errType = failTypes.get(0); if (mIsVertical) verticalTitle.setText(Util.errorType2HumanStr(failTypes.get(0), mSide)); else horizontalTitle.setText(Util.errorType2HumanStr(failTypes.get(0), mSide)); mLstErrType = errType; errorType.setText(stringBuilder.toString()); } else { verticalTitle.setText(""); horizontalTitle.setText(""); } if (mCount != 0&& mTimSum!=0) fps.setText((1000 * mCount / mTimSum) + " FPS"); } }); } } } } catch (InterruptedException e) { e.printStackTrace(); } } private void handleSuccess(IDCardQualityResult result) { Intent intent = new Intent(); intent.putExtra("side", mSide == IDCardAttr.IDCardSide.IDCARD_SIDE_FRONT ? 0 : 1); intent.putExtra("idcardImg", Util.bmp2byteArr(result.croppedImageOfIDCard())); if (result.attr.side == IDCardAttr.IDCardSide.IDCARD_SIDE_FRONT) { intent.putExtra("portraitImg", Util.bmp2byteArr(result.croppedImageOfPortrait())); } setResult(RESULT_OK, intent); Util.cancleToast(IDCardScanActivity.this); finish(); } } public static void startMe(Context context, IDCardAttr.IDCardSide side) { if (side == null || context == null) return; Intent intent = new Intent(context, IDCardScanActivity.class); intent.putExtra("side", side == IDCardAttr.IDCardSide.IDCARD_SIDE_FRONT ? 0 : 1); context.startActivity(intent); } // 用取余运算 public boolean isEven01(int num) { if (num % 2 == 0) { return true; } else { return false; } } }