5.4. Piksellerin okunması ve yazılması¶
Bir görüntü üzerindeki çoğu işlem, piksel başına yapılan çalışmayı tek bir metot çağrısının içinde gizler; burada her piksele dokunan döngüler doğal (native) hızda çalışır. Yine de uygulama kodunun belirli bir piksele doğrudan dokunmak istediği durumlar vardır: belirli bir konumda ne olduğunu okumak, içine yeni bir değer yazmak, bir kalibrasyon adımı için tek bir noktayı örneklemek veya bilinen bir konumdaki değeri ayıklamak için. image modülü, bu erişim düzeyini, her biri bir pikselin nerede bulunduğunu düşünmenin farklı bir yoluna uyan iki adresleme biçimi aracılığıyla sunar.
5.4.1. Koordinata göre adresleme¶
En doğal biçim, Coordinates konusunun zaten sözlüğünü geliştirdiği biçimdir: bir pikseli Kartezyen (x, y) ile adlandırın. get_pixel() bir (x, y) alır ve o konumdaki değeri döndürür; set_pixel() aynı (x, y) ile birlikte bir değer alır ve onu yazar.
Bu çağrıların ne döndürdüğü veya kabul ettiği, görüntünün biçimine bağlıdır. Gri tonlama, ikili (binary) ve Bayer görüntüleri piksel başına tek bir değer taşır – gri tonlama için bir parlaklık, ikili için bir 0 veya 1, Bayer için tek bir renk kanalı örneği – bu nedenle get_pixel() tek bir tam sayı döndürür. RGB565, 16 bite paketlenmiş üç renk kanalı taşır ve get_pixel bunları varsayılan olarak bir (r, g, b) demetine (tuple) açar; her kanal 0 – 255 aralığına eşlenir.
Varsayılan davranış her iki uçta da tersine çevrilebilir. RGB565 görüntüsünde get_pixel çağrısına rgbtuple=False geçirmek, ham 16 bitlik paketlenmiş sözcüğe geri döner – bu, doğrusal indeksin döndürdüğü biçimin aynısıdır ve uygulama aynı paketlenmiş değeri doğrudan geri yazacaksa verimli olan biçimdir. Tek kanallı bir görüntüde rgbtuple=True geçirmek bunun tersini yapar: depolanan değer döndürülmeden önce bir RGB888 demetine dönüştürülür, Bayer görüntüleri ise yerinde bir debayer adımından geçer. Bu argüman, çağıran kodun, alttaki görüntünün pikselleri nasıl depoladığından bağımsız olarak piksellere tek tip bir renk uzayında erişebilmesi için vardır.
Sıkıştırılmış görüntüler – JPEG ve PNG – get_pixel veya set_pixel tarafından desteklenmez. Bunların baytları bilinen konumlardaki pikselleri temsil etmez ve bu metotlar, anlamlı olmayan bir değer döndürmek yerine bir hata yükseltir.
Pratikte örüntüler şöyle görünür:
v = img.get_pixel(40, 30) # grayscale: int 0..255
img.set_pixel(40, 30, 255) # write white
r, g, b = img.get_pixel(40, 30) # RGB565: defaults to (r, g, b) tuple
img.set_pixel(40, 30, (255, 0, 0)) # write red
İstenen (x, y) görüntünün dışındaysa, get_pixel None döndürür ve set_pixel hiçbir şey yapmaz. Bu, tasarım gereği bağışlayıcıdır: birçok algoritma bir görüntünün kenarlarına yakın yürür ve aralık dışı konumları kısa süreliğine indeksler; sessiz bir işlemsizlik (no-op), her seferinde bir istisna fırlatmaktan daha az yıkıcıdır.
5.4.2. Doğrusal indekse göre adresleme¶
Diğer biçim, pikselleri alttaki arabellekteki (buffer) konumlarına göre adreslemektir. Arabelleğin yerleşimini hatırlayın: pikseller satır satır depolanır, önce en üst satırın tüm pikselleri, sonra bir sonraki satırın tüm pikselleri ve aşağıya doğru en alta kadar bu şekilde devam eder. Bu düzenleme, her pikselin sol üstte 0‘dan başlayıp her satır boyunca sırayla artan tek bir tam sayı indeksine sahip olması anlamına gelir. (x, y) koordinatındaki pikselin doğrusal indeksi y * width + x‘tir.
Pikseller hem Kartezyen (x, y) ile hem de arabelleği satır satır, soldan sağa dolaşan bir doğrusal indeks ile adreslenir.¶
image modülü bu indeksi olağan Python alt simge (subscript) gösterimi aracılığıyla sunar: img[i], i doğrusal indeksindeki pikseli okur, img[i] = value ise bir piksel yazar. İndeks biçiminin döndürdüğü şey, biçim için ham depolanan değerdir, get_pixel()‘in varsayılan olarak döndürdüğü açılmış demet değildir. Bu ayrım önemlidir çünkü daha önce seçilen biçim, ham değerin neye benzediğini belirler:
Gri tonlama ve Bayer pikselleri 8 bitlik tam sayılar olarak geri döner.
RGB565 ve YUV422 pikselleri 16 bitlik tam sayılar – paketlenmiş sözcük – olarak geri döner.
İkili (binary) pikseller
0veya1olarak geri döner.JPEG ve PNG pikselleri, sıkıştırılmış akışın her seferinde bir baytı olmak üzere 8 bitlik tam sayılar olarak geri döner. Bu değerler şeffaf değildir (opaque) – bunlar herhangi bir olağan anlamda pikseller değil, sıkıştırılmış bir kodlamanın parçalarıdır.
İndeks biçimi, zaten arabellek ofsetleri açısından düşünen koda uyar: her pikseli bir kez dolaşan bir döngü, bir seferde bir satır atlaması gereken bir algoritma veya arabellek yerleşimleri arasında çeviri yapan bir kod parçası. x ve y koordinatları açısından düşünen koda ise get_pixel ve set_pixel daha iyi hizmet eder; iki biçim aynı piksellere farklı zihinsel modeller aracılığıyla erişir.
Image aynı zamanda yinelenebilirdir (iterable). for v in img: arabelleği aynı satır öncelikli (row-major) sırayla dolaşarak ham değerleri her seferinde bir piksel olarak verir ve len(img), sıkıştırılmamış biçimler için piksel sayısı veya sıkıştırılmış akışlar için bayt sayısıdır.
5.4.3. Piksel başına Python’un neden yavaş yol olduğu¶
Dürüst olmaya değer pratik bir not. Bir görüntüyü Python’dan her seferinde bir piksel olacak şekilde dolaşmak yavaştır. 320 × 240’lık bir gri tonlama görüntüsü 76.800 piksel tutar; bunların her birinde bir for döngüsü içinde get_pixel() çağırmak, eşdeğer bir doğal metodun birkaç yüz mikrosaniyede bitirebileceği işi yapmak için milyonlarca MicroPython bayt kodu komutu çalıştırır. Bu küçük bir faktör değildir. Çerçeveleri gerçek zamanlı işleyen bir betik ile kameranın çerçeve hızının çok altında sürünen bir betik arasındaki farktır.
Image yüzeyindeki neredeyse her metot, yaygın bir piksel başına örüntünün daha hızlı, doğal bir sürümü olduğu için vardır. İki görüntüyü toplayan bir döngü, tek bir doğal çağrıya dönüşür. Her pikseli komşularıyla ortalayarak yumuşatan bir döngü, bir başkasına dönüşür. Her pikseli bir eşiğe göre sınıflandıran bir döngü, üçüncü bir tanesine dönüşür. Uygulamanın işi, çoğu zaman, döngünün yapacağı işe hangi tüm görüntü metodunun uyduğunu fark etmek ve döngüyü elle yazmak yerine ona uzanmaktır.
Başka hiçbir şey uymadığında piksel düzeyinde okuma ve yazma hâlâ doğru araçtır – belirli bir ölçümü arabelleğe geri yamamak, bir kalibrasyon adımı için tek bir konumu örneklemek, bilinen bir konumdaki değeri ayıklamak. Mesele şudur ki bunlar yavaş yoldur; tüm görüntü metotları uygulamanın ihtiyaç duyduğu biçime sahip olmadığında kullanılır, pikseller üzerinde işlem yapmanın varsayılan yolu olarak değil.