pyPetrograph is a machine-learning toolkit for thin-section petrography. You can hand-label features on an RGB photo and train a LightGBM pixel model to map area % of each class (porosity in the demo). You can count grains on a painted mineral map by matching a color legend. You can stack several image types of the same field (PPL, XPL, or others) and segment grains from that overlay (experimental, incomplete). Or, from an Advanced Mineral Identification and Characterization System (AMICS) set (BSE, mineral map, and legend), you can outline grains on the BSE and name each grain from the map.
Warning
This tool is under development. Use with caution. Notebooks, APIs, and numbers can still change.
Note
For more careful grain-boundary detection and mapping, there is optional support for using SegmentEveryGrain developed by Zoltan Sylvester. SAM 2.1 weights (~860 MB) are a further opt-in on their page, not part of the default install.
pyPetrograph is developed by Larry Syu-Heng Lai (larrysyuhenglai@gmail.com). Part of the concept and base design (in Notebook 1) originated from early source code written by Zoltan Sylvester at the Quantitative Clastic Laboratory, Bureau of Economic Geology, The University of Texas at Austin. This project was initiated as part of a collaboration with Priyanka Periwal, Lucy Tingwei Ko, Kelly Hattori, and Amanda Calle across multiple research groups and the e-MAGE lab, and Larry intends to continue it after his departure from the Bureau.
Example thin-section images are in example_data/. Analyzed results go to outputs/.
Requires Python >=3.10.
pip install pyPetrographThis installs the library, the notebooks' dependencies and the app (
git clone https://github.com/GeoLarryLai/pyPetrograph.git
cd pyPetrograph
pip install .Use pip install -e . instead for an editable (development) install.
Tests (dev only): pip install pytest httpx.
Grain segmentation (notebooks 3 and 4, and Run segmentation in the app) uses SegmentEveryGrain. Install it into the same environment as pyPetrograph (it brings TensorFlow, PyTorch and SAM 2.1):
pip install segmenteverygrainpyPetrograph downloads the SegmentEveryGrain U-Net and, only if SAM 2.1 refine is on, the SAM 2.1 checkpoint (~860 MB) on first use. See the SegmentEveryGrain repository for full details and troubleshooting.
Without it, everything else works: notebooks 1 and 2, the non-segmentation steps of notebooks 3 and 4, and every app step, including Load saved grains and editing them. In the app, Run segmentation is greyed out with a link here.
pypetrograph-app opens pyPetrograph in your web browser. It is a local page: your files stay on this computer and nothing is uploaded.
pypetrograph-app
# or
python -m pyPetrograph.webappThe start page offers two jobs. Pick one, then follow the steps on the left. Each step must be done before the next one opens, and you can go back to any finished step.
- Thin-section photos (PPL / CPL): measure the porosity % of thin-section photos (notebook 1).
- AMICS grain counting: count and size the grains of each mineral from an AMICS set (notebook 4).
This job teaches a model what pores and grains look like from a few examples you mark, then measures the pore share of every photo.
Sped-up demo. Click it to watch the 1080p video (about 35 seconds).
Photos → Classes → Mark examples → Features → Train → Predict → Porosity %
- Photos: choose one or more photos and an output folder. Saved labels in that folder load by themselves.
- Classes: name what you want to find (pores and grains by default).
- Mark examples: click inside a pore or a grain, and the wand outlines the similar colour around it. Mark grains of every colour (white, pink, yellow, dark), not just the clear ones, or dark grains get counted as pores.
- Features: choose what the model looks at: colour, plus local texture and edges.
- Train: train one model for all photos or one per photo, and check its accuracy. Or load a saved model; it only works on photos and labels like the ones it was trained on.
- Predict: map every pixel of every photo, with a confidence map.
- Porosity %: read the pore share per photo with its mean ± sd, and save the table as a CSV.
This job outlines every grain on the BSE image, names each grain from the mineral map, and writes counts, grain sizes and figures. Its exports are byte-identical to notebook 4, and nothing is written until you press an Export button.
Sped-up demo. Click it to watch the 1080p video (about 25 seconds).
Files → Legend → BSE & footer → Non-grain regions → Scale (optional) → Grain segmentation → Minerals → Grain size → Outputs
- Files: pick the BSE image. The mineral map and legend with the same name fill in by themselves.
- Legend: check the mineral names read from the legend image; edit, add, delete or recolour any of them.
- BSE & footer: crop off the image's info bar so it is not counted.
- Non-grain regions: mark areas that hold no grains, such as resin or holes.
- Scale (optional): set the pixel size if the footer could not be read.
- Grain segmentation: run SegmentEveryGrain or load saved grains, then fix them by clicking or drawing.
- Minerals: see which mineral each grain was given: ✓ exact colour, ▲ reassigned, ✗ not found.
- Grain size: read the size curves and median sizes for each mineral.
- Outputs: view the knolling diagram and export the CSVs and figures.
Lai, L.S.-H. (2026) pyPetrograph. Zenodo, https://doi.org/10.5281/zenodo.23659316
- Hand-label features and map their area %: paint a few examples of each class (pores, grains, cement, …); a LightGBM pixel classifier maps the photo and reports area % of each class. The demo treats pore area as porosity.
- Grain counts on a painted mineral map: match pixels to a color legend, split grains that touch, and count how many grains sit in each class.
- Segment grains from a multi-image overlay: line up PPL and XPL of the same field, stack them, and outline grains (watershed, optional SegmentEveryGrain, and the shipped N-channel U-Net). (Experimental, incomplete.) Experimental and incomplete: results and functions may change, so use them with caution.
- AMICS workflow: read a legend, outline grains on the BSE, name each grain from the mineral map, and write counts, a composition figure (grains by mineral and the percent of grains), grain sizes (a, b, D, ECD), size figures, and a knolling sheet (5500 px wide, largest ECD first).
pyPetrograph_1_rgb_porosity.ipynb
You paint a few examples of each feature you care about (pores, grains, cement, …) using what you see — color, edge sharpness, and microtexture. A LightGBM pixel classifier (random forest optional) learns those examples, maps the rest of the photo, and reports area % of each class with a confidence range. The demo uses pores vs grains and treats pore area as porosity.
pyPetrograph_2_mineralmap_count.ipynb
You already have a color mineral map (one flat color per mineral) and a legend. The notebook matches pixels to the legend, splits grains that touch, and counts how many grains sit in each class.
Warning
Experimental and incomplete: grain, pore and cement segmentation from multiple overlaid images (PPL + XPL + others), meant for later ML/AI auto-classifier training. Use with caution; results and functions may change.
pyPetrograph_3_multimodal_grains.ipynb
A boundary drawn from PPL alone can miss contacts that only show under crossed polars (XPL), or the other way around. This notebook lines up PPL and XPL of the same field and stacks them as one overlay. Grain outlines go left to right in the same order as the work:
- Watershed — first guess from brightness, no training.
- SEG mask — optional SegmentEveryGrain U-Net (Zoltan Sylvester). This saved mask is the train target.
- N-channel U-Net (~25 MB, already in
outputs/models/grain_unet_ppl-xpl.keras) — trained on that PPL + XPL stack to copy the SEG mask. The notebook loads it and does not train again.
GrainPlot QC is a further optional window. SAM 2.1 (~860 MB, off by default) is only if you need still finer shapes.
pyPetrograph_4_AMICSworkflow.ipynb
Automated mineralogy (AMICS) gives a BSE image, a painted mineral map and a legend, but no per-grain sizes or counts. This notebook reads the legend (OCR, correctable), outlines every grain on the BSE with SegmentEveryGrain, names each grain by the map colour covering most of it, and measures its size. You get grain counts, composition and size distributions per mineral from one field. The same workflow is available in the app.
| Grain segmentation (10B) | Mineral composition (10B) | Knolling diagram (10B) |
|---|---|---|
![]() |
![]() |
![]() |
Outputs (in outputs/amics/{sample}/): CSV data sheets (counts and per-mineral grain sizes a, b, D, ECD), GeoJSON grains, and figures (composition, overlay, size curves, knolling). Notebook steps and settings are in GUIDE.md.
README.md
README-PyPI.md
GUIDE.md
FUNCTION_DOCUMENTATION.md
example_data/
image/
pyPetrograph/
align/
common/
fuse/
image_processing/
labeling_ml/
mineral_map/
objects/
amics/
app/
docs/images/
Requires Python >=3.10.
Installed with the package (
Note
rapidocr (legend OCR for notebook 4 and the app) is a large part of the default install: it adds ~190 MB (onnxruntime ~70 MB, OpenCV ~70 MB, RapidOCR ~33 MB, bundled PP-OCRv4 models ~16 MB, no download at run time) on top of the other dependencies (~700 MB; measured on Linux, Python 3.12).
Notebook pop-up windows (notebook pickers, label/align/GrainPlot windows; not installed by default, the app does not need it): pip install PySide6-Essentials. Another Qt binding supported by Matplotlib also works if already installed. On Linux, the pop-up windows' Qt also needs a few system libraries (Debian/Ubuntu; use libglib2.0-0 on older Ubuntu): sudo apt-get install -y libegl1 libgl1 libxkbcommon0 libxkbcommon-x11-0 libfontconfig1 libfreetype6 libdbus-1-3 libglib2.0-0t64 libx11-6 libx11-xcb1 libxcb1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xkb1. The app needs none of them.
Tests (dev only, not installed with the package): pip install pytest httpx.
SegmentEveryGrain (TensorFlow, PyTorch, SAM 2) is not installed with pyPetrograph; install it with its own instructions.








