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


Posted in Javascript onDecember 27, 2012

前言
上一篇文章,我们来给矩阵添加一些常用方法,这篇文章将讲解图像的虚拟边缘。

虚拟边缘
虚拟边缘就是按照一定映射关系,给图像添加边缘。
那么虚拟边缘有什么用呢?比如可以很容易做一个倒影的效果:
Javascript图像处理—虚拟边缘介绍及使用方法 
当然这只是附带效果了,虚拟边缘主要用在图像卷积运算(例如平滑操作)时候,由于卷积运算的特点,需要将图片扩大才能对边角进行卷积运算,这时候就需要对图片进行预处理,添加虚拟边缘。
说白了,就是在一些图片处理前进行预处理。

边缘类型
这里参考OpenCV相关文档的边缘描述:

/* 
Various border types, image boundaries are denoted with '|' 
* BORDER_REPLICATE: aaaaaa|abcdefgh|hhhhhhh 
* BORDER_REFLECT: fedcba|abcdefgh|hgfedcb 
* BORDER_REFLECT_101: gfedcb|abcdefgh|gfedcba 
* BORDER_WRAP: cdefgh|abcdefgh|abcdefg 
* BORDER_CONSTANT: iiiiii|abcdefgh|iiiiiii with some specified 'i' 
*/

举个例子BODER_REFLECT就是对于某一行或某一列像素点:

abcdefgh
其左的虚拟边缘对应为fedcba,右边对应为hgfedcb,也就是反射映射。上图就是通过对图片底部进行添加BORDER_REFLECT类型的虚拟边缘得到的。
而BORDER_CONSTANT则是所有边缘都是固定值i。
实现
因为BORDER_CONSTANT比较特殊,所以和其他类型分开处理。

function copyMakeBorder(__src, __top, __left, __bottom, __right, __borderType, __value){ 
if(__src.type != "CV_RGBA"){ 
console.error("不支持类型!"); 
} 
if(__borderType === CV_BORDER_CONSTANT){ 
return copyMakeConstBorder_8U(__src, __top, __left, __bottom, __right, __value); 
}else{ 
return copyMakeBorder_8U(__src, __top, __left, __bottom, __right, __borderType); 
} 
};

这个函数接受一个输入矩阵src,每个方向要添加的像素大小top,left,bottom,right,边缘的类型borderType,还有一个数组value,即如果是常数边缘时候添加的常数值。
然后我们引入一个边缘的映射关系函数borderInterpolate。
function borderInterpolate(__p, __len, __borderType){ 
if(__p < 0 || __p >= __len){ 
switch(__borderType){ 
case CV_BORDER_REPLICATE: 
__p = __p < 0 ? 0 : __len - 1; 
break; 
case CV_BORDER_REFLECT: 
case CV_BORDER_REFLECT_101: 
var delta = __borderType == CV_BORDER_REFLECT_101; 
if(__len == 1) 
return 0; 
do{ 
if(__p < 0) 
__p = -__p - 1 + delta; 
else 
__p = __len - 1 - (__p - __len) - delta; 
}while(__p < 0 || __p >= __len) 
break; 
case CV_BORDER_WRAP: 
if(__p < 0) 
__p -= ((__p - __len + 1) / __len) * __len; 
if(__p >= __len) 
__p %= __len; 
break; 
case CV_BORDER_CONSTANT: 
__p = -1; 
default: 
error(arguments.callee, UNSPPORT_BORDER_TYPE/* {line} */); 
} 
} 
return __p; 
};

这个函数的意义是对于原长度为len的某一行或者某一列的虚拟像素点p(p一般是负数或者大于或等于该行原长度的数,负数则表示该行左边的像素点,大于或等于原长度则表示是右边的像素点),映射成这一行的哪一个像素点。我们拿CV_BORDER_REPLICATE分析一下,其表达式是:

__p = __p < 0 ? 0 : __len - 1;
也就是说p为负数时(也就是左边)的时候映射为0,否则映射成len - 1。
然后我们来实现copyMakeBorder_8U函数:

function copyMakeBorder_8U(__src, __top, __left, __bottom, __right, __borderType){ 
var i, j; 
var width = __src.col, 
height = __src.row; 
var top = __top, 
left = __left || __top, 
right = __right || left, 
bottom = __bottom || top, 
dstWidth = width + left + right, 
dstHeight = height + top + bottom, 
borderType = borderType || CV_BORDER_REFLECT; 
var buffer = new ArrayBuffer(dstHeight * dstWidth * 4), 
tab = new Uint32Array(left + right); 
for(i = 0; i < left; i++){ 
tab[i] = borderInterpolate(i - left, width, __borderType); 
} 
for(i = 0; i < right; i++){ 
tab[i + left] = borderInterpolate(width + i, width, __borderType); 
} 
var tempArray, data; 
for(i = 0; i < height; i++){ 
tempArray = new Uint32Array(buffer, (i + top) * dstWidth * 4, dstWidth); 
data = new Uint32Array(__src.buffer, i * width * 4, width); 
for(j = 0; j < left; j++) 
tempArray[j] = data[tab[j]]; 
for(j = 0; j < right; j++) 
tempArray[j + width + left] = data[tab[j + left]]; 
tempArray.set(data, left); 
} 
var allArray = new Uint32Array(buffer); 
for(i = 0; i < top; i++){ 
j = borderInterpolate(i - top, height, __borderType); 
tempArray = new Uint32Array(buffer, i * dstWidth * 4, dstWidth); 
tempArray.set(allArray.subarray((j + top) * dstWidth, (j + top + 1) * dstWidth)); 
} 
for(i = 0; i < bottom; i++){ 
j = borderInterpolate(i + height, height, __borderType); 
tempArray = new Uint32Array(buffer, (i + top + height) * dstWidth * 4, dstWidth); 
tempArray.set(allArray.subarray((j + top) * dstWidth, (j + top + 1) * dstWidth)); 
} 
return new Mat(dstHeight, dstWidth, new Uint8ClampedArray(buffer)); 
}

这里需要解释下,边缘的复制顺序是:先对每行的左右进行扩展,然后在此基础上进行上下扩展,如图所示。
Javascript图像处理—虚拟边缘介绍及使用方法
然后我们根据ArrayBuffer的性质,将数据转成无符号32位整数来操作,这样每个操作单位就对应了每个像素点了。什么意思?
比如对于某个像素点:RGBA,由于某个通道是用无符号8为整数来存储的,所以实际上一个像素点则对应了32位的存储大小,由于ArrayBuffer的性质,可以将数据转成任意类型来处理,这样我们就可以通过转成Uint32Array类型,将数据变成每个像素点的数据数组。
那么copyMakeConstBorder_8U就比较容易实现了:
function copyMakeConstBorder_8U(__src, __top, __left, __bottom, __right, __value){ 
var i, j; 
var width = __src.col, 
height = __src.row; 
var top = __top, 
left = __left || __top, 
right = __right || left, 
bottom = __bottom || top, 
dstWidth = width + left + right, 
dstHeight = height + top + bottom, 
value = __value || [0, 0, 0, 255]; 
var constBuf = new ArrayBuffer(dstWidth * 4), 
constArray = new Uint8ClampedArray(constBuf); 
buffer = new ArrayBuffer(dstHeight * dstWidth * 4); 
for(i = 0; i < dstWidth; i++){ 
for( j = 0; j < 4; j++){ 
constArray[i * 4 + j] = value[j]; 
} 
} 
constArray = new Uint32Array(constBuf); 
var tempArray; 
for(i = 0; i < height; i++){ 
tempArray = new Uint32Array(buffer, (i + top) * dstWidth * 4, left); 
tempArray.set(constArray.subarray(0, left)); 
tempArray = new Uint32Array(buffer, ((i + top + 1) * dstWidth - right) * 4, right); 
tempArray.set(constArray.subarray(0, right)); 
tempArray = new Uint32Array(buffer, ((i + top) * dstWidth + left) * 4, width); 
tempArray.set(new Uint32Array(__src.buffer, i * width * 4, width)); 
} 
for(i = 0; i < top; i++){ 
tempArray = new Uint32Array(buffer, i * dstWidth * 4, dstWidth); 
tempArray.set(constArray); 
} 
for(i = 0; i < bottom; i++){ 
tempArray = new Uint32Array(buffer, (i + top + height) * dstWidth * 4, dstWidth); 
tempArray.set(constArray); 
} 
return new Mat(dstHeight, dstWidth, new Uint8ClampedArray(buffer)); 
}

效果图
CV_BORDER_REPLICATE

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

CV_BORDER_REFLECT

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

CV_BORDER_WRAP

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

CV_BORDER_CONSTANT

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

Javascript 相关文章推荐
有关DOM元素与事件的3个谜题
Nov 11 Javascript
UI Events 用户界面事件
Jun 27 Javascript
Jquery实现网页跳转或用命令打开指定网页的解决方法
Jul 09 Javascript
JavaScript字符串对象slice方法入门实例(用于字符串截取)
Oct 16 Javascript
JS实现的表格行鼠标点击高亮效果代码
Nov 27 Javascript
JavaScript知识点总结(五)之Javascript中两个等于号(==)和三个等于号(===)的区别
May 31 Javascript
jQuery 局部div刷新和全局刷新方法总结
Oct 05 Javascript
js遮罩效果制作弹出注册界面效果
Jan 25 Javascript
JavaScript基础之流程控制语句的用法
Aug 31 Javascript
Node.js应用设置安全的沙箱环境
Apr 23 Javascript
vue2.0 自定义组件的方法(vue组件的封装)
Jun 05 Javascript
详解关于JSON.parse()和JSON.stringify()的性能小测试
Mar 14 Javascript
JS 添加网页桌面快捷方式的代码详细整理
Dec 27 #Javascript
JavaScript初学者应注意的七个细节详细介绍
Dec 27 #Javascript
圣诞节Merry Christmas给博客添加浪漫的下雪效果基于jquery实现
Dec 27 #Javascript
js/ajax跨越访问-jsonp的原理和实例(javascript和jquery实现代码)
Dec 27 #Javascript
关于火狐(firefox)及ie下event获取的两种方法
Dec 27 #Javascript
Javascript图像处理—为矩阵添加常用方法
Dec 27 #Javascript
ie支持function.bind()方法实现代码
Dec 27 #Javascript
You might like
1982年日本摄影师镜头下的中国孩子 那无忧无虑的童年
2020/03/12 杂记
一个连接两个不同MYSQL数据库的PHP程序
2006/10/09 PHP
php根据日期显示所在星座的方法
2015/07/13 PHP
php用xpath解析html的代码实例讲解
2019/02/14 PHP
Laravel框架数据库迁移操作实例详解
2020/04/06 PHP
JavaScript 动态创建VML的方法
2009/10/14 Javascript
JQuery获取文本框中字符长度的代码
2011/09/29 Javascript
js bind 函数 使用闭包保存执行上下文
2011/12/26 Javascript
JS实现多物体缓冲运动实例代码
2013/11/29 Javascript
jQuery Trim去除字符串首尾空字符的实现方法说明
2014/02/11 Javascript
jQuery+slidereveal实现的面板滑动侧边展出效果
2015/03/14 Javascript
JQuery操作textarea,input,select,checkbox方法
2015/09/02 Javascript
js面向对象实现canvas制作彩虹球喷枪效果
2016/09/24 Javascript
js实现html table 行,列锁定的简单实例
2016/10/13 Javascript
简单实现js无缝滚动效果
2017/02/05 Javascript
vue1.0和vue2.0的watch监听事件写法详解
2018/09/11 Javascript
Vue开发Html5微信公众号的步骤
2019/04/11 Javascript
详解JS实现简单的时分秒倒计时代码
2019/04/25 Javascript
jquery+css3实现的经典弹出层效果示例
2020/05/16 jQuery
微信小程序实现自定义动画弹框/提示框的方法实例
2020/11/06 Javascript
详解Typescript里的This的使用方法
2021/01/08 Javascript
Python文件夹与文件的操作实现代码
2014/07/13 Python
Python中支持向量机SVM的使用方法详解
2017/12/26 Python
Python 3 实现定义跨模块的全局变量和使用教程
2019/07/07 Python
利用Tensorflow构建和训练自己的CNN来做简单的验证码识别方式
2020/01/20 Python
Python数组并集交集补集代码实例
2020/02/18 Python
商务邀请函范文
2014/01/14 职场文书
列车长先进事迹材料
2014/01/25 职场文书
大学英语演讲稿范文
2014/04/24 职场文书
祖国在我心中演讲稿600字
2014/05/04 职场文书
2014年青年志愿者工作总结
2014/12/09 职场文书
2014年人大工作总结
2014/12/10 职场文书
2015年党风廉政建设工作总结
2015/04/09 职场文书
教务处教学工作总结
2015/08/10 职场文书
海贼王十大逆天果实 魂魂果实上榜,岩浆果实攻击力最强
2022/03/18 日漫
详细介绍MySQL中limit和offset的用法
2022/05/06 MySQL