Showing posts with label csharp. Show all posts
Showing posts with label csharp. Show all posts

Monday, September 17, 2012

How to Decode QR Codes using Unity3D for Android (Zxing + Vuforia)

Following my previous blog post, here is my solution to decode QR codes on Android using Zxing and Vuforia.

UPDATE: If you also need to decode barcodes as well, here is a great solution. I haven't tried it though, I don't need to deal with barcodes as for now. My friend refers me to that page. I was late several months and didn't realize the solution was already out there :D oh well.

Step 1: on the blank Unity project, import Vuforia packages, and the Zxing.dll (provided at the end of this post, along with the camera prefab and this full script). Then add the ARCamera prefab to the scene, then add a script to it. Let's say it is CameraImageAccess.cs or whatever name you like.

Step 2: in the script, add
using com.google.zxing.qrcode;
then implement ITrackerEventHandler so that it will look like this
class CameraImageAccess : MonoBehaviour, ITrackerEventHandler
then define the implementation of  ITrackerEventHandler
 public void OnTrackablesUpdated () { try { if(!isFrameFormatSet) { isFrameFormatSet = CameraDevice.Instance.SetFrameFormat(Image.PIXEL_FORMAT.GRAYSCALE, true); } cameraFeed = CameraDevice.Instance.GetCameraImage(Image.PIXEL_FORMAT.GRAYSCALE); tempText = new QRCodeReader().decode(cameraFeed.Pixels, cameraFeed.BufferWidth, cameraFeed.BufferHeight).Text; } catch { // Fail to detect QR Code! } finally { if(!string.IsNullOrEmpty(tempText)) { qrText = tempText; } } }
OnTrackablesUpdated will be called by QCARBehaviour on every frame. So you could set a flag variable to determine whether you would need to decode QR or not, in this example we'll constantly try to decode QR.
I use Image.PIXEL_FORMAT.GRAYSCALE because we don't need colors to process QR codes.
tempText and qrText are just string variables to hold the decoded QR codes.
You could display qrText at OnGUI event or just use it as you need.

Finally, in the Start function, register this component to QCARBehaviour and initialize the camera
void Start () { QCARBehaviour qcarBehaviour = GetComponent(); if (qcarBehaviour) { qcarBehaviour.RegisterTrackerEventHandler(this); } isFrameFormatSet = CameraDevice.Instance.SetFrameFormat(Image.PIXEL_FORMAT.GRAYSCALE, true); InvokeRepeating("Autofocus", 1f, 2f); }
That's it, now we're done. Here is my Unity Package file containing the QRCamera prefab, the required scripts, and the Zxing.dll itself.
To use it, you just drag the QRCamera prefab to a blank/new scene. Then build it.

Final Notes:

  • This will only run on mobile devices (Android, and maybe IOS but I haven't test this on IOS, why? because I have no device/access to it, and I dislike Apple, so I won't ever buy Apple products). With that said, it will NOT run on Editor or any other platform, so don't forget to set the correct build target.
  • The zxing.dll used here is NOT the same dll file from my previous post. Now it receives byte[] parameter right from Vuforia as opposed to sbyte[] from Color32[] as in my previous post.
  • Lastly, the provided Unity Package here does not include all the Vuforia components, so you'll need to import them first yourself into your project.


Monday, May 7, 2012

How to decode QR code using Unity3D

UPDATE 2012-09-17: For decoding QR codes on Android devices, see here.

Hi,
here's how to decode QR code using Unity3D through camera. Please note that I'm using a library called ZXing (Zebra Crossing). I used the C# version since it is the most familiar language to me. I modified it a bit since Unity doesn't allow to use System.Drawing namespace.

To get access to the camera device I use WebCamTexture class which is new since Unity 3.5 (if I'm not mistaken). Here's my code in the Unity side:

using com.google.zxing.qrcode;

This is the only namespace to use from the dll if you just want to decode QR codes and nothing else.

WebCamTexture camTexture;
Thread qrThread;

void Start () {
    camTexture = new WebCamTexture();
    OnEnable();
    
    qrThread = new Thread(DecodeQR);
    qrThread.Start();
}

At first I'm declaring a new WebCamTexture to get the texture taken from the camera, and then I'm going to use another thread to process that texture.

int W, H, WxH;

void OnEnable () {
    if(camTexture != null) {
        camTexture.Play();
        W = camTexture.width;
        H = camTexture.height;
        WxH = W * H;
    }
}

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

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

I use the OnEnable event handler to start the camera and retrieve it's size.

Color32[] c;
sbyte[] d;
int x, y, z;

void Update () {
    c = camTexture.GetPixels32();
}

void DecodeQR () {
    while(true) {
        try {
            d = new sbyte[WxH];
            z = 0;
            for(y = H - 1; y >= 0; y--) { // flip
                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));
                }
            }
            
            print(new QRCodeReader().decode(d, W, H).Text);
        }
        catch {
            continue;
        }
    }
}

Now, everytime a QR code is detected from the camera it will decode it. Nice and simple.
Here is the .dll file: link
Here is the .cs file: link

Thanks.

Tuesday, December 14, 2010

Cascading DataGridView ComboBox

        ...


        [Flags]
        //This is the enum of column list in the dataGridView.
        private enum Column
        {
            ParentCombo,
            ChildCombo
        }



        private string oldValue = null;
        private string newValue = null;



        private void Form_Load(object sender, EventArgs e)
        {
            #region Set the DataSource of Column: Parent ComboBox
            ColumnParent.DataSource = GetParentDT();
            ColumnParent.DisplayMember = "YourDisplayMember";
            ColumnParent.ValueMember = "YourValueMember";
            #endregion


            #region Fill the dataGridView
            using (SqlConnection sqlConn = GetYourSQLconn())
            {
                SqlCommand sqlComm = new SqlCommand(storedProcedure, sqlConn);
                sqlComm.CommandType = CommandType.StoredProcedure;
                sqlComm.Parameters.AddRange(GetYourSQLparameters());


                using (SqlDataReader dataReader = sqlComm.ExecuteReader())
                {
                    dataGridView.Rows.Clear();
                    while (dataReader.Read())
                    {
                        dataGridView.Rows.Add();
                        for (int colIndex = 0; colIndex < dataReader.FieldCount; colIndex++)
                        {
                            dataGridView[colIndex, dataGridView.RowCount - 1].Value = dataReader[colIndex];
                        }
                    }
                }
            }
            #endregion
        }


        private void dataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            #region Set the DataSource of Child ComboBox According to Parent ComboBox Value
            if (e.ColumnIndex == (int)Column.ParentCombo)
            {
                (dataGridView[(int)Column.ChildCombo, e.RowIndex] as DataGridViewComboBoxCell).DataSource = GetChildDT(e.RowIndex);
                (dataGridView[(int)Column.ChildCombo, e.RowIndex] as DataGridViewComboBoxCell).DisplayMember = "YourDisplayMember";
                (dataGridView[(int)Column.ChildCombo, e.RowIndex] as DataGridViewComboBoxCell).ValueMember = "YourValueMember";
            }
            #endregion
        }


        #region Clear Child ComboBox Selection on Parent ComboBox Value Changed
        private void dataGridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
        {
            oldValue = dataGridView[(int)Column.ParentCombo, e.RowIndex].Value.ToString();
        }


        private void dataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            newValue = dataGridView[(int)Column.Problem, e.RowIndex].Value.ToString();


            if (newValue != oldValue)
            {
                dataGridView[(int)Column.ChildCombo, e.RowIndex].Value = null;
            }
        }
        #endregion


        ...




Please note that I set my dataGridView to:

  • Enable Adding = false;
  • Enable Editing = true;
  • Enable Deleting = false;

I provided some buttons for Add/Edit/Delete though (unseen in this code).