Makale Özeti

Barkodların uygulamalarda ne amaçlı kullanıldığına, ve bir Code39 barkodunun nasıl oluşturulacağına bir kontrol yazarak önceki makalemde değinmiştim. Bu makalemde hep beraber bize resim olarak gelen dosya içerisinden barkod bilgisinin nasıl okunacağı konusu üzerinde bir çalışma yapacağız. Öncelikle kullanım alanı olarak barkod okuyucusu olmayan el terminallerinden resim çekilerek barkodun okunmasını sağlayacağını düşündüğüm bu metod ile uygulamalarınıza ekstra özellikler katabilirsiniz.

Makale

         Merhabalar,

         Barkodların uygulamalarda ne amaçlı kullanıldığına, ve bir Code39 barkodunun nasıl oluşturulacağına bir kontrol yazarak önceki makalemde değinmiştim. Bu makalemde hep beraber bize resim olarak gelen dosya içerisinden barkod bilgisinin nasıl okunacağı konusu üzerinde bir çalışma yapacağız. Öncelikle kullanım alanı olarak barkod okuyucusu olmayan el terminallerinden resim çekilerek barkodun okunmasını sağlayacağını düşündüğüm bu metod ile uygulamalarınıza ekstra özellikler katabilirsiniz.

         Code39 Barkod standardından çok kısa bahsetmek gerekirse

         Code39(3 Of 9) denmesinin sebebi 9 tane çizginden 3'ünün kalın diğerlerinin ince olmasıdır. Bu barkod sisteminde her karakter 9 tane çizgi ile temsil edilir. Bu çizgiler siyah, beyaz renklerinde ve kalın, ince tiplerinde olabilirler. Renk sıralaması Siyah, Beyaz, Siyah, Beyaz şeklinde standart bir şekilde ilerlerken kalın, ince çizgi sıralaması barkod'a yazılacak karakterlere bağlıdır. Bu karakterlere ait listeyi i:İnce k:Kalın olmak üzere aşağıda bulabilirsiniz.

Karakter Barkod Karakter Barkod Karakter Barkod Karakter Barkod
1 kiikiiiik B iikiikiik M kikiiiiki X ikiikiiik
2 iikkiiiik C kikiikiii i iiiikiikk Y kkiikiiii
3 kikkiiiii D iiiikkiik O kiiikiiki Z ikkikiiii
4 iiikkiiik E kiiikkiii P iikikiiki - ikiiiikik
5 kiikkiiii F iikikkiii Q iiiiiikkk . kkiiiikii
6 iikkkiiii G iiiiikkik R kiiiiikki BOŞLUK ikkiiikii
7 iiikiikik H kiiiikkii S iikiiikki * ikiikikii
8 kiikiikii I iikiikkii T iiiikikki $ ikikikiii
9 iikkiikii J iiiikkkii U kkiiiiiik / ikikiiiki
0 iiikkikii K kiiiiiikk V ikkiiiiik + ikiiikiki
A kiiiikiik L iikiiiikk k kkkiiiiii % iiikikiki

         Çalışma mantığı olarak gelen resimde bulunan siyah ve beyaz alanların kontrol edilerek barkod çubuklarının genişliği hakkında yorum yapabilen bir metod yazmamız gerekiyor. Bu noktada resimde oluşabilecek renk kaymaları, barkod dışında oluşabilecek beyaz boşluklar, ve resmin yamuk çekilmesi gibi bazı istisnai durumları düşünmeli ve burumlara göre kod yazmalıyız.

         Artık Class'ımızı oluşturmaya başlayabiliriz ancak yukarıda bahsettiğim tablodan karakterlere geri çevrim yapacağımızdan bu tablonun içeriğini tanımlamak zorundayız. Bu tablonun içeriğini tanımlarken kalın çizgileri 1 ince çizgileri ise 0 ile temsil edeceğiz. Ayrıca bu işlemi Class'ın constructor'unda yapmamız gerekecektir.

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
 
namespace BarcodeReader
{
    public class BarcodeDecoder
    {
        private Dictionary<string, string> chars = new Dictionary<string, string>();
        public BarcodeDecoder()
        {
            chars.Add("100100001", "1");chars.Add("001100001", "2");
            chars.Add("101100000", "3");chars.Add("000110001", "4");
            chars.Add("100110000", "5");chars.Add("001110000", "6");
            chars.Add("000100101", "7");chars.Add("100100100", "8");
            chars.Add("001100100", "9");chars.Add("000110100", "0");
            chars.Add("100001001", "A");chars.Add("001001001", "B");
            chars.Add("101001000", "C");chars.Add("000011001", "D");
            chars.Add("100011000", "E");chars.Add("001011000", "F");
            chars.Add("000001101", "G");chars.Add("100001100", "H");
            chars.Add("001001100", "I");chars.Add("000011100", "J");
            chars.Add("100000011", "K");chars.Add("001000011", "L");
            chars.Add("101000010", "M");chars.Add("000010011", "N");
            chars.Add("100010010", "O");chars.Add("001010010", "P");
            chars.Add("000000111", "Q");chars.Add("100000110", "R");
            chars.Add("001000110", "S");chars.Add("000010110", "T");
            chars.Add("110000001", "U");chars.Add("011000001", "V");
            chars.Add("111000000", "W");chars.Add("010010001", "X");
            chars.Add("110010000", "Y");chars.Add("011010000", "Z");
            chars.Add("010000101", "-");chars.Add("110000100", ".");
            chars.Add("011000100", "");chars.Add("010010100", "*");
            chars.Add("010101000", "$");chars.Add("010100010", "/");
            chars.Add("010001010", "+");chars.Add("000101010", "%");
       
}

         Bu tanımlamalarımızı yaptıktan sonra yapacağımız ilk işlem ise bu imaj dosyasında bulunan ilk siyah çizginin en küçük x koordinatını bulmak, sonrasında ise bu x koordinatı üzerinde siyah çizginin y koordinatına göre orta noktasını bulmak olacaktır. Bunun için yazılmış olan metodu inceleyelim. Daha sonrasında bu metodun çağrıldığı yeri de inceleyeceğiz.

        private Point FindFirstBlack(Bitmap b)
        {
            Point pFirstBlack = new Point();
            for (int i = 0; i < b.Width; i++)
            {
                int firstYBlack = 0;
                int lastYBlack = 0;
                for (int j = 0; j < b.Height; j++)
                {
                    Color c = b.GetPixel(i, j);
                    int average = (c.R + c.G + c.B) / 3;
                    if (average < 128)
                    {
                        if (firstYBlack == 0) { firstYBlack = j; }
                        lastYBlack = j;
                    }
                }
                if (firstYBlack != 0)
                {
                    pFirstBlack.Y = (firstYBlack + lastYBlack) / 2;
                    pFirstBlack.X = i;
                    break;
                }
            }
            return pFirstBlack;
        }


         Bu metodun yaptığı işi inceleyecek olursak, metod kendisine parametre olarak gelen Bitmap nesnesini en üst sol köşesinden başlamak üzere pixel pixel dolaşıyor. İlk siyah pixel'i bulduğunda artık sadece y eksenine bakıyor. Baktığı y ekseninde siyah pixel'lerin bittiği nokta ile başladığı noktanın orta noktasını alarak buradaki çizginin orta noktasını bulmuş oluyoruz. Burda dikkat edilmesi gereken önemli bir nokta bir pixel'in siyah'a mı beyaz'a mı yakın olduğuna karar vermek için kullanılan yöntemdir. Bu yöntemde pixelin R,G,B değerlerinin ortalamasına bakarak siyaha mı daya yakın yoksa beyaza mı daha yakın olduğuna karar verilebilmektedir. Bu metod daha sonra bize bahsettiğim noktanın koordinatlarını point cinsinden döndürüyor.

        public string DecodeCode39(Image img)
        {
            List<int> order = new List<int>();
            Bitmap b = (Bitmap)img;
            Point pFirstBlack = FindFirstBlack(b);
 
            int blackIndex = pFirstBlack.X;
            int whiteIndex = -1;
            for (int i = pFirstBlack.X + 1; i < img.Width; i++)
            {
                Color c = b.GetPixel(i, pFirstBlack.Y + 1);
                int average = (c.R + c.G + c.B) / 3;
 
                if (average < 128)
                {
                    if (whiteIndex > blackIndex)
                    {
                        order.Add(i - whiteIndex);blackIndex = i;
                    }
                }
                else
                {
                    if (blackIndex > whiteIndex)
                    {
                        order.Add(i - blackIndex);whiteIndex = i;
                    }
                }
            }
            order.Add(img.Width - blackIndex);
 
            int TotalLength = 0;
            foreach (int i in order)
            {
                TotalLength += i;
            }
            int avgLength = TotalLength / order.Count;
            string text = "";
            for (int i = 0; i < order.Count; i = i + 9)
            {
                string s = "";
                s += (order[i] > avgLength ? "1" : "0");s += (order[i + 1] > avgLength ? "1" : "0");
                s += (order[i + 2] > avgLength ? "1" : "0");s += (order[i + 3] > avgLength ? "1" : "0");
                s += (order[i + 4] > avgLength ? "1" : "0");s += (order[i + 5] > avgLength ? "1" : "0");
                s += (order[i + 6] > avgLength ? "1" : "0");s += (order[i + 7] > avgLength ? "1" : "0");
                s += (order[i + 8] > avgLength ? "1" : "0");
                text += chars[s];
                i++;
            }
            return text;
        }


         Gönderdiğimiz resmi decode edecek metodumuzu yazmaya başlıyoruz. Metodumuz bizden parametre olarak Image nesnesini alıyor ve bu nesneyi bitmap nesnesine çevirerek ilk siyah çizgiye ait noktanın bulunması için biraz önce bahsettiğim metoda gönderiyor ve buradan dönen point değerlerini alıyoruz.

         Bu kısımda tanımlamış olduğumuz blackIndex değişkeni yeni bir siyah çizgi bulunduğunda o siyah çizginin başladığı pixel'i temsil edecektir. Zaten bulduğumuz ilk nokta siyah çizgi olduğundan pointimizdeki değeri bu değişkene atayabiliyoruz. whiteIndex ise aynı şekilde en son bulduğumuz beyaz noktanın başlangıç pixel'ini tutacaktır. Henüz bir beyaz nokta bulmadığımızdan buraya değer atamıyor ve başlangıç değeri olan -1'i veriyoruz.

         Sonraki aşamada bulduğumuz ilk siyah noktadan sağa doğru pixel pixel geliyoruz ve her noktanın siyah mı beyaz mı olduğuna karar veriyoruz. Bu döngü içinde bir önceki noktamız siyah iken tekrar siyah bir noktaya gelirsek siyah çizgi devam ediyor demektir. Bunun için ancak renk değişimlerine siyah çizginin veya beyaz çizginin bittiğini anlayabiliriz. Zaten if sınamalarında bu açık bir şekilde gözükmektedir. En sonda bulunan çizgimiz siyah bir çizgi olacağından dolayı resmimizin bittiği yerden geri kalan noktayı ise tekrar collectionumuza ekliyoruz.

         Elimizde bulunan collection'da ki değerlerin siyah, beyaz, siyah, beyaz şeklinde ilerlediğinden bahsetmiştim. Zaten collection'da tuttuğumuz değerler bu çizgilerin uzunlukları. Örneğin Collection'da ilk sırada 10 ikinci sirada 5 değerleri var ise bu bizim için 10 pixel genişliğinde bir siyah çizgi(kalın çizgi), 5 pixel genişliğinde bir beyaz çizgi(ince çizgi) var demektir. Daha sonra bu çizgilerden hangilerinin uzun hangilerinin kısa olduğunu bulmak için collection'da bulunan tüm değerlerin ortalamasını alıyoruz. Bu ortalama değerden büyük olan değerler kalın, küçük olan değerler ise ince çizgileri temsil edeceklerdir.

          Barkod'daki karakterlerin 9 çizgi ile temsil edildiklerini sonrasında ise bir karakter boşluk bırakıldığını söylemiştirm. Bunun için bir döngüde değerleri 8er 8 er dolaşıyoruz ve kalın olanlar için 1 ince olanlar için 0 değerlerini ekleyerek bir string oluşturuyoruz. Bu 9 karakterden oluşan string bize, barkod içindeki 9 çubuğun hangi karakteri temsil ettiğini söyleyecek. Bu değeri bulmak için ise Class'ın constructor'unda tanımlamış olduğumuz List'i kullanıyoruz ve metni barkod içinden okuyoruz.

         Bu kodu örnek bir uygulama ile downloadlar kısmından indirerek inceleyebilirsiniz. Örnek uygulama içerisinde bulunan bu yazdığımız class'ın nasıl çağrılacağına ait örnek kodlar ve screenshotlar aşağıdadır.
 

            BarcodeDecoder dec = new BarcodeDecoder();
            Image img1 = Image.FromFile("a.jpg");
            Image img2 = Image.FromFile("b.jpg");
            pictureBox1.Image = img1;
            pictureBox2.Image = img2;
            label1.Text = dec.DecodeCode39(img1);
            label2.Text = dec.DecodeCode39(img2);




         Umarım faydalı olmuştur.

         oztamer@hotmail.com
         tamer.oz@yazgelistir.com
         oztamer@hotmail.comOrnek Kodlar