image_processing/
image_processing.rs1use avila_fft::{fft2d::*, filters::*};
10use std::f64::consts::PI;
11
12fn main() {
13 println!("=== Processamento de Imagens 2D com FFT ===\n");
14
15 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 println!("=== 1. Análise de Frequência Espacial ===\n");
25 analyze_spatial_frequency(&image);
26
27 println!("\n=== 2. Filtro Passa-Baixas (Blur) ===\n");
29 apply_lowpass_filter(&image);
30
31 println!("\n=== 3. Filtro Passa-Altas (Detecção de Bordas) ===\n");
33 apply_highpass_filter(&image);
34
35 println!("\n=== 4. Filtro Gaussiano ===\n");
37 apply_gaussian_blur(&image);
38
39 println!("\n=== 5. Análise de Potência por Quadrante ===\n");
41 analyze_quadrants(&image);
42
43 println!("\n=== 6. Teste de Reversibilidade 2D ===\n");
45 test_reversibility(&image);
46
47 println!("\n=== Processamento Completo ===");
48}
49
50fn 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 let pattern1 = 0.5 * (2.0 * PI * x_f / (size as f64) * 2.0).sin();
62
63 let pattern2 = 0.3 * (2.0 * PI * y_f / (size as f64) * 4.0).sin();
65
66 let pattern3 = 0.2 * (2.0 * PI * (x_f + y_f) / (size as f64) * 8.0).sin();
68
69 let dc = 1.0;
71
72 let value = dc + pattern1 + pattern2 + pattern3;
74 data.push(value);
75 }
76 }
77
78 Image2D::from_real(size, size, data).unwrap()
79}
80
81fn analyze_spatial_frequency(image: &Image2D<f64>) {
83 println!("Executando FFT 2D...");
84 let freq = fft2d(image).unwrap();
85
86 let power = freq.power_spectrum();
88
89 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 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 let threshold = max_power * 0.01; 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
110fn apply_lowpass_filter(image: &Image2D<f64>) {
112 println!("Aplicando filtro passa-baixas ideal...");
113
114 let mut freq = fft2d(image).unwrap();
115
116 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 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
140fn apply_highpass_filter(image: &Image2D<f64>) {
142 println!("Aplicando filtro passa-altas ideal...");
143
144 let mut freq = fft2d(image).unwrap();
145
146 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 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 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
178fn apply_gaussian_blur(image: &Image2D<f64>) {
180 println!("Aplicando filtro Gaussiano...");
181
182 let mut freq = fft2d(image).unwrap();
183
184 let sigma = 5.0;
186 let filter = GaussianFilter::new(
187 image.width,
188 image.height,
189 sigma,
190 false );
192
193 filter.apply(&mut freq);
194
195 let filtered = ifft2d(&freq).unwrap();
196
197 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
207fn analyze_quadrants(image: &Image2D<f64>) {
209 println!("Dividindo espectro em quadrantes...");
210
211 let mut freq = fft2d(image).unwrap();
212 freq.fftshift(); let half_w = freq.width / 2;
215 let half_h = freq.height / 2;
216
217 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 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
249fn 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 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 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
298fn 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}