Rust Wasm 图片转 ASCII 艺术

在这里插入图片描述

有一些隐藏在代码中的 ASCII 有意思的图片,如:

/*__ooOoo_o8888888o88" . "88(| -_- |)O\  =  /O____/`---'\____.'  \\|     |//  `./  \\|||  :  |||//  \/  _||||| -:- |||||_  \|   | \\\  -  /'| |   || \_|  `\`---'//  |_/ |\  .-\__ `-. -'__/-.  /___`. .'  /--.--\  `. .'___."" '<  `.___\_<|>_/___.' _> \"".| | :  `- \`. ;`. _/; .'/ /  .' ; |    \  \ `-.   \_\_`. _.'_/_/  -' _.' /================-.`___`-.__\ \___  /__.-'_.'_.-'================`=--=-'                  佛祖保佑    永无BUG    永不宕机
*/

可以把一些有意思的图片转成 ASCII 艺术图,嵌到代码中,或者 log 中。

整体原理比较简单,这里用 Rust Wasm 实现一下。

1. 原理

先简单说一下原理。

  1. RGB 图片转成灰度图片。
  2. 准备一些不同密度的 ASCII 字符。
  3. 遍历灰度图片像素,根据亮度值 替换相应的 ASCII 字符。

这里主要说一下灰度的处理过程。

1.1 灰度处理

灰度和彩色图片的区别就是 R=G=B

关于灰度值的计算,有 3 种主流方式:

  • 最大值法:Max(R, G, B)
  • 平均值法:(R + G + B) / 3
  • 加权平均值法:
    • 0.2126 * R + 0.7152 * G + 0.0722 * B
    • 0.299 * R + 0.587 * G + 0.114 * B
    • Math.sqrt( (0.299 * R) ** 2 + (0.587 * G) ** 2 + (0.114 * B) ** 2 )

效果如下图所示 (演示地址):

在这里插入图片描述

这里在 Rust 中用的是加权平均值的第一种方式:

pub fn get_luminance(r: u8, g: u8, b: u8) -> f32 {let r = 0.2126 * (r as f32);let g = 0.7152 * (g as f32);let b = 0.0722 * (b as f32);r + g + b
}

2. Rust Image 的一些处理

这里罗列一下一些注意点。

2.1 JS 到 Rust 的 File 传递

这里需要转成 Uint8Array 进行传递:

const file = e.target.files[0];
const reader = new FileReader();reader.onloadend = (evt) => {try {const u8buffer = new Uint8Array(evt.target.result);const result = get_rust_image(u8buffer);} catch (error) {console.log({ error });}
};
file && reader.readAsArrayBuffer(file);

对应的 Rust 按照 Vec<u8> 处理 :

#[wasm_bindgen]
pub fn get_rust_image(raw: Vec<u8>) { ... }

2.2 Rust 到 JS Vec<u8> 传递

Rust 部分只要传递 Vec<u8> 即可:

#[wasm_bindgen]
pub fn get_rust_image(raw: Vec<u8>)  -> Vec<u8> { ... }

JS 消费时,按照 Uint8Array 处理即可:

// to Blob
const blob = new Blob([u8buffer.buffer]);
// to File
const file = new File([blob], 'image.unknown');
// to URL
const url = URL.createObjectURL(blob);

2.3 Rust Image Crate 输出图片数据

Image Crate 将图片加载完后,默认输出的 bytes 是一个解码后的原始数据,传递给 JS 后是无法正常使用的,需要对原始数据进行编码后,输出才行。

image

// 给编码器一块内存空间,用来写入数据
let mut output_buffer = vec![];
// 创建一个编码器
let mut encoder = JpegEncoder::new_with_quality(&mut output_buffer, 100);// 编码输出
encoder.encode(&img_raw, width, height, ColorType::L8).unwrap();// 直接把内存输出就行
output_buffer

3. 实现

这里做了两个版本。

3.1 简版实现

这个比较简单,就是去色,匹配,再连接即可:

#[wasm_bindgen]
pub fn get_ascii_by_image(raw: Vec<u8>, scale: u32, reverse: bool) -> String {let img = load_from_memory(&raw).unwrap();let img = img.resize((img.width() * scale / 100) as u32,(img.height() * scale / 100) as u32,FilterType::Nearest,).grayscale();let mut pallete = [' ', '.', '\\', '*', '#', '$', '@'];let mut current_line = 0;let mut result = "".to_string();if reverse {pallete.reverse();}for (_, line, rgba) in img.pixels() {if current_line != line {result.push('\n');current_line = line;}let r = 0.2126 * (rgba.0[0] as f32);let g = 0.7152 * (rgba.0[0] as f32);let b = 0.0722 * (rgba.0[0] as f32);let gray = r + g + b;let caracter = ((gray / 255.0) * (pallete.len() - 1) as f32).round() as usize;result.push(pallete[caracter]);// 填充一下,有些扁if caracter < (pallete.len() - 2) {result.push('.');} else {result.push(' ');}}result
}

演示地址

执行时间在 20ms 左右。

image

3.2 Tai 版

看到一个支持 ASCII 种类挺多的 Rust 项目 https://github.com/MustafaSalih1993/tai ,于是将这个项目的 IO 部分进行了修改,适配 WASM 进行了编译处理。

演示地址

这个耗时在 50ms 左右。

image

4. 安装&使用

<script type="module">import initWasm, {get_gray_image,get_ascii_by_image,get_ascii_by_image_tai,} from "./pkg/rust_wasm_image_ascii.js";initWasm().then(() => {});
</script>

可以直接使用仓库中 pkg/ 目录中的文件,也可以使用 upkg 的资源 https://unpkg.com/browse/rust-wasm-image-ascii/ ,也可以 npm install rust-wasm-image-ascii 使用。

接口描述参考这里:pkg/rust_wasm_image_ascii.d.ts

🌟 Github 代码地址:https://github.com/lecepin/rust-wasm-image-ascii

🌟 Github 原文地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401588.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我的新书《PWA入门与实践》上市了

前言 我初接触PWA是在2017年年初&#xff0c;当时参加了一个前端分享会&#xff0c;其中一个主题就是与PWA相关的&#xff0c;介绍了PWA的Service Worker和安装到桌面的能力&#xff0c;以及这门技术未来的发展趋势&#xff0c;听完这个分享后&#xff0c;我就为PWA的一些能力…

Linux网络编程组播测试代码

Linux网络编程组播测试代码 (转载)组播客户端代码如下&#xff1a; #include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define BUFLEN 255int main(int arg…

ubuntu下搭建tftp服务器

1.1 搭建tftp服务器TFTP&#xff08;Trivial File Transfer Protocol&#xff0c;简单文件传输协议&#xff09;&#xff0c;是一个基于UDP协议实现的用于在客户机和服务器之间进行简单文件传输的协议&#xff0c;适合于开销不大、不复杂的应用场合。TFTP协议专门为小文件传输而…

ubuntu 10.10 修改下载源的方法

打开Ubuntu的终端,输入sudo gedit /etc/apt/sources.list在最后加上如下源&#xff1a;deb http://mirror.lupaworld.com/ubuntu maverick main restricted universe multiversedeb http://mirror.lupaworld.com/ubuntu maverick-security main restricted universe multiverse…

Linux ARM交叉编译工具链制作过程

一、下载源文件 源代码文件及其版本与下载地址&#xff1a; Binutils-2.19.tar.bz2 http://ftp.gnu.org/gnu/binutils/ gcc-4.4.4.tar.bz2 http://mirrors.kernel.org/gnu/gcc/gcc-4.4.4/ Glibc-2.11.2.tar.bz2 Glibc-ports-2.11.tar.bz2 http://ftp.gnu.org/gnu/glibc/ Gmp-4.…

ARM开发板上iconv_open(utf-8, gb2312) 调用失败的解决方法(转)

ARM开发板上iconv_open("utf-8", "gb2312") 调用失败的解决方法 应用程序代码如下&#xff1a;static int code_convert(char* from_charset, char* to_charset, char* inbuf, size_t inlen, char* outbuf, size_t outlen){ iconv_t cd; char…

linux c语言获取时间

在程序中&#xff0c;经常需要输出系统的当前时间、计算程序的执行时间、使用计时器等。 一、时间的类型 1.格林威治标准时间 coordinated universal time(UTC)是世界标准时间&#xff0c;即常说的格林威治标准时间(greenwich mean time,GMT). 2.日历时间 日历时间(calendar ti…

source insight快捷键及使用技巧

source insight快捷键及使用技巧 退出程序 : AltF4 重画屏幕 : CtrlAltSpace 完成语法 : CtrlE 复制一行 : CtrlK 恰好复制该位置右边的该行的字符 :…

Crosstool-ng制作交叉编译工具链

Crosstool-ng制作交叉编译工具链 交叉编译器可以用现成的&#xff0c;比如CodeSourcery制作的交叉编译器&#xff0c;也可以自己制作&#xff0c;一般是用kernelgccglibcbinutils的源码包来编译安装&#xff0c;后来由于这个太麻烦于是就有类似crosstool这样的自动化配置工具。…

数学建模之聚类模型详解

聚类模型 引言 “物以类聚&#xff0c;人以群分”&#xff0c;所谓的聚类&#xff0c;就是将样本划分为由类似的对象组成的多个类的过程。聚类后&#xff0c;我们可以更加准确的在每个类中单独使用统计模型进行估计、分析或预测&#xff1b;也可以探究不同类之间的相关性和主…

嵌入式Linux启动过程中的问题积累

嵌入式Linux启动过程中的问题积累 Dongas 07-12-19 1.Bad Magic Number ## Booting image at 33000000 ... Bad Magic Number OMAP5912 OSK # &#xff08;tftp下载好kernel的uImage后就停止在这&#xff0c;不能启动kernel&#xff09; 问题原因&#xff1a;启动参数设置错误,…

使用BusyBox制作嵌入式Linux根文件系统

STEP 1&#xff1a;构建目录结构 创建根文件系统目录&#xff0c;主要包括以下目录/dev /etc /lib /usr /var /proc /tmp /home /root /mnt /bin /sbin /sys #mkdir /home/rootfs#cd /home/rootfs #mkdir dev etc lib usr var proc tmp home root …

Linux串口c_cc[VTIME]和c_cc[VMIN]属性设置的作用

Linux串口c_cc[VTIME]和c_cc[VMIN]属性设置的作用 在串口编程模式下&#xff0c;open未设置O_NONBLOCK或O_NDELAY的情况下。 c_cc[VTIME]和c_cc[VMIN]映像read函数的返回。 VTIME定义等待的时间&#xff0c;单位是百毫秒(通常是一个8位的unsigned char变量&#xff0c;取值不能…

Linux_2.6字符设备驱动实例

第一步&#xff1a;my74hc595.c #include <linux/module.h> //模块所需的大量符号和函数定义#include <linux/init.h> //指定初始化和清除函数#include <linux/fs.h> //文件系统相关的函数和头文件#include <linux/cdev.h> //cdev结构的头文件#include…

移植mysql到嵌入式ARM平台

移植MySQL到嵌入式ARM平台 MySQL没有专门针对ARM的版本&#xff0c;移植到ARM没有官方文档可参考&#xff0c;因此&#xff0c;暂时参考这样一篇文档&#xff1a; http://blog.chinaunix.net/space.php?uid9701860&doblog&id285428&#xff0c;因为MySQL5.5之后&…

'./mysql-bin.index' not found (Errcode: 13) 的解决方法

将文件系统复制到PC机上&#xff0c;然后再拷贝到别的SD卡后&#xff0c;发现mysql无法启动了&#xff0c;首先检查了一下mysql的错误日志&#xff0c;发现最后出现以下错误&#xff1a;/usr/local/mysql/libexec/mysqld: File ./mysql-bin.index not found (Errcode: 13)提示.…

Unity Hub安装Android Build Support报错:Failed to delete old Unity Android Supprtinstallation files

安装失败&#xff1a; 解决方法&#xff1a; 需要先安装 Android Build Support &#xff0c;再安装 Android SDK & NDK Tools 和 OpenJDK 其他情况&#xff1a; 如果已经安装了Android SDK & NDK Tools 和 OpenJDK&#xff0c;只剩Android Build Support 未安装成功…

红米5 Plus刷开发版/小米手机刷开发版

到官网选择对应手机型号下载Rom包&#xff1a;MIUI下载 下载完成后手机打开&#xff1a;设置→我的设备→MIUI版本→右上角选择【手动选择安装包】&#xff0c;找到下载的包等待手机重启就行了。 PS&#xff1a;如果是像下面这样 小米手机找不到手动选择安装包 选项的话&#…

小米手机开启Root权限

1.开启权限前手机先要刷成开发版&#xff0c;可以看 → 小米手机刷开发版 2.设备解锁&#xff0c;先在 设置→我的设备→全部参数→开启开发者模式→多击MIUI版本 开启开发者模式&#xff0c;然后进入 设置→更多设置→开发者选项→设备解锁状态 &#xff0c;根据操作提示解锁…

小米手机Linux Deploy安装Ubuntu

1.Linux Deploy 2.4版本之后无需再安装Busy Box&#xff0c;所以BusyBox安装报错失败的不需要再安装了。直接GooglePlay下载inux Deploy最新版进行安装即可&#xff0c;或 点击此处下载。 2.下载安装Linux Deploy之后进行Ubuntu的安装。 我的配置如下&#xff1a; 引导设置&a…