鴻蒙開源第三方組件—Zbar_ohos條形碼閱讀器
前言
基于安卓平臺的條形碼閱讀器控件ZBar(https://github.com/ZBar/ZBar),實現了鴻蒙化遷移和重構,代碼已經開源到(https://gitee.com/isrc_ohos/ZBar),歡迎各位下載使用并提出寶貴意見!
背景
Zbar-ohos是基于鴻蒙系統的條形碼閱讀器,支持EAN-13 / UPC-A、UPC-E、EAN-8、Code 128、CODE39、Codabar和QR碼的識別,目前已經廣泛應用于掃碼登記、掃碼觀影、掃碼登錄等多個領域。
組件效果展示
1、添加權限
打開軟件后,會顯示如圖1所示的添加攝像頭權限提示。點擊“始終允許”按鈕,并重啟該軟件(刷新UI界面),即可掃描條形碼。
圖1 掃描二維碼界面
2、掃描效果
掃描界面包含兩個部分:對準器和狀態欄。對準器顯示攝像頭拍攝的畫面,條形碼需要置于此范圍內,才可以被掃描。狀態欄用于顯示當前的掃描狀態或掃描結果。
(1)一維條形碼掃描
一維條形碼一般是在水平方向上表達信息,而在垂直方向不表達任何信息。為了方便對準器的讀取,其高度通常是固定的。
ZBar組件掃描一維條形碼的效果圖2所示。攝像頭掃到條形碼時,下方狀態欄的顯示內容由“掃描中”更新為條形碼的掃描結果。掃到下一個條碼時,狀態欄的掃描結果也實時更新。
圖2 條形碼掃描結果
(2)二維條形碼掃描
二維條形碼在水平和垂直方向上都表示信息,信息容量大,結構通常為方形結構,保密級別高,可直接顯示英文、中文、數字、符號、圖型等。
ZBar組件掃描二維條形碼的效果圖3所示。掃描過程與上述一維條形碼一致,狀態欄會顯示二維條形碼的掃描結果。
圖3 二維碼掃描結果
Sample解析
Sample部分首先創建相機設備并合理配置,然后將相機獲得的原始數據傳遞給Library掃描處理,最后獲取掃描結果并顯示在屏幕上。下面對Sample部分的代碼進行具體解釋:
1、生成Camera類對象
CameraKit類可以提供使用相機功能的條目,CameraStateCallbackImpl 類是相機創建和相機運行時的回調。此處通過CameraKit類來生成Camera對象,不同尋常的是,CameraKit類并沒有將Camera對象直接返回,而是需要從CameraStateCallbackImpl 回調中獲取。
- private void openCamera(){
- // 獲取 CameraKit 對象
- cameraKit = CameraKit.getInstance(this);
- if (cameraKit == null) {
- return;
- }
- try {
- // 獲取當前設備的邏輯相機列表cameraIds
- String[] cameraIds = cameraKit.getCameraIds();
- if (cameraIds.length <= 0) {
- System.out.println("cameraIds size is 0");
- }
- // 用于相機創建和相機運行的回調
- CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
- if(cameraStateCallback ==null) {
- System.out.println("cameraStateCallback is null");
- }
- // 創建用于運行相機的線程
- EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb"));
- if(eventHandler ==null) {
- System.out.println("eventHandler is null");
- }
- // 創建相機
- cameraKit.createCamera(cameraIds[0], cameraStateCallback, eventHandler);
- } catch (IllegalStateException e) {
- System.out.println("getCameraIds fail");
- }
- }
2、綁定相機的Surface
Surface用于實現相機的預覽、拍照、錄像等功能。此處為相機添加:previewSurface和 dataSurface。前者用來展示相機拍攝到的界面;后者用來讀取并處理相機拍攝到的數據信息。
- private final class CameraStateCallbackImpl extends CameraStateCallback {
- // 相機創建和相機運行時的回調
- @Override
- public void onCreated(Camera camera) {
- mcamera = camera;//獲取到Camera 對象
- CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
- if (cameraConfigBuilder == null) {
- System.out.println("onCreated cameraConfigBuilder is null");
- return;
- }
- // 配置預覽的 Surface
- cameraConfigBuilder.addSurface(previewSurface);
- // 配置處理數據的Surface
- dataSurface = imageReceiver.getRecevingSurface();
- cameraConfigBuilder.addSurface(dataSurface);
- try {
- // 相機設備配置
- camera.configure(cameraConfigBuilder.build());
- } catch (IllegalArgumentException e) {
- System.out.println("Argument Exception");
- } catch (IllegalStateException e) {
- System.out.println("State Exception");
- }
- }
- }
3、開啟循環幀捕獲
用戶一般在畫面生成后,才執行拍照或者其他操作。開啟循環幀捕獲后,dataSurface可以獲得來自相機的數據。
- @Override
- public void onConfigured(Camera camera) {
- // 獲取預覽配置模板
- FrameConfig.Builder frameConfigBuilder = mcamera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);
- // 配置預覽 Surface
- frameConfigBuilder.addSurface(previewSurface);
- // 配置拍照的 Surface
- frameConfigBuilder.addSurface(dataSurface);
- try {
- // 啟動循環幀捕獲
- int triggerId = mcamera.triggerLoopingCapture(frameConfigBuilder.build());
- } catch (IllegalArgumentException e) {
- System.out.println("Argument Exception");
- } catch (IllegalStateException e) {
- System.out.println("State Exception");
- }
- }
4、掃描相機數據
dataSurface中的數據為相機原始數據,其格式為YUV420,需要將其封裝為Image類的數據才能執行傳入ImageScanner類進行正式掃描。
- // 相機原始數據封裝為Image數據
- Image barcode = new Image(mImage.getImageSize().width,mImage.getImageSize().height, "Y800");
- barcode.setData(YUV_DATA);
- //Image數據掃描
- int result = scanner.scanImage(barcode);
5、顯示預覽數據的掃描結果
由于對準器中的條形碼可能不止一個,ImageScanner類的掃描結果可能也有多個,因此最后返回的掃描結果是SymbolSet類型,此數據類型是可以盛納多個Symbol數據的容器,每個Symbol數據代表一個條形碼的掃描結果。
- //創建可以盛納多個Symbol數據的容器SymbolSet
- SymbolSet syms = scanner.getResults();
- //遍歷SymbolSet 中的每個元素
- for (Symbol sym : syms) {
- handler.postTask(new Runnable() {
- @Override
- public void run() {
- scanText.setText("掃描結果:" + sym.getData());//獲取Symbol中的信息
- scanText.invalidate();
- }
- });
Library解析
Library部分主要是對dataSurface的數據進行掃描,此處主要涉及兩個功能:(1)相機原始數據封裝為Image數據;(2)對Image數據進行掃描。由于這部分主要由C語言實現,所以此處只解析大概原理,展示主要接口,不再進行底層代碼的展示。
(1)相機原始數據封裝為Image數據
Image支持多種數據格式,包括常見的YUV以及RGB數據。此處需要的Image數據是“Y800”類型或者“GRAY”類型,即條形碼的掃描數據僅需要圖像的灰度數據。
- public native void setData(byte[] data);
(2)對Image數據進行掃描
使用scanImage()方法對傳入的Image數據進行掃描。該過程首先對傳入的圖像進行配置校驗,然后以一個像素點為增量逐行掃描,掃描路徑為Z字型,并且完成對掃描數據的濾波,求取邊緣梯度,梯度閾值自適應,確定邊緣等操作,最后將掃描數據轉化成明暗寬度流。 通過明暗寬度流的變化格律可以知道當前正在被掃描的條形碼的種類,然后依據固定的解碼方法進行解碼,便可得到條形碼信息。
- public native int scanImage(Image image);
項目貢獻人
陳叢笑 鄭森文 朱偉 陳美汝 張馨心