Skip to content

Commit

Permalink
Merge pull request #3 from mlhoutel/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
mlhoutel authored Jun 28, 2022
2 parents 89061ac + 27a9d0a commit f9c1b1d
Show file tree
Hide file tree
Showing 19 changed files with 272 additions and 101 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

*An online pipeline for image processing.*

![screenshot](docs/screenshot.png)

```markdown
*carbaseus (feminine carbasea, neuter carbaseum)* : first/second-declension adjective

Expand Down
3 changes: 0 additions & 3 deletions docs/README.md

This file was deleted.

20 changes: 10 additions & 10 deletions docs/carbaseus.js
Original file line number Diff line number Diff line change
Expand Up @@ -1419,24 +1419,24 @@ function getImports() {
const ret = wasm.memory;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1436 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 504, __wbg_adapter_28);
imports.wbg.__wbindgen_closure_wrapper1469 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 523, __wbg_adapter_28);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1437 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 504, __wbg_adapter_31);
imports.wbg.__wbindgen_closure_wrapper1470 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 523, __wbg_adapter_31);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1439 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 504, __wbg_adapter_34);
imports.wbg.__wbindgen_closure_wrapper1472 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 523, __wbg_adapter_34);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1442 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 504, __wbg_adapter_34);
imports.wbg.__wbindgen_closure_wrapper1475 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 523, __wbg_adapter_34);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1655 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 623, __wbg_adapter_39);
imports.wbg.__wbindgen_closure_wrapper1688 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 640, __wbg_adapter_39);
return addHeapObject(ret);
};

Expand Down
Binary file modified docs/carbaseus_bg.wasm
Binary file not shown.
Binary file added docs/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ impl eframe::App for App {

egui::SidePanel::left("side_panel").show(ctx, |ui| layout::side_pannel::show(state, ui));

egui::SidePanel::right("output_pannel")
.show(ctx, |ui| layout::output_pannel::show(state, ui));

egui::CentralPanel::default().show(ctx, |ui| layout::central_pannel::show(state, ui, ctx));

if state.first_loop {
Expand Down
38 changes: 38 additions & 0 deletions src/app/components/display/image_spectrum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::collections::HashMap;

const COLOR_MIN: u8 = 0;
const COLOR_MAX: u8 = 255;
const SCALE: f64 = 0.02;

pub fn show(ui: &mut egui::Ui, spectrum: &[HashMap<u8, usize>]) {
egui::plot::Plot::new("Image spectrum")
.legend(egui::plot::Legend::default())
.data_aspect(1.0)
.show(ui, |plot_ui| {
for (index, c_map) in spectrum.iter().enumerate() {
let (color, name) = match index {
0 => (egui::Color32::RED, "Red spectrum"),
1 => (egui::Color32::GREEN, "Green spectrum"),
_ => (egui::Color32::BLUE, "Blue spectrum"),
};

let chart = egui::plot::BarChart::new(
(COLOR_MIN..COLOR_MAX)
.map(|intensity| {
if let Some(&count) = c_map.get(&intensity) {
(intensity, count as f64)
} else {
(intensity, 0.0_f64)
}
})
// The 10 factor here is purely for a nice 1:1 aspect ratio
.map(|(x, f)| egui::plot::Bar::new(x as f64, f * SCALE).width(1.0))
.collect(),
)
.color(color)
.name(name);

plot_ui.bar_chart(chart)
}
});
}
1 change: 1 addition & 0 deletions src/app/components/display/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod image_frame;
pub mod image_spectrum;
23 changes: 20 additions & 3 deletions src/app/components/graph/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ const LABEL_SLICE_B_OUT: &str = "slice_b_out";
const LABEL_SLICE_S_OUT: &str = "slice_s_out";

const LABEL_SCALAR_SIGMA_IN: &str = "scalar_sigma";
// const LABEL_COLOR_OUT: &str = "color_out";
// const LABEL_SCALAR_OUT: &str = "scala_out";
const _LABEL_COLOR_OUT: &str = "color_out";
const _LABEL_SCALAR_OUT: &str = "scala_out";

pub type _Node = egui_node_graph::Node<NodeData>;
pub type NodeId = egui_node_graph::id_type::NodeId;
pub type OutputId = egui_node_graph::id_type::OutputId;
pub type _InputId = egui_node_graph::id_type::InputId;

// ========= First, define your user data types =============

Expand Down Expand Up @@ -416,14 +421,26 @@ impl NodeDataTrait for NodeData {
if let Some(image) = user_state.outputs_images.get(id) {
egui::CollapsingHeader::new(format!("Image for {label}"))
.default_open(first_header)
.show(ui, |ui| display::image_frame::show(ui, image));
.show(ui, |ui| {
display::image_frame::show(ui, image);
});

first_header = false;
}
}
}
responses
}

fn titlebar_color(
&self,
_ui: &egui::Ui,
_node_id: NodeId,
_graph: &Graph<Self, Self::DataType, Self::ValueType>,
_user_state: &Self::UserState,
) -> Option<egui::Color32> {
None
}
}

pub type ProcessGraph = Graph<NodeData, DataType, ValueType>;
Expand Down
1 change: 0 additions & 1 deletion src/app/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod display;
pub mod graph;
pub mod input;
pub mod output;
35 changes: 0 additions & 35 deletions src/app/components/output/image_output.rs

This file was deleted.

1 change: 0 additions & 1 deletion src/app/components/output/mod.rs

This file was deleted.

25 changes: 22 additions & 3 deletions src/app/layout/central_pannel.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use egui::TextStyle;

use crate::app::components::graph::node::*;
use crate::app::state;
use crate::app::state::{self, SelectedNode};

use egui_node_graph::NodeResponse;

Expand Down Expand Up @@ -34,6 +34,25 @@ pub fn show(state: &mut state::AppState, ui: &mut egui::Ui, ctx: &egui::Context)
evaluate_graph(&mut state.graph)
};
}

// Check if we need to update the current selected node
responses.node_responses.iter().for_each(|&event| {
if let NodeResponse::SelectNode(node_id) = event {
state.selected_node = SelectedNode::default(); // reset node
state.selected_node.node_id = Some(node_id);
}
});

// Check if the current node was removed
responses.node_responses.iter().for_each(|&event| {
if let NodeResponse::DeleteNode(deleted_node) = event {
if let Some(current_node) = state.selected_node.node_id {
if current_node == deleted_node {
state.selected_node = SelectedNode::default(); // reset node
}
}
}
});
}

pub fn show_state(state: &mut state::AppState, _ui: &mut egui::Ui, ctx: &egui::Context) {
Expand Down Expand Up @@ -100,8 +119,8 @@ pub fn show_background(ui: &mut egui::Ui) {
let size = ui.available_size();
let start = egui::pos2(next.x + spacing * 0.25, next.y + spacing * 0.25);

let number_x = (size.x / spacing).ceil() as i32 + 1;
let number_y = (size.y / spacing).ceil() as i32 + 1;
let number_x = (size.x / spacing).floor() as i32 + 1;
let number_y = (size.y / spacing).floor() as i32 + 1;

for x in 0..number_x {
for y in 0..number_y {
Expand Down
1 change: 1 addition & 0 deletions src/app/layout/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod central_pannel;
pub mod output_pannel;
pub mod side_pannel;
pub mod top_bar;
121 changes: 121 additions & 0 deletions src/app/layout/output_pannel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use egui_extras::RetainedImage;
use std::collections::HashMap;

use crate::app::components::display;
use crate::app::components::graph::node::{self, *};
use crate::app::math::image::slice_to_image;
use crate::app::state;

pub fn show(state: &mut state::AppState, ui: &mut egui::Ui) {
egui::ScrollArea::vertical().show(ui, |ui| {
if let Some(selected_id) = state.selected_node.node_id {
let selected_node = state
.graph
.graph
.nodes
.iter()
.find(|&(node_id, _node)| node_id == selected_id);

// Check if the node exist in the graph
if let Some((_, node)) = selected_node {
let outputs = node.output_ids();

// Try to find a node output that correspond to an image
let find_image = outputs
.into_iter()
.find(|id| state.graph.user_state.outputs_images.contains_key(id));

// If the image was found, we update the value
if let Some(output_id) = find_image {
show_selected(state, ui, output_id);
}
}
} else {
show_not_selected(ui);
}
});
}

fn show_not_selected(ui: &mut egui::Ui) {
ui.allocate_ui_with_layout(
ui.available_size(),
egui::Layout::centered_and_justified(egui::Direction::TopDown),
|ui| ui.label("Select a node to preview it"),
);
}

fn show_selected(state: &mut state::AppState, ui: &mut egui::Ui, output_id: OutputId) {
// Extract the color image from the results
if state.selected_node.color_image.is_none() {
state.selected_node.color_image = match state.graph.user_state.outputs_cache.get(&output_id)
{
Some(node::ValueType::Image { value }) => Some(value.clone()),
Some(node::ValueType::Slice { value }) => Some(slice_to_image(value)),
_ => None,
};

// If the color image was just initialized
if let Some(color_image) = &state.selected_node.color_image {
// We compute and store the image spectrum
for px in &color_image.pixels {
for i in 0..3 {
let intensity = px[i];

if intensity == 0 {
continue; // dont take into account the intensity 0
}

if let Some(&count) = &mut state.selected_node.spectrum[i].get(&intensity) {
state.selected_node.spectrum[i].insert(intensity, count + 1);
} else {
state.selected_node.spectrum[i].insert(intensity, 1);
}
}
}
}
}

// Compute a new retained image
if state.selected_node.retained_image.is_none() {
if let Some(color_image) = &state.selected_node.color_image {
state.selected_node.retained_image = Some(RetainedImage::from_color_image(
"selected node retained image",
(*color_image).clone(),
))
}
}

if let Some(retained_image) = &state.selected_node.retained_image {
if let Some(_color_image) = &state.selected_node.color_image {
ui.horizontal(|ui| {
ui.selectable_value(
&mut state.o_pannel,
state::OutputPanel::Image,
"Image display",
);
ui.selectable_value(
&mut state.o_pannel,
state::OutputPanel::Spectrum,
"Spectrum graph",
);
});

ui.separator();

match state.o_pannel {
state::OutputPanel::Image => show_image_display(ui, retained_image),
state::OutputPanel::Spectrum => {
show_image_spectrum(ui, &state.selected_node.spectrum)
}
}
}
}
}

fn show_image_display(ui: &mut egui::Ui, image: &RetainedImage) {
display::image_frame::show(ui, image);
}

fn show_image_spectrum(ui: &mut egui::Ui, spectrum: &[HashMap<u8, usize>]) {
display::image_spectrum::show(ui, spectrum);
}
2 changes: 1 addition & 1 deletion src/app/math/fft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub fn row_fft(buffer: &mut Vec<Complex<f32>>) {
fft.process(buffer);
}
// Swap opposite diagonal quadrants
pub fn shift_fft(buffer: &mut Vec<Complex<f32>>, row_size: usize, col_size: usize) {
pub fn shift_fft(buffer: &mut [Complex<f32>], row_size: usize, col_size: usize) {
let h_half = (row_size as f32) / 2.0; // horizontal half
let v_half = (col_size as f32) / 2.0; // vertical half

Expand Down
18 changes: 18 additions & 0 deletions src/app/math/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,24 @@ pub fn image_to_gray(image: &ColorImage) -> ImageSlice {
luma_to_slice(output_image)
}

pub fn slice_to_image(slice: &ImageSlice) -> ColorImage {
let mut image = ColorImage::new(slice.size, Color32::from_rgb(0, 0, 0));
let cols: Vec<Color32> = slice
.clone()
.pixels
.into_iter()
.map(|px| match slice.color {
SliceColor::Red => Color32::from_rgb(px, 0, 0),
SliceColor::Green => Color32::from_rgb(0, px, 0),
SliceColor::Blue => Color32::from_rgb(0, 0, px),
SliceColor::Gray => Color32::from_rgb(px, px, px),
})
.collect();
image.pixels = cols;

image
}

// Image blur
pub fn image_blur(image: &ColorImage, sigma: f32) -> ColorImage {
let temp_image = egui_to_image(image.clone());
Expand Down
Loading

0 comments on commit f9c1b1d

Please sign in to comment.