Picture
For every frame of an image sequence, measure the distance from the visible
growth front (the circle edge) to the origin — the reference line's
endpoint nearest the spherulite nucleus — along the user's straight reference
line. Then:
- convert pixels to real units with the scale bar,
- fit the growth rate with a moving-window least-squares slope,
- invert
r(t)to get the arrival time of every marker dot, - export an Excel workbook, plots, and one measurement-verification image per
frame.
A dataset usually has three reference lines (参考1 / 参考2 / 参考3) drawn on the
same specimen; analyse each separately and deliver one self-contained result
per line.
Workflow
1. Confirm the inputs
Ask for, or read from the request:
| Item | Notes |
|---|---|
| Frame sequence | A folder or a .zip. Unzip first; frames must be numbered. |
| Reference image(s) | 参考1/2/3. May carry a drawn line and marker dots, or only a line. |
| Origin rule | "The endpoint of the reference line nearest the nucleus" (default). |
| Scale bar | e.g. 20x-2-L.jpg, total tick length 1 mm → measure it, do not assume. |
| Frame interval | Always verify against EXIF DateTimeOriginal (see §2). |
| Output | One package per reference line, plus measurement-reference images. |
2. Normalise the sequence (scripts/prepare_sequence.py)
Cameras get rotated mid-acquisition, and editing tools rewrite the EXIF
Orientation tag. A frame that loads into a different pixel frame than the
reference image silently makes the measured front jump. This script scans every
frame, reports the transitions, and rotates the odd ones into the reference
frame — the rotation direction is chosen by maximum correlation with the nearest
in-frame neighbour, not guessed from the tag.
python scripts/prepare_sequence.py \
--input-dir <frames> --out-dir <normalised> \
--reference <参考1.jpg> # defines the target orientation
It prints the transitions and the post-normalisation continuity (should be
≥ 0.99). If it reports "all frames already share the target orientation",
use the original folder. Do not skip this step on a new dataset — the
failure is silent.
3. Fit the reference geometry (scripts/fit_reference.py)
python scripts/fit_reference.py \
--images 参考1.jpg 参考2.jpg 参考3.jpg --names 参考1 参考2 参考3 \
--prefix <dataset> --out-dir . \
--scale-px 2235.5 --scale-real 1000 --unit um --interval-s 5
It detects the marker dots and the drawn line, fits the direction (dots when
there are ≥ 5 with collinear RMS ≤ 1.5 px, otherwise the line), takes the drawn
span from the dark pixels, and picks the origin end. When several lines are
given the ends are chosen jointly, because the lines of one specimen share
their near-nucleus endpoint.
Outputs <prefix>_<name>_config.json, <prefix>_参考线几何.json, and
<prefix>_参考线几何核对.jpg. Look at the overlay. A healthy fit has:
- dots = the number the user drew,
dot_rms≤ 1.5 px; line_rms≈ 1–2 px;- origin spread across the three lines of one specimen ≲ 30 px.
If the spread is hundreds of pixels, one origin landed at the far end: override
with --origin "x,y x,y x,y".
4. Run the measurement
python scripts/picture_analysis.py \
--input-dir <normalised frames> \
--output-dir <参考1_分析结果> \
--config <prefix>_参考1_config.json \
--interval-s 5
Run the three lines in parallel (they are independent). Then inspect
参考线确认图.jpg and a few 核对图/*.jpg.
5. Verify before delivering
- growth front monotonic, no negative increments, no unexplained jump > 60 px;
- the three lines agree (relative range typically < 10 %);
- the rate curve is not dominated by per-frame noise;
- frames where the ray leaves the image are flagged
是否出画幅, not read as
"growth stopped"; 黑点到达时间marks extrapolated points explicitly.
Scripts
| Script | Purpose |
|---|---|
scripts/prepare_sequence.py |
Scan EXIF orientation, rotate off-orientation frames into one coordinate system, verify continuity. |
scripts/fit_reference.py |
Fit line direction / span / origin from annotated reference images; emit ready-to-run configs + overlay. |
scripts/picture_analysis.py |
The measurement itself: per-frame front, rate, bands, arrival times, Excel, plots, verification images. |
Key picture_analysis.py options:
--markers auto|green|black— annotation colour for auto-detection.--verify-max-px N— downscale verification images (long side ≤ N px). Use
2000–2400 for long sequences: 579 frames × 3 lines at full 24 MP otherwise
fills the disk.--rate-window W— half-width of the rate fit (window = 2W+1). Use 2 for a
30 s interval, 5 for a 5 s interval.--inpaint-markers— remove the drawn markers from the measured frames.--first-frame/--last-frame/--global-contact-frame— restrict or cut
the sequence.
Non-negotiable rules
- Measure along a straight ray from the origin:
x = cx + r cos θ,
y = cy + r sin θ. Never bend, shift, or re-draw the reference line. - The origin is the reference-line endpoint nearest the nucleus, not
necessarily a dot. - The growth front is the outermost visible circle edge. Never substitute an
internal bright/dark boundary for it. - The physical radius is monotonic nondecreasing; the raw detection stays in a
separate diagnostic column and no negative rate is ever reported. - Never trust a user-stated frame interval without checking EXIF. A wrong
interval scales every rate linearly (a 5 s vs 60 s mistake is 12×), and the
number looks perfectly plausible. - Never interpret an isolated zero rate as a pause — check the raw column. A long
run of zeros at the end means the ray left the frame. 区间序号is only a within-frame inner-to-outer order. Never treat it as a
cross-frame physical ring identity.- Report every distance in both pixels and real units; if no scale exists, say
so instead of inventing one.
Outputs
--output-dir receives:
picture_测量结果.xlsx— sheets生长前沿距离-时间,黑点距离,
黑点到达时间,明暗环带明细,明暗环带速率汇总,聚合绘图数据,核对图清单.参考线确认图.jpg— detected geometry with numbered markers.核对图/核对图_<frame>.jpg— one verification image per frame.半径-时间.png,生长速率-时间.png.黑点到达时间.csv, CSV diagnostics,分析参数.json.
Reporting the result
State, at minimum: the interval and how it was verified; the origin and angle
per line; the valid frame range and why any frames were excluded; the mean rate
and the windowed-rate median per line; and that the three lines agree. If the
rate oscillates, say so — a single mean rate is an incomplete description of a
banded spherulite.
Dependencies
numpy, pandas, pillow, scipy, matplotlib, openpyxl. Install into an
isolated environment if any are missing.
Detailed segmentation, collision, band, scale, and output rules are in
references/method.md. Failure modes and their fixes are in
references/pitfalls.md.