image_anonymizer/
lib.rs

1pub mod ocr;
2pub mod face;
3
4use anyhow::{Context, Result};
5use std::fs;
6use std::path::Path;
7use tracing::{debug, info};
8
9use ocr::detection::detect_text_with_api;
10use ocr::masking::mask_text;
11use face::detection::detect_faces_with_api;
12use face::masking::mask_faces;
13
14/// Process an image to mask sensitive text and faces
15///
16/// # Arguments
17///
18/// * `input_path` - The path to the input image
19/// * `output_dir` - The directory to save the output image
20/// * `mask_texts` - The texts to mask
21/// * `mask_faces` - Whether to mask faces in the image
22///
23/// # Returns
24///
25/// * `Result<()>` - The result of the image processing
26///
27/// # Errors
28///
29/// * `anyhow::Error` - If the image processing fails
30///
31pub fn process_image(
32    input_path: &Path, 
33    output_dir: &Path, 
34    mask_texts: Option<&str>,
35    mask_faces_flag: bool
36) -> Result<()> {
37    info!("Image processing started");
38    info!("Reading input image: {:?}", input_path);
39    let mut img = image::open(input_path).context("Failed to open input image")?;
40
41    let file_name = input_path
42        .file_name()
43        .context("Invalid input filename")?
44        .to_str()
45        .context("Non-UTF8 filename")?;
46
47    let output_path = output_dir.join(format!("masked_{}", file_name));
48
49    // Create output directory if it doesn't exist
50    if !output_dir.exists() {
51        debug!("Creating output directory: {:?}", output_dir);
52        fs::create_dir_all(output_dir).context("Failed to create output directory")?;
53    }
54
55    // Process text masking
56    let additional_masks = if let Some(texts) = mask_texts {
57        texts
58            .split(',')
59            .map(|text| text.trim().to_string())
60            .collect::<Vec<_>>()
61    } else {
62        Vec::new()
63    };
64
65    let annotations = detect_text_with_api(input_path).context("Failed to detect text in image")?;
66
67    if annotations.is_empty() {
68        debug!("No text detected in the image");
69    } else {
70        debug!("Detected {} text annotations", annotations.len());
71        mask_text(&mut img, &annotations, &additional_masks).context("Failed to mask text")?;
72    }
73
74    // Process face masking if enabled
75    if mask_faces_flag {
76        info!("Face detection enabled, detecting faces...");
77        match detect_faces_with_api(input_path) {
78            Ok(face_annotations) => {
79                if face_annotations.is_empty() {
80                    info!("No faces detected in the image");
81                } else {
82                    info!("Detected {} faces in the image", face_annotations.len());
83                    mask_faces(&mut img, &face_annotations).context("Failed to mask faces")?;
84                }
85            },
86            Err(e) => {
87                debug!("Face detection failed: {}", e);
88                info!("Skipping face masking due to detection error");
89            }
90        }
91    }
92
93    img.save(&output_path)
94        .context("Failed to save output image")?;
95
96    info!("Saved processed image to: {:?}", output_path);
97    Ok(())
98}