Zxing scan code TWO-DIMENSIONAL code source code optimization

background

The company has a long press to identify the picture two-dimensional code function, but there are the following problems based on native Zxing recognition

There is a problem

1. The qr code area of the picture is clear, but the area occupied is too small to be recognizedLike this one,Dingpin can not be identified, wechat can be identified


2. Image qr code can not be recognized when there are too many noise pointsLike this one,Online open source library can not be identified, Pin pin wechat can be identified

The solution

Don’t upload the whole bitmap, just upload the screen shot of the view

This method can solve the problem that the TWO-DIMENSIONAL code is too small to recognize, but there is a problem that if the user does not slide to the two-dimensional code area is also unable to recognize. This method was tried before the second problem, but abandoned after the second problem.

Source optimization + pass in a bitmap image with the same width as the screen height in proportion to the original bitmap

After trying one round of optimization at the application layer without success, I had to start with the source code. So check the source code of Zxing. As for why we need to pass in the same proportion of pictures, it is because the two-dimensional code generated by compatible with small screen phones can not be recognized to make the pictures more clearly visible in a certain size.

The scan area is too small

  • Check the source code, it is found that the bitmap passed in by the user will go to the imageFinderPatternFinder
Public class FinderPatternFinder {………… final core.qrcode.detector.FinderPatternInfo find(Map<DecodeHintType, ?> hints) throws NotFoundException { boolean tryHarder = hints ! = null && hints.containsKey(DecodeHintType.TRY_HARDER); int maxI = image.getHeight(); int maxJ = image.getWidth(); // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, Int iSkip = (3 * maxI)/(4 * MAX_MODULES); look for all // QR versions regardless of how dense they are. if ( iSkip < MIN_SKIP || tryHarder ) { iSkip = MIN_SKIP; }.....................Copy the code

` // Let’s assume that the maximum version QR Code we support takes up 1/4 the height of the

    // image, and then account for the center being 3 modules in size. This gives the smallest
    // number of pixels the center could be, so skip this often. When trying harder, look for all`
Copy the code

The translation is

// Assume that the maximum version of the QR code we support takes up 1/4 of the height. // The image then shows that the center is the size of 3 modules. This is the minimum number of pixels the // center can be, so often skip this. As you push harder, look for all //QR versions, no matter how dense they are.Copy the code

Int iSkip = (3 * maxI)/(4 * MAX_MODULES); Int iSkip = (3 * maxI)/(8 * MAX_MODULES); . The magic find identifies the little picture.

Excessive noise problem solved

This problem can not be so lucky, really to clarify the whole source process. After checking, it is found that the modified class is still the same.

/** * @return the 3 best {@link core.qrcode.detector.FinderPattern}s from our list of candidates. The "best" are * those  that have been detected at least {@link #CENTER_QUORUM} times, and whose module * size differs from the average among those patterns the least * @throws NotFoundException if 3 such finder patterns do not exist */ private core.qrcode.detector.FinderPattern[] selectBestPatterns() throws NotFoundException { int startSize = possibleCenters.size(); if ( startSize < 3 ) { // Couldn't find enough finder patterns throw NotFoundException.getNotFoundInstance(); } // Filter outlier possibilities whose module size is too different if ( startSize > 3 ) { // But we can only afford to Do so if we have at least 4 possibilities to choose from double totalModuleSize = 0.0; Double square = 0.0; for ( core.qrcode.detector.FinderPattern center : possibleCenters ) { float size = center.getEstimatedModuleSize(); totalModuleSize += size; square += size * size; } double average = totalModuleSize / startSize; float stdDev = ( float ) Math.sqrt(square / startSize - average * average); Collections.sort(possibleCenters, new FurthestFromAverageComparator(( float ) average)); Max (0.2f * (float) average, stdDev); float limit = math. Max (0.2f * (float) average, stdDev); for ( int i = 0 ; i < possibleCenters.size() && possibleCenters.size() > 3 ; i++ ) { core.qrcode.detector.FinderPattern pattern = possibleCenters.get(i); if ( Math.abs(pattern.getEstimatedModuleSize() - average) > limit ) { possibleCenters.remove(i); i--; } } } if ( possibleCenters.size() > 3 ) { // Throw away all but those first size candidate points we found. float TotalModuleSize = 0.0 f; for ( core.qrcode.detector.FinderPattern possibleCenter : possibleCenters ) { totalModuleSize += possibleCenter.getEstimatedModuleSize(); } float average = totalModuleSize / possibleCenters.size(); Collections.sort(possibleCenters, new CenterComparator(average)); possibleCenters.subList(3, possibleCenters.size()).clear(); / / take more after the first three key} return new core. The qrcode. The detector. FinderPattern [] {possibleCenters. Get (0), possibleCenters. Get (1), possibleCenters.get(2) }; }Copy the code

@ returned from the candidate list three best {@ link core. Qrcode. Detector. FinderPattern} s. The “best” are those modules that have been detected at least {@link#CENTER_QUORUM} times and whose modules

In these patterns, the size of the smallest deviation from the average if there are no three search patterns, @ throws

All key points go to this place, too much will be filtered according to zxing’s own methods, too little will throw an exception. As for what is the key point, in the image recognition, it is determined according to the proportion of the black and white interval at the top of the TWO-DIMENSIONAL code, so there may be errors.

Back to the problem, identify the image with too much noise at the beginning of the article, and identify the following coordinates will be returned

PointF = new PointF(187.5f, 391.5f); PointF pointF1 = new PointF(693.5f,391.5f); PointF pointF2 = new PointF(655.0f,606.0f); PointF pointF3 = new PointF(325.0f,859.0f); PointF pointF4 = new PointF(187.5f,897.5f);Copy the code

There are 5 points here. Look back at the source code

possibleCenters.subList(3, possibleCenters.size()).clear(); // take the first three key points after comparisonCopy the code

Here zxing help us come out is the first three points, obviously this is not right, actually according to our naked eye observation. There must be two points where the X-axis is close to the same, and two points where the Y-axis is close to the same. So the points that you filter out are 1, 2, 5

So I did a screening myself

//possibleCenters.subList(3, possibleCenters.size()).clear(); // Set the first three key CustomSort(possibleCenters. Size ())Copy the code
private void CustomSort(int startSize) { centerPos.clear(); if ( startSize ! = 0 ) { possibleCenterCopy.clear(); int size = possibleCenters.size(); boolean isNotFindX = true; boolean isNotFindY = true; for ( int i = 0 ; i < size ; i++ ) { for ( int i1 = i + 1 ; i1 < size ; i1++ ) { if ( isNotFindX && isQrCodeRate(possibleCenters.get(i).getX(), possibleCenters.get(i1).getX()) ) { centerPos.add(i); centerPos.add(i1); isNotFindX = false; } if ( isNotFindY && isQrCodeRate(possibleCenters.get(i).getY(), possibleCenters.get(i1).getY()) ) { centerPos.add(i); centerPos.add(i1); isNotFindY = false; } } } // Log.i("---","centerPos====>"+centerPos.size()); for ( Integer center : centerPos ) { possibleCenterCopy.add(possibleCenters.get(center)); // If (possiblecentercopy.size () < 3) {possibleCenters. SubList (3, possibleCenters. Size ()).clear(); } else {possibleCenters. Clear (); // If there is too much noise. Take more after the first three key points of the if (possibleCenterCopy. The size () > 3) {possibleCenterCopy. SubList (3, possibleCenters. The size ()). The clear (); } possibleCenters.addAll(possibleCenterCopy); }}}Copy the code

Through the test, it is found that sometimes there is some error between the two x’s, but theoretically it is still the key point. So the following filters were done

Private Boolean isQrCodeRate(float x, float x1) {float errorSize = 0.5f; return Math.abs(x - x1) <= errorSize; }Copy the code

At this point, the two images above are recognizable.

Scan code to enlarge camera automatically

In fact, wechat, Alipay and other scanning codes all have a function of automatic camera amplification, and there are some applications of this function on the Internet, but it is not feasible, the link is as follows.

  • Combined with ZXing to achieve similar wechat scan TWO-DIMENSIONAL code zoom camera

] (blog.csdn.net/u010705554/…).

Because the callback in the application layer is to recognize the QR code callback, so it is meaningless to zoom in after the QR code recognition. So to do that wechat can edge detection zoom camera needs to be modified source code is selectBestPatterns this method. I haven’t done this part yet, but it can be done according to the existing conditions. For example, the strategy I’m thinking of is:

Zoom in when two vertical or horizontal points are detected, if more than five times the camera does not return to its original size
//TODO key checks key code. You can add your own policy to automatically zoom in and out the camera // if (startSize! = 0 ) { // for ( int i = 0 ; i < startSize ; i++ ) { // Log.i("----", "Keypoint detection key codes ====" + I + "-----" + possibleCenters.get(I).tostring () // + possibleCenters.get(I).tostring () + possibleCenters.get(i).getEstimatedModuleSize() // ); / / / /} / / Log i. (" - ", "key to detect the key code -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); / /}Copy the code

I believe this requirement can be met simply by doing a callback here to the scan activity.

insufficient

The filtering efficiency of the two for loops did not reach the optimal performance, here also hope that the big guys can guide optimization. Here is the unit test code

@test public void sortPoint(){List<PointF> pointFS =new ArrayList<>(); PointF PointF = new PointF(187.5f, 391.5f); PointF pointF1 = new PointF(693.5f,391.5f); PointF pointF2 = new PointF(655.0f,606.0f); PointF pointF3 = new PointF(325.0f,859.0f); PointF pointF4 = new PointF(187.5f,897.5f); pointFS.add(pointF); pointFS.add(pointF1); pointFS.add(pointF2); pointFS.add(pointF3); pointFS.add(pointF4); Boolean isNotFindX = true; boolean isNotFindY = true; Set<Integer> centerPos =new HashSet<>(); int size = pointFS.size(); for ( int i = 0 ; i < size ; i++ ) { for ( int i1 = i+1 ; i1 < size ; i1++ ) { if ( isNotFindX && isQrCodeRate(pointFS.get(i).x, pointFS.get(i1).x) ) { centerPos.add(i); centerPos.add(i1); isNotFindX = false; } if ( isNotFindY && isQrCodeRate(pointFS.get(i).y, pointFS.get(i1).y) ) { centerPos.add(i); centerPos.add(i1); isNotFindY = false; }}} System. Out. Println (" key point coordinates centerPos = = = = = = > "+" - "+ centerPos. The size ()); For (Integer centerPo: centerPos) {system.out.println (" centerPos======>"+ centerPo+"----"+centerPos. Size ()); Private Boolean isQrCodeRate(float x, float x1) {float errorSize = 0.5f; return Math.abs(x - x1) <= errorSize; }Copy the code

The last

I haven’t written a blog for several months. The first reason is that I am busy after changing my job. Besides, I broke my foot during the period of self-cultivation and figured out some things. Of course these words are to oneself

Finally, you should not only absorb the knowledge of the community on the Internet, but also give back your own research results.