feat: CutThenThink v3.0 初始版本
完整实现 Tauri + Vanilla JS 轻量级截图工具 Phase 1 - 项目搭建 - Tauri 2.x 项目初始化 - Vite 前端项目搭建 - 基础 UI 框架(CSS 变量、组件库) - 构建配置优化 Phase 2 - 核心截图功能 - 全屏/区域/窗口截图 - 截图预览和管理 - 文件命名和缩略图 - 全局快捷键集成 Phase 3 - 上传与存储 - 多图床上传(GitHub/Imgur/自定义) - 配置管理系统 - SQLite 数据库 Phase 4 - OCR 集成 - 云端 OCR(百度/腾讯云) - 插件管理系统 - 本地 OCR 插件(Go) - OCR 结果处理 Phase 5 - AI 分类系统 - Claude/OpenAI API 集成 - Prompt 模板引擎 - 模板管理界面 - 自动分类流程 Phase 6 - 历史记录与管理 - 图库视图(网格/列表) - 搜索与筛选 - 批量操作 - 导出功能(JSON/CSV/ZIP) Phase 7 - 打包与发布 - 多平台构建配置 - CI/CD 工作流 - 图标和资源 - 安装包配置 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
43
src-ocr-plugin/Makefile
Normal file
43
src-ocr-plugin/Makefile
Normal file
@@ -0,0 +1,43 @@
|
||||
.PHONY: all clean build-windows build-linux build-mac
|
||||
|
||||
# 版本信息
|
||||
VERSION := 1.0.0
|
||||
BINARY_NAME := ocr-plugin
|
||||
|
||||
# 所有目标
|
||||
all: build-linux build-windows build-mac
|
||||
|
||||
# 构建当前平台
|
||||
build:
|
||||
go build -o $(BINARY_NAME) main.go
|
||||
|
||||
# 构建 Linux 版本
|
||||
build-linux:
|
||||
GOOS=linux GOARCH=amd64 go build -o $(BINARY_NAME)-linux-amd64 main.go
|
||||
GOOS=linux GOARCH=arm64 go build -o $(BINARY_NAME)-linux-arm64 main.go
|
||||
|
||||
# 构建 Windows 版本
|
||||
build-windows:
|
||||
GOOS=windows GOARCH=amd64 go build -o $(BINARY_NAME)-windows-amd64.exe main.go
|
||||
|
||||
# 构建 macOS 版本
|
||||
build-mac:
|
||||
GOOS=darwin GOARCH=amd64 go build -o $(BINARY_NAME)-darwin-amd64 main.go
|
||||
GOOS=darwin GOARCH=arm64 go build -o $(BINARY_NAME)-darwin-arm64 main.go
|
||||
|
||||
# 清理构建文件
|
||||
clean:
|
||||
rm -f $(BINARY_NAME)*
|
||||
|
||||
# 运行
|
||||
run:
|
||||
go run main.go recognize -image ../test-images/sample.png
|
||||
|
||||
# 测试
|
||||
test:
|
||||
go test -v ./...
|
||||
|
||||
# 下载依赖
|
||||
deps:
|
||||
go mod download
|
||||
go mod tidy
|
||||
109
src-ocr-plugin/README.md
Normal file
109
src-ocr-plugin/README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# CutThenThink OCR Plugin
|
||||
|
||||
本地 OCR 插件,基于 Tesseract OCR 引擎实现。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 支持多语言识别(中文、英文、日文、韩文等)
|
||||
- 返回文本内容和边界框信息
|
||||
- 提供置信度评分
|
||||
- 跨平台支持(Windows、macOS、Linux)
|
||||
|
||||
## 系统依赖
|
||||
|
||||
### Linux (Ubuntu/Debian)
|
||||
```bash
|
||||
sudo apt-get install tesseract-ocr
|
||||
sudo apt-get install tesseract-ocr-chi-sim # 简体中文
|
||||
sudo apt-get install tesseract-ocr-chi-tra # 繁体中文
|
||||
sudo apt-get install libtesseract-dev
|
||||
```
|
||||
|
||||
### macOS
|
||||
```bash
|
||||
brew install tesseract
|
||||
brew install tesseract-lang # 包含中文语言包
|
||||
```
|
||||
|
||||
### Windows
|
||||
1. 下载安装 Tesseract from [UB Mannheim](https://github.com/UB-Mannheim/tesseract/wiki)
|
||||
2. 将 Tesseract 安装目录添加到 PATH 环境变量
|
||||
3. 安装中文语言包
|
||||
|
||||
## 编译
|
||||
|
||||
```bash
|
||||
# 下载依赖
|
||||
go mod download
|
||||
|
||||
# 编译当前平台
|
||||
go build -o ocr-plugin main.go
|
||||
|
||||
# 交叉编译
|
||||
GOOS=windows GOARCH=amd64 go build -o ocr-plugin.exe main.go
|
||||
GOOS=darwin GOARCH=amd64 go build -o ocr-plugin-mac main.go
|
||||
GOOS=linux GOARCH=amd64 go build -o ocr-plugin-linux main.go
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
### 查看版本
|
||||
```bash
|
||||
./ocr-plugin version
|
||||
```
|
||||
|
||||
### 识别文本
|
||||
```bash
|
||||
./ocr-plugin recognize -image /path/to/image.png -lang eng+chi_sim
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
- `-image`: 图片文件路径(必需)
|
||||
- `-lang`: OCR 语言(默认: eng+chi_sim)
|
||||
|
||||
#### 支持的语言代码
|
||||
- `eng` - English
|
||||
- `chi_sim` - 简体中文
|
||||
- `chi_tra` - 繁体中文
|
||||
- `jpn` - Japanese
|
||||
- `kor` - Korean
|
||||
|
||||
## 输出格式
|
||||
|
||||
### 成功响应
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"engine": "tesseract",
|
||||
"language": "eng+chi_sim",
|
||||
"blocks": [
|
||||
{
|
||||
"text": "识别的文本",
|
||||
"confidence": 95.5,
|
||||
"bbox_x": 100,
|
||||
"bbox_y": 200,
|
||||
"bbox_width": 150,
|
||||
"bbox_height": 30,
|
||||
"block_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "错误信息"
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
1. **图片预处理**: 在识别前对图片进行降噪、二值化处理可提高准确率
|
||||
2. **语言选择**: 只加载需要的语言包可以提高速度
|
||||
3. **图片尺寸**: 过大的图片会降低识别速度,建议缩放到合理尺寸
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
8
src-ocr-plugin/go.mod
Normal file
8
src-ocr-plugin/go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module github.com/cutthenthink/ocr-plugin
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/otiai10/gosseract v1.4.0
|
||||
)
|
||||
185
src-ocr-plugin/main.go
Normal file
185
src-ocr-plugin/main.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/otiai10/gosseract/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "1.0.0"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 命令行参数
|
||||
command := flag.String("command", "", "Command to execute (recognize, version)")
|
||||
imagePath := flag.String("image", "", "Path to image file")
|
||||
language := flag.String("lang", "eng+chi_sim", "OCR language (default: eng+chi_sim)")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *command == "" || len(os.Args) < 2 {
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch *command {
|
||||
case "version":
|
||||
printVersion()
|
||||
case "recognize":
|
||||
if *imagePath == "" {
|
||||
fmt.Fprintln(os.Stderr, "Error: image path is required for recognize command")
|
||||
os.Exit(1)
|
||||
}
|
||||
recognize(*imagePath, *language)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", *command)
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Println("CutThenThink OCR Plugin v" + Version)
|
||||
fmt.Println("\nUsage:")
|
||||
fmt.Println(" ocr-plugin version - Print version information")
|
||||
fmt.Println(" ocr-plugin recognize -image <path> - Recognize text from image")
|
||||
fmt.Println("\nOptions:")
|
||||
fmt.Println(" -lang <language> - OCR language (default: eng+chi_sim)")
|
||||
fmt.Println("\nSupported languages:")
|
||||
fmt.Println(" eng - English")
|
||||
fmt.Println(" chi_sim - Simplified Chinese")
|
||||
fmt.Println(" chi_tra - Traditional Chinese")
|
||||
fmt.Println(" jpn - Japanese")
|
||||
fmt.Println(" kor - Korean")
|
||||
fmt.Println(" (combine with + for multiple languages)")
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
fmt.Println(Version)
|
||||
}
|
||||
|
||||
// OCRBlock represents a single text block with bounding box
|
||||
type OCRBlock struct {
|
||||
Text string `json:"text"`
|
||||
Confidence float32 `json:"confidence"`
|
||||
BBoxX uint32 `json:"bbox_x"`
|
||||
BBoxY uint32 `json:"bbox_y"`
|
||||
BBoxWidth uint32 `json:"bbox_width"`
|
||||
BBoxHeight uint32 `json:"bbox_height"`
|
||||
BlockType string `json:"block_type"`
|
||||
}
|
||||
|
||||
// OCRResponse represents the JSON response from the plugin
|
||||
type OCRResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Error *string `json:"error,omitempty"`
|
||||
Engine string `json:"engine,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Blocks []OCRBlock `json:"blocks"`
|
||||
}
|
||||
|
||||
func recognize(imagePath, language string) {
|
||||
// Check if file exists
|
||||
if _, err := os.Stat(imagePath); os.IsNotExist(err) {
|
||||
response := OCRResponse{
|
||||
Success: false,
|
||||
Error: stringPtr("Image file not found: " + imagePath),
|
||||
}
|
||||
printJSON(response)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create Tesseract client
|
||||
client := gosseract.NewClient()
|
||||
defer client.Close()
|
||||
|
||||
// Set language
|
||||
client.SetLanguage(language)
|
||||
|
||||
// Set image
|
||||
client.SetImage(imagePath)
|
||||
|
||||
// Get text
|
||||
text, err := client.Text()
|
||||
if err != nil {
|
||||
response := OCRResponse{
|
||||
Success: false,
|
||||
Error: stringPtr("OCR failed: " + err.Error()),
|
||||
}
|
||||
printJSON(response)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get detailed text boxes
|
||||
boxes, err := client.GetBoundingBoxes(gosseract.RIL_TEXT_LINE)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Failed to get bounding boxes: %v", err)
|
||||
// Continue without bounding boxes
|
||||
}
|
||||
|
||||
// Convert to our format
|
||||
var blocks []OCRBlock
|
||||
for i, box := range boxes {
|
||||
if box.Box != nil {
|
||||
blocks = append(blocks, OCRBlock{
|
||||
Text: box.Word,
|
||||
Confidence: float32(box.Confidence),
|
||||
BBoxX: uint32(box.Box.Left),
|
||||
BBoxY: uint32(box.Box.Top),
|
||||
BBoxWidth: uint32(box.Box.Width),
|
||||
BBoxHeight: uint32(box.Box.Height),
|
||||
BlockType: "text",
|
||||
})
|
||||
} else {
|
||||
// Fallback for lines without bounding boxes
|
||||
blocks = append(blocks, OCRBlock{
|
||||
Text: box.Word,
|
||||
Confidence: float32(box.Confidence),
|
||||
BBoxX: 0,
|
||||
BBoxY: uint32(i * 20),
|
||||
BBoxWidth: 100,
|
||||
BBoxHeight: 20,
|
||||
BlockType: "text",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// If no boxes found, create a single block with the full text
|
||||
if len(blocks) == 0 && text != "" {
|
||||
blocks = append(blocks, OCRBlock{
|
||||
Text: text,
|
||||
Confidence: 80.0, // Default confidence
|
||||
BBoxX: 0,
|
||||
BBoxY: 0,
|
||||
BBoxWidth: 100,
|
||||
BBoxHeight: 100,
|
||||
BlockType: "text",
|
||||
})
|
||||
}
|
||||
|
||||
response := OCRResponse{
|
||||
Success: true,
|
||||
Engine: "tesseract",
|
||||
Language: language,
|
||||
Blocks: blocks,
|
||||
}
|
||||
|
||||
printJSON(response)
|
||||
}
|
||||
|
||||
func printJSON(v interface{}) {
|
||||
encoder := json.NewEncoder(os.Stdout)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(v); err != nil {
|
||||
log.Fatalf("Failed to encode JSON: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
Reference in New Issue
Block a user