|
41 | 41 | ;; you automatically remove a 60 Hz electrical hum without affecting the music? |
42 | 42 | ;; |
43 | 43 | ;; These questions lead us to **signal transforms**—mathematical tools that reveal hidden structure |
44 | | -;; in data. Every JPEG image you view uses the Discrete Cosine Transform for compression. MP3 |
45 | | -;; audio files rely on the same mathematics. Speech recognition, radar systems, medical imaging—all |
46 | | -;; depend on transforming signals between different representations. |
| 44 | +;; in data. Classic JPEG images use the Discrete Cosine Transform for compression. MP3 audio uses |
| 45 | +;; a variant called the Modified DCT. Speech recognition, radar systems, medical imaging—all depend |
| 46 | +;; on transforming signals between different representations. |
47 | 47 | ;; |
48 | 48 | ;; At their core, transforms answer a simple question: **can we express this signal as a weighted |
49 | 49 | ;; sum of simpler basis functions?** The Fourier transform uses sines and cosines. Wavelets use |
|
399 | 399 | (def freq-bin-5-real (dtype/get-value cosine-spectrum (* 2 5))) |
400 | 400 | (def freq-bin-5-imag (dtype/get-value cosine-spectrum (+ 1 (* 2 5)))) |
401 | 401 |
|
402 | | -;; Pure cosine = all energy in REAL part (cosine basis function) |
403 | | -;; Imaginary part ≈ 0 (no sine component needed) |
| 402 | +;; Pure cosine = nearly all energy in REAL part (cosine basis function) |
| 403 | +;; Imaginary part ≈ 0 (no sine component needed, apart from numerical noise) |
404 | 404 |
|
405 | 405 | ;; Now try a pure sine wave |
406 | 406 | (def pure-sine-signal |
|
412 | 412 | (def sine-freq-bin-5-real (dtype/get-value sine-spectrum (* 2 5))) |
413 | 413 | (def sine-freq-bin-5-imag (dtype/get-value sine-spectrum (+ 1 (* 2 5)))) |
414 | 414 |
|
415 | | -;; Pure sine = all energy in IMAGINARY part (sine basis function) |
416 | | -;; Real part ≈ 0 (no cosine component needed) |
| 415 | +;; Pure sine = nearly all energy in IMAGINARY part (sine basis function) |
| 416 | +;; Real part ≈ 0 (no cosine component needed, apart from numerical noise) |
417 | 417 |
|
418 | 418 | ;; **Key Takeaway**: |
419 | 419 | ;; - Pure **cosine** signal → FFT has large **real part**, near-zero imaginary part |
|
523 | 523 | ;; a high-performance Java library that automatically parallelizes large transforms. |
524 | 524 | ;; |
525 | 525 | ;; **Key performance features**: |
526 | | -;; - **Automatic parallelization**: For arrays larger than ~8K elements, JTransforms splits |
| 526 | +;; - **Automatic parallelization**: For large arrays (typically >8-16K elements), JTransforms splits |
527 | 527 | ;; work across available CPU cores using Java's `ForkJoinPool` |
528 | 528 | ;; - **SIMD optimizations**: Uses Java's auto-vectorization where possible |
529 | 529 | ;; - **In-place transforms**: Can operate directly on input arrays (fastmath wraps this safely) |
530 | 530 | ;; - **Algorithm selection**: Chooses optimal FFT algorithm (split-radix, mixed-radix, or |
531 | 531 | ;; Bluestein) based on array size |
532 | 532 | ;; |
533 | 533 | ;; **What this means for you**: You don't need to think about parallelism—just pass large |
534 | | -;; arrays and JTransforms automatically uses all available cores. For smaller signals (<8K), |
| 534 | +;; arrays and JTransforms automatically uses available CPU cores. For smaller signals, |
535 | 535 | ;; the overhead of parallelization exceeds the benefit, so it runs single-threaded. |
536 | 536 | ;; |
537 | 537 | ;; **Performance tip**: Reuse transformer objects! The constructor pre-computes lookup tables |
|
718 | 718 | :passed? (:passed? result) |
719 | 719 | :status (if (:passed? result) "✓ PASS" "✗ FAIL")})) |
720 | 720 |
|
721 | | -;; **Result**: All signals pass - FFT is perfectly invertible! |
| 721 | +;; **Result**: All signals pass - FFT is mathematically invertible (within floating-point precision)! |
722 | 722 | ;; |
723 | 723 | ;; This round-trip property is crucial. It means we can: |
724 | 724 | ;; 1. Transform signal → frequency domain |
725 | 725 | ;; 2. Manipulate frequencies (filter, compress, analyze) |
726 | 726 | ;; 3. Transform back → time domain |
727 | | -;; 4. Get our original signal back (within numerical precision) |
| 727 | +;; 4. Get our original signal back (within numerical precision, typically ~1e-10 RMSE) |
728 | 728 | ;; |
729 | 729 | ;; Next, let's use this round-trip capability to build practical tools. |
730 | 730 |
|
|
820 | 820 | ;; concentrates in low frequencies. The **Discrete Cosine Transform (DCT)** uses only cosines, |
821 | 821 | ;; which for smooth signals concentrates energy even more efficiently than FFT. |
822 | 822 | ;; |
823 | | -;; This is why JPEG compresses images with DCT, not FFT. Let's see why. |
| 823 | +;; This is why classic JPEG (baseline) compresses images with DCT, not FFT. Let's see why. |
824 | 824 |
|
825 | 825 | ;; ### Understanding DCT as Cosine Decomposition |
826 | 826 | ;; |
|
836 | 836 | ;; |
837 | 837 | ;; Smooth signals have most energy in low frequencies. |
838 | 838 | ;; DCT with cosine-only basis captures this more efficiently than DFT. |
839 | | -;; Result: [JPEG](https://en.wikipedia.org/wiki/JPEG), [MP3](https://en.wikipedia.org/wiki/MP3), |
840 | | -;; and modern codecs all use DCT! |
| 839 | +;; Result: [JPEG](https://en.wikipedia.org/wiki/JPEG) (baseline), [MP3](https://en.wikipedia.org/wiki/MP3) |
| 840 | +;; (using Modified DCT), and many modern codecs use DCT or DCT variants! |
841 | 841 |
|
842 | 842 | ;; ### Energy Concentration Example |
843 | 843 |
|
|
977 | 977 |
|
978 | 978 | ;; **What we just learned**: The DCT uses only cosines (instead of FFT's sines + cosines), |
979 | 979 | ;; which concentrates energy more efficiently for smooth signals. This makes it ideal for |
980 | | -;; compression—JPEG and MP3 both exploit this property. |
| 980 | +;; compression—classic JPEG and MP3 (via Modified DCT) both exploit this property. |
981 | 981 | ;; |
982 | 982 | ;; But both FFT and DCT have a fundamental limitation: they use **global basis functions** |
983 | 983 | ;; that span the entire signal. A sine wave at 10 Hz in the FFT basis oscillates across the |
|
1316 | 1316 |
|
1317 | 1317 | ;; ### 2D DCT Basis Functions |
1318 | 1318 | ;; |
1319 | | -;; Understanding how JPEG compression works: visualizing the 8×8 DCT basis images. |
1320 | | -;; Each 8×8 block in JPEG is decomposed into a weighted sum of these 64 basis patterns. |
| 1319 | +;; Understanding how baseline JPEG compression works: visualizing the 8×8 DCT basis images. |
| 1320 | +;; Each 8×8 block in baseline JPEG is decomposed into a weighted sum of these 64 basis patterns. |
1321 | 1321 |
|
1322 | 1322 | ;; 2D DCT-II basis function |
1323 | 1323 | (defn dct-2d-basis |
|
1356 | 1356 | :=mark-color :value |
1357 | 1357 | :=facet-x :freq-u |
1358 | 1358 | :=facet-y :freq-v |
1359 | | - :=title "2D DCT Basis Functions (JPEG uses these!)" |
| 1359 | + :=title "2D DCT Basis Functions (Baseline JPEG uses these!)" |
1360 | 1360 | :=width 600 |
1361 | 1361 | :=height 600}) |
1362 | 1362 | (plotly/layer-point {:=mark-symbol "square"})) |
|
1365 | 1365 | ;; - Top-left (0,0) = DC component (constant, average brightness) |
1366 | 1366 | ;; - Moving right → increasing horizontal frequency |
1367 | 1367 | ;; - Moving down → increasing vertical frequency |
1368 | | -;; - JPEG keeps low-frequency basis (top-left) and discards high-frequency (bottom-right) |
1369 | | -;; - This is why JPEG works well for natural images (most energy in low frequencies) |
| 1368 | +;; - Baseline JPEG keeps low-frequency basis (top-left) and discards high-frequency (bottom-right) |
| 1369 | +;; - This is why JPEG compression works well for natural images (most energy in low frequencies) |
1370 | 1370 |
|
1371 | 1371 | ;; ## Part 7: Practical Applications |
1372 | 1372 |
|
|
1701 | 1701 | :speedup "~2-3x vs non-power-of-2"} |
1702 | 1702 |
|
1703 | 1703 | {:tip "Use dtype-next" |
1704 | | - :reason "Vectorized operations, lazy evaluation" |
1705 | | - :speedup "10-100x vs seq operations"} |
| 1704 | + :reason "Vectorized operations, avoids boxing" |
| 1705 | + :speedup "Often 10-100x vs boxed seq operations"} |
1706 | 1706 |
|
1707 | 1707 | {:tip "Avoid boxing" |
1708 | 1708 | :reason "Type hints for primitive arrays" |
|
0 commit comments