Fractal dimension analysis platform for composite material microstructure research. Built to support the study of pore evolution in eco-friendly curing agent and cement-stabilised loess composites.
This repository is a curated public edition created for the Sampo AI OS portfolio. It is intended to be a clean, reviewable product showcase rather than a raw copy of the original internal working folder.
See PUBLIC_EDITION_SCOPE.md for what is included here and what was intentionally excluded from the original source project.
Scientific basis: Dynamic Response and Pore Evolution Mechanism of Composite Improved Loess Using an Eco-Friendly Curing Agent and Cement — Tianshui Normal University
FractalScope computes the box-counting fractal dimension (D) of composite material microstructures from SEM/TEM images. The D-value is a single number (1.0–3.0) that quantifies the geometric complexity of a material's pore network — a higher D means a denser, more interconnected microstructure.
| D range | Classification | Meaning |
|---|---|---|
| < 1.7 | Linear Structure | Weakly cemented, minimal porosity branching |
| 1.7–2.0 | Porous Network | Natural loess, lightly treated composites |
| 2.0–2.4 | Composite Matrix | Cement-stabilised with interlocking pore networks |
| > 2.4 | Dense Composite | Full hydration, hierarchical pore collapse |
- Materials scientists studying composite soil stabilisation
- Civil engineers researching eco-friendly curing agents
- R&D labs working on loess, ceramics, or polymer microstructure characterisation
┌─────────────────────────────────────────────────────┐
│ Browser (React 18 + D3.js) │
│ • Material catalogue with auto-generated IDs │
│ • SEM/TEM image drop-zone │
│ • D3 gauge + trend chart │
└──────────────────┬──────────────────────────────────┘
│ HTTP
┌──────────────────▼──────────────────────────────────┐
│ FastAPI (main.py) │
│ GET / → SPA frontend │
│ GET /health → status check │
│ POST /analyze/{id} → simulation mode │
│ POST /analyze-image/{id} → real image mode │
└──────┬───────────────────────┬──────────────────────┘
│ │
┌──────▼──────┐ ┌───────────▼────────────────────┐
│ fractal_ │ │ FractalAnalysisService.py │
│ analysis.py │ │ • Simulation fallback │
│ • PIL/OpenCV│ │ • DB persistence (SQLAlchemy) │
│ • Otsu │ └───────────┬────────────────────┘
│ threshold │ │
│ • Box- │ ┌───────────▼────────────────────┐
│ counting │ │ material_metadata.py │
│ • log-log │ │ • Material ORM model │
│ regression│ │ • SQLite (dev) / PostgreSQL │
└─────────────┘ └────────────────────────────────┘
# 1. Clone and enter project
cd fractal-scope
# 2. Create virtual environment
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux/macOS
# 3. Install dependencies
pip install -r requirements.txt
# 4. Run tests
pytest tests/ -v
# 5. Start server
uvicorn main:app --host 0.0.0.0 --port 8000 --reload \
--reload-dir . --reload-exclude ".venv"Open http://localhost:8000 in your browser.
Supported Python version for the reproducible local and Docker path is currently 3.11+.
docker compose up --buildThe app starts on port 8000, PostgreSQL on port 5432. The app waits for the database healthcheck before starting.
If those host ports are already in use, override them without editing the compose file:
$env:APP_PORT=18000
$env:DB_PORT=15432
docker compose up --buildThe container still listens internally on ports 8000 and 5432. Only the host-side bindings change.
docker compose down # stop
docker compose down -v # stop + delete database volumeChoose from the Material Catalogue dropdown in the sidebar. The ID is auto-generated in a standardised format:
| Preset | ID format example |
|---|---|
| Untreated Loess | LOESS-NAT-001 |
| Cement-Stabilised Loess | LOESS-CEM-001 |
| Eco-Agent Treated Loess | LOESS-ECO-5PCT-001 |
| Eco-Agent + Cement | LOESS-ECO-CEM-5PCT-001 |
| Ceramic Matrix | CER-001 |
| Custom | manually entered |
For eco-agent presets, set the concentration slider (0–20%) to match your specimen.
Drop a greyscale or colour SEM/TEM image onto the drop zone, or click to browse. Supported formats: PNG, TIFF, JPG, BMP.
- With image: real box-counting analysis via OpenCV + NumPy → result badge shows Real Image
- Without image: random simulation for demonstration → badge shows Simulation
Optional free-text field for curing conditions, temperature, loading frequency etc. The notes are shown in the current browser session and included in the local Recent Analyses view.
Click 🔬 Analyze SEM Image (or Run Simulation). Results appear immediately:
- Gauge needle moves to computed D-value
- Classification chip and interpretation text update
- Entry added to the local Recent Analyses history in the UI
- Fractal result persisted to the database
├── main.py # FastAPI app, endpoint routing
├── fractal_analysis.py # Core algorithm: image load → box-counting → D
├── FractalAnalysisService.py # Service layer: simulation + DB persistence
├── material_metadata.py # SQLAlchemy ORM: Material table schema
├── requirements.txt # Pinned production dependencies
├── pytest.ini # pytest configuration (asyncio_mode=auto)
├── Dockerfile # Production container image
├── docker-compose.yml # App + PostgreSQL orchestration
├── frontend/
│ └── index.html # Single-page app (React 18 + D3.js v7)
├── tests/
│ ├── test_fractal.py # Unit tests: image loading, box-counting
│ └── test_analysis.py # Integration tests: service + SQLite
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Serves the frontend SPA |
GET |
/health |
{"status": "ok"} |
GET |
/docs |
Swagger UI (auto-generated) |
POST |
/analyze/{material_id} |
Simulation analysis, persists to DB |
POST |
/analyze-image/{material_id} |
Real image analysis (multipart/form-data, field: file) |
Example — image upload via curl:
curl -X POST "http://localhost:8000/analyze-image/LOESS-NAT-001" \
-F "file=@sample_sem.png"
# → {"material_id": "LOESS-NAT-001", "fractal_dim": 1.87, "source": "image"}pytest tests/ -vThe current curated public edition passes 6 automated tests.
Tests use SQLite in-memory — no database setup required.
The current implementation is a working demo. Below are the concrete next steps to turn it into a production research tool, roughly in priority order.
- AI-assisted segmentation — replace Otsu thresholding with a lightweight U-Net or scikit-image
morphological_chan_veseto better separate pore space from solid matrix in noisy SEM images - Multi-scale box-counting — extend box sizes beyond 8–64px to cover the full image resolution; currently only 4 data points are used for the log-log regression
- Batch upload — accept a ZIP of images for a single specimen, average the D-values, report standard deviation
- Image preview in UI — show the uploaded image alongside its binarised version so the researcher can verify segmentation quality before accepting the result
- Export to CSV/Excel — download all analysis results with metadata for use in statistical analysis (R, SPSS, Origin)
- Export to PDF report — auto-generated per-specimen report with gauge, interpretation, and lab notes
- Specimen comparison view — side-by-side D-value comparison across multiple IDs (e.g. 0% vs 5% vs 10% eco-agent concentration)
- Database migrations — add Alembic for schema versioning so production data survives upgrades
- Calibration mode — upload images with known D-values (synthetic fractals) to validate algorithm accuracy
- Literature reference values — display expected D-range for each material type based on published papers
- Uncertainty estimation — report confidence interval on D using bootstrap resampling across box sizes
- Correlation with mechanical properties — if stress-strain data is available, show scatter plot of D vs. compressive strength
- User authentication — simple API key or OAuth2 so multiple researchers can share the same instance without overwriting each other's results
- Project/experiment grouping — organise analyses into named experiments (e.g. "28-day curing series")
- Public cloud deployment — Dockerfile + docker-compose are ready; configure a reverse proxy (Nginx/Caddy) with HTTPS for institutional access
- Federated data — allow partner institutions to contribute results without sharing raw images (privacy-preserving)
| Package | Version | Purpose |
|---|---|---|
fastapi |
0.111.0 | REST API framework |
uvicorn |
0.29.0 | ASGI server |
sqlalchemy |
2.0.36 | ORM, database abstraction |
numpy |
2.4.4 | Numerical array operations |
Pillow |
12.2.0 | Image loading |
opencv-python |
4.13.0.92 | Otsu thresholding, image preprocessing |
psycopg2-binary |
2.9.7 | PostgreSQL driver (production) |
aiofiles |
23.2.1 | Async file I/O |
python-multipart |
0.0.9 | Multipart file upload parsing |
pytest + pytest-asyncio |
latest | Test framework |
The fractal dimension D is computed using the box-counting method:
where
The image is first converted to a binary mask using Otsu's method (OpenCV), which automatically finds the optimal threshold to separate pore space (black) from solid matrix (white) without manual parameter tuning.
Reference range for loess composites (from the source paper):
- Untreated loess: D ≈ 1.85–2.05
- Cement-stabilised (7%): D ≈ 2.15–2.35
- Eco-agent + cement: D ≈ 2.30–2.55
This repository is the curated public edition prepared for publication. The original working project contained additional internal staging materials that were intentionally excluded from this copy.
