Makale Özeti

Bu makalemde CSharp ile bir picturebox içine mouse yardımı ile yazılan el yazısını nasıl tanıyacağımızdan bahsedeceğim.

Makale

Tüm yazılımcı arkadaşlara selamlar,

Bu makalemde CSharp ile bir picturebox içine mouse yardımı ile yazılan el yazısını nasıl tanıyacağımızdan bahsedeceğim.

İlk önce böyle bir uygulama nerelerde işimize yarayabilir diye sorabilirsiniz.Alışveriş merkezlerinde veya bunun gibi halka açık yerlerde bulunan kiosk larda kullanabilirsiniz.Tabiki piyasada bu işlemi yapan yazılımlar veya işletim sistemiyle beraber gelen uygulamalar mevcut fakat bu makale sonunda bu işlemi c# ile hiçbir yardımcı komponent kullanmadan yapabiliyor olmanın dayanılmaz hafifliğini hissedeceğinizden emin olabilirsiniz. :)


İlk önce algoritmamızı ardından ne gibi kontroller kullanacağımızı belirlememiz gerekmektedir.

Algoritma:
Kullanıcı yazı yazarken mouse ile belirli yönlerde çizgiler çizer.İlk önce bu yönleri belirleyelim ve bir değer verelim.


Yön Şeması

Kullanıcı mouse yardımı ile ekranda yazı yazacağı için ilk önce bu iş için bir picturebox kontrolü kullanacağız.
Kullanıcının gittiği yönleri anlık olarak görmek için bir textbox ve çizilen harfin gösterileceği bir textbox kullanacağız.

Basit olarak düşündüğümüzde "I" harfinin yön bilgisi 2 olacaktır.
Yani kullanıcı picturebox'a tıkladığında Güney yönünde bir çizgi çizecektir.
Eğer biz kullanıcının picturebox üzerinde mouse'un sol tuşunu basılı tuttuğu zaman gittiği yön bilgilerini elde edersek hangi harfin yazıldığını öğrenebiliriz.

Bu durumda kullanıcının picturebox'da çizim yaparken yaptığı yön değişikliklerinden haberdar olmamız gerekir.
Aşağıdaki kodu inceleyelim.

private byte XDirection=0;
private byte YDirection=0;
private ArrayList X=new ArrayList();
private ArrayList Y=new ArrayList();
private StringBuilder OCR=new StringBuilder();
private byte History=10;
private int Range=5;
...
...
...//Windows Form Designer generated code
...
...
private void pictureBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
    bool XDirChanged=false;
    bool YDirChanged=false;
    if (e.Button==MouseButtons.Left)
    {
        Graphics g=pictureBox1.CreateGraphics();
        g.DrawEllipse(new Pen(Color.Brown), e.X ,e.Y,5,5);
        X.Add(e.X);
        Y.Add(e.Y);
        if (X.Count>History)
        {
            if (XDirection==0)
            {
                if ((e.X-(int)X[X.Count-History])>Range)
                {
                    XDirection=3;
                    XDirChanged=true;
                }
                else if (((int)X[X.Count-History]-e.X)>Range)
                {
                    XDirection=4;
                    XDirChanged=true;
                }
            }
            else
            {
                if ((e.X-(int)X[X.Count-History])>Range && XDirection==4)
                { 
                    XDirection=3; 
                    XDirChanged=true;
                }
                else if (((int)X[X.Count-History]-e.X)>Range && XDirection==3)
                { 
                    XDirection=4; 
                    XDirChanged=true; 
                }
            }
        }
        if (Y.Count>History)
        {
            if (YDirection==0)
            {
                if ((e.Y-(int)Y[Y.Count-History])>Range)
                {
                    YDirection=2;
                    YDirChanged=true;
                }
                else if (((int)Y[Y.Count-History]-e.Y)>Range)
                {
                    YDirection=1;
                    YDirChanged=true;
                }
            }
            else
            {
                if ((e.Y-(int)Y[Y.Count-History])>Range && YDirection==1)
                { 
                    YDirection=2;
                    YDirChanged=true;
                }
                else if (((int)Y[Y.Count-History]-e.Y)>Range && YDirection==2)
                { 
                    YDirection=1; 
                    YDirChanged=true;
                }
            }
        }
        if (XDirChanged && XDirection!=0)
            OCR.Append(XDirection);
        if (YDirChanged && YDirection!=0)
            OCR.Append(YDirection);
        textBox1.Text=OCR.ToString(); 
    }
}

Kullanıcının çizim yapması
Kullanıcının çizim yapabilmesi için picturebox'ın mousemove event'ini kullanıyoruz.Eğer mouse picturebox üzerinde hareket ediyor ve mouse'un sol tuşu basılı ise pictureboxda mouse koordinatlarının bulunduğu yere şekil çiziyoruz.

...
    if (e.Button==MouseButtons.Left)
    {
        Graphics g=pictureBox1.CreateGraphics();
        g.DrawEllipse(new Pen(Color.Brown), e.X ,e.Y,5,5);
...

Yön değişiminden haberdar olmak
Koordinat sistemi olarak 2D kullandığımız için X ve Y koordinatlarımız mevcuttur.Dolayısı ile yön değişimi bu iki koordinat sisteminde olacaktır.Programda bunu anlamak için XDirection ve YDirection değişkenlerini kullanıyoruz.Eğer XDirection 3 ise doğu,4 ise batı eğer YDirection 1 ise kuzey,2 ise güney yönünde çizim yapılıyor demektir.Eğer bu değişkenler 0 değerinde ise henüz çizim işlemi yapılmamış demektir.

Yön değişimi ancak mevcut koordinatın daha önceki koordinatların değerleri ile karşılaştırılması sonucu anlaşılabilir.Dolayısı ile daha önce çizilen noktaları hafızada tutmamız gerekmektedir.Bunun için X ve Y isminde iki Arraylist kullanıyoruz.

Yön değişiminden haberdar olmamız için ilk önce hangi yöne doğru çizim yapıldığını anlamamız gerekmektedir.Bunun için iki değişkenimiz mevcut.History ve Range.History değişkeni X ve Y Arraylist'inde ne kadar önceki veriyi okuyacağımızı ve Range değişkeni de mevcut koordinat ile X,Y Arraylistlerindeki değerin farkının olabileceği maximum değeri içerir.

Aşağıdaki şekli inceleyelim.
 

History=10
Range=5


private void pictureBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
    bool XDirChanged=false;
    bool YDirChanged=false;
    if (e.Button==MouseButtons.Left)
    {
        Graphics g=pictureBox1.CreateGraphics();
        g.DrawEllipse(new Pen(Color.Brown), e.X ,e.Y,5,5);
        X.Add(e.X);
        Y.Add(e.Y);
        //Eğer X Arraylist kayıt sayısı geriye dönülebilecek kadar büyükse
        if (X.Count>History)
        {
            //Eğer kullanıcı ilk defa çizim yapıyorsa (Yön daha önce belirlenmemişse)
            if (XDirection==0)
            {
                //Eğer Mevcut X koordinat değeri 10 kayıt önceki değerden 5 pixel büyükse
                //Bu kullanıcının 3(Doğu) yönünde gittiği anlamına gelmektedir.
                //XDirChanged true yapılarak Yön değişiklik listesi güncellenir.

                if ((e.X-(int)X[X.Count-History])>Range)
                {
                    XDirection=3;
                    XDirChanged=true;
                }
                //Eğer 10 kayıt önceki X koordinat değeri mevcut değerden 5 pixel büyükse
                //Bu kullanıcının 4(Batı) yönünde gittiği anlamına gelmektedir.
                //XDirChanged true yapılarak Yön değişiklik listesi güncellenir.

                else if (((int)X[X.Count-History]-e.X)>Range)
                {
                    XDirection=4;
                    XDirChanged=true;
                }
            }
            else
            {
                //Eğer Mevcut X koordinat değeri 10 kayıt önceki değerden 5 pixel büyükse
                //ve mevcut yön 4(Batı) yönünde ise bu kullanıcının 3(Doğu) yönünde gittiği 
                //anlamına gelmektedir.
                //XDirChanged true yapılarak Yön değişiklik listesi güncellenir.

                if ((e.X-(int)X[X.Count-History])>Range && XDirection==4)
                { 
                    XDirection=3; 
                    XDirChanged=true;
                }
                //Eğer 10 kayıt önceki X koordinat değeri mevcut değerden 5 pixel büyükse
                //ve mevcut yön 3(Doğu) yönünde ise bu kullanıcının 4(Batı) yönünde gittiği 
                //anlamına gelmektedir.
                //XDirChanged true yapılarak Yön değişiklik listesi güncellenir.

                else if (((int)X[X.Count-History]-e.X)>Range && XDirection==3)
                { 
                    XDirection=4; 
                    XDirChanged=true; 
                }
            }
        }
...

Aynı işlemler Y koordinatı için geçerlidir.

Yön değişimlerini hafızada tutmak

Yön değişimlerini hafızada tutmak için StringBuider veri tipinde OCR isimli bir değişken kullanıyoruz.
Eğer XDirChanged değişkeni true değerinde ise ve XDirection 0 değerinden farklı bir değerdeyse OCR değişkenine yön bilgisini kaydediyoruz.

...
        if (XDirChanged && XDirection!=0)
            OCR.Append(XDirection);
        if (YDirChanged && YDirection!=0)
            OCR.Append(YDirection);

...

Son işlem, yön değişikliklerini yorumlamak
Çizim işleminin bittiğini anlamamız için kullanıcının mouse'un sağ tuşuna basması yeterli olacaktır.Bu sayede OCR stringbuilder değişkenindeki değerleri AnalyseThis() metodunda yorumlayacağız. 

Fakat yorumlama işleminden önce bir kağıt kalem alıp A harfini çizin ve çizerken hangi yönlere gittiğinizi not alın.
(Makalenin başındaki yön şemasını hatırlayın,)

A Harfi
42323 ?

haydi beraber çizelim,

1. çizgiyi çizerken gittiğimiz yön X için 4 Y için 2,
2. çizgiyi çizerken gittiğimiz yön X için 3 Y için 2,
3. çizgiyi çizerken gittiğimiz yön X için 3 Y için 0(Yok) 

Sonuç olarak eğer OCR değeri 42323 ise kullanıcının bir A harfi çizdiğini anlıyoruz.
Aklınıza gelecek ilk soru "-herkes bizim yazdığımız gibi yazmayabilir,Bu durumda ne yapacağız Levent?" olacaktır.

Aşağıdaki kodu inceleyelim.

private void AnalyseThis()
{
    Graphics Gc=pictureBox1.CreateGraphics();
    Gc.Clear(Color.White);
    //Yorumlama;
    string OCRText=OCR.ToString();
    switch(OCRText)
    {
        //A
        case "3123":
            textBox2.Text+="A";
            break;
        case "1323":
            goto case "3123";
        case "1332":
            goto case "3123";
        case "42233":
            goto case "3123";
        case "42323":
            goto case "3123";
        case "24323":
            goto case "3123";
        case "31323":
            goto case "42233";
        //B
        case "2324324":
            textBox2.Text+="B";
            break;
        case "231243124":
            goto case "2324324";
        case "232434":
            goto case "2324324";
        //C
        case "423":
            textBox2.Text+="C";
            break;
        case "243":
            goto case "423";
        //D
        case "2324":
            textBox2.Text+="D";
            break;
        case "2234":
            goto case "2324";
        //E
        case "2333":
            textBox2.Text+="E";
            break;
        //F
        case "233":
            textBox2.Text+="F";
            break;
        //G
        case "42313":
            textBox2.Text+="G";
            break;
        //H
        case "223":
            textBox2.Text+="H";
            break;
        case "232":
            goto case "223";
        //I
        case "2":
            textBox2.Text+="I";
            break;
        //J
        case "241":
            textBox2.Text+="J";
            break;
        //K
        case "2423":
            textBox2.Text+="K";
            break;
        case "2243":
            goto case "2423";
        //L
        case "23":
            textBox2.Text+="L";
            break;
        //M
        case "22312":
            textBox2.Text+="M";
            break;
        case "23212":
            goto case "22312";
        //N
        case "2232":
            textBox2.Text+="N";
            break;
        case "2322":
            goto case "2232";
            case "2321":
            goto case "2232";
        //O
        case "42314":
            textBox2.Text+="O";
            break;
        //P
        case "22343":
            textBox2.Text+="P";
            break;
        case "23243":
            goto case "22343";
        //R
        case "223432":
            textBox2.Text+="R";
            break;
        case "223423":
            goto case "223432";
        case "232432":
            goto case "223432";
        case "232423":
            goto case "223432";
        //S
        case "4234":
            textBox2.Text+="S";
            break;
        //T
        case "32":
            textBox2.Text+="T";
            break;
        //U
        case "231":
            textBox2.Text+="U";
            break;
        //V
        case "2313":
            textBox2.Text+="V";
            break;
        case "2331":
            goto case "2313";
        case "3213":
            goto case "2313";
        case "3231":
            goto case "2313";
        //Y
        case "2312":
            textBox2.Text+="Y";
            break;
        case "3212":
            goto case "2312";
        //Z
        case "3423":
            textBox2.Text+="Z";
            break;
        case "3243":
            goto case "3423";
    }
    OCR.Remove(0,OCR.Length);
}

Gördüğünüz üzere harflerin yazılışlarını yorumlamak için değişik yön kombinasyonları kullanabiliyoruz.
Bu sayede birden fazla yazım şekli anlaşılabilmektedir.

Bazı harflerde (D,P) bu yöntem sağlıklı çalışmayabilir.Bu durumlarda ek kod yazılarak kullanıcının hangi harfi yazdığı kullanıcıya seçtirilebilir.

Evet bir makalenin daha sonuna geldik.Umarım yararlı olmuştur.
Hepinize mutlu günler dilerim. 

Levent YILDIZ
Gulf Agency Company - TURKEY
IT Manager
MCP
msmoracle@hotmail.com 
levent.yildiz@gacworld.com 
http://www.gacworld.com

Kaynaklar
http://msdn.microsoft.com/
Proje dosyası