Add ability to generate SVG image based on image.
This commit is contained in:
parent
bb496e61a5
commit
9a1837896e
237
Cargo.lock
generated
237
Cargo.lock
generated
@ -6,6 +6,32 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
|
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -36,6 +62,21 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color_quant"
|
name = "color_quant"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -125,6 +166,15 @@ dependencies = [
|
|||||||
"lzw",
|
"lzw",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
@ -268,9 +318,13 @@ dependencies = [
|
|||||||
name = "perler"
|
name = "perler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"image",
|
"image",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"structopt",
|
||||||
|
"svg",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -285,6 +339,50 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"syn-mid",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -322,6 +420,93 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt"
|
||||||
|
version = "0.3.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"lazy_static",
|
||||||
|
"structopt-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt-derive"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svg"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b65a64d32a41db2a8081aa03c1ccca26f246ff681add693f8b01307b137da79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn-mid"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -332,3 +517,55 @@ dependencies = [
|
|||||||
"lzw",
|
"lzw",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|||||||
@ -9,6 +9,10 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
image = "0.23.6"
|
image = "0.23.6"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
svg = "0.8.0"
|
||||||
|
structopt = "0.3.15"
|
||||||
|
thiserror = "1.0.20"
|
||||||
|
anyhow = "1.0.31"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|||||||
75
src/lib.rs
75
src/lib.rs
@ -1,5 +1,80 @@
|
|||||||
use image::{DynamicImage, GenericImage, GenericImageView};
|
use image::{DynamicImage, GenericImage, GenericImageView};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use svg::node::element::Group;
|
||||||
|
use svg::node::element::{Circle, Rectangle};
|
||||||
|
use svg::node::Node;
|
||||||
|
use svg::Document;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
const GRID_SIZE: (usize, usize) = (29, 29);
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PerlerError {
|
||||||
|
#[error("source image too big {image_size:?} > {max_size:?}")]
|
||||||
|
TooBig {
|
||||||
|
image_size: (usize, usize),
|
||||||
|
max_size: (usize, usize),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn svgify(input: &DynamicImage) -> Result<impl Node, PerlerError> {
|
||||||
|
let dim = input.dimensions();
|
||||||
|
let (w, h) = (dim.0 as usize, dim.1 as usize);
|
||||||
|
if w > GRID_SIZE.0 || h > GRID_SIZE.1 {
|
||||||
|
return Err(PerlerError::TooBig {
|
||||||
|
image_size: (w, h),
|
||||||
|
max_size: GRID_SIZE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let box_offset = (5, 5);
|
||||||
|
let mut grp = Group::new();
|
||||||
|
for y in 0..GRID_SIZE.0 {
|
||||||
|
for x in 0..GRID_SIZE.1 {
|
||||||
|
let x_pos = box_offset.0 + 5 + x * 5;
|
||||||
|
let y_pos = box_offset.1 + 5 + y * 5;
|
||||||
|
let c = Circle::new()
|
||||||
|
.set("stroke", "black")
|
||||||
|
.set("stroke-width", 0.1)
|
||||||
|
.set("cx", x_pos)
|
||||||
|
.set("cy", y_pos);
|
||||||
|
|
||||||
|
let c = if x < w && y < h {
|
||||||
|
let p = input.get_pixel(x as u32, y as u32);
|
||||||
|
let [r, g, b, a] = p.0;
|
||||||
|
if a == 255 {
|
||||||
|
let color = format!("#{:02x}{:02x}{:02x}", r, g, b);
|
||||||
|
c.set("fill", color).set("r", 2.4)
|
||||||
|
} else {
|
||||||
|
c.set("fill", "none").set("r", 0.5)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.set("fill", "none").set("r", 0.5)
|
||||||
|
};
|
||||||
|
|
||||||
|
grp = grp.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let board = Rectangle::new()
|
||||||
|
.set("fill", "none")
|
||||||
|
.set("width", 150)
|
||||||
|
.set("height", 150)
|
||||||
|
.set("stroke", "black")
|
||||||
|
.set("stroke-width", 0.1)
|
||||||
|
.set("x", box_offset.0)
|
||||||
|
.set("y", box_offset.1)
|
||||||
|
.set("rx", 1)
|
||||||
|
.set("ry", 1);
|
||||||
|
Ok(Document::new()
|
||||||
|
.set("width", "100%")
|
||||||
|
.set("height", "100%")
|
||||||
|
.set(
|
||||||
|
"viewBox",
|
||||||
|
(0, 0, (3 + GRID_SIZE.0) * 5, (3 + GRID_SIZE.1) * 5),
|
||||||
|
)
|
||||||
|
//.set("viewBox", (0, 0, 215.9, 279.4))
|
||||||
|
.add(board)
|
||||||
|
.add(grp))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn simplify(input: &DynamicImage) -> DynamicImage {
|
pub fn simplify(input: &DynamicImage) -> DynamicImage {
|
||||||
let (w, h) = input.dimensions();
|
let (w, h) = input.dimensions();
|
||||||
|
|||||||
47
src/main.rs
47
src/main.rs
@ -1 +1,46 @@
|
|||||||
fn main() {}
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
//use image::imageops::colorops::{index_colors, BiLevel};
|
||||||
|
//use image::math::nq::NeuQuant;
|
||||||
|
//use image::DynamicImage;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use perler::{simplify, svgify};
|
||||||
|
|
||||||
|
/// Convert image to SVG in the style of perler instructions.
|
||||||
|
#[derive(StructOpt, Debug)]
|
||||||
|
#[structopt(name = "perler")]
|
||||||
|
struct Opt {
|
||||||
|
/// Input image file.
|
||||||
|
input: PathBuf,
|
||||||
|
/// Optional output .svg file. Default prints to stdout.
|
||||||
|
output: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
let img = image::open(opt.input)?;
|
||||||
|
/*
|
||||||
|
let mut img2 = img.to_rgba();
|
||||||
|
let raw = img.into_rgba().into_raw();
|
||||||
|
|
||||||
|
let cmap = NeuQuant::new(1, 256, &raw);
|
||||||
|
index_colors(&mut img2, &cmap);
|
||||||
|
|
||||||
|
let img = DynamicImage::ImageRgba8(img2.into());
|
||||||
|
*/
|
||||||
|
let img = simplify(&img);
|
||||||
|
let out_svg = svgify(&img)?;
|
||||||
|
let w: Box<dyn Write> = if let Some(path) = opt.output {
|
||||||
|
Box::new(File::create(path)?)
|
||||||
|
} else {
|
||||||
|
Box::new(io::stdout())
|
||||||
|
};
|
||||||
|
svg::write(w, &out_svg)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user