Aug
30
ZXING库是一个由谷歌出品的开源识别库,可以解码几乎所有一维和二维的图形,当然,也可以编码。zxing生来是为java和android二开发的,而现在,它已经被移植到包括ios在内的各个平台。官方地址是:https://github.com/zxing/zxing,在其中可以看到各种example和各个语言的移植或者接口。
只是使用zxing的话,最核心的调用方法是:
- 解码:new MultiFormatReader().decodeWithState(bitmap),传入BinaryBitmap格式的图像,一句话就可以得到返回的结果
- 编码:new MultiFormatWriter().encode(str,BarcodeFormat.QR_CODE, width, height, hints),传入一个字符串和要编码的格式,大小等,就可以获得一副BitMatrix格式的图像数据已供使用了
1.识别程序运行流程
整个程序启动相机与识别流程如下:
1.intent启动activity,获取intent需求
2.初始化界面,界面主要包括相机浏览图容器与悬浮与浏览图上方的样式与按钮
3.初始化处理机A,这个处理机用户获取相机的浏览图并绑定到容器中
4.初始化处理机B,接收对焦结果,用于识别图片
5.启动相机,绑定处理机,开始正式运行
6.主动进行自动对焦
7.对焦完成后处理机B自动接收对焦结果,新开一个线程调用zxing进行识别
8.识别失败,返回第6步
9.识别成功,结束activity,返回识别到的字符串
当然,过程中还涉及到相机的闪光灯等功能的交叉控制等。
2.浏览界面更新与拍照数据的方向
安卓的相机对象有一个浏览功能,它可以setPreviewDisplay一个低分辨率的图像到一个SurfaceHolder控件上,并且可以通过绑定事件监听器Camera.PreviewCallback来收到界面的更新并且更新到SurfaceHolder控件上去,这是个耗时操作,多半是新开一个线程。同时,当确定拍摄是也会获得一个数据,一张巨大的图片数据。然而,相机输出的图像数据是横向的,即使我们使用的是竖直拍摄的模式。这就是系统自带相机应用和大部分类似应用的activity和相机输出的图片数据都是横向的原因......
然而对于二维码扫描软件,并不存在横向的设计,普遍都是纵向的界面设计和实际编写(除了zxing自带的demo)
因此,需要解决问题——把图像翻转为纵向:
1.首先,我们都会想到在AndroidManifest.xml将浏览用的activity改造纵向
2.同时,要在相机初始化的时候将相机旋转90度
3.当然这样还不行,还要讲相机拍摄浏览图的数据固定为纵向的比例
4.最后,当然最终拍摄结果传递给zxing解析前,还要变换一次方向
这样一来,基本上相机功能可以适应纵向屏幕了,再调整界面即可。
3.相机硬件控制冲突
在部分手机或者部分相机上,频繁操作相机的各种属性和方法有一定概率造成操作失败,抛出错误并导致程序崩溃。经过谷歌与排查,这是由于操作相机硬件太过频繁而相机硬件来不及操作导致的硬伤。因此,需要某一种机制来管理操作相机的方法。
通过网站上查询与大家的意见,有以下几种方法:
1.将操作压入队列,按一定时间频率进行操作。
2.循环尝试,失败的话或延迟一段时间再试直到成功。
3.无视用户操作或可反馈错误,直接结束掉其中一个会导致崩溃的操作。
在zxing的使用中,个大软件都遇到了一个坑,是:在相机进行自动聚焦的时候,开关闪光灯有很高的概率失败。
- UC采用的解决方案是:在进行开关闪光灯时,强制结束自动对焦,操作成功后再开启。(这个功能在我的手机lt26ii上有bug,再次开启会失败;包括我们尝试编写的程序和UC,遇到了相同的问题)
- QQ采用了与众不同的对焦策略,避免了这个问题。
- 其他app则压根没有开启闪光灯这个功能。
考虑到我们的程序的使用场景,闪光灯是有必要的,因此我们折中才用的解决方法是方法2,循环重试。代码如下:
我们设置最大尝试次数100次,然后try尝试设置,如果设置失败就继续++循环,否则就结束运行。另外有方案是在失败的情况下将程序挂起20ms或者50ms之类的再继续。然而由于操作相机必须在主线程里进行,强制挂起会影响用户体验并有程序ANR风险,所以放弃。
根据测试,平均遇到冲突的概率不超过20%,每次冲突从重试开始到最终解决平均需要300ms左右,最高能到500ms以上。虽然不是漂亮的数字,但在不会影响功能使用的情况下解决问题,也是一种可行方案。
只是使用zxing的话,最核心的调用方法是:
- 解码:new MultiFormatReader().decodeWithState(bitmap),传入BinaryBitmap格式的图像,一句话就可以得到返回的结果
- 编码:new MultiFormatWriter().encode(str,BarcodeFormat.QR_CODE, width, height, hints),传入一个字符串和要编码的格式,大小等,就可以获得一副BitMatrix格式的图像数据已供使用了
1.识别程序运行流程
整个程序启动相机与识别流程如下:
1.intent启动activity,获取intent需求
2.初始化界面,界面主要包括相机浏览图容器与悬浮与浏览图上方的样式与按钮
3.初始化处理机A,这个处理机用户获取相机的浏览图并绑定到容器中
4.初始化处理机B,接收对焦结果,用于识别图片
5.启动相机,绑定处理机,开始正式运行
6.主动进行自动对焦
7.对焦完成后处理机B自动接收对焦结果,新开一个线程调用zxing进行识别
8.识别失败,返回第6步
9.识别成功,结束activity,返回识别到的字符串
当然,过程中还涉及到相机的闪光灯等功能的交叉控制等。
2.浏览界面更新与拍照数据的方向
安卓的相机对象有一个浏览功能,它可以setPreviewDisplay一个低分辨率的图像到一个SurfaceHolder控件上,并且可以通过绑定事件监听器Camera.PreviewCallback来收到界面的更新并且更新到SurfaceHolder控件上去,这是个耗时操作,多半是新开一个线程。同时,当确定拍摄是也会获得一个数据,一张巨大的图片数据。然而,相机输出的图像数据是横向的,即使我们使用的是竖直拍摄的模式。这就是系统自带相机应用和大部分类似应用的activity和相机输出的图片数据都是横向的原因......
然而对于二维码扫描软件,并不存在横向的设计,普遍都是纵向的界面设计和实际编写(除了zxing自带的demo)
因此,需要解决问题——把图像翻转为纵向:
1.首先,我们都会想到在AndroidManifest.xml将浏览用的activity改造纵向
android:screenOrientation="portrait"
2.同时,要在相机初始化的时候将相机旋转90度
camera.setDisplayOrientation(90);
3.当然这样还不行,还要讲相机拍摄浏览图的数据固定为纵向的比例
Point screenResolutionForCamera = new Point();
screenResolution = new Point(display.getWidth(), display.getHeight());
screenResolutionForCamera.x = screenResolution.x;
screenResolutionForCamera.y = screenResolution.y;
if (screenResolution.x < screenResolution.y) {
screenResolutionForCamera.x = screenResolution.y;
screenResolutionForCamera.y = screenResolution.x;
}
parameters.setPreviewSize(screenResolutionForCamera.x, screenResolutionForCamera.y);
4.最后,当然最终拍摄结果传递给zxing解析前,还要变换一次方向
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;
data = rotatedData;
这样一来,基本上相机功能可以适应纵向屏幕了,再调整界面即可。
3.相机硬件控制冲突
在部分手机或者部分相机上,频繁操作相机的各种属性和方法有一定概率造成操作失败,抛出错误并导致程序崩溃。经过谷歌与排查,这是由于操作相机硬件太过频繁而相机硬件来不及操作导致的硬伤。因此,需要某一种机制来管理操作相机的方法。
通过网站上查询与大家的意见,有以下几种方法:
1.将操作压入队列,按一定时间频率进行操作。
2.循环尝试,失败的话或延迟一段时间再试直到成功。
3.无视用户操作或可反馈错误,直接结束掉其中一个会导致崩溃的操作。
在zxing的使用中,个大软件都遇到了一个坑,是:在相机进行自动聚焦的时候,开关闪光灯有很高的概率失败。
- UC采用的解决方案是:在进行开关闪光灯时,强制结束自动对焦,操作成功后再开启。(这个功能在我的手机lt26ii上有bug,再次开启会失败;包括我们尝试编写的程序和UC,遇到了相同的问题)
- QQ采用了与众不同的对焦策略,避免了这个问题。
- 其他app则压根没有开启闪光灯这个功能。
考虑到我们的程序的使用场景,闪光灯是有必要的,因此我们折中才用的解决方法是方法2,循环重试。代码如下:
//尝试100次来保证闪光灯设置成功
int tryCount = 0;
boolean success = false;
while (!success){
if(tryCount++ >= 100){
success = true;
}
try{
camera.setParameters(parameter);
flashstate = !flashstate;
success = true;
}catch(Exceptione){
/*//要不要挂50ms在继续呢?
try{
Thread.currentThread().sleep(50);
}catch(InterruptedExceptione1){
e1.printStackTrace();
}*/
}
}
我们设置最大尝试次数100次,然后try尝试设置,如果设置失败就继续++循环,否则就结束运行。另外有方案是在失败的情况下将程序挂起20ms或者50ms之类的再继续。然而由于操作相机必须在主线程里进行,强制挂起会影响用户体验并有程序ANR风险,所以放弃。
根据测试,平均遇到冲突的概率不超过20%,每次冲突从重试开始到最终解决平均需要300ms左右,最高能到500ms以上。虽然不是漂亮的数字,但在不会影响功能使用的情况下解决问题,也是一种可行方案。