new version for apigo.cc/ai
This commit is contained in:
parent
6d990dfb14
commit
bbce32b3fc
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,4 +6,6 @@ node_modules
|
|||||||
package.json
|
package.json
|
||||||
*.bak.*
|
*.bak.*
|
||||||
*.png
|
*.png
|
||||||
*.jpg
|
*.jpg
|
||||||
|
*.jpeg
|
||||||
|
*.xls
|
||||||
|
22
ai_test.go
Normal file
22
ai_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package textin_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "apigo.cc/ai"
|
||||||
|
_ "apigo.cc/ai/textin"
|
||||||
|
"apigo.cc/gojs"
|
||||||
|
_ "apigo.cc/gojs/console"
|
||||||
|
_ "apigo.cc/gojs/file"
|
||||||
|
_ "apigo.cc/gojs/util"
|
||||||
|
"github.com/ssgo/u"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAI(t *testing.T) {
|
||||||
|
gojs.ExportForDev()
|
||||||
|
r, err := gojs.RunFile("ai_test.js")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
println(u.Cyan(u.JsonP(r)))
|
||||||
|
}
|
8
ai_test.js
Normal file
8
ai_test.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import file from 'apigo.cc/gojs/file'
|
||||||
|
import u from 'apigo.cc/gojs/util'
|
||||||
|
import co from 'apigo.cc/gojs/console'
|
||||||
|
import ai from 'apigo.cc/ai'
|
||||||
|
|
||||||
|
let r = ai.textin.scanTable(file.readBytes('table.jpeg'), { excel: true })
|
||||||
|
file.write('table.xls', u.unBase64(r.detail.excel))
|
||||||
|
return r
|
20
config.go
Normal file
20
config.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package textin
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"apigo.cc/ai"
|
||||||
|
"github.com/ssgo/u"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed default.yml
|
||||||
|
var defaultYml string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
defaultConf := ai.AILoadConfig{}
|
||||||
|
u.Convert(u.UnYamlMap(defaultYml), &defaultConf)
|
||||||
|
ai.Register("textin", &ai.Agent{
|
||||||
|
ScanConfigs: defaultConf.Scan,
|
||||||
|
Scan: Scan,
|
||||||
|
})
|
||||||
|
}
|
5
default.yml
Normal file
5
default.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
scan:
|
||||||
|
scanText:
|
||||||
|
type: text
|
||||||
|
scanTable:
|
||||||
|
type: table
|
13
go.mod
13
go.mod
@ -3,9 +3,11 @@ module apigo.cc/ai/textin
|
|||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/gojs v0.0.1
|
apigo.cc/ai v0.0.2
|
||||||
apigo.cc/gojs/file v0.0.1
|
apigo.cc/gojs v0.0.4
|
||||||
github.com/ssgo/config v1.7.7
|
apigo.cc/gojs/console v0.0.1
|
||||||
|
apigo.cc/gojs/file v0.0.2
|
||||||
|
apigo.cc/gojs/util v0.0.3
|
||||||
github.com/ssgo/httpclient v1.7.7
|
github.com/ssgo/httpclient v1.7.7
|
||||||
github.com/ssgo/u v1.7.9
|
github.com/ssgo/u v1.7.9
|
||||||
)
|
)
|
||||||
@ -14,12 +16,15 @@ require (
|
|||||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
|
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/ssgo/config v1.7.8 // indirect
|
||||||
github.com/ssgo/log v1.7.7 // indirect
|
github.com/ssgo/log v1.7.7 // indirect
|
||||||
github.com/ssgo/standard v1.7.7 // indirect
|
github.com/ssgo/standard v1.7.7 // indirect
|
||||||
github.com/ssgo/tool v0.4.27 // indirect
|
github.com/ssgo/tool v0.4.27 // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.30.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
128
scan.go
Normal file
128
scan.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package textin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"apigo.cc/ai"
|
||||||
|
"github.com/ssgo/httpclient"
|
||||||
|
"github.com/ssgo/u"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hc = httpclient.GetClient(time.Second * 60)
|
||||||
|
|
||||||
|
func Scan(aiConf *ai.AIConfig, image []byte, conf map[string]any) (ai.ScanResult, error) {
|
||||||
|
if bytes.HasPrefix(image, []byte("http")) && bytes.Contains(image, []byte("://")) {
|
||||||
|
if r := hc.Get(u.String(image)); r.Error != nil {
|
||||||
|
return ai.ScanResult{}, r.Error
|
||||||
|
} else {
|
||||||
|
image = r.Bytes()
|
||||||
|
}
|
||||||
|
} else if bytes.HasPrefix(image, []byte("data:")) {
|
||||||
|
// TODO 解析BLOB
|
||||||
|
}
|
||||||
|
endpoint := "https://api.textin.com/ai/service/v2"
|
||||||
|
if aiConf.Endpoint != "" {
|
||||||
|
endpoint = aiConf.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := u.String(conf["type"])
|
||||||
|
aksk := strings.SplitN(aiConf.ApiKey, ",", 2)
|
||||||
|
if len(aksk) == 1 {
|
||||||
|
aksk = append(aksk, aksk[0])
|
||||||
|
}
|
||||||
|
character := u.Int(conf["character"])
|
||||||
|
straighten := u.Int(conf["straighten"])
|
||||||
|
if typ == "table" {
|
||||||
|
output_order := u.String(conf["output_order"])
|
||||||
|
if output_order == "" {
|
||||||
|
output_order = "perpendicular"
|
||||||
|
}
|
||||||
|
table_type_hint := u.String(conf["table_type_hint"])
|
||||||
|
if table_type_hint == "" {
|
||||||
|
table_type_hint = "automatic"
|
||||||
|
}
|
||||||
|
excel := u.Int(conf["excel"])
|
||||||
|
resMap := hc.Post(fmt.Sprintf("%s/recognize/table/multipage?character=%d&straighten=%d&output_order=%s&table_type_hint=%s&excel=%d", endpoint, character, straighten, output_order, table_type_hint, excel), image, "x-ti-app-id", aksk[0], "x-ti-secret-code", aksk[1]).Map()
|
||||||
|
r := struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
Duration float64
|
||||||
|
Result struct {
|
||||||
|
Pages []struct {
|
||||||
|
Tables []struct {
|
||||||
|
Lines []struct {
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
Table_cells []struct {
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
u.Convert(resMap, &r)
|
||||||
|
if r.Code != 200 {
|
||||||
|
return ai.ScanResult{}, errors.New(r.Message)
|
||||||
|
}
|
||||||
|
results := make([]string, 0)
|
||||||
|
for _, table := range r.Result.Pages[len(r.Result.Pages)-1].Tables {
|
||||||
|
partResults := make([]string, 0)
|
||||||
|
for _, line := range table.Lines {
|
||||||
|
partResults = append(partResults, line.Text)
|
||||||
|
}
|
||||||
|
if len(partResults) > 0 {
|
||||||
|
results = append(results, strings.Join(partResults, " "))
|
||||||
|
}
|
||||||
|
partResults = make([]string, 0)
|
||||||
|
for _, line := range table.Table_cells {
|
||||||
|
partResults = append(partResults, line.Text)
|
||||||
|
}
|
||||||
|
if len(partResults) > 0 {
|
||||||
|
results = append(results, strings.Join(partResults, " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detail := map[string]any{}
|
||||||
|
u.Convert(resMap["result"], &detail)
|
||||||
|
return ai.ScanResult{
|
||||||
|
Result: strings.Join(results, "\n"),
|
||||||
|
Detail: detail,
|
||||||
|
UsedTime: int64(r.Duration),
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
// 通用文本识别
|
||||||
|
resMap := hc.Post(fmt.Sprintf("%s/recognize/multipage?character=%d&straighten=%d", endpoint, character, straighten), image, "x-ti-app-id", aksk[0], "x-ti-secret-code", aksk[1]).Map()
|
||||||
|
r := struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
Duration float64
|
||||||
|
Result struct {
|
||||||
|
Pages []struct {
|
||||||
|
Lines []struct {
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
u.Convert(resMap, &r)
|
||||||
|
if r.Code != 200 {
|
||||||
|
return ai.ScanResult{}, errors.New(r.Message)
|
||||||
|
}
|
||||||
|
results := make([]string, 0)
|
||||||
|
for _, line := range r.Result.Pages[len(r.Result.Pages)-1].Lines {
|
||||||
|
results = append(results, line.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
detail := map[string]any{}
|
||||||
|
u.Convert(resMap["result"], &detail)
|
||||||
|
return ai.ScanResult{
|
||||||
|
Result: strings.Join(results, " "),
|
||||||
|
Detail: detail,
|
||||||
|
UsedTime: int64(r.Duration),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
6
test.js
6
test.js
@ -1,6 +0,0 @@
|
|||||||
import ti from "apigo.cc/ai/textin"
|
|
||||||
import file from "apigo.cc/gojs/file"
|
|
||||||
|
|
||||||
function main(){
|
|
||||||
return {a : ti.OCR(file.readBytes("img.png")), b : ti.OCRFromFile("img.png")}
|
|
||||||
}
|
|
86
textin.go
86
textin.go
@ -1,86 +0,0 @@
|
|||||||
package textin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"apigo.cc/gojs"
|
|
||||||
"apigo.cc/gojs/goja"
|
|
||||||
_ "embed"
|
|
||||||
"errors"
|
|
||||||
"github.com/ssgo/config"
|
|
||||||
"github.com/ssgo/httpclient"
|
|
||||||
"github.com/ssgo/u"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed textin.ts
|
|
||||||
var textinTS string
|
|
||||||
|
|
||||||
type Conf struct {
|
|
||||||
AKey string
|
|
||||||
SKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
var conf = Conf{}
|
|
||||||
|
|
||||||
var confAes = u.NewAes([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
|
|
||||||
var keysIsSet = false
|
|
||||||
|
|
||||||
func SetSSKey(key, iv []byte) {
|
|
||||||
if !keysIsSet {
|
|
||||||
confAes = u.NewAes(key, iv)
|
|
||||||
keysIsSet = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
obj := map[string]interface{}{
|
|
||||||
"name": "textin",
|
|
||||||
"OCR": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
|
||||||
return vm.ToValue(OCR(args.Bytes(0), vm))
|
|
||||||
},
|
|
||||||
"OCRFromFile": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
||||||
path := gojs.MakeArgs(&argsIn, vm).Check(1).Path(0)
|
|
||||||
if file, err := u.ReadFileBytes(path); err != nil {
|
|
||||||
panic(vm.NewGoError(err))
|
|
||||||
} else {
|
|
||||||
return vm.ToValue(OCR(file, vm))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
config.LoadConfig("textin", &conf)
|
|
||||||
conf.AKey = confAes.DecryptUrlBase64ToString(conf.AKey)
|
|
||||||
conf.SKey = confAes.DecryptUrlBase64ToString(conf.SKey)
|
|
||||||
//log.DefaultLogger.Info("conf", "", conf)
|
|
||||||
gojs.Register("apigo.cc/ai/textin", gojs.Module{
|
|
||||||
Object: obj,
|
|
||||||
TsCode: textinTS,
|
|
||||||
Example: "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func OCR(file []byte, vm *goja.Runtime) string {
|
|
||||||
resMap := httpclient.GetClient(time.Second*30).Post("https://api.textin.com/ai/service/v2/recognize/multipage", file, "x-ti-app-id", conf.AKey, "x-ti-secret-code", conf.SKey).Map()
|
|
||||||
res := struct {
|
|
||||||
Result struct {
|
|
||||||
Pages []struct {
|
|
||||||
Lines []struct {
|
|
||||||
Text string
|
|
||||||
} `json:"lines"`
|
|
||||||
} `json:"pages"`
|
|
||||||
} `json:"result"`
|
|
||||||
}{}
|
|
||||||
//log.DefaultLogger.Info("Result", "", resMap)
|
|
||||||
if resMap["code"] != 200 {
|
|
||||||
panic(vm.NewGoError(errors.New(u.BRed(resMap["message"]))))
|
|
||||||
}
|
|
||||||
str := ""
|
|
||||||
u.Convert(resMap, &res)
|
|
||||||
if len(res.Result.Pages) < 1 {
|
|
||||||
panic(vm.NewGoError(errors.New(u.BRed("no characters recognized"))))
|
|
||||||
}
|
|
||||||
for _, k := range res.Result.Pages[len(res.Result.Pages)-1].Lines {
|
|
||||||
str += "" + k.Text
|
|
||||||
}
|
|
||||||
//log.DefaultLogger.Info("OCRResult", "Map", resMap, "Text", str)
|
|
||||||
return str
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default {
|
|
||||||
OCR,
|
|
||||||
OCRFromFile
|
|
||||||
}
|
|
||||||
|
|
||||||
function OCR(pictureData) {return ""}
|
|
||||||
function OCRFromFile(picturePath) {return ""}
|
|
@ -1,22 +0,0 @@
|
|||||||
package textin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"apigo.cc/gojs"
|
|
||||||
_ "apigo.cc/gojs/file"
|
|
||||||
"fmt"
|
|
||||||
"github.com/ssgo/u"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExport(t *testing.T) {
|
|
||||||
gojs.ExportForDev()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLLM(t *testing.T) {
|
|
||||||
r, err := gojs.RunFile("test.js")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println(u.JsonP(r))
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user