3个版本
0.1.4 | 2023年1月28日 |
---|---|
0.1.3 | 2023年1月28日 |
0.1.0 | 2023年1月21日 |
#436 在 图像
每月 31次下载
用于 greenriver
115KB
972 行
Greenfield
绿色字段图像格式的Rust实现
绿色字段图像格式
绿色字段图像格式是一个简单的颜色2D数组,前面有图像的宽度和高度以及一个量化信息。所有字段都以大端格式存储。
- 前64位(一个完整的u64)是绿色字段魔数,用于识别文件为绿色字段图像(
b"grnfld42"
)。 - 接下来的16位(一个完整的u64)是图像的宽度。
- 接下来的16位(一个完整的u64)是图像的高度。
- 接下来的12位是量化信息元组(见[
quantization
])。量化元组的形式为:(bits_r, bits_g, bits_b)
,其中每个值都是存储相应颜色组件使用的位数。 - 剩余的位是图像颜色数据,按行主序排列。每个颜色有(bits_r + bits_g + bits_b)位。例如,如果量化元组是
(5, 6, 5)
,那么每个颜色是16位。要获取所有颜色,必须读取(width * height) * (bits_r + bits_g + bits_b)位。
磁盘格式
╔════════════════════════════╤══════════════════════════════════════════════════════════╗
║ Bits │ Description ║
╠════════════════════════════╪══════════════════════════════════════════════════════════╣
║ 64 │ b"grnfld42": Magic value (0x47524E464C443432) ║
╟────────────────────────────┼──────────────────────────────────────────────────────────╢
║ 16 │ u32: Image width ║
╟────────────────────────────┼──────────────────────────────────────────────────────────╢
║ 16 │ u32: Image height ║
╟────────────────────────────┼──────────────────────────────────────────────────────────╢
║ 12 │ (bits_r, bits_g, bits_b): Quantization tuple ║
╟────────────────────────────┼──────────────────────────────────────────────────────────╢
║ width * height * │ [RGB]: (bits_r + bits_g + bits_r) per pixel ║
║ (bits_r + bits_g + bits_b) │ row-major ║
╚════════════════════════════╧══════════════════════════════════════════════════════════╝
颜色量化
RGB量化是通过将图像的颜色空间大小减小来减少磁盘上图像的大小。这是通过在空间中将相似的颜色分组并引用组而不是特定颜色来完成的。
绿野图像使用了名为均匀量化的量化技术。在均匀量化中,我们将色彩空间的每个分量等间隔地划分,用数字索引每个间隔,然后为每个间隔分配一个颜色(通常是该间隔的平均值),最后,我们只将这个索引存储到磁盘上,而不是颜色。通过这种方式,我们可以减少存储每个颜色分量的位数。一旦图像从磁盘加载,我们可以找到每个索引相应的颜色(由该索引表示的间隔的平均值)并重建图像。
例如,如果我们有一个24位的RGB图像,我们可以将存储每个分量的位数减少到5位,将每个像素所需的位数从24减少到15。将每个分量减少到5位,我们现在每个分量有2^5 = 32个可能值。每个不同的值都是RGB色彩空间中某个间隔的平均值。
安装
此库可在crates.io/crates/greenfield上找到。因此,只需使用cargo进行安装即可。
cargo add greenfield
用法
基本操作
use std::error::Error;
use greenfield::prelude::*;
fn main() -> Result<(), Box<dyn Error>> {
// Creation of images
let image = Image::new(
1,
1,
UniformQuantization::new(5, 6, 5)?,
vec![Rgb::new(255, 255, 255)],
)?;
println!("Image: {}", image);
// Fields
let (width, height) = image.dimensions();
let quantization = image.quantization();
println!("Dimensions: {}x{}", width, height);
println!("Quantization: {}", quantization);
// Iterators over image data
let colors = image.colors().collect::<Vec<&Rgb>>(); // Vec<Item = Rgb>
let pixels = image.pixels(); // Iter<Item = Pixel>
let bytes = image.bytes(); // Iter<Item = u8>
for color in colors {
println!("Color: {}", color);
}
for pixel in pixels {
println!("Pixel: {}", pixel);
}
for byte in bytes {
println!("Byte: {}", byte);
}
Ok(())
}
量化示例
Lenna图像量化为5 6 5位。
序列化/反序列化
此crate使用deku进行序列化和反序列化操作。
我提供了些在greenfield图像和字节之间的转换的实用方法。
use std::{error::Error, path::PathBuf};
use greenfield::prelude::*;
fn main() -> Result<(), Box<dyn Error>> {
let image = Image::new(
1,
1,
UniformQuantization::new(8, 8, 8)?,
vec![Rgb::new(255, 255, 255)],
)?;
// to/from bytes
let serialized = image.clone().serialize()?;
let deserialized = Image::deserialize(&serialized)?;
println!("{}", deserialized);
// to/from file
image.to_file(&PathBuf::from("image.gfd"))?;
let img = Image::from_file(&PathBuf::from("image.gfd"))?;
println!("{}", img);
Ok(())
}
常见格式之间的转换
对于将gfd文件与其他格式(如png和bmp)之间的转换,使用image crate。
出于简便起见,我提供了greenfield::io::{load_image, and save_image}
函数。请注意,用于保存或加载的格式是从文件扩展名推断出来的(例如,.png以png格式保存,.gfd以greenfield格式保存)。
use std::{error::Error, path::PathBuf};
use greenfield::prelude::*;
fn main() -> Result<(), Box<dyn Error>> {
// Load a PNG an save as GFD
let lenna = load_image(
&PathBuf::from("Lenna.png"),
UniformQuantization::new(8, 8, 8)?,
)?;
save_image(&lenna, &PathBuf::from("Lenna.gfd"))?;
// Load a GFD and save as PNG
let lenna_gfd = load_image(
&PathBuf::from("Lenna.gfd"),
UniformQuantization::new(8, 8, 8)?,
)?;
save_image(&lenna_gfd, &PathBuf::from("Lenna.png"))?;
Ok(())
}
依赖项
~17–27MB
~213K SLoC