image_splitting/lib.rs
1//! Image Splitting Library
2//!
3//! This library provides functionality to split images into smaller sub-images.
4//! It supports both equal division (3x3 grid) and custom-sized sub-images.
5
6use image::{GenericImageView, ImageBuffer, Rgba};
7use std::path::Path;
8
9/// Splits an image into 9 equal parts (3x3 grid).
10///
11/// # Arguments
12///
13/// * `image_path` - Path to the input image file
14///
15/// # Returns
16///
17/// * `Result<Vec<ImageBuffer<Rgba<u8>, Vec<u8>>>, image::ImageError>` - A vector of 9 sub-images or an error if the operation fails
18///
19/// # Example
20///
21/// ```
22/// use image_splitting::split_image;
23///
24/// let sub_images = split_image("path/to/image.png")?;
25/// assert_eq!(sub_images.len(), 9);
26/// ```
27pub fn split_image<P: AsRef<Path>>(image_path: P) -> Result<Vec<ImageBuffer<Rgba<u8>, Vec<u8>>>, image::ImageError> {
28 // Load the image
29 let img = image::open(image_path)?;
30
31 // Get image dimensions
32 let (width, height) = img.dimensions();
33
34 // Calculate dimensions for each sub-image
35 let sub_width = width / 3;
36 let sub_height = height / 3;
37
38 let mut sub_images = Vec::new();
39
40 // Split the image into 9 parts
41 for y in 0..3 {
42 for x in 0..3 {
43 let sub_img = img.crop_imm(
44 x * sub_width,
45 y * sub_height,
46 sub_width,
47 sub_height
48 );
49
50 sub_images.push(sub_img.to_rgba8());
51 }
52 }
53
54 Ok(sub_images)
55}
56
57/// Splits an image into sub-images of specified dimensions.
58///
59/// This function allows splitting an image into sub-images of any size. The last row and column
60/// may be smaller if the image dimensions aren't perfectly divisible by the specified sub-image size.
61///
62/// # Arguments
63///
64/// * `image_path` - Path to the input image file
65/// * `sub_width` - Desired width of each sub-image
66/// * `sub_height` - Desired height of each sub-image
67///
68/// # Returns
69///
70/// * `Result<Vec<ImageBuffer<Rgba<u8>, Vec<u8>>>, image::ImageError>` - A vector of sub-images or an error if the operation fails
71///
72/// # Example
73///
74/// ```
75/// use image_splitting::split_image_with_size;
76///
77/// let sub_images = split_image_with_size("path/to/image.png", 100, 100)?;
78/// ```
79pub fn split_image_with_size<P: AsRef<Path>>(
80 image_path: P,
81 sub_width: u32,
82 sub_height: u32,
83) -> Result<Vec<ImageBuffer<Rgba<u8>, Vec<u8>>>, image::ImageError> {
84 // Load the image
85 let img = image::open(image_path)?;
86
87 // Get image dimensions
88 let (width, height) = img.dimensions();
89
90 // Calculate number of sub-images in each dimension using ceiling division
91 let num_cols = (width + sub_width - 1) / sub_width;
92 let num_rows = (height + sub_height - 1) / sub_height;
93
94 let mut sub_images = Vec::new();
95
96 // Split the image into parts
97 for y in 0..num_rows {
98 for x in 0..num_cols {
99 let x_pos = x * sub_width;
100 let y_pos = y * sub_height;
101
102 // Calculate actual dimensions for the last row/column
103 let actual_width = (width - x_pos).min(sub_width);
104 let actual_height = (height - y_pos).min(sub_height);
105
106 let sub_img = img.crop_imm(
107 x_pos,
108 y_pos,
109 actual_width,
110 actual_height
111 );
112
113 sub_images.push(sub_img.to_rgba8());
114 }
115 }
116
117 Ok(sub_images)
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_split_image() {
126 // This test assumes there's a test image in the tests directory
127 let result = split_image("tests/test_image.png");
128 assert!(result.is_ok());
129 let sub_images = result.unwrap();
130 assert_eq!(sub_images.len(), 9);
131 }
132
133 #[test]
134 fn test_split_image_with_size() {
135 let result = split_image_with_size("tests/test_image.png", 100, 100);
136 assert!(result.is_ok());
137 let sub_images = result.unwrap();
138 assert!(sub_images.len() > 0);
139 }
140}