灰度變換
將顏色的RGB設(shè)置為相同的值即可使得圖片為灰色在抛,一般處理方法有:
1满着、取三種顏色的平均值
2蟹地、取三種顏色的最大值(最小值) 3授翻、加權(quán)平均值:0.3R + 0.59G + 0.11*B
人眼對綠色最為敏感
黑白濾鏡
計算RGB的平均值arg或悲,arg>=100,r=g=b=255堪唐,否則均為0
去色濾鏡
RGB取三種顏色的最大值和最小值的平均值
單色濾鏡
只保留一個通道巡语,其他設(shè)為0
高斯模糊
根據(jù)正態(tài)分布為每個像素點周圍的像素點分配權(quán)重,將各個權(quán)重(各個權(quán)重值和為1)與對應(yīng)的色值相乘淮菠,所得結(jié)果求和作為中心像素點新的色值
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;
}
懷舊濾鏡
注意更新RGB值需要對0~255范圍之外的值進行判斷處理
連環(huán)畫濾鏡
對比度增強和亮度增強
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濾鏡
特點:色彩濃郁男公、高飽和度、可能伴隨晃動兜材、照片暗角
- 將原圖與原圖進行“柔光”圖層混合得到圖B
柔光圖層混合
根據(jù)混合色的通道數(shù)值選擇不同公式計算:數(shù)值大于128的時候理澎,結(jié)果色就比基色稍亮;數(shù)值小于或等于128曙寡,結(jié)果色就比基色稍暗。
柔光模式是以基色為主導(dǎo)执隧,混合色只相應(yīng)改變局部明暗。其中混合色為黑色镀琉,結(jié)果色不會為黑色峦嗤,只比結(jié)果色稍暗,混合色為中性色屋摔,結(jié)果色跟基色一樣。
計算公式:
- 圖B與一種自己設(shè)定的風(fēng)格色(比如藍色:R-200,G-37,B-11)進行“排除”圖層混合装黑,設(shè)定40%透明度,得到圖C
差值&排除圖層混合
差值圖層混合用基色減去混合色或用混合色減去基色從基色亮度中減去混合色弓熏,如果產(chǎn)生負值取正進行反相恋谭,白色與任何顏色混合得到反相色,黑色與任何顏色混合顏色不變疚颊。
排除圖層混合與差值類似信认,但對比度更低,白色與基色混合得到基色補色母截,黑色與任何顏色混合顏色不變橄教。
計算公式:
- 選擇一種暗角模板,與圖C進行“疊加”圖層混合
疊加圖層混合
根據(jù)基色通道的數(shù)值選擇不同公式計算护蝶,對顏色進行正片疊加或濾色混合持灰,結(jié)果色保留基色的明暗對比,以基色為主導(dǎo)堤魁。
計算公式:
暗角生成
暗角特效在Vignette類中實現(xiàn)妥泉,主要的算法實現(xiàn)原理是首先建立兩個以圖像中心為中心的橢圓邊界將圖像劃分為Zone A,Zone B和Zone C三個區(qū)域蝇率,然后根據(jù)圖像中各像素點在橢圓邊界內(nèi)外的判斷對像素點進行操作:Zone A保持原始圖像的像素值,Zone C完全使用純黑色的邊角顏色本慕,Zone B則進行暗角和原圖的融合疊加。為了實現(xiàn)融合區(qū)域的漸變效果监氢,Zone B建立了一系列以圖像中心為中心的橢圓藤违,各個相鄰橢圓間的區(qū)域根據(jù)其邊界的坐標(biāo)距離使用不同的融合權(quán)重系數(shù)以實現(xiàn)中間亮到四個角漸暗的暗角效果,具體的融合公式如下所示:
關(guān)于漸變暗角的融合權(quán)重系數(shù)的代碼具體實現(xiàn)如下:
List<double> aVals; //融合區(qū)域系列橢圓的寬向坐標(biāo)
List<double> bVals; //融合區(qū)域系列橢圓的高向坐標(biāo)
List<double> aValsMidPoints;
List<double> bValsMidPoints;
List<double> imageWeight; // 原圖融合權(quán)重系數(shù)
List<double> vignetteWeight; // 暗角融合權(quán)重系數(shù)
double a0 = vignetteWidthHalf - bandPixelsHalf; //融合區(qū)域系列橢圓寬向起始坐標(biāo)
double b0 = vignetteHeightHalf - bandPixelsHalf; //融合區(qū)域系列橢圓高向起始坐標(biāo)
//計算融合區(qū)域系列橢圓的坐標(biāo)數(shù)組
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);
}
// 計算融合權(quán)重
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)+塊內(nèi)平均
SLIC算法使用k-means聚類實現(xiàn)骡男,首先根據(jù)用戶界面設(shè)置的聚類中心個數(shù)的超參數(shù)初始化k個聚類中心隔盛,也即超像素中心,將其均勻分布到圖像的像素點上吮炕。
初始化label數(shù)組保存每一個像素點所屬的超像素標(biāo)簽,初始化lengths數(shù)組保存各個像素點到所屬的超像素中心的距離陕凹。
如果圖片包含N個像素鳄炉,要分割成K個超像素,那么每個超像素的大小是N/K 佑女,超像素之間的距離谈竿。為了將算法復(fù)雜度降低為O(n),在聚類時將搜索區(qū)域限制在2S*2S范圍內(nèi)嚎花。聚類的目標(biāo)是使各個像素到所屬的超像素中心的距離之和最小劫恒。實現(xiàn)時首先將圖像的RGB顏色轉(zhuǎn)換為LAB顏色空間轿腺,使得計算距離時能夠同時考慮LAB顏色信息和XY距離信息丛楚,并可以通過用戶界面設(shè)置的M值調(diào)整顏色和距離的比重。
對每一個超像素中心x仿荆,搜索它2S*2S范圍內(nèi)的點:如果點到超像素中心x的5維信息的距離小于這個點到它原來所屬的超像素中心的距離坏平,那么說明這個點屬于超像素x,更新lengths數(shù)組和label數(shù)組令境,然后對每個聚類中心顾瞪,找到所有l(wèi)abel值為該聚類中心的點,求他們的平均值從而更新得到k個新的聚類中心惕橙。上述過程迭代十次钉跷,該部分核心實現(xiàn)代碼如下所示:
//計算每個超像素分割區(qū)域的面積
double S = Math.Sqrt((w * h) / numberOfCenters);
//生成聚類中心
Center[] centers = createCenters(w, h, lband, aband, labbband, numberOfCenters, S);
//生成聚類標(biāo)簽
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);
}
效果如下:
參考: