Search Unity

decode QR code

Discussion in 'iOS and tvOS' started by Russel, Jun 6, 2012.

  1. Russel

    Russel

    Joined:
    Oct 12, 2011
    Posts:
    40
    Last edited: Jun 6, 2012
  2. v_radar

    v_radar

    Joined:
    May 25, 2012
    Posts:
    1
    hey Russel,

    here is the code i modified from Ariady Putra using zXing lib.
    It cost me long hours, but thanks to the community finally it works

    for time issues it does not got a nice performance and I do not have much spare time to
    clearify for you.

    hope it could help you.


    public class qrCam : MonoBehaviour {

    private WebCamTexture camTexture;
    private Thread qrThread;
    private Texture2D tex;
    Texture2D rotImage;

    private Color32[] c;
    private Color32[] f;
    private sbyte[] d;
    private int W, H, WxH;
    private int x, y, z;
    bool ifDone;

    /*void OnDisable () {
    if(camTexture != null) {
    camTexture.Pause();
    }
    }*/

    /*void OnDestroy () {
    qrThread.Abort();
    camTexture.Stop();
    }*/

    IEnumerator Start () {
    camTexture = new WebCamTexture(480,360,5);
    camTexture.Play();

    while(true)
    {
    if(camTexture.didUpdateThisFrame ifDone == false)
    {
    tex = new Texture2D(camTexture.width, camTexture.height, TextureFormat.RGB24, false);

    W = camTexture.width;
    H = camTexture.height;
    WxH = W * H;
    Debug.Log(W + " " + H);
    ifDone = true;

    qrThread = new Thread(DecodeQR);
    qrThread.Start();

    break;
    }
    else
    yield return null;
    }
    }

    void Update () {
    if(ifDone){

    if(Time.frameCount % 30 == 0){
    f = camTexture.GetPixels32();
    tex.SetPixels32(f);
    tex = rotateTexture(tex,90);
    c = tex.GetPixels32();
    //tex.Apply();
    this.renderer.material.mainTexture = tex;
    }

    /*if(f != null){
    tex1.LoadImage(f);
    this.renderer.material.mainTexture = tex1;
    }*/

    }
    }



    void DecodeQR () {
    while(true) {
    if(c == null)
    {
    continue;
    }
    try {
    d = new sbyte[WxH];
    z = 0;
    int tmp;
    for(y = H - 1; y >= 0; y--) {
    for(x = 0; x < W; x++) {
    d[z++] = (sbyte)(((int)c[y * W + x].r) << 16 | ((int)c[y * W + x].g) << 8 | ((int)c[y * W + x].b));
    }
    }
    Debug.Log("Decode: " + new QRCodeReader().decode(d, W, H).Text);
    }
    catch(Exception e){

    //Debug.Log(e.Message);
    continue;
    }
    }
    }


    Texture2D rotateTexture(Texture2D tex, float angle)
    {
    rotImage = new Texture2D(camTexture.width, camTexture.height, TextureFormat.RGB24, false);

    int x,y;
    float x1, y1, x2,y2;


    float x0 = 480;
    float y0 = rot_y (angle, -W/2.0f, -H/2.0f) + H/2.0f;

    float dx_x = rot_x (angle, 1.0f, 0.0f);
    float dx_y = rot_y (angle, 1.0f, 0.0f);
    float dy_x = rot_x (angle, 0.0f, 1.0f);
    float dy_y = rot_y (angle, 0.0f, 1.0f);
    //Debug.Log(x0 + " " + y0 + " " + dx_x + " " +dx_y + " " + dy_x + " " + dy_y);

    x1 = x0;
    y1 = y0;


    for (x = 0; x < W; x++) {
    x2 = x1;
    y2 = y1;
    for ( y = 0; y < H; y++) {
    x2 += dx_x;
    y2 += dx_y;
    rotImage.SetPixel( x, y, getPixel(tex,x2, y2));
    }
    x1 += dy_x;
    y1 += dy_y;
    }

    rotImage.Apply();
    return rotImage;
    }



    private Color getPixel(Texture2D tex, float x, float y)
    {
    Color pix;
    int x1 = (int) Mathf.Floor(x);
    int y1 = (int) Mathf.Floor(y);


    if(x1 > tex.width || x1 < 0 ||
    y1 > tex.height || y1 < 0) {
    pix = Color.clear;
    } else {
    pix = tex.GetPixel(x1,y1);
    }
    return pix;
    }


    private float rot_x (float angle, float x, float y) {
    float cos = Mathf.Cos(angle/180.0f*Mathf.PI);
    float sin = Mathf.Sin(angle/180.0f*Mathf.PI);
    return (x * cos + y * (-sin));
    }

    private float rot_y (float angle, float x, float y) {
    float cos = Mathf.Cos(angle/180.0f*Mathf.PI);
    float sin = Mathf.Sin(angle/180.0f*Mathf.PI);
    return (x * sin + y * cos);
    }
    }
     
    Last edited: Jun 9, 2012
  3. luisanton

    luisanton

    Joined:
    Aug 25, 2009
    Posts:
    325
    Note that it's a 90º rotation, so there's no need for Sin and Cos. You may rotate a texture with something like this:

    Code (csharp):
    1.    
    2.     void rotateTexture(Texture2D tex)
    3.     {
    4.         int M = camTexture.width;
    5.         int N = camTexture.height;
    6.        
    7.         Texture2D rotated = new Texture2D (N, M, TextureFormat.RGB24, false);
    8.        
    9.         for (int r = 0; r < M; r++)
    10.             for (int c = 0; c < N; c++)
    11.                 rotated.SetPixel (c, M - 1 - r, tex.GetPixel(r, c));
    12.        
    13.         rotated.Apply();
    14.     }
    15.  
    But it needs a second transformation, that is also added to the GetPixel function. The whole thing would finally look like this:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Threading;
    5.  
    6. using com.google.zxing.qrcode;
    7.  
    8. public class qrCam : MonoBehaviour {
    9.  
    10.     private WebCamTexture camTexture;
    11.     private Thread qrThread;
    12.     private Texture2D tex;
    13.     Texture2D rotImage;
    14.    
    15.     private Color32[] c;
    16.     private Color32[] f;
    17.     private sbyte[] d;
    18.     private int W, H, WxH;
    19.     private int x, y, z;
    20.     bool camReady;
    21.    
    22.     QRCodeReader QR;
    23.     string output;
    24.    
    25.     void OnDisable () {
    26.         if(camTexture != null) {
    27.         camTexture.Pause();
    28.         }
    29.     }
    30.    
    31.     void OnDestroy () {
    32.         qrThread.Abort();
    33.         camTexture.Stop();
    34.     }
    35.    
    36.    
    37.     void Start () {
    38.         QR = null;
    39.         rotImage = null;
    40.        
    41.         StartCoroutine (Launch());
    42.     }
    43.    
    44.     IEnumerator Launch() {
    45.         camTexture = new WebCamTexture(512, 512);  
    46.         camTexture.Play();
    47.        
    48.         while (true)
    49.         {
    50.             if(camTexture.didUpdateThisFrame  camReady == false)
    51.             {
    52.                 tex = new Texture2D(camTexture.width, camTexture.height, TextureFormat.RGB24, false);
    53.                
    54.                 W = camTexture.width;
    55.                 H = camTexture.height;
    56.                 WxH = W * H;
    57.                 Debug.Log(W + " " + H);
    58.                 camReady = true;
    59.                
    60.                 qrThread = new Thread(DecodeQR);
    61.                 qrThread.Start();
    62.                
    63.                 break;
    64.             }
    65.             else
    66.                 yield return null;
    67.         }  
    68.     }
    69.    
    70.     void Update () {
    71.         if (camReady) {
    72.             f = camTexture.GetPixels32();
    73.             tex.SetPixels32(f);
    74.             correctIOSWebcamTexture(tex);
    75.             c = rotImage.GetPixels32();
    76.             this.renderer.material.mainTexture = rotImage;
    77.         }
    78.     }
    79.    
    80.    
    81.     void correctIOSWebcamTexture(Texture2D tex)
    82.     {
    83.         int M = camTexture.width;
    84.         int N = camTexture.height;
    85.        
    86.         Destroy (rotImage);
    87.         rotImage = new Texture2D (N, M, TextureFormat.RGB24, false);
    88.        
    89.         for (int r = 0; r < M; r++)
    90.             for (int c = 0; c < N; c++)
    91.                 rotImage.SetPixel (c, M - 1 - r, tex.GetPixel(r, N - 1 - c));
    92.        
    93.         rotImage.Apply();
    94.     }
    95.    
    96.    
    97.     void DecodeQR () {
    98.         while(true) {
    99.             if(c == null)
    100.                 continue;
    101.             try
    102.             {
    103.                 d = new sbyte[WxH];
    104.                 z = 0;
    105.                 int tmp;
    106.                 for(y = 0; y < H; y++) {
    107.                     for(x = 0; x < W; x++) {
    108.                         d[z++] = (sbyte)(((int)c[y * W + x].r) << 16 | ((int)c[y * W + x].g) << 8 | ((int)c[y * W + x].b));
    109.                     }
    110.                 }
    111.                 if (QR == null)
    112.                     QR = new QRCodeReader();
    113.                 output = QR.decode(d, W, H).Text;
    114.                
    115.                 print (output);
    116.             }
    117.             catch {
    118.                 continue;
    119.             }
    120.         }
    121.     }
    122. }
    123.  
    But it would be nice avoiding the creation and destruction of textures in each step, as I did with QRCodeReader (avoiding a memory leak, by the way). It's not printing the QR code, however. I'll check all this once again tomorrow... it's quite late here and I'm tires.

    EDITED: As the texture is properly rotated, there's no need for the backwards vertical loop in the DecodeQR function, so y = 0; y < H; y++. But it still doesn't print the code. Still trying.
     
    Last edited: Jul 4, 2012
  4. luisanton

    luisanton

    Joined:
    Aug 25, 2009
    Posts:
    325
    I think I've got it.

    First of all, there's no need to rotate the image to decode the QR code. We just need to change the DecodeQR loop to the one in my code above, using the texture that comes directly from camTexture!! That makes the whole thing ULTRA fast, of course.

    And if you want to properly visualize the camera on a plane, simply rotate and scale it properly. I use a plane that, when imported, faces the default camera (so its axis correspond to the World's axis). Such a plane must simply be rotated 90 degrees on the Z plane and scaled x -1.0 in the x dimension. And that's all!

    That way the update loop looks like this:

    Code (csharp):
    1.  
    2.     void Update () {
    3.         if (camReady) {
    4.             c = camTexture.GetPixels32();
    5.             this.renderer.material.mainTexture = camTexture;
    6.         }
    7.     }
    8.  
    and the Decode function like this:

    Code (csharp):
    1.  
    2. void DecodeQR () {
    3.         while(true) {
    4.             if(c == null)
    5.                 continue;
    6.             try
    7.             {
    8.                 d = new sbyte[WxH];
    9.                 z = 0;
    10.                 int tmp;
    11.                 for(y = 0; y < H; y++) {
    12.                     for(x = 0; x < W; x++) {
    13.                         d[z++] = (sbyte)(((int)c[y * W + x].r) << 16 | ((int)c[y * W + x].g) << 8 | ((int)c[y * W + x].b));
    14.                     }
    15.                 }
    16.                 if (QR == null)
    17.                     QR = new QRCodeReader();
    18.                 output = QR.decode(d, W, H).Text;
    19.                 print (output);
    20.             }
    21.             catch {
    22.                 continue;
    23.             }
    24.         }
    25.     }
    26.  
    And that's simply real time on an iPad 2 :D
     
  5. Deleted User

    Deleted User

    Guest

    Brilliant work but I'm getting the error of missing a Renderer to the Camera of the script. Is there a possibility that you can put up a unity package or point me in the right direction?

    What I've done so far is as below:
    - I created a new script with your code in it
    - created a new camera and attached a new renderer texture as the target texture

    The error comes on during runtime and there isn't anything showing.

    Many Thanks





     
  6. luisanton

    luisanton

    Joined:
    Aug 25, 2009
    Posts:
    325
    But I never used a 'render to texture', because I just own the basic version! It's a simple texture, which is applied to a plane. So: it's not the camera being rendered to a texture, just a simple texture being update by the webcam. Try doing it like that : )
     
  7. sama-van

    sama-van

    Joined:
    Jun 2, 2009
    Posts:
    1,734
    Golden thread O_O!
    Thanks guys!!
     
  8. Deleted User

    Deleted User

    Guest

    Thanks but I still don't see why its not working on mine. Would you please send me a link to download the unity package instead? It is so frustrating that the code is there but I'm not able to make it work... Many thanks
     
  9. biodam

    biodam

    Joined:
    Jul 10, 2013
    Posts:
    17