#jpeg #图像压缩 #EXIF #压缩 #图像 #保留 #turbojpeg

jippigy

在保留EXIF数据的同时压缩JPEG。提供多线程方法进行批量压缩。

3个版本 (稳定)

1.0.1 2024年4月24日
1.0.0 2024年4月15日
0.4.0 2024年4月6日

#377图像

Download history 154/week @ 2024-04-19 17/week @ 2024-04-26 3/week @ 2024-05-17 2/week @ 2024-05-24

每月131次下载

MIT许可协议

29KB
400

jippigy

一个简单、多线程的JPEG压缩crate,由turbojpeg提供动力。

使用常见的2x2色度子采样进行压缩。

目前这个crate没有提供更精细的控制JPEG压缩的方式。更多选项请查看turbojpeg

提供单线程或多线程压缩JPEG图像的方法。两种方法都通过img_parts crate保留原始JPEG的EXIF数据。

1.0.1 补丁

摘要

  • 并行压缩返回的jpeg字节顺序与传入的顺序相同.
  • 重新导出 ParallelIntoIterator
  • 轻微优化。

有关更多详细信息,请参阅CHANGELOG.md

构建turbojpeg时出错?

问题通常与turbojpeg-sys有关(参见这个问题和我在设置此crate CI时的尝试)。

要成功构建turbojpeg-sys,您需要在系统中安装cmake、C编译器(gcc、clang等)和NASM(参见:turbojpeg's 要求)。更多详情请参阅turbojpeg-sys's Building部分。

示例

with_ 方法是可选的。

使用 Single 进行单图像压缩

use jippigy::Single;
use image::{RgbImage, ImageFormat::Jpeg};
use std::io::Cursor;
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut vec: Vec<u8> = Vec::new();
    let img = RgbImage::new(1000, 1000);
    let _write = img.write_to(&mut Cursor::new(&mut vec), Jpeg)?;
    let _result: Vec<u8> = Single::from_bytes(vec)
        .with_quality(80)
        .build()
        .compress()?;
    Ok(())
}

使用 Parallel 进行多线程批量压缩

通过 into_iter()Parallel 转换为 ParallelIntoIterator,它以相同的顺序返回传入的项目。这意味着,您可以像以下示例那样做,将JPEG文件的文件名保存到向量中,然后稍后与您创建的 ParallelIntoIterator 进行zip。

 use jippigy::Parallel;
 use std::path::PathBuf;
 use tempdir::TempDir;
 const TEST_DIR: &str = "./tests/images/";
 fn main() -> Result<(), Box<dyn std::error::Error>> {
     let image_dir_path = PathBuf::from(format!("{}", TEST_DIR));

     let mut vec_of_bytes = Vec::new();
     let mut list_of_names = Vec::new();

     // push the filenames and read bytes into a separate vector.
     for file in std::fs::read_dir(image_dir_path.clone())? {
         let filepath = file?.path();
         if filepath.is_file() {
             let filename = filepath.clone()
                 .file_name()
                 .and_then(|osstr| osstr.to_str())
                 .and_then(|a| Some(a.to_string()))
                 .unwrap_or_default();
             list_of_names.push(filename);
             let read_file = std::fs::read(filepath);
             vec_of_bytes.push(read_file?);
         }
     }
     // this temporary directory is here for doctest purposes,
     // but you will create your own directory.
     let tempdir = TempDir::new("compressed")?;

     // zip list_of_names vector with this iterator.
     for zipped in Parallel::from_vec(vec_of_bytes)
         .with_quality(50)
         .with_device(4)
         .build()
         .into_iter()
         .zip(list_of_names)
     {
         // saves compresssed JPEG with the original name.
         let (compressed_bytes, name) = zipped;
         if let Ok(bytes) = compressed_bytes {
             std::fs::write(
                 image_dir_path
                     .join(tempdir.path())
                     .join(format!("{name}").as_str()),
                 bytes,
             )?;
             println!("saved: {name}");
         }
     }
     tempdir.close()?;
     Ok(())
 }

依赖项

~11MB
~173K SLoC