Skip to content

Commit 3d1a95d

Browse files
committed
reintroduce complex dependencies
1 parent 2e0b080 commit 3d1a95d

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

src/concurrency/parallel.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010

1111
{{#include parallel/rayon-map-reduce.md}}
1212

13+
{{#include parallel/rayon-thumbnails.md}}
14+
1315
{{#include ../links.md}}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
## Generate jpg thumbnails in parallel
2+
3+
[![rayon-badge]][rayon] [![glob-badge]][glob] [![image-badge]][image] [![cat-concurrency-badge]][cat-concurrency] [![cat-filesystem-badge]][cat-filesystem]
4+
5+
This example generates thumbnails for all .jpg files in the current directory
6+
then saves them in a new folder called `thumbnails`.
7+
8+
[`glob::glob_with`] finds jpeg files in current directory. `rayon` resizes
9+
images in parallel using [`par_iter`] calling [`DynamicImage::resize`].
10+
11+
```rust,edition2018,no_run
12+
# use error_chain::error_chain;
13+
14+
use std::path::Path;
15+
use std::fs::create_dir_all;
16+
17+
# use error_chain::ChainedError;
18+
use glob::{glob_with, MatchOptions};
19+
use image::{FilterType, ImageError};
20+
use rayon::prelude::*;
21+
22+
# error_chain! {
23+
# foreign_links {
24+
# Image(ImageError);
25+
# Io(std::io::Error);
26+
# Glob(glob::PatternError);
27+
# }
28+
# }
29+
30+
fn main() -> Result<()> {
31+
let options: MatchOptions = Default::default();
32+
let files: Vec<_> = glob_with("*.jpg", options)?
33+
.filter_map(|x| x.ok())
34+
.collect();
35+
36+
if files.len() == 0 {
37+
error_chain::bail!("No .jpg files found in current directory");
38+
}
39+
40+
let thumb_dir = "thumbnails";
41+
create_dir_all(thumb_dir)?;
42+
43+
println!("Saving {} thumbnails into '{}'...", files.len(), thumb_dir);
44+
45+
let image_failures: Vec<_> = files
46+
.par_iter()
47+
.map(|path| {
48+
make_thumbnail(path, thumb_dir, 300)
49+
.map_err(|e| e.chain_err(|| path.display().to_string()))
50+
})
51+
.filter_map(|x| x.err())
52+
.collect();
53+
54+
image_failures.iter().for_each(|x| println!("{}", x.display_chain()));
55+
56+
println!("{} thumbnails saved successfully", files.len() - image_failures.len());
57+
Ok(())
58+
}
59+
60+
fn make_thumbnail<PA, PB>(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<()>
61+
where
62+
PA: AsRef<Path>,
63+
PB: AsRef<Path>,
64+
{
65+
let img = image::open(original.as_ref())?;
66+
let file_path = thumb_dir.as_ref().join(original);
67+
68+
Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest)
69+
.save(file_path)?)
70+
}
71+
```
72+
73+
[`glob::glob_with`]: https://docs.rs/glob/*/glob/fn.glob_with.html
74+
[`par_iter`]: https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefIterator.html#tymethod.par_iter
75+
[`DynamicImage::resize`]: https://docs.rs/image/*/image/enum.DynamicImage.html#method.resize
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
## Draw fractal dispatching work to a thread pool
2+
3+
[![threadpool-badge]][threadpool] [![num-badge]][num] [![num_cpus-badge]][num_cpus] [![image-badge]][image] [![cat-concurrency-badge]][cat-concurrency][![cat-science-badge]][cat-science][![cat-rendering-badge]][cat-rendering]
4+
5+
This example generates an image by drawing a fractal from the [Julia set]
6+
with a thread pool for distributed computation.
7+
8+
<a href="https://cloud.githubusercontent.com/assets/221000/26546700/9be34e80-446b-11e7-81dc-dd9871614ea1.png"><img src="https://cloud.githubusercontent.com/assets/221000/26546700/9be34e80-446b-11e7-81dc-dd9871614ea1.png" width="150" /></a>
9+
10+
Allocate memory for output image of given width and height with [`ImageBuffer::new`].
11+
[`Rgb::from_channels`] calculates RGB pixel values.
12+
Create [`ThreadPool`] with thread count equal to number of cores with [`num_cpus::get`].
13+
[`ThreadPool::execute`] receives each pixel as a separate job.
14+
15+
[`mpsc::channel`] receives the jobs and [`Receiver::recv`] retrieves them.
16+
[`ImageBuffer::put_pixel`] uses the data to set the pixel color.
17+
[`ImageBuffer::save`] writes the image to `output.png`.
18+
19+
```rust,edition2018,no_run
20+
# use error_chain::error_chain;
21+
use std::sync::mpsc::{channel, RecvError};
22+
use threadpool::ThreadPool;
23+
use num::complex::Complex;
24+
use image::{ImageBuffer, Pixel, Rgb};
25+
#
26+
# error_chain! {
27+
# foreign_links {
28+
# MpscRecv(RecvError);
29+
# Io(std::io::Error);
30+
# Image(image::ImageError);
31+
# }
32+
# }
33+
#
34+
# // Function converting intensity values to RGB
35+
# // Based on http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm
36+
# fn wavelength_to_rgb(wavelength: u32) -> Rgb<u8> {
37+
# let wave = wavelength as f32;
38+
#
39+
# let (r, g, b) = match wavelength {
40+
# 380..=439 => ((440. - wave) / (440. - 380.), 0.0, 1.0),
41+
# 440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),
42+
# 490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),
43+
# 510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),
44+
# 580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),
45+
# 645..=780 => (1.0, 0.0, 0.0),
46+
# _ => (0.0, 0.0, 0.0),
47+
# };
48+
#
49+
# let factor = match wavelength {
50+
# 380..=419 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
51+
# 701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),
52+
# _ => 1.0,
53+
# };
54+
#
55+
# let (r, g, b) = (normalize(r, factor), normalize(g, factor), normalize(b, factor));
56+
# Rgb::from_channels(r, g, b, 0)
57+
# }
58+
#
59+
# // Maps Julia set distance estimation to intensity values
60+
# fn julia(c: Complex<f32>, x: u32, y: u32, width: u32, height: u32, max_iter: u32) -> u32 {
61+
# let width = width as f32;
62+
# let height = height as f32;
63+
#
64+
# let mut z = Complex {
65+
# // scale and translate the point to image coordinates
66+
# re: 3.0 * (x as f32 - 0.5 * width) / width,
67+
# im: 2.0 * (y as f32 - 0.5 * height) / height,
68+
# };
69+
#
70+
# let mut i = 0;
71+
# for t in 0..max_iter {
72+
# if z.norm() >= 2.0 {
73+
# break;
74+
# }
75+
# z = z * z + c;
76+
# i = t;
77+
# }
78+
# i
79+
# }
80+
#
81+
# // Normalizes color intensity values within RGB range
82+
# fn normalize(color: f32, factor: f32) -> u8 {
83+
# ((color * factor).powf(0.8) * 255.) as u8
84+
# }
85+
86+
fn main() -> Result<()> {
87+
let (width, height) = (1920, 1080);
88+
let mut img = ImageBuffer::new(width, height);
89+
let iterations = 300;
90+
91+
let c = Complex::new(-0.8, 0.156);
92+
93+
let pool = ThreadPool::new(num_cpus::get());
94+
let (tx, rx) = channel();
95+
96+
for y in 0..height {
97+
let tx = tx.clone();
98+
pool.execute(move || for x in 0..width {
99+
let i = julia(c, x, y, width, height, iterations);
100+
let pixel = wavelength_to_rgb(380 + i * 400 / iterations);
101+
tx.send((x, y, pixel)).expect("Could not send data!");
102+
});
103+
}
104+
105+
for _ in 0..(width * height) {
106+
let (x, y, pixel) = rx.recv()?;
107+
img.put_pixel(x, y, pixel);
108+
}
109+
let _ = img.save("output.png")?;
110+
Ok(())
111+
}
112+
```
113+
114+
[`ImageBuffer::new`]: https://docs.rs/image/*/image/struct.ImageBuffer.html#method.new
115+
[`ImageBuffer::put_pixel`]: https://docs.rs/image/*/image/struct.ImageBuffer.html#method.put_pixel
116+
[`ImageBuffer::save`]: https://docs.rs/image/*/image/struct.ImageBuffer.html#method.save
117+
[`mpsc::channel`]: https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html
118+
[`num_cpus::get`]: https://docs.rs/num_cpus/*/num_cpus/fn.get.html
119+
[`Receiver::recv`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html#method.recv
120+
[`Rgb::from_channels`]: https://docs.rs/image/*/image/struct.Rgb.html#method.from_channels
121+
[`ThreadPool`]: https://docs.rs/threadpool/*/threadpool/struct.ThreadPool.html
122+
[`ThreadPool::execute`]: https://docs.rs/threadpool/*/threadpool/struct.ThreadPool.html#method.execute
123+
124+
[Julia set]: https://en.wikipedia.org/wiki/Julia_set

src/concurrency/threads.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010

1111
{{#include thread/threadpool-walk.md}}
1212

13+
{{#include thread/threadpool-fractal.md}}
14+
1315
{{#include ../links.md}}

0 commit comments

Comments
 (0)