Rust Wasm 图片转 ASCII 艺术

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

有一些隐藏在代码中的 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 )

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

image.png

这里在 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.png

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

3.2 Tai 版

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

演示地址

这个耗时在 50ms 左右。

image.png

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 原文地址
目录
相关文章
|
存储 Rust 前端开发
给 Web 前端工程师看的用 Rust 开发 wasm 组件实战
wasm 全称 WebAssembly,是通过虚拟机的方式,可以在服务端、客户端如浏览器等环境执行的二进制程序。它有速度快、效率高、可移植的特点
210 0
|
Rust JavaScript 前端开发
【Rust 实战】Rust 与 Wasm
【Rust 实战】Rust 与 Wasm
2284 0
【Rust 实战】Rust 与 Wasm
|
6月前
|
数据采集 存储 Rust
Rust高级爬虫:如何利用Rust抓取精美图片
Rust高级爬虫:如何利用Rust抓取精美图片
|
Rust JavaScript 前端开发
【Rust 实战】Rust 与 Wasm (2) —— 操作 Dom
【Rust 实战】Rust 与 Wasm (2) —— 操作 Dom
【Rust 实战】Rust 与 Wasm (2) —— 操作 Dom
|
Rust 定位技术 数据安全/隐私保护
使用 Rust Wasm 开发小米 12S Utra 莱卡水印生成工具
前言 最近看到小米 12S Utra 的发布,看了下详情页面,发现演示的照片都好看的,包含了品牌、设备、镜头、位置等信息,如下图所示:
1938 0
使用 Rust Wasm 开发小米 12S Utra 莱卡水印生成工具
|
缓存 JavaScript 算法
[译] 或许你并不需要 Rust 和 WASM 来提升 JS 的执行效率 — 第二部分
以下内容为本系列文章的第二部分,如果你还没看第一部分,请移步或许你并不需要 Rust 和 WASM 来提升 JS 的执行效率 — 第一部分。 我尝试过三种不同的方法对 Base64 VLQ 段进行解码。
1610 0
|
7天前
|
Rust 安全 区块链
探索Rust语言:系统编程的新选择
【10月更文挑战第27天】Rust语言以其安全性、性能和并发性在系统编程领域受到广泛关注。本文介绍了Rust的核心特性,如内存安全、高性能和强大的并发模型,以及开发技巧和实用工具,展示了Rust如何改变系统编程的面貌,并展望了其在WebAssembly、区块链和嵌入式系统等领域的未来应用。
|
8天前
|
Rust 安全 Java
编程语言新宠:Rust语言的特性、优势与实战入门
【10月更文挑战第27天】Rust语言以其独特的特性和优势在编程领域迅速崛起。本文介绍Rust的核心特性,如所有权系统和强大的并发处理能力,以及其性能和安全性优势。通过实战示例,如“Hello, World!”和线程编程,帮助读者快速入门Rust。
21 1
|
9天前
|
Rust 安全 编译器
编程语言新宠:Rust语言的特性、优势与实战入门
【10月更文挑战第26天】Rust语言诞生于2006年,由Mozilla公司的Graydon Hoare发起。作为一门系统编程语言,Rust专注于安全和高性能。通过所有权系统和生命周期管理,Rust在编译期就能消除内存泄漏等问题,适用于操作系统、嵌入式系统等高可靠性场景。
21 2
|
10天前
|
Rust 安全 云计算
Rust语言入门:安全性与并发性的完美结合
【10月更文挑战第25天】Rust 是一种系统级编程语言,以其独特的安全性和并发性保障而著称。它提供了与 C 和 C++ 相当的性能,同时确保内存安全,避免了常见的安全问题。Rust 的所有权系统通过编译时检查保证内存安全,其零成本抽象设计使得抽象不会带来额外的性能开销。Rust 还提供了强大的并发编程工具,如线程、消息传递和原子操作,确保了数据竞争的编译时检测。这些特性使 Rust 成为编写高效、安全并发代码的理想选择。
11 0