felixkky commited on
Commit
a801bc2
·
verified ·
1 Parent(s): d4b4554

Upload 33 files

Browse files
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: DSE OMR V3.1 Mobile Scanner
3
  emoji: 📊
4
  colorFrom: blue
5
  colorTo: indigo
@@ -10,60 +10,63 @@ app_file: app.py
10
  pinned: false
11
  ---
12
 
13
- # DSE OMR V3.1 手機陰影局部補救系統
14
 
15
- V3.1 專為學生手機拍攝 DSE 類型答題紙而設系統先執行 V3 嚴格角定位;若手機或手部陰影令一至兩個定位標記黑白巢狀輪廓失真,才啟動局部補救。補救後仍必須通過完整點幾何安全檢查,才會把圖片交給 OMRChecker。
 
16
 
17
- ## V3.1 主要更新
18
 
19
- - 保留 V3 `144 × 144 px` 五層黑白大型定位標記。
20
- - 第一階段仍使用全頁 contour hierarchy 嚴格偵測。
21
- - 只有全頁偵測未能形成可靠四點組合,且仍找到至少兩個嚴格候選時,才啟動 V3.1 局部補救。
22
- - 每個缺失角點在限定 ROI 內使用:CLAHE、局部 Otsu、Adaptive Threshold、邊緣匹配及 `marker_reference.png` 多尺度比對。
23
- - 最多補救兩角點;完全缺失或相似度不足的定位符不會被猜測
24
- - 局部補救得到角點仍須通過四邊形面積、跨度、邊長比例、對角線比例及標記尺寸一致性檢查
25
- - 偵測預覽中,V3 嚴格角點為綠色;局部補救角點以黃橙色及 `*` 標
26
- - `strictMarkerMode=true`:補救後仍不靠便直接拒絕,不把錯誤校正圖交給 OMRChecker
 
 
27
 
28
  ## Hugging Face 部署
29
 
30
  1. 解壓本 ZIP。
31
- 2. 建立 Hugging Face **Gradio Space**
32
- 3. 將解壓後的全部檔案資料夾一次拖入 Space 根目錄。
33
  4. 等待 Build 完成。
34
- 5. 第一次掃描會下載 OMRChecker,因此需較長時間。
35
 
36
- > 不要只上傳 ZIP;Hugging Face Space 需要 ZIP 解壓後的根目錄檔案
37
 
38
- ## 根目錄必要檔案
39
 
40
  - `app.py`
41
  - `README.md`
42
  - `requirements.txt`
43
  - `packages.txt`
44
  - `template.jpg`
 
45
  - `template.json`
46
  - `marker_config.json`
47
  - `marker_reference.png`
 
48
 
49
  ## 測試樣本
50
 
51
  `samples/` 內包括:
52
 
53
- - `sample_phone_perspective.jpg`:正常手機透視樣本,應由 V3 嚴格模式通過
54
- - `sample_phone_shadow_rescue.jpg`:手機陰影破壞 TL 巢狀輪廓,應由 V3.1 局部補救通過
55
- - `sample_phone_missing_tl.jpg`:TL 真正缺失,應維持安全拒絕
56
- - `validation_shadow_detected.jpg`:局部補救標示結果。
57
- - `validation_shadow_warped.jpg`:補救後透視校正結果
 
 
58
 
59
  ## 拍攝要求
60
 
61
  - 四個大型定位標記必須完整入鏡。
62
- - 輕至中度手機陰影可以嘗試局部補救,但不要讓手機本體或手指真正遮住標記。
63
  - 避免嚴重反光、動態模糊及裁切。
64
  - 相機不要太近,保留紙張四周少量背景。
65
- - 系統拒絕圖片時應重新拍攝,不要強行提交
66
-
67
- ## 執行流程
68
-
69
- `手機相片 → V3 全頁嚴格偵測 →(必要時)V3.1 四角 ROI 局部補救 → 四點整組幾何驗證 → 3093×4374 透視校正 → OMRChecker 三輪掃描 → 成功輪次投票 → CSV/預覽/日誌`
 
1
  ---
2
+ title: DSE OMR V3.1 Four-Corner Scanner
3
  emoji: 📊
4
  colorFrom: blue
5
  colorTo: indigo
 
10
  pinned: false
11
  ---
12
 
13
+ # DSE OMR V3.1:只使用四個大型定位標記
14
 
15
+ 本版本以學生手機拍攝 DSE 類型答題紙為目標答題紙只保留答案區的四
16
+ `144 × 144 px` 五層黑白大型定位標記,已移除所有每五題之間的小標記。
17
 
18
+ ## 核心流程
19
 
20
+ `手機相片 V3 嚴格四角偵測 必要時 V3.1 局部陰影補救 → 四點幾何安全檢查 → 3093×4374 透視校正 OMRChecker 三輪掃描 → 成功輪次投票 → CSV/預覽/日誌`
21
+
22
+ ## 本版本的設計
23
+
24
+ - 只使用四大型定位標記
25
+ - 沒有題目之間小型定位符號
26
+ - `template.json` 的 100 題答案座保持不變
27
+ - V3.1 局部補救可處理手機或手部陰影令一至兩個大型標記對比下降的情況
28
+ - 補救角點仍須通過四邊形面積、跨度、比例及尺寸一致性檢查。
29
+ - `strictMarkerMode=true`:四角不可靠便拒絕,不把錯誤校正圖交給 OMRChecker。
30
 
31
  ## Hugging Face 部署
32
 
33
  1. 解壓本 ZIP。
34
+ 2. 建立 Hugging Face Gradio Space。
35
+ 3. 將解壓後的所有檔案資料夾上傳到 Space 根目錄。
36
  4. 等待 Build 完成。
37
+ 5. 第一次掃描會下載 OMRChecker 核心,因此需較長時間。
38
 
39
+ 不要只 ZIP 本身上傳為單一檔案;Space 需要 ZIP 解壓後的根目錄內容
40
 
41
+ ## 必要檔案
42
 
43
  - `app.py`
44
  - `README.md`
45
  - `requirements.txt`
46
  - `packages.txt`
47
  - `template.jpg`
48
+ - `template_lossless.png`
49
  - `template.json`
50
  - `marker_config.json`
51
  - `marker_reference.png`
52
+ - `template_A4_print.pdf`
53
 
54
  ## 測試樣本
55
 
56
  `samples/` 內包括:
57
 
58
+ - `sample_filled_clean.jpg`:平整填答案樣本。
59
+ - `sample_phone_perspective.jpg`:正常手機透視樣本
60
+ - `sample_phone_detected.jpg`:四角偵測預覽
61
+ - `sample_phone_warped.jpg`:透視校正結果。
62
+ - `sample_phone_shadow_rescue.jpg`:大型標記附近手機陰影測試
63
+ - `sample_phone_missing_tl.jpg`:真正缺失左上標記,應安全拒絕。
64
+ - `sample_answer_key.csv`:測試答案。
65
 
66
  ## 拍攝要求
67
 
68
  - 四個大型定位標記必須完整入鏡。
69
+ - 不要讓手機本體或手指真正遮住定位標記。
70
  - 避免嚴重反光、動態模糊及裁切。
71
  - 相機不要太近,保留紙張四周少量背景。
72
+ - 系統拒絕圖片時應重新拍攝。
 
 
 
 
VALIDATION_REPORT.txt CHANGED
@@ -1,59 +1,23 @@
1
- DSE OMR Hugging Face V3.1 Validation Report
2
- ===========================================
3
 
4
- Project version
5
- ---------------
6
- DSE-OMR-v3.1-local-shadow-rescue
7
-
8
- Static validation
9
- -----------------
10
- PASS Required root files present
11
- PASS app.py Python syntax
12
- PASS template.jpg = 3093 x 4374
13
- PASS template_lossless.png = 3093 x 4374
14
- PASS template.json pageDimensions = 3093 x 4374
15
- PASS marker_config.json pageDimensions = 3093 x 4374
16
- PASS largeMarkerOuterSize = 144
17
- PASS strictMarkerMode = true
18
- PASS localRescue.enabled = true
19
- PASS minimumGlobalCandidates = 2
20
- PASS maxRescueCorners = 2
21
- PASS 16 small group markers retained
22
-
23
- Functional marker tests
24
- -----------------------
25
- PASS sample_phone_perspective.jpg
26
- Expected: V3 strict four-marker detection
27
- Result: strict pass; no rescue used
28
-
29
- PASS sample_phone_shadow_rescue.jpg
30
- Expected: mobile-phone shadow breaks one strict contour hierarchy
31
- Result: V3.1 local ROI rescue pass; geometry validation passed
32
-
33
- PASS sample_phone_missing_tl.jpg
34
- Expected: genuine missing TL marker must not be guessed
35
- Result: safely rejected; image was not sent to OMRChecker
36
-
37
- Additional stress observations
38
- ------------------------------
39
- PASS even low light
40
- PASS bright exposure
41
- PASS warm/cool colour casts
42
- PASS left-side shadow
43
- PASS diagonal shadow via local rescue
44
- PASS top-bright/bottom-dark via local rescue
45
- PASS vignette via local rescue
46
- PASS low-light sensor noise
47
- REJECT low-light motion blur (intended safety rejection)
48
-
49
- Limitations
50
- -----------
51
- - Full OMRChecker answer recognition is executed in Hugging Face after the
52
- upstream OMRChecker repository is downloaded on first use.
53
- - Local rescue corrects lighting-induced loss of marker hierarchy; it does not
54
- invent a marker that is cropped out, physically covered, or severely blurred.
55
-
56
- Final status
57
- ------------
58
  PROJECT VALIDATION PASSED
59
- V3.1 strict detection, phone-shadow rescue and missing-marker rejection passed.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ DSE OMR V3.1 Four Large Markers Only
2
+ =====================================
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  PROJECT VALIDATION PASSED
5
+
6
+ Checks completed:
7
+ - app.py syntax: PASS
8
+ - validate_project.py syntax: PASS
9
+ - Colab marker tool syntax: PASS
10
+ - template.jpg size 3093 × 4374: PASS
11
+ - template_lossless.png size 3093 × 4374: PASS
12
+ - exactly four large markers in marker_config.json: PASS
13
+ - no smallGroupMarkers setting: PASS
14
+ - no smallMarkerOuterSize setting: PASS
15
+ - 16 former small-marker positions cleared from template: PASS
16
+ - normal phone-perspective four-corner detection: PASS
17
+ - perspective warp output: PASS
18
+ - missing-TL sample safely rejected: PASS
19
+ - template PDF regenerated: PASS
20
+ - Hugging Face required files present: PASS
21
+
22
+ Normal sample local rescue used: False
23
+ Shadow sample local rescue used: True
docs/ARCHITECTURE.md CHANGED
@@ -1,14 +1,25 @@
1
- # V3.1 Architecture
2
-
3
- 1. 讀取手機相片與 template/marker 設
4
- 2. V3 全頁 CLAHE、Otsu、RETR_TREE 巢狀方框偵測。
5
- 3. 嘗試從嚴格候選形成點組合並執行幾何驗證。
6
- 4. 若失敗但仍有至少兩個嚴格候選:
7
- - 找出缺失角點的限定 ROI;
8
- - 使用 CLAHE、局部 Otsu、Adaptive Threshold、Canny edge;
9
- - 以 marker_reference.png 做多尺度及小角度模板比對;
10
- - 最多補救兩角。
11
- 5. 將嚴格及補救候選重新組合,重新執行四點幾何安全檢查。
12
- 6. 通過後用 homography 映射至 3093×4374
13
- 7. 成功校正後移除第二次 FeatureBasedAlignment。
14
- 8. OMRChecker 執行三輪靈敏度掃描,僅成功輪次參與投票。
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 系統架構
2
+
3
+ ## 目前位結構
4
+
5
+ 本專案只使用個大型五層黑白定位標記:
6
+
7
+ - top_left
8
+ - top_right
9
+ - bottom_left
10
+ - bottom_right
11
+
12
+ 題目之間沒有小型定位標記,也沒有局部五題區塊校正
13
+
14
+ ## 處理層
15
+
16
+ 1. 圖片載入與基本檢查
17
+ 2. V3 全頁大型標記偵測
18
+ 3. 必要時 V3.1 四角 ROI 局部陰影補救
19
+ 4. 四點幾何安全驗證
20
+ 5. `warpPerspective` 映射至 `3093 × 4374`
21
+ 6. 按 `template.json` 執行 OMRChecker
22
+ 7. 三輪靈敏度掃描及成功輪次投票
23
+ 8. 輸出 CSV、定位預覽、校正預覽與日誌
24
+
25
+ 四角校正成功後會停用第二次 `FeatureBasedAlignment`,避免雙重幾何變換。
docs/TEMPLATE_COORDINATES.md CHANGED
@@ -1,12 +1,25 @@
1
- # Template V3 座標
2
-
3
- - 頁面:`3093 x 4374`
4
- - 答案區:`(829,1598)` 至 `(2917,4170)`
5
- - 大型標記外尺寸`144px`
6
- - TL`(720, 1410)`
7
- - TR:`(3015, 1410)`
8
- - BL:`(720, 4285)`
9
- - BR:`(3015, 4285)`
10
- - 小型標記外尺寸`34px`
11
-
12
- 答案框座標仍由根目錄的 `template.json` 定義;V3 沒有移動原本 100 題的答案位置。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 模板及定位座標
2
+
3
+ ## 頁面
4
+
5
+ - 3093 px
6
+ - 4374 px
7
+
8
+ ## 四個大型定位標記
9
+
10
+ - TL[720, 1410]
11
+ - TR:[3015, 1410]
12
+ - BL:[720, 4285]
13
+ - BR:[3015, 4285]
14
+ - 外框尺寸:144 px
15
+ - 黑白巢狀層數:5
16
+
17
+ ## 小標記
18
+
19
+ 本版本沒有任何題目間小標記。`marker_config.json` 亦不包含
20
+ `smallGroupMarkers` 或 `smallMarkerOuterSize`。
21
+
22
+ ## 答案座標
23
+
24
+ 100 題答案座標繼續由 `template.json` 的 20 個五題 field block 定義。
25
+ 移除小標記沒有移動任何答案格,因此不需要改動 field block 座標。
docs/TESTING_GUIDE.md CHANGED
@@ -1,23 +1,19 @@
1
- # V3.1 Testing Guide
2
 
3
- ## 必測三張
4
 
5
  1. `sample_phone_perspective.jpg`
6
- - 預期:V3 strict pass,四角全為綠色。
7
  2. `sample_phone_shadow_rescue.jpg`
8
- - 預期:V3.1 local rescue pass,至少一角為黃橙色並帶 `*`。
9
  3. `sample_phone_missing_tl.jpg`
10
- - 預期:安全拒絕,不進入 OMRChecker。
11
 
12
- ## 日誌判讀
 
13
 
14
- - `V3 嚴格模式成功`沒有使用補救。
15
- - `V3.1 局部補救成功`:陰影中的缺失角點已恢復且幾何檢查通過。
16
- - `未通過 V3.1 安全檢查`:應重新拍攝。
17
 
18
- ## 不應放寬的情況
 
 
 
19
 
20
- - 定位標記真正被裁走或遮住
21
- - 嚴重動態模糊。
22
- - 反光把黑白環帶完全洗白。
23
- - 只找到 0–1 個全頁嚴格候選。
 
1
+ # 測試指引
2
 
3
+ 建議依次上傳:
4
 
5
  1. `sample_phone_perspective.jpg`
 
6
  2. `sample_phone_shadow_rescue.jpg`
 
7
  3. `sample_phone_missing_tl.jpg`
 
8
 
9
+ 正常樣本應找到四個大型標記並完成透視校正。陰影樣本可由嚴格模式或
10
+ V3.1 局部補救通過。缺失 TL 的樣本必須拒絕。
11
 
12
+ 檢查畫面
 
 
13
 
14
+ - TL、TR、BR、BL 是否落在四個大型標記。
15
+ - 校正後紅色答案欄是否垂直。
16
+ - OMRChecker 是否成功返回 CSV。
17
+ - 執行摘要中是否沒有 config schema 錯誤。
18
 
19
+ 本版本沒有小標記,因此不需要檢查題目間定位符號
 
 
 
docs/V3_1_CHANGELOG.md CHANGED
@@ -1,8 +1,9 @@
1
- # V3.1 Changelog
2
 
3
- - 新增手機/手部陰影局部補救
4
- - 保留 V3 全頁嚴格 contour hierarchy 偵測作第一階段
5
- - 缺失角點使用限定 ROI、多尺度 marker reference、局部 Otsu、Adaptive Threshold edge matching
6
- - 至少需要兩個 V3 嚴格候選,最多補救兩角
7
- - 局部結果必須再次通過完整四點幾何驗證
8
- - 真正缺失 TL 的負面樣本仍會被拒絕
 
 
1
+ # V3.1 Four-Large-Markers-Only 變更
2
 
3
+ - 保留四個 `144 × 144 px` 五層黑白大型定位標記
4
+ - 移除 16 個每五題之間的小型定位標記
5
+ - `marker_config.json` 移除小標記座標尺寸設定
6
+ - Colab 產生工具只會加入四個大型標記
7
+ - 保留 V3 嚴格偵測、V3.1 局部陰影補救及幾何安全檢查
8
+ - 重新產生 template、PDF、座標圖及所有測試樣本。
9
+ - 答案格座標及 OMRChecker 三輪辨識流程不變。
docs/V3_CHANGELOG.md CHANGED
@@ -1,20 +1,9 @@
1
- # V3 更新紀錄
2
-
3
- ## 定位紙設計
4
-
5
- - 大型標記:72px -> 144px
6
- - 座標:TL `(720,1410)`、TR `(3015,1410)`、BL `(720,4285)`、BR `(3015,4285)`
7
- - 造型:黑/白/黑/白/黑五層巢狀方形
8
- - 小型分隔標記維持 34px
9
-
10
- ## 偵測器
11
-
12
- - 強制 hierarchy depth。
13
- - 依頁面比例估計大型標記像素尺寸,排除小型標記。
14
- - 四角候選整組組合及幾何驗證。
15
- - 失敗圖會顯示所有通過初步篩選的候選框。
16
-
17
- ## 安全行為
18
-
19
- - 預設 strict mode,不再把誤判 TL/TR/BL/BR 的圖片交給 OMRChecker。
20
- - 可在 `marker_config.json` 將 `strictMarkerMode` 設為 `false` 才啟用舊回退模式。
 
1
+ # V3.1 Four-Large-Markers-Only 變
2
+
3
+ - 保留四個 `144 × 144 px` 五層黑白大型定位標記。
4
+ - 移除 16 個每五題之間的小型定位標記。
5
+ - `marker_config.json` 移除小標記座標及尺寸設定
6
+ - Colab 產生工具只會加入四個大型標記
7
+ - 保留 V3 嚴格偵測、V3.1 局部陰影補救及幾何安全檢查
8
+ - 重新產生 template、PDF、座標圖及所有測試樣本
9
+ - 答案格座標及 OMRChecker 三輪辨識流程不變。
 
 
 
 
 
 
 
 
 
 
 
docs/template_coordinate_overlay.jpg CHANGED

Git LFS Details

  • SHA256: 2a047c39f005de0d2a20b30c369505401c8060cc1581aa81e918c22edd36eb7d
  • Pointer size: 132 Bytes
  • Size of remote file: 1.6 MB

Git LFS Details

  • SHA256: 3db779e3a69622ecf047373df6882bf70c8d3a52f253acc496c28d1a046b433d
  • Pointer size: 132 Bytes
  • Size of remote file: 1.65 MB
marker_config.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "templateVersion": "DSE-OMR-v3.1-local-shadow-rescue",
3
  "pageDimensions": [
4
  3093,
5
  4374
@@ -31,73 +31,6 @@
31
  },
32
  "largeMarkerOuterSize": 144,
33
  "largeMarkerNestedLevels": 5,
34
- "smallGroupMarkers": [
35
- [
36
- 1310,
37
- 2122
38
- ],
39
- [
40
- 1310,
41
- 2637
42
- ],
43
- [
44
- 1310,
45
- 3152
46
- ],
47
- [
48
- 1310,
49
- 3667
50
- ],
51
- [
52
- 1830,
53
- 2122
54
- ],
55
- [
56
- 1830,
57
- 2637
58
- ],
59
- [
60
- 1830,
61
- 3152
62
- ],
63
- [
64
- 1830,
65
- 3667
66
- ],
67
- [
68
- 2350,
69
- 2122
70
- ],
71
- [
72
- 2350,
73
- 2637
74
- ],
75
- [
76
- 2350,
77
- 3152
78
- ],
79
- [
80
- 2350,
81
- 3667
82
- ],
83
- [
84
- 2870,
85
- 2122
86
- ],
87
- [
88
- 2870,
89
- 2637
90
- ],
91
- [
92
- 2870,
93
- 3152
94
- ],
95
- [
96
- 2870,
97
- 3667
98
- ]
99
- ],
100
- "smallMarkerOuterSize": 34,
101
  "detection": {
102
  "minNestedDepth": 3,
103
  "minQuadrilateralAreaRatio": 0.2,
@@ -123,13 +56,12 @@
123
  ]
124
  },
125
  "notes": [
126
- "V3 enlarges the four main markers from 72 px to 144 px.",
127
- "Large markers use five alternating black/white square layers for contour-hierarchy validation.",
128
- "V3.1 keeps strict full-page detection first, then activates local rescue only when the four-marker set is incomplete.",
129
- "Local rescue uses corner ROI CLAHE, local Otsu, adaptive threshold, edge matching and marker_reference.png.",
130
- "At least two strict global candidates are required before rescue; all recovered points must still pass four-point geometry validation.",
131
- "Strict marker mode rejects the image when a reliable four-marker set is not found.",
132
- "Small group markers remain 34 px and are reserved for future local-warp checks."
133
  ],
134
  "localRescue": {
135
  "enabled": true,
@@ -155,4 +87,4 @@
155
  "adaptiveBlockSize": 31,
156
  "adaptiveC": 7
157
  }
158
- }
 
1
  {
2
+ "templateVersion": "DSE-OMR-v3.1-four-large-markers-only",
3
  "pageDimensions": [
4
  3093,
5
  4374
 
31
  },
32
  "largeMarkerOuterSize": 144,
33
  "largeMarkerNestedLevels": 5,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  "detection": {
35
  "minNestedDepth": 3,
36
  "minQuadrilateralAreaRatio": 0.2,
 
56
  ]
57
  },
58
  "notes": [
59
+ "This variant keeps only the four 144 px five-layer large corner markers.",
60
+ "All 16 small markers between five-question groups have been removed.",
61
+ "V3.1 strict full-page detection remains the first stage.",
62
+ "Local corner-ROI rescue remains available when phone or hand shadow degrades one or two large markers.",
63
+ "Recovered points must still pass full four-point geometry validation.",
64
+ "No local group-marker correction is performed or required in this project."
 
65
  ],
66
  "localRescue": {
67
  "enabled": true,
 
87
  "adaptiveBlockSize": 31,
88
  "adaptiveC": 7
89
  }
90
+ }
project_manifest.json CHANGED
@@ -1,130 +1,193 @@
1
  {
2
- ".gitignore": {
3
- "size_bytes": 69,
4
- "sha256": "220f22568b058b917b79feb609ec12fc63085941efafd3698cf4575c0f6ad05f"
5
- },
6
- "README.md": {
7
- "size_bytes": 2925,
8
- "sha256": "109f5f6ad4e617019cde8c37acdd224df13c6a226a51c83ce0d20573c0715e86"
9
- },
10
- "VALIDATION_REPORT.txt": {
11
- "size_bytes": 1953,
12
- "sha256": "bb1b66058920c864fd5f44b83af12af71dccb23d27472fc16ea3cda23ca28175"
13
- },
14
- "app.py": {
15
- "size_bytes": 55081,
16
- "sha256": "d96a6f4a48b071774d1dfcacbf787581260e4a9e301dd3232582716629c4c3e7"
17
- },
18
- "docs/ARCHITECTURE.md": {
19
- "size_bytes": 723,
20
- "sha256": "1bcd1b4979e3238ccc520d4dc218557ccca879018bf63ff78b13041263cda366"
21
- },
22
- "docs/TEMPLATE_COORDINATES.md": {
23
- "size_bytes": 353,
24
- "sha256": "c53f9dd30fb5b5938fdda5f1fbd89c2bda5136515684c3b89740a22dc69c3213"
25
- },
26
- "docs/TESTING_GUIDE.md": {
27
- "size_bytes": 709,
28
- "sha256": "3a33930526df391e052f8a2dc24fc24a05f6e60feb0f0d514868cac14037d322"
29
- },
30
- "docs/V3_1_CHANGELOG.md": {
31
- "size_bytes": 420,
32
- "sha256": "bb980211944e3f5c05f3f6ee91c216b49dbefeb1d218b6a60de048e11bedd5ee"
33
- },
34
- "docs/V3_CHANGELOG.md": {
35
- "size_bytes": 661,
36
- "sha256": "efa46cb301f4a361cfc2d0c54551570c5eb04bf0b80c800dffd0ec7048cd0823"
37
- },
38
- "docs/template_coordinate_overlay.jpg": {
39
- "size_bytes": 1599880,
40
- "sha256": "2a047c39f005de0d2a20b30c369505401c8060cc1581aa81e918c22edd36eb7d"
41
- },
42
- "marker_config.json": {
43
- "size_bytes": 2803,
44
- "sha256": "7f4a1f660d9609766187a92fbaae4a577851800a6e69c767249480c7bfee5819"
45
- },
46
- "marker_reference.png": {
47
- "size_bytes": 1497,
48
- "sha256": "9b896e436e359df8a3bdba19013cce4d646ba9e2eb38ce590fa9ff5065a6eba3"
49
- },
50
- "packages.txt": {
51
- "size_bytes": 32,
52
- "sha256": "4de86580e7ebc03a7790808fa6655cac191045bd030c15d4cc07acf03e6706f1"
53
- },
54
- "requirements.txt": {
55
- "size_bytes": 576,
56
- "sha256": "b7411f9ebbf827d2272fb789c10c2d03ae27e7f3f908d0d0f4da5b4103497e22"
57
- },
58
- "samples/sample_answer_key.csv": {
59
- "size_bytes": 597,
60
- "sha256": "c61e0234c426ef58c6aa7d6a998e313c6223dc769b16df2fc7a4ec11ff024ec7"
61
- },
62
- "samples/sample_filled_clean.jpg": {
63
- "size_bytes": 1533382,
64
- "sha256": "e9ddaa0c6abfb1ce7327b491e893c9368f9c732b6f6ade01e6d32072f0a922c1"
65
- },
66
- "samples/sample_phone_detected.jpg": {
67
- "size_bytes": 603541,
68
- "sha256": "298b30859ef9d5e355c05638540205939d29a127bcea087d94b7fe9a97e3675d"
69
- },
70
- "samples/sample_phone_missing_tl.jpg": {
71
- "size_bytes": 582004,
72
- "sha256": "019e0dcc680b84aac7261ecaa657a0d760e12948bb9e2137d492ba107427b76a"
73
- },
74
- "samples/sample_phone_perspective.jpg": {
75
- "size_bytes": 476343,
76
- "sha256": "36afed2af57fad73f8d3938de401ec02e415fcce06c1f9a8c35c952af4352d71"
77
- },
78
- "samples/sample_phone_shadow_rescue.jpg": {
79
- "size_bytes": 396067,
80
- "sha256": "71afc6b1ce658511fe3c039a3539f50fcab3457363e19f679c3749fa7e65bd50"
81
- },
82
- "samples/sample_phone_warped.jpg": {
83
- "size_bytes": 1589690,
84
- "sha256": "9491f19b1c41ba2448ba0a6d71e1234eb96dd079b7bed39c1d9fd0bb6dadb1cb"
85
- },
86
- "samples/validation_missing_failed.jpg": {
87
- "size_bytes": 587623,
88
- "sha256": "8e905fd74b05344e0bfc6a7a5e396abca12f8c4266a5ead4d0695cd49b66bbd2"
89
- },
90
- "samples/validation_shadow_detected.jpg": {
91
- "size_bytes": 541116,
92
- "sha256": "5a21a7e403ec05f2bab664ad4f02ffaeb7fcef852e2ca3e545241fea80ca7b64"
93
- },
94
- "samples/validation_shadow_warped.jpg": {
95
- "size_bytes": 1195295,
96
- "sha256": "edcf2d2f5c7072dd680f1b108892fc43fc567d4e24e2a873bcb7cc7d5c95000f"
97
- },
98
- "template.jpg": {
99
- "size_bytes": 1769252,
100
- "sha256": "569cdf55938b6a90a9575569ddcdc050ce236784b0b7610a03f45493a315db62"
101
- },
102
- "template.json": {
103
- "size_bytes": 4723,
104
- "sha256": "d49c9a881225afae81cae9521a85a68c044d5d86844b3774cc9130de1d484bd2"
105
- },
106
- "template_A4_print.pdf": {
107
- "size_bytes": 8170224,
108
- "sha256": "05b38fffc36d6bb4daabbe51bbc18ebb76dd8e7a9b359d1a029dec04c346a542"
109
- },
110
- "template_lossless.png": {
111
- "size_bytes": 5097851,
112
- "sha256": "b9eebc6d7581218978ad52425a18a34d4715a797c49beb50bdd9e0536bba3648"
113
- },
114
- "tools/add_markers_and_warp_colab.py": {
115
- "size_bytes": 5146,
116
- "sha256": "178ec38a1a81bc146a3cdb42599ed164fe4382d24010f19f6bb0bcd0adc8f4f8"
117
- },
118
- "tools/generate_filled_sample.py": {
119
- "size_bytes": 869,
120
- "sha256": "9562dfe9a13aed8d8c13ff86b1415c399c5a629a10e1706b8fbc387729d53777"
121
- },
122
- "tools/test_marker_v31.py": {
123
- "size_bytes": 1414,
124
- "sha256": "28bddae2d69932cec4f6c871c91a24e1c9db82cc2612ae231cc330fa7b8566a7"
125
- },
126
- "validate_project.py": {
127
- "size_bytes": 2735,
128
- "sha256": "13c54cc6e5eee1699a7dd818f723e7c5b264b0948476e54c798696b522d023e4"
129
- }
130
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  {
2
+ "project": "DSE OMR V3.1 Four Large Markers Only",
3
+ "template_version": "DSE-OMR-v3.1-four-large-markers-only",
4
+ "large_marker_count": 4,
5
+ "small_marker_count": 0,
6
+ "files": [
7
+ {
8
+ "path": ".gitignore",
9
+ "size_bytes": 69,
10
+ "sha256": "220f22568b058b917b79feb609ec12fc63085941efafd3698cf4575c0f6ad05f"
11
+ },
12
+ {
13
+ "path": "README.md",
14
+ "size_bytes": 2424,
15
+ "sha256": "0d885050aef4963afe8a90496322ea15102bbff3f9b75b7d72f559206ecc3a7a"
16
+ },
17
+ {
18
+ "path": "VALIDATION_REPORT.txt",
19
+ "size_bytes": 779,
20
+ "sha256": "7f3a17271ca4636c3dea290e849f36bcdce3ec73454f5cddf612dcbb8ba02887"
21
+ },
22
+ {
23
+ "path": "__pycache__/app.cpython-313.pyc",
24
+ "size_bytes": 68024,
25
+ "sha256": "5c99d35bc4003ea494844b88c5b3ba1bb589422fb3e47b4bd2bfd9c001738c05"
26
+ },
27
+ {
28
+ "path": "__pycache__/validate_project.cpython-313.pyc",
29
+ "size_bytes": 4362,
30
+ "sha256": "55c81cd08cc9e50693aba2a3cf2c9f831259bdb64bf209e015567ac68c183e4c"
31
+ },
32
+ {
33
+ "path": "app.py",
34
+ "size_bytes": 55081,
35
+ "sha256": "d96a6f4a48b071774d1dfcacbf787581260e4a9e301dd3232582716629c4c3e7"
36
+ },
37
+ {
38
+ "path": "docs/ARCHITECTURE.md",
39
+ "size_bytes": 661,
40
+ "sha256": "b4ab0a6766dc79e7c2fb273f690a8e83b02df883e031e5389ca2fdbaa4d9d191"
41
+ },
42
+ {
43
+ "path": "docs/TEMPLATE_COORDINATES.md",
44
+ "size_bytes": 557,
45
+ "sha256": "10e43a06ea3e74f37ab9588da894fbb652c12de70563ca29251013118c4755f3"
46
+ },
47
+ {
48
+ "path": "docs/TESTING_GUIDE.md",
49
+ "size_bytes": 582,
50
+ "sha256": "1830a6284a1cfd09c9bcb13305b302e66436143f6f411bbaa97726e42f35ce46"
51
+ },
52
+ {
53
+ "path": "docs/V3_1_CHANGELOG.md",
54
+ "size_bytes": 481,
55
+ "sha256": "588aa9895b36abd22e30ef05a3dd7f8efd6bf8560180290ed203d058602cc230"
56
+ },
57
+ {
58
+ "path": "docs/V3_CHANGELOG.md",
59
+ "size_bytes": 481,
60
+ "sha256": "588aa9895b36abd22e30ef05a3dd7f8efd6bf8560180290ed203d058602cc230"
61
+ },
62
+ {
63
+ "path": "docs/template_coordinate_overlay.jpg",
64
+ "size_bytes": 1651047,
65
+ "sha256": "3db779e3a69622ecf047373df6882bf70c8d3a52f253acc496c28d1a046b433d"
66
+ },
67
+ {
68
+ "path": "marker_config.json",
69
+ "size_bytes": 1936,
70
+ "sha256": "6d12faf818e64a16dde6e6762cfcbd08b676a50a0de4b84ca36b2e9371b8a7b9"
71
+ },
72
+ {
73
+ "path": "marker_reference.png",
74
+ "size_bytes": 1497,
75
+ "sha256": "9b896e436e359df8a3bdba19013cce4d646ba9e2eb38ce590fa9ff5065a6eba3"
76
+ },
77
+ {
78
+ "path": "packages.txt",
79
+ "size_bytes": 32,
80
+ "sha256": "4de86580e7ebc03a7790808fa6655cac191045bd030c15d4cc07acf03e6706f1"
81
+ },
82
+ {
83
+ "path": "project_manifest.json",
84
+ "size_bytes": 4580,
85
+ "sha256": "58d8f4e19bcd6e3fad305e09779bb63826c86938b2096b1e8f75f4fb4564bce6"
86
+ },
87
+ {
88
+ "path": "requirements.txt",
89
+ "size_bytes": 576,
90
+ "sha256": "b7411f9ebbf827d2272fb789c10c2d03ae27e7f3f908d0d0f4da5b4103497e22"
91
+ },
92
+ {
93
+ "path": "samples/sample_answer_key.csv",
94
+ "size_bytes": 597,
95
+ "sha256": "c61e0234c426ef58c6aa7d6a998e313c6223dc769b16df2fc7a4ec11ff024ec7"
96
+ },
97
+ {
98
+ "path": "samples/sample_filled_clean.jpg",
99
+ "size_bytes": 1647356,
100
+ "sha256": "0731fad2c9712cf8f02a3662286f92ac801e8632b116ead97d756b63e599473b"
101
+ },
102
+ {
103
+ "path": "samples/sample_phone_detected.jpg",
104
+ "size_bytes": 624509,
105
+ "sha256": "f80183c0fb42961d9f889b2865236d369ad2c305160e64eb3c1142a7c01263a2"
106
+ },
107
+ {
108
+ "path": "samples/sample_phone_missing_tl.jpg",
109
+ "size_bytes": 608318,
110
+ "sha256": "963d897dbc2969b68bc5dd558c2c78f792863e4d2b55fbc50d669e1ab3a702d4"
111
+ },
112
+ {
113
+ "path": "samples/sample_phone_perspective.jpg",
114
+ "size_bytes": 609664,
115
+ "sha256": "05ee2debd12a855ee8f999fbe3e0732c159b5718013ccebc97d76e8187ea971a"
116
+ },
117
+ {
118
+ "path": "samples/sample_phone_shadow_rescue.jpg",
119
+ "size_bytes": 611829,
120
+ "sha256": "ee14c40f1e9826766247cde4fb3e15134a81e0b0f1ac43383718d1aed462e109"
121
+ },
122
+ {
123
+ "path": "samples/sample_phone_warped.jpg",
124
+ "size_bytes": 1490520,
125
+ "sha256": "ecd740b282740f2fd4843b82cb979357328e0960596aa3807ab0b0032f5efaaa"
126
+ },
127
+ {
128
+ "path": "samples/validation_missing_failed.jpg",
129
+ "size_bytes": 629418,
130
+ "sha256": "20bed2ae0cfc3a5d9ea377570cf79c2cac4b974a6a28a8aebbc0ba53cad0bba5"
131
+ },
132
+ {
133
+ "path": "samples/validation_shadow_detected.jpg",
134
+ "size_bytes": 636319,
135
+ "sha256": "9bf985c6686f0c06a5f579d708c5a7859eb15e78d286349149011a364522f4cc"
136
+ },
137
+ {
138
+ "path": "samples/validation_shadow_warped.jpg",
139
+ "size_bytes": 1501053,
140
+ "sha256": "e79b1d775c0afb472b80a50f811b8c6883901a12641453ca8534d5f3f67297b4"
141
+ },
142
+ {
143
+ "path": "template.jpg",
144
+ "size_bytes": 1760828,
145
+ "sha256": "6b8ae7fcb30f1bd928015ec2ca1ddb6e7d4bd982b93fdf7f81778f7fcfe626e9"
146
+ },
147
+ {
148
+ "path": "template.json",
149
+ "size_bytes": 4723,
150
+ "sha256": "d49c9a881225afae81cae9521a85a68c044d5d86844b3774cc9130de1d484bd2"
151
+ },
152
+ {
153
+ "path": "template_A4_print.pdf",
154
+ "size_bytes": 716725,
155
+ "sha256": "ba117aa756a9c4cecc0b913563e91e2c8998d4f36688530afe0a677e685657db"
156
+ },
157
+ {
158
+ "path": "template_lossless.png",
159
+ "size_bytes": 6858945,
160
+ "sha256": "71c031b628adea74b435bda57e46eaa555d68f3aed0804ea69f95fa0fc60ad40"
161
+ },
162
+ {
163
+ "path": "tools/__pycache__/add_markers_and_warp_colab.cpython-313.pyc",
164
+ "size_bytes": 8417,
165
+ "sha256": "5f054c790adb40f5f578259472070b6ecec4632bab83b6e55d2126c6af776ca7"
166
+ },
167
+ {
168
+ "path": "tools/__pycache__/generate_filled_sample.cpython-313.pyc",
169
+ "size_bytes": 2083,
170
+ "sha256": "0f889cb32338dd181b1fa366f1394ea870fdfc80ab99ea316ac41093f7d01fc1"
171
+ },
172
+ {
173
+ "path": "tools/add_markers_and_warp_colab.py",
174
+ "size_bytes": 4775,
175
+ "sha256": "60342bae732564aee6b4671a648c8cafdfca53c38f891f4ba9c687acb466fbec"
176
+ },
177
+ {
178
+ "path": "tools/generate_filled_sample.py",
179
+ "size_bytes": 869,
180
+ "sha256": "9562dfe9a13aed8d8c13ff86b1415c399c5a629a10e1706b8fbc387729d53777"
181
+ },
182
+ {
183
+ "path": "tools/test_marker_v31.py",
184
+ "size_bytes": 1414,
185
+ "sha256": "28bddae2d69932cec4f6c871c91a24e1c9db82cc2612ae231cc330fa7b8566a7"
186
+ },
187
+ {
188
+ "path": "validate_project.py",
189
+ "size_bytes": 2805,
190
+ "sha256": "5732882b7ef24bd24c6b2712db042d124212c0b647f407be0bf8579814a8fabc"
191
+ }
192
+ ]
193
+ }
samples/sample_filled_clean.jpg CHANGED

Git LFS Details

  • SHA256: e9ddaa0c6abfb1ce7327b491e893c9368f9c732b6f6ade01e6d32072f0a922c1
  • Pointer size: 132 Bytes
  • Size of remote file: 1.53 MB

Git LFS Details

  • SHA256: 0731fad2c9712cf8f02a3662286f92ac801e8632b116ead97d756b63e599473b
  • Pointer size: 132 Bytes
  • Size of remote file: 1.65 MB
samples/sample_phone_detected.jpg CHANGED

Git LFS Details

  • SHA256: 298b30859ef9d5e355c05638540205939d29a127bcea087d94b7fe9a97e3675d
  • Pointer size: 131 Bytes
  • Size of remote file: 604 kB

Git LFS Details

  • SHA256: f80183c0fb42961d9f889b2865236d369ad2c305160e64eb3c1142a7c01263a2
  • Pointer size: 131 Bytes
  • Size of remote file: 625 kB
samples/sample_phone_missing_tl.jpg CHANGED

Git LFS Details

  • SHA256: 019e0dcc680b84aac7261ecaa657a0d760e12948bb9e2137d492ba107427b76a
  • Pointer size: 131 Bytes
  • Size of remote file: 582 kB

Git LFS Details

  • SHA256: 963d897dbc2969b68bc5dd558c2c78f792863e4d2b55fbc50d669e1ab3a702d4
  • Pointer size: 131 Bytes
  • Size of remote file: 608 kB
samples/sample_phone_perspective.jpg CHANGED

Git LFS Details

  • SHA256: 36afed2af57fad73f8d3938de401ec02e415fcce06c1f9a8c35c952af4352d71
  • Pointer size: 131 Bytes
  • Size of remote file: 476 kB

Git LFS Details

  • SHA256: 05ee2debd12a855ee8f999fbe3e0732c159b5718013ccebc97d76e8187ea971a
  • Pointer size: 131 Bytes
  • Size of remote file: 610 kB
samples/sample_phone_shadow_rescue.jpg CHANGED

Git LFS Details

  • SHA256: 71afc6b1ce658511fe3c039a3539f50fcab3457363e19f679c3749fa7e65bd50
  • Pointer size: 131 Bytes
  • Size of remote file: 396 kB

Git LFS Details

  • SHA256: ee14c40f1e9826766247cde4fb3e15134a81e0b0f1ac43383718d1aed462e109
  • Pointer size: 131 Bytes
  • Size of remote file: 612 kB
samples/sample_phone_warped.jpg CHANGED

Git LFS Details

  • SHA256: 9491f19b1c41ba2448ba0a6d71e1234eb96dd079b7bed39c1d9fd0bb6dadb1cb
  • Pointer size: 132 Bytes
  • Size of remote file: 1.59 MB

Git LFS Details

  • SHA256: ecd740b282740f2fd4843b82cb979357328e0960596aa3807ab0b0032f5efaaa
  • Pointer size: 132 Bytes
  • Size of remote file: 1.49 MB
samples/validation_missing_failed.jpg CHANGED

Git LFS Details

  • SHA256: 8e905fd74b05344e0bfc6a7a5e396abca12f8c4266a5ead4d0695cd49b66bbd2
  • Pointer size: 131 Bytes
  • Size of remote file: 588 kB

Git LFS Details

  • SHA256: 20bed2ae0cfc3a5d9ea377570cf79c2cac4b974a6a28a8aebbc0ba53cad0bba5
  • Pointer size: 131 Bytes
  • Size of remote file: 629 kB
samples/validation_shadow_detected.jpg CHANGED

Git LFS Details

  • SHA256: 5a21a7e403ec05f2bab664ad4f02ffaeb7fcef852e2ca3e545241fea80ca7b64
  • Pointer size: 131 Bytes
  • Size of remote file: 541 kB

Git LFS Details

  • SHA256: 9bf985c6686f0c06a5f579d708c5a7859eb15e78d286349149011a364522f4cc
  • Pointer size: 131 Bytes
  • Size of remote file: 636 kB
samples/validation_shadow_warped.jpg CHANGED

Git LFS Details

  • SHA256: edcf2d2f5c7072dd680f1b108892fc43fc567d4e24e2a873bcb7cc7d5c95000f
  • Pointer size: 132 Bytes
  • Size of remote file: 1.2 MB

Git LFS Details

  • SHA256: e79b1d775c0afb472b80a50f811b8c6883901a12641453ca8534d5f3f67297b4
  • Pointer size: 132 Bytes
  • Size of remote file: 1.5 MB
template.jpg CHANGED

Git LFS Details

  • SHA256: 569cdf55938b6a90a9575569ddcdc050ce236784b0b7610a03f45493a315db62
  • Pointer size: 132 Bytes
  • Size of remote file: 1.77 MB

Git LFS Details

  • SHA256: 6b8ae7fcb30f1bd928015ec2ca1ddb6e7d4bd982b93fdf7f81778f7fcfe626e9
  • Pointer size: 132 Bytes
  • Size of remote file: 1.76 MB
template_A4_print.pdf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:05b38fffc36d6bb4daabbe51bbc18ebb76dd8e7a9b359d1a029dec04c346a542
3
- size 8170224
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ba117aa756a9c4cecc0b913563e91e2c8998d4f36688530afe0a677e685657db
3
+ size 716725
template_lossless.png CHANGED

Git LFS Details

  • SHA256: b9eebc6d7581218978ad52425a18a34d4715a797c49beb50bdd9e0536bba3648
  • Pointer size: 132 Bytes
  • Size of remote file: 5.1 MB

Git LFS Details

  • SHA256: 71c031b628adea74b435bda57e46eaa555d68f3aed0804ea69f95fa0fc60ad40
  • Pointer size: 132 Bytes
  • Size of remote file: 6.86 MB
tools/add_markers_and_warp_colab.py CHANGED
@@ -2,7 +2,7 @@
2
  """Colab utility for the DSE OMR V3 large-marker template.
3
 
4
  Functions:
5
- - add_v3_markers(): add four 144px five-layer markers and 34px group markers.
6
  - detect_and_warp(): detect the four large markers and perspective-correct a photo.
7
 
8
  For the full production detector and OMR flow, use app.py in the Space project.
@@ -13,19 +13,12 @@ import numpy as np
13
 
14
  PAGE_SIZE = (3093, 4374)
15
  LARGE_SIZE = 144
16
- SMALL_SIZE = 34
17
  LARGE_MARKERS = {
18
  'top_left': (720, 1410),
19
  'top_right': (3015, 1410),
20
  'bottom_left': (720, 4285),
21
  'bottom_right': (3015, 4285),
22
  }
23
- SMALL_MARKERS = [
24
- (1310, 2122), (1310, 2637), (1310, 3152), (1310, 3667),
25
- (1830, 2122), (1830, 2637), (1830, 3152), (1830, 3667),
26
- (2350, 2122), (2350, 2637), (2350, 3152), (2350, 3667),
27
- (2870, 2122), (2870, 2637), (2870, 3152), (2870, 3667),
28
- ]
29
 
30
 
31
  def _draw_nested_marker(img, center, size):
@@ -51,8 +44,6 @@ def add_v3_markers(input_path='template_original.jpg',
51
  img = cv2.resize(img, PAGE_SIZE, interpolation=cv2.INTER_CUBIC)
52
  for point in LARGE_MARKERS.values():
53
  _draw_nested_marker(img, point, LARGE_SIZE)
54
- for point in SMALL_MARKERS:
55
- _draw_nested_marker(img, point, SMALL_SIZE)
56
  cv2.imwrite(output_path, img, [int(cv2.IMWRITE_JPEG_QUALITY), 97])
57
  return output_path
58
 
 
2
  """Colab utility for the DSE OMR V3 large-marker template.
3
 
4
  Functions:
5
+ - add_v3_markers(): add only four 144px five-layer corner markers.
6
  - detect_and_warp(): detect the four large markers and perspective-correct a photo.
7
 
8
  For the full production detector and OMR flow, use app.py in the Space project.
 
13
 
14
  PAGE_SIZE = (3093, 4374)
15
  LARGE_SIZE = 144
 
16
  LARGE_MARKERS = {
17
  'top_left': (720, 1410),
18
  'top_right': (3015, 1410),
19
  'bottom_left': (720, 4285),
20
  'bottom_right': (3015, 4285),
21
  }
 
 
 
 
 
 
22
 
23
 
24
  def _draw_nested_marker(img, center, size):
 
44
  img = cv2.resize(img, PAGE_SIZE, interpolation=cv2.INTER_CUBIC)
45
  for point in LARGE_MARKERS.values():
46
  _draw_nested_marker(img, point, LARGE_SIZE)
 
 
47
  cv2.imwrite(output_path, img, [int(cv2.IMWRITE_JPEG_QUALITY), 97])
48
  return output_path
49
 
validate_project.py CHANGED
@@ -43,8 +43,8 @@ try:
43
  errors.append('V3.1 largeMarkerOuterSize must remain 144')
44
  if markers.get('strictMarkerMode') is not True:
45
  errors.append('strictMarkerMode must default to true')
46
- if len(markers.get('smallGroupMarkers', [])) != 16:
47
- errors.append('Expected 16 small group markers')
48
  rescue = markers.get('localRescue') or {}
49
  if rescue.get('enabled') is not True:
50
  errors.append('V3.1 localRescue.enabled must be true')
@@ -65,4 +65,4 @@ if errors:
65
  sys.exit(1)
66
 
67
  print('PROJECT VALIDATION PASSED')
68
- print('V3.1 template, local-shadow rescue settings and required files are present.')
 
43
  errors.append('V3.1 largeMarkerOuterSize must remain 144')
44
  if markers.get('strictMarkerMode') is not True:
45
  errors.append('strictMarkerMode must default to true')
46
+ if 'smallGroupMarkers' in markers or 'smallMarkerOuterSize' in markers:
47
+ errors.append('Small group marker settings must not exist in this variant')
48
  rescue = markers.get('localRescue') or {}
49
  if rescue.get('enabled') is not True:
50
  errors.append('V3.1 localRescue.enabled must be true')
 
65
  sys.exit(1)
66
 
67
  print('PROJECT VALIDATION PASSED')
68
+ print('V3.1 four-large-marker-only template, local-shadow rescue settings and required files are present.')