mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Graphene CLI + quantization research (#1320)
* Implement skeleton for graphene-cli * Configure gpu surface on non wasm32 targets * Create window with full hd size * Create window using the graphen-cli * Use window size for surface creation * Reuse surface configuration * Reduce window size for native applications to 800x600 * Add compute pipeline test * Poll wgpu execution externally * Remove cache node after texture upload * Add profiling instructions * Add more debug markers * Evaluate extract node before flattening the network * Reenable hue saturation node for compilation * Make hue saturation node work on the gpu + make f32 default for user inputs * Add version of test files without caching * Only dispatch each workgroup not pixel * ICE * Add quantization to gpu code * Fix quantization * Load images at graph runtime * Fix quantization calculation * Feature gate quantization * Use git version of autoquant * Add license to `graphene-cli` * Fix graphene-cli test case * Ignore tests on non unix platforms * Fix flattening test
This commit is contained in:
committed by
Keavon Chambers
parent
61c5dd1f88
commit
3c2d371173
@@ -1,3 +1,4 @@
|
||||
use autoquant::packing::ErrorFunction;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use graphene_core::quantization::*;
|
||||
use graphene_core::raster::{Color, ImageFrame};
|
||||
@@ -13,47 +14,93 @@ pub struct GenerateQuantizationNode<N, M> {
|
||||
|
||||
#[node_macro::node_fn(GenerateQuantizationNode)]
|
||||
fn generate_quantization_fn(image_frame: ImageFrame<Color>, samples: u32, function: u32) -> [Quantization; 4] {
|
||||
let image = image_frame.image;
|
||||
generate_quantization_from_image_frame(&image_frame)
|
||||
}
|
||||
|
||||
pub fn generate_quantization_from_image_frame(image_frame: &ImageFrame<Color>) -> [Quantization; 4] {
|
||||
let image = &image_frame.image;
|
||||
|
||||
let len = image.data.len().min(10000);
|
||||
let mut channels: Vec<_> = (0..4).map(|_| Vec::with_capacity(image.data.len())).collect();
|
||||
image
|
||||
let data = image
|
||||
.data
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| i % (image.data.len() / len) == 0)
|
||||
.map(|(_, x)| vec![x.r() as f64, x.g() as f64, x.b() as f64, x.a() as f64])
|
||||
.for_each(|x| x.into_iter().enumerate().for_each(|(i, value)| channels[i].push(value)));
|
||||
let quantization: Vec<Quantization> = channels.into_iter().map(|x| generate_quantization_per_channel(x, samples)).collect();
|
||||
core::array::from_fn(|i| quantization[i].clone())
|
||||
.flat_map(|(_, x)| vec![x.r() as f64, x.g() as f64, x.b() as f64, x.a() as f64])
|
||||
.collect::<Vec<_>>();
|
||||
generate_quantization(data, len)
|
||||
}
|
||||
fn generate_quantization(data: Vec<f64>, samples: usize) -> [Quantization; 4] {
|
||||
let red = create_distribution(data.clone(), samples, 0);
|
||||
let green = create_distribution(data.clone(), samples, 1);
|
||||
let blue = create_distribution(data.clone(), samples, 2);
|
||||
let alpha = create_distribution(data, samples, 3);
|
||||
|
||||
let fit_red = autoquant::calculate_error_function(&red, 1, &red);
|
||||
let fit_green = autoquant::calculate_error_function(&green, 1, &green);
|
||||
let fit_blue = autoquant::calculate_error_function(&blue, 1, &blue);
|
||||
let fit_alpha = autoquant::calculate_error_function(&alpha, 1, &alpha);
|
||||
let red_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_red.as_slice());
|
||||
let green_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_green.as_slice());
|
||||
let blue_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_blue.as_slice());
|
||||
let alpha_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_alpha.as_slice());
|
||||
let merged: ErrorFunction<20> = autoquant::packing::merge_error_functions(&red_error, &green_error);
|
||||
let merged: ErrorFunction<30> = autoquant::packing::merge_error_functions(&merged, &blue_error);
|
||||
let merged: ErrorFunction<40> = autoquant::packing::merge_error_functions(&merged, &alpha_error);
|
||||
|
||||
let bin_size = 32;
|
||||
let mut distributions = [red, green, blue, alpha].into_iter();
|
||||
|
||||
let bits = &merged.bits[bin_size];
|
||||
|
||||
core::array::from_fn(|i| {
|
||||
let fit = autoquant::models::OptimizedLin::new(distributions.next().unwrap(), (1 << bits[i]) - 1);
|
||||
let parameters = fit.parameters();
|
||||
Quantization::new(parameters[0] as f32, parameters[1] as f32, bits[i] as u32)
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_quantization_per_channel(data: Vec<f64>, samples: u32) -> Quantization {
|
||||
/*
|
||||
// TODO: make this work with generic size parameters
|
||||
fn generate_quantization<const N: usize>(data: Vec<f64>, samples: usize, channels: usize) -> [Quantization; N] {
|
||||
let mut quantizations = Vec::new();
|
||||
let mut merged_error: Option<ErrorFunction<10>> = None;
|
||||
let bin_size = 32;
|
||||
|
||||
for i in 0..channels {
|
||||
let channel_data = create_distribution(data.clone(), samples, i);
|
||||
|
||||
let fit = autoquant::calculate_error_function(&channel_data, 0, &channel_data);
|
||||
let error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit.as_slice());
|
||||
|
||||
// Merge current error function with previous ones
|
||||
merged_error = match merged_error {
|
||||
Some(prev_error) => Some(autoquant::packing::merge_error_functions(&prev_error, &error)),
|
||||
None => Some(error.clone()),
|
||||
};
|
||||
|
||||
println!("Merged: {:?}", merged_error);
|
||||
|
||||
let bits = merged_error.as_ref().unwrap().bits.iter().map(|x| x[i]).collect::<Vec<_>>();
|
||||
let model_fit = autoquant::models::OptimizedLin::new(channel_data, 1 << bits[bin_size]);
|
||||
let parameters = model_fit.parameters();
|
||||
let quantization = Quantization::new(parameters[0] as f32, parameters[1] as u32, bits[bin_size] as u32);
|
||||
|
||||
quantizations.push(quantization);
|
||||
}
|
||||
|
||||
core::array::from_fn(|x| quantizations[x])
|
||||
}*/
|
||||
|
||||
fn create_distribution(data: Vec<f64>, samples: usize, channel: usize) -> Vec<(f64, f64)> {
|
||||
let data: Vec<f64> = data.chunks(4 * (data.len() / (4 * samples.min(data.len() / 4)))).map(|x| x[channel] as f64).collect();
|
||||
let max = *data.iter().max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)).unwrap();
|
||||
let data: Vec<f64> = data.iter().map(|x| x / max).collect();
|
||||
dbg!(max);
|
||||
//let data = autoquant::generate_normal_distribution(3.0, 1.1, 1000);
|
||||
//data.iter_mut().for_each(|x| *x = x.abs());
|
||||
let mut dist = autoquant::integrate_distribution(data);
|
||||
autoquant::drop_duplicates(&mut dist);
|
||||
let dist = autoquant::normalize_distribution(dist.as_slice());
|
||||
let max = dist.last().unwrap().0;
|
||||
/*let linear = Box::new(autoquant::SimpleFitFn {
|
||||
function: move |x| x / max,
|
||||
inverse: move |x| x * max,
|
||||
name: "identity",
|
||||
});*/
|
||||
|
||||
let linear = Quantization {
|
||||
fn_index: 0,
|
||||
a: max as f32,
|
||||
b: 0.,
|
||||
c: 0.,
|
||||
d: 0.,
|
||||
};
|
||||
let log_fit = autoquant::models::OptimizedLog::new(dist, samples as u64);
|
||||
let parameters = log_fit.parameters();
|
||||
let log_fit = Quantization {
|
||||
fn_index: 1,
|
||||
a: parameters[0] as f32,
|
||||
b: parameters[1] as f32,
|
||||
c: parameters[2] as f32,
|
||||
d: parameters[3] as f32,
|
||||
};
|
||||
log_fit
|
||||
dist
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user