Instagram Filtrelerinin Modellenmesi


Bir önceki yazımızda Instagram' ın nasıl çalıştığından bahsetmiştik. Bu yazı bir önceki yazıyı temel alarak yazıldığından devam etmeden önce Instagram Nedir? Nasıl Çalışır? yazımızı okumanızı tavsiye ederim. Önceki yazımızda da bahsedildiği gibi instagram içerisinde bulunan renk tabanlı dönüşümleri iki başlık altında toplayabiliriz. Bunlar basitçe renk-renk dönüşümleri ve nokta-nokta dönüşümleridir; bu yazımızın konusu nokta-nokta dönüşümü olacaktır. Bu dönüşüm ile resmin renkleri üzerinde  yalnızca piksel koordinat değerlerine bağlı bir dönüşüm işlemi tanımlayacağız. Bunu önceki mantığa benzetmek adına, renk tonuna değil piksel koordinatlarına özgü bir renk dönüşümü olarak düşünebiliriz.


Renk dönüşümüne benzer olarak burada her bir piksel üzerindeki rengin atanması gereken yeni rengi gösteren bir dönüşüm haritası ile bu işlemi gerçekleştireceğiz. Yanda dairesel bir harita ile filtrelenmiş imge görünmektedir. İmgenin filtre sonucu değerleri merkez noktalar için korunurken köşe noktalar siyaha yaklaştırılmıştır.

Burada anlatılacak olan işlem görüntü işleme literatüründe image blend (görüntü karıştırma) olarak geçmektedir ve aynı boyutlarda (genişlik ve yükseklik) iki görüntünün (yada bu konseptde katmanın) bir biri ile bir matematiksel bir işlem aracılığı ile karıştırılması işlemidir. x alt katmana y üst katmana ait karşılıklı nokta değerleri olmak üzere, bu matematiksel işlemlere birer örnek ile göz alacak olursak:

Alt Katman :  5 7 3 2 6 5 8 7 1 9 1 0 9 2
Üst Katman : 1 4 5 6 8 9 7 8 9 9 7 1 2 5

Doğrusal Toplam : f(x,y) = ax+(1-a)y
İki resmin belirli ağırlıklarla (a,1-a) birbirine karıştırılması işlemidir. İşlem zorluğu açısından en basit karıştırma biçimidir ve genellikle alt katmanın değerlerini artırmak için kullanılır.


  • a=0.5 (aritmetik ortalama) seçilerek oluşturulan yeni görüntü : 3 5 4 4 7 7 7 7 5 9 4 0 5 3 bulunur.

Çarpma : f(x,y) = xy
Karşılıklı iki pikselin çarpılması ile yeni görüntü elde edilir. Sabit bir b sayısı ile çarpılarak görüntüye transparanlık verileceği gibi farklı haritalar ile de çarpılarak renklerin değiştirilmesi gerçekleştirilebilir.


  • Çarpım sonucu oluşacak yeni görüntü : 5 28 15 12 48 45 56 56 9 81 7 0 18 10 şeklindedir. Örnekte olduğu gibi sayılar çok yüksek değerler çıkacağından bir normalizasyon katsayısı ile çarpılır. Bu katsayı 8 bit görüntüler için (1/255) dir. Örneğimizde en yüksek değer 10 kabul edildiğinden normalize görüntü : 0 3 1 1 5 4 5 5 0 8 0 0 1 1 olacaktır.


Aydınlatma : f(x,y) = max(x,y)
Adından da kolaylıkla anlaşılabileceği gibi aydınlatma iki katmanı her nokta için en büyük değeri seçerek birleştirir. Bu işlem sonucunda (HDR görüntüler örnek verilebilir) aynı noktaya ait 2 görüntü kullanılarak karanlık/gölgeli çıkmış yerler düzeltilebilir.


  • İki katman içerisinde her nokta için en büyük değerler seçilerek : 5 7 5 6 8 9 8 8 9 9 7 1 9 5 görüntüsü elde edilir.


Karartma : f(x,y) = min(x,y)
Karartma aydınlatma işleminin tersi olarak tanımlıdır ve yeni görüntü iki katman içerisindeki en düşük noktalar aracılığı ile oluşturulur.

  • İki katman içerisinden her piksel için en düşük değerler seçilerek : 1 4 3 2 6 5 7 7 1 9 1 0 2 2 görüntüsü oluşturulur.


Kaplama : f(x,y) = (x<0.5) ? (2xy) : (1-2(1-x)(1-y))
Kaplama karıştırma işlemleri içerisinde en sık kullanılan işlemlerden biridir ve üst katmanı alt katmanın üzerine kaplama etkisi verir (Dağların üzerine kar kaplama efekti gibi). Koşullu çıkış üreten bu işlem alt katman değeri 0.5 den küçük ise iki katmanın çarpımının 2 katını, büyük ise iki katmanın terslerinin (1-a,1-b) çarpımının 2 katının tersini döndürür. Formülasyonda görünen 0.5 değeri görüntü skalasındaki renklerin ortası,1 değeri ise en yüksek değeri anlamına gelmektedir, 8 bit görüntüler için bu değer 0.5-->128, 1--->255 dir.

Şimdilik bu kadar işlem ile bırakıp geriye kalan işlemleri kod içerisinde görerek yorumlayabileceğinizi söylemekle yetinelim. İşlemlerin C kodu kodlamaları şu şekilde yapılmıştır.


double lin_com(double a, double b)
{
       return (a*0.8+b*0.3);
}

double lighten(double a,double b)
{
       return (a < b) ? a : b;
}

double darken(double a,double b)
{
       return (a  >  b) ? b : a;
}
       
double overlay(double a,double b)
{
       return (a < 0.5) ? (2*a*b) : (1-2*(1-a)*(1-b));
}

double hardlight(double a,double b)
{
       return overlay(b,a);
}

double softlight(double a,double b)
{
       return (b < 0.5) ? (2*a*b-a*a*(1-2*b)) : (2*a*(1-b) + sqrt(a)*(2*b-1));
}


double multiply(double a, double b)
{
       return a*b;
}

double color_dodge(double a, double b)
{
       return b/(1-a);
}

double burn(double a,double b)
{
       return 1-(1-a)/b;
}
       
double screen(double a, double b)
{
       return 1-(1-a)*(1-b);
} 

Tabiki bu kodlama tek başına bir işe yaramamaktadır. Burada görünen fonksiyonlar her bir piksel değerine tek tek uygulanmalıdır. Seçilen yöntem adına göre bu karıştırma(blend) işlemini gerçekleştiren fonksiyonumuz aşağıda verilmiştir. Kodlama içerisinde blender isimli bir fonskiyon tutucu kullanılarak kullanıcı tarafından seçilen fonksiyon switch case yapısı içerisinde bu fonsksiyon tutucuya atanmıştır. Her piksele fonksiyonun uygulanması işlemi switch case yapısından sonra görülen for döngüleri içerisinde yapılmıştır.


double *blend(double *Im, double *B, char model, int M,int N)

{
       double (*blender) (double,double);
       
       double *out = (double*) malloc(3*M*N*sizeof(double));
       
       switch (model) {
              
       
              case 'l'  : 
                   printf("Blend model is Lighten!\n");
                   blender = &lighten;  
                   break; 
              case 'd'  : 
                   printf("Blend model is Darken!\n");
                   blender = &darken;  
                   break; 
              case 'o'  : 
                   printf("Blend model is Overlay!\n");
                   blender = &overlay;
                   break;
              case 'h'  : 
                   printf("Blend model is Hard Light!\n");
                   blender = &hardlight;  
                   break;
              case 's'  : 
                   printf("Blend model is Soft Light!\n");
                   blender = &softlight;  
                   break;
              case 'c'  : 
                   printf("Blend model is Color Dodge!\n");
                   blender = &color_dodge;  
                   break;
              case 'b'  : 
                   printf("Blend model is Burn!\n");
                   blender = &burn;  
                   break;     
              case 'n'  : 
                   printf("Blend model is Screen!\n");
                   blender = &screen;  
                   break;     
              case 'm'  : 
                   printf("Blend model is Multiply!\n");
                   blender = &multiply;  
                   break;           
              case 'q'  : 
                   printf("Blend model is Linear Combination!\n");
                   blender = &lin_com;  
                   break;
              default   : 
                        printf("Blend model is Default(Overlay)!\n");
                        blender = &overlay;    
                        break;
        }
              
        int i,j,c;
       
        for(c=0; c < 3; c++) {
            int ktsc = M*N*c;
            for(i=0; i < M; i++) {
                 int ktsi = N*i;
                 for(j=0; j < N; j++) {
                         
                         *(out+ j + ktsi + ktsc) =  256*blender(*(Im+ j + ktsi + ktsc)/256,*(B+ j + ktsi + ktsc)/256);
       
                                    }
                                }
                           }   
       return out;
}

Her zaman ki gibi yazımızı örneklerle bitirelim. Yazımın başında da söylediğim gibi bir önceki yazımıza oldukça bağımlı bir konu işledik bu nedenle örnekler içerisnde bir önceki yazımızda anlatılan renk dönüşümleri sonrasında karıştırma işlemi yapılmıştır. Karıştırma işlemi için kullanılabilecek örnek bir kod aşağıda verilmiştir.


BMP I   = resim_oku("ara.bmp"); // alt katman
BMP M1  = resim_oku("cen.bmp"); // dairesel üst katman
    
BMP G   = resim_cev(I,rgb2gra);

double *Ig = resim_dou(G);
    
int M = I.bminfo.height;
int N = I.bminfo.width;
double map[3][256];

for(i=0;i < 256;i++) {
                       //255-i-0 - sarı turuncu map
                       map[0][i] = 255;
                       map[1][i] = i;
                       map[2][i] = i &lt 128 ? 255-2*i : 0;
}
    
while (1) {

    int i,j;
    
    printf("Select the blend Mode: ");
    fflush(stdin);
    char mode = getchar();    

    BMP Map = resim_har(G,map);                   
    resim_yaz(Map,"ara_form.bmp");
    
    double *Im = resim_dou(I);
    double *Mp = resim_dou(Map);
    double *Mi = resim_dou(M1);
    
    double *outx = blend(Im,Mp,mode,M,N);
    const char *s = "s";

    outx = blend(outx,Mi,*(s+0),M,N);
    
    BMP out = resim_bmp(outx,M,N,0); 
    resim_yaz(out,"sonuc.bmp");
}

Kod içerisinde resim_har ve blend fonksiyonları ile iki aşamalı bir renk dönüşümü yapılmaktadır. İlk dönüşüm basit renk dönüşümüdür ve ikinci adımda kullanılacak olan üst katmanı oluşturmak için kullanılmaktadır. Oluşturulan üst katman alt katman ile yumuşak aydınlatma (soft-light) karışımına girerek sonuç resmi oluşturulmuştur. Kullanılan resimler ve farklı örneklere ait görüntüler kod satırlarının ardından verilmiştir.

instagram nasıl çalışır
İnstgarm Nasıl Çalışır
Bir instagram klasiği kahve çekirdekleri ile örneklerimizi bitirelim:

instagram filters
İnstagram Nasıl Çalışır

Eveet geldik 2 serilik instagram yazımızın sonuna, benim içinde epeyce bilgilendirici olan bu yazının umarım sizlere de faydası olacaktır. Bu yazıyı okuyan sizilerin de kolaylıkla yaratabileceği basitlikte olan bu dönüşümlerin, her birinin yaklaşık 1.3 milyon $ nakit karşılığı Facebook tarafından satın alındığını aklınızdan çıkarmamanız dileğiyle..

1 yorum:

Görüntü işleme ile ilgili yeni yazıları ve bu sitede yer alan yazıların güncellenmiş sürümlerini www.imlab.io veya cescript.github.io adreslerinden takip edebilirsiniz.

X