中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用canvas實現顏色容差摳圖

發布時間:2022-02-28 16:03:34 來源:億速云 閱讀:115 作者:iii 欄目:開發技術

本篇內容介紹了“怎么用canvas實現顏色容差摳圖”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

 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); // 對白色進行摳除,容差為0.2
}
function cutout(canvas, color, range = 0) {
    let context = canvas.getContext('2d');
    let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);
    // pixiArr是一個數組,每四個數組元素代表一個像素點,這四個數組元素分別對應一個像素的r,g,b,a值。
    let pixiArr = imageInfo.data;
    for (let i = 0; i < pixiArr.length; i += 4) {
    // 匹配到目標像素就將目標像素的alpha設為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) 方法三個參數分別為 待檢測像素點的rgb數組 、 目標像素點的rgb數組 和 容差范圍 ,這里的容差只是簡單用r、g、b的值分別乘以(1 + range)和(1 - range)來計算并對比,不同的容差參數會得到不同的效果↓

range = 0.095

range = 0.1

range = 0.2

當然對于底色是標準的純色的圖片就不需要容差了。

邊界處理

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

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

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) { // 當左右指針都為true,即都碰到邊界時結束
            if (!leftF) {
                if (testColor([pixiArr[left], pixiArr[left + 1], pixiArr[left + 2]], color, range)) {
                    pixiArr[left + 3] = 0; // 此像素的alpha設為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;
            };
        }
    }
    context.putImageData(imageInfo, 0, 0);
}

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

因為我們僅僅只進行了行掃描,當左指針碰到頭發時就會停止掃描,但是頭發弧度里面的就無法被掃描到了,我們還需要進行列掃描,改造一下上面的方法:

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;
            };
        }
    }
    // 同理進行列掃描
    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為啥是那樣計算畫個矩陣圖大概就知道了。

處理后↓

其實還可以先將 pixiArr 包裝為以一個像素點為單位的矩陣

[
    [{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}]
]

處理后計算像素下標也就會更簡單,列掃描時直接先將這個矩陣旋轉,再用行掃描處理一遍就行了。這樣處理pixiArr也有利于進一步對算法進行優化。

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

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

下面的衣服也因為顏色和底色一樣且沒有邊界在列掃描中被直接抹去了↓

“怎么用canvas實現顏色容差摳圖”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

黔西县| 武邑县| 宜良县| 平山县| 绥滨县| 固安县| 咸阳市| 四会市| 永嘉县| 城固县| 资兴市| 门源| 定安县| 宁蒗| 兰溪市| 稻城县| 扶余县| 普兰县| 杭锦旗| 龙岩市| 河南省| 神农架林区| 竹山县| 双流县| 易门县| 漳州市| 分宜县| 盐津县| 平利县| 九江县| 长岭县| 峡江县| 汪清县| 内江市| 惠安县| 称多县| 韶山市| 尉犁县| 茌平县| 商南县| 精河县|