hexatess-code 0.3.0__tar.gz → 0.4.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.
Potentially problematic release.
This version of hexatess-code might be problematic. Click here for more details.
- {hexatess_code-0.3.0/src/hexatess_code.egg-info → hexatess_code-0.4.0}/PKG-INFO +50 -14
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/README.md +49 -13
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/pyproject.toml +1 -1
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/__init__.py +12 -8
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/camera.py +434 -157
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/cli.py +21 -9
- hexatess_code-0.4.0/src/hexatess/decoder.py +110 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/encoder.py +39 -6
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/header.py +27 -3
- {hexatess_code-0.3.0 → hexatess_code-0.4.0/src/hexatess_code.egg-info}/PKG-INFO +50 -14
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_camera.py +12 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_codec.py +72 -1
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_conformance.py +6 -3
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_header.py +32 -0
- hexatess_code-0.3.0/src/hexatess/decoder.py +0 -75
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/LICENSE +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/setup.cfg +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/galois.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/geometry.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/masks.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/reedsolomon.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/render.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess/resilience.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess_code.egg-info/SOURCES.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess_code.egg-info/dependency_links.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess_code.egg-info/entry_points.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess_code.egg-info/requires.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/src/hexatess_code.egg-info/top_level.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_galois.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_geometry.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_masks.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_reedsolomon.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_render.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.4.0}/tests/test_resilience.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexatess-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Hexatess Code - an experimental 2D barcode on a hexagonal grid with Reed-Solomon error correction
|
|
5
5
|
Author: The Hexatess Code Authors
|
|
6
6
|
License: MIT License
|
|
@@ -115,10 +115,12 @@ text, stats = decode(grid) # ('Hello, Hexatess!', {...})
|
|
|
115
115
|
> specification and reference implementation are solid and heavily
|
|
116
116
|
> tested (2,500+ tests, conformance vectors). A **camera decoder**
|
|
117
117
|
> (`hexatess.camera`, optional `[camera]` extra) already reads symbols
|
|
118
|
-
> from real photographs — printed labels, foil
|
|
119
|
-
> and rotated shots.
|
|
120
|
-
> is
|
|
121
|
-
>
|
|
118
|
+
> from real photographs in about a second — printed labels, foil
|
|
119
|
+
> transparencies, tilted and rotated shots. Since spec v0.3 payload
|
|
120
|
+
> text is zlib-compressed automatically, so long texts fit into
|
|
121
|
+
> considerably smaller symbols. See the roadmap below. Adopting a
|
|
122
|
+
> young format is a deliberate bet; the [full format
|
|
123
|
+
> specification](SPECIFICATION.md) is the insurance.
|
|
122
124
|
|
|
123
125
|
## Installation
|
|
124
126
|
|
|
@@ -141,19 +143,43 @@ hexatess --demo # demo symbol + robustness statistics
|
|
|
141
143
|
hexatess decode-photo photo1.jpg photo2.jpg # read symbols from photos
|
|
142
144
|
```
|
|
143
145
|
|
|
146
|
+
Payload text is zlib-compressed automatically when that saves space
|
|
147
|
+
(`--no-compress` disables it; the header flag keeps decoders fully
|
|
148
|
+
backward compatible).
|
|
149
|
+
|
|
150
|
+
## Payload compression (spec v0.3)
|
|
151
|
+
|
|
152
|
+
One header bit marks the payload as a zlib stream. The encoder applies
|
|
153
|
+
it only when it strictly helps, and decoders inflate transparently —
|
|
154
|
+
symbols without the flag are byte-identical to v0.2. What that means
|
|
155
|
+
in practice (EC 30 unless noted):
|
|
156
|
+
|
|
157
|
+
| payload | raw | stored | symbol |
|
|
158
|
+
|---|---|---|---|
|
|
159
|
+
| 80 digits | 80 B | 21 B | rmax 17 → 11 |
|
|
160
|
+
| `"X" × 250` | 250 B | 12 B | rmax 30 → 10 |
|
|
161
|
+
| 849-byte Slovene paragraph | 849 B | 203 B | would not fit → rmax 28 |
|
|
162
|
+
| short strings (≤ ~30 B) | — | unchanged | overhead wins |
|
|
163
|
+
|
|
164
|
+
The maximum *stored* capacity is unchanged (329 bytes at EC 5), so
|
|
165
|
+
incompressible data behaves exactly as before.
|
|
166
|
+
|
|
144
167
|
## API
|
|
145
168
|
|
|
146
169
|
| Function | Description |
|
|
147
170
|
|---|---|
|
|
148
|
-
| `encode(text, ec_pct=30, mask_id="auto", min_rings=None)` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
|
|
149
|
-
| `decode(grid)` | grid → `(text, stats)`; RS-corrects transparently |
|
|
171
|
+
| `encode(text, ec_pct=30, mask_id="auto", min_rings=None, compress="auto")` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
|
|
172
|
+
| `decode(grid)` | grid → `(text, stats)`; RS-corrects and inflates transparently |
|
|
150
173
|
| `render(grid, path, size_px=18, ...)` | grid → PNG (pointy-top hexagons, quiet zone, supersampling) |
|
|
151
174
|
| `sample_grid_from_image(path, rmax, ...)` | ideal re-sampling of a rendered PNG (self-test helper) |
|
|
152
175
|
| `run_tests(...)` | noise/blob robustness statistics |
|
|
153
176
|
| `hexatess.camera.decode_photo(path)` | photograph → `(text, stats)`; finder detection, perspective handling, adaptive sampling (optional `[camera]` extra) |
|
|
154
177
|
|
|
155
178
|
`params` / `stats` contain `rmax` (radius in rings), `mask`, `ec`,
|
|
156
|
-
`blocks` (list of `(data_bytes, ecc_bytes)`)
|
|
179
|
+
`blocks` (list of `(data_bytes, ecc_bytes)`), `data_len` (stored
|
|
180
|
+
length) and `compressed`; `stats` also reports `repair_bits` (the RS
|
|
181
|
+
correction ledger) and, for camera decodes, `sector` and
|
|
182
|
+
`finder_hits`.
|
|
157
183
|
|
|
158
184
|
## Error-correction budget
|
|
159
185
|
|
|
@@ -174,10 +200,14 @@ concentrate inside whole bytes.
|
|
|
174
200
|
|
|
175
201
|
## Implement it in your own language
|
|
176
202
|
|
|
203
|
+
A **pure-JavaScript encoder** already ships in this repository — see
|
|
204
|
+
[`javascript/`](javascript/) (zero dependencies, byte-identical to the
|
|
205
|
+
Python reference for uncompressed symbols, includes a browser demo).
|
|
206
|
+
|
|
177
207
|
The format is deliberately **specification-first**: everything needed
|
|
178
208
|
for an independent implementation is in
|
|
179
209
|
[`SPECIFICATION.md`](SPECIFICATION.md), and
|
|
180
|
-
[`test_vectors/vectors_v0.
|
|
210
|
+
[`test_vectors/vectors_v0.3.json`](test_vectors/vectors_v0.3.json)
|
|
181
211
|
contains fixed inputs/outputs (grids, headers, damaged symbols, expected
|
|
182
212
|
results) to verify conformance. If your Rust/Go/JS decoder passes the
|
|
183
213
|
vectors, it speaks Hexatess Code.
|
|
@@ -187,12 +217,18 @@ vectors, it speaks Hexatess Code.
|
|
|
187
217
|
1. ~~v0.2/0.3 — camera decoding~~ **done (v0.3.0):** `hexatess.camera`
|
|
188
218
|
reads symbols from photographs — bullseye detection, homography +
|
|
189
219
|
correction-field warp handling, adaptive sampling; validated on
|
|
190
|
-
printed foil with curl and glare.
|
|
191
|
-
|
|
220
|
+
printed foil with curl and glare. **v0.3.1:** ≈10× faster
|
|
221
|
+
(a typical 12 MP photo now takes about a second) plus stable
|
|
222
|
+
outer-ring sampling and mis-decode-proof pose selection.
|
|
223
|
+
2. ~~v0.3 — payload compression~~ **done (v0.3.1):** zlib flag bit in
|
|
224
|
+
the header, applied automatically when it helps.
|
|
225
|
+
3. **Erasure decoding:** declare blob-occluded modules as
|
|
192
226
|
erasures → doubles correctable symbol counts.
|
|
193
|
-
|
|
194
|
-
in the browser in 10 seconds)
|
|
195
|
-
|
|
227
|
+
4. **JavaScript/TypeScript SDK** + online playground (generate a code
|
|
228
|
+
in the browser in 10 seconds) — **encoder done:**
|
|
229
|
+
[`javascript/`](javascript/); decoder + hosted playground next.
|
|
230
|
+
5. Larger radii / capacity beyond 329 stored bytes (breaking header
|
|
231
|
+
change).
|
|
196
232
|
|
|
197
233
|
Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
198
234
|
|
|
@@ -51,10 +51,12 @@ text, stats = decode(grid) # ('Hello, Hexatess!', {...})
|
|
|
51
51
|
> specification and reference implementation are solid and heavily
|
|
52
52
|
> tested (2,500+ tests, conformance vectors). A **camera decoder**
|
|
53
53
|
> (`hexatess.camera`, optional `[camera]` extra) already reads symbols
|
|
54
|
-
> from real photographs — printed labels, foil
|
|
55
|
-
> and rotated shots.
|
|
56
|
-
> is
|
|
57
|
-
>
|
|
54
|
+
> from real photographs in about a second — printed labels, foil
|
|
55
|
+
> transparencies, tilted and rotated shots. Since spec v0.3 payload
|
|
56
|
+
> text is zlib-compressed automatically, so long texts fit into
|
|
57
|
+
> considerably smaller symbols. See the roadmap below. Adopting a
|
|
58
|
+
> young format is a deliberate bet; the [full format
|
|
59
|
+
> specification](SPECIFICATION.md) is the insurance.
|
|
58
60
|
|
|
59
61
|
## Installation
|
|
60
62
|
|
|
@@ -77,19 +79,43 @@ hexatess --demo # demo symbol + robustness statistics
|
|
|
77
79
|
hexatess decode-photo photo1.jpg photo2.jpg # read symbols from photos
|
|
78
80
|
```
|
|
79
81
|
|
|
82
|
+
Payload text is zlib-compressed automatically when that saves space
|
|
83
|
+
(`--no-compress` disables it; the header flag keeps decoders fully
|
|
84
|
+
backward compatible).
|
|
85
|
+
|
|
86
|
+
## Payload compression (spec v0.3)
|
|
87
|
+
|
|
88
|
+
One header bit marks the payload as a zlib stream. The encoder applies
|
|
89
|
+
it only when it strictly helps, and decoders inflate transparently —
|
|
90
|
+
symbols without the flag are byte-identical to v0.2. What that means
|
|
91
|
+
in practice (EC 30 unless noted):
|
|
92
|
+
|
|
93
|
+
| payload | raw | stored | symbol |
|
|
94
|
+
|---|---|---|---|
|
|
95
|
+
| 80 digits | 80 B | 21 B | rmax 17 → 11 |
|
|
96
|
+
| `"X" × 250` | 250 B | 12 B | rmax 30 → 10 |
|
|
97
|
+
| 849-byte Slovene paragraph | 849 B | 203 B | would not fit → rmax 28 |
|
|
98
|
+
| short strings (≤ ~30 B) | — | unchanged | overhead wins |
|
|
99
|
+
|
|
100
|
+
The maximum *stored* capacity is unchanged (329 bytes at EC 5), so
|
|
101
|
+
incompressible data behaves exactly as before.
|
|
102
|
+
|
|
80
103
|
## API
|
|
81
104
|
|
|
82
105
|
| Function | Description |
|
|
83
106
|
|---|---|
|
|
84
|
-
| `encode(text, ec_pct=30, mask_id="auto", min_rings=None)` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
|
|
85
|
-
| `decode(grid)` | grid → `(text, stats)`; RS-corrects transparently |
|
|
107
|
+
| `encode(text, ec_pct=30, mask_id="auto", min_rings=None, compress="auto")` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
|
|
108
|
+
| `decode(grid)` | grid → `(text, stats)`; RS-corrects and inflates transparently |
|
|
86
109
|
| `render(grid, path, size_px=18, ...)` | grid → PNG (pointy-top hexagons, quiet zone, supersampling) |
|
|
87
110
|
| `sample_grid_from_image(path, rmax, ...)` | ideal re-sampling of a rendered PNG (self-test helper) |
|
|
88
111
|
| `run_tests(...)` | noise/blob robustness statistics |
|
|
89
112
|
| `hexatess.camera.decode_photo(path)` | photograph → `(text, stats)`; finder detection, perspective handling, adaptive sampling (optional `[camera]` extra) |
|
|
90
113
|
|
|
91
114
|
`params` / `stats` contain `rmax` (radius in rings), `mask`, `ec`,
|
|
92
|
-
`blocks` (list of `(data_bytes, ecc_bytes)`)
|
|
115
|
+
`blocks` (list of `(data_bytes, ecc_bytes)`), `data_len` (stored
|
|
116
|
+
length) and `compressed`; `stats` also reports `repair_bits` (the RS
|
|
117
|
+
correction ledger) and, for camera decodes, `sector` and
|
|
118
|
+
`finder_hits`.
|
|
93
119
|
|
|
94
120
|
## Error-correction budget
|
|
95
121
|
|
|
@@ -110,10 +136,14 @@ concentrate inside whole bytes.
|
|
|
110
136
|
|
|
111
137
|
## Implement it in your own language
|
|
112
138
|
|
|
139
|
+
A **pure-JavaScript encoder** already ships in this repository — see
|
|
140
|
+
[`javascript/`](javascript/) (zero dependencies, byte-identical to the
|
|
141
|
+
Python reference for uncompressed symbols, includes a browser demo).
|
|
142
|
+
|
|
113
143
|
The format is deliberately **specification-first**: everything needed
|
|
114
144
|
for an independent implementation is in
|
|
115
145
|
[`SPECIFICATION.md`](SPECIFICATION.md), and
|
|
116
|
-
[`test_vectors/vectors_v0.
|
|
146
|
+
[`test_vectors/vectors_v0.3.json`](test_vectors/vectors_v0.3.json)
|
|
117
147
|
contains fixed inputs/outputs (grids, headers, damaged symbols, expected
|
|
118
148
|
results) to verify conformance. If your Rust/Go/JS decoder passes the
|
|
119
149
|
vectors, it speaks Hexatess Code.
|
|
@@ -123,12 +153,18 @@ vectors, it speaks Hexatess Code.
|
|
|
123
153
|
1. ~~v0.2/0.3 — camera decoding~~ **done (v0.3.0):** `hexatess.camera`
|
|
124
154
|
reads symbols from photographs — bullseye detection, homography +
|
|
125
155
|
correction-field warp handling, adaptive sampling; validated on
|
|
126
|
-
printed foil with curl and glare.
|
|
127
|
-
|
|
156
|
+
printed foil with curl and glare. **v0.3.1:** ≈10× faster
|
|
157
|
+
(a typical 12 MP photo now takes about a second) plus stable
|
|
158
|
+
outer-ring sampling and mis-decode-proof pose selection.
|
|
159
|
+
2. ~~v0.3 — payload compression~~ **done (v0.3.1):** zlib flag bit in
|
|
160
|
+
the header, applied automatically when it helps.
|
|
161
|
+
3. **Erasure decoding:** declare blob-occluded modules as
|
|
128
162
|
erasures → doubles correctable symbol counts.
|
|
129
|
-
|
|
130
|
-
in the browser in 10 seconds)
|
|
131
|
-
|
|
163
|
+
4. **JavaScript/TypeScript SDK** + online playground (generate a code
|
|
164
|
+
in the browser in 10 seconds) — **encoder done:**
|
|
165
|
+
[`javascript/`](javascript/); decoder + hosted playground next.
|
|
166
|
+
5. Larger radii / capacity beyond 329 stored bytes (breaking header
|
|
167
|
+
change).
|
|
132
168
|
|
|
133
169
|
Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
134
170
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hexatess-code"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Hexatess Code - an experimental 2D barcode on a hexagonal grid with Reed-Solomon error correction"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""Hexatess Code - an experimental 2D barcode on a hexagonal grid.
|
|
2
2
|
|
|
3
|
-
Reference implementation of specification v0.
|
|
3
|
+
Reference implementation of specification v0.3. The symbol is a
|
|
4
4
|
hexagonal lattice with a hexagonal bullseye finder, an
|
|
5
5
|
orientation key ring, spiral serialization from the centre outwards,
|
|
6
6
|
a Reed-Solomon protected header and a continuously selectable
|
|
7
|
-
error-correction budget of 5-90 percent (Aztec-style).
|
|
7
|
+
error-correction budget of 5-90 percent (Aztec-style). Since spec
|
|
8
|
+
v0.3 the payload can optionally be zlib-compressed (one header flag
|
|
9
|
+
bit; applied automatically whenever it saves space).
|
|
8
10
|
|
|
9
11
|
Quick start
|
|
10
12
|
-----------
|
|
@@ -18,13 +20,13 @@ Quick start
|
|
|
18
20
|
'Hello, Hexatess!'
|
|
19
21
|
|
|
20
22
|
See SPECIFICATION.md in the repository for the full format
|
|
21
|
-
specification, and test_vectors/vectors_v0.
|
|
23
|
+
specification, and test_vectors/vectors_v0.3.json for conformance
|
|
22
24
|
data usable by independent implementations.
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
27
|
from __future__ import annotations
|
|
26
28
|
|
|
27
|
-
from .decoder import decode
|
|
29
|
+
from .decoder import decode, payload_to_text
|
|
28
30
|
from .encoder import encode
|
|
29
31
|
from .geometry import (
|
|
30
32
|
DIRS,
|
|
@@ -37,6 +39,7 @@ from .geometry import (
|
|
|
37
39
|
from .header import (
|
|
38
40
|
BULLSEYE_RINGS,
|
|
39
41
|
BLOCK_DATA_MAX,
|
|
42
|
+
COMPRESSED_FLAG,
|
|
40
43
|
DATA_RING0,
|
|
41
44
|
KEY_RING,
|
|
42
45
|
MAX_DATA_BYTES,
|
|
@@ -51,14 +54,15 @@ from .header import (
|
|
|
51
54
|
pack_mode,
|
|
52
55
|
plan_blocks,
|
|
53
56
|
unpack_mode,
|
|
57
|
+
unpack_mode_ex,
|
|
54
58
|
)
|
|
55
59
|
from .masks import evaluate_mask, mask_bit, mask_payload, select_mask
|
|
56
60
|
from .reedsolomon import rs_correct_msg, rs_encode_msg
|
|
57
61
|
from .render import render, sample_grid_from_image
|
|
58
62
|
from .resilience import add_blob_damage, add_random_noise, run_tests
|
|
59
63
|
|
|
60
|
-
__version__ = "0.
|
|
61
|
-
SPEC_VERSION = "0.
|
|
64
|
+
__version__ = "0.4.0"
|
|
65
|
+
SPEC_VERSION = "0.3"
|
|
62
66
|
|
|
63
67
|
__all__ = [
|
|
64
68
|
# high-level API
|
|
@@ -69,7 +73,7 @@ __all__ = [
|
|
|
69
73
|
"ring_capacity",
|
|
70
74
|
# framing
|
|
71
75
|
"bytes_to_bits", "bits_to_bytes", "pack_mode", "unpack_mode",
|
|
72
|
-
"plan_blocks",
|
|
76
|
+
"unpack_mode_ex", "plan_blocks", "payload_to_text",
|
|
73
77
|
# masks
|
|
74
78
|
"mask_bit", "mask_payload", "evaluate_mask", "select_mask",
|
|
75
79
|
# error correction
|
|
@@ -77,7 +81,7 @@ __all__ = [
|
|
|
77
81
|
# constants
|
|
78
82
|
"BULLSEYE_RINGS", "KEY_RING", "DATA_RING0", "MAX_RINGS",
|
|
79
83
|
"BLOCK_DATA_MAX", "MODE_BYTES", "MODE_ECC", "MODE_BITS",
|
|
80
|
-
"MIN_EC_PCT", "MAX_EC_PCT", "MAX_DATA_BYTES",
|
|
84
|
+
"MIN_EC_PCT", "MAX_EC_PCT", "MAX_DATA_BYTES", "COMPRESSED_FLAG",
|
|
81
85
|
# meta
|
|
82
86
|
"__version__", "SPEC_VERSION",
|
|
83
87
|
]
|