Merhaba. Bu yazıda ihtiyaca yönelik bir problemin çözümüne değindim. Problem, bir dizindeki görsellere toplu bir şekilde yazı eklemek. Örneğin bir telif notu veya adres bilgisi gibi. Sanıyorum bu, GIMP'te bir şablon (stencil) oluşturularak çözülebilir. Peki eklenecek metin sabit olmadığı durumda? Benim sorunum görsellere sıra numarası eklemekti. Her fotoğrafın köşesine birden başlayan ardışık sayılar... Bu durumda, şablon çözüm olmuyor. Ayrıca bu GIMP'le tek tek yapılabiliyorsa bile yüze yakın görsel için pratik değil. En pratik çözüm bunun için basit bir script yazmak. Bu arada, bu numaralandırmayı dosya adıyla yapabilirdim, ama dosya adındaki zaman damgasını korumak istediğim için buna dokunmak istemedim. Üstelik bu görselleri bir web sayfasında kullanmak isteseydim, dosya adları görünmüyor olacaktı. Son olarak bu script ile istenirse dosya adlarından, görselin kenarına 90'larda fotoğraf makinalarının yaptığına benzer şekilde fotoğrafın ne zaman çekildiği yazılabilir.
Script deyince akl(ım)a ilk gelen bash maalesef bu iş için en iyi araç değil. Bildiğim kadarıyla bash için yazılmış bir görsel kütüphanesi yok. Yapılmaz değil elbette yapılır, ama nasıl ki balyoz varken duvar yıkmak için çekiç kullanılmıyorsa, her iş için ona uygun aracı seçmek çözümün ilk adımı. Python sever okurlar kızacaktır ama bash'tan sonra ikinci en iyi script dili python'un bu iş için Python Imaging Library (PIL) adında uygun bir kütüphanesi var. Ben kendi scriptimde PIL'in Pillow adındaki fork'unu kullandım. Kütüphane pip install pillow komutuyla kolayca yükleniyor.
Yazıyı kısa tutmak için kodu yine github hesabıma yükledim. Kısa bir script ve içinde sadece ufak numaralar var. Öncelikle her normal python script'i gibi başta import'lar var. PIL kütüphanesindeki Image modülü görsellerle ilgili fonksiyonları içeriyor. ImageDraw, görsellerle ilgili basit 2B efektler, benim kodumda görseli döndürmek için gerekli ve son olarak ImageFont font ve diğer metin efekleri için.
Exif Verileri ve Oryantasyon
Bu projedeki birinci zorluk, görsellerin bilgisayarda, telefonda çekildikleri şekilde görüntülenememeleri. Örn. aşağıda cep telefonumla çektiğim görseli ele alalım:
>>> img = Image.open("image.jpg")
>>> img.show()
görsel yatay formatta görünüyor.
ve GIMP'le açtığımda garip bir pencere görselin "Exif orientation metadata" içerdiğini ve bunu çevirmek isteyip istemediğimi soruyor. Peki neden?
Cep telefonunu yatay* tutup fotoğraf çekerken, telefon fotoğrafa herhangi bir döndürme işlemi uygulamaz. Cep telefonu veya dijital fotoğraf makinasıyla çekilmiş fotoğrafları python ile yukarıdaki gibi açtığımda, dikey açıyla çekilmemiş olan fotoğraflar bu yüzden çekildikleri açıda ekrana basılırlar. Kamera fotoğrafın çekildiği oryantasyonu da birlikte kaydedip, görüntüleme sırasında fotoları çevirir. Yönelim (oryantasyon) bilgisi kaydedilmemiş olsaydı, fotoğrafı düzgün görmek için her seferinde bizim telefonu ilk çekildiği açıya çevirmemiz gerekirdi.
*: Bazı kameralarda dikey pozisyon default olup, yatay pozisyonun yönelimini kaydederken, bazılarında tam tersidir.
Dolayısıyla bir görsel dosyasının içinde sadece piksellere ait bilgiler olmadığı yukarıdaki ifadeden de anlaşılabilir. Dosyada görsele ait metadatanın saklandığı Exif adlı bir alan vardır ve bugün tüm görsel formatları ve kameralar Exif'i destekler. Wikipedia'da Exif'te saklanan verilere ait örnek bir tablo var. Buna göre başlıca alanlar, kameranın marka ve modeli, görselin yönelimi, çekildiği gün ve saat, çözünürlük vb. Örn. çekildiği gün ve saat bilgisinin fotoğrafa gömülmesi, dosya adı 20230809_143341.jpg formatında olmasa bile fotoğrafın ne zaman çekildiğinin bulunmasına ve tarihe göre sıralabilmesine olanak sağlar. Öte yandan bazı telefonlar GPS'ten aldığı koordinat verisini Exif'e gömerek fotoğrafın nerede çekildiğini açık ederler. Bazı ortamlar böylece fotoğrafın çekildiği yeri paylaşılırken otomatik etiketleyebilir. Bunlar kişisel bilgilerin gizliliği konusunda hassas olanların tüylerini diken diken edecek durumlar. Bir söylentiye göre Ukraynalılar, Rus askerlerinden online foto isteyip bundan elde ettikleri koordinatlara saldırılar düzenlediler.
Linux'ta exiftool adında bir araçla bu bilgiler görüntülenebilir (exiftool -list <dosyaadi>), değiştirilebilir veya tamamen silinebilir (exiftool -all= <dosyaadi>). Yukarıdaki örnek görselin tüm Exif verisi silinmiş haliyle orjinali arasında 77 KB civarı bir fark var.
Exif konusunda gereğinden uzun bir açıklama yaptıktan sonra, tekrar Exif oryantasyon bilgisine geri döneyim. Pillow kütüphanesinde Exif'te saklanan veriyi okumak için Image.getexif() fonksiyonu var [1]. Bunun çıktısını ekrana yazdırdığımda (kodun içinde 22., 23. ve 24. satırlar, comment out edilmiş) dizindeki .jpg dosyaların 'Orientation' bilgisini görüyorum. [1]'de de belirtildiği gibi 2, 7, 4 ve 5 benim görsellerde yoktu. Keza 8 de bulunmadığı için bunu kendi kodumda uygulamadım, ancak bunu uygulamak gayet kolay. 1 değeri için if yapısında else'i kullandım (satır 31), bu nedenle 8 ve diğer değerlerde görsel döndürülmüyor.
Döndürme için Pillow'da Image.rotate() fonksiyonu var [2]. Bunu kullanarak Orientation=3 için görseli 90 derece ve Orientation=6 için görseli 270 derece çevirdim. Burada 27. satır için bir parantez açmak gerek: Eğer Exif'te yönelim alanı bulunmuyorsa veya görselde Exif bilgisi yoksa kod hata verecektir. Bu nedenle getexif fonksiyonundan dönen değeri kontrol edip herşey tamamsa döndürmek daha doğru. Bendeki verilerin hepsinde Exif bulunduğu için sorun yaşamadım, bu nedenle bu kısmı hızlı ve çabuk şekilde halletmeyi tercih ettim.
Buraya kadar görselin yönelimini düzeltmiş olduk. Ana problemin çözümünde [3]'ten yararlandım. Burada nasıl yazı ekleneceği basitçe gösterilmiş, ben yalnızca kendi problemime göre parametreleri düzenledim. Satır 35'te yazı eklenmek üzere bir ImageDraw nesnesi oluşturuluyor. Sonraki satırda ImageFont.truetype() fonksiyonuyla bir font nesnesi oluşturuluyor. [3]'te kullanılan font benim makinamda olmadığından (ve fonksiyondan dönen değeri hata için kontrol etmediğimden), ben /usr/share/fonts/ altındaki fontlardan birini seçtim. İkinci parametre font büyüklüğü (punto). Bunu deneme yanılma yoluyla buldum. Bendeki görseller görece büyüktü (8 MP) bu nedenle 128 ancak görülebilir bir yazı üretebildi. Ondan sonraki satırda sırasıyla, görselin verilen koordinatına (25, 25 - sol üst), sirano değişkenini, bir önceki satırla oluşturduğum fontla ve kırmızı renkle ekledim. Bu adımda, yukarıda bahsettiğim, dosyanın adından veya Exif etiketinden fotoğrafın ne zaman çekildiği bilgisi alınarak yazdırılabilirdi. Aşağıda örnek bir numaralandırılmış fotoğraf görülüyor:
Not: Alternatif olarak bunu OpenCV'de cv2.putText() fonksiyonuyla yapabilmek de mümkün ama bu başka bir yazıya kalsın.
[1]: https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
[2]: https://note.nkmk.me/en/python-pillow-rotate/
[3]: https://www.geeksforgeeks.org/python/adding-text-on-image-using-python-pil/
Hiç yorum yok:
Yorum Gönder