編輯:關於Android編程
美顏包含磨皮、美白、瘦臉等效果,其中磨皮算法在很多博客中均有介紹
例如:
雙指數邊緣平滑濾波器用於磨皮算法的嘗試
選擇性模糊及其算法的實現
基於局部均方差相關信息的圖像去噪及其在實時磨皮美容算法中的應用
導向濾波磨皮
遞歸雙邊濾波磨皮
以上博客均有相關代碼/公式,經試驗若選取合適參數均有不錯的效果,可惜水平有限尚未在shader中實現不卡頓的實時效果~
觀察美圖秀秀和華為自帶相機等相機APP,發現實時美顏效果均不如PC端和手機端後處理,可能在這一領域目前解決辦法不多或者需求不高吧。
下面就探討簡單的美顏濾鏡處理方法。
一.模糊處理
這裡可以采用簡單的高斯模糊或者雙邊濾波處理,可以簡單參考GPUImage中的高斯模糊,或者可以將上述代碼優化到可以實時執行的程度
二.將模糊後的圖像灰度化
const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
這就是我們設置三個元素的向量,為我們的亮度來保存顏色比重的地方。這三個值加起來要為 1,這樣我們才能把亮度計算為 0.0 - 1.0 之間的值。注意中間的值,就是表示綠色的值,用了 70% 的顏色比重,而藍色只用了它的 10%。這是SONY Trinitron的數據,更加一般的系數是ITU HDTV標准 0.2125, 0.7154, 0.0721以及用於CRT顯示器非線性色彩的0.299, 0.587, 0.114)。
lowp float luminance = dot(blurColor.rgb, luminanceWeighting);
使用 GLSL 中的點乘運算,計算出這個像素綜合的亮度值。
lowp float satura = 0.7; lowp vec3 greyScaleColor = vec3(luminance); gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);
創建一個三個值都是亮度信息的 vec3,把所有的片段組合起來。為了確定每個新的顏色是什麼,使用 mix 函數(mix(x, y, a): x, y的線性混疊, x(1-a) + y*a;)。mix 函數會把我們剛剛計算的灰度值和初始的紋理顏色以及我們得到的飽和度的信息相結合。
PS:以上代碼可以在GPUImage中找到
三.美白映射
找到何時的顏色曲線即可對照片進行美白處理,例如這篇文章討論了一些美白方法。PS水平高的同學這裡可以自己設計出效果。下面是一個從某APP拿出來現成的映射表。將其作為紋理傳遞給片段著色器。
byte[] arrayOfByte = new byte[1024]; int[] arrayOfInt1 = { 95, 95, 96, 97, 97, 98, 99, 99, 100, 101, 101, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 113, 114, 115, 115, 116, 117, 117, 118, 119, 120, 120, 121, 122, 122, 123, 124, 124, 125, 126, 127, 127, 128, 129, 129, 130, 131, 131, 132, 133, 133, 134, 135, 136, 136, 137, 138, 138, 139, 140, 140, 141, 142, 143, 143, 144, 145, 145, 146, 147, 147, 148, 149, 149, 150, 151, 152, 152, 153, 154, 154, 155, 156, 156, 157, 158, 159, 159, 160, 161, 161, 162, 163, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 186, 187, 188, 188, 189, 190, 191, 191, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 200, 200, 201, 202, 202, 203, 204, 204, 205, 206, 207, 207, 208, 209, 209, 210, 211, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 222, 223, 223, 224, 225, 225, 226, 227, 227, 228, 229, 229, 230, 231, 232, 232, 233, 234, 234, 235, 236, 236, 237, 238, 239, 239, 240, 241, 241, 242, 243, 243, 244, 245, 245, 246, 247, 248, 248, 249, 250, 250, 251, 252, 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; int[] arrayOfInt2 = { 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 25, 26, 26, 27, 27, 28, 28, 28, 28, 29, 29, 30, 29, 31, 31, 31, 31, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 36, 36, 37, 37, 37, 38, 38, 39, 39, 39, 40, 40, 40, 41, 42, 42, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 48, 48, 49, 50, 51, 51, 52, 52, 53, 53, 54, 55, 55, 56, 57, 57, 58, 59, 60, 60, 61, 62, 63, 63, 64, 65, 66, 67, 68, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 91, 93, 94, 95, 96, 97, 98, 100, 101, 103, 104, 105, 107, 108, 110, 111, 113, 115, 116, 118, 119, 120, 122, 123, 125, 127, 128, 130, 132, 134, 135, 137, 139, 141, 143, 144, 146, 148, 150, 152, 154, 156, 158, 160, 163, 165, 167, 169, 171, 173, 175, 178, 180, 182, 185, 187, 189, 192, 194, 197, 199, 201, 204, 206, 209, 211, 214, 216, 219, 221, 224, 226, 229, 232, 234, 236, 239, 241, 245, 247, 250, 252, 255 }; for (int i = 0; i < 256; i++){ arrayOfByte[(i * 4)] = ((byte)arrayOfInt1[i]); arrayOfByte[(1 + i * 4)] = ((byte)arrayOfInt1[i]); arrayOfByte[(2 + i * 4)] = ((byte)arrayOfInt2[i]); arrayOfByte[(3 + i * 4)] = -1; } GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256, 1, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(arrayOfByte));
lowp float redCurveValue = texture2D(curve, vec2(textureColor.r, 0.0)).r; lowp float greenCurveValue = texture2D(curve, vec2(textureColor.g, 0.0)).r; lowp float blueCurveValue = texture2D(curve, vec2(textureColor.b, 0.0)).r; lowp float strength = -1.0 / 512.0; redCurveValue = min(1.0, redCurveValue + strength); greenCurveValue = min(1.0, greenCurveValue + strength); blueCurveValue = min(1.0, blueCurveValue + strength);
將映射後的rgb與第一步模糊後的混合(代碼可采用GPUImage中的OverBlend)
正片疊底(Multiply)和濾色(Screen)是兩種基本的混合模式,分別用於使圖片變暗和變亮。它們之間的組合還可以形成更復雜的混合模式,如疊加(Overlay)和柔光(Soft Light)。
正片疊底 —— 就是把兩層圖像的像素相乘,最後會得到一個更暗的圖像。這個模式是對稱的,也就是說交換基色和混合色得到的結果是一樣的。
f(a,b) = ab,其中a是基色,b是混合色。
濾色 —— 首先把兩層圖像的像素值取互補數,然後將它們相乘,最後再去互補數。這和正片疊底得到的結果是相反的。它會得到一個更亮的圖像。
f(a,b)=1-(1-a)(1-b),其中a是基色,b是混合色。
疊加 —— 結合了正片疊底和濾色兩種混合模式。基色中亮色的部分會更加亮,而暗色的部分會更暗。
f(a,b)當a<0.5,則為2ab,否則為1-(1-a)(1-b),其中a是基色,b是混合色。
//overlay blending mediump float ra; if (base.r < 0.5) { ra = overlay.r * base.r * 2.0; }else{ ra = 1.0 - ((1.0 - base.r) * (1.0 - overlay.r) * 2.0); } mediump float ga; if (base.g < 0.5) { ga = overlay.g * base.g * 2.0; } else { ga = 1.0 - ((1.0 - base.g) * (1.0 - overlay.g) * 2.0); } mediump float ba; if (base.b < 0.5) { ba = overlay.b * base.b * 2.0; } else { ba = 1.0 - ((1.0 - base.b) * (1.0 - overlay.b) * 2.0); } textureColor = vec4(ra, ga, ba, 1.0); gl_FragColor = vec4(textureColor.r, textureColor.g, textureColor.b, 1.0);
五.效果圖
最近在做的一個項目需要展示一個頁面,標題和內容,我以前雖然用過。但是是從手機本地數據庫讀的。現在是從公司PHP網站的服務器讀取。原來用simpleCursorAdapte
我們經常看到使用了ViewPager的App,在每頁上面都會有一個滑塊來標志當前處於哪一頁。在PagerView包裡有android.support.v4.view.Pa
拍照——裁剪,或者是選擇圖片——裁剪,是我們設置頭像或上傳圖片時經常需要的一組操作。上篇講了Camera的使用,這篇講一下
0、在認識HTTP前先認識URL 在我們認識HTTP之前,有必要先弄清楚URL的組成,例如: http://www.******.com/china/index.htm