diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3c44241 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore index 8e5d337..82676ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Rust files +/target/ + # Windows image file caches Thumbs.db ehthumbs.db diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd9682..16d8ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -17,4 +17,3 @@ All notable changes to this project will be documented in this file. * huerotate * invert * thumbnail - diff --git a/Cargo.lock b/Cargo.lock index 443c83b..39a9167 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,11 +1,3 @@ -[root] -name = "ipcli" -version = "0.1.0" -dependencies = [ - "clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "adler32" version = "1.0.1" @@ -141,6 +133,14 @@ name = "inflate" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ipcli" +version = "0.1.0" +dependencies = [ + "clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "jpeg-decoder" version = "0.1.13" diff --git a/Cargo.toml b/Cargo.toml index ba438da..2665644 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,5 @@ version = "0.1.0" authors = ["MikoĊ‚aj Wawrzyniak "] [dependencies] -clap = "*" +clap = "2" image = "0.15.0" - - diff --git a/README.md b/README.md index c52fc1c..8c95791 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 Basic CLI for image processing @@ -69,4 +70,4 @@ OPTIONS: -o, --operation Specifies operation to be done on the image -v, --value Value for the transformation. To see what values are needed, check the documentation. - ``` +``` diff --git a/assets/histogram.png b/assets/histogram.png new file mode 100644 index 0000000..720375e Binary files /dev/null and b/assets/histogram.png differ diff --git a/src/histogram.rs b/src/histogram.rs new file mode 100644 index 0000000..f3786c9 --- /dev/null +++ b/src/histogram.rs @@ -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, Vec> { + let mut buffer = ImageBuffer::, Vec>::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, Vec>, 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; + } +} diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..cdccf8f --- /dev/null +++ b/src/io.rs @@ -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, Vec>, 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) +} diff --git a/src/main.rs b/src/main.rs index fe32ea4..ff780fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,405 +1,175 @@ +#![allow(non_snake_case)] + extern crate clap; extern crate image; -use clap::{Arg,App,SubCommand}; -use std::fs::File; -use image::{FilterType, GenericImage, Pixel,ImageBuffer,Rgb}; -use image::DynamicImage; -use std::path::Path; -use std::collections::HashMap; +mod histogram; +mod io; + +use clap::{App, Arg}; +use image::{FilterType, GenericImage, Pixel, Rgb}; +use io::{openImage, saveBuffer, saveImage}; fn main() { - let matches = App::new("IPCLI ") - .version("0.1") - .author("Mikolaj Wawrzyniak ") - .about("Basic CLI for image processing") - .arg(Arg::with_name("operation") - .short("o") - .long("operation") - .help("Specifies operation to be done on the image") - .takes_value(true) - .required(true)) - .arg(Arg::with_name("value") - .short("v") - .long("value") - .help("Value for the transformation. To see what values are needed, check the documentation.") - .takes_value(true) - .required(false)) - .arg(Arg::with_name("image") - .short("i") - .long("image") - .value_name("FILE") - .help("Opens specified image file and uses it for transformations.") - .takes_value(true) - .required(true)) - .get_matches(); + let matches = App::new("IPCLI") + .version("0.1") + .author("Mikolaj Wawrzyniak ") + .about("Basic CLI for image processing") + .arg(Arg::with_name("operation") + .short("o") + .long("operation") + .help("Specifies operation to be done on the image") + .takes_value(true) + .required(true)) + .arg(Arg::with_name("value") + .short("v") + .long("value") + .help("Value for the transformation. To see what values are needed, check the documentation.") + .takes_value(true) + .required(false)) + .arg(Arg::with_name("image") + .short("i") + .long("image") + .value_name("FILE") + .help("Opens specified image file and uses it for transformations.") + .takes_value(true) + .required(true)) + .get_matches(); let imagePath = matches.value_of("image").unwrap_or("empty"); - //println!("Transforming the image: {}", imagePath); - let operation = matches.value_of("operation").unwrap_or("empty"); - //println!("Using operation: {}", operation); - let value = matches.value_of("value").unwrap_or("empty"); - //println!("Value: {}", value); - match operation.as_ref(){ - "average" => {averageColor(imagePath)} - "copy" => {copy(imagePath)} + match operation { + "average" => averageColor(imagePath), + "copy" => copy(imagePath), "thumbnail" => { let size: u32 = value.parse().unwrap(); - createThumnbail(imagePath, size)} + createThumbnail(imagePath, size) + } "blur" => { - let v : f32 = value.parse().unwrap(); - gaussianBlur(imagePath,v)} + let v: f32 = value.parse().unwrap(); + gaussianBlur(imagePath, v) + } "brighten" => { let v: i32 = value.parse().unwrap(); - brighten(imagePath,v)} + brighten(imagePath, v) + } "huerotate" => { let v: i32 = value.parse().unwrap(); - huerotate(imagePath,v)} + huerotate(imagePath, v) + } "contrast" => { let v: f32 = value.parse().unwrap(); - contrast(imagePath,v);} + contrast(imagePath, v); + } "grayscale" => { grayscale(imagePath); } "invert" => { invert(imagePath); } - "histogramGrayscale" => {histogramGrayscale(imagePath)} - "histogram" => {histogram(imagePath)} - "binaryTreshold" => { + "histogramGrayscale" => histogram::grayscale(imagePath), + "histogram" => histogram::rgb(imagePath), + "binaryThreshold" => { let v: u8 = value.parse().unwrap(); - binary_treshold(imagePath,v); - } - _ => {println!("Not implemented yet!")} + binary_threshold(imagePath, v); + } + _ => println!("Operation not recognised!"), } } -fn averageColor(i: &str){ - let img = image::open(i).expect("Opening image failed"); +fn averageColor(i: &str) { + let img = openImage(i); let mut r: u32 = 0; let mut g: u32 = 0; let mut b: u32 = 0; - let (width,height) = img.dimensions(); - for x in 0..(width){ - for y in 0..(height){ - let px = img.get_pixel(x,y); + let (width, height) = img.dimensions(); + for x in 0..width { + for y in 0..height { + let px = img.get_pixel(x, y); let rgb = px.to_rgb(); - r = (r as u32 + rgb.data[0] as u32)/2; - g = (g as u32 + rgb.data[1] as u32)/2; - b = (b as u32 + rgb.data[2] as u32)/2; - println!("R G B {} {} {}", r,g,b); + r = (r + u32::from(rgb.data[0])) / 2; + g = (g + u32::from(rgb.data[1])) / 2; + b = (b + u32::from(rgb.data[2])) / 2; } } - println!("Average color is: RGB {} {} {}",r,g,b); + println!("Average color is: RGB {} {} {}", r, g, b); } -fn createThumnbail(i: &str, size: u32){ +fn createThumbnail(i: &str, size: u32) { let operation = "Thumbnail"; - let img = image::open(i).expect("Opening image failed"); - let thumbnail = img.resize(size,size, FilterType::Lanczos3); - saveFile(&thumbnail, &i, &operation); - + let img = openImage(i); + let thumbnail = img.resize(size, size, FilterType::Lanczos3); + saveImage(&thumbnail, &i, &operation); } -fn copy(i: &str){ +fn copy(i: &str) { let operation = "Copy"; - let img = image::open(i).expect("Opening image failed"); - saveFile(&img, &i, &operation); + let img = openImage(i); + saveImage(&img, &i, &operation); } -fn gaussianBlur(i: &str, v: f32){ +fn gaussianBlur(i: &str, v: f32) { let operation = "GuassianBlur"; - let img = image::open(i).expect("Opening image failed"); + let img = openImage(i); let blurred = img.blur(v); - saveFile(&blurred, &i, &operation); + saveImage(&blurred, &i, &operation); } -fn brighten(i: &str,v: i32){ +fn brighten(i: &str, v: i32) { let operation = "Brighten"; - let img = image::open(i).expect("Opening image failed"); + let img = openImage(i); let brightened = img.brighten(v); - saveFile(&brightened, &i, &operation); + saveImage(&brightened, &i, &operation); } -fn huerotate(i: &str, v: i32){ +fn huerotate(i: &str, v: i32) { let operation = "Huerotate"; - let img = image::open(i).expect("Opening image failed"); + let img = openImage(i); let huerotated = img.huerotate(v); - saveFile(&huerotated, &i, &operation); + saveImage(&huerotated, &i, &operation); } -fn contrast(i: &str, v: f32){ +fn contrast(i: &str, v: f32) { let operation = "AdjustContrast"; - let img = image::open(i).expect("Opening image failed"); + let img = openImage(i); let contrast = img.adjust_contrast(v); - saveFile(&contrast, &i, &operation); + saveImage(&contrast, &i, &operation); } -fn grayscale(i: &str){ +fn grayscale(i: &str) { let operation = "Grayscale"; - let img = image::open(i).expect("Opening image failed"); + let img = openImage(i); let grayscale = img.grayscale(); - saveFile(&grayscale, &i, &operation); + saveImage(&grayscale, &i, &operation); } -fn invert(i: &str){ +fn invert(i: &str) { let operation = "Invert"; - let mut img = image::open(i).expect("Opening image failed"); + let mut img = openImage(i); img.invert(); - saveFile(&img, &i, &operation); -} - -fn binary_treshold(i: &str, low: u8){ - let operation = "BinaryTreshold"; - let img = image::open(i).expect("Opening image failed"); - let mut grayscale = img.grayscale(); - let (width,height) = img.dimensions(); - // create image to draw the image after binary treshold - let mut image = image::ImageBuffer::, Vec>::new(width, height); - for w in 0..(width){ - for h in 0..(height){ - let pixel = grayscale.get_pixel(w,h); - let rgb = pixel.to_rgb(); - let intensity = rgb.data[0]; - if low < intensity { - image.get_pixel_mut(w,h).data = [255,255,255]; - }else{ - image.get_pixel_mut(w,h,).data = [0,0,0]; - } - } - } - 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); - image.save(outputPath).unwrap(); + saveImage(&img, &i, &operation); } -/// Generate histogram for grayscale images -fn histogramGrayscale(i: &str){ - let operation = "HistogramGrayscale"; - // size of the output image containing the histogram - let WIDTH = 255; - let HEIGHT = 200; - - // open image and convert it to grayscale - let img = image::open(i).expect("Opening image failed"); +fn binary_threshold(i: &str, low: u8) { + let operation = "BinaryThreshold"; + let img = openImage(i); let grayscale = img.grayscale(); - - // create a hashmap for storing the number of occurences of each intensity - let mut occurences = HashMap::new(); - // and fill it with a placeholder value - for fill in 0..255{ - &occurences.insert(fill,0); - } - - // iterate over each pixel in the image and count the occurences 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 (width, height) = img.dimensions(); + // create buffer to draw the image after binary threshold + let mut buffer = image::ImageBuffer::, Vec>::new(width, height); + for w in 0..width { + for h in 0..height { + let pixel = grayscale.get_pixel(w, h); let rgb = pixel.to_rgb(); let intensity = rgb.data[0]; - match occurences.get(&intensity){ - Some(&oc) => { - let mut current = oc; - current = current + 1; - &occurences.insert(intensity,current); - } - _ => {&occurences.insert(w as u8,0);} - } - } - } - - // find highest value of occurences, so that we can use it as 100% in the histogram - let mut maxValue = 0; - for (occurences, &value) in occurences.iter() { - if(value > maxValue){ - maxValue = value; - } - } - - // create image to draw the histogram on - let mut image = image::ImageBuffer::, Vec>::new(WIDTH, HEIGHT); - - // dirty hack: fill the image with white pixels - for w in 0..WIDTH{ - for h in 0..HEIGHT{ - image.get_pixel_mut(w,h).data = [255,255,255]; - } - } - - // dirty hack: intensity index - let mut cc: u32 = 0; - // Potential bug: 254 and 255 cause panic! - for i in 0..253{ - match occurences.get(&i) { - Some(&value) => { - let mut height = ((value as f32 / maxValue as f32) * 300.0) as u8; - let mut pixel = image.get_pixel_mut(cc, (HEIGHT-1) - height as u32); - pixel.data = [0,0,0]; - }, - _ => println!("Value out of bounds #BarryBonds"), - } - cc = cc + 1; - } - - 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); - image.save(outputPath).unwrap(); -} - -/// Generate histogram for grayscale images -fn histogram(i: &str){ - let operation = "Histogram"; - // size of the output image containing the histogram - let WIDTH = 255; - let HEIGHT = 200; - - // open image and convert it to grayscale - let img = image::open(i).expect("Opening image failed"); - - // create a hashmap for storing the number of occurences of each intensity - let mut occurencesR = HashMap::new(); - let mut occurencesG = HashMap::new(); - let mut occurencesB = HashMap::new(); - // and fill it with a placeholder value - for fill in 0..255{ - &occurencesR.insert(fill,0); - &occurencesG.insert(fill,0); - &occurencesB.insert(fill,0); - } - - // iterate over each pixel in the image and count the occurences 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(); - let intensityR = rgb.data[0]; - let intensityG = rgb.data[1]; - let intensityB = rgb.data[2]; - match occurencesR.get(&intensityR){ - Some(&oc) => { - let mut current = oc; - current = current + 1; - &occurencesR.insert(intensityR,current); - } - _ => {&occurencesR.insert(w as u8,0);} - } - match occurencesG.get(&intensityG){ - Some(&oc) => { - let mut current = oc; - current = current + 1; - &occurencesG.insert(intensityG,current); - } - _ => {&occurencesG.insert(w as u8,0);} - } - match occurencesB.get(&intensityB){ - Some(&oc) => { - let mut current = oc; - current = current + 1; - &occurencesB.insert(intensityB,current); - } - _ => {&occurencesB.insert(w as u8,0);} + if low < intensity { + buffer.get_pixel_mut(w, h).data = [255, 255, 255]; + } else { + buffer.get_pixel_mut(w, h).data = [0, 0, 0]; } } } - - // find highest value of occurences, so that we can use it as 100% in the histogram - let mut maxValueR = 0; - let mut maxValueG = 0; - let mut maxValueB = 0; - for (occurencesR, &value) in occurencesR.iter() { - if(value > maxValueR){ - maxValueR = value; - } - } - for (occurencesG, &value) in occurencesG.iter() { - if(value > maxValueG){ - maxValueG = value; - } - } - for (occurencesB, &value) in occurencesB.iter() { - if(value > maxValueB){ - maxValueB = value; - } - } - - // create image to draw the histogram on - let mut image = image::ImageBuffer::, Vec>::new(WIDTH, HEIGHT); - - // dirty hack: fill the image with white pixels - for w in 0..WIDTH{ - for h in 0..HEIGHT{ - image.get_pixel_mut(w,h).data = [255,255,255]; - } - } - - // dirty hack: intensity index - let mut cc: u32 = 0; - // Potential bug: 254 and 255 cause panic! - for i in 0..253{ - match occurencesR.get(&i) { - Some(&value) => { - let mut height = ((value as f32 / maxValueR as f32) * 200.0) as u8; - let mut pixel = image.get_pixel_mut(cc, (HEIGHT-1) - height as u32); - pixel.data = [255,0,0]; - }, - _ => println!("Value out of bounds #BarryBonds"), - } - match occurencesG.get(&i) { - Some(&value) => { - let mut height = ((value as f32 / maxValueG as f32) * 200.0) as u8; - let mut pixel = image.get_pixel_mut(cc, (HEIGHT-1) - height as u32); - pixel.data = [0,255,0]; - }, - _ => println!("Value out of bounds #BarryBonds"), - } - match occurencesB.get(&i) { - Some(&value) => { - let mut height = ((value as f32 / maxValueB as f32) * 200.0) as u8; - let mut pixel = image.get_pixel_mut(cc, (HEIGHT-1) - height as u32); - pixel.data = [0,0,255]; - }, - _ => println!("Value out of bounds #BarryBonds"), - } - cc = cc + 1; - } - - 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); - image.save(outputPath).unwrap(); + saveBuffer(&buffer, &i, &operation); } - -fn saveFile(img: &DynamicImage, i: &str, operation: &str){ - 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); - let mut out = File::create(outputPath).unwrap(); - match ext.as_ref() { - "jpg" | "JPG" | "jpeg" | "JPEG" => {img.save(&mut out, image::JPEG).expect("Saving image failed");} - "png" | "PNG" => {img.save(&mut out, image::PNG).expect("Saving image failed");} - "gif" | "GIF" => {img.save(&mut out, image::GIF).expect("Saving image failed");} - "bmp" | "BMP" => {img.save(&mut out, image::BMP).expect("Saving image failed");} - "ico" | "ICO" => {img.save(&mut out, image::ICO).expect("Saving image failed");} - _ => {println!("Unsupported file format")} - } -} -