Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gpu node refactor #41

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 172 additions & 42 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions Cargo.toml
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ dhat-heap = ["dhat"]
track-drop = ["backtrace"]

[dependencies]
clap = { version = "3.1.18", features = ["default", "derive"] }
clap = { version = "4.1.8", features = ["default", "derive", "string"] }
indicatif = "0.17.0"
glob = "0.3.0"
anyhow = "1.0.57"
anyhow = { version = "1.0.57", features = ["backtrace"] }
itertools = "0.10.3"
bytemuck = "1.9.1"
vulkano = "0.30.0"
Expand Down Expand Up @@ -44,12 +44,20 @@ vulkano_maybe_molten = "0.30.1"
pollster = "0.2.5"
anymap = "1.0.0-beta.2"
parking_lot = { version = "0.12.1", features = ["send_guard"] }
dng = { version = "1.5.0", features = ["yaml"] }
dng = { version = "1.5.1", features = ["yaml"] }
dav-server = { version = "0.5.2", features = ["hyper"] }
tokio = { version = "1.19.2", features = ["full"] }
hyper = { version = "0.14.23", features = ["full"] }
futures-util = "0.3.25"
portpicker = "0.1.1"
shaderc = "0.8.2"
indoc = "2.0.0"
spirv-reflect = "0.2.3"
regex = "1.7.1"
textwrap = "0.16.0"

[dev-dependencies]
once_cell = "1.17.1"

[target.'cfg(target_os = "linux")'.dependencies]
v4l = "0.13.0"
Expand Down
187 changes: 157 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,144 @@ A GUI tool for doing recording in a more convenient way is planned but not imple
Processing pipelines can either be specified directly on the cli:

```sh
$ target/debug/cli from-cli --help
cli-from-cli
target/debug/cli from-cli --help
specify the processing pipeline directly on the cli

USAGE:
cli from-cli [PIPELINE]...
Usage: cli from-cli [PIPELINE]...

ARGS:
<PIPELINE>... example: <Node1> --source-arg ! <Node2> --sink-arg
Arguments:
[PIPELINE]... example: <Node1> --source-arg ! <Node2> --sink-arg

OPTIONS:
-h, --help Print help information
Options:
-h, --help Print help

NODES:
* Average [OPTIONS] --n <n>
* BenchmarkSink [OPTIONS]
* BitDepthConverter
* Cache [OPTIONS]
* Calibrate --height <height> --darkframe <darkframe> --width <width>
* CinemaDngFrameserver [OPTIONS]
* CinemaDngReader [OPTIONS] --file-pattern <file-pattern>
* CinemaDngWriter [OPTIONS] --path <path>
* ColorVoodoo [OPTIONS]
* BenchmarkSink
--priority <priority> [default: 0]

* Cache
--size <size> [default: 1]

* Calibrate
--height <height>
--width <width>
--darkframe <darkframe>

* CinemaDngFrameserver
--priority <priority> [default: 0]
--dcp-yaml <dcp-yaml>
--host <host> [default: 127.0.0.1]
--port <port>

* CinemaDngReader
--internal-loop <internal-loop>
--cache-frames <cache-frames>
--file-pattern <file-pattern>

* CinemaDngWriter
--number-of-frames <number-of-frames>
--path <path>
--dcp-yaml <dcp-yaml>
--priority <priority> [default: 0]

* ColorVoodoo
--pedestal <pedestal> [default: 0]
--s_gamma <s_gamma> [default: 1]
--v_gamma <v_gamma> [default: 1]

* Debayer
* DualFrameRawDecoder [OPTIONS]
* FfmpegWriter [OPTIONS] --output <output>
* GpuBitDepthConverter

* DualFrameRawDecoder
--bayer <bayer> [default: RGBG]
--debug <debug>

* FfmpegWriter
--input-options <input-options> [default: ]
--output <output>
--priority <priority> [default: 0]

* Histogram
* Lut3d --file <file>
* RawBlobReader [OPTIONS] --height <height> --width <width> --file <file>
* RawBlobWriter [OPTIONS] --path <path>
* RawDirectoryReader [OPTIONS] --height <height> --width <width> --file-pattern <file-pattern>
* RawDirectoryWriter [OPTIONS] --path <path>
* ReverseDualFrameRawDecoder [OPTIONS]
* SZ3Compress [OPTIONS] --data_type <data_type> --tolerance <tolerance> --error_control <error_control>
* Split --element <element>
* TcpReader [OPTIONS] --width <width> --height <height> --address <address>
* ZstdBlobReader [OPTIONS] --file <file> --width <width> --height <height>

* Lut3d
--file <file>

* NullFrameSource
--fps <fps> [default: 24]
--height <height>
--bayer <bayer> [default: RGGB]
--uint-bits <uint-bits>
--fp32
--rgb
--fp16
--width <width>
--rgba

* RawBlobReader
--cache-frames <cache-frames>
--fp16
--fp32
--uint-bits <uint-bits>
--width <width>
--bayer <bayer> [default: RGGB]
--height <height>
--rgb
--fps <fps> [default: 24]
--rgba
--file <file>

* RawBlobWriter
--path <path>
--priority <priority> [default: 0]
--number-of-frames <number-of-frames>

* RawDirectoryReader
--fps <fps> [default: 24]
--height <height>
--fp32
--bayer <bayer> [default: RGGB]
--cache-frames <cache-frames>
--internal-loop <internal-loop>
--fp16
--uint-bits <uint-bits>
--rgb
--file-pattern <file-pattern>
--width <width>
--rgba

* RawDirectoryWriter
--number-of-frames <number-of-frames>
--priority <priority> [default: 0]
--path <path>

* ReverseDualFrameRawDecoder
--flip <flip>

* Split
--element <element>

* TcpReader
--fps <fps> [default: 24]
--address <address>
--rgb
--uint-bits <uint-bits>
--fp16
--width <width>
--height <height>
--fp32
--bayer <bayer> [default: RGGB]
--rgba

* ZstdBlobReader
--uint-bits <uint-bits>
--fps <fps> [default: 24]
--rgba
--file <file>
--height <height>
--bayer <bayer> [default: RGGB]
--rgb
--fp16
--fp32
--width <width>
```

Alternatively you can use the yaml based config file, for example:
Expand Down Expand Up @@ -89,6 +189,33 @@ display:
```
The config file supports variable substitution. You can set name value pairs on the cli using `--set name=value`.

### Frame interpretations
Some Nodes e.g. `RawBlobReader` or `RawDirectoryReader` need to know how to interpret the data. For this some things need to be specified:

* A `--width` and a `--height`
* A frame rate with `--fps`
* A format for the samples (pixels / color channels of the pixels) needs to be specified: Either `--fp16`, `--fp32` or `--uint-bits N` where `N` is the number of bits per sample (e.g. 12 for the AXIOM Beta).
* A color format. This can either be `--rgb`, `--rgba` or `--bayer PATTERN`. Bayer Patterns are specified as left to right top to bottom as a 2x2 pixel string indicating the color. E.g. `GRBG` is the following cfa pattern
| Green | Red | Green | Red | ... |
|-------|-------|-------|-------|-----|
| Blue | Green | Blue | Green | ... |
| Green | Red | Green | Red | ... |
| Blue | Green | Blue | Green | ... |
| ... | ... | ... | ... | ... |


These parameters only specify the interpretation but no conversions. So if you want to get an RGB image from a Bayer source you still need to take care of conversion with a `Debayer` node in between.

### DNG output
In the pipeline of the AXIOM raw recorder, sometimes images in floating point format can occur
(for example when averaging multiple frames). Although the DNG specification specifies floating
point DNG files, not a lot of software actually supports it. To avoid issues with downstream
processing software you can convert your data to uint16 before writing DNG files. Example:

```shell
$ target/release/cli from-cli RawBlobReader --fp32 --width 3840 --height 2160 --bayer GBRG --file Halogen_Xrite_0_8ms.raw32 ! Fp32ToUInt16 --multiplier 16 ! CinemaDngWriter --path out
```

## Examples

Convert a directory of raw12 files from the Beta to mp4 (h264) using FFmpeg:
Expand Down
82 changes: 30 additions & 52 deletions src/bin/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::{anyhow, Context, Result};
use clap::{Arg, Parser};
use clap::{Arg, ArgAction, Parser};

use indicatif::{ProgressBar, ProgressStyle};
use itertools::Itertools;
Expand All @@ -17,6 +17,7 @@ use std::{
iter::once,
sync::{Arc, Mutex},
};
use textwrap::indent;

#[derive(Deserialize, Debug)]
struct PipelineConfig {
Expand Down Expand Up @@ -57,13 +58,7 @@ enum Command {
/// path to the configuration file
file: std::path::PathBuf,
/// variables to substitute in the config file
#[clap(
short = 's',
long = "set",
name = "key=value",
allow_hyphen_values(true),
takes_value = true
)]
#[clap(short = 's', long = "set", name = "key=value", allow_hyphen_values(true))]
vars: Vec<String>,
},
}
Expand Down Expand Up @@ -161,17 +156,17 @@ fn nodes_usages_string() -> String {
.keys()
.sorted()
.map(|node_name| {
Box::leak(Box::new(
clap_app_from_node_name(node_name)
Box::leak(Box::new(indent(
&clap_app_from_node_name(node_name)
.unwrap()
.help_template(" * {usage}")
.no_binary_name(true)
.try_get_matches_from(once::<&str>("--help"))
.unwrap_err()
.help_template("* {name}\n{options}")
.disable_help_flag(true)
.render_help()
.to_string(),
))
" ",
)))
})
.join("")
.join("\n")
}
fn processing_node_from_commandline(
commandline: &[String],
Expand All @@ -183,7 +178,7 @@ fn processing_node_from_commandline(
let node_descriptor: &ParameterizableDescriptor =
available_nodes.get(name).ok_or_else(|| {
anyhow!(
"cant find node with name {}. avalable nodes are: \n{}",
"cant find node with name {}. available nodes are: \n{}",
name,
nodes_usages_string()
)
Expand All @@ -204,15 +199,10 @@ fn processing_node_from_commandline(
Mandatory(NodeInputParameter) | WithDefault(NodeInputParameter, _)
)
})
.map(|(key, parameter_type)| {
Ok((
key.to_string(),
parameter_type
.parse(results.value_of(key))
.context(format!("parameter is {}", key))?,
))
.filter_map(|(key, _parameter_type)| {
results.get_one::<ParameterValue>(key).map(|v| (key.clone(), v.clone()))
})
.collect::<Result<_, anyhow::Error>>()?;
.collect();

Ok(ProcessingNodeConfig::single_input_node(
name.to_string(),
Expand All @@ -223,18 +213,18 @@ fn processing_node_from_commandline(

fn leak<T: Clone>(s: &T) -> &'static T { Box::leak(Box::new(s.clone())) }

fn clap_app_from_node_name(name: &str) -> Result<clap::Command<'static>> {
fn clap_app_from_node_name(name: &str) -> Result<clap::Command> {
let available_nodes: HashMap<String, ParameterizableDescriptor> = list_available_nodes();
let node_descriptor: &ParameterizableDescriptor =
available_nodes.get(name).ok_or_else(|| {
anyhow!(
"cant find node with name {}. avalable nodes are: {:?}",
"cant find node with name {}. available nodes are: {:?}",
name,
available_nodes.keys()
)
})?;

let mut app = clap::Command::new(node_descriptor.name.clone());
let mut app = clap::Command::new(&node_descriptor.name);
if let Some(description) = node_descriptor.description.clone() {
app = app.about(leak(&description).as_str());
}
Expand All @@ -244,31 +234,19 @@ fn clap_app_from_node_name(name: &str) -> Result<clap::Command<'static>> {
if let Mandatory(NodeInputParameter) | WithDefault(NodeInputParameter, _) = parameter_type {
continue;
};
let parameter_type_for_closure = parameter_type.clone();
let value_parser = parameter_type.get_parameter_type().clone();
let arg = Arg::new(key).long(key);
app = app.arg(match parameter_type {
Mandatory(_) => Arg::new(leak(&key).as_str())
.long(key)
.takes_value(true)
.allow_hyphen_values(true)
.validator(move |v| {
parameter_type_for_closure
.parse(Some(v))
.map(|_| ())
.map_err(|e| format!("{}", e))
})
.required(true),
WithDefault(_, default) => Arg::new(key.as_str())
.long(key)
.takes_value(true)
.allow_hyphen_values(true)
.validator(move |v| {
parameter_type_for_closure
.parse(Some(v))
.map(|_| ())
.map_err(|e| format!("{}", e))
})
.default_value(Box::leak(Box::new(default.to_string())))
.required(false),
Mandatory(_) => arg.required(true).value_parser(value_parser).allow_hyphen_values(true),
WithDefault(BoolParameter, BoolValue(false)) => {
arg.required(false).action(ArgAction::SetTrue).value_parser(value_parser)
}
WithDefault(_, default) => arg
.default_value(default.to_string())
.required(false)
.value_parser(value_parser)
.allow_hyphen_values(true),
Optional(_) => arg.required(false).value_parser(value_parser).allow_hyphen_values(true),
})
}
Ok(app)
Expand Down
Loading