11//! Analyze image characteristics to understand encoder preferences
22
3- use std:: path:: Path ;
3+ use std:: path:: { Path , PathBuf } ;
4+
5+ /// Get path to the codec-corpus directory.
6+ ///
7+ /// # Panics
8+ /// Panics if directory cannot be found.
9+ fn get_corpus_dir ( ) -> PathBuf {
10+ // Check environment variable first
11+ if let Ok ( dir) = std:: env:: var ( "CODEC_CORPUS_DIR" ) {
12+ let path = PathBuf :: from ( dir) ;
13+ if path. exists ( ) {
14+ return path;
15+ }
16+ }
17+
18+ // Check relative to manifest dir
19+ if let Ok ( manifest) = std:: env:: var ( "CARGO_MANIFEST_DIR" ) {
20+ let candidates = [
21+ PathBuf :: from ( & manifest) . join ( "../../codec-corpus" ) ,
22+ PathBuf :: from ( & manifest) . join ( "../../../codec-corpus" ) ,
23+ PathBuf :: from ( & manifest) . join ( "codec-corpus" ) ,
24+ ] ;
25+ for path in candidates {
26+ if path. exists ( ) {
27+ return path;
28+ }
29+ }
30+ }
31+
32+ panic ! (
33+ "codec-corpus directory not found.\n \
34+ Set CODEC_CORPUS_DIR environment variable or clone:\n \
35+ git clone --depth 1 https://github.com/imazen/codec-corpus ../codec-corpus"
36+ ) ;
37+ }
438
539fn load_image ( path : & Path ) -> Option < ( Vec < u8 > , usize , usize ) > {
640 let img = image:: open ( path) . ok ( ) ?;
@@ -15,7 +49,8 @@ fn analyze(rgb: &[u8], width: usize, height: usize) -> ImageStats {
1549 let pixels = width * height;
1650
1751 // Convert to grayscale for analysis
18- let gray: Vec < f32 > = rgb. chunks ( 3 )
52+ let gray: Vec < f32 > = rgb
53+ . chunks ( 3 )
1954 . map ( |p| 0.299 * p[ 0 ] as f32 + 0.587 * p[ 1 ] as f32 + 0.114 * p[ 2 ] as f32 )
2055 . collect ( ) ;
2156
@@ -25,8 +60,8 @@ fn analyze(rgb: &[u8], width: usize, height: usize) -> ImageStats {
2560
2661 // Edge strength (Sobel-like)
2762 let mut edge_sum = 0.0f32 ;
28- for y in 1 ..height- 1 {
29- for x in 1 ..width- 1 {
63+ for y in 1 ..height - 1 {
64+ for x in 1 ..width - 1 {
3065 let idx = y * width + x;
3166 let gx = gray[ idx + 1 ] - gray[ idx - 1 ] ;
3267 let gy = gray[ idx + width] - gray[ idx - width] ;
@@ -47,9 +82,11 @@ fn analyze(rgb: &[u8], width: usize, height: usize) -> ImageStats {
4782 }
4883 }
4984 let block_mean: f32 = block_pixels. iter ( ) . sum :: < f32 > ( ) / 64.0 ;
50- let block_var: f32 = block_pixels. iter ( )
85+ let block_var: f32 = block_pixels
86+ . iter ( )
5187 . map ( |& v| ( v - block_mean) . powi ( 2 ) )
52- . sum :: < f32 > ( ) / 64.0 ;
88+ . sum :: < f32 > ( )
89+ / 64.0 ;
5390 block_variances. push ( block_var) ;
5491 }
5592 }
@@ -64,9 +101,11 @@ fn analyze(rgb: &[u8], width: usize, height: usize) -> ImageStats {
64101
65102 // Variance of block variances (texture uniformity)
66103 let mean_block_var: f32 = block_variances. iter ( ) . sum :: < f32 > ( ) / block_variances. len ( ) as f32 ;
67- let var_of_var: f32 = block_variances. iter ( )
104+ let var_of_var: f32 = block_variances
105+ . iter ( )
68106 . map ( |& v| ( v - mean_block_var) . powi ( 2 ) )
69- . sum :: < f32 > ( ) / block_variances. len ( ) as f32 ;
107+ . sum :: < f32 > ( )
108+ / block_variances. len ( ) as f32 ;
70109
71110 ImageStats {
72111 mean_luminance : mean,
@@ -92,34 +131,38 @@ fn main() {
92131 let args: Vec < String > = std:: env:: args ( ) . collect ( ) ;
93132
94133 let images: Vec < std:: path:: PathBuf > = if args. len ( ) > 1 {
95- args[ 1 ..] . iter ( ) . map ( |s| std:: path:: PathBuf :: from ( s) ) . collect ( )
134+ args[ 1 ..]
135+ . iter ( )
136+ . map ( |s| std:: path:: PathBuf :: from ( s) )
137+ . collect ( )
96138 } else {
97- // Default Kodak outliers
98- vec ! [
99- "/home/lilith/work/codec-eval/codec-corpus/kodak/22.png" ,
100- "/home/lilith/work/codec-eval/codec-corpus/kodak/20.png" ,
101- "/home/lilith/work/codec-eval/codec-corpus/kodak/10.png" ,
102- "/home/lilith/work/codec-eval/codec-corpus/kodak/23.png" ,
103- "/home/lilith/work/codec-eval/codec-corpus/kodak/12.png" ,
104- "/home/lilith/work/codec-eval/codec-corpus/kodak/8.png" ,
105- ] . into_iter ( ) . map ( std:: path:: PathBuf :: from) . collect ( )
139+ // Default Kodak outliers - find relative to manifest or via CODEC_CORPUS_DIR
140+ let corpus_dir = get_corpus_dir ( ) ;
141+ vec ! [ "22.png" , "20.png" , "10.png" , "23.png" , "12.png" , "8.png" ]
142+ . into_iter ( )
143+ . map ( |name| corpus_dir. join ( "kodak" ) . join ( name) )
144+ . collect ( )
106145 } ;
107146
108- println ! ( "{:>12} | {:>8} | {:>8} | {:>8} | {:>8} | {:>8} | {:>8}" ,
109- "Image" , "Mean" , "Variance" , "Edges" , "Flat%" , "Detail%" , "TexUnif" ) ;
147+ println ! (
148+ "{:>12} | {:>8} | {:>8} | {:>8} | {:>8} | {:>8} | {:>8}" ,
149+ "Image" , "Mean" , "Variance" , "Edges" , "Flat%" , "Detail%" , "TexUnif"
150+ ) ;
110151 println ! ( "{}" , "-" . repeat( 85 ) ) ;
111152
112153 for path in images {
113154 if let Some ( ( rgb, width, height) ) = load_image ( & path) {
114155 let stats = analyze ( & rgb, width, height) ;
115- println ! ( "{:>12} | {:>8.1} | {:>8.1} | {:>8.2} | {:>8.1} | {:>8.1} | {:>8.1}" ,
156+ println ! (
157+ "{:>12} | {:>8.1} | {:>8.1} | {:>8.2} | {:>8.1} | {:>8.1} | {:>8.1}" ,
116158 path. file_name( ) . unwrap_or_default( ) . to_string_lossy( ) ,
117159 stats. mean_luminance,
118160 stats. global_variance,
119161 stats. edge_strength,
120162 stats. flat_block_pct,
121163 stats. detail_block_pct,
122- stats. texture_uniformity) ;
164+ stats. texture_uniformity
165+ ) ;
123166 }
124167 }
125168}
0 commit comments