>
npx skills add https://github.com/claude-office-skills/skills --skill smart-ocr
This skill enables intelligent text extraction from images and scanned documents using PaddleOCR - a leading OCR engine supporting 100+ languages. Extract text from photos, screenshots, scanned PDFs, and handwritten documents with high accuracy.
Example prompts:
from paddleocr import PaddleOCR
# Initialize OCR engine
ocr = PaddleOCR(use_angle_cls=True, lang='en')
# Run OCR on image
result = ocr.ocr('image.png', cls=True)
# Result structure: [[box, (text, confidence)], ...]
for line in result[0]:
box = line[0] # [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
text = line[1][0] # Extracted text
conf = line[1][1] # Confidence score
print(f"{text} ({conf:.2f})")
# Common language codes
languages = {
'en': 'English',
'ch': 'Chinese (Simplified)',
'cht': 'Chinese (Traditional)',
'japan': 'Japanese',
'korean': 'Korean',
'french': 'French',
'german': 'German',
'spanish': 'Spanish',
'russian': 'Russian',
'arabic': 'Arabic',
'hindi': 'Hindi',
'vi': 'Vietnamese',
'th': 'Thai',
# ... 100+ languages supported
}
# Use specific language
ocr = PaddleOCR(lang='ch') # Chinese
ocr = PaddleOCR(lang='japan') # Japanese
ocr = PaddleOCR(lang='multilingual') # Auto-detect
from paddleocr import PaddleOCR
ocr = PaddleOCR(
# Detection settings
det_model_dir=None, # Custom detection model
det_limit_side_len=960, # Max side length for detection
det_db_thresh=0.3, # Binarization threshold
det_db_box_thresh=0.5, # Box score threshold
# Recognition settings
rec_model_dir=None, # Custom recognition model
rec_char_dict_path=None, # Custom character dictionary
# Angle classification
use_angle_cls=True, # Enable angle classification
cls_model_dir=None, # Custom classification model
# Language
lang='en', # Language code
# Performance
use_gpu=True, # Use GPU if available
gpu_mem=500, # GPU memory limit (MB)
enable_mkldnn=True, # CPU optimization
# Output
show_log=False, # Suppress logs
)
# Single image
result = ocr.ocr('image.png')
# Multiple images
images = ['img1.png', 'img2.png', 'img3.png']
for img in images:
result = ocr.ocr(img)
process_result(result)
from pdf2image import convert_from_path
def ocr_pdf(pdf_path):
"""OCR a scanned PDF."""
# Convert PDF pages to images
images = convert_from_path(pdf_path)
all_text = []
for i, img in enumerate(images):
# Save temp image
temp_path = f'temp_page_{i}.png'
img.save(temp_path)
# OCR the image
result = ocr.ocr(temp_path)
# Extract text
page_text = '\n'.join([line[1][0] for line in result[0]])
all_text.append(f"--- Page {i+1} ---\n{page_text}")
os.remove(temp_path)
return '\n\n'.join(all_text)
import requests
from io import BytesIO
# From URL
response = requests.get('https://example.com/image.png')
result = ocr.ocr(BytesIO(response.content))
# From bytes
with open('image.png', 'rb') as f:
img_bytes = f.read()
result = ocr.ocr(BytesIO(img_bytes))
def process_ocr_result(result):
"""Process OCR result into structured data."""
lines = []
for line in result[0]:
box = line[0]
text = line[1][0]
confidence = line[1][1]
# Calculate bounding box
x_coords = [p[0] for p in box]
y_coords = [p[1] for p in box]
lines.append({
'text': text,
'confidence': confidence,
'bbox': {
'left': min(x_coords),
'top': min(y_coords),
'right': max(x_coords),
'bottom': max(y_coords),
},
'raw_box': box
})
return lines
# Sort by position (top to bottom, left to right)
def sort_by_position(lines):
return sorted(lines, key=lambda x: (x['bbox']['top'], x['bbox']['left']))
def reconstruct_layout(result, line_threshold=10):
"""Reconstruct text layout from OCR results."""
lines = process_ocr_result(result)
lines = sort_by_position(lines)
# Group into logical lines
text_lines = []
current_line = []
current_y = None
for line in lines:
y = line['bbox']['top']
if current_y is None or abs(y - current_y) < line_threshold:
current_line.append(line)
current_y = y
else:
# New line
text_lines.append(' '.join([l['text'] for l in current_line]))
current_line = [line]
current_y = y
# Add last line
if current_line:
text_lines.append(' '.join([l['text'] for l in current_line]))
return '\n'.join(text_lines)
from PIL import Image, ImageEnhance, ImageFilter
def preprocess_image(image_path):
"""Preprocess image for better OCR."""
img = Image.open(image_path)
# Convert to grayscale
img = img.convert('L')
# Enhance contrast
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
# Sharpen
img = img.filter(ImageFilter.SHARPEN)
# Save preprocessed
preprocessed_path = 'preprocessed.png'
img.save(preprocessed_path)
return preprocessed_path
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
def batch_ocr(image_paths, max_workers=4):
"""OCR multiple images in parallel."""
results = {}
def process_single(img_path):
result = ocr.ocr(img_path)
return img_path, result
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(process_single, p) for p in image_paths]
for future in tqdm(futures, desc="Processing OCR"):
path, result = future.result()
results[path] = result
return results
from paddleocr import PaddleOCR
import re
def read_business_card(image_path):
"""Extract contact info from business card."""
ocr = PaddleOCR(use_angle_cls=True, lang='en')
result = ocr.ocr(image_path)
# Extract all text
all_text = []
for line in result[0]:
all_text.append(line[1][0])
full_text = '\n'.join(all_text)
# Parse contact info
contact = {
'name': None,
'email': None,
'phone': None,
'company': None,
'title': None,
'raw_text': full_text
}
# Email pattern
email_match = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', full_text)
if email_match:
contact['email'] = email_match.group()
# Phone pattern
phone_match = re.search(r'[\+\d][\d\s\-\(\)]{8,}', full_text)
if phone_match:
contact['phone'] = phone_match.group().strip()
# Name is usually the largest/first text
if all_text:
contact['name'] = all_text[0]
return contact
card_info = read_business_card('business_card.jpg')
print(f"Name: {card_info['name']}")
print(f"Email: {card_info['email']}")
print(f"Phone: {card_info['phone']}")
from paddleocr import PaddleOCR
import re
def scan_receipt(image_path):
"""Extract items and total from receipt."""
ocr = PaddleOCR(use_angle_cls=True, lang='en')
result = ocr.ocr(image_path)
lines = []
for line in result[0]:
text = line[1][0]
y_pos = line[0][0][1]
lines.append({'text': text, 'y': y_pos})
# Sort by vertical position
lines.sort(key=lambda x: x['y'])
receipt = {
'items': [],
'subtotal': None,
'tax': None,
'total': None
}
for line in lines:
text = line['text']
# Look for total
if 'total' in text.lower():
amount = re.search(r'\$?([\d,]+\.?\d*)', text)
if amount:
if 'sub' in text.lower():
receipt['subtotal'] = float(amount.group(1).replace(',', ''))
else:
receipt['total'] = float(amount.group(1).replace(',', ''))
# Look for tax
elif 'tax' in text.lower():
amount = re.search(r'\$?([\d,]+\.?\d*)', text)
if amount:
receipt['tax'] = float(amount.group(1).replace(',', ''))
# Look for items (line with price)
else:
item_match = re.search(r'(.+?)\s+\$?([\d,]+\.?\d+)$', text)
if item_match:
receipt['items'].append({
'name': item_match.group(1).strip(),
'price': float(item_match.group(2).replace(',', ''))
})
return receipt
receipt_data = scan_receipt('receipt.jpg')
print(f"Items: {len(receipt_data['items'])}")
print(f"Total: ${receipt_data['total']}")
from paddleocr import PaddleOCR
def ocr_multilingual(image_path, languages=['en', 'ch']):
"""OCR document with multiple languages."""
all_results = {}
for lang in languages:
ocr = PaddleOCR(use_angle_cls=True, lang=lang)
result = ocr.ocr(image_path)
texts = []
for line in result[0]:
texts.append({
'text': line[1][0],
'confidence': line[1][1]
})
all_results[lang] = texts
# Merge results, keeping highest confidence
merged = {}
for lang, texts in all_results.items():
for item in texts:
text = item['text']
conf = item['confidence']
if text not in merged or merged[text]['confidence'] < conf:
merged[text] = {'confidence': conf, 'language': lang}
return merged
result = ocr_multilingual('bilingual_document.png')
for text, info in result.items():
print(f"[{info['language']}] {text} ({info['confidence']:.2f})")
# CPU version
pip install paddlepaddle paddleocr
# GPU version (CUDA 11.x)
pip install paddlepaddle-gpu paddleocr
# Additional dependencies
pip install pdf2image Pillow
Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks
Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. When Claude needs to fill in a PDF form or programmatically process, generate, or analyze PDF documents at scale.
Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.
Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). Triggers include: any mention of 'Word doc', 'word document', '.docx', or requests to produce professional documents with formatting like tables of contents, headings, page numbers, or letterheads. Also use when extracting or reorganizing content from .docx files, inserting or replacing images in documents, performing find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a 'report', 'memo', 'letter', 'template', or similar deliverable as a Word or .docx file, use this skill. Do NOT use for PDFs, spreadsheets, Google Docs, or general coding tasks unrelated to document generation.
Use this skill any time a .pptx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates, layouts, speaker notes, or comments. Trigger whenever the user mentions \"deck,\" \"slides,\" \"presentation,\" or references a .pptx filename, regardless of what they plan to do with the content afterward. If a .pptx file needs to be opened, created, or touched, use this skill.
Create and edit Obsidian Flavored Markdown with wikilinks, embeds, callouts, properties, and other Obsidian-specific syntax. Use when working with .md files in Obsidian, or when the user mentions wikilinks, callouts, frontmatter, tags, embeds, or Obsidian notes.
Take claude-office-skills/smart-ocr from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip.
Without those the skill loads but fails at the first command.