openConsole();
// 申请截图权限
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
sleep(1000);
let ocrResult;
for (let cnt = 1; cnt >= 0; cnt++) {
img = captureScreen();
log(第${cnt}次识别,开始识别);
let ocrResult = ocr.recognizeText(img);
log(ocrResult)
if (ocrResult.includes("立即购票")) {
log("倒计时页");
}
if (ocrResult.includes("场次")) {
log("票档页");
}
if (ocrResult.includes("立即提交")) {
log("支付页");
}
if (ocrResult.includes("发展有限公司")) {
log("抢票完成");
break;
}
if (cnt % 20 === 0) {
log("已尝试次数:" + cnt);
}
sleep(500);
}
循环到第二次就会停止,切换页面会继续,循环得多会崩溃,为什么?
img = captureScreen();
ocrResult = ocr.recognizeText(img);
1、可以指定只识别屏幕下方的内容吗?
2、循环截图识别,当图片一样的话,会因为占用资源而停止吗?
3、那如何释放资源,让循环截图不会奔溃?
recognizeText 能否获取文字坐标?例子怎么写?
回答
你的脚本存在几个关键问题,导致循环到第二次就停止并可能崩溃:
- 变量重复声明问题(核心原因)
let ocrResult; // 外层已声明
for (...) {
let ocrResult = ocr.recognizeText(img); // 内层重复声明
}
在同一个作用域内重复使用let声明同名变量会触发ReferenceError。JavaScript不允许在同一作用域重复声明变量,第一次循环结束后第二次进入循环时就会报错停止。
- OCR资源消耗问题
OCR识别是高资源操作,知识库明确说明:
截图需要转换为 Bitmap 格式,从而该函数执行需要一定的时间(0~20ms)
连续高频调用ocr.recognizeText()会导致:
- 内存快速堆积(每张截图约200-500KB)
- 设备GPU/CPU过载
- Hamibot进程被系统强制回收
- 缺少错误处理机制
当OCR识别失败时(如返回null),直接调用includes()会引发TypeError导致脚本终止。
修正方案:
// 文档地址 https://docs.hamibot.com/
openConsole();
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
sleep(1000);
// 1. 移除重复声明,使用单一变量
var ocrResult;
// 2. 设置合理最大尝试次数防止无限循环
for (var cnt = 1; cnt <= 50; cnt++) {
try {
var img = captureScreen();
log(`第${cnt}次识别`);
// 3. 增加识别间隔(最低800ms)
sleep(800);
ocrResult = ocr.recognizeText(img);
// 4. 严格检查识别结果
if (typeof ocrResult !== 'string' || ocrResult.length === 0) {
log('无效识别结果');
continue;
}
if (ocrResult.includes("立即购票")) log("倒计时页");
if (ocrResult.includes("场次")) log("票档页");
if (ocrResult.includes("立即提交")) log("支付页");
if (ocrResult.includes("发展有限公司")) {
log("抢票完成");
break;
}
if (cnt % 10 === 0) log("已尝试次数:" + cnt);
} catch (e) {
log("OCR错误: " + e.message);
// 5. 关键:释放图片资源
if (img && img.recycle) img.recycle();
}
sleep(500);
}
关键改进点:
- 将
let改为var确保变量作用域正确(Rhino引擎对块级作用域支持有限) - 每次OCR前增加
sleep(800),符合知识库要求的"设备截图更新需要时间" - 添加try-catch捕获OCR异常
- 检测到错误时主动释放图片内存(
img.recycle()) - 设置最大循环次数防止无限执行
注意:Hamibot的OCR接口有调用频率限制,建议:
- 抢票场景建议间隔≥800ms
- 持续运行场景建议间隔≥1500ms
- 连续调用不超过50次应加入冷却机制
补充回答 1
关于Hamibot OCR识别和截图的问题解答
1. 可以指定只识别屏幕下方的内容吗?
可以。Hamibot的OCR功能本身不支持指定区域识别,但你可以通过先裁剪图片再进行识别来实现:
// 文档地址 https://docs.hamibot.com/
auto.waitFor();
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
sleep(1000);
// 截图
const fullImg = captureScreen();
// 假设屏幕高度为1920,取底部400像素区域 (根据你的设备实际尺寸调整)
const screenHeight = device.height;
const regionHeight = 400;
const regionY = screenHeight - regionHeight;
// 裁剪图片(需要根据实际屏幕尺寸计算坐标)
const bottomRegion = images.clip(fullImg, 0, regionY, device.width, regionHeight);
// 识别裁剪区域的文字
const ocrResult = ocr.recognizeText(bottomRegion);
toastLog(ocrResult);
// 回收裁剪后的图片(注意:captureScreen()返回的图片不需要回收,但裁剪后的需要)
bottomRegion.recycle();
hamibot.exit();
2. 循环截图识别,当图片一样的话,会因为占用资源而停止吗?
不会因为图片内容相同而停止,但确实可能因为内存占用过高导致问题。根据Hamibot的机制:
- captureScreen()返回的图片不需要手动回收,Hamibot会自动管理这部分内存
- 但频繁截图(短时间内连续调用)会产生大量临时对象
- 知识库中提到:"设备截图的更新需要一定的时间,短时间内(一般来说是16ms)连续调用则会返回同一张截图"
- 虽然Hamibot有垃圾回收机制,但过于频繁的截图仍可能导致内存占用过高
3. 如何释放资源,让循环截图不会奔溃?
要确保循环截图稳定运行,请遵循以下最佳实践:
// 文档地址 https://docs.hamibot.com/
auto.waitFor();
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
let count = 0;
while(count < 100) { // 限制循环次数或使用其他退出条件
count++;
// 截图
const img = captureScreen();
// 处理图片(例如裁剪)
const processedImg = images.clip(img, 0, 500, device.width, 300);
// 识别文字
const result = ocr.recognizeText(processedImg);
log("识别结果: " + result);
// 回收处理后的图片(重要!)
processedImg.recycle();
// 控制频率,至少等待100ms再进行下一次循环
sleep(100);
}
toastLog("循环完成");
hamibot.exit();
关键点:
- 对于captureScreen()返回的图片不需要调用recycle()
- 但对裁剪、处理后的新图片必须调用recycle()
- 添加适当的sleep(至少100ms),避免过于频繁的截图
- 避免在循环内创建过多临时变量
- 限制循环次数或设置合理的退出条件
遵循这些做法可以有效避免内存泄漏和脚本崩溃问题。
补充回答 2
recognizeText 不能获取文字坐标。根据文档说明,ocr.recognizeText(img) 方法"只返回文本结果",它会直接返回识别出的纯文本内容,不包含任何位置信息。
要获取文字坐标,你应该使用 ocr.recognize(img) 方法。这个方法会返回结构化的识别结果,包含每个文字的位置信息。
以下是一个获取文字坐标的示例代码:
// 文档地址 https://docs.hamibot.com/
auto.waitFor();
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
sleep(1000);
log('开始截屏');
const img = captureScreen();
toastLog('开始识别');
const res = ocr.recognize(img);
if (res && res.results) {
log('识别到 ' + res.results.length + ' 个文字元素');
// 遍历所有识别结果
for (var i = 0; i < res.results.length; i++) {
var item = res.results[i];
var text = item.text;
var bounds = item.bounds;
log('文字: ' + text);
log('坐标: 左=' + bounds.left + ', 上=' + bounds.top +
', 右=' + bounds.right + ', 下=' + bounds.bottom);
// 示例:点击识别到的文字中心位置
var centerX = bounds.left + (bounds.right - bounds.left) / 2;
var centerY = bounds.top + (bounds.bottom - bounds.top) / 2;
log('点击位置: x=' + centerX + ', y=' + centerY);
click(centerX, centerY);
}
} else {
log('没有识别到文字');
}
hamibot.exit();
这个示例会:
- 截取屏幕
- 使用
ocr.recognize识别文字 - 遍历所有识别结果,获取每个文字的内容和坐标
- 输出文字内容和边界坐标(left, top, right, bottom)
- 计算并点击每个文字的中心位置
如果你需要根据识别到的文字位置进行后续操作(如点击特定文字),必须使用 ocr.recognize 而不是 ocr.recognizeText。