Hesaplamalı Fotoğrafçılıkta Python Görüntü İşlemeye Giriş
Yayınlanan: 2022-03-11Hesaplamalı fotoğrafçılık, fotoğraf sürecini hesaplama ile geliştirmekle ilgilidir. Normalde bunun yalnızca nihai sonucun sonradan işlenmesi (fotoğraf düzenlemeye benzer) için geçerli olduğunu düşünme eğiliminde olsak da, olasılıklar çok daha zengindir, çünkü hesaplama fotoğraf sürecinin her adımında etkinleştirilebilir – sahne aydınlatmasından başlayarak, devam ederek. lens ve sonunda yakalanan görüntünün ekranında bile.
Bu önemlidir, çünkü normal bir kamerayla elde edilebileceklerden çok daha fazlasını ve farklı şekillerde yapılmasına olanak tanır. Aynı zamanda önemlidir, çünkü günümüzde en yaygın kamera türü olan mobil kamera, daha büyük kardeşine (DSLR) kıyasla özellikle güçlü değildir, ancak cihazda sahip olduğu bilgi işlem gücünü kullanarak iyi bir iş çıkarmayı başarır. .
Hesaplamanın fotoğrafçılığı geliştirebileceği iki örneğe göz atacağız - daha doğrusu, daha fazla çekim yapmanın ve bunları birleştirmek için biraz Python kullanmanın, mobil kamera donanımının olmadığı iki durumda nasıl güzel sonuçlar yaratabileceğini göreceğiz. gerçekten parlıyor—düşük ışık ve yüksek dinamik aralık.
Düşük Işıkta Fotoğrafçılık
Diyelim ki bir sahnenin düşük ışıkta fotoğrafını çekmek istiyoruz, ancak kameranın küçük bir açıklığı (lensi) ve sınırlı pozlama süresi var. Bu, düşük ışıkta bir sahne verildiğinde aşağıdaki gibi bir görüntü üretebilen cep telefonu kameraları için tipik bir durumdur (bir iPhone 6 kamerasıyla çekilmiş):
Kontrastı iyileştirmeye çalışırsak, sonuç şudur ki bu da oldukça kötüdür:
Ne oluyor? Bütün bu gürültü nereden geliyor?
Cevap, gürültünün sensörden gelmesidir - ışığın ona ne zaman çarptığını ve bu ışığın ne kadar yoğun olduğunu belirlemeye çalışan cihaz. Bununla birlikte, düşük ışıkta, herhangi bir şeyi kaydetmek için hassasiyetini büyük ölçüde artırmak zorundadır ve bu yüksek hassasiyet, aynı zamanda yanlış pozitifleri, yani orada olmayan fotonları da algılamaya başladığı anlamına gelir. (Bir not olarak, bu sorun yalnızca cihazları değil, biz insanları da etkiler: Bir dahaki sefere karanlık bir odaya girdiğinizde, görüş alanınızdaki gürültüyü fark etmek için bir dakikanızı ayırın.)
Bir görüntüleme cihazında her zaman bir miktar gürültü olacaktır; bununla birlikte, sinyal (faydalı bilgi) yüksek yoğunluğa sahipse, gürültü ihmal edilebilir (yüksek sinyal-gürültü oranı) olacaktır. Düşük ışıkta olduğu gibi sinyal düşük olduğunda, gürültü göze çarpacaktır (düşük sinyalden gürültüye).
Yine de, yukarıdakinden daha iyi çekimler elde etmek için tüm kamera sınırlamalarına rağmen gürültü sorununun üstesinden gelebiliriz.
Bunu yapmak için zaman içinde ne olduğunu hesaba katmalıyız: Gürültü tamamen rastgele olurken sinyal aynı kalacak (aynı sahne ve bunun statik olduğunu varsayıyoruz). Bu, sahnenin birçok fotoğrafını çekersek, gürültünün farklı versiyonlarına sahip olacakları, ancak aynı faydalı bilgilere sahip olacakları anlamına gelir.
Bu nedenle, zaman içinde çekilen birçok görüntünün ortalamasını alırsak, sinyal etkilenmeden gürültü ortadan kalkar.
Aşağıdaki çizim basitleştirilmiş bir örneği göstermektedir: Gürültüden etkilenen bir sinyalimiz (üçgen) var ve aynı sinyalin farklı gürültüden etkilenen birden çok örneğinin ortalamasını alarak sinyali kurtarmaya çalışıyoruz.
Gürültünün, herhangi bir durumda sinyali tamamen bozacak kadar güçlü olmasına rağmen, ortalama almanın gürültüyü aşamalı olarak azalttığını ve orijinal sinyali kurtardığımızı görüyoruz.
Bu ilkenin görüntülere nasıl uygulandığını görelim: İlk olarak, kameranın izin verdiği maksimum pozlama ile nesnenin birden fazla fotoğrafını çekmemiz gerekiyor. En iyi sonuçlar için manuel çekime izin veren bir uygulama kullanın. Çekimlerin aynı yerden yapılması önemlidir, bu nedenle (doğaçlama) bir tripod yardımcı olacaktır.
Daha fazla çekim genellikle daha iyi kalite anlamına gelir, ancak tam sayı duruma bağlıdır: ne kadar ışık olduğu, kameranın ne kadar hassas olduğu vb. İyi bir aralık 10 ile 100 arasında herhangi bir yerde olabilir.
Bu görüntülere sahip olduğumuzda (mümkünse ham formatta), Python'da okuyabilir ve işleyebiliriz.
Python'da görüntü işlemeye aşina olmayanlar için, bir görüntünün 2B bayt değerleri (0-255) dizisi olarak temsil edildiğini, yani tek renkli veya gri tonlamalı bir görüntü için belirtmeliyiz. Renkli bir görüntü, her bir renk kanalı (R, G, B) için bir tane olmak üzere bu tür üç görüntüden oluşan bir set olarak veya dikey konum, yatay konum ve renk kanalıyla (0, 1, 2) indekslenen etkili bir 3B dizi olarak düşünülebilir. .
İki kütüphaneden faydalanacağız: NumPy (http://www.numpy.org/) ve OpenCV (https://opencv.org/). İlki, diziler üzerinde çok etkili bir şekilde (şaşırtıcı derecede kısa kodla) hesaplamalar yapmamızı sağlarken, OpenCV bu durumda görüntü dosyalarının okunmasını/yazılmasını yönetir, ancak çok daha yeteneklidir ve birçok gelişmiş grafik prosedürü sağlar - bazılarını burada yapacağız. makalenin devamında kullanın.
import os import numpy as np import cv2 folder = 'source_folder' # We get all the image files from the source folder files = list([os.path.join(folder, f) for f in os.listdir(folder)]) # We compute the average by adding up the images # Start from an explicitly set as floating point, in order to force the # conversion of the 8-bit values from the images, which would otherwise overflow average = cv2.imread(files[0]).astype(np.float) for file in files[1:]: image = cv2.imread(file) # NumPy adds two images element wise, so pixel by pixel / channel by channel average += image # Divide by count (again each pixel/channel is divided) average /= len(files) # Normalize the image, to spread the pixel intensities across 0..255 # This will brighten the image without losing information output = cv2.normalize(average, None, 0, 255, cv2.NORM_MINMAX) # Save the output cv2.imwrite('output.png', output)
Sonuç (otomatik kontrast uygulanmış olarak), orijinal görüntüden çok büyük bir gelişme olan gürültünün gittiğini gösterir.
Ancak yine de yeşilimsi çerçeve ve ızgara benzeri desen gibi bazı garip eserler fark ediyoruz. Bu sefer rastgele bir gürültü değil, sabit bir kalıp gürültüsü. Ne oldu?
Yine, sensörü suçlayabiliriz. Bu durumda, sensörün farklı bölümlerinin ışığa farklı tepki verdiğini ve bunun da görünür bir desen oluşturduğunu görüyoruz. Bu modellerin bazı öğeleri düzenlidir ve büyük olasılıkla sensör alt katmanı (metal/silikon) ve gelen fotonları nasıl yansıttığı/absorbe ettiği ile ilgilidir. Beyaz piksel gibi diğer öğeler, ışığa karşı aşırı duyarlı veya aşırı duyarsız olabilen kusurlu sensör pikselleridir.
Neyse ki, bu tür gürültüden kurtulmanın da bir yolu var. Buna karanlık çerçeve çıkarma denir.
Bunu yapmak için, desen gürültüsünün kendisinin bir görüntüsüne ihtiyacımız var ve bu, karanlığın fotoğrafını çekersek elde edilebilir. Evet, bu doğru—sadece kamera deliğini kapatın ve maksimum pozlama süresi ve ISO değeriyle bir sürü fotoğraf çekin (örneğin 100) ve bunları yukarıda açıklandığı gibi işleyin.
Birçok siyah çerçevenin (rastgele gürültüden dolayı aslında siyah olmayan) ortalamasını alırken, sabit desen gürültüsüyle sonuçlanırız. Bu sabit gürültünün sabit kalacağını varsayabiliriz, bu nedenle bu adıma yalnızca bir kez ihtiyaç duyulur: Ortaya çıkan görüntü, gelecekteki tüm düşük ışıklı çekimler için yeniden kullanılabilir.
Bir iPhone 6 için desen gürültüsünün (kontrast ayarlı) sağ üst kısmı şöyle görünür:

Yine, ızgara benzeri dokuyu ve hatta sıkışmış beyaz bir piksel gibi görünen şeyi fark ediyoruz.
Bu karanlık çerçeve gürültüsünün (ortalama_gürültü değişkeninde) değerine sahip olduğumuzda, normalleştirmeden önce onu şu ana kadarki average_noise
yeterlidir:
average -= average_noise output = cv2.normalize(average, None, 0, 255, cv2.NORM_MINMAX) cv2.imwrite('output.png', output)
İşte son fotoğrafımız:
Yüksek dinamik aralık
Küçük (mobil) bir kameranın sahip olduğu bir diğer sınırlama, küçük dinamik aralığıdır, yani ayrıntıları yakalayabileceği ışık yoğunluğu aralığı oldukça küçüktür.
Başka bir deyişle, kamera bir sahneden ışık yoğunluklarının yalnızca dar bir bandını yakalayabilir; bu bandın altındaki yoğunluklar saf siyah olarak görünürken, üstündeki yoğunluklar saf beyaz olarak görünür ve bu bölgelerdeki tüm ayrıntılar kaybolur.
Bununla birlikte, kameranın (veya fotoğrafçının) kullanabileceği bir numara vardır ve bu, sensöre gelen toplam ışık miktarını etkin bir şekilde kontrol etmek için pozlama süresini (sensörün ışığa maruz kaldığı süre) ayarlamaktır. Belirli bir sahne için en uygun aralığı yakalamak için aralığı yukarı veya aşağı kaydırma.
Ama bu bir uzlaşmadır. Pek çok detay son fotoğrafa sığmıyor. Aşağıdaki iki resimde, aynı sahnenin farklı pozlama süreleriyle çekilmiş olduğunu görüyoruz: çok kısa poz (1/1000 sn), orta poz (1/50 sn) ve uzun poz (1/4 sn).
Gördüğünüz gibi, üç görüntüden hiçbiri mevcut tüm ayrıntıları yakalayamıyor: Lambanın filamanı yalnızca ilk çekimde görülüyor ve çiçek detaylarının bir kısmı ya ortada ya da son çekimde görünüyor, ancak görünmüyor. ikisi birden.
İyi haber şu ki, bu konuda yapabileceğimiz bir şey var ve yine biraz Python koduyla birden fazla çekim yapmayı içeriyor.
Alacağımız yaklaşım, buradaki makalesinde yöntemi tanımlayan Paul Debevec ve diğerlerinin çalışmasına dayanmaktadır. Yöntem şöyle çalışır:
İlk olarak, aynı sahnenin (sabit) ancak farklı pozlama süreleriyle birden fazla çekimi gerekir. Yine, önceki durumda olduğu gibi, kameranın hiç hareket etmemesini sağlamak için bir tripoda veya desteğe ihtiyacımız var. Pozlama süresini kontrol edebilmemiz ve kamera otomatik ayarlamalarını önleyebilmemiz için (telefon kullanıyorsanız) bir manuel çekim uygulamasına da ihtiyacımız var. Gereken çekim sayısı, görüntüde mevcut olan yoğunluk aralığına bağlıdır (üçten yukarıya) ve korumayla ilgilendiğimiz ayrıntıların en az bir çekimde net bir şekilde görünmesi için pozlama süreleri bu aralık boyunca aralıklı olmalıdır.
Ardından, farklı pozlama süreleri boyunca aynı piksellerin rengine dayalı olarak kameranın tepki eğrisini yeniden oluşturmak için bir algoritma kullanılır. Bu temel olarak, bir noktanın gerçek sahne parlaklığı, pozlama süresi ve yakalanan görüntüde karşılık gelen pikselin sahip olacağı değer arasında bir harita oluşturmamızı sağlar. OpenCV kütüphanesinden Debevec'in metodunun uygulamasını kullanacağız.
# Read all the files with OpenCV files = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg'] images = list([cv2.imread(f) for f in files]) # Compute the exposure times in seconds exposures = np.float32([1. / t for t in [1000, 500, 100, 50, 10]]) # Compute the response curve calibration = cv2.createCalibrateDebevec() response = calibration.process(images, exposures)
Yanıt eğrisi şuna benzer:
Dikey eksende, bir noktanın sahne parlaklığının ve pozlama süresinin kümülatif etkisine sahibiz, yatay eksende ise karşılık gelen pikselin sahip olacağı değere (kanal başına 0 ila 255) sahibiz.
Bu eğri, (işlemdeki bir sonraki adım olan) ters işlemi gerçekleştirmemize izin verir - piksel değeri ve pozlama süresi göz önüne alındığında, sahnedeki her noktanın gerçek parlaklığını hesaplayabiliriz. Bu parlaklık değerine ışıma denir ve bir birim sensör alanına düşen ışık enerjisi miktarını ölçer. Görüntü verilerinden farklı olarak, çok daha geniş bir değer aralığını (dolayısıyla yüksek dinamik aralık) yansıttığı için kayan nokta sayıları kullanılarak temsil edilir. Işınım görüntüsüne (HDR görüntüsü) sahip olduğumuzda, onu basitçe kaydedebiliriz:
# Compute the HDR image merge = cv2.createMergeDebevec() hdr = merge.process(images, exposures, response) # Save it to disk cv2.imwrite('hdr_image.hdr', hdr)
HDR ekrana sahip olacak kadar şanslı olanlarımız için (ki bu giderek yaygınlaşıyor), bu görüntüyü tüm ihtişamıyla doğrudan görselleştirmek mümkün olabilir. Ne yazık ki, HDR standartları hala emekleme aşamasındadır, bu nedenle bunu yapma süreci farklı ekranlar için biraz farklı olabilir.
Geri kalanımız için iyi haber şu ki, normal bir görüntüleme görüntünün bayt değeri (0-255) kanallarına sahip olmasını gerektirse de, bu verilerden hala yararlanabiliyoruz. Işınım haritasının zenginliğinin bir kısmından vazgeçmemiz gerekse de, en azından nasıl yapılacağı üzerinde kontrolümüz var.
Bu işleme ton eşleme denir ve kayan nokta ışıma haritasının (yüksek bir değer aralığı ile) standart bir bayt değeri görüntüsüne dönüştürülmesini içerir. Ekstra ayrıntıların çoğunun korunması için bunu yapmak için teknikler vardır. Size bunun nasıl çalışabileceğine dair bir örnek vermek için, kayan nokta aralığını bayt değerlerine sıkıştırmadan önce HDR görüntüsünde bulunan kenarları iyileştirdiğimizi (keskinleştirdiğimizi) hayal edin. Bu kenarları geliştirmek, düşük dinamik aralık görüntüsünde de onları (ve dolaylı olarak sağladıkları ayrıntıyı) korumaya yardımcı olacaktır.
OpenCV, Drago, Durand, Mantiuk veya Reinhardt gibi bu ton haritalama operatörlerinden bir dizi sağlar. İşte bu operatörlerden birinin (Durand) nasıl kullanılabileceğine ve ürettiği sonuca bir örnek.
durand = cv2.createTonemapDurand(gamma=2.5) ldr = durand.process(hdr) # Tonemap operators create floating point images with values in the 0..1 range # This is why we multiply the image with 255 before saving cv2.imwrite('durand_image.png', ldr * 255)
Süreç üzerinde daha fazla kontrole ihtiyacınız varsa Python kullanarak kendi operatörlerinizi de oluşturabilirsiniz. Örneğin bu, değer aralığını 8 bit'e düşürmeden önce (ardından bir otomatik kontrast adımı takip eden) çok az pikselde temsil edilen yoğunlukları kaldıran özel bir operatörle elde edilen sonuçtur:
Ve işte yukarıdaki operatörün kodu:
def countTonemap(hdr, min_fraction=0.0005): counts, ranges = np.histogram(hdr, 256) min_count = min_fraction * hdr.size delta_range = ranges[1] - ranges[0] image = hdr.copy() for i in range(len(counts)): if counts[i] < min_count: image[image >= ranges[i + 1]] -= delta_range ranges -= delta_range return cv2.normalize(image, None, 0, 1, cv2.NORM_MINMAX)
Çözüm
Biraz Python ve birkaç destekleyici kitaplıkla, sonucu iyileştirmek için fiziksel kameranın sınırlarını nasıl zorlayabileceğimizi gördük. Tartıştığımız her iki örnek de daha iyi bir şey yaratmak için birden fazla düşük kaliteli çekim kullanır, ancak farklı sorunlar ve sınırlamalar için birçok başka yaklaşım vardır.
Pek çok kameralı cep telefonunda bu belirli örnekleri ele alan mağaza veya yerleşik uygulamalar olsa da, bunları elle programlamak ve kazanılabilecek daha yüksek düzeyde kontrol ve anlayıştan keyif almak açıkçası hiç de zor değil.
Bir mobil cihazda görüntü hesaplamaları ile ilgileniyorsanız, OpenCV Eğitimine göz atın: Diğer Toptaler ve seçkin OpenCV geliştiricisi Altaibayar Tseveenbayar tarafından iOS'ta MSER Kullanarak Gerçek Zamanlı Nesne Algılama.