Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor code, fix bugs, add example to README #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Rust files
/target/

# Windows image file caches
Thumbs.db
ehthumbs.db
Expand Down
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
All notable changes to this project will be documented in this file.

## [Unreleased]
* binary treshold
* binary threshold
* histogram for rgb
* histogram for grayscale

Expand All @@ -17,4 +17,3 @@ All notable changes to this project will be documented in this file.
* huerotate
* invert
* thumbnail

16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ version = "0.1.0"
authors = ["Mikołaj Wawrzyniak <[email protected]>"]

[dependencies]
clap = "*"
clap = "2"
image = "0.15.0"


41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Image-Processing-CLI-in-Rust [![Build Status](https://travis-ci.org/spejss/Image-Processing-CLI-in-Rust.svg?branch=master)](https://travis-ci.org/spejss/Image-Processing-CLI-in-Rust)

CLI for processing images in Rust. Some implementation is custom and for some functionality it uses 3rd party libraries.

This project uses following libraries:

https://github.com/PistonDevelopers/image


Processed images are being stored in the same folder as the source image using the name of the source image with an appended suffix.

Source: hotelroom.jpg
Expand All @@ -16,43 +16,44 @@ Blur: hotelroomBlur.jpg

Generated histograms are also stored this way.


## Implemented functions
* ***Binary treshold*** -o binaryTreshold -v 200
* ***(NAIVE) Histogram for colors (RGB)*** -o histogram
* ***(NAIVE) Histogram for grayscale images*** -o histogramGrayscale
* ***Average color*** -o average
* ***Copy:*** -o copy

* ***Binary threshold*** -o binaryThreshold -v 200
* ***(NAIVE) Histogram for colors (RGB)*** -o histogram
* ***(NAIVE) Histogram for grayscale images*** -o histogramGrayscale
* ***Average color*** -o average
* ***Copy:*** -o copy
* ***Thumbnail:*** -o thumbnail -v 96
* ***blur:*** -o blur -v 4.0
* ***blur:*** -o blur -v 4.0
* ***brighten:*** -o brighten -v 10
* ***huerotate:*** -o huerotate -v 10
* ***contrast:*** -o contrast -v 20.0
* ***grayscale***: -o grayscale
* ***grayscale***: -o grayscale
* ***invert*** -o invert

## Examples
## Examples

### Copy image
```

```bash
ipcli -o copy --image "D:\Image\hotelroom.jpg"
```

### Create a thumbnail
```
### Create a thumbnail

```bash
ipcli -o thumbnail -v 96 --image "D:\Image\hotelroom.jpg"
```

### Generate a historgram of colors
```
### Generate a histogram of colors

```bash
ipcli -o histogram -i "D:\Image\hotelroom.jpg"
```

![Example histogram of colors](assets/histogram.png)




```
```text
IPCLI 0.1
Mikolaj Wawrzyniak <mikolaj.wawrzyniak at fh-dortmund.de>
Basic CLI for image processing
Expand All @@ -69,4 +70,4 @@ OPTIONS:
-o, --operation <operation> Specifies operation to be done on the image
-v, --value <value> Value for the transformation. To see what values are needed, check the
documentation.
```
```
Binary file added assets/histogram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions src/histogram.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use image::{GenericImage, ImageBuffer, Pixel, Rgb};
use io::{openImage, saveBuffer};
use std::cmp;

const WIDTH: u32 = 255;
const HEIGHT: u32 = 200;

/// Generate histogram of grayscale intensity
pub fn grayscale(i: &str) {
let operation = "HistogramGrayscale";
let img = openImage(i);

let grayscale = img.grayscale();

// create a vector for storing the number of occurrences of each intensity
let mut occurrences = vec![0; 255];

// iterate over each pixel in the image and count the occurrences of each intensity
let (width, height) = grayscale.dimensions();
for w in 0..width {
for h in 0..height {
let pixel = grayscale.get_pixel(w, h);
let rgb = pixel.to_rgb();
occurrences[rgb.data[0] as usize] += 1;
}
}

// find highest value of occurrences, so that we can use it as 100% in the histogram
let maxValue = *occurrences.iter().max().unwrap();

let mut buffer = createBuffer();
draw(&mut buffer, &occurrences, maxValue, [0, 0, 0]);

saveBuffer(&buffer, &i, &operation);
}

/// Generate histogram of RGB values
pub fn rgb(i: &str) {
let operation = "Histogram";
let img = openImage(i);

// create a vector for storing the number of occurrences of each intensity
let mut occurrencesR = vec![0; 255];
let mut occurrencesG = vec![0; 255];
let mut occurrencesB = vec![0; 255];

// iterate over each pixel in the image and count the occurrences of each intensity
let (width, height) = img.dimensions();
for w in 0..width {
for h in 0..height {
let pixel = img.get_pixel(w, h);
let rgb = pixel.to_rgb();
occurrencesR[rgb.data[0] as usize] += 1;
occurrencesG[rgb.data[1] as usize] += 1;
occurrencesB[rgb.data[2] as usize] += 1;
}
}

// find highest value of occurrences, so that we can use it as 100% in the histogram
let maxValueR = *occurrencesR.iter().max().unwrap();
let maxValueG = *occurrencesG.iter().max().unwrap();
let maxValueB = *occurrencesB.iter().max().unwrap();

let mut buffer = createBuffer();
draw(&mut buffer, &occurrencesR, maxValueR, [255, 0, 0]);
draw(&mut buffer, &occurrencesG, maxValueG, [0, 255, 0]);
draw(&mut buffer, &occurrencesB, maxValueB, [0, 0, 255]);

saveBuffer(&buffer, &i, &operation);
}

fn createBuffer() -> ImageBuffer<Rgb<u8>, Vec<u8>> {
let mut buffer = ImageBuffer::<Rgb<u8>, Vec<u8>>::new(WIDTH, HEIGHT);
for w in 0..WIDTH {
for h in 0..HEIGHT {
buffer.get_pixel_mut(w, h).data = [255, 255, 255];
}
}
buffer
}

fn draw(buffer: &mut ImageBuffer<Rgb<u8>, Vec<u8>>, values: &[u32], max: u32, data: [u8; 3]) {
for i in 0..255_u8 {
let mut height = cmp::min(
((values[usize::from(i)] as f32 / max as f32) * 200.0) as u32,
u32::from(u8::max_value()),
) as u8;
let c = (HEIGHT - 1).saturating_sub(u32::from(height));
let mut pixel = buffer.get_pixel_mut(u32::from(i), c);
pixel.data = data;
}
}
39 changes: 39 additions & 0 deletions src/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use image::{self, DynamicImage, ImageBuffer, Rgb};
use std::fs::File;

pub fn openImage(i: &str) -> DynamicImage {
image::open(i).expect("Opening image failed")
}

pub fn saveImage(img: &DynamicImage, i: &str, operation: &str) {
let outputPath = generateFilename(i, operation);
let mut out = File::create(outputPath.0).unwrap();
let format = match outputPath.1.to_lowercase().as_ref() {
"jpg" | "jpeg" => Some(image::JPEG),
"png" => Some(image::PNG),
"gif" => Some(image::GIF),
"bmp" => Some(image::BMP),
"ico" => Some(image::ICO),
_ => None,
};
if let Some(format) = format {
img.save(&mut out, format).expect("Saving image failed");
} else {
println!("Unsupported file format");
}
}

pub fn saveBuffer(buffer: &ImageBuffer<Rgb<u8>, Vec<u8>>, i: &str, operation: &str) {
let outputPath = generateFilename(i, operation);
buffer.save(outputPath.0).unwrap();
}

fn generateFilename(i: &str, operation: &str) -> (String, String) {
let mut outputPath: String = i.chars().take(i.len() - 4).collect();
let ext: String = i.chars().skip(i.len() - 3).take(3).collect();
outputPath.push_str(operation);
outputPath.push_str(".");
outputPath.push_str(&ext);
println!("Output path: {}", outputPath);
(outputPath, ext)
}
Loading