Repository: 465857721/IDCardOCR_China Branch: master Commit: c93231a20ae0 Files: 35 Total size: 55.5 KB Directory structure: gitextract_pqg52avl/ ├── .idea/ │ ├── compiler.xml │ ├── copyright/ │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── assets/ │ │ └── eng.traineddata │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── kingsoft/ │ │ └── idcardocr_china/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── kingsoft/ │ │ │ └── idcardocr_china/ │ │ │ ├── MainActivity.java │ │ │ └── idcardocr/ │ │ │ ├── AutoFocusManager.java │ │ │ ├── CameraActivity.java │ │ │ ├── CameraManager.java │ │ │ └── PreviewBorderView.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── activity_camera.xml │ │ │ └── activity_main.xml │ │ ├── values/ │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ └── test/ │ └── java/ │ └── com/ │ └── kingsoft/ │ └── idcardocr_china/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ Abstraction issuesJava Android > Lint > Correctness Android > Lint > Correctness > Messages Android > Lint > Performance Android > Lint > Security Android > Lint > Usability > Icons Internationalization issuesJava J2ME issuesJava Java Java language level issuesJava Logging issuesJava Numeric issuesJava Portability issuesJava Security issuesJava Serialization issuesJava Android C:\Users\周康\AppData\Roaming\Subversion 1.8 ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ # IDCardOCR_China 基于tesseract,实现摄像头扫描识别中国二代身份证 # Usage demo主要提供思路,通过摄像头取景,预览裁剪,ocr识别. # ScreenShots ![image](https://github.com/465857721/IDCardOCR_China/blob/master/ScreenShots/device-2016-12-24-095238-small.jpg) # TODO - 姓名ocr识别 - 民族识别 # 建议 1.对esseract进行训练,目前用的文件为eng数据,没有针对二代身份证进行优化,可以自己进行训练,只识别1-0和英文X。数据包也会相应减小。 ( baseApi.setVariable("tessedit_char_whitelist", "0123456789Xx");)也可以设置白名单,代码里设置了,但是这样没有减少训练文件的大小. 2.目前demo仅仅识别了身份证号码,可以裁剪出来其他信息进行ocr,后续进行优化.... #Thanks https://github.com/tdk-farkas/CameraSFZ 身份证裁剪 #Changelog V0.0.2(2016/12/24) - 6.0权限适配 - 识别单词设置白名单 增加识别率 V0.0.1(2016/12/01) - 项目导入 ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.kingsoft.idcardocr_china" minSdkVersion 21 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.0.0' testCompile 'junit:junit:4.12' compile 'com.rmtheis:tess-two:6.1.1' compile 'com.anthonycr.grant:permissions:1.0' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in C:\dev\android-sdk-windows/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/kingsoft/idcardocr_china/ExampleInstrumentedTest.java ================================================ package com.kingsoft.idcardocr_china; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.kingsoft.idcardocr_china", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/kingsoft/idcardocr_china/MainActivity.java ================================================ package com.kingsoft.idcardocr_china; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.anthonycr.grant.PermissionsManager; import com.anthonycr.grant.PermissionsResultAction; import com.kingsoft.idcardocr_china.idcardocr.CameraActivity; public class MainActivity extends AppCompatActivity { private static final int GETPERMISSION_SUCCESS = 1;//获取权限成功 private static final int GETPERMISSION_FAILER = 2;//获取权限失败 private TextView tv_id; private int MY_SCAN_REQUEST_CODE = 100; private Context mContext; private MyHandler myHandler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; tv_id = (TextView) findViewById(R.id.tv_id); findViewById(R.id.btn_go).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // getPermissions(); requestAllPermission(); } }); } private void requestAllPermission() { PermissionsManager.getInstance().requestAllManifestPermissionsIfNecessary(MainActivity.this, new PermissionsResultAction() { @Override public void onGranted() { myHandler.sendEmptyMessage(GETPERMISSION_SUCCESS); } @Override public void onDenied(String permission) { myHandler.sendEmptyMessage(GETPERMISSION_FAILER); } }); } //因为权限管理类无法监听系统,所以需要重写onRequestPermissionResult方法,更新权限管理类,并回调结果。这个是必须要有的。 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == MY_SCAN_REQUEST_CODE && resultCode == RESULT_OK && data != null) { String id = data.getStringExtra("id"); Toast.makeText(this, id, Toast.LENGTH_LONG).show(); if (id != null && id.length() == 18) { tv_id.setText(id); } } } private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case GETPERMISSION_SUCCESS: Intent scanIntent = new Intent(mContext, CameraActivity.class); startActivityForResult(scanIntent, MY_SCAN_REQUEST_CODE); break; case GETPERMISSION_FAILER: Toast.makeText(mContext, "此功能须获摄像头权限1111111", Toast.LENGTH_LONG).show(); break; } } } } ================================================ FILE: app/src/main/java/com/kingsoft/idcardocr_china/idcardocr/AutoFocusManager.java ================================================ package com.kingsoft.idcardocr_china.idcardocr; import android.hardware.Camera; import android.os.AsyncTask; import android.util.Log; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.RejectedExecutionException; public class AutoFocusManager implements Camera.AutoFocusCallback{ private static final String TAG = AutoFocusManager.class.getSimpleName(); private static final long AUTO_FOCUS_INTERVAL_MS = 3000L; private static final Collection FOCUS_MODES_CALLING_AF; static { FOCUS_MODES_CALLING_AF = new ArrayList(2); FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO); FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO); } private boolean stopped; private boolean focusing; private final boolean useAutoFocus; private final Camera camera; private AsyncTask outstandingTask; private Camera.PreviewCallback mp; public AutoFocusManager(Camera camera, Camera.PreviewCallback mp) { this.camera = camera; String currentFocusMode = camera.getParameters().getFocusMode(); useAutoFocus = FOCUS_MODES_CALLING_AF.contains(currentFocusMode); Log.e(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus); this.mp = mp; start(); } @Override public synchronized void onAutoFocus(final boolean success, Camera theCamera) { // if(success){ // camera.takePicture(null, null, myjpegCallback); // } ; focusing = false; autoFocusAgainLater(); } private synchronized void autoFocusAgainLater() { if (!stopped && outstandingTask == null) { AutoFocusTask newTask = new AutoFocusTask(); try { newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); outstandingTask = newTask; } catch (RejectedExecutionException ree) { Log.e(TAG, "Could not request auto focus", ree); } } } /** * 开始自动对焦 */ public synchronized void start() { // camera.setOneShotPreviewCallback(new Camera.PreviewCallback() { // @Override // public void onPreviewFrame(byte[] data, Camera camera) // }); if (useAutoFocus) { outstandingTask = null; if (!stopped && !focusing) { try { camera.setOneShotPreviewCallback(mp); camera.autoFocus(this); focusing = true; } catch (RuntimeException re) { // Have heard RuntimeException reported in Android 4.0.x+; continue? Log.e(TAG, "Unexpected exception while focusing", re); // Try again later to keep cycle going autoFocusAgainLater(); } } } } private synchronized void cancelOutstandingTask() { if (outstandingTask != null) { if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) { outstandingTask.cancel(true); } outstandingTask = null; } } /** * 停止自动对焦 */ public synchronized void stop() { stopped = true; if (useAutoFocus) { cancelOutstandingTask(); // Doesn't hurt to call this even if not focusing try { camera.cancelAutoFocus(); } catch (RuntimeException re) { // Have heard RuntimeException reported in Android 4.0.x+; continue? Log.e(TAG, "Unexpected exception while cancelling focusing", re); } } } private final class AutoFocusTask extends AsyncTask { @Override protected Object doInBackground(Object... voids) { try { Thread.sleep(AUTO_FOCUS_INTERVAL_MS); } catch (InterruptedException e) { // continue } start(); return null; } } } ================================================ FILE: app/src/main/java/com/kingsoft/idcardocr_china/idcardocr/CameraActivity.java ================================================ package com.kingsoft.idcardocr_china.idcardocr; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.YuvImage; import android.hardware.Camera; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.googlecode.tesseract.android.TessBaseAPI; import com.kingsoft.idcardocr_china.R; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class CameraActivity extends Activity implements SurfaceHolder.Callback, Camera.PreviewCallback { private TessBaseAPI baseApi; // Camera.PreviewCallback mp = ; private CameraManager cameraManager; private boolean hasSurface; private String type; private Button btn_close, light, btn_resacn; private boolean toggleLight = false; private Handler mHandler; private TextView tv_lightstate, tv_input; private String sdPath; // private ImageView iv_close; // private View ErrorView; private int times = 0; private Long opentime; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); opentime = System.currentTimeMillis(); sdPath = Environment.getExternalStorageDirectory() + "/ayy/"; try { copyAssetFile(); } catch (Exception e) { e.printStackTrace(); } baseApi = new TessBaseAPI(); baseApi.init(sdPath, "eng"); baseApi.setVariable("tessedit_char_whitelist", "0123456789Xx"); // baseApi. setContentView(R.layout.activity_camera); tv_lightstate = (TextView) findViewById(R.id.tv_openlight); mHandler = new Handler(); initLayoutParams(); } /** * 重置surface宽高比例为3:4,不重置的话图形会拉伸变形 */ private void initLayoutParams() { // ErrorView = findViewById(R.id.ll_cameraerrorview); btn_close = (Button) findViewById(R.id.btn_close); light = (Button) findViewById(R.id.light); btn_close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setResult(RESULT_CANCELED); onBackPressed(); } }); light.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { long time = System.currentTimeMillis();// 摄像头 初始化 需要时间 if (time - opentime > 2000) { opentime = time; if (!toggleLight) { toggleLight = true; tv_lightstate.setText("关闭闪关灯"); cameraManager.openLight(); } else { toggleLight = false; tv_lightstate.setText("打开闪关灯"); cameraManager.offLight(); } } } }); // btn_resacn = (Button) findViewById(R.id.btn_rescan); // btn_resacn.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // times = 0; // ErrorView.setVisibility(View.GONE); // } // }); // tv_input = (TextView) findViewById(R.id.tv_inputbyself); // tv_input.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // setResult(RESULT_CANCELED); // onBackPressed(); // } // }); // iv_close = (ImageView) findViewById(R.id.iv_closetips); // iv_close.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // times = 0; // ErrorView.setVisibility(View.GONE); // // } // }); } @Override protected void onResume() { super.onResume(); /** * 初始化camera */ cameraManager = new CameraManager(); SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceview); SurfaceHolder surfaceHolder = surfaceView.getHolder(); if (hasSurface) { // activity在paused时但不会stopped,因此surface仍旧存在; // surfaceCreated()不会调用,因此在这里初始化camera initCamera(surfaceHolder); } else { // 重置callback,等待surfaceCreated()来初始化camera surfaceHolder.addCallback(this); } } @Override public void surfaceCreated(SurfaceHolder holder) { if (!hasSurface) { hasSurface = true; initCamera(holder); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { hasSurface = false; } /** * 初始camera * * @param surfaceHolder SurfaceHolder */ private void initCamera(SurfaceHolder surfaceHolder) { if (surfaceHolder == null) { throw new IllegalStateException("No SurfaceHolder provided"); } if (cameraManager.isOpen()) { return; } try { // 打开Camera硬件设备 cameraManager.openDriver(surfaceHolder, this); // 创建一个handler来打开预览,并抛出一个运行时异常 cameraManager.startPreview(this); } catch (Exception ioe) { Log.d("zk", ioe.toString()); } } @Override protected void onPause() { /** * 停止camera,是否资源操作 */ cameraManager.stopPreview(); cameraManager.closeDriver(); if (!hasSurface) { SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceview); SurfaceHolder surfaceHolder = surfaceView.getHolder(); surfaceHolder.removeCallback(this); } super.onPause(); } private boolean copyAssetFile() throws Exception { String dir = sdPath + "/tessdata"; String filePath = sdPath + "/tessdata/eng.traineddata"; File f = new File(dir); if (f.exists()) { } else { f.mkdirs(); } File dataFile = new File(filePath); if (dataFile.exists()) { return true;// 文件存在 } else { InputStream in = this.getAssets().open("eng.traineddata"); File outFile = new File(filePath); if (outFile.exists()) { outFile.delete(); } OutputStream out = new FileOutputStream(outFile); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); } return false; } public String doOcr(Bitmap bitmap) { bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); baseApi.setImage(bitmap); String text = baseApi.getUTF8Text(); baseApi.clear(); // baseApi.end(); return text; } @Override public void onBackPressed() { if (baseApi != null) baseApi.end(); super.onBackPressed(); } @Override public void onPreviewFrame(byte[] data, Camera camera) { ByteArrayOutputStream baos; byte[] rawImage; Bitmap bitmap; Camera.Size previewSize = camera.getParameters().getPreviewSize();//获取尺寸,格式转换的时候要用到 BitmapFactory.Options newOpts = new BitmapFactory.Options(); newOpts.inJustDecodeBounds = true; YuvImage yuvimage = new YuvImage( data, ImageFormat.NV21, previewSize.width, previewSize.height, null); baos = new ByteArrayOutputStream(); yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100, baos);// 80--JPG图片的质量[0-100],100最高 rawImage = baos.toByteArray(); //将rawImage转换成bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length, options); if (bitmap == null) { Log.d("zka", "bitmap is nlll"); return; } else { int height = bitmap.getHeight(); int width = bitmap.getWidth(); final Bitmap bitmap1 = Bitmap.createBitmap(bitmap, (width - height) / 2, height / 6, height, height * 2 / 3); int x, y, w, h; x = (int) (bitmap1.getWidth() * 0.340); y = (int) (bitmap1.getHeight() * 0.800); w = (int) (bitmap1.getWidth() * 0.6 + 0.5f); h = (int) (bitmap1.getHeight() * 0.12 + 0.5f); Bitmap bit_hm = Bitmap.createBitmap(bitmap1, x, y, w, h); String id = doOcr(bit_hm); // if (++times > 10) // ErrorView.setVisibility(View.VISIBLE); if (id.length() == 18) { Intent i = new Intent(); i.putExtra("id", id); setResult(RESULT_OK, i); onBackPressed(); } } } } ================================================ FILE: app/src/main/java/com/kingsoft/idcardocr_china/idcardocr/CameraManager.java ================================================ package com.kingsoft.idcardocr_china.idcardocr; import android.content.Context; import android.hardware.Camera; import android.hardware.Camera.Size; import android.util.DisplayMetrics; import android.util.Log; import android.view.SurfaceHolder; import java.io.IOException; import java.util.List; public class CameraManager { private static final String TAG = CameraManager.class.getName(); private Camera camera; private Camera.Parameters parameters; private AutoFocusManager autoFocusManager; private int requestedCameraId = -1; private boolean initialized; private boolean previewing; private Size mPreviewSize; private List mSupportedPreviewSizes; /** * 打开摄像头 * * @param cameraId 摄像头id * @return Camera */ public Camera open(int cameraId, Context mContext) { int nucameras = Camera.getNumberOfCameras(); if (nucameras == 0) { Log.e(TAG, "No cameras!"); return null; } boolean explicitRequest = cameraId >= 0; if (!explicitRequest) { // Select a camera if no explicit camera requested int index = 0; while (index < nucameras) { Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); Camera.getCameraInfo(index, cameraInfo); if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { break; } index++; } cameraId = index; } Camera camera; if (cameraId < nucameras) { Log.e(TAG, "Opening camera #" + cameraId); camera = Camera.open(cameraId); } else { if (explicitRequest) { Log.e(TAG, "Requested camera does not exist: " + cameraId); camera = null; } else { Log.e(TAG, "No camera facing back; returning camera #0"); camera = Camera.open(0); } } mSupportedPreviewSizes = camera.getParameters() .getSupportedPreviewSizes(); if (mSupportedPreviewSizes != null) { mPreviewSize = getOptimalPreviewSize( mSupportedPreviewSizes, getScreenWidth(mContext), getScreenHeight(mContext)); } Log.d("zkcam", "w=" + getScreenWidth(mContext)); Log.d("zkcam", "h=" + getScreenHeight(mContext)); Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); Log.d("zkcam", "pw=" + mPreviewSize.width); Log.d("zkcam", "ph=" + mPreviewSize.height); camera.setParameters(parameters); return camera; } private Camera.Size getOptimalPreviewSize(List sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.2; double targetRatio = (double) w / h; if (sizes == null) return null; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; for (Camera.Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } /** * 打开camera * * @param holder SurfaceHolder * @throws IOException IOException */ public synchronized void openDriver(SurfaceHolder holder, Context mContext) throws IOException { Log.e(TAG, "openDriver"); Camera theCamera = camera; if (theCamera == null) { theCamera = open(requestedCameraId, mContext); if (theCamera == null) { throw new IOException(); } camera = theCamera; } theCamera.setPreviewDisplay(holder); // if (!initialized) { // initialized = true; // parameters = camera.getParameters(); // parameters.setPreviewSize(800, 600); // parameters.setPictureFormat(ImageFormat.JPEG); // parameters.setJpegQuality(100); // parameters.setPictureSize(800, 600); //// theCamera.setParameters(parameters); // } } /** * camera是否打开 * * @return camera是否打开 */ public synchronized boolean isOpen() { return camera != null; } /** * 关闭camera */ public synchronized void closeDriver() { Log.e(TAG, "closeDriver"); if (camera != null) { camera.release(); camera = null; } } /** * 开始预览 */ public synchronized void startPreview(Camera.PreviewCallback mp) { Log.e(TAG, "startPreview"); Camera theCamera = camera; if (theCamera != null && !previewing) { // theCamera.setOneShotPreviewCallback(mp); theCamera.startPreview(); previewing = true; autoFocusManager = new AutoFocusManager(camera, mp); } // camera.setPreviewCallback(new Camera.PreviewCallback() { // @Override // public void onPreviewFrame(byte[] data, Camera camera) { // Log.d("zka","onPreviewFrame"); // } // }); } /** * 关闭预览 */ public synchronized void stopPreview() { Log.e(TAG, "stopPreview"); if (autoFocusManager != null) { autoFocusManager.stop(); autoFocusManager = null; } if (camera != null && previewing) { camera.stopPreview(); previewing = false; } } /** * 打开闪光灯 */ public synchronized void openLight() { Log.e(TAG, "openLight"); if (camera != null) { parameters = camera.getParameters(); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); camera.setParameters(parameters); } } /** * 关闭闪光灯 */ public synchronized void offLight() { Log.e(TAG, "offLight"); if (camera != null) { parameters = camera.getParameters(); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); camera.setParameters(parameters); } } /** * 拍照 * * @param shutter ShutterCallback * @param raw PictureCallback * @param jpeg PictureCallback */ public synchronized void takePicture(final Camera.ShutterCallback shutter, final Camera.PictureCallback raw, final Camera.PictureCallback jpeg) { camera.takePicture(shutter, raw, jpeg); } public int getScreenWidth(Context context) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); return dm.widthPixels; } /** * 获得屏幕高度 * * @param context 上下文 * @return 屏幕除去通知栏的高度 */ public int getScreenHeight(Context context) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); return dm.heightPixels; } } ================================================ FILE: app/src/main/java/com/kingsoft/idcardocr_china/idcardocr/PreviewBorderView.java ================================================ package com.kingsoft.idcardocr_china.idcardocr; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.SurfaceHolder; import android.view.SurfaceView; import com.kingsoft.idcardocr_china.R; public class PreviewBorderView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private static final String DEFAULT_TIPS_TEXT = "请将身份证照片面置于框内扫描,并尽量对齐边框"; private static final int DEFAULT_TIPS_TEXT_SIZE = 18; private static final int DEFAULT_TIPS_TEXT_COLOR = Color.GREEN; private int mScreenH; private int mScreenW; private Canvas mCanvas; private Paint mPaint; private Paint mPaintLine; private SurfaceHolder mHolder; private Thread mThread; /** * 自定义属性 */ private float tipTextSize; private int tipTextColor; private String tipText; public PreviewBorderView(Context context) { this(context, null); } public PreviewBorderView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PreviewBorderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); init(); } /** * 初始化自定义属性 * * @param context Context * @param attrs AttributeSet */ private void initAttrs(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PreviewBorderView); try { tipTextSize = a.getDimension(R.styleable.PreviewBorderView_tipTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TIPS_TEXT_SIZE, getResources().getDisplayMetrics())); tipTextColor = a.getColor(R.styleable.PreviewBorderView_tipTextColor, DEFAULT_TIPS_TEXT_COLOR); tipText = a.getString(R.styleable.PreviewBorderView_tipText); if (tipText == null) { tipText = DEFAULT_TIPS_TEXT; } } finally { a.recycle(); } } /** * 初始化绘图变量 */ private void init() { this.mHolder = getHolder(); this.mHolder.addCallback(this); this.mHolder.setFormat(PixelFormat.TRANSPARENT); setZOrderOnTop(true); setZOrderMediaOverlay(true); this.mPaint = new Paint(); this.mPaint.setAntiAlias(true); this.mPaint.setColor(Color.WHITE); this.mPaint.setStyle(Paint.Style.FILL_AND_STROKE); this.mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); this.mPaintLine = new Paint(); this.mPaintLine.setColor(Color.WHITE); this.mPaintLine.setStrokeWidth(3.0F); setKeepScreenOn(true); } /** * 绘制取景框 */ private void draw() { try { this.mCanvas = this.mHolder.lockCanvas(); this.mCanvas.drawARGB(100, 0, 0, 0); // this.mScreenW = (this.mScreenH * 4 / 3); Log.e("TAG", "mScreenW:" + mScreenW + " mScreenH:" + mScreenH); this.mCanvas.drawRect(new RectF(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6), this.mPaint); this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6 + 150, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6 + 150, this.mScreenH * 1 / 6, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH * 1 / 6 + 150, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6 - 150, this.mScreenH * 1 / 6, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6 - 150, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6 + 150, this.mScreenH - this.mScreenH * 1 / 6, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6 - 150, this.mPaintLine); this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6 - 150, this.mScreenH - this.mScreenH * 1 / 6, this.mPaintLine); mPaintLine.setTextSize(tipTextSize); mPaintLine.setAntiAlias(true); mPaintLine.setDither(true); float length = mPaintLine.measureText(tipText); this.mCanvas.drawText(tipText, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6 + this.mScreenH / 2 - length / 2, this.mScreenH * 1 / 2 - tipTextSize, mPaintLine); Log.e("TAG", "left:" + (this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6)); Log.e("TAG", "top:" + (this.mScreenH * 1 / 6)); Log.e("TAG", "right:" + (this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6)); Log.e("TAG", "bottom:" + (this.mScreenH - this.mScreenH * 1 / 6)); } catch (Exception e) { e.printStackTrace(); } finally { if (this.mCanvas != null) { this.mHolder.unlockCanvasAndPost(this.mCanvas); } } } @Override public void surfaceCreated(SurfaceHolder holder) { //获得宽高,开启子线程绘图 this.mScreenW = getWidth(); this.mScreenH = getHeight(); this.mThread = new Thread(this); this.mThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { //停止线程 try { mThread.interrupt(); mThread = null; } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { //子线程绘图 draw(); } } ================================================ FILE: app/src/main/res/layout/activity_camera.xml ================================================