@@ -24,12 +24,6 @@ pub use traits::*;
2424/// "input/**/*.jpg", we want "input/sub/file.jpg" to become "output/sub/file.jpg",
2525/// not "output/input/sub/file.jpg". This function extracts "input" as the base
2626/// to enable proper relative path computation.
27- ///
28- /// # Examples
29- ///
30- /// - `"input/**/*.jpg"` → `"input"`
31- /// - `"a/b/**/*"` → `"a/b"`
32- /// - `"*.jpg"` → `"."`
3327fn extract_base_directory ( pattern : & str ) -> PathBuf {
3428 let wildcard_pos = pattern. find ( [ '*' , '?' , '[' ] ) . unwrap_or ( pattern. len ( ) ) ;
3529
@@ -49,28 +43,28 @@ fn extract_base_directory(pattern: &str) -> PathBuf {
4943 }
5044}
5145
52- /// Simple parallel image processor using Rayon for single-machine batch processing .
46+ /// Batch image processor using trait-based model abstraction .
5347///
54- /// This processor is generic over any type implementing `ImageSegmentationModel`,
55- /// allowing different model backends (ONNX, TensorRT, etc.) to be used with the
56- /// same processing logic. Uses Rayon for straightforward parallel processing without
57- /// the complexity of queues or distributed architecture.
48+ /// Generic over any type implementing `ImageSegmentationModel`, allowing different
49+ /// model backends to be used with the same processing logic. Processes images
50+ /// sequentially to maintain simplicity and avoid resource contention with GPU inference.
5851pub struct ImageProcessor < M : ImageSegmentationModel > {
5952 model : M ,
6053 config : Config ,
6154}
6255
6356impl < M : ImageSegmentationModel > ImageProcessor < M > {
64- /// Create a new image processor with the given model and configuration.
6557 pub fn new ( model : M , config : Config ) -> Self {
6658 Self { model, config }
6759 }
6860
6961 /// Process all images matching the input pattern and save results to output directory.
7062 ///
71- /// This method orchestrates the entire processing workflow: collecting image files,
72- /// creating output directories, processing each image with progress tracking, and
73- /// handling errors gracefully without stopping the entire batch.
63+ /// # Error handling strategy
64+ ///
65+ /// Individual image failures are logged but do not stop the entire batch. This design
66+ /// prioritizes completing as much work as possible rather than failing fast, which is
67+ /// appropriate for batch processing where partial results are valuable.
7468 pub fn process_directory ( & mut self ) -> Result < ( ) > {
7569 let input_pattern = & self . config . input_pattern ;
7670 let output_path = self . config . output_dir . clone ( ) ;
@@ -111,12 +105,9 @@ impl<M: ImageSegmentationModel> ImageProcessor<M> {
111105 }
112106
113107 /// Collect all valid image files matching the glob pattern.
114- ///
115- /// Only includes files with supported image formats to avoid processing errors later.
116108 fn collect_image_files ( & self , pattern : & str ) -> Result < Vec < PathBuf > > {
117109 let mut image_files = Vec :: new ( ) ;
118110
119- // Execute glob pattern and filter for valid image files
120111 for entry in glob ( pattern) . map_err ( |e| AnimeSegError :: ImageProcessing {
121112 path : pattern. to_string ( ) ,
122113 operation : "glob pattern parsing" . to_string ( ) ,
@@ -139,9 +130,11 @@ impl<M: ImageSegmentationModel> ImageProcessor<M> {
139130
140131 /// Check if the file has a supported image format that can be read.
141132 ///
142- /// Uses the `image` crate's format detection to determine support dynamically,
143- /// which allows the set of supported formats to expand automatically when
144- /// enabling additional feature flags.
133+ /// # Why dynamic format detection
134+ ///
135+ /// Using the image crate's format detection allows the set of supported formats
136+ /// to expand automatically when enabling additional feature flags, avoiding the
137+ /// need to maintain a hardcoded list of extensions.
145138 pub fn is_supported_image_format ( & self , path : & Path ) -> bool {
146139 if let Some ( extension) = path. extension ( ) . and_then ( |ext| ext. to_str ( ) ) {
147140 let format = ImageFormat :: from_extension ( extension) ;
@@ -156,9 +149,11 @@ impl<M: ImageSegmentationModel> ImageProcessor<M> {
156149
157150 /// Process a single image: load, segment, and save with preserved directory structure.
158151 ///
159- /// The output path preserves the relative directory structure from the input pattern's
160- /// base directory. This ensures that hierarchical folder organization is maintained
161- /// in the output, which is essential for batch processing large organized datasets.
152+ /// # Why preserve directory structure
153+ ///
154+ /// Maintaining the relative directory structure from input to output is essential
155+ /// for batch processing large organized datasets where folder hierarchy provides
156+ /// semantic organization (e.g., by date, category, or project).
162157 fn process_single_image ( & mut self , input_file : & Path , output_dir : & Path ) -> Result < ( ) > {
163158 let img = image:: open ( input_file) . map_err ( |e| AnimeSegError :: ImageProcessing {
164159 path : input_file. display ( ) . to_string ( ) ,
@@ -195,16 +190,18 @@ impl<M: ImageSegmentationModel> ImageProcessor<M> {
195190 Ok ( ( ) )
196191 }
197192
198- /// Delegate segmentation to the underlying model implementation.
199193 fn segment_image ( & mut self , img : & DynamicImage ) -> Result < DynamicImage > {
200194 self . model . segment_image ( img)
201195 }
202196
203197 /// Compute the relative path from the input pattern's base directory.
204198 ///
205- /// This is necessary to reconstruct the directory hierarchy in the output.
206- /// For example, with input pattern "photos/**/*.jpg" and file "photos/2024/jan/pic.jpg",
207- /// this returns "2024/jan/pic.jpg" so the output becomes "output/2024/jan/pic.png".
199+ /// # Why this computation is necessary
200+ ///
201+ /// To reconstruct the directory hierarchy in the output while avoiding duplication
202+ /// of the base directory. For example, with pattern "photos/**/*.jpg" and file
203+ /// "photos/2024/jan/pic.jpg", this returns "2024/jan/pic.jpg" so the output becomes
204+ /// "output/2024/jan/pic.png" rather than "output/photos/2024/jan/pic.png".
208205 pub fn get_relative_path ( & self , input_file : & Path ) -> Result < PathBuf > {
209206 let base_dir = extract_base_directory ( & self . config . input_pattern ) ;
210207 input_file
@@ -224,7 +221,9 @@ impl<M: ImageSegmentationModel> ImageProcessor<M> {
224221impl ImageProcessor < Model > {
225222 /// Convenience constructor that creates an ONNX-based image processor.
226223 ///
227- /// This provides a simpler API for the common case of using the default ONNX model
224+ /// # Why this exists
225+ ///
226+ /// Provides a simpler API for the common case of using the default ONNX model
228227 /// implementation, avoiding the need to construct the Model separately.
229228 pub fn with_onnx_model ( config : Config ) -> Result < Self > {
230229 let model = Model :: new ( & config. model_path ) ?;
0 commit comments