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 相关文章推荐
JS 的应用开发初探(mootools)
Dec 19 Javascript
extjs 的权限问题 要求控制的对象是 菜单,按钮,URL
Mar 09 Javascript
js特效,页面下雪的小例子
Jun 17 Javascript
javascript实现的一个带下拉框功能的文本框
May 08 Javascript
JavaScript编写点击查看大图的页面半透明遮罩层效果实例
May 09 Javascript
yarn的使用与升级Node.js的方法详解
Jun 04 Javascript
详解Angular.js中$http拦截器的介绍及使用
Jul 04 Javascript
vue.config.js中配置Vue的路径别名的方法
Feb 11 Javascript
微信小程序使用GoEasy实现websocket实时通讯
May 19 Javascript
js实现全选和全不选
Jul 28 Javascript
three.js 将图片马赛克化的示例代码
Jul 31 Javascript
react中props 的使用及进行限制的方法
Apr 28 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代码
2013/03/24 PHP
php面向对象中static静态属性与方法的内存位置分析
2015/02/08 PHP
PHP实现图片上传并压缩
2015/12/22 PHP
golang实现php里的serialize()和unserialize()序列和反序列方法详解
2018/10/30 PHP
Laravel6.18.19如何优雅的切换发件账户
2020/06/14 PHP
php并发加锁问题分析与设计代码实例讲解
2021/02/26 PHP
Javascript 网页黑白效果实现代码(兼容IE/FF等)
2010/04/23 Javascript
20个最新的jQuery插件
2012/01/13 Javascript
Js 获取Gridview选中行的内容操作步骤
2013/02/05 Javascript
推荐一款jQuery插件模板
2015/01/09 Javascript
js实现Select下拉框具有输入功能的方法
2015/02/06 Javascript
javascript之Array 数组对象详解
2016/06/07 Javascript
一个极为简单的requirejs实现方法
2016/10/20 Javascript
浅谈angularjs $http提交数据探索
2017/01/20 Javascript
AngularJS实现动态添加Option的方法
2017/05/17 Javascript
vue-router+vuex addRoutes实现路由动态加载及菜单动态加载
2017/09/28 Javascript
vue+springboot实现项目的CORS跨域请求
2018/09/05 Javascript
django js 实现表格动态标序号的实例代码
2019/07/12 Javascript
解决layer.msg 不居中 ifram中的问题
2019/09/05 Javascript
[02:28]DOTA2亚洲邀请赛 LGD战队巡礼
2015/02/03 DOTA
django js实现部分页面刷新的示例代码
2018/05/28 Python
Random 在 Python 中的使用方法
2018/08/09 Python
Django 实现购物车功能的示例代码
2018/10/08 Python
解决webdriver.Chrome()报错:Message:'chromedriver' executable needs to be in Path
2019/06/12 Python
python数据持久存储 pickle模块的基本使用方法解析
2019/08/30 Python
Python爬取破解无线网络wifi密码过程解析
2019/09/17 Python
Python 爬虫实现增加播客访问量的方法实现
2019/10/31 Python
马克华菲官方商城:Mark Fairwhale
2016/09/04 全球购物
苹果台湾官网:Apple台湾
2019/01/05 全球购物
数控技术与应用毕业生自荐信
2013/09/24 职场文书
消防先进事迹材料
2014/02/10 职场文书
技校毕业生自荐信范文
2014/03/07 职场文书
大学生社会实践评语
2014/04/25 职场文书
春节慰问信范文
2015/02/15 职场文书
试用期自我评价怎么写
2015/03/10 职场文书
2016暑期社会实践心得体会范文
2016/01/14 职场文书