Skip to content

Commit

Permalink
bump to py38
Browse files Browse the repository at this point in the history
  • Loading branch information
jungerm2 committed Jun 9, 2024
1 parent 983b457 commit 4254953
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 532 deletions.
785 changes: 343 additions & 442 deletions Cargo.lock

Large diffs are not rendered by default.

32 changes: 15 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "photoncube2video"
version = "0.3.5"
version = "0.3.8"
edition = "2021"

[lib]
Expand All @@ -12,35 +12,33 @@ crate-type = ["cdylib", "lib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.79"
clap = { version = "4.4.18", features = ["derive", "cargo"] }
anyhow = "1.0.86"
clap = { version = "4.5.6", features = ["derive", "cargo"] }
conv = "0.3.3"
ffmpeg-sidecar = "0.5.1"
glob = "0.3.1"
image = "0.24.8"
image = "0.24.9"
imageproc = "0.23.0"
indicatif = {version = "*", features = ["rayon"]}
itertools = "0.12.0"
itertools = "0.13.0"
memmap2 = "0.9.4"
natord = "1.0.9"
ndarray = { version = "0.15.6", features = ["rayon", "approx"] }
ndarray-npy = "0.8.1"
ndarray-stats = "0.5.1"
noisy_float = "0.2.0"
nshare = "0.9.0"
num-traits = "0.2.17"
rayon = "1.8.1"
num-traits = "0.2.19"
rayon = "1.10.0"
rusttype = "0.9.3"
tempfile = "3.9.0"

# Pin pyo3 to 0.15.2 as it's the last version to support py36, see: https://github.com/PyO3/maturin/pull/945
pyo3 = { version = "0.15.2", features = ["extension-module", "anyhow", "abi3-py36"] }

numpy = "0.15.1"
strum_macros = "0.26.1"
strum = { version = "0.26.1", features = ["derive"] }
paste = "1.0.14"
fastrand = "2.0.1"
tempfile = "3.10.1"
pyo3 = { version = "0.21.2", features = ["extension-module", "anyhow"] }

numpy = "0.21.0"
strum_macros = "0.26.4"
paste = "1.0.15"
fastrand = "2.1.0"
strum = { version = "0.26.2", features = ["strum_macros"] }

[package.metadata.cargo-machete]
ignored = ["strum"]
Expand Down
14 changes: 13 additions & 1 deletion photoncube2video.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
from enum import Enum, auto
from typing import List, Tuple, Optional
from typing_extensions import Self

import numpy as np

class Transform(Enum):
Identity = auto()
Rot90 = auto()
Rot180 = auto()
Rot270 = auto()
FlipUD = auto()
FlipLR = auto()

def from_str(transform_name: str) -> Self: ...


class PhotonCube:
path: str
cfa_mask: Optional[np.ndarray]
Expand Down Expand Up @@ -32,7 +44,7 @@ class PhotonCube:
def set_range(
self: Self, start: int, end: Optional[int] = None, step: Optional[int] = None
) -> None: ...
def set_transforms(self: Self, transforms: List[str]) -> None: ...
def set_transforms(self: Self, transforms: List[Transform]) -> None: ...
def set_quantile(self: Self, quantile: Optional[float]) -> None: ...
def save_images(
img_dir,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "photoncube2video"
requires-python = ">=3.6"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
Expand Down
14 changes: 9 additions & 5 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use clap::{Args, Parser, Subcommand};
use pyo3::prelude::*;

use crate::{cube::PhotonCube, signals::DeferedSignal, transforms::Transform};
use crate::{cube::PhotonCube, signals::DeferredSignal, transforms::Transform};

/// Convert a photon cube (npy file/directory of bin files) between formats or to
/// a video preview (mp4) by naively averaging frames.
Expand Down Expand Up @@ -141,18 +141,22 @@ pub struct ProcessArgs {
pub output: String,
}

fn load_cube(args: &PreviewProcessCommonArgs, step: Option<usize>, quantile: Option<f32>) -> Result<PhotonCube> {
fn load_cube(
args: &PreviewProcessCommonArgs,
step: Option<usize>,
quantile: Option<f32>,
) -> Result<PhotonCube> {
// Load all the neccesary files
let mut cube = PhotonCube::open(&args.input)?;
if let Some(cfa_path) = &args.cfa_path {
cube.load_cfa(&cfa_path)?;
cube.load_cfa(cfa_path)?;
}
for inpaint_path in args.inpaint_path.iter() {
cube.load_mask(inpaint_path)?;
}
cube.set_range(args.start.unwrap_or(0), args.end, step);
cube.set_transforms(args.transform.clone());
cube.set_quantile(quantile.clone());
cube.set_quantile(quantile);
Ok(cube)
}

Expand Down Expand Up @@ -212,7 +216,7 @@ pub fn cli_entrypoint(py: Python) -> Result<()> {
// Start by telling python to not intercept CTRL+C signal,
// Otherwise we won't get it here and will not be interruptable.
// See: https://github.com/PyO3/pyo3/pull/3560
let _defer = DeferedSignal::new(py, "SIGINT")?;
let _defer = DeferredSignal::new(py, "SIGINT")?;

// Parse arguments defined in struct
// Since we're actually calling this via python, the first argument
Expand Down
67 changes: 28 additions & 39 deletions src/cube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::{
io::Read,
ops::BitOr,
path::Path,
str::FromStr,
};

use anyhow::{anyhow, Error, Result};
Expand All @@ -14,15 +13,14 @@ use itertools::multizip;
use memmap2::{Mmap, MmapMut};
use ndarray::{prelude::*, Array, ArrayView3, Axis, Slice};
use ndarray_npy::{read_npy, write_zeroed_npy, ViewMutNpyExt, ViewNpyError, ViewNpyExt};
use nshare::ToNdarray2;
use numpy::{PyArray2, ToPyArray};
use numpy::{PyArray2, PyArrayMethods, ToPyArray};
use pyo3::{prelude::*, types::PyType};
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use tempfile::tempdir;

use crate::{
ffmpeg::{ensure_ffmpeg, make_video},
signals::DeferedSignal,
signals::DeferredSignal,
transforms::{
annotate, apply_transforms, array2_to_grayimage, binary_avg_to_rgb, gray_to_rgbimage,
grayimage_to_array2, interpolate_where_mask, linearrgb_to_srgb, pack_single,
Expand Down Expand Up @@ -52,9 +50,10 @@ pub struct PhotonCube {
#[pyo3(get, set)]
pub step: Option<usize>,

#[pyo3(get, set)]
#[pyo3(get)]
pub quantile: Option<f32>,

#[pyo3(get)]
pub transforms: Vec<Transform>,

_storage: Mmap,
Expand Down Expand Up @@ -287,10 +286,7 @@ impl PhotonCube {
let arr: Array2<bool> = read_npy(path)?;
Ok(arr)
} else {
let arr = ImageReader::open(path)?
.decode()?
.into_luma8()
.into_ndarray2()
let arr = grayimage_to_array2(ImageReader::open(path)?.decode()?.into_luma8())
.mapv(|v| v != 255);
Ok(arr)
}
Expand Down Expand Up @@ -449,7 +445,7 @@ impl PhotonCube {
"Step must be set before virtual exposures can be created!"
));
}
create_dir_all(&img_dir).ok();
create_dir_all(img_dir).ok();

// Create virtual exposures iterator over all data
let view = self.view()?;
Expand Down Expand Up @@ -508,6 +504,7 @@ impl PhotonCube {
}

/// Save all virtual exposures as a video (and optionally images).
#[warn(clippy::too_many_arguments)]
pub fn save_video(
&self,
output: &str,
Expand All @@ -521,16 +518,16 @@ impl PhotonCube {
// Get img path or tempdir, ensure it exists.
let tmp_dir = tempdir()?;
let img_dir = img_dir.unwrap_or(tmp_dir.path().to_str().unwrap());
create_dir_all(&img_dir).ok();
create_dir_all(img_dir).ok();

// Generate preview frames
let num_frames = self.save_images(&img_dir, process_fn, annotate_frames, message)?;
let num_frames = self.save_images(img_dir, process_fn, annotate_frames, message)?;

// Assemble them into a video
ensure_ffmpeg(true);
make_video(
Path::new(&img_dir).join("frame%06d.png").to_str().unwrap(),
&output,
output,
fps,
num_frames as u64,
message,
Expand All @@ -551,7 +548,7 @@ impl PhotonCube {
/// entirely loading the photoncube into memory. Convert to `.npy` first.
#[classmethod]
#[pyo3(name = "open", text_signature = "(path)")]
pub fn open_py(_: &PyType, path: &str) -> Result<Self> {
pub fn open_py(_: &Bound<'_, PyType>, path: &str) -> Result<Self> {
Self::open(path)
}

Expand All @@ -571,7 +568,7 @@ impl PhotonCube {
is_full_array: bool,
message: Option<&str>,
) -> PyResult<()> {
let _defer = DeferedSignal::new(py, "SIGINT")?;
let _defer = DeferredSignal::new(py, "SIGINT")?;
Self::convert_to_npy(src, dst, is_full_array, message).map_err(|e| e.into())
}

Expand All @@ -589,7 +586,7 @@ impl PhotonCube {
grayspad_fix: Option<bool>,
message: Option<&str>,
) -> Result<(usize, usize, usize)> {
let _defer = DeferedSignal::new(py, "SIGINT")?;
let _defer = DeferredSignal::new(py, "SIGINT")?;
self.process_cube(
dst,
colorspad_fix.unwrap_or(false),
Expand All @@ -604,7 +601,7 @@ impl PhotonCube {
self.inpaint_mask
.as_ref()
.map(|a| -> Result<Py<PyAny>> {
let py_arr = a.to_pyarray(py).to_owned().into_py(py);
let py_arr = a.to_pyarray_bound(py).to_owned().into_py(py);
py_arr
.getattr(py, "setflags")?
.call1(py, (false, None::<bool>, None::<bool>))?;
Expand All @@ -614,8 +611,8 @@ impl PhotonCube {
}

#[setter(inpaint_mask)]
pub fn inpaint_mask_setter(&mut self, arr: Option<&PyArray2<bool>>) -> Result<()> {
self.inpaint_mask = arr.map(|a| a.to_owned_array());
pub fn inpaint_mask_setter(&mut self, arr: Option<&Bound<'_, PyArray2<bool>>>) -> Result<()> {
self.inpaint_mask = arr.map(|a| a.to_owned().to_owned_array());
Ok(())
}

Expand All @@ -625,7 +622,7 @@ impl PhotonCube {
self.cfa_mask
.as_ref()
.map(|a| -> Result<Py<PyAny>> {
let py_arr = a.to_pyarray(py).to_owned().into_py(py);
let py_arr = a.to_pyarray_bound(py).to_owned().into_py(py);
py_arr
.getattr(py, "setflags")?
.call1(py, (false, None::<bool>, None::<bool>))?;
Expand All @@ -635,8 +632,8 @@ impl PhotonCube {
}

#[setter(cfa_mask)]
pub fn cfa_mask_setter(&mut self, arr: Option<&PyArray2<bool>>) -> Result<()> {
self.cfa_mask = arr.map(|a| a.to_owned_array());
pub fn cfa_mask_setter(&mut self, arr: Option<&Bound<'_, PyArray2<bool>>>) -> Result<()> {
self.cfa_mask = arr.map(|a| a.to_owned().to_owned_array());
Ok(())
}

Expand Down Expand Up @@ -674,26 +671,17 @@ impl PhotonCube {
/// sequentially and can thus be composed (i.e: Rot90+Rot90=Rot180).
/// Options are: "Identity", "Rot90", "Rot180", "Rot270", "FlipUD", "FlipLR"
#[pyo3(name = "set_transforms", text_signature = "(transforms)")]
pub fn set_transforms_py(&mut self, transforms: Vec<&str>) -> Result<()> {
let transforms = transforms
.iter()
.map(|t| Transform::from_str(t))
.collect::<Result<Vec<_>, _>>();
self.transforms = transforms.map_err(|_| {
anyhow!(
"Invalid transforms encountered. Expected one or more of \
'Identity', 'Rot90', 'Rot180', 'Rot270', 'FlipUD', 'FlipLR'."
)
})?;
pub fn set_transforms_py(&mut self, transforms: Vec<Transform>) -> Result<()> {
self.transforms = transforms;
Ok(())
}

/// Set quantile to use when inverting SPAD response, this is only used
/// when `invert_response` is set to True. Quantile must be in 0-1 range.
/// Set quantile to use when inverting SPAD response, this is only used
/// when `invert_response` is set to True. Quantile must be in 0-1 range.
#[pyo3(name = "set_quantile", text_signature = "(quantile=None)")]
pub fn set_quantile_py(&mut self, quantile: Option<f32>) -> Result<()> {
if let Some(qtl) = quantile {
if qtl < 0.0 || qtl > 1.0 {
if !(0.0..=1.0).contains(&qtl) {
return Err(anyhow!("Quantile value must be in 0-1 range."));
}
}
Expand All @@ -710,6 +698,7 @@ impl PhotonCube {
text_signature = "(img_dir, invert_response=False, tonemap2srgb=False, \
colorspad_fix=False, grayspad_fix=False, annotate_frames=False, message=None)"
)]
#[warn(clippy::too_many_arguments)]
pub fn save_images_py(
&self,
py: Python,
Expand All @@ -721,15 +710,15 @@ impl PhotonCube {
annotate_frames: Option<bool>,
message: Option<&str>,
) -> Result<isize> {
let _defer = DeferedSignal::new(py, "SIGINT")?;
let _defer = DeferredSignal::new(py, "SIGINT")?;
let process = self.process_single(
invert_response.unwrap_or(false),
tonemap2srgb.unwrap_or(false),
colorspad_fix.unwrap_or(false),
grayspad_fix.unwrap_or(false),
)?;
self.save_images(
&img_dir,
img_dir,
Some(process),
annotate_frames.unwrap_or(false),
message,
Expand Down Expand Up @@ -757,7 +746,7 @@ impl PhotonCube {
annotate_frames: Option<bool>,
message: Option<&str>,
) -> Result<isize> {
let _defer = DeferedSignal::new(py, "SIGINT")?;
let _defer = DeferredSignal::new(py, "SIGINT")?;
let process = self.process_single(
invert_response.unwrap_or(false),
tonemap2srgb.unwrap_or(false),
Expand Down
2 changes: 1 addition & 1 deletion src/ffmpeg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn make_video(
}
s
})
.unwrap_or(vec![]);
.unwrap_or_default();

let cmd = format!(
// Scale to a max width of 1280 pixels as long as the height is divisible by 2
Expand Down
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ pub mod transforms;
pub mod utils;

use pyo3::prelude::*;
use transforms::Transform;

use crate::{cli::__pyo3_get_function_cli_entrypoint, cube::PhotonCube};
use crate::{cli::cli_entrypoint, cube::PhotonCube};

#[pymodule]
fn photoncube2video(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn photoncube2video(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(cli_entrypoint, m)?)?;
m.add_class::<PhotonCube>()?;
m.add_wrapped(wrap_pyfunction!(cli_entrypoint))?;
m.add_class::<Transform>()?;

Ok(())
}
Loading

0 comments on commit 4254953

Please sign in to comment.