国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > HTML5 > 正文

html5利用canvas實(shí)現(xiàn)顏色容差摳圖功能

2024-09-05 07:23:11
字體:
供稿:網(wǎng)友

利用canvas的getImageData,我們可以獲取到一張圖片每一個(gè)像素的信息,而通過對每一個(gè)像素信息的對比,我們就可以找到需要消去的像素點(diǎn)。比如下面這一張圖片,如果我們想要扣去白色部分(粉色是body的背景顏色)。

 

let canvas = document.querySelector('#canvas');let context = canvas.getContext('2d');let img = document.createElement('img');img.src = './head2.png';img.onload = function () {    canvas.height = img.height;    canvas.width = img.width;    context.drawImage(img, 0, 0);    cutout(canvas, [255, 255, 255], 0.2); // 對白色進(jìn)行摳除,容差為0.2}function cutout(canvas, color, range = 0) {    let context = canvas.getContext('2d');    let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);    // pixiArr是一個(gè)數(shù)組,每四個(gè)數(shù)組元素代表一個(gè)像素點(diǎn),這四個(gè)數(shù)組元素分別對應(yīng)一個(gè)像素的r,g,b,a值。    let pixiArr = imageInfo.data;    for (let i = 0; i < pixiArr.length; i += 4) {    // 匹配到目標(biāo)像素就將目標(biāo)像素的alpha設(shè)為0        if (testColor([pixiArr[i], pixiArr[i + 1], pixiArr[i + 2]], color, range)) pixiArr[i + 3] = 0;    }    context.putImageData(imageInfo, 0, 0);}function testColor(current, target, range) {    for (let i = 0; i < 3; i++) {        if (!((1 - range) * target[i] <= current[i] && (1 + range) * target[i] >= current[i])) return false;    }    return true;}

 

testColor(current, target, range) 方法三個(gè)參數(shù)分別為 待檢測像素點(diǎn)的rgb數(shù)組 、 目標(biāo)像素點(diǎn)的rgb數(shù)組 和 容差范圍 ,這里的容差只是簡單用r、g、b的值分別乘以(1 + range)和(1 - range)來計(jì)算并對比,不同的容差參數(shù)會得到不同的效果↓

range = 0.095

 

range = 0.1

 

range = 0.2

 

當(dāng)然對于底色是標(biāo)準(zhǔn)的純色的圖片就不需要容差了。

邊界處理

但是有時(shí)候我們希望有一個(gè)邊界,讓摳圖操作不對邊界內(nèi)部的像素造成影響。比如上面的圖片,我們希望不會對人物頭像內(nèi)部的像素造成影響。 如果我們一行一行來看,是不是只要在碰到不是邊界像素的時(shí)候停止操作,就可以達(dá)到效果了呢?

我們對每一行分別進(jìn)行掃描,定義一個(gè)左指針 left 指向這一行的第一個(gè)像素,定義一個(gè)右指針 right 指向這一行的最后一個(gè)像素,并用一個(gè) leftF 標(biāo)識左邊是否碰到邊界,一個(gè) rightF 標(biāo)識右邊是否碰到邊界,當(dāng)沒碰到邊界時(shí)指針就一直向內(nèi)收縮,直到兩個(gè)指針都碰到邊界或者左右指針重合就跳到下一行,直到所有行都掃描完畢。

function cutout(canvas, color, range = 0) {    let context = canvas.getContext('2d');    let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);    let pixiArr = imageInfo.data;    for (let row = 0; row < canvas.height; row++) {        let left = row * 4 * canvas.width; // 指向行首像素        let right = left + 4 * canvas.width - 1 - 3; // 指向行尾像素        let leftF = false; // 左指針是否碰到邊界的標(biāo)識        let rightF = false; // 右指針是否碰到邊界的標(biāo)識        while (!leftF || !rightF) { // 當(dāng)左右指針都為true,即都碰到邊界時(shí)結(jié)束            if (!leftF) {                if (testColor([pixiArr[left], pixiArr[left + 1], pixiArr[left + 2]], color, range)) {                    pixiArr[left + 3] = 0; // 此像素的alpha設(shè)為0                    left += 4; // 移到下一個(gè)像素                } else leftF = true; // 碰到邊界            }            if (!rightF) {                if (testColor([pixiArr[right], pixiArr[right + 1], pixiArr[right + 2]], color, range)) {                    pixiArr[right + 3] = 0;                    right -= 4;                } else rightF = true;            }            if (left == right) { // 左右指針重合                leftF = true;                rightF = true;            };        }    }    context.putImageData(imageInfo, 0, 0);}

 

雖然大概完成了我們的需求,但是看一下上面頭發(fā)那為啥會多了一塊白色

 

因?yàn)槲覀儍H僅只進(jìn)行了行掃描,當(dāng)左指針碰到頭發(fā)時(shí)就會停止掃描,但是頭發(fā)弧度里面的就無法被掃描到了,我們還需要進(jìn)行列掃描,改造一下上面的方法:

function cutout(canvas, color, range = 0) {    let context = canvas.getContext('2d');    let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);    let pixiArr = imageInfo.data;    for (let row = 0; row < canvas.height; row++) {        let left = row * 4 * canvas.width;        let right = left + 4 * canvas.width - 1 - 3;        let leftF = false;        let rightF = false;        while (!leftF || !rightF) {            if (!leftF) {                if (testColor([pixiArr[left], pixiArr[left + 1], pixiArr[left + 2]], color, range)) {                    pixiArr[left + 3] = 0;                    left += 4;                } else leftF = true;            }            if (!rightF) {                if (testColor([pixiArr[right], pixiArr[right + 1], pixiArr[right + 2]], color, range)) {                    pixiArr[right + 3] = 0;                    right -= 4;                } else rightF = true;            }            if (left == right) {                leftF = true;                rightF = true;            };        }    }    // 同理進(jìn)行列掃描    for (let col = 0; col < canvas.width; col++) {        let top = col * 4; // 指向列頭        let bottom = top + (canvas.height - 2) * canvas.width * 4 + canvas.width * 4; // 指向列尾        let topF = false;        let bottomF = false;        while (!topF || !bottomF) {            if (!topF) {                if (testColor([pixiArr[top], pixiArr[top + 1], pixiArr[top + 2]], color, range)) {                    pixiArr[top + 3] = 0;                    top += canvas.width * 4;                } else topF = true;            }            if (!bottomF) {                if (testColor([pixiArr[bottom], pixiArr[bottom + 1], pixiArr[bottom + 2]], color, range)) {                    pixiArr[bottom + 3] = 0;                    bottom -= canvas.width * 4;                } else bottomF = true;            }            if (top == bottom) {                topF = true;                bottomF = true;            };        }    }    context.putImageData(imageInfo, 0, 0);}

 

至于top和bottom為啥是那樣計(jì)算畫個(gè)矩陣圖大概就知道了。

 

處理后↓

 

其實(shí)還可以先將 pixiArr 包裝為以一個(gè)像素點(diǎn)為單位的矩陣

[    [{r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}],    [{r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}]    [{r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}]]

處理后計(jì)算像素下標(biāo)也就會更簡單,列掃描時(shí)直接先將這個(gè)矩陣旋轉(zhuǎn),再用行掃描處理一遍就行了。這樣處理pixiArr也有利于進(jìn)一步對算法進(jìn)行優(yōu)化。

上述方法雖然大概完成了摳圖效果,但是這種簡單處理還會有許多情況沒有考慮到。

比如右邊頭發(fā)這里是行掃描和列掃描都無法觸碰到的區(qū)域↓

 

下面的衣服也因?yàn)轭伾偷咨粯忧覜]有邊界在列掃描中被直接抹去了↓

 

最后

對于主體和底色區(qū)分度很大的圖片來說,最開始的那種方法就已經(jīng)夠用了。這篇中我采用的容差和邊界處理算法的優(yōu)化空間還很大,但是它們是非常容易實(shí)現(xiàn)與理解的,這篇權(quán)當(dāng)做一個(gè)引子,各位完全可以根據(jù)自己的能力繼續(xù)去實(shí)現(xiàn)邊界與容差算法。

總結(jié)

以上所述是小編給大家介紹的html5利用canvas實(shí)現(xiàn)顏色容差摳圖功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對武林網(wǎng)網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 吉林省| 聂荣县| 乌拉特前旗| 喀喇| 阿坝县| 江安县| 西安市| 车致| 中牟县| 辽中县| 固镇县| 柳河县| 宽甸| 屏东县| 茌平县| 明星| 钟祥市| 拉萨市| 鄂托克旗| 内江市| 大城县| 岳阳县| 东安县| 上思县| 潼南县| 古蔺县| 祁东县| 丰都县| 苏州市| 通化县| 清远市| 安庆市| 武隆县| 长阳| 徐汇区| 县级市| 兰州市| 玛多县| 屯留县| 安平县| 黔西县|