Rust 图像处理库 image-rs

一、图像编解码
[dependencies]
image = "*"
imageproc ="*"

Rust下的图像处理库,image-rs 提供了基本的图像处理功能和用于在各种图像格式之间进行转换的方法。所有图像处理功能都对实现GenericImageViewGenericImage特性并返回ImageBuffer的类型进行操作。

  • Github: image-rs repo
  • Docs rs: image-rs doc

image-rs支持的图像格式如下:

FormatDecodingEncoding
PNGAll supported color typesSame as decoding
JPEGBaseline and progressiveBaseline JPEG
GIFYesYes
BMPYesRGB(8), RGBA(8), Gray(8), GrayA(8)
ICOYesYes
TIFFBaseline(no fax support) + LZW + PackBitsRGB(8), RGBA(8), Gray(8)
WebPLossy(Luma channel only)No
AVIFOnly 8-bitLossy
PNMPBM, PGM, PPM, standard PAMYes
DDSDXT1, DXT3, DXT5No
TGAYesRGB(8), RGBA(8), BGR(8), BGRA(8), Gray(8), GrayA(8)
farbfeldYesYes

从上表我们可以看出image-rs基本支持了应用中常见的图像容器格式类型。

关于ImageDecoder和ImageDecoderExt 所有的图像格式decoders都包含了ImageDecoder实现,其中主要过程是从图像文件中获取图像的metadata并解码图像。

ImageDecoder
pub trait ImageDecoder: Sized {
    type Reader: Read;
    fn dimensions(&self) -> (u64, u64);        // 返回一个包含图像宽度和高度的元组
    fn colortype(&self) -> ColorType;          // 返回图像的颜色类型,例如RGB(8)(8位RGB)
    fn into_reader(self) -> ImageResult<Self::Reader>;  // 返回可用于获取图像字节的阅读器。

    fn row_bytes(&self) -> u64 { ... }                  // 返回图像的一行中的字节数。
    fn total_bytes(&self) -> u64 { ... }                // 返回图像中的字节总数。
    fn scanline_bytes(&self) -> u64 { ... }             // 返回可以从此解码器有效读取的最小字节数。
    fn read_image(self) -> ImageResult<Vec<u8>> { ... } //返回图像中的所有字节。
    fn read_image_with_progress<F: Fn(Progress)>(
        self, 
        progress_callback: F
    ) -> ImageResult<Vec<u8>> { ... }   //与read_image相同,但会定期调用提供的回调以提供有关加载进度的更新。
}

其中一些decoders的比较重要的参数包括:

  • dimensions:返回包含图像的宽度和高度的元组数据
  • color_type:返回由decoder返回的图像的色彩类型
  • read_image:把图像解码成bytes片
像素类型

image提供了如下几种像素类型:

  • Rgb: 包含Rgb像素
  • Rgba: 包含Rgba像素(a为alpha,透明通道)
  • Luma: 灰度像素
  • LumaA: 包含alpha通道的灰度像素
图像处理函数
  • blur:使用高斯模糊来处理图像
  • brighten:图像高亮处理
  • huerotate: 旋转色彩空间
  • contrast: 调整图像的对比度
  • crop: 剪裁图像
  • filter3x3:使用3x3的矩阵来处理图像,可用于图像降噪,升噪
  • grayscale: 灰度化图像
  • flip_horizontal: 水平翻转图像
  • flip_vertical: 垂直翻转图像
  • invert: 对图像的每个像素求反
  • resize: 改变图像尺寸
  • rotate180: 图像顺时针旋转180度
  • rotate270: 图像顺时针旋转270度
  • rotate90: 图像顺时针
  • unsharpen: 降低图像锐度
二、GenericImageView与GenericImage

图像中的各个像素在左上角以(0,0)索引。
GenericImageViewGenericImage特点

特性提供了检查(GenericImageView)和操作(GenericImage)图像的方法,这些参数在图像的像素类型上进行了参数化。

GenericImageView:获取图像视图属性与像素内容

  • Dimensions:返回一个包含图像宽度和高度的元组。
  • get_pixel:返回位于(x,y)的像素。
  • pixel:返回一个迭代器,迭代该图像的像素。

GenericImage:操作图像视图

  • put_pixel:将像素放置在位置(x,y)。
  • copy_from:将所有像素从另一个图像复制到该图像中。
三、ImageBuffer 与 DynamicImage
ImageBuffer

image 提供了两种表示图像数据的主要方式:ImageBuffer文档

由其像素类型参数化的图像,由像素的宽度和高度以及像素向量表示。它提供对像素的直接访问并实现GenericImageViewGenericImage特性。

extern crate image;

use image::{GenericImage, GenericImageView, ImageBuffer, RgbImage};

// 构建具有指定宽度和高度的RGB图像缓冲区。
let img: RgbImage = ImageBuffer::new(512, 512);

// Construct a new by repeated calls to the supplied closure.
let mut img = ImageBuffer::from_fn(512, 512, |x, y| {
    if x % 2 == 0 {
        image::Luma([0u8])
    } else {
        image::Luma([255u8])
    }
});

// 获取图像的宽和高。
let (width, height) = img.dimensions();

//访问坐标像素。 或者使用`GenericImage` trait.的`get_pixel`方法
let pixel = img[(100, 100)];
let pixel = *img.get_pixel(100, 100);

// 指定坐标点的像素赋值.
img.put_pixel(100, 100, pixel);

// 迭代图像中的所有像素。
for pixel in img.pixels() {
    // Do something with pixel.
}
DynamicImage

DynamicImage是所有受支持ImageBuffer<P>类型的枚举。其确切的图像类型在运行时确定。它是打开图像时返回的类型。

为了方便起见,DynamicImage重新实现了所有图像处理功能。
DynamicImage实现RGBA像素的GenericImageViewGenericImage特征。

SubImage

SubImage 以矩形坐标为界的另一幅图像的视图。给定的坐标设置矩形左上角的位置。这用于在图像的子区域上执行图像处理功能。

extern crate image;

use image::{GenericImageView, ImageBuffer, RgbImage, imageops};

let mut img: RgbImage = ImageBuffer::new(512, 512);
let subimg = imageops::crop(&mut img, 0, 0, 100, 100);

assert!(subimg.dimensions() == (100, 100));
四、示例
打开和保存图像
extern crate image;

use image::GenericImageView;

fn main() {
    // image::open 通过文件路径打开图片并返回`DynamicImage`对象。
    let img = image::open("tests/images/jpg/progressive/cat.jpg").unwrap();

    // 获取图像属性
    println!("dimensions {:?}", img.dimensions());
    println!("{:?}", img.color());

    // 保存
    img.save("test.png").unwrap();

	// 获取图像缓冲(网络请求获得)并保存
	let buffer: &[u8] = unimplemented!(); // Generate the image data
	image::save_buffer("image.png", buffer, 800, 600, image::ColorType::Rgb8).unwrap()
}

测试示例:

use image;
use image::GenericImageView;

// Uage: cargo run 1.jpg 2.jpg
fn main() {
    let img_inputpath = std::env::args().nth(1).expect("no pattern given");
    let img_outputpath = std::env::args().nth(2).expect("no path given");
    let img = image::open(img_inputpath).unwrap();
    let (width,height) = img.dimensions();
    println!("图像:宽={},高={} ",width,height);
    img.save(img_outputpath).unwrap();
}

image提供open从路径打开图像的功能。图像格式由路径的文件扩展名确定。一个io模块为阅读器提供了更多控制权。

Generating Fractals例子

dependencies中添加引用

[dependencies]
image = "0.23.4"
num-complex = "0.2.4"

修改源文件main.rs如下

extern crate image;
extern crate num_complex;

fn main() {
    let imgx = 800;
    let imgy = 800;

    let scalex = 3.0 / imgx as f32;
    let scaley = 3.0 / imgy as f32;

    // Create a new ImgBuf with width: imgx and height: imgy
    let mut imgbuf = image::ImageBuffer::new(imgx, imgy);

    // Iterate over the coordinates and pixels of the image
    for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
        let r = (0.3 * x as f32) as u8;
        let b = (0.3 * y as f32) as u8;
        *pixel = image::Rgb([r, 0, b]);
    }

    // A redundant loop to demonstrate reading image data
    for x in 0..imgx {
        for y in 0..imgy {
            let cx = y as f32 * scalex - 1.5;
            let cy = x as f32 * scaley - 1.5;

            let c = num_complex::Complex::new(-0.4, 0.6);
            let mut z = num_complex::Complex::new(cx, cy);

            let mut i = 0;
            while i < 255 && z.norm() <= 2.0 {
                z = z * z + c;
                i += 1;
            }

            let pixel = imgbuf.get_pixel_mut(x, y);
            let image::Rgb(data) = *pixel;
            *pixel = image::Rgb([data[0], i as u8, data[2]]);
        }
    }

    // Save the image as “fractal.png”, the format is deduced from the path
    imgbuf.save("fractal.png").unwrap();
}

效果图:
在这里插入图片描述

使用 GTK-rs 需要先安装 GTK+3,可以在 Linux 系统下通过包管理器安装,也可以在 Windows 和 macOS 下从官网下载安装程序进行安装。 安装 GTK+3 后,可以在 Rust 项目中添加 gtk 和 gdk 两个依赖: ```toml [dependencies] gtk = "0.9" gdk = "0.9" ``` 然后在 Rust 代码中引入 gtk 和 gdk : ```rust extern crate gtk; extern crate gdk; ``` 在 GTK-rs 中,主要有两种方式创建 GUI 窗口:Builder 和手动创建。下面分别介绍这两种方式。 ### 使用 Builder 创建 GUI 窗口 使用 Builder 可以通过 XML 文件描述 GUI 界面,然后使用 Rust 代码加载并显示窗口。以下是一个简单的例子: ```xml <?xml version="1.0" encoding="UTF-8"?> <interface> <object class="GtkWindow" id="main_window"> <property name="title">Hello, World!</property> <property name="default-width">400</property> <property name="default-height">300</property> <child> <object class="GtkLabel" id="label"> <property name="label">Hello, World!</property> </object> </child> </object> </interface> ``` 在 Rust 代码中,可以使用 gtk::Builder 加载并显示窗口: ```rust use gtk::prelude::*; use gtk::{Builder, Window}; fn main() { if let Err(err) = gtk::init() { eprintln!("Failed to initialize GTK: {}", err); return; } let glade_src = include_str!("path/to/glade/file.glade"); let builder = Builder::from_string(glade_src); let window: Window = builder.get_object("main_window").unwrap(); window.show_all(); gtk::main(); } ``` ### 手动创建 GUI 窗口 手动创建 GUI 窗口需要在 Rust 代码中定义窗口和窗口中的组件,以下是一个简单的例子: ```rust use gtk::prelude::*; use gtk::{Window, WindowType, Label}; fn main() { if let Err(err) = gtk::init() { eprintln!("Failed to initialize GTK: {}", err); return; } let window = Window::new(WindowType::Toplevel); window.set_title("Hello, World!"); window.set_default_size(400, 300); let label = Label::new(Some("Hello, World!")); window.add(&label); window.show_all(); gtk::main(); } ``` 以上代码中,创建了一个顶层窗口和一个标签组件,将标签添加到窗口中并显示窗口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SongpingWang

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值