hexatess-code 0.3.0__tar.gz → 0.3.1__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.3.1}/PKG-INFO +44 -13
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/README.md +43 -12
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/pyproject.toml +1 -1
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/__init__.py +12 -8
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/camera.py +434 -157
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/cli.py +21 -9
- hexatess_code-0.3.1/src/hexatess/decoder.py +110 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/encoder.py +39 -6
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/header.py +27 -3
- {hexatess_code-0.3.0 → hexatess_code-0.3.1/src/hexatess_code.egg-info}/PKG-INFO +44 -13
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_camera.py +12 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_codec.py +72 -1
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_conformance.py +6 -3
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/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.3.1}/LICENSE +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/setup.cfg +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/galois.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/geometry.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/masks.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/reedsolomon.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/render.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/resilience.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/SOURCES.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/dependency_links.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/entry_points.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/requires.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/top_level.txt +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_galois.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_geometry.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_masks.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_reedsolomon.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_render.py +0 -0
- {hexatess_code-0.3.0 → hexatess_code-0.3.1}/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.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
|
@@ -177,7 +203,7 @@ concentrate inside whole bytes.
|
|
|
177
203
|
The format is deliberately **specification-first**: everything needed
|
|
178
204
|
for an independent implementation is in
|
|
179
205
|
[`SPECIFICATION.md`](SPECIFICATION.md), and
|
|
180
|
-
[`test_vectors/vectors_v0.
|
|
206
|
+
[`test_vectors/vectors_v0.3.json`](test_vectors/vectors_v0.3.json)
|
|
181
207
|
contains fixed inputs/outputs (grids, headers, damaged symbols, expected
|
|
182
208
|
results) to verify conformance. If your Rust/Go/JS decoder passes the
|
|
183
209
|
vectors, it speaks Hexatess Code.
|
|
@@ -187,12 +213,17 @@ vectors, it speaks Hexatess Code.
|
|
|
187
213
|
1. ~~v0.2/0.3 — camera decoding~~ **done (v0.3.0):** `hexatess.camera`
|
|
188
214
|
reads symbols from photographs — bullseye detection, homography +
|
|
189
215
|
correction-field warp handling, adaptive sampling; validated on
|
|
190
|
-
printed foil with curl and glare.
|
|
191
|
-
|
|
216
|
+
printed foil with curl and glare. **v0.3.1:** ≈10× faster
|
|
217
|
+
(a typical 12 MP photo now takes about a second) plus stable
|
|
218
|
+
outer-ring sampling and mis-decode-proof pose selection.
|
|
219
|
+
2. ~~v0.3 — payload compression~~ **done (v0.3.1):** zlib flag bit in
|
|
220
|
+
the header, applied automatically when it helps.
|
|
221
|
+
3. **Erasure decoding:** declare blob-occluded modules as
|
|
192
222
|
erasures → doubles correctable symbol counts.
|
|
193
|
-
|
|
223
|
+
4. **JavaScript/TypeScript SDK** + online playground (generate a code
|
|
194
224
|
in the browser in 10 seconds).
|
|
195
|
-
|
|
225
|
+
5. Larger radii / capacity beyond 329 stored bytes (breaking header
|
|
226
|
+
change).
|
|
196
227
|
|
|
197
228
|
Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
198
229
|
|
|
@@ -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
|
|
|
@@ -113,7 +139,7 @@ concentrate inside whole bytes.
|
|
|
113
139
|
The format is deliberately **specification-first**: everything needed
|
|
114
140
|
for an independent implementation is in
|
|
115
141
|
[`SPECIFICATION.md`](SPECIFICATION.md), and
|
|
116
|
-
[`test_vectors/vectors_v0.
|
|
142
|
+
[`test_vectors/vectors_v0.3.json`](test_vectors/vectors_v0.3.json)
|
|
117
143
|
contains fixed inputs/outputs (grids, headers, damaged symbols, expected
|
|
118
144
|
results) to verify conformance. If your Rust/Go/JS decoder passes the
|
|
119
145
|
vectors, it speaks Hexatess Code.
|
|
@@ -123,12 +149,17 @@ vectors, it speaks Hexatess Code.
|
|
|
123
149
|
1. ~~v0.2/0.3 — camera decoding~~ **done (v0.3.0):** `hexatess.camera`
|
|
124
150
|
reads symbols from photographs — bullseye detection, homography +
|
|
125
151
|
correction-field warp handling, adaptive sampling; validated on
|
|
126
|
-
printed foil with curl and glare.
|
|
127
|
-
|
|
152
|
+
printed foil with curl and glare. **v0.3.1:** ≈10× faster
|
|
153
|
+
(a typical 12 MP photo now takes about a second) plus stable
|
|
154
|
+
outer-ring sampling and mis-decode-proof pose selection.
|
|
155
|
+
2. ~~v0.3 — payload compression~~ **done (v0.3.1):** zlib flag bit in
|
|
156
|
+
the header, applied automatically when it helps.
|
|
157
|
+
3. **Erasure decoding:** declare blob-occluded modules as
|
|
128
158
|
erasures → doubles correctable symbol counts.
|
|
129
|
-
|
|
159
|
+
4. **JavaScript/TypeScript SDK** + online playground (generate a code
|
|
130
160
|
in the browser in 10 seconds).
|
|
131
|
-
|
|
161
|
+
5. Larger radii / capacity beyond 329 stored bytes (breaking header
|
|
162
|
+
change).
|
|
132
163
|
|
|
133
164
|
Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
134
165
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hexatess-code"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.1"
|
|
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.3.
|
|
61
|
-
SPEC_VERSION = "0.
|
|
64
|
+
__version__ = "0.3.1"
|
|
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
|
]
|