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
 | 
			
		||||
*.bak.*
 | 
			
		||||
*.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
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	apigo.cc/gojs v0.0.1
 | 
			
		||||
	apigo.cc/gojs/file v0.0.1
 | 
			
		||||
	github.com/ssgo/config v1.7.7
 | 
			
		||||
	apigo.cc/ai v0.0.2
 | 
			
		||||
	apigo.cc/gojs v0.0.4
 | 
			
		||||
	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/u v1.7.9
 | 
			
		||||
)
 | 
			
		||||
@ -14,12 +16,15 @@ require (
 | 
			
		||||
	github.com/dlclark/regexp2 v1.11.4 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.7.0 // 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/standard v1.7.7 // indirect
 | 
			
		||||
	github.com/ssgo/tool v0.4.27 // indirect
 | 
			
		||||
	golang.org/x/net v0.30.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.26.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
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user