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感应鼠标图片透明度显示的方法
Feb 24 Javascript
javascript实现10个球随机运动、碰撞实例详解
Jul 08 Javascript
浅谈javascript中的DOM方法
Jul 16 Javascript
AngularJS表单和输入验证实例
Nov 02 Javascript
Angular在一个页面中使用两个ng-app的方法(二)
Feb 20 Javascript
jQuery选择器特殊字符与属性空格问题
Aug 14 jQuery
将jquery.qqFace.js表情转换成微信的字符码
Dec 01 jQuery
JS获取input[file]的值并显示在页面的实现方法
Mar 09 Javascript
Bootstrap table中toolbar新增条件查询及refresh参数使用方法
May 18 Javascript
jQuery实现的页面详情展开收起功能示例
Jun 11 jQuery
vue监听对象及对象属性问题
Aug 20 Javascript
VUE中setTimeout和setInterval自动销毁案例
Sep 07 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
让你的WINDOWS同时支持MYSQL4,MYSQL4.1,MYSQL5X
2006/12/06 PHP
php中cookie实现二级域名可访问操作的方法
2014/11/11 PHP
php发送短信验证码完成注册功能
2015/11/24 PHP
jQuery Tips 为AJAX回调函数传递额外参数的方法
2010/12/28 Javascript
用JS控制回车事件的代码
2011/02/20 Javascript
JavaScript之自定义类型
2012/05/04 Javascript
Javascript数组的排序 sort()方法和reverse()方法
2012/06/04 Javascript
js自动生成对象的属性示例代码
2013/10/28 Javascript
javascript闭包传参和事件的循环绑定示例探讨
2014/04/17 Javascript
Extjs grid panel自带滚动条失效的解决方法
2014/09/11 Javascript
javascript设置连续两次点击按钮时间间隔的方法
2014/10/28 Javascript
JS中dom0级事件和dom2级事件的区别介绍
2016/05/05 Javascript
js添加千分位的实现代码(超简单)
2016/08/01 Javascript
jQuery实现二维码扫描功能
2017/01/09 Javascript
Bootstrap缩略图的创建方法
2017/03/22 Javascript
angularjs实现的购物金额计算工具示例
2018/05/08 Javascript
Vue props 单向数据流的实现
2018/11/06 Javascript
JS箭头函数和常规函数之间的区别实例分析【 5 个区别】
2020/05/27 Javascript
python实现人人网登录示例分享
2014/01/19 Python
详解Python当中的字符串和编码
2015/04/25 Python
Python读大数据txt
2016/03/28 Python
Python 内置函数进制转换的用法(十进制转二进制、八进制、十六进制)
2018/04/30 Python
selenium+python截图不成功的解决方法
2019/01/30 Python
Python3 Tkinter选择路径功能的实现方法
2019/06/14 Python
Keras实现将两个模型连接到一起
2020/05/23 Python
使用HTML5的File实现base64和图片的互转
2013/08/01 HTML / CSS
网络通讯中,端口有什么含义,端口的取值范围
2012/11/23 面试题
一些网络技术方面的面试题
2014/05/01 面试题
项目计划书范文
2014/01/09 职场文书
成立公司计划书
2014/05/07 职场文书
党的群众路线教育实践活动整改落实情况报告
2014/10/28 职场文书
Python机器学习三大件之一numpy
2021/05/10 Python
使用Python脚本对GiteePages进行一键部署的使用说明
2021/05/27 Python
动漫APP软件排行榜前十名,半次元上榜,第一款由腾讯公司推出
2022/03/18 杂记
python使用torch随机初始化参数
2022/03/22 Python
Python字符串格式化方式
2022/04/07 Python