amfora.core.analysis#
Per-sherd and batch analysis. analyze_single_sherd is the main entry point; full_analysis is the batch wrapper that produces a CSV / DataFrame from a folder of images. The other functions are component helpers and are exposed mostly so you can pull them out of the pipeline when you need finer control.
Source: src/amfora/core/analysis.py.
Top-level pipeline#
- amfora.core.analysis.analyze_single_sherd(image, scan_dpi=1200, analyze_inclusions=True, analyze_voids=True, analyze_core_periphery=True, use_blob=True, use_contour=True, enhance_contrast=True, clahe_clip=2.0, clahe_grid=(8, 8), channels=('B', 'G', 'R'), combine_mode='union', vote_min=2, void_intensity_max=60.0, paste_pop_k=None, paste_pop_floor=8.0, watershed_enabled=True, multigrain_split_enabled=True, pre_masked=False)[source]#
Comprehensive analysis of a single ceramic sherd image.
- Parameters:
image (numpy.ndarray) – Image array of a scanned sherd
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200)
analyze_inclusions (bool, optional) – Whether to analyze inclusions (default: True)
analyze_voids (bool, optional) – Whether to analyze voids (default: True)
analyze_core_periphery (bool, optional) – Whether to perform core-periphery color analysis for firing atmosphere interpretation. This is computationally intensive. (default: True)
use_blob (bool, optional) – Whether to use blob detection method (default: True)
use_contour (bool, optional) – Whether to use contour detection method (default: True)
enhance_contrast (bool, optional) – If True (default), apply CLAHE before running blob/contour detection. When
channels == ('L',)CLAHE is applied once to the masked image’s L* channel via the BGR round-trip; when multiple channels are requested CLAHE is instead applied to each channel inside the detectors so every channel benefits from the contrast enhancement. Set to False to disable entirely.clahe_clip (float, optional) – CLAHE clip limit when
enhance_contrast=True(default: 2.0).clahe_grid (tuple of int, optional) – CLAHE tile grid size when
enhance_contrast=True(default: (8, 8)).channels (tuple of str, optional) – Image channels passed to both detectors. Default
('B', 'G', 'R')runs detection independently on each of OpenCV’s native BGR channels (not RGB) and combines the results, so inclusions that only contrast strongly in one channel (e.g. iron-rich grains in R, organic dark cores in B) are still picked up. L* is excluded from the default because it is a perceptually-weighted blend of B, G, and R, so including it gives features visible in L* an extra redundant vote in the combination step. Setchannels=('L',)to recover the pre-multi-channel behavior.combine_mode ({'union', 'vote'}, optional) – How to combine per-channel detections when
len(channels) > 1. Default'union'pools detections across channels and removes spatial duplicates without requiring cross-channel agreement. This catches monochromatic features — e.g. an iron-bearing sand grain can contrast strongly in B (where the warm-toned grain reads dark against a cream matrix) while showing near- zero contrast in R (where both grain and matrix read bright) — that the old'vote'default withvote_min=2dropped: roughly half of legitimate single-channel detections were lost to the agreement requirement on sand-tempered fabrics. Noise rejection is instead handled by the paste-anchored pop gate (paste_pop_k/paste_pop_floor), which directly measures whether a candidate’s interior is statistically distinct from the sherd’s paste on the raw (pre-CLAHE) pixels and is a stronger discriminator than per-channel voting. Both detectors accept this parameter; the contour detector’s noise rejection is less voting-dependent (shape filters do more of the work), so the'union'default trades a small contour-recall hit (~5%) for a large blob- recall gain (~27%) on sand-tempered fabrics. Use'vote'only if you have a specific reason to require cross-channel agreement (e.g. very noisy scans).vote_min (int, optional) – Minimum number of channels that must agree for a feature to be kept when
combine_mode='vote'(default: 2 of 3 BGR channels). Ignored under the defaultcombine_mode='union'.void_intensity_max (float in 0..255, optional) – Brightness gate applied to void detections in both detectors. A candidate void’s interior must read below this mean pixel intensity on its channel (default: 60). This is the primary inclusion-vs-void discriminator on real scans, because the DPI-scaled blur smooths shape concavities that would otherwise distinguish a pore (hole, near-black inside) from a dark mineral grain (just darker paste). Lower (e.g. 45) for stricter void detection; raise (e.g. 90) for low-contrast scans where genuine pores don’t quite reach black.
paste_pop_k (float or None, optional) –
Minimum “pop” required for a candidate inclusion to be kept, expressed as a multiple of the paste’s per-channel Median Absolute Deviation. Default
Nonelets each detector use its own calibrated default —sherd_blobsdefaults to 2.0 (looser, since SimpleBlobDetector’s own shape filters already cull most noise) andcontour_detectiondefaults to 2.5 (tighter, since contour candidates pass fewer prior gates). Setting an explicit value overrides BOTH detectors with the same K. “Pop” here is informal shorthand for how much the feature stands out against the sherd’s paste — concretely, on the raw (pre-CLAHE) BGR channels of the masked input image:paste_ref = per-channel median of non-zero pixels paste_MAD = per-channel median(|pixel - paste_ref|) keep iff |median(interior) - paste_ref| >= max(K * MAD, floor) on at least one BGR channel
The MAD scaling makes the same K work across paste types — smooth cream paste (MAD ~5) gets a tight absolute threshold, mottled grog-tempered paste (MAD ~18) gets a loose one. K=2.0 is roughly a 3σ detection threshold, K=2.5 ~3.7σ (σ ≈ 1.4826 · MAD for normal data). Anchoring on the global paste reference instead of the local ring used by earlier versions is what fixed the dense-cluster failure mode where bright paste pockets between dark grains were detected as light inclusions and dark cluster grains were rejected for having dark neighbors. Set to 0 to disable; raise (e.g. 3.0+) for tighter precision; lower (e.g. 1.5) when chasing very subtle features.
paste_pop_floor (float in 0..255, optional) – Absolute brightness floor under the MAD-scaled threshold (default: 8.0). Effective threshold per channel is
max(paste_pop_k * MAD, paste_pop_floor). Inactive on every real flatbed scan (MAD ≥ ~7 across our calibration set), but protects against pathological near-zero-MAD inputs (synthetic test images, heavily slipped pieces) where a tiny MAD would otherwise collapse the gate.watershed_enabled (bool, optional) – Enable distance-transform watershed recovery of dark cluster contours the size cap would otherwise drop (default: True). Contour detection only. Disable for legacy behavior or to save ~10-30 % runtime on cluster-dense scans.
multigrain_split_enabled (bool, optional) – Enable a second watershed pass that breaks already-accepted lumpy contours into individual grain sub-contours (default: True). Contour detection only. Splitting triggers only when a contour exceeds both an area threshold (default 0.005 cm²) and a solidity ceiling (default 0.75) — single big convex grains (large grog fragments, quartz pebbles) pass through untouched.
pre_masked (bool, optional) – If True, skip
sherd_maskand treat the entire input image as the sherd (default: False). Use for backgroundless / tight-cropped images where every pixel is sherd and GrabCut has nothing to segment against.
- Returns:
dict – Dictionary containing comprehensive analysis results.
Contour inclusion orientation includes circular, multivariate-ready metrics (v1.0.3+) computed on the sherd-corrected axial angles:
contour_inclusion_orientation_strength(0 = random .. 1 = perfectly aligned),_concentration(von Mises),_uniformity,_bimodality,_dominant_deg, and_alignment— a signedmean(cos 2*theta)where +1 = grains parallel to the sherd surface, 0 = random, -1 = perpendicular. Unlike the retained legacy_mean/_stdcolumns (plain statistics on circular degrees), these are valid for PCA/LDA and clustering. Seeanalyze_orientation_for_pca.Limitations
———–
**Optical contrast limit on same-coloured temper-in-matrix systems.**
Both detectors find features by their intensity contrast against the
surrounding paste on the scanned image. When the optical signature
of the temper grains overlaps the optical signature of the matrix —
e.g. a quartz/feldspar/iron-mineral sand temper in an iron-rich
terra-cotta paste, where the warmer-toned mineral fraction of the
sand reads similarly to the surrounding red paste — those grains
become chromatically camouflaged and the detectors can only resolve
the subset that still differs in lightness. This is a property of
the scan, not of the algorithm (in the AMFOrA_Test_Bars set the)
R08G (grey clay body) and R08TC (terra-cotta clay body) bars share
an identical sand temper, but R08G yields ~200 inclusions/cm²
while R08TC yields ~88/cm² (a 2.3× gap) because the warmer grains
in the sand disappear visually against the warmer paste. Note
that the scan colour rendering can also overstate this effect
sand that appears as a “salt and pepper” mix in the lab can read
as predominantly warm-toned under flatbed-scanner illumination,
which is what drives the chromatic overlap with terra-cotta
matrices.
Practical implications
- Inclusion counts and densities are NOT directly comparable across – sherds with substantially different matrix colours, even when the temper is identical. Reported counts represent an optically- visible lower bound on the true grain population, not a complete census.
- Same-coloured temper-in-matrix systems known to be affected – include: red-bodied wares with self-temper or iron-rich sand, calcareous (white-firing) clays with calcareous (limestone) temper, and reduced black wares with carbonaceous inclusions.
- For cross-fabric comparison, group sherds by matrix colour – first (e.g. compare R##G to R##G, R##TC to R##TC), or use petrographic thin-sectioning + polarised-light microscopy when a true grain census is required.
No parameter adjustment can recover the missing grains — the
information is not in the image — so the limitation should be
reported alongside any cross-fabric comparison of inclusion
metrics.
- amfora.core.analysis.full_analysis(folder_path, scan_dpi=1200, analyze_inclusions=True, analyze_voids=True, analyze_core_periphery=True, use_blob=True, use_contour=True, interleave_columns=False, file_formats=None, save_csv=True, output_filename=None, enhance_contrast=True, clahe_clip=2.0, clahe_grid=(8, 8), channels=('B', 'G', 'R'), combine_mode='union', vote_min=2, void_intensity_max=60.0, paste_pop_k=None, paste_pop_floor=8.0, watershed_enabled=True, multigrain_split_enabled=True, pre_masked=False)[source]#
Comprehensive analysis of all ceramic sherds in a directory with both blob and contour detection.
This function processes all images in a directory using both detection methods and provides complete size, orientation, color, and morphological analysis for archaeological research.
- Parameters:
folder_path (str) – Path to folder containing ceramic images
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200)
analyze_inclusions (bool, optional) – Whether to analyze inclusions (default: True)
analyze_voids (bool, optional) – Whether to analyze voids (default: True)
analyze_core_periphery (bool, optional) – Whether to perform core-periphery color analysis for firing atmosphere interpretation. This is computationally intensive. (default: True)
use_blob (bool, optional) – Whether to use blob detection method (default: True)
use_contour (bool, optional) – Whether to use contour detection method (default: True)
interleave_columns (bool, optional) – Whether to reorder columns so blob/contour variants of the same metric are placed side-by-side. (default: False)
file_formats (list, optional) – List of file extensions to process (default: [‘jpg’, ‘jpeg’, ‘png’, ‘bmp’, ‘tiff’, ‘tif’])
save_csv (bool, optional) – Whether to automatically save results as CSV (default: True)
output_filename (str, optional) – Custom filename for CSV output (default: auto-generated based on folder name)
enhance_contrast (bool, optional) – If True (default), apply CLAHE before running blob/contour detection. With the default multi-channel
channelssetting, CLAHE is applied to each channel independently inside the detectors; withchannels=('L',)it falls back to the legacy single-pass L* CLAHE on the masked image. Set to False to disable entirely.clahe_clip (float, optional) – CLAHE clip limit when
enhance_contrast=True(default: 2.0).clahe_grid (tuple of int, optional) – CLAHE tile grid size when
enhance_contrast=True(default: (8, 8)).channels (tuple of str, optional) – Image channels passed to both detectors. Default
('B', 'G', 'R')runs detection on each BGR channel and combines the results so inclusions that only contrast in one channel are still picked up. Setchannels=('L',)to recover the pre-multi-channel L*-only behavior. Seeanalyze_single_sherdfor the full description.combine_mode ({'union', 'vote'}, optional) – How to combine per-channel detections (default:
'union'). Pools detections without requiring cross-channel agreement, catching monochromatic features (e.g. an iron-bearing sand grain visible only in B against a warm matrix) that voting would drop. Noise rejection is handled by the paste-anchored pop gate (paste_pop_k/paste_pop_floor) instead. Seeanalyze_single_sherdfor the full rationale.vote_min (int, optional) – Minimum number of channels that must agree under
combine_mode='vote'(default: 2 of 3 BGR channels). Ignored under the defaultcombine_mode='union'.void_intensity_max (float in 0..255, optional) – Brightness gate for void detection in both detectors (default: 60). See
analyze_single_sherdfor the full description; lower this for stricter voids, raise for low-contrast scans.paste_pop_k (float or None, optional) – Minimum paste-pop required for a candidate inclusion to be kept, expressed as a multiple of the paste’s per-channel Median Absolute Deviation. Default
Nonelets each detector use its own calibrated default (sherd_blobs= 2.0, looser;contour_detection= 2.5, tighter). Setting an explicit value overrides BOTH detectors. Seeanalyze_single_sherdfor the formal definition and full rationale.paste_pop_floor (float in 0..255, optional) – Absolute brightness floor under the MAD-scaled threshold (default: 8.0). Effective threshold per channel is
max(paste_pop_k * MAD, paste_pop_floor).watershed_enabled (bool, optional) – Enable distance-transform watershed recovery of dark cluster contours the size cap would otherwise drop (default: True). Contour detection only.
multigrain_split_enabled (bool, optional) – Enable a second watershed pass that breaks already-accepted lumpy contours into individual grain sub-contours (default: True). Contour detection only.
pre_masked (bool, optional) – If True, skip
sherd_maskand treat every input image as already isolated to its sherd (default: False). Use for backgroundless / tight-cropped directories where GrabCut has nothing to segment against.
- Returns:
Comprehensive DataFrame containing: - Blob detection results (blob_inclusion_*, blob_void_*) - Contour detection results (contour_inclusion_*, contour_void_*) - Orientation analysis (inclusion_orientation_* — including circular
strength / concentration / uniformity / bimodality / alignment metrics valid for multivariate analysis — plus sherd_orientation)
Color analysis (inclusion_color_*, sherd_color_*)
Density and percentage calculations
Processing status and metadata
- Return type:
Notes
Output includes dual detection methods:
Blob Detection - Better for round, circular features: - Good for: quartz grains, rounded temper, spherical voids - Metrics: blob_inclusion_count, blob_inclusion_total_area_cm2, etc.
Contour Detection - Better for irregular, angular features: - Good for: angular rock fragments, irregular voids, elongated inclusions - Metrics: contour_inclusion_count, contour_inclusion_total_area_cm2, etc.
All area measurements are in cm², densities in features per cm².
Limitations#
Inclusion counts and densities are NOT directly comparable across sherds whose matrix colours overlap their temper colours (e.g. an iron-bearing sand temper in a terra-cotta paste vs the same sand in a grey paste). The detectors can only resolve grains that contrast against the matrix; chromatically camouflaged grains are invisible to optical scanning regardless of detection parameters. See
analyze_single_sherdfor the full discussion and the R08G/R08TC calibration data showing the ~2.3× density gap on bars sharing identical sand temper.
Per-stage helpers#
- amfora.core.analysis.size_count_summary_single(blobs_light, blobs_dark, scan_dpi=1200)[source]#
Analyze size distributions for a single image’s detected blobs.
- Parameters:
blobs_light (list) – List of light blob keypoints (inclusions)
blobs_dark (list) – List of dark blob keypoints (voids)
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200)
- Returns:
Dictionary containing comprehensive size statistics for inclusions and voids
- Return type:
dict
- amfora.core.analysis.size_count_summary(folder_path, fileformat='jpeg', scan_dpi=1200, use_blob=True, use_contour=True, interleave_columns=False, pre_masked=False)[source]#
Analysis of inclusions and voids using blob and/or contour detection.
- Parameters:
folder_path (str) – Path to folder containing ceramic images
fileformat (str, optional) – Image file format to process (default: ‘jpeg’)
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200) Valid range: 150-2400 DPI
use_blob (bool, optional) – Whether to use blob detection (default: True)
use_contour (bool, optional) – Whether to use contour detection (default: True)
interleave_columns (bool, optional) – Whether to reorder columns so blob/contour variants of the same metric are placed side-by-side. (default: False)
pre_masked (bool, optional) – If True, skip
sherd_maskand treat every input as already isolated to its sherd (default: False).
- Returns:
Summary statistics for each ceramic’s inclusions and voids. All area measurements are in cm². Column names are prefixed with
blob_orcontour_to indicate detection method.- Return type:
- amfora.core.analysis.void_counter(image, scan_dpi=1200)[source]#
Calculate the number and area of void spaces within a ceramic sherd.
- Parameters:
image (numpy.ndarray) – Image of a scanned sherd
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200) Valid range: 150-2400 DPI
- Returns:
(list of void areas in cm², number of voids found)
- Return type:
tuple
- amfora.core.analysis.contour_counter(image, scan_dpi=1200)[source]#
Calculate the number and area of contours within a ceramic sherd.
- Parameters:
image (numpy.ndarray) – Image of a scanned sherd
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200) Valid range: 150-2400 DPI
- Returns:
(list of contour areas in cm², number of contours found)
- Return type:
tuple
- amfora.core.analysis.sacredsquare(og_img, blobs)[source]#
Extract squares representing inclusions using blob detection results.
- Parameters:
og_img (numpy.ndarray) – Original image from which blobs were detected
blobs (list) – Blob KeyPoint objects from blob detection
- Returns:
- (sorted list of [(left_vertex, right_vertex), size],
image with squares drawn over blobs)
- Return type:
tuple
- amfora.core.analysis.inclusion_colors(image, inclusion_list)[source]#
Extract color information for each inclusion using k-means clustering.
- Parameters:
image (numpy.ndarray) – Masked scanned image for color analysis (BGR format)
inclusion_list (list) – List of inclusions and their locations from sacredsquare
- Returns:
List of CIELAB colors for dominant 3 colors of each inclusion [[L*, a*, b*], [L*, a*, b*], [L*, a*, b*]] ordered by frequency L* = lightness (0-100), a* = green-red, b* = blue-yellow
- Return type:
list
- amfora.core.analysis.inclusion_colors_from_contours(image, contours)[source]#
Extract color information for contour-detected inclusions using contour masks.
Unlike inclusion_colors() which uses rectangular bounding boxes, this function masks each inclusion to its exact contour boundary, avoiding paste/matrix color contamination.
- Parameters:
image (numpy.ndarray) – Masked scanned image (BGR format)
contours (list) – List of contour arrays from contour_detection[‘inclusions’]
- Returns:
List of CIELAB colors for dominant 3 colors of each inclusion [[L*, a*, b*], [L*, a*, b*], [L*, a*, b*]] ordered by frequency
- Return type:
list
- amfora.core.analysis.inclusion_orientation(image, scan_dpi, contour_result=None)[source]#
Estimate orientations of inclusions and voids.
- Parameters:
image (numpy.ndarray) – Masked image of a scanned sherd (background zeroed).
scan_dpi (int) – Scan resolution in dots per inch.
contour_result (dict, optional) – Output dict from
contour_detection(). When provided the already-filtered inclusion and void contours are used directly, avoiding a redundant re-detection pass. When None the function falls back to independent threshold-based detection.
- Returns:
(inclusion_angles, void_angles)— lists of integer angles in degrees derived from the minimum-area bounding rectangle of each detected feature.- Return type:
tuple
Notes
Orientation will depend on how sherds were scanned — trends will be in modal angles, not true measures. Use
inclusion_orientation2to correct for the sherd’s own principal axis.
- amfora.core.analysis.inclusion_orientation2(image, scan_dpi, contour_result=None, sherd_contour=None)[source]#
Enhanced orientation analysis that corrects angles relative to the sherd’s own principal axis.
- Parameters:
image (numpy.ndarray) – Masked image of a scanned sherd (background zeroed).
scan_dpi (int) – Scan resolution in dots per inch.
contour_result (dict, optional) – Output dict from
contour_detection(). When provided the already-filtered inclusion and void contours are used directly, avoiding a redundant re-detection pass. When None the function falls back to independent threshold-based detection.sherd_contour (numpy.ndarray or None, optional) – The
best_contourreturned bysherd_mask(). When provided the sherd’s principal axis is derived from this exact contour viacv2.minAreaRect— the same geometry that determined the mask and crop. This is the authoritative sherd orientation because the mask bounding box is what orients the entire sherd in the pipeline. When None the function derives the sherd orientation by thresholding the masked image (fallback — less reliable on dark-matrix sherds).
- Returns:
(inclusion_angles, void_angles, sherd_angle)— angle lists in degrees corrected for the sherd’s principal axis orientation, plus the sherd angle itself.- Return type:
tuple
- amfora.core.analysis.sherd_color_analysis(image, mask=None, crop=None, pre_masked=False)[source]#
Analyze color properties of a single ceramic sherd image using CIELAB.
- Parameters:
image (numpy.ndarray) – Input image array (BGR format). Pass the original (un-cropped) image; the function will slice it using
cropwhen provided.mask (numpy.ndarray, optional) – Mask to apply (already cropped to the sherd region when
cropis given). If None, a mask is generated automatically viasherd_mask(orfull_image_maskwhenpre_masked=True).crop (tuple or None, optional) –
(y1, y2, x1, x2)crop rectangle as returned bysherd_mask. When provided together withmask, the image is sliced to this region so its dimensions match the (already-cropped) mask.pre_masked (bool, optional) – If True and
maskis None, skipsherd_maskand treat the entire input as the sherd (default: False). Ignored whenmaskis given.
- Returns:
Dictionary containing CIELAB color values: - mean_l: L* lightness (0-100) - mean_a: a* green-red axis (-128 to +127) - mean_b: b* blue-yellow axis (-128 to +127)
- Return type:
dict
- amfora.core.analysis.sherd_color_summary(folder_path, scan_dpi=1200, use_blob=True, use_contour=True, analyze_core_periphery=True, interleave_columns=False, pre_masked=False)[source]#
Provide summary of color aspects of sherds in CIELAB colorspace.
- Parameters:
folder_path (str) – Path to folder containing ceramic images
scan_dpi (int, optional) – Scan resolution in dots per inch (default: 1200)
use_blob (bool, optional) – Whether to analyze inclusion colors using blob detection (default: True)
use_contour (bool, optional) – Whether to analyze inclusion colors using contour detection (default: True)
analyze_core_periphery (bool, optional) – Whether to perform core-periphery color analysis for firing atmosphere interpretation. This is computationally intensive. (default: True)
interleave_columns (bool, optional) – Whether to reorder columns so blob/contour variants of the same metric are placed side-by-side. (default: False)
pre_masked (bool, optional) – If True, skip
sherd_maskand treat every input as already isolated to its sherd (default: False).
- Returns:
Summary statistics for each ceramic’s color and inclusion colors in CIELAB. L* = lightness (0-100), a* = green-red, b* = blue-yellow. Columns are prefixed with
blob_orcontour_to indicate method.- Return type:
- amfora.core.analysis.extract_core_periphery_colors(masked_image, mask, scan_dpi=1200)[source]#
Extract ceramic paste colors from core vs margin regions using distance transform.
Uses distance transform to define the structural core (innermost 20% by distance from edges) and analyzes color differences between core and margin regions.
Archaeological Significance#
The a* channel (red-green axis) is the primary indicator of iron oxidation state in ceramics. Oxidized iron (Fe2O3, hematite) produces red/brown colors (high a*), while reduced iron and preserved carbon remain chromatically neutral (gray/black). L* alone cannot distinguish a dark oxidized ceramic from a reduced one.
Each zone is independently classified as oxidized, reduced, incomplete_oxidation, or carbonaceous using the full CIELAB color, then a whole-sherd
firing_interpretationis derived from the combination.- param masked_image:
Masked ceramic sherd image (BGR format)
- type masked_image:
numpy.ndarray
- param mask:
Binary mask defining ceramic boundaries
- type mask:
numpy.ndarray
- param scan_dpi:
Scan resolution (default: 1200)
- type scan_dpi:
int, optional
- returns:
‘core_lab’: [L*, a*, b*] for ceramic core
‘inner_margin_lab’: [L*, a*, b*] for inner margin
‘outer_margin_lab’: [L*, a*, b*] for outer margin
‘core_atmosphere’: per-zone classification
‘inner_margin_atmosphere’: per-zone classification
‘outer_margin_atmosphere’: per-zone classification
‘color_gradient’: Max Delta-E between regions
‘firing_interpretation’: Archaeological assessment
‘margin_symmetry’: symmetric/symmetric_transitional/asymmetric
‘core_pixels’, ‘inner_margin_pixels’, ‘outer_margin_pixels’: Counts
- rtype:
dict
- amfora.core.analysis.analyze_inclusion_angularity(contours, scan_dpi=1200)[source]#
Analyze geometric angularity and roundness of inclusion contours.
This function uses polygon approximation and roundness metrics to classify inclusions into the six standard sedimentological roundness categories established by Muller (1964) and Powers (1953), as applied to ceramic petrography by Stienstra (1986).
- Parameters:
contours (list) – List of contour objects from cv2.findContours()
scan_dpi (int, optional) – Scan resolution for size-aware filtering (default: 1200)
- Returns:
Dictionary containing: - ‘angularity_scores’: list of angularity scores (0-1, higher = more angular) - ‘vertex_counts’: list of vertex counts for each inclusion - ‘roundness_ratios’: list of roundness ratios (0-1, higher = more round) - ‘roundness_classifications’: list of Muller/Powers roundness classes
(‘very_angular’, ‘angular’, ‘sub_angular’, ‘sub_rounded’, ‘rounded’, ‘well_rounded’)
’approx_polygons’: list of approximated polygon contours
’summary_stats’: dict with aggregate statistics
’pca_metrics’: dict with metrics formatted for PCA analysis
- Return type:
dict
Notes
Roundness classification follows the Powers (1953) / Muller (1964) scale adapted for automated circularity measurement: - Very angular: circularity < 0.17 - Angular: 0.17 <= circularity < 0.25 - Sub-angular: 0.25 <= circularity < 0.35 - Sub-rounded: 0.35 <= circularity < 0.49 - Rounded: 0.49 <= circularity < 0.70 - Well-rounded: circularity >= 0.70
- amfora.core.analysis.analyze_orientation_for_pca(orientation_angles)[source]#
Convert circular orientation data to PCA-compatible metrics.
Handles the statistical challenges of circular data by computing: 1. Vector components (sine/cosine) - preserves directional information 2. Circular statistical measures (mean direction, concentration) 3. Fabric strength indicators (preferred orientation vs randomness)
- Parameters:
orientation_angles (list or array-like) – List of angles in degrees (0-360° or -180° to +180°)
- Returns:
Dictionary containing PCA-ready orientation metrics: - orientation_strength: How strongly oriented the fabric is (0=random, 1=perfectly aligned) - mean_orientation_x: X-component of mean orientation vector - mean_orientation_y: Y-component of mean orientation vector - orientation_concentration: Circular concentration parameter (higher = more aligned) - orientation_uniformity: Measure of how evenly distributed angles are - dominant_orientation_deg: Main orientation direction in degrees - orientation_bimodality: Whether fabric shows two preferred orientations
- Return type:
dict
Notes
This approach solves the “circular data problem” for PCA by: 1. Converting angles to unit vectors, avoiding 0°/360° discontinuity 2. Computing vector statistics that are PCA-compatible 3. Providing archaeological interpretations (fabric strength, preferred orientations)
For ceramic analysis: - High orientation_strength = strong fabric, deliberate manufacturing technique - Low orientation_strength = random fabric, hand-building or poor clay preparation - Bimodality = cross-hatched or woven fabric structure
- amfora.core.analysis.analyze_manufacturing_technique(orientation_metrics, size_metrics, geometric_metrics)[source]#
Identify likely ceramic manufacturing technique based on inclusion patterns.
Based on archaeological research (Berg 2008, Roux & Courty 2005, EXARC 2021): - Coiling: wavy/spiral patterns, radial orientations, moderate alignment - Wheel throwing: strong horizontal alignment, high uniformity, low bimodality - Slab construction: parallel to walls, moderate-high alignment, clustered joints - Pinching: random orientations, low alignment, clustering at stress points
- Parameters:
orientation_metrics (dict) – Results from analyze_orientation_for_pca()
size_metrics (dict) – Size distribution metrics
geometric_metrics (dict) – Geometric analysis results
- Returns:
Manufacturing technique analysis with confidence scores
- Return type:
dict