我正在学习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.");
1条答案
按热度按时间lx0bsm1f1#
好了,我继续尝试解决这个问题,看起来你只需要使用一个像PngEncoder这样的图像编码器和一个文件来写入,以降低图像的位深度。编码器接受字节,而不是像素,但幸运的是,图像有一个
as_bytes
方法,它返回你所需要的。下面是代码: