第07章 - 屏幕截图与图像处理
截图是图像识别、自动化测试、AI 计算机使用等高级场景的基础。RobotGo 提供了从“一行代码保存截图”到“底层位图操作”的多层次截图 API,并配套了丰富的类型转换函数,方便与 Go 标准库 image 包以及第三方图像库互通。本章系统讲解截图、保存、图像转换的完整流程。
7.1 截图相关的核心类型
在深入函数之前,先认识 RobotGo 中几个关键的图像类型,理解它们之间的转换关系是用好截图功能的前提:
robotgo.Bitmap:RobotGo 自定义的位图结构(Go 侧)。robotgo.CBitmap:底层 C 位图的句柄(指向 C 的MMBitmapRef),由CaptureScreen返回。image.Image/*image.RGBA:Go 标准库的图像类型,便于与生态中其他库协作。[]byte/string:编码后的图像字节流,便于网络传输或存储。
这些类型之间可以通过一组转换函数互相转化,下文 7.6 节会给出完整对照表。
7.2 一行代码截图:SaveCapture
最简单的截图方式是 SaveCapture,它截图并直接保存为文件:
// 截取整个屏幕
robotgo.SaveCapture("full.png")
// 截取指定区域:起点 (x, y),宽 w,高 h
robotgo.SaveCapture("region.png", 10, 10, 300, 200)
对于只需要“拍个图存下来”的场景,SaveCapture 已经足够。
7.3 截图为位图:CaptureScreen
CaptureScreen 返回底层的 C 位图(CBitmap),适合需要进一步处理(如位图查找)的场景。特别重要:C 位图占用的是 C 侧内存,使用完毕后必须手动释放,否则会内存泄漏:
// 截取从 (10, 10) 开始、30x30 的区域
bit := robotgo.CaptureScreen(10, 10, 30, 30)
defer robotgo.FreeBitmap(bit) // 必须释放!
fmt.Println("位图:", bit)
// 转换为 Go 的 image.Image
img := robotgo.ToImage(bit)
不带参数的 robotgo.CaptureScreen() 会截取整个屏幕。养成 bit := robotgo.CaptureScreen(...) 后立刻 defer robotgo.FreeBitmap(bit) 的习惯,可以有效避免内存泄漏。
如需一次释放多个位图,可用 FreeBitmapArr:
bit0 := robotgo.CaptureScreen()
bit1 := robotgo.CaptureScreen(10, 10, 30, 30)
defer robotgo.FreeBitmapArr(bit0, bit1)
7.4 截图为 image.Image:CaptureImg
如果你更习惯直接使用 Go 标准库的 image.Image,CaptureImg 是更省心的选择——它返回的是 Go 侧的图像,无需手动释放 C 内存:
// 截取整个屏幕
img, err := robotgo.CaptureImg()
if err != nil {
fmt.Println("截图失败:", err)
return
}
// 截取指定区域
img2, _ := robotgo.CaptureImg(10, 10, 200, 200)
CaptureImg 在多数图像处理场景下比 CaptureScreen 更方便,推荐优先使用,除非你确实需要 C 位图(例如直接做位图查找)。
7.5 保存图像
RobotGo 提供了多种保存函数:
img, _ := robotgo.CaptureImg()
// 保存为 PNG
robotgo.Save(img, "screen.png")
// 保存为 JPEG,第三个参数是质量(0-100)
robotgo.SaveJpeg(img, "screen.jpeg", 50)
结合 imgo 库也可以保存:
import "github.com/vcaesar/imgo"
img := robotgo.ToImage(bit)
imgo.Save("test.png", img)
7.6 图像类型转换大全
RobotGo 提供了一整套类型转换函数,覆盖了上面提到的各种类型之间的互转。下表整理自官方文档(带 * 的为需要注意内存释放的 C 相关转换):
| 转换方向 | 函数 |
|---|---|
| Bitmap → CBitmap | ToCBitmap() |
| Bitmap → *image.RGBA | ToRGBAGo() |
| CBitmap → C.MMBitmapRef | ToMMBitmapRef() |
| CBitmap → Bitmap | ToBitmap() |
| CBitmap → image.Image | ToImage() |
| CBitmap → *image.RGBA | ToRGBA() |
| C.MMBitmapRef → CBitmap | CBitmap() |
| image.Image → Bitmap | ImgToBitmap() |
| image.Image → CBitmap | ImgToCBitmap() |
| image.Image → []byte | ToByteImg() |
| image.Image → string | ToStringImg() |
| *image.RGBA → Bitmap | RGBAToBitmap() |
| []byte → image.Image | ByteToImg() |
| []byte → CBitmap | ByteToCBitmap() |
| string → image.Image | StrToImg() |
借助这张表,你几乎可以在任意两种图像表示之间自由转换。例如把截图编码为 []byte 通过网络发送,对端再用 ByteToImg 还原。
7.7 从文件解码图像
DecodeImg 可以把磁盘上的图片文件读入为 image.Image:
img, _, err := robotgo.DecodeImg("test.png")
if err != nil {
fmt.Println("解码失败:", err)
return
}
这在图像查找(把预先保存的“模板图”读入再到屏幕上匹配)时非常常用。
7.8 完整示例:多屏截图
下面是官方屏幕示例的整理版,演示截图、转换、保存以及多屏遍历:
package main
import (
"fmt"
"strconv"
"github.com/go-vgo/robotgo"
"github.com/vcaesar/imgo"
)
func main() {
x, y := robotgo.Location()
fmt.Println("鼠标位置:", x, y)
color := robotgo.GetPixelColor(100, 200)
fmt.Println("颜色:", color)
sx, sy := robotgo.GetScreenSize()
fmt.Println("屏幕尺寸:", sx, sy)
// CaptureScreen 返回 C 位图,必须释放
bit := robotgo.CaptureScreen(10, 10, 30, 30)
defer robotgo.FreeBitmap(bit)
img := robotgo.ToImage(bit)
imgo.Save("test.png", img)
// 遍历每块显示器分别截图
num := robotgo.DisplaysNum()
for i := 0; i < num; i++ {
robotgo.DisplayID = i
img1, _ := robotgo.CaptureImg()
path1 := "save_" + strconv.Itoa(i)
robotgo.Save(img1, path1+".png")
robotgo.SaveJpeg(img1, path1+".jpeg", 50)
img2, _ := robotgo.CaptureImg(10, 10, 20, 20)
robotgo.Save(img2, "test_"+strconv.Itoa(i)+".png")
dx, dy, w, h := robotgo.GetDisplayBounds(i)
img3, err := robotgo.CaptureImg(dx, dy, w, h)
fmt.Println("截图错误:", err)
robotgo.Save(img3, path1+"_1.png")
}
}
7.9 实战技巧与注意事项
- 优先 CaptureImg:除非要做位图查找需要
CBitmap,否则用CaptureImg拿image.Image更省心,避免手动管理 C 内存。 - 务必释放 C 位图:凡是
CaptureScreen/ToCBitmap等返回CBitmap的地方,配对FreeBitmap/FreeBitmapArr。 - 截图区域要合理:只截需要的区域可以显著提升性能,尤其是在循环中频繁截图时。
- 保存格式选择:PNG 无损、体积大,适合做模板匹配;JPEG 有损、体积小,适合传输和归档。做图像识别时优先 PNG。
- 多屏先设 DisplayID:对某块副屏截图前,先设置
robotgo.DisplayID或用GetDisplayBounds计算坐标。 - 高分屏注意倍率:Retina 等环境下截图的物理像素可能是逻辑坐标的整数倍,做坐标换算时要考虑缩放。
7.10 小结
本章我们深入学习了 RobotGo 的截图与图像处理能力:用 SaveCapture 一行截图、用 CaptureScreen 获取需要手动释放的 C 位图、用 CaptureImg 获取省心的 image.Image、用 Save / SaveJpeg 保存,以及一整套图像类型转换函数和从文件解码图像的方法。我们特别强调了 C 位图的内存释放问题。下一章将基于截图能力,讲解“位图查找”——在屏幕上自动定位某张小图的位置。