image_processing/
image_processing.rs

1//! Exemplo de processamento de imagens 2D usando FFT
2//!
3//! Demonstra:
4//! - FFT 2D e análise de frequências espaciais
5//! - Filtros passa-baixas e passa-altas
6//! - Detecção de bordas no domínio da frequência
7//! - Análise de espectro de potência 2D
8
9use avila_fft::{fft2d::*, filters::*};
10use std::f64::consts::PI;
11
12fn main() {
13    println!("=== Processamento de Imagens 2D com FFT ===\n");
14
15    // Cria imagem sintética 64x64 com padrões
16    println!("Gerando imagem de teste 64x64...");
17    let size = 64;
18    let image = create_test_image(size);
19
20    println!("Dimensões: {}x{}", image.width, image.height);
21    println!("Total de pixels: {}\n", image.data.len());
22
23    // 1. Análise de frequência espacial
24    println!("=== 1. Análise de Frequência Espacial ===\n");
25    analyze_spatial_frequency(&image);
26
27    // 2. Filtro passa-baixas (blur)
28    println!("\n=== 2. Filtro Passa-Baixas (Blur) ===\n");
29    apply_lowpass_filter(&image);
30
31    // 3. Filtro passa-altas (sharpen/edges)
32    println!("\n=== 3. Filtro Passa-Altas (Detecção de Bordas) ===\n");
33    apply_highpass_filter(&image);
34
35    // 4. Filtro Gaussiano
36    println!("\n=== 4. Filtro Gaussiano ===\n");
37    apply_gaussian_blur(&image);
38
39    // 5. Análise de potência por quadrante
40    println!("\n=== 5. Análise de Potência por Quadrante ===\n");
41    analyze_quadrants(&image);
42
43    // 6. Demonstração de reversibilidade
44    println!("\n=== 6. Teste de Reversibilidade 2D ===\n");
45    test_reversibility(&image);
46
47    println!("\n=== Processamento Completo ===");
48}
49
50/// Cria imagem de teste com padrões conhecidos
51fn create_test_image(size: usize) -> Image2D<f64> {
52    let mut data = Vec::with_capacity(size * size);
53
54    for y in 0..size {
55        for x in 0..size {
56            let x_f = x as f64;
57            let y_f = y as f64;
58
59            // Padrão composto:
60            // 1. Senoide horizontal (baixa frequência)
61            let pattern1 = 0.5 * (2.0 * PI * x_f / (size as f64) * 2.0).sin();
62
63            // 2. Senoide vertical (média frequência)
64            let pattern2 = 0.3 * (2.0 * PI * y_f / (size as f64) * 4.0).sin();
65
66            // 3. Padrão diagonal (alta frequência)
67            let pattern3 = 0.2 * (2.0 * PI * (x_f + y_f) / (size as f64) * 8.0).sin();
68
69            // 4. Região constante (DC)
70            let dc = 1.0;
71
72            // Combina todos
73            let value = dc + pattern1 + pattern2 + pattern3;
74            data.push(value);
75        }
76    }
77
78    Image2D::from_real(size, size, data).unwrap()
79}
80
81/// Analisa frequências espaciais
82fn analyze_spatial_frequency(image: &Image2D<f64>) {
83    println!("Executando FFT 2D...");
84    let freq = fft2d(image).unwrap();
85
86    // Calcula espectro de potência
87    let power = freq.power_spectrum();
88
89    // Estatísticas
90    let total_power: f64 = power.iter().sum();
91    let max_power = power.iter().cloned().fold(0.0, f64::max);
92    let avg_power = total_power / power.len() as f64;
93
94    println!("Potência total: {:.2e}", total_power);
95    println!("Potência média: {:.2e}", avg_power);
96    println!("Potência máxima: {:.2e}", max_power);
97
98    // Analisa componente DC
99    let dc = freq.get(0, 0);
100    println!("\nComponente DC: {:.2} + {:.2}i", dc.re, dc.im);
101    println!("Magnitude DC: {:.2}", dc.norm());
102
103    // Conta componentes significativas
104    let threshold = max_power * 0.01; // 1% do máximo
105    let significant = power.iter().filter(|&&p| p > threshold).count();
106    println!("\nComponententes significativas (>1% max): {}", significant);
107    println!("Percentual: {:.1}%", 100.0 * significant as f64 / power.len() as f64);
108}
109
110/// Aplica filtro passa-baixas (suavização/blur)
111fn apply_lowpass_filter(image: &Image2D<f64>) {
112    println!("Aplicando filtro passa-baixas ideal...");
113
114    let mut freq = fft2d(image).unwrap();
115
116    // Cutoff em 20% da frequência máxima
117    let cutoff = (image.width as f64) * 0.2;
118    let filter = IdealFilter::new(
119        image.width,
120        image.height,
121        FilterType::LowPass,
122        cutoff
123    );
124
125    filter.apply(&mut freq);
126
127    let filtered = ifft2d(&freq).unwrap();
128
129    // Estatísticas
130    let original_variance = calculate_variance(&image.magnitude());
131    let filtered_variance = calculate_variance(&filtered.magnitude());
132
133    println!("Cutoff: {:.1} pixels", cutoff);
134    println!("Variância original: {:.4}", original_variance);
135    println!("Variância filtrada: {:.4}", filtered_variance);
136    println!("Redução: {:.1}%",
137        100.0 * (1.0 - filtered_variance / original_variance));
138}
139
140/// Aplica filtro passa-altas (detecção de bordas)
141fn apply_highpass_filter(image: &Image2D<f64>) {
142    println!("Aplicando filtro passa-altas ideal...");
143
144    let mut freq = fft2d(image).unwrap();
145
146    // Cutoff em 10% (remove baixas frequências)
147    let cutoff = (image.width as f64) * 0.1;
148    let filter = IdealFilter::new(
149        image.width,
150        image.height,
151        FilterType::HighPass,
152        cutoff
153    );
154
155    filter.apply(&mut freq);
156
157    let filtered = ifft2d(&freq).unwrap();
158
159    // Calcula intensidade de bordas
160    let edge_intensity: f64 = filtered.magnitude().iter()
161        .map(|&m| m.abs())
162        .sum::<f64>() / filtered.data.len() as f64;
163
164    println!("Cutoff: {:.1} pixels", cutoff);
165    println!("Intensidade média de bordas: {:.4}", edge_intensity);
166
167    // Detecta pixels de borda significativos
168    let threshold = edge_intensity * 2.0;
169    let edge_pixels = filtered.magnitude().iter()
170        .filter(|&&m| m > threshold)
171        .count();
172
173    println!("Pixels de borda detectados: {} ({:.1}%)",
174        edge_pixels,
175        100.0 * edge_pixels as f64 / filtered.data.len() as f64);
176}
177
178/// Aplica filtro Gaussiano (blur suave)
179fn apply_gaussian_blur(image: &Image2D<f64>) {
180    println!("Aplicando filtro Gaussiano...");
181
182    let mut freq = fft2d(image).unwrap();
183
184    // Sigma = 5.0 (blur moderado)
185    let sigma = 5.0;
186    let filter = GaussianFilter::new(
187        image.width,
188        image.height,
189        sigma,
190        false // low-pass
191    );
192
193    filter.apply(&mut freq);
194
195    let filtered = ifft2d(&freq).unwrap();
196
197    // Comparação
198    let original_mean = image.magnitude().iter().sum::<f64>() / image.data.len() as f64;
199    let filtered_mean = filtered.magnitude().iter().sum::<f64>() / filtered.data.len() as f64;
200
201    println!("Sigma: {:.1}", sigma);
202    println!("Média original: {:.4}", original_mean);
203    println!("Média filtrada: {:.4}", filtered_mean);
204    println!("Tipo: Gaussiano (sem ringing)");
205}
206
207/// Analisa potência por quadrante
208fn analyze_quadrants(image: &Image2D<f64>) {
209    println!("Dividindo espectro em quadrantes...");
210
211    let mut freq = fft2d(image).unwrap();
212    freq.fftshift(); // Move DC para centro
213
214    let half_w = freq.width / 2;
215    let half_h = freq.height / 2;
216
217    // Calcula potência em cada quadrante
218    let mut quadrants = vec![0.0; 4];
219
220    for y in 0..freq.height {
221        for x in 0..freq.width {
222            let power = freq.get(x, y).norm_sqr();
223            let q = if x < half_w {
224                if y < half_h { 0 } else { 2 }
225            } else {
226                if y < half_h { 1 } else { 3 }
227            };
228            quadrants[q] += power;
229        }
230    }
231
232    let total: f64 = quadrants.iter().sum();
233
234    println!("\nDistribuição de potência por quadrante:");
235    for (i, &power) in quadrants.iter().enumerate() {
236        println!("  Q{}: {:.2e} ({:.1}%)",
237            i + 1, power, 100.0 * power / total);
238    }
239
240    // Identifica direção dominante
241    let horizontal = (quadrants[0] + quadrants[1]) / total;
242    let vertical = (quadrants[0] + quadrants[2]) / total;
243
244    println!("\nDireção dominante:");
245    println!("  Horizontal: {:.1}%", horizontal * 100.0);
246    println!("  Vertical: {:.1}%", vertical * 100.0);
247}
248
249/// Testa reversibilidade FFT 2D
250fn test_reversibility(image: &Image2D<f64>) {
251    println!("Testando FFT -> IFFT...");
252
253    let freq = fft2d(image).unwrap();
254    let recovered = ifft2d(&freq).unwrap();
255
256    // Calcula erro
257    let mut max_error: f64 = 0.0;
258    let mut total_error = 0.0;
259
260    for i in 0..image.data.len() {
261        let error = (image.data[i].re - recovered.data[i].re).abs();
262        max_error = max_error.max(error);
263        total_error += error * error;
264    }
265
266    let rms_error = (total_error / image.data.len() as f64).sqrt();
267
268    println!("Erro RMS: {:.2e}", rms_error);
269    println!("Erro máximo: {:.2e}", max_error);
270
271    if rms_error < 1e-10 {
272        println!("✓ Reversibilidade perfeita!");
273    } else {
274        println!("⚠ Pequeno erro numérico (aceitável)");
275    }
276
277    // Verifica Parseval
278    let energy_spatial: f64 = image.data.iter()
279        .map(|c| c.norm_sqr())
280        .sum();
281
282    let energy_freq: f64 = freq.data.iter()
283        .map(|c| c.norm_sqr())
284        .sum::<f64>() / (image.data.len() as f64);
285
286    let parseval_error = (energy_spatial - energy_freq).abs() / energy_spatial;
287
288    println!("\nTeorema de Parseval:");
289    println!("Energia espacial: {:.2e}", energy_spatial);
290    println!("Energia frequência: {:.2e}", energy_freq);
291    println!("Erro relativo: {:.2e}", parseval_error);
292
293    if parseval_error < 1e-8 {
294        println!("✓ Parseval verificado!");
295    }
296}
297
298/// Calcula variância de um vetor
299fn calculate_variance(data: &[f64]) -> f64 {
300    let mean = data.iter().sum::<f64>() / data.len() as f64;
301    let variance = data.iter()
302        .map(|&x| (x - mean).powi(2))
303        .sum::<f64>() / data.len() as f64;
304    variance
305}