Derinlik I: Stereo Kalibrasyon ve Düzeltme
Merhaba arkadaşlar. Bugün sizinle stereo kamera nedir ve nasıl kullanıyoruz sorusu üzerine yoğunlaşacağız. Sizler için olabildiğince temiz hazırlamaya çalıştığım kod üzerinden stereo kamera nasıl kalibre ediliyor ve derinlik bilgisi nasıl ölçülebiliyor gibi soruları da cevaplamış olacağım. Her zamanki gibi matematik kısmında bir iddiam veya anlatımım olmayacak, openCV dökümanı üzerinden konunun daha derinine inebilirsiniz.
Derinliği algılayabilmemizin temel sebebi mükemmel hizalanmış iki gözümüzdür. Farkettiyseniz yakın objelere tek gözle bakarsak sağ ve sol gözümüzle algıladığımız obje lokasyonu farklı olacak. Fakat uzak objelere bakarsanız neredeyse hiç bir fark görmeyeceksiniz. Sağ ve sol gözümüz arasındaki bu ilişki bizim otonom olarak derinliği algılamamıza sebeptir. Ördekler gibi gözleri yanlarda olan hayvanlar ise gözleri ortak bir yeri görmediği için derinlik algısı oluşturmak amacıyla sürekli kafalarını hareket ettirirler veya koşarlar. Buna da hareketten yapılanma(structure from motion) deniyor. Bu yöntemi şimdilik bir kenara bırakalım ve kendi gözlerimize benzeyen bir sistem düşünelim.
Eğer iki kamera, dikey düzlemde farklı fakat yatay düzlemde aynı hizada olursa( insan gözü gibi ) tahmin edebileceğiniz gibi gördüğümüz objenin bir noktası iki kamerada da aynı yatay eksene düşecektir. Objelerin yakınlığından ve uzaklığından derinlik konusunda bir bilgi sahibi olabiliyorduk. İki görüntüde aynı noktanın nerelere denk geldiğini aramak büyük bir yük olduğundan yatay eksenleri eşlediğimizde sadece yatay eksende arayarak büyük bir yükten kurtulmuş oluruz. Buna stereo kalibrasyon ve düzeltme(rectification) deniyor. Öyle bir kalibrasyon olsun ki kameralardan aldığımız görüntüler gürültüden arındırılabilsin ve eksenleri aynı olsun. Bu durumda yukarıdaki şekilde gözükeceği üzere bir P noktasının görüntülerdeki karşılığını göreceli olarak az bir işlem gücüyle bulabiliyoruz. Bundan sonrası da basitçe piksellerin arasındaki farklar ile yeni bir görüntü oluşturmaktan ibaret. Kod kısmından bahsederken ayrıntılara gireceğim. Görsel olarak sonucu şu şekilde özetleyebiliriz:
İlk başta yapmamız gereken kameraları yatay eksenleri düzgün ve sabit olarak monte etmek. Bunun için iki kamerayı tahta bir düzleme yapıştırabilirsiniz. Ayrıca Intel realsense, Zed gibi küçük hata paylarıyla yatay eksene özellikle sabitlenmiş kameralar da alabilirsiniz. Sonraki aşama ise iki kamerayı da tek tek kalibre etmek. Bunun için kalibrasyon yazımı okuyabilirsiniz, buraya referanslarım olacak. Stereo kalibrasyon için, tek kamera kalibrasyonuna benzer bir yöntem uyguluyoruz. Satranç tahtamızın görüntülerini iki kamerayla da alacağız:
Bu görüntüler bize iki kamera arasındaki ilişkiyi verecek. Görüntüleri alırken sabit durmalısınız, sol ve sağ görüntüler senkronize olmalı. Bunun için opencv ile grab&retreive metotlarını kullanarak olabildiğince yakın senkronizasyonda görüntü almaya çalışabilirsiniz. Görüntüleri almak için hazırladığım kodu kullanabilirsiniz. Yeterince görüntü aldığınıza inanıyorsanız şuradaki kod ile kalibrasyonu gerçekleştirebilirsiniz. Bu kodun yaptığı iki görev:
- Stereo calibration: İki kamera arasındaki ilişki ve matrisin çıkarımı.
- Stereo rectification: İki kameranın gürültüden arındırılmış görüntülerini doğru açıda çevirip kırparak yatay düzlemde eşit hale getirmek.
OpenCV stereo calibration fonksiyonu:
ret, K1, D1, K2, D2, R, T, E, F = cv2.stereoCalibrate(objp, leftp, rightp, K1, D1, K2, D2, image_size, criteria, flag)
stereoCalibrate fonksiyonunda üzerinde dikkat etmemiz gereken şey flag kısmı. Flag değerlerini görelim:
- CV_CALIB_FIX_INTRINSIC: K ve D olarak geçen kamera matrisi ve gürültü katsayıları üzerinde bir değişiklik yapmaz. Default değerdir. Daha önceden kamerayı iyi kalibre ettiğinizde bu değer sizin için önemli olacak. Sadece aradaki ilişkiyi veren matrisleri alsanız yetebiliyor.
- CV_CALIB_USE_INTRINSIC_GUESS: K ve D matrislerini optimize eder. Başlangıçtaki değerlerin mantıklı olması gerekiyor, hesaplanmış olmalı ki optimize edilebilsin.
- CV_CALIB_FIX_PRINCIPAL_POINT: K matrisinde bulunan referans noktasını sabitler.
- CV_CALIB_FIX_FOCAL_LENGTH: K matrisinde bulunan odak uzaklığını sabitler.
- CV_CALIB_FIX_ASPECT_RATIO: Odak uzaklıkları arasındaki oranı sabitler.
- CV_CALIB_SAME_FOCAL_LENGTH: Odak uzaklıklarını optimize eder ve fx, fy odak uzaklıkları eşit olur. Burada farklı modeller veya durumlardaki kameraları kullanırsanız hata payına sebep olabileceğini düşünmekteyim.
- CV_CALIB_ZERO_TANGENT_DIST: Gürültü katsayılarını sıfırlayarak sabitler.
- CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6: Gürültü katsayılarını ilkten sona kadar sabitlemek için kullanılır. En önemli flag değerlerindendir. Matematiksel olarak yorumlayamasam da opencv forumunda sorduğum sorular ve yaptığım sayısız(gerçekten sayısız) kalibrasyon denemelerinde edindiğim tecrübeyle söylemekteyim.
Sonuçta elde ettiğimiz R, T, E, F katsayıları iki kamera arasındaki ilişkiyi vermekte. Bundan sonraki aşama düzeltme(rectification):
R1, R2, P1, P2, Q, roi_left, roi_right = cv2.stereoRectify(K1, D1, K2, D2, image_size, R, T, flags=cv2.CALIB_ZERO_DISPARITY, alpha=0.9)
Fark etmişsinizdir ki stereoCalibrate fonksiyonundan edindiğimiz R ve T matrislerini, kamera ve gürültü matrislerimizi görüntü çözünürlüğü ile iletiyoruz. Burada flag olarak tek değer var, o da CALIB_ZERO_DISPARITY. Tahmin edeceğiniz gibi bu flag aktif olmalı, yatay eksenleri eşlemek için kullanıyoruz. Alpha değeri ise, görüntülerin ekseni eşlenirken uygulanacak transformasyon sonucu oluşacak siyah kısımları ayarlamamıza yardımcı oluyor. Değeri ben kendi kamera göre ayarladım. Ayarlar şu şekilde:
- alpha=-1 -> OpenCV siyah kısımları optimize etsin.
- alpha= 0 ->Hiç siyah kısım kalmayacak şekilde görüntüyü döndür ve kes.
- alpha= 1 -> Görüntünün transformasyonunu gerçekleştir ve hiç kesme işlemi yapma:
- alpha=deneysel -> Bazen değerlerin hiç biri işe yaramaz. Bu durumda sizin çeşitli değerlerle kesme işlemi yapmanız gerek. Ayrıca biraz siyah kısımları görüp görüntünün çok kesilmesini istemiyorsanız bu yöntemi tercih edebilirsiniz. Bazen en güzel görüntüyü 0.9756 gibi değerlerde buldum, asla ümidinizi yitirmeyin derim.
Dönen değerlerimizden R ve P matrisleri rotation ve projection matrisleri olarak geçiyor. R1 ve P1, ilk kameranın(sol) ikinci kameraya(sağ) göre rotasyonu ve pozisyonunu veriyor. R2 ve P2 ise tam tersi. Q matrisi ise disparity görüntüsünden derinlik haritasına geçmemizi sağlıyor. İçinde kamera arası uzaklık, odak uzaklığı gibi değerler var. Derinlik haritasına geçtiğinizde satranç tahtasının ölçümünü hangi metrikle yaptıysanız bu derinlik değerlerini ona göre alacağınızı hatırlatmak isterim. Sonra ben 5 metre hesaplıyorum bu 7.8 metre diyor demeyin. Çok fazla parametrenin olduğunu ve sorunun iyi teşhis edilmesi gerektiğini unutmayalım.
Şimdi geldik bu güzel matrislerin hayattaki amacına. Test kodumuzu incelediğimizde( Derinlik hesaplama yöntemlerini sonra işleyeceğiz, main fonskiyonuna bakmanız yeterli ) düzeltme işlemini gürültü engelleme ile beraber yapıyoruz:
leftMapX, leftMapY = cv2.initUndistortRectifyMap(K1, D1, R1, P1, (width, height), cv2.CV_32FC1)left_rectified = cv2.remap(leftFrame, leftMapX, leftMapY, cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)rightMapX, rightMapY = cv2.initUndistortRectifyMap(K2, D2, R2, P2, (width, height), cv2.CV_32FC1)right_rectified = cv2.remap(rightFrame, rightMapX, rightMapY, cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)
Türkçesi, initUndistortRectifyMap fonksiyonu ile hem gürültüyü giderip hem de görüntülerin yatay eksende eşlenmesi işlemini gerçekleştiriyoruz. Sol kameramız için K1(kamera matrisi) ve D1(gürültü matrisi) ile gürültü temizlemeyi yaparken R1(soldan sağa rotasyon dönüşüm matrisi) ve P1(soldan sağa paralel yansıma matrisi) ile eksen hizalamayı gerçekleştirmiş oluyoruz. Sonuç görüntüsünün çözünürlük ve veri tipini de ekliyoruz elbette. Dönüşüm matrislerini elde ettikten ve remap fonksiyonuna ilettikten sonra görüntümüz düzeltilmiş oluyor. Aynısını da sağ görüntü için sağ kameradan sol kameraya gerçekleştirerek düzeltilmiş görüntülerimizi elde etmiş oluyoruz. Türkçesi yetmez diyenlere görselleştirelim:
Stereo kalibrasyon ve düzeltme konusunda anlatacaklarım bu kadar. İkinci aşamamız disparity map nasıl elde ediliyor, yöntemler nelerdir sorularını cevaplamak üzere olacak.
Kaynaklar:
- http://zone.ni.com/reference/en-XX/help/372916P-01/nivisionconceptsdita/guid-c9c3535b-faf7-4ade-9166-513a49d1b90a/
- https://www.google.at/search?q=depth+map&source=lnms&tbm=isch&sa=X&ved=0ahUKEwjlqIq1xsjeAhUkglwKHahhDVIQ_AUIDigB&biw=1536&bih=732#imgrc=o7eiUTU5f3tKpM:
- https://www.mathworks.com/discovery/stereo-vision.html
- https://docs.opencv.org/3.0-beta/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html