Reference edition · compiled from the current script package

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.

142images, 6 categories
8pipeline scripts
1 → 6engine, then 5 analyses
6metrics per image

01. Project status and scope

What this package does, in one pass
Package basis. This manual documents the eight numbered scripts shipped together in 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 (0206) 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 principleOperational meaning
File numbering is a reading order, not a run order07_Multifractal.py must run first — it produces multifractal_stable_results.csv, the single input every script 0206 depends on. See §02 for the actual execution order.
One shared results table, many independent viewsScripts 0206 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 fileDataset 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

The graph of which script must run before which
labelled image dataset (dataset_root/<category>/*.jpg) 07_Multifractal.py ← run this first, always writes multifractal_stable_results.csv + multifractal_summary.csv ├───> 02_extract_spectrum_width_distribution.py KDE / normal-fit / Q-Q ├───> 03_generate_pairplot_enhanced.py 4×4 scatter matrix, correlations ├───> 04_generate_spectrum_width_plot.py box / violin / combined plots ├───> 05_spectrum_width_statistics.py CI, IQR, CV, Mann-Whitney report ├───> 06_shapiro_analysis.py normality per category × metric └───> 06_dendogram.py clustering, distances, silhouette (independent branch, same source images, own output — run any time) labelled image dataset 01_generate_f_alpha_spectra_manyPictures.py per-image f(α) curves + composites
The numbering is misleading. 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

Run from the folder containing all eight scripts
StepCommand
0. Install dependenciespip install opencv-python numpy pandas matplotlib seaborn scipy scikit-learn
1. Point the engine at your datasetEdit 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 enginepython 07_Multifractal.py
3. Distribution shape (KDE, normal fit, Q-Q)python 02_extract_spectrum_width_distribution.py
4. Scatter matrix & correlationspython 03_generate_pairplot_enhanced.py
5. Publication-ready spectrum-width plotspython 04_generate_spectrum_width_plot.py
6. Full statistics table + significance reportpython 05_spectrum_width_statistics.py
7. Normality testing (Shapiro–Wilk)python 06_shapiro_analysis.py
8. Hierarchical clustering / dendrogrampython 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
No arguments to remember. None of the eight scripts take command-line flags — each is a plain 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

What the engine expects on disk

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.

Don't rerun the engine over its own output. When 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 only two scripts that touch raw images
07_Multifractal.py

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.

01_generate_f_alpha_spectra_manyPictures.py

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)
Stale category list in the master composite. The mean-curve overlay (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

All five read multifractal_stable_results.csv and only that file
02_extract_spectrum_width_distribution.py

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
03_generate_pairplot_enhanced.py

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
04_generate_spectrum_width_plot.py

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
05_spectrum_width_statistics.py

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.

06_shapiro_analysis.py

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
06_dendogram.py

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

What the six output columns actually measure

Preprocessing (shared by every script that touches raw images)

image → grayscale, resized to 512×512 gradient magnitude = √(Sobel_x² + Sobel_y²) measure(x,y) = gradient_magnitude(x,y) / Σ gradient_magnitude  # normalised to a probability measure

Capacity dimension, D₀

binary mask = pixels in the top SPARSITY_PERCENT of gradient magnitude N(ɛ) = number of non-empty boxes of size ɛ covering the mask D₀ = − slope of log N(ɛ) vs log ɛ (least-squares fit, capped at 2.0)

Generalised dimensions Dq and the singularity spectrum

Z(q,s) = Σboxes μ(box)q  # partition function over the probability measure τ(q) = − slope of log Z(q,s) vs log(1/s), accepted only if R² ≥ R2_THRESHOLD Dq = τ(q) / (q−1), q ≠ 1  D₁ = limq→1 , taken as the derivative of a spline fit to τ(q) at q=1 α(q) = dτ/dq f(α) = q·α(q) − τ(q)  # Legendre transform → the f(α) singularity spectrum

Spectrum width, Δα, and singularity strength, α₀

α₀ = α at the peak of f(α)  # the most probable local scaling exponent Δα = αmax − αmin  # heterogeneity of local scaling; wide = many different roughness regimes

Asymmetry

left = α₀ − αmin right = αmax − α₀ asymmetry = left / right  # ≈1 = symmetric spectrum; >1 = long left tail; <1 = long right tail
Reading the six columns together. 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

Constants block, 07_Multifractal.py
ConstantReference valueMeaning
IMG_SIZE512Every image is resized to this square before any measurement.
SPARSITY_PERCENT0.7Percentage of strongest-gradient pixels kept for the D₀ binary mask.
MIN_BOX_D032Smallest box size for the D₀ box-counting sweep.
MIN_BOX_DQ16Smallest box size for the Dq partition-function sweep.
NUM_SCALES8Number of logarithmically spaced box sizes between the minimum and min(h,w)//2.
R2_THRESHOLD0.9Minimum R² for a log–log fit to be accepted; scales failing this are dropped (contribute NaN) rather than silently biasing the slope.
Q_VALUESlinspace(-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_PREPROCESSEDTrueWhether to write the four diagnostic PNGs per image (see §11 for the rerun caveat this creates).
Keep engine and viewer in sync. 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

Where results land, script by script
ScriptOutput locationKey files
07_Multifractal.pyworking directory + dataset_root/<cat>/preprocessed_images/multifractal_stable_results.csv, multifractal_summary.csv
01_generate_f_alpha_spectra_manyPictures.pymultifractal_spectra/f_alpha_curves.csv, per-image and per-category spectrum PNGs
02_extract_spectrum_width_distribution.py02_extract_spectrum_width_distribution_output/KDE, enhanced KDE, Q–Q PNGs
03_generate_pairplot_enhanced.py03_generate_pairplot_enhanced_output/pairplot, focused subplot, correlation heatmaps
04_generate_spectrum_width_plot.py04_generate_spectrum_width_plot_output/boxplot, violin plot, combined panel, spectrum_width_summary.csv
05_spectrum_width_statistics.py05_spectrum_width_statistics_output/spectrum_width_statistics.csv, spectrum_width_statistics_report.txt
06_shapiro_analysis.pyworking directoryshapiro_wilk_results.csv
06_dendogram.pyhierarchical_clustering_output/dendrogram, heatmaps, silhouette, distance_matrix.csv, standardised_means.csv
Rebuild from CSV, not from PNGs. Every figure-producing script also writes its underlying numbers to CSV (or reuses 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

142 images · 6 categories · the run shipped with this package
CategoryNMean ΔαStd Dev95% CIShapiro–Wilk p
AI191.3820.6041.091 – 1.6740.0316
Animals241.1020.4260.922 – 1.2820.5693
Caves350.7670.4380.622 – 0.9120.0003
Human160.6130.2400.485 – 0.7400.2935
Math230.8630.4150.684 – 1.0430.7846
Photos251.1200.4890.918 – 1.3220.6913

Reproduced from spectrum_width_statistics.csv, the output of 05_spectrum_width_statistics.py on the shipped multifractal_stable_results.csv.

Hierarchical clustering dendrogram of the six categories
06_dendogram.py → dendrogram.png. Ward-linkage hierarchical clustering of the six category means (standardised across all six metrics).
Pairwise distance heatmap between category means
06_dendogram.py → distance_heatmap.png. Pairwise Euclidean distance between category-mean metric vectors.
Q-Q plots of spectrum width per category
02_extract_spectrum_width_distribution.py → spectrum_width_qq_plots.png. Q–Q plots per category; departures from the red line flag non-normality (compare to the Shapiro–Wilk p-values above — Caves departs visibly, matching p = 0.0003).
KDE of spectrum width per category
02_extract_spectrum_width_distribution.py → spectrum_width_distribution.png. Kernel density estimate of spectrum width per category, with per-category means marked.
Per-category spectrum width distributions with normal fit
02_extract_spectrum_width_distribution.py → spectrum_width_distribution_enhanced_per_artist.png. Same distributions, one panel per category, each with its fitted normal curve for visual comparison against the KDE.

11. Known issues before you rerun

Verified against the shipped scripts and the shipped results table
Colour palettes silently collide on the current category names. 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.
Hardcoded Windows dataset path. Both 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.
Rerunning the engine over its own output. See §04 — 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.
Duplicate 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.
No CLI, no config file. All eight scripts take zero command-line arguments. To change dataset path, output folder, DPI, or scale parameters, edit the constants block at the top of the relevant file directly.

12. Recommended workflow

From a raw image folder to a reportable set of statistics and figures
  1. Organise the dataset as dataset_root/<category>/<image files>, per §04.
  2. Point 07_Multifractal.py at the dataset and run it. Confirm multifractal_stable_results.csv contains one row per image and the expected category counts before continuing.
  3. Run 06_shapiro_analysis.py to establish, per category and metric, whether parametric or non-parametric statistics are appropriate for everything downstream.
  4. Run 05_spectrum_width_statistics.py for the full descriptive table and the Bonferroni-corrected pairwise comparisons; this is normally the table that goes straight into a manuscript.
  5. Run 02, 03, and 04 for the distributional, correlational, and publication-plot views — independent of each other, any order.
  6. Run 06_dendogram.py once you want a cross-category structural summary (clustering, distances, silhouette) rather than a per-metric comparison.
  7. Optionally run 01_generate_f_alpha_spectra_manyPictures.py if you need full f(α) curves for a figure, not just the six summary numbers per image.
  8. 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

What these numbers do and do not support on their own
Single pipeline, single formalism. All six metrics come from one box-counting / partition-function implementation. They have not been cross-validated here against an independent multifractal method (e.g. detrended fluctuation analysis), so absolute values should be treated as specific to this pipeline rather than universal constants of the images.
Category composition affects category-level claims. A category label (e.g. 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.
Imaging conditions are a confound the pipeline does not control for. Lighting, resolution, compression, and cropping differ across source images and are not normalised beyond the fixed 512×512 resize and grayscale conversion. Differences in multifractal statistics between categories can reflect these conditions as much as the underlying artwork.
No claim about the maker. D₀/D₁/D₂, spectrum width, α₀, and asymmetry are texture statistics. On their own they do not establish intentionality, skill, or any cognitive or experiential property of whoever or whatever produced the image; any such interpretation requires an explicit, separately argued inferential framework, not just these six numbers.