如何用 canvas 做阈值效应?

How can I do a threshold effect with canvas?

我找到了使用 canvas 和 JavaScript 展示图片 filters/effects 的指南:https://www.html5rocks.com/en/tutorials/canvas/imagefilters

虽然,我不知道如何实现它。我不擅长 JavaScript(我是一名平面设计师),所以我什至不理解语法。这段代码是我从页面中提取出来的,不知道是否完整,不知道如何使用:

Filters = {};

Filters.threshold = function(pixels, threshold) {
    var d = pixels.data;
    for (var i=0; i<d.length; i+=4) {
        var r = d[i];
        var g = d[i+1];
        var b = d[i+2];
        var v = (0.2126*r + 0.7152*g + 0.0722*b >= threshold) ? 255 : 0;
        d[i] = d[i+1] = d[i+2] = v
    }
    return pixels;
};

threshold = function() {
  runFilter('threshold', Filters.threshold, 128);
}

I want to replicate this effect 在 div(具有 CSS background-image 属性)、imgcanvas 元素上,这无关紧要只要它有效。

如果您想在 HTML <div> 上应用此过滤器,那么 canvas 解决方案并不是最好的。

请转而查看 CSS filters for the simple cases, or SVG filters 以获得更复杂的内容。

你想要的很简单,只需 CSS 个过滤器即可实现:

#target {
  background-image: url(https://i.stack.imgur.com/5Md7Z.jpg);
  width: 600px;
  height: 600px;
  filter: brightness(115%) grayscale(100%) contrast(5000%);
}
<div id="target"></div>

过滤器适用于图像数据 ImageData。图像数据无法显示,因此需要转换为某种可显示的形式,例如 canvas.

你还需要从图像中获取图像数据。标准图像无法访问像素,因此您需要将图像转换为 canvas 才能获取像素。

请注意 不安全的图像(跨域、本地文件存储)可能无法为您提供像素访问权限。如果您在请求中提供正确的 CORS headers 并且服务器接受请求(如下例所示),一些跨域图像将允许访问。

例子

我已经在过滤器 object 中添加了一些辅助函数,它们可以从各种图像源中复制、创建和获取像素。

示例中的

Filter 抽象了术语图像。就示例而言,图像是 object 之类的图像,可以是 CSSImageValueHTMLImageElementSVGImageElementHTMLVideoElementHTMLCanvasElement 中的任何一个, ImageBitmap, OffscreenCanvasImageData.

const image = new Image;
image.src = "https://upload.wikimedia.org/wikipedia/commons/1/15/Late_model_Ford_Model_T.jpg";
image.crossOrigin = "Anonymous"; // CORS 
image.addEventListener("load", () => applyFilter(image), {once: true});
const Filters = {
    createImage(w, h) {
        const can = document.createElement("canvas");
        can.width = w;
        can.height= h;  
        return can;
    },
    copyImage(img) {
        const image = this.createImage(img.width, img.height);
        const ctx = image.getContext("2d");
        if (img instanceof ImageData) { ctx.putImageData(img, 0, 0) }
        else { ctx.drawImage(img, 0, 0, img.width, img.height) }
        return image;
    },
    getPixels(img) {
        if (!(img instanceof HTMLCanvasElement)) { img = this.copyImage(img) }
        const ctx = img.getContext("2d");
        return ctx.getImageData(0, 0, img.width, img.height);
    },
    threshold(pixels, threshold, light = [255,255,255], dark = [0,0,0]) { // light, dark arrays of RGB
        var d = pixels.data, i = 0, l = d.length;
        while (l-- > 0) {
            const v = d[i] * 0.2126 + d[i+1] * 0.7152 + d[i+2] * 0.0722;
            [d[i], d[i+1], d[i+2]] = v >= threshold ? light : dark;
            i += 4;
        }
        return pixels;
    }

};
function applyFilter(image) {
    const pixels = Filters.getPixels(image);
    Filters.threshold(pixels, 100);
    const thresholdImage = Filters.copyImage(pixels);
    att.classList.remove("hide"); // Image can be seen so show attribution
    document.body.appendChild(image); // add original to page
    document.body.appendChild(thresholdImage); // add filtered to page 
}

/* Image source 
   By Rmhermen - Transferred from en.wikipedia to Commons., CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=468996 
*/
.hide {display: none}
div { font-size: x-small }
canvas { width: 46% }
img { width: 46% }
<div id="att" class="hide">By Rmhermen - Transferred from en.wikipedia to Commons., CC BY-SA 3.0, <a href="https://commons.wikimedia.org/w/index.php?curid=468996">image source</a></div>