一、canvas的情況
canvas自從出來了之后,在前端的圖像處理上面提供了各種各樣的遍歷,雖然很多的操作其實都是要應用到算法的,但是這個也給前端提供了很多的可能性,其中最終要的一個canvas函數(至少我認為)就是getImageData,這個函數可以提取圖像每個像素的RGBA值。因為有這個函數所有才有豐富多彩的canvas圖像處理。
二、預備知識
2.1 獲取一個canvas對象
<canvas id="test"></canvas> <script> var test = document.querySelector("#test");// 方法一 var test_1=document.getElementById("test");// 方法二 console.log(test); console.log(test_1); </script>
2.2 創建一個畫布的空間類型(2D,3D)
創建一個2D的畫布
var ctx = test.getContext("2d");
2.3 getImageData對象
getImageData對象可以獲取畫布中的圖片對應的所有像素的RGBA值,這個有利於我們對圖片進行重新的計算。在使用這個屬性的時候,需要配置好HTTP的訪問環境。
2.4 Uint8ClampedArray
這個代表的是一個無類型8位的字符串,也就是說明最多存儲到255
2.5 Uint8ClampedArray與getImageData對象
Uint8ClampedArray包含在getImageData對象中,getImageData除了有Uint8ClampedArray之外,還有獲取圖片數據的長度和寬度。
三、多種圖片效果canvas實現
3.1 原圖
通過canvas來渲染原圖,首選我們在網上隨意找一張圖片。
·
window.onload = function () { var img = new Image(); img.src = "rx.jpg"; // 加載完成圖片之后才能夠執行canvas的操作 img.onload = function () { var canvas = document.querySelector("#canvas"); var cxt = canvas.getContext("2d"); canvas.width=293; canvas.height=220; cxt.drawImage(img, 0, 0, 293, 220); } }
其中canvas的width,height屬性都是必須要設置的,不設置的話,canvas會采用默認的width=300,height=150來設置圖片。
3.2 反色(負片)效果
圖片反色的原理其實很簡單不多做解釋:將圖片中的每一個元素進行如下的公式運算就可以得到最終的結果
window.onload = function () { var img = new Image(); img.src = "rx.jpg"; img.onload = function () { var canvas = document.querySelector("#canvas"); var cxt = canvas.getContext("2d"); canvas.width = 293; canvas.height = 220; cxt.drawImage(img, 0, 0, canvas.width, canvas.height); var imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); var imageData_length = imageData.data.length / 4; // 解析之后進行算法運算 for (var i = 0; i < imageData_length; i++) { imageData.data[i * 4] = 255 - imageData.data[i * 4]; imageData.data[i * 4 + 1] = 255 - imageData.data[i * 4 + 1]; imageData.data[i * 4 + 2] = 255 - imageData.data[i * 4 + 2]; } cxt.putImageData(imageData, 0, 0); } }
運行之后的效果如下:
3.3 去色效果
這里說的去色效果與第二種效果不同的是,去色效果相當於就是老舊相機拍出來的黑白照片。
要得到去色效果的照片有很多種方法,但是鹵煮比較推崇的做法是采用基於人眼感覺的加權平均數來實現,這個算法的原理是采用人眼對RGB不同顏色的敏感程度不同,然后通過得出的加權平均數來運算出最后的結果
Gray = (Red * 0.3 + Green * 0.59 + Blue * 0.11)
代碼如下實現:
window.onload = function () { var img = new Image(); img.src = "rx.jpg"; img.onload = function () { var canvas = document.querySelector("#canvas"); var cxt = canvas.getContext("2d"); canvas.width = 293; canvas.height = 220; cxt.drawImage(img, 0, 0, canvas.width, canvas.height); var imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); var imageData_length = imageData.data.length / 4; // 解析之后進行算法運算 for (var i = 0; i < imageData_length; i++) { var red = imageData.data[i * 4]; var green = imageData.data[i * 4 + 1]; var blue = imageData.data[i * 4 + 2]; var gray = 0.3 * red + 0.59 * green + 0.11 * blue; imageData.data[i * 4] = gray; imageData.data[i * 4 + 1] = gray; imageData.data[i * 4 + 2] = gray; } cxt.putImageData(imageData, 0, 0); } }
運行效果如下:
3.4 單色效果
單顏色效果原理就是將當前像素的其他色值去除。
假設我們要實現的單顏色效果是紅色,那么實現的代碼如下:
window.onload = function () { var img = new Image(); img.src = "rx.jpg"; img.onload = function () { var canvas = document.querySelector("#canvas"); var cxt = canvas.getContext("2d"); canvas.width = 293; canvas.height = 220; cxt.drawImage(img, 0, 0, canvas.width, canvas.height); var imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); var imageData_length = imageData.data.length / 4; // 解析之后進行算法運算 for (var i = 0; i < imageData_length; i++) { imageData.data[i * 4 + 1] = 0; imageData.data[i * 4 + 2] = 0; } cxt.putImageData(imageData, 0, 0); } }
效果圖:
3.5 中國版畫效果
中國版畫不同於去色和反色的效果,在中國版畫的效果中除了黑就是白色,不存在其他的顏色,下面就是一張傳統的中國版畫的效果。
這個的實現算法比較的靈活一般是根據你要得到的效果來進行參數配置的,原理就是通過判斷當前元素的色值是否高於這個給定值,高於我們就顯示為黑色,小於我們就顯示為白色這樣的一種方法來實現的。
按照鹵煮一般的設置值來說會設置為126.因為126是2的8次方的中間數。相對來說比較的對稱
實現的代碼如下:
window.onload = function () { var img = new Image(); img.src = "rx.jpg"; img.onload = function () { var canvas = document.querySelector("#canvas"); var cxt = canvas.getContext("2d"); canvas.width = 293; canvas.height = 220; cxt.drawImage(img, 0, 0, canvas.width, canvas.height); var imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); var imageData_length = imageData.data.length / 4; // 解析之后進行算法運算 for (var i = 0; i < imageData_length; i++) { var red = imageData.data[i * 4]; var green = imageData.data[i * 4 + 1]; var blue = imageData.data[i * 4 + 2]; var gray = 0.3 * red + 0.59 * green + 0.11 * blue; var new_black; if (gray > 126) { new_black = 255; } else { new_black = 0; } imageData.data[i * 4] = new_black; imageData.data[i * 4 + 1] = new_black; imageData.data[i * 4 + 2] = new_black; } cxt.putImageData(imageData, 0, 0); } }
運行的效果如下:
如果是我們要渲染更多的黑顏色的話,我們應該要將值調高,反之將其調低,下面我們把值調到150運行一下:
3.6 高斯模糊效果
高斯模糊是一種兩維的卷積模糊操作,簡單的介紹就是通過讓圖片的每個像素與四周的像素按照某種權重進行分布求值,要了解具體的同學可以戳戳這里。
這里我們就展示結論,推導不做介紹(二維高斯分布函數):
我們直接貼出實現的代碼:
function gaussBlur(imgData) { console.log(imgData); var pixes = imgData.data; var width = imgData.width; var height = imgData.height; var gaussMatrix = [], gaussSum = 0, x, y, r, g, b, a, i, j, k, len; var radius = 30; var sigma = 5; a = 1 / (Math.sqrt(2 * Math.PI) * sigma); b = -1 / (2 * sigma * sigma); //生成高斯矩陣 for (i = 0, x = -radius; x <= radius; x++, i++){ g = a * Math.exp(b * x * x); gaussMatrix[i] = g; gaussSum += g; } //歸一化, 保證高斯矩陣的值在[0,1]之間 for (i = 0, len = gaussMatrix.length; i < len; i++) { gaussMatrix[i] /= gaussSum; } //x 方向一維高斯運算 for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { r = g = b = a = 0; gaussSum = 0; for(j = -radius; j <= radius; j++){ k = x + j; if(k >= 0 && k < width){//確保 k 沒超出 x 的范圍 //r,g,b,a 四個一組 i = (y * width + k) * 4; r += pixes[i] * gaussMatrix[j + radius]; g += pixes[i + 1] * gaussMatrix[j + radius]; b += pixes[i + 2] * gaussMatrix[j + radius]; // a += pixes[i + 3] * gaussMatrix[j]; gaussSum += gaussMatrix[j + radius]; } } i = (y * width + x) * 4; // 除以 gaussSum 是為了消除處於邊緣的像素, 高斯運算不足的問題 // console.log(gaussSum) pixes[i] = r / gaussSum; pixes[i + 1] = g / gaussSum; pixes[i + 2] = b / gaussSum; // pixes[i + 3] = a ; } } //y 方向一維高斯運算 for (x = 0; x < width; x++) { for (y = 0; y < height; y++) { r = g = b = a = 0; gaussSum = 0; for(j = -radius; j <= radius; j++){ k = y + j; if(k >= 0 && k < height){//確保 k 沒超出 y 的范圍 i = (k * width + x) * 4; r += pixes[i] * gaussMatrix[j + radius]; g += pixes[i + 1] * gaussMatrix[j + radius]; b += pixes[i + 2] * gaussMatrix[j + radius]; // a += pixes[i + 3] * gaussMatrix[j]; gaussSum += gaussMatrix[j + radius]; } } i = (y * width + x) * 4; pixes[i] = r / gaussSum; pixes[i + 1] = g / gaussSum; pixes[i + 2] = b / gaussSum; } } console.log(imgData); return imgData; }
運行之后的效果如下:
3.7 浮雕效果與刻雕效果
這里要感謝CSDN的算法專家gloomyfish,他的博文給我提供了一個好的思路,同時根據他的一個代碼邏輯我們可以得出如下的公式:
(C常量,Xa后一個像素的RGB,Xb前一個像素的RGB)
其中color代表的是最后的色值,Xa和Xb代表的是當前像素前后兩點的RGB(中的某一個值),C代表的是一個常量【根據你的具體需要來定】
原理就是將某個像素與周邊的差值較大的檢測出來,然后替換成為255,一般我們將這個常量C設置成為128【255的一半】
/** * after pixel value - before pixel value + 128 * 代碼引用自 gloomyfish * 浮雕效果 */ reliefProcess: function(context, canvasData) { //caontext 畫布對象 document.querySelector().getContext("2d"); // conavas document.querySelector().getContext("2d").getImageData(); console.log("Canvas Filter - relief process"); var tempCanvasData = this.copyImageData(context, canvasData); for ( var x = 0; x < tempCanvasData.width-1; x++) { for ( var y = 0; y < tempCanvasData.height-1; y++) { // Index of the pixel in the array var idx = (x + y * tempCanvasData.width) * 4; var bidx = ((x-1) + y * tempCanvasData.width) * 4; var aidx = ((x+1) + y * tempCanvasData.width) * 4; // calculate new RGB value var nr = tempCanvasData.data[aidx + 0] - tempCanvasData.data[bidx + 0] + 128; var ng = tempCanvasData.data[aidx + 1] - tempCanvasData.data[bidx + 1] + 128; var nb = tempCanvasData.data[aidx + 2] - tempCanvasData.data[bidx + 2] + 128; nr = (nr < 0) ? 0 : ((nr >255) ? 255 : nr); ng = (ng < 0) ? 0 : ((ng >255) ? 255 : ng); nb = (nb < 0) ? 0 : ((nb >255) ? 255 : nb); // assign new pixel value canvasData.data[idx + 0] = nr; // Red channel canvasData.data[idx + 1] = ng; // Green channel canvasData.data[idx + 2] = nb; // Blue channel canvasData.data[idx + 3] = 255; // Alpha channel } } },
運行的圖像如下:
其中常量的數值越大,浮雕的顏色越淺,反之加深。我們把它設置到50看下效果:
刻雕效果與浮雕效果基本一樣,就是要調整公式里面的Xa和Xb的位置,調整如下:
四、小結
由於canvas涉及到的知識點比較的深奧,所以里面有很多的內容沒法再這里一一的進行說明,這一篇文章也就是對於canvas實際應用的一個簡單的說明,在圖形學方面,磨皮處理和高通濾波,低通濾波等等方面的處理還沒有實現,在一些粒子化,動畫,碰撞方面也不能一一道來,深表遺憾