chore: infrastructure alignment and doc sync (by checkall)

This commit is contained in:
AI Engineer 2026-05-14 21:49:44 +08:00
parent 6573dd6f24
commit aa56493bb2
5 changed files with 31 additions and 24 deletions

21
TODO.md
View File

@ -1,21 +0,0 @@
# @go/vision 演进路线图 (TODO)
## 🎨 1. 水印系统 (Watermarking) - [已完成]
- [x] **图片水印**: 支持 `func (c *Canvas) Watermark(mark *Canvas, pos Position, opacity float64)`
- [x] **文字水印**: 支持指定字体、颜色、旋转角度的快速文字遮盖。
- [x] **视频水印**: 封装 FFmpeg 指令,支持给视频一键添加静态水印。
## 🧩 2. 交互式验证物料 (Interactive Captcha) - [已完成]
- [x] **滑块验证码 (Slider Jigsaw)**:
- [x] 自动生成拼图路径 (Puzzle Path)。
- [x] 输出带阴影的“拼图块”与带“槽口”的底图。
- [x] **开箱即用**: 输出 `JigsawResult` 结构。
## 🎞 3. 动态验证码 (Dynamic GIF) - [已完成]
- [x] **GIF 动态验证码**: 不同帧出现不同字符,并伴随背景波形干扰。
## 🛠 4. 向量路径补完 (Vector)
- [ ] **SVG Path 基础指令**: 实现 `M, L, C, Q, Z` 的解析,支持业务中复杂的异形裁剪(如滑块形状)。
## 基础维护
- [ ] 移除超纲的视频编排计划,保持 FFmpeg 仅用于帧提取与基础合成。

View File

@ -74,3 +74,17 @@ func (c *Canvas) DecodeAll() (string, error) {
// 再尝试条形码 // 再尝试条形码
return c.DecodeBarcode() return c.DecodeBarcode()
} }
// Recognize 是 DecodeAll 的别名,用于自动识别图片中的任何码 (QR, Barcode)
func (c *Canvas) Recognize() (string, error) {
return c.DecodeAll()
}
// Recognize 从指定路径加载图片并自动识别其中的任何码 (QR, Barcode)
func Recognize(path string) (string, error) {
c, err := Load(path)
if err != nil {
return "", err
}
return c.DecodeAll()
}

2
go.mod
View File

@ -3,7 +3,7 @@ module apigo.cc/go/vision
go 1.25.0 go 1.25.0
require ( require (
apigo.cc/go/cast v1.3.1 apigo.cc/go/cast v1.3.2
apigo.cc/go/file v1.3.1 apigo.cc/go/file v1.3.1
apigo.cc/go/rand v1.3.0 apigo.cc/go/rand v1.3.0
github.com/boombuler/barcode v1.1.0 github.com/boombuler/barcode v1.1.0

4
go.sum
View File

@ -1,5 +1,5 @@
apigo.cc/go/cast v1.3.1 h1:Y64mit3tCtA1gnSaeaPNf9QjvwX1RA+hFc80j/yUMnI= apigo.cc/go/cast v1.3.2 h1:hh9MWDSwh3T/kQdCHjFpjDwHrh2A05Q4wt1AAWs8NBI=
apigo.cc/go/cast v1.3.1/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk= apigo.cc/go/cast v1.3.2/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk=
apigo.cc/go/encoding v1.3.0 h1:8jqNHoZBR8vOU/BGsLFebfp1Txa1UxDRpd7YwzIFLJs= apigo.cc/go/encoding v1.3.0 h1:8jqNHoZBR8vOU/BGsLFebfp1Txa1UxDRpd7YwzIFLJs=
apigo.cc/go/encoding v1.3.0/go.mod h1:kT/uUJiuAOkZ4LzUWrUtk/I0iL1D8aatvD+59bDnHBo= apigo.cc/go/encoding v1.3.0/go.mod h1:kT/uUJiuAOkZ4LzUWrUtk/I0iL1D8aatvD+59bDnHBo=
apigo.cc/go/file v1.3.1 h1:qHgiJsn1K9DazWRrPoHVnXtp6hDGGsUpAE/4G1bFXqY= apigo.cc/go/file v1.3.1 h1:qHgiJsn1K9DazWRrPoHVnXtp6hDGGsUpAE/4G1bFXqY=

View File

@ -24,6 +24,20 @@ func NewVideo() (*Video, error) {
return &Video{FFmpegPath: p}, nil return &Video{FFmpegPath: p}, nil
} }
// Capture 是从视频中提取指定时间的帧的便捷封装。
// 如果未提供 offsetSeconds默认提取第 0 秒。
func Capture(videoPath string, offsetSeconds ...float64) (*Canvas, error) {
v, err := NewVideo()
if err != nil {
return nil, err
}
offset := 0.0
if len(offsetSeconds) > 0 {
offset = offsetSeconds[0]
}
return v.ExtractFrame(videoPath, offset)
}
// ExtractFrame 从视频中提取指定时间的帧 // ExtractFrame 从视频中提取指定时间的帧
func (v *Video) ExtractFrame(videoPath string, offsetSeconds float64) (*Canvas, error) { func (v *Video) ExtractFrame(videoPath string, offsetSeconds float64) (*Canvas, error) {
tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("frame_%d.png", os.Getpid())) tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("frame_%d.png", os.Getpid()))