Skip to content

Commit f19a958

Browse files
authored
Merge pull request #25 from blenderskool/cli
Pigmnts CLI 0.1.2
2 parents a0a6fc9 + e7647df commit f19a958

File tree

8 files changed

+125
-36
lines changed

8 files changed

+125
-36
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
**/*.rs.bk
3-
pkg
3+
pkg
4+
*.cbor

Cargo.lock

Lines changed: 22 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pigmnts-cli"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
authors = ["Akash Hamirwasia"]
55
edition = "2018"
66
description = "Generate a color palette from an image using WebAssesmbly"
@@ -16,6 +16,12 @@ termion = "1.5.5"
1616
prettytable-rs = "0.8.0"
1717
pigmnts = { path = "lib" }
1818
reqwest = { version = "0.10", features = ["blocking"] }
19+
serde_cbor = "0.11.1"
20+
lazy_static = "^1.4.0"
21+
22+
[build-dependencies]
23+
serde_json = "^1.0.52"
24+
serde_cbor = "^0.11.1"
1925

2026
[[bin]]
2127
name = "pigmnts"

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ FLAGS:
3333
-l, --lab Enable L*AB output of colors
3434
-q, --quiet Suppress the normal output [aliases: silent]
3535
-r, --rgb Enable RGB output of colors
36+
-n, --name Nearest name for the color
3637
-V, --version Prints version information
3738
3839
OPTIONS:
@@ -47,10 +48,14 @@ OPTIONS:
4748
- `pigmnts pic-1.jpg -rxdl`
4849
Generate a palette of 5 colors from pic-1.jpg and show the RGB code, hex code, dominance, LAB code for each color in the palette.
4950

51+
- `pigmnts pic-1.jpg --count 10 --name --hex`
52+
Generate a palette of 10 colors from pic-1.jpg and show the name, hex code for each color in the palette.
53+
5054
- `pigmnts pic-1.jpg pic-2.jpg -sxq`
5155
Generate a palette of 5 colors from pic-1.jpg and pic-2.jpg. For each color in the palette show the HSL code, hex code in `quiet` mode.
5256

5357

58+
5459
## Contributing
5560
This repository is a Cargo workspace that includes the development of both core Pigmnts library and the CLI.
5661

@@ -60,4 +65,5 @@ This repository is a Cargo workspace that includes the development of both core
6065

6166

6267
## License
63-
Pigmnts is [MIT Licensed](https://github.com/blenderskool/pigmnts/blob/master/LICENSE.md)
68+
Pigmnts is [MIT Licensed](https://github.com/blenderskool/pigmnts/blob/master/LICENSE.md)
69+
The dataset for color names used in Pigmnts CLI is taken from https://github.com/meodai/color-names

build.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
extern crate serde_cbor;
2+
extern crate serde_json;
3+
4+
use std::fs::File;
5+
6+
fn main() {
7+
// This dataset has been taken from https://github.com/meodai/color-names
8+
let color_names_file = File::open("./data/colornames.bestof.min.json").unwrap();
9+
let color_names: serde_json::Value = serde_json::from_reader(color_names_file).unwrap();
10+
11+
let dest_file = File::create("./data/colornames.cbor").unwrap();
12+
serde_cbor::to_writer(dest_file, &color_names).unwrap();
13+
}

data/colornames.bestof.min.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

src/main.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ extern crate termion;
77
extern crate image;
88
extern crate pigmnts;
99
extern crate reqwest;
10+
#[macro_use]
11+
extern crate lazy_static;
12+
13+
pub mod utils;
1014

1115
use clap::{App, Arg};
1216
use spinners::{Spinner, Spinners};
@@ -17,7 +21,7 @@ use image::GenericImageView;
1721
use pigmnts::{Pixels, color::{LAB, RGB, HSL}, weights, pigments_pixels};
1822

1923
/// Creates a vector of strings with elements added conditonally
20-
///
24+
///
2125
/// # Example
2226
/// ```
2327
/// let myvec = conditional_vec![
@@ -40,11 +44,11 @@ macro_rules! conditional_vec {
4044
}
4145

4246
/// Creates a color palette from image
43-
///
47+
///
4448
/// Image is loaded from `image_path` and a palette of `count` colors are created
4549
fn pigmnts(image_path: &str, count: u8) -> Result<(Vec<(LAB, f32)>, u128), Box<dyn std::error::Error>> {
4650
let mut img;
47-
51+
4852
if image_path.starts_with("http://") || image_path.starts_with("https://") {
4953
let mut res = reqwest::blocking::get(image_path)?;
5054
let mut buf: Vec<u8> = vec![];
@@ -54,28 +58,27 @@ fn pigmnts(image_path: &str, count: u8) -> Result<(Vec<(LAB, f32)>, u128), Box<d
5458
else {
5559
img = image::open(image_path)?;
5660
}
57-
61+
5862
img = img.resize(512, 512, image::imageops::FilterType::CatmullRom);
59-
63+
6064
// Start a timer
6165
let now = Instant::now();
62-
66+
6367
let pixels: Pixels = img
6468
.pixels()
6569
.map(|(_, _, pix)| LAB::from_rgb(pix[0], pix[1], pix[2]))
6670
.collect();
67-
71+
6872
let weightfn = weights::resolve_mood(&weights::Mood::Dominant);
6973
let mut output = pigments_pixels(&pixels, count, weightfn, None);
70-
74+
7175
// Sort the output colors based on dominance
7276
output.sort_by(|(_, a), (_, b)| b.partial_cmp(a).unwrap());
73-
77+
7478
return Ok((output, now.elapsed().as_millis()));
7579
}
7680

7781
fn main() {
78-
7982
let matches = App::new("Pigmnts")
8083
.version(env!("CARGO_PKG_VERSION"))
8184
.author(env!("CARGO_PKG_AUTHORS"))
@@ -119,22 +122,27 @@ fn main() {
119122
.short("d")
120123
.long("dominance")
121124
.help("Enable dominance percentage of colors"))
125+
.arg(Arg::with_name("name")
126+
.short("n")
127+
.long("name")
128+
.help("Enable nearest names of colors"))
122129
.get_matches();
123-
130+
124131
let image_paths = matches.values_of("input").unwrap();
125132
let mut counts = values_t!(matches, "count", u8).unwrap_or(Vec::new());
126133
let is_quiet = matches.is_present("quiet");
127134
let is_rgb = matches.is_present("rgb");
128135
let is_hsl = matches.is_present("hsl");
129136
let is_lab = matches.is_present("lab");
130137
let is_dom = matches.is_present("dominance");
138+
let is_name = matches.is_present("name");
131139
let mut is_hex = matches.is_present("hex");
132-
140+
133141
// Hex format is enabled when other formats are disabled
134142
if !is_rgb && !is_hsl & !is_lab {
135143
is_hex = true;
136144
}
137-
145+
138146
// Fill the default count value (5) for every input file if not specified
139147
loop {
140148
let diff: i8 = image_paths.len() as i8 - counts.len() as i8;
@@ -144,40 +152,41 @@ fn main() {
144152
break;
145153
}
146154
}
147-
155+
148156
// Enumerate through each image_path and generate palettes
149157
for (i, image_path) in image_paths.enumerate() {
150-
158+
151159
if is_quiet {
152160
// Quiet mode only shows the result separated by ':'
153-
161+
154162
let (result, _) = pigmnts(image_path, counts[i])
155163
.unwrap_or_else(|err| {
156164
eprintln!("Problem creating palette: {}", err);
157165
process::exit(1);
158166
});
159-
167+
160168
for (color, dominance) in result.iter() {
161169
let rgb = RGB::from(color);
162-
170+
163171
let record = conditional_vec![
164172
is_hex => rgb.hex(),
165173
is_rgb => rgb,
166174
is_hsl => HSL::from(color),
167175
is_lab => color,
168-
is_dom => dominance * 100.0
176+
is_dom => dominance * 100.0,
177+
is_name => utils::near_color_name(color)
169178
];
170-
179+
171180
println!("{}", record.join(":"));
172181
}
173-
182+
174183
} else {
175-
184+
176185
print!("{}{}Creating a palette of ", color::Fg(color::White), style::Bold);
177186
print!("{}{} ", color::Fg(color::Blue), counts[i]);
178187
print!("{}colors from ", color::Fg(color::White));
179188
println!("{}{}{}", color::Fg(color::Blue), image_path, style::Reset);
180-
189+
181190
// Show the spinner in the terminal
182191
let sp = Spinner::new(Spinners::Dots, String::default());
183192
let (result, time) = pigmnts(image_path, counts[i])
@@ -191,11 +200,11 @@ fn main() {
191200
);
192201
process::exit(1);
193202
});
194-
203+
195204
// Stop the spinner
196205
sp.stop();
197206
println!();
198-
207+
199208
let mut table = Table::new();
200209
table.set_format(
201210
format::FormatBuilder::from(*format::consts::FORMAT_CLEAN)
@@ -204,6 +213,7 @@ fn main() {
204213
);
205214
let titles = conditional_vec![
206215
true => "", // Title for color preview
216+
is_name => "Name",
207217
is_hex => "Hex",
208218
is_rgb => "RGB",
209219
is_hsl => "HSL",
@@ -218,10 +228,11 @@ fn main() {
218228
.collect()
219229
)
220230
);
221-
231+
222232
for (color, dominance) in result.iter() {
223233
let rgb = RGB::from(color);
224234
let values = conditional_vec![
235+
is_name => utils::near_color_name(color),
225236
is_hex => rgb.hex(),
226237
is_rgb => rgb,
227238
is_hsl => HSL::from(color),
@@ -232,16 +243,16 @@ fn main() {
232243
// Color preview is added
233244
format!("{} {}", color::Bg(color::Rgb(rgb.r, rgb.g, rgb.b)), style::Reset)
234245
];
235-
246+
236247
for value in values.iter() {
237248
record.add_cell(cell!(value));
238249
}
239-
250+
240251
table.add_row(record);
241252
}
242253
table.printstd();
243254
println!();
244-
255+
245256
println!(
246257
"{}{}✓ Success!{} Took {}ms",
247258
color::Fg(color::Green),
@@ -251,5 +262,4 @@ fn main() {
251262
);
252263
}
253264
}
254-
255265
}

src/utils.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
extern crate serde_cbor;
2+
3+
use pigmnts::color::{LAB, RGB};
4+
use std::collections::HashMap;
5+
6+
/// Coverts a hex string to RGB color
7+
fn hex_to_rgb(s: &str) -> RGB {
8+
let hex = s.replace("#", "").to_lowercase();
9+
10+
let hex_num = usize::from_str_radix(&hex, 16).unwrap();
11+
return RGB {
12+
r: (hex_num >> 16) as u8,
13+
g: ((hex_num >> 8) & 0x00FF) as u8,
14+
b: (hex_num & 0x0000_00FF) as u8,
15+
};
16+
}
17+
18+
lazy_static! {
19+
static ref COLOR_NAMES: (Vec<String>, Vec<LAB>) = {
20+
let data: HashMap<String, String> = serde_cbor::from_slice(include_bytes!("../data/colornames.cbor")).unwrap();
21+
let values: Vec<LAB> = data
22+
.iter()
23+
.map(|(val, _)| LAB::from(&hex_to_rgb(val)))
24+
.collect();
25+
26+
return (data.values().cloned().collect(), values);
27+
};
28+
}
29+
30+
/// Returns nearest name of a color
31+
pub fn near_color_name(color: &LAB) -> &String {
32+
return &COLOR_NAMES.0[color.nearest(&COLOR_NAMES.1).0];
33+
}

0 commit comments

Comments
 (0)