153 lines
5.3 KiB
Python
153 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
MacVisionOCR - macOS Vision Text Recognition via Swift script
|
|
|
|
Uses Apple's Vision Framework via Swift script (confirmed working).
|
|
|
|
Requirements:
|
|
- macOS 10.15+ (Catalina)
|
|
- Swift (built-in on macOS)
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Dict, List
|
|
|
|
# Path to Swift script
|
|
SWIFT_SCRIPT_PATH = Path(__file__).parent / "macvision_swift.swift"
|
|
|
|
|
|
class MacVisionOCR:
|
|
"""macOS Vision OCR wrapper using Swift script"""
|
|
|
|
def __init__(self, verbose: bool = False):
|
|
"""Initialize MacVisionOCR processor."""
|
|
self.verbose = verbose
|
|
|
|
if sys.platform != 'darwin':
|
|
raise RuntimeError("MacVisionOCR only works on macOS")
|
|
|
|
if self.verbose:
|
|
print("[INFO] MacVisionOCR initialized (via Swift)")
|
|
|
|
def _estimate_confidence(self, text: str) -> float:
|
|
"""Estimate OCR confidence from text quality."""
|
|
if not text:
|
|
return 0.0
|
|
|
|
printable = sum(1 for c in text if c.isprintable() or c in '\n\r\t')
|
|
length_factor = min(1.0, len(text) / 100)
|
|
quality = printable / max(len(text), 1)
|
|
|
|
confidence = 0.85 + (0.15 * quality * length_factor)
|
|
return round(min(confidence, 1.0), 4)
|
|
|
|
def extract(self, image_path: str) -> Dict:
|
|
"""Extract text from image using Mac Vision OCR via Swift."""
|
|
from time import time
|
|
|
|
if sys.platform != 'darwin':
|
|
return {
|
|
'text': '',
|
|
'confidence': 0.0,
|
|
'error': 'MacVisionOCR only works on macOS',
|
|
'processing_time_ms': 0
|
|
}
|
|
|
|
start_time = time()
|
|
|
|
try:
|
|
# Run Swift script with image path as argument
|
|
result = subprocess.run(
|
|
['swift', str(SWIFT_SCRIPT_PATH), image_path],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
return {
|
|
'text': '',
|
|
'confidence': 0.0,
|
|
'error': result.stderr.strip(),
|
|
'processing_time_ms': (time() - start_time) * 1000
|
|
}
|
|
|
|
# Parse Chinese output
|
|
lines = result.stdout.strip().split('\n')
|
|
text_lines = []
|
|
all_confidences = []
|
|
|
|
current_text = None
|
|
for line in lines:
|
|
if line.startswith('文本:'):
|
|
current_text = line[3:].strip()
|
|
elif line.startswith('置信度:') and current_text:
|
|
try:
|
|
conf_str = line[4:].strip()
|
|
# Swift returns confidence as percentage (0-100)
|
|
# Output format: "置信度:50.00%" -> need to divide by 100
|
|
numeric_part = conf_str.rstrip('%').strip()
|
|
confidence = float(numeric_part) / 100.0
|
|
text_lines.append(current_text)
|
|
all_confidences.append(confidence)
|
|
current_text = None
|
|
except (ValueError, IndexError):
|
|
pass
|
|
|
|
full_text = '\n'.join(text_lines)
|
|
processing_time = (time() - start_time) * 1000
|
|
avg_confidence = sum(all_confidences) / len(all_confidences) if all_confidences else 0.0
|
|
|
|
return {
|
|
'text': full_text.strip(),
|
|
'confidence': round(avg_confidence, 4),
|
|
'line_count': len(text_lines),
|
|
'processing_time_ms': round(processing_time, 2)
|
|
}
|
|
|
|
except subprocess.TimeoutExpired:
|
|
return {
|
|
'text': '',
|
|
'confidence': 0.0,
|
|
'error': 'MacVisionOCR timeout',
|
|
'processing_time_ms': 60000
|
|
}
|
|
except Exception as e:
|
|
processing_time = (time() - start_time) * 1000
|
|
return {
|
|
'text': '',
|
|
'confidence': 0.0,
|
|
'error': f"MacVision error: {str(e)}",
|
|
'processing_time_ms': round(processing_time, 2)
|
|
}
|
|
|
|
def batch_extract(self, image_paths: List[str]) -> List[Dict]:
|
|
"""Process multiple images"""
|
|
return [self.extract(path) for path in image_paths]
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import argparse
|
|
|
|
if sys.platform != 'darwin':
|
|
print("[ERROR] MacVisionOCR only works on macOS")
|
|
sys.exit(1)
|
|
|
|
if not SWIFT_SCRIPT_PATH.exists():
|
|
print(f"[ERROR] Swift script not found: {SWIFT_SCRIPT_PATH}")
|
|
sys.exit(1)
|
|
|
|
parser = argparse.ArgumentParser(description='macOS Vision OCR via Swift')
|
|
parser.add_argument('image', help='Image file to process')
|
|
parser.add_argument('--verbose', '-v', action='store_true')
|
|
args = parser.parse_args()
|
|
|
|
processor = MacVisionOCR(verbose=args.verbose)
|
|
result = processor.extract(args.image)
|
|
|
|
print(f"\nText:\n{result['text']}")
|
|
print(f"\nConfidence: {result['confidence']:.4f}")
|
|
print(f"Lines: {result.get('line_count', 0)}")
|
|
print(f"Time: {result.get('processing_time_ms', 0):.2f}ms") |