[數字圖像處理] 濾鏡算法整理

灰度變換

將顏色的RGB設置為相同的值即可使得圖片為灰色,一般處理方法有:
1、取三種顏色的平均值
2、取三種顏色的最大值(最小值) 3、加權平均值:0.3R + 0.59G + 0.11*B
人眼對綠色最為敏感

黑白濾鏡

計算RGB的平均值arg,arg>=100,r=g=b=255,否則均為0

去色濾鏡

RGB取三種顏色的最大值和最小值的平均值

單色濾鏡

只保留一個通道,其他設為0

高斯模糊

根據正態分布為每個像素點周圍的像素點分配權重,將各個權重(各個權重值和為1)與對應的色值相乘,所得結果求和作為中心像素點新的色值
G(x,y) = \frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}

function gaussBlur(imgData, radius, sigma) {
    var pixes = imgData.data,
        height = imgData.height,
        width = imgData.width,
        radius = radius || 5;
        sigma = sigma || radius / 3;
    
    var gaussEdge = radius * 2 + 1;

    var gaussMatrix = [],
        gaussSum = 0,
        a = 1 / (2 * sigma * sigma * Math.PI),
        b = -a * Math.PI;
    
    for(var i = -radius; i <= radius; i++) {
        for(var j = -radius; j <= radius; j++) {
            var gxy = a * Math.exp((i * i + j * j) * b);
            gaussMatrix.push(gxy);
            gaussSum += gxy;
        }
    }
    var gaussNum = (radius + 1) * (radius + 1);
    for(var i = 0; i < gaussNum; i++) {
        gaussMatrix[i] /= gaussSum;
    }

    for(var x = 0; x < width; x++) {
        for(var y = 0; y < height; y++) {
            var r = g = b = 0;
            for(var i = -radius; i<=radius; i++) {
                var m = handleEdge(i, x, width);
                for(var j = -radius; j <= radius; j++) {
                    var mm = handleEdge(j, y, height);
                    var currentPixId = (mm * width + m) * 4;
                    var jj = j + radius;
                    var ii = i + radius;
                    r += pixes[currentPixId] * gaussMatrix[jj * gaussEdge + ii];
                    g += pixes[currentPixId + 1] * gaussMatrix[jj * gaussEdge + ii];
                    b += pixes[currentPixId + 2] * gaussMatrix[jj * gaussEdge + ii];
                }
            }
            var pixId = (y * width + x) * 4;

            pixes[pixId] = ~~r;
            pixes[pixId + 1] = ~~g;
            pixes[pixId + 2] = ~~b;
        }
    }
    imgData.data = pixes;
    return imgData;
}

function handleEdge(i, x, w) {
    var m = x + I;
    if(m < 0) {
        m = -m;
    } else if(m >= w) {
        m = w + i -x;
    }
    return m;
}
懷舊濾鏡

R = (0.393 * r + 0.769 * g + 0.189 * b)
G = (0.349 * r + 0.686 * g + 0.168 * b)
B = (0.272 * r + 0.534 * g + 0.131 * b)
注意更新RGB值需要對0~255范圍之外的值進行判斷處理

連環畫濾鏡

R = |g – b + g + r| * r / 256
G = |b – g + b + r| * r / 256
B = |b – g + b + r| * g / 256

對比度增強和亮度增強
private static byte ContrastModify(int degree, byte basePixel)
 {
     if (degree < -100) degree = -100;
     if (degree > 100) degree = 100;
     double contrast = (100.0 + degree) / 100.0;
     contrast *= contrast;
     double pixel = ((basePixel / 255.0 - 0.5) * contrast + 0.5) * 255;
     if (pixel < 0) pixel = 0;
     if (pixel > 255) pixel = 255;
     return (byte)pixel;
 }

 private static byte BrightModify(int degree, byte basePixel)
 {
     if (degree < -255) degree = -255;
     if (degree > 255) degree = 255;
     int pixel = basePixel + degree;
     if (pixel < 0) pixel = 0;
     if (pixel > 255) pixel = 255;
     return (byte)pixel;
}
LOMO濾鏡

特點:色彩濃郁、高飽和度、可能伴隨晃動、照片暗角

  1. 將原圖與原圖進行“柔光”圖層混合得到圖B
    柔光圖層混合
    根據混合色的通道數值選擇不同公式計算:數值大于128的時候,結果色就比基色稍亮;數值小于或等于128,結果色就比基色稍暗。
    柔光模式是以基色為主導,混合色只相應改變局部明暗。其中混合色為黑色,結果色不會為黑色,只比結果色稍暗,混合色為中性色,結果色跟基色一樣。
    計算公式:
    C=\begin{cases} A + (2 *B - 255) * (A -A *A / 255) / 255 \;\; if\; B <=128 \\ A + (2 *B - 255) * (Sqrt(A/255)*255 -A)/255 \;otherwise \end{cases}
  2. 圖B與一種自己設定的風格色(比如藍色:R-200,G-37,B-11)進行“排除”圖層混合,設定40%透明度,得到圖C
    差值&排除圖層混合
    差值圖層混合用基色減去混合色或用混合色減去基色從基色亮度中減去混合色,如果產生負值取正進行反相,白色與任何顏色混合得到反相色,黑色與任何顏色混合顏色不變。
    排除圖層混合與差值類似,但對比度更低,白色與基色混合得到基色補色,黑色與任何顏色混合顏色不變。
    計算公式:
    C = (B + A) -B*A / 128
  3. 選擇一種暗角模板,與圖C進行“疊加”圖層混合
    疊加圖層混合
    根據基色通道的數值選擇不同公式計算,對顏色進行正片疊加或濾色混合,結果色保留基色的明暗對比,以基色為主導。
    計算公式:
    C= \begin{cases} B*A / 128 \;\; if \; A < = 128 \\ 255 - (255 -B)* (255 -A) / 128 \; otherwise \end{cases}
暗角生成

暗角特效在Vignette類中實現,主要的算法實現原理是首先建立兩個以圖像中心為中心的橢圓邊界將圖像劃分為Zone A,Zone B和Zone C三個區域,然后根據圖像中各像素點在橢圓邊界內外的判斷對像素點進行操作:Zone A保持原始圖像的像素值,Zone C完全使用純黑色的邊角顏色,Zone B則進行暗角和原圖的融合疊加。為了實現融合區域的漸變效果,Zone B建立了一系列以圖像中心為中心的橢圓,各個相鄰橢圓間的區域根據其邊界的坐標距離使用不同的融合權重系數以實現中間亮到四個角漸暗的暗角效果,具體的融合公式如下所示:
P=l_1*w_1+l_2*w_2
w_1 = 1/2(1+\cos(\pi s/d)), w_2 = 1/2(1-\cos(\pi s/d)),
關于漸變暗角的融合權重系數的代碼具體實現如下:

List<double> aVals; //融合區域系列橢圓的寬向坐標          
 List<double> bVals; //融合區域系列橢圓的高向坐標          
 List<double> aValsMidPoints; 
 List<double> bValsMidPoints; 
 List<double> imageWeight;    // 原圖融合權重系數 
 List<double> vignetteWeight; // 暗角融合權重系數 

 double a0 = vignetteWidthHalf - bandPixelsHalf; //融合區域系列橢圓寬向起始坐標 
 double b0 = vignetteHeightHalf - bandPixelsHalf; //融合區域系列橢圓高向起始坐標 

 //計算融合區域系列橢圓的坐標數組 
 for (int i = 0; i <= numberSteps; ++i) 
 { 
?     aEll = a0 + stepSize * i; 
?     bEll = b0 + stepSize * i; 
?     aVals.Add(aEll); 
?     bVals.Add(bEll); 
 } 
 for (int i = 0; i < numberSteps; ++i) 
 { 
?     aEll = a0 + stepSize * (i + 0.5); 
?     bEll = b0 + stepSize * (i + 0.5); 
?     aValsMidPoints.Add(aEll); 
?     bValsMidPoints.Add(bEll); 
 } 

 // 計算融合權重 
 double weight1, weight2, arg, argCosVal; 
 double arguFactor = Math.PI / bandPixels; 
 for (int i = 0; i < numberSteps; ++i) 
 { 
?     arg = arguFactor * (aValsMidPoints[i] - a0); 
?     argCosVal = Math.Cos(arg); 
?     weight1 = 0.5 * (1.0 + argCosVal); 
?     weight2 = 0.5 * (1.0 - argCosVal); 
?     imageWeight.Add(weight1); 
?     vignetteWeight.Add(weight2); 
 }

效果如下:


晶格化濾鏡

超像素分割SLIC(simple linear iterative clustering)+塊內平均
SLIC算法使用k-means聚類實現,首先根據用戶界面設置的聚類中心個數的超參數初始化k個聚類中心,也即超像素中心,將其均勻分布到圖像的像素點上。
初始化label數組保存每一個像素點所屬的超像素標簽,初始化lengths數組保存各個像素點到所屬的超像素中心的距離。

如果圖片包含N個像素,要分割成K個超像素,那么每個超像素的大小是N/K ,超像素之間的距離S=\sqrt{N/K}。為了將算法復雜度降低為O(n),在聚類時將搜索區域限制在2S*2S范圍內。聚類的目標是使各個像素到所屬的超像素中心的距離之和最小。實現時首先將圖像的RGB顏色轉換為LAB顏色空間,使得計算距離時能夠同時考慮LAB顏色信息和XY距離信息,并可以通過用戶界面設置的M值調整顏色和距離的比重。

對每一個超像素中心x,搜索它2S*2S范圍內的點:如果點到超像素中心x的5維信息的距離小于這個點到它原來所屬的超像素中心的距離,那么說明這個點屬于超像素x,更新lengths數組和label數組,然后對每個聚類中心,找到所有label值為該聚類中心的點,求他們的平均值從而更新得到k個新的聚類中心。上述過程迭代十次,該部分核心實現代碼如下所示:

//計算每個超像素分割區域的面積 
 double S = Math.Sqrt((w * h) / numberOfCenters); 
 //生成聚類中心 
 Center[] centers = createCenters(w, h, lband, aband, labbband, numberOfCenters, S); 
 //生成聚類標簽 
 double[,] labels = new double[h, w]; 
 for (int i = 0; i < h; i++) 
 { 
?     for (int j = 0; j < w; j++) 
?     { 
?         labels[i, j] = -1; 
?     } 
 } 

 for (int iteration = 0; iteration < 10; iteration++) 
 { 
?     double[,] lengths = new double[h, w]; 
?     for (int ii = 0; ii < h; ii++) 
?     { 
?         for (int j = 0; j < w; j++) 
?         { 
?             lengths[ii, j] = Double.MaxValue; 
?         } 
?     } 

?     int i = 0; 
?     foreach (Center center in centers) 
?     { 
?         for (int k = (int)Math.Round(center.X - S); k < (int)Math.Round(center.X + S); k++) 
?             for (int l = (int)Math.Round(center.Y - S); l < (int)Math.Round(center.Y + S); l++) 
?                 if (k >= 0 && k < h && l >= 0 && l < w) 
?                 { 
?                     double L = lband[k, l]; 
?                     double A = aband[k, l]; 
?                     double B = labbband[k, l]; 

?                     double Dc = Math.Sqrt(Math.Pow(L - center.L, 2) + Math.Pow(A - center.A, 2) + Math.Pow(B - center.B, 2)); 
?                     double Ds = Math.Sqrt(Math.Pow(l - center.Y, 2) + Math.Pow(k - center.X, 2)); 
?                     double length = Math.Sqrt(Math.Pow(Dc, 2) + Math.Pow(Ds / 2, 2) * Math.Pow(m, 2)); 

?                     if (length < lengths[k, l]) 
?                     { 
?                         lengths[k, l] = length; 
?                         labels[k, l] = i; 
?                     } 
?                 } 
?         i++; 
?     } 
?     centers = calculateNewCenters(lband, aband, labbband, w, h, centers, labels); 
 } 

效果如下:


參考:

  1. 圖像處理的簡單濾鏡算法
  2. 看云文檔圖像濾鏡匯總
  3. 微像素
  4. 圖層混合模式計算公式
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容