基于HTML5的轻量级图像处理引擎Demo

来源:互联网 发布:c语言制作俄罗斯方块 编辑:程序博客网 时间:2024/05/11 19:32

    前几天看了看腾讯Web前端Alloy Team基于HTML5的专业级开源图像处理引擎,非常喜欢,毕竟这样的项目本身在国内就不多,更别说做得好的。我说的所谓这样的项目,指的是基于HTML5和javascript的图像处理技术的Web项目。

    Web前端在世界范围内越来越受到重视,国内也有很多优秀的Web前端团队。说到Web前端,就不得不提HTML5,这个发布了那么久却依然没有流行起来(起码看起来还没有)的,被苹果、Facebook等大公司推崇的最新网页标准和技术。

    HTML5为什么至今还没有流行起来,具体原因很复杂。有人想用HTML5做游戏,结果很失望地发现在性能上HTML5还不够好;有人想用HTML5做网站,又怕IE之流不支持最新的HTML5标准;有人想用HTML5做应用,以代替移动端的App,但是App开发技术已经比较成熟,没有所少人人愿意把移动App转到HTML5应用上(想想Facebook放弃Html5转到ios和Android App就知道)。

    不管怎么说,Html5在很多曾经对Html5有过很多期望的开发者看来,它还不够成熟。

    个人觉得,开发者还需要对Html5做更多的尝试,只有尝试过才知道好不好。Html5是一种标准,至于应该怎么用,没有人会给你制定标准。几乎每一个刚刚接触Html5的人都会有这样的一种感觉:真是太棒了,我以前从来没想过可以这样!确实,这样一种令人眼前一亮的技术,不管它好与坏,我们都愿意尝试。

    Alloy Team的尝试就是非常好的例子。在Web前端做图像处理,本身对时间性能要求不高(和游戏相比),而且可以完全抛弃Flash(很多网页版图像处理都是Flash的,比如美图看看网页版)。

    想起自己去年上过数字图像处理的课,做过一个Java版的图像处理软件,现在又刚开始接触HTML5,于是我也想做一个基于HTML5的图像处理应用。花了一天时间,做成现在的样子:


下面是灰度转换之后的:


下面是Sobel算子锐化(或者边缘检测)的结果:


下面是方形窗口(中值滤波)平滑多次的效果:


其它效果不一一列举,算法也都比较简单。

时间比较仓促,很多功能没有加进来。其实Java代码转Javascript代码也挺简单的,不过因为本身对Canvas画图像不熟悉,中间纠结了很长一节课的时间。

下面是代码,供大家分享,欢迎提出意见或建议。

/// 全局变量// 模板smooth1 = new Array(1, 1, 1, 1, 0, 1, 1, 1, 1); // 平滑模板 参数1/8smooth2 = new Array(1, 2, 1, 2, 4, 2, 1, 2, 1); // 平滑模板 参数1/16smooth3 = new Array(1, 1, 1, 1, 2, 1, 1, 1, 1); // 平滑模板 参数1/10laplacian0 = new Array(0, -1, 0, -1, 4, -1, 0, -1, 0); // 拉普拉斯算子laplacian1 = new Array(0, -1, 0, -1, 5, -1, 0, -1, 0); // 拉普拉斯算子laplacian2 = new Array(1, 4, 1, 4, -20, 4, 1, 4, 1); // 拉普拉斯算子Sobelx = new Array(1, 0, -1, 2, 0, -2, 1, 0, -1); // Sobel算子xSobely = new Array(-1, -2, -1, 0, 0, 0, 1, 2, 1); // Sobel算子yPrewittx = new Array(1, 0, -1, 1, 0, -1, 1, 0, -1); // Prewitt算子xPrewitty = new Array(-1, -1, -1, 0, 0, 0, 1, 1, 1); // Prewitt算子y/// 处理函数// 重置function chongzhi(image) {ctx.drawImage(img, 0, 0);}// 图像求补function qiubu(image) {var imageData = ctx.getImageData(0, 0, image.width, image.height);//ctx.clearRect(0, 0, image.width, image.height); // 清除画布原来的内容for(var i = 0; i < imageData.data.length; i += 4) {imageData.data[i] = 255 - imageData.data[i];imageData.data[i+1] = 255 - imageData.data[i+1];imageData.data[i+2] = 255 - imageData.data[i+2];imageData.data[i+3] = imageData.data[i+3];}ctx.putImageData(imageData, 0, 0);}// 线性运算function liangdu(image, du) {var imageData = ctx.getImageData(0, 0, image.width, image.height);//ctx.clearRect(0, 0, image.width, image.height); // 清除画布原来的内容for(var i = 0; i < imageData.data.length; i += 4) {imageData.data[i] = imageData.data[i]+du;imageData.data[i+1] = imageData.data[i+1]+du;imageData.data[i+2] = imageData.data[i+2]+du;imageData.data[i+3] = imageData.data[i+3];}ctx.putImageData(imageData, 0, 0);}// 二值化function erzhihua(image, yuzhi) {var imageData = ctx.getImageData(0, 0, image.width, image.height);//ctx.clearRect(0, 0, image.width, image.height); // 清除画布原来的内容for(var i = 0; i < imageData.data.length; i += 4) {imageData.data[i] = (imageData.data[i] < yuzhi) ? 0 : 255;imageData.data[i+1] = imageData.data[i];imageData.data[i+2] = imageData.data[i];imageData.data[i+3] = imageData.data[i+3];}ctx.putImageData(imageData, 0, 0);}// 灰度图function huidutu(image) {var imageData = ctx.getImageData(0, 0, image.width, image.height);//ctx.clearRect(0, 0, image.width, image.height); // 清除画布原来的内容for(var i = 0; i < imageData.data.length; i += 4) {var gray = (imageData.data[i] + imageData.data[i+1] + imageData.data[i+2]) / 3;imageData.data[i] = imageData.data[i+1] = imageData.data[i+2] = gray;imageData.data[i+3] = imageData.data[i+3];}ctx.putImageData(imageData, 0, 0);}/** * 卷积运算 * @param matrix 矩阵 * @param template 模板 * @return result 返回矩阵和模板卷积运算的结果 */function Convolution(matrix, template) {var result = 0;for (var i = 0; i < matrix.length; ++i) {result += matrix[i] * template[i];}return result;}/** * 平滑滤波器 * @param image Image对象 * @param template 模板 * @param times 模板参数 */function smoothFilter(image, template, times) {var imageData = ctx.getImageData(0, 0, image.width, image.height);var tempData = ctx.getImageData(0, 0, image.width, image.height);var srcw = image.width*4;var srch = image.height;var data = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0);for(var i = 0; i < srch; ++i) {for (var j = 0; j < srcw; j += 4) {// 左上、右上、左下、右下四个角不处理if((i == 0 && j == 0) || (i == 0 && j == srcw - 4) || (i == srch - 1 && j == 0) || (i == srch - 1 && j == srcw - 4)) {continue;}// 左边界if (j == 0) {data[0] = imageData.data[(i - 1) * srcw + j];data[3] = imageData.data[i * srcw + j];data[6] = imageData.data[(i + 1) * srcw + j];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}// 右边界else if(j == srcw - 4) {data[2] = imageData.data[(i - 1) * srcw + j];data[5] = imageData.data[i * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j];data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[6] = imageData.data[i * srcw + j - 4];data[7] = imageData.data[i * srcw + j];}// 上边界else if(i == 0) {data[0] = imageData.data[i * srcw + j - 4];data[1] = imageData.data[i * srcw + j];data[2] = imageData.data[i * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}// 下边界else if(i == srch - 1) {data[6] = imageData.data[i * srcw + j - 4];data[7] = imageData.data[i * srcw + j];data[8] = imageData.data[i * srcw + j + 4];data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];}// 其它点else {data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}var gray = Convolution(data, template) / times;tempData.data[i * srcw + j] = tempData.data[i * srcw + j+1] = tempData.data[i * srcw + j+2] = gray;tempData.data[i * srcw + j+3] = imageData.data[i * srcw + j+3];}}ctx.putImageData(tempData, 0, 0);}/** * 中值滤波器-十字窗口 * @param image Image对象 */function CrossWindow(image) {if(image == null) {return false;}var imageData = ctx.getImageData(0, 0, image.width, image.height);var tempData = ctx.getImageData(0, 0, image.width, image.height);var srcw = image.width*4;var srch = image.height;var data = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0);for(var i = 0; i < srch; ++i) {for (var j = 0; j < srcw; j += 4) {// 边界不处理if(i == 0 || i == 1 || i == srch - 2 || i == srch - 1 || j == 0 || j == 1 || j == srcw - 8 || j == srcw - 4) {continue;}else {data[0] = imageData.data[(i - 2) * srcw + j];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[i * srcw + j - 8];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[i * srcw + j + 8];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 2) * srcw + j];}data.sort(sortNumber);var gray = data[4];tempData.data[i * srcw + j] = tempData.data[i * srcw + j+1] = tempData.data[i * srcw + j+2] = gray;tempData.data[i * srcw + j+3] = imageData.data[i * srcw + j+3];}}ctx.putImageData(tempData, 0, 0);return true;}/** * 中值滤波器-方形窗口 * @param image Image对象 */function SquareWindow(image) {if(image == null) {return false;}var imageData = ctx.getImageData(0, 0, image.width, image.height);var tempData = ctx.getImageData(0, 0, image.width, image.height);var srcw = image.width*4;var srch = image.height;var data = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0);for(var i = 0; i < srch; ++i) {for (var j = 0; j < srcw; j += 4) {// 边界不处理if(i == 0 || i == 1 || i == srch - 2 || i == srch - 1 || j == 0 || j == 1 || j == srcw - 8 || j == srcw - 4) {continue;}else {data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}data.sort(sortNumber);var gray = data[4];tempData.data[i * srcw + j] = tempData.data[i * srcw + j+1] = tempData.data[i * srcw + j+2] = gray;tempData.data[i * srcw + j+3] = imageData.data[i * srcw + j+3];}}ctx.putImageData(tempData, 0, 0);return true;}/** * 高通滤波器 * @param image Image对象 * @param template 模板 */function highPassFilter(image, template) {var imageData = ctx.getImageData(0, 0, image.width, image.height);var tempData = ctx.getImageData(0, 0, image.width, image.height);var srcw = image.width*4;var srch = image.height;var data = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0);for(var i = 0; i < srch; ++i) {for (var j = 0; j < srcw; j += 4) {// 左上、右上、左下、右下四个角不处理if((i == 0 && j == 0) || (i == 0 && j == srcw - 4) || (i == srch - 1 && j == 0) || (i == srch - 1 && j == srcw - 4)) {continue;}// 左边界if (j == 0) {data[0] = imageData.data[(i - 1) * srcw + j];data[3] = imageData.data[i * srcw + j];data[6] = imageData.data[(i + 1) * srcw + j];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}// 右边界else if(j == srcw - 4) {data[2] = imageData.data[(i - 1) * srcw + j];data[5] = imageData.data[i * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j];data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[6] = imageData.data[i * srcw + j - 4];data[7] = imageData.data[i * srcw + j];}// 上边界else if(i == 0) {data[0] = imageData.data[i * srcw + j - 4];data[1] = imageData.data[i * srcw + j];data[2] = imageData.data[i * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}// 下边界else if(i == srch - 1) {data[6] = imageData.data[i * srcw + j - 4];data[7] = imageData.data[i * srcw + j];data[8] = imageData.data[i * srcw + j + 4];data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];}// 其它点else {data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}var gray = Convolution(data, template);tempData.data[i * srcw + j] = tempData.data[i * srcw + j+1] = tempData.data[i * srcw + j+2] = gray;tempData.data[i * srcw + j+3] = imageData.data[i * srcw + j+3];}}ctx.putImageData(tempData, 0, 0);}/** *  */function sharpenFilter(image, templateX, templateY) {var imageData = ctx.getImageData(0, 0, image.width, image.height);var tempData = ctx.getImageData(0, 0, image.width, image.height);var srcw = image.width*4;var srch = image.height;var data = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0);for(var i = 0; i < srch; ++i) {for (var j = 0; j < srcw; j += 4) {// 左上、右上、左下、右下四个角不处理if((i == 0 && j == 0) || (i == 0 && j == srcw - 4) || (i == srch - 1 && j == 0) || (i == srch - 1 && j == srcw - 4)) {continue;}// 左边界if (j == 0) {data[0] = imageData.data[(i - 1) * srcw + j];data[3] = imageData.data[i * srcw + j];data[6] = imageData.data[(i + 1) * srcw + j];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}// 右边界else if(j == srcw - 4) {data[2] = imageData.data[(i - 1) * srcw + j];data[5] = imageData.data[i * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j];data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[6] = imageData.data[i * srcw + j - 4];data[7] = imageData.data[i * srcw + j];}// 上边界else if(i == 0) {data[0] = imageData.data[i * srcw + j - 4];data[1] = imageData.data[i * srcw + j];data[2] = imageData.data[i * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}// 下边界else if(i == srch - 1) {data[6] = imageData.data[i * srcw + j - 4];data[7] = imageData.data[i * srcw + j];data[8] = imageData.data[i * srcw + j + 4];data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];}// 其它点else {data[0] = imageData.data[(i - 1) * srcw + j - 4];data[1] = imageData.data[(i - 1) * srcw + j];data[2] = imageData.data[(i - 1) * srcw + j + 4];data[3] = imageData.data[i * srcw + j - 4];data[4] = imageData.data[i * srcw + j];data[5] = imageData.data[i * srcw + j + 4];data[6] = imageData.data[(i + 1) * srcw + j - 4];data[7] = imageData.data[(i + 1) * srcw + j];data[8] = imageData.data[(i + 1) * srcw + j + 4];}var tempx = Convolution(data, templateX);var tempy = Convolution(data, templateY);var gray = Math.sqrt(Math.pow(tempx, 2) + Math.pow(tempy, 2));tempData.data[i * srcw + j] = tempData.data[i * srcw + j+1] = tempData.data[i * srcw + j+2] = gray;tempData.data[i * srcw + j+3] = imageData.data[i * srcw + j+3];}}ctx.putImageData(tempData, 0, 0);}/// 辅助函数/** * 升序函数,用于数组排序 */function sortNumber(a, b) {return a - b;}/** * 降序函数,用于数组排序 */function sortNumber(a, b) {return b - a;}

具体调用过程:

<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>HTML5数字图像处理</title><script src="image.js"></script><script type="text/javascript"></script></head><body><canvas id="myCanvas" width="200" height="200" style="border:1px solid #c3c3c3;">Your browser does not support the canvas element.</canvas><button onclick="return qiubu(img)">求补</button><button onclick="return liangdu(img,10)">亮度加</button><button onclick="return liangdu(img,-10)">亮度减</button><button onclick="return erzhihua(img,127)">二值化</button><button onclick="return huidutu(img,127)">灰度图</button><button onclick="return chongzhi(img)">重置</button><button onclick="return smoothFilter(img, smooth1, 8)">平滑1</button><button onclick="return smoothFilter(img, smooth2, 16)">平滑2</button><button onclick="return CrossWindow(img)">十字窗口</button><button onclick="return SquareWindow(img)">方形窗口</button><button onclick="return highPassFilter(img, laplacian1)">拉普拉斯锐化</button><button onclick="return sharpenFilter(img, Sobelx, Sobely)">Sobel算子锐化</button><button onclick="return sharpenFilter(img, Prewittx, Prewitty)">Prewitt算子锐化</button><script type="text/javascript">var cvs=document.getElementById("myCanvas");var ctx=cvs.getContext("2d");var img=new Image();img.src = "head.jpg";img.onload = function() {cvs.width = img.width;cvs.height = img.height;ctx.drawImage(img, 0, 0);};</script></body></html>

有一个值得注意的问题:本人用Chrome测试,在测试的时候,似乎必须要求在服务器运行,否则图片显示不出来,具体原因没有细究,也没有用其它浏览器测试。

原创粉丝点击