Javascript图像处理——边缘梯度计算

系统 1597 0

前言

上一篇文章 ,我们讲解了图像处理中的膨胀和腐蚀函数,这篇文章将做边缘梯度计算函数。直接摘自 OpenCV 2.4+ C++ 边缘梯度计算

 

图像的边缘

图像的边缘从数学上是如何表示的呢?

图像的边缘上,邻近的像素值应当显著地改变了。而在数学上,导数是表示改变快慢的一种方法。 梯度值的大变预示着图像中内容的显著变化了。

用更加形象的图像来解释,假设我们有一张一维图形。下图中灰度值的“跃升”表示边缘的存在:

使用一阶微分求导我们可以更加清晰的看到边缘“跃升”的存在(这里显示为高峰值):

由此我们可以得出:边缘可以 通过定位梯度值大于邻域的相素的方法找到。

 

近似梯度

比如内核为3时。

首先对x方向计算近似导数:

然后对y方向计算近似导数:

然后计算梯度:

当然你也可以写成:

 

函数实现

      
        var
      
       Sobel = 
      
        function
      
      
        (__src, __xorder, __yorder, __size, __borderType, __dst){
    (__src 
      
      && (__xorder ^ __yorder)) || error(arguments.callee, IS_UNDEFINED_OR_NULL
      
        /*
      
      
         {line} 
      
      
        */
      
      
        );
    
      
      
        if
      
      (__src.type && __src.type === "CV_GRAY"
      
        ){
        
      
      
        var
      
      
         kernel1,
            kernel2,
            height 
      
      =
      
         __src.row,
            width 
      
      =
      
         __src.col,
            dst 
      
      = __dst || 
      
        new
      
       Mat(height, width, CV_16I, 1
      
        ),
            dstData 
      
      =
      
         dst.data
            size 
      
      = __size || 3
      
        ;
        
      
      
        switch
      
      
        (size){
            
      
      
        case
      
       1
      
        :
                size 
      
      = 3
      
        ;
            
      
      
        case
      
       3
      
        :
                
      
      
        if
      
      
        (__xorder){
                    kernel 
      
      = [-1, 0, 1
      
        ,
                              
      
      -2, 0, 2
      
        ,
                              
      
      -1, 0, 1
      
        
                             ];
                }
      
      
        else
      
      
        if
      
      
        (__yorder){
                    kernel 
      
      = [-1, -2, -1
      
        ,
                               
      
      0,  0,  0
      
        ,
                               
      
      1,  2,  1
      
        
                             ];
                }
                
      
      
        break
      
      
        ;
            
      
      
        case
      
       5
      
        :
                
      
      
        if
      
      
        (__xorder){
                    kernel 
      
      = [-1, -2, 0, 2, 1
      
        ,
                              
      
      -4, -8, 0, 8, 4
      
        ,
                              
      
      -6,-12, 0,12, 6
      
        ,
                              
      
      -4, -8, 0, 8, 4
      
        ,
                              
      
      -1, -2, 0, 2, 1
      
        
                             ];
                }
      
      
        else
      
      
        if
      
      
        (__yorder){
                    kernel 
      
      = [-1, -4, -6, -4, -1
      
        ,
                              
      
      -2, -8,-12, -8, -2
      
        ,
                               
      
      0,  0,  0,  0,  0
      
        ,
                               
      
      2,  8, 12,  8,  2
      
        ,
                               
      
      1,  4,  6,  4,  1
      
        
                             ];
                }
                
      
      
        break
      
      
        ;
            
      
      
        default
      
      
        :
                error(arguments.callee, UNSPPORT_SIZE
      
      
        /*
      
      
         {line} 
      
      
        */
      
      
        );
            
        }
        
        GRAY216IC1Filter(__src, size, height, width, kernel, dstData, __borderType);

    }
      
      
        else
      
      
        {
        error(arguments.callee, UNSPPORT_DATA_TYPE
      
      
        /*
      
      
         {line} 
      
      
        */
      
      
        );
    }
    
      
      
        return
      
      
         dst;
};
      
    

这里只提供了内核大小为3和5的Sobel算子,主要原因是7或以上的内核计算就比较慢了。

输出一个单通道的16位有符号整数矩阵。

      
        function
      
      
         GRAY216IC1Filter(__src, size, height, width, kernel, dstData, __borderType){
    
      
      
        var
      
       start = size >> 1
      
        ;
        
    
      
      
        var
      
       withBorderMat = copyMakeBorder(__src, start, start, 0, 0
      
        , __borderType);
            
    
      
      
        var
      
       mData =
      
         withBorderMat.data,
        mWidth 
      
      =
      
         withBorderMat.col;
        
    
      
      
        var
      
      
         i, j, y, x, c;
    
      
      
        var
      
      
         newValue, nowX, offsetY, offsetI;
        
    
      
      
        for
      
      (i = height; i--
      
        ;){
        offsetI 
      
      = i *
      
         width;
        
      
      
        for
      
      (j = width; j--
      
        ;){
            newValue 
      
      = 0
      
        ;
            
      
      
        for
      
      (y = size; y--
      
        ;){
                offsetY 
      
      = (y + i) *
      
         mWidth;
                
      
      
        for
      
      (x = size; x--
      
        ;){
                    nowX 
      
      = x +
      
         j;
                    newValue 
      
      += (mData[offsetY + nowX] * kernel[y * size +
      
         x]);
                }
            }
            dstData[j 
      
      + offsetI] =
      
         newValue;
        }
    }
}
      
    

然后把内核和矩阵交给这个滤波器处理,就OK了。

把这个滤波器独立出来的原因是,可以给其他类似的计算边缘函数使用,比如Laplacian和Scharr算子。

 

转为无符号8位整数

由于Sobel算子算出来的是16位有符号整数,无法显示成图片,所以我们需要一个函数来将其转为无符号8位整数矩阵。

convertScaleAbs函数是将每个元素取绝对值,然后放到Int8Array数组里面,由于在赋值时候大于255的数会自动转成255,而小于0的数会自动转成0,所以不需要我们做一个函数来负责这一工作。

      
        function
      
      
         convertScaleAbs(__src, __dst){
    __src 
      
      || error(arguments.callee, IS_UNDEFINED_OR_NULL
      
        /*
      
      
         {line} 
      
      
        */
      
      
        );
    
      
      
        var
      
       height =
      
         __src.row,
        width 
      
      =
      
         __src.col,
        channel 
      
      =
      
         __src.channel,
        sData 
      
      =
      
         __src.data;
        
    
      
      
        if
      
      (!
      
        __dst){
        
      
      
        if
      
      (channel === 1
      
        )
            dst 
      
      = 
      
        new
      
      
         Mat(height, width, CV_GRAY);
        
      
      
        else
      
      
        if
      
      (channel === 4
      
        )
            dst 
      
      = 
      
        new
      
      
         Mat(height, width, CV_RGBA);
        
      
      
        else
      
      
        
            dst 
      
      = 
      
        new
      
      
         Mat(height, width, CV_8I, channel);
    }
      
      
        else
      
      
        {
        dst 
      
      =
      
         __dst;
    }
    
    
      
      
        var
      
       dData =
      
         dst.data;

    
      
      
        var
      
      
         i, j, c;
    
    
      
      
        for
      
      (i = height; i--
      
        ;){
        
      
      
        for
      
      (j = width * channel; j--
      
        ;){
            dData[i 
      
      * width * channel + j] = Math.abs(sData[i * width * channel +
      
         j]);
        }
    }
    
    
      
      
        return
      
      
         dst;
}
      
    

 

按比例合并值

我们还需要一个函数将x方向梯度计算值和y方向梯度计算值叠加起来。

      
        var
      
       addWeighted = 
      
        function
      
      
        (__src1, __alpha, __src2, __beta, __gamma, __dst){
    (__src1 
      
      && __src2) || error(arguments.callee, IS_UNDEFINED_OR_NULL
      
        /*
      
      
         {line} 
      
      
        */
      
      
        );
    
      
      
        var
      
       height =
      
         __src1.row,
        width 
      
      =
      
         __src1.col,
        alpha 
      
      = __alpha || 0
      
        ,
        beta 
      
      = __beta || 0
      
        ,
        channel 
      
      =
      
         __src1.channel,
        gamma 
      
      = __gamma || 0
      
        ;
    
      
      
        if
      
      (height !== __src2.row || width !== __src2.col || channel !==
      
         __src2.channel){
        error(arguments.callee, 
      
      "Src2 must be the same size and channel number as src1!"
      
        /*
      
      
         {line} 
      
      
        */
      
      
        );
        
      
      
        return
      
      
        null
      
      
        ;
    }
    
    
      
      
        if
      
      (!
      
        __dst){
        
      
      
        if
      
      (__src1.type.match(/CV\_\d+/
      
        ))
            dst 
      
      = 
      
        new
      
      
         Mat(height, width, __src1.depth(), channel);
        
      
      
        else
      
      
        
            dst 
      
      = 
      
        new
      
      
         Mat(height, width, __src1.depth());
    }
      
      
        else
      
      
        {
        dst 
      
      =
      
         __dst;
    }
    
    
      
      
        var
      
       dData =
      
         dst.data,
        s1Data 
      
      =
      
         __src1.data,
        s2Data 
      
      =
      
         __src2.data;
    
    
      
      
        var
      
      
         i;
    
    
      
      
        for
      
      (i = height * width * channel; i--
      
        ;)
        dData[i] 
      
      = __alpha * s1Data[i] + __beta * s2Data[i] +
      
         gamma;
        
    
      
      
        return
      
      
         dst;
};
      
    

这个函数很简单,实际上只是对两个矩阵的对应元素按固定比例相加而已。

 

效果图

Javascript图像处理——边缘梯度计算_第1张图片

 

系列目录

Javascript图像处理系列

Javascript图像处理——边缘梯度计算


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论