惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MyScale Blog
MyScale Blog
博客园 - 司徒正美
A
About on SuperTechFans
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
爱范儿
爱范儿
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
F
Fortinet All Blogs
S
SegmentFault 最新的问题
阮一峰的网络日志
阮一峰的网络日志
D
Docker
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
Jina AI
Jina AI
H
Help Net Security
量子位
IT之家
IT之家

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw
第08章 - Bitmap 位图与图像查找
我才是银古 · 2026-06-24 · via 博客园 - 我才是银古

第08章 - Bitmap 位图与图像查找

“在屏幕上找到某张小图并定位它的坐标”,是桌面自动化中极其实用的能力:找到按钮、找到图标、找到游戏中的某个元素,然后移动鼠标点击它。RobotGo 通过配套的 bitmap 子库(vcaesar/bitmap)提供了基于位图的查找能力。本章讲解位图的打开、保存、查找与定位。

8.1 为什么需要位图查找

第 6 章我们用“像素颜色”来识别界面状态,但单个像素信息量太小、容易误判。位图查找则是用“一整张小图”作为特征,在更大的截图(或整个屏幕)中寻找它出现的位置。相比硬编码坐标,它的最大优势是位置无关:哪怕目标元素移动了位置,只要外观不变,就能被找到。

位图查找属于“精确匹配”——要求目标在屏幕上的外观与模板图基本一致(同样的缩放、同样的颜色)。如果需要更鲁棒的、容忍缩放和细微差异的匹配,则要用第 12 章的 OpenCV 方案。

8.2 引入 bitmap 子库

位图查找的核心函数在 bitmap 子库中,需要单独导入:

import (
    "github.com/go-vgo/robotgo"
    "github.com/vcaesar/bitmap"
)

并在 Linux 上确保已安装 libpng 开发库(见第 2 章),因为位图功能依赖它。

8.3 获取位图

位图可以来自截图,也可以从文件打开。

8.3.1 从截图获取

bit := robotgo.CaptureScreen(10, 20, 30, 40)
defer robotgo.FreeBitmap(bit) // C 位图需释放

8.3.2 从文件打开

bit := bitmap.Open("template.png")
defer robotgo.FreeBitmap(bit)

8.3.3 从 image.Image 转换

如果你手上是 image.Image,可以先转成位图:

img, _ := robotgo.CaptureImg()
bit := robotgo.ToCBitmap(robotgo.ImgToBitmap(img))

8.4 保存位图

bit := robotgo.CaptureScreen(10, 20, 30, 40)
defer robotgo.FreeBitmap(bit)

bitmap.Save(bit, "test.png")

也可以借助前一章的转换函数把位图转为 image.Image 再用 robotgo.Save 保存。

8.5 查找位图:Find 与 FindAll

bitmap 子库的核心是查找函数。

8.5.1 Find:查找第一个匹配位置

bitmap.Find 在屏幕(或指定的大图)中查找给定的小位图,返回它左上角的坐标:

bit := bitmap.Open("button.png")
defer robotgo.FreeBitmap(bit)

fx, fy := bitmap.Find(bit)
fmt.Println("找到位置:", fx, fy)

if fx >= 0 && fy >= 0 {
    robotgo.Move(fx, fy)
    robotgo.Click()
}

如果没找到,返回的坐标通常为 -1(请以实际返回值判断,对负值做“未找到”处理)。

8.5.2 FindAll:查找所有匹配位置

当目标图在屏幕上出现多次时,用 FindAll 获取所有匹配位置:

bit := bitmap.Open("icon.png")
defer robotgo.FreeBitmap(bit)

arr := bitmap.FindAll(bit)
fmt.Println("所有匹配位置:", arr)

8.6 完整示例

下面是官方位图示例的整理版,演示从截图获取位图、保存、转换与查找:

package main

import (
    "fmt"

    "github.com/go-vgo/robotgo"
    "github.com/vcaesar/bitmap"
)

func main() {
    // 截取屏幕的一小块作为位图
    bit := robotgo.CaptureScreen(10, 20, 30, 40)
    defer robotgo.FreeBitmap(bit) // 释放 C 位图

    fmt.Println("位图:", bit)

    // 转为 image.Image 并保存
    img := robotgo.ToImage(bit)
    robotgo.Save(img, "test_1.png")

    // image -> Bitmap -> CBitmap,然后查找
    bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img))
    fx, fy := bitmap.Find(bit2)
    fmt.Println("FindBitmap:", fx, fy)
    robotgo.Move(fx, fy)

    // 查找所有匹配
    arr := bitmap.FindAll(bit2)
    fmt.Println("FindAll:", arr)

    // 直接用截图位图查找
    fx, fy = bitmap.Find(bit)
    fmt.Println("FindBitmap:", fx, fy)

    bitmap.Save(bit, "test.png")
}

8.7 典型工作流:截取模板 → 查找 → 点击

位图查找在实践中通常遵循这样一个工作流:

  1. 准备模板图:先手动截取目标元素(按钮、图标)的小图,保存为 PNG,作为模板。
  2. 运行时查找:脚本运行时打开模板图,在当前屏幕中 Find 它的位置。
  3. 执行操作:找到后移动鼠标到该坐标并点击。
func clickByImage(template string) bool {
    bit := bitmap.Open(template)
    defer robotgo.FreeBitmap(bit)

    fx, fy := bitmap.Find(bit)
    if fx < 0 || fy < 0 {
        return false // 没找到
    }
    robotgo.Move(fx, fy)
    robotgo.MilliSleep(100)
    robotgo.Click()
    return true
}

配合轮询,可以实现“等待某个按钮出现再点击”的健壮逻辑:

func waitAndClick(template string, timeoutMs int) bool {
    elapsed := 0
    for elapsed < timeoutMs {
        if clickByImage(template) {
            return true
        }
        robotgo.MilliSleep(300)
        elapsed += 300
    }
    return false
}

8.8 位图查找 vs 像素颜色 vs OpenCV

RobotGo 生态里有三种“看屏幕找东西”的层次,适用场景各不相同:

方法 所在库 特点 适用场景
像素颜色 GetPixelColor robotgo 主库 最轻量,只看单点颜色 判断简单的开关/状态
位图查找 bitmap.Find bitmap 子库 精确匹配整张小图 外观固定的按钮/图标定位
OpenCV 匹配 gcv gcv 子库 模板匹配 + 特征匹配,容错强 有缩放/细微差异的复杂识别

选择原则:能用像素就用像素(最快),需要定位元素用位图查找,遇到位图查找不稳定(缩放、抗锯齿、半透明等)再升级到 OpenCV。

8.9 实战技巧与注意事项

  1. 模板要“干净”:截取模板图时尽量只包含目标元素本身,避免包含会变化的背景,否则匹配率会下降。
  2. 保持环境一致:位图查找是精确匹配,模板图必须与运行环境的分辨率、缩放、主题保持一致。换了缩放比例往往就匹配不到了。
  3. 判断未找到:始终检查返回坐标是否为负,不要拿着 -1 去 Move
  4. 释放内存:所有 CaptureScreen / bitmap.Open / ToCBitmap 得到的 C 位图都要 FreeBitmap
  5. 性能考量FindAllFind 慢;在大屏上全屏查找也较慢。尽量缩小查找范围(先截取目标可能出现的区域)。
  6. 优先 PNG 模板:用无损的 PNG 作为模板图,避免 JPEG 压缩引入的像素差异影响匹配。

8.10 小结

本章我们学习了基于 bitmap 子库的位图查找:如何从截图或文件获取位图、保存位图,以及用 Find / FindAll 在屏幕上定位目标小图的位置。我们梳理了“截模板→查找→点击”的典型工作流,并对比了像素颜色、位图查找、OpenCV 三种识别方式的取舍。下一章我们回到主库,讲解更基础但同样重要的剪贴板操作。