javascript
canvas 实现图片局部模糊_JavaScript中的图片处理与合成(四)
引言:
本系列現(xiàn)在構(gòu)思成以下4個(gè)部分:
- 基礎(chǔ)類型圖片處理技術(shù)之縮放、裁剪與旋轉(zhuǎn)(傳送門);
- 基礎(chǔ)類型圖片處理技術(shù)之圖片合成(傳送門);
- 基礎(chǔ)類型圖片處理技術(shù)之文字合成(傳送門);
- 算法類型圖片處理技術(shù)(傳送門);
通過(guò)這些積累,我封裝了幾個(gè)項(xiàng)目中常用的功能:
圖片合成 圖片裁剪 人像摳除
之前文章主要介紹了裁剪/旋轉(zhuǎn)/合成等基礎(chǔ)類型的圖片處理(文字的合成編寫中... ),我們開始來(lái)介紹算法類型的圖片處理技術(shù)!~~??????
這類型的重點(diǎn)主要在于 算法 和 性能 層面,在前端由于js及設(shè)備性能的限制,通常表現(xiàn)并不理想。在真正的線上業(yè)務(wù)中,為了追求更好的用戶體驗(yàn),只能運(yùn)行一些相對(duì)比較輕量級(jí)的,性能好的算法。由服務(wù)端來(lái)進(jìn)行進(jìn)行,會(huì)是更好的選擇。
Tips: 由于我對(duì)算法方面并沒(méi)有很深的理解,因此本文主要是一些算法外層及基礎(chǔ)原理的講解,不涉及算法本身。希望大家諒解哈~我們以下面兩個(gè) 來(lái)做初步的了解:
(一) 萬(wàn)圣節(jié)小應(yīng)用
萬(wàn)圣節(jié)效果
效果圖如下:
這個(gè)小應(yīng)用是一個(gè)萬(wàn)圣節(jié)活動(dòng)。人物臉部的木偶妝容確實(shí)很炫酷,但是這里需要復(fù)雜的人臉識(shí)別,模型比對(duì)以及妝容算法,放在前端性能堪憂,因此讓服務(wù)端來(lái)處理,顯然是更好的選擇。而邊框和背景圖的模糊處理,這類型的處理就比較適合放在前端了,首先性能能接受,而且更具靈活性,能在不同入口隨時(shí)替換不同的邊框素材。
對(duì)于服務(wù)端的妝容算法,由于我對(duì)算法并沒(méi)有深入研究,在這里就不班門弄斧了,我們就直接來(lái)梳理下前端的部分:
- 發(fā)送原圖給服務(wù)端,接受 妝容處理 后的效果圖;
- 下載效果圖后,縮放成合適大小后進(jìn)行 模糊化處理 ,得到模糊后的結(jié)果圖;
- 將結(jié)果圖 / 模糊圖 / 邊框進(jìn)行 像素級(jí)的融合 ;
算法性能提升
圖片算法處理實(shí)質(zhì)原理其實(shí)是 遍歷像素點(diǎn),對(duì)像素點(diǎn)的RGBA值進(jìn)行改造。對(duì)于改造算法本身,本文就不深入了,不過(guò)可以與大家分享下相關(guān)的經(jīng)驗(yàn)。
眾所周知,一個(gè)好的算法,一個(gè)最重要的指標(biāo)便是性能,而如何提升性能呢?一種是 算法優(yōu)化 ,提高循環(huán)內(nèi)部的性能或者優(yōu)化遍歷算法,算法中的性能會(huì)由于遍歷的存在被放大無(wú)數(shù)倍。另一種則是 減少像素點(diǎn)。
像素點(diǎn)的遍歷是一個(gè)算法的重要性能消耗點(diǎn),循環(huán)次數(shù)直接決定著算法的性能。而像素點(diǎn)的數(shù)量與圖片的大小尺寸成正向指數(shù)級(jí)增長(zhǎng),因此 適當(dāng)?shù)目s放圖片源后再去處理,對(duì)性能的提升十分巨大。例如一張2000*2000的圖片,像素點(diǎn)足足有400萬(wàn)個(gè),意味著需要遍歷400萬(wàn)次,而把圖片縮小成 800*800 時(shí),循環(huán)次數(shù)為64萬(wàn),這里我做過(guò)一個(gè)測(cè)試:
let st = new Date().getTime(); let imgData = []; for (let i = 0; i < n * 10000; i += 4) {let r = getRandom(0,255),g = getRandom(0,255),b = getRandom(0,255),a = 1;if (r <= 30 && g <= 30 && b<= 30) a = 0;imgData[i] = r;imgData[i + 1] = g;imgData[i + 2] = b;imgData[i + 3] = a; } let et = new Date().getTime(); let t = et - st; console.log(`${n}萬(wàn)次耗時(shí):${et - st}ms`, imgData);測(cè)試結(jié)果為(mac-chrome-20次取平均):
| 圖片尺寸 | 像素?cái)?shù)量 | 耗時(shí)(ms) | 縮放倍數(shù) | 提升 | |:-------:|:--------:|:--------:|:------:|:-----:| |2000*2000| 400萬(wàn) | 168 | 1 | 0% | |1600*1600| 256萬(wàn) | 98 | 0.8 | 42% | |1200*1200| 144萬(wàn) | 64 |0.6 | 62% | |800*800| 64萬(wàn) | 32 |0.4 | 81% | |400*400| 16萬(wàn) | 10 |0.2| 94% |
可以看出圖片的縮小,對(duì)性能有非常顯著的提升。這里有個(gè)特點(diǎn),性能收益會(huì)隨著縮放系數(shù)的變大而越來(lái)越低,當(dāng)縮放系數(shù)為0.8時(shí),性能已經(jīng)大大提升了42%,而繼續(xù)縮放為0.6時(shí),收益便開始大幅降低,只提升了20%。同時(shí)縮放圖片意味著質(zhì)量的下降,所以這里需要尋找一個(gè) 平衡點(diǎn) ,在不影響結(jié)果圖效果的前提下,盡可能地提升性能,這需要根據(jù)算法對(duì)圖片質(zhì)量的要求來(lái)定。
另外,對(duì) 原圖的裁剪也是個(gè)很好的辦法,裁剪掉多余的背景部分,也能達(dá)到減少遍歷次數(shù),提升性能的效果。
模糊算法
小應(yīng)用中模糊部分使用的是 StackBlur.js 的模糊算法,應(yīng)用代碼如下:
// 縮放妝容圖; let srcImg = scaleMid(imgData);// 創(chuàng)建模糊結(jié)果圖的容器; let blurCvs = document.createElement('canvas'),blurCtx = blurCvs.getContext('2d');// 先復(fù)制一份原圖數(shù)據(jù),; let blurImg = blurCtx.createImageData(srcImg.width, srcImg.height); let size = srcImg.width * srcImg.height * 4; for (let i = 0; i < size; i++) {blurImg.data[i] = srcImg.data[i]; }// 縮放成400*400的大小; blurImg = scale(blurImg, 400);// 進(jìn)行模糊處理; StackBlur.imageDataRGBA(blurImg, 0, 0, blurImg.width, blurImg.height, 1);// 處理完后再放大為800*800; blurImg = scale(blurImg, 800);圖像融合
我們已經(jīng)準(zhǔn)備好合成最終效果圖的所有素材了,模糊背景 / 妝容圖 / 邊框素材,最后一步便是將三者進(jìn)行融合,融合的原理是 根據(jù)最終效果圖分區(qū)域,在不同區(qū)域分別填入對(duì)應(yīng)的素材數(shù)據(jù):
// 圖片融合 function mix(src, blur, mtl) {// 最終結(jié)果圖為固定800*800;縱向800的數(shù)據(jù);for (let i = 0; i < 800; i++) {let offset1 = 800 * i;// 橫向800的數(shù)據(jù);for (let j = 0; j < 800; j++) {let offset = (offset1 + j) * 4;// 在特定的位置填入素材;if (i <= 75 || i >= 609 || j <= 126 || j >= 676) {let alpha = mtl.data[offset + 3] / 255.0;mtl.data[offset] = alpha * mtl.data[offset] + (1 - alpha) * blur.data[offset];mtl.data[offset + 1] = alpha * mtl.data[offset + 1] + (1 - alpha) * blur.data[offset + 1];mtl.data[offset + 2] = alpha * mtl.data[offset + 2] + (1 - alpha) * blur.data[offset + 2];mtl.data[offset + 3] = 255;} else {let alpha = mtl.data[offset + 3] / 255.0;let x = i - 75;let y = j - 126;let newOffset = (x * 550 + y) * 4;mtl.data[offset] = alpha * mtl.data[offset] + (1 - alpha) * src.data[newOffset];mtl.data[offset + 1] = alpha * mtl.data[offset + 1] + (1 - alpha) * src.data[newOffset + 1];mtl.data[offset + 2] = alpha * mtl.data[offset + 2] + (1 - alpha) * src.data[newOffset + 2];mtl.data[offset + 3] = 255;}}}return mtl; }(二) 摳除人像
這是一個(gè)基于服務(wù)端的人像mask層,在前端把人像摳出的服務(wù),這樣便可以進(jìn)一步做背景的融合和切換,現(xiàn)在已經(jīng)用在多個(gè)線上項(xiàng)目中了。
人像摳除
這里需要基于由服務(wù)端處理后的兩張效果圖:
帶背景的結(jié)果圖和mask圖:
1、我們需要先將mask圖進(jìn)行處理:
// 繪制mask; // mask_zoom: 既為了優(yōu)化性能所做的縮放系數(shù); mask = document.createElement('canvas'); maskCtx = mask.getContext('2d'); mask.width = imgEl.naturalWidth * ops.mask_zoom; mask.height = imgEl.naturalHeight * ops.mask_zoom / 2; maskCtx.drawImage(imgEl, 0, - imgEl.naturalHeight * ops.mask_zoom / 2, imgEl.naturalWidth * ops.mask_zoom , imgEl.naturalHeight * ops.mask_zoom);2、去除mask的黑色背景,變成透明色,這里需要用到像素操作:
// 獲取圖片數(shù)據(jù); let maskData = maskCtx.getImageData(0, 0, mask.width, mask.height);// 遍歷改造像素點(diǎn),將接近黑色的點(diǎn)的透明度改成0; for (let i = 0; i < data.length; i += 4) {let r = data[i],g = data[i + 1],b = data[i + 2];if (r <= 30 && g <= 30 && b<= 30)data[i + 3] = 0; }// 將改造后的數(shù)據(jù)重新填回mask層中; maskCtx.putImageData(maskData, 0, 0);3、圖像融合,這里用到了一個(gè)神奇的canvas方法,相信大家聽(tīng)過(guò),但并不熟悉 --- globalCompositeOperation,該值可以修改canvas的融合模式,有多種融合模式大家可以自行研究,這里使用的是source-in;
// 創(chuàng)建最終效果圖容器; result = document.createElement('canvas'); resultCtx = result.getContext('2d'); result.width = imgEl.naturalWidth; result.height = imgEl.naturalHeight;// 先繪制mask圖層做為背景; resultCtx.drawImage(mask, 0, 0, imgEl.naturalWidth, imgEl.naturalHeight);// 修改融合模式 resultCtx.globalCompositeOperation = 'source-in';// 繪制帶背景的結(jié)果圖 resultCtx.drawImage(origin, 0, 0);最終得到的效果圖:
最后就可以使用這種人像圖與任何背景或者素材根據(jù)業(yè)務(wù)需求再做融合了。
結(jié)語(yǔ)
算法部分由于本人才疏學(xué)淺,沒(méi)辦法深入闡述,只能分享一些比較粗淺的經(jīng)驗(yàn),期望能在未來(lái)往更底層的領(lǐng)域發(fā)展。
不過(guò)還是期望能對(duì)大家有所啟發(fā)。~~有圖片處理興趣的童鞋,可以隨時(shí)與我探討哈,期望得到大神的指導(dǎo),也希望js能在圖片處理領(lǐng)域有更快的發(fā)展。
感謝大家的閱讀。~~~ 。
總結(jié)
以上是生活随笔為你收集整理的canvas 实现图片局部模糊_JavaScript中的图片处理与合成(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 将null转换成数组_Javscript
- 下一篇: oraclemt 无法启动服务_电脑文件