使用Rust绘制图像,实现文字与图形的旋转

程序员咋不秃头 2024-09-09 00:06:01

在图像处理领域,Rust 凭借其强大的性能和安全性,正逐渐成为开发者的首选语言。本文将深入探讨如何在 Rust 中使用 image 和 imageproc 库,实现对图像的灵活操作,特别是任意角度的文字和图形绘制。

项目准备

在开始之前,我们需要先设置好项目环境:

[dependencies]anyhow = "1.0.75"image = "0.25.2"imageproc = "0.25.0"ab_glyph = "0.2.28"anyhow 用于简化错误处理,你也可以选择直接使用 unwrap。image 和 imageproc 是图像处理的核心库,提供读取、操作和保存图像的功能。ab_glyph 用于加载、缩放、定位和栅格化 OpenType 字体,据说比 rusttype 更快更好用 (Disclaimer: 我没有亲自比较过,只是网络传言)。imageproc绘图功能概览

imageproc 库的 drawing 模块提供了一系列便捷的函数,用于在图像上绘制基本形状。

每个 draw_ 函数都有两个版本:一个创建输入图像的新副本,另一个则直接修改原图像。后者更节省内存,但会丢失原始图像数据。

需要注意的是,这里的“图像”指的是我们从文件中读取的图像数据。只要不将新的图像数据保存到与原始文件相同的路径下,就不会丢失原始文件!

绘制文字

水平文字绘制

要绘制水平文字,我们可以使用以下两个函数:

draw_text:在新图像副本上绘制彩色文字。draw_text_mut:直接在原图像上绘制彩色文字。

这两个函数都需要传入一个 font: &impl Font 参数,用于指定要使用的字体。

我们可以使用 ab_glyph::FontVec::try_from_vec(data: Vec<u8>) 来加载字体,其中 data 是字体的二进制数据。

这里我将使用从 Google Fonts 下载的 Matemasie 字体。你可以自由选择你喜欢的字体,并下载其 ttf 文件!

Matemasie 字体示例

以下代码演示了如何在皮卡丘图像的右上角绘制粉色的水平文字 "Pikachu":

let image_path = "test_data/pikachu.png";let image = ImageReader::open(image_path)?.decode()?;let (dimension_x, dimension_y) = image.dimensions();let font_vec = Vec::from(include_bytes!("../test_data/font/Matemasie-Regular.ttf") as &[u8]);let font = FontVec::try_from_vec(font_vec)?;let scale = PxScale::from(36.0);let text = "Pikachu";let new_image = draw_text( &image, Rgba([255, 192, 203, 255]), (dimension_x / 2).try_into()?, (dimension_y / 8).try_into()?, scale, &font, &text);new_image.save("test_data/pikachu_new.png")?;

代码解释:

使用 ImageReader 从路径创建 Rust image 对象。获取图像的 dimensions (尺寸)。使用 include_bytes! 加载字体数据,并使用 FontVec::try_from_vec 创建字体对象。使用 draw_text 函数在新图像副本上绘制文字,颜色为粉色 Rgba([255, 192, 203, 255]),字体大小为 36px,位置为 (dimension_x / 2, dimension_y / 8)。如果你想使用 pt 而不是 px,可以使用 Font::pt_to_px_scale 将 pt 转换为 PxScale。保存新图像。

需要注意的是,我们指定的 point 是文字首行边缘中心的坐标。

如果要绘制特定角度的文字,还需要一些额外的步骤。

任意角度文字绘制

要绘制特定角度的文字,我们需要扩展上述思路:

使用 image::ImageBuffer::new 创建一个与原始图像大小相同的新空图像。使用 draw_text 或 draw_text_mut 将文字绘制到新图像上。使用 imageproc::geometric_transformations::rotate_about_center 旋转文字图像。使用 image::imageops::overlay 将文字图像叠加到目标图像上。let image_path = "test_data/pikachu.png";let mut image = ImageReader::open(image_path)?.decode()?;let (dimension_x, dimension_y) = image.dimensions();let scale = PxScale::from(36.0);let font_vec = Vec::from(include_bytes!("../test_data/font/Matemasie-Regular.ttf") as &[u8]);let font = FontVec::try_from_vec(font_vec)?;let text = "Pikachu";let mut text_image: image::ImageBuffer<Rgba<u8>, Vec<u8>> = image::ImageBuffer::new(dimension_x, dimension_y);draw_text_mut( &mut text_image, Rgba([255, 192, 203, 255]), (dimension_x / 2).try_into()?, (dimension_y / 8).try_into()?, scale, &font, &text);text_image = imageproc::geometric_transformations::rotate_about_center(&text_image, 0.3, imageproc::geometric_transformations::Interpolation::Nearest, Rgba([0, 0, 0, 0]));image::imageops::overlay(&mut image, &text_image, 0, 0);image.save("test_data/new_pikachu_rotate.png")?;

这里有几点需要注意:

rotate_about_center 的输出图像与输入图像尺寸相同,超出输入图像范围的部分将设置为 default 值,在本例中为透明色 Rgba([0, 0, 0, 0])。rotate_about_center 函数默认绕图像中心顺时针旋转。如果想绕特定点旋转,可以使用 rotate 函数。

同样的思路也适用于以特定角度叠加两张图像!

绘制图形

imageproc 的 drawing 模块提供了许多用于绘制预定义形状的函数,例如 line_segment、polygon、cross 等等。

本文将以矩形为例,使用 draw_hollow_rect 函数进行演示。

与 text 类似,draw_hollow_rect 也有一个 mut 版本,用于直接在原图像上绘制矩形轮廓。还有一个 填充版本,可以用特定颜色填充矩形。

让我们在上面绘制的文字周围添加一个矩形框:

let image_path = "test_data/pikachu.png";let mut image = ImageReader::open(image_path)?.decode()?;let (dimension_x, dimension_y) = image.dimensions();let scale = PxScale::from(36.0);let font_vec = Vec::from(include_bytes!("../test_data/font/Matemasie-Regular.ttf") as &[u8]);let font = FontVec::try_from_vec(font_vec)?;let point_x: i32 = (dimension_x / 2).try_into()?;let point_y: i32 = (dimension_y / 8).try_into()?;let text = "Pikachu";let text_dimension = text_size(scale.clone(), &font, &text);draw_text_mut( &mut image, Rgba([255, 192, 203, 255]), point_x, point_y, scale, &font, &text);let rect: Rect = Rect::at(point_x, point_y + (text_dimension.1 / 2) as i32).of_size(text_dimension.0, text_dimension.1);draw_hollow_rect_mut( &mut image, rect, Rgba([255, 192, 203, 255]),);image.save("test_data/new_pikachu_rect.png")?;

代码中需要注意的是,矩形的原点与文字的坐标点相差 text_dimension.1/2。这是因为矩形的原点定义为左上角,而我们绘制文字时指定的坐标点是首行边缘的中心。

如果想绘制特定角度的矩形,可以套用绘制文字时的思路,这里就不再赘述了。

总结

本文介绍了如何在 Rust 中使用 image 和 imageproc 库对图像进行操作,包括绘制任意角度的文字和图形。希望这些内容能够帮助你更好地利用 Rust 进行图像处理。

尽情发挥你的创造力,绘制出你想要的图像吧!

0 阅读:12