|
Download docs/zh/INFERENCE.md from FlanChanXwO/phpwind-captcha-ocr: direct link, hf CLI and curl.
- Browser
- Download file 3.98 kB
-
https://huggingface.co/FlanChanXwO/phpwind-captcha-ocr/resolve/main/docs/zh/INFERENCE.md
- Command line
-
hf download hf://FlanChanXwO/phpwind-captcha-ocr/docs/zh/INFERENCE.md
-
curl -L -o INFERENCE.md https://huggingface.co/FlanChanXwO/phpwind-captcha-ocr/resolve/main/docs/zh/INFERENCE.md
3.98 kB
| # 推理指南 | |
| ## 使用范围 | |
| 本指南仅适用于自有或已获明确授权测试的 PHPWind 部署。下列示例仅执行本地图片推理, | |
| 不用于自动化账户登录或绕过访问控制。 | |
| ## 规格速览 | |
| | 项 | 值 | | |
| |---|---| | |
| | 输入名 | `input` | | |
| | 输入形状 | `[batch, 3, 64, 160]` | | |
| | 输入类型 | float32, 值域 [0,1] (RGB) | | |
| | 输出名 | `logits` | | |
| | 输出形状 | `[batch, 4, 10]` | | |
| | 解码 | 每位置 argmax → 数字 | | |
| | ONNX opset | 18 | | |
| 预处理只有 3 步,无其他:RGB → 缩放到 160x64(双线性)→ 除以 255。 | |
| 不做灰度、不去噪、不做均值方差归一化。 | |
| ## Python | |
| ```bash | |
| pip install onnxruntime pillow numpy | |
| ``` | |
| ```python | |
| import numpy as np | |
| import onnxruntime as ort | |
| from PIL import Image | |
| _SESSION = None | |
| def get_session(model_path="model.onnx"): | |
| global _SESSION | |
| if _SESSION is None: | |
| _SESSION = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"]) | |
| return _SESSION | |
| def solve_captcha(img_bytes_or_path, model_path="model.onnx"): | |
| sess = get_session(model_path) | |
| im = Image.open(img_bytes_or_path).convert("RGB").resize((160, 64), Image.BILINEAR) | |
| x = np.asarray(im, dtype=np.float32) / 255.0 | |
| x = x.transpose(2, 0, 1)[None] # (1,3,64,160) | |
| logits = sess.run(None, {"input": x})[0] # (1,4,10) | |
| return "".join(str(int(logits[0, p].argmax())) for p in range(4)) | |
| code = solve_captcha("captcha.png") # "4821" | |
| ``` | |
| ## Go (onnxruntime_go) | |
| 依赖:`github.com/yalue/onnxruntime_go` + onnxruntime 共享库(.dylib/.so)。 | |
| ```go | |
| package main | |
| import ( | |
| "bytes" | |
| "image" | |
| _ "image/png" | |
| "golang.org/x/image/draw" | |
| ort "github.com/yalue/onnxruntime_go" | |
| ) | |
| const ( | |
| inW, inH = 160, 64 | |
| nDigits = 4 | |
| ) | |
| type Solver struct { | |
| sess *ort.Session[float32] | |
| in *ort.Tensor[float32] | |
| out *ort.Tensor[float32] | |
| } | |
| func NewSolver(modelPath, libPath string) (*Solver, error) { | |
| if libPath != "" { ort.SetSharedLibraryPath(libPath) } | |
| if err := ort.InitializeEnvironment(); err != nil { return nil, err } | |
| in, err := ort.NewEmptyTensor[float32](ort.NewShape(1, 3, inH, inW)) | |
| if err != nil { return nil, err } | |
| out, err := ort.NewEmptyTensor[float32](ort.NewShape(1, nDigits, 10)) | |
| if err != nil { in.Destroy(); return nil, err } | |
| sess, err := ort.NewSession[float32](modelPath, | |
| []string{"input"}, []string{"logits"}, | |
| []*ort.Tensor[float32]{in}, []*ort.Tensor[float32]{out}) | |
| if err != nil { in.Destroy(); out.Destroy(); return nil, err } | |
| return &Solver{sess, in, out}, nil | |
| } | |
| func (s *Solver) Solve(png []byte) (string, error) { | |
| src, _, err := image.Decode(bytes.NewReader(png)) | |
| if err != nil { return "", err } | |
| dst := image.NewRGBA(image.Rect(0, 0, inW, inH)) | |
| draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil) | |
| data := s.in.GetData() | |
| idx := 0 | |
| // 模型输入为 NCHW:先写全部 R,再 G,再 B | |
| for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { r,_,_,_ := dst.At(x,y).RGBA(); data[idx]=float32(r>>8)/255; idx++ } } | |
| for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { _,g,_,_ := dst.At(x,y).RGBA(); data[idx]=float32(g>>8)/255; idx++ } } | |
| for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { _,_,b,_ := dst.At(x,y).RGBA(); data[idx]=float32(b>>8)/255; idx++ } } | |
| if err := s.sess.Run(); err != nil { return "", err } | |
| got := s.out.GetData() | |
| code := make([]byte, nDigits) | |
| for p := 0; p < nDigits; p++ { | |
| best := 0 | |
| for c := 1; c < 10; c++ { if got[p*10+c] > got[p*10+best] { best = c } } | |
| code[p] = '0' + byte(best) | |
| } | |
| return string(code), nil | |
| } | |
| ``` | |
| > 注意:`onnxruntime_go.NewSession` 从文件读模型。若用 `go:embed` 内嵌模型,改用 `NewSessionWithONNXData` 传字节。 | |
| ## 性能 | |
| - 单张推理:约 5-15ms(CPU,M1/现代 x86) | |
| - 全流程(加载+预处理+推理):约 10-30ms | |
| - 无 GPU 依赖,内存占用 < 50MB | |