斗图不用怕!用 Serverless 随时创建你的表情包

415 阅读3分钟

meme economy(表情包经济)显然是下一件大事!这是互联网注意力经济的自然延伸,Elon Musk也沉迷于此。据估计,meme economy 已经价值 2.5亿美元了。

The meme economy is where FOMO meets YOLO. — Felix Salmon from AXIOS

现在谁的手机还没存几个表情包 meme 呢?

然而,找到合适的 meme 需要时间。程序员之间的调侃对常规创作者来说还是比较偏僻。那程序员自己做一个允许任何人自定义 meme 并生成新 meme 的 web 应用程序怎么样?这就是 Meme as a service 😎。

这样,妈妈再也不用担心我斗图输了!开干!

为什么要用 serverless

作为开发者,创建一个在图片上添加文本标题的 Web 应用程序可能并不困难。但是,“meme as a service”还有一些其他要求。

  • 图像处理任务通常需要大量计算,并且需要高性能。
  • meme as a service 要么使用的人数很少,要么可能会迅速普及。换句话说,meme as a service 需要具有可伸缩性,而开发者只需为实际使用付费。

上述问题的解决方案如下。首先,我们要使用高性能的现代编程语言来编写图像和文本操作函数。因此我们选择了 Rust,Rust 提供了本机性能,同时又具有内存安全性。

接下来,使用公共云中的 serverless 函数可以最好地解决可伸缩性要求。serverless 函数在没有人使用的时候是免费的,并且可以迅速扩展到数百万个用户。

虽然可以将从 Rust 编译的本机程序作为 serverless 函数运行,但更好的方法是在 WebAssembly 虚拟机中作为 serverless 函数运行 Rust 程序。 WebAssembly 虚拟机充当本机应用程序与 serverless 主机环境之间的兼容层和安全沙箱。由于 WebAssembly 虚拟机已经经过预先配置,可以在公有云的 serverless 运行时的各种老旧的操作系统和容器映像中运行,因此它使 Rust 程序更具可移植性。此外,WebAssembly 使 Rust 程序可以轻松安全地访问以 C / C ++编写的软件库。一个示例是从 Rust 访问旧版操作系统中的Tensorflow 库

快速开始

在这篇文章里,我会使用GitHub 上的模板函数 tencent-meme-scf 来在腾讯云上部署 Rust meme-as-a-service。这个模板基于开源的 Serverless Framework。Rust 程序编译成 Wasm 字节码并运行在 SSVM 上。SSVM 已经为基于云的运行环境进行了优化。

While we use Tencent Cloud in this example, the Serverless Framework is agnostic to all leading public clouds. You can easily deploy to AWS or Azure with minor changes.

首先,你需要安装 Serverless Framework ,并在腾讯云上创建一个账号。腾讯云会提供免费的额度给开发者使用。接下来是 fork 或 clone 我们提供的模板函数,然后转到 tencent-meme-scf。

$ git clone https://github.com/second-state/tencent-meme-scf
$ cd tencent-meme-scf

使用 Serverless Framework 部署云函数、用于函数服务的 API 网关、让用户使用函数服务的静态 HTML 页面。 .env 文件里配置了要将函数部署到腾讯云。 只需按照屏幕上的说明,登录腾讯云并部署。

$ sls deploy
... ...
    region: ap-hongkong
    website: https://sls-website-ap-hongkong-3wxv81e-1302315972.cos-website.ap-hongkong.myqcloud.com
60s › tencent-meme-scf › "deploy" ran for 2 apps successfully.

在浏览器加载 website URL,来看看你的 meme-as-a-service! 你可以自定义 meme 图片上的每个标题/水印的文本、大小和位置。

如何工作

这个应用程序的总体结构是一个典型的 JAMStack 应用程序。用于图像处理的后端逻辑被部署为 serverless 函数,并且可以通过API 使用。前端用户界面是静态 HTML 和 JavaScript 网页。前端通过 JavaScript Ajax 调用与后端 API 进行交互。

main.rs 是为 meme 图片添加文字的后端 serverless 函数,是用 Rust 编写的。它先读取文本/水印的字体文件和背景图像文件。

然后,Rust 函数读取用户在 HTML 网页中输入的文本、位置和大小。输入为 JSON 文本形式, serde_json 库将 JSON 文本解析为 Rust 结构的数组。

_watermark() 函数将每个水印添加到图像。_watermark() 函数输出最终的图像并对其进行 base64 编码。serverless 运行时的 API 网关将 base64 编码的图像返回给调用JavaScript,以显示在网页上。

const FONT_FILE : &[u8] = include_bytes!("PingFang-Bold.ttf") as &[u8];
const TEMPLATE_BUF : &[u8] = include_bytes!("bg.png") as &[u8];

fn main() {
  let mut buffer = String::new();
  io::stdin().read_to_string(&mut buffer).expect("Error reading from STDIN");
  let obj: FaasInput = serde_json::from_str(&buffer).unwrap();

  let mut img = image::load_from_memory(TEMPLATE_BUF).unwrap();

  let memes: Vec<Watermark> = serde_json::from_str(&(obj.body)).unwrap();
  for m in memes {
    _watermark(m, &mut img);
  }

  let mut buf = vec![];
  img.write_to(&mut buf, image::ImageOutputFormat::Png).unwrap();
  println!("{}", base64::encode_config(buf, base64::STANDARD));
}

_watermark() 函数在图像上添加了一条水印文本,这里用了标准的 Rust 图像处理库(Crate)来处理 meme 图像。

fn _watermark(w: Watermark, img: &mut image::DynamicImage) {
  let font_size = w.font_size;

  let font = Vec::from(FONT_FILE);
  let font = Font::try_from_vec(font).unwrap();

  let scale = Scale {
    x: font_size + 1.0,
    y: font_size + 1.0,
  };
  drawing::draw_text_mut(img, image::Rgba([0, 0, 0, 255u8]), w.left - 2, w.top - 2, scale, &font, &w.text);

  let scale = Scale {
    x: font_size,
    y: font_size,
  };
  drawing::draw_text_mut(img, image::Rgba([255u8, 255u8, 255u8, 255u8]), w.left, w.top, scale, &font, &w.text);
}

前端 JavaScript 从 HTML 表单中获取用户输入的文本、位置和字体大小,将输入数据以 JSON 提交到云函数,然后显示返回的base64 图像。

var memes = [];
memes[0] = {};
memes[0].text = $('#red-girl-says').val();
memes[0].left = parseInt($('#red-girl-left').val());
memes[0].top = parseInt($('#red-girl-top').val());
memes[0].font_size = parseInt($('#red-girl-font').val());
... ...

$.ajax({
  url: window.env.API_URL,
  type: "post",
  data : JSON.stringify(memes),
  dataType: "text",
  success: function (data) {
    const img_url = "data:image/png;base64," + data;
    $('#wm_img').prop('src', img_url);
  },
  error: function(jqXHR, exception){
    console.log("Error Status: " + jqXHR.statusText);
  }
`});`

创建你的 meme

你可以使用源代码模板来创建你自己的 meme-as-a-service。 你可以更改 meme 背景图像,并更改用于添加文本水印的 UI。 因此,请首先确保已安装 Rust 编译器和 ssvmup 构建工具。

更改 Rust 代码和 HTML 文件来适配你自己的 meme 文件。将 Rust 代码编译成 WebAssembly,并把结果复制到 scf/ 文件夹

$ ssvmup build --enable-aot
$ cp pkg/scf.so scf/

运行 Serverless Framework 命令,部署函数。

$ sls deploy
... ...
    region: ap-hongkong
    website: https://sls-website-ap-hongkong-3wxv81e-1302315972.cos-website.ap-hongkong.myqcloud.com
60s › tencent-meme-scf › "deploy" ran for 2 apps successfully.

这样就有了新的表情包。

接下来

你可以用 JAMStack 应用和 serverless 函数做很多事情。例如,你可以为 TensorFlow 推理创建 serverless 函数,将图像识别和图片识别集成到你的应用中。

创建你自己的 meme 网站,并提交 PR 给我们,填写这个表单,我们将为你送上一份周边!