Javascript图像处理—平滑处理实现原理


Posted in Javascript onDecember 28, 2012

前言

上一篇文章,我们讲解了图像的虚拟边缘,这篇文章开始进行平滑(也就是模糊)处理。

基本原理

这里直接引用OpenCV 2.4+ C++ 平滑处理和OpenCV 2.4+ C++ 边缘梯度计算的相关内容:

平滑也称模糊, 是一项简单且使用频率很高的图像处理方法。

平滑处理时需要用到一个滤波器

。 最常用的滤波器是线性 滤波器,线性滤波处理的输出像素值(例如:Javascript图像处理—平滑处理实现原理)是输入像素值(例如:Javascript图像处理—平滑处理实现原理)的加权平均:

Javascript图像处理—平滑处理实现原理

Javascript图像处理—平滑处理实现原理称为核

, 它仅仅是一个加权系数。

这里涉及一种叫做“卷积”的运算,那么卷积是什么呢?

卷积是在每一个图像块与某个算子(核)之间进行的运算。

核?!

nbsp;
dsds

核就是一个固定大小的数值数组。该数组带有一个锚点

 ,一般位于数组中央。

Javascript图像处理—平滑处理实现原理

 可是这怎么运算啊?

假如你想得到图像的某个特定位置的卷积值,可用下列方法计算:

    将核的锚点放在该特定位置的像素上,同时,核内的其他值与该像素邻域的各像素重合;将核内各值与相应像素值相乘,并将乘积相加;将所得结果放到与锚点对应的像素上;对图像所有像素重复上述过程。

用公式表示上述过程如下:

Javascript图像处理—平滑处理实现原理

在图像边缘的卷积怎么办呢?

计算卷积前,需要通过复制源图像的边界创建虚拟像素,这样边缘的地方也有足够像素计算卷积了。这就是为什么上一篇文章需要做虚拟边缘函数。

 

均值平滑

均值平滑实际上就是内核元素全是1的卷积运算,然后再除以内核的大小,用数学表达式来表示就是:

Javascript图像处理—平滑处理实现原理

下面我们来实现均值平滑函数blur:

function blur(__src, __size1, __size2, __borderType, __dst){ 
if(__src.type && __src.type == "CV_RGBA"){ 
var height = __src.row, 
width = __src.col, 
dst = __dst || new Mat(height, width, CV_RGBA), 
dstData = dst.data; 
var size1 = __size1 || 3, 
size2 = __size2 || size1, 
size = size1 * size2; 
if(size1 % 2 !== 1 || size2 % 2 !== 1){ 
console.error("size大小必须是奇数"); 
return __src; 
} 
var startX = Math.floor(size1 / 2), 
startY = Math.floor(size2 / 2); 
var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType), 
mData = withBorderMat.data, 
mWidth = withBorderMat.col; var newValue, nowX, offsetY, offsetI; 
var i, j, c, y, x; 
for(i = height; i--;){ 
offsetI = i * width; 
for(j = width; j--;){ 
for(c = 3; c--;){ 
newValue = 0; 
for(y = size2; y--;){ 
offsetY = (y + i) * mWidth * 4; 
for(x = size1; x--;){ 
nowX = (x + j) * 4 + c; 
newValue += mData[offsetY + nowX]; 
} 
} 
dstData[(j + offsetI) * 4 + c] = newValue / size; 
} 
dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3]; 
} 
} 
}else{ 
console.error("不支持类型。"); 
} 
return dst; 
}

其中size1和size2分别是核的横向和纵向大小,并且必须是正奇数。

高斯平滑

最有用的滤波器 (尽管不是最快的)。 高斯滤波是将输入数组的每一个像素点与高斯内核

卷积将卷积和当作输出像素值。

Javascript图像处理—平滑处理实现原理

参考一维高斯函数,我们可以看见,他是个中间大两边小的函数。

所以高斯滤波器其加权数是中间大,四周小的。

其二维高斯函数为:

Javascript图像处理—平滑处理实现原理 

其中 

Javascript图像处理—平滑处理实现原理 为均值 (峰值对应位置), Javascript图像处理—平滑处理实现原理 代表标准差 (变量 Javascript图像处理—平滑处理实现原理 和 变量 Javascript图像处理—平滑处理实现原理 各有一个均值,也各有一个标准差)。

这里参考OpenCV的实现,不过应该还有优化空间,因为还没用到分离滤波器。

首先我们做一个getGaussianKernel来返回高斯滤波器的一维数组。

function getGaussianKernel(__n, __sigma){ 
var SMALL_GAUSSIAN_SIZE = 7, 
smallGaussianTab = [[1], 
[0.25, 0.5, 0.25], 
[0.0625, 0.25, 0.375, 0.25, 0.0625], 
[0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125] 
]; var fixedKernel = __n & 2 == 1 && __n <= SMALL_GAUSSIAN_SIZE && __sigma <= 0 ? smallGaussianTab[__n >> 1] : 0; 
var sigmaX = __sigma > 0 ? __sigma : ((__n - 1) * 0.5 - 1) * 0.3 + 0.8, 
scale2X = -0.5 / (sigmaX * sigmaX), 
sum = 0; 
var i, x, t, kernel = []; 
for(i = 0; i < __n; i++){ 
x = i - (__n - 1) * 0.5; 
t = fixedKernel ? fixedKernel[i] : Math.exp(scale2X * x * x); 
kernel[i] = t; 
sum += t; 
} 
sum = 1 / sum; 
for(i = __n; i--;){ 
kernel[i] *= sum; 
} 
return kernel; 
};

然后通过两个这个一维数组,便可以计算出一个完整的高斯内核,再利用blur里面用到的循环方法,就可以算出高斯平滑后的矩阵了。
function GaussianBlur(__src, __size1, __size2, __sigma1, __sigma2, __borderType, __dst){ 
if(__src.type && __src.type == "CV_RGBA"){ 
var height = __src.row, 
width = __src.col, 
dst = __dst || new Mat(height, width, CV_RGBA), 
dstData = dst.data; 
var sigma1 = __sigma1 || 0, 
sigma2 = __sigma2 || __sigma1; 
var size1 = __size1 || Math.round(sigma1 * 6 + 1) | 1, 
size2 = __size2 || Math.round(sigma2 * 6 + 1) | 1, 
size = size1 * size2; 
if(size1 % 2 !== 1 || size2 % 2 !== 1){ 
console.error("size必须是奇数。"); 
return __src; 
} 
var startX = Math.floor(size1 / 2), 
startY = Math.floor(size2 / 2); 
var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType), 
mData = withBorderMat.data, 
mWidth = withBorderMat.col; var kernel1 = getGaussianKernel(size1, sigma1), 
kernel2, 
kernel = new Array(size1 * size2); 
if(size1 === size2 && sigma1 === sigma2) 
kernel2 = kernel1; 
else 
kernel2 = getGaussianKernel(size2, sigma2); 
var i, j, c, y, x; 
for(i = kernel2.length; i--;){ 
for(j = kernel1.length; j--;){ 
kernel[i * size1 + j] = kernel2[i] * kernel1[j]; 
} 
} 
var newValue, nowX, offsetY, offsetI; 
for(i = height; i--;){ 
offsetI = i * width; 
for(j = width; j--;){ 
for(c = 3; c--;){ 
newValue = 0; 
for(y = size2; y--;){ 
offsetY = (y + i) * mWidth * 4; 
for(x = size1; x--;){ 
nowX = (x + j) * 4 + c; 
newValue += (mData[offsetY + nowX] * kernel[y * size1 + x]); 
} 
} 
dstData[(j + offsetI) * 4 + c] = newValue; 
} 
dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3]; 
} 
} 
}else{ 
console.error("不支持的类型"); 
} 
return dst; 
}

中值平滑

中值滤波将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的

中值代替 。

依然使用blur里面用到的循环,只要得到核中的所有值,再通过sort排序便可以得到中值,然后锚点由该值替代。

function medianBlur(__src, __size1, __size2, __borderType, __dst){ 
if(__src.type && __src.type == "CV_RGBA"){ 
var height = __src.row, 
width = __src.col, 
dst = __dst || new Mat(height, width, CV_RGBA), 
dstData = dst.data; 
var size1 = __size1 || 3, 
size2 = __size2 || size1, 
size = size1 * size2; 
if(size1 % 2 !== 1 || size2 % 2 !== 1){ 
console.error("size必须是奇数"); 
return __src; 
} 
var startX = Math.floor(size1 / 2), 
startY = Math.floor(size2 / 2); 
var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType), 
mData = withBorderMat.data, 
mWidth = withBorderMat.col; var newValue = [], nowX, offsetY, offsetI; 
var i, j, c, y, x; 
for(i = height; i--;){ 
offsetI = i * width; 
for(j = width; j--;){ 
for(c = 3; c--;){ 
for(y = size2; y--;){ 
offsetY = (y + i) * mWidth * 4; 
for(x = size1; x--;){ 
nowX = (x + j) * 4 + c; 
newValue[y * size1 + x] = mData[offsetY + nowX]; 
} 
} 
newValue.sort(); 
dstData[(j + offsetI) * 4 + c] = newValue[Math.round(size / 2)]; 
} 
dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3]; 
} 
} 
}else{ 
console.error("类型不支持"); 
} 
return dst; 
};
Javascript 相关文章推荐
初学JavaScript_03(ExtJs Grid的简单使用)
Oct 02 Javascript
node.js操作mysql(增删改查)
Jul 24 Javascript
EasyUI布局 高度自适应
Jun 04 Javascript
JS动态给对象添加事件的简单方法
Jul 19 Javascript
jQuery基于ajax操作json数据简单示例
Jan 05 Javascript
windows下vue.js开发环境搭建教程
Mar 20 Javascript
vue bus全局事件中心简单Demo详解
Feb 26 Javascript
vue axios数据请求get、post方法及实例详解
Sep 11 Javascript
小程序页面动态配置实现方法
Feb 05 Javascript
Vue.js实现可编辑的表格
Dec 11 Javascript
Vue中el-form标签中的自定义el-select下拉框标签功能
Apr 20 Javascript
Vue ElementUI实现:限制输入框只能输入正整数的问题
Jul 31 Javascript
js获取网页高度(详细整理)
Dec 28 #Javascript
前台js改变Session的值(用ajax实现)
Dec 28 #Javascript
window.open的页面如何刷新(父页面)上层页面
Dec 28 #Javascript
CSS(js)限制页面显示的文本字符长度
Dec 27 #Javascript
jQuery基础框架浅入剖析
Dec 27 #Javascript
Three.js源码阅读笔记(基础的核心Core对象)
Dec 27 #Javascript
Three.js源码阅读笔记(Object3D类)
Dec 27 #Javascript
You might like
php数组函数序列之ksort()对数组的元素键名进行升序排序,保持索引关系
2011/11/02 PHP
PHP中将网页导出为Word文档的代码
2012/05/25 PHP
探讨:web上存漏洞及原理分析、防范方法
2013/06/29 PHP
thinkphp模板用法和内容输出实例
2014/11/28 PHP
php实现转换ubb代码的方法
2015/06/18 PHP
Laravel 5.5基于内置的Auth模块实现前后台登陆详解
2017/12/21 PHP
php微信公众号开发之图片回复
2018/10/20 PHP
跟着JQuery API学Jquery 之三 筛选
2010/04/09 Javascript
JavaScript具有类似Lambda表达式编程能力的代码(改进版)
2010/09/14 Javascript
JS实现在Repeater控件中创建可隐藏区域的代码
2010/09/16 Javascript
XMLHTTP 乱码的解决方法(UTF8,GB2312 编码 解码)
2011/01/12 Javascript
jQuery fadeTo方法调整图片的透明度使用介绍
2013/05/06 Javascript
jquery自动将form表单封装成json的具体实现
2014/03/17 Javascript
javascript实现数组去重的多种方法
2016/03/14 Javascript
Vue响应式添加、修改数组和对象的值
2017/03/20 Javascript
Node.js实现连接mysql数据库功能示例
2017/09/15 Javascript
原生js+cookie实现购物车功能的方法分析
2017/12/21 Javascript
JavaScript设计模式之原型模式分析【ES5与ES6】
2018/07/26 Javascript
Electron-vue脚手架改造vue项目的方法
2018/10/22 Javascript
详解vue中v-on事件监听指令的基本用法
2020/07/22 Javascript
python自动格式化json文件的方法
2015/03/11 Python
浅析Python多线程下的变量问题
2015/04/28 Python
Eclipse中Python开发环境搭建简单教程
2016/03/23 Python
对PyQt5中的菜单栏和工具栏实例详解
2019/06/20 Python
Python PyCharm如何进行断点调试
2019/07/05 Python
Matplotlib绘制雷达图和三维图的示例代码
2020/01/07 Python
python如何求数组连续最大和的示例代码
2020/02/04 Python
Python爬虫设置Cookie解决网站拦截并爬取蚂蚁短租的问题
2021/02/22 Python
html5 touch事件实现页面上下滑动效果【附代码】
2016/03/10 HTML / CSS
大学四年个人自我小结
2014/03/05 职场文书
高考诚信考试承诺书
2015/04/29 职场文书
python获取淘宝服务器时间的代码示例
2021/04/22 Python
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis
Nginx 配置 HTTPS的详细过程
2022/05/30 Servers
使用Cargo工具高效创建Rust项目
2022/08/14 Javascript
python index() 与 rindex() 方法的使用示例详解
2022/12/24 Python