rtiow: add greyscale images for debugging.

This commit is contained in:
Bill Thiede 2019-10-24 14:27:40 -07:00
parent ea31b570db
commit 96e74b3ebf
3 changed files with 112 additions and 63 deletions

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use std::sync::Mutex;
use chrono::Local;
use image::RgbImage;
use image;
use lazy_static::lazy_static;
use log::info;
@ -17,13 +17,42 @@ pub const MAIN_IMAGE: &str = "@final";
// Red indicates recursion hit maximum depth splitting the pixel.
// Green indicates no subdivision necessary
pub const ADAPTIVE_DEPTH: &str = "adaptive_depth";
// Grey scale showing rays cast per pixel.
pub const RAYS_PER_PIXEL: &str = "rays_per_pixel";
lazy_static! {
static ref DEBUGGER: Arc<Mutex<Debugger>> = Arc::new(Mutex::new(Debugger::new()));
}
pub enum ImageType {
RGB01,
Grey01,
GreyNormalized,
}
struct Image {
w: usize,
h: usize,
pix: Vec<Vec3>,
}
impl Image {
fn new(w: usize, h: usize) -> Image {
Image {
w,
h,
pix: (0..w * h).map(|_| [0., 0., 0.].into()).collect(),
}
}
fn put_pixel(&mut self, x: usize, y: usize, p: Vec3) {
let offset = x + y * self.w;
self.pix[offset] = p;
}
}
struct Debugger {
images: HashMap<String, RgbImage>,
images: HashMap<String, (ImageType, Image)>,
}
impl Debugger {
@ -34,94 +63,106 @@ impl Debugger {
}
}
pub fn register_image(name: String, dimensions: (u32, u32)) {
pub fn register_image(name: String, dimensions: (usize, usize), it: ImageType) {
let mut debugger = DEBUGGER.lock().unwrap();
debugger
.images
.insert(name, RgbImage::new(dimensions.0, dimensions.1));
.insert(name, (it, Image::new(dimensions.0, dimensions.1)));
}
pub fn set_pixel(name: &str, x: usize, y: usize, pixel: Vec3) {
let mut debugger = DEBUGGER.lock().unwrap();
let img = debugger
let (_it, img) = debugger
.images
.get_mut(name)
.expect(&format!("couldn't find image named '{}'", name));
let y_inv = img.height() - y as u32 - 1;
img.put_pixel(
x as u32,
y_inv as u32,
image::Rgb([
(pixel[0] * 255.).min(255.) as u8,
(pixel[1] * 255.).min(255.) as u8,
(pixel[2] * 255.).min(255.) as u8,
]),
);
let y_inv = img.h - y - 1;
img.put_pixel(x, y_inv, pixel);
}
pub fn set_pixel_grey(name: &str, x: usize, y: usize, grey: f32) {
let mut debugger = DEBUGGER.lock().unwrap();
let (_it, img) = debugger
.images
.get_mut(name)
.expect(&format!("couldn't find image named '{}'", name));
let y_inv = img.h - y - 1;
img.put_pixel(x, y_inv, [grey, grey, grey].into());
}
trait ImageSaver {
fn save<Q>(&self, path: Q) -> std::io::Result<()>
where
Q: AsRef<Path> + Sized;
}
pub fn write_images<P: AsRef<Path>>(output_dir: P) -> std::io::Result<()> {
let output_dir: &Path = output_dir.as_ref();
let debugger = DEBUGGER.lock().unwrap();
let now = Local::now();
/*
let style = r#"
<style>
.frame {
display: inline-block;
}
figure {
margin: 0.25em;
}
figcaption {
text-align: center;
}
img {
max-width: 97%;
height: auto;
margin: 0;
border: 1px solid #999;
padding: 5px;
background: #fff;
}
</style>
"#;
writeln!(f, "{}", style)?;
writeln!(
f,
"<h1>Run @ {} ({})</h1>",
now.to_rfc2822(),
now.timestamp()
)?;
*/
// Write out images in consistent order.
let mut names = debugger.images.keys().collect::<Vec<_>>();
names.sort();
let mut ratio = 1.;
let mut size = (0, 0);
for name in &names {
let img = debugger.images.get(*name).unwrap();
if *name == MAIN_IMAGE {
let (w, h) = img.dimensions();
ratio = w as f32 / h as f32;
size = img.dimensions();
}
let (it, img) = debugger.images.get(*name).unwrap();
let filename = format!("{}.png", name);
/*
writeln!(
f,
r#"<div class="frame"><figure><a href="{0}?t={2}"><img src="{0}?t={2}"></a><figcaption>{1} ({2})</figcaption></figure></div>"#,
filename, name, now.timestamp())?;
*/
let path = output_dir.join(filename);
info!("Saving {}", path.to_string_lossy());
img.save(path)?;
match it {
ImageType::RGB01 => {
let mut out_img = image::RgbImage::new(img.w as u32, img.h as u32);
out_img
.enumerate_pixels_mut()
.enumerate()
.for_each(|(i, (_x, _y, p))| {
let pixel = img.pix[i];
*p = image::Rgb([
(pixel[0] * 255.).min(255.) as u8,
(pixel[1] * 255.).min(255.) as u8,
(pixel[2] * 255.).min(255.) as u8,
])
});
if *name == MAIN_IMAGE {
ratio = img.w as f32 / img.h as f32;
size = (img.w, img.h);
}
out_img.save(path)?;
}
ImageType::Grey01 => {
let mut out_img = image::GrayImage::new(img.w as u32, img.h as u32);
out_img
.enumerate_pixels_mut()
.enumerate()
.for_each(|(i, (_x, _y, p))| {
let pixel = img.pix[i];
*p = image::Luma([(pixel[0] * 255.).min(255.) as u8])
});
out_img.save(path)?;
}
ImageType::GreyNormalized => {
let mut out_img = image::GrayImage::new(img.w as u32, img.h as u32);
let max_val = img.pix.iter().map(|v| v.x).fold(0., f32::max);
dbg!(&max_val);
out_img
.enumerate_pixels_mut()
.enumerate()
.for_each(|(i, (_x, _y, p))| {
let pixel = img.pix[i];
*p = image::Luma([(pixel[0] / max_val * 255.).min(255.) as u8])
});
out_img.save(path)?;
}
};
}
let f = File::create(output_dir.join("data.json"))?;
#[derive(Serialize)]
struct Data {
timestamp: i64,
ratio: f32,
size: (u32, u32),
size: (usize, usize),
names: Vec<String>,
images: Vec<String>,
}

View File

@ -441,6 +441,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
([0., 0., 0.].into(), 0),
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
);
output::set_pixel_grey(output::RAYS_PER_PIXEL, x, y, rays as f32);
(pixel / scene.subsamples as f32, rays)
};
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
@ -575,14 +576,21 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
info!("Rendering with {} subsamples", scene.subsamples);
output::register_image(
output::MAIN_IMAGE.to_string(),
(scene.width as u32, scene.height as u32),
(scene.width, scene.height),
output::ImageType::RGB01,
);
if scene.adaptive_subsampling.is_some() {
output::register_image(
output::ADAPTIVE_DEPTH.to_string(),
(scene.width as u32, scene.height as u32),
(scene.width, scene.height),
output::ImageType::RGB01,
);
}
output::register_image(
output::RAYS_PER_PIXEL.to_string(),
(scene.width, scene.height),
output::ImageType::GreyNormalized,
);
let pixel_total = scene.width * scene.height;
let mut last_time = time::Instant::now();

View File

@ -72,8 +72,8 @@ pub fn new(opt: &Opt) -> Scene {
camera,
world,
subsamples: opt.subsamples,
// adaptive_subsampling: None,
adaptive_subsampling: Some(0.5),
adaptive_subsampling: None,
// adaptive_subsampling: Some(0.5),
num_threads: opt.num_threads,
width: opt.width,
height: opt.height,