C# ile Görüntü İşleme – 2
Tarih: January 2nd, 2009 | Yazan: Ahmet Kakıcı | Kategori: Programlama | Etiketler: c, görüntü işleme, image processing, kod, Programlama | 34 Yorum »Daha önce görüntü okuma, gösterme ve kaydetme gibi başlıca fonksiyonları vermiştim. Aşağıda ise asıl görüntüyü işeyecek fonksiyonlar bulunmaktadır. Tabii buradaki fonksiyonları kullanbilmek için daha önceden verdiğim şekilde görüntünün dizilere aktarılmış olması gerekiyor.
Önceki yazıda gri seviyeye çevirilmiş görüntümüz vardı eğer bu görüntüyü siyah beyaza çevirmek istiyorsanız bunun için bir eşik değeri seçerek 0-255 arasındaki gri seviye görüntüyü bu seviyeye göre siyah veya beyaz olarak ayırmak gerekiyor. Eşik değerini sabit bir değer olarak belirleyebileceğiniz gibi her görüntüye göre dinamik olarak bir eşik değeri belirleyebilen bir yöntem de mevcuttur: otsu. Otsu algoritması sayesinde üzerinde çalıştığınız görüntüye özel bir eşik değerini otomatik olarak belirleyebilirsiniz. Bunun için görüntünün histogram dizisine ihtiyacınız olacak. İlk yazıda verdiğim kodda histogram çıkartma özelliği yoktu. Bunun için aşağıdaki kodu kullanabilirsiniz:
int[,] pixelArray = new int[pictureBox1.Image.Height, pictureBox1.Image.Width];
int[,] greyPixelArray = new int[pictureBox1.Image.Height, pictureBox1.Image.Width];
int[] histogram = new int[256];
void BuildPixelArray(ref Image myImage)
{
pixelArray = new int[imageWidth, imageHeight];
greyPixelArray = new int[imageWidth, imageHeight];
Rectangle rect = new Rectangle(0, 0, myImage.Width, myImage.Height);
Bitmap temp = new Bitmap(myImage);
BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int remain = bmpData.Stride - bmpData.Width * 3;
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
for (int j = 0; j < bmpData.Height; j++)
{
for (int i = 0; i < bmpData.Width; i++)
{
pixelArray[i, j] = ptr[0] + ptr[1] * 256 + ptr[2] * 256 * 256;
greyPixelArray[i, j] = (int)((double)ptr[0] * 0.11 + (double)ptr[1] * 0.59 + (double)ptr[2] * 0.3);
histogram[greyPixelArray[i, j]]++;
ptr += 3;
}
ptr += remain;
}
}
temp.UnlockBits(bmpData);
}
Burada histogram dizisini de oluşturduktan sonra artık otsu algoritması sayesinde dinamik olarak eşik değerini belirleyebiliriz:
int otsuValue;
void Otsu()
{
double fmax = -1.0;
double m1, m2, S, toplam1 = 0.0, toplam2 = 0.0;
int nTop = 0, n1 = 0, n2;
for (int i = 0; i < 256; i++)
{
toplam1 += (double)i * (double)histogram[i];
nTop += histogram[i];
}
for (int i = 0; i < 256; i++)
{
n1 += histogram[i];
if (n1 == 0)
continue;
n2 = nTop - n1;
if (n2 == 0)
break;
toplam2 += (double)i * (double)histogram[i];
m1 = toplam2 / n1;
m2 = (toplam1 - toplam2) / n2;
S = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2);
if (S > fmax)
{
fmax = S;
otsuValue = i;
}
}
}
Otsu veya sabit bir değere göre görüntüyü siyah beyaza çevirecek kod ise aşağıdadır. Bu fonksiyonun aldığı argThreshold parametresi eşik değerini belirtern 0-255 arasında bir değerdir.
void Binary(int argThreshold)
{
binaryPixelArray = new int[imageWidth, imageHeight];
for (int i = 0; i < imageWidth; i++)
{
for (int j = 0; j < imageHeight; j++)
{
if (greyPixelArray[i, j] < threshold)
binaryPixelArray[i, j] = 0;
else
binaryPixelArray[i, j] = 255;
}
}
}
Görüntüyü siyah beyaza çevirdikten sonra eğer kenar bulma algoritmalarını kullanmak isterseniz bir kaç seçeneğiniz mevcut. Bunlardan en popüleri sobel kenar bulma filtresidir. Aşağıdaki fonksiyonda type parametresi hangi tür kenarların bulunacağını belirtmek içindir. Dikey, yatay, köşegen şeklindeki kenarları veya tamamını ayrı ayrı bulabilirsiniz.
void Sobel(int type)
{
int normalizeMax = 0;
int normalizeMin = maxIntVal;
sobelPixelArray = new int[imageWidth, imageHeight];
int[,] sobelArray1 =
new int[3, 3] {
{ 1, 0, -1 },
{ 2, 0, -2 },
{ 1, 0, -1 }
};
int[,] sobelArray2 =
new int[3, 3] {
{ 1, 2, 1 },
{ 0, 0, 0 },
{-1,-2,-1 }
};
int[,] sobelArray3 =
new int[3, 3] {
{ 2, 1, 0 },
{ 1, 0,-1 },
{ 0,-1,-2 }
};
int G1, G2, G3;
for (int i = 1; i < imageWidth - 1; i++)
{
for (int j = 1; j < imageHeight - 1; j++)
{
G1 = 0;
G2 = 0;
G3 = 0;
for (int k = -1; k < 2; k++)
{
for (int l = -1; l < 2; l++)
{
G1 += greyPixelArray[i + k, j + l] * sobelArray1[k + 1, l + 1];
G2 += greyPixelArray[i + k, j + l] * sobelArray2[k + 1, l + 1];
G3 += greyPixelArray[i + k, j + l] * sobelArray3[k + 1, l + 1];
}
}
if(type == 0)
{
sobelPixelArray[i, j] = Math.Abs(G1) + Math.Abs(G2) + Math.Abs(G3);
}
else if (type == 1)
{
sobelPixelArray[i, j] = Math.Abs(G2);
}
else if (type == 2)
{
sobelPixelArray[i, j] = Math.Abs(G1);
}
else if (type == 3)
{
sobelPixelArray[i, j] = Math.Abs(G3);
}
if (normalizeMax < sobelPixelArray[i, j])
normalizeMax = sobelPixelArray[i, j];
if (normalizeMin > sobelPixelArray[i, j])
normalizeMin = sobelPixelArray[i, j];
}
}
NormalizeArray(ref sobelPixelArray, normalizeMax, normalizeMin);
}
Yukarıda bulunan kodun en altında çağırılan normalize fonksiyonu ise sonuç dizisini 0-255 değerleri arasına düşürmek içindir:
void NormalizeArray(ref int[,] sourceArray,int normalizeMax,int normalizeMin)
{
int factor = normalizeMax - normalizeMin;
for (int i = 0; i < sourceArray.GetLength(0); i++)
{
for (int j = 0; j < sourceArray.GetLength(1); j++)
{
sourceArray[i, j] = (sourceArray[i, j] - normalizeMin) * 255 / factor;
}
}
}
Bir diğer kenar bulma yöntemi ise Prewitt’tir:
int[,] prewittPixelArray;
void Prewitt()
{
int normalizeMax = 0;
int normalizeMin = maxIntVal;
prewittPixelArray = new int[imageWidth, imageHeight];
int[,] PrewittArray1 =
new int[3, 3] {
{-1,-1,-1 },
{ 0, 0, 0 },
{ 1, 1, 1 }
};
int[,] PrewittArray2 =
new int[3, 3] {
{ -1, 0, 1 },
{ -1, 0, 1 },
{ -1, 0, 1 }
};
int[,] PrewittArray3 =
new int[3, 3] {
{-1,-1, 0 },
{-1, 0, 1 },
{ 0, 1, 1 }
};
int[,] PrewittArray4 =
new int[3, 3] {
{ 1, 1, 0 },
{ 1, 0,-1 },
{ 0,-1,-1 }
};
int G1, G2, G3, G4;
for (int i = 1; i < imageWidth - 1; i++)
{
for (int j = 1; j < imageHeight - 1; j++)
{
G1 = 0;
G2 = 0;
G3 = 0;
G4 = 0;
for (int k = -1; k < 2; k++)
{
for (int l = -1; l < 2; l++)
{
G1 += greyPixelArray[i + k, j + l] * PrewittArray1[k + 1, l + 1];
G2 += greyPixelArray[i + k, j + l] * PrewittArray2[k + 1, l + 1];
G3 += greyPixelArray[i + k, j + l] * PrewittArray3[k + 1, l + 1];
G4 += greyPixelArray[i + k, j + l] * PrewittArray4[k + 1, l + 1];
}
}
prewittPixelArray[i, j] = Math.Abs(G1) + Math.Abs(G2) + Math.Abs(G3) + Math.Abs(G4);
if (normalizeMax < prewittPixelArray[i, j])
normalizeMax = prewittPixelArray[i, j];
if (normalizeMin > prewittPixelArray[i, j])
normalizeMin = prewittPixelArray[i, j];
}
}
int factor = normalizeMax - normalizeMin;
for (int j = 1; j < imageHeight - 1; j++)
{
for (int i = 1; i < imageWidth - 1; i++)
{
prewittPixelArray[i, j] = (prewittPixelArray[i, j] - normalizeMin) * 255 / factor;
}
}
}
Prewitt ve sobel’e alternatif olarak Robert kenar bulma filtresi de mevcuttur :
int[,] robertPixelArray;
void Robert()
{
robertPixelArray = new int[imageWidth, imageHeight];
for (int i = 1; i < imageHeight - 1; i++)
{
for (int j = 1; j < imageWidth - 1; j++)
{
robertPixelArray[j, i] = Math.Abs(greyPixelArray[j, i] - greyPixelArray[j - 1, i - 1]) + Math.Abs(greyPixelArray[j, i - 1] - greyPixelArray[j - 1, i]);
}
}
}
Eğer kenar bulma algoritmalarını kullanmadan önce görüntüdeki gürültüleri temizlemek istiyorsanız mean, median ve gaussian filtereleri ile bu işlemi yapabilirsiniz.
Mean filtresi :
int[,] meanPixelArray;
void Mean()
{
meanPixelArray = new int[imageWidth, imageHeight];
for (int i = 1; i < imageHeight - 1; i++)
{
for (int j = 1; j < imageWidth - 1; j++)
{
meanPixelArray[j, i] =
(
greyPixelArray[j, i - 1]
+ greyPixelArray[j, i + 1]
+ greyPixelArray[j, i]
+ greyPixelArray[j - 1, i - 1]
+ greyPixelArray[j - 1, i + 1]
+ greyPixelArray[j - 1, i]
+ greyPixelArray[j + 1, i - 1]
+ greyPixelArray[j + 1, i + 1]
+ greyPixelArray[j + 1, i]
) / 9;
}
}
}
Median filtresi:
int[,] medianPixelArray;
void Median()
{
medianPixelArray = new int[imageWidth, imageHeight];
int[] tempArray = new int[9];
for (int i = 1; i < imageHeight - 1; i++)
{
for (int j = 1; j < imageWidth - 1; j++)
{
int counter = 0;
for (int k = -1; k < 2; k++)
{
for (int l = -1; l < 2; l++)
{
tempArray[counter++] = greyPixelArray[j + l, i + k];
}
}
System.Array.Sort(tempArray);
medianPixelArray[j, i] = tempArray[4];
}
}
}
Gaussian filtresi
int[,] gaussianPixelArray;
void Gaussian()
{
gaussianPixelArray = new int[imageWidth, imageHeight];
int[,] gaussianArray =
new int[5, 5] {
{1,4,7,4,1},
{4,16,26,16,4},
{7,26,41,26,7},
{4,16,26,16,4},
{1,4,7,4,1}
};
int tempSum;
for (int i = 2; i < imageHeight - 2; i++)
{
for (int j = 2; j < imageWidth - 2; j++)
{
tempSum = 0;
for (int k = -2; k < 3; k++)
{
for (int l = -2; l < 3; l++)
{
tempSum += greyPixelArray[j + l, i + k] * gaussianArray[k + 2, l + 2];
}
}
gaussianPixelArray[j, i] = tempSum / 273;
}
}
}
Daha sonraki yazıda yapısal (morfolojik) işlemleri yapan filtreleri de açıklamalarıyla birlikte vermeyi düşünüyorum. O zamana kadar hepinize kolay gelsin.

Ellerine sağlık Ahmetçim, çok faydalı oldu görüntü işleme hakkındaki yazı dizin.. Teşekkkür ederim.. bir arada, fraktal geometri hakkındaki düşüncelerini paylaşırsan daha menmun olacağım (9 ocaktan sonrası ilgilendirmiyor;))).. İyi yazmalar..
Emrah 9 ocak tarihine kadar fraktalı bırak adımı yazmaya vaktim yok :(
[...] algılama, gürültü yok etme ve yapısal (morfolojik) filtreleri de bir sonraki yazımda paylaşmak üzere. İyi işlemeler diliyorum :) Benzer yazılar:C# ile Görüntü İşleme – [...]
bekliyoruz hoca
Bende bekliyorum vaktin olunca yazarsan iyi olur.
yazılarınızı dikkatle okuyup,uygulama yapmaya çalıştım.
Anladığım kadarıyla Görüntü işleme uygulamalarında işlem sırası şöyle olmalıdır.
1 – Resmin pixelleri taranarak gri seviyeye dönüştürülür.
2 – otsu algoritması ile yada sabit eşik değerine göre resim siyah beyaz hale getirilir.
3 – Siyah beyaz resim üzerindeki gürültüleri temizlemek için mean,median yada gaussian filtreleri uygulanır.
4 – Filtrelenen görüntü kenar bulma algoritmalarından(sobel , prewitt yada robert…) biri ile netleştirilir.
Artık net olarak işlenen resim bundan sonrasında istenilen doğrultuda kullanılır.
Benim yazılarınızda anladığım ve uygulanması gereken işlem sırası budur.Öğrendiklerim arasında yanlış bilgilar var ise düzeltirmisiniz.
Uygulama esnasında karşıma çıkan bir problemde şudur:Form ekranına picturebox1′de orjınal resim bulunmakta. picturebox2 ye ise siyah beyaza çevrilmiş resmi gönderiyorum. for dongulerınde picturebox’ların width ve height değerlerini kullanıyorum fakat oluşturulan dizinin küçük olduğuna dair hata alıyorum.Picturebox ile resim boyutları arasındaki fark nereden kaynaklanıyor acaba.
Teşekkür ediyorum.iyi çalışmalar.
Görüntünün boyutlarını picturebox’tan alıyorsan, picturebox’ın boyutlarının görüntünün boyutlarına eşit olduğuna emin olmalısın. Tam hatırlamıyorum ama AutoSize diye bir özelliği vardı picturebox nesnesinin. Ona true değeri verirsen picturebox boyutları yüklenen görüntünün boyutuna göre otomatik olarak ayarlanıyor. İkinci picturebox değerlerini ise birincinin boyutlarını okuyarak kendin değiştirebilirsin de.
Sorununu doğru anladıysam ve tek hata boyut hatasıysa bunları denersen işini görebilir.
Kolay Gelsin.
autosize özeliğini true yapmıştım ama tekrar deniyeceğim.işlem sırası ile ilgili bir cevap yazmadığınızdan sıralamanın doğru olduğu kanısına varıyorum.Bir sonraki yazınızı sabırsızlıkla bekliyorum.
teşekkür edirim.
boyut problemini çözdüm. bundan sonra mean filtresini ve kenar bulma algoritmasını deniyeceğim.
İşlem sırasına bir yorum yapmadım çünkü hedefinizin ne olduğunu bilmiyorum.
Ancak aşağıdaki adresten efeklterin ne gibi işlere yaradığını ve sonuçlarını görebilirsiniz;
http://homepages.inf.ed.ac.uk/rbf/HIPR2/wksheets.htm
Kolay Gelsin.
yapmaya çalıştığım sey,görüntüsünü aldığım nesnenin veri tabanında kayıtlı nesne olup olmadığını bulmak.Örneğin bir kalem gorüntüsünden bulunan nesnenin kalem olduğunu analayabilmek.
Son yazınızda “Daha sonraki yazıda yapısal (morfolojik) işlemleri yapan filtreleri de açıklamalarıyla birlikte vermeyi düşünüyorum. O zamana kadar hepinize kolay gelsin.” demiştiniz.
erosion ve dilate filtrelerini internetten araştırdım,bi takım kaynak kodlar da buldum.Mantığını anladım fakat uygulamada sıkıntı yaşıyorum.
Eğer yazınız, hazır olmasa dahi yayınlanacak durumda ise yapısal (morfolojik) filtreleme konusunu yayınlayabilirmisiniz.Yada kaynak kod noktasında yardımcı olurmusunuz.Bunun nedeni ise denemelerimi sizin verdiğiniz kaynak kodlar üzerinden yapıyorum.
iyi çalışmalar dilerim.
kodları çalıştırırken tam olarak ne gibi değiştirmeler yapmalıyım çalıştırmak için hataları düzeltmeye çalışırken içinde kayboldum lütfen mail adresime yardımcı olur musunuz?
@Mustafa morfolojik filtreleri de yazdım 3. yazıda.
@Rıdvan herhangi bir değişiklik yapmana gerek yoktu ki !?
Elinize sağlık, ama ekran görüntüsü falan verseydiniz sonuçları gözleme imkanımız olurdu…
Madem ihtiyaç var yakında ekran görüntülerini de eklerim.
[...] işleme ile ilgili kodları verdiğim bir yazıya gelen yorumdan sonra yazdığım programdan aldığım sonuçları da yayınlamaya karar [...]
Hocam 2 tane resmi tek bir resim gibi üst üste işlememiz mümkün müdür? yani 2 aynı boyutlu jpg resmi var. 1.resmi 2 resmin üzerine işlemek istiyorum, bu mümkünse naslı yapmalıyım?
Soru bana değil ama evet mümkün. Bunun için her iki resimden okuduğun pixelleri kırmızı yeşil ve mavi renklerine ayır. Sonra herbirini ayrı ayrı topla ve ikiye böl, yani ortalamalarını al k=(k1+k2/2), y=(y1+y2)/2 ve m=(m1+m2)/2. Daha sonra son durumu pixele yaz. Böylece aynı çerçevede her iki resim görünebilir. Eğer biri daha baskın olsun istiyorsan, baskın olmasını istediğini 2/3 ile çarpar, diğerini 1/3 ile çarpar, sonra bunları toplar 3′e bölersin…
Hocam 2 tane resmi tek bir resim gibi üst üste işlememiz mümkün müdür? yani 1 aynı boyutlu jpg resmi var. 1.resmi 2 resmin üzerine işlemek istiyorum, bu mümkünse naskı yapmalıyım?;
Hocam Mean algoritmasını oluşturdum fakat burdaki MeanArray dizisini nasıl kullanacaz acaba
Mean fonksiyonu greyPixelArray dizisi içindeki verileri uygun katsayılarla çarpıp topladıktan sonra meanPixelArray dizisine yazıyor.
Detaylı bilgi için http://homepages.inf.ed.ac.uk/rbf/HIPR2/mean.htm
ahmet bey sizden istirhamim youtubeye koyduğum videoların ayarlarini seçerken aramalarda gozukucek goruntu resmını belirlemek için sadece 3 tane resim çikiyor fakat ben firmamin logosunun en one gelmesini istiyorum bunu nasil sağlarim yardimci olursaniz sevinirim tam olrak anlatmak istediğim şey youtubede kı videolarimizin youtubede yayınlanirken kı gozuken goruntuyu ben o video içersinde istediğim her hangi bir goruntu nasil yaparim
Yakup bey youtube video için uygun görüntüyü neye göre seçiyor bilmediğim için bu sorunuza cevap veremeyeceğim.
Ahmet hocam bir sorum olcaktı ben optik okuyucu sistemini SetPixel ile beli bir noktanın belli bir rgb değerlerini alsam olmaz mı diye soracaktım Kolay gelsin
SetPixel ve GetPixel fonksiyonları yukarıdaki yönetme göre çok çok daha yavaştır.
merhaba hocam.biz canny edge detectionla resim işlemeye çalışıyoruz.opencv kütüphanesiyle yaptık fakat hazır fonksiyon kullanmadan yapmamız lazım.bize hangi kodları kullanacağımızı söyleyebilirmisiniz?
yardımınız için teşekkürler
Hangi dilde yazmak istiyorsunuz ?
Merhaba hocam bende şuan görüntü işleme ile ilgilneniyorum.ancak ben c ile yüz tanımayla ilgileniyorum.ancak
Bitmap img1 = new Bitmap(pictureBox2.Image);
img1.Save(@”C:\”);
img1.Dispose();
bu şekilde fotoyu kaydediyorum.ancak foto kaydolmasına rağmen devamında ki işlemlerde hata alıyorum..
sanki hafızada kalıyor.bunu hafızan nasıl silebilirim
Ne gibi bir hata veriyor?
Ahmet Bey öncelikle yazmış olduğunuz yazılar için çok teşekkür ederim. Bunları deneyeceğim bir de bundan bir aşama sonrası olan karşılaştırma olayını da kullanmak istiyorum. Bu konuda nasıl birşey tavsiye edersiniz? Hangi yollar ile yapmalıyım?
otsu metodundan dönen değeri,binary fonksiyonunda parametre olarak kullanılacak sanırım,bir de binary fonksiyonunda argthreshold değeri fonksiyon içerisinde threshold değerini ifade ediyor değil mi ,iyi çalışmalar ,teşekkür ederiz çalışmalarınız için .
Dedikleriniz tamamen doğru.
iyi günler hocam benim C# veya java ile fotograftan plaka okuma projesi yapmam gerekiyor bunu anlatan bi makale yayınlamanız mümkün müdür veya bana yardımcı olabilecek bir kaynak kodu göndermeniz eger varsa tabi