Manşet
C# .Net FrameWork 3.5 ve .NetFrameWork 4 versiyonları ile birlikte resim işleme konusunda güzel özellikler kazanmıştır. Burada şimdilik bir resmi gri tonlamalı hale nasıl getireceğimiz anlatılacak olup ilerde diğer resim işleme konuları da ayrıca anlatılacaktır. Resim işleme tamamen bir resmin piksellerindeki değerlerin değiştirilmesi temeline dayanır. İşin püf noktası da burası tekniğinizin kalitesini belirleyen pikseli nasıl ele aldığınızdır. İnternette araştırma yaptığınızda resim işleme ile ilgili birçok kaynak var fakat bunların büyük bir çoğunluğu piksellere erişirken dolaylı yolları kullandığı için biraz yavaş çalışan kodlar paylaşmaktadır. Birazdan anlatacağım teknik ise diğer kodlara kıyasla en az 2-3 kat daha hızlı çalışmaktadır. Kodlamaya geçmeden önce biraz ön hazırlık yapmak gerekiyor. Bunun için Solution Explorer’da projemizin özelliklerini açıyoruz(Proje adı ->Properties). Daha sonra Build Sekmesini açıp “Allow unsafe code” seçeneğini işaretliyip ayarları kaydediyoruz. Bunu yapmamızın sebebi C# da varsayılan olarak Pointer(işaretçi) kullanımı pasiftir önce bunu aktifleştirmek gerekiyor. Daha sonra form üzerine bir adet PictureBox nesnesi oluşturup ismini pctResim diye değiştirin.
Şimdi projemize
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace lerini ekliyoruz. Öncelikle program içinde kullanacağımız değişkenlerimizi oluşturuyoruz.
byte siyah=0; // Piksel renk değeri
int dizisayisi; // rgbdeger dizisi için eleman sayısı
IntPtr baslangic; // Resmin RAM üzerindeki adresi
byte[] rgbdeger; //Resmin pikselleri
Bitmap resim; //gösterilecek resim
Rectangle rct; // resim için çerçeve
BitmapData bmData; // resim üzerinde işlem yapacak sınıf.
Değişkenlerimizi oluşturduktan sonra hangi resim üzerinde çalışacağımızı belirtmemiz gerekiyor.
resim = new Bitmap(@”C:\Fsecurity.jpg”);
Sonra resmimizle aynı boyutta bir çerçeve oluşturuyoruz
rct = new Rectangle(0, 0, resim.Width, resim.Height);
Sonra üzerinde çalışacağımız resmi RAM üzerine aktarıyoruz. Bunun için Bitmap nesnesinin LockBits fonksiyonunu kullanıyoruz. Bu fonksiyon bir tanesi 3 parametre alan diğeri 4 parametre alan 2 adet overload edilmiş şekilde kullanılabilir.Kullanım şekilleri şunlardır.
1- Bitmap.LockBits(Rectangele r, ImageLockMode flags, PixelFormat format);
2- Bitmap.LockBits(Rectangele r, ImageLockMode flags, PixelFormat format, BitmapData bitmapdata);
Ben ilk şeklini kullandım.
bmData = resim.LockBits(rct, ImageLockMode.ReadWrite, resim.PixelFormat);
dilerseniz
bmData = new BitmapData();
resim.LockBits(rct, ImageLockMode.ReadWrite, resim.PixelFormat,bmData);
şeklinde de kullanabilirsiniz. Resmi RAM üzerine aktardığımıza göre bunun hangi adreste olduğunu tespit etmeliyiz. bunun için
baslangic = bmData.Scan0;
komutunu kullanıyoruz. Scan0 özelliği resmin ilk pixel değerinin adresini verir.bunu da IntPtr türünden tanımladığımız baslangic değişkenine aktardık.
Şimdi bir dizi oluşturacağız. Bu dizi byte[] türünden olacak ve içerisine resmimizin piksel değerlerini aktaracağız. Tabii bunun için öncelikle kaç elemanlı bir diziye ihtiyacım olduğunu belirlemem gerekiyo. Herbir pikseli bir dizi değişkenine atayacağıma göre resmim kaç pikselse o kadar sayıda diziye ihtiyacım olacak bunu bulmak için de
dizisayisi = bmData.Stride * resim.Height; // Stride özelliği bitmap nesnesinin tarama genişliğini verir. Daha kaba bir tabirle bir satırın kaç renk elemanından oluştuğunu verir. Örneğin 100 piksellik bir satırda 300 değerini verir çünkü her piksel 3 renk elemanından oluşur.
rgbdeger = new byte[dizisayisi]; // dizimiz oluşturuldu.
Dizimi oluştuğuna göre resmin piksel değerlerini bu diziye aktarmanın zamanı gelmiş demektir. Bunun için Marshal.Copy komutunu kullanıyoruz. bu komut RAM üzerindeki belli bir adresten verileri alır ve diziye aktarır veya tersini yapar.
Marshal.Copy(baslangic, rgbdeger, 0, dizisayisi);
Artık resmin piksel değerlerini diziye aktardık. Hadi biraz resim yapalım Bunun için for döngüsünü kullanıyoruz. Yavaş olur diye düşünmeyin işaretçi kullanmanın verdiği avantajla gerçekten hızlı çalışıyor. Gri tonlamalı resim oluşturmanın kuralı aynı pikseldeki Kırmızı,Mavi,Yeşil değerlerinin ortalamasını almak ve o pikseldeki değerlere bu ortalama değeri tekrar atamaktır.
for (int i = 2; i < rgbdeger.Length; i += 3)
{
siyah = (Byte)Math.Abs((Byte.Parse(rgbdeger[i - 2].ToString()) + Byte.Parse(rgbdeger[i - 1].ToString()) + Byte.Parse(rgbdeger[i].ToString())) / 3);
rgbdeger[i - 2] = siyah;
rgbdeger[i - 1] = siyah;
rgbdeger[i] = siyah;
}
Burada üçer üçer atlamamızın sebebi dizi her pikselin 3 elemandan oluşuyor olması. Yani her 3 dizi elemanı bir araya geldiğinde bir piksel değeri oluşuyor. Resmin bütün piksellerini düzenlediğimize göre şimdi bu değerleri tekrar RAM üzerindeki yerlerine aktarmayalıyız. bunun için yine Marshal.Copy komutunu kullanıyoruz. Tabii ki işlem ters olduğu için parametreler biraz farklı olacak
Marshal.Copy(rgbdeger, 0, baslangic, dizisayisi);
RAM deki piksel değerlerini de güncellediğimize göre artık resimdeki kilidi açabiliriz.
resim.UnlockBits(bmData);
ve son olarak resmi görüntülemenin zamanı geldi. Resim nesnesini picturebox nesnemize atıyoruz.
pctResim.Image = resim;
NOT: BitmapData sınıfı ile işlenen resimler RGB olarak değil BGR olarak düşünülmelidir. Yani Piksel Kırmızı, Yeşil, Mavi olarak değil de Mavi, Yeşil, Kırmızı olarak oluşur
Şimdi projemize
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace lerini ekliyoruz. Öncelikle program içinde kullanacağımız değişkenlerimizi oluşturuyoruz.
byte siyah=0; // Piksel renk değeri
int dizisayisi; // rgbdeger dizisi için eleman sayısı
IntPtr baslangic; // Resmin RAM üzerindeki adresi
byte[] rgbdeger; //Resmin pikselleri
Bitmap resim; //gösterilecek resim
Rectangle rct; // resim için çerçeve
BitmapData bmData; // resim üzerinde işlem yapacak sınıf.
Değişkenlerimizi oluşturduktan sonra hangi resim üzerinde çalışacağımızı belirtmemiz gerekiyor.
resim = new Bitmap(@”C:\Fsecurity.jpg”);
Sonra resmimizle aynı boyutta bir çerçeve oluşturuyoruz
rct = new Rectangle(0, 0, resim.Width, resim.Height);
Sonra üzerinde çalışacağımız resmi RAM üzerine aktarıyoruz. Bunun için Bitmap nesnesinin LockBits fonksiyonunu kullanıyoruz. Bu fonksiyon bir tanesi 3 parametre alan diğeri 4 parametre alan 2 adet overload edilmiş şekilde kullanılabilir.Kullanım şekilleri şunlardır.
1- Bitmap.LockBits(Rectangele r, ImageLockMode flags, PixelFormat format);
2- Bitmap.LockBits(Rectangele r, ImageLockMode flags, PixelFormat format, BitmapData bitmapdata);
Ben ilk şeklini kullandım.
bmData = resim.LockBits(rct, ImageLockMode.ReadWrite, resim.PixelFormat);
dilerseniz
bmData = new BitmapData();
resim.LockBits(rct, ImageLockMode.ReadWrite, resim.PixelFormat,bmData);
şeklinde de kullanabilirsiniz. Resmi RAM üzerine aktardığımıza göre bunun hangi adreste olduğunu tespit etmeliyiz. bunun için
baslangic = bmData.Scan0;
komutunu kullanıyoruz. Scan0 özelliği resmin ilk pixel değerinin adresini verir.bunu da IntPtr türünden tanımladığımız baslangic değişkenine aktardık.
Şimdi bir dizi oluşturacağız. Bu dizi byte[] türünden olacak ve içerisine resmimizin piksel değerlerini aktaracağız. Tabii bunun için öncelikle kaç elemanlı bir diziye ihtiyacım olduğunu belirlemem gerekiyo. Herbir pikseli bir dizi değişkenine atayacağıma göre resmim kaç pikselse o kadar sayıda diziye ihtiyacım olacak bunu bulmak için de
dizisayisi = bmData.Stride * resim.Height; // Stride özelliği bitmap nesnesinin tarama genişliğini verir. Daha kaba bir tabirle bir satırın kaç renk elemanından oluştuğunu verir. Örneğin 100 piksellik bir satırda 300 değerini verir çünkü her piksel 3 renk elemanından oluşur.
rgbdeger = new byte[dizisayisi]; // dizimiz oluşturuldu.
Dizimi oluştuğuna göre resmin piksel değerlerini bu diziye aktarmanın zamanı gelmiş demektir. Bunun için Marshal.Copy komutunu kullanıyoruz. bu komut RAM üzerindeki belli bir adresten verileri alır ve diziye aktarır veya tersini yapar.
Marshal.Copy(baslangic, rgbdeger, 0, dizisayisi);
Artık resmin piksel değerlerini diziye aktardık. Hadi biraz resim yapalım Bunun için for döngüsünü kullanıyoruz. Yavaş olur diye düşünmeyin işaretçi kullanmanın verdiği avantajla gerçekten hızlı çalışıyor. Gri tonlamalı resim oluşturmanın kuralı aynı pikseldeki Kırmızı,Mavi,Yeşil değerlerinin ortalamasını almak ve o pikseldeki değerlere bu ortalama değeri tekrar atamaktır.
for (int i = 2; i < rgbdeger.Length; i += 3)
{
siyah = (Byte)Math.Abs((Byte.Parse(rgbdeger[i - 2].ToString()) + Byte.Parse(rgbdeger[i - 1].ToString()) + Byte.Parse(rgbdeger[i].ToString())) / 3);
rgbdeger[i - 2] = siyah;
rgbdeger[i - 1] = siyah;
rgbdeger[i] = siyah;
}
Burada üçer üçer atlamamızın sebebi dizi her pikselin 3 elemandan oluşuyor olması. Yani her 3 dizi elemanı bir araya geldiğinde bir piksel değeri oluşuyor. Resmin bütün piksellerini düzenlediğimize göre şimdi bu değerleri tekrar RAM üzerindeki yerlerine aktarmayalıyız. bunun için yine Marshal.Copy komutunu kullanıyoruz. Tabii ki işlem ters olduğu için parametreler biraz farklı olacak
Marshal.Copy(rgbdeger, 0, baslangic, dizisayisi);
RAM deki piksel değerlerini de güncellediğimize göre artık resimdeki kilidi açabiliriz.
resim.UnlockBits(bmData);
ve son olarak resmi görüntülemenin zamanı geldi. Resim nesnesini picturebox nesnemize atıyoruz.
pctResim.Image = resim;
NOT: BitmapData sınıfı ile işlenen resimler RGB olarak değil BGR olarak düşünülmelidir. Yani Piksel Kırmızı, Yeşil, Mavi olarak değil de Mavi, Yeşil, Kırmızı olarak oluşur
Hiç yorum yok: