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 相关文章推荐
解决 firefox 不支持 document.all的方法
Mar 12 Javascript
jquery的$(document).ready()和onload的加载顺序
May 26 Javascript
基于JQuery的一句代码实现表格的简单筛选
Jul 26 Javascript
javascript判断是手机还是电脑访问网页的简单实例分享
Jun 03 Javascript
js调试系列 断点与动态调试[基础篇]
Jun 18 Javascript
jquery中each方法示例和常用选择器
Jul 08 Javascript
使用node.js 获取客户端信息代码分享
Nov 26 Javascript
在线所见即所得HTML编辑器的实现原理浅析
Apr 25 Javascript
JavaScript核心语法总结(推荐)
Jun 02 Javascript
Vue下路由History模式打包后页面空白的解决方法
Jun 29 Javascript
深入了解Vue.js 混入(mixins)
Jul 23 Javascript
vue使用openlayers实现移动点动画
Sep 24 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
PHP 采集程序中常用的函数
2009/12/09 PHP
php操作mysql数据库的基本类代码
2014/02/25 PHP
php批量删除cookie的简单实现方法
2015/01/26 PHP
PHP下载文件的函数实例代码
2016/05/18 PHP
PHP封装的XML简单操作类完整实例
2017/11/13 PHP
php并发加锁问题分析与设计代码实例讲解
2021/02/26 PHP
Javascript - HTML的request类
2006/07/15 Javascript
js 跳出页面的frameset框架示例介绍
2013/12/23 Javascript
jQuery函数的第二个参数获取指定上下文中的DOM元素
2014/05/19 Javascript
微信WeixinJSBridge API使用实例
2015/05/25 Javascript
jquery实现简单的轮换出现效果实例
2015/07/23 Javascript
jQuery插件实现图片轮播特效
2016/06/16 Javascript
jQuery包裹节点用法完整示例
2016/09/13 Javascript
深入掌握 react的 setState的工作机制
2017/09/27 Javascript
微信、QQ、微博、Safari中使用js唤起App
2018/01/24 Javascript
Angular 实现输入框中显示文章标签的实例代码
2018/11/07 Javascript
解决layui弹出层layer的area过大被遮挡的问题
2019/09/21 Javascript
ES6学习笔记之字符串、数组、对象、函数新增知识点实例分析
2020/01/22 Javascript
Vue基于localStorage存储信息代码实例
2020/11/16 Javascript
Python基于生成器迭代实现的八皇后问题示例
2018/05/23 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
2018/09/17 Python
Django给admin添加Action的步骤详解
2019/05/01 Python
详解pandas获取Dataframe元素值的几种方法
2020/06/14 Python
福克斯租车:Fox Rent A Car
2017/04/13 全球购物
95%的面试官都会问到的50道Java线程题,附答案
2012/08/03 面试题
二年级数学教学反思
2014/01/21 职场文书
《小壁虎借尾巴》教学反思
2014/02/16 职场文书
益达广告词
2014/03/14 职场文书
租赁意向书范本
2014/04/01 职场文书
勤奋学习演讲稿
2014/05/10 职场文书
三八节标语
2014/06/27 职场文书
2015羊年春节慰问信
2015/02/14 职场文书
质量保证书格式模板
2015/02/27 职场文书
2015年保育员个人工作总结
2015/05/13 职场文书
2015年乡镇卫生院妇幼保健工作总结
2015/05/19 职场文书
董事会决议范本
2015/07/01 职场文书