Professional Documents
Culture Documents
https://burakbayramli.github.io/dersblog/sk/
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
B 0 = BA ⇐⇒ B = B 0 A−1
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,
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,
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]
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
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
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
1 0
0 −1
L : Rn → Rn ; x → Rx + T
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
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
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.
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
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]
1
vTi Svj = λi vTi vj
λj vTi vj = λi vTi vj
(λj − λi )vTi vj = 0
0 2
−2 0
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
û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
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,
Neden?
O zaman,
= 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
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,
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
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,
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
vx = n̂ × v = Nv
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,
7
vk = v − v⊥ = v + vxx = v + N2 v = (I + N2 )v
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⊥
u = u⊥ + vk
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]
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)
9
normal = np.roll((1.0,0,0), index)
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
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,
= VΣ−1 UT
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
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
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,
1
hu, vi = (|u + v|2 − |u − v|2 )
4
üç 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
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
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,
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) = ŵ(t)R(t)
Ṙ(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
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,
R(0) = I
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!
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ŵ
r32 − r23
trace(R) − 1 w 1
|w| = cos−1 , = r13 − r31
2 |w| 2 sin |w|
r21 − r12
ŵ ŵ2
eŵ = I + sin |w| + (1 − cos |w|)
|w| |w|2
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
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!
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,
R T
SE(3) ≡ g = R ∈ SO(3), T ∈ R ⊂ R4×4
3
0 1
tx
R ty
g=
tz
0 0 0 1
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
ġ = ġ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
∧
v ŵ v
ξ̂ ≡ = ∈ R4×4
w 0 0
∨
ŵ v v
= ∈ R6
0 0 w
ġ(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.
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,
R(t) T (t)
g(t) = ∈ SE(3)
0 1
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
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
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
−1 ŵ(t) v(t)
V̂(t) ≡ ġg (t) = ∈ se(3)
0 0
Ẋ(t) = V̂(t)X(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,
4
Yani birinci kameradaki olanın ikinci kamerada görülen izafi hızı alttaki fırıldak
ile hesaplanır,
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,
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.
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.
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
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
λx = Kf Π0 gX0
λ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
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,
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
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
λi xi = PXi
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')
4
Mv = 0
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
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
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)
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
xT2 Ex1 = aT Es = 0
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
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̂
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')
x 0 = Hx
1
baz alır,
x0
h1 h2 h3 x
y 0 h3 h4 h5 y
w0 h6 h7 h8 w
x0
a1 a2 t x x
y 0 a3 a4 t y y
1 0 0 1 1
0 A t
x = x
0 1
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
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
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))
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]
Ç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,
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.
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))
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);
zt = αgt + (1 − α)zt−1
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.
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')
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
` = (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
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
p = np.cross(l1,l2)
print p / p[2]
[-2 6 1]
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
[-4 -7 19]
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
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.
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
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)
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).
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
x 0T Fx = 0
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")
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
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)
pts1 = np.array(pts1)
pts2 = np.array(pts2)
3
h,w,dum = img1.shape
pts1[:,1] = h-pts1[:,1]
pts2[:,1] = h-pts2[:,1]
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.
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]
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]])
Üç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
x(p3T X) − (p1T X) = 0
y(p3T X) − (p2T X) = 0
x(p2T X) − (p1T X) = 0
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,
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
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.
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],
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
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
p(g1 , g2 , ..|H1 )
L=
p(g1 , g2 , ..|H0 )
3
σm1 +m2
=
σm
1 σ2
m
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
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,
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
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
!−1
X
n X
n
zTi zk zTk zi
i=1 k=1
!−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
p({x}|H1 )
L=
p({x}|H0 )
Cm
0
1 +m2
L=
Cm 1
1 C2
m2
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.
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
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')
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
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:
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
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ı.
1
w[i] =
1 + (y[i] − p[i] )2 )
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.
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
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.
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
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,
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,
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,
5
from PIL import Image, ImageDraw
import pandas as pd, zipfile
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)
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.
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')
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
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
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
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.
1
Takip edilen nokta her karede aynı renkte ise, şu ifade doğru demektir
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
dx dy
, = u, v
dt dt
∂I ∂I
,
∂x ∂y
∂I
∂t
Daha derli toplu olarak göstermek gerekirse ana formül nihai olarak şöyle
Ix u + Iy v + It = 0
ya da
Ş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 )
Ad=b
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]
import scipy.signal as si
from PIL import Image
import numpy.linalg as lin
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
I(x + u, y + v) − H(x, y) = 0
∂I ∂I
I(x, y) + u+ v − H(x, y) = 0
∂x ∂y
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
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.
1
Takip edilen nokta her karede aynı renkte ise, şu ifade doğru demektir
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
dx dy
, = u, v
dt dt
∂I ∂I
,
∂x ∂y
∂I
∂t
Daha derli toplu olarak göstermek gerekirse ana formül nihai olarak şöyle
Ix u + Iy v + It = 0
ya da
Ş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 )
Ad=b
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]
import scipy.signal as si
from PIL import Image
import numpy.linalg as lin
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
I(x + u, y + v) − H(x, y) = 0
∂I ∂I
I(x, y) + u+ v − H(x, y) = 0
∂x ∂y
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
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