分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
转载请标明出处:http://blog.csdn.net/xx326664162/article/details/50952856 文章出自:薛瑄的博客
你也可以查看我的其他同类文章,也会让你有一定的收货!
ZXing
ZXing是由谷歌开发的一款二维码扫描工具
官方github地址: https://github.com/zxing/zxing
这个工程包含很多项目,这里主要说一下android方面的。在工程中的android 项目依赖core项目 和android-core项目
core项目
ZXing使用maven构建整个工程,可以使用intelli IDEA 打开整个项目,编译打包core项目(核心解码库),得到jar
也可以使用maven中心库http://mvnrepository.com/artifact/com.google.zxing/core
android-core项目
这个项目中只有一个CameraConfigurationUtils类,主要是配置摄像头的一些参数,可以手动合并到android项目中
android项目
在这个工程中android项目也是使用maven构建的,Android Studio 中的项目是使用gradle构建的,使用file->new ->import project 导入项目,AS可以自动转换为gradle。
导入core项目的jar包,
- 导入android-core项目CameraConfigurationUtils类
至此就可以打包apk了
简化ZXing中的Android 项目
官方android项目,包含了剪切板,扫描历史,分享,扫描二维码,等等。
如果只使用扫码功能,需要剔除其他的功能,这里为大家提供一个封装好精简的ZXing Lib:
https://github.com/JantHsueh/ZxingAndroid
基于ZXing3.1封装,包含了最新的jar包和代码。
clone 项目。并导入AS后:
原版和简化版本共有类解析
CaptureActivity
原版本:ZXing的主Activity。ZXing启动后执行CaptureActivity,在handleDecode()方法中对扫码成功后的动作作处理。
简化后:主Activity是MainActivity,分别有扫描二维码和生成二维码,单击扫描二维码,也是执行CaptureActivity在handleDecode()方法中对扫码成功后的动作作处理。
ViewfinderView
原版本:ZXing扫码窗口的绘制,可以在这个类中改变原版扫描窗口的界面
简化后:ZXing扫码窗口,集成到了CaptureActivity中,抽取到了XML文件中,这样修改起来更加方便了。
CameraConfigurationManager
该类主要负责设置相机的参数信息,获取最佳的预览界面。修改横竖屏、处理变形效果的核心类。
1、public void setDesiredCameraParameters(Camera camera, boolean safeMode)方法中(读取配置设置相机的对焦模式、闪光灯模式等等),可以将扫描改为竖屏:
在方法最后加上:
/** 设置相机预览为竖屏 */camera.setDisplayOrientation(90);
- 1
- 2
2、在public void initFromCameraParameters(Camera camera)方法中(计算了屏幕分辨率和当前最适合的相机像素),我们可以对修改为竖屏扫码后,由于像素信息点没有对调造成图像扭曲变形进行修改。
在Log.d(TAG, “Screen resolution: ” + screenResolution);后加上如下的代码:
/** 因为换成了竖屏显示,所以不替换屏幕宽高得出的预览图是变形的 */Point screenResolutionForCamera = new Point();screenResolutionForCamera.x = screenResolution.x;screenResolutionForCamera.y = screenResolution.y;if (screenResolution.x < screenResolution.y) { screenResolutionForCamera.x = screenResolution.y; screenResolutionForCamera.y = screenResolution.x;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
将screenResolution替换为screenResolutionForCamera:
cameraResolution = findBestPreviewSizeValue(parameters, screenResolutionForCamera);
- 1
DecodeHandler.decode
ZXing解码的核心类
CaptureActivityHandler
当DecodeHandler.decode完成解码后,系统会向CaptureActivityHandler发消息。如果编码成功则调用CaptureActivity.handleDecode方法对扫描到的结果进行分类处理。
简化版本分析
MainActivity 主Activity
public class MainActivity extends Activity { private TextView resultTextView; private EditText qrStrEditText; private ImageView qrImgImageView; private CheckBox mCheckBox; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); resultTextView = (TextView) this.findViewById(R.id.tv_scan_result); qrStrEditText = (EditText) this.findViewById(R.id.et_qr_string); qrImgImageView = (ImageView) this.findViewById(R.id.iv_qr_image); mCheckBox = (CheckBox) findViewById(R.id.logo); //扫描二维码按钮 Button scanBarCodeButton = (Button) this.findViewById(R.id.btn_scan_barcode); scanBarCodeButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //打开扫描界面扫描条形码或二维码 Intent openCameraIntent = new Intent(MainActivity.this, CaptureActivity.class); startActivityForResult(openCameraIntent, 0); } }); //生成二维码按钮 Button generateQRCodeButton = (Button) this.findViewById(R.id.btn_add_qrcode); generateQRCodeButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String contentString = qrStrEditText.getText().toString(); if (!contentString.equals("")) { //根据字符串生成二维码图片并显示在界面上,第二个参数为图片的大小(350*350) Bitmap qrCodeBitmap = EncodingUtils.createQRCode(contentString, 350, 350, mCheckBox.isChecked() ? BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher) : null); qrImgImageView.setImageBitmap(qrCodeBitmap); } else { Toast.makeText(MainActivity.this, "Text can not be empty", Toast.LENGTH_SHORT).show(); } } }); } //扫描二维码,返回的结果 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { Bundle bundle = data.getExtras(); String scanResult = bundle.getString("result"); resultTextView.setText(scanResult); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
扫描截取界面的计算
因为在通过相机预览画面识别二维码时,需要知道二维码在相机预览画面中的位置,就会有这么一个问题:相机的分辨率和屏幕的分辨率不一样,怎么使屏幕中显示的扫描框,就是相机所捕捉的扫描框??
解决思路也很简单,按照比例去计算,相机预览画面尺寸/屏幕布局尺寸= 相机预览画面二维码位置/屏幕布局扫描框位置
pt:预览图中二维码图片的左上顶点坐标,也就是手机中相机预览中看到的待扫描二维码的位置
qrheight:预览图中二维码图片的高度
qrwidth:预览图中二维码图片的宽度
pheight:预览图的高度,也即camera的分辨率高度
pwidth:预览图的宽度,也即camera的分辨率宽度
st:布局文件中扫描框的左上顶点坐标
sheight:布局文件中扫描框的高度
swidth:布局文件中扫描框的宽度
cheight:布局文件中相机预览控件的高度
cwidth:布局文件中相机预览控件的宽度
存在下面的等比公式:
ptx / pwidth = stx / cwidth ;pty / pheight = sty / cheight ;qrwidth / pwidth = swidth / cwidth ;qrheight / pheight = sheight / cheight ;
- 1
- 2
- 3
- 4
确定相机中捕捉的扫描框大小和尺寸:
ptx = stx * pwidth / cwidth ;pty = sty * pheight / cheight ;qrwidth = swidth * pwidth / cwidth ;qrheight = sheight * pheight / cheight ;
- 1
- 2
- 3
- 4
- 5
CaptureActivity类
熟悉这个类中几个函数的使用,可以自由使用ZXing功能,改变UI
public final class CaptureActivity extends Activity implements SurfaceHolder.Callback { private static final String TAG = CaptureActivity.class.getSimpleName(); private CameraManager cameraManager; private CaptureActivityHandler handler; private InactivityTimer inactivityTimer; private BeepManager beepManager; private SurfaceView scanPreview = null; private RelativeLayout scanContainer; private RelativeLayout scanCropView; private ImageView scanLine; private Rect mCropRect = null; private boolean isHasSurface = false; public Handler getHandler() { return handler; } public CameraManager getCameraManager() { return cameraManager; } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_capture); //获得UI控件 scanPreview = (SurfaceView) findViewById(R.id.capture_preview); scanContainer = (RelativeLayout) findViewById(R.id.capture_container); scanCropView = (RelativeLayout) findViewById(R.id.capture_crop_view); scanLine = (ImageView) findViewById(R.id.capture_scan_line); //结束闲置一段时间的activity,如果这个设备时电池供电 inactivityTimer = new InactivityTimer(this); //蜂鸣器 beepManager = new BeepManager(this); //扫描动画 TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation .RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.9f); //动画持续的时间 animation.setDuration(4500); //动画重复的此时 animation.setRepeatCount(-1); //动画如何重复,从下到上,还是重新开始从上到下 animation.setRepeatMode(Animation.RESTART); scanLine.startAnimation(animation); } @Override protected void onResume() { super.onResume(); // 下面这句是我自行加入的,在原作者的代码中,转动屏幕,出现崩溃,所以使用下面这条语句,固定屏幕方向为竖屏 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // CameraManager must be initialized here, not in onCreate(). This is // necessary because we don't // want to open the camera driver and measure the screen size if we're // going to show the help on // first launch. That led to bugs where the scanning rectangle was the // wrong size and partially // off screen. cameraManager = new CameraManager(getApplication()); handler = null; if (isHasSurface) { // The activity was paused but not stopped, so the surface still // exists. Therefore // surfaceCreated() won't be called, so init the camera here. initCamera(scanPreview.getHolder()); } else { // Install the callback and wait for surfaceCreated() to init the // camera. scanPreview.getHolder().addCallback(this); } inactivityTimer.onResume(); } @Override protected void onPause() { if (handler != null) { handler.quitSynchronously(); handler = null; } inactivityTimer.onPause(); beepManager.close(); cameraManager.closeDriver(); if (!isHasSurface) { scanPreview.getHolder().removeCallback(this); } super.onPause(); } @Override protected void onDestroy() { inactivityTimer.shutdown(); super.onDestroy(); } //实现SurfaceHolder.Callback,需要实现下面三个函数 @Override public void surfaceCreated(SurfaceHolder holder) { if (holder == null) { Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!"); } if (!isHasSurface) { isHasSurface = true; initCamera(holder); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { isHasSurface = false; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * A valid barcode has been found, so give an indication of success and show the results. * 扫描完后的结果在这个函数处理 * @param rawResult The contents of the barcode. * @param bundle The extras */ public void handleDecode(Result rawResult, Bundle bundle) { inactivityTimer.onActivity(); beepManager.playBeepSoundAndVibrate(); Intent resultIntent = new Intent(); bundle.putInt("width", mCropRect.width()); bundle.putInt("height", mCropRect.height()); bundle.putString("result", rawResult.getText()); resultIntent.putExtras(bundle); this.setResult(RESULT_OK, resultIntent); CaptureActivity.this.finish(); } //初始化摄像机 private void initCamera(SurfaceHolder surfaceHolder) { if (surfaceHolder == null) { throw new IllegalStateException("No SurfaceHolder provided"); } if (cameraManager.isOpen()) { Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?"); return; } try { //设置取景的使用哪个surfaceView来取景 cameraManager.openDriver(surfaceHolder); // Creating the handler starts the preview, which can also throw a // RuntimeException. if (handler == null) { //这个类处理所有包括状态机捕获的消息。 handler = new CaptureActivityHandler(this, cameraManager, DecodeThread.ALL_MODE); } //初始化截取的矩形区域 initCrop(); } catch (IOException ioe) { Log.w(TAG, ioe); displayFrameworkBugMessageAndExit(); } catch (RuntimeException e) { // Barcode Scanner has seen crashes in the wild of this variety: // java.?lang.?RuntimeException: Fail to connect to camera service Log.w(TAG, "Unexpected error initializing camera", e); displayFrameworkBugMessageAndExit(); } } private void displayFrameworkBugMessageAndExit() { // camera error AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.app_name)); builder.setMessage("Camera error"); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { finish(); } }); builder.show(); } public void restartPreviewAfterDelay(long delayMS) { if (handler != null) { handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS); } } public Rect getCropRect() { return mCropRect; } /** * 初始化截取的矩形区域 */ private void initCrop() { int cameraWidth = cameraManager.getCameraResolution().y; int cameraHeight = cameraManager.getCameraResolution().x; /** 获取布局中扫描框的位置信息 */ int[] location = new int[2]; scanCropView.getLocationInWindow(location); int cropLeft = location[0]; int cropTop = location[1] - getStatusBarHeight(); int cropWidth = scanCropView.getWidth(); int cropHeight = scanCropView.getHeight(); /** 获取布局容器的宽高 */ int containerWidth = scanContainer.getWidth(); int containerHeight = scanContainer.getHeight(); /** 计算最终截取的矩形的左上角顶点x坐标 */ int x = cropLeft * cameraWidth / containerWidth; /** 计算最终截取的矩形的左上角顶点y坐标 */ int y = cropTop * cameraHeight / containerHeight; /** 计算最终截取的矩形的宽度 */ int width = cropWidth * cameraWidth / containerWidth; /** 计算最终截取的矩形的高度 */ int height = cropHeight * cameraHeight / containerHeight; /** 生成最终的截取的矩形 */ mCropRect = new Rect(x, y, width + x, height + y); } //获取状态条的高度 private int getStatusBarHeight() { try { Class<?> c = Class.forName("com.android.internal.R$dimen"); Object obj = c.newInstance(); Field field = c.getField("status_bar_height"); int x = Integer.parseInt(field.get(obj).toString()); return getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } return 0; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
至于更深层次的如何处理扫描的结果,图形处理等等,就不深究了,我的水平有限。这些代码都在core项目中
demo地址:https://github.com/JantHsueh/ZxingAndroid
参考:
通过ZXing获取静态二维码图片结果:http://blog.csdn.net/luzhenyuxfcy/article/details/50475670
http://blog.csdn.net/eclipsexys/article/details/47834865
http://blog.csdn.net/xiaanming/article/details/10163203
扫描截取界面的计算
关注我的公众号,轻松了解和学习更多技术