trace-digitiser 2.1.0__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/CHANGELOG.md +58 -0
  2. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/MANIFEST.in +2 -0
  3. {trace_digitiser-2.1.0/src/trace_digitiser.egg-info → trace_digitiser-2.2.0}/PKG-INFO +14 -1
  4. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/README.md +13 -0
  5. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/RELEASING.md +15 -2
  6. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/pyproject.toml +1 -1
  7. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/__init__.py +1 -0
  8. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/axis_labels.py +4 -1
  9. trace_digitiser-2.2.0/src/trace_digitiser/bars.py +591 -0
  10. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/frames.py +3 -1
  11. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/markers.py +192 -4
  12. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/panel_detection.py +60 -3
  13. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/points.py +19 -0
  14. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/polar.py +10 -3
  15. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/trace_detection.py +17 -0
  16. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0/src/trace_digitiser.egg-info}/PKG-INFO +14 -1
  17. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser.egg-info/SOURCES.txt +1 -0
  18. trace_digitiser-2.2.0/tests/test_figure_styles.py +138 -0
  19. trace_digitiser-2.1.0/src/trace_digitiser/bars.py +0 -324
  20. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/LICENSE +0 -0
  21. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/examples/README.md +0 -0
  22. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/examples/digitise_published_figure.py +0 -0
  23. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/examples/lopatina2014_fig4.jpg +0 -0
  24. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/examples/validate_with_known_data.py +0 -0
  25. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/examples/walkthrough.ipynb +0 -0
  26. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/setup.cfg +0 -0
  27. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/_figure.py +0 -0
  28. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/_results.py +0 -0
  29. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/calibration.py +0 -0
  30. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/cli.py +0 -0
  31. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/diagnostics.py +0 -0
  32. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/digitise.py +0 -0
  33. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/explore.py +0 -0
  34. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/geometry.py +0 -0
  35. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/heatmap.py +0 -0
  36. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/io.py +0 -0
  37. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/line_detection.py +0 -0
  38. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/models.py +0 -0
  39. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/ocr.py +0 -0
  40. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/provenance.py +0 -0
  41. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/resampling.py +0 -0
  42. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/rows.py +0 -0
  43. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/straighten.py +0 -0
  44. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/summarise.py +0 -0
  45. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/synthetic.py +0 -0
  46. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/validation.py +0 -0
  47. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser/x_calibration.py +0 -0
  48. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser.egg-info/dependency_links.txt +0 -0
  49. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser.egg-info/entry_points.txt +0 -0
  50. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser.egg-info/requires.txt +0 -0
  51. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/src/trace_digitiser.egg-info/top_level.txt +0 -0
  52. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/__init__.py +0 -0
  53. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/conftest.py +0 -0
  54. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/data/lopatina2014_fig4_reference.csv +0 -0
  55. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_axis_labels.py +0 -0
  56. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_calibration.py +0 -0
  57. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_cli.py +0 -0
  58. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_digitise.py +0 -0
  59. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_frames.py +0 -0
  60. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_geometry.py +0 -0
  61. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_integration.py +0 -0
  62. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_io.py +0 -0
  63. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_panel_detection.py +0 -0
  64. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_published_figure.py +0 -0
  65. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_readers.py +0 -0
  66. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_straighten.py +0 -0
  67. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_trace_detection.py +0 -0
  68. {trace_digitiser-2.1.0 → trace_digitiser-2.2.0}/tests/test_validation.py +0 -0
@@ -4,6 +4,63 @@ All notable changes to trace-digitiser are recorded here.
4
4
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
5
  and the project uses [semantic versioning](https://semver.org/).
6
6
 
7
+ ## [2.2.0] - 2026-11-15
8
+
9
+ Tested on the figures of published papers whose underlying numbers are
10
+ published too (Leypunskiy et al. 2017, eLife 6:e23539: markers with error
11
+ bars, a line over shaded bands; Foley et al. 2019, eLife 8:e50063: bar charts;
12
+ Pearl and Reed 1920, PNAS 6:275, a microfilm scan), 2.1.0 read what it found
13
+ accurately but often found nothing. This release finds much more of it, there
14
+ and on the figures of a paper held out until the changes were made (Andreani
15
+ et al. 2022, eLife 11:e74327). Line plots digitise as before: the package's
16
+ validation suite and the Hearn and Whitmore (2026) examples give the same
17
+ results.
18
+
19
+ ### Added
20
+
21
+ - **A test set of published figures**
22
+ ([`validation/published-figures`](https://github.com/comparativechrono/trace-digitiser/tree/main/validation/published-figures)):
23
+ seven figures from those four papers, the numbers behind them from their
24
+ authors' data, and a script that reads them with nothing given and with the
25
+ help a user can give, and compares. Its README has the results of 2.1.0 and
26
+ 2.2.0, case by case, including the few that 2.2.0 reads worse. It is run
27
+ before each release and is not part of the package.
28
+
29
+ ### Fixed
30
+
31
+ - **Bar charts in the style of GraphPad Prism**: black, grey, hatched and
32
+ outlined bars are found, on an axis that does not start at 0 (2.1.0 found 3
33
+ of the 35 bars of Foley et al.'s figure 2, and read those upside down; all
34
+ 35 are now read, within 0.3 % of the axis). Bars are the columns filled from
35
+ the baseline, so an error bar or a significance bracket joined to a bar no
36
+ longer spoils it; a border of another colour (a black outline round a grey
37
+ bar) is part of the bar; a bar cut by the edge of the plot area is kept.
38
+ - **Error bars drawn in the colour of their bar or marker** (the default in
39
+ Prism, MATLAB and matplotlib) are followed beyond the bar's end, or from the
40
+ marker, to their caps; one running across a fitted line goes on beyond it.
41
+ Where only the half beyond a bar's end shows, a note says so.
42
+ - **Markers sitting on a line of their own colour** (a fitted line, their
43
+ error bars) are found: other sizes are tried where the lines' ridges
44
+ outnumber the markers, and such a colour counts as a colour of markers if
45
+ its markers are joined to its lines and much thicker than them. Round
46
+ letters of words written in the plot, and a legend's markers beside their
47
+ words, are left out; a marker found in two series of close colours is kept
48
+ in the nearer one.
49
+ - **A plot area given by hand** (a box drawn in the workbench, or numbers read
50
+ off a picture) moves to the middle of the axis lines within a few pixels of
51
+ it, and their thickness is measured: with a thick axis line the tick labels
52
+ were looked for inside it, and the axis title was read instead.
53
+ - Long tick marks of a small plot in a large picture are found, so that its
54
+ labels are anchored to them.
55
+ - A colour that fills areas (a shaded band behind a plot) is not taken for a
56
+ trace of a line plot.
57
+ - Tick labels that Tesseract misreads as a line of text (75 as 15, in large
58
+ Helvetica) are read once more as a single word when an axis is not settled.
59
+ - Looking at a whole page of a scanned paper (`describe_figure`, the
60
+ workbench's first look at a page of a PDF) took a minute and a half, nearly
61
+ all of it in the search for polar axes; the circles are now proposed on a
62
+ smaller copy of the page (16 seconds in all).
63
+
7
64
  ## [2.1.0] - 2026-11-14
8
65
 
9
66
  Everything in 2.0.0 works as before. What is new is readers for other kinds of
@@ -165,6 +222,7 @@ First version tracked in this repository; identical to the 1.2.0 files on PyPI.
165
222
  Releases 0.1.0 to 1.0.0 (6-7 June 2026) were uploaded to PyPI directly and are
166
223
  not recorded here.
167
224
 
225
+ [2.2.0]: https://github.com/comparativechrono/trace-digitiser/compare/v2.1.0...v2.2.0
168
226
  [2.1.0]: https://github.com/comparativechrono/trace-digitiser/compare/v2.0.0...v2.1.0
169
227
  [2.0.0]: https://github.com/comparativechrono/trace-digitiser/compare/1d34f75...v2.0.0
170
228
  [1.2.0]: https://pypi.org/project/trace-digitiser/1.2.0/
@@ -5,3 +5,5 @@ recursive-include tests *.py *.csv
5
5
  recursive-include examples *.py *.md *.jpg *.ipynb
6
6
  prune examples/output
7
7
  include CHANGELOG.md RELEASING.md
8
+ # The published-figure test set (validation/published-figures) is run from the repository, not shipped.
9
+ prune validation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trace-digitiser
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Recover the data from figure images - lines, points, bars, actograms, heat maps - on your own computer: no uploads, no AI model.
5
5
  Author: Trace Digitiser Contributors
6
6
  License-Expression: MIT
@@ -267,6 +267,18 @@ Labels under 8 pixels high are never given high confidence, however well they ag
267
267
  that small is misread in consistent ways — the decimal point dropped from every label, say. If
268
268
  your figure has such labels, enlarging the image does not help; typing in two numbers does.
269
269
 
270
+ **Published figures.** The tests above draw their own figures.
271
+ [`validation/published-figures`](https://github.com/comparativechrono/trace-digitiser/tree/main/validation/published-figures)
272
+ holds seven figures from four papers whose authors published the numbers behind them — markers with error bars, a
273
+ line over shaded bands, ten bar charts, dot plots, and a scanned page from 1920 — and reads them with nothing given
274
+ and with the help a user can give. With nothing given, 2.2.0 reads all 35 bars of the bar charts to within 0.3 % of
275
+ the axis, and the 29 points of a scatter plot of four series to within 0.5 % (2.1.0 read none of either); with two
276
+ ticks clicked on each axis and the dots' colour, the census dots of the 1920 figure to within 0.9 %. On the figures
277
+ of a paper held out until the changes were made, with the plot area drawn, it reads 24 bars to within 0.4 % once
278
+ two ticks are clicked, and 33 of 37 points once the ticks of a slanted axis and the colours are. Stacked plots with no gap between them, dot
279
+ plots whose dots overlap, and tick labels that are slanted or hand-lettered still need help or remain hard; that
280
+ folder's README has the details.
281
+
270
282
  ### Test it on data you know
271
283
 
272
284
  The best test of a digitisation is one on data like yours. `round_trip` draws a known series as a
@@ -312,6 +324,7 @@ cd trace-digitiser
312
324
  pip install -e ".[dev]"
313
325
  pytest # the tests that need Tesseract are skipped without it
314
326
  ruff check src tests
327
+ python validation/published-figures/run.py # figures from papers against their authors' data (minutes)
315
328
  ```
316
329
 
317
330
  `src/trace_digitiser/` in the order the work is done:
@@ -227,6 +227,18 @@ Labels under 8 pixels high are never given high confidence, however well they ag
227
227
  that small is misread in consistent ways — the decimal point dropped from every label, say. If
228
228
  your figure has such labels, enlarging the image does not help; typing in two numbers does.
229
229
 
230
+ **Published figures.** The tests above draw their own figures.
231
+ [`validation/published-figures`](https://github.com/comparativechrono/trace-digitiser/tree/main/validation/published-figures)
232
+ holds seven figures from four papers whose authors published the numbers behind them — markers with error bars, a
233
+ line over shaded bands, ten bar charts, dot plots, and a scanned page from 1920 — and reads them with nothing given
234
+ and with the help a user can give. With nothing given, 2.2.0 reads all 35 bars of the bar charts to within 0.3 % of
235
+ the axis, and the 29 points of a scatter plot of four series to within 0.5 % (2.1.0 read none of either); with two
236
+ ticks clicked on each axis and the dots' colour, the census dots of the 1920 figure to within 0.9 %. On the figures
237
+ of a paper held out until the changes were made, with the plot area drawn, it reads 24 bars to within 0.4 % once
238
+ two ticks are clicked, and 33 of 37 points once the ticks of a slanted axis and the colours are. Stacked plots with no gap between them, dot
239
+ plots whose dots overlap, and tick labels that are slanted or hand-lettered still need help or remain hard; that
240
+ folder's README has the details.
241
+
230
242
  ### Test it on data you know
231
243
 
232
244
  The best test of a digitisation is one on data like yours. `round_trip` draws a known series as a
@@ -272,6 +284,7 @@ cd trace-digitiser
272
284
  pip install -e ".[dev]"
273
285
  pytest # the tests that need Tesseract are skipped without it
274
286
  ruff check src tests
287
+ python validation/published-figures/run.py # figures from papers against their authors' data (minutes)
275
288
  ```
276
289
 
277
290
  `src/trace_digitiser/` in the order the work is done:
@@ -45,8 +45,21 @@ plans). Now that the repository is public, one can be added:
45
45
  2. In `CHANGELOG.md`, give the section for the version its date in place of
46
46
  "unreleased", and end its link at the new tag (`...vX.Y.Z` in place of
47
47
  `...HEAD`).
48
- 3. Merge to `main` and wait for the *Tests* workflow to pass.
49
- 4. On GitHub: *Releases -> Draft a new release*, create the tag `vX.Y.Z`
48
+ 3. Run the published-figure test set
49
+ ([`validation/published-figures`](validation/published-figures/README.md)),
50
+ which reads figures from papers whose data are published, and compare the
51
+ results with the last release's:
52
+
53
+ ```bash
54
+ python validation/published-figures/run.py \
55
+ --json validation/published-figures/results-X.Y.Z.json \
56
+ --compare validation/published-figures/results-<last release>.json
57
+ ```
58
+
59
+ Look into any case marked WORSE before going on. Commit the new results,
60
+ and update the table in that folder's README (`--markdown` prints it).
61
+ 4. Merge to `main` and wait for the *Tests* workflow to pass.
62
+ 5. On GitHub: *Releases -> Draft a new release*, create the tag `vX.Y.Z`
50
63
  (matching the version exactly, with a leading `v`) on `main`, and publish.
51
64
 
52
65
  Publishing the release starts the workflow. It checks that the tag matches the
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "trace-digitiser"
7
- version = "2.1.0"
7
+ version = "2.2.0"
8
8
  description = "Recover the data from figure images - lines, points, bars, actograms, heat maps - on your own computer: no uploads, no AI model."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -231,6 +231,7 @@ def digitise(
231
231
  for p in panels:
232
232
  masks = detect_trace_masks(
233
233
  rgb, p, colours=trace_colours, exclude=exclude_boxes, achromatic=achromatic, darkness=dark,
234
+ lines_only=True,
234
235
  )
235
236
  if show_debug:
236
237
  print(f"Panel {p['panel_id']}: detected {len(masks)} trace(s)")
@@ -176,13 +176,16 @@ class Preparation:
176
176
  # settled by the first (fast) attempt are therefore read again in other ways
177
177
  # and the readings pooled; the axis fit then keeps the readings that agree.
178
178
  # The settings and their order were chosen on labels 5 to 7 pixels high in
179
- # three typefaces: each one added reads labels that the ones before it miss.
179
+ # three typefaces: each one added reads labels that the ones before it miss. The last was added for large print.
180
180
  PREPARATIONS = (
181
181
  Preparation(44, True, 2.0, True, True, 6),
182
182
  Preparation(64, False, 2.0, True, False, 7),
183
183
  Preparation(64, True, 0.0, False, False, 7, gamma=0.6),
184
184
  Preparation(32, False, 2.0, True, False, 7),
185
185
  Preparation(64, False, 0.0, False, False, 7, gamma=0.6),
186
+ # Large, clean print in some typefaces (MATLAB's Helvetica at 600 dpi) is misread as a line of text (75 as 15)
187
+ # and read right as a single word.
188
+ Preparation(64, False, 0.0, True, False, 8),
186
189
  )
187
190
 
188
191