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

/*                                 _                              _ooOoo_                             o8888888o                             88" . "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 Blobconst blob = new Blob([u8buffer.buffer]);// to Fileconst file = new File([blob], 'image.unknown');// to URLconst url = URL.createObjectURL(blob);

2.3 Rust Image Crate 输入图片数据

Image Crate 将图片加载完后,默认输入的 bytes 是一个解码后的原始数据,传递给 JS 后是无奈失常应用的,须要对原始数据进行编码后,输入才行。

// 给编码器一块内存空间,用来写入数据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 左右。

3.2 Tai 版

看到一个反对 ASCII 品种挺多的 Rust 我的项目 https://github.com/MustafaSal... ,于是将这个我的项目的 IO 局部进行了批改,适配 WASM 进行了编译解决。

演示地址

这个耗时在 50ms 左右。

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... ,也能够 npm install rust-wasm-image-ascii 应用。

接口形容参考这里:pkg/rust_wasm_image_ascii.d.ts

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

Github 原文地址