javascript实现比较相似图片算法

昨天看了阮一峰老师的文章:《相似图片搜索原理》,于是把直方图和向量那块算法用js实现了一下,

源码如下:

function getHistogram(imageData) {
    var arr = [];
    for (var i = 0; i < 64; i++) {
        arr[i] = 0;
    }
    var data = imageData.data;
    var pow4 = Math.pow(4, 2);
    for (var i = 0, len = data.length; i < len; i += 4) {
        var red = (data[i] / 64) | 0;
        var green = (data[i + 1] / 64) | 0;
        var blue = (data[i + 2] / 64) | 0;
        var index = red * pow4 + green * 4 + blue;
        arr[index]++;
    }

    return arr;
}

function cosine(arr1, arr2) {
    var axb = 0,
        a = 0,
        b = 0;
    for (var i = 0, len = arr1.length; i < len; i++) {
        axb += arr1[i] * arr2[i];
        a += arr1[i] * arr1[i];
        b += arr2[i] * arr2[i];
    }
    return axb / (Math.sqrt(a) * Math.sqrt(b));
}
function gray(imgData) {
    var data = imgData.data;
    for (var i = 0, len = data.length; i < len; i += 4) {
        var gray = parseInt((data[i] + data[i + 1] + data[i + 2]) / 3);
        data[i + 2] = data[i + 1] = data[i] = gray;
    }
    return imgData;
}

有个问题,假如图片是灰色的跟原图进行比较,那么要比较相似度,需要将图片都转成灰色的,即使用上面代码的gray函数来处理

怎么清空javascript数组

这是一个很基础的问题,但是我们往往经常做错。

当一个数组需要清空的时候,很多人使用下面的方法:

a = [];

我们知道javascript变量存储方式分为引用类型和直接量。数组属于对象,即引用类型,引用的是变量指针地址,之所以这样设计也是为了节省内存。

再说到上面的请空数组的方式,如果采用直接赋值一个新数组的方式,那么之前引用的数组可能不会被释放(有其他的引用),例如下面的代码:

var a = [2,3];
var b = a;
a = [];
console.log(b);

这时候a和b就不是同一个数组了,清空了a而b还是指到之前的引用地址,除非你是故意的,否则这将会有隐患。

所以最佳的请空数组方式是:将length设置为0,即:

a.length = 0;

javascript的类型数组(Typed Array)

在《javascript canvas图片像素》中提到了类型数组(Typed Array),javascript这是一种快速操作二进制数据的解决方案。

随着web发展和html5的推进,javascript来处理音视频、二进制文件和网络协议等都成为了可能,这时候原生的数组Array已经不能够满足需求,需要一种快速操作二进制流的东东产生,于是ArrayBuffer类型和Typed Array诞生了。

类型数组介绍

类型化数组类型表示可编制索引和操纵的ArrayBuffer对象的各种识图。所有数组类型的长度固定不变。如下表所示:

名称 大小(以字节为单位) 说明
Int8Array 1 8 位二补码有符号整数
Uint8Array 1 8 位无符号整数
Int16Array 2 16 位二补码有符号整数
Uint16Array 2 16 位无符号整数
Int32Array 4 32 位二补码有符号整数
Uint32Array 4 32 位无符号整数
Float32Array 4 32 位 IEEE 浮点数
Float64Array 8 64 位 IEEE 浮点数

html5 canvas 图片像素

html5 canvas图像转换成数组

html5的canvas可以进行图片处理,图片是由像素组成的。我们知道canvas有方法getImageData,返回的对象中可以取出,image的data 数组(类似数组的Type Array类型数组,Uint8ClampedArray)。

假设canvas宽高是500*400,那么应该有200000个像素点,而通过getImageData().data获取的data数组的length是800000。

我们还知道rgba表示颜色的方法:

  • r=red
  • g=green
  • b=blue
  • a=alpha

即红绿蓝三原色和透明值。

所以这个data数组是每四个数组值代表一个像素点。这样一来,一个图像就转化成了数据表示的数组。如下面的代码:

var canvas = document.getElementById('canvas');
var ctxt = canvas.getContext('2d');
var img = new Image;
img.onload = function(){
    ctxt.drawImage(img, 0, 0);
    var data = ctxt.getImageData(0, 0, 480, 480).data;
    console.log(data, data.toString());
}
img.src = 'img/pic.jpg';

图像数组的遍历

如上面的代码,我们可以看到数组data就是图片转换成的数组,那么遍历这个数组就可以取得图片的像素点内容:

var data = ctxt.getImageData(0, 0, 480, 480).data;
for(var i =0,len = data.length; i<len;i+=4){
    var red = data[i],
    green = data[i+1],
    blue = data[i+2],
    alpha = data[i+3];

}

ECMAScript 262 5th :执行环境一些概念解释

可执行代码

ECMAScript 5th规定了3种可执行代码:Global code、Eval code和Function code。

根据名字就已经知道,Global codeEval code分别对应全局代码和eval函数中执行的代码。

三种可执行代码中最为复杂的就是Function code,即函数代码。因为javascript的特性,所以Function code中还可以嵌入Function code,导致了Function的执行环境会较为复杂。

执行环境(执行上下文)

当javascript引擎开始执行(进入)一段可执行代码时,就会生成一个执行环境Execution Context,或称执行上下文)。

javascript引擎通过一个栈(Stack)来维护执行环境,当进入一个执行环境,则将当前运行的执行环境压入到这个栈的顶部,代码表示:

var ECStack = [];//维护执行环境的栈
function enterExecutabCode(){
    var ec = new ExecutionContext();
    ECStack.push(ec);
}

一个执行环境是由:LexicalEnvironmentVariableEvironmentThisBinding组成的。

function ExecutionContext(){
    return {
        LexicalEnvironment,
        VariableEnvironment,
        ThisBinding
    }
}

词法环境(Lexical Environments)注意是复数哦~

一个词法环境对象包括:环境数据(Environment Record)和外部环境(outer Lexical Environment)。

外部环境(outer Lexical Environment)

表示外层函数的词法环境,有两种:nullglobal环境),或者外层函数的词法环境(嵌套函数)

环境数据(记录)(Environment Record)

有两种环境数据:declarative environment recordsobject environment records,因为存在两种类型的环境数据,所以词法环境的实现类型包括了两种:DecarativeEnvironmentObjectEnvironment

Declarative environment records

常见标识符绑定基本都是这个类型,例如函数定义,var声明,trycatch子句

Object environment records

包括两种一种是程序级别的(Program),另外一种是with语句,因为这两种绑定标识符过程需要传入一个对象做为环境数据的属性值。

继续说环境数据,环境数据存在于词法环境或者变量环境中,它包含了一个绑定对象(bindingObject),这个对象是一种键值对象,即有name和value一一对应关系,其中name就是标识符,value则为对应的变量值。

既然环境数据是保存数据的地方,必然有一些方法用于存储数据和读取数据。所以环境数据常见的方法有:

js模块并行加载器:MixJS

自己写了一个js模块加载器,所谓的并行其实也不完全是并行的,只是依赖关系模块是并行的。当然我知道现在有很多类似的js框架,而且MixJS也没有做更加全面的浏览器测试,只是跟我的意识写的一个加载器。

项目放在github上面:https://github.com/ksky521/MixJS
算是给2012年的一个总结。

简单说下功能

跟玉伯的Seajs不同,也跟CMD和AMD不同,我使用的是依赖关系明确的声明模块方式,这样减少了扫描代码读取依赖关系的方式,开发者虽然有了一定的束缚,但是总体来说,后期的维护成本,代码效率和实时combo、上线打包都会大大的降低成本。

MixJS介绍

轻量级前端模块化解决方案,提供模块管理、php实时合并、打包工具等方案。可以用于提供给第三方开发者使用的小组件,核心文件可以单独拿出来作为框架core,在此基础上可以开发出一整套的前端框架

MixJS还在开发完善阶段,未作完整兼容性测试,多数代码是出于解决问题的想法而写的,当然也copy了很多牛人的代码,比如cssload回调借鉴了seajs,Deferred来自于jQuery。。。。

。。。。鉴于最近前端圈比较乱,我的小手都开始颤抖,造轮子必然会被喷,我只能说我是根据自己的意识码了这些的代码,据此操作后果自负。。。。

0.2开发完成,现在开发基于0.2模块的开放平台部分代码,主要包括:Deferred(延迟队列)、API(API接口调用)、Widget(小组件)和XDomain(跨域)

模块加载不是单纯的文件加载,需要根据模块规范来写模块哦~

MixJS代码示例

参考github项目readme文件。

用途

关于MixJS的用途,主要解决是模块开发中的管理、加载。

我赞成使用MixJS是:细模块,粗文件。总体来看MixJS适合Widget开发、开放平台开放js开发,也适合作为模块管理器来打造更加强大的前端架构。

不多说了,吹嘘会被喷的,哈哈~

感谢

MixJS也借鉴了不少大牛的代码(嗯,你可以说是抄袭),感谢seajs,jQuery,mass,curl.js等作者。

再探javascript作用域

对于javascript作用域的理解,之前整理过两篇文章:《javascript作用域和作用域链》《javascript的词法作用域》。最近读了周爱民老师的《javascript语言精髓》,对javascript作用域有了新的认识,特地整理下思路。

javascript的作用域广义来分:语法作用域变量作用域

javascript语法作用域

javascript语法作用域是讨论代码的组织结构上抽象,可以理解为讨论的是“圈地”问题,也有书籍中对纯粹的语法作用域称为“静态作用域”。

语法作用域分四级,下标依次增高

级别 作用域
级别一 表达式
级别二 语句/批语句
级别三 函数
级别四 全局(宿主)
  1. 同级别可以平行也可以相互嵌套(包含)
  2. 高级别可以嵌套(包含)低级别
  3. 低级别不可以嵌套(包含)高级别作用域;假如低级别嵌套高u别,可以理解成相互平行,例如下面的代码是完全等效的
if(true){
    //
    function fn(){
        //
    }
}

if(true){
    //
}
function fn(){
    //
}

开源个html5播放器

前言:人家都说没有几个项目在github,都不好意思说自己混前端的……

好吧,看见越来越多的人都推自己的github主页,我也把自己的之前写的一些东西整理下,从sae的svn迁移到github上,开了几个项目,虽然说代码的确乱糟糟的,但是也凑凑数吧,今天开源的是html5音乐播放器

html5音乐播放器

截图如下:

之前放出的是jquery版本的,见文章《写了一个html5音乐播放器》也整理到了这个项目中,文件夹是withjQuery内。

使用广播事件来实现模块解耦

模块化开发往往最大的问题是解耦,怎么设计一个低耦合的组件呢?这里我用到的方法是时间总线(eventBus)方法,也可以叫做广播事件,模块之间通过发送广播的方式来实现通信,事件发起者只需要派发事件,而不必关心事件是否被接收(订阅)。

广播事件也是自定义事件的一种,不同于自定义事件,广播事件没有绑定的主体,但是都是通过观察者设计模式来写的代码。
大体的javascript实现代码如下:其中包括一些简单方法没有列出,例如$.isUndefined

var _cache = {};
var broadcast = {
    /**
     * 派发
     * @param  {[type]} type 事件类型
     * @param  {[type]} data 回调数据
     * @return {[type]}      [description]
     */
    fire:function(type, data){
        var listeners = _cache[type],len = 0;
        if(!$.isUndefined(listeners)){
            var args = [].slice.call(arguments);
            args = args.length > 2 ? args.splice(2, args.length-1) : [];
            args = [data].concat(args);

            len = listeners.length;
            for(var i = 0; i<len;i++){
                var listener = listeners[i];
                if(listener && listener.callback) {
                    args = args.concat(listener.args);
                    listener.callback.apply(listener.scope, args);
                }
            }
        }
        return this;
    },
    /**
     * 订阅广播事件
     * @param  {[type]}   types     事件类型,支持,分隔符
     * @param  {Function} callback 回调函数
     * @param  {[type]}   scope    回调函数上下文
     * @return {[type]}            this
     */
    subscribe:function(types, callback, scope){
        types = types || [];
        var args = [].slice.call(arguments);

        if($.isString(types)){
            types = types.split(',');
        }
        var len = types.length;
        if(len===0){
            return this;
        }
        args = args.length > 3 ? args.splice(3, args.length-1) : [];
        for(var i = 0;i<len;i++){
            var type = types[i];
            _cache[type] = _cache[type] || [];
            _cache[type].push({callback:callback,scope:scope,args:args});
        }
        return this;
    },
    /**
     * 退订
     * @param  {[type]}   type     [description]
     * @param  {Function} callback 假如传入则移出传入的监控事件,否则移出全部
     * @return {[type]}            [description]
     */
    unsubscribe:function(type, callback, scope){
        var listeners = _cache[type];
        if (!listeners) {
            return this;
        }
        if(callback){
            var len = listeners.length,
                tmp = [];

            for(var i=0; i<len; i++) {
                var listener = listeners[i];
                if(listener.callback == callback && listener.scope == scope) {
                } else {
                    tmp.push(listener);
                }
            }
            listeners = tmp;
        }else{
            listeners.length = 0;
        }
        return this;
    },
    /**
     * 订阅别名
     * @return {[type]} [description]
     */
    on:function(){
        return this.subscribe.apply(this,arguments);
    },
    /**
     * 退订别名
     * @return {[type]} [description]
     */
    un:function(){
        return this.unsubscribe.apply(this,arguments);
    },
    dispatch:function(){
        return this.fire.apply(this,arguments);
    },

    removeAll:function(){
        _cache = {};
        return this;
    }
};

使用方法:

broadcast.fire('event name', json);
broadcast.on('event name', function(data){
    console.log(data);
});

VPS 上配置 iptables 防火墙

今天vps告急,ssh都登录不上去,上去很艰难的查了日志,发现几个ip访问异常,应该是某插件的爬虫,之前把图简单iptables禁用了,只能再次开启,主要命令如下:

# 查看状态
$ service iptables status

# 查看规则
$ iptables -L -n

# 清除默认规则
$ iptables -F
$ iptables -X
$ iptables -Z

#####建立新的规则######
# 允许本地回环 127.0.0.1
$ iptables -A INPUT -i lo -p all -j ACCEPT

# 允许已经建立的所有连接
$ iptables -A INPUT -p all -m state --state ESTABLISHED,RELATED -j ACCEPT

# 允许所有向外发起的连接
$ iptables -A OUTPUT -j ACCEPT

# 拒绝 ping
$ iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j REJECT

# 允许 SSH 服务端口(一定要打开,不然就不能ssh了)
$ iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 允许 Web 服务端口
$ iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# 拒绝其他所有未被允许的连接
$ iptables -A INPUT -j REJECT
$ iptables -A FORWARD -j REJECT

# 禁用ip
$ iptables -I INPUT -s 124.115.0.199 -j DROP
# 封IP段的命令是
$ iptables -I INPUT -s 124.115.0.0/16 -j DROP
# 封整个段的命令是
$ iptables -I INPUT -s 194.42.0.0/8 -j DROP
# 封几个段的命令是
$ iptables -I INPUT -s 61.37.80.0/24 -j DROP
$ iptables -I INPUT -s 61.37.81.0/24 -j DROP

dport表示目的,sport表示来源,output表示本机出,input表示访问本机