Spaces:
Running
Running
fix cline
Browse files- .DS_Store +0 -0
- .clinerules/activeContext.md +0 -31
- .clinerules/apiDocumentation.md +0 -29
- .clinerules/complexFeature.md +0 -29
- .clinerules/hocr-basics-api.md +0 -106
- .clinerules/integrationSpecs.md +0 -27
- .clinerules/principle-of-simplicity.md +0 -55
- .clinerules/productContext.md +0 -25
- .clinerules/progress.md +0 -27
- .clinerules/systemPatterns.md +0 -31
- .clinerules/techContext.md +0 -40
- .gitignore +9 -0
- docs/config_refactoring.md +47 -0
- improvements.md +587 -0
- memory-bank/activeContext.md +31 -0
- memory-bank/productContext.md +31 -0
- memory-bank/progress.md +34 -0
- .clinerules/projectBrief.md → memory-bank/project-brief.md +8 -10
- .clinerules/project-brief.md → memory-bank/projectbrief.md +0 -0
- memory-bank/systemPatterns.md +66 -0
- memory-bank/techContext.md +35 -0
- utils/README.md +75 -0
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
.clinerules/activeContext.md
DELETED
@@ -1,31 +0,0 @@
|
|
1 |
-
# Current Work Focus
|
2 |
-
|
3 |
-
Refining image preprocessing pipelines to better balance cleaning and preservation of fine details (especially for handwritten inputs)
|
4 |
-
|
5 |
-
Improving document type detection accuracy to feed better prompts to the OCR system
|
6 |
-
|
7 |
-
Enhancing structured output schemas to cover additional types like travel logs and scientific diagrams
|
8 |
-
|
9 |
-
Recent Changes
|
10 |
-
|
11 |
-
Implemented more document-type-specific preprocessing pipelines
|
12 |
-
|
13 |
-
Switched default OCR engine to Mistral for both printed and handwritten material
|
14 |
-
|
15 |
-
Modularized utility functions across utils.py, ocr_utils.py, and newly proposed submodules
|
16 |
-
|
17 |
-
Active Decisions and Considerations
|
18 |
-
|
19 |
-
Whether to expose preprocessing options to end users (e.g., deskew threshold)
|
20 |
-
|
21 |
-
Whether to allow fallback to local Tesseract OCR for offline cases
|
22 |
-
|
23 |
-
Determining best practices for handling multi-page PDFs with mixed layouts
|
24 |
-
|
25 |
-
Important Patterns and Learnings
|
26 |
-
|
27 |
-
Document type detection greatly improves OCR quality when tuned per-class
|
28 |
-
|
29 |
-
Over-aggressive preprocessing can erase faint handwriting; thresholds must be conservative for historical artifacts
|
30 |
-
|
31 |
-
Keeping preprocessing modular enables rapid experimentation and tuning.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/apiDocumentation.md
DELETED
@@ -1,29 +0,0 @@
|
|
1 |
-
apiDocumentation.md
|
2 |
-
API Interaction Documentation
|
3 |
-
Mistral OCR API
|
4 |
-
|
5 |
-
Endpoint: /v1/ocr
|
6 |
-
|
7 |
-
Payload:
|
8 |
-
|
9 |
-
image (binary)
|
10 |
-
|
11 |
-
prompt (optional contextual instructions)
|
12 |
-
|
13 |
-
Response:
|
14 |
-
|
15 |
-
structured_data: Hierarchical text + metadata output
|
16 |
-
|
17 |
-
raw_text: Plain extracted text
|
18 |
-
|
19 |
-
Error Handling:
|
20 |
-
|
21 |
-
Timeout retries (up to 3 attempts)
|
22 |
-
|
23 |
-
Local fallback to Tesseract if Mistral service unavailable
|
24 |
-
|
25 |
-
Tesseract Fallback
|
26 |
-
|
27 |
-
Only invoked if Mistral API fails after retries.
|
28 |
-
|
29 |
-
No structured output; raw text only.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/complexFeature.md
DELETED
@@ -1,29 +0,0 @@
|
|
1 |
-
# Complex Feature Documentation
|
2 |
-
|
3 |
-
Document Type Detection
|
4 |
-
|
5 |
-
Utilizes lightweight statistical heuristics combined with visual features.
|
6 |
-
|
7 |
-
Preprocessing-driven (thresholding, aspect ratios, contour analysis).
|
8 |
-
|
9 |
-
Outputs labels such as "handwritten letter", "scientific report", "recipe".
|
10 |
-
|
11 |
-
Preprocessing Pipelines
|
12 |
-
|
13 |
-
Customizable per document type.
|
14 |
-
|
15 |
-
Adaptive thresholding for delicate handwriting.
|
16 |
-
|
17 |
-
Morphological operations for removing bleed-through or artifacts.
|
18 |
-
|
19 |
-
Multilingual Handling
|
20 |
-
|
21 |
-
Language detection on OCR snippets using language_detection.py.
|
22 |
-
|
23 |
-
Allows contextual OCR prompting based on dominant language.
|
24 |
-
|
25 |
-
Structured Output Generation
|
26 |
-
|
27 |
-
Parsing OCR results into structured categories: titles, subtitles, body, marginalia, dates.
|
28 |
-
|
29 |
-
Supports output in raw text, JSON, and annotated Markdown.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/hocr-basics-api.md
DELETED
@@ -1,106 +0,0 @@
|
|
1 |
-
# HOCR Basics: API Integrations (Streamlit and Mistral OCR)
|
2 |
-
|
3 |
-
This rule defines the essential development standards for integrating the Mistral OCR API and using Streamlit components in the `milwright/historical-ocr` application.
|
4 |
-
|
5 |
-
## 📌 Rule 1: Mistral OCR API Usage
|
6 |
-
|
7 |
-
* **Endpoint:**
|
8 |
-
`POST https://api.mistral.ai/v1/ocr`
|
9 |
-
|
10 |
-
* **Headers:**
|
11 |
-
|
12 |
-
```http
|
13 |
-
Authorization: Bearer YOUR_API_KEY
|
14 |
-
Content-Type: application/json
|
15 |
-
```
|
16 |
-
|
17 |
-
* **Required JSON Body Fields:**
|
18 |
-
|
19 |
-
```json
|
20 |
-
{
|
21 |
-
"file_url": "https://example.com/your.pdf"
|
22 |
-
}
|
23 |
-
```
|
24 |
-
|
25 |
-
* **Expected Response Fields:**
|
26 |
-
|
27 |
-
* `text`: Raw OCR output
|
28 |
-
* `metadata`: Document structure, language, layout information
|
29 |
-
|
30 |
-
> **Note:** Always validate presence of required fields and handle error codes gracefully.
|
31 |
-
|
32 |
-
---
|
33 |
-
|
34 |
-
### 🖼️ Rule 2: Streamlit Usage Standards
|
35 |
-
|
36 |
-
* Use these core components:
|
37 |
-
|
38 |
-
* `st.file_uploader()`
|
39 |
-
* `st.selectbox()`
|
40 |
-
* `st.image()`
|
41 |
-
* `st.markdown()`
|
42 |
-
* `st.download_button()`
|
43 |
-
|
44 |
-
* Always set:
|
45 |
-
`use_container_width=True` for responsive display where supported
|
46 |
-
|
47 |
-
* Avoid global state; prefer `st.session_state` for interactivity and stateful inputs
|
48 |
-
|
49 |
-
## Mistral OCR Examples
|
50 |
-
|
51 |
-
``` json
|
52 |
-
{
|
53 |
-
"id": "string",
|
54 |
-
"object": "model",
|
55 |
-
"created": 0,{
|
56 |
-
"model": "string",
|
57 |
-
"id": "string",
|
58 |
-
"document": {
|
59 |
-
"document_url": "string",
|
60 |
-
"document_name": "string",
|
61 |
-
"type": "document_url"
|
62 |
-
},
|
63 |
-
"pages": [
|
64 |
-
0
|
65 |
-
],
|
66 |
-
"include_image_base64": true,
|
67 |
-
"image_limit": 0,
|
68 |
-
"image_min_size": 0
|
69 |
-
}
|
70 |
-
```
|
71 |
-
|
72 |
-
``` json
|
73 |
-
{
|
74 |
-
"pages": [
|
75 |
-
{
|
76 |
-
"index": 0,
|
77 |
-
"markdown": "string",
|
78 |
-
"images": [
|
79 |
-
{
|
80 |
-
"id": "string",
|
81 |
-
"top_left_x": 0,
|
82 |
-
"top_left_y": 0,
|
83 |
-
"bottom_right_x": 0,
|
84 |
-
"bottom_right_y": 0,
|
85 |
-
"image_base64": "string"
|
86 |
-
}
|
87 |
-
],
|
88 |
-
"dimensions": {
|
89 |
-
"dpi": 0,
|
90 |
-
"height": 0,
|
91 |
-
"width": 0
|
92 |
-
}
|
93 |
-
}
|
94 |
-
],
|
95 |
-
"model": "string",
|
96 |
-
"usage_info": {
|
97 |
-
"pages_processed": 0,
|
98 |
-
"doc_size_bytes": 0
|
99 |
-
}
|
100 |
-
}
|
101 |
-
```
|
102 |
-
|
103 |
-
### Links and Resources to Understand
|
104 |
-
|
105 |
-
* [URL to Mistral OCR APi doc](https://docs.mistral.ai/api/#tag/batch/operation/jobs_api_routes_batch_cancel_batch_job)
|
106 |
-
* [URL to Streamlit API documentation](https://docs.streamlit.io/develop/api-reference)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/integrationSpecs.md
DELETED
@@ -1,27 +0,0 @@
|
|
1 |
-
# Integration Specifications
|
2 |
-
|
3 |
-
External Services
|
4 |
-
|
5 |
-
Mistral OCR API: Primary service for document transcription and structured extraction.
|
6 |
-
|
7 |
-
Tesseract OCR (Local Fallback): Optional backup when API unavailable.
|
8 |
-
|
9 |
-
Internal Module Communication
|
10 |
-
|
11 |
-
app.py triggers ocr_processing.py orchestration based on user input.
|
12 |
-
|
13 |
-
ocr_processing.py dynamically calls preprocessing and OCR modules based on document type.
|
14 |
-
|
15 |
-
Preprocessed images passed through structured_ocr.py for API interaction and postprocessing.
|
16 |
-
|
17 |
-
Session State
|
18 |
-
|
19 |
-
Streamlit session stores:
|
20 |
-
|
21 |
-
Uploaded file metadata
|
22 |
-
|
23 |
-
Preprocessing parameters
|
24 |
-
|
25 |
-
Detected document type
|
26 |
-
|
27 |
-
OCR structured output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/principle-of-simplicity.md
DELETED
@@ -1,55 +0,0 @@
|
|
1 |
-
# Project Rule: Maintain Clean Separation Between Data and Presentation
|
2 |
-
The Principle of Content Purity
|
3 |
-
Core rule: Data that needs to be processed or stored should never contain presentation markup that's only meant for display.
|
4 |
-
|
5 |
-
What This Means in Practice
|
6 |
-
Avoid HTML in data structures
|
7 |
-
|
8 |
-
✅ DO: Keep raw text as pure text content
|
9 |
-
❌ DON'T: Embed HTML, CSS, or other presentation-specific markup in data fields
|
10 |
-
Design clear boundaries between data and presentation layers
|
11 |
-
|
12 |
-
✅ DO: Add presentation elements at the final rendering stage only
|
13 |
-
❌ DON'T: Add and strip presentation elements repeatedly throughout the processing pipeline
|
14 |
-
Fix problems at their source, not their symptoms
|
15 |
-
|
16 |
-
✅ DO: Prevent markup injection at the origin rather than adding complex stripping logic later
|
17 |
-
❌ DON'T: Create complex sanitization functions to clean data that shouldn't be contaminated in the first place
|
18 |
-
The OCR Text Formatter Example
|
19 |
-
Before (problematic):
|
20 |
-
|
21 |
-
def format_ocr_text(text, for_display=True):
|
22 |
-
# Text processing...
|
23 |
-
|
24 |
-
if for_display:
|
25 |
-
html = f"""
|
26 |
-
<div class="ocr-text-container">
|
27 |
-
{formatted_text}
|
28 |
-
</div>
|
29 |
-
"""
|
30 |
-
return html
|
31 |
-
else:
|
32 |
-
return formatted_text
|
33 |
-
After (better):
|
34 |
-
|
35 |
-
def format_ocr_text(text, for_display=False):
|
36 |
-
# Text processing...
|
37 |
-
|
38 |
-
if for_display:
|
39 |
-
html = f"""
|
40 |
-
{formatted_text}
|
41 |
-
"""
|
42 |
-
return html
|
43 |
-
else:
|
44 |
-
return formatted_text
|
45 |
-
What changed:
|
46 |
-
|
47 |
-
Default parameter changed to avoid accidental HTML addition
|
48 |
-
HTML wrapper div completely removed to eliminate the source of pollution
|
49 |
-
The simplest solution (removing the container) was better than any complex stripping logic
|
50 |
-
Benefits
|
51 |
-
Cleaner data: Raw content remains genuinely raw and easier to work with
|
52 |
-
More predictable processing: No need to account for unexpected HTML in processing pipelines
|
53 |
-
Easier debugging: Problems are visible at their source rather than as mysterious artifacts later
|
54 |
-
Reduced complexity: Eliminates the need for complex HTML stripping and sanitization logic
|
55 |
-
Remember: Simplicity is not just an ideal—it's a practical strategy that prevents entire classes of bugs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/productContext.md
DELETED
@@ -1,25 +0,0 @@
|
|
1 |
-
# Why the Project Exists
|
2 |
-
|
3 |
-
Historians, archivists, and researchers often struggle to extract reliable text from scanned archival materials. Many OCR tools fail when dealing with handwritten letters, historical scientific documents, and poorly digitized photographs.
|
4 |
-
|
5 |
-
Problems Being Solved
|
6 |
-
|
7 |
-
Low OCR accuracy on handwritten or degraded historical documents
|
8 |
-
|
9 |
-
Lack of structured metadata extraction for archival research
|
10 |
-
|
11 |
-
Inability to easily apply context-specific AI prompting for nuanced historical material
|
12 |
-
|
13 |
-
How the Product Should Work
|
14 |
-
|
15 |
-
Users upload images or PDFs
|
16 |
-
|
17 |
-
Preprocessing automatically improves OCR readiness
|
18 |
-
|
19 |
-
Document type detection informs customized AI prompting
|
20 |
-
|
21 |
-
Mistral OCR processes the document to output structured data (titles, authors, dates, body text, marginalia, etc.)
|
22 |
-
|
23 |
-
Users can download raw text, structured JSON, or annotated markdown
|
24 |
-
|
25 |
-
Example: "The OCR system must intelligently handle multilingual documents, support marginal notes and irregular layouts, and allow historians to guide the extraction process."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/progress.md
DELETED
@@ -1,27 +0,0 @@
|
|
1 |
-
# Current Status of Features
|
2 |
-
|
3 |
-
✅ Upload and preprocess historical documents
|
4 |
-
|
5 |
-
⚙️ Document type detection (estimated 80% accuracy)
|
6 |
-
|
7 |
-
✅ OCR extraction with structured outputs (titles, body, marginalia)
|
8 |
-
|
9 |
-
✅ Multiple output formats (Raw text, JSON, Markdown)
|
10 |
-
|
11 |
-
Known Issues and Limitations
|
12 |
-
|
13 |
-
Inconsistent marginalia capture in low-quality scans
|
14 |
-
|
15 |
-
Difficulties with heavily degraded non-Latin handwritten scripts
|
16 |
-
|
17 |
-
Layout detection errors on highly irregular, mixed-content PDFs
|
18 |
-
|
19 |
-
Evolution of Project Decisions
|
20 |
-
|
21 |
-
Migrated from Tesseract-only OCR to Mistral-first hybrid approach
|
22 |
-
|
23 |
-
Modularized preprocessing steps to allow flexible experimentation
|
24 |
-
|
25 |
-
Added support for marginalia and footnotes where feasible
|
26 |
-
|
27 |
-
Enhanced session state management to preserve intermediate results.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/systemPatterns.md
DELETED
@@ -1,31 +0,0 @@
|
|
1 |
-
# System Architecture
|
2 |
-
|
3 |
-
Frontend: Streamlit app (app.py) for user interface and interactions.
|
4 |
-
|
5 |
-
Core Processing: ocr_processing.py orchestrates preprocessing, document type detection, and OCR operations.
|
6 |
-
|
7 |
-
Image Preprocessing: preprocessing.py, image_segmentation.py handle deskewing, thresholding, and cleaning.
|
8 |
-
|
9 |
-
OCR and Structuring: structured_ocr.py and ocr_utils.py manage API communication and formatting structured outputs.
|
10 |
-
|
11 |
-
Utilities and Detection: language_detection.py, utils.py, and constants.py provide language detection, helpers, and prompt templates.
|
12 |
-
|
13 |
-
Key Technical Decisions
|
14 |
-
|
15 |
-
Streamlit cache management for upload processing efficiency.
|
16 |
-
|
17 |
-
Modular design of preprocessing paths based on document type.
|
18 |
-
|
19 |
-
Mistral AI as the primary OCR processor, with Tesseract fallback for redundancy.
|
20 |
-
|
21 |
-
Design Patterns in Use
|
22 |
-
|
23 |
-
Delegation: Frontend delegates all processing to backend orchestrators.
|
24 |
-
|
25 |
-
Modularity: Preprocessing and OCR tasks divided into clean, testable modules.
|
26 |
-
|
27 |
-
State-driven Processing: Output dynamically reflects session state and user input.
|
28 |
-
|
29 |
-
Component Relationships
|
30 |
-
|
31 |
-
app.py ⇨ ocr_processing.py ⇨ preprocessing.py, structured_ocr.py, language_detection.py, etc.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clinerules/techContext.md
DELETED
@@ -1,40 +0,0 @@
|
|
1 |
-
techContext.md
|
2 |
-
Technologies and Frameworks Used
|
3 |
-
|
4 |
-
Frontend Framework: Streamlit 1.44.1
|
5 |
-
|
6 |
-
OCR Engine: Mistralai Python SDK (≥ 0.1.0)
|
7 |
-
|
8 |
-
Image Processing: OpenCV, Pillow
|
9 |
-
|
10 |
-
PDF Parsing: pdf2image
|
11 |
-
|
12 |
-
Fallback OCR: Pytesseract
|
13 |
-
|
14 |
-
Utilities: NumPy, Requests, pycountry
|
15 |
-
|
16 |
-
Development Setup
|
17 |
-
|
18 |
-
Python 3.11+ virtual environment
|
19 |
-
|
20 |
-
Requirements managed through requirements.txt
|
21 |
-
|
22 |
-
.env file setup for API keys and environment configs
|
23 |
-
|
24 |
-
Type checking with mypy, linting with ruff
|
25 |
-
|
26 |
-
Technical Constraints
|
27 |
-
|
28 |
-
API rate limits and payload size restrictions from Mistral
|
29 |
-
|
30 |
-
Streamlit's session state limitations for very large files
|
31 |
-
|
32 |
-
Processing timeouts for oversized or complex PDFs
|
33 |
-
|
34 |
-
Dependencies and Tool Configurations
|
35 |
-
|
36 |
-
Mistralai pinned version (≥ 0.1.0)
|
37 |
-
|
38 |
-
OpenCV configured for headless environments
|
39 |
-
|
40 |
-
Pillow used for post-processing and visualization checks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
CHANGED
@@ -18,6 +18,13 @@ output/preview/
|
|
18 |
logs/
|
19 |
*.backup
|
20 |
*.json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
# Test files
|
23 |
test_*.py
|
@@ -33,3 +40,5 @@ input/*.pdf
|
|
33 |
# Temporary documents
|
34 |
Tmplf6xnkgr*
|
35 |
.env
|
|
|
|
|
|
18 |
logs/
|
19 |
*.backup
|
20 |
*.json
|
21 |
+
*.jpg
|
22 |
+
*.png
|
23 |
+
*.txt
|
24 |
+
*.csv
|
25 |
+
*.log
|
26 |
+
*.zip
|
27 |
+
*.tar
|
28 |
|
29 |
# Test files
|
30 |
test_*.py
|
|
|
40 |
# Temporary documents
|
41 |
Tmplf6xnkgr*
|
42 |
.env
|
43 |
+
output/pipeline_test/americae-retectio/americae-retectio_comparison.jpg
|
44 |
+
docs/environment_variables.md
|
docs/config_refactoring.md
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Configuration Refactoring
|
2 |
+
|
3 |
+
## Overview
|
4 |
+
This document outlines the changes made to centralize configuration parameters and reduce technical debt in the OCR processing system.
|
5 |
+
|
6 |
+
## Key Changes
|
7 |
+
|
8 |
+
### Centralized Configuration
|
9 |
+
All previously hard-coded parameters have been moved to `config.py` and organized by functional category:
|
10 |
+
|
11 |
+
- **PDF_SETTINGS**: Parameters for PDF processing
|
12 |
+
- **SEGMENTATION_SETTINGS**: Image segmentation configuration
|
13 |
+
- **CACHE_SETTINGS**: Cache TTL and capacity settings
|
14 |
+
- **TEXT_REPAIR_SETTINGS**: Duplication detection and repair thresholds
|
15 |
+
|
16 |
+
### Environment Variable Support
|
17 |
+
All configuration parameters can now be overridden via environment variables:
|
18 |
+
|
19 |
+
```bash
|
20 |
+
# Example: Override PDF DPI
|
21 |
+
export PDF_DEFAULT_DPI=200
|
22 |
+
|
23 |
+
# Example: Increase cache size
|
24 |
+
export CACHE_MAX_ENTRIES=50
|
25 |
+
```
|
26 |
+
|
27 |
+
### Import Strategy
|
28 |
+
To prevent circular dependencies, configuration is imported at function level where needed:
|
29 |
+
|
30 |
+
```python
|
31 |
+
def process_image():
|
32 |
+
from config import SEGMENTATION_SETTINGS
|
33 |
+
# Function implementation using settings
|
34 |
+
```
|
35 |
+
|
36 |
+
## Benefits
|
37 |
+
|
38 |
+
- **Maintainability**: Settings are centralized and documented
|
39 |
+
- **Flexibility**: Configuration can be adjusted without code changes
|
40 |
+
- **Consistency**: Standardized approach to configuration across modules
|
41 |
+
- **Traceability**: Clear overview of all configurable parameters
|
42 |
+
|
43 |
+
## Future Improvements
|
44 |
+
|
45 |
+
- Add configuration schema validation
|
46 |
+
- Support for configuration profiles (dev/test/prod)
|
47 |
+
- Add detailed documentation for each parameter
|
improvements.md
ADDED
@@ -0,0 +1,587 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Historical OCR Application Improvements
|
2 |
+
|
3 |
+
Based on a thorough code review of the Historical OCR application, I've identified several areas for improvement to reduce technical debt and enhance the application's functionality, maintainability, and performance.
|
4 |
+
|
5 |
+
## 1. Code Organization and Structure
|
6 |
+
|
7 |
+
### 1.1 Modularize Large Functions
|
8 |
+
Several functions in the codebase are excessively long and handle multiple responsibilities:
|
9 |
+
|
10 |
+
- **Issue**: `process_file()` in ocr_processing.py is over 400 lines and handles file validation, preprocessing, OCR processing, and result formatting.
|
11 |
+
- **Solution**: Break down into smaller, focused functions:
|
12 |
+
```python
|
13 |
+
def process_file(uploaded_file, options):
|
14 |
+
# Validate and prepare file
|
15 |
+
file_info = validate_and_prepare_file(uploaded_file)
|
16 |
+
|
17 |
+
# Apply preprocessing based on document type
|
18 |
+
preprocessed_file = preprocess_document(file_info, options)
|
19 |
+
|
20 |
+
# Perform OCR processing
|
21 |
+
ocr_result = perform_ocr(preprocessed_file, options)
|
22 |
+
|
23 |
+
# Format and enhance results
|
24 |
+
return format_and_enhance_results(ocr_result, file_info)
|
25 |
+
```
|
26 |
+
|
27 |
+
### 1.2 Consistent Error Handling
|
28 |
+
Error handling approaches vary across modules:
|
29 |
+
|
30 |
+
- **Issue**: Some functions use try/except blocks with detailed logging, while others return error dictionaries or raise exceptions.
|
31 |
+
- **Solution**: Implement a consistent error handling strategy:
|
32 |
+
```python
|
33 |
+
class OCRError(Exception):
|
34 |
+
def __init__(self, message, error_code=None, details=None):
|
35 |
+
self.message = message
|
36 |
+
self.error_code = error_code
|
37 |
+
self.details = details
|
38 |
+
super().__init__(self.message)
|
39 |
+
|
40 |
+
def handle_error(func):
|
41 |
+
@functools.wraps(func)
|
42 |
+
def wrapper(*args, **kwargs):
|
43 |
+
try:
|
44 |
+
return func(*args, **kwargs)
|
45 |
+
except OCRError as e:
|
46 |
+
logger.error(f"OCR Error: {e.message} (Code: {e.error_code})")
|
47 |
+
return {"error": e.message, "error_code": e.error_code, "details": e.details}
|
48 |
+
except Exception as e:
|
49 |
+
logger.error(f"Unexpected error: {str(e)}")
|
50 |
+
return {"error": "An unexpected error occurred", "details": str(e)}
|
51 |
+
return wrapper
|
52 |
+
```
|
53 |
+
|
54 |
+
## 2. API Integration and Performance
|
55 |
+
|
56 |
+
### 2.1 API Client Optimization
|
57 |
+
The Mistral API client initialization and usage can be improved:
|
58 |
+
|
59 |
+
- **Issue**: The client is initialized for each request and error handling is duplicated.
|
60 |
+
- **Solution**: Create a singleton API client with centralized error handling:
|
61 |
+
```python
|
62 |
+
class MistralClient:
|
63 |
+
_instance = None
|
64 |
+
|
65 |
+
@classmethod
|
66 |
+
def get_instance(cls, api_key=None):
|
67 |
+
if cls._instance is None:
|
68 |
+
cls._instance = cls(api_key)
|
69 |
+
return cls._instance
|
70 |
+
|
71 |
+
def __init__(self, api_key=None):
|
72 |
+
self.api_key = api_key or os.environ.get("MISTRAL_API_KEY", "")
|
73 |
+
self.client = Mistral(api_key=self.api_key)
|
74 |
+
|
75 |
+
def process_ocr(self, document, **kwargs):
|
76 |
+
try:
|
77 |
+
return self.client.ocr.process(document=document, **kwargs)
|
78 |
+
except Exception as e:
|
79 |
+
# Centralized error handling
|
80 |
+
return self._handle_api_error(e)
|
81 |
+
```
|
82 |
+
|
83 |
+
### 2.2 Caching Strategy
|
84 |
+
The current caching approach can be improved:
|
85 |
+
|
86 |
+
- **Issue**: Cache keys don't always account for all relevant parameters, and TTL is fixed at 24 hours.
|
87 |
+
- **Solution**: Implement a more sophisticated caching strategy:
|
88 |
+
```python
|
89 |
+
def generate_cache_key(file_content, options):
|
90 |
+
# Create a comprehensive hash of all relevant parameters
|
91 |
+
options_str = json.dumps(options, sort_keys=True)
|
92 |
+
content_hash = hashlib.md5(file_content).hexdigest()
|
93 |
+
return f"{content_hash}_{hashlib.md5(options_str.encode()).hexdigest()}"
|
94 |
+
|
95 |
+
# Adaptive TTL based on document type
|
96 |
+
def get_cache_ttl(document_type):
|
97 |
+
ttl_map = {
|
98 |
+
"handwritten": 48 * 3600, # 48 hours for handwritten docs
|
99 |
+
"newspaper": 24 * 3600, # 24 hours for newspapers
|
100 |
+
"standard": 12 * 3600 # 12 hours for standard docs
|
101 |
+
}
|
102 |
+
return ttl_map.get(document_type, 24 * 3600)
|
103 |
+
```
|
104 |
+
|
105 |
+
## 3. State Management
|
106 |
+
|
107 |
+
### 3.1 Streamlit Session State
|
108 |
+
The application uses a complex state management approach:
|
109 |
+
|
110 |
+
- **Issue**: Many session state variables with unclear relationships and reset logic.
|
111 |
+
- **Solution**: Implement a more structured state management approach:
|
112 |
+
```python
|
113 |
+
class DocumentState:
|
114 |
+
def __init__(self):
|
115 |
+
self.document = None
|
116 |
+
self.original_bytes = None
|
117 |
+
self.name = None
|
118 |
+
self.mime_type = None
|
119 |
+
self.is_sample = False
|
120 |
+
self.processed = False
|
121 |
+
self.temp_files = []
|
122 |
+
|
123 |
+
def reset(self):
|
124 |
+
# Clean up temp files
|
125 |
+
for temp_file in self.temp_files:
|
126 |
+
if os.path.exists(temp_file):
|
127 |
+
os.unlink(temp_file)
|
128 |
+
|
129 |
+
# Reset state
|
130 |
+
self.__init__()
|
131 |
+
|
132 |
+
# Initialize in session state
|
133 |
+
if 'document_state' not in st.session_state:
|
134 |
+
st.session_state.document_state = DocumentState()
|
135 |
+
```
|
136 |
+
|
137 |
+
### 3.2 Result History Management
|
138 |
+
The current approach to managing result history can be improved:
|
139 |
+
|
140 |
+
- **Issue**: Results are stored directly in session state with limited management.
|
141 |
+
- **Solution**: Create a dedicated class for result history:
|
142 |
+
```python
|
143 |
+
class ResultHistory:
|
144 |
+
def __init__(self, max_results=20):
|
145 |
+
self.results = []
|
146 |
+
self.max_results = max_results
|
147 |
+
|
148 |
+
def add_result(self, result):
|
149 |
+
# Add timestamp and ensure result is serializable
|
150 |
+
result = self._prepare_result(result)
|
151 |
+
self.results.insert(0, result)
|
152 |
+
|
153 |
+
# Trim to max size
|
154 |
+
if len(self.results) > self.max_results:
|
155 |
+
self.results = self.results[:self.max_results]
|
156 |
+
|
157 |
+
def _prepare_result(self, result):
|
158 |
+
# Add timestamp and ensure result is serializable
|
159 |
+
result = result.copy()
|
160 |
+
result['timestamp'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
161 |
+
|
162 |
+
# Ensure result is serializable
|
163 |
+
return json.loads(json.dumps(result, default=str))
|
164 |
+
```
|
165 |
+
|
166 |
+
## 4. Image Processing Pipeline
|
167 |
+
|
168 |
+
### 4.1 Preprocessing Configuration
|
169 |
+
The preprocessing configuration can be improved:
|
170 |
+
|
171 |
+
- **Issue**: Preprocessing options are scattered across different parts of the code.
|
172 |
+
- **Solution**: Create a centralized preprocessing configuration:
|
173 |
+
```python
|
174 |
+
PREPROCESSING_CONFIGS = {
|
175 |
+
"standard": {
|
176 |
+
"grayscale": True,
|
177 |
+
"denoise": True,
|
178 |
+
"contrast": 5,
|
179 |
+
"deskew": True
|
180 |
+
},
|
181 |
+
"handwritten": {
|
182 |
+
"grayscale": True,
|
183 |
+
"denoise": True,
|
184 |
+
"contrast": 10,
|
185 |
+
"deskew": True,
|
186 |
+
"adaptive_threshold": {
|
187 |
+
"block_size": 21,
|
188 |
+
"constant": 5
|
189 |
+
}
|
190 |
+
},
|
191 |
+
"newspaper": {
|
192 |
+
"grayscale": True,
|
193 |
+
"denoise": True,
|
194 |
+
"contrast": 5,
|
195 |
+
"deskew": True,
|
196 |
+
"column_detection": True
|
197 |
+
}
|
198 |
+
}
|
199 |
+
```
|
200 |
+
|
201 |
+
### 4.2 Image Segmentation
|
202 |
+
The image segmentation approach can be improved:
|
203 |
+
|
204 |
+
- **Issue**: Segmentation is optional and not well-integrated with the preprocessing pipeline.
|
205 |
+
- **Solution**: Make segmentation a standard part of the preprocessing pipeline for certain document types:
|
206 |
+
```python
|
207 |
+
def preprocess_document(file_info, options):
|
208 |
+
# Apply basic preprocessing
|
209 |
+
preprocessed_file = apply_basic_preprocessing(file_info, options)
|
210 |
+
|
211 |
+
# Apply segmentation for specific document types
|
212 |
+
if options["document_type"] in ["newspaper", "book", "multi_column"]:
|
213 |
+
return apply_segmentation(preprocessed_file, options)
|
214 |
+
|
215 |
+
return preprocessed_file
|
216 |
+
```
|
217 |
+
|
218 |
+
## 5. User Experience Enhancements
|
219 |
+
|
220 |
+
### 5.1 Progressive Loading
|
221 |
+
Improve the user experience during processing:
|
222 |
+
|
223 |
+
- **Issue**: The UI can appear frozen during long-running operations.
|
224 |
+
- **Solution**: Implement progressive loading and feedback:
|
225 |
+
```python
|
226 |
+
def process_with_feedback(file, options, progress_callback):
|
227 |
+
# Update progress at each step
|
228 |
+
progress_callback(10, "Validating document...")
|
229 |
+
file_info = validate_and_prepare_file(file)
|
230 |
+
|
231 |
+
progress_callback(30, "Preprocessing document...")
|
232 |
+
preprocessed_file = preprocess_document(file_info, options)
|
233 |
+
|
234 |
+
progress_callback(50, "Performing OCR...")
|
235 |
+
ocr_result = perform_ocr(preprocessed_file, options)
|
236 |
+
|
237 |
+
progress_callback(80, "Enhancing results...")
|
238 |
+
final_result = format_and_enhance_results(ocr_result, file_info)
|
239 |
+
|
240 |
+
progress_callback(100, "Complete!")
|
241 |
+
return final_result
|
242 |
+
```
|
243 |
+
|
244 |
+
### 5.2 Result Visualization
|
245 |
+
Enhance the visualization of OCR results:
|
246 |
+
|
247 |
+
- **Issue**: Results are displayed in a basic format with limited visualization.
|
248 |
+
- **Solution**: Implement enhanced visualization options:
|
249 |
+
```python
|
250 |
+
def display_enhanced_results(result):
|
251 |
+
# Create tabs for different views
|
252 |
+
tabs = st.tabs(["Text", "Annotated", "Side-by-Side", "JSON"])
|
253 |
+
|
254 |
+
with tabs[0]:
|
255 |
+
# Display formatted text
|
256 |
+
st.markdown(format_ocr_text(result["ocr_contents"]["raw_text"]))
|
257 |
+
|
258 |
+
with tabs[1]:
|
259 |
+
# Display annotated image with bounding boxes
|
260 |
+
display_annotated_image(result)
|
261 |
+
|
262 |
+
with tabs[2]:
|
263 |
+
# Display side-by-side comparison
|
264 |
+
col1, col2 = st.columns(2)
|
265 |
+
with col1:
|
266 |
+
st.image(result["original_image"])
|
267 |
+
with col2:
|
268 |
+
st.markdown(format_ocr_text(result["ocr_contents"]["raw_text"]))
|
269 |
+
|
270 |
+
with tabs[3]:
|
271 |
+
# Display raw JSON
|
272 |
+
st.json(result)
|
273 |
+
```
|
274 |
+
|
275 |
+
## 6. Testing and Reliability
|
276 |
+
|
277 |
+
### 6.1 Automated Testing
|
278 |
+
Implement comprehensive testing:
|
279 |
+
|
280 |
+
- **Issue**: Limited or no automated testing.
|
281 |
+
- **Solution**: Implement unit and integration tests:
|
282 |
+
```python
|
283 |
+
# Unit test for preprocessing
|
284 |
+
def test_preprocess_image():
|
285 |
+
# Test with various document types
|
286 |
+
for doc_type in ["standard", "handwritten", "newspaper"]:
|
287 |
+
# Load test image
|
288 |
+
with open(f"test_data/{doc_type}_sample.jpg", "rb") as f:
|
289 |
+
image_bytes = f.read()
|
290 |
+
|
291 |
+
# Apply preprocessing
|
292 |
+
options = {"document_type": doc_type, "grayscale": True, "denoise": True}
|
293 |
+
result = preprocess_image(image_bytes, options)
|
294 |
+
|
295 |
+
# Assert result is not None and different from original
|
296 |
+
assert result is not None
|
297 |
+
assert result != image_bytes
|
298 |
+
```
|
299 |
+
|
300 |
+
### 6.2 Error Recovery
|
301 |
+
Implement better error recovery mechanisms:
|
302 |
+
|
303 |
+
- **Issue**: Errors in one part of the pipeline can cause the entire process to fail.
|
304 |
+
- **Solution**: Implement graceful degradation:
|
305 |
+
```python
|
306 |
+
def process_with_fallbacks(file, options):
|
307 |
+
try:
|
308 |
+
# Try full processing pipeline
|
309 |
+
return full_processing_pipeline(file, options)
|
310 |
+
except OCRError as e:
|
311 |
+
logger.warning(f"Full pipeline failed: {e.message}. Trying simplified pipeline.")
|
312 |
+
try:
|
313 |
+
# Try simplified pipeline
|
314 |
+
return simplified_processing_pipeline(file, options)
|
315 |
+
except Exception as e2:
|
316 |
+
logger.error(f"Simplified pipeline failed: {str(e2)}. Falling back to basic OCR.")
|
317 |
+
# Fall back to basic OCR
|
318 |
+
return basic_ocr_only(file)
|
319 |
+
```
|
320 |
+
|
321 |
+
## 7. Documentation and Maintainability
|
322 |
+
|
323 |
+
### 7.1 Code Documentation
|
324 |
+
Improve code documentation:
|
325 |
+
|
326 |
+
- **Issue**: Inconsistent documentation across modules.
|
327 |
+
- **Solution**: Implement consistent docstring format and add module-level documentation:
|
328 |
+
```python
|
329 |
+
"""
|
330 |
+
OCR Processing Module
|
331 |
+
|
332 |
+
This module handles the core OCR processing functionality, including:
|
333 |
+
- File validation and preparation
|
334 |
+
- Image preprocessing
|
335 |
+
- OCR processing with Mistral AI
|
336 |
+
- Result formatting and enhancement
|
337 |
+
|
338 |
+
The main entry point is the `process_file` function.
|
339 |
+
"""
|
340 |
+
|
341 |
+
def process_file(file, options):
|
342 |
+
"""
|
343 |
+
Process a file with OCR.
|
344 |
+
|
345 |
+
Args:
|
346 |
+
file: The file to process (UploadedFile or bytes)
|
347 |
+
options: Dictionary of processing options
|
348 |
+
- document_type: Type of document (standard, handwritten, etc.)
|
349 |
+
- preprocessing: Dictionary of preprocessing options
|
350 |
+
- use_vision: Whether to use vision model
|
351 |
+
|
352 |
+
Returns:
|
353 |
+
Dictionary containing OCR results and metadata
|
354 |
+
|
355 |
+
Raises:
|
356 |
+
OCRError: If OCR processing fails
|
357 |
+
"""
|
358 |
+
# Implementation
|
359 |
+
```
|
360 |
+
|
361 |
+
### 7.2 Configuration Management
|
362 |
+
Improve configuration management:
|
363 |
+
|
364 |
+
- **Issue**: Configuration is scattered across multiple files.
|
365 |
+
- **Solution**: Implement a centralized configuration system:
|
366 |
+
```python
|
367 |
+
"""
|
368 |
+
Configuration Module
|
369 |
+
|
370 |
+
This module provides a centralized configuration system for the application.
|
371 |
+
"""
|
372 |
+
|
373 |
+
import os
|
374 |
+
import yaml
|
375 |
+
from pathlib import Path
|
376 |
+
|
377 |
+
class Config:
|
378 |
+
_instance = None
|
379 |
+
|
380 |
+
@classmethod
|
381 |
+
def get_instance(cls):
|
382 |
+
if cls._instance is None:
|
383 |
+
cls._instance = cls()
|
384 |
+
return cls._instance
|
385 |
+
|
386 |
+
def __init__(self):
|
387 |
+
self.config = {}
|
388 |
+
self.load_config()
|
389 |
+
|
390 |
+
def load_config(self):
|
391 |
+
# Load from config file
|
392 |
+
config_path = Path(__file__).parent / "config.yaml"
|
393 |
+
if config_path.exists():
|
394 |
+
with open(config_path, "r") as f:
|
395 |
+
self.config = yaml.safe_load(f)
|
396 |
+
|
397 |
+
# Override with environment variables
|
398 |
+
for key, value in os.environ.items():
|
399 |
+
if key.startswith("OCR_"):
|
400 |
+
config_key = key[4:].lower()
|
401 |
+
self.config[config_key] = value
|
402 |
+
|
403 |
+
def get(self, key, default=None):
|
404 |
+
return self.config.get(key, default)
|
405 |
+
```
|
406 |
+
|
407 |
+
## 8. Security Enhancements
|
408 |
+
|
409 |
+
### 8.1 API Key Management
|
410 |
+
Improve API key management:
|
411 |
+
|
412 |
+
- **Issue**: API keys are stored in environment variables with limited validation.
|
413 |
+
- **Solution**: Implement secure API key management:
|
414 |
+
```python
|
415 |
+
def get_api_key():
|
416 |
+
# Try to get from secure storage first
|
417 |
+
api_key = get_from_secure_storage("mistral_api_key")
|
418 |
+
|
419 |
+
# Fall back to environment variable
|
420 |
+
if not api_key:
|
421 |
+
api_key = os.environ.get("MISTRAL_API_KEY", "")
|
422 |
+
|
423 |
+
# Validate key format
|
424 |
+
if api_key and not re.match(r'^[A-Za-z0-9_-]{30,}$', api_key):
|
425 |
+
logger.warning("API key format appears invalid")
|
426 |
+
|
427 |
+
return api_key
|
428 |
+
```
|
429 |
+
|
430 |
+
### 8.2 Input Validation
|
431 |
+
Improve input validation:
|
432 |
+
|
433 |
+
- **Issue**: Limited validation of user inputs.
|
434 |
+
- **Solution**: Implement comprehensive input validation:
|
435 |
+
```python
|
436 |
+
def validate_file(file):
|
437 |
+
# Check file size
|
438 |
+
if len(file.getvalue()) > MAX_FILE_SIZE:
|
439 |
+
raise OCRError("File too large", "FILE_TOO_LARGE")
|
440 |
+
|
441 |
+
# Check file type
|
442 |
+
file_type = get_file_type(file)
|
443 |
+
if file_type not in ALLOWED_FILE_TYPES:
|
444 |
+
raise OCRError(f"Unsupported file type: {file_type}", "UNSUPPORTED_FILE_TYPE")
|
445 |
+
|
446 |
+
# Check for malicious content
|
447 |
+
if is_potentially_malicious(file):
|
448 |
+
raise OCRError("File appears to be malicious", "SECURITY_RISK")
|
449 |
+
|
450 |
+
return file_type
|
451 |
+
```
|
452 |
+
|
453 |
+
## 9. Performance Optimizations
|
454 |
+
|
455 |
+
### 9.1 Parallel Processing
|
456 |
+
Implement parallel processing for multi-page documents:
|
457 |
+
|
458 |
+
- **Issue**: Pages are processed sequentially, which can be slow for large documents.
|
459 |
+
- **Solution**: Implement parallel processing:
|
460 |
+
```python
|
461 |
+
def process_pdf_pages(pdf_path, options):
|
462 |
+
# Extract pages
|
463 |
+
pages = extract_pdf_pages(pdf_path)
|
464 |
+
|
465 |
+
# Process pages in parallel
|
466 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
|
467 |
+
future_to_page = {executor.submit(process_page, page, options): i
|
468 |
+
for i, page in enumerate(pages)}
|
469 |
+
|
470 |
+
results = []
|
471 |
+
for future in concurrent.futures.as_completed(future_to_page):
|
472 |
+
page_idx = future_to_page[future]
|
473 |
+
try:
|
474 |
+
result = future.result()
|
475 |
+
results.append((page_idx, result))
|
476 |
+
except Exception as e:
|
477 |
+
logger.error(f"Error processing page {page_idx}: {str(e)}")
|
478 |
+
|
479 |
+
# Sort results by page index
|
480 |
+
results.sort(key=lambda x: x[0])
|
481 |
+
|
482 |
+
# Combine results
|
483 |
+
return combine_page_results([r[1] for r in results])
|
484 |
+
```
|
485 |
+
|
486 |
+
### 9.2 Resource Management
|
487 |
+
Improve resource management:
|
488 |
+
|
489 |
+
- **Issue**: Temporary files are not always cleaned up properly.
|
490 |
+
- **Solution**: Implement better resource management:
|
491 |
+
```python
|
492 |
+
class TempFileManager:
|
493 |
+
def __init__(self):
|
494 |
+
self.temp_files = []
|
495 |
+
|
496 |
+
def create_temp_file(self, content, suffix=".tmp"):
|
497 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
|
498 |
+
tmp.write(content)
|
499 |
+
self.temp_files.append(tmp.name)
|
500 |
+
return tmp.name
|
501 |
+
|
502 |
+
def cleanup(self):
|
503 |
+
for temp_file in self.temp_files:
|
504 |
+
try:
|
505 |
+
if os.path.exists(temp_file):
|
506 |
+
os.unlink(temp_file)
|
507 |
+
except Exception as e:
|
508 |
+
logger.warning(f"Failed to remove temp file {temp_file}: {str(e)}")
|
509 |
+
self.temp_files = []
|
510 |
+
|
511 |
+
def __enter__(self):
|
512 |
+
return self
|
513 |
+
|
514 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
515 |
+
self.cleanup()
|
516 |
+
```
|
517 |
+
|
518 |
+
## 10. Extensibility
|
519 |
+
|
520 |
+
### 10.1 Plugin System
|
521 |
+
Implement a plugin system for extensibility:
|
522 |
+
|
523 |
+
- **Issue**: Adding new document types or processing methods requires code changes.
|
524 |
+
- **Solution**: Implement a plugin system:
|
525 |
+
```python
|
526 |
+
class OCRPlugin:
|
527 |
+
def __init__(self, name, description):
|
528 |
+
self.name = name
|
529 |
+
self.description = description
|
530 |
+
|
531 |
+
def can_handle(self, file_info):
|
532 |
+
"""Return True if this plugin can handle the file"""
|
533 |
+
raise NotImplementedError
|
534 |
+
|
535 |
+
def process(self, file_info, options):
|
536 |
+
"""Process the file and return results"""
|
537 |
+
raise NotImplementedError
|
538 |
+
|
539 |
+
# Example plugin
|
540 |
+
class HandwrittenDocumentPlugin(OCRPlugin):
|
541 |
+
def __init__(self):
|
542 |
+
super().__init__("handwritten", "Handwritten document processor")
|
543 |
+
|
544 |
+
def can_handle(self, file_info):
|
545 |
+
# Check if this is a handwritten document
|
546 |
+
return file_info.get("document_type") == "handwritten"
|
547 |
+
|
548 |
+
def process(self, file_info, options):
|
549 |
+
# Specialized processing for handwritten documents
|
550 |
+
# ...
|
551 |
+
```
|
552 |
+
|
553 |
+
### 10.2 API Abstraction
|
554 |
+
Create an abstraction layer for the OCR API:
|
555 |
+
|
556 |
+
- **Issue**: The application is tightly coupled to the Mistral AI API.
|
557 |
+
- **Solution**: Implement an abstraction layer:
|
558 |
+
```python
|
559 |
+
class OCRProvider:
|
560 |
+
def process_image(self, image_path, options):
|
561 |
+
"""Process an image and return OCR results"""
|
562 |
+
raise NotImplementedError
|
563 |
+
|
564 |
+
def process_pdf(self, pdf_path, options):
|
565 |
+
"""Process a PDF and return OCR results"""
|
566 |
+
raise NotImplementedError
|
567 |
+
|
568 |
+
class MistralOCRProvider(OCRProvider):
|
569 |
+
def __init__(self, api_key=None):
|
570 |
+
self.client = MistralClient.get_instance(api_key)
|
571 |
+
|
572 |
+
def process_image(self, image_path, options):
|
573 |
+
# Implementation using Mistral API
|
574 |
+
|
575 |
+
def process_pdf(self, pdf_path, options):
|
576 |
+
# Implementation using Mistral API
|
577 |
+
|
578 |
+
# Factory function to get the appropriate provider
|
579 |
+
def get_ocr_provider(provider_name="mistral"):
|
580 |
+
if provider_name == "mistral":
|
581 |
+
return MistralOCRProvider()
|
582 |
+
# Add more providers as needed
|
583 |
+
raise ValueError(f"Unknown OCR provider: {provider_name}")
|
584 |
+
```
|
585 |
+
|
586 |
+
## Implementation Priority
|
587 |
+
|
memory-bank/activeContext.md
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Active Context
|
2 |
+
|
3 |
+
## Current Work Focus
|
4 |
+
|
5 |
+
* Initializing the core Memory Bank files (`projectbrief.md`, `productContext.md`, `systemPatterns.md`, `techContext.md`, `activeContext.md`, `progress.md`) based on the existing project structure, defined `.clinerules`, and the global `memory-bank.md` specification.
|
6 |
+
|
7 |
+
## Recent Changes
|
8 |
+
|
9 |
+
* This is the initial population of the Memory Bank. No prior changes within this system.
|
10 |
+
|
11 |
+
## Next Steps
|
12 |
+
|
13 |
+
* Complete the creation of the initial Memory Bank files (specifically `progress.md`).
|
14 |
+
* Await further instructions from the user regarding the next development task for the `historical-ocr` project.
|
15 |
+
|
16 |
+
## Active Decisions, Patterns, and Preferences
|
17 |
+
|
18 |
+
* **Memory Bank Structure:** Adopting the 6-core-file structure defined in the global `memory-bank.md`.
|
19 |
+
* **Content Derivation:** Populating initial Memory Bank content by analyzing the existing file structure and `.clinerules`.
|
20 |
+
* **Iterative Improvement Workflow:** Implementing a post-task review step after completing tasks in Act Mode. This involves:
|
21 |
+
1. Completing the assigned task.
|
22 |
+
2. Presenting the result using `attempt_completion`.
|
23 |
+
3. Explicitly asking the user: "Are there any takeaways from this interaction that should be added to the project's `.clinerules`?"
|
24 |
+
4. If takeaways are provided, update the relevant `.clinerules` file(s).
|
25 |
+
5. Recursively update the Memory Bank (especially `activeContext.md` and `progress.md`) to reflect the new rule or pattern.
|
26 |
+
|
27 |
+
## Learnings and Project Insights
|
28 |
+
|
29 |
+
* The project emphasizes clear documentation and rule definition from the start (`.clinerules`).
|
30 |
+
* Maintaining synchronization between `.clinerules` and the Memory Bank is crucial for consistent development.
|
31 |
+
* The iterative feedback loop (post-task review) is a key requirement for evolving project standards.
|
memory-bank/productContext.md
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Product Context
|
2 |
+
|
3 |
+
## Why This Project Exists
|
4 |
+
|
5 |
+
Historical documents often contain invaluable information but are locked away in formats (scans, photos, complex PDFs) that are difficult to search, analyze, or integrate into digital research workflows. Standard OCR tools may struggle with the unique challenges presented by archival materials, such as varying print quality, handwriting, complex layouts, and archaic language.
|
6 |
+
|
7 |
+
## Problems It Solves
|
8 |
+
|
9 |
+
This application aims to bridge the gap between physical historical archives and modern digital research by:
|
10 |
+
|
11 |
+
* Providing high-accuracy text extraction from challenging historical documents.
|
12 |
+
* Structuring the extracted text and metadata in a usable format.
|
13 |
+
* Making advanced OCR capabilities accessible to researchers without requiring deep technical expertise.
|
14 |
+
* Optimizing documents specifically for OCR to improve accuracy.
|
15 |
+
|
16 |
+
## How It Should Work
|
17 |
+
|
18 |
+
The core workflow involves:
|
19 |
+
|
20 |
+
1. **Upload:** Users upload historical documents (images or PDFs) via a web interface.
|
21 |
+
2. **Preprocessing:** The application automatically applies image enhancement and optimization techniques tailored to historical materials.
|
22 |
+
3. **OCR:** Processed documents are sent to the Mistral AI OCR API for text extraction. Document type detection may inform specific OCR prompting.
|
23 |
+
4. **Structuring:** The raw OCR output is processed to extract structured information (e.g., paragraphs, headings, metadata) based on document type and potentially user instructions.
|
24 |
+
5. **Output:** Users can view the extracted text and download structured transcripts and analysis.
|
25 |
+
|
26 |
+
## User Experience Goals
|
27 |
+
|
28 |
+
* **Intuitive Interface:** A clean, straightforward Streamlit web application that is easy for researchers to use.
|
29 |
+
* **Clear Feedback:** Provide users with status updates during processing and clear presentation of results.
|
30 |
+
* **Flexibility:** Allow users some control over the process (e.g., contextual instructions) where appropriate.
|
31 |
+
* **Reliability:** Ensure consistent and accurate results.
|
memory-bank/progress.md
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Project Progress
|
2 |
+
|
3 |
+
## Current Status
|
4 |
+
|
5 |
+
* **Overall:** Initial project setup phase. Core application structure exists, and foundational documentation (Memory Bank, `.clinerules`) is being established.
|
6 |
+
* **Memory Bank:** The six core Memory Bank files have just been created with initial content derived from the project structure and rules.
|
7 |
+
|
8 |
+
## What Works
|
9 |
+
|
10 |
+
* The basic file and directory structure for a modular Python/Streamlit application is in place.
|
11 |
+
* Project rules (`.clinerules`) defining the brief, API usage, simplicity principle, and technical debt priorities exist.
|
12 |
+
* The Memory Bank system is now initialized.
|
13 |
+
|
14 |
+
## What's Left to Build / Next Steps
|
15 |
+
|
16 |
+
* Implement the actual functionality described in the project brief (file upload, preprocessing, OCR integration, structuring, UI interactions).
|
17 |
+
* Address the technical debt items listed below.
|
18 |
+
* Refine and expand Memory Bank documentation as development progresses.
|
19 |
+
* Specific next development tasks are pending user direction.
|
20 |
+
|
21 |
+
## Known Issues / Technical Debt
|
22 |
+
|
23 |
+
The following technical debt items have been identified in `.clinerules/technical-debt.md` and should be addressed during development:
|
24 |
+
|
25 |
+
1. **Modularize large functions:** Break down functions exceeding 100 lines into smaller, focused units.
|
26 |
+
2. **Consistent Error Handling:** Implement a uniform error handling strategy across all modules.
|
27 |
+
3. **Preprocessing Pipeline Improvement:** Enhance the preprocessing steps to better handle diverse historical document types.
|
28 |
+
4. **Image Segmentation Enhancement:** Improve the current approach for identifying text regions.
|
29 |
+
5. **Documentation:** Create comprehensive documentation (docstrings, comments) for public functions and APIs.
|
30 |
+
|
31 |
+
## Evolution of Project Decisions
|
32 |
+
|
33 |
+
* **[YYYY-MM-DD]:** Initialized Memory Bank structure based on global rules.
|
34 |
+
* **[YYYY-MM-DD]:** Adopted a post-task review workflow to iteratively update `.clinerules` and the Memory Bank.
|
.clinerules/projectBrief.md → memory-bank/project-brief.md
RENAMED
@@ -1,21 +1,19 @@
|
|
1 |
-
#
|
2 |
|
3 |
Historical OCR is an advanced optical character recognition (OCR) application designed to support historical research. It leverages Mistral AI's OCR models alongside image preprocessing pipelines optimized for archival material.
|
4 |
|
5 |
-
High-Level Overview
|
6 |
|
7 |
Building a Streamlit-based web application to process historical documents (images or PDFs), optimize them for OCR using advanced preprocessing techniques, and extract structured text and metadata through Mistral's large language models.
|
8 |
|
9 |
-
Core Requirements and Goals
|
10 |
|
11 |
-
Upload and preprocess historical documents
|
12 |
|
13 |
-
|
14 |
|
15 |
-
|
16 |
|
17 |
-
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
Example: "Building a Streamlit web app for OCR transcription and structured extraction from historical documents using Mistral AI."
|
|
|
1 |
+
# Project Brief
|
2 |
|
3 |
Historical OCR is an advanced optical character recognition (OCR) application designed to support historical research. It leverages Mistral AI's OCR models alongside image preprocessing pipelines optimized for archival material.
|
4 |
|
5 |
+
## High-Level Overview
|
6 |
|
7 |
Building a Streamlit-based web application to process historical documents (images or PDFs), optimize them for OCR using advanced preprocessing techniques, and extract structured text and metadata through Mistral's large language models.
|
8 |
|
9 |
+
## Core Requirements and Goals
|
10 |
|
11 |
+
* Upload and preprocess historical documents
|
12 |
|
13 |
+
* Apply tailored OCR prompting and structured output based on document type
|
14 |
|
15 |
+
* Support user-defined contextual instructions to refine output
|
16 |
|
17 |
+
* Provide downloadable structured transcripts and analysis
|
18 |
|
19 |
+
* Example: "Building a Streamlit web app for OCR transcription and structured extraction from historical documents using Mistral AI."
|
|
|
|
.clinerules/project-brief.md → memory-bank/projectbrief.md
RENAMED
File without changes
|
memory-bank/systemPatterns.md
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# System Patterns
|
2 |
+
|
3 |
+
## Architecture Overview
|
4 |
+
|
5 |
+
The application follows a modular Python structure, orchestrated by a main Streamlit application script (`app.py`). Key architectural components include:
|
6 |
+
|
7 |
+
* **Entry Point:** `app.py` likely initializes the Streamlit application and coordinates calls to other modules.
|
8 |
+
* **UI Layer:** Managed by Streamlit. Core UI elements are defined in `ui/layout.py` and potentially reusable components in `ui/ui_components.py` (or the root `ui_components.py`). Custom styling is applied via `ui/custom.css`.
|
9 |
+
* **Processing Modules:** Functionality is separated into distinct Python modules:
|
10 |
+
* `preprocessing.py`: Handles image optimization and preparation for OCR.
|
11 |
+
* `image_segmentation.py`: Deals with identifying regions of interest within documents.
|
12 |
+
* `ocr_processing.py`: Manages the interaction with the OCR engine (Mistral API).
|
13 |
+
* `structured_ocr.py`: Focuses on interpreting raw OCR output and structuring it.
|
14 |
+
* `language_detection.py`: Detects the language of the document content.
|
15 |
+
* `letterhead_handler.py`: Specific logic for dealing with letterheads.
|
16 |
+
* `pdf_ocr.py`: Handles OCR specific to PDF inputs (likely coordinating other modules).
|
17 |
+
* `process_file.py`: A potential high-level orchestrator for the entire file processing pipeline.
|
18 |
+
* **Configuration:** `config.py` likely holds application settings, potentially including API keys or processing parameters. `constants.py` holds fixed values used across the application.
|
19 |
+
* **Utilities:** Common functions are grouped in the `utils/` directory, further categorized (e.g., `image_utils.py`, `text_utils.py`, `file_utils.py`).
|
20 |
+
* **Error Handling:** A dedicated `error_handler.py` suggests a centralized approach to managing exceptions.
|
21 |
+
|
22 |
+
## Key Technical Decisions & Patterns
|
23 |
+
|
24 |
+
* **Modularity:** Code is organized into feature-specific modules, promoting separation of concerns.
|
25 |
+
* **External API Integration:** Relies on the Mistral AI OCR API for core text extraction (`.clinerules/hocr-basics-api.md`). API interaction logic is likely within `ocr_processing.py` or related utilities.
|
26 |
+
* **Streamlit Framework:** Leverages Streamlit for the web interface, using standard components (`.clinerules/hocr-basics-api.md`). State management likely uses `st.session_state`.
|
27 |
+
* **Content Purity:** Adheres to the principle of separating data from presentation markup (`.clinerules/principle-of-simplicity.md`). Presentation logic should reside primarily in the UI layer.
|
28 |
+
* **Configuration Management:** Centralized configuration likely managed through `config.py`.
|
29 |
+
|
30 |
+
## Component Relationships (Conceptual)
|
31 |
+
|
32 |
+
```mermaid
|
33 |
+
graph TD
|
34 |
+
A[User via Streamlit UI] --> B(app.py);
|
35 |
+
B --> C{process_file.py?};
|
36 |
+
C --> D[preprocessing.py];
|
37 |
+
C --> E[image_segmentation.py];
|
38 |
+
C --> F[language_detection.py];
|
39 |
+
C --> G[pdf_ocr.py / ocr_processing.py];
|
40 |
+
G -- Mistral API --> H((External Mistral OCR));
|
41 |
+
H -- OCR Result --> G;
|
42 |
+
G --> I[structured_ocr.py];
|
43 |
+
I --> J[Output Generation];
|
44 |
+
J --> A;
|
45 |
+
|
46 |
+
subgraph Modules
|
47 |
+
D; E; F; G; I; J;
|
48 |
+
end
|
49 |
+
|
50 |
+
subgraph Configuration & Utils
|
51 |
+
K[config.py];
|
52 |
+
L[constants.py];
|
53 |
+
M[utils/];
|
54 |
+
N[error_handler.py];
|
55 |
+
end
|
56 |
+
|
57 |
+
B --> K; B --> L; B --> M; B --> N;
|
58 |
+
Modules --> K; Modules --> L; Modules --> M; Modules --> N;
|
59 |
+
```
|
60 |
+
|
61 |
+
## Critical Implementation Paths
|
62 |
+
|
63 |
+
* The end-to-end flow from file upload (`st.file_uploader`) through preprocessing, OCR API call, structuring, and displaying results (`st.markdown`, `st.download_button`).
|
64 |
+
* Handling different input file types (Images vs. PDFs).
|
65 |
+
* Integration and error handling for the Mistral API calls.
|
66 |
+
* Implementation of specific preprocessing steps relevant to historical documents.
|
memory-bank/techContext.md
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Technical Context
|
2 |
+
|
3 |
+
## Technologies Used
|
4 |
+
|
5 |
+
* **Primary Language:** Python 3.x
|
6 |
+
* **Web Framework:** Streamlit
|
7 |
+
* **Core API:** Mistral AI OCR API (via HTTPS requests)
|
8 |
+
* **Potential Libraries:**
|
9 |
+
* `requests`: For making HTTP calls to the Mistral API.
|
10 |
+
* `streamlit`: For the web UI framework.
|
11 |
+
* `Pillow` (PIL Fork): For basic image loading and manipulation.
|
12 |
+
* `OpenCV` (`cv2`): Likely used for more advanced image preprocessing tasks (e.g., thresholding, noise reduction, deskewing).
|
13 |
+
* `python-dotenv`: Potentially used for managing environment variables like API keys (especially if `config.py` loads from a `.env` file).
|
14 |
+
* `PyMuPDF` or similar: If PDF processing involves direct text/image extraction from PDF structures beyond just sending to OCR.
|
15 |
+
|
16 |
+
*(Note: Specific libraries beyond Streamlit and requests need confirmation, e.g., by inspecting `requirements.txt` or import statements in the code).*
|
17 |
+
|
18 |
+
## Development Setup
|
19 |
+
|
20 |
+
* **Environment:** Standard Python environment (virtual environment recommended, e.g., `venv` or `conda`).
|
21 |
+
* **Dependencies:** Install required packages (likely via `pip install -r requirements.txt` if a requirements file exists).
|
22 |
+
* **API Keys:** Requires a Mistral AI API key, which needs to be configured securely (likely via environment variables loaded in `config.py`).
|
23 |
+
* **Running the App:** Typically run using `streamlit run app.py` from the project root directory.
|
24 |
+
|
25 |
+
## Technical Constraints
|
26 |
+
|
27 |
+
* **API Limits:** Subject to Mistral AI API usage limits, rate limits, and potential costs. Error handling for API responses (e.g., 429 Too Many Requests, 401 Unauthorized, 5xx Server Errors) is crucial.
|
28 |
+
* **Processing Time:** OCR and complex image preprocessing can be time-consuming, especially for large documents or high-resolution images. Streamlit's execution model needs to be considered for long-running tasks (e.g., using background processes or providing user feedback).
|
29 |
+
* **Resource Usage:** Image processing can be memory and CPU intensive.
|
30 |
+
|
31 |
+
## Tool Usage Patterns
|
32 |
+
|
33 |
+
* **Streamlit Components:** Primarily use core components as specified in `.clinerules/hocr-basics-api.md` (`st.file_uploader`, `st.selectbox`, `st.image`, `st.markdown`, `st.download_button`).
|
34 |
+
* **State Management:** Use `st.session_state` for managing user interactions and state across reruns.
|
35 |
+
* **API Interaction:** Follow standard practices for REST API calls (headers, JSON body, error checking) as defined in `.clinerules/hocr-basics-api.md`.
|
utils/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# OCR Utilities
|
2 |
+
|
3 |
+
This directory contains utility modules for the Historical OCR project.
|
4 |
+
|
5 |
+
## PDF OCR Processing
|
6 |
+
|
7 |
+
The `pdf_ocr.py` module provides specialized functionality for processing PDF documents with OCR.
|
8 |
+
|
9 |
+
### Features
|
10 |
+
|
11 |
+
- **Robust PDF-to-Image Conversion**: Converts PDF documents to images using optimized settings before OCR processing
|
12 |
+
- **Multi-Page Support**: Intelligently handles multi-page documents, allowing processing of specific pages or page ranges
|
13 |
+
- **Memory-Efficient Processing**: Processes PDFs in batches to prevent memory issues with large documents
|
14 |
+
- **Fallback Mechanism**: Falls back to structured_ocr's internal processing if direct conversion fails
|
15 |
+
- **Cleanup Management**: Automatically cleans up temporary files after processing
|
16 |
+
|
17 |
+
### Key Components
|
18 |
+
|
19 |
+
- **PDFOCR**: Main class for processing PDF files with OCR
|
20 |
+
- **PDFConversionResult**: Helper class that holds PDF conversion results and manages cleanup
|
21 |
+
|
22 |
+
### Basic Usage
|
23 |
+
|
24 |
+
```python
|
25 |
+
from utils.pdf_ocr import PDFOCR
|
26 |
+
|
27 |
+
# Initialize the processor
|
28 |
+
processor = PDFOCR()
|
29 |
+
|
30 |
+
# Process a PDF file (all pages, with vision model)
|
31 |
+
result = processor.process_pdf('document.pdf')
|
32 |
+
|
33 |
+
# Process a PDF file (specific pages, with vision model)
|
34 |
+
result = processor.process_pdf('document.pdf', custom_pages=[1, 3, 5])
|
35 |
+
|
36 |
+
# Process a PDF file (first N pages, without vision model)
|
37 |
+
result = processor.process_pdf('document.pdf', max_pages=3, use_vision=False)
|
38 |
+
|
39 |
+
# Process a PDF file with custom prompt
|
40 |
+
result = processor.process_pdf(
|
41 |
+
'document.pdf',
|
42 |
+
custom_prompt="This is a historical newspaper with multiple columns."
|
43 |
+
)
|
44 |
+
|
45 |
+
# Save results to JSON
|
46 |
+
output_path = processor.save_json_output('document.pdf', 'results.json')
|
47 |
+
```
|
48 |
+
|
49 |
+
### Command Line Usage
|
50 |
+
|
51 |
+
The module can also be used directly from the command line:
|
52 |
+
|
53 |
+
```bash
|
54 |
+
python utils/pdf_ocr.py document.pdf --output results.json
|
55 |
+
python utils/pdf_ocr.py document.pdf --max-pages 3
|
56 |
+
python utils/pdf_ocr.py document.pdf --pages 1,3,5
|
57 |
+
python utils/pdf_ocr.py document.pdf --prompt "This is a historical newspaper with multiple columns."
|
58 |
+
python utils/pdf_ocr.py document.pdf --no-vision
|
59 |
+
```
|
60 |
+
|
61 |
+
### How It Works
|
62 |
+
|
63 |
+
1. The module first attempts to convert the PDF to images using `pdf2image`
|
64 |
+
2. It processes the first page with the vision model (if requested) for detailed analysis
|
65 |
+
3. Additional pages are processed with the text model for efficiency
|
66 |
+
4. All text is combined into a single result with appropriate metadata
|
67 |
+
5. If direct conversion fails, it falls back to using `structured_ocr.py` for PDF processing
|
68 |
+
|
69 |
+
### Parameters
|
70 |
+
|
71 |
+
- **pdf_path**: Path to the PDF file to process
|
72 |
+
- **use_vision**: Whether to use vision model for improved analysis (default: True)
|
73 |
+
- **max_pages**: Maximum number of pages to process (default: all pages)
|
74 |
+
- **custom_pages**: Specific page numbers to process, 1-based indexing (e.g., [1, 3, 5])
|
75 |
+
- **custom_prompt**: Custom instructions for OCR processing
|