From 11440b539a665a83899494adb62dfe8c9f794823 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Thu, 2 Jul 2026 18:46:22 +0800 Subject: [PATCH] Use macOS Vision OCR for image extraction --- ...racter_level_external_manifest_latest.json | 2 +- ...haracter_level_external_manifest_latest.md | 2 +- ...aracter_level_extraction_queue_latest.json | 2 +- ...character_level_extraction_queue_latest.md | 2 +- ...acter_level_extraction_results_latest.json | 830 ++++++++++-------- ...aracter_level_extraction_results_latest.md | 14 +- ...acter_level_inventory_manifest_latest.json | 30 +- ...aracter_level_inventory_manifest_latest.md | 4 +- progress.md | 7 + scripts/character_level_inventory_manifest.py | 159 +++- ...test_character_level_inventory_manifest.py | 9 +- 11 files changed, 666 insertions(+), 395 deletions(-) diff --git a/docs/research/character_level_external_manifest_latest.json b/docs/research/character_level_external_manifest_latest.json index bbfed507..daf9ce74 100644 --- a/docs/research/character_level_external_manifest_latest.json +++ b/docs/research/character_level_external_manifest_latest.json @@ -1,7 +1,7 @@ { "scope": "external", "status": "pass", - "generated_at": "2026-07-02T09:43:31.236878+00:00", + "generated_at": "2026-07-02T10:44:46.651144+00:00", "mode": { "heavy_ocr": false, "whole_machine_scan": false, diff --git a/docs/research/character_level_external_manifest_latest.md b/docs/research/character_level_external_manifest_latest.md index 45afbc1e..58c1c12f 100644 --- a/docs/research/character_level_external_manifest_latest.md +++ b/docs/research/character_level_external_manifest_latest.md @@ -2,7 +2,7 @@ - status: pass - scope: external -- generated_at: 2026-07-02T09:43:31.236878+00:00 +- generated_at: 2026-07-02T10:44:46.651144+00:00 - total_files: 883 - unhashed_files: 0 - unclassified_files: 0 diff --git a/docs/research/character_level_extraction_queue_latest.json b/docs/research/character_level_extraction_queue_latest.json index 07a65157..095c16a8 100644 --- a/docs/research/character_level_extraction_queue_latest.json +++ b/docs/research/character_level_extraction_queue_latest.json @@ -1,7 +1,7 @@ { "scope": "extraction-queue", "status": "pass", - "generated_at": "2026-07-02T09:43:43.085623+00:00", + "generated_at": "2026-07-02T10:45:03.271522+00:00", "mode": { "heavy_ocr": false, "whole_machine_scan": false, diff --git a/docs/research/character_level_extraction_queue_latest.md b/docs/research/character_level_extraction_queue_latest.md index 0f310ccf..e21b0664 100644 --- a/docs/research/character_level_extraction_queue_latest.md +++ b/docs/research/character_level_extraction_queue_latest.md @@ -2,7 +2,7 @@ - status: pass - scope: extraction-queue -- generated_at: 2026-07-02T09:43:43.085623+00:00 +- generated_at: 2026-07-02T10:45:03.271522+00:00 - queued_files: 66 - unhashed_files: 0 - Heavy OCR: disabled diff --git a/docs/research/character_level_extraction_results_latest.json b/docs/research/character_level_extraction_results_latest.json index 2be5c810..633aa127 100644 --- a/docs/research/character_level_extraction_results_latest.json +++ b/docs/research/character_level_extraction_results_latest.json @@ -1,9 +1,11 @@ { "scope": "extraction-results", "status": "pass", - "generated_at": "2026-07-02T09:44:22.267617+00:00", + "generated_at": "2026-07-02T10:45:41.296864+00:00", "mode": { "heavy_ocr": false, + "macos_vision_available": true, + "ocr_backend_policy": "prefer_tesseract_else_macos_vision_else_blocked", "whole_machine_scan": false, "external_high_relevance_scan": true, "semantic_promotion": false, @@ -15,18 +17,18 @@ "stored_text_payload_fields": 0 }, "result_counts": { - "ocr_blocked_missing_engine": 53, - "text_extracted": 13 + "text_empty": 17, + "text_extracted": 49 }, "method_counts": { "docx": 11, - "pdfplumber": 2, - "pytesseract": 53 + "macos_vision": 53, + "pdfplumber": 2 }, "post_extraction_classification_counts": { - "extracted_candidate_for_review": 10, + "extracted_candidate_for_review": 44, "extracted_private_reference_only": 3, - "extracted_reference_only": 53 + "extracted_reference_only": 19 }, "results": [ { @@ -237,19 +239,21 @@ "classification": "external_skill_fragment", "priority": "priority_1", "promotion_status": "external_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 6, + "extracted_line_count": 2, + "text_sha256": "6268f153a2dbec6a0294a2164eed3f4b3f38deb549fd55c856531a742f154d37", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -261,19 +265,21 @@ "classification": "external_skill_fragment", "priority": "priority_1", "promotion_status": "external_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 11, + "extracted_line_count": 2, + "text_sha256": "15932d34d0d39cfb45324e59f76a8a7e6ed0751971570166f5402361885e83b8", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -285,19 +291,21 @@ "classification": "external_skill_fragment", "priority": "priority_1", "promotion_status": "external_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 30, + "extracted_line_count": 6, + "text_sha256": "418fec26dc90172b495527a16d9054bd75ea188a64d568ffab959fb6223d0864", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -309,19 +317,21 @@ "classification": "external_skill_fragment", "priority": "priority_1", "promotion_status": "external_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 24, + "extracted_line_count": 4, + "text_sha256": "cabb393532f5e24293c5fd0a59f0b423a2da2d7d6a261651e69032d45fac3d6f", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -333,19 +343,21 @@ "classification": "external_historical_report", "priority": "priority_2", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 6, + "extracted_line_count": 2, + "text_sha256": "6268f153a2dbec6a0294a2164eed3f4b3f38deb549fd55c856531a742f154d37", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -357,19 +369,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 2396, + "extracted_line_count": 203, + "text_sha256": "022d397c72b950fe3780952b52004c166cb731fc04edc7530ac6cb2d84951097", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -381,19 +395,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 2931, + "extracted_line_count": 231, + "text_sha256": "468967a7cfdce5a6b0dc23e534ceae4776a6f9f415a9fcaa4a5bc34a4fb982cb", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -405,19 +421,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 3635, + "extracted_line_count": 329, + "text_sha256": "ae6ded6ec4433f6a75e29c91d551fa3033b381be765d22d97915b7bfc4e0cf0c", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -429,19 +447,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 4205, + "extracted_line_count": 513, + "text_sha256": "6e148836e7847c3a71e90536fbe52b3795fe2867a5d07dffc3a368d1aec0b92c", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -453,19 +473,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 4203, + "extracted_line_count": 513, + "text_sha256": "a76ecdaa8dd9b852186a157d7b7fd4c5fb7ddce598b7272a37df634762a32408", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -477,19 +499,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 4155, + "extracted_line_count": 547, + "text_sha256": "1a504051cf1d6573679d32968c51c84a767457e722cd42f60a942cde455e27c3", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -501,19 +525,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 3514, + "extracted_line_count": 472, + "text_sha256": "3e9f59c189e98d9fb07bba1524b7eea209fbf7cb3a7f5f83f66d5b80f660919f", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -525,19 +551,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 3080, + "extracted_line_count": 219, + "text_sha256": "848194fea9a0f4b8e05121f5b07261a12fa04351c76cb8cd6740b596050f60d0", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -549,19 +577,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 359, + "extracted_line_count": 97, + "text_sha256": "b90601cd0134a26f07f4cde1773aa6c6ec06a2285ae4ba6f9eba2dcb43ef5c05", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -573,19 +603,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 369, + "extracted_line_count": 88, + "text_sha256": "ec1f69ff40ea201baffcddb64a1c6ec6a8a4bd5179bf4cb74023b55710d69ee0", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -597,19 +629,21 @@ "classification": "external_archive_or_binary", "priority": "priority_3", "promotion_status": "external_index_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 340, + "extracted_line_count": 93, + "text_sha256": "324e69607b771ee1d20509fdf33ed7f144593495e48f16ac62f9224b44e84eb0", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -621,19 +655,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -645,19 +681,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 30, + "extracted_line_count": 3, + "text_sha256": "cc8b8423797e8914515dba9684ebfeba17a8fa329071b6ca4099276aac0a49b8", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -669,19 +707,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -693,19 +733,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 398, + "extracted_line_count": 36, + "text_sha256": "f91d33b01facb8e7a6adf9d924f5dfa216a04ff6e883526e4278ccc58b17ea11", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -717,19 +759,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -741,19 +785,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 1197, + "extracted_line_count": 80, + "text_sha256": "d64970b2ccc7be0c33de8e48e465193a62b32a0f231299c1c035f84fa22672df", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -765,19 +811,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 1080, + "extracted_line_count": 64, + "text_sha256": "5519193313ca62697b8a23a0f1296b66550a0341aa70c4e18305995e23389d52", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -789,19 +837,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 536, + "extracted_line_count": 46, + "text_sha256": "bb1d2bfb683260c25402d34a639025894a223fa5a33b80c30a1ef0f16df85067", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -813,19 +863,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 2, + "extracted_line_count": 2, + "text_sha256": "b26e110e558e62dc69d16888f8c6914fb96fccc843ebf0d30000cd8784728fc0", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -837,19 +889,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -861,19 +915,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 11, + "extracted_line_count": 3, + "text_sha256": "44899d783bdaad21e5a47d01ec9e73e3410703cfb0f68a6171ef7a0bc1f8d732", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -885,19 +941,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -909,19 +967,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -933,19 +993,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -957,19 +1019,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 14, + "extracted_line_count": 3, + "text_sha256": "cc9443af4ef850050043353a95499938590b234a85125c0c2ad99103d2d7c06f", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -981,19 +1045,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 7, + "extracted_line_count": 2, + "text_sha256": "c6b24a111d9fb03d1bf0d55030b0d9edfb65708e4feb8dc80892eca0a6a54f1d", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1005,19 +1071,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 14, + "extracted_line_count": 3, + "text_sha256": "08c157171909ced95edefb310fe369f66f04a1db897affcaa6945e3df3a3bf7e", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1029,19 +1097,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 160, + "extracted_line_count": 16, + "text_sha256": "193c01df41388ffd21c3c99c27c0f458af4537584fbb71253c8dcfd82c09cb2c", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1053,19 +1123,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1077,19 +1149,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1101,19 +1175,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1125,19 +1201,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1149,19 +1227,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1173,19 +1253,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1197,19 +1279,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1221,19 +1305,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1245,19 +1331,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1269,19 +1357,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", + "extraction_result": "text_empty", + "extraction_method": "macos_vision", + "blocked_reason": null, "extracted_character_count": 0, "extracted_line_count": 0, "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1293,19 +1383,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 766, + "extracted_line_count": 49, + "text_sha256": "4ff4d9180c050ef0ee7befcf09851a4c86f77ed6a3ff562935e70dec20545d79", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1317,19 +1409,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 763, + "extracted_line_count": 55, + "text_sha256": "433d35e43ad0d4e5c94144e4e5360e775d884b31a04a48567bd031d4af67c71a", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1341,19 +1435,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 442, + "extracted_line_count": 36, + "text_sha256": "2e79a0ceadfecd2a99f1c04ced05a9795dba285d789eb93cf30d682bcb887a91", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1365,19 +1461,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 1297, + "extracted_line_count": 57, + "text_sha256": "1ad3fce3afb8290f54caaa668462a9f6f2d197b98069cc0a03f7a63975dcacc5", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1389,19 +1487,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 482, + "extracted_line_count": 42, + "text_sha256": "9e95083cfc45527ebf8f9e120d4b94f59951d71a224162e8f6772b65f7f21c6b", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", @@ -1413,19 +1513,21 @@ "classification": "external_book_or_document", "priority": "priority_1", "promotion_status": "external_reference_only", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, - "post_extraction_classification": "extracted_reference_only", + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 714, + "extracted_line_count": 50, + "text_sha256": "f127c3f663c2ebaf5ca1dc069c3e0ceb0f10398be496407b1aa3b68d1e465268", + "post_extraction_classification": "extracted_candidate_for_review", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "project", @@ -1437,19 +1539,21 @@ "classification": "open_source_reference", "priority": "priority_2", "promotion_status": "reference_layer_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 11, + "extracted_line_count": 2, + "text_sha256": "15932d34d0d39cfb45324e59f76a8a7e6ed0751971570166f5402361885e83b8", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "project", @@ -1461,19 +1565,21 @@ "classification": "open_source_reference", "priority": "priority_1", "promotion_status": "reference_layer_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 30, + "extracted_line_count": 6, + "text_sha256": "418fec26dc90172b495527a16d9054bd75ea188a64d568ffab959fb6223d0864", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "project", @@ -1485,19 +1591,21 @@ "classification": "open_source_reference", "priority": "priority_1", "promotion_status": "reference_layer_candidate", - "extraction_result": "ocr_blocked_missing_engine", - "extraction_method": "pytesseract", - "blocked_reason": "tesseract executable not found", - "extracted_character_count": 0, - "extracted_line_count": 0, - "text_sha256": null, + "extraction_result": "text_extracted", + "extraction_method": "macos_vision", + "blocked_reason": null, + "extracted_character_count": 24, + "extracted_line_count": 4, + "text_sha256": "cabb393532f5e24293c5fd0a59f0b423a2da2d7d6a261651e69032d45fac3d6f", "post_extraction_classification": "extracted_reference_only", "ocr_engine_available": false, "ocr_requested_languages": [ "chi_sim", "eng" ], - "ocr_available_languages": [] + "ocr_available_languages": [], + "ocr_backend": "macos_vision", + "ocr_cache_status": "hit" }, { "source_scope": "external", diff --git a/docs/research/character_level_extraction_results_latest.md b/docs/research/character_level_extraction_results_latest.md index ed44e562..0e8a8c2a 100644 --- a/docs/research/character_level_extraction_results_latest.md +++ b/docs/research/character_level_extraction_results_latest.md @@ -2,28 +2,30 @@ - status: pass - scope: extraction-results -- generated_at: 2026-07-02T09:44:22.267617+00:00 +- generated_at: 2026-07-02T10:45:41.296864+00:00 - total_files: 66 - unhashed_files: 0 - stored_text_payload_fields: 0 - Heavy OCR: disabled +- macos_vision_available: True +- ocr_backend_policy: prefer_tesseract_else_macos_vision_else_blocked ## Result Counts -- `ocr_blocked_missing_engine`: 53 -- `text_extracted`: 13 +- `text_empty`: 17 +- `text_extracted`: 49 ## Method Counts - `docx`: 11 +- `macos_vision`: 53 - `pdfplumber`: 2 -- `pytesseract`: 53 ## Post-Extraction Classification Counts -- `extracted_candidate_for_review`: 10 +- `extracted_candidate_for_review`: 44 - `extracted_private_reference_only`: 3 -- `extracted_reference_only`: 53 +- `extracted_reference_only`: 19 ## Boundary diff --git a/docs/research/character_level_inventory_manifest_latest.json b/docs/research/character_level_inventory_manifest_latest.json index 3034167e..bb20372b 100644 --- a/docs/research/character_level_inventory_manifest_latest.json +++ b/docs/research/character_level_inventory_manifest_latest.json @@ -1,7 +1,7 @@ { "scope": "project", "status": "pass", - "generated_at": "2026-07-02T09:43:08.434092+00:00", + "generated_at": "2026-07-02T10:44:17.758196+00:00", "mode": { "heavy_ocr": false, "whole_machine_scan": false, @@ -47,7 +47,7 @@ }, "docs/research": { "files": 538, - "bytes": 2965145 + "bytes": 2969221 }, "references": { "files": 236, @@ -5173,7 +5173,7 @@ "path": "docs/research/character_level_external_manifest_latest.json", "suffix": ".json", "byte_count": 666472, - "sha256": "9fceb19121870ae252b77645b4e49515b8c1c6b7bd465de227dca8de47437b35", + "sha256": "e7a98a44b5ad6c2582bddb9482bf7d980a93784d74a5a448b6aee51c358269fd", "extraction_status": "text_indexed", "character_count": 661666, "line_count": 12446, @@ -5187,7 +5187,7 @@ "path": "docs/research/character_level_external_manifest_latest.md", "suffix": ".md", "byte_count": 1280, - "sha256": "c030624888b4bc361635468d268653358d51f359d57a597b30620a5ccf24182e", + "sha256": "dd2c84a86dc7e691d5bd9088829ba2088ff32ad305f8eb8e7d492f6eb40ab9f4", "extraction_status": "text_indexed", "character_count": 1272, "line_count": 47, @@ -5201,7 +5201,7 @@ "path": "docs/research/character_level_extraction_queue_latest.json", "suffix": ".json", "byte_count": 31649, - "sha256": "7aba199609805e911f07d0443e7b759ca46cbffa0527af932581c712808358e6", + "sha256": "4946bd810c6b312bae1fc9bb7a78bc34f886a4a5c7532d74732399fe7f612cdb", "extraction_status": "text_indexed", "character_count": 30151, "line_count": 758, @@ -5215,7 +5215,7 @@ "path": "docs/research/character_level_extraction_queue_latest.md", "suffix": ".md", "byte_count": 633, - "sha256": "8e5277feb3e35c124f65869c39e9a6038a403f284ed7dea786fea50b76365ca2", + "sha256": "8baea40e157fca6adc9228988561253f0803eb719b7392bc74ef4e64145394b6", "extraction_status": "text_indexed", "character_count": 633, "line_count": 27, @@ -5228,11 +5228,11 @@ "docs/research/character_level_extraction_results_latest.json": { "path": "docs/research/character_level_extraction_results_latest.json", "suffix": ".json", - "byte_count": 61620, - "sha256": "02413ba1ec2877ef67794bffde2f4a0ed35ea60677ee94c9bcbe517fac5bd778", + "byte_count": 65711, + "sha256": "a06874a9c54148891373069cd3933b3be51e3b045e51bd8b121c751f9dcf83ac", "extraction_status": "text_indexed", - "character_count": 60122, - "line_count": 1544, + "character_count": 64213, + "line_count": 1650, "decode_replacement_count": 0, "classification": "research_governance", "priority": "priority_3", @@ -5242,10 +5242,10 @@ "docs/research/character_level_extraction_results_latest.md": { "path": "docs/research/character_level_extraction_results_latest.md", "suffix": ".md", - "byte_count": 764, - "sha256": "fd16ff882e9b290a4cb4aa6f613b41e54eb1e9065577e9af322f817455b0a383", + "byte_count": 749, + "sha256": "6f7d1e04b3bbb0a977e44b2cd6ddc6a71fe7b97b016a76b6e7d3dc0da37b32af", "extraction_status": "text_indexed", - "character_count": 764, + "character_count": 749, "line_count": 33, "decode_replacement_count": 0, "classification": "research_governance", @@ -5257,7 +5257,7 @@ "path": "docs/research/character_level_inventory_manifest_latest.json", "suffix": ".json", "byte_count": 722955, - "sha256": "197892b1f3b259f7aa868593670cbb693dd8717bd5f304407b37bdaaf7a97463", + "sha256": "4fcf6ce8815022fbe308fca4d725c50a481ce2f1383397c3617f08bb138cc3fc", "extraction_status": "text_indexed", "character_count": 722405, "line_count": 15118, @@ -5271,7 +5271,7 @@ "path": "docs/research/character_level_inventory_manifest_latest.md", "suffix": ".md", "byte_count": 1090, - "sha256": "18ffd1520954f0a98b79a56f9d3919fdfb5a9e447c5802bc4a3c011204d2934e", + "sha256": "547a1e4ec8187636e39dbb09551541a0d2296cb798ee1895804feaca2612e1c1", "extraction_status": "text_indexed", "character_count": 1090, "line_count": 44, diff --git a/docs/research/character_level_inventory_manifest_latest.md b/docs/research/character_level_inventory_manifest_latest.md index 37ed76c2..2bedd0bf 100644 --- a/docs/research/character_level_inventory_manifest_latest.md +++ b/docs/research/character_level_inventory_manifest_latest.md @@ -2,7 +2,7 @@ - status: pass - scope: project -- generated_at: 2026-07-02T09:43:08.434092+00:00 +- generated_at: 2026-07-02T10:44:17.758196+00:00 - total_files: 1075 - unhashed_files: 0 - unclassified_files: 0 @@ -16,7 +16,7 @@ | --- | ---: | ---: | | `AGENTS.md` | 1 | 3932 | | `SKILL.md` | 1 | 43187 | -| `docs/research` | 538 | 2965145 | +| `docs/research` | 538 | 2969221 | | `references` | 236 | 4008411 | | `references/open_source_sources` | 299 | 9365015 | diff --git a/progress.md b/progress.md index 5f3ce97b..7ad194b0 100644 --- a/progress.md +++ b/progress.md @@ -836,3 +836,10 @@ - 增强 `scripts/character_level_inventory_manifest.py --scope extraction-results`:图片 OCR 记录 `ocr_engine_available`、`ocr_requested_languages`、`ocr_available_languages`,便于安装 OCR 后复跑。 - 为异常 DOCX 增加 `word/document.xml` 备用提取路径;`/Users/wuyongnaren/文件仓库/印度占星文章/4印度占星.docx` 已从 `extraction_failed` 转为 `text_extracted`,字符数 `19643`。 - 当前提取结果更新为:`text_extracted=13`、`ocr_blocked_missing_engine=53`、`extraction_failed=0`;仍未保存正文,`stored_text_payload_fields=0`。 + +## 2026-07-02T18:43:00+08:00 - Intel macOS 12 Vision OCR 完成 + +- 机器确认:macOS `12.7.6`、Intel `x86_64`、Core i7-4980HQ;Homebrew 当前 tesseract 主程序没有可用 bottle,只能取源码 tarball,继续安装会拖入大规模依赖/源码构建。 +- 采用更适合本机的 macOS Vision OCR:脚本自动编译并缓存 `~/.cache/jyotish-ocr/vision_ocr`,不依赖 Homebrew tesseract。 +- `scripts/character_level_inventory_manifest.py --scope extraction-results` 新增 OCR cache;缓存只保存 OCR 文本 hash、字符数、行数、后端、状态,不保存 OCR 正文。 +- 53 张图片已用 `macos_vision` 复跑完成;当前结果:`text_extracted=49`、`text_empty=17`、`ocr_blocked_missing_engine=0`、`extraction_failed=0`、`stored_text_payload_fields=0`。 diff --git a/scripts/character_level_inventory_manifest.py b/scripts/character_level_inventory_manifest.py index 6b21fc6e..c545e315 100644 --- a/scripts/character_level_inventory_manifest.py +++ b/scripts/character_level_inventory_manifest.py @@ -12,6 +12,7 @@ import argparse import hashlib import json import shutil +import subprocess import sys import zipfile from datetime import datetime, timezone @@ -29,6 +30,47 @@ EXTRACTION_QUEUE_JSON = ROOT / "docs" / "research" / "character_level_extraction EXTRACTION_QUEUE_MD = ROOT / "docs" / "research" / "character_level_extraction_queue_latest.md" EXTRACTION_RESULTS_JSON = ROOT / "docs" / "research" / "character_level_extraction_results_latest.json" EXTRACTION_RESULTS_MD = ROOT / "docs" / "research" / "character_level_extraction_results_latest.md" +VISION_OCR_SOURCE = r''' +import Foundation +import Vision +import AppKit + +let args = CommandLine.arguments +if args.count < 2 { + fputs("usage: vision_ocr image\n", stderr) + exit(2) +} +let url = URL(fileURLWithPath: args[1]) +guard let image = NSImage(contentsOf: url), let tiff = image.tiffRepresentation, + let bitmap = NSBitmapImageRep(data: tiff), let cgImage = bitmap.cgImage else { + fputs("cannot load image\n", stderr) + exit(3) +} +let request = VNRecognizeTextRequest { request, error in + if let error = error { + fputs("vision error: \(error)\n", stderr) + exit(4) + } + let observations = request.results as? [VNRecognizedTextObservation] ?? [] + for obs in observations { + if let top = obs.topCandidates(1).first { + print(top.string) + } + } +} +request.recognitionLevel = .accurate +request.usesLanguageCorrection = true +if #available(macOS 11.0, *) { + request.recognitionLanguages = ["zh-Hans", "en-US"] +} +let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) +do { + try handler.perform([request]) +} catch { + fputs("perform error: \(error)\n", stderr) + exit(5) +} +''' PROJECT_SCAN_ROOTS = [ "references", @@ -533,7 +575,7 @@ def _extract_pdf_text(path: Path) -> tuple[str, str, str | None]: def _extract_image_text(path: Path) -> tuple[str, str, str | None]: if not shutil.which("tesseract"): - return "", "pytesseract", "tesseract executable not found" + return _extract_image_text_with_vision(path) try: from PIL import Image import pytesseract @@ -544,6 +586,99 @@ def _extract_image_text(path: Path) -> tuple[str, str, str | None]: return "", "pytesseract", f"{type(exc).__name__}: {exc}" +def _vision_ocr_binary() -> Path | None: + if sys.platform != "darwin" or not shutil.which("swiftc"): + return None + cache_dir = Path.home() / ".cache" / "jyotish-ocr" + cache_dir.mkdir(parents=True, exist_ok=True) + source = cache_dir / "vision_ocr.swift" + binary = cache_dir / "vision_ocr" + if binary.exists() and binary.stat().st_mtime >= source.stat().st_mtime if source.exists() else False: + return binary + source.write_text(VISION_OCR_SOURCE, encoding="utf-8") + completed = subprocess.run( + ["swiftc", str(source), "-o", str(binary)], + text=True, + capture_output=True, + timeout=60, + check=False, + ) + if completed.returncode != 0: + return None + return binary + + +def _extract_image_text_with_vision(path: Path) -> tuple[str, str, str | None]: + binary = _vision_ocr_binary() + if not binary: + return "", "none", "no OCR backend available: tesseract missing and macOS Vision unavailable" + completed = subprocess.run( + [str(binary), str(path)], + text=True, + capture_output=True, + timeout=90, + check=False, + ) + if completed.returncode != 0: + return "", "macos_vision", completed.stderr.strip() or f"vision_ocr exit {completed.returncode}" + return completed.stdout, "macos_vision", None + + +def _ocr_cache_path(item: dict[str, Any], method: str) -> Path: + cache_dir = Path.home() / ".cache" / "jyotish-ocr" / "results" + cache_dir.mkdir(parents=True, exist_ok=True) + return cache_dir / f"{item['sha256']}.{method}.json" + + +def _load_ocr_cached_item(item: dict[str, Any]) -> dict[str, Any] | None: + for method in ["macos_vision", "pytesseract"]: + path = _ocr_cache_path(item, method) + if not path.exists(): + continue + try: + cached = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + continue + if cached.get("source_sha256") != item.get("sha256"): + continue + return { + **item, + "extraction_result": cached["extraction_result"], + "extraction_method": cached["extraction_method"], + "blocked_reason": cached.get("blocked_reason"), + "extracted_character_count": cached["extracted_character_count"], + "extracted_line_count": cached["extracted_line_count"], + "text_sha256": cached.get("text_sha256"), + "post_extraction_classification": cached["post_extraction_classification"], + "ocr_engine_available": cached["ocr_engine_available"], + "ocr_requested_languages": cached["ocr_requested_languages"], + "ocr_available_languages": cached["ocr_available_languages"], + "ocr_backend": cached["ocr_backend"], + "ocr_cache_status": "hit", + } + return None + + +def _write_ocr_cached_item(item: dict[str, Any]) -> None: + method = str(item.get("ocr_backend") or item.get("extraction_method") or "unknown") + path = _ocr_cache_path(item, method) + cache_payload = { + "source_sha256": item["sha256"], + "extraction_result": item["extraction_result"], + "extraction_method": item["extraction_method"], + "blocked_reason": item.get("blocked_reason"), + "extracted_character_count": item["extracted_character_count"], + "extracted_line_count": item["extracted_line_count"], + "text_sha256": item.get("text_sha256"), + "post_extraction_classification": item["post_extraction_classification"], + "ocr_engine_available": item.get("ocr_engine_available"), + "ocr_requested_languages": item.get("ocr_requested_languages", ["chi_sim", "eng"]), + "ocr_available_languages": item.get("ocr_available_languages", []), + "ocr_backend": item.get("ocr_backend", method), + } + path.write_text(json.dumps(cache_payload, ensure_ascii=False, indent=2), encoding="utf-8") + + def _ocr_available_languages() -> list[str]: if not shutil.which("tesseract"): return [] @@ -580,9 +715,12 @@ def _extract_queued_item(item: dict[str, Any]) -> dict[str, Any]: elif status == "pdf_text_extraction_queued": text, method, error = _extract_pdf_text(path) elif status == "image_ocr_queued": + cached = _load_ocr_cached_item(item) + if cached: + return cached text, method, error = _extract_image_text(path) - if error and "tesseract executable not found" in error: - return { + if error and "no OCR backend available" in error: + blocked_item = { **item, "extraction_result": "ocr_blocked_missing_engine", "extraction_method": method, @@ -594,7 +732,11 @@ def _extract_queued_item(item: dict[str, Any]) -> dict[str, Any]: "ocr_engine_available": False, "ocr_requested_languages": ["chi_sim", "eng"], "ocr_available_languages": [], + "ocr_backend": "none", + "ocr_cache_status": "miss", } + _write_ocr_cached_item(blocked_item) + return blocked_item else: text = "" method = "unsupported" @@ -607,7 +749,7 @@ def _extract_queued_item(item: dict[str, Any]) -> dict[str, Any]: result = "text_extracted" else: result = "text_empty" - return { + result_item = { **item, "extraction_result": result, "extraction_method": method, @@ -621,11 +763,16 @@ def _extract_queued_item(item: dict[str, Any]) -> dict[str, Any]: "ocr_engine_available": shutil.which("tesseract") is not None, "ocr_requested_languages": ["chi_sim", "eng"], "ocr_available_languages": _ocr_available_languages(), + "ocr_backend": "tesseract" if method == "pytesseract" else method, + "ocr_cache_status": "miss", } if status == "image_ocr_queued" else {} ), } + if status == "image_ocr_queued": + _write_ocr_cached_item(result_item) + return result_item def build_extraction_results(*, write: bool = True) -> dict[str, Any]: @@ -647,6 +794,8 @@ def build_extraction_results(*, write: bool = True) -> dict[str, Any]: "generated_at": datetime.now(timezone.utc).isoformat(), "mode": { "heavy_ocr": shutil.which("tesseract") is not None, + "macos_vision_available": _vision_ocr_binary() is not None, + "ocr_backend_policy": "prefer_tesseract_else_macos_vision_else_blocked", "whole_machine_scan": False, "external_high_relevance_scan": True, "semantic_promotion": False, @@ -791,6 +940,8 @@ def _render_extraction_results_markdown(report: dict[str, Any]) -> str: f"- unhashed_files: {report['summary']['unhashed_files']}", f"- stored_text_payload_fields: {report['summary']['stored_text_payload_fields']}", f"- Heavy OCR: {'enabled' if report['mode']['heavy_ocr'] else 'disabled'}", + f"- macos_vision_available: {report['mode'].get('macos_vision_available', False)}", + f"- ocr_backend_policy: {report['mode'].get('ocr_backend_policy', 'unknown')}", "", "## Result Counts", "", diff --git a/tests/test_character_level_inventory_manifest.py b/tests/test_character_level_inventory_manifest.py index 02ebd045..839cf707 100644 --- a/tests/test_character_level_inventory_manifest.py +++ b/tests/test_character_level_inventory_manifest.py @@ -190,6 +190,7 @@ def test_extraction_results_extract_pdf_and_docx_without_storing_text() -> None: assert report["result_counts"].get("extraction_failed", 0) == 0 assert report["method_counts"]["docx"] >= 10 assert report["method_counts"]["pdfplumber"] + report["method_counts"].get("pypdf", 0) >= 2 + assert report["mode"]["macos_vision_available"] in {True, False} for item in report["results"]: assert item["sha256"] @@ -210,10 +211,12 @@ def test_extraction_results_extract_pdf_and_docx_without_storing_text() -> None: "extracted_private_reference_only", "extracted_candidate_for_review", } - if item["extraction_result"] == "ocr_blocked_missing_engine": - assert item["ocr_engine_available"] is False + if item["extraction_status"] == "image_ocr_queued": assert item["ocr_requested_languages"] == ["chi_sim", "eng"] - assert item["ocr_available_languages"] == [] + assert item["ocr_backend"] in {"tesseract", "macos_vision", "none"} + if item["ocr_backend"] == "none": + assert item["extraction_result"] == "ocr_blocked_missing_engine" + assert item["ocr_engine_available"] is False json_path = ROOT / report["artifacts"]["json_report"] md_path = ROOT / report["artifacts"]["markdown_report"]