Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use ZXing with android CameraX to decode Barcode and QR Codes

How can I integrate ZXing library in order to use it with new Android Jetpack CameraX?

I know that I've to build an ImageAnalyzer and inside it I've to use ZXing to decode QR Codes and Barcodes

like image 978
shadowsheep Avatar asked Sep 26 '19 09:09

shadowsheep


People also ask

How do I scan a barcode with Zxing Android?

On click of button_scan_qr_code , CaptureActivity will start scanning using default camera. Once it scans any QR code, it sends back the result to onActivityResult the MainActivity . ZXing also provides online QR Code Generator. Enter the required fields, generate and scan it to get the results.

How does Google use ZXing?

Google uses ZXing by web search to obtain millions of barcodes on the web indexable. It also creates the foundation of Android’s Barcode Scanner app and is combined into Google Product and Book Search.

Which barcodes are supported by ZXing?

It has support for the 1D product, 1D industrial, and 2D barcodes. Google uses ZXing by web search to obtain millions of barcodes on the web indexable. It also creates the foundation of Android’s Barcode Scanner app and is combined into Google Product and Book Search.

How to use ZXing library in Android Studio?

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language. Note: Choose API 24 and onwards as Minimum SDK . In order to use the Zxing library in our application we need to add it’s dependency in our application’s gradle file.

Do you need a QR code scanner for your smartphone?

Unlike barcodes, the QR codes can be scanned accurately with a smartphone by giving the camera permission to scan. And it doesn’t require any specialized hardware like the barcodes. However, in some smartphones, you may need a QR code scanner for accessing QR codes.


1 Answers

Here below the ImageAnalyzer I've built up to do that:

class ZxingQrCodeAnalyzer(
    private val onQrCodesDetected: (qrCode: Result) -> Unit
) : ImageAnalysis.Analyzer {

  companion object {
    val reader = MultiFormatReader()
  }

  /*
      https://developer.android.com/training/camerax/configuration

      Default resolution: The default target resolution setting is 640x480.

      Adjusting both target resolution and corresponding aspect ratio will result
      in a best-supported resolution under 1080p (max analysis resolution).
  */
  override fun analyze(imageProxy: ImageProxy, rotationDegrees: Int) {
    // okay - manage rotation, not needed for QRCode decoding [-;
    // okay - manage it for barcode scanning instead!!!
    try {
      imageProxy.image?.let {
        // ImageProxy uses an ImageReader under the hood:
        // https://developer.android.com/reference/androidx/camera/core/ImageProxy.html
        // That has a default format of YUV_420_888 if not changed.
        // https://developer.android.com/reference/android/graphics/ImageFormat.html#YUV_420_888
        // https://developer.android.com/reference/android/media/ImageReader.html
        if ((it.format == ImageFormat.YUV_420_888
            || it.format == ImageFormat.YUV_422_888
            || it.format == ImageFormat.YUV_444_888)
            && it.planes.size == 3) {
          val buffer = it.planes[0].buffer // We get the luminance plane only, since we
          // want to binarize it and we don't wanna take color into consideration.
          val bytes = ByteArray(buffer.capacity())
          buffer.get(bytes)
          // Create a LuminanceSource.
          val rotatedImage = RotatedImage(bytes, imageProxy.width, imageProxy.height)

          rotateImageArray(rotatedImage, rotationDegrees)

          val source = PlanarYUVLuminanceSource(rotatedImage.byteArray,
              rotatedImage.width,
              rotatedImage.height,
              0,
              0,
              rotatedImage.width,
              rotatedImage.height,
              false)

          // Create a Binarizer
          val binarizer = HybridBinarizer(source)
          // Create a BinaryBitmap.
          val binaryBitmap = BinaryBitmap(binarizer)
          // Try decoding...
          val result: Result
          try {
            result = reader.decode(binaryBitmap)
            onQrCodesDetected(result)
          } catch (e: NotFoundException) {
            e.printStackTrace()
          }
        } else {
          // Manage other image formats
          // TODO - https://developer.android.com/reference/android/media/Image.html
        }
      }
    } catch (ise: IllegalStateException) {
      ise.printStackTrace()
    }
  }

  // 90, 180. 270 rotation
  private fun rotateImageArray(imageToRotate: RotatedImage, rotationDegrees: Int) {
    if (rotationDegrees == 0) return // no rotation
    if (rotationDegrees % 90 != 0) return // only 90 degree times rotations

    val width = imageToRotate.width
    val height = imageToRotate.height

    val rotatedData = ByteArray(imageToRotate.byteArray.size)
    for (y in 0 until height) { // we scan the array by rows
      for (x in 0 until width) {
        when (rotationDegrees) {
          90 -> rotatedData[x * height + height - y - 1] =
              imageToRotate.byteArray[x + y * width] // Fill from top-right toward left (CW)
          180 -> rotatedData[width * (height - y - 1) + width - x - 1] =
              imageToRotate.byteArray[x + y * width] // Fill from bottom-right toward up (CW)
          270 -> rotatedData[y + x * height] =
                            imageToRotate.byteArray[y * width + width - x - 1] // The opposite (CCW) of 90 degrees
        }
      }
    }

    imageToRotate.byteArray = rotatedData

    if (rotationDegrees != 180) {
      imageToRotate.height = width
      imageToRotate.width = height
    }
  }
}

private data class RotatedImage(var byteArray: ByteArray, var width: Int, var height: Int)
like image 186
shadowsheep Avatar answered Sep 25 '22 20:09

shadowsheep