diff options
author | Maxwell Beck <max@rastertail.net> | 2025-03-30 15:46:30 -0500 |
---|---|---|
committer | Maxwell Beck <max@rastertail.net> | 2025-03-30 15:46:30 -0500 |
commit | 1d888631c3e4c423c2b782b01ca29fc1057c51a2 (patch) | |
tree | c43e996ea16b577ee7a02b5d4f97ecf99dde033c /src/main.rs |
Initial commit
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..53f3a41 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,95 @@ +use std::path::PathBuf; + +use bpaf::{construct, positional, OptionParser, Parser}; +use demosaic::Demosaic; +use image::buffer::ConvertBuffer; + +mod demosaic; +mod pipeline; + +#[derive(Clone, Debug)] +struct Args { + paths: Vec<PathBuf> +} + +fn args() -> OptionParser<Args> { + let paths = positional("FILE").some("must process at least one image"); + + construct!(Args { paths }) + .to_options() + .descr("Intuitive raw photo processing engine") +} + +fn main() { + // Parse arguments + let args = args().fallback_to_usage().run(); + + // Initialize GPU + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default()); + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())).unwrap(); + + let downlevel_caps = adapter.get_downlevel_capabilities(); + if !downlevel_caps.flags.contains(wgpu::DownlevelFlags::COMPUTE_SHADERS) { + panic!("GPU does not support compute"); + } + + let (gpu, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, + required_limits: wgpu::Limits::default(), + memory_hints: wgpu::MemoryHints::MemoryUsage, + }, None)).unwrap(); + + // Process images + for path in &args.paths { + let image = rawloader::decode_file(path).unwrap(); + dbg!(&image.cfa); + + let pipeline = pipeline::Resources { + demosaic: Box::new(demosaic::Lmmse::new(&gpu, &queue)) + }; + let demosaiced = pipeline.demosaic.demoasic(&gpu, &queue, &image); + + let readback_buf = gpu.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 4 * 4 * image.width as u64 * image.height as u64, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let mut encoder = gpu.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: None, + }); + encoder.copy_texture_to_buffer( + wgpu::TexelCopyTextureInfo { + texture: &demosaiced, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyBufferInfo { + buffer: &readback_buf, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(4 * 4 * image.width as u32), + rows_per_image: Some(image.height as u32), + }, + }, + wgpu::Extent3d { + width: image.width as u32, + height: image.height as u32, + depth_or_array_layers: 1, + } + ); + queue.submit([encoder.finish()]); + + let readback_slice = readback_buf.slice(..); + readback_slice.map_async(wgpu::MapMode::Read, |_| {}); + gpu.poll(wgpu::Maintain::Wait); + { + let readback_data = readback_slice.get_mapped_range(); + let result_image = image::ImageBuffer::<image::Rgba<f32>, _>::from_raw(image.width as u32, image.height as u32, bytemuck::cast_slice(&readback_data)).unwrap(); + <_ as ConvertBuffer<image::ImageBuffer<image::Rgb<u16>, Vec<u16>>>>::convert(&result_image).save_with_format("out.png", image::ImageFormat::Png).unwrap(); + } + readback_buf.unmap(); + } +} |