Skip to content

Commit

Permalink
🎨 桌面端支持搜索图片 OCR 文本 #3470
Browse files Browse the repository at this point in the history
  • Loading branch information
88250 committed Jan 16, 2023
1 parent 6f51de0 commit c420859
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 359 deletions.
3 changes: 1 addition & 2 deletions app/appearance/langs/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,6 @@
"182": "Sharing document, please wait...",
"183": "Validating index document tree [%d/%d %s]",
"184": "Powered by <a href=\"https://b3log.org/siyuan\" target=\"_blank\">SiYuan</a>",
"185": "Index verification complete",
"186": "Extracted text [%s] from asset [%s]"
"185": "Index verification complete"
}
}
3 changes: 1 addition & 2 deletions app/appearance/langs/es_ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,6 @@
"182": "Compartiendo documento, por favor espere...",
"183": "Validando el árbol del documento de índice [%d/%d %s]",
"184": "Con la tecnología de <a href=\"https://b3log.org/siyuan\" target=\"_blank\">SiYuan</a>",
"185": "Verificación de índice completada",
"186": "Texto extraído [%s] del recurso [%s]"
"185": "Verificación de índice completada"
}
}
3 changes: 1 addition & 2 deletions app/appearance/langs/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,6 @@
"182": "Partage du document, veuillez patienter...",
"183": "Validation de l'arborescence du document d'index [%d/%d %s]",
"184": "Propulsé par <a href=\"https://b3log.org/siyuan\" target=\"_blank\">SiYuan</a>",
"185": "Vérification de l'index terminée",
"186": "Texte extrait [%s] de l'actif [%s]"
"185": "Vérification de l'index terminée"
}
}
7 changes: 3 additions & 4 deletions app/appearance/langs/zh_CHT.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"leftRightLayout": "左右佈局",
"topBottomLayout": "上下佈局",
"keyword": "關鍵字",
"searchMethod":"搜索方式",
"regex":"正則表達式",
"searchMethod": "搜索方式",
"regex": "正則表達式",
"keywordsLimit": "關鍵字數量限制",
"exportAsImage": "導出為圖片",
"exportBySiYuan": "由思源筆記導出",
Expand Down Expand Up @@ -1060,7 +1060,6 @@
"182": "正在分享文檔,請稍等...",
"183": "正在校驗索引文檔樹 [%d/%d %s]",
"184": "由<a href=\"https://b3log.org/siyuan\" target=\"_blank\">思源筆記</a>強力驅動",
"185": "索引校驗完畢",
"186": "已提取資源文件 [%s] 圖片中的文本 [%s]"
"185": "索引校驗完畢"
}
}
3 changes: 1 addition & 2 deletions app/appearance/langs/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,6 @@
"182": "正在分享文档,请稍等...",
"183": "正在校验索引文档树 [%d/%d %s]",
"184": "由<a href=\"https://b3log.org/siyuan\" target=\"_blank\">思源笔记</a>强力驱动",
"185": "索引校验完毕",
"186": "已识别资源文件 [%s] 图片中的文本 [%s]"
"185": "索引校验完毕"
}
}
6 changes: 3 additions & 3 deletions kernel/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func main() {
model.BootSyncData()
model.InitBoxes()
model.InitFlashcards()
util.LoadAssetsTexts()
model.LoadAssetsTexts()

go model.AutoGenerateDocHistory()
go model.AutoSync()
Expand All @@ -53,8 +53,8 @@ func main() {
go treenode.AutoFlushBlockTree()
go cache.LoadAssets()
go model.AutoFixIndex()
go util.AutoOCRAssets()
go util.AutoFlushAssetsTexts()
go model.AutoOCRAssets()
go model.AutoFlushAssetsTexts()
go model.HookDesktopUIProc()
model.WatchAssets()
model.HandleSignal()
Expand Down
6 changes: 3 additions & 3 deletions kernel/mobile/kernel.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func StartKernel(container, appDir, workspaceBaseDir, timezoneID, localIPs, lang
model.BootSyncData()
model.InitBoxes()
model.InitFlashcards()
util.LoadAssetsTexts()
model.LoadAssetsTexts()

go model.AutoGenerateDocHistory()
go model.AutoSync()
Expand All @@ -67,8 +67,8 @@ func StartKernel(container, appDir, workspaceBaseDir, timezoneID, localIPs, lang
go treenode.AutoFlushBlockTree()
go cache.LoadAssets()
go model.AutoFixIndex()
go util.AutoOCRAssets()
go util.AutoFlushAssetsTexts()
go model.AutoOCRAssets()
go model.AutoFlushAssetsTexts()
}()
}

Expand Down
1 change: 0 additions & 1 deletion kernel/model/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ func NetImg2LocalAssets(rootID string) (err error) {
if err = writeJSONQueue(tree); nil != err {
return
}
sql.WaitForWritingDatabase()
util.PushUpdateMsg(msgId, fmt.Sprintf(Conf.Language(120), files), 5000)
} else {
util.PushUpdateMsg(msgId, Conf.Language(121), 3000)
Expand Down
2 changes: 1 addition & 1 deletion kernel/model/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ func Close(force bool, execInstallPkg int) (exitCode int) {
Conf.Close()
sql.CloseDatabase()
treenode.SaveBlockTree(false)
util.SaveAssetsTexts()
SaveAssetsTexts()
clearWorkspaceTemp()
clearPortJSON()
util.UnlockWorkspace()
Expand Down
184 changes: 184 additions & 0 deletions kernel/model/ocr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package model

import (
"github.com/dustin/go-humanize"
"io"
"os"
"path/filepath"
"runtime"
"runtime/debug"
"strings"
"sync"
"time"

"github.com/88250/gulu"
"github.com/panjf2000/ants/v2"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/cache"
"github.com/siyuan-note/siyuan/kernel/util"
)

func AutoOCRAssets() {
if !util.TesseractEnabled {
return
}

for {
autoOCRAssets()
time.Sleep(7 * time.Second)
}
}

func autoOCRAssets() {
defer logging.Recover()

assetsPath := util.GetDataAssetsAbsPath()
assets := getUnOCRAssetsAbsPaths()

poolSize := runtime.NumCPU()
if 4 < poolSize {
poolSize = 4
}
waitGroup := &sync.WaitGroup{}
p, _ := ants.NewPoolWithFunc(poolSize, func(arg interface{}) {
defer waitGroup.Done()

assetAbsPath := arg.(string)
text := util.Tesseract(assetAbsPath)
p := strings.TrimPrefix(assetAbsPath, assetsPath)
p = "assets" + filepath.ToSlash(p)
util.AssetsTextsLock.Lock()
util.AssetsTexts[p] = text
util.AssetsTextsLock.Unlock()
util.AssetsTextsChanged = true
})
for _, assetAbsPath := range assets {
waitGroup.Add(1)
p.Invoke(assetAbsPath)
}
waitGroup.Wait()
p.Release()

cleanNotFoundAssetsTexts()
}

func cleanNotFoundAssetsTexts() {
tmp := util.AssetsTexts

assetsPath := util.GetDataAssetsAbsPath()
var toRemoves []string
for asset, _ := range tmp {
assetAbsPath := strings.TrimPrefix(asset, "assets")
assetAbsPath = filepath.Join(assetsPath, assetAbsPath)
if !gulu.File.IsExist(assetAbsPath) {
toRemoves = append(toRemoves, asset)
}
}

util.AssetsTextsLock.Lock()
for _, asset := range toRemoves {
delete(util.AssetsTexts, asset)
util.AssetsTextsChanged = true
}
util.AssetsTextsLock.Unlock()
return
}

func getUnOCRAssetsAbsPaths() (ret []string) {
var assetsPaths []string
assets := cache.GetAssets()
for _, asset := range assets {
lowerName := strings.ToLower(asset.Path)
if !strings.HasSuffix(lowerName, ".png") && !strings.HasSuffix(lowerName, ".jpg") && !strings.HasSuffix(lowerName, ".jpeg") {
continue
}
assetsPaths = append(assetsPaths, asset.Path)
}

assetsPath := util.GetDataAssetsAbsPath()
assetsTextsTmp := util.AssetsTexts
for _, assetPath := range assetsPaths {
if _, ok := assetsTextsTmp[assetPath]; ok {
continue
}
absPath := filepath.Join(assetsPath, strings.TrimPrefix(assetPath, "assets"))
ret = append(ret, absPath)
}
return
}

func AutoFlushAssetsTexts() {
for {
SaveAssetsTexts()
time.Sleep(7 * time.Second)
}
}

func LoadAssetsTexts() {
assetsPath := util.GetDataAssetsAbsPath()
assetsTextsPath := filepath.Join(assetsPath, "ocr-texts.json")
if !gulu.File.IsExist(assetsTextsPath) {
return
}

start := time.Now()
var err error
fh, err := os.OpenFile(assetsTextsPath, os.O_RDWR, 0644)
if nil != err {
logging.LogErrorf("open assets texts failed: %s", err)
return
}
defer fh.Close()

data, err := io.ReadAll(fh)
if nil != err {
logging.LogErrorf("read assets texts failed: %s", err)
return
}

util.AssetsTextsLock.Lock()
if err = gulu.JSON.UnmarshalJSON(data, &util.AssetsTexts); nil != err {
logging.LogErrorf("unmarshal assets texts failed: %s", err)
if err = os.RemoveAll(assetsTextsPath); nil != err {
logging.LogErrorf("removed corrupted assets texts failed: %s", err)
}
return
}
util.AssetsTextsLock.Unlock()
debug.FreeOSMemory()

if elapsed := time.Since(start).Seconds(); 2 < elapsed {
logging.LogWarnf("read assets texts [%s] to [%s], elapsed [%.2fs]", humanize.Bytes(uint64(len(data))), assetsTextsPath, elapsed)
}
return
}

func SaveAssetsTexts() {
if !util.AssetsTextsChanged {
return
}

start := time.Now()

util.AssetsTextsLock.Lock()
data, err := gulu.JSON.MarshalIndentJSON(util.AssetsTexts, "", " ")
if nil != err {
logging.LogErrorf("marshal assets texts failed: %s", err)
return
}
util.AssetsTextsLock.Unlock()

assetsPath := util.GetDataAssetsAbsPath()
assetsTextsPath := filepath.Join(assetsPath, "ocr-texts.json")
if err = gulu.File.WriteFileSafer(assetsTextsPath, data, 0644); nil != err {
logging.LogErrorf("write assets texts failed: %s", err)
return
}
debug.FreeOSMemory()

if elapsed := time.Since(start).Seconds(); 2 < elapsed {
logging.LogWarnf("save assets texts [size=%s] to [%s], elapsed [%.2fs]", humanize.Bytes(uint64(len(data))), assetsTextsPath, elapsed)
}

util.AssetsTextsChanged = false
}
2 changes: 1 addition & 1 deletion kernel/treenode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package treenode

import (
"bytes"
util2 "github.com/siyuan-note/siyuan/kernel/util"
"strings"
"sync"

Expand All @@ -31,7 +32,6 @@ import (
"github.com/88250/lute/render"
"github.com/88250/lute/util"
"github.com/siyuan-note/logging"
util2 "github.com/siyuan-note/siyuan/kernel/util"
)

func GetBlockRef(n *ast.Node) (blockRefID, blockRefText, blockRefSubtype string) {
Expand Down

0 comments on commit c420859

Please sign in to comment.