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 相关文章推荐
获取Javscript执行函数名称的方法
Dec 22 Javascript
Ruffy javascript 学习笔记
Nov 30 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
Apr 20 Javascript
ExtJS4 表格的嵌套 rowExpander应用
May 02 Javascript
JavaScript Array对象扩展indexOf()方法
May 09 Javascript
JQuery1.8 判断元素是否绑定事件的方法
Jul 10 Javascript
jQuery中:has选择器用法实例
Dec 30 Javascript
javascript实现避免页面按钮重复提交
Jan 08 Javascript
微信小程序通过api接口将json数据展现到小程序示例
Jan 20 Javascript
vue.js实现数据动态响应 Vue.set的简单应用
Jun 15 Javascript
vue使用stompjs实现mqtt消息推送通知
Jun 22 Javascript
详谈innerHTML innerText的使用和区别
Aug 18 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
动漫定律:眯眯眼都是怪物!这些角色狠话不多~
2020/03/03 日漫
PDO的安全处理与事物处理方法
2016/10/31 PHP
php安装dblib扩展,连接mssql的具体步骤
2017/03/02 PHP
php使用ftp实现文件上传与下载功能
2017/07/21 PHP
ThinkPHP框架使用redirect实现页面重定向的方法实例分析
2018/04/12 PHP
php识别翻转iphone拍摄的颠倒图片
2018/05/17 PHP
PHP实现Markdown文章上传到七牛图床的实例内容
2020/02/11 PHP
关于JavaScript的gzip静态压缩方法
2007/01/05 Javascript
为开发者准备的10款最好的jQuery日历插件
2014/02/04 Javascript
js弹出div并显示遮罩层
2014/02/12 Javascript
ExtJS中设置下拉列表框不可编辑的方法
2014/05/07 Javascript
JavaScript数据结构和算法之图和图算法
2015/02/11 Javascript
js制作支付倒计时页面
2016/10/21 Javascript
Bootstrap Table实现定时刷新数据的方法
2018/08/13 Javascript
利用Dectorator分模块存储Vuex状态的实现
2019/02/05 Javascript
小程序实现搜索界面 小程序实现推荐搜索列表效果
2019/05/18 Javascript
Ant Design Pro 下实现文件下载的实现代码
2019/12/03 Javascript
[01:33]一分钟玩转DOTA2第三弹:DOTA2&DotA快捷操作大对比
2014/06/04 DOTA
[52:06]完美世界DOTA2联赛决赛日 Inki vs LBZS 第一场 11.08
2020/11/10 DOTA
利用python画一颗心的方法示例
2017/01/31 Python
在Pycharm中执行scrapy命令的方法
2019/01/16 Python
Python 获取ftp服务器文件时间的方法
2019/07/02 Python
Python 列表去重去除空字符的例子
2019/07/20 Python
python GUI库图形界面开发之PyQt5树形结构控件QTreeWidget详细使用方法与实例
2020/03/02 Python
Python MOCK SERVER moco模拟接口测试过程解析
2020/04/13 Python
关于Kotlin中SAM转换的那些事
2020/09/15 Python
收集的22款给力的HTML5和CSS3帮助工具
2012/09/14 HTML / CSS
文言文形式的学生求职信
2013/12/03 职场文书
学生安全承诺书
2014/05/22 职场文书
2014年幼儿园园务工作总结
2014/12/05 职场文书
入团介绍人意见范文
2015/06/04 职场文书
2016关于军训的心得体会
2016/01/11 职场文书
安全生产培训心得体会
2016/01/18 职场文书
中国古代史学名著《战国策》概述
2019/08/09 职场文书
详解CSS故障艺术
2021/05/25 HTML / CSS
vue ref如何获取子组件属性值
2022/03/31 Vue.js