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 相关文章推荐
JQuery自定义事件的应用 JQuery最佳实践
Aug 01 Javascript
Javascript实现仿WebQQ界面的“浮云”兼容 IE7以上版本及FF
Apr 27 Javascript
js调用activeX获取u盘序列号的代码
Nov 21 Javascript
Function.prototype.call.apply结合用法分析示例
Jul 03 Javascript
jquery滚动条插件jScrollPane的使用介绍
Nov 08 Javascript
javascript中hasOwnProperty() 方法使用指南
Mar 09 Javascript
【经验总结】编写JavaScript代码时应遵循的14条规律
Jun 20 Javascript
原生JS封装animate运动框架的实例
Oct 12 Javascript
es6基础学习之解构赋值
Dec 10 Javascript
JS forEach跳出循环2种实现方法
Jun 24 Javascript
微信小程序实现列表的横向滑动方式
Jul 15 Javascript
react实现复选框全选和反选组件效果
Aug 25 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
《神奇女侠:血脉》神力女超人大战犯罪公司
2020/04/09 欧美动漫
一个分页的论坛
2006/10/09 PHP
php面向对象中static静态属性和静态方法的调用
2015/02/08 PHP
html下载本地
2006/06/19 Javascript
在Javascript里访问SharePoint列表数据的实现方法
2011/05/22 Javascript
兼容ie、firefox的图片自动缩放的css跟js代码分享
2012/01/21 Javascript
jquery入门——事件机制之事件中的冒泡现象示例解释
2020/09/12 Javascript
jquery实现漂浮在网页右侧的qq在线客服插件示例
2013/05/13 Javascript
jQuery的css()方法用法实例
2014/12/24 Javascript
jQuery搜索子元素的方法
2015/02/10 Javascript
JS实现当前页居中分页效果的方法
2015/06/18 Javascript
基于bootstrap3和jquery的分页插件
2015/07/31 Javascript
JQuery 的跨域方法推荐_可跨任何网站
2016/05/18 Javascript
Bootstrap3使用typeahead插件实现自动补全功能
2016/07/07 Javascript
Vue.js递归组件构建树形菜单
2017/12/24 Javascript
vue+element实现批量删除功能的示例
2018/02/28 Javascript
jQuery AJAX 方法success()后台传来的4种数据详解
2018/08/08 jQuery
微信小程序自定义toast的实现代码
2018/11/16 Javascript
Angular6新特性之Angular Material
2018/12/28 Javascript
vue+element实现表单校验功能
2019/05/20 Javascript
vue实现多级菜单效果
2019/10/19 Javascript
python类定义的讲解
2013/11/01 Python
python 创建弹出式菜单的实现代码
2017/07/11 Python
对Tensorflow中权值和feature map的可视化详解
2018/06/14 Python
python算法与数据结构之单链表的实现代码
2019/06/27 Python
python 线性回归分析模型检验标准--拟合优度详解
2020/02/24 Python
python 实现Harris角点检测算法
2020/12/11 Python
德国大型和小型家用电器网上商店:Energeto
2019/05/15 全球购物
工程力学硕士生的自我评价范文
2013/11/16 职场文书
中专生自荐信
2014/06/25 职场文书
领导干部作风整顿个人剖析材料
2014/10/11 职场文书
初中作文评语集锦
2014/12/25 职场文书
教师学期个人总结
2015/02/11 职场文书
高质量“欢迎词”
2019/04/03 职场文书
关于PHP数组迭代器的使用方法实例
2021/11/17 PHP
不想升级Win11?教你彻底锁定老版Windows系统的方法(附下载地址)
2022/09/23 数码科技