Skip to main content

agent_image_diff/
cli.rs

1use clap::{Parser, ValueEnum};
2use std::path::PathBuf;
3
4const HELP_TEMPLATE: &str = "\
5{bin} - {about}
6
7Usage: {usage}
8
9Arguments:
10  <baseline>                 Path to the baseline (expected) image
11  [candidate]                Path to the candidate (actual) image (required for diff)
12
13Output:
14  -f, --format <format>      Output format: json, summary, image [default: json]
15  -o, --output <path>        Write visual diff image (works alongside any format)
16  -v, --verbose              Include all diagnostic fields in JSON
17      --pretty               Pretty-print JSON output
18  -q, --quiet                Suppress stdout; check JSON output for results
19
20Tuning:
21  -t, --threshold <float>    Color difference sensitivity, 0.0-1.0 [default: 0.1]
22      --denoise <n>          Remove noise clusters smaller than N pixels [default: 25]
23      --dilate <n>           Expand diff mask by N pixels [default: 0]
24      --merge-distance <n>   Merge regions within N pixels of each other [default: 50]
25      --min-region-size <n>  Filter out regions smaller than N pixels [default: 25]
26      --connectivity <n>     Pixel connectivity: 4 (cross) or 8 (diagonals) [default: 8]
27      --detect-antialias     Ignore anti-aliased edge pixels [default: true]
28
29Crop:
30      --crop                 Crop a region from the baseline image instead of diffing
31      --x <n>                X coordinate of crop region (screenshot pixels)
32      --y <n>                Y coordinate of crop region (screenshot pixels)
33      --crop-width <n>       Width of crop region (screenshot pixels)
34      --crop-height <n>      Height of crop region (screenshot pixels)
35
36Options:
37  -h, --help                 Print help
38  -V, --version              Print version
39
40Examples:
41  {bin} baseline.png candidate.png
42  {bin} baseline.png candidate.png -v --pretty
43  {bin} baseline.png candidate.png -o diff.png
44  {bin} baseline.png candidate.png -q -o diff.png
45  {bin} screenshot.png --crop --x 100 --y 200 --crop-width 400 --crop-height 300 -o cropped.png";
46
47#[derive(Parser)]
48#[command(
49    name = "agent-image-diff",
50    version,
51    about = "structured image diff with JSON output for agent workflows",
52    help_template = HELP_TEMPLATE,
53)]
54pub struct Cli {
55    /// Path to the baseline (expected) image
56    pub baseline: PathBuf,
57
58    /// Path to the candidate (actual) image (required for diff, unused in crop mode)
59    pub candidate: Option<PathBuf>,
60
61    /// Crop mode: extract a region from the baseline image instead of diffing
62    #[arg(long)]
63    pub crop: bool,
64
65    /// X coordinate of crop region (screenshot pixels, requires --crop)
66    #[arg(long, requires = "crop")]
67    pub x: Option<u32>,
68
69    /// Y coordinate of crop region (screenshot pixels, requires --crop)
70    #[arg(long, requires = "crop")]
71    pub y: Option<u32>,
72
73    /// Width of crop region (screenshot pixels, requires --crop)
74    #[arg(long, requires = "crop")]
75    pub crop_width: Option<u32>,
76
77    /// Height of crop region (screenshot pixels, requires --crop)
78    #[arg(long, requires = "crop")]
79    pub crop_height: Option<u32>,
80
81    /// Output format (written to stdout)
82    #[arg(short = 'f', long, default_value = "json", value_enum, hide = true)]
83    pub format: OutputFormat,
84
85    /// Path to write visual diff image (works alongside any output format)
86    #[arg(short = 'o', long = "output", hide = true)]
87    pub output: Option<PathBuf>,
88
89    /// Color difference threshold (0.0 = exact match, 1.0 = everything matches)
90    #[arg(short = 't', long, default_value = "0.1", hide = true)]
91    pub threshold: f64,
92
93    /// Enable anti-aliasing detection (reduces false positives at edges)
94    #[arg(long, default_value = "true", hide = true)]
95    pub detect_antialias: bool,
96
97    /// Minimum region size in pixels (regions smaller than this are filtered out)
98    #[arg(long, default_value = "25", hide = true)]
99    pub min_region_size: u32,
100
101    /// Connectivity for region clustering: 4 (cross) or 8 (including diagonals)
102    #[arg(long, default_value = "8", hide = true)]
103    pub connectivity: u8,
104
105    /// Remove noise clusters smaller than this many pixels before dilation (0 = disabled)
106    #[arg(long, default_value = "25", hide = true)]
107    pub denoise: u32,
108
109    /// Dilation radius in pixels (expands diff mask to merge nearby changes, 0 = disabled)
110    #[arg(long, default_value = "0", hide = true)]
111    pub dilate: u32,
112
113    /// Max gap in pixels between regions to merge them (0 = disabled)
114    #[arg(long, default_value = "50", hide = true)]
115    pub merge_distance: u32,
116
117    /// Include all diagnostic fields in JSON output (dimensions, pixel counts, deltas)
118    #[arg(short = 'v', long, hide = true)]
119    pub verbose: bool,
120
121    /// Pretty-print JSON output (default is compact single-line)
122    #[arg(long, hide = true)]
123    pub pretty: bool,
124
125    /// Suppress stdout output
126    #[arg(short = 'q', long, hide = true)]
127    pub quiet: bool,
128}
129
130#[derive(ValueEnum, Clone)]
131pub enum OutputFormat {
132    Json,
133    Image,
134    Summary,
135}