当前位置:首页 > 日记 > 正文

详解使用JS如何制作简单的ASCII图与单极图

详解使用JS如何制作简单的ASCII图与单极图

ASCII图

在终端执行各种命令的时候经常会看到一些终端里显示出来的"图片",远看仿佛一张图,近看则是一个个的 ASCII码,它们 大致长这样子

而今天我们要做的则是用JS把一张给定的图片转换成这种用ASCII字符组成的“ASCII图” 先看看最终效果,假设我们给定的图片是这样子的,

这是代码处理后的结果,用了 I'mYasic 这8个字符来表示,还是可以分辨出大致的轮廓的。

单级图

而另一种图则是单极图,也就是黑白图片,还是刚刚那张图片,输出如下

基础知识

这两种图都是比较简单的,只需要以下知识即可

  • HTML5中的Canvas
  • 像素的RGB值
  • JS中的Canvas相关API

制作ASCII图

一般来说,在计算机当中,我们看到的大多数图片都是由一个个像素点构成的,每一个像素点则由 RGBA 构成,在 css 中我们时常用的 rgba(255, 255, 255, 255, 0)就是一组RGBA值, 也即是RGB三原色和Alpha透明度。当然一张图片不是仅仅包含所有像素点数据的,还包括一些描述信息,也称为图片的 profile,这一部分小则几KB,多则几百KB,是图片压缩中经常被处理的部分。

那么对于图片中每一个像素点来说,只要我们改变了其相应的RGBA的值,最终的图片也就变了样子。而修改哪些像素点、修改成什么样的RGBA,则决定着最终的图片风格,这也是许多滤镜采用的机制。

基于以上的理论知识,我们的ASCII图制作思路也就有了。ASCII图其实就是将一张图中的一个像素点,通过计算其RGBA的值,划分成给定的几个量化值,在这里由于我们用的 I'mYasic 这8个字符来表示,所以要分成8组值,每一组用一个ASCII字符来表示,最终就能组成一幅完整的ASCII图片。

接下来就是具体的代码实现。

获取图片的像素信息

通过 Canvas API 中的 getImageData() 方法我们可以获得一个对象,这个对象的属性里包含一个一维数组 data,这个一维数组每4个元素为一组,代表了一个 canvas 中指定范围的全部像素信息,并且依次是 RED,GREEN,BLUE,ALPHA。因此我们可以先把图片放进 canvas 中,再调用这个方法拿到像素。

不过我很疑惑为什么 data 是一个一维数组,通常处理的图片都是二维图片,如果用二维数组来表示像素信息,代码读取和处理会方便很多,也更容易理解。甚至可以用一个三维数组,专门用一个维度来放置RGBA信息。

获取图片像素信息的代码如下所示

var canvasContext = canvas.getContext("2d");canvasContext.drawImage(sourceImg, 0, 0);var imgData = canvasContext.getImageData(0 , 0, sourceImg.width, sourceImg.height);var imgDataArray = imgData.data;

那么对于某一个像素点的RGBA值就可以这样获取

var r = imgDataArray[lineIndex];var g = imgDataArray[lineIndex + 1];var b = imgDataArray[lineIndex + 2];var a = imgDataArray[lineIndex + 3];

其中 lineIndex 是遍历每一个像素点的基准变量。

图片灰度化

灰度化,也就是获取像素点的灰度值。由于每一个像素点包含着RGBA四种信息,而我们需要将所有像素点的RGBA值分成8组,因此需要统一一下RGBA的值,最终得到一个值Y,而相应的像素点的RGBA值满足 Y = R = G = B ,在这里我们不考虑透明度 Alpha。由于RGB的值相等像素点颜色是介于白色与黑色之间的灰色,所以这一过程也称为灰度化。

灰度化算法有很多种,我们在这里采取最简单的方式,即

Y = (R + G + B) * 1/3

相应代码如下

function rgb2gray(r, g, b) { return r * 0.333 + g * 0.333 + b * 0.333;}

灰度图量化

灰度化以后的图片大致长这样子,可以看到色彩已经都变成灰色了。

那么接下来就是关键的“量化”过程。也就是说,我们要让把这些不同灰度的值分成8组,并且每一组都赋予一个ASCII字符作为标示,当然选取的ASCII字符也要有一定规律,简单来说就是颜色由深到浅相应的字符由繁到简。而量化过程就是将0-255范围等分成8个区间,依次判断灰度值在哪一个区间内,代码如下。

由于图像像素数目巨大,为了效率,判决时可以采取“二分判决”法提高判决速度。

function gray2asc(gray) { /*ASCII--I'mYasic*/ /*32 64 96 128 160 192 224 256*/ gray = 255 - gray; if (gray < 128){  if (gray < 64){   if (gray < 32){    return '\''   }   else {    return 'c'   }  }  else {   if (gray < 96){    return 'i'   }   else {    return 's'   }  } } else {  if (gray < 192){   if (gray < 160){    return 'I'   }   else {    return 'm'   }  }  else {   if (gray < 224){    return 'a'   }   else {    return 'Y'   }  } }}

遍历与显示

上面大概讲解完了对于一个像素点变换为ASCII码的过程,接下来就是遍历和显示了。

遍历

遍历全部像素点并变换为ASCII码基本是不可能的,因为图片稍微大一些计算量就增长很多,所以我们折中一下,对于像素阵列的行与列都进行等间隔采样,最终展示出来的图片分辨率会随着采样间隔减小而增强。另外要注意 data 数组是一维数组,并且每4个元素为一组RGBA数据。相应代码如下

 var result = ""; var lineIndex = 0; for (var lineHeight = 0; lineHeight < sourceImg.height; lineHeight += 12){  var lineASC = "";  for (var lineFlag = 0; lineFlag < sourceImg.width; lineFlag += 5){   lineIndex = (lineHeight * sourceImg.width + lineFlag) * 4;   var r = imgDataArray[lineIndex];   var g = imgDataArray[lineIndex + 1];   var b = imgDataArray[lineIndex + 2];   lineASC += gray2asc(rgb2gray(r, g, b));  }  lineASC += '\n';  result += lineASC; }

显示

最终获得的 result 字符串就是需要展示的ASCII码。但是必须注意,如果直接展示到页面上会因为每一个字符的字符宽度不一样而导致ASCII图“失真”,这里我们可以采用 Monospace 字体来确保字符宽度一致。

制作单极图

其实看完上面部分,就应该知道单极图非常好实现,同样需要获取像素信息并灰度化,只是量化时直接量化为 rgb(0, 0, 0) 和 rgb(255, 255, 255) 两种颜色就可以。

 var canvasContext = targetCanvas.getContext("2d"); canvasContext.drawImage(sourceImg, 0, 0); var imgData = canvasContext.getImageData(0 , 0, sourceImg.width, sourceImg.height); var imgDataArray = imgData.data; for (var index = 0; index <= sourceImg.width * sourceImg.height * 4; index += 4){  var red = imgDataArray[index];  var green = imgDataArray[index + 1];  var blue = imgDataArray[index + 2];  var gray = rgb2gray(red, green, blue);  if (gray < 128){   imgData.data[index] = 0;   imgData.data[index + 1] = 0;   imgData.data[index + 2] = 0;  }  else {   imgData.data[index] = 255;   imgData.data[index + 1] = 255;   imgData.data[index + 2] = 255;  } } canvasContext.putImageData(imgData, 0, 0);

别忘了最后要用 putImageData 方法将修改后的像素信息放回 canvas 中进行显示。

ASCII图完整代码

function rgb2gray(r, g, b) { return r * 0.333 + g * 0.333 + b * 0.333;}function gray2asc(gray) { /*ASCII--I'mYasic*/ /*32 64 96 128 160 192 224 256*/ gray = 255 - gray; if (gray < 128){  if (gray < 64){   if (gray < 32){    return '\''   }   else {    return 'c'   }  }  else {   if (gray < 96){    return 'i'   }   else {    return 's'   }  } } else {  if (gray < 192){   if (gray < 160){    return 'I'   }   else {    return 'm'   }  }  else {   if (gray < 224){    return 'a'   }   else {    return 'Y'   }  } }}var img2ASC = function (canvas, sourceImg) { console.log(sourceImg.width + " " + sourceImg.height); var canvasContext = canvas.getContext("2d"); canvasContext.drawImage(sourceImg, 0, 0); var imgData = canvasContext.getImageData(0 , 0, sourceImg.width, sourceImg.height); var imgDataArray = imgData.data; var result = ""; var lineIndex = 0; for (var lineHeight = 0; lineHeight < sourceImg.height; lineHeight += 12){  var lineASC = "";  for (var lineFlag = 0; lineFlag < sourceImg.width; lineFlag += 5){   lineIndex = (lineHeight * sourceImg.width + lineFlag) * 4;   var r = imgDataArray[lineIndex];   var g = imgDataArray[lineIndex + 1];   var b = imgDataArray[lineIndex + 2];   lineASC += gray2asc(rgb2gray(r, g, b));  }  lineASC += '\n';  result += lineASC; } document.getElementById("result").innerHTML = result;};

单极图完整代码

function rgb2gray(r, g, b) { return r * 0.333 + g * 0.333 + b * 0.333;}function gray2asc(gray) { /*ASCII--I'mYasic*/ /*32 64 96 128 160 192 224 256*/ if (gray < 128){  if (gray < 64){   if (gray < 32){    return '\''   }   else {    return 'c'   }  }  else {   if (gray < 96){    return 'i'   }   else {    return 's'   }  } } else {  if (gray < 192){   if (gray < 160){    return 'I'   }   else {    return 'm'   }  }  else {   if (gray < 224){    return 'a'   }   else {    return 'Y'   }  } }}var monoImg = function (targetCanvas, sourceImg) { targetCanvas.width = sourceImg.width; targetCanvas.height = sourceImg.height; var canvasContext = targetCanvas.getContext("2d"); canvasContext.drawImage(sourceImg, 0, 0); var imgData = canvasContext.getImageData(0 , 0, sourceImg.width, sourceImg.height); var imgDataArray = imgData.data; for (var index = 0; index <= sourceImg.width * sourceImg.height * 4; index += 4){  var red = imgDataArray[index];  var green = imgDataArray[index + 1];  var blue = imgDataArray[index + 2];  var gray = rgb2gray(red, green, blue);  if (gray < 128){   imgData.data[index] = 0;   imgData.data[index + 1] = 0;   imgData.data[index + 2] = 0;  }  else {   imgData.data[index] = 255;   imgData.data[index + 1] = 255;   imgData.data[index + 2] = 255;  } } canvasContext.putImageData(imgData, 0, 0);};

总结

这一篇博客主要讲了利用JS中的 Canvas API 进行一些简单的像素化操作,但其实还有很多地方可以继续改进。比如一般单极图出来后很多地方会有噪点,也就是一些碍眼的白点和黑点,可以通过一些方式“去掉噪点”,就留在以后写吧!以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

相关文章

电脑如何升级为英雄联盟游戏

电脑如何升级为英雄联盟游戏

英雄联盟,升级为,游戏,电脑软件,问题: 我的旧电脑,9300ge e5200处理器,显卡,2GB内存,玩英雄联盟很困,只20fps。后来,它改变了G210显卡帧速度增加到30fps,游戏仍不顺畅。如何升级为英雄联盟 答案uff1a 根据计算机的硬件平台,升级处理器没有必要,因为…

mysql5.7安装和配置教程

mysql5.7安装和配置教程

安装,教程,配置,电脑软件,本文共享mysql安装配置教程,供您参考,详情如下 1。配置MySQL数据库 1、解压MySQL的绿色版,并改名为mysql5.7,如下 相比于图5.6以前的版本中,更少的数据目录(数据存储)和my-default.ini文件(配置信息) 两。安装服务 1。运…

Service Unavailable 是什么意思?Se

Service Unavailable 是什么意思?Se

解决方法,原因,电脑软件,Service,Unavailable,一、 如果出现“Service Unavailable”的提示,刷新几下又可以访问。   出现这种情况是由于 网站超过了iis限制造成的由于2003的操作系统在提示IIS过多时并非像2000系统提示“链接人数过多”,而…

laravel 5.1下php artisan migrate

laravel 5.1下php artisan migrate

注意事项,电脑软件,laravel,php,migrate,PHP artisan migrate命令是laravel十分重要的问题,但在使用时需要注意几个情况:(1)在migration文件夹下创建一张新的表,要使用php artisan make:migration语句php artisan已经拥有强大的功能,就不要自己新…

js 两个日期比较相差多少天的实例

js 两个日期比较相差多少天的实例

日期比较,实例,两个,电脑软件,js,如下所示:var day1 = new Date("2017-9-17");var day2 = new Date("2017-10-18");console.log((day2 - day1) / (1000 * 60 * 60 * 24)); 以上这篇js 两个日期比较相差多少天的实例就是小编分享给大家的…

Windows 2003终端服务器架设攻略

Windows 2003终端服务器架设攻略

服务器架设,终端,攻略,电脑软件,Windows,  Windows Server 2003终端服务器可用来管理每个客户远程登录的资源,它提供了一个基于远程桌面协议(RDP)的服务,使Windows Server 2003成为真正的多会话环境操作系统,并让用户能使用服务器上的各种合…

简单了解PHP编程中数组的指针的使

简单了解PHP编程中数组的指针的使

数组,指针,编程,简单,电脑软件,要使用数组中的元素,就需要数组的定位。定位的实现需要借助数组指针来完成。PHP中有很多函数可以移动数组指针。下面将给大家介绍几个。将指针移动到下一个数组位置next()next()函数返回紧接着放在当前数组指…

PS合成复仇者联盟2电影海报全过程

PS合成复仇者联盟2电影海报全过程

复仇者,电影海报,全过程,联盟,电脑软件,这篇教程教的PS图片合成学习者们使用PS合成《复仇者联盟2》电影海报,教程制作的海报角色是漫威漫画中注明的超级翻拍角色之一,漫画原作中是由汉克&middot;皮姆博士(蚁人)为了管理被诸位超级英雄逮捕超级…

利用ps玻璃和畸变滤波器制作边界效

利用ps玻璃和畸变滤波器制作边界效

边界,畸变,滤波器,效应,玻璃,今天,我们教你利用ps玻璃,扭曲和其他滤镜来制作边框效果。效果非常好。我希望它会对你有帮助。 利用ps玻璃和畸变滤波器制作边界效应 结果如下: 使用玻璃、扭曲和锐化滤波的PS,几个简单的步骤可以用来创建一个相…

photoshop滤镜画笔描边

photoshop滤镜画笔描边

滤镜,电脑软件,photoshop,  Brush Strokes(画笔描边) 滤镜主要模拟使用不同的画笔和油墨进行描边创造出的绘画效果。 (注:此类滤镜不能应用在CMYK和Lab模式下)photoshop滤镜画笔描边的图文教程1、Angled Strokes(成角的线条滤镜…

IIS 错误代码大汇总

IIS 错误代码大汇总

错误代码,大汇总,电脑软件,IIS, 400 无法解析此请求。 401.1 未经授权:访问由于凭据无效被拒绝。 401.2 未经授权: 访问由于服务器配置倾向使用替代身份验证方法而被拒绝。 401.3 未经授权:访问由于 ACL 对所请求资源的设置被拒…

PS制作红色和灰色潮汐图海报

PS制作红色和灰色潮汐图海报

红色,潮汐,海报,灰色,电脑软件,PS的各种功能给它一个自由组合。今天的PS教程是制作时尚艺术海报,我们需要用到大量的潮流装饰材料,如溅水、曲线、图案、圆形笔刷等,在制作之前,我们需要收集相关的材料,最好安装更多的字体,让我们来了解一下。 ps…