Skip to content
This repository has been archived by the owner on Oct 28, 2023. It is now read-only.

Add --flips-and-rotates flag #57

Merged
merged 6 commits into from
Oct 28, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 21 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use structopt::StructOpt;

use std::path::PathBuf;
use texture_synthesis::{
image::ImageOutputFormat as ImgFmt, Dims, Error, Example, ImageSource, SampleMethod, Session,
image::ImageOutputFormat as ImgFmt, Dims, Error, Example, ImageSource, SampleMethod, Session, Transformation,
};

fn parse_size(input: &str) -> Result<Dims, std::num::ParseIntError> {
Expand Down Expand Up @@ -118,6 +118,9 @@ struct Tweaks {
/// Enables tiling of the output image
#[structopt(long = "tiling")]
enable_tiling: bool,
/// Flips and rotates traning images
#[structopt(long = "flips-and-rotates")]
enable_flips_and_rotates: bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider this "flips and rotates mode" different enough to warrant being in its own subcommand, as I don't really see it making sense to also add inpainting and tiling, as you kind of get a tiling effect anyway (in a way).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider this "flips and rotates mode" different enough to warrant being in its own subcommand

Just to double check (since you mention subcommand), you mean something like?

#[derive(StructOpt)]
enum Subcommand {
    /// Transfers the style from an example onto a target guide
    #[structopt(name = "transfer-style")]
    TransferStyle(TransferStyle),
    /// Generates a new image from 1 or more examples
    #[structopt(name = "generate")]
    Generate(Generate),
    /// Generates a new image from 1 or more flipped and rotated examples
    #[structopt(name = "flips-and-rotates")]
    FlipsAndRotates(FlipsAndRotates),
}

or do you mean something else, like moving enable_flips_and_rotates to struct Generate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, like that. Not sure about the naming though, but it can be changed later if anyone comes up with a clearer and/or shorter name. 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 5fdab54

}

#[derive(StructOpt)]
Expand Down Expand Up @@ -213,6 +216,23 @@ fn real_main() -> Result<(), Error> {
}
}

if args.tweaks.enable_flips_and_rotates {
let mut transformed_examples: Vec<_> = vec![];
for example in &examples {
let mut new_examples: Vec<_> = vec![
example.clone().with_transformations(vec![Transformation::FlipH]).clone(),
example.clone().with_transformations(vec![Transformation::Rot90]).clone(),
example.clone().with_transformations(vec![Transformation::FlipH, Transformation::Rot90]).clone(),
example.clone().with_transformations(vec![Transformation::Rot180]).clone(),
example.clone().with_transformations(vec![Transformation::FlipH, Transformation::Rot180]).clone(),
example.clone().with_transformations(vec![Transformation::Rot270]).clone(),
example.clone().with_transformations(vec![Transformation::FlipH, Transformation::Rot270]).clone(),
];
transformed_examples.append(&mut new_examples);
}
examples.append(&mut transformed_examples);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this whole part can be made a lot simpler by just calling load_image for the example and doing the transforms here. That way, you don't actually need to change anything in the lib crate at all, as the CLI is the only part that needs to know that the example inputs need to be transformed, and ImageSource already supports just passing a raw image.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2d0c83b

(examples, gen.target_guide.as_ref())
}
Subcommand::TransferStyle(ts) => (vec![Example::new(&ts.style)], Some(&ts.guide)),
Expand Down
34 changes: 27 additions & 7 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ mod unsync;

pub use image;
pub use utils::ImageSource;
pub use utils::Transformation;

pub use errors::Error;

Expand Down Expand Up @@ -90,7 +91,7 @@ impl<'a> CoordinateTransform {
) -> Result<image::RgbaImage, Error> {
let ref_maps: Vec<image::RgbaImage> = source
.into_iter()
.map(|t| load_image(t.into(), None))
.map(|t| load_image(t.into(), None, vec![]))
.collect::<Result<Vec<_>, Error>>()?;
//assert same number of maps
if ref_maps.len() as u32 != self.max_map_id {
Expand Down Expand Up @@ -237,6 +238,7 @@ impl AsRef<image::RgbaImage> for GeneratedImage {
}

/// Method used for sampling an example image.
#[derive(Clone)]
pub enum SampleMethod<'a> {
/// All pixels in the example image can be sampled.
All,
Expand Down Expand Up @@ -287,6 +289,7 @@ pub struct ExampleBuilder<'a> {
img: ImageSource<'a>,
guide: Option<ImageSource<'a>>,
sample_method: SampleMethod<'a>,
transformations: Vec<Transformation>,
}

impl<'a> ExampleBuilder<'a> {
Expand All @@ -296,6 +299,7 @@ impl<'a> ExampleBuilder<'a> {
img: img.into(),
guide: None,
sample_method: SampleMethod::All,
transformations: vec![],
}
}

Expand All @@ -315,6 +319,12 @@ impl<'a> ExampleBuilder<'a> {
self.sample_method = method.into();
self
}

/// Specify the transformations to apply to the example image.
pub fn with_transformations(mut self, transformations: Vec<Transformation>) -> Self {
self.transformations = transformations;
self
}
}

impl<'a> Into<Example<'a>> for ExampleBuilder<'a> {
Expand All @@ -323,15 +333,18 @@ impl<'a> Into<Example<'a>> for ExampleBuilder<'a> {
img: self.img,
guide: self.guide,
sample_method: self.sample_method,
transformations: self.transformations,
}
}
}

/// An example to be used in texture generation
#[derive(Clone)]
pub struct Example<'a> {
img: ImageSource<'a>,
guide: Option<ImageSource<'a>>,
sample_method: SampleMethod<'a>,
transformations: Vec<Transformation>,
}

impl<'a> Example<'a> {
Expand All @@ -346,6 +359,7 @@ impl<'a> Example<'a> {
img: img.into(),
guide: None,
sample_method: SampleMethod::All,
transformations: vec![],
}
}

Expand All @@ -366,19 +380,25 @@ impl<'a> Example<'a> {
self
}

/// Specify the transformations to apply to the example image.
pub fn with_transformations(&mut self, transformations: Vec<Transformation>) -> &mut Self {
self.transformations = transformations;
self
}

fn resolve(
self,
backtracks: u32,
resize: Option<Dims>,
target_guide: &Option<ImagePyramid>,
) -> Result<ResolvedExample, Error> {
let image = ImagePyramid::new(load_image(self.img, resize)?, Some(backtracks));
let image = ImagePyramid::new(load_image(self.img, resize, self.transformations)?, Some(backtracks));

let guide = match target_guide {
Some(tg) => {
Some(match self.guide {
Some(exguide) => {
let exguide = load_image(exguide, resize)?;
let exguide = load_image(exguide, resize, vec![])?;
ImagePyramid::new(exguide, Some(backtracks))
}
None => {
Expand All @@ -397,7 +417,7 @@ impl<'a> Example<'a> {
SampleMethod::All => SamplingMethod::All,
SampleMethod::Ignore => SamplingMethod::Ignore,
SampleMethod::Image(src) => {
let img = load_image(src, resize)?;
let img = load_image(src, resize, vec![])?;
SamplingMethod::Image(img)
}
};
Expand Down Expand Up @@ -626,8 +646,8 @@ impl<'a> SessionBuilder<'a> {

let (inpaint, out_size, in_size) = match self.inpaint_mask {
Some((src, ind, size)) => {
let inpaint_mask = load_image(src, Some(size))?;
let color_map = load_image(self.examples[ind].img.clone(), Some(size))?;
let inpaint_mask = load_image(src, Some(size), vec![])?;
let color_map = load_image(self.examples[ind].img.clone(), Some(size), vec![])?;

(
Some(InpaintExample {
Expand All @@ -644,7 +664,7 @@ impl<'a> SessionBuilder<'a> {

let target_guide = match self.target_guide {
Some(tg) => {
let tg_img = load_image(tg, Some(out_size))?;
let tg_img = load_image(tg, Some(out_size), vec![])?;

let num_guides = self.examples.iter().filter(|ex| ex.guide.is_some()).count();
let tg_img = if num_guides == 0 {
Expand Down
40 changes: 35 additions & 5 deletions lib/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
use crate::{Dims, Error};
use std::path::Path;

/// Helper type used to transform image sources
#[derive(Clone)]
pub enum Transformation {
/// Flips an image horizontally
FlipH,
/// Flips an image vertically
FlipV,
/// Rotates the image by 90 degrees (clockwise)
Rot90,
/// Rotates the image by 180 degrees (clockwise)
Rot180,
/// Rotates the image by 270 degrees (clockwise)
Rot270,
}

/// Helper type used to pass image data to the Session
#[derive(Clone)]
pub enum ImageSource<'a> {
Expand Down Expand Up @@ -29,16 +44,31 @@ where
}
}

pub(crate) fn load_image(
pub(crate) fn get_dynamic_image(
src: ImageSource<'_>,
resize: Option<Dims>,
) -> Result<image::RgbaImage, Error> {
let img = match src {
) -> Result<image::DynamicImage, image::ImageError> {
return match src {
ImageSource::Memory(data) => image::load_from_memory(data),
ImageSource::Path(path) => image::open(path),
ImageSource::Image(img) => Ok(img),
}?;
};
}

pub(crate) fn load_image(
src: ImageSource<'_>,
resize: Option<Dims>,
transformations: Vec<Transformation>,
) -> Result<image::RgbaImage, Error> {
let mut img = get_dynamic_image(src)?;
for t in transformations {
match t {
Transformation::FlipH => img = img.fliph(),
Transformation::FlipV => img = img.flipv(),
Transformation::Rot90 => img = img.rotate90(),
Transformation::Rot180 => img = img.rotate180(),
Transformation::Rot270 => img = img.rotate270(),
}
}
Ok(match resize {
None => img.to_rgba(),
Some(ref size) => {
Expand Down