feat(OCR): add recognition level and minimum confidence options for Mac system OCR provider

This commit is contained in:
suyao
2025-03-28 22:09:36 +08:00
parent 29ccbf16d8
commit 7f310bc29f
11 changed files with 121 additions and 102 deletions
+7 -1
View File
@@ -12,6 +12,11 @@ export default class MacSysOcrProvider extends BaseOcrProvider {
private readonly BATCH_SIZE = 4
private readonly CONCURRENCY = 2
private readonly MIN_TEXT_LENGTH = 1000
private getRecognitionLevel(level?: number) {
return level === 0 ? MacOCR.RECOGNITION_LEVEL_FAST : MacOCR.RECOGNITION_LEVEL_ACCURATE
}
constructor(provider: OcrProvider) {
super(provider)
}
@@ -42,7 +47,8 @@ export default class MacSysOcrProvider extends BaseOcrProvider {
const ocrResults = await MacOCR.recognizeBatchFromBuffer(pageBuffers, {
maxThreads: 4,
ocrOptions: {
recognitionLevel: MacOCR.RECOGNITION_LEVEL_ACCURATE
recognitionLevel: this.getRecognitionLevel(this.provider.options?.recognitionLevel),
minConfidence: this.provider.options?.minConfidence || 0.5
}
})
+1 -1
View File
@@ -497,7 +497,7 @@ class KnowledgeService {
try {
const ocrProvider = new OcrProvider(base.ocrProvider)
Logger.info(`Starting OCR processing for file: ${file.path}`)
Logger.info(`OCR provider: ${base.ocrProvider?.options}`)
const { processedFile } = await ocrProvider.parseFile(item.id, file)
Logger.info(`OCR processing completed: ${processedFile.path}`)
fileToProcess = processedFile
+9 -2
View File
@@ -1213,8 +1213,15 @@
"ocr": {
"title": "OCR",
"provider": "OCR Provider",
"ocr": "OCR Settings",
"provider_placeholder": "Choose a OCR provider"
"provider_placeholder": "Choose a OCR provider",
"mac_system_ocr_options": {
"mode": {
"title": "Recognition Mode",
"accurate": "Accurate",
"fast": "Fast"
},
"min_confidence": "Minimum Confidence"
}
},
"title": "Tool Settings"
},
+9 -28
View File
@@ -1188,10 +1188,16 @@
"title": "ツール設定",
"ocr": {
"title": "ナレッジベース設定",
"preprocessing": "預処理",
"preprocessing_tooltip": "アップロードされたファイルのOCR預処理",
"provider": "OCRプロバイダー",
"provider_placeholder": "OCRプロバイダーを選択する"
"provider_placeholder": "OCRプロバイダーを選択する",
"mac_system_ocr_options": {
"mode": {
"title": "辨識モード",
"accurate": "正確な",
"fast": "速い"
},
"min_confidence": "最小信頼度"
}
},
"websearch": {
"blacklist": "ブラックリスト",
@@ -1225,33 +1231,8 @@
"tray.onclose": "閉じるときにトレイに最小化",
"tray.show": "トレイアイコンを表示",
"tray.title": "トレイ",
"websearch": {
"blacklist": "ブラックリスト",
"blacklist_description": "以下のウェブサイトの結果は検索結果に表示されません",
"blacklist_tooltip": "以下の形式を使用してください(改行区切り)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
"check": "チェック",
"check_failed": "検証に失敗しました",
"check_success": "検証に成功しました",
"enhance_mode": "検索強化モード",
"enhance_mode_tooltip": "デフォルトモデルを使用して問題から検索キーワードを抽出し、検索を実行します",
"get_api_key": "APIキーを取得",
"no_provider_selected": "検索サービスプロバイダーを選択してから再確認してください。",
"search_max_result": "検索結果の数",
"search_provider": "検索サービスプロバイダー",
"search_provider_placeholder": "検索サービスプロバイダーを選択する",
"search_result_default": "デフォルト",
"search_with_time": "日付を含む検索",
"tavily": {
"api_key": "Tavily API キー",
"api_key.placeholder": "Tavily API キーを入力してください",
"description": "Tavily は、AI エージェントのために特別に開発された検索エンジンで、最新の結果、インテリジェントな検索提案、そして深い研究能力を提供します",
"title": "Tavily"
},
"title": "ウェブ検索"
},
"general.auto_check_update.title": "自動更新チェックを有効にする"
},
"general.auto_check_update.title": "自動更新チェックを有効にする",
"translate": {
"any.language": "任意の言語",
"button.translate": "翻訳",
+10 -28
View File
@@ -1189,7 +1189,15 @@
"ocr": {
"title": "Настройки OCR",
"provider": "Поставщик OCR",
"provider_placeholder": "Выберите поставщика OCR"
"provider_placeholder": "Выберите поставщика OCR",
"mac_system_ocr_options": {
"mode": {
"title": "Режим распознавания",
"accurate": "Точный",
"fast": "Быстрый"
},
"min_confidence": "Минимальная достоверность"
}
},
"websearch": {
"blacklist": "Черный список",
@@ -1214,8 +1222,7 @@
"title": "Tavily"
},
"title": "Поиск в Интернете"
},
"general.auto_check_update.title": "Включить автоматическую проверку обновлений"
}
},
"topic.position": "Позиция топиков",
"topic.position.left": "Слева",
@@ -1224,33 +1231,8 @@
"tray.onclose": "Свернуть в трей при закрытии",
"tray.show": "Показать значок в трее",
"tray.title": "Трей",
"websearch": {
"blacklist": "Черный список",
"blacklist_description": "Результаты из следующих веб-сайтов не будут отображаться в результатах поиска",
"blacklist_tooltip": "Пожалуйста, используйте следующий формат (разделенный переносами строк)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
"check": "проверка",
"check_failed": "Проверка не прошла",
"check_success": "Проверка успешна",
"enhance_mode": "Режим улучшения поиска",
"enhance_mode_tooltip": "Используйте модель по умолчанию для извлечения ключевых слов из проблемы и поиска",
"get_api_key": "Получить ключ API",
"no_provider_selected": "Пожалуйста, выберите поставщика поисковых услуг, затем проверьте.",
"search_max_result": "Количество результатов поиска",
"search_provider": "поиск сервисного провайдера",
"search_provider_placeholder": "Выберите поставщика поисковых услуг",
"search_result_default": "По умолчанию",
"search_with_time": "Поиск, содержащий дату",
"tavily": {
"api_key": "Ключ API Tavily",
"api_key.placeholder": "Введите ключ API Tavily",
"description": "Tavily — это поисковая система, специально разработанная для ИИ-агентов, предоставляющая актуальные результаты, умные предложения по запросам и глубокие исследовательские возможности",
"title": "Tavily"
},
"title": "Поиск в Интернете"
},
"general.auto_check_update.title": "Включить автоматическую проверку обновлений"
},
"general.auto_check_update.title": "Включить автоматическую проверку обновлений",
"translate": {
"any.language": "Любой язык",
"button.translate": "Перевести",
+15 -10
View File
@@ -394,7 +394,11 @@
"rename": "重命名",
"search": "搜索知识库",
"search_placeholder": "输入查询内容",
"settings": "知识库设置",
"settings": {
"title": "知识库设置",
"preprocessing": "预处理",
"preprocessing_tooltip": "使用 OCR 预处理上传的文件"
},
"sitemap_placeholder": "请输入站点地图 URL",
"sitemaps": "网站",
"source": "来源",
@@ -952,13 +956,6 @@
"launch.onboot": "开机自动启动",
"launch.title": "启动",
"launch.totray": "启动时最小化到托盘",
"knowledge": {
"title": "知识库",
"ocr": {
"title": "OCR设置",
"provider": "OCR服务商"
}
},
"mcp": {
"actions": "操作",
"active": "启用",
@@ -1191,9 +1188,17 @@
"tool": {
"title": "工具设置",
"ocr": {
"title": "知识库设置",
"title": "OCR设置",
"provider": "OCR服务商",
"provider_placeholder": "选择一个OCR服务商"
"provider_placeholder": "选择一个OCR服务商",
"mac_system_ocr_options": {
"mode": {
"title": "识别模式",
"accurate": "精准",
"fast": "快速"
},
"min_confidence": "最小置信度"
}
},
"websearch": {
"blacklist": "黑名单",
+9 -27
View File
@@ -1188,9 +1188,16 @@
"title": "工具",
"ocr": {
"title": "OCR",
"description": "OCR 設定",
"provider": "OCR 提供者",
"provider_placeholder": "選擇一個 OCR 提供者"
"provider_placeholder": "選擇一個 OCR 提供者",
"mac_system_ocr_options": {
"mode": {
"title": "辨識模式",
"accurate": "精確",
"fast": "快速"
},
"min_confidence": "最小置信度"
}
},
"websearch": {
"blacklist": "黑名單",
@@ -1224,33 +1231,8 @@
"tray.onclose": "關閉時最小化到系统匣",
"tray.show": "顯示系统匣圖示",
"tray.title": "系统匣",
"websearch": {
"blacklist": "黑名單",
"blacklist_description": "以下網站不會出現在搜尋結果中",
"blacklist_tooltip": "請使用以下格式 (換行符號分隔)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
"check": "檢查",
"check_failed": "驗證失敗",
"check_success": "驗證成功",
"enhance_mode": "搜索增強模式",
"enhance_mode_tooltip": "使用預設模型提取關鍵詞後搜索",
"get_api_key": "點選這裡取得金鑰",
"no_provider_selected": "請選擇搜尋服務商後再檢查",
"search_max_result": "搜尋結果個數",
"search_provider": "搜尋服務商",
"search_provider_placeholder": "選擇一個搜尋服務商",
"search_result_default": "預設",
"search_with_time": "搜尋包含日期",
"tavily": {
"api_key": "Tavily API 金鑰",
"api_key.placeholder": "請輸入 Tavily API 金鑰",
"description": "Tavily 是一個為 AI 代理量身訂製的搜尋引擎,提供即時、準確的結果、智慧查詢建議和深入的研究能力",
"title": "Tavily"
},
"title": "網路搜尋"
},
"general.auto_check_update.title": "啟用自動更新檢查"
},
"general.auto_check_update.title": "啟用自動更新檢查",
"translate": {
"any.language": "任意語言",
"button.translate": "翻譯",
@@ -4,13 +4,22 @@ import { useOcrProvider } from '@renderer/hooks/useOcr'
import { formatApiKeys } from '@renderer/services/ApiService'
import { OcrProvider } from '@renderer/types'
import { hasObjectKey } from '@renderer/utils'
import { Avatar, Divider, Flex, Input } from 'antd'
import { Avatar, Divider, Flex, Input, InputNumber, Segmented } from 'antd'
import Link from 'antd/es/typography/Link'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle, SettingTitle } from '../..'
import {
SettingDivider,
SettingHelpLink,
SettingHelpText,
SettingHelpTextRow,
SettingRow,
SettingRowTitle,
SettingSubtitle,
SettingTitle
} from '../..'
interface Props {
provider: OcrProvider
@@ -21,6 +30,7 @@ const OcrProviderSetting: FC<Props> = ({ provider: _provider }) => {
const { t } = useTranslation()
const [apiKey, setApiKey] = useState(ocrProvider.apiKey || '')
const [apiHost, setApiHost] = useState(ocrProvider.apiHost || '')
const [options, setOptions] = useState(ocrProvider.options || {})
const ocrProviderConfig = OCR_PROVIDER_CONFIG[ocrProvider.id]
const apiKeyWebsite = ocrProviderConfig?.websites?.apiKey
@@ -29,7 +39,8 @@ const OcrProviderSetting: FC<Props> = ({ provider: _provider }) => {
useEffect(() => {
setApiKey(ocrProvider.apiKey ?? '')
setApiHost(ocrProvider.apiHost ?? '')
}, [ocrProvider.apiKey, ocrProvider.apiHost])
setOptions(ocrProvider.options ?? {})
}, [ocrProvider.apiKey, ocrProvider.apiHost, ocrProvider.options])
const onUpdateApiKey = () => {
if (apiKey !== ocrProvider.apiKey) {
@@ -49,6 +60,12 @@ const OcrProviderSetting: FC<Props> = ({ provider: _provider }) => {
}
}
const onUpdateOptions = (key: string, value: any) => {
const newOptions = { ...options, [key]: value }
setOptions(newOptions)
updateOcrProvider({ ...ocrProvider, options: newOptions })
}
return (
<>
<SettingTitle>
@@ -102,6 +119,40 @@ const OcrProviderSetting: FC<Props> = ({ provider: _provider }) => {
</Flex>
</>
)}
{hasObjectKey(ocrProvider, 'options') && ocrProvider.id === 'system' && (
<>
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
<SettingRow>
<SettingRowTitle>{t('settings.tool.ocr.mac_system_ocr_options.mode.title')}</SettingRowTitle>
<Segmented
options={[
{
label: t('settings.tool.ocr.mac_system_ocr_options.mode.accurate'),
value: 1
},
{
label: t('settings.tool.ocr.mac_system_ocr_options.mode.fast'),
value: 0
}
]}
value={options.recognitionLevel}
onChange={(value) => onUpdateOptions('recognitionLevel', value)}
/>
</SettingRow>
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
<SettingRow>
<SettingRowTitle>{t('settings.tool.ocr.mac_system_ocr_options.min_confidence')}</SettingRowTitle>
<InputNumber
value={options.minConfidence}
onChange={(value) => onUpdateOptions('minConfidence', value)}
min={0}
max={1}
step={0.1}
/>
</SettingRow>
</>
)}
</>
)
}
+1 -1
View File
@@ -44,7 +44,7 @@ const persistedReducer = persistReducer(
{
key: 'cherry-studio',
storage,
version: 86,
version: 87,
blacklist: ['runtime', 'messages'],
migrate
},
+5 -1
View File
@@ -23,7 +23,11 @@ const initialState: OcrState = {
},
{
id: 'system',
name: 'System(Mac Only)'
name: 'System(Mac Only)',
options: {
recognitionLevel: 0,
minConfidence: 0.5
}
}
],
defaultProvider: ''
+1
View File
@@ -349,6 +349,7 @@ export interface OcrProvider {
apiKey?: string
apiHost?: string
model?: string
options?: any
}
export type GenerateImageParams = {