image-processingqr-codeaforge

Damaged QrCode Reconstructing


EDIT below image is the pre-processed sequence on original image. 1. Original Image -> 2. Blur x n times to make qrcode position significant -> 3. crop original image, position extracted from second step using blob -> 4. sharpen and threshold -> 5. check three square for qrcode -> 6. to do additional transformation like rotation -> (final image) (cropped image with resize resolution.)

Old Question I am trying to reconstruct qrcode from original image. As you can see the photo has damaged qrcode, so I use Aforge library to detect 3 square from image using blob. Now what I don't understand is the logic to generate qrcode from this information. Is it technically possible to reconstruct qrcode with given information?

breakdown


Solution

  • This is an interesting problem. To answer your question, is this technically possible. Yes it is certainly possible. The QR code in your question encode "5176941.12".

    Here's the prepossessed image so that it's easier to manually set the pixels.

    enter image description here

    After this step, I use excel to set each pixel one by one. After that simply point your phone towards the computer screen. This is what it looks like. If you want the excel sheet, you can get it here.

    enter image description here

    Now that the question of possibility is out of the way, how to automate it? Without knowing further additional samples, it is difficult to say for sure. However, just based on this sample alone, the simplest approach is simply align a 21x21 grid over your cropped QR image and fill in the values by using a threshold. And then pass this image to your QR decoder. QR code has certain level of redundancy so even if some of the pixels are missing, you will most likely be able to recover the original data.


    Edit

    Here's some code in python which may serve as a guide to how you might automate this. A few things to note:

    Code:

    import cv2
    import numpy as np
    
    def fill3box(qr):
        qr[0:7,0:7] = 1
        qr[14:21,14:21] = 1
        qr[14:21,0:7] = 1
        qr[0,0:6]=0
        qr[0:6,0]=0
        qr[0:6,6]=0
        qr[6,0:7]=0
        qr[2:5,2:5]=0
        qr[14:21,14:21] = qr[0:7,0:7]
        qr[14:21,0:7] = qr[0:7,0:7]
        return qr
    
    im = cv2.imread('to_process.png')
    im = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
    im = cv2.resize(im,(210,210))
    
    im = 1-((im - im.min())/(im.max()-im.min())) #normalize and adjust contrast
    avg=np.average(im)
    qr = np.ones((21,21))
    w,h = im.shape[:2]
    im_orig = im.copy()
    
    im[im<avg]=0#binarize
    im[im>avg]=1
    for y in range(21):
        for x in range(21):
            x1,y1 = (round(x*w/21),round(y*h/21))
            x2,y2 = (round(x1+10),round(y1+10))
    
            im_box = im[y1:y2,x1:x2]
            if np.average(im_box)<0.6 and qr[y,x]!=0:#0.6 need tweaking
                qr[y,x]=0
    
    qr = fill3box(qr) #clean up 3 box areas as they need to be fixed
    # debug visualization
    for x in range(21):
        p1 = (round(x*w/21),0)
        p2 = (round(x*w/21),h)
        cv2.line(im_orig,p1,p2,(255),1)
    
    for y in range(21):
        p1 = (0,round(y*h/21))
        p2 = (w,round(y*h/21))
        cv2.line(im_orig,p1,p2,(255),1)
    
    qr = cv2.resize(qr,(210,210),interpolation=cv2.INTER_NEAREST)
    
    im = (im*255).astype(np.uint8)
    qr= (qr*255).astype(np.uint8)
    im_orig= (im_orig*255).astype(np.uint8)
    
    cv2.imwrite('im.png',im)
    cv2.imwrite('qr.png',qr)
    cv2.imwrite('im_orig.png',im_orig)
    

    Cropped image to_process.png in code.

    enter image description here

    Grid overlayed to show how this method works

    Thresholded image.

    enter image description here

    Regenerated QR, note that it still works even though there are multiple errors.

    enter image description here