Günümüz dünyasında bilgisayar sistemlerinin ana kullanıcılarının insanlar olduğunu sıklıkla unutuyoruz. İnsanlar olarak muhakeme yeteneğimiz ve fiziksel dünyayı doğru algılama yeteneğimiz açısından pek mükemmel değiliz. İşte bu açıdan bazı programcılar bilgisayarların da yaptıkları her şeyde kusursuz olmaları gerekmediğini fark ettiler. Bunun sonucu olarak JPEG formatı bulundu. Yaklaşık hesaplama tekniklerini veya buluşsal yöntemleri kullanmanın da iyi bir deneyim sağlayabileceğini keşfettiler.
İnsanoğlu bahsettiğim mükemmel ve kusurlu olmak arasındaki bu dengenin güzel bir örneği mahiyetinde JPEG görüntü sıkıştırma standardını üretti. JPEG, görüntüler için yaygın olarak kullanılan bir format, mühendislik ve bilim harikası olarak kabul ediliyor. Sıkıştırdığı görüntünün verimli olması için çeşitli teknik yapı taşlarını ve konseptleri birleştirerek mükemmel olmasa da günümüzde görüntüler için popüler ve yerleşik bir format haline geldi.
JPEG, görüntü dosyalarını görsel anlamda kabul edilebilir tutarak boyutunu küçültmek için kullanılan bir yöntemdir. Bunu dosya boyutu ile görüntünün ne kadar iyi göründüğü arasında bir denge kurarak yapıyor. Makalenin başlangıcında da kusurlu olduğumuzdan bahsetmiştim. İşte tekniğinde de insanların ışığı nasıl gördüğü ve algıladığıyla alakalı, bu minvalde çeşitli mühendislik becerileri kullanılıyor. Bugün bile JPEG’in sağladığı iyileştirmelerden yararlanıyoruz. Hatta insanlar bazı şeyleri JPEG’le karşılaştırıyor, örneğin “ChatGPT web’in bulanık bir JPEG’idir” gibi. Bu makalede de bir JPEG görüntüsünü kaydettiğinizde veya yüklediğinizde meydana gelen tüm ilginç şeyleri açıklayacağım. JPEG’in nasıl çalıştığını bilmiyorsanız insanların ışığı nasıl algıladığından bahsederek başlayalım.
Bilgisayarları ve teknolojiyi kullandığımızda onlarla etkileşime geçmek için fiziksel duyularımıza güveniriz. Örneğin klavyede yazı yazmak, fareyi hareket ettirmek veya tablet ya da telefon ekranına dokunmak için ellerimizi kullanıyoruz. İşitme duyumuz, cihazlarımızdan gelen bildirimler veya video görüşmeleri sırasındaki gibi sesleri duymamızı sağlar. Gözlerimiz ekrandaki metin, resim veya video gibi şeyleri görmemize yardımcı olur.
Teknolojiyle etkileşim kurmanın tüm bu yolları, yeni teknolojileri kullanma ve üretme şeklimizi büyük ölçüde değiştirdi. Ancak asıl şaşırtıcı olan şey duyularımızın benzersiz çalışma prensibiyle bilgisayar kullanımını kolaylaştıran arayüzler tasarlamamız olsa gerek. Duyularımızın nasıl çalıştığını anlayarak teknolojiyle etkileşim kurmanın etkili ve kullanıcı dostu yollarını buluyoruz.
Aynı zamanda insan algısının sınırlarını da keşfediyoruz. Örneğin, gözlerimiz bir videonun yeterince hızlı yenilenmediğini algılayabilir ve bu da resmin yırtıldığı veya parçalandığı izlenimi verir. Ancak bilim farklı türdeki bilgisayar monitörleri ve izleme mesafeleri için hangi resim yenileme hızının yeterli olarak kabul edildiğini belirledi. Tipik olarak saniyede 30-60 kare (FPS), çoğu insanın yırtılmayı fark etmemesi için yeterlidir.
Parlaklık, görsel algının ölçülebilen başka bir yönü, nit veya metrekare başına kandela cinsinden ölçülür. Kandela Latince’de mum anlamına geldiğinden kandela ortalama bir mumun parlaklığıdır ve metrekare ile birleştirildiğinde bir yüzeye yayılan ışık yoğunluğunu temsil eder. Örneğin, bir iPhone 15’in maksimum parlaklığı 1000 nit, 1600 nit (HDR) ve 2000 nit (dış mekan) olarak belirlenmiş.
İnsanoğlunun görsel algısının bu sınırlarını anlamak algılanan kalitede önemli bir kayıp olmadan görüntülerin etkili bir şekilde sıkıştırılmasına yardımcı oluyor. Yaygın olarak kullanılan bir görüntü sıkıştırma formatı olan JPEG de bu türdeki bir kayıplı sıkıştırmadır. Kayıplı sıkıştırma diyoruz çünkü görsel algı üzerindeki etkiyi en aza indirirken daha yüksek bir sıkıştırma oranı elde etmek için resimdeki belirli bilgilerin kasıtlı olarak hesaplanmış bir şekilde atıldığı anlamına geliyor.
Elbette görüntüleri bilgisayarlarda depolamak için kullanılan farklı türde görüntü formatları var. Bu formatlardan biri de PNG formatıdır, bu formatta olan görüntüler kayıpsız olarak kaydedilir. Ancak bu aynı zamanda görselin dosya boyutunun diğer formatlara göre daha büyük olmasını sağlıyor.
Örneğin, 2592 x 1944 piksel boyutlarında bir görselimiz varsa ve bunu PNG formatında kaydedersek yaklaşık 15 megabayt depolama alanı kaplayacaktır. Öte yandan, aynı görüntüyü JPEG formatında kaydedersek, yalnızca 0,75 megabayt kadar yer kaplar, bu da kabaca 20 kat daha küçük. Dosya boyutundaki fark önemli ancak görsel olarak insan gözünün PNG görüntüsüne kıyasla JPEG görüntüsünde herhangi bir kalite kaybını fark etmesi zordur.
JPEG 1990’ların başında standartlaştırıldı çünkü Microsoft tarafından geliştirilen BMP formatının nispeten büyük görüntü dosyaları üretmesi nedeniyle bir ihtiyaç hâline gelmişti. O zamanki bilgisayarların, özellikle Web’in ilk günleri göz önüne alındığında, bu kadar büyük miktarda veriyi kaydetme, yükleme ve aktarma konusunda sınırlamaları vardı. Dolayısıyla JPEG, görsel kaliteyi korurken görüntülerin daha az bit kullanılarak sıkıştırılıp saklanmasına olanak tanıyan bir çözüm sağladı.
JPEG sıkıştırma formatında asıl amaç gereksiz bilgileri kaldırarak dosya boyutunu küçültmektir. Bu işleme kayıplı sıkıştırma denir çünkü sıkıştırma sırasında bazı veriler kaybolur. Sormamız gereken soru, hangi bilgilerin kalitesinde gözle görülür bir düşüşe neden olmadan güvenli bir şekilde atılabileceğidir. Şimdi interaktif zamanı, hadi bakalım. Aşağıdaki görselde A ve B olarak işaretlenmiş fayanslar bulunuyor. Bu fayansların hangi renk olduğunu söyleyebilir misiniz?
Cevabınızın “B gri ancak A daha koyu bir tonu” olması size doğru gelebilir ancak bu iki fayans aslında aynı renkte. Bilim insanları, gözlerimizin renk farklılıklarından ziyade parlaklık farklılıklarına daha duyarlı olduğunu keşfettiler. Parlaklığa olan bu hassasiyet önemlidir çünkü JPEG sıkıştırma tekniğinde büyük rol oynar.
Resimler piksel adı verilen küçük noktalardan oluşur ve her piksel üç değerle kodlanır: kırmızı, yeşil ve mavi kısaca RGB formatı olarak bilinir. Bu üç ana rengin bir karışımını kullanarak neredeyse algılanabilen diğer tüm renkleri elde edebilirsiniz ve insan gözü de bu renk spektrumlarını algılayıp yorumlamada iyidir. Her rengi temsil etmek için 8 bit (yani 1 bayt) kullanacaksak, rengi kodlamak için piksel başına 24 bit’e ihtiyacımız olduğu anlamına gelir.
Bu mantıkla bir pikselde 16 milyondan fazla rengi elde edebiliyoruz. Bir pikselin ne kadar kırmızı renge sahip olduğunu seçmek için 0 ile 255 arasında bir sayı seçiyoruz. Sayı ne kadar yüksekse kırmızı da o kadar parlak hâle geliyor. Bu sistemde (0, 0, 0) siyahı, (255, 255, 255) ise beyazı temsil ediyor. Kırmızı, yeşil ve mavi için farklı değerler kullanarak geniş bir renk yelpazesi oluşturabiliriz.
RGB renk formatı, renkleri dijital olarak tanımlamak için yaygın olarak kullanılır. Ancak konu görüntüleri sıkıştırmak olduğunda insan gözünün parlaklığa daha duyarlı olmasından faydalanabiliriz. YCbCr formatının devreye girdiği yer burasıdır. YCbCr formatı da üç sayıdan oluşur ancak farklı bilgileri yakalar. “Y” parlaklığı veya lümeni temsil eder. Bize bir pikselin ne kadar parlak olduğunu söyler. “Cb” kroma mavisi ve “Cr” kroma kırmızısı anlamına gelir. Bu iki bileşen renk bilgisini saklar. Yeşil renge ne olduğunu merak ediyor olabilirsiniz, aslında hâlâ işin içinde. YCbCr formatında, kroma mavisi bir pikseldeki yeşil ve mavi arasındaki oranı veya kontrastı tanımlarken kroma kırmızısı kırmızı ve yeşil arasındaki oranı veya kontrastı tanımlar. Yani hiçbir renk bilgisini kaybetmedik.
Adım 1 – Renk Alt Örnekleme
Renk alt örnekleme, bir görüntü veya videodaki renk bilgisi miktarını azaltmak için JPEG standardında kullanılan bir tekniktir. İsteğe bağlıdır ve genel kaliteyi önemli ölçüde etkilemeden dosya boyutunu küçültmeye yardımcı olur. Renk alt örneklemesini gerçekleştirmek için görüntüyü genellikle 8×8 piksel olmak üzere piksel bloklarına bölersiniz. Daha sonra bu bloklardan renk bileşenlerini (Cb ve Cr) çıkarırsınız. Her pikselin tüm renk bilgilerini saklamak yerine, bunları 2×2 piksellik alt gruplar halinde gruplandırırsınız.
Renk alt örneklemesinin farklı seviyeleri mevcuttur. Görüntüler için renk bilgisinin yarısı atılabilirken, videolar ve MPEG sıkıştırma formatı için renk bilgisinin dörtte biri atılır. Her alt grubun rengini belirlemek için 2×2 pikselin ortalamasını alıp bu ortalamayı her piksele uygulayabilir veya sol üst pikselin rengini seçip bunu alt gruptaki diğer üç piksel için kullanabilirsiniz.
Üç kanalla (Y, Cb ve Cr) başladık, bilgiyi tek bir kanalda (parlaklık bileşeni, Y) sabit tuttuk ve diğer iki kanaldaki (Cb ve Cr) bilginin yarısını attık. Yani üç kanal yerine artık fiilen iki kanalımız var, bu da orijinal resmin %66’sı anlamına geliyor. Örneklemede daha agresif olsaydık yani 4×4 blok kullansaydık bilginin dörtte üçünü atardık. Bu durumda, parlaklık kanalımız tam iken renk kanallarının her biri için yalnızca dörtte bir bilgiye sahip olurduk. Bu da 1 + ¼ + ¼ = 1,5 kanal yani orijinal görüntünün %50’si oluyor.
Adım 2 – Verileri Grafiğe Dökme
Verileri grafiğe dökmekten kasıt bir görüntüyü renkleri ve desenleri açısından anlamaya çalışmak diyebiliriz. Bunu yapabilmek için görüntüyü bir sinyal olarak düşünebiliriz. Görüntüden bir dizi piksel aldığınızı ve bunların renk değerlerini çizdiğinizi hayal edin. Her pikselin parlaklığını veya yoğunluğunu temsil eden 0 ile 255 arasında bir değeri olduğunu söylemiştik.
Bu piksel değerlerini bir grafik üzerinde noktalar olarak görselleştirerek renklerin o grafik boyunca nasıl değiştiğine dair bir fikir edinebiliriz. Tıpkı ses dalgalarının perdelerini belirleyen farklı frekansları olması gibi, görüntülerin de görünümlerine katkıda bulunan farklı frekansları vardır. Görüntüyü frekans alanına dönüştürmek, bu frekansları analiz etmemize ve görüntüde bulunan desen ve renkleri daha etkili bir şekilde anlamamıza olanak tanır.
Grafiğe bakarak pikseller arasındaki hızlı değişimi yüksek frekanslı sinyal, pikseller arasındaki yavaş değişimi ise düşük frekanslı sinyal olarak tanımlayabiliriz. Sinyalleri yüksek veya düşük frekans olarak sınıflandırmak, insanın başka bir algısal olgusundan faydalanmamızı sağlıyor. İnsanoğlunun görsel algısı, yüksek frekanslı piksellere karşı daha az duyarlıdır. Bu yüzden yüksek frekanslı piksellerden kurtulmamız gerekiyor.
Adım 3 – Ayrık Kosinüs Dönüşümü (DCT)
Ayrık kosinüs dönüşümü (DCT) kavramını basit bir ifadeyle anlamak için, piksel adı verilen küçük karelerden oluşan bir resmimiz olduğunu hayal edelim. Her pikselin belirli bir rengi veya parlaklık değeri vardır. Artık resme tek tek piksel değerleri açısından bakmak yerine onu farklı bir şekilde analiz etmek istiyoruz. Resmi farklı desen veya dalgaların birleşimine bölmek istiyoruz. Bu dalgalara kosinüs dalgaları denir çünkü kosinüs fonksiyonuna benzeyen özel bir şekle sahiptirler.
DCT, piksel değerlerinden kosinüs dalgalarına bu dönüşümü gerçekleştirmemize yardımcı olur. Bunu üç önemli parametreyi kullanarak yapar: frekans, genlik ve kayma. Frekans bize resimdeki piksel değerlerinin ne sıklıkla değiştiğini söyler. Genlik, belirli bir kosinüs dalgasının gücünü veya ağırlığını temsil eder. Kayma ise resmin parlaklığını ayarlamamızı sağlar.
Bu parametreleri kullanan DCT, resmi kosinüs alanı olarak bilinen farklı bir perspektiften görmemize olanak tanır. Bu perspektifin onu sıkıştırma açısından faydalı kılan bazı güzel matematiksel özellikleri vardır. En önemli bir özelliği de spektral sıkıştırmadır. Gözlerimiz için daha önemli olan düşük frekanslarla ilgili bilgileri bir araya getirerek ve bunları yüksek frekanslardan ayırarak resmi verimli bir şekilde görmemizi sağlar.
Görüntü sıkıştırma için işe yarayabilecek başka iyi dönüşümler olmasına rağmen DCT verimli olması ve açık patentlere sahip olması nedeniyle JPEG için geniş çapta benimsendi. IBM’de diğer birçok planın patenti vardı, bu nedenle DCT’nin açık patenti onun JPEG standardına dahil edilmesinde önemli bir rol oynadı. DCT’yi 8×8 piksel bloğuna uygulamak için katsayılarını hesaplıyoruz. Bu katsayılar her bir kosinüs dalgasının bloğun tamamına ne kadar etki ettiğini gösterir. Başka bir deyişle, 64 kosinüs dalgasının birleşimini kullanarak 8×8 bloğu gözlemliyoruz.
Adım 4 – Niceleme
Niceleme, büyük bir değer kümesini yuvarlama ve ölçeklendirme yoluyla daha küçük bir değer kümesiyle eşleyerek basitleştirme işlemidir. Benzetmem gerekirse bir ürünün fiyatını kuruşlara odaklanmak yerine en yakın liraya yuvarlamak gibidir. JPEG bağlamında niceleme, düşük frekanslı bilgiyi korurken yüksek frekanslı bilgi miktarının azaltılmasına yardımcı olur. Demin bahsettiğimiz 8×8 piksel bloğundaki her bir elemanın niceleme tablosundan karşılık gelen bir değere bölünmesi ve en yakın tam sayıya yuvarlanmasıyla elde edilir. Niceleme tablosu görüntüye uygulanan sıkıştırma düzeyini belirleyen farklı matrislerden oluşur.
Niceleme görüntünün hem luma (parlaklık) hem de chroma (renk) bileşenlerine uygulanabilir. Ancak kroma için kullanılan niceleme tabloları genellikle daha acımasızdır. Bunun nedeni gözlerimizin parlaklıktaki değişikliklere karşı daha duyarlı olmasıdır, dolayısıyla luma bileşenini mümkün olduğunca korumak istiyoruz. Yine de çok fazla algısal kayba yol açmadan kromaya bir miktar niceleme uygulayabiliriz. Nicelemenin görüntü kalitesi üzerindeki etkisini görmekle ilgileniyorsanız, farklı niceleme ayarlarıyla oynamanıza ve ortaya çıkan görüntü kalitesini gözlemlemenize olanak tanıyan bir simülatörü de buraya not etmiş olayım.
Adım 5 – Sayı Uzunluğu ve Huffman Kodlaması
Niceleme işlemi uygulanmış bir DCT matrisindeki birçok değer sıfır olur. Bu da sayı uzunluğu kodlaması adı verilen bir teknik kullanılarak kalan sıfır olmayan değerlerin daha verimli şekilde kodlanması fırsatını yaratıyor. Sayı uzunluğu kodlamasının ardındaki fikir, aynı sayının ardışık olarak ne sıklıkta göründüğünü sayarak uzun bir sayı dizisini temsil etmektir. Bu teknik, nicelenmiş DCT’de genellikle uzun sıfır dizilerinin bulunması gerçeğinden yararlanır; bu, sıralı fazlalık olduğu anlamına gelir.
JPEG algoritmasında sayı uzunluğu kodlamasını uygulamak için nicelenmiş DCT’deki rakamlar zikzak düzeninde düzenlenir. Bu düzenleme, sıranın sonundaki sıfırların sayısını maksimuma çıkararak bunların etkili bir şekilde tanımlanmasını ve sıkıştırılmasını kolaylaştırır. Şimdi yukarıdaki tablonun şu şekilde bir sayı dizisi oluşturduğunu görebilirsiniz.
Bu sayı dizisindeki sıfır tekrarını da sıkıştırarak daha kompakt bir hâle getirebiliriz. O zaman elde edeceğimiz sayı dizisinin 8×8’lik piksel bloğu için 64 değer depolamak yerine 17 değer depolayabildiğini görüyoruz.
Bu sayı tekrarını önlemenin sadece bir yoluydu, bir diğer tekniği ise Huffman Kodlaması olarak bilinen yoldur. Huffman Kodlaması, her sayıyı ayrı ayrı temsil etmek yerine, bunları üçerli gruplar halinde gruplandırır. Her üçlü üç bölümden oluşur:
- Bir değerden önce gelen sıfırların sayısı
- Değeri kodlamak için gereken bit sayısı
- Gerçek değerin kendisi
Bunu yaparak, ortak üçlüler daha az bit kullanılarak daha verimli bir şekilde temsil edilebilir. Örneğin, sekiz sıfırdan oluşan bir dizi “A” gibi tek bir karakterle temsil edilebilirken, daha az sıklıkta görülen bir desen olduğu gibi kodlanabilir. Bu şekilde ortak desenler daha etkili bir şekilde sıkıştırılır ve sonuçta genel dosya boyutu küçülür.
Tüm web sitelerinin %77’sinden fazlası görüntüleri sıkıştırmak için JPEG standardını kullanıyor. Bunun nedeni de JPEG’in görüntü dosyalarını insan gözüyle gözle görülür bir kalite kaybı olmadan küçültme yeteneğidir. WebP veya AVIF gibi daha yeni formatlar son zamanlarda daha iyi sıkıştırma oranlarıyla popülerlik kazanmış olsa da hiçbiri yaygın kullanım ve benimsenme açısından JPEG’i geçemedi.
Bu modern formatların ortaya çıkmasına rağmen, JPEG’in son otuz yılda kaydettiği önemli ilerlemeleri hâlâ takdir ediyorum. Hem mühendislik açısından hem de insanlar tarafından nasıl algılandığı açısından etkileyici bir başarı olarak değerlendirilebilir. JPEG geniş çapta tanındı ve bilgisayardan uzak diyebileceğimiz kişiler tarafından bile günlük dilde kullanıldı, bu da onu uzun süre hatırlanacak gerçekten dikkat çekici bir algoritma hâline getirdi.