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 相关文章推荐
通过js简单实现将一个文本内容转译成加密文本
Oct 22 Javascript
jQuery插件实现表格隔行变色及鼠标滑过高亮显示效果代码
Feb 25 Javascript
JS实现把鼠标放到链接上出现滚动文字的方法
Apr 06 Javascript
详解VueJs异步动态加载块
Mar 09 Javascript
Javascript 严格模式use strict详解
Sep 16 Javascript
详解webpack + vue + node 打造单页面(入门篇)
Sep 23 Javascript
Angular实现的简单查询天气预报功能示例
Dec 27 Javascript
JS关于刷新页面的相关总结
May 09 Javascript
使用Vue实现图片上传的三种方式
Jul 17 Javascript
JS通过位运算实现权限加解密
Aug 14 Javascript
vue中利用Promise封装jsonp并调取数据
Jun 18 Javascript
js字符串类型String常用操作实例总结
Jul 05 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
4月1日重磅发布!《星际争霸II》6.0.0版本更新
2020/04/09 星际争霸
一个简单的网页密码登陆php代码
2012/07/17 PHP
PHP判断密码强度的方法详解
2017/05/26 PHP
jQuery autocomplete插件修改
2009/04/17 Javascript
有关js的变量作用域和this指针的讨论
2010/12/16 Javascript
基于jQuery替换table中的内容并显示进度条的代码
2011/08/02 Javascript
JS重要知识点小结
2011/11/06 Javascript
js拦截alert对话框另类应用
2013/01/16 Javascript
使用不同的方法结合/合并两个JS数组
2014/09/18 Javascript
很不错的两款Bootstrap Icon图标选择组件
2016/01/28 Javascript
jQuery实现最简单的切换图效果【可兼容IE6、火狐、谷歌、opera等】
2016/09/04 Javascript
Angular.js中下拉框实现渲染html的方法
2017/06/18 Javascript
vue中阻止click事件冒泡,防止触发另一个事件的方法
2018/02/08 Javascript
Vue 实现双向绑定的四种方法
2018/03/16 Javascript
基于bootstrap页面渲染的问题解决方法
2018/08/09 Javascript
Vue.extend 编程式插入组件的实现
2019/11/18 Javascript
jQuery实现高度灵活的表单验证功能示例【无UI】
2020/04/30 jQuery
JS原形与原型链深入详解
2020/05/09 Javascript
jQuery 函数实例分析【函数声明、函数表达式、匿名函数等】
2020/05/19 jQuery
[02:49]2014DOTA2电竞也是体育项目! 势要把荣誉带回中国!
2014/07/20 DOTA
Python实现扫描指定目录下的子目录及文件的方法
2014/07/16 Python
Python 处理数据的实例详解
2017/08/10 Python
python编程线性回归代码示例
2017/12/07 Python
详谈Python中列表list,元祖tuple和numpy中的array区别
2018/04/18 Python
python list删除元素时要注意的坑点分享
2018/04/18 Python
python实现黑客字幕雨效果
2018/06/21 Python
python处理multipart/form-data的请求方法
2018/12/26 Python
python实现年会抽奖程序
2019/01/22 Python
python使用opencv实现马赛克效果示例
2019/09/28 Python
Python使用tkinter实现摇骰子小游戏功能的代码
2020/07/02 Python
YOINS官网:时尚女装网上购物
2017/03/17 全球购物
圣诞树世界:Christmas Tree World
2019/12/10 全球购物
音乐专业应届生教师求职信
2013/11/04 职场文书
会展策划与管理专业求职信
2014/06/09 职场文书
励志演讲稿大全
2014/08/21 职场文书
Python之matplotlib绘制饼图
2022/04/13 Python