You are on page 1of 146

Yapay Görüş

Öğretmen: Daniel Cremers

Tercüme: Burak Bayramlı


Sayılar ve Kuramlar

https://burakbayramli.github.io/dersblog/sk/

Tüm Dosyalar, Kodlar

https://github.com/burakbayramli/classnotes
Ders 1
Ders kitabımız Ma, Soatto, Kosecka, ve Sastry’nin An Invitation to 3-D Vision
kitabıdır. Videolar [1] adresinde. Dersin ana odağı kameradan gelen iki boyut-
taki görüntüleri birleştirerek o görüntülerin geldiği dünyanın 3 boyutlu modelini
oluşturmak.
Bilgisayar kontrollü arabalar bugünlerde çok konuşuluyor, bu arabalar etrafını
nasıl algılıyor acaba? İnsan gibi mi davranıyorlar? Bu soruya kısa cevap hayır.
[2015 itibariyle] bu arabalar aslında Lidar adı verilen lazer bazlı uzaklık ölçüm
algılayıcıları kullanıyorlar (bu kelime ışık -light- ile radar kelimelerinin birleşimi),
etraflarına lazer ışığı yollayıp yansımayı alıyorlar, ve bunu çok yüksek çözünürlük
ile yapıyorlar, sonuç olarak dış dünya hakkında çok detaylı bilgileri var. Fakat biz
insan olarak biliyoruz ki sadece görüntü ile araba kullanmak mümkün, çünkü in-
sanlar Lidar’a sahip değil. Bunu nasıl yapıyoruz?
Bazıları stereo görüş (stereo vision) ile bunu yapabildiğimizi söylüyor fakat bir
gözümüzü kapatsak tek gözümüz ile bile araba kullanabiliriz. İnsanlar aslında
şunu beceriyor; hareket ederken farklı açılardan gördüğümüz objelerin 3 boyutlu
yerini hesaplayabilmek. Bu işi iyi yapıyoruz, hatta bilim adamlarına göre zihinsel
işlem gücümüzün neredeyse yarısı görüntü işlemekle haşır neşir! Peki bu hesap
nasıl yapılıyor?
Bir binaya bakalım, şimdi birkaç adım atarak tekrar bakalım, bina iki boyutlu
görüntümüz içinde, yani gözümüzde, farklı bir yere gelmiş olacak. Bu fark,
attığımız adım, o binanın büyüklüğü, ve yeri ile orantılı bir fark. Solumuzda
ve çok ileride olan bir obje ona yaklaştıktan sonra gözümüzde çok az sola kayma
oluşturabilir, sağımızda çok yakınımızda olan bir obje ona doğru ilerlerken ve
yanından geçerken çok hızlı bir şekilde gözümüzdeki resimde sağa doğru ka-
yar. Eğer yeterince adım atıp o binanın, objenin yeterince değişik görüntüsünü
alırsak, ve kuvvetli algoritmalar kullanarak bina hakkında üç boyutlu bir şekil
oluşturmuş oluruz. Hareketten Yapı Oluşturmak (Structure from Motion -SfM-)
bilim dalının yapmaya uğraştığı işte budur, SfM bu derslerin ana amacı olacaktır.
Altta örnek olarak Alkatraz adasının iki değişik açıdan çekilmiş resmini görüyoruz.

Biri daha uzaktan, biri daha yakından, büyük bir ihtimalle adaya yaklaşmakta
olan bir tekne üzerinden aynı kişi tarafından çekilmiş. SfM için önce her iki

1
resim üzerinde o resimlerin özelliklerini (features) çıkartan bir algoritma kul-
lanırız (alttaki örnekte SURF kullandık, bir diğer alternatif SIFT). Daha sonra bu
özelliklerin her iki resim arasında eşleştirilmesini sağlayan bir diğer algoritma
kullanırız, böylece onların hangi yöne kaymış olduklarını anlayabiliriz. SURF
hakkında daha fazla detay bu yazının altında. Eşleştirmeleri 1. resim üzerinde
görsel olarak gösterirsek (kırmızı nokta ilk resimden, yeşil nokta ikinci resimden),

Yani kamera hareketini, ve hareketin resim üzerinde nasıl bir değişim yarattığının
bulabiliyoruz. Muhakkak 1. resimdeki tüm özellik noktalarının 2. resimde nerede
olduğu mükemmel bir şekilde bulunamamış olabilir, ama bu “gürültü” içerisinden
bir model çıkartmak SfM’in bir parçası olacaktır.
İlk önce Lineer Cebir’den bazı kavramları hatırlayalım.
Uzaylar
Her tür kavram için akılda tek bir örnek tutmak iyi oluyor; Vektör uzayı için
mesela R3 , altuzay (subspace) için ise bu uzay içindeki bir düzlem (plane) olabilir.
Bu düzlemin orijinden (0,0,0) noktasından geçmesi gerekir.
Bazlar
Sonsuz tane baz olabilir, mesela R3 için. Örnek, kordinat eksenleri bir bazdır,
onları, birbirine dikgen olma şartıyla, pek çok değişik şekilde seçebilirim.
Bir bazı oluşturan vektörlerin lineer kombinasyonunu alarak bir başka baz oluşturabilirim.
Buna baz transformasyonu deniyor. Baz B = {b1 , .., bn } olsun, yeni bir baz bj0 ∈ B 0 ,

2
X
n
bj0 = αji bj
j=1

ki αji özgün bir transformasyonu temsil eden transformasyon katsayıları ola-


caktır.
Katsayıları bir matris A içine koyarsak bu matrisi bir transformasyon matrisi
olarak kullanabiliriz,

B 0 = BA ⇐⇒ B = B 0 A−1

Baz transformasyonu çok faydalı çünkü 3 boyutlu dünyayı oluştururken onu


hangi şekilde oluşturacağız? Artık biliyoruz ki hiçbir model, temsiliyet özgün
değil. Mesela kameranın ardı ardına aldığı resimleri birleştiriyoruz, fakat baz
aldığımız kameranın yeri sürekli değişiyor, bu sırada bazı değiştirmemiz gereke-
biliyor. Ya da, yeri değişmeyen tek bir “referans temsiliyet” baz alarak ona dönük
transformasyon yapmak gerekebiliyor.
İçsel / Noktasal Çarpım (Inner / Dot Product)
İki vektörün noktasal çarpımı hu, vi. Norm,
p yani v vektörünün uzunluğu ile nok-
tasal çarpım arasında bir ilişki var, |v| = hv, vi.
Tabii ki hv, vi > 0 yani pozitif kesin.
İki vektör arasındaki mesafeyi bir tür norm hesabı ile yapabilirim,

d(v, w) = |v − w| =
p
hv − w, v − wi

Yani iki vektörün farkının normu bu iki vektör arasındaki mesafeyi verir.
Üstteki mümkün ölçevlerden (metric) sadece biri, farklı ölçevler olabilir, mesela
2D iki nokta arasında Manhattan mesafesi kullanılabilir,

Bu mesafeye Manhattan deniyor çünkü Manhattan bilindiği gibi New York’un


üzerinde pek çok gökdeleni olan bir adası, ve bir noktadan diğerine gitmek için
binaların etrafından dolaşarak gitmemiz gerekiyor, direk pat diye dümdüz istediğimiz
noktaya gidemiyoruz. Düz mesafe Öklitsel (Euclidian) olurdu.
Üstte noktasal çarpım üzerinden bir eşleme (V diyelim) yaratmış olduk aslında,
bu durumda V bir ölçev uzayı haline geldi. Bu uzay noktasal çarpımla yaratıldığı

3
için bu sebeple ona bir Hilbert Uzayı deniyor (detaylar için [2] notları). Her
ölçek uzayı noktasal çarpım üzerinden yaratılmayabilir, mesela bir ezber tablo
üzerinde bile bir mesafe eşlemesi yaratırdım, bu bir tür ölçev olurdu, ama bu
ölçev noktasal çarpım olmadığı için ortaya bir Hilbert Uzayı çıkmazdı.
Doğal Baz
In ile n × n boyutunda birim matrise doğal baz (canonical basis) ismi veriliyor.
Diyelim bu bazdan diğer bir baz B 0 ’ye A ile geçiş yapabiliyoruz, ve hx, yi’nin bu
bazda nasıl gözükeceğini merak ediyoruz,

hx 0 , y 0 i = x 0T y 0 = (Ax)T (Ay) = xT AT Ay = hx 0 , y 0 iAT A

Eşitliğin en sağ tarafı notasyonel bir ek. Bu çarpıma doğuşturucu (induced) içsel
çarpım ismi veriliyor, doğuşturucu kelimesi kullanılmış çünkü yeni bazın “etk-
isi” ile ortaya çıkan, “doğan” bir içsel çarpım bu.
Dikgenlik
Eğer hx, yi = 0 ise x, y birbirine dikgen demektir.
Bir bazın, yani o bazı temsil eden vektörlerin birbirine dikgen olması gerekmez.
Ama bu durum var ise, faydalıdır.
Kronecker Çarpımı
A herhangi bir matris olabilir, illa karesel olması gerekmez, A ∈ Rm×n ve B ∈
Rk×l . Çarpım şöyle,
 
a11 B . . . a1n B
A ⊗ B =  ... ... ..
 
. 
am1 B . . . amn B

Yani A’nin her öğesi B’nin tamamı ile çarpılıyor ve bu sonuçlar yanyana, üst üste
diziliyor. Bu tabii ki devasa yeni bir matris ortaya çıkartır, sonuç A ⊗ B ∈ Rmk×nl .

A = np.array([[3,4,5],[4,3,5]])
B = np.array([[3,4],[4,5]])
print np.kron(A,B)

[[ 9 12 12 16 15 20]
[12 15 16 20 20 25]
[12 16 9 12 15 20]
[16 20 12 15 20 25]]

Yığma (Stacking)
Yine çok basit bir operasyon, AS , bir matrisin kolonlarını alıyoruz, ve her kolonu
diğerinin altına gelecek şekilde koyuyoruz, ve dikey olarak çok büyük bir vektör
ortaya çıkartıyor. Numpy ile,

4
print A
print A.flatten(order='F')

[[3 4 5]
[4 3 5]]
[3 4 4 3 5 5]

Bu iki operasyondan ilginc bir yetenek elde ettik,

uT Av = (v ⊗ u)T AS

Yani eşitliğin sol tarafı A’nin öğeleri üzerinden bir lineer kombinasyon.
[Gruplar, Halkalar konuları atlandı]
Grupları matris olarak temsil etmek mümkündür, bu fikir biraz garip gelebilir,
çünkü grup oldukça soyut bir kavram, ama matrisler gayet somut, sayısal kavram-
lar. Bunun nasıl olduğuna gelelim; çoklu bakış açıdan 3D tekrar oluşturma (re-
construction) halinde hareket halindeki bir kameranın bir eksen etrafında tüm
mümkün dönüşleri bir grup oluştururlar. Nasıl? Mesela kamera 30 derece dönmüş
(rotate) olsun, sonra bir 30 derece daha dönmüş olsun. Toplam 60 derece dönüşün
kendisi, ayrı ayrı 30 dereceler gibi, bir dönüş sayılır. Yani dönüşler, toplam op-
erasyonu için kapalıdır. Ayrıca her dönüşün bir tersi vardır.
R2 ’daki bir θ dönüşü tipik olarak

 
cos θ − sin θ
Aθ =
sin θ cos θ

şeklinde gösterilir, ki 0 6 θ 6 2π. Üstteki matris soyut bir grubun somut olarak
belirtilmiş hali, 2 boyuttaki tüm dönüşler. Yani bir grubun her üyesi somut bir
matris ile ifade edilebiliyor.
Dönüş dışında ve yine kamera bağlamında diğer transformasyonlar vardır; mesela
kameranın yerini değiştirebilirim (translation). Dönüş ile beraber bu hareket te
bir grup oluşturur, çünkü üç eksende ileri geri hareket, üç eksende dönüş, toplam
6 boyutlu bir grup ortaya çıkar, ya da “serbestlik derecemiz 6” diyebiliriz, ki bu
grubun da bir matris temsili olacaktır.
Yani matris üzerinden grupları incelemiş olurum. Matrisler somuttur, onları
hesapsal rutinlerde de kullanabilirim.
İlgin Transformasyon (Affine Transformation)
Hareket ettirmek bir vektör toplamıdır, döndürmek / rotasyon matris çarpımıdır
(eğer matris döndürme için tasarlammışsa), bir araya koyarsak,
L : Rn → Rn , A ∈ GL(n) ve b ∈ Rn olmak uzere

L(x) = Ax + b

5
işlemini tanımlayabiliriz, bu bir ilgin transformasyondur. Verilen bir x vektörünün
yerini değiştirir ve döndürür. Tabii A tersi alınabilir (invertible) bir matris ol-
malıdır çünkü bu işlemin tersini de alabilmek isterim, A tersi alınabilir olmasaydı
tüm transformasyon tersi alınabilir olmazdı.
Dikkat: eğer b = 0 değilse, L bir lineer transformasyon olamaz (bu cebirsel olarak
kontrol edilebilir, mesela x+y vektörünün ilgin transformasyonu, sonuç içinde 2b
olur tek b değil), fakat bu işlemi boyut büyüterek bir lineer transformasyon haline
getirebiliriz. Bu arada, bu boyut büyütme işlemini bu derste çok kullanacağız. Bu
işlem şöyle; bir x vektörünü alıyoruz, altına ’1’ ekliyoruz. Bu işleme “homojen
kordinata çevirmek” ismi veriliyor. Bu işlem ardından L(x)’i

  
A b x
0 1 1

olarak temsil edebiliriz, yani tek bir matris çarpımıyla. Üstteki işlem sonucunun
Ax + b ile aynı olduğu kontrol edilebilir.
Homojen kordinata çevirerek ilgin transformasyonu bir lineer transformasyon
haline getirmiş olduk. Bu numara işimize yarayacak, dersin ilerisinde göreceğiz,
pek çok kez kamera açısı, yer değişimini hesaplamak gerekecek, ve bunun için
lineer cebir kullanmak istiyoruz [lineer cebirin çarpım işlemini yani] ve bu nu-
marayla bu kullanım mümkün oluyor.
Üstteki matrislerden solda olanı ilgin matris; bu matris ayrıca tersi alınabilir bir
matris, eğer A da böyle ise.
Ilgin matrisler grubu lineer GL(n + 1)’in bir alt grubunu oluşturur. Alt grup
olduklarını ispatlamak için grubun çarpım operasyonu için kapalı olduğunu, ve
tersi alınabilir olduğunu ispatlamak gerekir.
Dikgen Grup (Orthogonal Group)
Bu grubu tanıştırmanın pek çok yolu var, bizim seçeceğimiz yol, eğer A ∈ M(n)
üzerinden transformasyon noktasal çarpımı muhafaza ediyorsa, yani değiştirmiyorsa,
yani

hAx, Ayi = hx, yi, ∀x, y ∈ Rn

Noktasal çarpım hesabının hatırlayacağımız üzere iki vektör arasındaki açıyı hesapla-
mak ile yakından bir bağlantısı var. Yani iki vektörü A ile çarpmak o vektörler
arasındaki açıyı değiştirmiyor. İspat için

hAx, Ayi = xT AT Ay = xT y

çünkü AT A = AAT = I. Lineer Cebir kaynaklarında dikgenlik tanımı için çoğunlukla


bu devriği ile çarpımın birim matrise eşit olması kavramının kullanıldığını görürsünüz;

6
bana göre bu tanım akılda canlandırmak için yeterli değil, üstte gördüğümüz “A
ile çarpımın iki vektörün arasındaki açıyı değiştirmiyor olduğu” tanımı başlangıç
noktası olarak akılda canladırmakta daha faydalı. Dikgen grup (A yerine R kul-
lanalım artık)

O(n) = {R ∈ GL(n) | RT R = I}

GL bir genel lineer grup notasyonu. Devam edelim, bir dikgen matris R için

det(RT R) = (det(R))2 = det(I) = 1

ki o zaman det(R) ∈ {±1}.


O(n)’in bir alt grubu det(R) = +1 şartını getirince tanımlanabilir, bu gruba özel
dikgen grup ismi veriliyor, SO(n). Bu grup aslında tüm rotasyon matrislerini
tanımlıyor; sezgisel olarak bunu görebiliriz, eğer iki vektörü dikgen matrisle
transform edersem aradaki açı değişmez, ama başka bir lineer transformasyon
uygularsam açının değişmeyeceği garanti değil.
Soru
Eğer det(R) = −1 şartını kullansaydım başka bir alt grup elde edebilir miydim?
Cevap
Hayır, çünkü mesela

 
1 0
0 −1

matrisini düşünelim, bu matrisin determinantı -1. Ama bu matrisin devriğini


kendisi ile çarparsam sonucun determinantı -1 değil.
Gerçek dünyada üstteki gibi bir matrisle transformasyon ne anlama gelir acaba?
Bir tür aynadaki yansımayı almak.. mesela x ekseninde eksi bölgedeyken artı
bölgeye geçmek, bir tür “çevirmek (flip)”.
Öklitsel Grup (Euclidian Group)
R ∈ O(n) ve T ∈ Rn olmak üzere (T bir vektör)

L : Rn → Rn ; x → Rx + T

Üstteki tanıma uyan tüm transformasyonlar Öklitsel Grubu oluşturur. Bu grup


doğal olarak ilgin grubun bir alt grubudur. Homojenleştirmek mümkündür,

 
R T
R ∈ O(n), T ∈ R n
E(n) =
0 1

7
Öklitsel Grup içinde, eğer R ∈ SO(n) olan alt grubu alırsam (yani det R = 1), o
zaman özel Öklitsel Grup SE(n)’i elde ederim. Bu grup önemli bir grup, çünkü
bu grubun SE(3) formu, fizikte katı gövde hareketi (rigid-body motion) diye bi-
linen hareketi modellememize izin veriyor, ki kameramızın hareketini bu grupla
modelleyeceğiz; katı gövde normal bildiğimiz cisimler (hareket ederken kütlesi
şekil değiştirmeyen).
Özet olarak

SO(n) ⊂ O(n) ⊂ GL(n) ⊂ SE(n) ⊂ E(n) ⊂ A(n) ⊂ GL(n + 1)

ki ⊂ altküme sembolüdür.
GL(n), genel lineer grup, tüm tersi alınabilir matrisler. O(n) dikgen matrisler,
ayna imajı, dönüşümler için. SO(n) özel dikgen grup ki dikgen matrisin deter-
minantının +1 olduğu durum. GL(n + 1) genel lineer grubun homojenleştirilmiş
hali. Onun alt kümesi A(n) ki bu kümede R, T gelişigüzel matrisler. A(n)’in
altkümesi E(n), bu durumda R dikgen olmalı. Onun altkümesi için özel Öklitsel
grup, ki katı gövde transformasyonu burada.
Çekirdek (Kernel), Menzil (Range)
Aslında çekirdek sıfır uzayı (nullspace), menzil ise kapsam (span) aynı şey. Daha
fazla detay için [3] notları.
Menzil ve sıfır uzayı kavramları bir lineer denklem sistemini çözerken faydalı.
Hatırlayacağımız üzere Ax = b denklem sisteminin çözüm bağlamında 3 seçeneği
vardır; ya hiç çözüm yoktur, ya tek çözüm vardır, ya da sonsuz tane çözüm vardır.
Bunlardan hangisinin olacağı menzil ve sıfır uzayına bağlıdır.
Ax = b’nin, ki x ∈ Rn olacak şekilde, sadece ve sadece b ∈ range(A) ise çözümü
vardır. Bu çözüm özgündür eğer kernel(A) = {0} ise, yani sıfır uzayı boş ise
(sıfır haricinde boş tabii). Ayrıca eğer xs bir çözüm ise ve xo ∈ kernel(A) olacak
şekilde ise, o zaman xs + x0 da bir çözümdür. yani A(xs + x0 ) = Axs + Axo = b.
Algoritma şöyle; elimdeki vektör A’nin menzilinde mi? Evet ise o zaman elimde
bir çözüm var (hatta bu çıkarım neredeyse kendi etrafında dönmeye benziyor,
menzil tanım itibariyle zaten A’nin tüm lineer kombinasyonlarıdır), bundan sonra
sıfır uzayına bakarız, boş mu? Öyleyse elimizdeki çözüm özgündür. Eğer sıfır
uzayı boş değilse, bu uzaydaki her öğe x0 üzerinden xs + x0 da bir çözümdür.
Niye? Çünkü x0 sıfır uzayında olduğu için Ax0 = 0, o zaman Axs +  Ax = b,
0
ve bu durumda sonsuz tane çözüm olacaktır, çünkü x0 ’i istediğim sabitle çarpıp
büyütebilirim, o hala sıfır uzayında olur.
Kerte (Rank)
Bir matrisin kertesi o matrisin menzilinin boyutudur.
Sylvester’in eşitsizliği

8
A ∈ Rm×n , B ∈ Rn×k olsun. O zaman

rank(A) + rank(B) − n 6 rank(AB) 6 min(rank(A), rank(B))

Yani A, B’nin kertesi üzerinden bu iki matrisin çarpımının kertesi hakkında bir
fikir edinebiliyorum.
Eğer elimde iki eşsiz olmayan (nonsingular), yani tersi alınabilir matris var ise,
diyelim C ∈ Rm×m , D ∈ Rn×n , o zaman rank(A) = rank(CAD), yani eşsiz
olmayan matrisler ile çarpım kerteyi değiştirmiyor.
SURF
İmajlardan özellik çıkartıp bunları eşleştireceğiz demiştik; SURF algoritması özellik
bulabilen yaklaşımlardan biri. SURF resimde köşe olarak betimlenebilecek, ya da
diğer ilginç yerlere odaklanıyor, bu bölgelerin yeri, genel rengi, resmin bütününe
göre açısı, vs. hesaplanıyor. İmajda bu tür yerler keşfedilince, SURF onları 64
öğesi olan bir vektör olarak temsil eder, ve bu vektöre “tarif edici (descriptor)”
adı verilir. Bu vektördeki değerler o özelliği özgün olarak temsil ederler.
SURF ve SİFT yaklaşımları genel kategori olarak görüntü işleme (image process-
ing) alanına girerler, bu alandaki diğer yaklaşımlar mesela kenar keşfi (edge de-
tection), köşe (corner) keşfi -Harris algoritması burada ünlü-, imajdan bulanıklık
giderme gibi işlemlerdir.
Altta 1. Alkatraz resmindeki SURF noktalarını görebiliriz.

from mahotas.features import surf


import pandas as pd
from PIL import Image

im=Image.open("alcatraz1.pgm")
descriptors = pd.DataFrame(surf.surf(np.array(im)))
print descriptors.shape

(461, 70)

descriptors.plot(kind='scatter',x=1,y=0)
plt.hold(True)
plt.imshow(im,cmap = plt.get_cmap('gray'))
plt.savefig('vision_02_01.png')

9
Üstteki imajın mesela ilk SURF vektörünün içeriğine bakarsak (sadece ilk 10 öğesi)

print descriptors.ix[0][:10]
0 226.943034
1 339.099974
2 2.125709
3 1477.629660
4 -1.000000
5 -0.029199
6 0.003429
7 0.000933
8 0.003470
9 0.001833
Name: 0, dtype: float64

İlk iki hücre özelliğin yeridir (x,y kordinatı olarak), tarif edici bölge ise 6. hücreden
başlar, ve 64 tane vardır.
Peki eşleştirmenin başarılı olması ne kadar garantidir? Cevap için yazının başındaki
örneğe dönelim tekrar, mesela bir binaya bakıyorum, SURF işletiyorum, sonra
adım atıp aynı binaya tekrar bakıyorum (daha doğrusu bakar halde adım atıyorum).
Büyük bir ihtimalle bina iki adım arasında mor rengine dönüşmedi. Hala beyaz
renkte, hala kapısı, penceresi aynı şekilde, aynı yerlerde duruyor. O zaman ikinci
imaj üzerinde bir daha SURF işletirsem, benzer özelliklerin çıkartılıyor olmasını
beklerim, yani tarif edici bölgeleri birbirine çok benzeyen özellik vektörleri elde
etmem lazım. Kullanılan ana numara da bu zaten; birinci imajın özelliklerinin
tarif edici bölgeleri (vektörlerini) ikinciye eşleştiriyorum, ki bu eşleştirme ba-
sit vektör uzaklığı üzerinden olabilir; 1. resimdeki vektörlerin her biri için 2.
resimden gelen vektörlerin en yakınını bulurum, ve bir eşleşme elde ederim.
Bu eşleşmeleri bulduktan sonra imajdaki piksellerin hangi yöne hareket ettiği

10
hakkında bir fikir edinmiş oluyorum, çünkü mesela, belli bir tarif bloğuna sahip
bir bölge (10,10) kordinatından (12,12) kordinatına gitmiş olsun - bu önemli bir
bilgi. Bu bilgiyi kendi hareketim, kamera açısı, ve diğer imajlar ile birleştirince ve
SfM algoritmaları uygulayarak baktığım objelerin üç boyutlu uzaydaki yerlerini
hesaplayabilirim.
Kaynaklar
[1] Cremers, Multiple View Geometry, https://www.youtube.com/watch?v=
RDkwklFGMfo&list=PLTBdjV_4f-EJn6udZ34tht9EVIW7lbeo4
[2] Bayramli, Fonksiyonel Analiz
[3] Bayramli, Lineer Cebir

11
Ders 2
Çoğunlukla özvektör kavramına atıf yapıldığında söylenmek istenen (C kom-
pleks sayıların kümesi olsun),

Av = λv, λ∈C

ifadesidir, yani “sağ özvektör”, yani bir matrisi sağdan çarpınca boyu büyüyen ya
da küçülen vektör. Sol özvektörler de mümkün, bu durumda,

vT A = λvT , λ∈C

A’nin spektrumu σ(A) o matrisin tüm özvektörlerinin kümesidir. Numpy ile

import numpy.linalg as lin


A = [[3,4,3],[5,5,6],[5,5,5]]
A = np.array(A)
[V,D] = lin.eig(A)
print 'ozvektorler'
print D
print 'ozdegerler'
print V

ozvektorler
[[-0.41963784+0.j 0.72738656+0.j 0.72738656-0.j ]
[-0.66394149+0.j -0.42567121+0.33744486j -0.42567121-0.33744486j]
[-0.61893924+0.j -0.25117492-0.33579002j -0.25117492+0.33579002j]]
ozdegerler
[ 13.75351937+0.j -0.37675969+0.47073926j -0.37675969-0.47073926j]

Eğer B = PAP−1 , ki P eşsiz olmayacak şekilde, o zaman σ(B) = σ(A). İspatsız


veriyoruz. Yani P ve onun tersi ile bir matrisi iki taraftan çarpmak, o matrisin
spektrumunu değiştirmiyor.
Eğer λ ∈ C bir özdeğer ise, onun eşleniği (conjugate) λ̄ da bir özdeğerdir. Bu
sebeple reel matris A için σA = σA.
Bir reel matrisin tüm özdeğerlerinin reel olduğu “güzel” bir durum vardır; bu du-
rum matrisin simetrik olduğu durumdur, yani ST = S. Bu güzel durum aslında
pratikte pek çok kez karşımıza çıkar; mesela kovaryans matrisleri olarak.
Simetrik matrisin özgün özdeğerlerine tekabül eden özvektörleri birbirine dik-
gendir. İspat, vTi Svj formülüne bakalım, ki vi , vj özvektörler, S simetrik matris.
Özdeğer eşitliğinden Svj = λj vj kullanırsam,

vTi Svj = λj vTi vj

Eğer vTi S = λi vTi kullanırsam,

1
vTi Svj = λi vTi vj

elde ederim. İkisini bir araya koyalım,

λj vTi vj = λi vTi vj

(λj − λi )vTi vj = 0

Bu eşitliğin doğru olması sadece iki durumda olabilir; ya λi , λj birbiriyle aynıdır,


ya da birbirinden farklıdır ama o zaman vi , vj birbirine dikgen olmalıdır.
[norm atlandı]
Eksi Bakışımlı Matrisler (Skew-symmetric Matrices)
Eğer AT = −A ise bu matrislere eksi bakışımlı deniyor [5]. Mesela

 
0 2
−2 0

Bu matrisin devriği ve negatifi aynıdır. Eksi bakışımlı matrislerin köşegeni sıfır


olmalıdır. Bu tür matrislerin ilginç bazı özellikleri var, mesela, hatırlarsak simetrik
matrislerin pür reel spektrumu vardı. Eksi bakışımlı matrislerin spektrumu pür
sanaldır (imaginary).
[köşegenleştirme atlandı]
Çapraz Çarpım (cross-product)
Noktasal çarpım bize bir skalar verir. İki vektör arasındaki çapraz çarpım bize
başka bir vektör verir; u, v ∈ R3 olmak üzere çapraz çarpım,

 
u 2 v3 − u 3 v2
u × v =  u 3 v1 − u 1 v3 
u 1 v2 − u 2 v1

Yani R3 ’teki iki vektör u, v’nin çapraz çarpımı alınabilir, ve R3 ’te yeni bir vektör
elde ederiz. Bu yeni vektör u, v’ye dikgendir. Sağ el kuralı (alttaki resimde u, v
yerine a, b kullanılmış)

2
Ayrıca çapraz çarpım simetriktir, u × v = −u × v.
Eksi Bakışımlı Matris (Skew-Symmetric Matrix)
Bilgisayar Görüşü (Computer Vision) alanında oldukça yaygın bir matris olan
eksi bakışımlı bir matris alttadır, ve onu herhangi bir vektörden oluşturan şapka
operatörüyle gösterelim, yani u için û olsun,

 
0 −u3 u2
û =  u3 0 −u1  ∈ R3×3
−u2 u1 0

Bu tür matrislerin kertesinin çift sayı olması şarttır.


û’nun ilginç bir özelliği var, herhangi bir vektör v ile

ûv = u × v

ki × bir çapraz çarpım. Yani, öncelikle, her vektörün bir eksi bakışımlı matris
karşılığı var, ve üstteki operatör ile bu geçişi yapabiliriz, ayrıca ne zaman bir
çapraz çarpım görsek, onu eksi bakışımlı matris çevirimi üzerinen bir normal ma-
tris çarpımı olarak temsil edebiliriz. Bu faydalı çünkü çapraz çarpımla uğraşmak
biraz külfetli olabiliyor.
3 boyut bağlamında û’nun kertesi tabii ki 2’dir; çünkü eksi bakışımlı matrislerinin
kertesinin çift olması şart ise, ve 3 boyutlu durumda bu kerte en fazla 3 olabilir,
ama 3 olamaz çünkü 3 çift sayı değil, o zaman 2 olur.

import numpy as np

def skew(a):
return np.array([[0,-a[2],a[1]],[a[2],0,-a[0]],[-a[1],a[0],0]])

A = np.array([[1,2,3]]).T
print skew(A)

[[ 0 -3 2]
[ 3 0 -1]
[-2 1 0]]

A3
Diyelim ki A bir 3 × 3 eksi bakışımlı matris, ve a = (a1 , a2 , a3 ) vektörü üzerinden
oluşturulmuş. A3 nedir?

A·x=u×x

olduğunu biliyoruz. O zaman

3
A3 x = a × (a × (a × x))

demektir. Eşitliğin sağ tarafına bakarsak, eğer a = e, ki e bir birim vektörü olsun,
yani ||e|| = 1, o zaman norm’ların ilişkisi şu şekilde olurdu,

||e × (e × (e × x)))k = ||e × (e × x))|| = ||e × x||

Neden?

||e × x|| = ||e||||x|| sin θ = ||x|| sin θ

O zaman,

||e × (e × x))|| = ||e||||e × x|| sin 90 = ||e × x||

Böyle gider. Yani norm eşitlikleri doğru.


Eğer a = ||a||e kabul edersek,

||a × (a × (a × x)))|| = ||a||3 ||e × (e × (e × x)))||

= kak3 ke × xk

= kak2 ka × xk

= (aT a)ka × xk

Yani A3 x = ±(aT a)Ax; eksi mi artı mı? Eksi işareti olduğunu sağ el kuralıyla
görebiliriz. Demek ki A3 = −(aT a) · A.
Simetrik matrisler için

A = UDUT

olduğunu biliyoruz. Eksi bakışımlı matrisler için

S = UBUT

olur ki B bir blok köşegen matristir, U dikgen. S’in özde  ğerleripür sanaldır. Blok
0 1
köşegen matris diag(a1 Z, a2 Z, ..., zm Z, 0, ..0), ve Z = . Köşegende blok
−1 0

4
olması garip gelebilir, fakat tek sayılar yerine çapraz yönde birkaç sayının “üst
üste” olduğu bir durum bu. Basit matris çarpımı ile kolay bir şekilde kontrol
edilebilir ki
Z2 = −I, Z3 = −Z, Z4 = I
Matris Üstelleri (Matrix Exponentials) [1, sf. 482]
t ∈ R ve bir kare matris n × n matrisi A için, matris üsteli,

U(t) = etA = exp(tA)

alttaki problem için özgün bir n × n çözümüdür;

dU
= AU, U(0) = I
dt

Dikkat: matris üsteli exp fonksiyonun teker teker matris öğeleri üzerinde işletilmiş
hali değildir.
Matris üstelleri bir seri olarak ta gösterilebilir,

X

tn t 2 2 t3 3
tA
e = An = I + tA + A + A + ...
n=0
n! 2 6

Bu seri yakınsayan (converging) bir seridir.


Örnek

 
0 1
A=
0 0

için

 
tA 1 t
e =
0 1

Örnek

 
1 0
A=
0 1

için

 
tA et 0
e =
0 et

5
Teori
Eğer A bir eksi bakışımlı matris ise, Q(t) = etA muntazam (proper) bir dikgen
matristir.
İspat
Dikgenlik tersi ile devriğin aynı olması demektir, o zaman üstteki eşitlikte sol
T
tarafın tersi, sağ tarafın devriği aynı olmalı, yani Q(t)−1 ile etA .

T
Q(t)−1 = e−tA = etA = (etA )T = Q(t)T

Hakikaten de öyle.
Not: Bir diğer ispata göre tüm dikgen matrisler bir eksi bakışımlı matrisin e’nin
üsteli alınarak oluşturulabilir. Buradaki nüansa dikkat, tüm eksi bakışımlı matris-
lerin üsteli dikgendir demek ile tüm dikgen matrisler eksi bakışımlı matrislerin
üsteli alınmış halidir demek farklı.
Rotasyon
Genel olarak rotasyon bir eksen ve o eksen etrafındaki bir açı olarak gösterilebilir,

Ya da rotasyon eksenini n̂ olarak gösterelim, ve dönüşün o eksene dikgen olan


bir düzlem üzerinde olduğunu düşünelim [3, sf. 37],

6
Yani v vektörü, n̂ etrafında θ kadar dönüp u olacak. n̂ birim vektör, ve dikgen
olduğu düzlemi tanımlamak için kullanılıyor.
v’nin dönüşten etkilenmeyen bileşeni vk ’yi hesaplamak için v’nin n̂ üzerine olan
yansımasını (projection) hesaplayabiliriz. Yansıtma formülü, bkz [2],

n̂n̂T
vk = v = (n̂n̂T )v
n̂T n̂
Peki v’nin düzlem üzerindeki yansıması v⊥ nedir? Resme göre v = v⊥ + vk
olduğuna göre ve üstteki formülü yerine koyunca,

v⊥ = v − vk = v − (n̂n̂T )v = (I − n̂n̂T )v

Burada v⊥ ’in 90 derece çevrilmiş hali vx nedir? Aslında bu n̂ × v olmalı, sağ el


kuralıyla bu görülebilir. Eğer N matrisini n̂’i baz alan bir eksi bakışımlı matris
olarak alırsak,

vx = n̂ × v = Nv

ki n̂ öğeleri n̂x , n̂y , n̂z olacak şekilde

 
0 −n̂x n̂y
N =  n̂z 0 −n̂x 
−n̂y n̂z 0

Eğer v⊥ ’u tekrar saat yönü tersinde 90 derece döndürmek istesek, tekrar aynı
çarpımı yapardık,

vxx = n̂ × vx = Nvx = N · Nv = N2 v = −v⊥

çünkü vxx = −v⊥ . Şimdi tekrar vk = v − v⊥ formülüne dönelim,

7
vk = v − v⊥ = v + vxx = v + N2 v = (I + N2 )v

Eğer u⊥ ’u v⊥ ve vx üzerinden tanımlamak istersek, önce u⊥ ’un v⊥ vektörünün θ


kadar döndürülmüş hali olduğu bilgisini kullanabiliriz.
Bu dönme işlemi iki boyuttadır (yani aynı düzlem üzerinde) o zaman standart
rotasyon matrisi yeterli,

v1⊥ v1⊥ cos θ − v2⊥ sin θ


    
cos θ − sin θ
u⊥ = Rθ · v⊥ = =
sin θ cos θ v2⊥ v2⊥ cos θ + v1⊥ sin θ

v1⊥ −v2⊥
   
= cos θ + sin θ
v2⊥ +v1⊥

Dikkat, sin θ ile çarpılan vektör, aynı zamanda v⊥ ’un 90 derece döndürülmüş
hali. Kontrol edelim, θ = 90’lik rotasyon matrisi üzerinden,

v1⊥ −v2⊥
    
0 −1
=
1 0 v2⊥ v1⊥

Doğrulandı. Ayrıca önceden biliyoruz ki v⊥ ’u 90 derece döndürerek vx ’i elde


etmiştik. O zaman iki üstteki formül

u⊥ = cos θv⊥ + sin θvx

olarak gösterilebilir. Daha önce hesapladığımız v⊥ ve vx ’i yerlerine koyarsak,

= sin θNv − cos θN2 v

u⊥ = (sin θN − cos θN2 )v

Hepsini bir araya koyarsak,

u = u⊥ + vk

= (sin θN − cos θN2 + I + N2 )v

= I + sin θN − (1 − cos θ)N2 v




Yani bir eksen n̂ etrafında θ kadar dönüşü bir matris olarak yazabiliriz ki bu
matrisin formülü şu şekilde olur,

8
R(n̂, θ) = I + sin θN − (1 − cos θ)N2

ki bu Rodriguez formülüdür.
Altta (−1/3, 2/3, 2/3) ekseni etrafında 70 derece dönüş birkaç farklı açıdan gösteriliyor.

o = np.array([5,5,5])
v = np.array([3,3,3])
n = [-1/3.,2/3.,2/3.]

import skew
theta = np.deg2rad(70)
N = skew.skew(n)
R = np.eye(3) + np.sin(theta) * N - (1-np.cos(theta))*N**2
print R
vr = np.dot(R,v)
print vr

[[ 1. -0.91889724 0.33402626]
[ 0.33402626 1. 0.240122 ]
[-0.91889724 -0.38633975 1. ]]
[ 1.24538705 4.72244477 -0.91571096]

from mpl_toolkits.mplot3d import axes3d


from matplotlib.patches import Circle, PathPatch
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
from mpl_toolkits.mplot3d import art3d
import numpy as np

def plot_vector(fig, orig, v, color='blue'):


ax = fig.gca(projection='3d')
orig = np.array(orig); v=np.array(v)
ax.quiver(orig[0], orig[1], orig[2], v[0], v[1], v[2],color=color)
ax.set_xlim(0,10);ax.set_ylim(0,10);ax.set_zlim(0,10)
ax = fig.gca(projection='3d')
return fig

def rotation_matrix(d):
sin_angle = np.linalg.norm(d)
if sin_angle == 0:return np.identity(3)
d /= sin_angle
eye = np.eye(3)
ddt = np.outer(d, d)
skew = np.array([[ 0, d[2], -d[1]],
[-d[2], 0, d[0]],
[d[1], -d[0], 0]], dtype=np.float64)

M = ddt + np.sqrt(1 - sin_angle**2) * (eye - ddt) + sin_angle * skew


return M

def pathpatch_2d_to_3d(pathpatch, z, normal):


if type(normal) is str: #Translate strings to normal vectors
index = "xyz".index(normal)

9
normal = np.roll((1.0,0,0), index)

normal /= np.linalg.norm(normal) #Make sure the vector is normalised


path = pathpatch.get_path() #Get the path and the associated transform
trans = pathpatch.get_patch_transform()

path = trans.transform_path(path) #Apply the transform

pathpatch.__class__ = art3d.PathPatch3D #Change the class


pathpatch._code3d = path.codes #Copy the codes
pathpatch._facecolor3d = pathpatch.get_facecolor #Get the face color

verts = path.vertices #Get the vertices in 2D

d = np.cross(normal, (0, 0, 1)) #Obtain the rotation vector


M = rotation_matrix(d) #Get the rotation matrix

pathpatch._segment3d = np.array([np.dot(M, (x, y, 0)) + (0, 0, z) for x, y in vert

def pathpatch_translate(pathpatch, delta):


pathpatch._segment3d += delta

def plot_plane(ax, point, normal, size=10, color='y'):


p = Circle((0, 0), size, facecolor = color, alpha = .2)
ax.add_patch(p)
pathpatch_2d_to_3d(p, z=0, normal=normal)
pathpatch_translate(p, (point[0], point[1], point[2]))

from mpl_toolkits.mplot3d import Axes3D


import plot3d
fig = plt.figure()
ax = Axes3D(fig)
plot3d.plot_vector(fig, o, v)
ax.hold(True)
plot3d.plot_vector(fig, o, vr, 'cyan')
ax.hold(True)
plot3d.plot_vector(fig, o, 3*np.array(n), 'red')
ax.hold(True)
plot3d.plot_plane(ax, o, n, size=3)
ax.view_init(elev=40., azim=10)
plt.savefig('vision_02_01.png')
ax.view_init(elev=30., azim=40)
plt.savefig('vision_02_02.png')
ax.view_init(elev=40., azim=50)
plt.savefig('vision_02_03.png')
ax.view_init(elev=50., azim=80)
plt.savefig('vision_02_04.png')

10
SVD
Bu işlemin özdeğer / özvektör hesabının karesel olmayan matrisler durumundaki
genelleştirilmiş hali olduğu düşünülebilir. Pek çok lineer cebir işlemi, mesela
tersini alma, kerte hesabı, vs. SVD bağlamında incelenebilir. Genelleştirme dedik,
eğer A karesel değilse özvektörleri hesaplayamayız, ama AT A kareseldir, ve bu
matrisin özvektörleri A’nin SVD’si ile yakından alakalıdır.
[İspat atlandı]
Geometrik olarak A = UΣV T ile gösterilen SVD’nin bir x ∈ Rn ’yi A uzerinden
transform ettiğimiz durumda, y = Ax diyelim, y’nin U’da bazındaki kordinat-
larının V bazındaki kordinatları ile bir ilişki ortaya çıkarttığını söyleyebiliriz; bu
ilişki Σ’nin öğeleri üzerinden bir ölçeklemeden ibarettir. Yani

y = Ax = UΣV T x ⇔ UT y = ΣV T x

[birim küre ellipsoid eşlemesi atlandı]


Genelleştirilmiş Ters (Generalized -Moore Penrose- Inverse)
Lineer sistem çözerken Ax = b için eğer A’nin tersi alınabiliyorsa, çözüm kolay,

11
x∗ = A−1 b. Fakat A’nin tersi alınamıyorsa, ki bu A karesel olmadığında otomatik
olarak doğru olacaktır, ne yapacağız? Genelleştirilmiş tersi alma, ya da sözde
ters (pseudoinverse) işlemi burada ise yarar. Sözde ters için, her A için bir SVD
olduğuna göre, A = UΣV T , ve Σ’nın sıfır olmayan eşsiz değerlerinin tersi alınır
(yani öğe σi , 1/σi olur), sıfır değerlerine dokunulmaz, bu sonuçlar yeni bir Σ† ’in
köşegenine dizilir, “sözde ters” bu matris olur,

Σ−1
 
† 1 0
Σ =
0 0

Ve bu ters işlemi tüm A’nin tersini almak için kullanılır, ki bu sözde tersi de
boyutu n × m olan bir A† ile gösteriyoruz (İngilizce “A-dagger” olarak telafuz
ediliyor, biz “A-kama” diyelim),

A† = VΣ† UT (1)

Bu noktaya nasıl geldiğimize dikkat, eğer SVD sonucunun pür tersini alabilsey-
dik,

A−1 = V −T Σ−1 U−1

Dikgen matrisler için Q−1 = QT olduğu için

= VΣ−1 UT

Σ−1 olmadığı için yerine Σ† kullanıyoruz ve (1)’e erişiyoruz.


Bazı özellikler,

AA† A = A

ya da

A† AA† = A†

Peki sözde tersi alma işlemini denklem sistemi çözmekte nasıl kullanırız?
Lineer sistem çözümü bağlamında durumunda 3 türlü sonuç olabileceğini görmüştük.
Eğer sonsuz tane çözüm varsa, bu büyük bir ihtimalle problemin tam kısıtlanmamış
(constrained) olması ile alakalıdır. Tabii hiçbir çözüm olmayabilir, ve size para
veren kişi sizden hala çözüm beklemektedir (!), bu durumda Ax = b’yi çözmek
yerine ona en yakın olabilecek şeyi çözebiliriz, yani |Ax − b|2 ’yi minimize etmeyi
seçebiliriz, Ax’i b’yi mümkün olduğu kadar yaklaştırırız. Burada sözde ters ise
yarar, çünkü x∗ = A† b ile hesaplanan çözüm aynı zamanda |Ax − b|2 ’yi minimize

12
eder! Bu çözüm En Az Kareler (Least Squares) çözümü olarak ta bilinir, tabii
burada sistem aşırı belirtilmiş (overdetermined) değil, eksik belirtilmiş (under-
determined) durumda. Not: Ayrıca sözde ters ile bulunan x∗ ’in mümkün tüm
çözümler arasında “norm’ü en az olan x∗ ’i bulduğu da” söylenir.
Eğer çözüm özgün ise, sözde ters yine işler, özgün çözümü bulur. Yani her
halükarda sözde ters tüm problemlerimizi çözer.
Lineer cebir’i böylece gözden geçirmiş olduk. Artık dersimizin ana konularına
başlayabiliriz.
Hareketli bir Sahneyi Temsil Etmek
Burada sahne dış dünya, yani kamera ile hareket ederken gördüğümüz şeyler.
Hareket ederken kamera pek çok resim alabilir, tabii bu dersimiz uzun zamandır
yapılan araştırmalara dayanıyor, ve bu araştırmalar çoğunlukla iki resim du-
rumuna odaklandılar; fakat günümüzde bir kamera saniyede mesela 30 tane
resim çekebilir, bu durum için gerekli matematiği de göreceğiz. Önce iki res-
imle başlayacağız, ve bu matematiğin daha genel, çok resimli haline de kendimizi
hazırlayacağız.
3D’de Yeniden Oluşturmak (3D Reconstruction)
Durağan olduğu kabul edilen 3D dış dünyayı pek çok açıdan ama iki boyutlu
resmi ile tekrar oluşturma çabasının bilimde uzun bir tarihi var. Bu problem
klasik bir “kötü konumlanmış (ill-posed)” problemdir, çünkü yeniden oluşturulan
sonuç tipik olarak özgün değildir (pek çok farklı yeniden oluşturma mümkündür).
Bu yüzden ek bazı kısıtlamalar getirmek gerekir. Bu alanda hala yapılacak çok
iş olduğunu belirtmek isterim, yani bilim dalımız oldukça bakir [araştırmacılar,
atlayın].
Dış dünyanın gördüğümüz imajdaki nasıl oluştuğunu perspektif izdüşümü (per-
spective projection) üzerinden modelleyeceğiz, bu modelleme kamera modeli
olarak iğne deliği kamera (pinhole camera) modelini kullanır. Bu modeli şöyle
hayal etmek mümkün, karanlık bir odadayız, duvarda tek bir delik var, ve bu oda
dışındaki tüm görüntüler bu delik üzerinden odaya giriyor. Perspektif izdüşüm
ilk kez Öklit tarafından, I.O. 400 yılında araştırıldı; bu hakikaten çok ilginç, yani
sonuçta bugün bilgisayarlar ile araştırdığımız bu konunun temelindeki bazı kavram-
ların ne kadar önceden beri bilindiği şaşırtıcı olabiliyor.
Ardından bu konu Rönesans sırasında çok yoğun araştırıldı, bu zamanlarda yapılan
resimlerdeki derinliği temsil etme çabaları bugüne kalan eserlerden hepimiz biliy-
oruz. Sanatçılar, bilimciler iki boyut üzerinde derinliği, üç boyutluluğu gösterebilmek
için kafa yordular, ve perspektif izdüşümün araştırılması 17. ve 18. yüzyılda per-
spektif geometrisi adında bir yeni alana dönüştü.
Çoklu Bakış Açıdan Tekrar Oluşturma (Multiview Reconstruction) alanında yapılan
ilk araştırma oldukça eskiye gidiyor, ki bu da şaşırtıcı. Kruppa bu konuyu 1913’te
araştırdı, iki farklı kameranın aynı objeye dönük iki resmine odaklandı, kendine

13
şu soruyu sordu, “bu iki resimde en az kaç tane noktaya bakmalıyım ki 3 boyutta
bir model oluşturabileyim”. Kruppa gösterdi ki en az 5 nokta sonlu miktarda
çözüm bulmak için yeterli, ki kameranın hareketi de buna dahil. Tabii bulunan
özgün tek çözüm değildir, ama daha önce söylediğimiz gibi bu problem kötü
konumlanmış bir problemdir, ama en azından çözüm sonsuz tane değil, sonlu
sayıda.
İki görüntüden hareket ve yapıyı çıkartabilen ilk lineer algoritma Lonquet-Higgins
tarafından 1981’de bulundu, bu algoritma eş kutupsal kısıtlama (epipolar con-
straint) kullanarak bu işi becerdi. Bu derste eş kutupsal kısıtlama konusunu
öğreneceğiz. Bu buluş pek çok takip eden diğer buluşa ilham verdi, 80 ve 90’li
yıllarda ek buluşlar yapıldı. Ardından alanımızın klasik kitaplarından Zisserman
ve arkadaşlarının yazdığı Çoklu Bakış Açı Geometrisi (Multiple View Geometry)
adlı kitap var, ve iş giderek bizim bu derste kullandığımız en güncel olan kitaba
geliyor, An Invitation to 3D Vision.
Derste işlediğimiz konu farklı isimlerde ortaya çıkabiliyor, hareketten yapı çıkartmak
(structure from motion) ismini gördük, bir diğer isim görsel (visual) SLAM, ki
SLAM kısaltması “aynı anda yer belirlemek ve haritalamak (simultaneous local-
ization and mapping)” kelimelerinden geliyor. Robotik alanındakiler bu kelimeyi
çok kullanırlar, bir robotun dış dünyada hem etrafını haritalaması, hem de aynı
anda o harita içindeki yerini kestirebilmesi, hesaplaması bu alanın baş problem-
lerinden. Tabii görsel SLAM bunu görsel olarak yapabilmek; çünkü çok farklı
şekillerde SLAM yapılabiliyor, mesela 1. ders başında söylediğimiz gibi lazer
algılayıcılarla SLAM yapılabilir, hatta sonar algılayıcılarla bile, ya da tüm bunları
bir algılayıcı füzyonu (sensor fusion) üzerinden birleştirerek.
Kaynaklar
[1] Olver, Applied Linear Algebra
[2] Bayramli, Lineer Cebir, Ders 15
[3] Sastry, An Invitation to 3-D Vision
[4] Zissermann, Multiple View Geometry
[5] Bayramli, Lineer Cebir, Ders 5

14
Ders 3
Bugünkü dersin konusu dış dünyayı modellemek, kamera hareketini temsil ede-
bilmek, bu sırada Lie grupları, Lie cebirini de öğreneceğiz. Lie grubu, adı üstünde
bir grup, fakat ek olarak bazı ek tanımlar içeriyor. Örnek olarak kamera hareketi
bir Lie grubu oluşturuyor. Kamera transformasyonunun (hareketinin) tersi alınabilir,
ki genel Lie grupları için de bu mümkün, tabii bu fiziksel kamera hareketi için
de doğru, kameraya başlattığımız noktaya geri getirebiliriz. Ama esas önemli
nokta şu: kameranın hareketini sürekli bir ortamda tanımlamak mümkün - son-
suz küçüklükteki (infinitesimal) kamera hareketleri tanımlanabilir, ki bu durum
kamera hareketini Lie grubu yapan en önemli faktör.
Üç Boyutlu Öklit Uzayı
Üç boyutlu Öklit uzayı E3 , tüm p ∈ E3 noktalarından oluşur, ki bu noktalar

T
∈ R3

X= X1 , X2 , X3

kordinatları ile karakterize edilir. E3 ve R3 aynı olarak kabul edilebilir.


Eldeki iki nokta X, Y için

v = Y − X ∈ R3

Bir vektör elde ettik, ki bu vektör (diğer tüm vektörler gibi) başlangıç noktasından
bağımsız. R3 içindeki tüm vektörler bir lineer vektör uzayı oluşturur. E3 ’u R3 ile
eş kabul ettik, o zaman R3 ’den tek sayı çarpımı, norm, ölçekler gibi özellikleri al-
abiliriz, bu sayede uzaklıkları, ya da eğri uzunluğu gibi şeyleri hesaplayabiliriz,
mesela

Z1
I(γ) = |γ̇(s)| ds
0

ki bu herhangi bir γ : [0, 1] → R3 eğrisi için. Formüldeki || bir Öklitsel norm,


R3 ’ten geliyor.
Çapraz çarpımı görmüştük.
Tüm 3×3 eksi bakışımlı matrislerin uzayı so(3) olarak gösterilir, dikkat daha önce
SO(3) vardı, özel dikgen matrislerin uzayı. Bu so(3), küçük harfli olan, farklı. Ne
şekilde onu birazdan göreceğiz. Aradaki bağlantıyı hemen belirtebilirim ama,
SO(3) bir Lie grup, so(3) onunla ilişkili olan Lie cebiri.
Daha önce katı gövde transformasyonundan bahsettik, ve kamera hareketi böyledir
dedik; yani yer değişimi + rotasyon. Fakat katı gövde hareketini tanıştırmanın
bir değişik yolu daha var, hatta bu yol katı gövde tanımındaki “katı” kelimesine
daha uygun, bu tanıma göre bir objenin üzerinde iki nokta düşünelim, bu iki
nokta arasındaki mesafe transformasyon ardından değişmeden kalmalı. İşte bu

1
sebeple gövde “katı” çünkü değişmiyor. Formel olarak belirtmek gerekirse katı
gövde transformasyonları su aileye

gt : R3 → R3 ; X → gt (X), t ∈ [0, T ]

ait olan eşlemelerdir (yani fonksiyonlardır) öyle ki herhangi iki vektörün norm
ve çapraz çarpımı muhafaza edilir,

|gt (v)| = |v|, ∀v ∈ R3

gt (u) × gt (v) = gt (u × v), ∀u, v ∈ R3

Üstteki tanım aynı zamanda noktasal çarpımın da değişmediği anlamına geliyor,


bu üstteki tanımdan bariz olmayabilir, ama norm ve tek sayı çarpımı kutupsal
özdeşlik (polarization identity) üzerinden norm ile alakalı olduğu için,

1
hu, vi = (|u + v|2 − |u − v|2 )
4

o zaman noktasal çarpımın da değişmemesi gerekir.


Daha bitmedi: eğer üstteki üç tanım doğru ise o zaman üçlü çarpım (triple prod-
uct) da muhafaza edilir demektir, ki ∀u, v, w ∈ R3 için

hgt (u), gt (v) × gt (w)i = hu, v × wi

eşitliği doğru olmalıdır. Bu ifade aynı zamanda katı gövde transformasyonun


“hacmi muhafaza ettiği” anlamına da gelir, çünkü üstteki ifade hacim hesabı için
kullanılır. Lise matematiğinden hatırlanacağı üzere,

üç vektörün kapsadığı hacmin hesabı üstteki formüldür. Yani katı gövde hareketi
üçlü çarpımı muhafaza ediliyor demek, bu da hacmi muhafaza ediyor demek-
tir. Bazı hareketler vardır ki katı gövde değildir, mesela bir süngeri alıyorum,
sıkıştırıyorum, bu hareket hacmi muhafaza etmedi.
Peki üstteki tanım katı gövde hareketini kesinlikle temsil eder mi? Bunu formel
bir şekilde göstermek istiyoruz; transformasyon gt , ki t anındaki katı gövde değişimini
gösteriyor. Bu ispat için değişimin bir orijin ve 3 tane birimdik (orthonormal)

2
vektör e1 , e2 , e3 ∈ R3 ’u nasıl etkilediğini göstermem yeterli. Diğer her nokta bu
baza referanslı olacağı için bu yeterli oluyor. Orijinin hareketini yer değişimi
T ∈ R3 olarak göstereyim, vektör ei ’ların transformasyonu ise ri = gt (ei ), bu
transformasyon sonrası yeni bir baz elde etmiş olacağız.
Tek sayı ve çapraz çarpımı muhafaza edilir demiştik, yani

rTi rj = gt (ei )T gt (ej ) = eTi ej = δij , r1 × r2 = r3

δij hatırlarsak i = j ise 1, değil ise 0 veren bir notasyonel ifade.


 T
Üstteki 1. kısıtlama matris R = r1 r2 r3 dikgen (rotasyon) matrisi demek
T T
ile aynı şeydir, yani R R = RR = I. Ve çapraz çarpımlı 2. ifade det(R) = +1
demektir, yani R matrisi,

SO(3) = {R ∈ R3×3 | RT R = I, det(R) = +1}

grubunun bir üyesidir. Ve evet, katı gövde hareketi hakikaten de

gt (x) = Rx + T

olarak yazılabilir.
Rotasyon matrisine yakında bakalım. Dediğimiz gibi bir açı üzerinden rotasyon
yapabiliriz, ve tüm bu rotasyonlar bir grup oluşturur. Sonsuz küçük rotasyon da
mümkündür, kamerayı alırım, azıcık döndürürüm. Bu döndürme gruptaki bir
öğeye tekabül eder. Bu her türlü grup için geçerli olmayabilir, mesela içinde yine
sonsuz tane öğe olan tam sayıları alsam, azıcık değişimi bu küme içinde temsil
edemezdim, çünkü öğeler ayrıksal (discrete).
Niye sonsuz küçüklükteki rotasyonlara bakıyoruz? Rotasyonları temsil etmek
aslında külfetli bir iş; mesela rotasyonların olduğu uzay lineer değil, iki rotasyon
matrisi R, R̃’yi alsam mesela ve onları toplasam (ki böylece bir toplam döndürmeyi
hesaplayacağımı umardım) yeni bir rotasyon matrisi elde edemiyorum. Zorluk
aslında RT R = I ve det(R) = +1 kısıtlamalarından ileri geliyor. Mesela iki resim
var, bu resme bakarım bir kameranın nasıl döndüğünü göstermek istiyorum, ro-
tasyon matrisi şöyle olacak,

 
a b c
 d e f 
g h i

Bu matristeki değişkenlerin değerini atamakta serbest olamıyorum, dediğim gibi,


belirtilen iki kısıtlamaya uymam lazım. Yani bir optimizasyon işletip üstteki 9
değişkeni hesaplamaya uğraşırken bir de onun üstüne 2 tane çok ağır şarta da
uymam lazım. Bu şartlardan en ağırı determinant aslında.

3
O zaman sonsuz küçüklükteki rotasyonun temsilini türetelim; bir rotasyon ailesini
temsil eden R(t) olsun, ki bir noktayı sürekli transform ediyorlar, başlangıç nok-
tası R(0) = I, yani birim matrisi,

Xtrans (t) = R(t)Xorig , R(t) ∈ SO(3)

Bir Xorig noktasını aldım, ve her t anında döndürüyorum, sonuç Xtrans . Tabii
noktanın yeri değişmiyor, ama kameranın ekseni değiştiği için ona göre nokta
değişmiş gibi oluyor.
R(t)R(t)T = I, ∀t olduğu için (olmalı çünkü rotasyon matrisleri dikgen) bu aynı
anda, her t anı için bu çarpımın sabit olduğu sonucunu verir, ve her t için sabit
olan bir şeyin, Analiz dersinden hatırlayabileceğimiz üzere, t’ye göre türevi sıfır
olmalıdır. Yani,

d
(RRT ) = ṘRT + RṘT = 0
dt

ṘRT = −(ṘRT )T

Bu bize ṘRT ’nin eksi bakışımlı matris olması gerektiğini söylüyor, yani önceden
∧ operatörü ile ulaştığımız bir sonuç noktası. O sonuç noktasına ŵ(t) diyelim,

Ṙ(t)RT (t) = ŵ(t)

Sağdan R(t) ile çarpalım,

Ṙ(t) = ŵ(t)R(t)

R(0) = I olduğuna göre, üstte yerine koyalım,

Ṙ(0) = ŵ(0)

Bu demektir ki eksi bakışımlı matris ŵ(0) ∈ so(3) bize birim matris I etrafında
rotasyon için 1. derece bir yaklaşıksallık sağlıyor, yani

R(dt) = R(0) + dR = I + ŵ(0)dt

Yani rotasyonu bir teğet uzayında harekete çevirmiş oldum. Bu uzaya Lie cebiri
ismi veriliyor. Elde ettiğim avantaj bir teğet uzayının, ŵ(0) yönünde, daha rahat
işlem yapabilmeme izin vermesi. Bu uzayın öğeleri eksi bakışımlı matrisler, yani
köşegenleri sıfır, bazı öğeleri dolu, vs. ve serbestlik derecesi 3 olan rotasyon bir
uzay bu. Eğer rotasyonları 9 öğesi dolu olan bir matris, onun üstüne iki tane

4
kısıtlama üzerinden tanımlasaydım, işler arap saçına dönecekti. Eksi bakışımlı
matrisler üzerinde işlemler çok daha rahat oldu.
Kamera hareketinin kestirilmesi / hesaplanması için yapılan budur; Lie cebiri
içinde, o lineer uzayda kalarak bir hesap yapmak böylece kamera hareketini yaklaşıksal
olarak bulmak. Bunu sadece 3 serbestlik derecesi üzerinden yapabilirim, başka
hiçbir kısıtlamaya bakmam gerekmez. Kısıtlamaları dikkate alarak yapılması
gereken optimizasyon çok saç yolduracak bir iştir. Bundan mümkün olduğunca
kaçınmak gerekir.
Üstte yaptıklarımız işin ruhu olarak şuna da benzeyebilir: Mesela rotasyon şu
şekilde de gösterilebilir,

 
cos θ − sin θ
sin θ cos θ

θ = 0 dersem birim matrisi elde ederim. Şimdi diyelim ki sıfır değil ama “sıfıra
çok yakın” bir değerim var; matristeki terimler için bir Taylor açılımı yapabilirim,
ve 1. derece terimleri kullanırsam,

 
θ 0
0 θ

elde ederim, ki bu matris bir eksi bakışımlı matristir, birim matris artı üstteki
değişime geldik. Ama tabii tek düzlemde olunca zaten tek serbestlik derecesi
var, θ. Ama üç boyutlu rotasyon söz konusu olunca ifade üstteki kadar temiz
olmuyor, ki Lie cebirine vs. bunun için giriyoruz.
Ana konumuza dönelim; Tüm rotasyonlar Lie grubu, teğet uzayı Lie cebiri. Gösterdik
ki sonsuz küçük bir dönüş R ∈ SO(3)’un etkisi, eksi bakışımlı matrisler uzayının

so(3) = {ŵ | w ∈ R3 }

bir öğesi ile yaklaşıksal olarak temsil edilebilir. Bu rotasyon grubu SO(3) Lie
grubudur, so(3) ise Lie cebiridir.
Tanım
Bir Lie grup (ya da sonsuz ufak grup) aynı anda hem grup hem de bir pürüzsüz
bükümdür (smooth manifold). Grup operasyonları çarpma ve tersini alma bir
pürüzsüz eşlemedir (smooth maps). Gösterdik ki birim matris noktasında rota-
syon grubu SO(3)’un teğeti so(3) Lie cebiri.
Pek çok değişik Lie grubu vardır, ama bilimde en yaygın kullanılanı SO(3). Ayrıca
yer değiştirme için gereken SE(3) grubu (özel Öklitsel grup).
Bu arada “cebir” kelimesi kafa karıştırmasın; burada cebir kelimesinin soyut
matematikteki anlamını kullanıyoruz, yani bir alan (field) üzerinde tanımlanmış

5
olan cebir, bu tanım çarpım operasyonu ile bir vektör uzayı V’nin K üzerinde
V’deki bir çarpım üzerinden.
[Lie bracket atlandı]
Peki Lie cebirden Lie grubuna geri nasıl giderdik? Bunun için üstel fonksiyon-
lar (exponential functions) kullanılacak, yani Lie cebirden Lie gruba gidiş üstel
fonksiyonlar ile eşlenmiştir. Niye, sebebini göreceğiz, çok zor bir kavram değil.
Elimizde bir rotasyon grubu olduğunda eksi bakışımlı matrisler üzerinden son-
suz küçüklük formülasyonuyla rotasyon modelini belirttik. Peki bu modeli kul-
lanarak R(t) için bir model bulabilir miyiz? ŵ sabit olsun, diferansiyel denklem
sistemi,

Ṙ(t) = ŵR(t) (4)

R(0) = I

Bu sistemi çözmeye uğraşalım. Elimizde 1. derece türev var (ilk formül) ki bu


formül rotasyon matrisindeki değişimi gösteriyor, ve başlangıç şartı var (ikinci
formül). Fakat basit lineer denklemlerden biliyoruz ki değişkenin değişimi bir
sabit üzerinden o değişkeni bağlıysa, çözüm bir üstel çürüme (decay) ya da üstel
büyüme (growth) üzerinden modellenir. Matris durumunda da benzer bir sonuç
var,

R(t) = eŵt

Üstel fonksiyonların ayrıca seri olarak açılımı da var, tek değişkenli basit durum,

X

xn
ex =
n=0
n!

Matris bazlı problemimiz için,

X

(ŵt)n (ŵt)2
ŵt
R(t) = e = = I + ŵt + + ...
n=0
n! 2!

Bu R(t) ifadesi bir w ∈ R3 ekseni etrafında t açısı kadar olacak bir dönüşü temsil
eder (eğer ||w|| = 1 ise).
Alternatif olarak skalar t’yi eksi bakışımlı matris ŵ içine çekebiliriz, ki v̂ = ŵt
olacak şekilde, bu durumda R(t) = ev̂ olur.
Böylece bir matris üsteli üzerinden Lie cebirinden Lie grubuna geçiş yapabilmiş
olduk,

6
exp : so(3) → SO(3); ŵ → eŵ

Eşleme için üstel fonksiyon kullanınca o fonksiyonun tersini kullanarak tekrar


geriye Lie grubundan Lie cebirine geçiş için de bir kolay yol elde etmiş oldum,
üstel fonksiyonun tersi nedir? Logaritmadır. Matrisler üzerinde logaritma kul-
lanmak mümkün, analiz derslerinde çoğunlukla gösterilmez ama matris bazlı
fonksiyonların da Taylor serisi açılımları vardır, ve üstel, logaritma fonksiyon-
larının matrisler üzerindeki davranışı bu açılımlar üzerinden incelenir. Bu nok-
tada bir zorluk açılımlardaki matrisin üstünü almak (kare, küp, vs) olurdu, bu
çarpımların hesaplanması gerekiyor mu? İşin pratiğinde cevap çoğunlukla hayır;
ileride bu tür hesapları daha temiz bir şekilde temsil etmeninin yolunu göreceğiz.
Logaritma formülü şu şekilde, ŵ = log(R) için, ve R öğelerini rij olarak gösterelim,
ki R 6= I (I alttaki formülü patlatır, zaten durumunda hemen sıfır sonucuna vara-
bilirdik), w şöyle bulunur,

 
  r32 − r23
trace(R) − 1 w 1
|w| = cos−1 , =  r13 − r31 
2 |w| 2 sin |w|
r21 − r12

Bu formülü ispatsız veriyoruz. Not: Dikkat, üstteki formül bir yaklaşıksallık


değil.
Üstte ifade edilen şudur: bir rotasyonu w/|w| ekseni etrafındaki bir |w| açısı ile
temsil edebilirim. Yani w/|w| bir birim vektördür, bir eksen / yön gösterir, ve
eksen etrafında |w| kadar dönülür. Elimdeki bir R için bu hesabı yapabilirim. Bu
oldukça faydalı bir hesaptır.
Bir not daha, üstteki temsil özgün değil, yani bir R için hesaplanan ŵ bir çözüm
“ailesidir”, ve içinde sonsuz tane çözüm vardır, çünkü eğer açıyı 2π’nin katlarıyla
arttırırsam, tekrar aynı R’yi elde ederim.
Rodriguez formülünü daha önce bir şekilde türetmiştik, bu formüle göre rota-
syon,

ŵ ŵ2
eŵ = I + sin |w| + (1 − cos |w|)
|w| |w|2

ile hesaplanabilir. Bu formül faydalı çünkü pratikte matrislerin kuvvetini almak


tercih edilmez (matris üstel açılımının / serisinin içinde matris kuvvetleri var),
R’yi veri kullanarak kestirmek / hesaplamak bir optimizasyon problemidir, çoğunlukla
bir fiyat fonksiyonu (cost function) olur, mesela E(R), ve bu E’yi minimize edecek
en iyi R bulunmaya uğraşılır. Tabii, daha önce belirttiğimiz gibi, bu optimizasyon
9 değişken + kısıtlamalarla uğraşırsa işi zor olur, bu yüzden Rŵ üzerinden, yani
eksi bakışımlı bir matris içeren hali üzerinden optimizasyon yaparız, ki bu ma-
tris 3 tane değişken içerir. Tipik olarak kamera duruş optimizasyonu bu şekilde
yapılır.

7
Rodriguez formülü ispatı (2. yöntem)
A bir eksi bakışımlı matris, o zaman
   
sin θ 1 − cos θ
A
e =1+ A+ 2
A2
θ θ

İspat
Daha önce

A3 = −(aT a) · A

olduğunu göstermiştik. θ2 ≡ aT a dersek, üstteki eşitliği genelleştirebiliriz, ve su


özdeşlikleri (identity) elde ederiz,

A2i+1 = (−1)i θ2i A (2)

A2i+2 = (−1)i θ2i A2 (3)

Matris üstel açılımından biliyoruz ki,

1 1
eA = I + A + A2 + A3 + ... (1)
2 3!

Ayrıca

θ3 θ5 θ7
sin(θ) = θ − + − + ...
3! 5! 7!

θ2 θ4 θ6
cos(θ) = 1 − + − + ...
2! 4! 6!

Şimdi eğer (1)’deki eA ’in terimlerini şu şekilde gruplarsak,

A3 A5 A2 A4
   
A
e =I+ −A+ − + .. + − + + ...
3! 5! 2! 4!

İlk parantez içindeki A2i+1 , ikinci içindeki A2i+2 değil mi? O zaman özdeşlikler
(2,3)’u kullanalım,

X
∞  X∞
(−1)i θ2i (−1)i θ2i 2

A
e =I+ A + A
i=0
(2i + 1)! i=0
(2i + 2)!

8
Terimleri açıp tekrar gruplayalım,

θ2 θ4 θ2 θ4
   
A 1
e =I+ 1− + + .. A + − +
3! 5! 2! 4! 6!

İlk parantez içindeki sin θ açılımı olabilirdi, sadece θ’nin katı ile bölendeki fakto-
ryel farklı. Önemli değil, tüm ifadeyi θ’ya bölersek güçten bir eksiltmiş oluruz,
ve açılımı kullanabiliriz. İkinci parantez içindekiyse neredeyse cos θ açılımı, ama
1 − .. ifadesi yok, önemli değil, açılımı 1’den çıkartırız, ayrıca yine güçler ile bölen
faktoryel farkı var, bunu θ2 ’ye bölerek halledebiliriz. Sonuç,
   
sin θ 1 − cos θ
A
e = A+ A2
θ θ2

İspat tamamlandı. Bazı kaynaklar [4], ayrıca [1, sf. 27][2, sf. 583].
Katı Gövde Hareketi ve SE(3)
Rotasyon için kullandığımız teğet uzayı üzerinden Lie cebirine geçme numarasını
katı gövde hareketi bağlamında SE(3) ve se(3) için de kullanabiliriz. Bu durumda
rotasyona ek olarak yer değiştirme de var, fakat aynı şekilde sonsuz küçüklükteki
değişimleri modelleyebiliyoruz.
Verilen herhangi bir nokta için yer değiştirme (translation) T , ve rotasyon R üzerinden
tüm mümkün Öklitsel transformasyonlar,

SE(3) ≡ {g = (R, T ) | R ∈ SO(3), T ∈ R3 }

grubunu oluşturur. Homojen kordinatlarla,

 
R T
SE(3) ≡ g = R ∈ SO(3), T ∈ R ⊂ R4×4
3
0 1

Eğer matrisi tam boyutlarıyla göstermek istersek,

 
tx
 R ty 
g= 
 tz 
0 0 0 1

Sonsuz küçüklükteki değişimleri modellemek istiyoruz,

 
R(t) T (t)
g : R → SE(3); g(t) = ∈ R4×4
0 1

9
Homojen kordinatları kullandık, ki matrisler tersi alınabilir hale gelsin. Bu bize
”fırıldaklar” için Lie cebirini sağlıyor. Rotasyon durumuna benzer bir şekilde,
şimdi (4)’teki duruma benzer olarak

 
ṘRT Ṫ − ṘRT T
−1
ġ(t)g (t) = ∈ R4×4
0 0

formülüne bakalım. SO(3) durumunda olduğu gibi ṘRT bir eksi bakışımlı matris
ŵ ∈ so(3)’e tekabül ediyor. Bir v(t) = Ṫ (t) − ŵ(t)T (t) tanımlarsak, üstteki formül

 
ŵ(t) v(t)
−1
ġ(t)g (t) = ≡ ξ̂(t) ∈ R4×4
0 0

olacaktır. ξ̂’e fırıldak (twists) matrisleri ismi de veriliyor. Bu matris 4 × 4 boyu-


tunda, ve eğri g(t)’ye teğet bir vektör gibi görülebilir, mümkün tüm ξ̂’lerin uzayı,
aynen so(3) örneğinde olduğu gibi, bir Lie cebiri oluşturur.
Yani ġ’yi hesaplamak için

ġ = ġg−1 g = ξ̂g

Niye fırıldak ismi kullanılmış? Çünkü bir fırıldak hem döner, hem de yer değiştirir,
ve kameranın hareketi de aynen böyledir. Tüm grubu tanımlarsak,

 
ŵ v
se(3) ≡ ξ̂ = ŵ ∈ so(3), v ∈ R ⊂ R4×4
3
0 0

Daha önce olduğu gibi şapka notasyonunu bir operatör olarak görüyoruz, hatta
genişletelim, ileri ve geri gitmek için ∧ ve ∨ tanımlayalım,

 ∧  
v ŵ v
ξ̂ ≡ = ∈ R4×4
w 0 0

 ∨  
ŵ v v
= ∈ R6
0 0 w

Üstteki matrisin 6 serbestlik derecesi var, 3 tane dönüş için, 3 tane de yer değiştirme
için.
Kaynaklar
[1] Sastry, An Invitation to 3-D Vision
[2] Zissermann, Multiple View Geometry
[3] Zseliski, Computer Vision

10
[4] Eade, E., http://ethaneade.com/lie.pdf

11
Ders 4
Önceki derste Lie grup ve cebiri gördük. Lie cebirine geçiş sebebi lineer bir uza-
yda optimizasyon yapmak istememiz, alternatif gayrı-lineer uzayda, zor kısıtlamalar
üzerinden optimizasyon. Lie cebiri ile işleri kolaylaştırdık. Ayrıca cebirden gruba
giden bir üstel harita oluşturduk, ve onun tersi bir logaritma idi.
Sonra fırıldak cebirini gördük, ki bir 4 × 4 matrisi olan ξ̂, eğri g(t)’ye üzerinde bir
teğet vektör olarak görülebilirdi, ve ξ̂’ye bir fırıldak ismini verdik. g(t)’yi sağdan
çarpınca alttakini elde etmiştik.

ġ = ġg−1 g = ξ̂g

so(3) durumunda olduğu gibi tüm mümkün fırıldaklar bir teğet uzayı oluşturuyorlardı,
ki bu uzay bir Lie cebiridir.

 
ŵ v
se(3) ≡ ξ̂ = ŵ ∈ so(3), v ∈ R ⊂ R4×4
3
0 0

Üstteki uzay Lie grubu SE(3)’e teğet idi.


Daha önce olduğu gibi ∧ ve ∨ operatörlerini tanımlıyoruz, bu sayede fırıldak
ξ̂ ∈ se(3) ile fırıldak kordinatları ξ ∈ R6 arasında gidip gelebilecektik.

 ∧  
v ŵ v
ξ̂ ≡ = ∈ R4×4
w 0 0

Hesaplanacak 6 derece serbestlik üstteki v, w vektörlerinin içeriklerinden oluşuyor.


∧ uygulanan fırıldak vektörü, onun sonucu fırıldak matrisi.

 ∨  
ŵ v v
= ∈ R6
0 0 w

Diferansiyel denklem sistemi


ġ(t) = ξ̂g(t), ξ̂ = bir sabit
g(0) = I

Bu sistemin çözümü

X

(ξ̂t)n
ξ̂t
g(t) = e =
n=0
n!

Ve daha önce kendimize sorduğumuz aynı soruya tekrar geldik, acaba ξ̂’nin kat-
larını kodlama sırasında hesaplamamız gerekecek mi? Cevap yine hayır; ay-

1
nen Rodriguez formülüyle daha temiz bir şekilde hesap yapabildiğimiz gibi bu
fırıldak durumunda da benzer bir temiz hesap var,
" #
ŵ (I−eŵ )ŵv+wwT v
e
eξ̂ = |w| (1)
0 1
 
ξ̂I v
Eğer w = 0 olsaydı e = elde ederdik, üstteki formülün w 6= 0 için tama-
0 1
men hesaplanması gerekirdi. Formül içindeki eŵ Rodriguez formülüyle hesa-
planabilir.
Yani tekrar Lie grubundan Lie cebirine geçişi yapmış olduk.

exp : se(3) → SE(3); ξ̂ → eξ̂

Peki geriye gidiş mümkün mü? Bu noktada artık sürpriz olmayacak herhalde,
geriye gidiş yine logaritma ile mümkün. Matematiksel olarak ifade edelim,
Teori
Her g ∈ SE(3) için bir fırıldak kordinatı ξ̂ = (v, w) ∈ R6 vardır, ki g = exp(ξ̂).
İspat
Verilen herhangi bir R için eŵ = R olduğunu biliyoruz. Bu bize (1)’deki matrisin
sol üst köşesini sağlıyor. Geriye tek kalan matrisin sağ üst köşesindeki ifadeyi
ispatlamak. Bu ifadeyi,

(I − eŵ )ŵv + wwT v


|w|

v için çözersek, daha doğrusu çözülebileceğini gösterirsek, ispat tamamlanmış


olur.
Üzerinden tekrar geçmek gerekirse, hareket eden bir manzarayı (daha doğrusu
bizim / kameramızın hareketi yüzünden değişmekte olan arka planı) modelle-
mek için katı gövde transformasyonu kullanıyoruz, ki böylece dünya kordinat-
larındaki objeyi kamera kordinatlarına çeviriyoruz. Bu çevirme işlemi rotasyon
ve yer değiştirmeyi içeriyor, ki bu hesapları, her t anı için bir 4 × 4 matrisi

 
R(t) T (t)
g(t) = ∈ SE(3)
0 1

ile yapabiliyoruz. t = 0 anında kamera çerçevesi / ekseni / görüntüsü (frame) ile


dünya görüntü birbirine eşit, yani g(0) = I. Dünyadaki herhangi bir X0 noktası
için t anında kamera görüntü hali ,

2
X(t) = R(t)X0 + T (t)

3D tekrar oluşturma işlemlerinde üç boyutlu dünyayı bir görüntüye göre yarat-
mak gerekiyor; bu görüntünün seçiminde serbestsiz, istediğimiz görüntüyü seçebiliriz,
fakat gerçek dünya uygulamalarında bu görüntü genellikle ilk kamera pozisyonu
olarak seçilir. Kamera hareket eder, o hareket olurken ve biz hesaplarımızı ya-
parken dünyayı hep sanki ilk kameradan bakıyormuş gibi oluşturmaya çalışırız,
kordinatlar sürekli bu ilk kordinat sistemine tercüme ederiz.
Üstteki formül homojen kordinatlarda

X(t) = g(t)X0

Not: Hem normal hem homojen kordinatlarda aynı X0 ’in kullanılmış olması kafa
karıştırabilir, bunu notasyonu temiz tutmak için bilerek yaptım, eğer homojen
durum için X0 vektörüne ’1’ sayısını yeni bir öğe olarak eklemek gerekir, fakat bu
“yeni” vektör için yeni bir notasyon ekleseydim notasyon enflasyonu olacaktı. En
temizi aynı X0 ’i kullanmak, ama aklınızda olsun, eğer homojenlikten bahsediyor-
sak, X0 ’in dört ögesi var, ve sonuncu öğe 1.
Hareketleri Bitiştirmek
Diyelim ki kameranın iki ayrı hareketi sonrası t1 , t2 anlarında ardı ardına iki
görüntüye sırasıyla eriştik. t1 anındaki baktığımız noktaların t2 anına transforme
edilmesi için g(t2 , t1 ) kullanılacaktır, ve

X(t2 ) = g(t2 , t1 )X(t1 )

Tabii g’nin yapısı öyle olmalıdır ki t0 ’dan direk t2 ’deki hale gitmek ile önce t1
sonra t2 durumuna gitmek arasında fark olmamalı, yani

X(t2 ) = g(t3 , t2 )X2 = g(t3 , t2 )g(t2 , t1 )X(t1 ) = g(t3 , t1 )X(t1 )

ki üstteki ifade tüm noktalar için doğru olmalı, yani,

g(t3 , t1 ) = g(t3 , t2 )g(t2 , t1 )

Bu geçiş mantığını takip edersek, şunu da söyleyebiliriz; t1 görüntüsünün kordi-


natlarını t2 ’ye, oradan tekrar geriye gidersek, şu eşitliğin ortaya çıktığını görürüz,

X(t1 )g(t1 , t2 )X(t2 ) = g(t1 , t2 )g(t2 , t1 )X(t1 )

Bu eşitliğin tüm X(t1 ) noktaları için doğru olması gerektiği için,

3
g(t1 , t2 )g(t2 , t1 ) = I ⇔ g−1 (t2 , t1 ) = g(t1 , t2 )

Hız
Eğer t anındaki transformasyonun değişimini, bir hızı, yani Ẋ(t)’i tanımlamak
isteseydim, X(t) = g(t)X0 olduğunu hatırlayarak ve bu ifadenin türevini alarak,

Ẋ(t) = ġ(t)X0

Ayrıca

X(t) = g(t)X0 ⇐⇒ Xo = g−1 (t)X(t)

olduğuna göre, üstteki Xo ’i iki üstteki formüle koyabiliriz,

Ẋ(t) = ġ(t)g−1 (t)X(t)

Üstteki form tanıdık gelebilir, fırıldak tanımına yaklaştık. Fırıldak kordinatları


üzerinden,

 
−1 ŵ(t) v(t)
V̂(t) ≡ ġg (t) = ∈ se(3)
0 0

alttaki ifadeyi elde ederiz,

Ẋ(t) = V̂(t)X(t)

Basit 3D kordinat sisteminde

Ẋ(t) = ŵ(t)X(t) + v(t)

olurdu. Neyse, vardığımız sonuç şudur; V̂(t) bize dünya görüntüsünün kamera
çerçevesine göre izafi hızını vermiş oluyor.
Farklı Çerçevelerdeki Hız
Aynen noktaları transform edebildiğimiz gibi hızı da bir çerçeveden diğerine
taşıyabilmeliyiz. Diyelim ki iki kişi, A ve B değişik açılardan bir görüntüye
bakıyorlar, bu kişiler değişimi nasıl görürlerdi? Diyelim ki gxy : Y = gxy X(t).
Yeni görüntüdeki hız,

Ẏ(t) = gxy Ẋ(t) = gxy V̂(t)X(t) = gxy V̂g−1


xy Y(t)

4
Yani birinci kameradaki olanın ikinci kamerada görülen izafi hızı alttaki fırıldak
ile hesaplanır,

Vˆy = gxy V̂g−1


xy Y(t) = adgxy (V̂)

ad ifadesi katımlı (adjoint) kelimesinden geliyor,

adg : se(3) → se(3); ξ̂ → gξ̂g−1

se(3) üzerinde bir katımlı eşlemedir. Bu eşleme bir Lie cebir öğesini alır, ona
soldan ve sağdan bir katı gövde transformasyonu ve onun tersini uygular, ve
böylece yeni bir fırıldak elde etmiş oluruz.
Bu noktaya kadar pek çok farklı formülden bahsettik, özetlemek açısından alttaki
tablo faydalı olabilir,

Rotasyon SO(3) Katı gövde SE(3)



R T
Matris temsili R ∈ GL(3): RT R = I, g =
0 1
det(R) = 1
3-D kordinatlar X = RX0 X = RX0 + T 
−1 T −1 RT −RT
Tersi R =R g =
0 1
Üstel temsil R = exp(ŵ) g = exp(ξ̂)
Hız Ẋ = ŵX Ẋ = ŵX + v
T
Katımlı eşleme ŵ → RŵR ξ̂ → gξ̂g−1

[Euler açıları atlandı]

5
Ders 5
Bu derste kameradaki görüntünün nasıl oluştuğundan bahsedeceğiz. 3D tekrar
oluşturma (reconstruction) eyleminin amacı bu işlemi tersine çevirmek, 3D bir
şekilde 2D görüntü haline geliyor, biz 2D’deki bilgiden 3D’ye geri gitmeye uğraşıyoruz.
Önceki derste kameranın hareketini inceledik, rotasyon ve yer değiştirme. Bu
derste ana konu yansıtma / izdüşüm (projection), yani 3 boyutlu dünyanın 2
boyutlu düzlem üzerine yansıması.
Farklı yansıtma teknikleri vardır, en iyi bilineni perspektif yansıtma (perpective
projection) tekniğidir.
Görüntü oluşumunun incelenmesinin uzun bir tarihi var. Bu alandaki ilk in-
celemenin M.Ö. 400 tarihine, Öklit tarafından yapıldığı biliniyor. Kısmen doğru
perspektif yansıtma örnekleri M.Ö. 1 yılında Pompei’de bulunmuştur. Fakat bu
bilgi kesintisiz bir şekilde devam etmemiş anlaşılan, çünkü Rönesans’a kadar
uzun bir süre diğer bilimciler, sanatçılar perspektif geometrisinden habersiz bir
şekilde işlerini yapmışlar; Rönesans öncesi yağlıboya resimlerine bakılırsa çoğu
resimlerdeki objelerin, insanların düz bir şekilde, derinlik bilgisi kullanılmadan
resmedildiği görülebilir. Burada sanatçıların tercihleri de rol oynamıştır muhakkak,
sanatın gerçekçi değil ikonik, temsili olması istenmiş herhalde.
Rönesans sırasında perspektifsel geometri tekrar keşfedildi, ilerletildi ve sanat
bu şekilde üretilmeye başlandı. Sanatçılar Brunelleschi, Donatello, Alberti bu
yönde araştırmalar yaptılar, hatta yansıtma süreci hakkındaki ilk eser Della Pit-
tura kitabında Alberti’ye ait. Pür yansıtsal geometri olmasa da mesela ışığın
madde ile etkileşiminin nasıl olduğu da araştırıldı, 1500’lu yılllarda ünlü Leonardo
da Vinci bu konuyla çok ilgilendi, Rönesans sanatçıları Caravaggio, Raphael aynı
şekilde.
Perspektif yansıtma ve görüntü oluşumda en iyi bilinen model iğne deliği kam-
era (pinhole camera) modelidir. Bu model en basit haliyle bir kutuda çok ufak bir
delik açıldığı durumdur, böylece dışarıdan gelen objelerden yansıyan ışık (ki bu
ışık gözümüze düştüğü için o objeleri görüyoruz zaten), bu ufak delikten çıkarak
kutuya girer.

İlginç bir durum şu; ışık hüzmeleri düz gitmeye mecburlar, ama ufak bir delik-
ten geçmeye de mecburlar. Bu durumda geometriye göre kutunun arkasında,
yani deliğin arkasındaki yansıma terse dönmüş olacaktır. Böylece bir perspektif
yansıtma elde edeceğiz. Altta soldaki resimde temsili olarak bunu görebiliriz. Alt
sağdaki resim ise gerçek bir deneyden alınmıştır, aslında bu deney çok basit, bir
kutu, bir mum, kutuda bir delik yeterli. Kutunun arkasına bakınca hakikaten ters

1
dönmüş bir mum imajı görüyoruz!

İğne deliği kamera oldukça basit bir model / teknik, az ışıklı ortamlarda pek
başarılı değil; bugünlerde delik yerine mercek (lens) kullanılıyor, böylece lensin
farklı noktalarına düşen ışık hüzmeleri arka planda görüntü oluşumu için odak-
lanıyor / toparlanıyor. Merceklerin işleyişi hakkında biraz genel bilgi; mercekte
farklı noktalar ışığı değişik şekillerde “kırarlar” (refract), ve bu kırılma odaklama
amaçlı kullanılır.

Üstteki mercek te bir perspektif yansıtma oluşturur.


Odak noktaları ilginç, merceğin solunda ve sağında iki F noktası var. Bu nok-
taların mercege aynı uzaklıkta olması şart değil, ama eğer mercek simetrik ise
(üstteki gibi) o zaman uzaklık eşit.
Merceğin işleyişine göre, herhangi bir P noktası için diyelim, eksene paralel giren
P ışığı mercekten çıkınca arka odak noktasından geçmeli, sol odak noktasından
giren P ışığı mercekten çıkınca eksene paralel gitmeli. Böylece pek çok farklı yer-
den geçen ışık hüzmeleri aynı yerde odaklanıyor, iğne deliği kamerasına kıyasla
daha fazla ışığı toparlayabilmiş oluyoruz.
Üstteki modele göre perspektif yansıtmanın formüllerini nasıl türeteceğiz? Res-
imdeki iki üçgen A, B önemli; mesela P noktasını alıp yerini değiştirsem bu üçgenlerin
şekli değişecektir. A üçgeni P noktasının yüksekliği üzerinden Y’yi veriyor, B
üçgeninin sağ kısmı ise y’yi. Oranlar şöyle,

2
Y y Y
= − ⇐⇒ y = −f
Z f Z

Eksi değeri görüntünün ters dönmesinden ileri geliyor, mum örneğinde mumun
tepe taklak olmasında bu durumu görüyoruz. Dersin ilerisinde negatif işareti
kullanmayacağız, görüntüyü tekrar çevirilmiş kabul edeceğiz. Ayrıca zihinde
canlandırmanın kolay olması için mercekten geçtikten sonra üzerine görüntü düşen
algılayıcı düzlemi sanki mercek ile dış nesne arasındaymış gibi de hayal ede-
biliriz. Matematiksel olarak bu iki işlem (hem imajı dikey olarak, hem de imaj
perdesini mercek arkasından önüne almak) x, y eksenlerinin işaretlerinin tersini
kullanmak demektir. Bundan sonra işlemlerimizi bu şekilde yapacağız, aklımızda
olsun.

Perspektif transformasyonu π o zaman şu şekilde gösterilir,

fX
 
3 2
π:R →R ; X → x = π(X) = Z
Y
fZ

3D objeyi 2D’ye çevirmek böyle oluyor. Görüldüğü gibi oldukça basit bir işlem,
tek pürüz, işlemin gayrı lineer olması, çünkü Z ile bölüyoruz. Eğer bir matris
ile bu işi yapıyor olsaydım (ki bu çarpma, toplama işlemleri anlamına gelirdi),
bu matrisin tersi ile geriye gitmek kolay olurdu. Ama üstteki durumda işimiz
zorlaştı, çünkü gayrı lineer bir işlem yaptık. Bu zorluk tüm dersimiz boyunca
bizi uğraştırmaya devam edecek.
Fakat en azından notasyonel olarak işimizi biraz kolaylaştırabiliriz, eğer x’i ve
π(X)’i Z ile çarparsak, sağ tarafta bölümden kurtulmuş oluruz, ayrıca 2 boyutta
homojen kordinat sistemine geçersek

 
   X 
x f 0 0 0 
Y 
Zx = Z y = 0 f 0 0  
  
 Z  = Kf Π0 X

1 0 0 1 0
1

3
Böylece gayrı lineer kısmı ayırmış olduk, ve en sağdaki eşitlikte görüldüğü gibi
sadece lineer bir çarpımla iş görebiliyoruz. Ayrıca içinde f’ler olan matrisi Kf ve
Π0 olarak ayrıştırabiliriz,

   
f f 0 1 0 0 0
Kf =  0 f 0  , Π0 =  0 1 0 0 
0 0 1 0 0 1 0

Π0 ’ya standart yansıtma matrisi (standard projection matrix) ismi veriliyor. Bu


niye faydalı? Çünkü Π0 her durumda aynı, genel (generic) bir matris. Sadece
Kf kameraya özgü bir matris, her değişik f için (ki farklı kameraların farklı f’leri
olacaktır) farklıdır.
İlginç bir yaklaşıksallaşma Z’yi, yani kameradan olan uzaklığı sabit kabul etmek-
tir, Z yerine mesela λ > 0 diyelim; bu argüman der ki “eğer modellediğimiz 3D
noktalar kameradan yeterince uzakta ise Z’yi tek bir sabit ile gösterebiliriz”. λ
tabii ki gerçek uzaklıktır, çünkü onu Z’nin yerine geçecek şekilde seçeceğiz, ve Z
gerçek dünya kordinatından geliyor.
Biraraya Getirelim
Bu noktada her şeyi biraraya getirmeye uğraşalım - perspektif, kamera hareketi...
X0 dünya kordinatlarında (dikkat hareket eden kamera kordinatında değil) bir 3D
nokta. Bu noktayı kameranın katı gövde hareketi üzerinden kamera kordinatı X’e
çevirmek istiyoruz,

X = RX0 + T
 T
Homojen kordinatlarda, yani X vektörünün sonuna 1 ekleyerek X = X Y Z 1

 
R T
X = gX0 = X0
0 1

Üstteki matrisin tersi alınabilir olduğunu hatırlayalım. Homojen formatına geçmemizin


bir sebebi de buydu zaten, ki üstteki matris tersi alınabilir olabilsin. Tamamını bir
araya koyunca,

λx = Kf Π0 gX0

Eğer odak uzaklığı (focal length) f biliniyorsa görüntü kordinatlarını değiştirerek


onu 1’e normalize edebiliriz, ki bu duruma Kf ’i üstteki formülden çıkartmak
mümkündür,

λx = Π0 X = Π0 gX0

4
çünkü iki üstteki formüldeki çarpımlara sağdan sola bakarsak dünya kordinatından
kameraya, oradan iki boyutlu görüntüye, ve Kf olduğu durumda odak uzaklığı
üzerinden ölçekleme yapılıyor. Üstteki iki formülü ve benzer form’ları sağdan
sola okumak lazım.
Piksel Kordinatları
Dönüştürme, değiştirme daha bitmedi! Bir tane daha dönüşüm lazım,

Şimdiye kadar yaptıklarımız bize görüntüyü üstteki resim gibi verecek. Dış dünyadaki
objeler xcam , ycam kordinatlarına göre eksenlerin altına ya da üstüne düşebilecekleri
için eksi, artı değerlere sahip olabilecekler. Fakat biz kameradan gelen değerleri
hep artı değer olarak görmek istiyoruz, bu amaçla kordinat merkezini sol alt
köşeye taşımak lazım, ox , oy ile bunu yaparız.
Ayrıca kameranın görüntüsü dikey olarak yatay durumundan daha “yassı” ola-
bilir, bu durumda görüntüdeki pikseller de bir dikdörtgeni andıracaklardır. Hal-
buki dış dünyayı herhangi bir yönde yassılaşmış olarak değil, eşit ölçeklerde
görmek isteriz, eğer varsa bu eksikliği nötralize etmek için yatay / dikey ölçeklemeyi
düzeltmemiz gerekir, buna kaykı -skew- düzeltmek deniyor) sθ ile yapılır, fakat
pratikte biz bu durumun olmadığını farz edeceğiz.
Bir ölçekleme daha, piksel kordinatları birim ölçekte değil ise bunu da sx , sy ile
düzeltebiliriz.
Hepsi bir arada tek matris Ks içinde

 
sx sθ ox
Ks =  0 sy oy 
0 0 1

Hepsi bir arada

 
 0
    X 
x sx sθ ox f f 0 1 0 0 0 
Y 
λ  y 0  =  0 sy oy   0 f 0   0 1 0 0   
 Z 
1 0 0 1 0 0 1 0 0 1 0
| {z }| {z }| {z } 1
Ks Kf Π0

5
Elimizde pek çok transformasyon matrisi var artık. Peki bu matrislerden hangisi
kameranın içsel yapısıyla alakalıdır acaba? Yani marka ABC satın alıyorum, ya
da onun yerine marka XYZ alıyorum; bu iki kamera arasındaki imalatla alakalı
hangi parametreler birinden ötekine değişir? Odak uzaklığı f bunlardan biri, bu
parametre kameranın görüntü uzaklaştırma, yakınlaştırma (zoom in, out), yani
donanımı ile alakalı bir durum.
Ks matrisi de kameranın iç yapısıyla, donanım ayarlarıyla alakalı. Mesela bir
kameradaki çözünürlüğü değiştirdiğimiz zaman Ks içindeki değerleri değiştirmemiz
gerekir.
O zaman bu iki matrisi birleştirebiliriz, K = Ks Kf , ki K’ye içsel parametre matrisi
(intrinsic parameter matrix) adı verilir.

 
fsx fsθ ox
K = Ks Kf =  0 fsy oy 
0 0 1

Daha kısa olarak αx = fsx , αy = fsy . En-boy oranı (aspect ratio) ise σ = αx /αy .
Devam edelim,

λx = KΠ0 X = KΠ0 gX0 = ΠX0

Dikkat, yeni bir Π matrisi var, bu bir 3 × 4 matris ve Π = KΠ0 g olarak tanımlı.
Bu matrise genel yansıtma matrisi (general projection matrisi) adı veriliyor. Π =
(KR, KT ) yani.
K matrisi içsel parametreler ise, dışsal parametreler g içinde. Bir kamerayı hareket
ettirdiğimizde içsel parametreleri aynı kalacak, dışsal parametreler 6 serbestlik
derecesi üzerinden değişecek.
Konumuzdaki çoğu uygulama iki ana dala ayrılabilir, kamera kalibrasyonu bili-
nen durum, ya da bilinmeyen durum. Kamera bizimse hesaplarımıza başlamadan
önce onu inceleyip, odak uzaklığını kendimiz set edip kalibrasyonu öğrenebiliriz,
yani K bilinir. Bu kolay senaryo. Kalibrasyonsuz durum da olabilir; elimizde
sadece arka arkaya “bir” kameradan gelmiş görüntüler var, ama kamera hakkında
hiçbir şey bilmiyoruz. İtiraf etmek gerekirse bu senaryo oldukça işleri zorlaştırıyor.
Not: Bazı görüntü dosyalarının içinde içsel parametreler kaydedilmiş olabiliyor
bu arada, İnternet’ten bir sürü resim indirip 3D tekrar oluşturma yapmak is-
tenirse bunu akılda tutmak faydalı.
Tüm bunlardan sonra x 0 , y 0 formülüne dönelim, unutmayalım üstteki transfor-
masyonlar lineer değil demiştik, Z ile, yani λ ile bölüm gerekiyordu, o zaman
nihai sonuç

6
πT1 X0 πT2 X0
x0 = , y0 = , z0 = 1
πT3 X0 πT3 X0

ki πT1 , πT2 , πT3 ∈ R4 yansıtma matrisi Π’nin satırları.


[Küresel perspektif izdüşümü, yarıçapsal yamultma (radial distortion), preim-
age, coimage atlandı]
Ekler
[1]’deki programı kullanarak kamera matris parametrelerindeki değişimin görüntüye
nasıl yansıdığını görebiliriz. Her resim tek bir parametrenin değiştirilmesiyle
elde edilmiştir.
Odak Uzaklığı

Kaykı, x0

y0

7
Kaynaklar
[1] Kyle Simek, Dissecting the Camera Matrix, Part 3: The Intrinsic Matrix, http:
//ksimek.github.io/2013/08/13/intrinsic/

8
Yansıtma Matrisini Bilinen 3D-2D Eşlemelerinden Hesaplamak
Çoğu kaynakta ve bu ders anlatımlarında kameraya yansıtılan görüntü, dünya
kordinatları kavramları birbirine karışık şekilde gösterildi.

Fakat dünya kordinat sistemi, kamera merkezi gibi kavramları birbirinden ayırmamız
gerekiyor. Çünkü uygulamalarda kamera z kordinatı üzerinde durmuyor ve
kamera merkezi ile dünya merkezi birbirinden farklı, ayrıca çoğunlukla ne P’nin
ne de onu oluşturan içsel parametre matrisi (intrinsic parameter matrix) K biliniyor.
Önce kameranın nerede olduğuna bakalım. Kamera çoğunlukla dünya merkezin-
den değişik bir yerdedir, mesela elle tutulan bir cep telefonu durumunda boy
yüksekliğinde ve z kordinatına yönünde (ama üzerinde olmayabilir) doğru tu-
tulmaktadır. Önündeki objelerin yerleri dünya (world) kordinatlarına göredir,
ayrıca kameranın kendisi dünya merkezinden o noktaya bir döndürülme ve taşınma
(rotation and translation) sonucu gelmiştir.

1
Bu döndürülme ve taşınma matrislerini R, T olarak tanımlarsak,

 
P=K R t

olduğunu görmüştük, ve bu matris 3 x 4 boyutundadır, ki K içsel parametre


matrisi idi. Ayrıca biliyoruz ki P’yi baz alarak bir Xi üç boyutlu noktasının iki
boyutlu noktaya yansıması

λi xi = PXi

olarak hesaplanıyor. Kordinatlar homojen kordinatlar, yani (vektörleri bir kerelik


 T
net olması için koyu renkle gösterirsek) xi = xi yi 1 , aynı şekilde Xi =
 T
Xi Yi Zi 1 .
P’yi nasıl hesaplarız? Bu hesap için elimizde belli sayıda dış dünyada üç boyutlu
ve onun iki boyutlu yansımalarını içeren, birbiri ile eşlenmiş bir veri seti olduğunu
varsayacağız. Bu veriyi elde etmek zor değil, profosyonel ölçümler için kamera
önüne belli uzaklıkta, gerçek boyutları kesin bilinen bir obje konur, ve kamera
görüntüsünde bu cismin bilinen noktalarının nereye tekabül ettiğine bakılır, vs.
Ya da kabaca yeri bilinen objelerin piksel yerleri kaydedilir. Dış dünyada şöyle
bir resim olduğunu düşünelim,

2
Bu resimde ölçümleri bir dünya merkezine göre almak lazım. Resimde kam-
erayı ben tutuyorum, ve şimdi ölçümler için merkezin 20 metre arkamda ve 20
metre solumda olduğunu farzediyorum ve kameranın yerden yüksekliğini 2 me-
tre kabul ediyorum. Şimdi bu merkeze göre resimde görülen bazı noktaları elle
kendim seçerim, ve kabaca onların uzaklıklarını biliyordum, ona göre üç boyutlu
uzaklık atayıp, ayrıca bu noktaların hangi piksel kordinatında olduğunu bir imaj
programı üzerinden yine kendim bulup bu eşlemeyi bir yere kaydederim. Görsel
olarak irdeleme kolay olsun diye bunları aynı resim üzerinde ekrana basarsak,

w = 620; h = 465
from PIL import Image
im = Image.open('out-cam.png')
plt.imshow(im)
x = [[228, 398],\
[249, 338],\
[123, 245],\
[121, 186],\
[278, 248],\
[488,205],\
[291,194],\
[432,167],\
[73,288],\
[477,404],\
[123,130]]
X = [[20,0,21],\
[20,0,22],\
[18,0,30],\
[18,1,30],\
[20,0,30],\
[22,2,21],\
[20,1,30],\
[22,2,22],\
[18,0,25],\
[22,0,21],\

3
[18,2,30]]
for i in range(len(x)):
plt.plot(x[i][0],x[i][1],'r+')
plt.text(x[i][0]+2,x[i][1]+2,str(X[i]),color='cyan')
plt.savefig('out-cam2.png')

Bunlar çok mantıksız üç boyutlu değerler değiller herhalde.


P’nin hesabına gelelim. Her veri noktası i için elimizde bir λi xi = PXi denklemi
olduğuna göre, önce her bu tür denklemi PXi − λi xi = 0 olarak düzenlersek ve
bu denklemleri üst üste gelecek şekilde koyarsak,
 
XT1 0 0 −x1 0 0 ...  
T pT1
 0 X1 0 −y1 0 0 ... 
  pT2 
 0 0 XT1 −1 0 0 ...  

T
 pT3 
 X2 0 0 0 −x2 0 ... 
=0


T
 λ1
 0 X2 0 0 −y2 0 ...  
  λ2 

 0 0 XT2 0 −1 0 ... 
 ..

.. .. .. .. .. .. .
. . . . . .

sistemini elde ederiz. p1 , p2 , p3 degiskenleri P matrisinin satırlarıdır. Üstteki ma-


tris daha fazla veri noktası için sağa ve aşağı doğru uzayacaktır tabii.
Üstteki matrisi, M diyelim, görüldüğü gibi hazırladıktan sonra, ve çarpılan kolon
vektörüne v dersek artık,

4
Mv = 0

denklemini çözebiliriz. Bu denklemi yaklaşıksal olarak çözmek için problemi bir


||v|| = 1 şartına bağlı olmak üzere ||Mv|| minimizasyon problemi olarak görebiliriz,
yani “sıfıra olabildiğince yaklaşma problemi” olarak, ki bu problem çözümü için
SVD kullanılabilir, daha fazla detaylar [3] yazısında. Peki ||v|| = 1 şartını nasıl
getirebiliyoruz? Bunun sebebi homojen kordinat sisteminin getirdiği bir avanta-
jla alakalı; homojen kordinat sistemindeki noktalar “belli bir ölçek (up to scale)”
içinde tanımlıdır,
  ve mesela 2 boyutlu
  bir nokta ve herhangi
 bir sabit α için x =
x y w = αx αy αw = x/w y/w 1 noktalarının hepsi aslında
aynı 2 boyutlu noktadır. Bunun sonucu olarak M de belli bir ölçek içinde tanımlı
olacaktır, ve o zaman ||v|| = 1 farz edebiliriz. Bu tabii ki hesabımız için faydalı
oldu yoksa SVD bazlı minimizasyon kullanamazdık.

from scipy import linalg

def compute_P(x,X):
n = x.shape[1]
if X.shape[1] != n:
raise ValueError("Number of points don't match.")

M = np.zeros((3*n,12+n))
for i in range(n):
M[3*i,0:4] = X[:,i]
M[3*i+1,4:8] = X[:,i]
M[3*i+2,8:12] = X[:,i]
M[3*i:3*i+3,i+12] = -x[:,i]
print M.shape
U,S,V = linalg.svd(M)

return V[-1,:12].reshape((3,4))

xx = np.array(x)
# h'den cikar cunku imaj programlari sol ustu (0,0) olarak kabul
# ediyor, bizim hesap icin sol at (0,0) olmali
xx[:,1] = h-xx[:,1]
xx = np.hstack((xx,np.ones((len(x),1))))
XX = np.array(X)
XX = np.hstack((XX,np.ones((len(X),1))))

P = compute_P(xx.T,XX.T)
print P

(33, 23)
[[ -4.01225744e-02 5.31972373e-03 -1.06308256e-02 9.71131258e-01]
[ -9.79700368e-04 -2.64464969e-02 -1.09893337e-02 2.33086445e-01]
[ -1.80165364e-05 5.44992018e-06 -3.40782252e-05 8.40827305e-04]]

P’yi hesapladık. Bu P’yi şimdi resme bir üç boyutlu şekil yansıtmak için kul-
lanalım. Mesela iki metre solumdan bir metre sağımdan çıkan yerden uzağa
doğru yol üzerinde giten iki tane çizgiyi iki boyuta yansıtalım.

5
res1 = np.array([[18, 0, 20+i, 1.] for i in np.linspace(0,30,100)])
res2 = np.array([[21, 0, 20+i, 1.] for i in np.linspace(0,30,100)])

X3 = np.dot(P, res1.T)
X3 = X3 / X3[2]
im = Image.open('out-cam.png')
plt.imshow(im)

for xx in X3.T:
plt.hold(True)
if xx[0] > w or xx[0] < 0: continue
if xx[1] > h or xx[1] < 0: continue
plt.plot(xx[0],h-xx[1],'r.')

plt.hold(True)

X3 = np.dot(P, res2.T)
X3 = X3 / X3[2]
for xx in X3.T:
plt.hold(True)
if xx[0] > w or xx[0] < 0: continue
if xx[1] > h or xx[1] < 0: continue
plt.plot(xx[0],h-xx[1],'r.')

plt.savefig('out-cam4.png')

R, T Hesabı
 
Bilinen P ve R, T üzerinden K’yi hesaplamak için R t ’nin cebirsel olarak neyi
ifade ettigini hatırlayalım,

 
  r1,1 r1,2 r1,3 t1
R t =  r2,1 r2,2 r2,3 t2 
r3,1 r3,2 r3,3 t3
 
Çoğunlukla üstteki matrise bir ekstra 0 0 0 1 satırı eklenir, böylece matris
kare haline gelir, ve böylece dönüş ve taşınmanın basit çarpım olarak ayrıştırılabilmesi

6
sağlanır.

    
R t I t R 0
=
0 1 0 1 0 1
  
1 0 0 t1 r1,1 r1,2 r1,3 0
 0 1 0 t1   r2,1 r2,2 r2,3
  0 
=
 0

0 1 t1   r3,1 r3,2 r3,3 0 
0 0 1 1 0 0 1 1

P Üzerinden K, R, T
K’yi hesabı için şunu hatırlarız: Her matrisin bir QR ayrıştırmasının olduğunu
biliyoruz. O zaman eldeki P’yi ayrıştırarak R, t’yi bilmeden direk K, R, t hesaplarını
yapabiliriz.

K,R = linalg.rq(P[:,:3])
T = np.diag(np.sign(np.diag(K)))
if linalg.det(T) < 0: T[1,1] *= -1
K = np.dot(K,T)
R = np.dot(T,R)
t = np.dot(linalg.inv(K),P[:,3])
print K
print R
print t

[[ 2.99407581e-02 5.97285792e-03 2.86183659e-02]


[ 0.00000000e+00 -2.79384510e-02 6.37066885e-03]
[ 0.00000000e+00 0.00000000e+00 3.89309986e-05]]
[[-0.88366792 -0.15133543 0.44297698]
[-0.07045937 0.9785196 0.19373918]
[-0.46278126 0.13998922 -0.87534937]]
[ 12.47297147 -3.41799407 21.59788692]

Gerçi ayrıştırma özgün (unique) değil, bir işaret belirsizliği olabiliyor, ama bunun
çaresi var, detaylar için [1, sf. 108]. Bu hesabın bize ne verdiğini tekrar vurgula-
mak lazım - sadece P’ye bakarak hem K’yi hem de kameranın ne kadar hareket
ettiğini bulmuş olduk. Bu kuvvetli bir özellik.
Bu şekilde bulunan R, t belki ölçümlerin kalite kontrolu için kullanılabilir. Mesela
R, t’nin ne olduğunu kesin biliyorduk, ama P ayrıştırması bize beklediğimizden
farklı bir R, t verdi. O zaman belki 2D-3D eşleştirmesi daha iyi olabilirdi.
Kaynaklar
[1] Solem, Computer Vision with Python
[2] Dissecting the Camera Matrix, Part 2: The Extrinsic Matrix, http://ksimek.
github.io/2012/08/22/extrinsic/
[3] Bayramli, Lineer Cebir, Lineer Cebir ile Minimizasyon

7
Ders 8
İki Görüntüden Tekrar Oluşturma (Reconstruction from Two Views)
Problemi formüle edelim. İki faraziyemiz olacak. Faraziyeler şart, çünkü zor
problemler ile uğraşıyoruz, ve bazı faraziyeler ile işimizi kolaylaştırmamız gerekli.
Araştırmacılara tavsiyem yeni bir problem üzerinde uğraşıyorlarsa ise güçlü faraziyeler
ile başlayıp çözüm alanını kısıtlamaları ki bu şekilde çözüm daha rahat bulun-
abilsin; ve yer geldiğinde kısıtlamalar gevşetilebilir. Bunu vurguladım çünkü
bazı öğrencileri görüyorum, herşeyi tek seferde yapmaya uğraşıyorlar, sonra o
koca problem için bir program alelacele kodlanıyor, ve program işlemeyince moral-
leri bozuluyor, vs. Önce kısıtlı başlayın, sonra genelleştirirsiniz.
Faraziyeler şunlar;
1) İki imajdaki aynı objelerin her iki görüntüdeki ilginç noktalarını ve o aynı nok-
taların birbirleri ile nasıl eşleştiğini biliyoruz.
2) İki imaj statik bir dünyayı resmediyor, yani 1. ve 2. görüntü arasında resimdeki
objeler hareket etmiyorlar.
3) Kameranın iç parametreleri sabit ve biliniyor.
Bu bilgilere ve faraziyelere dayanarak ve eğer kameranın izafi yerini ve duruşunu
biliyorsak 3D yer bilgisini üçgenleme (triangulation) ile hesaplayabiliriz.
Çözmeye uğraşacağımız bir kameranın dış parametreleri ve görüntüdeki objenin
3D yeri. Elimizde iki resim var, resimdeki ilginç noktaların eşleşmesi var, kam-
eranın katı gövde hareketini, ve X’i bulacağız.

Üstteki aslında çetin bir tavuk-yumurta problemi. Eğer kamera hareketini biliyor
olsaydım iki görüntüdeki eşlemesini bildiğim noktalar üzerinden hemen 3D yer
hesaplayabilirdim. Mesela cep telefonlarında artık hareket algılayıcıları oluyor,
bu bilgi yeterince kesin olsa R, T ’yi hemen bulmuş olurdum, imajlara bakmak
gerekmezdi. O zaman üstteki resimde gösterilen iki çizginin kesiştiği noktayı
üçgenleme ile bulurdum, ve 3D noktası X bulunmuş olurdu. Bu hesap çok ba-
sittir. Ya da tam ters yönden, X’i bir şekilde biliyorsak kamera hareketi hesa-

1
planabilir. Eğer elimizde yeterince nokta var ise çözüm tek olacaktır. 3D tekrar
oluşturma hesaplarının zorluğu bu iki bilgiyi de aynı anda kestirmemiz gerektiğidir.
Bu derste takip edeceğimiz yöntem önce kamera hareketini, sonra obje yerini bul-
mak. Dediğimiz gibi bu problem yumurta-tavuk problemi, fakat bu iki prob-
lemin birbiriyle ilişkisini kesmek (decouple) mümkün.
Tipik bir resim üzerinde görelim,

Manzara iki farklı yönden görüntülenmiş. Birinde olan bazı noktalar diğerinde
olmayabilir ama çoğu nokta iki tarafta da var. Mesela bir 3D noktası P’yi düşünelim,
bu nokta bir bakış açısında 2D x1 noktasına, diğerinde x2 noktasına düşüyor.
Kamera merkezleri o1 ve o2 . İki bakış açılı örnek böyle. Bu derste üstteki gibi,
yani iki bakış açı üzerinden hesaplarla oldukça çok uğraşacağız, fakat çoklu bakış
açısından da bu hesapları nasıl genelleştirebileceğimizi göreceğiz [dersimizin adına
sadık kalmak lazım!].
Notasyonu netleştirelim (üstteki gibi bir resim daha)

2
Kamera orijin noktaları o1 , o2 görülüyor. Bu iki orijini bir düz çizgi ile birleştirelim,
bu çizginin her iki görüntü düzlemini kestiği noktalar e1 , e2 eş kutuplar (epipoles)
olarak isimlendirilir. X, e1 , e2 noktalarının üzerinde olduğu düzlem ise eş kutup-
sal düzlemdir (epipolar plane).
Notasyon böyle. Peki o zaman tekrar oluşturma (reconsruction) problemini nasıl
tanımlarız? Aslında bu problemi bir maliyet (cost) fonksiyonu üzerinden formülize
etmek oldukça basit. Bilimde pek çok problem belli bazı parametrelerin hesap-
sal tahminiyle alakalıdır, ve bu tahmini yapabilmek için tipik olarak bir maliyet
tanımlanır, ki bu maliyet fonksiyonu verilen belli parametre değerleri için bu
değerlerin iyi mi kötü mü olduğunu cevaplar. Tabii bir sonraki adım o maliyeti
minimize etmeye uğraşmak, ve bu minimizasyonu sağlayan “optimal” parame-
treleri bulmaya uğraşmaktır.
Diyelim ki elimizde iki değişik açıdan alınmış görüntüde eşleşmesi yapılmış 100
tane nokta var, xj1 , xj2 , j ∈ {1, .., 100}, yani j bir indis. Bu noktalar 3D Xj nokta-
larından geliyorlar, tahmin etmeye çalıştığımız onlar - bilinmeyen değişkenler.
Ayrıca R, T de bilinmiyor tabii, 6 tane bilinmeyen değişken de buradan geliyor.
Yani bilinmeyen parametreler çok, 100 x 3 (çünkü X1 ’in 3 tane öğesi var) artı 6
tane bilinmeyen var. Optimizasyon bağlamında bu 306 boyutlu bir uzayda iş
yapmaya çalışacağız demektir, ve bu pek iyi bir şey değil!
Problemi çözmek için yansıtma hatasını minimize etmeye uğraşabiliriz,

X
E(R, T , X1 , .., X100 ) = ||xj1 − π(Xj )||2 + ||xj2 − π(R, T , Xj )||2
j

Üstteki formülün eşitliğin sağ tarafının ilk teriminde kendimizi 1. kameranın


kordinat dünyasına alıyoruz; 3D X noktalarını kameraya yansıtıyoruz, ve aradaki
hatayı hesaplıyoruz. İkinci terimde 2. kamera kordinat dünyasındayız, aynı X
noktalarını bu sefer rotasyon, yer değiştirme sonrası 2. kameraya yansıttıktan
sonra o kameradaki yansıtma hatasını hesaplıyoruz. Minimizasyonun amacı E()

3
içindeki parametrelerin en optimal olanlarını bulmak ki E hatası en az olsun.
Üstteki yaklaşıma demet ayarlaması (bundle adjustment) ismi veriliyor; demet
çünkü pek çok parametreyi aynı anda vererek optimize etmeye uğraşıyoruz. Tek
problem maliyet fonksiyonu içbükey (convex) değil. Optimizasyon dersinden
hatırlayabileceğimiz üzere eğer elimizde çok boyutlu ve içbükey olmayan bir
problem var ise, bu kötü haber, bu çözümü büyük ihtimalle bulamayacağız de-
mektir. Bilim dalımız aslında hala bu problemi nihai olarak çözmek için yoğun
araştırma yapıyor, çünkü çözüm bulunabildiği zaman bile çözüm özgün değil,
vs.
Üstteki problemin çözümü için iki değişik yaklaşım var. Birisi problem tanımını
olduğu gibi almak, ve bir şekilde “becerikli” bir algoritma ile minimizasyonu iyi
becermeye uğraşmak. Mesela bir yaklaşıma göre birkaç nokta ile işe başlanır,
minimize edilir, sonra ötekiler eklenir ve rafine ede ede nihai sonuca erişilmeye
uğraşılır. Eğer varılan sonuçtan memnun olunmadıysa, optimizasyon başlangıç
noktası rasgele olarak tekrar seçilir, ve rutin tekrar işletilir, böylece iyi başlangıç
noktası ile daha iyi sonuca varılmaya uğraşılır, fakat tahmin edileceği üzere bu
kolay bir iş değil.
Bu derste takip edeceğimiz yöntem farklı maliyet fonksiyonlarıyla çalışmak; bu
fonksiyonlar orijinal maliyete benzeyecekler, fakat biraz daha basit oldukları için
minimize edilmeleri daha kolay olacak. Mesela R, T ile X noktalarının arasındaki
ilişki kesilecek, bu parametreler ayrı ayrı optimize edilecek. Bu ilişki kesimi nasıl
oluyor? Biraz sihirli bir yaklaşım gibi geliyor kulağa, kullanacağımız numara
eş kutupsal kısıtlama (epipolar constraint) kavramını devreye sokmak, böylece
8-nokta algoritmasını (8-point algorithm) elde etmiş olacağız.
Kamera matrisi K’nin bilindiğini varsayıyoruz. Ayrıca K = 1 alacağız, yani her
şeyin kameranın odak uzaklığının birimi üzerinden tanımlı olduğunu farzedeceğiz.
Birinci kamera için sadece bilinmeyen derinlik bilgisi bilinmeyen bir yansıtma
var. İkinci kamera için rotasyon ve yer değiştirme sonrası ardından bir yansıtma
var. Yani,

λ1 x1 = X, λ2 x2 = RS + T (1)

Resim üzerinde

4
Yani X’den x1 ’e gelmek demek sadece λ1 ile ölçeklemektir. Aynı durum dönme
ve yer değişim sonrası x2 için de geçerli. Tatmin etmemiz gereken iki denklem
bunlar. Bu iki denklemi birleştirerek ve diğer noktaları ekleyerek yavaş yavaş X’i
dışarı atmaya uğraşacağız. İlişki kesmeyi bu şekilde yapacağız. 1. denklemi 2.
denklem içine koyalım,

λ2 x2 = R(λ1 x1 ) + T

İki tarafı soldan T ’nin eksi bakışımlı hali T̂ ile çarpalım (T̂ v ≡ T × v). Niye?
Çünkü biliyoruz ki bir vektörün kendi eksi bakışımlı matrisi ile çarpımı sıfırdır
(ya da vektörün kendisi ile çapraz çarpımı sıfır verir), böylece eşitliğin sağındaki
T ’den kurtulmaya uğraşıyoruz. O zaman

λ2 T̂ x2 = λ1 T̂ Rx1

Böylece T ’den kurtulmuş olduk, aynı zamanda X’den de kurtulmuş olduk. Dolaylı
olarak X hala formülde tabii, çünkü λ1 ve λ2 3D noktaya olan uzaklıklar, ve λ1 x1
mesela bize 3D noktasını verir.
Devam edelim, üstteki ifadeyi x2 ’ye yansıtalım. Niye? Çünkü üstteki eşitliğin
sol tarafındaki T̂ x2 bir çapraz çarpım, ve bu çapraz çarpım bize x2 ’ye dikgen bir
vektör verir, ve eğer bu vektörü x2 ’ye yansıtırsam sıfır elde ederim, yani sol taraf
yokolur. Ayrıca λ1 ile bölerim. Geri kalanlar,

xT2 T̂ Rx1 = 0

olur. Buna eş kutupsal kısıtlama ismi veriliyor. Formül ilginç çünkü iki 2D nok-
tası x1 , x2 ve döndürme, yer değiştirme arasında bir ilişki kuruyor, 3D nokta bil-
gisi ortada yok. Bu bize bir kabiliyet kazandırdı, buradan hareketle diğer bili-
nen 2D nokta eşlerini alarak, ve üstteki sınırlamayı kullanarak bilinmeyen R, T ’yi

5
hesapsal tahmin etmeye uğraşabilirim.
(1)’den üstteki formüle gelmek için bazı transformasyonlar yaptık, bunlardan
bazılarının tersi alınabilir olmadığına dikkat; mesela son adımda x2 ’ye yansıtma
yaptık, bu durumda x2 ’e dikgen olan bilgi yokolmuş oldu. Ya da T̂ ile çarpım
işlemi - T̂ tersi alınabilir bir matris olmadığı için bu işlemi de geriye almak mümkün
değil. Yani son iki adımın ikisinde de bir şeyler kaybetmiş oluyoruz aslında. Tabii
kaybettiklerimiz yanında kazandıklarımız var, daha önce belirttiğimiz gibi, 3D
bilgisi ile uğraşmak zorunda değiliz artık. Belli kısıtlamalarla işe başladık, bazı
transformasyonlar sonunda daha zayıf bir kısıtlama elde ettik, ama bir avantaj
elde ettik.
Üstteki önemli bir formül, biraz daha üzerinde durmak iyi olur. Formüle bazen
gerekli kısıtlama (essential constraint) ya da iki lineerli kısıtlama (bilinear con-
traint) deniyor. Ayrıca formülün ortasındaki T̂ R çarpımına, ki bir 3 × 3 matristir,
gerekli matris (essential matrix) ismi veriliyor.
Genel kural olarak bir kavrama bir isim verilmişse, hatta birden fazla isim verilmişse,
o konunun önemli olduğunu ve çoğu zaman pek çok kişi tarafından araştırılmış
olduğu sonucuna varabiliriz.
Kolaylaştırmalar ardından buraya geldik, fakat R, T̂ çözümü hala zor; E = T̂ R
bilindiği durumda bu çarpımdan T̂ ve R’yi nasıl çıkartacağız?
O hesaba gelmeden önce eş kutupsal kısıtlamanın geometrik anlamına yakından
bakalım. Amacımız bir düzlem tanımlamak, ve düzlemin olma şartını eş kutup-
sal sınırlamaya bağlamak.

Eğer 1. kamera orijin kabul edilirse x1 ’e giden vektör o1~x1 olur, ya da sadece
x1 . Bu vektörü 2. kamerayı orijin olacak şekilde transforme edersek Rx1 . Bir
diğer vektör 2. kamera orijinli x2 noktası / vektörü. Ayrıca o2 çıkışlı ve T ’ye
oranlı (proportional, ∝ işareti oradan geliyor), bir vektör daha var. Bu üç vektör

6
üzerinden (üçlü çarpımla -triple product-) bir paralelepipe hacmi hesaplanabilir,
ve eş kutupsal kısıtlama formülünün söylediği bu hacmin sıfır olmasıdır, yani bir
düzlem olmasıdır (sıfır hacimli obje düz demektir)

hacim = xT2 (T × Rx1 ) = 0

ki o da dolaylı olarak o1 , o2 ’den çıkan ve x1 , x2 ’den geçen huzmelerin bir yerde


birleşiyor olmaları anlamına gelir. Artık 3D noktadan bahsetmeye gerek yok,
sadece iki huzmenin kesişiyor olması yeterli. Kesişiyorlarsa bir düzlem vardır,
kısıtlamanın söylediği de budur.
Gerekli matris E = T̂ R demiştik, tüm gerekli matrislerinin uzayı gerekli uzay
olarak adlandırılır,

ε ≡ T̂ R | R ∈ SO(3), T ∈ R 3

E’den T̂ , R çıkartmak matris ayrıştırması çağrışımları yapıyor olabilir; ve hakikaten


de Huang ve Faugeras’ın 1989 tarihinde ispatladığı bir teoriye göre sıfır olmayan
bir E ∈ R3 matrisi bir gerekli matristir sadece ve sadece E = UΣV T şeklinde bir
Eşsiz Değer Ayrıştırması (Singular Value Decomposition -SVD-) var ise, ve bu
ayrıştırma Σ = diag{σ, σ, 0} olmalı, σ > 0 için, U, V ∈ SO(3).
Bu teori gerekli matrisler ve SVD arasında bir eşdeğerlik (equivalence) tanımlamış
oluyor, gerekli matrislerin SVD’si olmalı, ve bu SVD’nin iki eşsiz değeri olmalı,
en küçüğü sıfır, en büyüğü σ olacak şekilde ve ondan iki tane var. Bu çok faydalı
çünkü sonuç itibariyle olabilecek mümkün matris seçeneklerini daraltmış oluyor,
ki bu iyi. Gerekli matrisi hesaplayan optimizasyonumuz bu bilgiyi kullanabilir.
Bir sonraki adım eldeki bir gerekli matristen rotasyon ve yer değişimi çıkartmak.
Ufak bir problem - bu sonuç özgün değil, pratikte 2 tane mümkün çözüm olabilir.
Ama iyi haber E = UΣV T sonrası alttaki çözümlerden sadece biri anlamlı pozitif
derinlik bilgisi verir.

(T̂1 , R1 ) = (URZ (+π/2)ΣUT , URZ (+π/2)V T )

(T̂2 , R2 ) = (URZ (−π/2)ΣUT , URZ (−π/2)V T )

Formüller biraz çetrefil gözüküyor, evet. İspat için [1, sf. 116]. Yani eğer gerekli
matrisi tahminsel hesaplayabiliyorsak, onu kullanarak rotasyon ve yer değişimini
üstteki formüllerle hesaplayabiliriz.
Algoritma
Bir 3D tekrar oluşturma algoritması şöyle olabilir; iki görüntüdeki birbiriyle bağlantılı
2D noktalar birbirleriyle eş kutupsal kısıtlama üzerinden ilişkideler. O zaman

7
1) Belli sayıda eşlenmiş noktayı kullanarak eş kutupsal kısıtlama üzerinden E’yi
hesapla.
2) E’den R, T ’yi hesapla.
Adım #2 için iki seçenek var. Birincisi direk E’yi hesaplamak, ama bu matrisin
gerekli (essential) uzayda olma zorunlulu olduğu için onu gerekli uzaya yansıt.
Yine bir pürüz; Biliniyor ki bu yöntem optimal altı (suboptimal). Diğer seçenek eş
kutupsal kısıtlamalardan E’yi hesaplarken o optimizasyon içinde ek bir kısıtlamayla
çözümü gerekli uzayda olmaya zorlamak.
Pratikte ikinci seçeneği kodlamak külfetlidir, çünkü bu bir gayrı lineer, dertli
kısıtlı optimizasyon, ayrıca ek kısıtlamalar SVD’nin her üç eşsiz değeri üzerinde
olmalıdır... Biz lineer, lineer cebirsel bir yaklaşım tercih ediyoruz.
8-Nokta Algoritması
Algoritmanın ismi en az 8 noktaya ihtiyaç duymasından geliyor. Bu algoritma
için xT2 Ex1 = 0 kısıtlamasını farklı bir şekilde tanımlamaya çalışacağız.
Bu kısıtlama E merkezli bir ikili lineerlik (bilinear) içeriyor. Bu ne demektir?
Bir tekrar düzenleme ile bu kısıtlama ifadesini “E’nin öğeleri çarpı x1 , x2 öğeleri”
şeklinde ifade edebiliriz demektir. Bunun için önce E matrisinin öğelerini “açarak”’
düz bir vektör içine dizelim. Üstsimge s hatırlarsak yığma (stacking) operatörüydü,
 T
Es = e11 e21 e31 e12 e22 e23 e31 e32 e33 ∈ R9 olacak. Şimdi

a ≡ x1 ⊗ x2
 T
tanımlayalım, ki ⊗ Kronecker çarpımı. xi = xi yi zi üzerinden üstteki
çarpımın açılımı

T
∈ R9

a= x1 x2 x1 y2 x1 z2 y1 x2 y1 y2 y1 z2 z1 x2 z1 y2 z1 z2

Bu tanımlar sayesinde eş kutupsal kısıtlama

xT2 Ex1 = aT Es = 0

olarak yazılabilir. Böylece bilinen değişkenleri bilinmeyenlerden net bir şekilde


ayırmış olduk, bilinen her şey aT içinde, bilinmeyenler Es içinde. Ayrıca kısıtlama
bir skalar çarpım haline geldi, ve bu çarpımın söylediği bir şey var, sonuç sıfır
olduğu için a, Es birbirine dikgen (orthogonal) vektörler. Eşlenmiş bir çift 2D
nokta için yapılanlar bunlar. Tüm n nokta çiftleri için üstteki denklemi bir lineer
sistem haline getirebiliriz,

T
χEs = 0,

χ= a1 a2 . . . an

8
Yani χ içinde a vektörleri bir kolon olarak yanyana diziliyorlar. Lineer Cebir
dilinde “E’nin ne olduğunu bilmiyoruz ama biliyoruz ki o χ’in sıfır uzayında
yaşıyor” diyebiliriz. Bir pürüz bu sıfır uzayından gelen çözümün özgün olma-
ması. χEs = 0’i tatmin eden herhangi bir çözüm vektörünün katları da çözümdür,
yani sonsuz tane çözüm vardır.
Bunun negatif sonucu ölçek bilgisini, 8 ya da kaç tane nokta daha olursa ol-
sun hiçbir zaman gerçek ölçekte tahmin edemiyor olacağımız. İki ev resmine
bakıyoruz mesela, fakat belki maket bir evin resimleri bunlar! Robot kodlamasına
bu problem çok ortaya çıkar, mesela biz görsel kamera ile yol bulan bir quad-
copter kodu geliştirdik, ek olarak sonar algılayıcısı eklememiz gerekti ki bu eksik
olan ölçek bilgisini elde edebilelim.
Pratikte hesapları kolaylaştırmak için o1 o2 uzaklığı 1’e eşitlenir, yani birim o1 o2
uzaklığı haline gelir; hesapların sonucu bu birim üzerinden raporlanmış olur.
Fakat pozitif yönde şu da var; sıfır uzayının tek boyutlu olmasını garantileye-
bilirsek, evet oradaki çözümün katları da çözümdür ama en azından ölçekleme
problemi tamir edilince elimize tek çözüm geçer. Bunu garantileyebiliriz; en az 8
nokta gerekliliği (ve algoritmanın ismi) buradan geliyor. Bunun için χ’nin kertesi
tamı tamına 8 olmalıdır. Eğer 8’den daha fazla eşli nokta var ise bunun zararı
yok. Ama daha az var ise, mesela 7, o zaman sıfır uzayı iki boyutlu olurdu, ve
özgün çözüm elde edilemezdi.
Patajolik durumlarda 8’den daha fazla nokta çifti bile özgün nokta bulmaya yet-
mez; mesela tüm noktaların 3D dünyada aynı düzlem üzerinde olduğu durumda.
O zaman çözüm dejenere çözümdür, çünkü ai vektörleri birbirinden bağımsız
değildir. Örnek olarak mesela ev resminde 2D nokta çiftlerinin hepsi evin ön
duvar üzerinden alınmış ise, bu problem çıkartır. Ama bazı noktalar evin ön du-
varı, diğerleri yoldan, diğerleri evin arkasındaki ağaçtan, vs. geliyor ise 8-nokta
algoritması düzgün işler.
E’nin artı ya da eksi işareti tekrar oluşturulamıyor. Her E için iki R iki de T
mümkündür, yani mümkün R, T çiftleri 4’tür. Ama pratikte E’nin işaretini bul-
mak kolaydır.
Ayrıca, daha önce söylediğimiz gibi, çoğunlukle hesaplanan Es öğeleri bir gerekli
matrise tekabül etmez, bir gerekli matrisi bulmak için Es ’i gerekli uzaya yansıtmamız
gerekir, yani en yakın gerekli matrisi hesaplamamız gerekir.
χEs = 0 hususunda bir nokta daha, eşlemelerde hata olabileceği için bu ifade
tam olarak çözülemeyebilir. O zaman, ona en yakın olabilecek çözüme erişmeye
uğraşırız; yani ||χEs ||2 ’yi en az kareler bağlamında minimize edecek Es ’i hesaplarız.
Bu minimizasyon Es ’i χT χ’nin en ufak özdeğerine tekabül eden özvektörü olarak
seçmek ile mümkün olabilir. Tabii χT χ özvektör hesabı ile χ eşsiz değer ayrıştırmasının
ilişkisi var, bkz [2], ve [3].

9
Yansıtma için kullanacağımız teorinin ispatı [1, sf. 119]’da. Herhangi bir F ma-
trisini alalım, ki bu matrisin SVD’si F = Udiag{λ1 , λ2 , λ3 }V T olsun, λ1 > λ2 > λ3
olmak üzere. O zaman Frobenius normu ||F − E||2f ’i minimize eden matris E

λ1 + λ2
E = U diag{σ, σ, 0}V T , σ=
2

Yani F’nin SVD’sini alıp buradan gelen en büyük iki eşsiz değerinin ortalamasını
E’nin SVD’sindeki en büyük iki eşsiz değer yapıyoruz, E’nin en küçük eşsiz değerini
sıfır kabul ediyoruz, bu kadar. Niye bu basit ortalamanın işlediği teorinin is-
patında.

Algoritma 8nokta xi1 , xi2

1. Gerekli matrisin yaklaşık halini bul.


 T
2. χ = a1 a2 . . . an ’yi hesapla, ki ai = xi1 ⊗ xi2 .

3. ||χEs ||’i minimize edecek şekilde Es ∈ R9 ’i bul, yani χ = Uχ Σχ VχT ayrıştırmasında


Vχ ’nin 9. kolonunu al, çünkü o kolon en küçük eşsiz değere tekabül ediyor.

4. Es ’i tersine yığma işlemiyle 3 × 3 E vektörüne aç.

5. Gerekli uzaya yansıtma yap; E = Udiag{σ1 , σ2 , σ3 }V T .

6. E belli bir skalara kadar tanımlı olduğu için E’yi normalize edilmiş
gerekli uzayına yansıt, σ1 , σ2 , σ3 yerine 1,1,0 değerleri kullan.

7. R, T̂ ’yi hesapla. Dört mümkün çözüm R = URZ (±π/2)V T ,T̂ = URZ (±π/2)ΣUT
return R, T̂

8’den daha az nokta mümkün mü? Evet. [Atlandı]


Eğer sadece rotasyon var ama yer değiştirme yok ise, 8-nokta algoritması işlemez,
çünkü o zaman T̂ sıfır olacak, gerekli matris te sıfır olacak. Bu tür durumlar
hiç yok değil, tatilde çekilmiş fotoğraflarda oluyor (hoca sadece kendi etrafında
dönerek ardı ardına fotoğraf çeken turist taklidi yapıyor, bu durumda yer değişimi
yok, rotasyon var.
[statik olmayan manzara yorumları atlandı]

10
Kaynaklar
[1] Sastry, An Invitation to 3-D Vision
[2] Bayramli, Lineer Cebir, PCA
[3] Bayramli, Lineer Cebir, Rayleigh-Ritz Teoremi

11
İki Boyut Nokta Eşleşmesi, Homografi, Video Stabilizasyonu
Oldukça çok ortaya çıkan bir imaj işleme problemi şudur: elimizde iki nokta
grubu var, bu noktaların arasındaki eşleşmeyi biliyoruz. Öyle bir H ilişkisi bul-
mak istiyoruz ki verili x noktasınının (homojen) kordinatını x 0 noktasına taşısın,
yani eldeki her veri noktasının ima ettiği eşleşmeyi bulsun.
Örnek

x1 = [[25.8064516129,25.0],[23.87096,45.625],
[20.0,69.375],[28.387,92.5],
[38.709,116.875],[64.5161290323,115.0],
[64.516,89.375],[65.16,66.875],
[57.4193,45.0],[45.80645,23.75]]
x2 = [[93.548,66.25],[114.838,110.0],
[138.709,153.125],[182.580,179.375],
[241.935,204.375],[276.77,163.75],
[254.193,123.125],[212.903,73.125],
[158.064,54.375],[120.6451,40.625]]

x1 = np.array(x1)
x2 = np.array(x2)
plt.plot(x1[:,0], x1[:,1], 'rd')
plt.plot(x2[:,0], x2[:,1], 'bd')
plt.xlim(0,320)
plt.ylim(0,240)
plt.savefig('vision_30vstab_02.png')

Yani kırmızı noktaları mavi noktalara çeviren ilişkiyi arıyoruz. Bu transformasyonda


ne var? Sağa doğru bir yer değiştirme (translation), ölçekleme (scaling), ve saat
yönüne doğru bir döndürme (rotation). Bu tür 2D-2D ilişkilerine homografi adı
veriliyor. Aradığımız alttaki türden bir formül [3],

x 0 = Hx

yani her x noktası H üzerinden x 0 haline gelecek. H matrisi homojen kordinatları

1
baz alır,

x0
   
h1 h2 h3 x
 y 0   h3 h4 h5   y 
w0 h6 h7 h8 w

H matrisinin bazı şekilleri vardır, mesela

x0
   
a1 a2 t x x
 y 0   a3 a4 t y   y 
1 0 0 1 1

Ya da matris içindeki bölgeleri vektör / matrisler ile özetlersek,

 
0 A t
x = x
0 1

Üstteki transformasyona ilgin transformasyonu (affine transformation) deniyor,


yamultma (warping) denen işlem budur. Bu transformasyon w = 1 şartını korur.
Eğer H şu türden olursa,

x0
   
s cos(θ) −s sin(θ) tx x
 y 0   s sin(θ) s cos(θ) ty   y 
1 0 0 1 1

Ya da

 
0 sR t
x = x
0 1

Dönüş R, taşınma t, dönme θ, ölçekleme s. Bu transformasyona ölçeklemeye


(scaling) izin veren bir katı (rigid) transformasyon deniyor. “Katı” demek s =
1, yani noktalar arası mesafeler değişmeyecek demek, daha büyük s ile tabii
ölçekleme olabilir, mesafeler artabilir, ama mesafe oranları yine aynı kalır, ayrıca
döndürme de -rotation- yapılabilir. Bu transformasyona yansıtsal (projective)
ismi de verilir. Yansıtsal transformasyonun ilgin transformasyondan daha esnek
/ kuvvetli olduğu bilinir.
Not: ilgin transformasyon ve onu kestirme hesabı bazen literatürde iki boyutlu
kordinat sisteminde ve x 0 = Rx+t, yani rotasyon artı yer değişimi gibi bir formda
da görülebilir, biz homojen sisteme geçerek her ikisini aynı matris H içinde ve tek
çarpım operasyonu ile gösterebilmiş oluyoruz. Homojen, tek matrisli formda
hesap yapmak daha kolay.

2
Homografi hesabının kullanım alanları geniş; mesela elde olan iki imaj arasında
birbirine uyan noktaları biliyorsak, H’yi hesaplayarak tüm imaj üzerinde bir değişim
matrisi hesaplamış oluruz.
Yansıtsal H hesabı için direk lineer transform (direct linear transform -DLT-) tekniği
var. Eldeki tüm eşleşmeler için alttaki sistemi yaratırız,

 
h1
  h2 
−x1 −y1 −1 0 0 0 x1 x10 y1 x10 x10
 
 h3 
0 0 0 −x1 −y1 −1 x1 y10 y1 y10 y10  
h4

  
 −x2 −y2 −1 0 0 0 x2 x20 y2 x20 x20  
  h5 =0
0 0 0 −x2 −y2 −1 x2 y20 y2 y20 y20  
h6

 
.. .. .. ..
  
. . . . 
 h7 

 h8 
h9

Bu sistem x 0 − Hx = 0 sistemini temsil etmiş oluyor, ne kadar fazla nokta olursa


üstteki matris o kadar aşağı doğru genişleyecektir (öğe ayarlaması öne göre yapılacak
tabii). Mükemmel H bulunamayabilir, ama sıfıra olabildiğince yaklaşmak için
üstteki problemi bir minimizasyon problemi olarak görürüz, SVD bu çözümü
bize sağlar.

import numpy.linalg as lin

def H_from_points(fp,tp):
if fp.shape != tp.shape:
raise RuntimeError('number of points do not match')

m = np.mean(fp[:2], axis=1)
maxstd = np.max(np.std(fp[:2], axis=1)) + 1e-9
C1 = np.diag([1/maxstd, 1/maxstd, 1])
C1[0][2] = -m[0]/maxstd
C1[1][2] = -m[1]/maxstd
fp = np.dot(C1,fp)

m = np.mean(tp[:2], axis=1)
maxstd = np.max(np.std(tp[:2], axis=1)) + 1e-9
C2 = np.diag([1/maxstd, 1/maxstd, 1])
C2[0][2] = -m[0]/maxstd
C2[1][2] = -m[1]/maxstd
tp = np.dot(C2,tp)

nbr_correspondences = fp.shape[1]
A = np.zeros((2*nbr_correspondences,9))
for i in range(nbr_correspondences):
A[2*i] = [-fp[0][i],-fp[1][i],-1,0,0,0,
tp[0][i]*fp[0][i],tp[0][i]*fp[1][i],tp[0][i]]
A[2*i+1] = [0,0,0,-fp[0][i],-fp[1][i],-1,
tp[1][i]*fp[0][i],tp[1][i]*fp[1][i],tp[1][i]]

3
U,S,V = lin.svd(A)
H = V[8].reshape((3,3))

H = np.dot(lin.inv(C2),np.dot(H,C1))

# normalize and return


return H / H[2,2]

x1h = np.ones((len(x1),3))
x1h[:,:2] = x1
x2h = np.ones((len(x1),3))
x2h[:,:2] = x2
A = H_from_points(x1h.T,x2h.T)
res = np.dot(A, x1h.T).T
res = res.T / res[:,2]

plt.plot(x1[:,0], x1[:,1], 'rd')


plt.plot(x2[:,0], x2[:,1], 'bd')
plt.plot(res.T[:,0], res.T[:,1], 'bx')
plt.xlim(0,320)
plt.ylim(0,240)
plt.savefig('vision_30vstab_03.png')

Çarpı ile işaretli noktalar kestirme hesabı yapılan H ile kırmızı noktaların trans-
form edilmesiyle elde edildi. Gerçek noktalara oldukca yakın!
İlgin transformasyon matrisinin hesabı için üstteki metotta h7 = 0, h8 = 0 kullan-
mak yeterli. Alternatif bir yöntem de var, daha fazla detay için [3, sf. 76].
İmaj Bölgesi Çekip Çıkarmak
Üstteki tekniğin ilginç uygulamalarından biri; diyelim ki bir imajın belli bir bölgesindeki

4
görüntüyü eşit kenarlı olacak şekilde çekip çıkarmak istiyorum, mesela alttaki
Sudoku oyun karesini,

from scipy import ndimage


from PIL import Image

im = np.array(Image.open('sudoku81.JPG').convert('L'))
corners = [[257.4166, 14.9375],
[510.8489, 197.6145],
[59.30208, 269.65625],
[325.598958, 469.05729]]
corners = np.array(corners)
plt.plot(corners[:,0], corners[:,1], 'rd')
plt.imshow(im, cmap=plt.cm.Greys_r)
plt.savefig('vision_30vstab_04.png')

Kenarları kırmızı noktalarla ben seçtim, şimdi o bölgenin alınıp eşit kenarlı halde
gösterilmesini istiyorum. Bu ne demektir? Bu seçilen her köşe noktasının eşit
kenarlı bir karenin köşelerine transform edilmesi demektir, mesela bu köşeler
(1, 300), (300, 300), .. gibi olabilir (imajın en uç noktaları). Sonra daha önce yaptığım
gibi H hesaplarım, ve o bölgedeki tüm pikselleri alıp hesapladığım transformasy-
onu onlara uygularım, scipy.ndimage.geometric_transform bu işi yapar.

from scipy import ndimage


import scipy

fp = [ [p[1],p[0],1] for p in corners]


fp = np.array(fp).T
tp = np.array([[0,0,1],[0,300,1],[300,0,1],[300,300,1]]).T

5
H = H_from_points(tp,fp)

def warpfcn(x):
x = np.array([x[0],x[1],1])
xt = np.dot(H,x)
xt = xt/xt[2]
return xt[0],xt[1]
im_g = ndimage.geometric_transform(im,warpfcn,(300,300))
scipy.misc.imsave('vision_30vstab_05.png', im_g)

Video Stabilizasyonu
Elde tutulan kamera ile kaydedilen görüntülerde titreme çok olabilir. Mesela
şurada [1] bizim cep telefonu ile kaydettiğimiz bir örnek var. Bu görüntüyü
yazılım ile stabilize etmek mümkün mü? Cevap evet - ve çözüm şaşırtıcı dere-
cede basit. [4]’ün tekniği şöyle özetlenebilir: bir video’yu baştan itibaren kare
kare işlerken, her karede ilginç köşe noktaları (Harris tekniği ile) buluruz, ve bu
noktaların bir sonraki resimdeki eşlerini elde ederiz, bu artık görüntü işlemede
demirbaş haline gelmiş bir işlem. Sonra tüm eşlemeleri kullanarak her video
karesi için bir homografi / transformasyon hesaplarız, bu transformasyon ma-
trisi içinde x, y değişimi, yani taşınma, ve a açısı ki döndürme bilgisi vardır. Bu
bilgileri dx, dy, da olarak biriktiririz.
Tüm kareler işlenince başa dönüp tüm bu değişimlerin kümülatif toplamını alarak
x, y, a zaman serilerini oluştururuz. Bu zaman serileri üzerinde bir yürüyen or-
talama (moving average) hesabı yaparız, bu bize pürüzüşleştirilmiş zaman seri-
leri verir. Şimdi kümülatif serinin pürüzsüz seriden olan farklarını buluruz, ve
her kare için bu farkı alıp, onunla bir H oluştururuz ve bu H ile bir önceki kare
üzerinde yamultma yaparak onu “düzeltiriz”. Bu kadar.
Bu teknik niye işliyor? İşliyor çünkü üstte gösterdiğimiz türde video’larda ”bek-
lenen” bir “akış”, bir nokta eşleşmesi var. Düz yürüyoruz, kamera ileri dönük, or-
tadaki pikseller dışa doğru eşleşmeli, sağdakiler daha sağa doğru, vs. Bu beklen-
tiyi hareketli ortalama ile hesaplamak mümkün, ve ondan olan sapmaları kam-
eranın istenmeyen titremesi olarak algılıyoruz, ve düzeltiyoruz.

#!/usr/bin/env python

6
import cv2, sys
import numpy as np
import pandas as pd

if len(sys.argv) < 2:
print "Usage: vs.py [input file]"
exit()
fin = sys.argv[1]
cap = cv2.VideoCapture(fin)
N = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = int(cap.get(cv2.CAP_PROP_FPS))

status, prev = cap.read()


prev_gray = cv2.cvtColor(prev, cv2.COLOR_BGR2GRAY)
(h,w) = prev.shape[:2]

last_T = None
prev_to_cur_transform = []
for k in range(N-1):
status, cur = cap.read()
cur_gray = cv2.cvtColor(cur, cv2.COLOR_BGR2GRAY)
prev_corner = cv2.goodFeaturesToTrack(prev_gray,
maxCorners = 200,
qualityLevel = 0.01,
minDistance = 30.0,
blockSize = 3)
cur_corner, status, err = cv2.calcOpticalFlowPyrLK(prev_gray,
cur_gray,
prev_corner,
None)
prev_corner2 = []
cur_corner2 = []
for i,st in enumerate(status):
if st==1:
prev_corner2.append(prev_corner[i])
cur_corner2.append(cur_corner[i])
prev_corner2 = np.array(prev_corner2)
cur_corner2 = np.array(cur_corner2)
T = cv2.estimateRigidTransform(prev_corner2, cur_corner2, False);
last_T = T[:]

dx = T[0,2];
dy = T[1,2];
da = np.arctan2(T[1,0], T[0,0])
prev_to_cur_transform.append([dx, dy, da])
prev = cur[:]
prev_gray = cur_gray[:]

prev_to_cur_transform = np.array(prev_to_cur_transform)
trajectory = np.cumsum(prev_to_cur_transform, axis=0)
trajectory = pd.DataFrame(trajectory)
smoothed_trajectory = pd.rolling_mean(trajectory,window=30)
smoothed_trajectory = smoothed_trajectory.fillna(method='bfill')
new_prev_to_cur_transform = prev_to_cur_transform + \

7
(smoothed_trajectory - trajectory)
new_prev_to_cur_transform = np.array(new_prev_to_cur_transform)

T = np.zeros((2,3))
cap = cv2.VideoCapture(fin)
out = cv2.VideoWriter('out.avi', cv2.VideoWriter_fourcc('P','I','M','1'),
fps, (w, h), True)

for k in range(N-1):
status, cur = cap.read()
T[0,0] = np.cos(new_prev_to_cur_transform[k][2]);
T[0,1] = -np.sin(new_prev_to_cur_transform[k][2]);
T[1,0] = np.sin(new_prev_to_cur_transform[k][2]);
T[1,1] = np.cos(new_prev_to_cur_transform[k][2]);
T[0,2] = new_prev_to_cur_transform[k][0];
T[1,2] = new_prev_to_cur_transform[k][1];
cur2 = cv2.warpAffine(cur, T, (w,h));
out.write(cur2);
cv2.waitKey(20);

cv2.estimateRigidTransform çağrısı katı transformasyonu hesaplayan bir çağrıdır,


aynen H_from_points gibi.
Üstteki kodu [1] üzerinde uygularsak stabilizasyon yapıldığını göreceğiz. Sonuç
[2]’de. C++ kodu vidstab.cpp’de bulunabilir.
Canlı Zamanda (Real-Time) Stabilizasyon
[4]’ün tekniği toptan (batch) işleyen bir teknik, ortalama alınması, düzeltme yapılması
için video’nun baştan sona işlenmesi, ve geriye dönülmesi gerekiyor. Düzeltme
işlemini canlı olarak yapamaz mıyız?
Bu mümkün olmalı; yürüyen ortalama için [6] yazısına bakabiliriz; orada işlenen
üstel ağırlıklı hareketli ortalama kullanılabilir. Bu ortalamanın özyineli (recur-
sive) formu da vardır,

zt = αgt + (1 − α)zt−1

ki α kullanıcı tarafından seçilen parametredir, en son verilere ne kadar ağırlık


verileceğini tanımlar. Algoritma şöyle olabilir: Stabilizasyon için her video karesi
işlenirken dx, dy, da farklarını hesaplarız, bunların kümülatif toplamını da anlık
hesaplarız (kolay). Bu kümülatif x, y, a’yı üstteki tanımda gt olarak formüle ver-
iriz, en son ortalama her zaman zt içinde olacaktır. Bu ortalamanın kumulatif
olandan farkı, “sapması” her kare üzerinde düzeltme amacı ile kullanılabilir. Bu
kod vsonline.py içinde bulunabilir.
Kaynaklar
[1] Bayramlı, Veri 1, https://drive.google.com/uc?export=view&id=
1nR4E7SYLfKhm8nO0BEfFcw0pwWmMNm19
[2] Bayramlı, Veri 2, https://drive.google.com/uc?export=view&id=

8
11fPP7bxL32AhTNUFPVRqeG-PIxTQ1lqB
[3] Solem, Computer Vision with Python
[4] Nghia Ho, Simple Video Stabilization using OpenCV, http://nghiaho.com/
?p=2093
[5] Bayramlı, OpenCV 3.0, https://burakbayramli.github.io/dersblog/
sk/2017/03/opencv-30.html
[6] Bayramli, Zaman Serileri ve Finans, ARIMA, ARCH, GARCH, Periyotlar, Yürüyen
Ortalama
[7] Bayramli, Kalman Filters and Homography: Utilizing the Matrix A https://
arxiv.org/abs/1006.4910

9
Birleşme Noktaları, Çizgiler, Hiperdüzlemler (Vanishing Points, Lines, Hyper-
planes)
Görüntü işlemede birleşme noktaları ufuk çizginde, dış dünyadaki genel yapının
“aktığı” yer olarak tanımlanabilir. Mesela önümüzde düz giden bir yol var ise o
yolun ufuk çizgisine değdiği yer birleşme noktasıdır. Birleşme noktalarının bir
nokta olarak ortaya çıkmasının sebebi 3D-2D dönüşümüyle alakalı; üç boyutta
parallel olan çizgilerin iki boyuta (diijal kameraya) yansıması onlarin tek noktada
birleşmesine sebep olur.

Üstte bazı örnekler görüyoruz. Soldaki imajda birleşme noktası tren raylarının
görülebilen son noktasıdır. Ortadaki imajda kırmızı çizgilerin birleştiği yer. Bir
resimde birden fazla birleşme noktası da olabilir, mesela sağdaki resimde bu
örnek görülüyor. Birleşme noktası imaj dışına da düşüyor olabilir, yine sağdaki
resim.
Görüntülerde derinliği anlamak, bu konuyu incelemek Rönesans’da başladı. Bu
çağda görüntünün ne olduğu ciddi şekilde araştırıldı, ressamlar perspektifi dikkate
alıp, birleşme noktalarını seçip ona göre resimlerini yapmaya başladılar. Mesela
ünlü ressam Raphael’ın Atina Okulu adlı resmi [4].

1
Bu resimde birleşme noktası filozof Sokrat’ın sol elinde, resimdeki tüm objeler bu
noktaya göre şekillendirilmiş.
Birleşme Noktalarını Bulmak
Görüntü işleme çerçevesinde verili herhangi bir görüntüde birleşme noktalarını
bulmak faydalı oluyor; bu noktalar robotik, yer bulma amaçlı olarak kullanılabiliyor.
Çünkü eğer görüntüdeki genel yapının nereye doğru aktığını bulabiliyorsak, oraya
doğru bir fiziksel gidiş de vardır demektir, ve otonom hareket eden robotlar bu
bilgiyi kullanabilirler, ya da bu bilgi diğer ek görüntü işleme adımları için bir
girdi olabilir. Belki elde tutulan kamera sürekli sallanıyordur, ama birleşim nok-
tasını her görüntüde doğru bulabiliyorsak bu bu bilgiyi bir stabilizasyon amaçlı
kullanabiliriz.
Hesap icin ilk aşama görüntüdeki ana çizgileri bulmak. Ana çizgileri bulmak
artık görüntü işlem biliminde demirbaş haline gelmiş Canny kenar bulucusu ve
Hough transformu ile yapılabilir.

from PIL import Image, ImageDraw


from skimage.transform import probabilistic_hough_line
from skimage.feature import canny
from skimage import data
import pandas as pd

im1 = Image.open('in1.jpg').convert('L')
edges1 = canny(np.array(im1), 2, 1, 25)
lines1 = probabilistic_hough_line(edges1, threshold=10, line_length=30,line_gap=3)
im1 = Image.open('in1.jpg')
for line in lines1:
p0, p1 = line
plt.plot((p0[0], p1[0]), (p0[1], p1[1]))

2
plt.imshow(im1)
plt.savefig('vision_40lines_08.png')

im2 = Image.open('in2.jpg').convert('L')
edges2 = canny(np.array(im2), 2, 1, 25)
lines2 = probabilistic_hough_line(edges2, threshold=10, line_length=30,line_gap=3)
im2 = Image.open('in2.jpg')
for line in lines2:
p0, p1 = line
plt.plot((p0[0], p1[0]), (p0[1], p1[1]))
plt.imshow(im2)
plt.savefig('vision_40lines_09.png')

3
Hough transformuna verilen threshold, line_length, line_gap parametreleri al-
goritmanın hassasiyetini ayarlıyor, mesela line_length bulunan çizgilerin en az
kaç piksel olması gerektiğini tanımlıyor.
Bir sonraki adım bu ana çizgileri alıp onların birleşebilecek olanlarını seçmek, ve
en çok birleşim yapabilenleri üzerinden bir birleşim noktası bulmak. Ama önce
iki boyutta çizgiler nasıl formüle edilir, ve kesişim nasıl bulunur, onu görelim.
Çizgiler
Bir çizgiyi ax + by + c = 0 genel formülüyle gösterebiliriz. a, b, c parametreleri
özgün olarak iki boyutta bir çizgiyi tanımlayabilir, bu formülü tatmin eden son-
suza kadar tüm x, y değerleri çizginin parçasıdır.
Üstteki formülü lise matematiğinden bilinen y = mx + i’e ilişkilendirmek için, ki
m eğim (slope) ve i kesi (intercept),

ax + by + c = 0

by = −ax − c

y = −a/bx − c/b

Yani eğim m = −a/b, kesi −c/b. Bu bilgiyi vektörel bir yön tanımlamak için
şöyle düşünebiliriz, eğime göre x yönünde atılan her b adımı için y yönünde −a
adımı atılacağına göre (ya da −b için a adımı), vektör alttaki gibi olur.

4
Birkaç örneği grafikleyelim,

import pandas as pd

def plot_line(a,b,c):
# Formula is ax+by+c = 0
x = np.linspace(-20,20,1000)
m = -a/b # slope
i = -c/b # intercept
y = m*x + i
plt.plot(x,y,'.')

l1 = np.array([1,1,-5])
plot_line(l1[0],l1[1],l1[2])
l2 = np.array([2,-1,10])
plot_line(l2[0],l2[1],l2[2])
plt.xlim(-10,10)
plt.ylim(0,30)
plt.grid(True)
plt.savefig('vision_40lines_01.png')

Homojen Kordinatlar, Kesişim


Homojen kordinatların (u, v, 1) şeklinde olduğunu hatırlayalım, ki (uw, vw, w)
aynı kordinat oluyordu, çünkü bir homojen kordinatin 3. hücresinde ne varsa
tüm kordinat degerlerini onunla bölebiliyorduk [1].
Kartezyen düzlemde çizgi denklemi ax + by + c = 0’i şu şekilde görebiliriz, x, y
çizgi üzerinde birer noktadır, homojen bağlamda x = u/w, y = v/w olsun, o

5
zaman w ile çarparak, yani bu homojen (u, v, w) noktasını ileri / geri hareket
ettirerek tüm çizgiyi kapsayabiliriz. Bu tanımları Kartezyen çizgi denklemine
geri sokarsak, çizgiyi homojen olarak tanımlayabileceğimizi görürüz,

au + bv + w = 0

Bu denklem homojen çizgi denklemi olarak biliniyor. Yani bir çizgiyi

` = (a, b, c)

homojen kordinatlarıyla tanımlayabiliriz. `’in sıfır olmayan herhangi bir katı aynı
çizgiyi tanımlayacağına göre `’yi bir yön olarak düşünmek te mümkün, ve çizgi
için yönden başka bir şeye zaten ihtiyaç yok.
Tüm bu tanımlara göre p = (u, v, w)’nin homojen kordinatta bir nokta olduğunu
düşünelim. O zaman p’nin bir çizgi üzerinde olması demek, p ve `’nin noktasal
çarpımının sıfır olması demektir,

p·`=0

Değil mi? Çünkü


  
a u
au + bv + cw =  b  ·  v  = 0
c w

O zaman iki çizginin kesişimini şöyle buluruz. Diyelim ki iki çizgi `1 ve `2 ’nin
kesişme noktası p, o zaman

`1 · p = 0, `2 · p = 0

ki herşey homojen kordinatta. Üstteki tanımlardan şu sonuç çıkıyor, p hem `1


hem de `2 ’ye dikgendir. İki vektöre dikgen olan üçüncü bir vektörü nasıl bulu-
ruz? Çapraz çarpımla! Yani p = `1 × `2 . O zaman kesişim noktasının hesabı gayet
basit, mesela üstteki örnek için

p = np.cross(l1,l2)
print p / p[2]

[-2 6 1]

Hakikaten de kesişim noktasının x = −2, y = 6’da olduğunu görebiliyoruz.


Aynı mantıkla iki noktadan geçen bir çizginin formülünü bulmak için şunun
doğru olduğundan hareket edebiliriz,

6
p1 · ` = 0, p2 · ` = 0

O zaman bilinen iki noktadan geçen çizgi bu iki noktanın (homojen kordinatındaki)
çapraz çarpımıdır!
Örnek
(3,1) ve (-4,5)’den geçen çizginin formülü nedir?
Cevap
Bu formül

` · (3, 1, 1) = 0

` · (−4, 5, 1) = 0

denklemlerini tatmin etmelidir. O zaman çizgi

print np.cross(np.array([3,1,1]), np.array([-4,5,1]))

[-4 -7 19]

olacaktır. Yani çizgi formülü 4x + 7y − 19 = 0.


Yol bulmak amaçlı yol sonunu gösteren kesişim noktasını bulmak için bir algo-
ritma şöyle olabilir,
1. Görüntüdeki yeterince büyük olan tüm çizgileri bul (çizgiler Hough transform-
dan başlangıç bitiş noktaları ile tanımlı, bunları çapraz çarpımı ile çizgi formülüne
çevir).
2. Tüm çizgiler arasındaki ikili kombinasyonlara teker teker bak, ve aralarındaki
kesişim noktasını hesapla.
3. İmajın orta noktasına uzak olan noktaları ele.
4. Ortalamayı al.

import itertools

def vanish(fin):
im = Image.open(fin).convert('L')
edges = canny(np.array(im), 2, 1, 25)
lines = probabilistic_hough_line(edges, threshold=20, line_length=30,line_gap=3)
im = Image.open(fin)
new_lines = []
for line in lines:
p1 = np.array([1,1,1]); p1[:2] = line[0]
p2 = np.array([1,1,1]); p2[:2] = line[1]
new_lines.append(np.cross(p1,p2))

7
res = []
for (l1,l2) in itertools.product(new_lines,new_lines):
if np.all(l1==l2): continue
inters = np.cross(l1,l2)
inters = inters / inters[2]
if np.sqrt((160-inters[0])**2 + (120-inters[1])**2) < 100:
res.append(inters)
res = np.array(res)
vanish = res.mean(axis=0)
return im, lines, vanish

im, lines, vp = vanish('in1.jpg')


for line in lines:
p0, p1 = line
plt.plot((p0[0], p1[0]), (p0[1], p1[1]))
plt.plot(vp[0], vp[1],'rd')
plt.imshow(im)
plt.savefig('vision_40lines_11.png')

im, lines, vp = vanish('in2.jpg')


for line in lines:
p0, p1 = line
plt.plot((p0[0], p1[0]), (p0[1], p1[1]))
plt.plot(vp[0], vp[1],'rd')
plt.imshow(im)
plt.savefig('vision_40lines_12.png')

8
Farklı birleşim nokta hesapları [2, sf. 21]’de bulunabilir.
Hiperdüzlemler
Hiperdüzlemler ve yarı uzaylar (halfspace) konusuna da bakalım. Bu kavram
Destek Vektör Makinaları tekniği için çok önemli.
 T
Bir düzlemi tanımlamak için bir vektör yeterli, mesela 2 boyutta düşünelim, 1 2
vektörü, bu vektöre dikgen olan tüm vektörlerin uzayı sonsuza giden bir çizgi
oluşturur. Örnek [4, sf. 378], orijinden geçen çizgi.

Bu çizgi x + 2y = 0, wT u = 0 olarak ta temsil edilebilir, vektör çarpım sonucunun


sıfır olduğuna dikkat, bu dikgenlikten ileri geliyor. İkinci çarpımda notasyon
 T  T
değişti, u = x y , ve w = 1 2 oldu, ama sonuç aynı.

9
Bu çizginin tüm uzayı ikiye böldüğü de söylenebilir, ortaya iki yarı uzay ortaya
çıkartarak.
Yarı uzayın nasıl tanımlandığını anlamadan önce, eğer x + 2y = 0’i 2 yukarı
çıkartmak istesek, x + 2y = 4 kullanabileceğimizi görelim, grafikte görüldüğü
gibi. O zaman x + 2y = 4 çizgisinin böldüğü yarı uzaylar,

x = 2y > 4

x = 2y < 4

olarak tanımlanabilir, çünkü bir çizginin üstünde ya da altında kalmak üstteki


şekilde eşitsizlikler olarak ortaya çıkartacaktır.
Bazı örnekler, ve grafikleme rutinleri görelim,

def plot_sep(w,color='blue'):
Q = np.array([[0, -1],[1, 0]])
x = np.linspace(-20,20,1000)
w2 = np.dot(Q,w[:2])
m = w2[1]/w2[0]
y = m*x + (-w[2]/w[1])
plt.plot(x,y,'.',color=color)

a = np.array([1., 2., -4])


plot_sep(a)
plt.xlim(-5,5)
plt.ylim(-5,5)
plt.grid(True)
plt.savefig('14_4.png')

Noktaların çizginin neresine düştüğünden hareketle bazı wx + b sonuçları

a1 = np.array([2., 2., -50.])


plot_sep(a1,color='green')

10
a2 = np.array([-1., 1., -4.])
plot_sep(a2,color='blue')

pt = np.array([10.,10.,1.])
plt.plot(pt[0],pt[1],'gd')
print np.dot(a1,pt)
print np.dot(a2,pt)

pt = np.array([14.,15.,1.])
plt.plot(pt[0],pt[1],'rd')
print np.dot(a1,pt)
print np.dot(a2,pt)

pt = np.array([8.,18.,1.])
plt.plot(pt[0],pt[1],'rx')
print np.dot(a1,pt)
print np.dot(a2,pt)

plt.xlim(5,15)
plt.ylim(0,20)
plt.savefig('14_5.png')

-10.0
-4.0
8.0
-3.0
2.0
6.0

Kaynaklar
[1] Jia, Problem Solving Techniques for Applied Computer Science, http://web.cs.
iastate.edu/˜cs577/
[2] Hoiem, Representations and Techniques for 3D Object Recognition and Scene Inter-
pretation
[3] Strang, Linear Algebra and Its Applications, 4th Ed
[4] Taylor, Kubovy, The Role of Perspective, http://www.webexhibits.org/
sciartperspective/perspective3.html

11
İki İmaj Kullanarak 3 Boyutta Tekrar Oluşturmak (3D Reconstruction from Two
Images)
Temel Matris (Fundamental Matrix)
8. derste vazgeçilmez matris (essential matrix) konusunu görmüştük. Şimdi
bu bölümdeki eşkutupsal kısıtlamanın (epipolar contraint) bir daha üzerinden
geçelim, ama bu sefer temel matrisi merkez alalım. Aslında vazgeçilmez ve
temel matrisler birbirine çok yakınlar, temel matris vazgeçilmezin içinden kalibre
edilme faraziyesinin çıkartılmış hali. [1, sf. 257] diyor ki vazgeçilmez matriste her
şey vazgeçilmez değilmiş demek ki (!).
Kalibrasyon, yani K nasıl çıkartılır? Diyelim ki bir kamera matrisi P = K[R|t]
olarak tanımlı ve x = PX görüntüdeki bir piksel noktası. Bilinen bir K varsa onun
tersini x’e uygulayarak x̂ = K−1 x noktasını elde edebiliriz, o zaman x̂ = [R|t]X
olur. Burada x̂’i bir tür “normalize edilmiş” kordinat sistemindeki bir görüntü
pikseli olarak düşünebiliriz, bu sistem sanki kalibrasyonu birim matris, yani I
olan bir kamera sistemidir. Aynı şekilde K−1 P = [R|t] normalize kamera matrisi
olarak adlandırılır.
Şimdi eşkutupsal kısıtlamaya tekrar bakalım. Altta soldaki resimde üç boyutlu
gerçek dünyada bir X noktası var, bu noktadan merkezi C1 ’de olan kameraya
bir çizgi çekiyoruz. Bu çizgi üzerindeki her nokta aslında aynı piksel noktasına
tekabül ediyor. Değil mi? Bu aslında bir bilgi kaybıdır, o çizgi üzerindeki tüm
noktalar aynı piksele yansırsa bir şeyler kaybediliyor. Bu kaybedilen derinlik
bilgisi. Neyse, şimdi bu çizgi üzerindeki tüm o noktaların ikinci bir kameradaki
yansımalarını düşünelim. Bu tüm değişik yansımalar ikinci kameranın görüntüsünde
bir çizgi oluştururlar (aynı piksel değil bu sefer, çünkü başka bir kameradayız),
bu çizgiye eşkutupsal çizgi diyoruz (alt sağda).

Aynı duruma tek bir X için bakalım,

1
Demek ki ilk kameradaki iki boyutlu bir x’i alıp ikinci kameradaki x 0 noktasına
transfer eden bir fonksiyon var, buna Hπ diyelim. Tranfer 2D-2D, yani iki boyut-
tan iki boyuta bir geçiş, bir homografi, ve π düzlemi üzerinde bu geçiş oluyor.
İkinci kameradaki eşkutupsal çizgi l 0 = [e 0 ]x x 0 ile elde edilir, çünkü hatırlarsak
iki noktadan çizgi elde etmek için çapraz çarpım lazım, ya da vektörlerden birinin
eksi bakışımlı hali ile normal çarpım (altsimge x eksi bakışımlılık dönüşümünü
temsil ediyor). O zaman, ve x 0 = Hπ x olduğu için,

l 0 = [e 0 ]x x 0 = l 0 = [e 0 ]x Hx = Fx

de denebilir. İşte bu denklemin [e 0 ]x H kısmına temel matris F denir.


Eşkutupsal kısıtlama nedir? Bu kısıtlama

x 0T Fx = 0

ifadesidir. Bu ifade doğru çünkü eğer x ve x 0 birbirlerine karşılık noktalar iseler,


o zaman x 0 eşkutupsal çizgi l 0 = Fx üzerinde olmalı, yani 0 = x 0T l 0 = x 0T Fx.
Nokta Karşılıkları ve 8-Nokta Algoritması
İki resimden üç boyutta tekrar oluşturma için önce F matrisini hesaplamak gerekiyor.
Oradan vazgeçilmez matris E’ye geçeceğiz, sonra E içinden R, T matrislerini çıkartabiliriz.
F’den E’ye geçiş basit, E = KT FK. İspat: Eğer eşkutupsal kısıtlama türetiminde
normalize edilmiş noktaları kullansaydık x̂ 0 Ex̂ = 0 elde ederdik, ve x̂ ve x̂ 0 yerine
x ve x 0 kullanırsak, x̂ = K−1 x, xˆ0 = K−1 x 0 , o zaman x 0T K−T EK−1 x = 0 elde ederiz,
bu demektir ki E = KT FK.
F hesabına dönelim. Elimizde iki imaj var, Alkatraz adasının iki değişik yerden
fotoğrafı [2,3]. Bu iki imaj üzerinde önce birbirine tekabül eden noktaları bu-
lacağız. Bu iş için OpenCV’nin ORB adı verilen nokta özelliği (feature) çıkartan
işlevini kullanabiliriz, onun yerine SIFT, SURF te olabilirdi.

from mpl_toolkits.mplot3d import axes3d

2
import scipy.linalg as lin
import cv2

dir = "/home/burak/Documents/Dropbox/Public/data/pcv_data"
img1 = cv2.imread(dir + "/alcatraz1.jpg")
img2 = cv2.imread(dir + "/alcatraz2.jpg")

detector = cv2.ORB_create( nfeatures = 10000 )

def detect_features(frame):
keypoints, descrs = detector.detectAndCompute(frame, None)
if descrs is None: descrs = []
return keypoints, descrs

FLANN_INDEX_LSH = 6
flann_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2

kp1, des1 = detect_features(img1)


kp2, des2 = detect_features(img2)

matcher = cv2.FlannBasedMatcher(flann_params, {})


matches = matcher.knnMatch(des1, des2, k = 2)

matches = [m[0] for m in matches \


if len(m) == 2 and m[0].distance < m[1].distance * 0.75]

print 'uyan noktalar', len(matches)

pts1 = []; pts2 = []

for i in range(len(matches)):
pt_a = kp1[matches[i].queryIdx].pt
pt_b = kp2[matches[i].trainIdx].pt
pt_a = np.array(pt_a).astype(int)
pt_b = np.array(pt_b).astype(int)
if np.sqrt(np.dot(pt_b-pt_a,pt_b-pt_a)) < 200:
pts1.append(pt_a)
pts2.append(pt_b)
cv2.line(img1, tuple(pt_a), tuple(pt_b), (255, 0, 0), 5)
cv2.circle(img1,tuple(pt_b), 5, (0,0,255), -1)

h,w,d = img1.shape
tmp = cv2.resize(img1, (int(w/4),int(h/4)))
cv2.imwrite('vision_20recons_01.jpg',tmp)

for pt in pts2: cv2.circle(img2,tuple(pt),5,(0,0,255),-1)


tmp = cv2.resize(img2, (int(w/4),int(h/4)))
cv2.imwrite('vision_20recons_02.jpg',tmp)

pts1 = np.array(pts1)
pts2 = np.array(pts2)

3
h,w,dum = img1.shape
pts1[:,1] = h-pts1[:,1]
pts2[:,1] = h-pts2[:,1]

uyan noktalar 1298

Birinci resimde saptanan ORB noktalarının ikinci resimdeki noktalara nasıl nasıl
eşleştiğini (yine birinci resimde) gösterdik, ikinci resimde o resimdeki eşleşme
noktaları görülüyor. Noktalardaki kayma kameranının hareketi hakkında bir
ipucu veriyor bize, hareketi çıplak gözle bile görebiliyoruz. Temel matrisi hesaplayınca
daha net bir sonuç alacağız tabii.
8-Nokta Algoritması

4
Daha önce E için 8-nokta algoritmasını gördük, benzer bir hesap F için de var.
Bu arada 8 nokta dedik daha fazlasına da izin veren bir çözüm yöntemi SVD ile
mümkün. Çözülecek sistem eşkutupsal kısıtlamadan başlar, i = 1, 2, .. olacak
şekilde her xi1 , xi2 eşleşmelerini bir xi1 Fxi2 = 0 hesabını içinde barındıran bir Af = 0
sistemi yaratabiliriz, xi1 = (xi1 , yi1 , wi1 ) ve xi2 = (xi2 , yi2 , wi2 ) olacak şekilde,
 
  F11
x12 x11 x12 y11 x12 w11 ... w12 w11  F12 
 x22 x21 x22 y21 x22 w21 ... w22 w21  

 .. .. .. .. ..

 F13 =0

 . . . . .  .. 
n n n n n n n n
 . 
x2 x1 x2 y1 x2 w1 . . . w2 w1
F33

ki f içinde F’nin öğeleri var. Üstteki çarpım yapılınca teker teker her satırda
eşkutupsal kısıtlamayı elde edebileceğimizi görebiliriz. Af = 0 sistemi yaklaşık
olarak SVD ile çözülebilir.

def compute_fundamental(x1, x2):


n = x1.shape[1]
A = np.zeros((n, 9))
for i in range(n):
A[i] = [x1[0, i] * x2[0, i], x1[0, i] * x2[1, i], x1[0, i] * x2[2, i],
x1[1, i] * x2[0, i], x1[1, i] * x2[1, i], x1[1, i] * x2[2, i],
x1[2, i] * x2[0, i], x1[2, i] * x2[1, i], x1[2, i] * x2[2, i],
]

U, S, V = np.linalg.svd(A)
F = V[-1].reshape(3, 3)

U, S, V = np.linalg.svd(F)
S[2] = 0
F = np.dot(U, np.dot(np.diag(S), V))
return F / F[2, 2]

Eğer biraz önce bulunan noktalar üzerinde uygularsak,

def make_homog(points):
return np.vstack((points, np.ones((1, points.shape[1]))))
print compute_fundamental(make_homog(pts1.T),make_homog(pts2.T))
[[ 1.30375335e-07 1.65553204e-07 -9.29038216e-04]
[ 5.01128878e-07 8.40553282e-07 -3.40774405e-03]
[ 3.28488982e-05 1.58554327e-03 1.00000000e+00]]

Dahası da var. Bu hesap fena değildir, fakat F gibi kritik bir hesap için daha
sağlam bir yaklaşım tercih ediliyor. RANSAC adı verilen metotla verilen tüm
eşleşme noktalarından ufak örneklemler toplanır, her örneklem üzerinde üstteki
hesap uygulanır, ve elde edilen sonuçlara bakılarak gerçek F’e yaklaşıp yaklaşılmadığı
kararlarlaştırılmaya çalışılır, en iyi, stabil olan nihai sonuç elde tutulur. Detay-
lar için [1, sf. 291]. OpenCV cv2.findFundamentalMat çağrısı F’yi RANSAC ile
hesaplayabilir. Sonra E, onu R, t parçalarına ayırırız, vs., böyle devam ederiz.

5
# kamera matrisi biliniyor
K = np.array([[2394,0,932],[0,2398,628],[0,0,1]])

F, mask = cv2.findFundamentalMat(pts1,pts2,method=cv2.RANSAC, param1=3., param2=0.99)


print 'F', F
E = K.T.dot(F).dot(K)
print 'E', E
R1,R2,t = cv2.decomposeEssentialMat(E)
print 'R1',R1
print 'R2',R2
print 't',t

F [[ 5.96322112e-08 5.60043096e-06 -2.04058699e-03]


[ -5.99484026e-06 1.84659966e-07 1.51380328e-02]
[ 1.78053340e-03 -1.63463214e-02 1.00000000e+00]]
E [[ 0.34176628 32.15102128 3.66775373]
[-34.41525089 1.0618694 23.18100597]
[ -4.61718585 -26.40378644 -0.10739647]]
R1 [[-0.29336175 -0.1052158 0.95019394]
[-0.13001529 -0.98029957 -0.14869019]
[ 0.94711927 -0.16715975 0.27390274]]
R2 [[ 0.9950157 -0.02174197 0.09731924]
[ 0.02293629 0.99967452 -0.01117018]
[-0.09704471 0.01334665 0.99519053]]
t [[ 0.63358512]
[-0.09669105]
[ 0.76760715]]

Üçgenleme (Triangulation)
Yer değiştirme, rotasyon matrislerini biliyoruz, oradan her kamera için yansıtma
matrisleri P, P 0 ’yi oluşturabiliriz. Peki bu matrisleri kullanarak üç boyutta gerçek
nokta X’leri nasıl hesaplarız? Halen elimizde sadece iki boyutlu imaj noktaları
var, 3D dünya noktaları yok. X’leri hesaplamak için daha önce gördüğümüz di-
rek lineer transform metotunun benzerini uygularız. Bu gerekli çünkü her iki
kameradaki yansımadan oluşan hatalar, vs. sonucu mesela iki kameradan direk
çizgi çekerek kesiştikleri yeri bulmaya çalışsak, alttaki durum ortaya çıkar,

O zaman yaklaşıksal bir çözüm gerekli, üstteki hata ortaya çıksa da, bu hatayı
olabildiğince minimize etmeye uğraşmalıyız.
Birbirinin eşi olan iki piksel noktası için elimizde x = PX, x 0 = P 0 X denklemleri

6
var, bu denklemde X aynı dikkat edersek, çünkü aynı 3D noktasının iki kamer-
adaki değişik yansımaları var. Bu denklemleri birleştirerek bir AX = 0 sistemi
ortaya çıkartabiliriz [1, sf. 312], ve bu sistem minimize edilebilir. Çapraz çarpım
ile homojen ölçek faktörünü çıkartırsak, mesela ilk imaj için

x × (PX) = 0

Bu bize üç denklem verir,

x(p3T X) − (p1T X) = 0

y(p3T X) − (p2T X) = 0

x(p2T X) − (p1T X) = 0

ki piT P matrisinin satırlarıdır. Bu denklemler X’in öğelerine göre lineerdir. Bu


sistemden hareketle AX = 0’daki A şöyle,

xp3T − p1T
 
 yp3T − p2T 
A= 
 x 0 p 03T − p 01T 
y 0 p 03T − p 02T

Her iki imajdan iki denklem alındı, toplam 4 denklem oldu. Bu denklem SVD
ile, ya da AX = b şeklinde tekrar düzenlenip 2. derste gördüğümüz sözde ters
(pseudoinverse) ile çözülebilir. Altta bu yöntem takip edildi,

import scipy.linalg as lin

def triangulate_point(u1, u2, P1, P2):


A = [[u1[0]*P1[2,0]-P1[0,0],u1[0]*P1[2,1]-P1[0,1],u1[0]*P1[2,2]-P1[0,2]],
[u1[1]*P1[2,0]-P1[1,0],u1[1]*P1[2,1]-P1[1,1],u1[1]*P1[2,2]-P1[1,2]],
[u2[0]*P2[2,0]-P2[0,0],u2[0]*P2[2,1]-P2[0,1],u2[0]*P2[2,2]-P2[0,2]],
[u2[1]*P2[2,0]-P2[1,0],u2[1]*P2[2,1]-P2[1,1],u2[1]*P2[2,2]-P2[1,2]]]
B = [[-(u1[0]*P1[2,3]-P1[0,3])],
[-(u1[1]*P1[2,3]-P1[1,3])],
[-(u2[0]*P2[2,3]-P2[0,3])],
[-(u2[1]*P2[2,3]-P2[1,3])]]
A = np.array(A)
B = np.array(B)
X = lin.lstsq(A,B)[0].T[0]
res = np.array([X[0],X[1],X[2],1])
return res

def triangulate(x1, x2, P1, P2):


X = [triangulate_point(x1[i, :], x2[i, :], P1, P2) for i in range(len(x1))]
return np.array(X).T

7
Test amaçlı olarak bilinen P1,P2 ve yine iki boyutta eşliği bilinen noktalarla üçgenleme
yapalım, sonra elde edilen üç boyutlu noktaları kameralara yansıtalım ve başladığımız
imaj noktalarına uyuyor mu kontrol edelim.

P1 = np.eye(4)
P2 = np.array([[ 0.878, -0.01 , 0.479, -1.995],
[ 0.01 , 1. , 0.002, -0.226],
[-0.479, 0.002, 0.878, 0.615],
[ 0. , 0. , 0. , 1. ]])
# Homogeneous arrays
x1real = np.array([[ 0.091, 0.167, 0.231, 0.083, 0.154],
[ 0.364, 0.333, 0.308, 0.333, 0.308],
[ 1. , 1. , 1. , 1. , 1. ]])
x2real = np.array([[ 0.42 , 0.537, 0.645, 0.431, 0.538],
[ 0.389, 0.375, 0.362, 0.357, 0.345],
[ 1. , 1. , 1. , 1. , 1. ]])
X = triangulate( x1real.T, x2real.T, P1, P2 )
X /= X[3]
x1 = np.dot(P1[:3],X)
x2 = np.dot(P2[:3],X)
x1 /= x1[2]
x2 /= x2[2]

print 'X', X
print 'x', x1
print 'x2', x2

X [[ 1.00277411 2.00859585 3.01259205 1.00350223 2.01053989]


[ 4.01217675 4.01023497 4.01743619 4.02955748 4.01893278]
[ 11.01977032 12.02833872 13.04162674 12.0914948 13.05493008]
[ 1. 1. 1. 1. 1. ]]
x [[ 0.09099773 0.16698863 0.23099818 0.0829924 0.15400618]
[ 0.36408896 0.33339891 0.30804717 0.33325553 0.3078479 ]
[ 1. 1. 1. 1. 1. ]]
x2 [[ 0.4200205 0.53709008 0.64501081 0.43105574 0.5379661 ]
[ 0.38890029 0.37453124 0.36194221 0.35671322 0.34517828]
[ 1. 1. 1. 1. 1. ]]

Ana problemimize dönelim; şimdi ikinci kamera için ayrıştırmadan elde edilen
R, t sonuçlarını kamera matrisi K ile çarparak P2 oluşturulmak lazım (P1 birim ma-
trisi, o biliniyor), ve böylece her imaj nokta eşleri için üçgenleme yapacağız. Fakat
8. derste bahsedildiği gibi E’nin ayrıştırmasından dört türlü farklı R, t olasılığı
ortaya çıkıyor, bu sonuçların her biri denenmeli. Altta bunu yapıyoruz, yani her
seçenek için bir üç boyutta tekrar oluşturma yapacağız, ve sonuçları farklı grafik-
lerde göstereceğiz.

for i,P in enumerate(((R1,t),(R1,-t),(R2,t),(R2,-t))):

P1 = K.dot(np.hstack(P))
P00 = np.float64([ [1,0,0,0],
[0,1,0,0],
[0,0,1,0]])
P0 = K.dot(P00)

8
X = triangulate(pts1, pts2, P0, P1)

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(X[0], X[2], X[1], 'r.')
ax.view_init(elev=23., azim=-67)
plt.savefig('vision_20recons_03_%d.png' % i)

Galiba alt sağdaki resim Alkatraz’a daha çok benziyor. Gerçek dünya uygula-
malarında “kamera önüne düşen en çok nokta hangisinde” gibi ek kodlar geliştirip
gerçek 3D sonucu bu şekilde elenebiliyor.
Kaynaklar
[1] Zisserman, Multiple View Geometry in Computer Vision 2nd Edition
[2] Bayramlı, Resim 1, https://www.dropbox.com/s/luuymnbh1njmz1v/
alcatraz1.jpg?dl=1
[3] Bayramlı, Resim 2, https://www.dropbox.com/s/ms3cp4htkxd8pw8/
alcatraz2.jpg?dl=1

9
Renk, Bölgeler ve Doku (Texture)
Renk Nicemlemesi, Posterleme (Color Quantization, Posterization)
Bir resimdeki en yaygın renkleri bulmak için [2],

from thief import ColorThief


color_thief = ColorThief('t00100.jpg')
colors = color_thief.get_palette(color_count=20)
import matplotlib.colors as mcolors
colors = [np.array(x)/255. for x in colors]
my_cmap = mcolors.ListedColormap(colors)
plt.figure(figsize=(20, 0.5))
plt.pcolormesh(np.arange(my_cmap.N).reshape(1, -1), cmap=my_cmap)
plt.gca().yaxis.set_visible(False)
plt.gca().set_xlim(0, my_cmap.N)
plt.savefig('vision_50colreg_02.png')

Şimdi resmin yaygın renklerinden birinin (üstteki renklerde en sağdaki kırmızı


mesela) resmin hangi piksellerine en yakın olduğunu bulalım. Basit uzaklık ölçüsü
kullanarak H,S,V renk üçlüsü üzerinden bir uzaklık hesaplayacağız, belli bir eşik
değeri altında olan tüm pikselleri mavi ile göstereceğiz.

import colorsys, pandas as pd


from PIL import Image
A = np.array(Image.open('t00100.jpg').convert('HSV'))
A2 = A.reshape(640*360, 3)
idx = np.array([[j, i] for i in range(360) for j in range(640)])
df = pd.DataFrame(np.hstack((A2,idx)))
df.columns = ['c1','c2','c3','x','y']

colors2 = [x*255. for x in colors]


colors3 = [colorsys.rgb_to_hsv(x[0], x[1], x[2]) for x in colors2]

diff = (df[['c1','c2','c3']] - colors2[18]).abs().sum(axis=1)


df2 = df[diff < 100.]
A3 = np.array(Image.open('t00100.jpg'))
plt.imshow(A3)
plt.hold(True)
plt.plot(df2.x,df2.y,'.')
plt.savefig('vision_50colreg_01.png')

1
Uzaklık için özellikle R,G,B değil H,S,V kullandık çünkü bu renk temsilinin uzaklık
hesaplarında daha iyi işlediği biliniyor.
Bölgeler Eşit mi?
İki imaj bölgesinin birbiriyle aynı mı farklı mı olduğu sorusu imaj gruplaması
(segmentation) ya da kümelemesi için önemli bir soru. Elimizde iki piksel grubu
var, birinin diğerine ait olduğunu nasıl bileceğiz?
İlginç bir çözüm şu olabilir; piksel değerlerinin bir olasılık dağılımından örneklendiğini
düşünmek, ve her iki bölgenin aynı dağılımdan gelip gelmediğini kontrol etmek
[1, sf. 99].
Diyelim ki belli bir düzeni, yapısı olan bir imaj bölgesi aynı / sabit bir gri değerinin,
istatistiki olarak bağımsız, 0-değerli Gaussian’dan gelen bir gürültü eklenmiş
hali. Elimizde iki bölge var, R1 , R2 , içlerinde sırasıyla m1 , m2 tane piksel değeri
var. İki hipotez mümkün,
H0 : Her iki bölge aynı objeye ait. Bu durumda her iki bölgenin tüm gri renk
değerleri tek bir Gaussian’dan örneklenmiştir, ki bu Gaussian (µ0 , σ20 ) olsun.
H1 : İmaj bölgeleri / pikselleri farklı objelere ait. Bu durumda her piksel grubu
ayrı Gaussian dağılımından geliyor, 1. bölge (µ1 , σ21 ), 2. bölge (µ2 , σ22 ).
Çoğunlukla bu parametreler bilinmez, maksimum olurluk (likelihood) kullanılarak
veriden kestirilirek hesaplanır,

2
1X
n
µ̂ = gi
n i=1

1X
n
σ̂ = (gi − µ̂)2
n i=1

Bunlar temel istatistikten bildiğimiz şeyler. Simdi herhangi bir µ, σ için bir piksel
değeri gi ’in olasılığı

1
p(gi ) = √ exp(−(gi − µ0 )2 /2σ2 )
2πσ0

H0 altında tüm pikseller aynı dağılımdan geliyor, o zaman tüm verileri gözönüne
alan ortak dağılım,

mY
1 +m2

p(g1 , g2 , ..., gm1 +m2 |H0 ) = p(gi |H0 )


i=1

mY
1 +m2
1
= √ exp(−(gi − µ0 )2 /2σ20 ))
i=1
2πσ0

mX
1 +m2
1  2 2

= 1
exp − ( (g i − µ 0 ) )/2σ0
( √2πσ )m1 +m2 i=1
0

1
= 1
exp(−(m1 + m2 )/2)
( √2πσ )m1 +m2
0

Çarpımın exp içine nüfuz edince toplam olduğuna dikkat; ayrıca exp içindeki
µ0 , σ0 nereye gitti? µ0 , σ0 yerine onların maksimum olurluk kestirme formüllerini
geçirince iptal olan terimlerden arta kalanın üstteki sonuç olduğunu görebiliriz.
H1 için durum farklı. Burada m1 tane piksel (µ1 , σ21 ) dağılımına sahip 1. bölgeye,
m2 tane piksel (µ2 , σ22 ) dağılımına sahip 2. bölgeye ait. Bu hipotez altında ortak
dağılım,

1 1
p(g1 , g2 , ..., gm1 +m2 |H1 ) = 1
exp(−m1 /2) 1
exp(−m2 /2)
( √2πσ ) m1 ( √2πσ ) m2
1 2

Artık olurluk oranını hesaplayabiliriz,

p(g1 , g2 , ..|H1 )
L=
p(g1 , g2 , ..|H0 )

3
σm1 +m2
=
σm
1 σ2
m

σ0 , σ1 , σ2 sırasıyla tüm m1 + m2 piksel, 1. bölgeden m1 piksel, 2. bölgeden m2 tane


piksel kullanarak veriden yine maksimum olurluk ile kestirilecek. Eğer üstteki
oran belli bir eşik değerinin altında ise bunu bölgelerin birbirine çok benzediğine
yönelik bir kanıt olarak kabul edeceğiz, üstte ise farklı bölgeler olduğuna kanaat
getireceğiz.
Örnek
Alttaki imajlarda iki bölge seçtik, iki bölge şunlar, biri iç dikdörtgen diğeri dış
dikdörtgenin kesişme dışında kalan pikselleri (eğer kesişme yoksa her iki dikdörtgen
ayrı ayrı),

from PIL import Image, ImageDraw


import pandas as pd
def draw_boxes(bs,imfile):
im = Image.open(imfile).convert('L')
draw = ImageDraw.Draw(im)
arr = np.asarray(im)
colors = ['white','yellow','white','white']
for i,b in enumerate(bs):
fr = b[0]; to = b[1]
bnew = [(fr[0],arr.shape[0]-fr[1]),(to[0],arr.shape[0]-to[1])]
draw.rectangle(bnew,outline=colors[i])
plt.imshow(im, cmap=plt.cm.Greys_r)

box1 = [(79,144),(100,282)]
box2 = [(63,154),(117,287)]
draw_boxes([box1,box2],'t00100.jpg')
plt.savefig('vision_50colreg_03.png')
box3 = [(80,63),(95,260)]
draw_boxes([box1,box3],'t00100.jpg')
plt.savefig('vision_50colreg_04.png')

4
def get_pixels(box, im):
arr = np.array(im)
yw = arr.shape[0]
xw = arr.shape[1]
(bx1,by1) = box[0]; (bx2,by2) = box[1]
by1 = yw-by1; by2 = yw-by2

5
x1 = min(bx1,bx2); x2 = max(bx1,bx2)
y1 = min(by1,by2); y2 = max(by1,by2)
arr = arr[y1:y2, x1:x2]
return arr

im = Image.open('t00100.jpg').convert('L')
arr1 = get_pixels(box1, im)
arr2 = get_pixels(box2, im)
print arr1.shape, arr2.shape

(138, 21) (133, 54)

Olurluk oranının log’unu alarak hesap yapınca

def likratio(arr1,arr2):
tarr1 = np.reshape(arr1, (arr1.shape[0]*arr1.shape[1]),1)
tarr2 = np.reshape(arr2, (arr2.shape[0]*arr2.shape[1]),1)
arr0 = np.hstack((tarr1,tarr2))
s0 = np.std(arr0); s1 = np.std(tarr1); s2 = np.std(tarr2)
L = len(arr0)*np.log(s0) - (len(tarr1)*np.log(s1) + len(tarr2)*np.log(s2))
return L
L = likratio(arr1, arr2)
print L

419.6536187

İkinci resimde her iki dikdörtgen aynı direğin üzerinde, yani aynı obje üzerindeler.
Bu durumda oranın daha düşük olmasını bekleriz,

arr1 = get_pixels(box1, im)


arr2 = get_pixels(box3, im)
L = likratio(arr1, arr2)
print L

244.473078548

Hakikaten de öyle.
Çok Boyutlu Gaussian Kullanmak
Eğer renkli resimleri işlemek istiyorsak, her pikselin H,S,V değerlerini kullanabil-
iriz, bu durumda bir resim bölgesini üç boyutlu Gaussian olarak temsil etmemiz
gerekir. Yani üç boyutlu herhangi bir piksel xi için

1 1
p(xi ) = exp − (xi − µ)T Σ−1 (xi − µ)
(2π)p/2 det(Σ)1/2 2

µ, Σ bu Gaussian’ın ait olduğu bölge olacaktır, p boyuttur, yani 3. Türetime


başlamadan önce Σ’yi kestirme hesaplayan Σ̂’yi hatırlayalım,

1X
n
Σ̂ = (xi − µ̂)(xi − µ̂)T
n i=1

6

Kısaltma amaçlı Cj = 1/ (2π)k/2 det(Σj )1/2 diyelim,

mY
1 +m2  
1 1 T −1
p({x}|H0 ) = exp − (xi − µ0 ) Σ0 (xi − µ0 )
i=1
C0 2

 mX
1 +m2 
1 1
= exp − (xi − µ0 )T Σ−1
0 (xi − µ0 )
Cm
0
1 +m2
i=1
2

Şimdi aynen tek boyutlu örnekte olduğu gibi Σ0 yerine onun kestirme hesabını
formüle sokalım,

X
n  mX
1 +m2 −1 
1 1 T 1 T
= exp − (xi − µ̂) (xk − µ̂0 )(xk − µ̂0 ) (xi − µ̂0 )
Cm
0
1 +m2
i=1
2 m 1 + m2
k=1

Bu formül nasıl kısalabilir? Herhangi bir µ için zi = xi − µ̂ diyelim, m1 + m2


yerine n olsun, ve zi ifadesi p × 1 boyutunda vektörler. Genel olarak şu ifadeyi

!−1
X
n X
n
zTi zk zTk zi
i=1 k=1

kısaltmaya uğraşıyoruz. Burada iz operatörünü kullanabiliriz, iz bildiğimiz gibi


bir matrisin köşegeninin toplamını verir. Güzel özellikleri vardır, mesela tr(A +
B) = tr(A) + tr(B) ve tr(AB) = tr(BA) gibi.

!−1  !−1 
X
n X
n X
n X
n
zTi zk zTk zi = tr  zTi zk zTk zi 
i=1 k=1 i=1 k=1

ile başlayabiliriz. İz kullanabildik çünkü izini aldığımız “matris” aslında bir tek
sayı. Şimdi izin üstteki ve toplam işlemleri içine nüfuz edebilme özelliğini kul-
lanacağız,
 !−1 
X
n X
n
= tr zTi zk zTk zi 
i=1 k=1

 !−1 
X
n X
n
= tr  zk zTk zi zTi 
i=1 k=1

7
 !−1 
X
n X
n
= tr  zk zTk zi zTi  = tr(Ip ) = p
k=1 i=1

O zaman

 !−1   !−1 
1X 1X nX X
n n n n
exp − zTi zk zTk zi  = exp − zTi zk zTk zi  = exp(−np/2)
2 i=1
n k=1 2 i=1 k=1

haline geldi, demek ki


 
1 (m1 + m2 )p
p({x}|H0 ) = exp −
Cm
0
1 +m2
2
   
1 m1 p 1 m2 p
p({x}|H1 ) = m1 exp − exp −
C1 2 Cm 2
2
2
 
1 1 m1 p m2 p
= m1 m2 exp − −
C1 C2 2 2
 
1 1 (m1 + m2 )p
= m1 m2 exp −
C1 C2 2

p({x}|H1 )
L=
p({x}|H0 )

Bölüm sırasında exp terimleri iptal olur, sonuç

Cm
0
1 +m2

L=
Cm 1
1 C2
m2

1/Cj = (2π)p/2 det(Σj )1/2 oldugu icin

(2π)m1 p/2 det(Σ1 )m1 /2 (2π)m2 p/2 det(Σ2 )m2 /2


(2π)(m1 +m2 )p/2 det(Σ0 )(m1 +m2 )/2

|Σ1 |m1 /2 |Σ2 |m2 /2


=
|Σ0 |(m1 +m2 )/2

Tabii hesaptan önce üstteki formülde yine kestirme değerleri yerine koyarak hesabı
yapacağız.
Renkli bir resme bakalım şimdi,

8
im = Image.open('t00100.jpg').convert('HSV')
print np.array(im).shape

(360, 640, 3)

Görüldüğü gibi imaj matrisinde artık her hücrede üç öğe var.

from PIL import Image, ImageDraw


import pandas as pd
import scipy.linalg as lin

def get_pixels(box, arr):


(yw,xw,d) = arr.shape
(bx1,by1) = box[0]; (bx2,by2) = box[1]
by1 = yw-by1; by2 = yw-by2
x1 = min(bx1,bx2); x2 = max(bx1,bx2)
y1 = min(by1,by2); y2 = max(by1,by2)
arr = arr[y1:y2, x1:x2, :]
return arr

def draw_boxes_color(bs, im):


arr = np.asarray(im)
draw = ImageDraw.Draw(im)
colors = ['magenta','green','white','red','yellow']
for i,b in enumerate(bs):
fr = b[0]; to = b[1]
bnew = [(fr[0],arr.shape[0]-fr[1]),(to[0],arr.shape[0]-to[1])]
draw.rectangle(bnew,outline=colors[i])
plt.imshow(im)

def loglikratio(box1,box2,arr):
arr1 = get_pixels(box1, arr)
arr2 = get_pixels(box2, arr)
tarr1 = np.reshape(arr1, (arr1.shape[0]*arr1.shape[1],3))
tarr2 = np.reshape(arr2, (arr2.shape[0]*arr2.shape[1],3))
tarr0 = np.vstack((tarr1,tarr2))
sd0 = lin.det(np.cov(tarr0.T))
sd1 = lin.det(np.cov(tarr1.T))
sd2 = lin.det(np.cov(tarr2.T))
LLR = len(tarr0)/2*np.log(sd0) - len(tarr1)/2*np.log(sd1) - len(tarr2)/2*np.log(sd
return LLR

box1 = [(79,144),(100,282)]
box2 = [(63,154),(117,287)]
box3 = [(80,63),(95,260)]

im = Image.open('t00100.jpg').convert('HSV')
draw_boxes_color([box1,box2],im)
plt.savefig('vision_50colreg_09.png')
im = Image.open('t00100.jpg').convert('HSV')
draw_boxes_color([box1,box3],im)
plt.savefig('vision_50colreg_11.png')

9
1. ve 2., sonra 1. ve 3. bölgeler arasında olurluk oranını hesaplayalım,

arr = np.array(im)
print loglikratio(box1,box2,arr)
print loglikratio(box1,box3,arr)

10
874.532775212
635.48295072

Farklı bir resme bakalım, Alcatraz adasının bir fotoğrafı mesela,

box1 = [(36,134),(86,201)]
box2 = [(3,125),(37,200)]
im = Image.open('../vision_01/alcatraz1.png').convert('HSV')
draw_boxes_color([box1,box2],im)
plt.savefig('vision_50colreg_05.png')

print loglikratio(box1,box2, arr)

6599.1051811

box3 = [(19,89),(76,124)]
im = Image.open('../vision_01/alcatraz1.png').convert('HSV')
draw_boxes_color([box1,box3],im)
plt.savefig('vision_50colreg_06.png')

11
print loglikratio(box1,box3,arr)

3171.54541435

Daha zor bir örnek

box1 = [(35,144),(87,292)]
box2 = [(106,183),(158,287)]
box3 = [(117,86),(132,160)]
box4 = [(106,183),(138,287)]
im = Image.open('castle.png').convert('HSV')
draw_boxes_color([box1,box2],im)
plt.savefig('vision_50colreg_07.png')
im = Image.open('castle.png').convert('HSV')
draw_boxes_color([box2,box3],im)
plt.savefig('vision_50colreg_08.png')
im = Image.open('castle.png').convert('HSV')
draw_boxes_color([box1,box4],im)
plt.savefig('vision_50colreg_10.png')

12
13
im = Image.open('castle.png').convert('HSV')
arr = np.array(im)
print loglikratio(box1,box2,arr)
print loglikratio(box2,box3,arr)
print loglikratio(box1,box3,arr)
print loglikratio(box1,box4,arr)

23886.6334257
527.840460625
15695.3369086
17913.2279323

Kaynaklar
[1] Schunk, Machine Vision
[2] Dhakar, Color Thief, https://github.com/fengsp/color-thief-py

14
Obje Takibi
Video görüntülerinde obje takibi için filtreleme kullanmak mümkün, bu teknik
ile iki boyutlu yansımadan üç boyutlu konum bilgisini takip edebiliriz. Kalman
Filtreleri (KF) ile görüntüde ilgilendiğimiz objeyi her seferinde iki boyutta “bul-
malıyız”, yani bu objenin örüntüsünün ne olduğunu önceden biliyor olmamız
gerekir, ve onu sonraki resimlerde takip etmemiz gerekir. Bulduğumuz, iki boyutlu
kordinat değerleridir, yani ölçümsel büyüklüklerdir, ardından KF’in en son kon-
umuna göre ürettiği tahmin ile aradaki fark KF’i düzeltmek için kullanılır.
Parçacık filtreleri (PF) ile yine konum ve ölçüm fonksiyonu ikilisi var, fakat ölçüm
ile konumdan üretilen tahmin arasındaki uyumu bir olasılık, olurluk (likelihood)
olarak belirtmemiz gerekiyor, ki böylece PF tahminde başarılı olan parçacıklara
daha fazla önem verebilsin, ve hipotezler o yönde devam etsin.
Alttaki örnekte OpenCV kütüphanesinden elde ettiğimiz 2 boyutlu değerleri ölçüm
yt için kullanacağız. Değerler OpenCV’nin bir satranç tahtası şeklinin köşe nok-
talarını cvFindChessboardCorners ile buluyor (ve onları cvDrawChessboardCorners
ile onları resimde gösteriyoruz).
Elimizdeki “gürültülü” ölçümler iki boyutlu noktasal değerler. Gürültülü çünkü
kamera bize bu imajları aktarırken hata eklemiş olabilir, OpenCV fonksiyonu
hesabı yaparken hata eklemiş olabilir, bir sürü olasılık var.
Kalman Fitreleri
Bu örnekte, ayrıca, ilk kez KF ortamında boyut değişikliği olasılığını net bir şekilde
görebiliyoruz. Gizli konum bilgisi xt 3 boyutlu bir nokta, ama elimizdeki ölçüm 2
boyutlu bir “yansıma”. Yansıma sırasında kaçınılmaz olarak değer kaybediliyor,
bir boyutun bilgisi ortadan yokoluyor. Ama tüm bu bilinmezlere rağmen Kalman
filtresinin bizim için gizli bilgiyi hesaplamasını istiyoruz.
Bu problemde Φ matrisi ne olacaktır? Obje takibi konularında Φ’nin ne olduğunu
hayal etmek daha kolay, Φ matrisi iki zaman dilimi arasındaki “hareketi” tem-
sil edecek. Bu problemdeki ek bir kolaylık bu hareketi önceden bildiğimiz, ve
hareketin tek yönde olduğu. Yani resimde benim tuttuğum kartonu ne kadar
hızla hareket ettirdiğimi ben önceden probleme bildiriyorum. Yer değişikliğini d
olarak tanımladım, ve Φ şöyle oldu:

 
1 0 0 0
 0 1 0 0 
Φ=
 0

0 1 d 
0 0 0 1

Dikkat edersek Φ 4x4 boyutunda, 3x3 değil. 3 boyutlu kordinatları temsil etmek
için homojen kordinat sistemini kullandığımız için böyle oldu, o sebeple zaten
xt de 4x1 oldu, ona uymak için Φ’nin değişmesi gerekiyordu. Φxt çarpımının
hakikaten kartonu hareket ettirdiğini göstermek
 için bu çarpımı bir örnek üzerinde
yapalım: Diyelim ki xt = a1 a2 a3 a4 o zaman Φxt ya da xt+1 şu hale gelir:

1
 
a1 a2 a3 + d a4 .
Bakıyoruz, hakikaten de d kadarlık bir yer değişimi z kordinatı, yani derinlik
üzerinde eklenmiş. Test amaçlarımız için d = -0.5 aldık, yani satranç tahta karto-
nunun her zaman diliminde kameraya doğru 0.5 cm ilerlediğini belirttik. Tabii
bu da kabaca bir tahmindi (her ne kadar hareketi yaptıran ben olsam bile!), ama
filrelemenin gücünü burada görüyoruz. Benim tahminimde “gürültü” yani “hata
payı” var, ölçümde gürültü var, tüm bunlar üst üste konsa bile filtre yine de gizli
konumu bulacak.
Ölçümsel dönüşümü temsil eden H’e ben onun temeli olan yansıtma (projection)
kelimesinden gelen P matrisinden bahsedelim. Yansıma matrisi görüntü (vision)
literatüründe iğne delik kamerası (pinhole camera) modelinden ileri gelen bir
matristir ve bu matrisi hesaplamak ayarlama / kalibrasyon (calibration) denen
apayrı bir işlemin parçasıdır. OpenCV içinde kalibrasyon için fonksiyonlar var,
biz de bunları denedik, kalibrasyon için kullandığımız resimlerle alakalı olmalı,
elde edilen sonuçlardan memnun kalmadık. Alternatif olarak şunu yaptık; res-
imde görülen yeşil yüzey bizim programın oluşturduğu hayali bir yüzey. Fil-
trenin o anki tahminini P üzerinden görüntüye yansıtarak bu yüzeyi oluşturduk,
böylece deneme / yanılma yöntemiyle pek çok P değerini deneyerek, yüzeyin
resimde görülen masanın sonunda çıkacak şekilde olmasını sağladık. Yansıtma
için kullanılan K matrisi, yansıtma metotu ve başlangıç imajı altta:

from numpy import *

K = array([[700., 0., 300.],


[0., 700., 330.],
[0., 0., 1.]])

def proj_board(im, xl, yl, z):


h,w = im.shape[:2]
for x in arange(xl-9, xl+9, 0.5):
for y in arange(yl-9, yl+9, 0.5):
X = array([x, y, z])
q = dot(K, X)
q = [int(q[0]/q[2]), int(q[1]/q[2])]

2
if q[0] >= w: return
if h-q[1] >= h: return
if h-q[1] < 0: return
im[h-q[1], q[0]] = 255

O noktaya gelince istediğimiz P değerini bulmuş oluyorduk. Yansıtma matrisleri


3x3 olur, KF buna bir dördüncü [0 0 0] satırı ekleyerek onu 4x3 H haline getiriyor.
KF’in başlangıç noktası olarak P’yi bulmak için kullandığımız masa sonunu kul-
landık. Kararsızlık ölçütü Q için, ki bu değişken bir Gaussian kovaryansıdır,
Q = I · 150cm değerini kullandık, yani oldukça büyük bir kararsızlık değeri kul-
landık. Sebep başlangıç değeri olan masa ortasını seçtik, ve takip edeceğimiz
satranç tahtasının nerede olduğunu bilmiyoruz, “emin değiliz”. Bu kararsızlığı
sayısal olarak programa bildirmiş olduk.

import sys; sys.path.append('../../tser/tser_083_kf')


import cv2
import util
from kalman_3d import *

dim = 3
if __name__ == "__main__":
fin = sys.argv[1]
cap = cv2.VideoCapture(fin)
N = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = int(cap.get(cv2.CAP_PROP_FPS))
kalman = Kalman(util.K, mu_init=array([1., 1., 165., 0.5]))

for i in range(N):
ret, frame = cap.read()
h,w = frame.shape[:2]
#proj_board(frame, 1, 1, 160) # basla
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
status, corners = cv2.findChessboardCorners( gray, (dim,dim))
is_x = []; is_y = []
if status:
cv2.drawChessboardCorners( gray, (dim,dim), corners, status)
for p in corners:
is_x.append(p[0][0])
is_y.append(p[0][1])

if len(is_x) > 0 :
kalman.update(array([is_x[5], h-is_y[5], 1.]))
util.proj_board(gray,
kalman.mu_hat[0],
kalman.mu_hat[1],
kalman.mu_hat[2])
if i % 10 == 0:
cv2.imwrite('/tmp/kf-out-%d.jpg' % i, gray)
cv2.imshow('frame',gray)
cv2.waitKey(20)

Kalman filtreleri (KF), eğer kararsızlık Gaussian olarak gösterilebiliyorsa çok fay-

3
dalı, ve hızlı bir yöntem. Bir KF bellekte çok az yer tutar, 3 boyutlu bir Gaussian
için 3x1 boyutunda bir ortalama vektörü, ve 3x3 boyutunda bir kovaryans matrisi
yeterlidir, yani 3 + 9 = 12 sayı.

Parcaçık Filtreleri (Partıcle Filters)


Parçacık filtreleri (PF) bir dağılımı ayrıksal olarak temsil edebilirler. Diyelim ki
tek boyutlu bir dağılımı 100 öğe içeren bir dizin ile temsil edebiliriz, o zaman
dağılımın değerlerini 100 tane noktada taşımamız gerekir. Bunun faydaları her
türlü dağılım şeklini temsil edebilmemiz. Gaussian ile sadece tek bir tepe nok-
tası olabilir, fakat ayrıksal temsil ile 2, 3, istediğimiz kadar tepe noktası olan bir
dağılımı temsil edebiliriz. Bu sayede birden fazla gayrı lineer hipotezi aynı anda
işletebiliriz. KF ile tepe noktası en iyi tahminimizdir (mesela.. satranç kartonu
masa ortasında), PF ile birkaç tahmini aynı anda hesaplatmak mümkün olabilir.
PF kodlaması xt için iki tane veri yapısı gerektirir. Biri dağılım değerlerini temsil
eden parçacıklardır, diğeri dağılımdaki önemini temsil eden ağırlıklardır. Fil-
treleme mekaniği KF’e benzer, önce bir geçiş uygulanır, ki bu geçiş kararsızlığı
arttıracaktır, ardından gözlem verisi ve bir hata fonksiyonu üzerinden dağılım
güncellenir. Bu işlem sırasında hatası yüksek olan parçacıklar cezalandırılır, on-
ların ağırlığı azalır, ötekilerinki yükselir. Her parçacık için hata fonksiyonu şudur:

1
w[i] =
1 + (y[i] − p[i] )2 )

y[i] gözlem değeri, p[i] geçiş uygulandıktan sonra elimizdeki tahminimizdir, ki


bu KF dünyasındaki Φxt + Q’nun karşılığıdır. PF için hareket geçişi şöyle hesa-
planır: Bir birörnek (uniform) dağılımdan örnekleme yapılır, ve bu örneklenen
değerler x’e eklenir. Örnekleme için z-kordinatı için Unif(−0.1, −1)’i, x kordinatı
için Unif(−40, 40)’i kullandık. Yani ileri doğru 0.1 ve 1 santimetre arasında bir
hareket ekliyoruz, ve sağa ve sola dönük olarak 80 santimetrelik bir kararsızlığı
hesaplara ekliyoruz.
Üstteki formülde (y[i] − p[i] )2 e niye 1 değeri eklediğimiz açıktır herhalde, bu
sayede hata fonksiyonunun olasılık değerlerini andıran bir sonuç döndürmesini
istiyoruz. Çok ufak hatalar için 1 + hata bölünendeki 1’i bölecek, ve 1’e yakın
bir değer geri getirecek. İstediğimiz de bu zaten, küçük hataların daha büyük
ağırlığa, büyük hataların ise tam tersine sebep olmaları.
Tekrar örnekleme (resampling) sürecinde parçacıklar tekrar düzenlenerek ağırlığı
çok olan parçacıkların ağırlığı az olanlara göre daha fazla tekrarlanmasını istiy-

4
oruz. Dikkat: tekrar örnekleme süreci yeni parçacık değerleri yaratmıyor, sadece
mevcut olanları tekrarlıyor ya da onları atlıyor.

import sys; sys.path.append('../../tser/tser_085_pf')


import cv2
import util
from PF import *

dim = 3
if __name__ == "__main__":

fin = sys.argv[1]
cap = cv2.VideoCapture(fin)
N = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

pf = PF(util.K, 200)

for i in range(N):
ret, frame = cap.read()
h,w = frame.shape[:2]
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
status, corners = cv2.findChessboardCorners( gray, (dim,dim))
is_x = []; is_y = []
if status:
cv2.drawChessboardCorners( gray, (dim,dim), corners, status)
for p in corners:
is_x.append(p[0][0])
is_y.append(p[0][1])

if len(is_x) > 0:
pf.update(array([is_x[5], h-is_y[5], 1.]))
mu_x = pf.average()
util.proj_board(gray, mu_x[0], mu_x[1], mu_x[2])

cv2.imshow('frame',gray)
if cv2.waitKey(20) & 0xFF == ord('q'):
break

Kaynaklar
[1] Bayramlı, Sample Video, https://drive.google.com/uc?export=view&
id=1gq6J3mPFj4UMbkmi3vDrXKwa9IdtxNLH
[2] Bayramlı, Sample Video, https://drive.google.com/uc?export=view&
id=1F8G5ROkD76YIRKOM5W9iVj6Jok4-CQxp

5
Yol Bulmak, Takip Etmek (Road Detection)
Bir arabanın önünde ya da elde tutulan tek bir kamera görüntüsü ile (monocular
vision) önümüzdeki açık yolu nasıl buluruz? Sürücüsüz arabalar için bu önemli
bir konu, çözüm için farklı teknikler var.
[1]’deki çözüm şöyledir: yolu kameranın görüntüsünün alt ortasından çıkan bir
eğri olarak modelle, sonra “yol piksellerini” bul; bunun icin görüntünün alt or-
tasındaki (yani hemen önümüzdeki) ufak bir kutudaki pikseller ile yol piksel-
lerinin aşağı yukarı aynı renkte olacağından hareket et, sonra yolu temsil eden
eğrinin o piksellere en iyi uyumlu formunu bulmak için filtreleme işlemi kullan.

Üstteki figürde yol modelinin kuşbakışı görünümü var. Eğri karesel bir formül
olarak seçilmiş, biz küpsel form kullanacağız, ve z yerine y kullanacağız, çünkü
[1]’deki yaklaşım z derinliğine göre eğrinin y noktalarını yansıtma ile ayarlıyor,
biz basitlik amaçlı olarak önceden seçilmiş bir yansıtmaya göre bilinen y seviyelerini
baz alacağız. Dikkat: İmajın alt ortasından çıkan bir eğriyi temsil etmek için y
bazlı formül kullanıyoruz, yani

y = ax3 + bx2 + cx + d

yerine

x = ay3 + by2 + cy + d

Bunun sebebi belli bir x noktasından çıkan küpsel eğriyi x temelli temsilin zor ol-
ması. Fakat eksenleri değiş/tokuş yapınca çıkış noktasını kesi (intercept) üzerinden
basit bir şekilde temsil edebiliriz. Mesela (320,240) boyutlarındaki bir resmin alt
ortasından çıkan eğri

x = ay3 + by2 + cy + 160

ile gösterilir. İki tane eğri örneği görelim (a, b, c, d sabitleri pols içinde),

yy = np.linspace(0,320,200)
pols = [ -4.08661281e-05, 0.79580150e-02, -2.02432986e-01, 160.]

1
xx = pols[3] + pols[2]*yy + pols[1]*yy**2 + pols[0]*yy**3
plt.plot(xx,240-yy)
pols = [ -4.08661281e-04, 0.79580150e-02, -2.02432986e-01, 160.]
xx = pols[3] + pols[2]*yy + pols[1]*yy**2 + pols[0]*yy**3
plt.plot(xx,240-yy)
plt.xlim(0,320)
plt.ylim(240,0)
plt.savefig('vision_70road_05.png')

Yol Pikselleri
Bu pikselleri bulmak için önce alt ortadaki bir kutu içine düşen HSV piksellerinin
üç boyutlu histogramını hesaplıyoruz. Bu bize bir ayrıksal dağılım veriyor. Sonra
bu dağılımı kullanarak imajdaki tüm piksellerin o dağılıma ait olma olasılığını
hesaplıyoruz. Belli bir eşik değerini geçen pikselleri yol pikseli olarak işaretliyoruz.

from PIL import Image, ImageDraw, ImageFilter


import pandas as pd, zipfile

def draw_boxes_color(bs, im):


arr = np.asarray(im)
draw = ImageDraw.Draw(im)
colors = ['magenta','green','white','red','yellow']
for i,b in enumerate(bs):
fr = b[0]; to = b[1]
bnew = [(fr[0],arr.shape[0]-fr[1]),(to[0],arr.shape[0]-to[1])]
draw.rectangle(bnew,outline=colors[0])
plt.imshow(im)

def eval(x, H, edges):


i=np.argmax(x[0]<edges[0])
j=np.argmax(x[1]<edges[1])
k=np.argmax(x[2]<edges[2])
return H[i-1,j-1,k-1]

def get_pixels(box, im):


arr = np.array(im)
(yw,xw,d) = arr.shape
(bx1,by1) = box[0]; (bx2,by2) = box[1]

2
by1 = yw-by1; by2 = yw-by2
x1 = min(bx1,bx2); x2 = max(bx1,bx2)
y1 = min(by1,by2); y2 = max(by1,by2)
arr = arr[y1:y2, x1:x2, :]
return arr

with zipfile.ZipFile('mitte.zip', 'r') as z:


im = Image.open(z.open('105.jpg')).convert('HSV')

box = [(110,0),(200,20)]

bins = (8,8,8)
bim = get_pixels(box, im)
bnim = np.reshape(bim, (bim.shape[0]*bim.shape[1], 3))
H, edges = np.histogramdd(bnim, bins=bins, normed=True, range=[(0,255),(0,255),(0,255)
imm = np.array(im)
nim = np.reshape(imm, (imm.shape[0]*imm.shape[1], 3))
e = map(lambda x: eval(x, H, edges), nim)
ee = np.array(e)
ee = np.log(ee + 1e-10)
imm2 = np.array(im)
nim2 = np.reshape(imm2, (imm2.shape[0]*imm2.shape[1], 3))
nim2[ee > -15] = [0,0,0]
imm2 = np.reshape(nim2,imm2.shape)
im2 = Image.fromarray(imm2,'HSV')

fig=plt.imshow(im2)
draw_boxes_color([box],im2)
plt.savefig('vision_70road_06.png')

fig=plt.imshow(im)
draw_boxes_color([box],im)
plt.savefig('vision_70road_07.png')

Fena değil; yol ortasındaki direkler yol sayılmadı, ve genel olarak yolun gidişini
görebiliyoruz.
Filtreleme
Elimizde yol pikselleri var. Bir eğri modeli var. Şimdi herhangi bir yol hipotezin-

3
den başladık diyelim, yol piksellerinin bu model üzerinde düzeltme yapmasını
nasıl sağlayacağız? Eğer Kalman Filtresi (KF) kullanacaksak sonuç bir ya da daha
fazla boyutlu reel sayılar olacak, o zaman ölçüm ne olacak, hata ne olacak? [1]’in
kullandığı dahiyane fikir şudur: Yol hipotezi / modeli üzerinde eşit büyüklükte,
belli aralıklarda, belli sayıda (bu çok önemli) şerit olduğunu düşünürüz,

İki boyuttaki yansıması

Filtreleme için tüm bu şeritler içine düşen yol piksellerini buluruz. Bu piksel-
lerin kordinatlarının ortalamasını alırız, bu bize bir x kordinatı verir. İşte ölçüm
budur, çünkü eğer yol hipotezi mükemmel olsaydı kutu içindeki tüm pikseller
yol olurdu, onların ortalaması yine modelin hesapladığı x olurdu. Eğer böyle
değilse, mesela soldan bir dışarı taşma var ise ortalama modelin sağına düşer,
sağdan taşma var ise, modelin soluna düşer. Bu bize düzeltme için gerekli ölçümü
sağlar.

4
Sonlu sayıda kutu var demiştik, mesela 5 (iki üstteki figürdeki gibi), o zaman
ölçümümüz 5 boyutlu olacaktır.
Ayrıca KF modeli için F, H matrisleri gerekiyordu. Kalman sistemini hatırlarsak,

xk = Fxk−1 + Q

zk = Hxk + R

H bize konum bilgisini dışa dönük bir tahmine çevirme imkanı verir. Konum
bilgisi yol eğrisinin son halidir, o zaman a, b, c, d katsayılarını içerecek. Ölçüm ve
model için önceden seçilmiş y noktaları kullanılacağız, bunlar y1 , y2 , .., y5 olsun,
o zaman H ve Hx çarpımı suna benzer,

y31 y21 ay31 + by21 + cy1 + d


   
y1 1  
 y32 y22 y2 1  a  ay32 + by22 + cy2 + d 
  b   
Hx = 
 y33 y23 y3 1   = 
 c   ay33 + by23 + cy3 + d 

 y34 y24 y4 1   ay34 + by24 + cy4 + d 
d
y35 y25 y5 1 ay35 + by25 + cy5 + d

ki d = 160 ve F = I, yani birim matrisi. H matrisi eğri modelini alıp bize ölçüm
ile karşılaştırılabilecek bir sonuç veriyor. Tüm bu tahmin, düzeltme işlemleri
KF matematiğinin içinde oluyor tabii. Şimdi ardı ardına üç resim üzerinde KF
güncelleme kodunu görelim,

import sys; sys.path.append('../../tser/tser_kf')


import kalman

5
from PIL import Image, ImageDraw
import pandas as pd, zipfile

# her kutu (y-kordinati, genislik, yukseklik) ile tanimli


boxes = [(5,50,20),(35,45,15),(60,40,10),(75,30,8),(90,25,6),(105,20,6)]
yy = np.linspace(0,120,1000)
kf = kalman.KalmanFilter(dim_x=4, dim_z=5)
kf.x = np.array([[-4.08661281e-05, 0.59580150e-02, -2.02432986e-01, 160.]]).T
kf.P = np.diag([1e-4,1e-4,1e-4,1e-4])
kf.F = np.eye(4)
H = [[ylev**3, ylev**2, ylev, 1 ] for (ylev, bwidth, bhight) in boxes[1:]]
kf.H = np.array(H)
kf.R *= 10.

def rcurve(yy, kf): return kf.x[0]*yy**3 + kf.x[1]*yy**2 + kf.x[2]*yy + kf.x[3]

bins = (8,8,8)

top = 120
import itertools
idxs = [(i,j) for (i,j) in itertools.product(range(240,0,-1),range(0,320)) ]
idxs = np.array(idxs)

with zipfile.ZipFile('mitte.zip', 'r') as zz:


for i in (105,106,107):
f = plt.figure()
xx = rcurve(yy, kf) # egriyi ciz
f = '%d.jpg' % i
im = Image.open(zz.open(f)).convert('HSV')
boxes2 = []
for (ylev, bwidth, bhight) in boxes:
boxes2.append(((rcurve(ylev,kf)-bwidth, ylev),\
(rcurve(ylev,kf)+bwidth, ylev+bhight)) )
draw_boxes_color(boxes2, im)

bim = get_pixels(box, im)


bnim = np.reshape(bim, (bim.shape[0]*bim.shape[1], 3))
H, edges = np.histogramdd(bnim, bins=bins, normed=True,
range=[(0,255),(0,255),(0,255)])
imm = np.array(im)
nim = np.reshape(imm, (imm.shape[0]*imm.shape[1], 3))
e = map(lambda x: eval(x, H, edges), nim)
ee = np.array(e)
ee = np.log(ee + 1e-20)

f=plt.imshow(im)

h = np.array(im).shape[0]
plt.plot(xx,h-yy)

z = []
for (ylev, bwidth, bhight) in boxes[1:]:
low_left = (rcurve(ylev,kf)-bwidth, ylev)
up_right = (rcurve(ylev,kf)+bwidth, ylev+bhight)
boxes2.append((low_left,up_right))

6
mask = (idxs[:,1] >= low_left[0]) & (idxs[:,1] <= up_right[0]) & \
(idxs[:,0] >= low_left[1]) & (idxs[:,0] <= up_right[1] )
mask2 = (ee > -15.0)
idxs2 = idxs[mask & mask2]
m = idxs2.mean(axis=0)
z.append(m[1])
plt.plot(idxs2[:,1], h-idxs2[:,0], '.b')
plt.plot(m[1], h-m[0], 'wd')

z = np.reshape(np.array(z),(5,1))
plt.axis('off')
plt.savefig('out-%d.png' % i)
kf.predict()
kf.update(z)

7
Görülüyor ki ilk başta kutulardan bazıları bir direk üzerindeydi, bu sebeple ölçüm
modelin sağına düştü. Düzeltme yapıldı, ve birkaç döngü sonrası son resimdeyiz,
ve direkler arasındaki yolu gösteriyoruz.
Kaynaklar
[1] Procházka, Road Tracking Method Suitable for Both Unstructured and Structured
Roads

8
Geri-Yansıtmayla 3D Işın Hesabı (Back-projecting a 3D Ray), ve Düzlem Mesafesi
Üç boyutlu bir noktanın iki boyuta yansımasında derinlik bilgisinin kaybolduğunu
gördük, birden fazla üç boyutlu nokta aynı piksele tekabül edebiliyor. Bu du-
rumda sadece piksel kullanarak obje mesafe ölçümünü tek bir görüntü üzerinden
nasıl yapabiliriz?
Eğer derinlik bilgisini kaybettiysek o zaman resimde bilinen diğer bazı faktörleri
yanyana koyarak bir uzaklık hesaplayabiliriz belki. Mesela alttaki resimdeki
kırmızı piksellerin mesafesini bulmak istiyorum.

from PIL import Image


import util

im = np.array(Image.open('mitte.png'))
plt.xlim(0,320)
plt.ylim(240,0)
plt.imshow(im)
h = np.array(im).shape[0]

np.random.seed(1)
quad = np.array([[140,0],[164,90.],[212,90],[234,0]])
util.plot_quad(quad, h, 'y')
N = 1000
random_points = np.random.uniform(0, 320, (N, 2)).astype(np.int)
random_points = random_points[random_points[:,1] < 240]
mask = np.array([util.inside_quad(quad, p)[0] for p in random_points])
plt.plot(random_points[mask][:,0], h-random_points[mask][:,1], 'r.')
p1 = np.array([215, 180, 1.])
plt.plot(p1[0], p1[1], 'c.')
plt.savefig('vision_80ray_02.png')

Problem öyle ki bu piksellerin yolu temsil eden pikseller olduğunu biliyorum. Bu


bilgiyi nasıl elde ettim? Renksel bazlı, ya da iki boyutta imajı parçalara bölmeyi
çok iyi yapan bir algoritmam var belki, vs. ve bu sayede o piksellerin caddeye ait
oldugunu biliyorum. O zaman, bu bilgi elde varsa, bu bana bir şey kazandırdı:
üç boyutta bu piksellerin hangi düzlemden geldiğini biliyorum artık. Bu düzlem

1
xy düzlemidir, orada z = 0.
Bir numara daha: bir piksele bakarak onun kesin üç boyutlu yerini hesaplaya-
mayabilirim. Ama bir piksele tekabül eden, onu oluşturan kamera merkezinden
dünyaya doğru fırlayan bir ışının (ray) kesin formülünü hesaplayabilirim.

Mesela örnek kırmızı piksellerden biri p1 noktası olabilir, kamera merkezi C’den
bir ışın fırlatıyoruz, bu ışın p1 ’i oluşturuyor ve dış dünyadaki bir X noktasına
doğru gidiyor. Şimdi bu iki fikri biraraya koyarsak, elde bir düzlem, bir çizgi var;
üç boyutlu yer nasıl bulunur? İkisinin kesiştiği yer ile! Bu nokta yol noktasının
üç boyutlu kordinatıdır.
Kamera Merkezi
Bu yazıda kamera merkezinin bilindiğini varsaydık. Ama eğer bilmeseydi, ve
elde sadece P matrisi olsa, kamera merkezini nasıl hesaplarız onu görelim. Biraz
önceki resmi işlerken kameranın yerden 1 metre yükseltilmiş olduğunu farzedeceğiz
(bunu biliyoruz), fakat bazen bu bilgi verilmemiş olabilir. Bu durumda dışsal ma-
tristen başlayabiliriz.
Dışsal (exintrinsic) matrisler dış dünya kordinatlarının kamera kordindatlarına
nasıl transform edildiğini tarif ederler. Bunun yerine kamera duruşunu model-
leyip oradan geriye gidersek aynı noktaya gelmiş oluruz [1].

   −1
R t Rc C
=
0 1 0 1

  −1
I C Rc 0
=
0 1 0 1

 −1  −1
Rc 0 I C
=
0 1 0 1

2
RTc 0
  
I −C
=
0 1 0 1

RTc −RTc C
 
=
0 1

Birbirine tekabül eden hücrelere bakınca

t = −RTc C

O zaman

C = −RTc t

Burada Rc P yansıtma matrisinin ilk üç kolonundan oluşan matristir. Ayrıca kam-
era merkezinin içsel matris K’ye bağlı olmadığına dikkat.
Sözde Ters ile X
Şimdi X bulmak lazım. Bir fikir akla geliyor, PX = x olduğuna göre, P’nin tersini
alıp bu tersi soldan iki tarafla çarpsak olmaz mı (solda P yokolur, X kalır)? Burada
bir problem var, P matrisi 3 × 4 matrisi, kare matris olmadığı için tersi alınamıyor.
Bu hesap için 2. derste işlenen sözde ters (pseudoinverse) işlemini kullanacağız.
Hatırlatarsak, P’nin sözde tersi P+

P+ = PT (PPT )−1

işlemidir, ki PP+ = I. Ama PPT çarpımı sayısal iyi sonuçlar vermeyebilir (çarpımlar
çok büyür), endişeye gerek yok, sayısal kütüphaneler sözde ters işlemini SVD
üzerinden çözüyor (çok hızlı), bkz. 2. ders.
O zaman P+ x ile bahsettiğimiz ışındaki bir noktayı buluruz. Dikkat, sadece birini
buluruz, diğer noktalar da mümkündür. Ama o noktalar bizi ilgilendirmiyor
(şimdilik) elimizde iki nokta olacak, biri kamera merkezi diğeri bu hesaplanacak
olan, bu ikisi yeterli. Ondan önce üstteki hesabın gerçekten bir X verip ver-
mediğini kontrol edelim, hesaplanan noktayı tekrar geri kameraya yansıtırsak
ne olur?

P(P+ x) = Ix = x

Hesap doğruymuş demek ki. Işın hesabı yapalım. Bir önceki resimde p1 ’e ben-
zeyen bir nokta iki üstteki resimde mavi renkli gösterildi. Bu piksele doğru giden
bir çizgi neye benzer?

3
from mpl_toolkits.mplot3d import Axes3D
import scipy.linalg as lin
import sys; sys.path.append('../vision_02')
import plot3d

K = [[ 282.363047, 0., 166.21515189],


[ 0., 280.10715905, 108.05494375],
[ 0., 0., 1. ]]
K = np.array(K)
R = np.eye(3)
t = np.array([[0],[1.],[0]])
P = K.dot(np.hstack((R,t)))
C = np.array([0., 0., 1.])

X = np.dot(lin.pinv(P),p1)
X = X / X[3]
XX = np.copy(X)
XX[1] = X[2]; XX[2] = X[1]; XX[2] = -XX[2]
w = 10
f = plt.figure()
ax = f.gca(projection='3d')
xvec = C - XX[:3]
xvec = -xvec
ax.quiver(C[0], C[1], C[2], xvec[0], xvec[1], xvec[2],color='red')
ax.set_xlim(0,10);ax.set_ylim(0,10);ax.set_zlim(0,10)
ax.quiver(0., 0., 1., 0, 5., 0.,color='blue')
plot3d.plot_plane(ax, [0., 0., 1.], [0, 5., 0.], color='y', size=7)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_xlim(-w,w);ax.set_ylim(-w,w);ax.set_zlim(-w,w)
ax.view_init(elev=5, azim=100)
plt.savefig('vision_80ray_04.png')
ax.view_init(elev=5, azim=50)
plt.savefig('vision_80ray_05.png')

Mavi renkli ok kameranın imaj düzlemine dik (normal) olan vektör. Kırmızı olan
ok p1 ’e işaret eden üç boyutlu çizgi.
Şimdi tüm noktaları yapalım. Altta ilk gösterilen kod iki noktayı baz alan son-
suza giden çizgi ile bir düzlem (bir nokta, bir normal ile tanımlı) arasında kesişmeyi
hesaplayan çağrıdır, bkz [3]. Üstteki gördüğümüz kırmızı renkli pikselleri alıp

4
teker teker onların ışınını bulacağız, sonra bu çizginin xy düzlemi ile kesişmesini
bulacağız. xy düzlemini tanımlamak için bir nokta, bir de normal vektör lazım;
 T
en basit nokta orijin, yani (0, 0, 0), normal ise dik yukarı giden birim vektör 0 0 1 .
Kamera matrisi K’yi biliyoruz, çünkü kamerayı biz kalibre ettik, detaylar için [2].

def intersect(n,V0,P0,P1):
"""
n: duzleme normal vektor
V0: duzlemdeki herhangi bir nokta
P0: P0P1 cizgisinin bir ucu
P1: P0P1 cizgisinin diger ucu
"""
w = P0 - V0;
u = P1-P0;
N = -np.dot(n,w);
D = np.dot(n,u)
sI = N / D
I = P0+ sI*u
return I

import scipy.linalg as lin

xx = np.ones((len(random_points[mask]), 3))
xx[:,0] = random_points[mask][:,0]
xx[:,1] = h-random_points[mask][:,1]

xyp = np.array([0,0,0])
xyn = np.array([0,0,1.])

for x in xx:
X = np.dot(lin.pinv(P),np.array(x))
X = X / X[3]
XX = np.copy(X)
# Y-Z degistir, Y'nin isaretini degistir
XX[1] = X[2]; XX[2] = X[1]; XX[2] = -XX[2]
Xi = intersect(xyn, xyp, XX[:3], C)
plt.plot(Xi[0], Xi[1],'b.')

plt.xlim(-3,3)
plt.ylim(0,20)
plt.savefig('vision_80ray_03.png')

5
Üstteki görüntü kırmızı piksellerin 3 boyutta, caddedeki kuşbakışı görüntüsü.
Noktalar mantıklı, bir sağa kayış var, bu doğru çünkü her ne kadar iki boyutlu
görüntüde noktalar yukarı gidiyor gibi dursa da, aslında kesişme noktasına gi-
den çizginin sağına doğru akmışlar. Bir diğer durum en altta birkaç metrelik bir
kısmın boş olması. Bu da mantıklı çünkü kamera direk altını göremiyor, en yakın
görebildiği noktalar biraz daha önde olanlar.
Peki kameranın duruşunu biliyorum, yere paralel, 1 metre yukarıda, direk düz
ileri bakıyor. Bu bilgiyi kullanarak bir üçgen oluşturup, açılarla ve benzeri şekillerle
daha basit şekilde mesafeyi hesaplayabilirdim, niye bunu yapmadım? Özellikle P
matrisini kullanmamızın sebebi eğer yer değiştirmeyle beraber kamerada dönüş
(rotation) durumu da varsa (bu örnekte yoktu) bu bilginin de P içinde olacağıdır.
Bu durumda üstteki sözde ters ile yine direk bir ışını basit bir şekilde elde ede-
bilirdik. Öteki türlü çetrefil bir sürü ek hesaplara girmek gerekecekti. Yani tarif
ettiğimiz yaklaşımla her türlü kamera duruşunu idare edebiliriz.
Hesapların metrik olarak bir anlamının olduğuna dikkat. Çünkü yerden 1 me-
tre yüksekte olmayı hesabın içine direk dahil ettik, bu sebeple mesela uzaklık
sonuçları, 2.5 metre, 5 metre gibi anlamlı çıktı.
Kaynaklar
[1] Kyle Simek, Dissecting the Camera Matrix, Part 2: The Extrinsic Matrix, http:
//ksimek.github.io/2012/08/22/extrinsic/
[2] Bayramlı, Algılayıcı Ölçümleri, Video, Android, https://burakbayramli.
github.io/dersblog/sk/2017/02/algilayici-olcumleri-video-android.
html
[3] Bayramlı, Çok Boyutlu Calculus, Ders 5

6
Piksel Takibi, Optik Akış, Lucas Kanade Algoritması
Hareket halindeki bir kameranın aldığı görüntülerdeki herhangi bir pikseli nasıl
takip ederiz?
Matematiksel olarak temsil etmek gerekirse, zamana göre değişen 2 boyutlu görüntüyü
bir fonksiyon olarak düşünelim, ki bu fonksiyonun değerleri ayrıksal olarak,
imajın ta kendisi. Bir I(x(t), y(t), t) fonksiyonu piksel değerlerini veriyor. Bu
fonksiyonda x, y ekran kordinatlarına tekabül ediyor, t ise zaman, 1, 2, .. gibi
değerleri indeks değerleri var, mesela I(100, 200, 1), bize 1. video karesindeki
x = 100, y = 200 kordinatlarındaki piksel değerini verecek.
x, y değişkenleri parametrize edildi, bir noktayı takip etmek istiyoruz çünkü,
ve t’ye göre bu takip edilen noktanın x, y kordinatları belli bir gidişat yönünde
değişiyor.
Şu faraziyeyi yaparak takip problemimizi kolaylaştırabiliriz. Diyelim ki takip
edilen bir nokta, görüldüğü her karede aynı piksel rengindedir. Bu çok sıradışı
bir faraziye değil, resim karelerinden bir araba geçiyor mesela, ve bu arabanın
üzerindeki piksellerin renkleri, en azından iki kare arasında değişmiyor. Işık se-
viyesi, gölgede olma, vs. gibi durumlarda biraz değişebilir, fakat basitleştirme
amacıyla bu faraziye geçerlidir.

Bir diğer faraziye, kameralar hareket ettiklerinde alınan iki görüntü arasındaki
tüm piksellerin yer değişimi genellikle aynı yönde olmasıdır. Bu değişim yönünü
< u, v > vektörü olarak görebiliriz, ve bu değişkenler iki görüntü arasındaki
değişimde tüm pikseller için aynı olacaktır. Bu da normal, kamerayı alıp mesela
sağa doğru hareket ettiriyoruz, ve görüntüdeki tüm pikseller sola doğru gidiyor-
lar.

Tüm bunları modelimizde nasıl kullanırız?

1
Takip edilen nokta her karede aynı renkte ise, şu ifade doğru demektir

I(x(t), y(t), t) = sabit

Eğer bu fonksiyonun zamana göre türevini alırsak

d I(x(t), y(t), t)
=0
dt

sonucu gelir. Eşitliğin sağı sıfır, çünkü bir sabitin türevini aldık. Sol tarafa Zincir-
leme Kanununu uygularsak,

∂I dx ∂I dy ∂I
+ + =0
∂x dt ∂y dt ∂t

Bu formülde dx/dt ve dy/dt, hareket halindeki (zaman geçerken) noktanın son-


suz küçüklükteki yer değimi. Ayrıksal bağlamda arka arkaya iki kare içindeki
yer değişimi. O zaman,

dx dy
, = u, v
dt dt

Alttakiler ise mesafesel (spatial) gradyanlardır, bunların nasıl hesaplanacağını


çok iyi biliyoruz!

∂I ∂I
,
∂x ∂y

Alttaki ise resim karelerinin zamana göre türevidir.

∂I
∂t

Daha derli toplu olarak göstermek gerekirse ana formül nihai olarak şöyle

Ix u + Iy v + It = 0

ya da

∇I· < u, v >= −It

Şimdi u, v’nin hesaplanmasına gelelim. Üstteki formülü bir veri noktası için yaz-
mak yeterli değil. Ama bu formülü hem takip ettiğimiz, hem de onun etrafındaki

2
pikseller için yazarsak (onların yer değişimi de aynı değil mi?), ve bu sistemi
çözersek, sonuca varabiliriz.
İki tane bilinmeyenimiz var, ama böylece pek çok formül elde ediyoruz. Veriler
gürültülü olduğu için, aslında bilinmeyenden ”daha fazla” formül elde etmek iyi,
bu tür denklem sistemlerine ”çok eşitliğe sahip (overdetermined)” denir, ve böyle
tür sistemler En Az Kareler (Least Squares) ile çözülür. Tüm bunları biraraya
koyunca şu ortaya çıkar.
   
Ix (p1 ) Iy (p1 ) It (p1 )
 Ix (p2 ) Iy (p1 )  
 u
 It (p2 ) 
= −
  
.. .. ..
 v
  
 . .  . 
Ix (pk ) Iy (pk ) It (pk )

Gradyanların belli noktalarda hesaplandığını unutmayalım, o sebeple p1 , p2 gibi


piksel noktalarını bu fonksiyonlara geçiyoruz.
Bu sistemi

Ad=b

olarak gösterebiliriz, ki d =< u, v >. Sol tarafı AT ile çarpalım

AT A d = AT b

Eğer AT A’nin matris tersini iki tarafla çarparsak, d yanlız kalır, ve sonuç elde
edilir.
Bu denklemi Python Numpy’da pinv kullanarak çözeriz.
Test için üç tane resim kullandık, bu resimlerden flow1-bw-0.png başlangıç resmi,
bu resmin ortasındaki objeleri GIMP kullanarak elle kopyaladık, bir üst sağ çapraza
doğru, bir alt sol çapraza doğru, ve iki yeni resim elde ettik (upright.png, dleft.png).
Takip edilen nokta gri dörtgenin alt sol köşesinde. Lucas Kanade algoritması bu
noktayı takip ederek, yeşil ile işaretledi.

import scipy.signal as si
from PIL import Image

def gauss_kern():
h1 = 15
h2 = 15
x, y = np.mgrid[0:h2, 0:h1]
x = x-h2/2
y = y-h1/2
sigma = 1.5
g = np.exp( -( x**2 + y**2 ) / (2*sigma**2) );
return g / g.sum()

3
def deriv(im1, im2):
g = gauss_kern()
Img_smooth = si.convolve(im1,g,mode='same')
fx,fy=np.gradient(Img_smooth)
ft = si.convolve2d(im1, 0.25 * np.ones((2,2))) + \
si.convolve2d(im2, -0.25 * np.ones((2,2)))

fx = fx[0:fx.shape[0]-1, 0:fx.shape[1]-1]
fy = fy[0:fy.shape[0]-1, 0:fy.shape[1]-1];
ft = ft[0:ft.shape[0]-1, 0:ft.shape[1]-1];
return fx, fy, ft

import warnings
warnings.simplefilter("ignore", np.ComplexWarning)

im1 = np.asarray(Image.open('flow1-bw-0.png'))
im2 = np.asarray(Image.open("upright.png"))
fx, fy, ft = deriv(im1, im2)
print fx[:5]

[[ 34.37477011 45.94010835 51.877951 ..., 53.83264716 51.877951


45.94010835]
[ 26.01168277 34.76327322 39.25648957 ..., 40.73562489 39.25648957
34.76327322]
[ 11.72919465 15.67546405 17.70154632 ..., 18.36851839 17.70154632
15.67546405]
[ 3.51803959 4.70167857 5.30937909 ..., 5.50942984 5.30937909
4.70167857]
[ 0.6961225 0.93033183 1.05057892 ..., 1.09016341 1.05057892
0.93033183]]

import scipy.signal as si
from PIL import Image
import numpy.linalg as lin

def lk(im1, im2, i, j, window_size) :


fx, fy, ft = deriv(im1, im2)
halfWindow = np.floor(window_size/2)
curFx = fx[i-halfWindow-1:i+halfWindow,
j-halfWindow-1:j+halfWindow]
curFy = fy[i-halfWindow-1:i+halfWindow,
j-halfWindow-1:j+halfWindow]
curFt = ft[i-halfWindow-1:i+halfWindow,
j-halfWindow-1:j+halfWindow]
curFx = curFx.T
curFy = curFy.T
curFt = curFt.T

curFx = curFx.flatten(order='F')
curFy = curFy.flatten(order='F')
curFt = -curFt.flatten(order='F')

A = np.vstack((curFx, curFy)).T
U = np.dot(np.dot(lin.pinv(np.dot(A.T,A)),A.T),curFt)
return U[0], U[1]

4
def test(image1,image2,output):
x=165
y=95
win=50
im1 = np.asarray(Image.open(image1))
im2 = np.asarray(Image.open(image2))
u, v = lk(im1, im2, x, y, win)
plt.imshow(im1, cmap='gray')
plt.hold(True)
plt.plot(x,y,'+r');
# 3 ile carptik cunku vektor degisimi iyi hesaplandi ama
# grafikleme icin cok ufakti, ikinci yesil nokta iyi gozuksun
# diye onu biraz buyuttuk
plt.plot(x+u*3,y+v*3,'og')
plt.savefig(output)

test('flow1-bw-0.png','dleft.png','lk_1.png')

test('flow1-bw-0.png','upright.png', 'lk_2.png')

Bu matematiksel modele alternatif bir bakış şöyle olabilir. İki imaj karesi içinde
birincisine I(x, y) ikincisine H(x, y) diyelim, burada t üzerinden parametrizasyon
olmasın; x, y pikselinin H içinde u, v kadar yer değişiminden sonra, bu noktaların

5
I’de geldiği yerdeki grilik değerinin aynı olduğunu (yine) farzediyoruz. Sonra
I(x + u, y + v)’nin birinci dereceden Taylor Açılımını yapıyoruz,

∂I ∂I
I(x + u, y + v) = I(x, y) + u+ v + ...
∂x ∂y

ya da

∂I ∂I
I(x + u, y + v) ≈ I(x, y) + u+ v
∂x ∂y

Grilik aynılığını ise şöyle belirtebiliriz

I(x + u, y + v) − H(x, y) = 0

Taylor açılımını üstteki formülde I yerine geçirelim

∂I ∂I
I(x, y) + u+ v − H(x, y) = 0
∂x ∂y

H’in yerini değiştirelim

I(x, y) − H(x, y) + Ix u + Iy v = 0

Şu ifade I(x, y) − H(x, y) nedir? Bunlar iki imajın, sonrası ve öncesi arasındaki
fark değil midir? O zaman bu hesabı imajın zamana göre alınmış türevi olarak
görebiliriz, yani It = I(x, y) − H(x, y). Yerine koyalım

It + Ix u + Iy v = 0

Ix u + Iy v = −It

Böylece aynı denkleme erişmiş olduk. Bu aslında normal, birinci dereceden Tay-
lor açılımı ile tam diferansiyel denklemi (ve Zincirleme Kanununu) birbiriyle çok
yakından alakalı.
Ufak Piksel Değişimleri
Konu hakkında bir nokta daha şu; Lucas-Kanade yöntemi 1. derece Taylor açılımı
kulladığı için ufak piksel değişimleri için geçerlidir, çünkü Taylor açılımı yerel
bir noktaya çok yakın bölgelerde bir fonksiyona yakın sonuçlar verir. Bu da
aklımızda bulunsun.e
OpenCV
OpenCV ile optik akış kullanımı altta görülüyor.

6
import pandas as pd, zipfile
import numpy as np
import cv2

def draw_flow(img, flow, step=16):


h, w = img.shape[:2]
y, x = np.mgrid[step/2:h:step, step/2:w:step].reshape(2,-1).astype(int)
fx, fy = flow[y,x].T
lines = np.vstack([x, y, x+fx, y+fy]).T.reshape(-1, 2, 2)
lines = np.int32(lines + 0.5)
vis = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.polylines(vis, lines, 0, (0, 255, 0))
for (x1, y1), (x2, y2) in lines:
cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1)
return vis

prevgray = cv2.imread('106.jpg', cv2.IMREAD_GRAYSCALE)


gray = cv2.imread('107.jpg', cv2.IMREAD_GRAYSCALE)
flow = cv2.calcOpticalFlowFarneback(prevgray, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
cv2.imwrite('pde_lk_01.png', draw_flow(gray, flow))

Kaynaklar
[1] Collins, Introduction to Computer Vision, http://www.cse.psu.edu/˜rtc12/
CSE486/
[2] Khurram Hassan-Shafique, CAP 5415 Lecture Notes, Spring 2003
[3] Suhr, Kanade-Lucas-Tomasi (KLT) Feature Tracker Feature Tracker, http://web.
yonsei.ac.kr/jksuhr/articles/Kanade-Lucas-Tomasi%20Tracker.pdf

7
Piksel Takibi, Optik Akış, Lucas Kanade Algoritması
Hareket halindeki bir kameranın aldığı görüntülerdeki herhangi bir pikseli nasıl
takip ederiz?
Matematiksel olarak temsil etmek gerekirse, zamana göre değişen 2 boyutlu görüntüyü
bir fonksiyon olarak düşünelim, ki bu fonksiyonun değerleri ayrıksal olarak,
imajın ta kendisi. Bir I(x(t), y(t), t) fonksiyonu piksel değerlerini veriyor. Bu
fonksiyonda x, y ekran kordinatlarına tekabül ediyor, t ise zaman, 1, 2, .. gibi
değerleri indeks değerleri var, mesela I(100, 200, 1), bize 1. video karesindeki
x = 100, y = 200 kordinatlarındaki piksel değerini verecek.
x, y değişkenleri parametrize edildi, bir noktayı takip etmek istiyoruz çünkü,
ve t’ye göre bu takip edilen noktanın x, y kordinatları belli bir gidişat yönünde
değişiyor.
Şu faraziyeyi yaparak takip problemimizi kolaylaştırabiliriz. Diyelim ki takip
edilen bir nokta, görüldüğü her karede aynı piksel rengindedir. Bu çok sıradışı
bir faraziye değil, resim karelerinden bir araba geçiyor mesela, ve bu arabanın
üzerindeki piksellerin renkleri, en azından iki kare arasında değişmiyor. Işık se-
viyesi, gölgede olma, vs. gibi durumlarda biraz değişebilir, fakat basitleştirme
amacıyla bu faraziye geçerlidir.

Bir diğer faraziye, kameralar hareket ettiklerinde alınan iki görüntü arasındaki
tüm piksellerin yer değişimi genellikle aynı yönde olmasıdır. Bu değişim yönünü
< u, v > vektörü olarak görebiliriz, ve bu değişkenler iki görüntü arasındaki
değişimde tüm pikseller için aynı olacaktır. Bu da normal, kamerayı alıp mesela
sağa doğru hareket ettiriyoruz, ve görüntüdeki tüm pikseller sola doğru gidiyor-
lar.

Tüm bunları modelimizde nasıl kullanırız?

1
Takip edilen nokta her karede aynı renkte ise, şu ifade doğru demektir

I(x(t), y(t), t) = sabit

Eğer bu fonksiyonun zamana göre türevini alırsak

d I(x(t), y(t), t)
=0
dt

sonucu gelir. Eşitliğin sağı sıfır, çünkü bir sabitin türevini aldık. Sol tarafa Zincir-
leme Kanununu uygularsak,

∂I dx ∂I dy ∂I
+ + =0
∂x dt ∂y dt ∂t

Bu formülde dx/dt ve dy/dt, hareket halindeki (zaman geçerken) noktanın son-


suz küçüklükteki yer değimi. Ayrıksal bağlamda arka arkaya iki kare içindeki
yer değişimi. O zaman,

dx dy
, = u, v
dt dt

Alttakiler ise mesafesel (spatial) gradyanlardır, bunların nasıl hesaplanacağını


çok iyi biliyoruz!

∂I ∂I
,
∂x ∂y

Alttaki ise resim karelerinin zamana göre türevidir.

∂I
∂t

Daha derli toplu olarak göstermek gerekirse ana formül nihai olarak şöyle

Ix u + Iy v + It = 0

ya da

∇I· < u, v >= −It

Şimdi u, v’nin hesaplanmasına gelelim. Üstteki formülü bir veri noktası için yaz-
mak yeterli değil. Ama bu formülü hem takip ettiğimiz, hem de onun etrafındaki

2
pikseller için yazarsak (onların yer değişimi de aynı değil mi?), ve bu sistemi
çözersek, sonuca varabiliriz.
İki tane bilinmeyenimiz var, ama böylece pek çok formül elde ediyoruz. Veriler
gürültülü olduğu için, aslında bilinmeyenden ”daha fazla” formül elde etmek iyi,
bu tür denklem sistemlerine ”çok eşitliğe sahip (overdetermined)” denir, ve böyle
tür sistemler En Az Kareler (Least Squares) ile çözülür. Tüm bunları biraraya
koyunca şu ortaya çıkar.
   
Ix (p1 ) Iy (p1 ) It (p1 )
 Ix (p2 ) Iy (p1 )  
 u
 It (p2 ) 
= −
  
.. .. ..
 v
  
 . .  . 
Ix (pk ) Iy (pk ) It (pk )

Gradyanların belli noktalarda hesaplandığını unutmayalım, o sebeple p1 , p2 gibi


piksel noktalarını bu fonksiyonlara geçiyoruz.
Bu sistemi

Ad=b

olarak gösterebiliriz, ki d =< u, v >. Sol tarafı AT ile çarpalım

AT A d = AT b

Eğer AT A’nin matris tersini iki tarafla çarparsak, d yanlız kalır, ve sonuç elde
edilir.
Bu denklemi Python Numpy’da pinv kullanarak çözeriz.
Test için üç tane resim kullandık, bu resimlerden flow1-bw-0.png başlangıç resmi,
bu resmin ortasındaki objeleri GIMP kullanarak elle kopyaladık, bir üst sağ çapraza
doğru, bir alt sol çapraza doğru, ve iki yeni resim elde ettik (upright.png, dleft.png).
Takip edilen nokta gri dörtgenin alt sol köşesinde. Lucas Kanade algoritması bu
noktayı takip ederek, yeşil ile işaretledi.

import scipy.signal as si
from PIL import Image

def gauss_kern():
h1 = 15
h2 = 15
x, y = np.mgrid[0:h2, 0:h1]
x = x-h2/2
y = y-h1/2
sigma = 1.5
g = np.exp( -( x**2 + y**2 ) / (2*sigma**2) );
return g / g.sum()

3
def deriv(im1, im2):
g = gauss_kern()
Img_smooth = si.convolve(im1,g,mode='same')
fx,fy=np.gradient(Img_smooth)
ft = si.convolve2d(im1, 0.25 * np.ones((2,2))) + \
si.convolve2d(im2, -0.25 * np.ones((2,2)))

fx = fx[0:fx.shape[0]-1, 0:fx.shape[1]-1]
fy = fy[0:fy.shape[0]-1, 0:fy.shape[1]-1];
ft = ft[0:ft.shape[0]-1, 0:ft.shape[1]-1];
return fx, fy, ft

import warnings
warnings.simplefilter("ignore", np.ComplexWarning)

im1 = np.asarray(Image.open('flow1-bw-0.png'))
im2 = np.asarray(Image.open("upright.png"))
fx, fy, ft = deriv(im1, im2)
print fx[:5]

[[ 34.37477011 45.94010835 51.877951 ..., 53.83264716 51.877951


45.94010835]
[ 26.01168277 34.76327322 39.25648957 ..., 40.73562489 39.25648957
34.76327322]
[ 11.72919465 15.67546405 17.70154632 ..., 18.36851839 17.70154632
15.67546405]
[ 3.51803959 4.70167857 5.30937909 ..., 5.50942984 5.30937909
4.70167857]
[ 0.6961225 0.93033183 1.05057892 ..., 1.09016341 1.05057892
0.93033183]]

import scipy.signal as si
from PIL import Image
import numpy.linalg as lin

def lk(im1, im2, i, j, window_size) :


fx, fy, ft = deriv(im1, im2)
halfWindow = np.floor(window_size/2)
curFx = fx[i-halfWindow-1:i+halfWindow,
j-halfWindow-1:j+halfWindow]
curFy = fy[i-halfWindow-1:i+halfWindow,
j-halfWindow-1:j+halfWindow]
curFt = ft[i-halfWindow-1:i+halfWindow,
j-halfWindow-1:j+halfWindow]
curFx = curFx.T
curFy = curFy.T
curFt = curFt.T

curFx = curFx.flatten(order='F')
curFy = curFy.flatten(order='F')
curFt = -curFt.flatten(order='F')

A = np.vstack((curFx, curFy)).T
U = np.dot(np.dot(lin.pinv(np.dot(A.T,A)),A.T),curFt)
return U[0], U[1]

4
def test(image1,image2,output):
x=165
y=95
win=50
im1 = np.asarray(Image.open(image1))
im2 = np.asarray(Image.open(image2))
u, v = lk(im1, im2, x, y, win)
plt.imshow(im1, cmap='gray')
plt.hold(True)
plt.plot(x,y,'+r');
# 3 ile carptik cunku vektor degisimi iyi hesaplandi ama
# grafikleme icin cok ufakti, ikinci yesil nokta iyi gozuksun
# diye onu biraz buyuttuk
plt.plot(x+u*3,y+v*3,'og')
plt.savefig(output)

test('flow1-bw-0.png','dleft.png','lk_1.png')

test('flow1-bw-0.png','upright.png', 'lk_2.png')

Bu matematiksel modele alternatif bir bakış şöyle olabilir. İki imaj karesi içinde
birincisine I(x, y) ikincisine H(x, y) diyelim, burada t üzerinden parametrizasyon
olmasın; x, y pikselinin H içinde u, v kadar yer değişiminden sonra, bu noktaların

5
I’de geldiği yerdeki grilik değerinin aynı olduğunu (yine) farzediyoruz. Sonra
I(x + u, y + v)’nin birinci dereceden Taylor Açılımını yapıyoruz,

∂I ∂I
I(x + u, y + v) = I(x, y) + u+ v + ...
∂x ∂y

ya da

∂I ∂I
I(x + u, y + v) ≈ I(x, y) + u+ v
∂x ∂y

Grilik aynılığını ise şöyle belirtebiliriz

I(x + u, y + v) − H(x, y) = 0

Taylor açılımını üstteki formülde I yerine geçirelim

∂I ∂I
I(x, y) + u+ v − H(x, y) = 0
∂x ∂y

H’in yerini değiştirelim

I(x, y) − H(x, y) + Ix u + Iy v = 0

Şu ifade I(x, y) − H(x, y) nedir? Bunlar iki imajın, sonrası ve öncesi arasındaki
fark değil midir? O zaman bu hesabı imajın zamana göre alınmış türevi olarak
görebiliriz, yani It = I(x, y) − H(x, y). Yerine koyalım

It + Ix u + Iy v = 0

Ix u + Iy v = −It

Böylece aynı denkleme erişmiş olduk. Bu aslında normal, birinci dereceden Tay-
lor açılımı ile tam diferansiyel denklemi (ve Zincirleme Kanununu) birbiriyle çok
yakından alakalı.
Ufak Piksel Değişimleri
Konu hakkında bir nokta daha şu; Lucas-Kanade yöntemi 1. derece Taylor açılımı
kulladığı için ufak piksel değişimleri için geçerlidir, çünkü Taylor açılımı yerel
bir noktaya çok yakın bölgelerde bir fonksiyona yakın sonuçlar verir. Bu da
aklımızda bulunsun.e
OpenCV
OpenCV ile optik akış kullanımı altta görülüyor.

6
import pandas as pd, zipfile
import numpy as np
import cv2

def draw_flow(img, flow, step=16):


h, w = img.shape[:2]
y, x = np.mgrid[step/2:h:step, step/2:w:step].reshape(2,-1).astype(int)
fx, fy = flow[y,x].T
lines = np.vstack([x, y, x+fx, y+fy]).T.reshape(-1, 2, 2)
lines = np.int32(lines + 0.5)
vis = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.polylines(vis, lines, 0, (0, 255, 0))
for (x1, y1), (x2, y2) in lines:
cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1)
return vis

prevgray = cv2.imread('106.jpg', cv2.IMREAD_GRAYSCALE)


gray = cv2.imread('107.jpg', cv2.IMREAD_GRAYSCALE)
flow = cv2.calcOpticalFlowFarneback(prevgray, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
cv2.imwrite('pde_lk_01.png', draw_flow(gray, flow))

Kaynaklar
[1] Collins, Introduction to Computer Vision, http://www.cse.psu.edu/˜rtc12/
CSE486/
[2] Khurram Hassan-Shafique, CAP 5415 Lecture Notes, Spring 2003
[3] Suhr, Kanade-Lucas-Tomasi (KLT) Feature Tracker Feature Tracker, http://web.
yonsei.ac.kr/jksuhr/articles/Kanade-Lucas-Tomasi%20Tracker.pdf

7
Ekler
Yunan Harfleri

You might also like