zoukankan      html  css  js  c++  java
  • [JavaScript/canvas] 创建基于坐标访问的图形数据对象

    在HTML5中,canvas节点所提供的2d上下文(context),具备一组对像素进行操作的方法:getImageData/putImageData/createImageData。通过这三个方法,我们可以进行像素级别的图像处理。比如可以移植传统的各种滤镜算法,在web页面中开发出一套类似于PS的图片处理应用,等等。

    一般来讲,我们首先会通过getImageData方法,获取canvas上一个区域的像素数据。例如:

    context.getImageData(10, 10, 100, 80);

    这样我们会得到坐标为{10, 10},宽100、高80的一个区域的像素。返回对象ImageData有三个属性:width/height/data:

    image

    这里需要注意的是data。从上图中我们可以看到,它的数据类型是Uint8ClampedArray,是一个8位的无符号数组。请移步至http://www.khronos.org/registry/typedarray/specs/latest/,了解一下ES5中有关Typed Array的规范。

    data是个一维的数组,每4个数字对应一个点的RGBA色值。所以可以知道,这个数组长度应该是总像素数*4。对像素的描述,是从左到右、从上到下逐行扫描的过程。这种存储方式,或者说一维的描述图像的方式,与我们所习惯的二维坐标系方式不同。所以我希望能创建一个对象,实现二维到一维的转换,把自己从苦逼算点的工作中解脱出来;同时也希望通过本文,让各位初学者能够了解对像素如何进行基本的操作。

    首先,我设计一个接口,它应该具备以下几个功能:

    //
    interface ISmartImageData{
        long getPointCount(); //获取像素总数
        Color getValue(long index); //获取索引为index的[像素]的色值
        long getPointIndex(x, y); //转换坐标为像素索引
        Color getPointValue(x, y); //获取坐标点的色值
    }

    这里我们还需要一个辅助对象Color。Color使用了数组作为基础结构,为了后续一些运算上的方便,绑定了一些额外的方法。

    //
    function _extend(a, b){    //a -> b
        for(var p in a) b[p] = a[p];
    }
    var _ColorMethods = {
        //两个色值相加
        add : function(v){
            this[0] += v[0];
            this[1] += v[1];
            this[2] += v[2];
            this[3] += v[3];
        },
        //色值平均到c个点
        avg : function(c){
            this[0] = Math.round(this[0] / c);
            this[1] = Math.round(this[1] / c);
            this[2] = Math.round(this[2] / c);
            this[3] = Math.round(this[3] / c);
        },
        //色值是否相等(Alpha值暂时忽略)
        eq : function(v){
            return this[0] === v[0]
                && this[1] === v[1]
                && this[2] === v[2]
                //&& this[3] === v[3]
                ;
        },
        //与色值v比较,各个色值差允许在sub以内。可以作为降噪的一种方式。
        fuzzy : function(v, sub){
            return Math.abs(this[0] - v[0]) <= sub
                && Math.abs(this[1] - v[1]) <= sub
                && Math.abs(this[2] - v[2]) <= sub
                //&& Math.abs(this[3] - v[3]) <= sub
                ;
        }
    }
    function Color(c){
        _extend(_ColorMethods, c);
        return c;
    }

    如果需要让Color对象具备更多的方法,可以对_ColorMethods进行扩展。额外说一点:Color使用数组还是{R:0,G:0,B:0,A:0}这种方式,在实际中性能上几乎没有的区别。

    现在我们开始构建SmartImageData对象。首先我们使用一个ImageData来初始化。

    //
    function SmartImageData(imageData){
        this.data = imageData.data;
        this.width = imageData.width;
        this.height = imageData.height;
    }

    在实际操作中发现一个问题。如果你频繁调用imageData原始对象的width/height属性时,会造成性能上的较大损耗。原因不明。所以我将三个属性分别独立出来。下面实现ISmartImageData接口。

    //
    SmartImageData.prototype = {
        getPointCount : function(){
            return this.width * this.height;
        },
        getValue : function(index){
            var i = index * 4;
            var d = this.data;
            return Color([d[i+0],d[i+1],d[i+2],d[i+3]]);
        },
        getPointIndex : function(x, y){
            return (y * this.width + x) * 4;
        },
        getPointValue : function(x, y){
            var index = this.getPointIndex(x, y);
            return this.getValue(index);
        }
    }

    剩下的工作就没什么悬念了,可以借助这套系统,扩展出一系列的set方法,将处理过的数据,通过putImageData再放回到canvas中。

    比如最近在做的马赛克的处理,至于是降低采样,还是算区域的平均值来得到区块的颜色,都可以通过坐标系来实现。

    PS1:在刚结束的HTML5开发者嘉年华上,腾讯的工程师们实现了通过摄像头捕捉用户动作、进行体感操作。相信各位在今晚好好学习、通宵的努力,一定也能解决这个难题。

    PS2:祝各位七夕快乐!

  • 相关阅读:
    ES6-11学习笔记--正则表达式的扩展
    ES6-11学习笔记--字符串的扩展
    ES6-11学习笔记--Map
    ES6-11学习笔记--Set
    ES6-11学习笔记--Symbol
    final
    MySQL
    爬虫1
    laravel
    HTML学习
  • 原文地址:https://www.cnblogs.com/muse/p/2652338.html
Copyright © 2011-2022 走看看