html5运用canvas完成色调容差抠图作用

日期:2020-10-09 类型:科技新闻 

关键词:ps网页版在线制作,静态网页制作方法,微云网页版,怎么制作一个网页,django网页模板

运用canvas的getImageData,大家能够获得到1张照片每个像素的信息内容,而根据对每个像素信息内容的比照,大家便可以寻找必须消去的像素点。例如下面这1张照片,假如大家要想扣去白色一部分(粉色是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); // 对白色开展抠除,容差为0.2
}
function cutout(canvas, color, range = 0) {
    let context = canvas.getContext('2d');
    let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);
    // pixiArr是1个数字能量数组,每4个数字能量数组元素意味着1个像素点,这4个数字能量数组元素各自对应1个像素的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) 方式3个主要参数各自为 待检验像素点的rgb数字能量数组 、 总体目标像素点的rgb数字能量数组 和 容差范畴 ,这里的容差只是简易用r、g、b的值各自乘以(1 + range)和(1 - range)来测算并比照,不一样的容差主要参数会获得不一样的实际效果↓

range = 0.095

 

range = 0.1

 

range = 0.2

 

自然针对底色是规范的单色的照片就不必须容差了。

界限解决

可是有时大家期待有1个界限,让抠图实际操作不对界限內部的像素导致危害。例如上面的照片,大家期待不容易对角色头像內部的像素导致危害。 假如大家1行1行看来,是否要是在碰到并不是界限像素的情况下终止实际操作,便可以做到实际效果了呢?

大家对每行各自开展扫描仪,界定1个左指针 left 指向这1行的第1个像素,界定1个右指针 right 指向这1行的最终1个像素,并用1个 leftF 标志左侧是不是碰到界限,1个 rightF 标志右侧是不是碰到界限,当没碰到界限时指针就1直向内收拢,直至两个指针都碰到界限或上下指针重叠就跳到下1行,直至全部行都扫描仪结束。

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; // 移到下1个像素
                } 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);
}

 

尽管大约进行了大家的要求,可是看1下上面秀发那为啥会多了1块白色

 

由于大家仅仅只开展了行扫描仪,当左指针碰到秀发时就会终止扫描仪,可是秀发弧度里边的就没法被扫描仪到了,大家还必须开展列扫描仪,更新改造1下上面的方式:

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 包装为以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}, {r: 255, g: 255, b: 255, a: 1}]
]

解决后测算像素下标也就会更简易,列扫描仪时立即先将这个引流矩阵转动,再用行扫描仪解决1遍就可以了。这样解决pixiArr也是有利于进1步对优化算法开展提升。

上述方式尽管大约进行了抠图实际效果,可是这类简易解决还会有很多状况沒有考虑到到。

例如右侧秀发这里是行扫描仪和列扫描仪都没法碰触到的地区↓

 

下面的衣服也由于色调和底色1样且沒有界限在列扫描仪中被立即抹去了↓

 

最终

针对行为主体和底色区别度很大的照片来讲,最初的那种方式就早已够用了。这篇中我选用的容差和界限解决优化算法的提升室内空间还很大,可是它们是是非非常非常容易完成与了解的,这篇权作为1个引子,各位彻底能够依据自身的工作能力再次去完成界限与容差优化算法。

总结

以上所述是网编给大伙儿详细介绍的html5运用canvas完成色调容差抠图作用,期待对大伙儿有一定的协助,假如大伙儿有任何疑惑请给我留言,网编会立即回应大伙儿的。在此也十分谢谢大伙儿对脚本制作之家网站的适用!
假如你感觉本文对你有协助,欢迎转载,烦请注明出处,感谢!