feat: Add KB knowledge base series skills

- kb-link-parser: Multi-platform URL parser (Bilibili, YouTube, Zhihu)
- kb-bilibili-metadata: Bilibili video/article/up主 metadata extractor
- kb-zhihu-metadata: Zhihu question/answer/user metadata extractor
- kb-bilibili-content: Bilibili video download and content extraction
- kb-ocr-processor: OCR text extraction from video frames
- kb-deep-analyzer: AI-powered deep content analysis
- kb-workflow: Workflow orchestration (inbox, archive, subscriptions, report)

Also add:
- ARCHITECTURE_FLOWCHART.md: System architecture diagrams
- DETAILED_DESIGN.md: Complete design documentation
This commit is contained in:
OpenClaw Agent
2026-02-10 12:18:52 +08:00
parent 2f5a269866
commit 5507b1d986
28 changed files with 7715 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""
OCR Processor - 视频帧文字识别工具
支持 EasyOCR 和 Tesseract 引擎
"""
import argparse
import json
import os
import glob
from pathlib import Path
from typing import List, Dict, Optional
import easyocr
import pytesseract
from PIL import Image
class OCRProcessor:
"""OCR 文字识别处理器"""
def __init__(self, engine: str = "easyocr", languages: List[str] = ["ch_sim", "en"]):
"""
初始化 OCR 处理器
Args:
engine: OCR 引擎 ("easyocr" 或 "tesseract")
languages: 语言列表
"""
self.engine = engine
self.languages = languages
self.reader = None
if engine == "easyocr":
self.reader = easyocr.Reader(languages, gpu=False)
def process_image(self, image_path: str) -> Dict:
"""
处理单张图片进行 OCR 识别
Args:
image_path: 图片路径
Returns:
包含 text 和 confidence 的字典
"""
if not os.path.exists(image_path):
return {"text": "", "confidence": 0.0, "error": "File not found"}
try:
if self.engine == "easyocr":
return self._process_with_easyocr(image_path)
elif self.engine == "tesseract":
return self._process_with_tesseract(image_path)
else:
return {"text": "", "confidence": 0.0, "error": f"Unknown engine: {self.engine}"}
except Exception as e:
return {"text": "", "confidence": 0.0, "error": str(e)}
def _process_with_easyocr(self, image_path: str) -> Dict:
"""使用 EasyOCR 处理图片"""
results = self.reader.readtext(image_path)
if not results:
return {"text": "", "confidence": 0.0}
texts = []
confidences = []
for bbox, text, conf in results:
texts.append(text)
confidences.append(conf)
merged_text = "\n".join(texts)
avg_confidence = sum(confidences) / len(confidences) if confidences else 0.0
return {
"text": merged_text,
"confidence": round(avg_confidence, 4),
"details": [
{"text": text, "confidence": round(conf, 4)}
for text, conf in zip(texts, confidences)
]
}
def _process_with_tesseract(self, image_path: str) -> Dict:
"""使用 Tesseract 处理图片"""
image = Image.open(image_path)
text = pytesseract.image_to_string(image, lang="+".join(self.languages))
# Tesseract 不直接返回置信度,返回 0.0
return {
"text": text.strip(),
"confidence": 0.0
}
def process_frames(self, input_dir: str, fps: float = 0.1) -> List[Dict]:
"""
处理目录下所有帧图片
Args:
input_dir: 帧图片目录
fps: 采样帧率
Returns:
OCR 结果列表
"""
if not os.path.isdir(input_dir):
print(f"Error: Directory not found: {input_dir}")
return []
# 支持的图片格式
image_extensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.webp"]
image_files = []
for ext in image_extensions:
image_files.extend(glob.glob(os.path.join(input_dir, ext)))
image_files.extend(glob.glob(os.path.join(input_dir, "**", ext), recursive=True))
# 按文件名排序
image_files = sorted(image_files)
if not image_files:
print(f"No images found in {input_dir}")
return []
print(f"Found {len(image_files)} images in {input_dir}")
print(f"Processing with {self.engine} engine...")
results = []
for i, image_path in enumerate(image_files):
print(f"Processing [{i+1}/{len(image_files)}]: {image_path}")
ocr_result = self.process_image(image_path)
ocr_result["source"] = os.path.basename(image_path)
results.append(ocr_result)
return results
def merge_texts(self, results: List[Dict]) -> str:
"""
合并多个 OCR 结果的文本
Args:
results: OCR 结果列表
Returns:
合并后的文本
"""
texts = []
for result in results:
if result.get("text"):
texts.append(result["text"])
return "\n\n---\n\n".join(texts)
def save_results(self, results: List[Dict], output_path: str, merge: bool = False):
"""
保存 OCR 结果到 JSON 文件
Args:
results: OCR 结果列表
output_path: 输出文件路径
merge: 是否同时保存合并后的文本文件
"""
output_data = {
"engine": self.engine,
"total_frames": len(results),
"results": results
}
# 保存 JSON
with open(output_path, "w", encoding="utf-8") as f:
json.dump(output_data, f, ensure_ascii=False, indent=2)
print(f"Results saved to {output_path}")
# 如果需要,同时保存合并的文本
if merge:
text_path = output_path.replace(".json", ".txt")
merged_text = self.merge_texts(results)
with open(text_path, "w", encoding="utf-8") as f:
f.write(merged_text)
print(f"Merged text saved to {text_path}")
def main():
"""命令行入口"""
parser = argparse.ArgumentParser(
description="OCR 视频帧文字识别工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 使用 EasyOCR 处理帧目录
python ocr_processor.py --input ./frames/ --output ./ocr.json
# 使用 Tesseract 处理
python ocr_processor.py --input ./frames/ --output ./ocr.json --engine tesseract
# 指定采样帧率
python ocr_processor.py --input ./frames/ --output ./ocr.json --fps 0.5
# 同时生成合并的文本文件
python ocr_processor.py --input ./frames/ --output ./ocr.json --merge
"""
)
parser.add_argument("--input", "-i", required=True, help="输入帧图片目录")
parser.add_argument("--output", "-o", required=True, help="输出 JSON 文件路径")
parser.add_argument("--fps", "-f", type=float, default=0.1, help="采样帧率 (默认: 0.1)")
parser.add_argument("--engine", "-e", choices=["easyocr", "tesseract"],
default="easyocr", help="OCR 引擎 (默认: easyocr)")
parser.add_argument("--merge", action="store_true", help="同时保存合并的文本文件")
parser.add_argument("--languages", "-l", nargs="+",
default=["ch_sim", "en"], help="语言列表")
args = parser.parse_args()
# 初始化处理器
processor = OCRProcessor(engine=args.engine, languages=args.languages)
# 处理帧
results = processor.process_frames(args.input, fps=args.fps)
if results:
# 保存结果
processor.save_results(results, args.output, merge=args.merge)
# 打印摘要
total_texts = sum(1 for r in results if r.get("text"))
avg_conf = sum(r.get("confidence", 0) for r in results) / len(results) if results else 0
print(f"\n=== 摘要 ===")
print(f"处理帧数: {len(results)}")
print(f"识别到文字的帧: {total_texts}")
print(f"平均置信度: {avg_conf:.2%}")
else:
print("未处理到任何结果")
return 1
return 0
if __name__ == "__main__":
exit(main())