抖动图像大于原始图像(使用Rust图像板条箱)

6tqwzwtp  于 2023-01-05  发布在  其他
关注(0)|答案(1)|浏览(156)

我正在学习Rust,想尝试一下错误扩散抖动。我已经让它工作了,但是抖动的文件比原始文件大,这与预期的情况相反。原始JPEG图像是605 KB大,但抖动图像有2.57MB的巨大空间。我对图像箱的了解非常有限,我发现所有用于表示图像的各种结构体都令人困惑,所以我一定是错过了一些关于API的东西。
下面是抖动图像的代码(只包含我认为相关的部分):

impl DiffusionKernel<'_> {
    pub const FLOYD_STEINBERG: DiffusionKernel<'_> = // Constructor

    fn distribute_error(
        &self,
        error: &(i16, i16, i16),
        image: &mut DynamicImage,
        width: u32,
        height: u32,
        x: u32,
        y: u32,
    ) {
        for target in self.targets /* "targets" are the pixels where to distribute the error */ {
            // Checks if the target x and y are in the bounds of the image
            // Also returns the x and y coordinates of the pixel, because the "target" struct only describes the offset of the target pixel from the pixel being currently processed
            let (is_valid_target, target_x, target_y) =
                DiffusionKernel::is_valid_target(target, width, height, x, y);

            if is_valid_target == false {
                continue;
            }

            let target_pix = image.get_pixel(target_x, target_y);

            // Distribute the error to the target_pix

            let new_pix = Rgba::from([new_r, new_g, new_b, 255]);

            image.put_pixel(target_x, target_y, new_pix);
        }
    }

    pub fn diffuse(&self, bit_depth: u8, image: &mut DynamicImage) {
        let width = image.width();
        let height = image.height();

        for x in 0..width {
            for y in 0..height {
                let pix = image.get_pixel(x, y);
                let pix_quantized = ColorUtil::reduce_color_bit_depth(pix, bit_depth); // Quantizes the color
                let error = (
                    pix.0[0] as i16 - pix_quantized.0[0] as i16,
                    pix.0[1] as i16 - pix_quantized.0[1] as i16,
                    pix.0[2] as i16 - pix_quantized.0[2] as i16,
                );

                image.put_pixel(x, y, pix_quantized);
                self.distribute_error(&error, image, width, height, x, y);
            }
        }

        // Distributing the error ends up creating colors like 7, 7, 7, or 12, 12, 12 instead of 0, 0, 0 for black,
        // so here I'm just ensuring that the colors are correctly quantized.
        // I think the algorithm shouldn't behave like this, I'll try to fix it later.
        for x in 0..width {
            for y in 0..height {
                let pix = image.get_pixel(x, y);
                let pix_quantized = ColorUtil::reduce_color_bit_depth(pix, bit_depth);
                image.put_pixel(x, y, pix_quantized);
            }
        }
    }
}

下面是加载和保存图像的代码:

let format = "jpg";
let path = String::from("C:\\...\\Cat.".to_owned() + format);

let trimmed_path = path.trim(); // This needs to be here if I'm getting the path from the console

let bfr = Reader::open(trimmed_path)
    .unwrap()
    .with_guessed_format()
    .unwrap();
let mut dynamic = bfr.decode().unwrap();
// dynamic = dynamic.grayscale();
error_diffusion::DiffusionKernel::FLOYD_STEINBERG.diffuse(1, &mut dynamic);

dynamic
    .save(trimmed_path.to_owned() + "_dithered." + format)
    .expect("There was an error saving the image.");
lx0bsm1f

lx0bsm1f1#

好了,我继续尝试解决这个问题,看起来你只需要使用一个像PngEncoder这样的图像编码器和一个文件来写入,以降低图像的位深度。编码器接受字节,而不是像素,但幸运的是,图像有一个as_bytes方法,它返回你所需要的。
下面是代码:

let img = image::open(path).expect("Failed to open image.");
let (width, height) = img.dimensions();
let writer = File::create(path.to_owned() + "_out.png").unwrap();
// This is the best encoder configuration for black/white images, which is my output
// Grayscale with multiple colors -> black/white using dithering
let encoder = PngEncoder::new_with_quality(writer, CompressionType::Best, FilterType::NoFilter);

encoder
  .write_image(img.as_bytes(), width, height, ColorType::L8)
  .expect("Failed to write image.");

相关问题