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 相关文章推荐
javascript 常用关键字列表集合
Dec 04 Javascript
JavaScript DOM 添加事件
Feb 14 Javascript
利用ajaxfileupload插件实现文件上传无刷新的具体方法
Jun 08 Javascript
laypage分页控件使用实例详解
May 19 Javascript
原生js实现class的添加和删除简单代码
Jul 12 Javascript
微信小程序进行微信支付的步骤昂述
Dec 01 Javascript
原生javascript上传图片带进度条【实例分享】
Apr 06 Javascript
JavaScript队列函数和异步执行详解
Jun 19 Javascript
D3.js(v3)+react 实现带坐标与比例尺的柱形图 (V3版本)
May 09 Javascript
如何在Vue中抽离接口配置文件
Oct 31 Javascript
Vuex的各个模块封装的实现
Jun 05 Javascript
vue实现几秒后跳转新页面代码
Sep 09 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
虹吸式咖啡壶操作
2021/03/03 冲泡冲煮
用php实现批量查询清除一句话后门的代码
2008/01/20 PHP
php strnatcmp()函数的用法总结
2013/11/27 PHP
CI框架数据库查询之join用法分析
2016/05/18 PHP
Yii2实现增删改查后留在当前页的方法详解
2017/01/13 PHP
[原创]PHP实现生成vcf vcard文件功能类定义与使用方法详解【附demo源码下载】
2017/09/02 PHP
PHP登录验证功能示例【用户名、密码、验证码、数据库、已登陆验证、自动登录和注销登录等】
2019/02/25 PHP
ExtJS Window 最小化的一种方法
2009/11/18 Javascript
jquery获取ASP.NET服务器端控件dropdownlist和radiobuttonlist生成客户端HTML标签后的value和text值
2010/06/28 Javascript
document.getElementById获取控件对象为空的解决方法
2013/11/20 Javascript
javascript校验价格合法性实例(必须输入2位小数)
2014/05/05 Javascript
javascript ajax的5种状态介绍
2014/08/18 Javascript
jQuery实现的导航动画效果(附demo源码)
2016/04/01 Javascript
js实现的简单图片浮动效果完整实例
2016/05/10 Javascript
vue-cli+webpack在生成的项目中使用bootstrap实例代码
2017/05/26 Javascript
jQuery动画_动力节点节点Java学院整理
2017/07/04 jQuery
node中Express 动态设置端口的方法
2017/08/04 Javascript
Django+Vue实现WebSocket连接的示例代码
2019/05/28 Javascript
如何在vue 中引入使用jquery
2020/11/10 jQuery
[04:29]【TI9采访】OG.N0tail在胜者组决赛后接受采访
2019/08/25 DOTA
详解Python的迭代器、生成器以及相关的itertools包
2015/04/02 Python
Python字符串匹配算法KMP实例
2015/07/18 Python
django小技巧之html模板中调用对象属性或对象的方法
2018/11/30 Python
python3 selenium自动化 下拉框定位的例子
2019/08/23 Python
使用PyTorch实现MNIST手写体识别代码
2020/01/18 Python
Python打包模块wheel的使用方法与将python包发布到PyPI的方法详解
2020/02/12 Python
Python数据分析库pandas高级接口dt的使用详解
2020/12/11 Python
欧缇丽美国官网:Caudalie美国
2016/12/31 全球购物
Bowflex美国官方网站:高级家庭健身器材
2017/12/22 全球购物
工程承诺书怎么写
2014/05/24 职场文书
创新社会管理心得体会
2014/09/12 职场文书
2014年销售员工作总结
2014/12/01 职场文书
小学生读书笔记范文
2015/06/30 职场文书
教师师德工作总结2015
2015/07/22 职场文书
《钓鱼的启示》教学反思
2016/02/18 职场文书
Python中的 Set 与 dict
2022/03/13 Python