Open ndaniels opened 3 years ago
If you the header data is needed (or any format specific information for that matter) then it's best to use skip the Reader
and instead utilize the corresponding codec directly. Sadly, as it turns out, this isn't quite as straightforward either.
let reader = codecs::pnm::PnmDecoder::new(Cursor::new(buffer))
.expect("Failed to read image format");
// I understand your confusion, there is no `reader.header()`
let img = DynamicImage::from_decoder(reader).unwrap();
This isn't impossible but requires quite a bit of tedium. Basically, from_decoder
consumes the reader
but you'd want the value to call PnmDecoder::into_inner
—which contains the header in the second component. There is no magic in from_decoder
and the method could work with a &mut
but this use case was not considered to be as relevant in the design of that method.
Ways forward:
PnmDecoder::header(&self) -> &PnmHeader
. Should be easy enough.from_decoder
to work with a mutable reference and then expose that as a separate method on DynamicImage
.0.24
.Since you have a Cursor
you can do this via a multi-pass approach as a workaround:
use image::codecs::pnm;
use image::{DynamicImage, GenericImageView};
use std::env;
use std::fs;
use std::io::{self, BufReader, BufRead, Cursor};
fn main() {
let input = env::args().nth(1);
let mut raw_reader: Box<dyn BufRead> = match input {
None => Box::new(BufReader::new(io::stdin())),
Some(filename) => Box::new(BufReader::new(fs::File::open(filename).unwrap()))
};
let mut buffer = Vec::new();
// read the whole contents
raw_reader.read_to_end(&mut buffer).unwrap();
// !! Changed
let (mut cursor, header) = pnm::PnmDecoder::new(Cursor::new(&buffer[..]))
.expect("Failed to read image format")
.into_inner();
// Rewind.
cursor.set_position(0);
let reader = image::codecs::pnm::PnmDecoder::new(cursor)
.expect("Failed to read image format");
let img = DynamicImage::from_decoder(reader).unwrap();
println!("{:?}", img);
println!("{:?}", img.get_pixel(1,1));
let sum = img.pixels().fold(0_usize, |acc, pixel| acc + (pixel.2[0] as usize)) as f64;
let denom = header.maximal_sample();
println!("{:.3}", sum / (img.width() * img.height() * denom) as f64);
}
This happens in ,...
Open a pnm (e.g. ppm or pgm) file with the PnmDecoder. Pixel values are integers, but they need not be u8 by the PNM spec. They can have denominators as large as 65535.
Expected
I see that the maxval is read in from the Pnm header, but it doesn't seem to be available in any public interface. So, if I read a Pgm with a maxval of 15, for instance, or 65525, how is the denominator available for (for instance) computing the brightness of a pixel?