FindText 深度教程 v1.2

FindText 详细教程

感谢 FindText 作者 feiyue及本教程作者 Decolada 所做的工作!       ——译者:北极星


非常感谢用户 feiyue 创建了 FindText,另外也感谢用户 ed1chandlerc4p,他们的基础教程让我受益非浅。

FindText 8.8中文版链接:https://www.autoahk.com/archives/28493

简介

FindText 是一个 AHK 库,可以替代内置的 ImageSearch 功能,并包含许多附加功能。FindText 主要用于在屏幕上快速查找图形(图像),其匹配精度比 ImageSearch 高。它将屏幕上抓取一个小图像转换成黑白图像(类似于 ASCII 艺术),然后再将黑白图像转换成单行文本。使用 FindText 时,它会截取屏幕截图,将屏幕截图中的每个像素也转换成黑白,然后尝试将抓取的图像与屏幕截图进行匹配。由于像素变成了黑白,要比较的次数要少得多(红、绿、蓝需要比较三次、黑白只需要比较一次),所以匹配速度要比 ImageSearch 快。

FindText 的一些优点:

1)它不需要图像文件,而是使用图像的文本表示。该函数将屏幕的图像抽象为具有代表意义的“0”(“0”=黑色像素=文本像素)和“_”(“_”=白色像素=背景像素),它们依次排列变成一行数字和字母序列。因为这是一种抽象,而不是逐位比较,所以它可以实现快速匹配及轻松调整容错。

2)它比ImageSearch更快,在我的测试中大概快了10%(在有些用例中甚至可能快2-3倍)。

3)提供了轻松存储及在脚本中获取图像的函数。

4)使用 BindWindow,实现了当窗口藏在其他窗口后面时也能使用 FindText。

5)允许创建简单的自定义 OCR 函数。

目录

(1) 入门

(2) FindText 主要功能

(3) ImageSearch

(4) 什么是Text?

(5) 抓取模式

(6) 辅助函数 Click, MouseTip, RangeTip, ScreenToWindow, ScreenToClient, ClientToScreen, WindowToScreen

(7) 文本库函数 PicLib, PicN, PicX, PicInfo

(8) FindText 组合查找

(9) 光学字符识别 (OCR)

(10) BindWindow 搜索遮挡窗口文本

(11) FindTextClass

(12) 截图功能:ScreenShot、GetTextFromScreen、SavePic、ShowPic、ShowScreenShot

(13) 杂项:Sort、Sort2、Sort3、WaitChange

(14) FindText 速度提升技巧

(1) 入门

下载 FindText.ahk 后,建议将它放在 Lib 文件夹中,然后在自己的脚本中通过以下代码来引入它:

#include <FindText>

做完这些后,你可以使用 FindText().ImageSearch 来替换掉原有的 ImageSearches,从而提升速度,如下所示:

ImageSearch, OutputVarX, OutputVarY, X1, Y1, X2, Y2, ImageFile
 ; 变成这样
FindText().ImageSearch(OutputVarX , OutputVarY, X1, Y1, X2, Y2, ImageFile)

但是使用 FindText 的推荐方法是:使用 FindText 主函数,并使用文本而非图像文件。为此,先运行 FindText.ahk 脚本,运行后应该会显示出以下 GUI :

FindText 深度教程 v1.2

本截图由 AHK1-僵尸 制作提供

抓图 按钮 -> 选择要抓取的区域 -> 右键单击两次(第一次右击选择,将鼠标移动到其他位置后再次右击以确认)-> 按灰度阈值二值化按钮 -> 按 确定。如果一切顺利,那么现在按下 测试 按钮后,FindText 应该会成功找到之前抓取的图像(找到图像的位置会出现一个闪烁的红色框)。

FindText 深度教程 v1.2

本截图由 AHK1-僵尸 制作提供

抓取图像后,在下方的文本框会显示一些自动生成的代码,如下所示:

Text:="|<>*151$101.000000000000000000000000U0000M0000000000100000k0000000400200011U0000000A0040002300000000M008000460000DVUnw7kHk7kzAA7X0vX1XUtkxktksMkvW036331UlVVUkUn11a02A6661X161V1g63A1wMAAA343A323kDy8DMkMMM286M247UM0NkFUkkkAEAkA8BUk0nVX1VVUMUNUMEPVU0b33331UV0lUUUnXU1zS7S63b21Xb1dX3a1bY7g71s431s1n33s300000000000000006000000000000000080000000000000000k00000000000000034"

if (ok:=FindText(X, Y, 312-150000, 79-150000, 312+150000, 79+150000, 0, 0, Text))
{
  ; FindText().Click(X, Y, "L")
}
...

Text 变量包含文本/字符串形式的图像,以及一些其他必要信息。

FindText(X, Y, 312-150000, 79-150000, 312+150000, 79+150000, 0, 0, Text) 表示变量 X 和 Y 将被赋值为找到的第一个图像的确切中心的坐标;数字 31279 是最初抓取图像的 x 和 y 坐标,312-15000079-150000 只是搜索区域左上角的一些非常负的 x 和 y 坐标(包括屏幕外的窗口或第二个显示器);312+15000079+150000 是搜索区域右下角的一些非常大的值;00 是默认误差范围(表示只允许完全匹配);Text 变量是我们截取图像的文本形式,提供给 FindText 使用。

• 如果删除 FindText().Click 之前的注释符号(;),则表示在 FindText 成功后,使用左键单击找到的图像。

接下来的部分将包含一些示例脚本,可以把它们复制粘贴到 FindText main Gui 的示例代码框顶部,然后按 测试 按钮运行这些脚本。将以下图像保存到计算机,并使用 Paint 或其他看图软件打开。在按下 测试 之前确保图片可见,屏幕 DPI 设置为 100%。

FindText 深度教程 v1.2

(2) FindText 主函数

下面的大部分内容摘自 FindText.ahk 顶部描述区(作了一些补充):

returnArray := FindText(
    OutputX --> 该变量用于存储找到的第一张图片中心的 X 坐标。OutputX 也可以设为"wait"、"wait1" (appear) 或"wait0" (disappear),以使 FindText 要么等待文本出现或消失。如果将 OutputX 留空,则不进行搜索,并返回一个 FindTextClass 实例:在 FindText().Click 示例中使用了这样的代码,等价于 FindText(,,,,,,,,Text).Click (Text 会被忽略)。调用 FindText 执行搜索的最简单形式为 FindText(X,,,,,,,,Text).
    , OutputY --> 该变量用于存储找到的第一张图片中心的 Y 坐标。如果 OutputX 设为 "wait",则 OutputY 可以设置为等待的秒数,负数表示无限等待。[/list]
    , X1 --> 搜索区域左上角的 X 坐标
    , Y1 --> 搜索区域左上角的 Y 坐标
    , X2 --> 搜索区域右下角的 X 坐标
    , Y2 --> 搜索区域右下角的 Y 坐标
    如果 X1, Y1, X2, Y2 都设为 0 (默认值),则 X1 和 Y1 会被设为 -150000,X2 和 Y2 被设为 150000。这样会将搜索区域设为整个屏幕以及所有不在屏幕上的东西也包括在内。
    , err1 --> 文本的容错比例(text=黑色="o")(0.1=10%)。
    , err0 --> 背景的容错比例(background=白色="_") (0.1=10%)
    如果 err1 和 err0 都为 0,并且没有找到匹配项,则 FindText 会自动使用 0.05=5% 错误容限再次进行搜索。为避免这种情况,可以将 err1 和 err0 指定为很小的一个值(如 0.000001,很接近 0,但是不等于 0)。
    , Text --> 可以是多个文本形式的图片,图片之间用 "|" 分隔
    , ScreenShot --> 取值为 0 时,使用上一次截屏,默认为 1(每次都重新截屏)
    , FindAll --> 取值为 0 时,找到第一个结果后立刻返回,默认为 1(查找全部结果)
    , JoinText --> 用于组合查询:可以是 1,也可以是要查找的单词数组,默认为 0
    , offsetX --> 设置组合查找时 X 方向最大文本偏移量
    , offsetY --> 设置组合查找时 Y 方向最大文本偏移量
    , dir --> 指定搜索方向,共9种取值:
    1 ==> ( 从左到右 ) 从上到下
    2 ==> ( 从右到左 ) 从上到下
    3 ==>(从左到右)从下到上
    4 ==>(从右到左)从下到上
    5 ==>(从上到下)从左到右
    6 ==>(从下到上)从左到右
    7 ==>(从上到下)从右到左
    8 ==>(从下到上)从右到左
    9 ==>从中心向外
)

该函数返回一个包含所有查找结果的二阶数组(returnArray[1] 表示找到的第一个结果,returnArray[2] 表示第二个,…),每个结果都是一个关联数组:{1:X, 2:Y, 3:W, 4:H, x:X+W/2, y:Y+H/2, id:Comment}

如果没有找到图像,则 FindText 返回 0

例如,返回变量的名称是 “ok”, 则 ok[1] 表示找到的第一个结果。

ok[1][1] 和 ok[1][2] 是找到的第一个图像左上角的 X 和 Y 坐标,
ok[1][3] 是找到图像的宽度,而 ok[1][4]是找到图像的高度,
ok[1].x <==> ok[1][1]+ok[1][3]/2 <==> OutputX(找到图像中心的 X 坐标),
ok[1].y <==> ok[1][2]+ok[1][4]/2 <==> OutputY(找到图像中心的 Y坐标 ),
ok[1].id 是包含在 Text 中 <> 部分的注释文本。

如果 OutputX 设置为“wait”、“wait1”(等待“出现”)或“wait0”(等待“消失”),出现超时则意味着搜索失败并返回 0,其他值表示成功。如果等待“出现”并且找到图像,则 FindText 返回找到的数组对象。如果等待“消失”且找不到图像,则 FindText 返回 1。

FindText(X:="wait", Y:=3, 0,0,0,0,0,0,Text)   ; 等待 3s 看是否出现
FindText(X:="wait0", Y:=-1, 0,0,0,0,0,0,Text) ; 一直等待到消失

更多注意事项:

• 所有返回的坐标都是相对于屏幕而言的绝对坐标(就像是设置了 CoordMode Screen 模式一样)。使用 ScreenToWindow 和 ScreenToClient,可以转换为相对坐标。

• 所有颜色均为十六进制 RGB 格式。

• FindText 对屏幕 DPI 敏感,这意味着使用一种 DPI 抓取的图像/文本无法在另一种 DPI 下使用。为确保用户使用正确的 DPI,请预先使用内置变量 A_ScreenDPI 进行检查。

• 使用多台显示器时,请注意将 X1,Y1,X2,Y2 设为 0,0,A_ScreenWidth,A_ScreenHeight 这一情形,因为此设置只会抓取主显示器。建议将它们留空(默认值 -150000,-150000,150000,150000 应该足够大,可以覆盖所有显示器),或者使用与默认值类似的大值。

对于以下示例,请使用 画图软件 打开第 1 节提供的 Examples.png。然后在新脚本中运行代码,或者将 #include 行以外的所有代码复制到 FindText Gui 代码框,然后按 测试 按钮进行测试。

#include <FindText>
Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8" ; 图片中 "autohotkey.com" 的 "auto" 部分。图像 Id 为 "auto" (位于 < 和 > 之间的字符)。
ok := FindText(outX, outY,,,,,,,Text) ; 调用 FindText 搜索 "auto" 图像。outX 和 outY 将被设置为最先找到结果的 X 和 Y 坐标。搜索范围坐标、err1 和 err0 都留空以使用默认值(搜索整个屏幕,并使用完全匹配)。结果会保存在 "ok" 变量中。
if ok { ; 检查 "ok" 是否为 0
    MsgBox, 找到的第一个图像(文本)位置:X: %outX% Y: %outY% ; 显示坐标 outX 和 outY
    MsgBox, % "共找到" ok.MaxIndex() "个结果。" ; ok.MaxIndex() 和 ok.Length() 返回一共找到几个结果。
    MsgBox, % "找到的第一个图像位于 X" ok[1][1] " Y" ok[1][2] ". 图像宽度为 " ok[1][3] " 图像高度为 " ok[1][4] ",其 id 为" ok[1].id
    if ok[2] ; ok[1] 表示第一个结果, ok[2] 表示第二个结果,依此类推。检查是否存在第二个结果,如果存在,则显示一些内容。
        MsgBox, % "找到的第二个图像位于 X" ok[2][1] " Y" ok[2][2] " 图像宽度为 " ok[2][3] " 图像宽度为 " ok[2][4] ",其注释文本为" ok[2].id
} else {
    MsgBox, 图像/文本未找到。请确认所有设置无误,且图像在 Paint 中可见! ; "ok" 变量中结果为空,表示未找到任何结果。
}
#include <FindText>

if (ok := FindText(outX, outY, 0, 0, A_ScreenWidth, A_ScreenHeight, 0.05, 0.05, "|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8")) { ; 调用 FindText 搜索 "auto" 图像。outX 和 outY 将被设置为最先找到结果的 X 和 Y 坐标。搜索时会覆盖整个屏幕,从左上角 (0;0) 到右下角 (A_ScreenWidth; A_ScreenHeight),多个显示器时可能不生效。"1" 和 "0" 的误差范围都设为 5%。搜索结果保存在 "ok" 变量,如果 "ok" 包含了任何结果(即搜索成功),则 "if" 的条件为真。
    for k, v in ok { ; 遍历 "ok" 中的所有搜索结果. "k" 表示第几个结果,"v" 表示结果本身。
        MsgBox, % "第" k " 个结果:X坐标:" v[1] " Y坐标:" v[2] " 图像宽度:" v[3] " 图像高度:" v[4] "。另外,图像的注释文本为" v.id ; v[1] 等价于 ok[k][1], v.id 等价于 ok[k].id,依此类推。
    }
} else {
    MsgBox, 图像/文本未找到。请确认所有设置无误,且图像在 Paint 中可见! ; "ok" 变量中结果为空,表示未找到任何结果。
}
#include <FindText>
Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8" ; 图片中 "autohotkey.com" 的 "auto" 部分。图像 Id 为 "auto" (位于 < 和 > 之间的字符)。
Text.="|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s" ; 将图片"autohotkey.com"中的"hot"部分附加到 Text 变量(注意使用 ".=" 运算符进行附加,之前使用的是 ":=" 进行赋值)。图像 Id 为 "hot"。
; 上两行代码等同于 Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s"

WinGetPos, pX, pY, pW, pH, ahk_class MSPaintApp ; 获取画图软件窗口的位置和大小

if !(ok := FindText(X, Y, pX, pY, pX+pW, pY+pH, 0.000001,, Text)) { ; 调用 FindText 查找 "auto" 或 "hot" 图像。X 和 Y 变量将设置为第一个找到图像的 X 和 Y 坐标。搜索范围限定为画图应用,将至少一个误差范围的值设为一个很小的非零值,以避免使用 5% 作为误差范围进行第二次查找。结果会保存在 "ok" 变量中。如果  "ok" 为空("!" 为取反运算符),则退出,否则继续。
    MsgBox, 图像/文本未找到。请确认所有设置无误,且图像在 Paint 中可见! ; "ok" 变量中结果为空,表示未找到任何结果。
    ExitApp
}
; 只有在找到 "hot" 或 "auto" 图像时,才会执行下面这部分内容。

for key, value in ok { ; 遍历 "ok" 中的所有结果。"key" 表示第几个结果,"value" 表示结果本身。
    FindText().MouseTip(value.x, value.y) ; 在结果的中心位置显示一个闪烁的框。
    MsgBox, % "第" key "个结果位于 X" value[1] " Y" value[2] " 图像宽度为" value[3] "图像高度为" value[4] ",其注释文本为" value.id ; value[1] 等价于 ok[k][1], value.id 等价于 ok[k].id, 依此类推。
    if (value.id == "auto")
        MsgBox, 找到 "auto" 图像。
}

(3) ImageSearch

FindText().ImageSearch 是 FindText 的包装函数,它更易于使用。它还可以像 AHK 原生函数 ImageSearch 一样接受图像文件(但不支持 *IconN 等选项),并且它支持不同的 CoordMode(FindText 总是返回相对于屏幕的坐标,但 FindText().ImageSearch 可以使用任何一个由 CoordMode Pixel 命令设置的坐标模式,例如 Window 或 Client 坐标模式)。

FindText().ImageSearch(OutputVarX, OutputVarY, X1, Y1, X2, Y2, TextOrImageFile, Screenshot:=1, FindAll:=0)

returnValue := FindText().ImageSearch(
    OutputVarX, OutputVarY --> 存储找到图像中心的 X 和 Y 坐标的变量名。除非用 CoordMode 来更改坐标模式,否则使用相对坐标。
    ,X1, Y1 --> 搜索矩形左上角的 X 和 Y 坐标。除非用 CoordMode 来更改坐标,否则使用相对坐标。
    ,X2, Y2 --> 搜索矩形右下角的 X 和 Y 坐标。除非用 CoordMode 来更改坐标,否则使用相对坐标。
    , TextOrImageFile --> FindText 文本或图像文件的文件名
    , Screenshot --> 如果值为 0,则使用最后一次截图,默认为 1(每次重新截图)
    , FindAll --> 如果值为0,则在找到第一个结果后返回,默认为1(查找所有结果)
)

如果搜索成功,returnValue 将被设置为 1,否则为 0。

如果搜索成功,ErrorLevel 将被设置为 0,否则为 1。

例子:

#include <FindText>
if FindText().ImageSearch(X,Y,,,,, A_ScriptDir "\Examples.png") ; 在屏幕上查找 Examples.png 中的图像
    FindText().MouseTip(X, Y)
if FindText().ImageSearch(X,Y,,,,, "|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8") ; 查找 "auto" 图像,其内容包含在 Text 中
    FindText().MouseTip(X, Y)

(4) 什么是 Text 文本?

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8" 
; 按从左到右顺序:"|" 是分隔符
; "<auto>" 表示 Text 的 id/comment 为 "auto"
; "*" 是捕获模式,本例中为 Gray
; 159 是灰度阈值
; 40 (between $ and .) 是原图像的宽度
; 0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8 是图像的 64-bit 表示

Text 文本字符串由 5 部分组成:

1.竖线字符(|)是图像的分隔符。这意味着可以将多个图像一个接一个地连接在一起,进行同时搜索(或用于组合搜索)。

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8" ; 首先设置 Text 变量包含 "auto" 图像
Text.="|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s" ; 使用 ".=" 运算符向 Text 变量添加 "hot" 图像
ok:=FindText(X,Y,,,,,,,Text)
for k, v in ok
    FindText().MouseTip(v.x, v.y)

等价于以下代码:

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s"
ok:=FindText(X,Y,,,,,,,Text)
for k, v in ok
    FindText().MouseTip(v.x, v.y)

将返回匹配“auto”和“hot”图像的所有结果。

2. <> 字符之间是图像的“id”或“comment”,用于为图像命名。如果 ok:=FindText(…) 成功,则 ok[1].id 将包含找到的第一个图像的 id。id 可以包含任何字母、数字和符号(如果使用 AHK 的 Unicode 版本,则可包括所有 Unicode 字符),”>” 和换行符除外。

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8" ; "<auto>" 设置图像 id 为 "auto".
Text.="|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s" ; "<hot>" 设置图像 id 为"hot".
ok:=FindText(X,Y,,,,,,,Text)
for k, v in ok { ; 遍历所有结果
    if v.id == "hot" { ; 只显示结果中 id 为"hot"的图像,忽略 id 为 "auto" 的结果
MsgBox, % "The ""hot"" image was found at X:" v.x " Y:" v.y
    }
}

3. > 和 $ 之间的数字和符号将定义用于搜索的模式。有 5 种不同的模式,我们稍后将在“抓取模式”部分中介绍。在“auto”图像的这些示例中,* 表示正在使用 Gray 灰度阈值模式,154 是阈值。

4. $ 和 . 之间的数字是图像的像素宽度。这个数值不应该手动更改,应使用 Cut 按钮进行修改。在第一个示例中,图像“auto”的宽度为 40 像素。

5. 句点(.)之后的所有内容是图像的 64 位文本表示。这里的内容也不应该手动更改,应使用 Cut 按钮进行修改。

6. 有一种在 Text 中包含 err1 和 err0 的非正式方式,它将覆盖传递给 FindText 函数的 err1 和 err0 值。这可以通过在 id 之后添加 [err1,err0] 来完成:

Text:="|<mycomment>[0.2,0.1]*197$15.1U8A11U8C11k8600s0700Q01U004" ; 覆盖 err1 的取值为 0.2 (20%) 、err0 的取值为 0.1 (10%)
ok:=FindText(X,Y,,,,,0.3,0.3,Text) ; 即使 err1 和 err0 均设为 0.3,在搜索时也会使用 Text 中的 0.2 和 0.1

(5) 抓取模式

1. Gray 灰度阈值模式。

FindText 深度教程 v1.2

在灰度模式下,每个像素的灰度值(取值从 0 到 255,其中 0 为纯黑色,255 为纯白色)将与指定阈值进行比较。如果像素灰度值超过阈值,则将其变为白色(white=background=”_”=”0″),否则变为黑色(black=text=”o”=”1″)。因此,任何比阈值更暗的像素都变成黑色,而任何比阈值更亮的像素都变成白色。阈值 255 会使整个图像变黑,从而匹配搜索范围内的所有内容。

在 Text 文本的形式中,灰度阈值模式* 字符标识,后跟阈值。

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8" ; "<auto>" 之后的 "*" 表示使用的是灰度模式,阈值为 159 (位于 * 和 $ 之间), 图像宽度为 40 像素 (位于 $ 和 . 之间)

2. GrayDiff 灰度差值模式。

FindText 深度教程 v1.2

每个像素都会被一一考虑到,如果所考虑的像素周围的任一像素的灰度值大于该像素灰度值+阈值(总共比较 8 次),则该像素将变为黑色,否则将变为白色。从本质上讲,它会生成形状的边缘,因为最大的灰度差值出现在边缘等对比度较大的区域,而纯色区域则变为白色。在 Examples.png 的 Gray vs GrayDiff 示例中,如果我们希望 FindText 只找到最右边的 2 个纯色正方形,使用 Gray 灰度阈值模式是无法成功的,但 GrayDiff 灰度差值模式却可以把它们成功分离出来(见下面的示例)。

在 Text 文本的形式中,灰度差值模式** 字符标识,后跟阈值。

#include <FindText>
Text:="|<solidcolorbox>**50$54.zzzzzzzzzU00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001U00000001zzzzzzzzzU" 
; "**" 表示 GrayDiff 模式,阈值设为 50, Text中图像宽度为 54 像素
ok:=FindText(X,Y,,,,,,,Text)
for k, v in ok
    FindText().MouseTip(v.x, v.y)

3. Color 颜色相似模式。

FindText 深度教程 v1.2

在 FindText Gui 中,单击图像上的某个像素会选择该像素的颜色,并将其定义为“黑色”。然后可以用相似度滑块来包含相似的颜色(相同颜色的阴影),单击颜色相似二值化按钮应用滑块所做的更改。

在 Text 文本的形式中,颜色相似模式由所选颜色的十六进制表示进行标识,后跟@滑块值(相似度取值从 0.00 到 1.00,1.00=100% )。

Text:="|<>505050@1.00$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s" 
; 只有 505050 这个颜色被视为 Text 文本(黑色像素)
; 相似度被设为 1.00 (100%),表示只允许完全匹配

4. ColorPos 颜色位置模式。

FindText 深度教程 v1.2

单击图像上的像素将定义该像素相对于图像左上角的位置,该位置的颜色将用于黑白色的转换。因此,此模式将找到单一颜色的图案,其中颜色取自指定位置的像素。和颜色相似模式一样,该模式也有一个相似度滑块,可以检测相似的颜色。

在 Text 文本的形式中,颜色位置模式# 标识,后跟一个数字,该数字是选定的像素位置(从左上角起算,从左到右,然后从上到下),然后是 @滑块值

Text:="|<>#49@0.90$47.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz07zzzzzy07zzzwDwQ7zzzsTsyDzzzkzlwQ1w007Xsk1k00D7V11127yC2D2DwDw08T47sTs0kyA0kzlzVwQ0VzXz3szV3z7z7VzW7yDy22667wTy0A0A1szy0s0w3zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
; # 表示 ColorPos 模式
; 49 表示第 49 个像素(从左至右、从上到下)被视为“黑色”。由于图像宽度是 47,49 对应于第二行第二个像素
; 0.90 为相似度 (90%)

5. ColorDiff 颜色分量模式。

FindText 深度教程 v1.2

在 FindText Gui 中,单击图像上的像素会选择该像素的颜色,并将其定义为“黑色”。颜色分量二值化按钮将使用所选颜色将图像转换成黑白色。现在,红、绿 三个输入框可用于选择允许的颜色差异(加或减,但不会从 255 环绕到 0,反之亦然)。例如,如果选择的颜色是 0098D9(十六进制 R=00,G=98,B=D9)并且输入框设置为 1,那么 FindText 将匹配颜色 0098D9 和 0198D9(但不是 FF98D9-0198D9,因为它不环绕)。然后将绿设置为 5 将匹配 0093D9 和 019DD9 之间的颜色。

Text:="|<>3F627F-00050A$51.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzk1zzzzzzy07zzzwDzlUTzzzVzyD3zzzwDzlwM1s007yDW06000zlsE0EEVzyA0D27wDzk0VsETVzy0ADW0ADzlzVwM0VzyDwD3w4DzlzVsTsVzyDy02467zlzk0k0k7yDz0C0C0zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzw"
; 定义为 "黑色" 的颜色是 3F627F。
; 允许的颜色误差范围是 00050A:红色 00 (十进制的 0)、绿色 05 (十进制的 5)、蓝色 0A (十进制的 10)。

6. MultiColor 多色查找模式。

FindText 深度教程 v1.2

单击多色查找复选框后,就可以选择多个像素,FindText 将仅在其相对坐标处查找选定的像素(第一个选定像素将位于 x=0、y=0,后面的像素将相对于该坐标),并且只查找选定像素的特定颜色。例如,如果在此模式下选择了两个像素,则 FindText 将仅搜索所选颜色且相对坐标相同的两个像素。RGB 滑块可用于更改误差范围。在Text 文本的形式中,多色查找模式## 标识,后跟一个数字表示 RGB 误差范围 (0-255)。然后在 $ 符号之后,将跟随所选像素的相对坐标和颜色:例如“0/0/3A8000,1/0/008000”表示将搜索颜色为3A8000008000 的两个像素,

多色查找模式也可用于查找文件中的图像。这可以通过指定文件位置而不是单个像素来完成。FindText().ImageSearch 函数在内部使用了此方法。

Text:="|<>##1$0/0/3F627F,-7/1/FFFFFF"
; ## 表示 MultiColor 模式
; RGB误差范围:1
; 匹配位置像素颜色为 3F627F (R、G、B 误差范围 ± 1:匹配 3E617E - 406380),第二个像素位置为向左7像素、向下1像素,像素颜色为 FFFFFF (R、G、B 误差范围 ± 1)

Text := "|<>##0$" A_ScriptDir "\Example.png" ; 在 FindText 中使用此 Text 将查找脚本目录的 Example.png,RGB 误差范围设为 0

(6) 辅助函数 Click、MouseTip、RangeTip、ScreenToWindow、ScreenToClient、ClientToScreen、WindowToScreen

1. FindText().Click(x, y, options) 的参数和 AHK 中的 Click 函数相同,但是在单击期间它将 CoordMode Mouse 设置为 Screen(绝对坐标)

FindText().Click(100, 200) ; 在屏幕坐标为 x 100, y 200 处左键单击

2. FindText().MouseTip(x:="", y:="", w:=10, h:=10, d:=4)会在指定的 x 和 y 坐标处创建一个闪烁的红色方块,通过直观的显示帮助确认正在使用正确的位置。如有需要,可以更改方框的宽度、高度及其边框的厚度。

FindText().MouseTip(100, 200) ; 在屏幕坐标 x 100, y 200 处闪烁一个红框

3. FindText().RangeTip(x:="", y:="", w:="", h:="", color:="Red", d:=2) 是 MouseTip 更一般的变体形式,它将显示指定大小、颜色和边框粗细的红色框。请注意,它不会像 MouseTip 那样自动消失。以下示例将在找到的图像边缘周围创建一个闪烁的矩形。

if (ok:=FindText(X,Y,,,,,,,"|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s")) {
    Loop 4
    {
        FindText().RangeTip(ok[1].1, ok[1].2, ok[1].3, ok[1].4, (A_Index & 1 ? "Red":"Blue"), 4) ; 在找到图像的边缘显示一个矩形框,边框颜色在红色和蓝色间每半秒切换一次。
        Sleep, 500
    }
}

4、ScreenToClient、ScreenToWindow、WindowToScreen、ClientToScreen 在不同 CoordMode 之间转换坐标。

FindText().ScreenToClient(outX, outY, x1, y1, ahk_id:="") 会将 x1 和 y1 从屏幕坐标转换为相对于 id 为 ahk_id 的窗口的客户端坐标,并将 outX 和 outY 设置为获得的结果值。例如,这在使用 ControlClick 时很有用:FindText 将返回相对于屏幕的坐标,但 ControlClick 使用相对于窗口的坐标,因此可以使用 ScreenToWindow

if (ok:=FindText(X,Y,,,,,,,"|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s")) {
    FindText().ScreenToWindow(outX, outY, X, Y, WinExist("ahk_class MSPaintApp")) ; 将绝对坐标 X 和 Y 转换为相对于画图窗口的相对坐标,结果存储在 outX 和 outY 变量中。
    ControlClick, X%outX% Y%outY%, ahk_class MSPaintApp ; 在画图程序中单击之前存储的坐标位置
}
FindText().WindowToScreen(outX2, outY2, 200, 300) ; 如果未指定 ahk_id,则使用活动窗口;将相对坐标 X200 Y300 转换为绝对坐标,结果保存在 outX2 和 outY2 中。

(7) 文本库函数PicLib、PicN、PicX、PicInfo

1. PicLib 可用于创建图像/文本库,以便使用Text ID(comment)轻松获取文本。

FindText().PicLib(commentOrText, addToLib:=0, libraryIndex:=1)

可以通过将 addToLib 设置为 1 来添加新文本,并且 libraryIndex 可以指定要添加文本的库编号。如果多个 Text 具有相同的 id,则仅添加最后一个。

FindText().PicLib("|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8", 1) ; 将 "auto" Text 添加至默认的 1 号库
FindText().PicLib("|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s",1,2) ; 将 "hot" Text 添加至 2 号库

可以通过 id 从库中获取文本,此时 addToLib 必须为 0(默认值)。可以同时获取多个id,中间用 | 分隔 (“comment 1|comment2|…”)。

MsgBox, % "从 1 号库中获取 auto 的文本:" FindText().PicLib("auto") ; 从 1 号库中获取 id 为 "auto" 的相应 Text
MsgBox, % "从 2 号库中获取 hot 的文本:" FindText().PicLib("hot",,2) ; 从 2 号库中获取 "hot" 的文本
MsgBox, % "从 3 号库中获取 hot 的文本:" FindText().PicLib("hot",,3) ; 返回空值,因为 "hot" 在 2 号库,不在 3 号库

2. PicN(Comment, libraryIndex:=1) 将字符串分解为字符,然后使用 PicLib 获取 Text。这在进行组合搜索时很有用。

PicN(“hot”) 等价于 PicLib(“h|o|t”),生成 id 为 “h”、“o” 和 “t” 的 Text。

3. PicX(Text) 将包含一个单词的 Text 分割成多个包含单独字符的 Text。因此,如果文本包含单词“this”的图像,那么它将返回一个包含 4 个单个字符(“t”、“h”、“i”、“s”)图像的文本,用 | 分隔。在字符之间的间距可能发生变化时(例如,网页每次呈现可能会略有不同),这很有用,因为我们可以将单词分割成单独的字符,最终再执行组合查找,这样字符之间的距离就可以是变化的。

MsgBox, % "将 ""hot"" 切分为多个字符: " FindText().PicX("|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s")

4. PicInfo(Text) 返回有关文本的低层信息。将返回以下数组:

[v, w, h, len1, len0, e1, e0 , mode, color, n, comment, seterr]
v --> 内容为 0 和 1 的 Text 值
w --> Text 宽度
h --> Text 高度
len1 --> Text 中 1 的总数
len0 --> Text 中 0 的总数
e1 --> 1 的允许误差范围:是一个绝对数量(返回 len1 的百分数)
e0 --> 0 的允许误差范围:是一个绝对数量(返回 len0 的百分数)
mode --> 0 = Color 或 未定义, 1 = Gray, 2 = GrayDiff, 3 = ColorPos, 4 = ColorDiff, 5 = MultiColor 或 使用外部文件
color --> Color 模式下的颜色
n --> MultiColor 模式下颜色的数量
comment --> Text 的 comment/id
seterr --> Text 中是否设置了误差范围

示例:

#include <FindText>
Text:="|<mycomment>[0.1,0.1]*197$15.1U8A11U8C11k8600s0700Q01U004"
info := FindText().PicInfo(Text)
MsgBox, % "v: " info[1] "`nw: " info[2] "`nh: " info[3] "`nlen1: " info[4] "`nlen0: " info[5] "`ne1: " info[6] "`ne0: " info[7] "`nmode: " info[8] "`ncolor: " info[9] "`nn: " info[10] "`ncomment: " info[11] "`nseterr: " info[12]

(8) FindText 组合查找

可以通过将 JoinText 参数设置为 1,或指定要查找的单词数组,来完成组合查找。其中 offsetX 和 offsetY 是图像间所允许的最大相对偏移量。如果查找成功,则返回的结果 id 将是用于搜索的 id 的组合(在以下示例中,ok[1].id == “autohot”)

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8"
Text.="|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s"
MsgBox, 查找 "auto" 和 "hot"
ok:=FindText(X,Y,,,,,,,Text) ; FindText 会分别返回 "auto" 和 "hot" 的所有位置
for k, v in ok
    FindText().MouseTip(v.x, v.y)
MsgBox, Looking for "auto" followed by "hot"
ok:=FindText(X,Y,,,,,,,Text,,,1) ; FindText 会返回所有后面是 "hot" 图像的"auto"图像位置,其中 "hot" 图像在 x 轴方向至多可以偏移 20 像素,在 y 轴方向至多可以偏移 10 像素。
for k, v in ok
    FindText().MouseTip(v.x, v.y)

如果创建了字符库,则可以使用组合查找来搜索单词。

; 首先将字母 "a" 和 "s" 的图像添加到默认图库
FindText().PicLib("|<a>**50$9.TXz0MzTzXkT7TtvU", 1)
FindText().PicLib("|<s>**50$8.DbzXs7kz0wDzTc", 1)

FindText(X, Y,,,,, 0.15, 0.15, FindText().PicN("sas"),,,1,5,3) ; FindText().PicN("sas") 使用图库组合出单词 "sas" 的 Text 值。误差范围设为 15%,因为所有的 "a" 和 "s" 字符都略有差别,设置成 15% 就可以找到所有的实例了。本例中默认的偏离值太大了,所以设置如下:OffsetX=5、 OffsetY=3。
FindText().MouseTip(X, Y)
FindText(X, Y,,,,, 0.15, 0.15, FindText().PicN("asa"),,,1,5,3) ; 查找单词 "asa"
FindText().MouseTip(X, Y)

在处理不同字体、浏览器中进行不同渲染或抗锯齿等情况时,同一字符需要有多个图像。此时,我们可以在Text 变量中提供字符集,然后在 JoinText 参数中提供数组形式的搜索词。常规 JoinText 只能使用一种字体。

Text:="|<A>*162$7.472VF4WzkMA"
Text.="|<A>*165$8.61UI92NWTYD1U"
Text.="|<s>**50$5.Tb3klz"
Text.="|<s>**50$5.Tb3klx"
Text.="|<a>**50$6.SH3TnnTU"
Text.="|<a>*152$5.x4/slz"
; Text now contains two fonts of "A", "s" and "a"
ok:=FindText(X, Y, , , , , 0.1, 0.1, Text,, 1,["Asa"]) ; FindText 将使用提供的字符集找到单词 "Asa" 的所有实例
for k, v in ok
    FindText().MouseTip(v.x, v.y)

(9) 光学字符识别 (OCR)

可以使用 FindText().OCR 函数来实现一个基本的光学字符识别器 (OCR)。可以在找到字符集中的所有字符后使用它。

FindText().Ocr(
    Ok --> 获取 FindText() 返回的结果
    ,offsetX:=20, offsetY:=20 --> x 和 y 的最大偏移量。如果超过此	值,则将插入 * 号。默认值为 20 和 20。
    ,overlapW:=0 --> 图像的最大重叠,默认为 0
)

返回的是一个关联数组:{text:text, x:X, y:Y, w:W, h:H}

使用 Examples.bmp 的示例:

 ; 首先将字母 "a" 和 "s" 的图像添加到默认图库
FindText().PicLib("|<a>**50$9.TXz0MzTzXkT7TtvU", 1)
FindText().PicLib("|<s>**50$8.DbzXs7kz0wDzTc", 1)
; 使用图库找到屏幕上所有 "a" 和 "s" 字符
ok:=FindText(X, Y,,,,, 0.15, 0.15, FindText().PicN("sa"))
if (ocr := FindText().OCR(ok, 3, 3))
MsgBox, % "找到 " ocr.text ; 返回结果应该为 "aaa*sss*asa*sas

更高级的示例是实现记事本搜索功能。在本例中,打开记事本并将字体(在菜单栏 格式 -> 字体…)设置为 Consolas、Regular、11。在记事本中写入一些示例文本,然后运行以下示例并按 F1,这将打开一个搜索对话框。当然在记事本中 Ctrl+F 要好用得多,因为这个 FindText 函数只能查找在记事本窗口中可见的文本,但类似的东西可以用来在游戏中查找文本。

#NoEnv
#SingleInstance Force

#include <FindText>

; 首先是一些数字、大写字母、小写字母的字符集(从显示在记事本中的字体截取的:Consolas, Regular, 11)
numbers :="|<0>*139$10.7Uza6MD1wDnjQz3sD0q6DkS8|<1>*139$9.73sv4M30M30M30M30MzzzU|<2>*139$9.D7wlk60k61UQ71kQ70zzzU|<3>*139$8.TDu70kA6D3s30E43zjm|<4>*139$11.1k3UD0q1g6MMkVX37zzzUM0k1W|<5>*139$9.zryk60k60z7y0k70kCzbsU|<6>*139$10.3sTXUM1U5wzvVw3kBUq6DsS8|<7>*139$10.zzzk30M1UA0k60M30A1U60k8|<8>*139$10.7lza6MBVbCDkT7CMD0y7Tsz8|<9>*139$10.7Vz66kP0w3sRznv081UATVw8"
uppercase := "|<A>**50$12.000000003k3k3s7M6M6QCQCAACTyTyM7s7s30000000000U|<B>**50$10.0000003zDysvVy6svzDyszVy7sTzjw000000008|<C>**50$10.0000000TXzSDUC0s30A0s3UC0SAzly000000008|<D>**50$11.0000000DsTwkxUv0y1w3s7kDUP1q7jyTk000000000E|<E>**50$9.000000zzzk60k60zryk60k60zzz00000004|<F>**50$9.000000zzzk60k60zryk60k60k6000000004|<G>**50$11.00000000z3zC6s1k30CDwTs6sBkPknzVy000000000E|<H>**50$10.00000030w3kD0w3kDzzzkD0w3kD0w3000000008|<I>**50$9.000000zzz60k60k60k60k60kzzz00000004|<J>**50$8.00000Dzz1kQ71kQ71kQ7Xjvw0000002|<K>**50$10.0000003VyCsnbCsz3sDUz3iCQsvXi7000000008|<L>**50$9.000000s70s70s70s70s70s70zzz00000004|<M>**50$12.00000000QCQCKOSSSLPrPrPrtbs7s7s7s7s70000000000U|<N>**50$10.0000003Uz3wDsxXrDgyntjayTszXy7000000008|<O>**50$12.000000007sDwSSQ6M7s7s7s7s7M7QCSSDw7s0000000000U|<P>**50$10.0000003zDyszVy3sTXzyzXUC0s3UC0000000008|<Q>**50$12.000000007sDwSSQ6M7s7s7s7s7M7QCSSDw7s1U1n0z0S00U|<R>**50$10.0000003yDwlv3gClnyDknX7AQkv3g7000000008|<S>**50$10.0000000z7ywPUC0S0y0y0w1k7kTzbw000000008|<T>**50$10.0000003zzz30A0k30A0k30A0k30A0k000000008|<U>**50$10.00000030w3kD0w3kD0w3kD0y7sxzXw000000008|<V>**50$12.00000000s3s7M6Q6QCACCACQ6Q6M7s3s3k3k0000000000U|<W>**50$11.k7UD0y1w3tbnjjPSqxhDSSwxsu|<X>**50$11.sDktnVr3w3k7UD0z1y7CCCsRUS|<Y>**50$11.kDUTVnX7C7MDkD0C0M0k1U3062|<Z>**50$10.0000003zzz0M3UQ1UC0k60s30M3zzz000000008"
lowercase := "|<a>**50$10.000000003wTtVk7Dxzy7sTXrzDQ0000008|<b>**50$10.03UC0s3UCwzvty7sDUy3sTXzyTU0000008|<c>**50$10.000000001yDxsr0M3UC0M1knz7s0000008|<d>**50$10.001k70Q1lzTxly7sT1y7sTbrzDQ0000008|<e>**50$10.000000001wTtly3zzzy0s1k7zDw0000008|<f>**50$11.003wDss1k3UzxzsQ0s1k3U70C0Q0s00000002|<g>**50$11.0000000007zTytnVX77yDss1k3zXzi3wCzwzW|<h>**50$10.03UC0s3UCwzvtz7sTVy7sTVy7sQ0000008|<i>**50$9.71s70007sz0s70s70s70szzz000000U|<j>**50$9.1kD1k003yTk60k60k60k60k60qCzXsU|<k>**50$9.060k60k67lqQr7ky6sr6Qlq7000000U|<l>**50$9.07sz0s70s70s70s70s70szzz000000U|<m>**50$11.000000000TizxrTCyNwntbnDaTAyNk0000002|<n>**50$10.00000000Cwzvtz7sTVy7sTVy7sQ0000008|<o>**50$10.000000003wTvny7kD0w3sTnryDk0000008|<p>**50$10.00000000Cwzvty7sDUy3sTXzyzXUC0s3U8|<q>**50$10.000000001zTxly7sT1y7sTbrzDQ1k70Q1s|<r>**50$9.00000006yzzbsS0k60k60k60000000U|<s>**50$9.00000003wzr6s7UTUS0y6zrw000000U|<t>**50$11.0000A0s1kTzzyC0Q0s1k3U7070DsDk0000002|<u>**50$10.00000000C7sTVy7sTVy7sRrrzDQ0000008|<v>**50$10.00000000A3sTVq6QsnXADkO1s7U0000008|<w>**50$12.0000000000s3s7s7NbNqPqPqOKSSSSCS00000000U|<x>**50$10.00000000C7QRnXw7US1wDlnb7sQ0000008|<y>**50$12.0000000000M7QCQCAACQ6M7M7s3k3k1U3U70z0w0U|<z>**50$10.000000007zTs3UQ1UA1UC1k7zzw0000008"

characterset := lowercase . uppercase . numbers
; 将新建的字符集添加到 PicLib 库
FindText().PicLib(characterset, 1)

return

F1::
InputBox, searchPhrase, Search Notepad, % "在记事本中搜索文本。如果要区分大小写,请把搜索内容置于双引号内。"

caseSensitive := (SubStr(searchPhrase,1,1) == """") && (SubStr(searchPhrase,0) == """")
if caseSensitive
searchPhrase := SubStr(searchPhrase,2,-1) ; 区分大小写进行搜索时,去除双引号
else
StringLower, searchPhrase, searchPhrase ; 不区分大小写进行搜索时,将搜索短语转成小写形式

if (ok:=FindText(X, Y,,,,, 0, 0, caseSensitive ? FindText().PicN(searchPhrase) : RegexReplace(characterset, "(?<=<)\p{L}(?=>)", "$L0"),1,1, caseSensitive ? 1 : [searchPhrase])) ; 如果进行的是区分大小写搜索,使用 PicN 将搜索短语中各个字符的图片拼起来,然后使用 FindAll=1 来查找图像。如果进行的是不区分大小写搜索,将所有大写字符图像替换成小写的图像(这表示默认情况下 FindText 在搜索时会同时使用大写和小写图像),然后再搜索小写的搜索短语。
{
FindText().MouseTip(X, Y)
 ; FindText().Click(X, Y, "L")
}
return

(10) BindWindow – 搜索遮挡窗口文本

BindWindow 允许 FindText 在被其他窗口遮挡的窗口中搜索文本。

FindText().BindWindow(
    bind_id -> 获取目标窗口的 ahk_id(这可以通过 WinExist() 获得)。		如果设置为 0,则禁用 BindWindow,采用正常方式搜索屏幕。
    , bind_mode -> 指定绑定的方法。有些窗口支持某些方法,有些窗口需要	其他的方法。奇数 bind_mode 会修改一个透明窗口以在其中进行搜索。通	  常 bind_mode 为 0 或 4 时起作用,默认为 0。
    ,get_id -> 如果设置为 1,则返回当前绑定窗口的 id,默认为 0。
    ,get_mode ->  如果设置为 1,则返回当前使用的绑定模式,默认为 0。
)
SetTitleMatchMode, 2
FindText().BindWindow(WinExist("Paint ahk_class MSPaintApp")) ; 将 FindText 绑定至画图程序
Text:="|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s"
FindText(X, Y, 72-150000, 228-150000, 72+150000, 228+150000, 0, 0, Text) ; 画图程序可能被其他窗口遮挡,但是 FindText 还是能正常找到文本
FindText().MouseTip(X, Y)
FindText().BindWindow(0) ; 把画图程序和 FindText 解绑

注意!BindWindow 将为运行脚本中的所有 FindText 函数进行全局设置。如果使用多个线程,则新线程可以覆盖旧线程的绑定窗口(例如,一个热键绑定一个窗口并循环 FindText,然后被另一个绑定另一个窗口的热键中断)。可以通过使用 FindTextClass 来避免这种情况,具体将在下面讨论。

(11) FindTextClass 查找文本类

FindText 的每个函数都包含在一个名为 FindTextClass 的类中。FindText() 返回 FindTextClass 的全局共享实例,因此 FindTextClass 的每个函数都在同一个数据集上运行。例如,BindWindow 为每个 FindText 调用更改其绑定窗口,Screenshot=0 将使用任何 FindText 调用最后创建的屏幕截图,等等。FindText(arguments) 与 FindText().FindText(arguments) 是相同的。

对于此示例,在 Paint 中打开“Examples.png”,然后通过在其上方移动另一个窗口来隐藏图像。按 F1,鼠标提示找到的图像的位置。按F2 后,找不到图像了。

#NoEnv
#SingleInstance Force

#include <FindText>

Esc::ExitApp

F1::
FindText().BindWindow(WinExist("Examples.png - 画图"))
Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8"
while (ok:=FindText(X, Y,,,,, 0, 0, Text)) {
FindText().MouseTip(X, Y)
Sleep, 1000
}
MsgBox, 找不到画图程序!
return

F2::
FindText().BindWindow(0) ; 绑定另一窗口
return

这可以通过使用 FindTextClass 的新实例来解决:

 F1::
ft := new FindTextClass
ft.BindWindow(WinExist("Examples.png - 画图"))
Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8"
while (ok:=ft.FindText(X, Y,,,,, 0, 0, Text)) {
ft.MouseTip(X, Y)
Sleep, 1000
}
MsgBox, 找不到画图程序!
return

(12) 截图功能:ScreenShot、GetTextFromScreen、SavePic、ShowPic、ShowScreenShot

1. FindText().ScreenShot(x1:=0, y1:=0, x2:=0, y2:=0)

在点 x1,y1 和 x2,y2 之间获取 FindText 内部屏幕截图,以便在以后的 FindText 调用中使用。

2. Text := FindText().GetTextFromScreen(x1:=0, y1:=0, x2:=0, y2:=0, Threshold="", ScreenShot:=1, outX, outY)

使用指定的阈值将 x1,y1 和 x2,y2 之间的截图转换为 Text。Threshold 可以是灰度阈值模式的“*”或灰度差值模式的“**”,后跟阈值。ScreenShot = 1 将拍摄一个新的屏幕截图,取值为 0 表示使用最后一个内部屏幕截图(例如由 ScreenShot 函数生成的截图)。文本将自动从顶部和底部裁剪。变量 outX 和 outY 将设置为包含屏幕上抓取文本的中点。

3. FindText().SavePic(FileName, x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)

将 x1,y1 和 x2,y2 两点之间的截屏区域保存到一个文件中。

4. FindText().ShowPic(FileName:="", show:=1, outX, outY, outW, outH)

在屏幕左上角显示一个图像文件内容。outX outY 将设置为显示图像的 X Y 坐标(在多屏幕设置中,此坐标可能不是 0,0),outW outH 设置为图像的宽度和高度。在不指定 FileName 的情况下调用 ShowPic() 将从屏幕中删除图像。

5. FindText().ShowScreenShot(x1:=0, y1:=0, x2:=0, y2:=0, ScreenShot:=1)

与 ShowPic 函数一样,显示点 x1,y1 和 x2,y2 之间的区域。如果 ScreenShot 为 1,则将拍摄新的屏幕截图,否则使用最后一次的内部屏幕截图。

示例:

#include <FindText>
FindText().Screenshot(100,100,500,500) ; 在 100,100 和 500,500 位置间截图
FindText().ShowScreenShot(100, 100,500, 500, 0) ; 将截图显示在屏幕上。由于我们最后一个参数指定为 0,函数调用时会使用  Screenshot 函数截取的最后一次截图
Sleep, 3000
FindText().ShowScreenShot() ; 3 秒后隐藏截图
FindText().SavePic(A_ScriptDir "\TestScreenShot.png", 100, 100, 400, 400, 0) ; 将截图的一部分保存在脚本所在目录的一个文件中。

(13) 杂项:Sort、Sort2、Sort3、WaitChange

1. FindText().Sort(ok, dy:=10)

接受一个 FindText 结果数组(ok),返回一个从左到右 -> 从上到下排序的数组。比较点位于图像的中间(不是左上角!),通过改变 dy 值可以忽略 y 位置的细微差异。

2. FindText().Sort2(ok, px, py)

接受一个 FindText 结果数组(ok)和一个坐标(px, py),并使用 (x-px)^2 + (y-py)^2 公式计算图像与坐标的距离,进而对数组进行排序。

3. FindText().Sort3(ok, dir:=1)

接受一个 FindText 结果数组(ok),按方向排序。Dir 与 FindText dir 参数取相同的值(9 除外)。

4. FindText().WaitChange(WaitTime:=-1, x1:=0, y1:=0, x2:=0, y2:=0)

等待 “WaitTime” 秒,看坐标 x1,y1 和 x2,y2 之间的屏幕区域是否变化。将 “WaitTime” 指定为 -1 将无限期等待。

(14) FindText 速度提升技巧

1. 使用尽可能小的搜索范围。可以通过多种方式实现,最简单的方法是使用 WinGetPos 将搜索限制在单个窗口中,以获取窗口位置和大小。

WinGetPos, pX, pY, pW, pH, ahk_class MSPaintApp ; 获取画图窗口的位置和大小
ok := FindText(X, Y, pX, pY, pX+pW, pY+pH,,, Text) ; 将搜索范围限定为画图窗口

有时搜索到的图像只会出现在特定区域,因此可以进一步调整搜索范围。在 Paint 窗口中,如果我们对菜单栏和工具栏不感兴趣,那么我们可以从顶部裁剪一部分:

WinGetPos, pX, pY, pW, pH, ahk_class MSPaintApp ; 获取画图窗口的位置和大小
ok := FindText(X, Y, pX, pY+150, pX+pW, pY+pH,,, Text) ; 将搜索范围限定为画图窗口,并从顶部剪切掉 150 像素,将菜单栏和工具栏排除在搜索范围外

2. 使用尽可能小的图像。可以通过使用 L3/R3/L/R/U/B/自动裁剪按钮,来裁剪抓取的图像。

3. 使用尽可能小的误差范围。例如,将 err1 err0 0 提高到 0.3 可能会导致搜索速度明显变慢(可能慢 5-10 倍)。

4. 如果要查找完全匹配,请为 err1、err0 或两者指定一个非常小的非零值。FindText 的默认动作是:如果 err1 和 err0 都设置为 0,那么如果搜索失败,将自动将 err1 和 err0 都设置为 0.05 并重新尝试搜索。当执行许多 FindText 调用来查找不同的图像时,这种额外的搜索会随着时间的推移而增加。但这仅在 err1 和 err0 恰好为 0 时发生,这意味着如果您将诸如 0.000001(很接近 0 但不等于 0)用于 err1、err0 或两者同时设置(只需一个非零),那么只会执行一次搜索(有效误差范围为 0.000001≈0)。

FindText(X, Y,,,,,,, Text) ; 如果 Text 在屏幕上不可见,那么会搜索 2 次:第一次 err1 和 err0 设为 0,第二次搜索设为 0.05(5% 的误差范围)
FindText(X, Y,,,,,0.000001,, Text) ; 如果 Text 在屏幕上不可见,只搜索 1 次,进行精确匹配(0.000001≈0)
FindText(X, Y,,,,,0.05,0.05, Text) ; 如果 Text 在屏幕上不可见,只搜索 1 次,误差范围是 5%

5. 如果要搜索多张图片,要么将 Text 拼成一个然后只调用一次 FindText,要么将 Screenshot 参数设置为 0 以进行后续搜索。这避免了 FindText 多次进行屏幕截图,这需要耗费大量时间。

使用组合查找方法的示例:

Text:="|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8"
Text.="|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s"
if (ok := FindText(X, Y,,,,,,, Text)) {
    if (ok[1].id == "auto") {
        ; 找到 "auto" 图像后要进行的操作
    } else if (ok[1].id == "hot") {
        ; 找到 "hot" 图像后要进行的操作
    }
}

重用第一个屏幕截图的示例:

FindText().PicLib("|<auto>*159$40.0000k000003000000A00DVUnw7lr6371nUAMAA630FUkkkAT63330rgMAAA1kFUkkkD363330wAAAA62vkvkkQtt1v1kS8",1)
FindText().PicLib("|<hot>*152$29.U00010000200014000280004Hk7kzxktktVVUkX161V43A3286M24EAkA8UNUMF0lUUW1Xb1g31s1s",1)
if (ok := FindText(X, Y,,,,,,, FindText().PicLib("auto"))) {
    ; 找到 "auto" 图像后要进行的操作
}
if (ok := FindText(X, Y,,,,,,, FindText().PicLib("hot"), 0)) { ; 使用上一次搜索"hot"的截图来调用 FindText
    ; 找到 "hot" 图像后要进行的操作
}

6. 使用需要最少像素比较次数的捕捉模式。通常灰度阈值模式是最快的选择;灰度差值、颜色位置 和 相似度设置为 100% 的颜色相似模式也非常快。最慢的是颜色相似模式(颜色范围越大或相似度越低 -> 搜索越慢)和多色查找(如果使用的是图像文件)。


这就是现有的全部内容,感谢您的阅读!

来自“飞跃”的2个细节补充:
1、最新版右键抓图时,可以使用方向键调整选框大小。
2、在放大界面制作字库时,可以点击黄色的分割标记来把图分割成几个部分,对应输入框的几个备注文字,就可以一次性分割添加几个字库了。

给TA捐赠
共{{data.count}}人
人已捐赠
教程

新手如何用AHK做游戏脚本?

2022-5-6 9:06:23

函数教程

AHK中变量为啥这么多种写法

2022-6-2 13:41:01

42 条回复 A文章作者 M管理员
  1. hexuren
    1河许人给您打赏了¥10
  2. 空
    给您打赏了¥10
  3. dbgba
    dbgba给您打赏了¥10
  4. 蜜獾哥
    蜜獾哥给您打赏了¥10
  5. 蜜獾哥

    希望这个教程持续下去,让更多的人知道!

  6. loveshare
    我是爱分享给您打赏了¥20
  7. loveshare

    希望能翻译更多优秀的作品

  8. sunwind
    feng给您捐赠了¥2
  9. sunwind

    提个建议,社区右侧已自动生成目录,可以合理利用起来,翻译我看主要是直译,很多地方可以意译,更符合中文用户的阅读习惯,感谢分享,希望看到你更多作品

    • 北极星

      内容有点多,时间也紧,所以没有过多润色。目录我看现在只能生成一级目录

    • sunwind

      嗯,确实是,不过也体谅社区老大,小社区做到这个程度已经不错了

  10. evomcc

    这个好,要学习一下

  11. doge

    厉害厉害,来学习了

  12. 无涯

    学习一下

  13. 乌咪

    学习一下

  14. 无涯

    学习一下!

  15. 辅导费

    学习了

  16. 无涯

    厉害厉害,来学习了

  17. 无涯

    学习

  18. 慢速爬行中

    实用、必须好好学

  19. baiyunem

    BindWindow绑定窗口后,已经实现找到字找图,点击呢? 点击的命令是啥?

    • baiyunem

      BindWindow绑定窗口后,使用FindText().Click点击的,依然是屏幕的坐标,如何后台点击绑定的窗口?

    • baiyunem

      F9:: SetTitleMatchMode, 2 id:=WinExist(“窗口”) FindText().BindWindow(id) FindText().Click(200, 200, “L”) return 想实现后台在窗口内点击右键,貌似这样写,少了点什么,鼠标会移到屏幕的200.200位置,而不是窗口的200,200,头疼?

    • 自然醒

      你用该使用教程中的这块代码
      “`
      FindText().ScreenToWindow(outX, outY, X, Y, WinExist(“ahk_class MSPaintApp”)) ; 将绝对坐标 X 和 Y 转换为相对于画图窗口的相对坐标,结果存储在 outX 和 outY 变量中。
      ControlClick, X%outX% Y%outY%, ahk_class MSPaintApp ; 在画图程序中单击之前存储的坐标位置
      “`

  20. 火

    ?

  21. Thomasssss

    必须赞一个?

  22. 月下马
    月下马给您捐赠了¥2
  23. 未名游客给您捐赠了¥5
  24. gdzrh917

    终于找到findtext的教程!

  25. 尽量c盘

    能否出一期 关于多色找图 找到放大比例不同的图片的 教程呢,感谢。

  26. 未名游客给作者打赏了¥2
  27. lxj

    用的时候碰到个问题,我写的是魔兽争霸3脚本,在war3全屏模式下,findText的截图感觉获取的只是切到全屏那一刻的图,让我只能限制这个脚本在war3窗口模式生效,有大佬有啥办法吗😥

    • 北极星

      游戏是不是有反截图机制?

  28. 阿所发生的

    将图像转换成对应的二值化的函数有包含在FindText里面吗?

    • 阿所发生的

      直接用函数转换,不用那样选择,使用中“灰度二值化”也是算法决定的。能不能直接导入图片,生成对应的二值化内容呢?

  29. 阿所发生的

    FindText 的默认动作是:如果 err1 和 err0 都设置为 0,那么如果搜索失败,将自动将 err1 和 err0 都设置为 0.05 并重新尝试搜索。

    这个的错误重新尝试次数可以修改吗,或者设置自动增加的值?有没有大佬知道的

个人中心
购物车
优惠劵
有新私信 私信列表
搜索