Multifractal Art-Texture Pipeline
User Guide & Reference Manual
Operational reference for the 8-script Python pipeline that computes multifractal texture descriptors (D₀, D₁, D₂, spectrum width, α₀, asymmetry) for a labelled image dataset, then runs distributional, comparative, and clustering analysis on the results — the engine behind the AI / animal / cave / human / mathematical / photographic art comparison.
01. Project status and scope
CodeFiles.zip: one core measurement engine (07_Multifractal.py), one independent spectrum-visualisation engine (01_generate_f_alpha_spectra_manyPictures.py), and five downstream analysis scripts (02–06) that all read the engine's output table. There is no orchestrator script or CLI wrapper — each file is run individually with python script_name.py, and each edits its own constants block at the top rather than taking command-line arguments.| Canonical principle | Operational meaning |
|---|---|
| File numbering is a reading order, not a run order | 07_Multifractal.py must run first — it produces multifractal_stable_results.csv, the single input every script 02–06 depends on. See §02 for the actual execution order. |
| One shared results table, many independent views | Scripts 02–06 never talk to each other. Each loads multifractal_stable_results.csv fresh, computes its own view (distribution, pairplot, boxplot, statistics table, or clustering), and writes to its own output folder. |
| Configuration lives in code, not in a config file | Dataset path, image size, box-counting scales, and colour palettes are Python constants near the top of each script. Editing a run means editing the script, not passing flags. |
02. Pipeline architecture and execution order
07_Multifractal.py is the file that must run first, despite being numbered last — its filename reflects when it was last edited during development, not its place in the pipeline. 01_generate_f_alpha_spectra_manyPictures.py is numbered first but is an optional, self-contained side branch: it reads the same raw images independently and does not feed, or depend on, any other script. Two files are both numbered 06 (06_dendogram.py and 06_shapiro_analysis.py); they are alternatives that both consume 07's output and can run in either order relative to each other.03. Quick-start command map
| Step | Command |
|---|---|
| 0. Install dependencies | pip install opencv-python numpy pandas matplotlib seaborn scipy scikit-learn |
| 1. Point the engine at your dataset | Edit the last line of 07_Multifractal.py: main(r"path\to\dataset") (default is a hardcoded Windows path, G:\Fractals\dataset — see §11) |
| 2. Run the core engine | python 07_Multifractal.py |
| 3. Distribution shape (KDE, normal fit, Q-Q) | python 02_extract_spectrum_width_distribution.py |
| 4. Scatter matrix & correlations | python 03_generate_pairplot_enhanced.py |
| 5. Publication-ready spectrum-width plots | python 04_generate_spectrum_width_plot.py |
| 6. Full statistics table + significance report | python 05_spectrum_width_statistics.py |
| 7. Normality testing (Shapiro–Wilk) | python 06_shapiro_analysis.py |
| 8. Hierarchical clustering / dendrogram | python 06_dendogram.py |
| Optional. Per-image f(α) spectra (independent) | Edit root_dataset in 01_generate_f_alpha_spectra_manyPictures.py, then python 01_generate_f_alpha_spectra_manyPictures.py |
python script.py invocation. Everything that varies between runs (dataset path, output folder, colour palette, box-counting scales) is a constant near the top of the file.04. Dataset and folder conventions
Expected layout
dataset_root/ ├─ AI/ │ ├─ AI_ (1).jpg │ ├─ AI_ (2).jpg │ └─ ... ├─ Animals/ ├─ Caves/ ├─ Human/ ├─ Math/ └─ Photos/
Each immediate subfolder of dataset_root becomes one category (the label column in the results table); its name is used verbatim, case included. Files are matched by extension: .png .jpg .jpeg .bmp .tiff (case-insensitive). The reference run shipped with this package used exactly six categories — AI (19)Animals (24)Caves (35)Human (16)Math (23)Photos (25) — but the pipeline places no limit on category count or naming; every downstream script reads categories dynamically from the data.
SAVE_PREPROCESSED = True (the default), 07_Multifractal.py writes a preprocessed_images/ subfolder of diagnostic PNGs inside every category folder. Because the engine walks dataset_root recursively, rerunning it on a dataset that still contains a previous run's preprocessed_images/ folders will pick those PNGs up as a spurious seventh category named preprocessed_images. Delete or move those subfolders (or set SAVE_PREPROCESSED = False) before rerunning on the same folder.05. Stage 1 — image-level engines
The core measurement engine. For every image: computes a sparse gradient-magnitude binary mask and box-counts it for D₀; builds a normalised gradient-magnitude measure and runs the partition-function / Legendre-transform pipeline for D₁, D₂, spectrum width, α₀, and asymmetry. Also produces a first-pass box/pairplot/heatmap figure set and prints Kruskal–Wallis and pairwise Mann–Whitney results to the console.
OUTPUT (in the working directory): multifractal_stable_results.csv ← the file every other script needs multifractal_summary.csv (groupby-describe per category) multifractal_stable_boxplots.png multifractal_stable_pairplot.png multifractal_stable_heatmap.png OUTPUT (inside dataset_root/<category>/preprocessed_images/, if SAVE_PREPROCESSED=True): <file>_01_original.png <file>_02_gradient_magnitude.png <file>_03_sparse_binary_D0.png <file>_04_probability_measure.png
Edit main(r"G:\Fractals\dataset") at the bottom of the file to point at your dataset. Runtime scales with image count and NUM_SCALES; the box-counting and partition-function loops are pure Python/NumPy, not vectorised across images.
Independent of the engine above (it recomputes its own gradient measure and its own D₁/D₂/spectrum, using the same maths but a slightly different scale range, MIN_BOX_DQ only, not MIN_BOX_D0). Its purpose is visual: a full, fine-grained f(α) singularity spectrum curve per image, not just the six summary numbers.
OUTPUT (in multifractal_spectra/): <category>/<file>_f_alpha.png (one high-res plot per image) <category>_composite_spectra.png (all images of a category, overlaid) master_composite_spectra.png (mean ± s.d. curve per category) f_alpha_curves.csv (raw α, f(α) points for every image)
master_composite_spectra.png) only has explicit colours/line-styles for four legacy category names (math, mondrian, pollock, vangogh). Categories outside that list (including the current AI, Animals, Caves, Human, Math, Photos scheme) still plot, but all in black with a solid line, so they overlap visually in that one figure. The per-image and per-category-composite outputs are unaffected.06. Stage 2 — downstream analysis scripts
Per-category kernel density estimate of spectrum width, an enhanced version with a normal-distribution overlay, and Q–Q plots for a quick-look normality check ahead of the formal test in 06_shapiro_analysis.py.
OUTPUT in 02_extract_spectrum_width_distribution_output/: spectrum_width_distribution.png spectrum_width_distribution_enhanced.png spectrum_width_distribution_enhanced_per_artist.png spectrum_width_qq_plots.png
The full 4×4 scatter matrix across D₀, D₁, D₂, spectrum width with per-category colour and marker coding, a focused subplot isolating spectrum width against each dimension, and per-category correlation heatmaps.
OUTPUT in 03_generate_pairplot_enhanced_output/: multifractal_stable_pairplot.png (4×4 scatter matrix, 600 DPI) spectrum_width_vs_dimensions.png correlation_heatmaps.png
Publication-style spectrum-width figures: a boxplot with significance-ready layout, a violin+strip alternative, and a combined 2×2 panel of all four core metrics.
OUTPUT in 04_generate_spectrum_width_plot_output/: spectrum_width_boxplot.png spectrum_width_violin_plot.png all_metrics_combined.png spectrum_width_summary.csv
The full descriptive + inferential statistics table for spectrum width: N, mean, s.d., SEM, 95% CI, median, min/max, quartiles, IQR, coefficient of variation, and Shapiro–Wilk p, plus a Bonferroni-corrected pairwise Mann–Whitney report. Console output is mirrored to a text file via a small Tee class.
OUTPUT in 05_spectrum_width_statistics_output/: spectrum_width_statistics.csv spectrum_width_statistics_report.txt (full console transcript)
The script prints step-by-step instructions for pasting the CSV into Word via Excel — useful if the table is going straight into a manuscript.
Shapiro–Wilk normality test for all six metrics (D₀, D₁, D₂, spectrum width, α₀, asymmetry), grouped by category, with a colour-coded pivot table and an explicit parametric-vs-non-parametric recommendation based on the results. The only script in the package with no functions and no output folder — it runs top to bottom and writes to the working directory.
OUTPUT (in the working directory): shapiro_wilk_results.csv
Standardises category-mean metric vectors (z-scores), then builds a hierarchical clustering dendrogram (Ward linkage), a pairwise distance heatmap, a standardised-feature heatmap, a silhouette analysis to suggest a cluster count, and a metrics bar chart with error bars.
OUTPUT in hierarchical_clustering_output/: dendrogram.png distance_heatmap.png feature_heatmap.png silhouette_analysis.png metrics_barplot.png distance_matrix.csv standardised_means.csv
07. Multifractal parameter reference
Preprocessing (shared by every script that touches raw images)
Capacity dimension, D₀
Generalised dimensions Dq and the singularity spectrum
Spectrum width, Δα, and singularity strength, α₀
Asymmetry
D₀/D₁/D₂ describe overall density, spatial uniformity, and clustering of marks and, in the reference run, varied only moderately across categories. Δα, α₀, and asymmetry proved the stronger discriminators between categories in that same run — see §10 for the actual numbers.08. Pipeline configuration reference
| Constant | Reference value | Meaning |
|---|---|---|
IMG_SIZE | 512 | Every image is resized to this square before any measurement. |
SPARSITY_PERCENT | 0.7 | Percentage of strongest-gradient pixels kept for the D₀ binary mask. |
MIN_BOX_D0 | 32 | Smallest box size for the D₀ box-counting sweep. |
MIN_BOX_DQ | 16 | Smallest box size for the Dq partition-function sweep. |
NUM_SCALES | 8 | Number of logarithmically spaced box sizes between the minimum and min(h,w)//2. |
R2_THRESHOLD | 0.9 | Minimum R² for a log–log fit to be accepted; scales failing this are dropped (contribute NaN) rather than silently biasing the slope. |
Q_VALUES | linspace(-5, 5, 21) | Moment orders swept when building τ(q). (Note: the standalone spectrum script, 01, uses this same range; it is independent of the paper-level pipeline's wider ±10 sweep if you have edited one without the other — keep them in sync if exact comparability matters.) |
SAVE_PREPROCESSED | True | Whether to write the four diagnostic PNGs per image (see §11 for the rerun caveat this creates). |
01_generate_f_alpha_spectra_manyPictures.py duplicates this configuration block rather than importing it from 07. If you change scale parameters in one file for a new analysis, the other will silently keep computing on the old settings unless you edit it too.09. Output interpretation map
| Script | Output location | Key files |
|---|---|---|
07_Multifractal.py | working directory + dataset_root/<cat>/preprocessed_images/ | multifractal_stable_results.csv, multifractal_summary.csv |
01_generate_f_alpha_spectra_manyPictures.py | multifractal_spectra/ | f_alpha_curves.csv, per-image and per-category spectrum PNGs |
02_extract_spectrum_width_distribution.py | 02_extract_spectrum_width_distribution_output/ | KDE, enhanced KDE, Q–Q PNGs |
03_generate_pairplot_enhanced.py | 03_generate_pairplot_enhanced_output/ | pairplot, focused subplot, correlation heatmaps |
04_generate_spectrum_width_plot.py | 04_generate_spectrum_width_plot_output/ | boxplot, violin plot, combined panel, spectrum_width_summary.csv |
05_spectrum_width_statistics.py | 05_spectrum_width_statistics_output/ | spectrum_width_statistics.csv, spectrum_width_statistics_report.txt |
06_shapiro_analysis.py | working directory | shapiro_wilk_results.csv |
06_dendogram.py | hierarchical_clustering_output/ | dendrogram, heatmaps, silhouette, distance_matrix.csv, standardised_means.csv |
multifractal_stable_results.csv directly). Pull numbers for a manuscript or report from those CSVs rather than reading values off the rendered plots.10. Sample results from the reference run
| Category | N | Mean Δα | Std Dev | 95% CI | Shapiro–Wilk p |
|---|---|---|---|---|---|
| AI | 19 | 1.382 | 0.604 | 1.091 – 1.674 | 0.0316 |
| Animals | 24 | 1.102 | 0.426 | 0.922 – 1.282 | 0.5693 |
| Caves | 35 | 0.767 | 0.438 | 0.622 – 0.912 | 0.0003 |
| Human | 16 | 0.613 | 0.240 | 0.485 – 0.740 | 0.2935 |
| Math | 23 | 0.863 | 0.415 | 0.684 – 1.043 | 0.7846 |
| Photos | 25 | 1.120 | 0.489 | 0.918 – 1.322 | 0.6913 |
Reproduced from spectrum_width_statistics.csv, the output of 05_spectrum_width_statistics.py on the shipped multifractal_stable_results.csv.
11. Known issues before you rerun
02, 03, 04, and 06_dendogram.py each hardcode a DEFAULT_COLORS dictionary keyed to an earlier dataset's category names (math, mondrian, pollock, vangogh, grigorescu, all lower-case). None of the current category names (AI, Animals, Caves, Human, Math, Photos) match — the comparison is case-sensitive — so every current category falls through to a 5-colour cycling fallback list. With 6 categories and 5 fallback colours, this was verified to assign AI and Photos the identical colour (#8c564b) in every colour-coded plot from these four scripts. Fix by adding the current category names to each script's DEFAULT_COLORS/ARTIST_COLOURS dict, or extend EXTRA_COLORS to at least as many entries as you have categories.07_Multifractal.py (main(r"G:\Fractals\dataset")) and 01_generate_f_alpha_spectra_manyPictures.py (root_dataset = r"G:\Fractals\dataset") point at a specific local drive. Update both before running on another machine; there is no environment variable or config file fallback.preprocessed_images/ subfolders left by a previous 07_Multifractal.py run will be picked up as a spurious category if the engine is rerun on the same dataset folder without cleaning up first.06 numbering is intentional, not a mistake. 06_dendogram.py and 06_shapiro_analysis.py are independent analyses that both depend only on 07's output; nothing breaks if you run them in either order, or only run one of them.12. Recommended workflow
- Organise the dataset as
dataset_root/<category>/<image files>, per §04. - Point
07_Multifractal.pyat the dataset and run it. Confirmmultifractal_stable_results.csvcontains one row per image and the expected category counts before continuing. - Run
06_shapiro_analysis.pyto establish, per category and metric, whether parametric or non-parametric statistics are appropriate for everything downstream. - Run
05_spectrum_width_statistics.pyfor the full descriptive table and the Bonferroni-corrected pairwise comparisons; this is normally the table that goes straight into a manuscript. - Run
02,03, and04for the distributional, correlational, and publication-plot views — independent of each other, any order. - Run
06_dendogram.pyonce you want a cross-category structural summary (clustering, distances, silhouette) rather than a per-metric comparison. - Optionally run
01_generate_f_alpha_spectra_manyPictures.pyif you need full f(α) curves for a figure, not just the six summary numbers per image. - Pull all reported numbers from the CSVs (§09), and check §11 for the colour-palette and rerun caveats before trusting a colour-coded plot at face value.
13. Scientific interpretation boundaries
Human, AI) describes whatever images were placed in that folder for a given run — a single artist, an unspecified set of generative models, a particular scrape of photographs. Findings about a category describe that specific sample, not the category in general, unless the dataset composition is documented alongside the results.