This is the 11th day of my participation in Gwen Challenge

QR QR code

QR Code (English: Quick Response Code; Quick response matrix graph code is a kind of TWO-DIMENSIONAL code, invented by DENSO WAVE in Japan in 1994. QR comes from Quick Response, or Quick Response, because the inventor wanted the code to decode its content quickly. QR codes use four standardized encoding modes (numbers, alphanumeric, bytes (binary), and Shift_JIS) to store data. QR code is commonly seen in Japan, Japan’s most common two-dimensional space bar code, widely used in the world’s mobile phone reading operation. QR codes have faster reading and greater storage capacity than ordinary one-dimensional bar codes, and there is no need for linear alignment of scanners like one-dimensional bar codes. Therefore, its application scope has been extended to include product tracking, item identification, document management, inventory marketing and so on. 【 Wikipedia 】

QR code format

QR codes are square and usually black and white. In three corners, printed with smaller, like “hui” square pattern. These three help the decoding software positioning patterns, users do not need to align, no matter at any Angle scanning, data can still be correctly read. The Japanese standard JIS X 0510 was released in January 1999, and the corresponding ISO international standard ISO/IEC18004 was approved in June 2000. According to Denso Wave’s website, THE QR code is an open standard, the QR code specifications are public, although the patent rights held by Denso Wave, but will not be run. In addition to the standard QR code, there is also a format called “micro QR code”, is a reduced version of the QR code standard, mainly designed for applications that cannot handle larger scans. Micro QR codes also have a variety of standards, can store up to 35 characters. 【 Wikipedia 】

QR code structure

QR code maximum feature for its left, right, left three large as “back” word between black and white concentric square pattern, for QR code identification positioning mark, loss of one will affect identification. Version 1 has no correction mark. Version 2 is at the lower right, and its center point is at the intersection of the outer border of the marker at the lower left and upper right. Version 10 began to appear in the lower right in the form of each isometric point correction to the outer border of the lower left and upper right position mark attachment, upper left and lower left position mark outside the border of the attachment, the outer border of the upper left and upper right position mark of attachment between, the quadrilateral online isometric point of edge connected line, 10 isometric version 1, 25 for the three versions, 40 for five versions. 【 Wikipedia 】

API

QRCodeDetector class structure

Detection of QR code

public boolean detect(Mat img, Mat points)
Copy the code
  • Parameter 1: IMG: gray scale image or color (BGR) image of QR code to be detected.
  • Parameter 2: points, the set of 4 vertex coordinates of the quadrilateral of the smallest region of the DETECTED QR code.
  • Return value: Boolean type, true, representing QR QR code detected; False: indicates that the QR code is not detected.
public boolean detectMulti(Mat img, Mat points)
Copy the code
  • Parameter 1: IMG: gray scale image or color (BGR) image of QR code to be detected.
  • Parameter 2: points, multiple detection results QR code the minimum region quadrilateral of the four vertex coordinates set.
  • Return value: Boolean type, true, representing QR QR code detected; False: indicates that the QR code is not detected.

Identify QR code

public String decode(Mat img, Mat points, Mat straight_qrcode) 
Copy the code
  • Parameter 1: IMG, gray image or color (BGR) image containing QR code.
  • Parameter 2: points: indicates the points value obtained by the detect method. The amount of data cannot be empty.
  • Parameter 3: Straight_qrcode, corrected and binarized QR CODE. [Optional parameters]
  • Return value: A string, or an empty string if decoding fails.
public boolean decodeMulti(Mat img, Mat points, List<String> decoded_info, List<Mat> straight_qrcode)
Copy the code
  • Parameter 1: IMG, gray image or color (BGR) image containing QR code.
  • Parameter 2: points: indicates the points value obtained by the detect method. The amount of data cannot be empty.
  • Parameter three: decoDED_info, multiple two-dimensional code decoding information.
  • Parameter 4: Straight_qrcode, the set of results for all detected QR code corrections and binarization. [Optional parameters]
  • Return value: Boolean, true, decoding success, otherwise decoding failure.

Detect and identify QR codes

public String detectAndDecode(Mat img, Mat points, Mat straight_qrcode)
Copy the code
  • Parameter 1: IMG, gray image or color (BGR) image containing QR code.
  • Parameter 2: points, 4 vertex coordinates of the quadrilateral of the smallest region of the DETECTED QR code.
  • Parameter 3: Straight_qrcode, corrected and binarized QR CODE. [Optional parameters]
  • Return value: A string, or an empty string if decoding fails.
public boolean detectAndDecodeMulti(Mat img, List<String> decoded_info, Mat points, List<Mat> straight_qrcode)
Copy the code
  • Parameter 1: IMG, gray image or color (BGR) image containing QR code.
  • Parameter two: decoDED_info, multiple two-dimensional code decoding information.
  • Parameter 3: points, the coordinate set of 4 vertices of the minimum region quadrilateral of multiple QR codes detected. [Optional parameters]
  • Parameter 4: Straight_qrcode, the set of results for all detected QR code corrections and binarization. [Optional parameters]
  • Return value: A string, or an empty string if decoding fails.

operation

/** * QR code detection * author: yidong * 2020/10/27 */
class QRDetectActivity : AppCompatActivity() {

    private lateinit var mBinding: ActivityQrDetectBinding
    private lateinit var mQRCodeDetector: QRCodeDetector

    private var mPhotoSavePath = ""
    private lateinit var mUri: Uri
    private lateinit var mSource: Mat
    private lateinit var mGray: Mat
    private lateinit var mOperationSheet: BottomSheetDialog
    private lateinit var mSheetBinding: LayoutQrDetectOpBinding

    private lateinit var mPhotoSheet: BottomSheetDialog
    private lateinit var mPhotoOpBinding: LayoutPhotoOpBinding


    // Request camera permission
    private val requestCameraPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) {
            if (it) {
                mPhotoSavePath =
                    cacheDir.path + File.separator + "${System.currentTimeMillis()}.png"
                mUri = MediaStoreUtils.getIntentUri(this, File(mPhotoSavePath))
                requestCamera.launch(mUri)
            } else {
                Toast.makeText(applicationContext, "No camera permission", Toast.LENGTH_SHORT).show()
            }
        }

    // Request external storage permission
    private val requestStoragePermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) {
            if (it) {
                pickImage.launch("image/*")}else {
                Toast.makeText(applicationContext, "No storage permission", Toast.LENGTH_SHORT).show()
            }
        }


    private val requestCamera = registerForActivityResult(ActivityResultContracts.TakePicture()) {
        if (it) {
            val bgr = Imgcodecs.imread(mPhotoSavePath, Imgcodecs.IMREAD_COLOR)
            if (bgr.empty()) {
                Toast.makeText(applicationContext, "Failed to read photo result", Toast.LENGTH_SHORT).show()
                return@registerForActivityResult
            } else {
                Imgproc.cvtColor(bgr, mSource, Imgproc.COLOR_BGR2RGB)
                Imgproc.cvtColor(bgr, mGray, Imgproc.COLOR_BGR2GRAY)
                mBinding.ivLena.showMat(mSource)
            }
        } else {
            Toast.makeText(applicationContext, "Failed to take photo", Toast.LENGTH_SHORT).show()
        }
    }

    private val pickImage = registerForActivityResult(ActivityResultContracts.GetContent()) {
        if(it ! =null) {
            val filePath = MediaStoreUtils.getMediaPath(this, it)
            if (filePath.isNullOrEmpty()) {
                Toast.makeText(applicationContext, "Failed to read picture", Toast.LENGTH_SHORT).show()
                return@registerForActivityResult
            }
            val bgr = Imgcodecs.imread(filePath, Imgcodecs.IMREAD_COLOR)
            if (bgr.empty()) {
                Toast.makeText(applicationContext, "Failed to read picture", Toast.LENGTH_SHORT).show()
                return@registerForActivityResult
            } else {
                Imgproc.cvtColor(bgr, mSource, Imgproc.COLOR_BGR2RGB)
                Imgproc.cvtColor(bgr, mGray, Imgproc.COLOR_BGR2GRAY)
                mBinding.ivLena.showMat(mSource)
            }
        } else {
            Toast.makeText(applicationContext, "Image selection failed", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_qr_detect)
        mQRCodeDetector = QRCodeDetector()
        mSource = Mat()
        mGray = Mat()
        val bgr = Utils.loadResource(this, R.drawable.qrcode)
        Imgproc.cvtColor(bgr, mSource, Imgproc.COLOR_BGR2RGB)
        Imgproc.cvtColor(bgr, mGray, Imgproc.COLOR_BGR2GRAY)
        mBinding.ivLena.showMat(mSource)
        createDialog()
    }

    private fun createDialog(a) {
        mOperationSheet = BottomSheetDialog(this)
        mSheetBinding = LayoutQrDetectOpBinding.inflate(layoutInflater, null.false)
        mOperationSheet.setContentView(mSheetBinding.root)
        mSheetBinding.tvDetect.setOnClickListener {
            mOperationSheet.dismiss()
            doDetect()
        }
        mSheetBinding.tvDecode.setOnClickListener {
            mOperationSheet.dismiss()
            doDecode()
        }

        mPhotoSheet = BottomSheetDialog(this)
        mPhotoOpBinding = LayoutPhotoOpBinding.inflate(layoutInflater, null.false)
        mPhotoSheet.setContentView(mPhotoOpBinding.root)
        mPhotoOpBinding.tvCamera.setOnClickListener {
            mPhotoSheet.dismiss()
            requestCameraPermission.launch(
                Manifest.permission.CAMERA
            )
        }
        mPhotoOpBinding.tvPhoto.setOnClickListener {
            mPhotoSheet.dismiss()
            requestStoragePermission.launch(
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            )

        }
    }

    private fun doDetect(a) {
        val points = Mat()
        val isHasQr = mQRCodeDetector.detect(mSource, points)
        if (isHasQr) {
            val pointArr = FloatArray(8)
            points.get(0.0, pointArr)
            Log.d(App.TAG, pointArr.toList().toString())
            val tmp = mSource.clone()
            for (i in pointArr.indices step 2) {
                val start = Point(pointArr[i % 8].toDouble(), pointArr[(i + 1) % 8].toDouble())
                val end = Point(pointArr[(i + 2) % 8].toDouble(), pointArr[(i + 3) % 8].toDouble())
                Imgproc.line(tmp, start, end, Scalar(255.0.0.0.0.0), 8, Imgproc.LINE_8)
            }
            mBinding.ivResult.showMat(tmp)
            tmp.release()
        }
    }

    private fun doDecode(a) {
        val points = Mat()
        val isHasQr = mQRCodeDetector.detect(mGray, points)
        if (isHasQr) {
            val result = mQRCodeDetector.decode(mGray, points)
            if (result.isEmpty()) {
                Toast.makeText(applicationContext, "Undecoded.", Toast.LENGTH_SHORT).show()
            } else {
                Snackbar.make(mBinding.root, "Decoding result:$result".3000).show()
            }
            Log.d(App.TAG, result)
        } else {
            Toast.makeText(applicationContext, "QRCode not detected", Toast.LENGTH_SHORT).show()
        }
    }

    private fun selectMedia(a) {
        if (this::mPhotoSheet.isInitialized) {
            mPhotoSheet.show()
        }
    }

    private fun selectOps(a) {
        if (this::mOperationSheet.isInitialized) {
            mOperationSheet.show()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?).: Boolean {
        menuInflater.inflate(R.menu.menu_qr_detect, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.menu_pick_photo -> selectMedia()
            R.id.menu_qr_ops -> selectOps()
        }
        return true
    }

    override fun onDestroy(a) {
        mSource.release()
        mGray.release()
        super.onDestroy()
    }
}
Copy the code

The results of

The source code

Github.com/onlyloveyd/…