javascript图像处理—边缘梯度计算函数

前言

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

图像的边缘

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

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

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

    

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

    

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

近似梯度

比如内核为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,
, 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,
, 8, 12, 8, 2,
, 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;
};

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

(0)

相关推荐

  • Javascript图像处理—平滑处理实现原理

    前言 上一篇文章,我们讲解了图像的虚拟边缘,这篇文章开始进行平滑(也就是模糊)处理. 基本原理 这里直接引用OpenCV 2.4+ C++ 平滑处理和OpenCV 2.4+ C++ 边缘梯度计算的相关内容: 平滑也称模糊, 是一项简单且使用频率很高的图像处理方法. 平滑处理时需要用到一个滤波器 . 最常用的滤波器是线性 滤波器,线性滤波处理的输出像素值(例如:)是输入像素值(例如:)的加权平均: 称为核 , 它仅仅是一个加权系数. 这里涉及一种叫做"卷积"的运算,那么卷积是什么呢? 卷

  • java数字图像处理基础使用imageio写图像文件示例

    一个BufferedImage的像素数据储存在Raster中,ColorModel里面储存颜色空间,类型等信息,当前Java只支持一下三种图像格式- JPG,PNG,GIF,如何向让Java支持其它格式,首先要 完成Java中的图像读写接口,然后打成jar,加上启动参数- Xbootclasspath/pnewimageformatIO.jar即可. Java中如何读写一个图像文件,使用ImageIO对象即可.读图像文件的代码如下: 复制代码 代码如下: File file = new File

  • Java图像处理工具类

    本工具类的功能:缩放图像.切割图像.图像类型转换.彩色转黑白.文字水印.图片水印等 复制代码 代码如下: package net.kitbox.util; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Re

  • Javascript图像处理—亮度对比度应用案例

    前言 上一篇文章,我们讲解了图像处理中的卷积操作和平滑(也就是模糊)处理,这篇文章我们进行亮度和对比度的变化. 其实,亮度是啥玩意? 亮度就是比较亮眼咯-- 实际上对于RGBA颜色空间,变亮其实就等于R.G.B三个通道同时加大,那么变暗就等于同时减小咯. 这比较好理解,因为最暗的黑色是RGB(0,0,0),而最亮的白色是RGB(255,255,255).所以变亮应该RGB各通道都要增大. 那么,对比度呢? 对比度,其实就是颜色差啦. 那么对于RGBA颜色空间,对比度变大其实就等于R.G.B三个通

  • javascript图像处理—仿射变换深度理解

    前言 上一篇文章,我们讲解了图像金字塔,这篇文章我们来了解仿射变换. 仿射? 任何仿射变换都可以转换成,乘以一个矩阵(线性变化),再加上一个向量(平移变化). 实际上仿射是两幅图片的变换关系. 例如我们可以通过仿射变换对图片进行:缩放.旋转.平移等操作. 一个数学问题 在解决仿射问题前,我们来做一个数学题. 如图,对于点(x1, y1),相对于原点旋转一个角度a,那么这个点到哪里了呢? 我们将坐标系变成极坐标系,则点(x1, y1)就变成了(r, β),而旋转后变成(r, α+ β). 转回直角

  • Javascript图像处理思路及实现代码

    思路 HTML5的canvas提供了getImageData接口来获取canvas中的数据,所以我们能够先用drawImage接口将图片画在canvas上然后再通过getImageData得到图片数据矩阵. 需要注意,虽然IE9开始支持了canvas接口,但是其getImageData获取的数据并不是以标准的TypedArray方式存储的,或者说IE9没有提供对WebGL Native binary data的支持,所以如果需要对IE9支持,下面的矩阵需要用Array的方式保存.虽然IE9以下版

  • Javascript图像处理—虚拟边缘介绍及使用方法

    前言 上一篇文章,我们来给矩阵添加一些常用方法,这篇文章将讲解图像的虚拟边缘. 虚拟边缘 虚拟边缘就是按照一定映射关系,给图像添加边缘. 那么虚拟边缘有什么用呢?比如可以很容易做一个倒影的效果:  当然这只是附带效果了,虚拟边缘主要用在图像卷积运算(例如平滑操作)时候,由于卷积运算的特点,需要将图片扩大才能对边角进行卷积运算,这时候就需要对图片进行预处理,添加虚拟边缘. 说白了,就是在一些图片处理前进行预处理. 边缘类型 这里参考OpenCV相关文档的边缘描述: 复制代码 代码如下: /* Va

  • Javascript图像处理—图像形态学(膨胀与腐蚀)

    前言 上一篇文章,我们讲解了图像处理中的阈值函数,这一篇文章我们来做膨胀和腐蚀函数. 膨胀与腐蚀 说概念可能很难解释,我们来看图,首先是原图: 膨胀以后会变成这样: 腐蚀以后则会变成这样: 看起来可能有些莫名其妙,明明是膨胀,为什么字反而变细了,而明明是腐蚀,为什么字反而变粗了. 实际上,所谓膨胀应该指: 较亮色块膨胀. 而所谓腐蚀应该指: 较亮色块腐蚀. 上面图里面,由于背景白色是较亮色块,所以膨胀时就把黑色较暗色块的字压扁了--相反腐蚀时,字就吸水膨胀了-- 用数学公式表示就是: 说白了就是

  • Javascript图像处理—为矩阵添加常用方法

    前言 上一篇文章,我们定义了矩阵,这篇文章我们来给矩阵添加一些常用方法. toString方法 toString方法通常用作将对象转成字符串描述,所以我们将这一方法定义为输出矩阵元素. 复制代码 代码如下: Mat.prototype.toString = function(){ var tempData = this.data, text = "Mat("+ this.type +") = {\n", num = this.col * this.channel;

  • Javascript图像处理—阈值函数实例应用

    前言 上一篇文章,我们讲解了图像处理中的亮度和对比度的变化,这篇文章我们来做一个阈值函数. 最简单的图像分割方法 阈值是最简单的图像分割方法. 比如为了从下图中分割出苹果,我们利用前景与背景的灰度差值,通过设定一个阈值,对于该像素大于这个阈值时就以黑色表示,小于便以灰色表示. 五种阈值类型 和OpenCV一样,我们将提供五种阈值类型,方便使用. 下面是原图像的波形表示,纵坐标表示像素点的灰度值大小,蓝线是阈值大小. 二进制阈值化 公式表示是: \texttt{thresh}$}{0}{other

随机推荐