前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >canvas 像素操作

canvas 像素操作

作者头像
多云转晴
发布2020-01-14 15:54:06
1.7K0
发布2020-01-14 15:54:06
举报
文章被收录于专栏:webTowerwebTower

在 canvas 中可以使用 context.drawImage(image,dx,dy) 方法将图片绘制在 canvas 上。将图片绘制上去后,还可以使用 context.getImageData(sx, sy, sw, sh) 方法获取 canvas 区域隐含的像素数据,该方法返回一个 ImageData 对象,里面包含的是 canvas 像素信息。

getImageData 函数参数

这个函数有四个参数,都是必选的。 格式:context.getImageData(sx, sy, sw, sh)。 其中:

  • sx:将要被提取的图像数据矩形区域的左上角 x 坐标;
  • sy:将要被提取的图像数据矩形区域的左上角 y 坐标;
  • sw:将要被提取的图像数据矩形区域的宽度;
  • sy:将要被提取的图像数据矩形区域的高度;
// 获取整个 canvas 画布上的像素信息
var imageData = context.getImageData(0,0,canvas.width,canvas.height);

console.log(imageData);

需要注意的是,如果是使用 image 对象动态生成 img 图片,然后使用 drawImagegetImageData 方法时,chrome 后可能会报图片跨域错误。

const cvs = document.querySelector("#myCvs");
const ctx = cvs.getContext("2d");

var image = new Image(cvs.height);
image.src = "./05.jpg";

// 图片加载好后在获得像素,不然获得不了
image.onload = function(){
    ctx.drawImage(image,0,0);
    var imageData = ctx.getImageData(0,0,cvs.width,cvs.height);

    console.log(imageData);
}

报错信息:

Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
    at Image.image.onload

解决办法是写一个本地服务器,把 HTML 文件部署到服务器上。

另一个办法是建立一个文件夹(比如 src 目录),把图片和 HTML 文件(HTML 文件命名为 index.html)放入其中,运行 npx serve ./src 命令。

serve 模块

打开 http:localhost:5000 就不会报错了。serve 模块是一个 npm 包,一个开箱即用的服务端模块。

ImageData 对象中有三个属性:

  • width:canvas 的宽度;
  • height:canvas 的高度;
  • data:指定区域的像素数据;

imageData.data 中的像素数据是一个一维正整数数组(Uint8ClampedArray 类型的数组,即:无符号整型),一个像素信息包含 RGB 三原色信息和透明度。data 数组数据每四个为一组,分别表示 RGB 通道和透明度。这四种值取值都在 0-255 之间。

putImageData() 方法

该方法可以将 imageData 对象绘制到 canvas 上。我们可以用 getImageData 将获取到的 imageData 数据处理后再使用 putImageData 方法重新绘制到 canvas 中。

该方法的参数:ctx.putImageData(imagedata, dx, dy);

  • dx:源图像数据在目标画布中的 x 轴方向的偏移量;
  • dy:源图像数据在目标画布中的 y 轴方向的偏移量;

除这两个参数之外还有四个可选属性,这里不做介绍,可以参考 MDN 文档: https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/putImageData[1]

简单的 canvas 的像素操作

在 CSS 颜色值里,可以使用六位十六进制法表示颜色值,比如:#000000 表示纯黑色,还可以使用 rgb 通道表示法表示一个颜色,格式:rgb(red,green,blue)。当值是 rgb(255,255,255) 是就是纯白色,rgb(255,0,0) 表示红色。rgb 通道的取值在 0-255 之间。在 CSS 当中,还定义了 rgba 颜色值,多出来的 a 表示透明度,只不过取值在 0-1 之间,0 表示透明度为 100%(而在 canvas 的像素中,透明度同样是 0-255 之间)。

下面就介绍几个简单且常见的像素处理结果。原始图片均以下面的彩图为例:

绫小路

灰度处理

使用上面的两个 API 就可以随意操作像素数据了。比如下面的例子,将一个彩色的图像变成灰色的图像:

<body>

    <img src="./img/05.jpg" height="423" width="423" />
    <canvas id="myCvs" height="423" width="423"></canvas>

    <button id="btn">gray</button>

    <script>

        const cvs = document.querySelector("#myCvs");
        const ctx = cvs.getContext("2d");
        const btn = document.querySelector("#btn");

        var image = new Image(cvs.height);
        image.src = "./05.jpg";

        image.onload = function(){
            ctx.drawImage(image,0,0);
        }

        btn.onclick = function(){
            // 获取像素信息
            var imageData = ctx.getImageData(0,0,cvs.width,cvs.height);
            var newImageData = gray(imageData);
            ctx.putImageData(newImageData,0,0);
        }

        // 将图像变成黑白图
        function gray(imageData){
            var data = imageData.data;
            var len = data.length;

            for(let i = 0;i < len;i += 4){
                var avg = 0;
                for(let j = 0;j < 3;j ++){
                    // RGB 颜色求平均值
                    avg += (data[i + j] / 3);
                }
                // 把平均值赋给 RGB
                data[i] = data[i + 1] = data[i + 2] = avg;
            }
            return imageData;
        }

    </script>
</body>

当点击按钮后,彩色图片就会变成灰色。黑白图的原理就是取 RGB 通道的均值,再把均值赋值给 RGB 三个通道。处理结果:

灰度图

需要注意的是,imageData.data 中的数据类型都是无符号整型,做平均运算时很可能会出现小数,不过 JavaScript 会自动进行取整操作,当然你也可以使用 Math.floor 或者 Math.ceil 方法进行取整操作。

纯黑白图

灰度处理后,颜色并不是纯白色或者黑色,而是介于黑与白之间。要想将一张图片处理成“纯”黑白的图片可以这样做:

  • 得到 RGB 通道的颜色平均值;
  • 如果平均值是小于 128(256 / 2),那么 RGB 都取 0(黑色);
  • 如果平均值大于等于 128,那么 RGB 都取值 255(白色);

所以,代码是这样的:

function gray(imageData){
    var data = imageData.data;
    var len = data.length;

    for(let i = 0;i < len;i += 4){
        var avg = 0;
        for(let j = 0;j < 3;j ++){
            avg += (data[i + j] / 3);
        }
        //!判断
        data[i] = data[i + 1] = data[i + 2] = avg < 128 ? 0 : 255;
    }
    return imageData;
}

处理结果:

纯黑白

通过上面也可以实现只有红色通道的图片,原理是只将平均值赋给红色通道,其他通道变成 0。

// 红色蒙版

var redMask = function(imageData){
    var data = imageData.data;
    var len = data.length;
    for (let i = 0; i < len; i += 4) {
        let sum = 0;
        for (let j = 0; j < 3; j++) {
            sum += data[i + j];
        }
        var avg = sum / 3;
        data[i] = avg; // 红色是平均值
        // 绿色和蓝色都设为零
        data[i + 1] = data[i + 2] = 0;
    }
    return imageData;
}

效果:

红色蒙版

可以试着将蓝色、绿色或者透明度设成均值,把别的通道置 0 看看图像变化。

透明度变换

透明度处理使用的是第四个值,方法是将透明度乘以一个加权值,这个加权值在 0-1 之间:

// decimal 取值应在 0-1 之间
var transparency = function(imageData,decimal){
    var data = imageData.data;
    for(let i = 0;i < data.length;i ++){
        // 第三个通道加权
        data[i + 3] *= decimal;
    }
    return imageData;
}

色彩反转

色彩反转的思路是:获得每个像素的 RGB 通道的值,用 255 减去该值,再把算出的结果赋给对应的 RGB 通道。

var colorReversal = function(imageData){
    var data = imageData.data;
    var len = imageData.length;
    for(let i = 0;i < len;i += 4){
        data[i] = 255 - data[i];
        data[i + 1] = 255 - data[i + 1]
        data[i + 2] = 255 - data[i + 2];
    }
    return imageData;
}

处理结果:

色彩反转

复古处理

图片复古处理可以让图片看着有“历史感”,原理是将 RGB 每个通道赋值为三个通道的加权值之和(0-1 之间),

for (let i = 0; i < len; i += 4) {
    let r = data[i],
        g = data[i + 1],
        b = data[i + 2];
    // 加权操作
    data[i] = r * 0.39 + g * 0.76 + b * 0.18;
    data[i + 1] = r * 0.35 + g * 0.68 + b * 0.16;
    data[i + 2] = r * 0.27 + g * 0.35 + b * 0.13;
}

ctx.putImageData(imageData, 0, 0);

效果:

复古

CSS3 中的滤镜

CSS3 中新增了滤镜属性:filter

filter 的取值可以有许多种,比如 blur(px) 可以给图像设置高斯模糊;hue-rotate(deg) 可以给图像应用色相旋转;opacity(%) 可以转化图像的透明程度;saturate(%) 可以转换图像饱和度;详细的介绍可以 参考 MDN 文档[2]

canvas 像素处理有个缺点,就是每次改变图像像素时,不能实时更新,如果要做一个滑动色彩变换,可以使用 CSS3 提供的 filter。思路是:使用 input 标签的 range 类型。range 类型属性有四个:min:该值不得小于 min. 默认值为 0;max:该值将不大于 max. 默认值为 100;step:该值表示滑动步数,预设值为 1。value 表示当前的步数,默认是长度的一半。

<body>
    <img src="./05.jpg" />
    <input value="0" id="range" type="range" min="0" max="100" step="1" />

    <script>
        const img = document.querySelector("img");
        const range = document.querySelector("#range");

        range.oninput = function(){
            // value 就是滑块滑动的距离
            img.style.filter = `blur(${this.value}px)`;
        }
    </script>
</body>

当滑动滑块时图片高斯模糊会平滑改变。当然,也可以使用 change 事件,当鼠标放开时才触发事件。CSS3 中的 filter 属性是很强大的,不足的就是浏览器兼容性现在还不太好。

canvas 视频处理

canvas 中的 drawImage 方法的第一个参数不仅可以传入图片对象,还可以传入 video 对象。

// 获取 video 元素
var video = document.getElementsByTagName("video")[0];
// 将视频绘制到 canvas 上
context.drawImage(video,0,0,cvs.width,cvs.height);

绘制到 canvas 上后只是视频的第一帧图,想要让 canvas 像视频一样播放图片帧,就需要使用 requestAnimationFrame 方法。

首先需要一个 video 标签和 canvas 标签。

<body>
    <video src="./xxx.mp4" muted controls height="423" width="423"></video>
    <canvas id="myCvs" height="423" width="423"></canvas>
</body>

然后用 JS 获取元素进行操作:

const video = document.querySelector("video");
const cvs = document.querySelector("#myCvs");
const ctx = cvs.getContext("2d");

// 处理像素
function greenMask(imageData) {
    var data = imageData.data;
    var len = data.length;
    for (let i = 0; i < len; i += 4) {
        let sum = 0;
        for (let j = 0; j < 3; j++) {
            sum += data[i + j];
        }
        var avg = sum / 3;
        data[i + 2] = avg;
        data[i + 1] = data[i] = 0;
    }
    return imageData;
}

// 该事件是当媒体资源的第一帧加载完成时被触发。
video.onloadeddata = function () {
    // 然后运行 play 函数
    window.requestAnimationFrame(play);
}

// play 函数:
function play(imageData) {
    window.requestAnimationFrame(play);
    // 先绘制图片
    ctx.drawImage(video, 0, 0,cvs.width,cvs.height);

    // 然后获取像素
    var imageData = ctx.getImageData(0,0,cvs.width,cvs.height);
    // 处理像素,然后重新绘制到 canvas 上
    var newImageData = redMask(imageData);
    ctx.putImageData(newImageData,0,0);
}

当然,CSS3 中的 filter 也可以用于 canvas 上:

const video = document.querySelector("video");
const cvs = document.querySelector("#myCvs");
const ctx = cvs.getContext("2d");

// 定义后,canvas 图像会呈现高斯模糊效果
cvs.style.filter = "blur(8px)";

video.onloadeddata = function () {
    window.requestAnimationFrame(play);
}
function play(){
    window.requestAnimationFrame(play);
    ctx.drawImage(video, 0, 0,cvs.width,cvs.height);
    // 就不需要再获取像素然后重新绘制了
}

当获取像素并能进行操作时,可以说几乎任何图像处理操作都可以通过 canvas 完成,可见 canvas 的强大之处,当然,canvas 的强大不只局限于基本的像素操作,图片合成、视频合成以及游戏动画等也是 canvas 的能够胜任的,学好 canvas 就如同打开了一扇新的大门。canvas 像素操作就说到这里。

参考资料

[1]

https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/putImageData: https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/putImageData

[2]

参考 MDN 文档: https://developer.mozilla.org/zh-CN/docs/Web/CSS/filter

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebTower 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • getImageData 函数参数
  • putImageData() 方法
  • 简单的 canvas 的像素操作
    • 灰度处理
      • 纯黑白图
        • 透明度变换
          • 色彩反转
            • 复古处理
            • CSS3 中的滤镜
            • canvas 视频处理
              • 参考资料
              相关产品与服务
              媒体处理
              媒体处理(Media Processing Service,MPS)是一种云端音视频处理服务。基于腾讯多年音视频领域的深耕,为您提供极致的编码能力,大幅节约存储及带宽成本、实现全平台播放,同时提供视频截图、音视频增强、内容理解、内容审核等能力,满足您在各种场景下对视频的处理需求。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档