OpenHarmony application realizes QR code scanning and recognition

This article is reproduced from " OpenHarmony Application Realizes QR Code Scanning Recognition ", author zhushangyuan_

concept introduction

QR codes can be used in a wide range of scenarios. In shopping applications, consumers can directly scan product QR codes to browse and purchase products. The picture shows the page for scanning QR codes in shopping applications.

This article takes the orange shopping sample application as an example to explain the technical points related to the development of QR codes for OpenHarmony applications.

Let's first look at a few concepts related to QR codes.

  • QR code generation

The OpenHarmony application framework provides the QRCode component , which is used to display a single QR code. This component can only be used to display QR codes, and cannot display barcodes and parsed code content.

  • QR code analysis

OpenHarmony provides a powerful three-party library @ohos/zxing, which is a library for parsing/generating 1D/2D codes. For details, please refer to @ohos/zxing .

When analyzing the QR code, there are usually two ways. Use the camera to take pictures or open the photo album to select pictures, and then analyze the appropriate image format for the QR code analysis.

An example image of the QR code scanned by the Tangerine Shopping example application :

configuration file

After understanding the concepts related to QR codes, let's take a look at the oh-package.json5 configuration file of the orange shopping sample application .

In the orange shopping sample application , the file location of the page that realizes the QR code scanning on the home page is: entry/src/main/ets/pages/ScanPage.ets . The content of the file is as follows:

import {
       
        QRCodeScanComponent } from "@ohos/scan-component"
@Entry
@Component
struct Scan {
       
       
  build() {
       
       
    Column() {
       
       
      QRCodeScanComponent()
    }
  }
}

The content is very simple, mainly the imported custom component QRCodeScanComponent, the code of this component comes from: QR code scanning sample application , we will analyze how to develop this QR code scanning application in this way later.

From this line, we can understand how the OpenHarmony application refers to the ohpm local three-party library.

"@ohos/scan-component":"file:../libs/ohos-qr-code-scan-1.0.1.har",

The oh-package.json5 configuration file fragment is as follows:

{
       
       
  "license": "ISC",
  "devDependencies": {},
  "name": "product",
  "description": "example description",
  "repository": {},
  "version": "1.0.0",
  "dependencies": {
       
       
    "@ohos/http": "file:../libs/ohos-http-1.0.0.tgz",
    "@ohos/video-component": "file:../libs/ohos-video-component-1.0.5.tgz",
    "@ohos/details-page-component": "file:../feature/detailPage",
    "@ohos/notification": "file:../libs/ohos-notification-1.0.0.tgz",
    "@ohos/scan-component": "file:../libs/ohos-qr-code-scan-1.0.1.har",
    "@ohos/updatedialog": "file:../libs/ohos-updatedialog-1.0.0.tgz",
    "@ohos/enter-animation": "file:../libs/ohos-enter-animation-1.0.1.tgz",
    "@ohos/share-component": "file:../libs/ohos-sharecomponent-1.0.1.tgz",
    "@ohos/emitter": "file:../feature/emitter",
    "@ohos/navigation-component": "file:../feature/navigationHome"
  }
}

development steps

Let's see how the QR code scanning function is developed.

Import ohpm tripartite library

Before development, we need to import the ohpm component library: @ohos/zxing. You can use the command line to import ohpm install @ohos/zxing , or configure it directly in the file entry\oh-package.json5 , as shown in the file snippet.

It can be seen that the core code of QR code scanning is stored in the Feature directory, which is an independent module for easy reuse:

“@ohos/feature-qr-code-scan”: “file:…/Feature”

File entry\oh-package.json5 fragment:

  "dependencies": {
       
       
    "@ohos/feature-qr-code-scan": "file:../Feature",
    "@ohos/zxing": "^2.0.0"
  }  

camera service

In the camera service constructor of the CameraService.ets file, a picture receiver will be created.

The image receiver can listen to the 'imageArrival' event, which is fired when the camera takes a picture. In the callback function of the listening event, realize the processing of the picture taken.

CameraService.ets file camera service constructor:

constructor(imgReceiver?: image.ImageReceiver) {
       
       
    if (imgReceiver === undefined) {
       
       
      this.imageReceiver = image.createImageReceiver(QRCodeScanConst.IMG_DEFAULT_SIZE.WIDTH,
      QRCodeScanConst.IMG_DEFAULT_SIZE.HEIGHT, image.ImageFormat.JPEG, QRCodeScanConst.MAX_IMAGE_CAPACITY)
    } else {
       
       
      this.imageReceiver = image.createImageReceiver(imgReceiver.size.width, imgReceiver.size.height,
      imgReceiver.format, imgReceiver.capacity)
    }
  }

In the CameraService.ets file to create a camera function, it mainly includes the following steps:

  • Get supported cameras

Get the CameraManager according to the context, and then get the supported cameras (cameras). If there is no supported camera then then.

If there are supported cameras, the first one in the camera list is used by default. In practical applications, for QR code scanning, the rear camera needs to be used.

  • Get camera input and output stream

First, according to the specified camera, create a camera input stream this.cameraInput .

Then, get the cameraOutputCapability parameter of the camera, and then create two output streams:

1. Preview the output stream

Create the camera preview output stream this.previewOutput, using the surfaceId from the XComponent component. Preview the output stream, corresponding to the picture preview before the camera takes a picture.

2. Photo output stream

Create a photo output stream this.photoOutput, using the receivingSurfaceId from the image receiver created above. Photo output stream for saving to Photos.

  • Configure camera sessions

It is also relatively simple to configure the camera session, just add the input stream and output stream, see the code and its comments.

The CameraService.ets file creates a camera function:

  /**
   * 创建相机
   */
  async createCamera(surfaceId: string) {
       
       
    Logger.info("createCamera start")
    // 根据context获取CameraManager
    let cameraManager = camera.getCameraManager(AppStorage.Get('context'))
    // 获取Camera对象数组
    let cameras = cameraManager.getSupportedCameras()
    // 没有相机就停止
    if (cameras.length === 0) {
       
       
      Logger.error("createCamera: cameras length is 0.")
      return
    }
    // 拿到相机列表中的第一个默认相机id, 根据id获取相机输入流
    this.cameraInput = cameraManager.createCameraInput(cameras[0])
    this.cameraInput.open()
    // 获取cameraOutputCapability参数
    let cameraOutputCapability = cameraManager.getSupportedOutputCapability(cameras[0])
    // 获取相机输出流
    this.previewOutput = cameraManager.createPreviewOutput(cameraOutputCapability.previewProfiles[0], surfaceId)
    // 获取一个可以创建相片输出流的id
    let receivingSurfaceId = await this.imageReceiver.getReceivingSurfaceId()
    // 创建相片输出流
    this.photoOutput = cameraManager.createPhotoOutput(cameraOutputCapability.photoProfiles[0], receivingSurfaceId)
    // 获取捕获会话的实例
    this.captureSession = cameraManager.createCaptureSession()
    // 开始会话配置
    this.captureSession.beginConfig()
    // 使用相机输入流---添加一个摄像头输入流
    this.captureSession.addInput(this.cameraInput)
    // 使用相机输出流---添加一个摄像头输出
     this.captureSession.addOutput(this.previewOutput)
    // 使用相片输出流---添加相机照片的输出
    this.captureSession.addOutput(this.photoOutput)
    // 结束并提交配置
    await this.captureSession.commitConfig()
    // 开始捕获会话
    await this.captureSession.start()
    Logger.info("createCamera end")
  }

In the camera function of the CameraService.ets file, specify the photo parameter settings, and then call the capture() function to complete the photo.

After taking a picture, the 'imageArrival' event of the image receiver will be triggered. The camera function is called when scanning the QR code with the camera.

The image receiver can listen to the 'imageArrival' event, which is fired when the camera takes a picture. In the callback function of the listening event, realize the processing of the picture taken.

CameraService.ets file camera function:

  takePicture() {
       
       
    let photoSetting = {
       
       
      rotation: camera.ImageRotation.ROTATION_0,
      quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
      mirror: false
    }
    this.photoOutput.capture(photoSetting)
  }

Two-dimensional code analysis implementation code

The QR code parsing class file is: QRCodeParser.ets , which supports taking pictures to recognize QR codes, and also supports selecting QR code images from the photo album for recognition.

Let's first look at how to parse the QR code image obtained from the camera. The corresponding function is: parseQRCodeImageFromCamera . This class specifies a time-random image file name and image file format, and then continues to call the function parseQRCodeImageWithNameFromCamera .

  /**
   * 解析从相机获取的二维码图片
   *
   * @param cameraService
   * @param canvasContext
   */
  parseQRCodeImageFromCamera(cameraService: CameraService,
                             imageComponentType?: image.ComponentType): void {
       
       
    Logger.info("parseQRCodeImageFromCamera start")
    let fileName = this.getRandomFileName(QRCodeScanConst.IMG_FILE_PREFIX, QRCodeScanConst.IMG_SUFFIX_JPG)
    this.parseQRCodeImageWithNameFromCamera(cameraService, fileName, imageComponentType);
    Logger.info("parseQRCodeImageFromCamera end")
  }

In the function parseQRCodeImageWithNameFromCamera , register the image receiver to listen to the 'imageArrival' event, and in the listening function, analyze and recognize the QR code image.

When the camera takes a photo of the QR code, the QR code image will be saved in the specified directory and the file URI will be returned. For the implementation of the function createPublicDirFileAsset for saving pictures , you can check the source code by yourself.

According to the returned image URI, call the function parseImageQRCode to parse the QR code. The function parseImageQRCode will be introduced later.

If the parsing fails, a pop-up window will prompt that the parsing failed. If the parsing is successful, the parsing result will be saved to AppStorage.

The QR code parsing results saved to AppStorage will be monitored by the variables of the @watch decorator. When a QR code recognition result is detected, it will be displayed on the interface, which will be introduced later.

QRCodeParser.ets file parseQRCodeImageWithNameFromCamera function code:

  /**
   * 解析从相机获取的二维码图片,指定文件名称
   *
   * @param cameraService
   * @param canvasContext
   */
  parseQRCodeImageWithNameFromCamera(cameraService: CameraService,
                                     fileDisplayName: string,
                                     imageComponentType?: image.ComponentType): void {
       
       
    Logger.info("parseQRCodeImageWithNameFromCamera...")
    cameraService.imageReceiver.on('imageArrival', async () => {
       
       
      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival start")
      // 从接收器获取下一个图像,并返回结果
      let targetImage: image.Image = await cameraService.imageReceiver.readNextImage()
      // 默认按JPEG格式处理
      let imgComponentType = imageComponentType === undefined ? image.ComponentType.JPEG : imageComponentType
      let imageComponent = await targetImage.getComponent(imgComponentType)
      // 将image的ArrayBuffer写入指定文件中,返回文件uri
      let imageUri = await this.createPublicDirFileAsset(fileDisplayName, mediaLibrary.MediaType.IMAGE,
                     mediaLibrary.DirectoryType.DIR_IMAGE, imageComponent.byteBuffer);
      // 释放已读取的image资源,以便处理下一个资源
      await targetImage.release()

      // 解析二维码
      let qrCodeParseRlt = await this.parseImageQRCode(imageUri);
      if (!qrCodeParseRlt.isSucess) {
       
       
        Logger.error("parseQRCodeImageWithNameFromCamera qrCodeParseRlt is null")
        prompt.showToast({
       
       
          message: $r('app.string.qrCodeNotRecognized')
        })
        return;
      }
      // 拼接解析结果
      AppStorage.SetOrCreate(QRCodeScanConst.QR_CODE_PARSE_RESULT, qrCodeParseRlt.decodeResult);
      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival end")
    })
  }

The QR code parsing class file is: QRCodeParser.ets , which supports taking pictures to recognize QR codes, and also supports selecting QR code images from the photo album for recognition.

Next, let's see how to parse the QR code image selected from the album.

The parameter imageSrc is the URI address of the selected picture.

The getImageSource() code can query by itself to return the width, height, and pixelMap data of the image according to the image URI. Then, write the pixel data into ArrayBuffer for use by the zxing two-dimensional code recognition program.

The functions RGBLuminanceSource, BinaryBitmap, BinaryBitmap, etc. are all zxing classes. The QR code image is parsed by calling the decode function of MultiFormatReader.

If the parsing is successful, the successful tag and parsed result will be returned.

If the parsing fails, it will be processed in the catch statement block, and the failed mark and the reason for the parsing failure will be returned.

QRCodeParser.ets file parseImageQRCode function code:

 /**
   * 解析图片二维码信息
   * @param canvasContext
   * @param imageSrc
   */
  async parseImageQRCode(imageSrc: string): Promise<DecodeResultAttribute> {
       
       
    Logger.info(`parseImageQRCode start`);
    // 获取图片的宽高
    let imageSource = await this.getImageSource(imageSrc);
    let imageWidth = imageSource.width;
    let imageHeight = imageSource.height;
    // 获取PixelMap图片数据
    let pixMapData = imageSource.pixelMap;
    let pixelBytesNumber = pixMapData.getPixelBytesNumber();
    let arrayBuffer: ArrayBuffer = new ArrayBuffer(pixelBytesNumber);
    // 读取图像像素数据,结果写入ArrayBuffer里
    await pixMapData.readPixelsToBuffer(arrayBuffer);
    let int32Array = new Int32Array(arrayBuffer);
    let luminanceSource = new RGBLuminanceSource(int32Array, imageWidth, imageHeight);
    let binaryBitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
    let mltiFormatReader = new MultiFormatReader();
    let hints = new Map();
    hints.set(DecodeHintType.POSSIBLE_FORMATS, [BarcodeFormat.QR_CODE]);
    mltiFormatReader.setHints(hints);
    try {
       
       
      // 解析二维码
      let decodeResult = mltiFormatReader.decode(binaryBitmap);
      let decodeText = decodeResult.getText();
      Logger.info(`parseImageQRCode end ${decodeText}`);
      return {
       
        isSucess: true, decodeResult: decodeText };
    } catch (err) {
       
       
      let error = `The error is ${err}`;
      Logger.info(`parseImageQRCode end`);
      return {
       
        isSucess: false, decodeResult: error };
    }
  }

Camera scans and recognizes QR codes

The QR code scanning custom component is implemented in the file QRCodeScanComponent.ets . Let's take a look at how to implement the camera to scan the QR code in this file.

The watchCameraPermission() function called by the aboutToAppear() function of the QR code scanning component is used to use the camera to scan the QR code for recognition.

In the watchCameraPermission() function, use the setInterval function to judge whether you have camera permission every 100ms. When you have camera permission, you can use the camera to scan the QR code.

When you have the camera permission, use the setInterval function to poll every 4000ms to determine whether the QR code picture is recognized, and cancel the polling if it is recognized.

If the QR code is not recognized, continue to call the function takePicture() to take pictures. After calling this function, the monitoring event 'imageArrival' of the image receiver will be triggered. For the monitoring and analysis of this event, see above.

In the file QRCodeScanComponent.ets, the code snippet for the camera to take pictures and recognize the QR code:

aboutToAppear() {
       
       
// 监听相机权限
this.watchCameraPermission()
// 设置扫描动画
this.setQRCodeScanAnimation()
// 解析二维码图片信息
this.qrCodeParser.parseQRCodeImageFromCamera(this.cameraService);
}
......
// 监听相机权限变化
watchCameraPermission() {
       
       
let interval = setInterval(() => {
       
       
  this.hasCameraPermission = AppStorage.Get(QRCodeScanConst.HAS_CAMERA_PERMISSION)
  if (this.hasCameraPermission) {
       
       
	let qrCodeScanInterval = setInterval(() => {
       
       
	  if (this.qrCodeParseResult.length > 0 || this.isQRCodeScanStopped) {
       
       
		clearInterval(qrCodeScanInterval)
	  }
	  // 拍照
	  this.cameraService.takePicture()
	}, 4000)
	clearInterval(interval)
  }
}, 100)
}

Identify album QR code pictures

The QR code scanning custom component is implemented in the file QRCodeScanComponent.ets . Let's see how to identify the QR code picture of the album in this file.

First, set this.isQRCodeScanStopped to true, which will turn off the camera to take pictures and recognize QR codes.

Then, start the photo album application through startAbilityForResult for the user to select a QR code picture.

If the image selection fails, an error will pop up.

If the picture is selected successfully, call the QR code decoding function parseImageQRCode to complete the recognition of the picture QR code.

If the QR code is recognized successfully, a pop-up window will display the QR code result.

If it is recognized, the toast will display: QR code not recognized.

In the file QRCodeScanComponent.ets, the photo album selects the QR code picture to identify the code snippet:

Image($r('app.media.scan_photo'))
  .width(30)
  .height(30)
  .id('scanPhoto')
  .onClick(async () => {
       
       
	// 打开相册获取图片
	this.isQRCodeScanStopped = true
	let context = AppStorage.Get('context') as common.UIAbilityContext
	await context.startAbilityForResult({
       
       
	  parameters: {
       
        uri: 'singleselect' },
	  bundleName: 'com.ohos.photos',
	  abilityName: 'com.ohos.photos.MainAbility',
	}).then(data => {
       
       
	  // 获取want数据
	  let want = data['want'];
	  if (want) {
       
       
		// param代表want参数中的paramters
		let param = want['parameters'];
		if (param) {
       
       
		  // 被选中的图片路径media/image/8
		  let selectedUri = param['select-item-list'];
		  setTimeout(async () => {
       
       
			if (!selectedUri) {
       
       
			  prompt.showToast({
       
       
				message: $r('app.string.queryImageFailed'),
				duration: 1000
			  })
			  return;
			}
			// 获取解析数据
			let qrCodeParseRlt = await this.qrCodeParser.parseImageQRCode(selectedUri[0]);
			if (qrCodeParseRlt.isSucess) {
       
       
			  prompt.showDialog({
       
       
				title: $r('app.string.qrcodeResult'),
				message: qrCodeParseRlt.decodeResult
			  })
			} else {
       
       
			  prompt.showToast({
       
       
				message: $r('app.string.qrCodeNotRecognized')
			  })
			}
		  }, 50)
		}
	  }
	})
  })

QR code scanning component interface

The QR code scanning custom component is implemented in the file QRCodeScanComponent.ets . Let's take a look at the page layout of the QR code scanning component.

The entire page uses Stack for stacking layout.

If there is a camera permission, an XComponent component will be used to display the preview output stream of the camera. The camera will be created in the onLoad function of the XComponent component, and the camera will be released in the onDestroy function.

Image($r('app.media.scan_border')) The picture is the QR code scanning box, which guides the user to put the QR code in the box for scanning and recognition.

Divider is a dividing line that enables animation effects. During the process of recognizing the QR code, the dividing line moves from top to bottom in the QR code recognition frame. The scanning animation implementation code is as follows:

  // 扫描扫描动画
  setQRCodeScanAnimation() {
       
       
    setInterval(() => {
       
       
      animateTo({
       
       
        duration: 1000, // 动画时间
        tempo: 0.5, // 动画速率
        curve: Curve.EaseInOut,
        delay: 200, // 动画延迟时间
        iterations: -1, // 动画是否重复播放
        playMode: PlayMode.Normal,
      }, () => {
       
       
        this.animationOrdinate = 390 // 扫描动画结束Y坐标
      })
    }, 2000)
  }

Text($r('app.string.putTheQRCodeToScan')) guides the user to put the QR code in the box for scanning and recognition.

Image($r('app.media.scan_back')) returns to exit the application.

Image($r('app.media.scan_photo')) selects a QR code image from the photo album for recognition.

build() {
       
       
Column() {
       
       
  Stack() {
       
       
	if (this.hasCameraPermission) {
       
       
	  XComponent({
       
       
		id: 'componentId',
		type: 'surface',
		controller: this.xComponentController
	  })
		.onLoad(() => {
       
       
		  // 适配可能需要获取设备信息
		  this.xComponentController.setXComponentSurfaceSize({
       
       
			surfaceWidth: QRCodeScanConst.IMG_DEFAULT_SIZE.WIDTH,
			surfaceHeight: QRCodeScanConst.IMG_DEFAULT_SIZE.HEIGHT
		  })
		  this.surFaceId = this.xComponentController.getXComponentSurfaceId()
		  this.cameraService.createCamera(this.surFaceId)
		})
		.onDestroy(() => {
       
       
		  this.cameraService.releaseCamera()
		})
		.height('100%')
		.width('100%')
	}
	Column() {
       
       
	  Column() {
       
       
		Image($r('app.media.scan_border'))
		......
		Divider()
		  .strokeWidth(1)
		  .height(4)
		  .width('100%')
		  .color(Color.White)
		  .width('100%')
		  .position({
       
        x: 0, y: 0 })
		  .translate({
       
        x: 0, y: this.animationOrdinate })
	  }
	......
	  Text($r('app.string.putTheQRCodeToScan'))
	......
	}
	......
	Row() {
       
       
	  Image($r('app.media.scan_back'))
	  ......
	  Row({ space: 16 }) {
       
       
		Image($r('app.media.scan_photo'))
		......
}

Run the test effect

You can download the orange shopping sample application code, use DevEco Studio to compile and build, and use the Simulator or real device for running experience. You can experience using the camera to recognize the QR code pictures, and you can also try to recognize the QR code pictures in the album.

git init
git config core.sparsecheckout true
echo code/Solutions/Shopping/OrangeShopping/ > .git/info/sparse-checkout
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
git pull origin master

Precautions

The current QR code example application recognizes the QR code of the photo album. After the recognition result pops up, the program will crash and the bill of lading has been tracked. Sample program to be improved.

The function of using the camera function to directly capture the QR code has not been successfully run and needs to be further optimized.

References

Tangerine shopping sample application

QR code scanning example application

@ohos/zxing

QRCode component

Camera Development Overview

Image Development Overview

XComponent

 

Guess you like

Origin blog.csdn.net/OpenHarmony_dev/article/details/132451986