Görüntü İşleme : Ölçülmüş veya kaydedilmiş olan elektronik (dijital) görüntü verilerini, elektronik ortamda (bilgisayar ve yazılımlar yardımı ile) amaca uygun şekilde değiştirmeye yönelik olarak yapılan bilgisayar çalışması. [wp]

 

 

      Bir görüntü gözek(pixel)lerden, gözeklerde kırmızı(R)-yeşil(G)-mavi(B) renk değerlerinden oluşur. Gözeklerin rengi RGB olarak üç ana rengin birleşimi şeklinde ifade edilir. R-G-B renk değerleri 0 ila 255 arasında değişir. 0 en koyu ton iken 255 e gittikçe açılır. Örneğin mavi rengin değeri :(0,0,255) kırmızın :(255,0,0) ve yeşilin de :(0,255,0) dir. Diğer renkler bu değerlerin değişmesi ile elde edilir. Artı olarak  32bitlik bir görüntünün gözekleri 4 kısından oluşur ve 4 kısımda görüntüye saydamlık derecesini veren 'Alpha' kanalıdır.

     Görüntünün bir satırda bulundurduğu gözeklerin sayısı o görüntünün genişliğini(width), satır sayısıda o resmin boyunu(height) verir.Boyutlarda (800x600), (1024x768) gibi iki değerli şeklinde yazılır. Bu değerlerden ilki görüntünün genişliğini, ikinci değer ise boyunu verir.

    C# ile görüntü işlemek için bir çok yöntem mevcut olmasına rağmen daha hızlı bir yöntem olduğu için pointer kullanacağım. Kullandığım yöntemin ana kalıbı aşağıdaki gibidir.

24bitlik format için ;



        public Bitmap griton(Bitmap resim)
        {
            unsafe
            {
                BitmapData bmpdata = resim.LockBits(new Rectangle(0, 0, resim.Width, resim.Height),
                        ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                byte* p = (byte*)bmpdata.Scan0;
                int atıl = bmpdata.Width*3 - bmpdata.Stride;

                for (int i = 0; i < bmpdata.Height; i++)
                {
                    for (int h = 0; h < bmpdata.Width; h++)
                    {
                        byte ort = Convert.ToByte((p[0] + p[1] + p[2]) / 3);
                        p[0] = p[1] = p[2] = ort;
                        p += 3;
                    }
                    p += atıl;
                }

                resim.UnlockBits(bmpdata);

                return resim;
            }
        }

32bitlik format için ;


        public Bitmap griton(Bitmap resim)
        {
            unsafe
            {
                BitmapData bmpdata = resim.LockBits(new Rectangle(0, 0, resim.Width, resim.Height),
                        ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

                byte* p = (byte*)bmpdata.Scan0;

                for (int i = 0; i < bmpdata.Height; i++)
                {
                    for (int h = 0; h < bmpdata.Width; h++)
                    {
                        byte ort = Convert.ToByte((p[0] + p[1] + p[2]) / 3);
                        p[0] = p[1] = p[2] = ort;
                        p += 4;
                    }
                }

                resim.UnlockBits(bmpdata);

                return resim;
            }
        }


      Dikkat ettiyseniz 24bitlik formatın aksine burada atıl oluşmamaktadır.

      Kodları unsafe bloğunun içinde yazabileceğimiz gibi metodu aşağıdaki gibi de tanımlayabiliriz.



        public unsafe Bitmap griton(Bitmap resim)
        {
            BitmapData bmpdata ...
            ....
       }


      Yukarıdaki kodları satır satır açıklarsak;

      İlk olarak pointerlar olarak çalışacağımız için kodları "unsafe" bloğu içinde yazmalıyız veya metodu tanımlarken "unsafe" özlelliğini kullanmalıyız. Yalnız projemizde unsafe komutunu kullanabilmemiz için öncelikli olarak projenin ayarlarından unsafe kullanımına izin vermeliyiz. Bunun için, 'Project' menüsünden, '[Proje ismi] Properties', açılan pencerede 'Build' seçeneğine girip 'Allow unsafe code' seçeneğini aktif hale getiriyoruz. 

      Ayrıca çizim işlemlerini yapabilmemiz için projemize aşağıdaki isim uzaylarını eklemeliyiz.

using System.Drawing;
using System.Drawing.Imaging;

 



   BitmapData bmpdata = resim.LockBits(new Rectangle(0, 0, resim.Width, resim.Height),
           ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

       Resmin bitlerini ramde kitleyerek üzerinde işlem yapabilceğimiz hale getiriyor. Aldığı parametreler ;

"new Rectangle(0, 0, resim.Width, resim.Height) " , resmin üzerinde işlem yapılacak alanı belirtir. Yazıldığı şekilde, resmin (0,0) noktasında sonuna kadar tüm alanı kullanır.

"ImageLockMode.ReadWrite" , kilitli alan üzerinde işlem izni. ".ReadWrite" hem okumaya hem yazmaya, ".ReadOnly" adından da anlaşılacağı gibi sadece okurken, ".WriteOnly" sadece yazmaya izin verir.

"PixelFormat.Format24bppRgb" kilitli alanın kullanacağı formatı belirtir.Genel olarak ".Format32bppArgb" ve ".Format24bppRgb formatları kullanılmaktadır. 32 bit formatın 24 formata göre farkı, bir gözekin 3 (r-g-b) yerine 4 (r-g-b-a) parçası olması.


 byte* p = (byte*)bmpdata.Scan0;

 
Ram üzerinde bir gözek ilerlemek.

         Bu komut ile kilitli bellek alanının ilk noktasına ulaşılmaktadır. Resim verilerinin ram üzerinde yerleşimini, resmin gözek yerleşiminin(2 boyutlu) tersine tek satır olarak yerleşmiş olduğunu düşünebiliriz. Yani bir gözekin 3 renk değeri(b-g-r) bitince diğer gözeğin ilk renk değeri (b) gelmektedir. Yukarıdaki resimde de temsili olarak gösterildiği şekilde, p=p[0] ilk renk değerini(mavi) verirken p[1] bir sonraki(yeşil) vermektedir. Eğer p yi bir artırırsak p[0] artık 2. renk değerini(yeşil) verirken p[1] 3. renk değerini(kırmızı) verir. Gözeklerin renk değerlerine ulaşmak için; eğer p değeri gözeğin başındaysa p[0] o gözeğin mavi(b), p[1] yeşil(g) ve p[3] de kırmızı(r) bileşenini vermektedir. Dikkat ettiysenin dizilimler standart r-g-b diziliminin tersine bellekte b-g-r (mavi-yeşil-kırmızı) şeklinde dizilmiştir. Eğer 32bit formatını seçtiyseniz, p[3] değeri bize o gözekin saydamlık derecesini verir. Tam görünür bir gözek için bu değer255 , hiç görünmemesi içinde bu değer 0'a ayarlanır. 32bitlik formatta bir sonraki gözeğe ulaşmak için p, 4 artırılır.

for (int i = 0; i < bmpdata.Height; i++)
{
    for (int h = 0; h < bmpdata.Width; h++)
    {
        byte ort = Convert.ToByte((p[0] + p[1] + p[2]) / 3);
        p[0] = p[1] = p[2] = ort;
        p += 4;
    }
}

        Görüntü üzerinde gözek gözek yapılacak (noktasal) işlemler için (parlaklık, gri ton, negatif gibi) tüm gözekleri dolaşacak şekilde içiçe iki döngü yapıyoruz. İçteki döngü bir satırın tüm gözeklerini dolaşırken, dıştaki döngü hangi satırda olduğumuzu belirliyor. Satır içinde dolaştığımız için bir gözekle işimiz bittiğinde bir sonraki gözeke geçmek için iç döngüde p'yi 3 artırıyoruz(32bit te 4). Bir satır bittiğinde de dıştaki döngünün sonunda p'ye atılı ekliyoruz(sadece 24bit için). Yukarıda ki kodda 32bitlik kullanılarak, bir resmi gri tonlarına çevirme uygulanmıştır. Bir gözekin tüm renk değerlerinin ortalaması alınıp, tüm renk değerlerine bu ortalama değeri atanmaktadır. Bir gözekle işimiz bittiğine bir sonraki gözeğe "p+=4;" kodu ile geçebiliyoruz. Artık p[0] değeri bize diğer gözeğin mavi renk değerini verecektir.

resim.UnlockBits(bmpdata);

 

Burada ise daha önce ram üzerinde kilitleyip üzerinde işlemler yaptığımız bellek alanını resme geri atıyoruz.

Burada anlatılanları uygulamak için ufak bir uygulama ve çeşitli Gİ örnekleri Kod Kütüphanesinde yayınlanacaktır....

 

Yanlış olduğunu, tam açıklanmamış olduğunu düşündüğünüz veya eklenmesini istediğiniz bir yer varsa lütfen yorumlarınızla mesajı zenginleştiriniz...

Not: Fazla kod işlerine girmeden hazır Gİ kodları kullanmak isterseniz, C# ile hazırlanmış AForge kütüphanesini deneyebilirsiniz.

 

 

5 Yorum