kişisel internet günlüğü
İlk yazıda hızlıca MATLAB programlamanın temellerini atmıştık. Şimdi de sıra geldi görüntü işlemek için gerekli temellere. Bundan böyle ilgi alanımıza resimler ve videolar girecek! Videoların arka arkaya gelen (yani içinde zamansal bilgi barındıran) imge/resim dizisi olduğunu biliyoruz. (Ben bu imge kelimesine pek ısınamadım, o yüzden resim demeyi tercih edeceğim.)
Cevap aradığımız sorular basitçe şöyle:
Bugün resim türleri, diskten resim okuma ve diske geri yazma, resim filtreleme gibi temellere değinelim. Videolarla çalışma işini de başka bahara bırakalım.
Not: Bu yazıdaki kodları çalıştırabilmeniz için İmge İşleme Araçkutusu (Image Processing Toolbox) yüklü olmalıdır. Bunu, komut satırına ver komutunu girerek öğrenebilirsiniz. Örneğin bendeki MATLAB’de Image Processing Toolbox sürüm 6.0 yüklü.
Yine Matlab Programlamaya Giriş‘te yaptığımız gibi işlere başlamadan önce clear ile ortamdaki değişkenleri temizleyelim ve clc ile de komut ekranını temizleyelim. Ve artık konumuza geçelim…
clear clc
Bilindiği üzere resimler farklı renklere sahip küçük küçük blokların bir araya gelmesiyle oluşur. Yani resimlerin yapıtaşları piksel adını verdiğimiz bu bloklardır. Eğer ekran çözünürlüğünü biraz azaltıp ekrana yaklaşırsanız pikselleri rahatlıkla gözlemleyebilirsiniz.
Bahsedilen pikselleri değerler olarak düşünürsek resmi de bu değerlerden oluşan matris olarak düşünmeliyiz. Bu değerleri kaç seçersek deri rengine benzer olur, en yüksek kaç seçebiliriz, skaler mi olmalı yoksa vektör olabilir mi gibi soruları cevaplamalıyız. Ancak bu sorulara çözüm getirdiğimiz takdirde (yani değerlerin ifade edilişi hakkında bir uzlaşıya vardığımızda) ekrandan istediğimiz renkleri göstermesini bekleyebiliriz.
Resimleri temel olarak dört türe ayırabiliriz:
Şimdi teker teker bunları ele alalım.
İkilik resimler
İkilik bir resimde her piksel 0 (siyah) veya 1 (beyaz) değerine sahiptir ve resim logical tipinde saklanır. Yazılarda ikilik resimler için bw önekini kullanacağız.

Yukarıda ikilik bir resme ve piksel değerlerine güzel bir örnek gösterim var. Şimdi de biz bir örnek yapalım. MATLAB’in yüklenmiş olduğu dizini kök dizin olarak kabul edersek ‘toolbox/images/imdemos’ dizininde örnek resimler var. Bunlardan ‘circles.png’ resmini okuyalım.
Not: Buradaki resimleri okumak için uzun uzun adres yazmaya gerek yok çünkü bu dizin path ile görülebiliyor. MATLAB dosya erişimlerinde, bulunduğunuz dizini baştaymış gibi düşünerek path fonksiyonuyla gözüken dizinleri sırası ile dener.
bwImg = imread('circles.png'); imshow(bwImg) title('İkilik resim')

Bakalım bwImg‘nin boyutları ve tipi neymiş?
sz = size(bwImg) imgType = class(bwImg)
sz = 256 256 imgType = logical
Peki içerdiği farklı değerler nelermiş?
unique(bwImg)
ans = 0 1
Görüldüğü üzere bu resim 256×256‘lık bir matris ve logical tipinde. Ayrıca sadece 0 ve 1 değerlerinden oluşuyor. Peki bu matrisin 5. satırındaki elemanlar resimde nereye tekabül ediyor? Beyaza boyayıp görelim.
bwImgCopy = bwImg; bwImgCopy(5,:) = 1; imshow(bwImgCopy)

Peki 7.sütun?
bwImgCopy = bwImg; bwImgCopy(:,7) = 1; imshow(bwImgCopy)

Demek ki resmi ifade eden matrisin satırları resmin yatay elemanlarını, sütunları da resmin dikey elemanlarını oluşturuyor, fakat (1,1) koordinatları resmin sol alt değil de sol üst kösesine denk geliyor.
Görüntü işleme de ikilik resimleri genelde maske olarak kullanırız. Yani işlem yapacağımız bir resimle eş boyutlarda bir maske oluştururuz. Resmi işleme sokarken yalnızca ilgili maskede beyaz olan yerleri kullanırız. İlerideki yazılarda buna uygun örnekler mutlaka denk gelir, şimdilik bitirelim.
İndekslenmiş resimler
Bu konuya başlamadan bilmemiz gereken birşey var: Bilgisayar ekranında gördüğümüz tüm renkler üç ana rengin belli oranlarda karışımından oluşur. Işıktaki ana renkler kırmızı, yeşil ve mavidir. Literatürde toplamsal (additive) renkler olarak geçer. RGB (R=kırmızı, G=yeşil, B=blue) kısaltması olarak da karşımıza çıkar.

(Halbuki bize resim dersinde yeşil yerine sarıyı göstermişlerdi :) Resim dersinde gördüğümüz renkler çıkarımsal (subtractive)nrenklerdi. Yazıcıda da kullanılan bu renkler cyan, magenta ve sarıdır.)

Artık herhangi bir rengin 3 farklı bileşenin karışımından oluştuğunu biliyoruz. Şimdi şöyle düşünün, siz bana her piksel için ona ait üç değeri değil de sadece bir değer göndermek istiyorsunuz. Benim gelen renkleri çözümleyebilmem için bir anahtara (eşleştirme haritasına) sahip olmam lazım ki örneğin bana 3 yolladığınız da turuncu demek istediğinizi bileyim. Yani bana yollayacağınız her farklı değer için bir tane de 3 elemanlı anahtar yollamanız gerekli. İşte bu anahtara colormap, skalar değerlerden oluşan bu matrise de indekslenmiş resim diyoruz. Bu değerler logical, uint8, uint16, single veya double tipinden olabilirler. m adet farklı değer olduğunu düşünürsek, indekslenmiş resim single veya double tipinde ise değerler 1 ile m arasında, diğer tipler içinse değerler 0 ile m-1 arasında olmalı. colormap de mx3‘lük, her satırı bir renge denk düşen double bir matris olmalı.

Üstteki örnekte görüldüğü üzere 5 ile ifade edilen renk aslında 5. satırdaki 0.2902xR + 0.0627xG + 0.0627xB‘dir.
Şimdi de kendi örneğimize geçelim ve palyaço (’clown’) resmini yükleyelim. X değerini indekslenmiş resim, map‘i de anahtar olarak kullanalım.
s = load('clown')
s = X: [200x320 double] map: [81x3 double] caption: [2x1 char]
Dikkat ederseniz resimleri okumak için genelde imread kullanıyoruz, fakat indekslenmiş resimlerin renk anahtarına ihtiyacımız olduğu için önceden hem değerleri hem de anahtarı kaydetmişiz ve şimdi onları load ile yüklüyoruz.
Mesela palyaço resminin (2,5) koordinatında hangi renk olacakmış, bir bakalım. (Not: s değişkeni bir nesne ve bu nesneye ait değişkenlere ulaşabilmek için nokta operatörünü kullanıyoruz.)
s.X(2,5)
ans = 69
Peki 69 ile ifade edilen renk hangisi?
s.map(69,:)
ans = 0.9961 0.7031 0.1250
O halde bu renk bol miktarda kırmızı ve yeşil, az miktarda da mavi içeriyor.
Kaç farklı renk bulunduğuna bakalım:
size(s.map,1)
ans = 81
İndekslenmiş resimleri göstermek için iki adım kullanacağız.
image(s.X) colormap(s.map) title('İndekslenmiş resim')

Başka renk anahtarları kullansaydık, başka resimler gözleyebilirdik! colormap fonksiyonunun kullanımına bakarsanız önceden tanımdı bazı değerlerle de kullanabilirsiniz. Haydi birisini deneyelim:
colormap(summer) title('''summer'' renk anahtarı ile indekslenmiş resim')

Bu alt konuyu bitirmeden önce bu kullanımın faydalarını düşünelim. Her piksel için 3 elemanlı vektör kullansaydık toplamda çok yer kaplayacaktı. Hele bir de bu değerlerin 8 bitlik olacağını düşünürsek her piksel için 24 bit kullanmalıyız. (Antiparantez: Hani hep duyarız 24 bitlik resim diye: 0-255 arası bileşenler, yani 8 bit kırmızı, 8 bit yeşil, 8 bit mavi. 8 bit de şeffaflık kullansaydık 32 bit olurdu.) Fakat indekslenmiş resim kullanırsak, örneğin 60 farklı renk varsa m=2^n ve n>=60, n bir tamsayı olmak üzere piksel başına m=6 bit kullanarak işin içinden çıkabiliriz. Tabii ki colormap için kullanacağımız mx3 kadar double hafızasını da gözardı etmemeliyiz.
İşin zor kısmı bitti, şimdi daha aşina olduğunuz gri (aydınlık) seviyeli resimlere geçelim.
Gri seviye resimler
Gri seviye resimler elemanları belli aralıklar arasındaki aydınlanma değerlerini gösteren matrislerdir. uint8, uint16, int16, single veya double tipi olabilirler. single veya double tipinde ise değerleri 0 siyahı, 1 beyazı belirtmek üzere 0-1 aralığındaki gerçel sayılardır. Diğer tipler ise intmin(class(I)) en düşük aydınlanma değerini, yani siyahı, intmax(class(I)) ise en yüksek aydınlanma değerini, yani beyazı temsil eder. Örneğin unit8 8 bitlik gri seviye resmin sınıfıdır ve değerleri 0 ile 255 arasında değişen tamsayılardır.

Yukarıdaki örnekte double tipindeki aydınlanma değerlerini gözlemleyebilirsiniz.
Şimdi de kendi örneğimizi yapalım. Bunun için de bozuk paraları (’coins.png’) kullanalım.
img = imread('coins.png'); imshow(img) title('Gri seviye resim')

Resmin tipine bakalım:
class(img)
ans = uint8
Demek ki, MATLAB gri seviye resimleri uint8 tipinde yüklüyor. Ortalardan bir bölüme bakalım:
img(51:60,28:33)
ans = 58 57 61 113 173 189 58 57 59 109 171 184 59 59 57 100 168 180 59 59 56 89 161 181 58 59 56 80 157 183 59 60 57 68 140 184 58 60 60 59 108 174 58 60 61 57 84 162 59 60 60 58 65 139 58 58 60 61 57 100
Görüldüğü üzere değerler tamsayı ve şu anki kısımdan görülmediği ama bizim bildiğimiz üzere 0 ile 255 arasındalar. (0,255 dahil)
Bizim double tipinde bir resme ihtiyacımız olsaydı im2double ile dönüşümü yapabilirdik. Haydi yapalım:
img = im2double(img); class(img)
ans = double
img(51:60,28:33)
ans = 0.2275 0.2235 0.2392 0.4431 0.6784 0.7412 0.2275 0.2235 0.2314 0.4275 0.6706 0.7216 0.2314 0.2314 0.2235 0.3922 0.6588 0.7059 0.2314 0.2314 0.2196 0.3490 0.6314 0.7098 0.2275 0.2314 0.2196 0.3137 0.6157 0.7176 0.2314 0.2353 0.2235 0.2667 0.5490 0.7216 0.2275 0.2353 0.2353 0.2314 0.4235 0.6824 0.2275 0.2353 0.2392 0.2235 0.3294 0.6353 0.2314 0.2353 0.2353 0.2275 0.2549 0.5451 0.2275 0.2275 0.2353 0.2392 0.2235 0.3922
Değerlerin [0-1] aralığında double değerlere dönüştüğü gözüküyor. Aynı şekilde im2uint8 fonksiyonu ile de ters yönde dönüşüm yapabilirdik. Diğerleri için de yardıma bakabilirsiniz.
Buradan şöyle bir sonuç çıkarmalıyız. Resimlerde çalışırken hangi tür olduğunun yanısıra elemanlarının sınıfı da (veri tipı) önemlidir. Örneğin bazı fonksiyonlar uint8 tipi için çalışmaz, o yüzden resimleri okurken hep aynı tipe dönüştürmek iyi bir alışkanlıktır.
Haydi bir tane de örnek resmi biz sentezleyelim. Önce bir ızgara oluşturalım:
x = 1:3; y = 7:10; [X, Y] = meshgrid(x, y)
X = 1 2 3 1 2 3 1 2 3 1 2 3 Y = 7 7 7 8 8 8 9 9 9 10 10 10
Örnekte görüldüğü gibi X ve Y‘nin aynı hücredeki elemanlarını yanyana koyarsak x ve y‘lerin sıralı kombinasyonları oluşur. Şimdi daha büyük bir ızgara oluşturalım, sonra bir fonksiyon yazalım ve bu ızgarayı
tanım kümesi olarak kullanalım.
x = linspace(-pi, pi, 300); [X, Y] = meshgrid(x); A = 10; imgSynth = sin(A*(X.^2 + Y.^2)); imshow(imgSynth)

Güzel halkalar çizdik! Çok dağıtmadan veri tipine, en küçük ve en büyük değerlere bakalım ve bitirelim.
class(imgSynth)
ans = double
max(max(imgSynth))
ans = 1.0000
min(min(imgSynth))
ans = -1.0000
Artık biliyoruz ki -1 siyah, +1 beyaz, aradaki değerler de gri seviyeler oldular.
Gerçek-renkli resimler
Son olarak gerçek-renkli resimlere değineceğiz. Kırmızı, mavi ve sarıyı oluşturacağımız güzel bir örnekle başlayalım:
RPlane = [1 0 1]; GPlane = [0 0 1]; BPlane = [0 1 0]; rgbImg = cat(3, RPlane, GPlane, BPlane); size(rgbImg)
ans = 1 3 3
imagesc(rgbImg) axis image title('Kırmızı, mavi ve sarı renkler')

cat fonksiyonu ile 1×3‘lük üç vektörü arka arkaya birleştirip 1×3x3‘lük yeni bir değişken oluşturduk. Evet, gerçek-renkli resimler mxnx3‘lük dizilerden başka birşey değil. Bunu (R,G ve B’nin oranlarını ifade eden) üç gri seviye resmin arka arkaya konulması olarak düşünebiliriz. Örnekte olduğu gibi sarı rengi kırmızı ve yeşilin karışımından elde ederiz. (Not: Bu arada piksellerin orta noktalarının tam değerlere geldiğine dikkat edin! Ayrıca imshow, image ve imagesc arasındaki farkları yardımdan öğrenin.)
Gerçek-renkli resimlerle çalışırken renk anahtarları bir işe yaramaz!
colormap(winter) title('Yine kırmızı, yine mavi, yine sarı!')

Şimdi de alttaki örneğe bakarak gerçek bir resimde bahsettiklerimizin neye tekabül ettiğini görelim:

Artık kendi örneğimize geçmenin vakti geldi. Bu sefer MATLAB dizinindekilerden değil de bize ait bir resmi kullanalım. Bu resim şu an çalıştığımız dizinin içindeki ‘resimler’ dizininde bulunsun ve adı ‘ornekresim.jpg’ olsun.
rgbImg = imread('resimler/ornekresim.jpg'); imshow(rgbImg)

size(rgbImg)
ans = 385 385 3
Harika, bize ait bir resmi MATLAB’e aktardık sonunda! Açılan pencerede alttaki gibi ‘data curser’ basılı iken pikselleri seçip koordinatlarına ve renk bileşenlerine bakabilirsiniz.
Resimlere ve piksel renklerine ait temelleri öğrendik, dahasını getirelim, biraz da basit işlem yapıp yazıyı sonlandıralım.
Öncelikle elimizdeki gerçek-renkli resmi griye çevirelim:
img = rgb2gray(rgbImg); imshow(img) truesize title('Örnek resim')

size(img)
ans = 385 385
Artık üç kanaldan değil de tek kanaldan oluşuyor resmimiz. Resim ekrana basılırken truesize kullandık ki her resim pikseli ekrandaki bir piksele denk gelsin. Mesela ekrandaki 0.5×0.5 piksellik bölgeye denk gelmesi için şöyle de yapabilirdik:
imshow(img) truesize(0.5*size(img)) title('Ekrandaki bir piksele dört resim pikseli geldi')

Dikkat edin, resmi ölçeklemedik, sadece ekran görüntüsünü küçülttük. Şimdi de resmi ölçekleyelim. Bunu yaparken de ‘bilinear’ interpolasyon kullanalım.
imgRescaled = imresize(img, 0.75, 'bil'); imshow(imgRescaled) title('0.75 oranda ölçeklenmiş resim')

imgRescaled = imresize(img, [100 150], 'bil'); imshow(imgRescaled) title('100x150 piksele ölçeklenmiş resim')

Resmi döndürseydik, sonra da orijinal boyutuna kirpsaydık:
imgRotated = imrotate(img, 30, 'bil', 'crop'); imshow(imgRotated) title('30 derece döndürülüp orijinal boyutuna kırpılmış resim')

Geometrik dönüşümleri başka zaman daha uzun konuşuruz. Şimdi matematiksel birkaç işlem uygulayalım resme. Ama bundan önce veri tipine bakalım:
class(img)
ans = uint8
Uygulayacağımız işlemler double tipinde veri istiyor, o halde dönüşümü yapalım.
img = im2double(img); class(img)
ans = double
Şimdi bu resim için hiçbir anlam ifade etmeyen birkaç işlemin sonucunu gözlemleyelim:
imagesc(img.^2)

imagesc(log(img))

Son olarak anlamlı bir işlem yapalım ve resmi 5×5‘lik bir Gauss çekirdeği ile filtreleyelim, sonra da yine ‘resimler’ dizinine kaydedelim. Önce çekirdeği oluşturalım:
gaussKernel = [1 4 7 4 1]' * [1 4 7 4 1] / 273
gaussKernel = 0.0037 0.0147 0.0256 0.0147 0.0037 0.0147 0.0586 0.1026 0.0586 0.0147 0.0256 0.1026 0.1795 0.1026 0.0256 0.0147 0.0586 0.1026 0.0586 0.0147 0.0037 0.0147 0.0256 0.0147 0.0037
Şimdi bu çekirdeği kullanarak resmi bulandıralım, yani Gauss ile konvolusyonunu alalım. Sonuç, orijinalle aynı boyutta olsun:
imgBlurred = conv2(img, gaussKernel, 'same');
Bu resmi png formatında diske yazalım:
imwrite(imgBlurred, 'resimler/yeniresim.png')
Son olarak orijinal resmi ve filtreleme sonucunu yanyana çizelim ve bu çizimi diske kaydedelim.
subplot(1, 2, 1), imshow(img), title('Orijinal resim') subplot(1, 2, 2), imshow(imgBlurred), title('Filtreleme sonucu'), truesize saveas(gcf, 'resimler/ikiResimYanyana.jpg')
Bu yazıda resim işleme ile ilgili temelleri atmış olduk, artık resimleri daha somut olarak düşünebiliriz. Dahası klasörlerden okuyup, basit işlemler uygulayıp, geri kaydedebiliriz.
Yazıyı burada bitiriyorum, buradaki fonksiyonlarla yetinmeyip benzerlerine yardımdan bakmanızı ve dene-gör çalışmanızı tavsiye ederim.
Yararlandığım kaynaklar:
durmus
24 Nisan 2008, 9:20 am
Öncelikle tekrar tekrar görüntü işleme konusunda Türkçe kaynağı oluşturup, anlayabileceğimiz şekle getirdiğiniz için teşekkürler. Matlab’e yabancı olduğum için verdiğiniz örneklerde imread ile dosyayı okutuyoruz ancak ekrana okunan değerlerin tamamını nasıl yazdırabilirim. teşekkürler
ismailari
24 Nisan 2008, 9:45 am
MATLAB’de bir değişkene atama yapılan herhangi bir satırın sonuna noktalı virgül koymazsan, o değişkenin yeni değerini ekrana basar. Alttaki iki satırı denersen farklarını görebilirsin:
resim1 = imread('resmim.jpg');resim2 = imread('resmim.jpg')
Tabii resim çok büyükse tümünü ekrana basmak yerine bir kısmını da basabilirsin, örneğin ilk 10luk kare bölgeyi görelim:
resim1(1:10,1:10)durmus
24 Nisan 2008, 10:22 am
Matlab konusunda öğreneceğimiz çok şey var anlaşılan. Hocam bir fotoğraftan evin çatılarına bakarak onları saymak için bu piksel değerlerinden nasıl faydalanabiliriz. Avrupa Yakası örneğinizdeki gibi bir veritabanı mı yapmam lazım. veya ne önerirsiniz.
ismailari
24 Nisan 2008, 11:30 am
Evet, mutlaka bir veritabanı yapmak lazım. Çünkü çatı dediğin şey her yerde sabit değildir ki? Bazı yerlerde kiremitlerin rengi farklıdır, bazı yerlerde tenekeler vardır, vb. Bunun için öncelikle nasıl çatıları bulmak istediğine karar verip böyle fotoğraflar bulmalısın (kendin de çekebilirsin). Sonra bunların nereleri çatı diye etiketlemelisin (maskeler gibi). Ancak böylelikle yazacağın kodun doğru çalışıp çalışmadığını, ne kadarını doğru bulup nerelerde yanlış yaptığını gözlemleyebilirsin. Senin oluşturduklarına doğruluk bilgisi dersek, bunları sistemini hem eğitmek hem de sınamak için kullanabilirsin.
Çatılar nasıl bulunur, pek bir fikrim yok açıkçası. Ama senin de söylediğin gibi (bilimsel metodun kaçınılmazı olan kontrollü deney için) veritabanı şart.
Benim tavsiyem, bulduğun resimleri en azından iki gruba ayır. İlkinde çatılar çok bariz olsun, etrafta çatıya benzeyen başka unsur olmasın. Renkleri de ayrışabilsin. İkinci grupta ise biraz daha zorlayıcı resimler olsun. Örneğin kırmızı renkte evler olsun, çatıların bir kısmı birbirlerinin arkasına geçsin, vb. Bir anda hepsini karıştırırsan içinden çıkılmaz hale gelebilir. En basitinden başlayarak ilerlemek en iyisi.
İyi çalışmalar.
sibel başoğlu
24 Eylül 2008, 12:13 pm
daha açıklayıcı şeyler olabilir .sizce
ismailari
24 Eylül 2008, 3:07 pm
@Sibel: Tabii ki, siz de eksik gördüğünüz kısımlar için bir dokümantasyon girerseniz seve seve onaylarım. Böylece okurlar da yararlanmış olur.
Murat Çavdarlı
4 Ekim 2008, 11:14 pm
iyi akşamlar. Güzel bir paylaşim yapmıssınız teşekkürler. benim size bir konuda sorum olacaktı imge işleme dersi ödevimde imread komutunu kendimiz tekrar programlıyacagız kendi programı var ama cok uzun biz onu renkli bir resmi okumasını yapacak sekilde for döngüleri kullanarak programlıyacagız. bu konuda nasıl bir yol izlemeliyim. teşekkürler. iyi çalışmalar
ismailari
5 Ekim 2008, 5:02 pm
@Murat: Teşekkür ederim. Normalde ödevlere yardım etmem ama nasıl bir yol izleyebileceğini kısaca anlatayım. Tüm resim formatlarının farklı dosya biçimleri vardır. Örneğin ‘png’ veya ‘jpeg’ okumak için farklı kodlar yazman lazım. Sizin muhtemelen ‘24-bit bmp’ okumanız istenmiştir. Eğer öyleyse, ‘bmp’ dosyalarının nasıl olduğunu (dosya formatını) internetten bulabilirsin. Daha sonra dosya okuma fonksiyonları (fread, vb.) ile renkleri okuyup imge matrisine atayabilirsin. Sonra da doğru olup olmadığını imshow ile denetlersin. İyi çalışmalar.