├── .gitignore ├── LICENSE ├── README-ZH.md ├── README.md ├── go.mod ├── go.sum ├── paddleocr.go ├── paddleocr_test.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | example/ 24 | PaddleOCR-json_v.1.3.1/ 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 doraemon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-ZH.md: -------------------------------------------------------------------------------- 1 | 中文|[English](README.md) 2 | 3 | 4 | # paddleocr 5 | 6 | [![Go Reference](https://pkg.go.dev/badge/github.com/doraemonkeys/paddleocr.svg)](https://pkg.go.dev/github.com/doraemonkeys/paddleocr) 7 | 8 | 9 | 10 | Go语言实现的对PaddleOCR-json的简单封装。 11 | 12 | ## 安装 13 | 14 | 1. 从[PaddleOCR-json releases](https://github.com/hiroi-sora/PaddleOCR-json/releases)下载程序并解压。 15 | 2. 安装paddleocr 16 | 17 | ```go 18 | go get github.com/doraemonkeys/paddleocr 19 | ``` 20 | 21 | ## 快速开始 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | 29 | "github.com/doraemonkeys/paddleocr" 30 | ) 31 | 32 | func main() { 33 | p, err := paddleocr.NewPpocr("path/to/PaddleOCR-json.exe", 34 | paddleocr.OcrArgs{}) 35 | if err != nil { 36 | panic(err) 37 | } 38 | defer p.Close() 39 | result, err := p.OcrFileAndParse(`path/to/image.png`) 40 | if err != nil { 41 | panic(err) 42 | } 43 | if result.Code != paddleocr.CodeSuccess { 44 | fmt.Println("orc failed:", result.Msg) 45 | return 46 | } 47 | fmt.Println(result.Data) 48 | } 49 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English|[中文](/README-ZH.md) 2 | 3 | 4 | 5 | # paddleocr 6 | 7 | [![Go Reference](https://pkg.go.dev/badge/github.com/doraemonkeys/paddleocr.svg)](https://pkg.go.dev/github.com/doraemonkeys/paddleocr) [![Go Report Card](https://goreportcard.com/badge/github.com/doraemonkeys/paddleocr)](https://goreportcard.com/report/github.com/doraemonkeys/paddleocr) 8 | 9 | 10 | A simple wrapper for hiroi-sora/PaddleOCR-json implemented in Go language. 11 | 12 | 13 | ## Installation 14 | 15 | 1. Download the program from [PaddleOCR-json releases](https://github.com/hiroi-sora/PaddleOCR-json/releases) and decompress it. 16 | 17 | 2. install paddleocr 18 | 19 | ```go 20 | go get github.com/doraemonkeys/paddleocr 21 | ``` 22 | 23 | ## Quick Start 24 | 25 | ```go 26 | package main 27 | 28 | import ( 29 | "fmt" 30 | 31 | "github.com/doraemonkeys/paddleocr" 32 | ) 33 | 34 | func main() { 35 | p, err := paddleocr.NewPpocr("path/to/PaddleOCR-json.exe", 36 | paddleocr.OcrArgs{}) 37 | if err != nil { 38 | panic(err) 39 | } 40 | defer p.Close() 41 | result, err := p.OcrFileAndParse(`path/to/image.png`) 42 | if err != nil { 43 | panic(err) 44 | } 45 | if result.Code != paddleocr.CodeSuccess { 46 | fmt.Println("orc failed:", result.Msg) 47 | return 48 | } 49 | fmt.Println(result.Data) 50 | } 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/doraemonkeys/paddleocr 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doraemonkeys/paddleocr/a4f40339514691257b3deacd6bc751352d61fd12/go.sum -------------------------------------------------------------------------------- /paddleocr.go: -------------------------------------------------------------------------------- 1 | package paddleocr 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | type OcrArgs struct { 19 | // 启用cls方向分类,识别方向不是正朝上的图片。默认为false。 20 | Cls *bool `paddleocr:"cls"` 21 | // 启用CPU推理加速,关掉可以减少内存占用,但会降低速度。默认为true。 22 | EnableMkldnn *bool `paddleocr:"enable_mkldnn"` 23 | // 若图片长边长度大于该值,会被缩小到该值,以提高速度。默认为960。 24 | // 如果对大图/长图的识别率低,可增大 limit_side_len 的值。 25 | // 建议为 32 & 48 的公倍数,如 960, 2880, 4320 26 | LimitSideLen *int32 `paddleocr:"limit_side_len"` 27 | // 启用方向分类,必须与cls值相同。 默认为false。 28 | UseAngleCls *bool `paddleocr:"use_angle_cls"` 29 | // 指定不同语言的配置文件路径,识别多国语言。 30 | // models 目录中,每一个 config_xxx.txt 是一组语言配置文件(如英文是congfig_en.txt)。 31 | // 只需将这个文件的路径传入 config_path 参数,即可切换为对应的语言。 32 | // 33 | // 例如: 34 | // paddleocr.OcrArgs{ ConfigPath: paddleocr.ConfigChinese } 35 | ConfigPath string `paddleocr:"config_path"` 36 | } 37 | 38 | const ( 39 | ConfigChinese = "models/config_chinese.txt" 40 | ConfigChineseCht = "models/config_chinese_cht.txt" 41 | ConfigCyrillic = "models/config_cyrillic.txt" 42 | ConfigEn = "models/config_en.txt" 43 | ConfigFrenchV2 = "models/config_french_v2.txt" 44 | ConfigGermanV2 = "models/config_german_v2.txt" 45 | ConfigJapan = "models/config_japan.txt" 46 | ConfigKorean = "models/config_korean.txt" 47 | ) 48 | 49 | const clipboardImagePath = `clipboard` 50 | 51 | func (o OcrArgs) CmdString() string { 52 | var s string 53 | v := reflect.ValueOf(o) 54 | for i := 0; i < v.NumField(); i++ { 55 | if v.Field(i).IsZero() { 56 | continue 57 | } 58 | f := v.Type().Field(i) 59 | if f.Tag.Get(paddleocrTag) == "" { 60 | continue 61 | } 62 | // value := v.Field(i).Elem().Interface() 63 | value := v.Field(i).Interface() 64 | 65 | switch valueType := value.(type) { 66 | case *bool: 67 | if *valueType { 68 | s += fmt.Sprintf("%s=1 ", f.Tag.Get(paddleocrTag)) 69 | } else { 70 | s += fmt.Sprintf("%s=0 ", f.Tag.Get(paddleocrTag)) 71 | } 72 | default: 73 | if v.Field(i).Kind() == reflect.Ptr { 74 | s += fmt.Sprintf("%s=%v ", f.Tag.Get(paddleocrTag), v.Field(i).Elem().Interface()) 75 | } else { 76 | s += fmt.Sprintf("%s=%v ", f.Tag.Get(paddleocrTag), value) 77 | } 78 | } 79 | } 80 | s = strings.TrimSpace(s) 81 | return s 82 | } 83 | 84 | // OcrFile processes the OCR for a given image file path using the specified OCR arguments. 85 | // It returns the raw OCR result as bytes and any error encountered. 86 | func OcrFile(exePath, imagePath string, argsCnf OcrArgs) ([]byte, error) { 87 | p, err := NewPpocr(exePath, argsCnf) 88 | if err != nil { 89 | return nil, err 90 | } 91 | defer p.Close() 92 | b, err := p.OcrFile(imagePath) 93 | if err != nil { 94 | return nil, err 95 | } 96 | return b, nil 97 | } 98 | 99 | func OcrFileAndParse(exePath, imagePath string, argsCnf OcrArgs) (Result, error) { 100 | data, err := OcrFile(exePath, imagePath, argsCnf) 101 | if err != nil { 102 | return Result{}, err 103 | } 104 | return ParseResult(data) 105 | } 106 | 107 | type Ppocr struct { 108 | exePath string 109 | args OcrArgs 110 | ppLock *sync.Mutex 111 | restartExitChan chan struct{} 112 | internalErr error 113 | 114 | cmdStdout io.ReadCloser 115 | cmdStdin io.WriteCloser 116 | cmd *exec.Cmd 117 | // 无缓冲同步信号通道,close()中接收,Run()中发送。 118 | // Run()退出必须有对应close方法的调用 119 | runGoroutineExitedChan chan struct{} 120 | // startTime time.Time 121 | } 122 | 123 | // NewPpocr creates a new instance of the Ppocr struct with the provided executable path 124 | // and OCR arguments. 125 | // It initializes the OCR process and returns a pointer to the Ppocr instance 126 | // and any error encountered. 127 | // 128 | // It is the caller's responsibility to close the Ppocr instance when finished. 129 | func NewPpocr(exePath string, args OcrArgs) (*Ppocr, error) { 130 | if !fileIsExist(exePath) { 131 | return nil, fmt.Errorf("executable file %s not found", exePath) 132 | } 133 | p := &Ppocr{ 134 | exePath: exePath, 135 | args: args, 136 | ppLock: new(sync.Mutex), 137 | restartExitChan: make(chan struct{}), 138 | runGoroutineExitedChan: make(chan struct{}), 139 | } 140 | 141 | p.ppLock.Lock() 142 | defer p.ppLock.Unlock() 143 | err := p.initPpocr(exePath, args) 144 | if err == nil { 145 | go p.restartTimer() 146 | } else { 147 | p.close() 148 | } 149 | return p, err 150 | } 151 | 152 | // 加锁调用,发生错误需要close 153 | func (p *Ppocr) initPpocr(exePath string, args OcrArgs) error { 154 | var cmdSlash string 155 | if runtime.GOOS == "windows" { 156 | cmdSlash = "\\" 157 | } else { 158 | cmdSlash = "/" 159 | } 160 | p.cmd = exec.Command("."+cmdSlash+filepath.Base(exePath), strings.Fields(args.CmdString())...) 161 | cmdDir := filepath.Dir(exePath) 162 | if cmdDir == "." { 163 | cmdDir = "" 164 | } 165 | p.cmd.Dir = cmdDir 166 | wc, err := p.cmd.StdinPipe() 167 | if err != nil { 168 | return err 169 | } 170 | rc, err := p.cmd.StdoutPipe() 171 | if err != nil { 172 | return err 173 | } 174 | p.cmdStdin = wc 175 | p.cmdStdout = rc 176 | 177 | var stderrBuffer bytes.Buffer 178 | p.cmd.Stderr = &stderrBuffer 179 | 180 | err = p.cmd.Start() 181 | if err != nil { 182 | return fmt.Errorf("OCR process start failed: %v", err) 183 | } 184 | 185 | go func() { 186 | p.internalErr = nil 187 | err := p.cmd.Wait() 188 | // fmt.Println("Run() OCR process exited, error:", err) 189 | if err != nil { 190 | p.internalErr = err 191 | } 192 | p.runGoroutineExitedChan <- struct{}{} 193 | }() 194 | 195 | buf := make([]byte, 4096) 196 | start := 0 197 | for { 198 | n, err := rc.Read(buf[start:]) 199 | if err != nil { 200 | if p.internalErr != nil { 201 | return fmt.Errorf("OCR init failed: %v,run error: %v", err, p.internalErr) 202 | } 203 | return fmt.Errorf("OCR init failed, error: %v, output: %s %s", err, buf[:start], stderrBuffer.String()) 204 | } 205 | start += n 206 | if start >= len(buf) { 207 | return fmt.Errorf("OCR init failed: output too long") 208 | } 209 | if bytes.Contains(buf[:start], []byte("OCR init completed.")) { 210 | break 211 | } 212 | } 213 | return p.internalErr 214 | } 215 | 216 | // Close cleanly shuts down the OCR process associated with the Ppocr instance. 217 | // It releases any resources and terminates the OCR process. 218 | // 219 | // Warning: This method should only be called once. 220 | func (p *Ppocr) Close() error { 221 | p.ppLock.Lock() 222 | defer p.ppLock.Unlock() 223 | // close(p.restartExitChan) // 只能关闭一次 224 | select { 225 | case <-p.restartExitChan: 226 | return fmt.Errorf("OCR process has been closed") 227 | default: 228 | close(p.restartExitChan) 229 | } 230 | p.internalErr = fmt.Errorf("OCR process has been closed") 231 | return p.close() 232 | } 233 | 234 | func (p *Ppocr) close() (err error) { 235 | select { 236 | case <-p.runGoroutineExitedChan: 237 | return nil 238 | default: 239 | } 240 | defer func() { 241 | // 可能的情况:Run刚退出,p.exited还没设置为true 242 | if r := recover(); r != nil { 243 | err = fmt.Errorf("close panic: %v", r) 244 | } 245 | // fmt.Println("wait OCR runGoroutineExitedChan") 246 | <-p.runGoroutineExitedChan 247 | // fmt.Println("wait OCR runGoroutineExitedChan done") 248 | }() 249 | if p.cmd == nil { 250 | return nil 251 | } 252 | if p.cmd.ProcessState != nil && p.cmd.ProcessState.Exited() { 253 | return nil 254 | } 255 | if err := p.cmdStdin.Close(); err != nil { 256 | fmt.Fprintf(os.Stderr, "close cmdIn error: %v\n", err) 257 | } 258 | if err := p.cmd.Process.Kill(); err != nil { 259 | return err 260 | } 261 | // fmt.Println("kill OCR process success") 262 | return nil 263 | } 264 | 265 | // 定时重启进程减少内存占用(ocr程序有内存泄漏) 266 | func (p *Ppocr) restartTimer() { 267 | // ticker := time.NewTicker(10 * time.Second) 268 | ticker := time.NewTicker(20 * time.Minute) 269 | for { 270 | select { 271 | case <-ticker.C: 272 | // fmt.Println("restart OCR process") 273 | p.ppLock.Lock() 274 | _ = p.close() 275 | p.internalErr = p.initPpocr(p.exePath, p.args) 276 | p.ppLock.Unlock() 277 | // fmt.Println("restart OCR process done") 278 | case <-p.restartExitChan: 279 | // fmt.Println("exit OCR process") 280 | return 281 | } 282 | } 283 | } 284 | 285 | type imageData struct { 286 | Path string `json:"image_path,omitempty"` 287 | ContentB64 []byte `json:"image_base64,omitempty"` 288 | } 289 | 290 | // OcrFile sends an image file path to the OCR process and retrieves the OCR result. 291 | // It returns the OCR result as bytes and any error encountered. 292 | func (p *Ppocr) OcrFile(imagePath string) ([]byte, error) { 293 | var data = imageData{Path: imagePath} 294 | dataJson, err := json.Marshal(data) 295 | if err != nil { 296 | return nil, err 297 | } 298 | p.ppLock.Lock() 299 | defer p.ppLock.Unlock() 300 | if p.internalErr != nil { 301 | return nil, p.internalErr 302 | } 303 | return p.ocr(dataJson) 304 | } 305 | 306 | func (p *Ppocr) ocr(dataJson []byte) ([]byte, error) { 307 | _, err := p.cmdStdin.Write(dataJson) 308 | if err != nil { 309 | return nil, err 310 | } 311 | _, err = p.cmdStdin.Write([]byte("\n")) 312 | if err != nil { 313 | return nil, err 314 | } 315 | content := make([]byte, 1024*10) 316 | start := 0 317 | for { 318 | n, err := p.cmdStdout.Read(content[start:]) 319 | if err != nil { 320 | return nil, err 321 | } 322 | start += n 323 | if start >= len(content) { 324 | content = append(content, make([]byte, 1024*10)...) 325 | } 326 | if content[start-1] == '\n' { 327 | break 328 | } 329 | } 330 | return content[:start], nil 331 | } 332 | 333 | // Ocr processes the OCR for a given image represented as a byte slice. 334 | // It returns the OCR result as bytes and any error encountered. 335 | func (p *Ppocr) Ocr(image []byte) ([]byte, error) { 336 | if p.internalErr != nil { 337 | return nil, p.internalErr 338 | } 339 | var data = imageData{ContentB64: image} 340 | dataJson, err := json.Marshal(data) //auto base64 341 | if err != nil { 342 | return nil, err 343 | } 344 | 345 | p.ppLock.Lock() 346 | defer p.ppLock.Unlock() 347 | return p.ocr(dataJson) 348 | } 349 | 350 | type Data struct { 351 | Rect [][]int `json:"box"` 352 | Score float32 `json:"score"` 353 | Text string `json:"text"` 354 | } 355 | 356 | type Result struct { 357 | Code int 358 | Msg string 359 | Data []Data 360 | } 361 | 362 | const ( 363 | // CodeSuccess indicates that the OCR process was successful. 364 | CodeSuccess = 100 365 | // CodeNoText indicates that no text was recognized. 366 | CodeNoText = 101 367 | ) 368 | 369 | // ParseResult parses the raw OCR result bytes into a slice of Result structs. 370 | // It returns the parsed results and any error encountered during parsing. 371 | func ParseResult(rawData []byte) (Result, error) { 372 | var resp map[string]any 373 | err := json.Unmarshal(rawData, &resp) 374 | if err != nil { 375 | return Result{}, err 376 | } 377 | var result = Result{} 378 | var resData = make([]Data, 0) 379 | if resp["code"] == nil { 380 | return Result{}, fmt.Errorf("no code in response") 381 | } 382 | if resp["code"].(float64) != 100 { 383 | result.Code = int(resp["code"].(float64)) 384 | result.Msg = fmt.Sprintf("%v", resp["data"]) 385 | return result, nil 386 | } 387 | if resp["data"] == nil { 388 | return Result{}, fmt.Errorf("no data in response") 389 | } 390 | dataSlice, ok := resp["data"] 391 | if !ok { 392 | return result, fmt.Errorf("data is not array") 393 | } 394 | result.Code = CodeSuccess 395 | result.Msg = "parse success" 396 | 397 | var data []any 398 | data, ok = dataSlice.([]any) 399 | if !ok { 400 | return result, fmt.Errorf("data is not array") 401 | } 402 | for _, v := range data { 403 | str, err := json.Marshal(v) 404 | if err != nil { 405 | return result, err 406 | } 407 | var r Data 408 | err = json.Unmarshal(str, &r) 409 | if err != nil { 410 | return result, err 411 | } 412 | resData = append(resData, r) 413 | } 414 | result.Data = resData 415 | return result, nil 416 | } 417 | 418 | // OcrFileAndParse processes the OCR for a given image file path and parses the result. 419 | // It returns the parsed OCR results as a slice of Result structs and any error encountered. 420 | func (p *Ppocr) OcrFileAndParse(imagePath string) (Result, error) { 421 | b, err := p.OcrFile(imagePath) 422 | if err != nil { 423 | return Result{}, err 424 | } 425 | return ParseResult(b) 426 | } 427 | 428 | // OcrAndParse processes and parses the OCR for a given image represented as a byte slice. 429 | // It returns the parsed OCR results as a slice of Result structs and any error encountered. 430 | func (p *Ppocr) OcrAndParse(image []byte) (Result, error) { 431 | b, err := p.Ocr(image) 432 | if err != nil { 433 | return Result{}, err 434 | } 435 | return ParseResult(b) 436 | } 437 | 438 | const paddleocrTag = "paddleocr" 439 | 440 | // Deprecated: Only PaddleOCR-json v1.3.1 is supported. 441 | // 442 | // OcrClipboard processes the OCR for an image stored in the clipboard. 443 | // It returns the raw OCR result as bytes and any error encountered. 444 | func (p *Ppocr) OcrClipboard() ([]byte, error) { 445 | return p.OcrFile(clipboardImagePath) 446 | } 447 | 448 | // Deprecated: Only PaddleOCR-json v1.3.1 is supported. 449 | // 450 | // OcrClipboardAndParse processes the OCR for an image stored in the clipboard and parses the result. 451 | // It returns the parsed OCR results as a slice of Result structs and any error encountered. 452 | func (p *Ppocr) OcrClipboardAndParse() (Result, error) { 453 | return p.OcrFileAndParse(clipboardImagePath) 454 | } 455 | -------------------------------------------------------------------------------- /paddleocr_test.go: -------------------------------------------------------------------------------- 1 | package paddleocr 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestOcrArgs_CmdString(t *testing.T) { 8 | truePtr := new(bool) 9 | *truePtr = true 10 | intPtr := new(int32) 11 | *intPtr = 960 12 | 13 | tests := []struct { 14 | name string 15 | o OcrArgs 16 | want string 17 | }{ 18 | {"1", OcrArgs{Cls: nil, EnableMkldnn: nil, LimitSideLen: nil, UseAngleCls: nil}, 19 | ""}, 20 | {"2", OcrArgs{Cls: new(bool), EnableMkldnn: nil, LimitSideLen: nil, UseAngleCls: nil}, 21 | "cls=0"}, 22 | {"3", OcrArgs{Cls: truePtr, EnableMkldnn: nil, LimitSideLen: nil, UseAngleCls: nil}, 23 | "cls=1"}, 24 | {"4", OcrArgs{Cls: truePtr, EnableMkldnn: truePtr, LimitSideLen: nil, UseAngleCls: nil}, 25 | "cls=1 enable_mkldnn=1"}, 26 | {"5", OcrArgs{Cls: truePtr, EnableMkldnn: truePtr, LimitSideLen: new(int32), UseAngleCls: nil}, 27 | "cls=1 enable_mkldnn=1 limit_side_len=0"}, 28 | {"6", OcrArgs{Cls: truePtr, EnableMkldnn: truePtr, LimitSideLen: intPtr, UseAngleCls: nil}, 29 | "cls=1 enable_mkldnn=1 limit_side_len=960"}, 30 | {"7", OcrArgs{Cls: truePtr, EnableMkldnn: truePtr, LimitSideLen: intPtr, UseAngleCls: truePtr}, 31 | "cls=1 enable_mkldnn=1 limit_side_len=960 use_angle_cls=1"}, 32 | {"8", OcrArgs{Cls: truePtr, EnableMkldnn: truePtr, LimitSideLen: intPtr, UseAngleCls: truePtr, ConfigPath: ConfigChinese}, 33 | "cls=1 enable_mkldnn=1 limit_side_len=960 use_angle_cls=1 config_path=models/config_chinese.txt"}, 34 | } 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | if got := tt.o.CmdString(); got != tt.want { 38 | t.Errorf("OcrArgs.CmdString() = %v, want %v", got, tt.want) 39 | } 40 | }) 41 | } 42 | } 43 | 44 | func TestNewPpocr(t *testing.T) { 45 | type args struct { 46 | exePath string 47 | args OcrArgs 48 | } 49 | tests := []struct { 50 | name string 51 | args args 52 | wantErr bool 53 | }{ 54 | {"1", args{"", 55 | OcrArgs{}}, true}, 56 | {"2", args{`E:\path\to\your\PaddleOCR-json.exe`, 57 | OcrArgs{}}, false}, 58 | {"3", args{`.\PaddleOCR-json_v.1.3.1\PaddleOCR-json.exe`, 59 | OcrArgs{}}, false}, 60 | {"3", args{`PaddleOCR-json_v.1.3.1\PaddleOCR-json.exe`, 61 | OcrArgs{}}, false}, 62 | {"4", args{`.\PaddleOCR-json.exe`, 63 | OcrArgs{}}, true}, 64 | } 65 | for _, tt := range tests { 66 | t.Run(tt.name, func(t *testing.T) { 67 | p, err := NewPpocr(tt.args.exePath, tt.args.args) 68 | if (err != nil) != tt.wantErr { 69 | t.Errorf("NewPpocr() error = %v, wantErr %v", err, tt.wantErr) 70 | } 71 | if err == nil { 72 | p.Close() 73 | } 74 | }) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package paddleocr 2 | 3 | import "os" 4 | 5 | // 文件是否存在 6 | func fileIsExist(path string) bool { 7 | f, err := os.Stat(path) 8 | if err != nil { 9 | return os.IsExist(err) 10 | } 11 | if f.IsDir() { 12 | return false 13 | } 14 | return true 15 | } 16 | --------------------------------------------------------------------------------