znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第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.ImageCaptureImg 是更省心的选择——它返回的是 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 实战技巧与注意事项

  1. 优先 CaptureImg:除非要做位图查找需要 CBitmap,否则用 CaptureImgimage.Image 更省心,避免手动管理 C 内存。
  2. 务必释放 C 位图:凡是 CaptureScreen / ToCBitmap 等返回 CBitmap 的地方,配对 FreeBitmap / FreeBitmapArr
  3. 截图区域要合理:只截需要的区域可以显著提升性能,尤其是在循环中频繁截图时。
  4. 保存格式选择:PNG 无损、体积大,适合做模板匹配;JPEG 有损、体积小,适合传输和归档。做图像识别时优先 PNG。
  5. 多屏先设 DisplayID:对某块副屏截图前,先设置 robotgo.DisplayID 或用 GetDisplayBounds 计算坐标。
  6. 高分屏注意倍率:Retina 等环境下截图的物理像素可能是逻辑坐标的整数倍,做坐标换算时要考虑缩放。

7.10 小结

本章我们深入学习了 RobotGo 的截图与图像处理能力:用 SaveCapture 一行截图、用 CaptureScreen 获取需要手动释放的 C 位图、用 CaptureImg 获取省心的 image.Image、用 Save / SaveJpeg 保存,以及一整套图像类型转换函数和从文件解码图像的方法。我们特别强调了 C 位图的内存释放问题。下一章将基于截图能力,讲解“位图查找”——在屏幕上自动定位某张小图的位置。