热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

详解使用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 

显示

最终获得的 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 

单极图完整代码

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


推荐阅读
  • 本文介绍了用户界面(User Interface, UI)的基本概念,以及在iOS应用程序中UIView及其子类的重要性和使用方式。文章详细探讨了UIView如何作为用户交互的核心组件,以及它与其他UI控件和业务逻辑的关系。 ... [详细]
  • 本文探讨了线性表中元素的删除方法,包括顺序表和链表的不同实现策略,以及这些策略在实际应用中的性能分析。 ... [详细]
  • 本文提供了一个关于AC自动机(Aho-Corasick Algorithm)的详细解析与实现方法,特别针对P3796题目进行了深入探讨。文章不仅涵盖了AC自动机的基本概念,还重点讲解了如何通过构建失败指针(fail pointer)来提高字符串匹配效率。 ... [详细]
  • 本文提供了一个详尽的前端开发资源列表,涵盖了从基础入门到高级应用的各个方面,包括HTML5、CSS3、JavaScript框架及库、移动开发、API接口、工具与插件等。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 本文探讨了在AspNetForums平台中实施基于角色的权限控制系统的方法,旨在为不同级别的用户提供合适的访问权限,确保系统的安全性和可用性。 ... [详细]
  • 如何高效渲染JSON数据
    本文介绍了在控制器中返回JSON结果的方法,并详细说明了如何利用jQuery处理和展示这些数据,为Web开发提供了实用的技巧。 ... [详细]
  • 笔记说明重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系& ... [详细]
  • Awk是一款功能强大的文本分析与处理工具,尤其在数据解析和报告生成方面表现突出。它通过读取由换行符分隔的记录,并按照指定的字段分隔符来划分和处理这些记录,从而实现复杂的数据操作。 ... [详细]
  • 深入解析Unity3D游戏开发中的音频播放技术
    在游戏开发中,音频播放是提升玩家沉浸感的关键因素之一。本文将探讨如何在Unity3D中高效地管理和播放不同类型的游戏音频,包括背景音乐和效果音效,并介绍实现这些功能的具体步骤。 ... [详细]
  • 本文探讨了一种常见的C++面试题目——实现自己的String类。通过此过程,不仅能够检验开发者对C++基础知识的掌握程度,还能加深对其高级特性的理解。文章详细介绍了如何实现基本的功能,如构造函数、析构函数、拷贝构造函数及赋值运算符重载等。 ... [详细]
  • 探讨了在HTML表单中使用元素代替进行表单提交的方法。 ... [详细]
  • 3DSMAX制作超现实的体育馆模型
    这篇教程是向脚本之家的朋友介绍3DSMAX制作超现实的体育馆模型方法,教程制作出来的体育馆模型非常地不错,不过教程有点难度,需要有一定基础的朋友学习,推荐到脚本之家,喜欢的朋友可 ... [详细]
  • 本文介绍了如何在AngularJS应用中使用ng-repeat指令创建可单独点击选中的列表项,并详细描述了实现这一功能的具体步骤和代码示例。 ... [详细]
  • 在项目冲刺的最后一天,团队专注于软件用户界面的细节优化,包括调整控件布局和字体设置,以确保界面的简洁性和用户友好性。 ... [详细]
author-avatar
八戒走你
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有