use std::path::PathBuf; use bpaf::{OptionParser, Parser, construct, positional}; use demosaic::Demosaic; use image::buffer::ConvertBuffer; mod demosaic; mod pipeline; #[derive(Clone, Debug)] struct Args { paths: Vec, } fn args() -> OptionParser { 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 mut pipeline = pipeline::Resources { demosaic: Box::new(demosaic::Lmmse::new(&gpu)), }; pipeline.demosaic.bind_image(&gpu, &queue, &image); pipeline.demosaic.demoasic(&gpu, &queue); let demosaiced = pipeline.demosaic.get_output(); 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::, _>::from_raw( image.width as u32, image.height as u32, bytemuck::cast_slice(&readback_data), ) .unwrap(); <_ as ConvertBuffer, Vec>>>::convert( &result_image, ) .save_with_format("out.png", image::ImageFormat::Png) .unwrap(); } readback_buf.unmap(); } }