Enroll from a repo data dir; read packshots in every orientation

Two enrollment defects, both found by trying to enroll real packshots.

`rebuild_catalog.py` crashed on every successful run: the report referenced
`small` but the call to `below_pyramid_floor` was missing, so the pyramid-floor
check the enrollment protocol asks for never ran and the tool died with a
NameError after writing the catalog.

The name/OCR check read one orientation. A packshot whose identifying text runs
vertically - the やまや めんたいマヨネーズ image reads 明太手屋さんが only when
rotated - was refused as a mismatch, which is the worst failure this check has:
it rejects good data while claiming to validate it. `best_orientation_text`
tries all four right-angle rotations and picks the one that explains the most of
the catalog name, not the one that reads the most characters, because the
upright pass returns stray kana that concatenate longer than the real read.

The regression test goes through `audit`, not the helper: the bug was that
`audit` called a single-orientation read, and a helper unit test would have
passed on the broken code. Verified to fail before the fix and pass after.

Data now lives in the repo, not /tmp:

  data/packshots/source/   packshots as downloaded, with ATTRIBUTION.json
  data/evaluations/        per-session run artifacts, so a number in
                           DECISION.md traces to the run that produced it

sku_l09 is re-sourced at 1000x1000 from Matsukiyo's Rakuten image, replacing
the 305px view that the README documents as the reason l09 wins facings it
should not.
This commit is contained in:
tngo 2026-09-29 02:33:47 +09:00
parent 443a1957a9
commit 68061635d9
63 changed files with 63861 additions and 3 deletions

View file

@ -170,6 +170,34 @@ def name_ocr_similarity(name: str, ocr_text: str) -> float:
return len(name_bigrams & bigrams(ocr_text)) / max(1, len(name_bigrams)) return len(name_bigrams & bigrams(ocr_text)) / max(1, len(name_bigrams))
def best_orientation_text(ocr_engine: Any, image: np.ndarray, name: str = "") -> str:
"""OCR text from whichever of the four right-angle rotations matches best.
A packshot's identifying text is not always horizontal: the やまや
めんたいマヨネーズ packshot reads 明太手屋さんが only at 270 degrees, and
the upright pass returns three stray characters. Checking one orientation
refused a correct image, which is the worst failure this check has - it
rejects good data while claiming to validate it.
The winner is the rotation that explains the most of the catalog name, not
the one that reads the most characters: the upright pass on that packshot
returns stray kana that concatenate longer than the real read, so length
picks the wrong one. With no name to compare against, length is the only
signal left.
"""
rotations = (
image,
cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE),
cv2.rotate(image, cv2.ROTATE_180),
cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE),
)
reads = [" ".join(line.text for line in ocr_engine.read(upscale(rotated)).lines)
for rotated in rotations]
if name:
return max(reads, key=lambda text: name_ocr_similarity(name, text))
return max(reads, key=lambda text: len(text.strip()))
def collect(source: str) -> Dict[str, List[str]]: def collect(source: str) -> Dict[str, List[str]]:
by_sku: Dict[str, List[str]] = {} by_sku: Dict[str, List[str]] = {}
for name in sorted(os.listdir(source)): for name in sorted(os.listdir(source)):
@ -291,7 +319,7 @@ def audit(
continue continue
if ocr_engine is not None and names is not None: if ocr_engine is not None and names is not None:
try: try:
text = " ".join(line.text for line in ocr_engine.read(upscale(image)).lines) text = best_orientation_text(ocr_engine, image, names.get(sku, ""))
except OcrUnavailable as error: except OcrUnavailable as error:
print(f"OCR skipped for {name}: {error}", file=sys.stderr) print(f"OCR skipped for {name}: {error}", file=sys.stderr)
else: else:
@ -535,6 +563,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
# and vetoes it from the CSV alone, so it is a coverage gap, not a refusal. # and vetoes it from the CSV alone, so it is a coverage gap, not a refusal.
text_only = sorted(set(names) - set(kept)) text_only = sorted(set(names) - set(kept))
siblings = text_discriminated_siblings(kept, args.source, names) siblings = text_discriminated_siblings(kept, args.source, names)
small = below_pyramid_floor(kept, args.source)
verdict = live_ready_verdict(kept, names, shelf_skus) verdict = live_ready_verdict(kept, names, shelf_skus)
report = { report = {
"source": args.source, "source": args.source,

View file

@ -184,5 +184,95 @@ class BadShelfListFailsBeforeTheSwapTests(unittest.TestCase):
self.assertFalse(os.path.exists(output), "the catalog was replaced before the check") self.assertFalse(os.path.exists(output), "the catalog was replaced before the check")
if __name__ == "__main__": class OrientationTests(unittest.TestCase):
unittest.main() """A packshot whose identifying text is not horizontal.
The やまや めんたいマヨネーズ packshot reads its brand only when rotated;
the upright pass returns stray kana. Checking one orientation refused a
correct image, which is the worst failure this check has - it rejects good
data while claiming to validate it.
"""
class FakeOcr:
"""Reads a fixed string per rotation.
Rotations are identified by aspect ratio, not by exact shape: the
caller upscales before OCR, so the fake sees scaled dimensions. The
upright and 180-degree passes keep the source's orientation (wide),
the two 90-degree passes swap it (tall). A corner marker cannot tell
them apart, because `cv2.rotate` carries the corner value through.
"""
name = "fake"
def __init__(self, by_orientation):
self.by_orientation = by_orientation
def read(self, image):
class Line:
def __init__(self, text):
self.text = text
class Read:
def __init__(self, text):
self.lines = [Line(part) for part in text.split("|") if part]
height, width = image.shape[:2]
key = "wide" if width > height else "tall"
return Read(self.by_orientation.get(key, ""))
def marked(self, height, width):
return np.zeros((height, width, 3), dtype="uint8")
def test_a_rotated_read_wins_when_it_matches_the_name(self):
"""The upright pass reads noise; the rotated pass reads the product."""
from rebuild_catalog import best_orientation_text
engine = self.FakeOcr({"wide": "つくつ|た", "tall": "明太手屋さんが|マヨネーズ"})
text = best_orientation_text(engine, self.marked(40, 60), "やまや めんたいマヨネーズタイプ 200g")
self.assertIn("明太手屋さんが", text)
def test_the_upright_read_wins_when_it_matches(self):
from rebuild_catalog import best_orientation_text
engine = self.FakeOcr({"wide": "ピュアセレクト|マヨネーズ|450g", "tall": "ノイズ"})
text = best_orientation_text(engine, self.marked(40, 60), "ピュアセレクト マヨネーズ 450g")
self.assertIn("ピュアセレクト", text)
def test_without_a_name_the_longest_read_wins(self):
from rebuild_catalog import best_orientation_text
engine = self.FakeOcr({"wide": "ab", "tall": "abcdefgh"})
self.assertEqual(best_orientation_text(engine, self.marked(40, 60)), "abcdefgh")
def test_audit_keeps_a_view_whose_name_only_reads_rotated(self):
"""The regression: `audit` must try every orientation before refusing.
This is the boundary that matters. `best_orientation_text` passing its
own unit test proves nothing about enrollment - the bug was that
`audit` called a single-orientation read, so a correct packshot whose
brand text runs vertically was refused as a name/OCR mismatch.
"""
from rebuild_catalog import audit
root = tempfile.mkdtemp(prefix="orient-")
# Wider than tall, so the upright pass is the "wide" read (noise) and
# only a 90-degree rotation produces the "tall" read that matches.
cv2.imwrite(os.path.join(root, "sku_x1.jpg"), self.marked(300, 400))
engine = self.FakeOcr({"wide": "つくつ|た", "tall": "キユーピー|エッグケア|305g"})
kept, refused = audit(root, min_views=1, names={"sku_x1": "キューピー エッグケア 305g"},
ocr_engine=engine)
self.assertEqual(kept, {"sku_x1": ["sku_x1.jpg"]}, refused)
self.assertEqual(refused, [])
def test_audit_still_refuses_a_view_the_name_never_matches(self):
"""The check must keep its teeth: a wrong product is still refused."""
from rebuild_catalog import audit
root = tempfile.mkdtemp(prefix="orient-bad-")
cv2.imwrite(os.path.join(root, "sku_x1.jpg"), self.marked(400, 300))
engine = self.FakeOcr({"wide": "キューピー|マヨネーズ", "tall": "キューピー|マヨネーズ"})
kept, refused = audit(root, min_views=1, names={"sku_x1": "やまや めんたいマヨネーズタイプ 200g"},
ocr_engine=engine)
self.assertEqual(kept, {})
self.assertEqual(len(refused), 1, refused)
self.assertIn("name/OCR", refused[0]["reason"])

View file

@ -0,0 +1,34 @@
# Recognition data
Everything the identity work needs to be reproducible: sourced packshots,
evaluation runs, and the audits that produced them. Nothing here is ephemeral;
a run that matters is written here, not to a temp directory.
## `packshots/source/`
Front packshots as downloaded, before enrollment. `ATTRIBUTION.json` records the
source URL, the product as printed, and the manufacturer for each. `ocr-reads.json`
holds the OCR text each image produced, so a name/OCR refusal can be re-checked
without re-running Vision.
`rebuild_catalog.py` reads this directory and writes a catalog:
.venv/bin/python code/rebuild_catalog.py --source data/packshots/source \
--output stock-lopia-clean --csv stock-lopia/stock.csv --min-views 1 --replace
## `evaluations/`
One directory per session. Each holds the raw artifacts a claim was computed
from, so a number in DECISION.md can be traced back to the run that produced it.
| Directory | Contents |
|---|---|
| `stream/` | WebSocket track dumps from `stream_client.py`, scored by `score_stream.py`. |
| `still/` | `/v1/recognize` responses for the still-image path. |
| `catalog-audit/` | `rebuild_catalog.py --json` reports. |
| `scratch/` | Intermediate images (rotations, crops) kept because they document a finding. |
Scoring a stream dump:
.venv/bin/python code/score_stream.py data/evaluations/<session>/stream/<file>.json \
--labels eval/labels/image6.json --stock-dir stock-lopia-clean

View file

@ -0,0 +1,70 @@
{
"source": "data/packshots/source",
"output": "/tmp/lopia-audit",
"min_width": 120,
"min_height": 160,
"min_views": 1,
"duplicate_cosine": 0.98,
"min_name_ocr": 0.7,
"ocr": "vision",
"text_only_skus": [
"sku_l04",
"sku_l07",
"sku_l08",
"sku_l09",
"sku_l10",
"sku_l12"
],
"text_discriminated_siblings": [],
"below_pyramid_floor": [],
"completeness": {
"live_ready": null,
"reason": "no --shelf-tags given; the completeness rule needs the shelf's SKU list",
"enrolled": 6,
"text_only": [
"sku_l04",
"sku_l07",
"sku_l08",
"sku_l09",
"sku_l10",
"sku_l12"
]
},
"skus": {
"sku_l01": [
"sku_l01.jpg"
],
"sku_l02": [
"sku_l02.jpg"
],
"sku_l03": [
"sku_l03.jpg"
],
"sku_l05": [
"sku_l05.jpg"
],
"sku_l06": [
"sku_l06.jpg"
],
"sku_l11": [
"sku_l11.jpg"
]
},
"refused": [
{
"sku": "sku_l04",
"view": "sku_l04.jpg",
"reason": "name/OCR 0.00 below 0.70: 明太手屋さんが つくつ た コ"
},
{
"sku": "sku_l07",
"view": "sku_l07.jpg",
"reason": "name/OCR 0.57 below 0.70: ラッキー マヨネーズ 調味料(アミノ酸)不使用"
},
{
"sku": "sku_l09",
"view": "sku_l09.jpg",
"reason": "name/OCR 0.05 below 0.70: キューピーマヨネーズ KEWPIE MAYONNAISE ® 450g"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,214 @@
{
"image": "add/image6.jpg",
"stock_dir": "stock-lopia-clean",
"labels": "eval/labels/image6.json",
"provenance": {
"catalog_hash": "10f55211435aa64c0ed4ca24a7b902ce4ed8a30299409680942b78ec9b3d08c8",
"labels_hash": "5aabf029921ced767d4e14a18e8bccb55812dd974852c56e0931a98fa10366cf",
"image_hash": "a08b89ba37d27c9d5a81bcd56bcd042e6baf54d2f182bbaaf47e97f14c9cd525",
"code_revision": "b6c87c45ea2aa3ed59f5764274341f3ffed30fe0"
},
"facings": 111,
"leakage": {
"excluded": 0,
"labels": []
},
"hub_share_limit": 0.3,
"enrollment": {
"catalog_skus": [
"sku_l01",
"sku_l02",
"sku_l03",
"sku_l04",
"sku_l05",
"sku_l06",
"sku_l07",
"sku_l08",
"sku_l09",
"sku_l10",
"sku_l11",
"sku_l12"
],
"enrolled_skus": [
"sku_l01",
"sku_l02",
"sku_l03",
"sku_l05",
"sku_l06",
"sku_l09",
"sku_l11"
],
"unenrolled_skus": {
"sku_l10": 11,
"sku_l07": 6,
"sku_l04": 1,
"sku_l08": 1
},
"facings_without_enrollment": 19,
"scored_facings": 109
},
"results": {
"full": [
{
"lane": "van",
"accept": 1.0,
"margin": 0.15,
"in_catalog": 61,
"out_catalog": 48,
"enrolled": 42,
"correct": 32,
"wrong": 77,
"rejected": 0,
"catalog_ceiling": 0.6885,
"identity_ceiling": 0.7619,
"decision_ceiling": 0.7619,
"end_to_end_coverage": 0.5246,
"false_accepts": 77,
"unknown_false_accepts": 48,
"unenrolled_false_accepts": 19,
"false_accept_rate": 1.0,
"near_unknown_false_accept_rate": 1.0,
"precision": 0.2936,
"max_sku_share": 0.2569,
"hub_sku": "sku_l09",
"hub_alarm": false,
"histogram": {
"sku_l09": 28,
"sku_l05": 19,
"sku_l02": 15,
"sku_l03": 14,
"sku_l06": 13,
"sku_l11": 12,
"sku_l01": 8
},
"per_sku": {
"sku_l01": {
"correct": 5,
"total": 5
},
"sku_l02": {
"correct": 4,
"total": 4
},
"sku_l03": {
"correct": 4,
"total": 4
},
"sku_l04": {
"correct": 0,
"total": 1
},
"sku_l05": {
"correct": 3,
"total": 3
},
"sku_l06": {
"correct": 0,
"total": 4
},
"sku_l07": {
"correct": 0,
"total": 6
},
"sku_l08": {
"correct": 0,
"total": 1
},
"sku_l09": {
"correct": 13,
"total": 13
},
"sku_l10": {
"correct": 0,
"total": 11
},
"sku_l11": {
"correct": 3,
"total": 9
}
},
"ms_per_crop": 15.4
},
{
"lane": "local",
"accept": 2.0,
"margin": 0.15,
"in_catalog": 61,
"out_catalog": 48,
"enrolled": 42,
"correct": 36,
"wrong": 5,
"rejected": 68,
"catalog_ceiling": 0.6885,
"identity_ceiling": 0.9286,
"decision_ceiling": 0.8571,
"end_to_end_coverage": 0.5902,
"false_accepts": 5,
"unknown_false_accepts": 4,
"unenrolled_false_accepts": 0,
"false_accept_rate": 0.0833,
"near_unknown_false_accept_rate": 0.0,
"precision": 0.878,
"max_sku_share": 0.2927,
"hub_sku": "sku_l09",
"hub_alarm": false,
"histogram": {
"sku_l09": 12,
"sku_l11": 8,
"sku_l03": 6,
"sku_l01": 5,
"sku_l07": 4,
"sku_l02": 3,
"sku_l05": 3
},
"per_sku": {
"sku_l01": {
"correct": 5,
"total": 5
},
"sku_l02": {
"correct": 2,
"total": 4
},
"sku_l03": {
"correct": 4,
"total": 4
},
"sku_l04": {
"correct": 0,
"total": 1
},
"sku_l05": {
"correct": 3,
"total": 3
},
"sku_l06": {
"correct": 0,
"total": 4
},
"sku_l07": {
"correct": 4,
"total": 6
},
"sku_l08": {
"correct": 0,
"total": 1
},
"sku_l09": {
"correct": 12,
"total": 13
},
"sku_l10": {
"correct": 0,
"total": 11
},
"sku_l11": {
"correct": 6,
"total": 9
}
},
"ms_per_crop": 26.6
}
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,554 @@
{
"windows": [
[
0,
640,
1280,
1360
],
[
12,
640,
1292,
1360
],
[
24,
640,
1304,
1360
],
[
36,
640,
1316,
1360
],
[
48,
640,
1328,
1360
],
[
60,
640,
1340,
1360
],
[
72,
640,
1352,
1360
],
[
84,
640,
1364,
1360
],
[
96,
640,
1376,
1360
],
[
108,
640,
1388,
1360
],
[
120,
640,
1400,
1360
],
[
132,
640,
1412,
1360
],
[
144,
640,
1424,
1360
],
[
156,
640,
1436,
1360
],
[
168,
640,
1448,
1360
],
[
180,
640,
1460,
1360
],
[
192,
640,
1472,
1360
],
[
204,
640,
1484,
1360
],
[
216,
640,
1496,
1360
],
[
220,
640,
1500,
1360
],
[
216,
640,
1496,
1360
],
[
204,
640,
1484,
1360
],
[
192,
640,
1472,
1360
],
[
180,
640,
1460,
1360
],
[
168,
640,
1448,
1360
],
[
156,
640,
1436,
1360
],
[
144,
640,
1424,
1360
],
[
132,
640,
1412,
1360
],
[
120,
640,
1400,
1360
],
[
108,
640,
1388,
1360
],
[
96,
640,
1376,
1360
],
[
84,
640,
1364,
1360
],
[
72,
640,
1352,
1360
],
[
60,
640,
1340,
1360
],
[
48,
640,
1328,
1360
],
[
36,
640,
1316,
1360
],
[
24,
640,
1304,
1360
],
[
12,
640,
1292,
1360
],
[
0,
640,
1280,
1360
],
[
12,
640,
1292,
1360
],
[
24,
640,
1304,
1360
],
[
36,
640,
1316,
1360
],
[
48,
640,
1328,
1360
],
[
60,
640,
1340,
1360
],
[
72,
640,
1352,
1360
],
[
84,
640,
1364,
1360
],
[
96,
640,
1376,
1360
],
[
108,
640,
1388,
1360
],
[
120,
640,
1400,
1360
],
[
132,
640,
1412,
1360
],
[
144,
640,
1424,
1360
],
[
156,
640,
1436,
1360
],
[
168,
640,
1448,
1360
],
[
180,
640,
1460,
1360
],
[
192,
640,
1472,
1360
],
[
204,
640,
1484,
1360
],
[
216,
640,
1496,
1360
],
[
220,
640,
1500,
1360
],
[
216,
640,
1496,
1360
],
[
204,
640,
1484,
1360
],
[
192,
640,
1472,
1360
],
[
180,
640,
1460,
1360
],
[
168,
640,
1448,
1360
],
[
156,
640,
1436,
1360
],
[
144,
640,
1424,
1360
],
[
132,
640,
1412,
1360
],
[
120,
640,
1400,
1360
],
[
108,
640,
1388,
1360
],
[
96,
640,
1376,
1360
],
[
84,
640,
1364,
1360
],
[
72,
640,
1352,
1360
],
[
60,
640,
1340,
1360
],
[
48,
640,
1328,
1360
],
[
36,
640,
1316,
1360
],
[
24,
640,
1304,
1360
],
[
12,
640,
1292,
1360
],
[
0,
640,
1280,
1360
],
[
12,
640,
1292,
1360
],
[
24,
640,
1304,
1360
],
[
36,
640,
1316,
1360
],
[
48,
640,
1328,
1360
],
[
60,
640,
1340,
1360
],
[
72,
640,
1352,
1360
],
[
84,
640,
1364,
1360
],
[
96,
640,
1376,
1360
],
[
108,
640,
1388,
1360
],
[
120,
640,
1400,
1360
],
[
132,
640,
1412,
1360
],
[
144,
640,
1424,
1360
],
[
156,
640,
1436,
1360
],
[
168,
640,
1448,
1360
]
],
"source": "add/image6.jpg",
"pace": "send",
"seconds": 8.1,
"tracks": []
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,721 @@
{
"source": "add/image6.jpg",
"pace": "result",
"seconds": 12.0,
"tracks": [
{
"track_id": 1,
"sku_id": "sku_l09",
"label_box": [
580,
122,
666,
332
],
"label_frame_id": 0,
"result_frame_id": 108,
"inherited": true,
"restarts": 2
},
{
"track_id": 2,
"sku_id": "sku_l09",
"label_box": [
659,
760,
746,
972
],
"label_frame_id": 71,
"result_frame_id": 81,
"inherited": true,
"restarts": 2
},
{
"track_id": 4,
"sku_id": "sku_l09",
"label_box": [
586,
119,
674,
333
],
"label_frame_id": 0,
"result_frame_id": 115,
"inherited": true,
"restarts": 5
},
{
"track_id": 5,
"sku_id": "sku_l09",
"label_box": [
512,
755,
604,
972
],
"label_frame_id": 98,
"result_frame_id": 114,
"inherited": true,
"restarts": 0
},
{
"track_id": 6,
"sku_id": "sku_l09",
"label_box": [
937,
764,
1036,
975
],
"label_frame_id": 16,
"result_frame_id": 25,
"inherited": true,
"restarts": 0
},
{
"track_id": 8,
"sku_id": "sku_l09",
"label_box": [
291,
754,
389,
971
],
"label_frame_id": 107,
"result_frame_id": 114,
"inherited": true,
"restarts": 0
},
{
"track_id": 9,
"sku_id": "sku_l09",
"label_box": [
459,
1068,
555,
1251
],
"label_frame_id": 67,
"result_frame_id": 89,
"inherited": true,
"restarts": 0
},
{
"track_id": 11,
"sku_id": "sku_l09",
"label_box": [
688,
1057,
789,
1142
],
"label_frame_id": 50,
"result_frame_id": 59,
"inherited": false,
"restarts": 1
},
{
"track_id": 12,
"sku_id": "sku_l09",
"label_box": [
1254,
1053,
1334,
1244
],
"label_frame_id": 61,
"result_frame_id": 69,
"inherited": true,
"restarts": 1
},
{
"track_id": 13,
"sku_id": "sku_l09",
"label_box": [
1079,
762,
1176,
973
],
"label_frame_id": 89,
"result_frame_id": 115,
"inherited": true,
"restarts": 1
},
{
"track_id": 14,
"sku_id": "sku_l09",
"label_box": [
1277,
1065,
1345,
1239
],
"label_frame_id": 52,
"result_frame_id": 63,
"inherited": true,
"restarts": 2
},
{
"track_id": 15,
"sku_id": "sku_l09",
"label_box": [
798,
765,
966,
971
],
"label_frame_id": 83,
"result_frame_id": 114,
"inherited": true,
"restarts": 0
},
{
"track_id": 16,
"sku_id": "sku_l09",
"label_box": [
430,
1070,
543,
1250
],
"label_frame_id": 99,
"result_frame_id": 113,
"inherited": false,
"restarts": 1
},
{
"track_id": 17,
"sku_id": "sku_l10",
"label_box": [
878,
1146,
970,
1245
],
"label_frame_id": 90,
"result_frame_id": 106,
"inherited": false,
"restarts": 0
},
{
"track_id": 18,
"sku_id": "sku_l09",
"label_box": [
1128,
763,
1271,
973
],
"label_frame_id": 99,
"result_frame_id": 112,
"inherited": false,
"restarts": 10
},
{
"track_id": 19,
"sku_id": "sku_l09",
"label_box": [
680,
1058,
780,
1142
],
"label_frame_id": 97,
"result_frame_id": 110,
"inherited": false,
"restarts": 2
},
{
"track_id": 21,
"sku_id": "sku_l10",
"label_box": [
673,
1146,
773,
1247
],
"label_frame_id": 98,
"result_frame_id": 111,
"inherited": false,
"restarts": 0
},
{
"track_id": 22,
"sku_id": "sku_l09",
"label_box": [
287,
775,
347,
969
],
"label_frame_id": 2,
"result_frame_id": 18,
"inherited": true,
"restarts": 0
},
{
"track_id": 23,
"sku_id": "sku_l10",
"label_box": [
917,
1146,
999,
1244
],
"label_frame_id": 98,
"result_frame_id": 112,
"inherited": true,
"restarts": 0
},
{
"track_id": 25,
"sku_id": "sku_l09",
"label_box": [
488,
1078,
570,
1248
],
"label_frame_id": 33,
"result_frame_id": 53,
"inherited": true,
"restarts": 1
},
{
"track_id": 26,
"sku_id": "sku_l09",
"label_box": [
198,
755,
258,
972
],
"label_frame_id": 2,
"result_frame_id": 15,
"inherited": false,
"restarts": 0
},
{
"track_id": 27,
"sku_id": "sku_l09",
"label_box": [
484,
1062,
572,
1248
],
"label_frame_id": 89,
"result_frame_id": 97,
"inherited": true,
"restarts": 2
},
{
"track_id": 29,
"sku_id": "sku_l09",
"label_box": [
879,
1055,
972,
1141
],
"label_frame_id": 57,
"result_frame_id": 70,
"inherited": true,
"restarts": 4
},
{
"track_id": 32,
"sku_id": "sku_l10",
"label_box": [
67,
422,
248,
613
],
"label_frame_id": 0,
"result_frame_id": 47,
"inherited": true,
"restarts": 2
},
{
"track_id": 35,
"sku_id": "sku_l09",
"label_box": [
1117,
763,
1179,
973
],
"label_frame_id": 85,
"result_frame_id": 113,
"inherited": true,
"restarts": 1
},
{
"track_id": 58,
"sku_id": "sku_l09",
"label_box": [
1328,
1065,
1379,
1238
],
"label_frame_id": 20,
"result_frame_id": 23,
"inherited": true,
"restarts": 0
},
{
"track_id": 59,
"sku_id": "sku_l09",
"label_box": [
951,
124,
1090,
218
],
"label_frame_id": 0,
"result_frame_id": 104,
"inherited": true,
"restarts": 5
},
{
"track_id": 114,
"sku_id": "sku_l09",
"label_box": [
1191,
1054,
1271,
1245
],
"label_frame_id": 99,
"result_frame_id": 111,
"inherited": false,
"restarts": 3
},
{
"track_id": 127,
"sku_id": "sku_l09",
"label_box": [
243,
755,
298,
972
],
"label_frame_id": 35,
"result_frame_id": 40,
"inherited": true,
"restarts": 1
},
{
"track_id": 135,
"sku_id": "sku_l09",
"label_box": [
521,
118,
608,
330
],
"label_frame_id": 0,
"result_frame_id": 115,
"inherited": true,
"restarts": 4
},
{
"track_id": 136,
"sku_id": "sku_l09",
"label_box": [
1072,
1054,
1147,
1245
],
"label_frame_id": 53,
"result_frame_id": 59,
"inherited": true,
"restarts": 1
},
{
"track_id": 163,
"sku_id": "sku_l09",
"label_box": [
125,
752,
191,
971
],
"label_frame_id": 32,
"result_frame_id": 48,
"inherited": true,
"restarts": 0
},
{
"track_id": 178,
"sku_id": "sku_l10",
"label_box": [
156,
419,
307,
616
],
"label_frame_id": 0,
"result_frame_id": 41,
"inherited": true,
"restarts": 5
},
{
"track_id": 262,
"sku_id": "sku_l09",
"label_box": [
79,
113,
152,
331
],
"label_frame_id": 0,
"result_frame_id": 74,
"inherited": true,
"restarts": 1
},
{
"track_id": 269,
"sku_id": "sku_l09",
"label_box": [
197,
755,
257,
973
],
"label_frame_id": 48,
"result_frame_id": 57,
"inherited": false,
"restarts": 1
},
{
"track_id": 295,
"sku_id": "sku_l09",
"label_box": [
84,
1059,
179,
1148
],
"label_frame_id": 78,
"result_frame_id": 90,
"inherited": true,
"restarts": 1
},
{
"track_id": 302,
"sku_id": "sku_l09",
"label_box": [
941,
410,
1009,
605
],
"label_frame_id": 0,
"result_frame_id": 50,
"inherited": true,
"restarts": 0
},
{
"track_id": 315,
"sku_id": "sku_l09",
"label_box": [
764,
414,
848,
505
],
"label_frame_id": 0,
"result_frame_id": 51,
"inherited": true,
"restarts": 0
},
{
"track_id": 331,
"sku_id": "sku_l09",
"label_box": [
399,
1057,
532,
1255
],
"label_frame_id": 105,
"result_frame_id": 115,
"inherited": true,
"restarts": 9
},
{
"track_id": 345,
"sku_id": "sku_l09",
"label_box": [
763,
415,
854,
499
],
"label_frame_id": 0,
"result_frame_id": 115,
"inherited": true,
"restarts": 3
},
{
"track_id": 353,
"sku_id": "sku_l09",
"label_box": [
1119,
1053,
1180,
1245
],
"label_frame_id": 99,
"result_frame_id": 113,
"inherited": false,
"restarts": 2
},
{
"track_id": 370,
"sku_id": "sku_l09",
"label_box": [
83,
117,
129,
333
],
"label_frame_id": 0,
"result_frame_id": 69,
"inherited": true,
"restarts": 1
},
{
"track_id": 378,
"sku_id": "sku_l09",
"label_box": [
576,
758,
667,
971
],
"label_frame_id": 106,
"result_frame_id": 114,
"inherited": true,
"restarts": 1
},
{
"track_id": 404,
"sku_id": "sku_l09",
"label_box": [
68,
753,
131,
971
],
"label_frame_id": 78,
"result_frame_id": 85,
"inherited": true,
"restarts": 1
},
{
"track_id": 416,
"sku_id": "sku_l09",
"label_box": [
1089,
416,
1147,
604
],
"label_frame_id": 0,
"result_frame_id": 83,
"inherited": true,
"restarts": 1
},
{
"track_id": 499,
"sku_id": "sku_l09",
"label_box": [
443,
767,
510,
968
],
"label_frame_id": 95,
"result_frame_id": 97,
"inherited": true,
"restarts": 0
},
{
"track_id": 507,
"sku_id": "sku_l09",
"label_box": [
149,
757,
208,
971
],
"label_frame_id": 84,
"result_frame_id": 90,
"inherited": true,
"restarts": 1
},
{
"track_id": 552,
"sku_id": "sku_l09",
"label_box": [
757,
413,
849,
507
],
"label_frame_id": 0,
"result_frame_id": 105,
"inherited": true,
"restarts": 1
},
{
"track_id": 582,
"sku_id": "sku_l09",
"label_box": [
701,
413,
842,
505
],
"label_frame_id": 0,
"result_frame_id": 102,
"inherited": true,
"restarts": 1
},
{
"track_id": 613,
"sku_id": "sku_l09",
"label_box": [
477,
767,
537,
969
],
"label_frame_id": 104,
"result_frame_id": 108,
"inherited": true,
"restarts": 0
},
{
"track_id": 673,
"sku_id": "sku_l09",
"label_box": [
1087,
1051,
1143,
1245
],
"label_frame_id": 108,
"result_frame_id": 113,
"inherited": true,
"restarts": 0
}
]
}

View file

@ -0,0 +1,791 @@
{
"source": "add/image6.jpg",
"pace": "send",
"seconds": 12.0,
"tracks": [
{
"track_id": 1,
"sku_id": "sku_l09",
"label_box": [
527,
122,
613,
331
],
"label_frame_id": 0,
"result_frame_id": 104,
"inherited": true,
"restarts": 2
},
{
"track_id": 2,
"sku_id": "sku_l09",
"label_box": [
696,
760,
783,
972
],
"label_frame_id": 70,
"result_frame_id": 81,
"inherited": true,
"restarts": 2
},
{
"track_id": 4,
"sku_id": "sku_l09",
"label_box": [
494,
120,
582,
332
],
"label_frame_id": 0,
"result_frame_id": 108,
"inherited": true,
"restarts": 5
},
{
"track_id": 5,
"sku_id": "sku_l09",
"label_box": [
390,
757,
482,
971
],
"label_frame_id": 106,
"result_frame_id": 112,
"inherited": true,
"restarts": 0
},
{
"track_id": 6,
"sku_id": "sku_l09",
"label_box": [
949,
763,
1049,
973
],
"label_frame_id": 16,
"result_frame_id": 25,
"inherited": true,
"restarts": 0
},
{
"track_id": 8,
"sku_id": "sku_l09",
"label_box": [
399,
754,
497,
971
],
"label_frame_id": 98,
"result_frame_id": 112,
"inherited": true,
"restarts": 0
},
{
"track_id": 9,
"sku_id": "sku_l09",
"label_box": [
441,
1071,
536,
1250
],
"label_frame_id": 74,
"result_frame_id": 89,
"inherited": false,
"restarts": 0
},
{
"track_id": 12,
"sku_id": "sku_l09",
"label_box": [
1194,
1053,
1274,
1244
],
"label_frame_id": 50,
"result_frame_id": 69,
"inherited": true,
"restarts": 1
},
{
"track_id": 13,
"sku_id": "sku_l09",
"label_box": [
1127,
763,
1224,
973
],
"label_frame_id": 97,
"result_frame_id": 112,
"inherited": true,
"restarts": 1
},
{
"track_id": 14,
"sku_id": "sku_l09",
"label_box": [
1277,
1065,
1345,
1239
],
"label_frame_id": 52,
"result_frame_id": 63,
"inherited": true,
"restarts": 2
},
{
"track_id": 15,
"sku_id": "sku_l09",
"label_box": [
805,
764,
976,
972
],
"label_frame_id": 89,
"result_frame_id": 109,
"inherited": false,
"restarts": 0
},
{
"track_id": 16,
"sku_id": "sku_l09",
"label_box": [
583,
1069,
683,
1251
],
"label_frame_id": 99,
"result_frame_id": 112,
"inherited": true,
"restarts": 1
},
{
"track_id": 17,
"sku_id": "sku_l10",
"label_box": [
867,
1147,
958,
1245
],
"label_frame_id": 98,
"result_frame_id": 109,
"inherited": false,
"restarts": 0
},
{
"track_id": 18,
"sku_id": "sku_l09",
"label_box": [
926,
123,
1068,
332
],
"label_frame_id": 0,
"result_frame_id": 102,
"inherited": true,
"restarts": 9
},
{
"track_id": 19,
"sku_id": "sku_l09",
"label_box": [
694,
1057,
794,
1142
],
"label_frame_id": 108,
"result_frame_id": 112,
"inherited": true,
"restarts": 2
},
{
"track_id": 20,
"sku_id": "sku_l10",
"label_box": [
211,
1128,
285,
1260
],
"label_frame_id": 6,
"result_frame_id": 17,
"inherited": false,
"restarts": 2
},
{
"track_id": 21,
"sku_id": "sku_l10",
"label_box": [
848,
1145,
948,
1246
],
"label_frame_id": 98,
"result_frame_id": 112,
"inherited": true,
"restarts": 0
},
{
"track_id": 22,
"sku_id": "sku_l09",
"label_box": [
287,
775,
347,
969
],
"label_frame_id": 2,
"result_frame_id": 18,
"inherited": true,
"restarts": 0
},
{
"track_id": 23,
"sku_id": "sku_l10",
"label_box": [
869,
1144,
952,
1244
],
"label_frame_id": 104,
"result_frame_id": 112,
"inherited": true,
"restarts": 0
},
{
"track_id": 25,
"sku_id": "sku_l09",
"label_box": [
488,
1078,
570,
1248
],
"label_frame_id": 33,
"result_frame_id": 53,
"inherited": true,
"restarts": 1
},
{
"track_id": 26,
"sku_id": "sku_l09",
"label_box": [
198,
755,
258,
972
],
"label_frame_id": 2,
"result_frame_id": 14,
"inherited": false,
"restarts": 0
},
{
"track_id": 27,
"sku_id": "sku_l09",
"label_box": [
522,
1067,
610,
1248
],
"label_frame_id": 87,
"result_frame_id": 92,
"inherited": true,
"restarts": 2
},
{
"track_id": 29,
"sku_id": "sku_l09",
"label_box": [
947,
1055,
1038,
1139
],
"label_frame_id": 58,
"result_frame_id": 73,
"inherited": true,
"restarts": 4
},
{
"track_id": 32,
"sku_id": "sku_l10",
"label_box": [
67,
422,
248,
613
],
"label_frame_id": 0,
"result_frame_id": 47,
"inherited": true,
"restarts": 2
},
{
"track_id": 35,
"sku_id": "sku_l09",
"label_box": [
1057,
764,
1118,
972
],
"label_frame_id": 75,
"result_frame_id": 112,
"inherited": true,
"restarts": 1
},
{
"track_id": 58,
"sku_id": "sku_l09",
"label_box": [
1264,
1065,
1315,
1238
],
"label_frame_id": 14,
"result_frame_id": 23,
"inherited": true,
"restarts": 0
},
{
"track_id": 59,
"sku_id": "sku_l09",
"label_box": [
1092,
126,
1229,
218
],
"label_frame_id": 0,
"result_frame_id": 112,
"inherited": true,
"restarts": 5
},
{
"track_id": 114,
"sku_id": "sku_l09",
"label_box": [
1191,
1054,
1271,
1245
],
"label_frame_id": 99,
"result_frame_id": 108,
"inherited": false,
"restarts": 3
},
{
"track_id": 127,
"sku_id": "sku_l09",
"label_box": [
354,
756,
409,
973
],
"label_frame_id": 26,
"result_frame_id": 40,
"inherited": true,
"restarts": 1
},
{
"track_id": 135,
"sku_id": "sku_l09",
"label_box": [
658,
760,
744,
972
],
"label_frame_id": 92,
"result_frame_id": 104,
"inherited": true,
"restarts": 3
},
{
"track_id": 136,
"sku_id": "sku_l09",
"label_box": [
1076,
1053,
1152,
1245
],
"label_frame_id": 54,
"result_frame_id": 59,
"inherited": true,
"restarts": 1
},
{
"track_id": 163,
"sku_id": "sku_l09",
"label_box": [
111,
754,
177,
970
],
"label_frame_id": 32,
"result_frame_id": 50,
"inherited": true,
"restarts": 0
},
{
"track_id": 178,
"sku_id": "sku_l10",
"label_box": [
156,
419,
307,
616
],
"label_frame_id": 0,
"result_frame_id": 41,
"inherited": true,
"restarts": 5
},
{
"track_id": 262,
"sku_id": "sku_l09",
"label_box": [
449,
763,
522,
968
],
"label_frame_id": 44,
"result_frame_id": 55,
"inherited": false,
"restarts": 0
},
{
"track_id": 269,
"sku_id": "sku_l09",
"label_box": [
113,
758,
172,
972
],
"label_frame_id": 46,
"result_frame_id": 55,
"inherited": true,
"restarts": 1
},
{
"track_id": 295,
"sku_id": "sku_l09",
"label_box": [
84,
1059,
179,
1148
],
"label_frame_id": 78,
"result_frame_id": 90,
"inherited": true,
"restarts": 1
},
{
"track_id": 302,
"sku_id": "sku_l09",
"label_box": [
929,
414,
995,
603
],
"label_frame_id": 0,
"result_frame_id": 51,
"inherited": true,
"restarts": 0
},
{
"track_id": 315,
"sku_id": "sku_l09",
"label_box": [
757,
413,
849,
507
],
"label_frame_id": 0,
"result_frame_id": 67,
"inherited": true,
"restarts": 1
},
{
"track_id": 331,
"sku_id": "sku_l09",
"label_box": [
422,
1057,
555,
1255
],
"label_frame_id": 100,
"result_frame_id": 112,
"inherited": true,
"restarts": 9
},
{
"track_id": 345,
"sku_id": "sku_l09",
"label_box": [
835,
1055,
927,
1140
],
"label_frame_id": 107,
"result_frame_id": 111,
"inherited": true,
"restarts": 2
},
{
"track_id": 353,
"sku_id": "sku_l09",
"label_box": [
1119,
1053,
1180,
1245
],
"label_frame_id": 99,
"result_frame_id": 111,
"inherited": false,
"restarts": 2
},
{
"track_id": 370,
"sku_id": "sku_l09",
"label_box": [
48,
118,
91,
331
],
"label_frame_id": 0,
"result_frame_id": 66,
"inherited": true,
"restarts": 1
},
{
"track_id": 378,
"sku_id": "sku_l09",
"label_box": [
696,
758,
787,
971
],
"label_frame_id": 97,
"result_frame_id": 112,
"inherited": true,
"restarts": 1
},
{
"track_id": 404,
"sku_id": "sku_l09",
"label_box": [
45,
112,
83,
331
],
"label_frame_id": 0,
"result_frame_id": 71,
"inherited": true,
"restarts": 0
},
{
"track_id": 416,
"sku_id": "sku_l09",
"label_box": [
1089,
416,
1147,
604
],
"label_frame_id": 0,
"result_frame_id": 83,
"inherited": true,
"restarts": 1
},
{
"track_id": 476,
"sku_id": "sku_l10",
"label_box": [
63,
519,
138,
614
],
"label_frame_id": 0,
"result_frame_id": 80,
"inherited": true,
"restarts": 0
},
{
"track_id": 499,
"sku_id": "sku_l09",
"label_box": [
79,
113,
152,
331
],
"label_frame_id": 0,
"result_frame_id": 112,
"inherited": true,
"restarts": 1
},
{
"track_id": 507,
"sku_id": "sku_l09",
"label_box": [
128,
114,
187,
331
],
"label_frame_id": 0,
"result_frame_id": 85,
"inherited": true,
"restarts": 1
},
{
"track_id": 536,
"sku_id": "sku_l09",
"label_box": [
619,
1075,
689,
1248
],
"label_frame_id": 100,
"result_frame_id": 108,
"inherited": true,
"restarts": 1
},
{
"track_id": 552,
"sku_id": "sku_l09",
"label_box": [
757,
413,
849,
507
],
"label_frame_id": 0,
"result_frame_id": 105,
"inherited": true,
"restarts": 1
},
{
"track_id": 568,
"sku_id": "sku_l10",
"label_box": [
45,
512,
143,
619
],
"label_frame_id": 0,
"result_frame_id": 90,
"inherited": true,
"restarts": 0
},
{
"track_id": 582,
"sku_id": "sku_l09",
"label_box": [
684,
413,
826,
506
],
"label_frame_id": 0,
"result_frame_id": 102,
"inherited": true,
"restarts": 1
},
{
"track_id": 606,
"sku_id": "sku_l09",
"label_box": [
204,
758,
247,
971
],
"label_frame_id": 102,
"result_frame_id": 112,
"inherited": false,
"restarts": 1
},
{
"track_id": 613,
"sku_id": "sku_l09",
"label_box": [
453,
773,
510,
969
],
"label_frame_id": 100,
"result_frame_id": 111,
"inherited": false,
"restarts": 0
},
{
"track_id": 653,
"sku_id": "sku_l10",
"label_box": [
60,
510,
163,
622
],
"label_frame_id": 0,
"result_frame_id": 104,
"inherited": true,
"restarts": 0
},
{
"track_id": 673,
"sku_id": "sku_l09",
"label_box": [
1087,
1051,
1143,
1245
],
"label_frame_id": 108,
"result_frame_id": 112,
"inherited": true,
"restarts": 0
}
]
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
{
"sku_l04": {
"url": "https://m.media-amazon.com/images/I/61yWDtDjDuL.jpg",
"product": "やまや めんたいマヨネーズタイプ 200g",
"source_page": "https://www.amazon.co.jp/dp/B0197VWHRG",
"notes": "Exact 200g product; major-retailer pack shot. Manufacturer: 株式会社やまやコミュニケーションズ."
},
"sku_l07": {
"url": "https://m.media-amazon.com/images/I/91ugfvmpCvL.jpg",
"product": "丸和油脂 ラッキーマヨネーズ 200g",
"source_page": "https://www.amazon.co.jp/dp/B0BHQW3VVP",
"notes": "Exact 200g product; major-retailer pack shot. JAN/part number 4978931401615, corroborated by Yahoo Shopping listing."
}
}

View file

@ -0,0 +1 @@
{"sku_l01.jpg": "卵を使って いない キューピーエッグケア e 乳成分 えび かに くるみ 特定原材料8品目不使用 そば 落花生 305g", "sku_l02.jpg": "機能性表示食品届出番号J1501 キューピー 血圧ケア アマニ油 マヨネーズ α-リノレン酸 商品中30% アマニ油配合 200g 食生活は、主食、主菜、副菜を基本に、食事のバランスを。", "sku_l03.jpg": "Spicy: 辛さとコクのおいしさ キューピー からしアヨネーズ ピリジと おいしい ® 200g", "sku_l05.jpg": "2つの製法で味わうコク コレステロール ゼロ キューピーゼロ ノンコレステロール カロリー 70第 当社マヨネーズ比 310g", "sku_l06.jpg": "機能性表示食品届出番号J1507 キューピー 内臓脂肪ケア フィツテ ローズヒップ由来 ティリロサイド 210g カロリー 50カット 兆社マヨネーズ比 食生活は、主食、主菜、副菜を基本に、食事のバランスを。", "sku_l09.jpg": "とれて3日以方の 各長算たまごとい。 ピュアセレクト コクうま カロリーカット 05 360g", "sku_l11.jpg": "キューピー New! 卵黄ならではのコクとうま味 ※写真はイメージです。 カロリー 50% カット 当社マヨネーズ比 ® 400g"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Before After
Before After