js购买食材OCR识图 (#610)

提高对话识别容错,购买增加识别
This commit is contained in:
JJMdzh
2025-04-20 20:51:56 +08:00
committed by GitHub
parent 96286d873b
commit 0d600ad1d4
3 changed files with 126 additions and 52 deletions

View File

@@ -138,51 +138,97 @@ async function simulateKeyOperations(key, duration) {
// 定义一个函数用于购买食材
async function purchaseIngredient(ingredient) {
log.info(`购买食材: ${ingredient}`);
// 模拟购买操作的后续点击
await click(1600, 1020); await sleep(1000); // 购买
await click(1181, 600); await sleep(200); // 选择100个
await click(1320, 780); await sleep(1000); // 最终确认
await click(1320, 780); await sleep(1000); // 点击空白
// 在购买前进行识别
let ComfirmRoResult1 = await recognizeImage("assets/Comfirm.png", 1585, 1005, 31, 31, 2000);
if (ComfirmRoResult1) {
// 模拟购买操作的后续点击
await click(1600, 1020);
await sleep(1000); // 购买
} else {
log.warn(`食材: ${ingredient}已售罄或背包已满`);
return; // 退出操作
}
// 在点击选择100个之前进行识别
let ComfirmRoResult2 = await recognizeImage("assets/Comfirm.png", 995, 766, 31, 31, 2000);
if (ComfirmRoResult2) {
log.info("选择100个的");
await click(1181, 600);
await sleep(200); // 选择100个
} else {
log.warn("尝试重新点击购买");
await click(1600, 1020);
await sleep(1000); // 购买
return; // 退出操作
}
await click(1320, 780);
await sleep(1000); // 最终确认
await click(1320, 780);
await sleep(1000); // 点击空白
}
// 定义一个通用的图像识别函数
function recognizeImage(templatePath, xMin, yMin, width, height) {
let template = file.ReadImageMatSync(templatePath);
let recognitionObject = RecognitionObject.TemplateMatch(template, xMin, yMin, width, height);
let result = captureGameRegion().find(recognitionObject);
return result.isExist() ? result : null;
function recognizeImage(templatePath, xMin, yMin, width, height, timeout = 2000) {
let startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
let template = file.ReadImageMatSync(templatePath);
let recognitionObject = RecognitionObject.TemplateMatch(template, xMin, yMin, width, height);
let result = captureGameRegion().find(recognitionObject);
if (result.isExist()) {
return { success: true, x: result.x, y: result.y, width: result.width, height: result.height };
}
} catch (error) {
log.error(`识别图像时发生异常: ${error.message}`);
return null;
}
}
log.warn("图像识别超时");
return null;
}
// 定义一个函数用于执行OCR识别
async function performOcr(targetText, xRange, yRange, tolerance) {
// 调整区域范围以包含容错区间
let adjustedXMin = xRange.min - tolerance;
let adjustedXMax = xRange.max + tolerance;
let adjustedYMin = yRange.min - tolerance;
let adjustedYMax = yRange.max + tolerance;
function performOcr(targetText, xRange, yRange, tolerance, timeout = 2000) {
let startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
// 调整区域范围以包含容错区间
let adjustedXMin = xRange.min - tolerance;
let adjustedXMax = xRange.max + tolerance;
let adjustedYMin = yRange.min - tolerance;
let adjustedYMax = yRange.max + tolerance;
// 在捕获的区域内进行OCR识别
let ra = captureGameRegion();
let resList = ra.findMulti(RecognitionObject.ocr(
adjustedXMin, adjustedYMin,
adjustedXMax - adjustedXMin, adjustedYMax - adjustedYMin
));
// 在捕获的区域内进行OCR识别
let ra = captureGameRegion();
let resList = ra.findMulti(RecognitionObject.ocr(
adjustedXMin, adjustedYMin,
adjustedXMax - adjustedXMin, adjustedYMax - adjustedYMin
));
// 遍历识别结果,检查是否找到目标文本
for (let i = 0; i < resList.count; i++) {
let res = resList[i];
// 后处理:根据替换映射表检查和替换错误识别的字符
let correctedText = res.text;
for (let [wrongChar, correctChar] of Object.entries(replacementMap)) {
correctedText = correctedText.replace(wrongChar, correctChar);
}
// 遍历识别结果,检查是否找到目标文本
for (let i = 0; i < resList.count; i++) {
let res = resList[i];
// 后处理:根据替换映射表检查和替换错误识别的字符
let correctedText = res.text;
const replacementMap = { /* 替换映射表,例如:'0': 'O', '1': 'l' */ };
for (let [wrongChar, correctChar] of Object.entries(replacementMap)) {
correctedText = correctedText.replace(new RegExp(wrongChar, 'g'), correctChar);
}
if (correctedText.includes(targetText)) {
// 如果找到目标文本,直接返回坐标
return { success: true, x: res.x, y: res.y, width: res.width, height: res.height };
if (correctedText.includes(targetText)) {
// 如果找到目标文本,直接返回坐标
return { success: true, x: res.x, y: res.y, width: res.width, height: res.height };
}
}
} catch (error) {
log.error(`识别文字时发生异常: ${error.message}`);
return { success: false };
}
}
return { success: false }; // 未找到符合条件的文本
log.warn("OCR识别超时");
return { success: false };
}
// 定义一个函数用于识别食材
@@ -194,7 +240,7 @@ async function recognizeIngredient(ingredient) {
let ocrResult = await performOcr(ingredient, { min: 210, max: 390 }, { min: 105, max: 920 }, 10);
if (ocrResult.success) {
log.info(`通过 OCR 识别找到食材: ${ingredient}`);
log.info(`坐标: x=${ocrResult.x}, y=${ocrResult.y}`);
// log.info(`坐标: x=${ocrResult.x}, y=${ocrResult.y}`);
await click(ocrResult.x, ocrResult.y + clickOffset);
await sleep(1000);
recognized = true;
@@ -202,17 +248,19 @@ async function recognizeIngredient(ingredient) {
// OCR 识别失败,尝试图像识别
let imagePath = `assets/Picture/${ingredientImageMap[ingredient]}`;
if (!imagePath) {
log.error(`未找到食材 '${ingredient}' 的图片文件`);
log.warn(`未找到食材 '${ingredient}' 的图片文件`);
return recognized;
}
let imageResult = recognizeImage(imagePath, 120, 90, 95, 865);
let imageResult = recognizeImage(imagePath, 120, 90, 95, 865, 1000);
if (imageResult) {
log.info(`通过图像识别找到食材: ${ingredient}`);
imageResult.click();
await sleep(1000);
// log.debug(`imageResult: ${JSON.stringify(imageResult)}`);
let x = Math.round(imageResult.x);
let y = Math.round(imageResult.y);
await click(x, y);await sleep(1000);
recognized = true;
} else {
log.error(`未能识别到食材: ${ingredient}`);
log.warn(`未能识别到食材: ${ingredient}`);
}
}
@@ -240,13 +288,13 @@ async function clickSelectedIngredients(selectedIngredients, filePath, npcNames)
await click(1300, 650); await sleep(500); // 双击增加低帧点击成功率
await click(1300, 650); await sleep(500);
await click(1300, 650); await sleep(1000);
await click(1600, 1020); await sleep(1000);
await click(1320, 780); await sleep(1000);
} else {
log.info("执行其他路径文件的点击操作");
await click(1300, 580); await sleep(500);
await click(1300, 580); await sleep(500);
await click(1300, 580); await sleep(1000);
await click(1600, 1020); await sleep(1000);
await click(1320, 780); await sleep(1000);
}
}
@@ -272,10 +320,10 @@ async function clickSelectedIngredients(selectedIngredients, filePath, npcNames)
await sleep(500);
} else {
// 第三次未找到 F 图标
log.error("尝试次数已达上限");
log.warn("尝试次数已达上限");
return false;
}
log.error(`尝试 ${f_attempts + 1}:寻找 F 图标`);
log.warn(`尝试 ${f_attempts + 1}:寻找 F 图标`);
}
}
// 获取 F 图标的中心点 Y 坐标
@@ -284,7 +332,7 @@ async function clickSelectedIngredients(selectedIngredients, filePath, npcNames)
// 在 F 图标右侧水平方向上识别 NPC 名称
let ocrResult = await performOcr(npcName, npcxRange, { min: fRes.y, max: fRes.y + fRes.height }, tolerance);
if (!ocrResult.success) {
log.error(`OCR 识别未找到 NPC: ${npcName},尝试滚动`);
log.warn(`OCR 识别未找到 NPC: ${npcName},尝试滚动`);
return false;
}
@@ -324,13 +372,39 @@ async function clickSelectedIngredients(selectedIngredients, filePath, npcNames)
isAligned = await checkNpcAndFAlignment(npcName, fDialogueRo);
}
if (isAligned) {
// 如果水平对齐,执行交互操作
keyPress("F");
await sleep(2500);
if (isAligned) {
keyPress("F");
await sleep(2500);
// 执行点击操作
// 首次执行点击操作
await performClickOperations(filePath);
let ComfirmRoResult = null;
let C_maxAttempts = 2; // 最大尝试次数
let C_attempts = 0; // 当前尝试次数
while (!ComfirmRoResult && C_attempts < C_maxAttempts) {
// 调用 recognizeImage 检测 ComfirmRo
ComfirmRoResult = await recognizeImage("assets/Comfirm.png", 1585, 1005, 31, 31, 2000);
if (ComfirmRoResult) {
log.info("识别到购买按钮,执行食材选择");
break; // 如果识别到,退出循环
} else {
log.warn("未识别到购买按钮,尝试重新识别");
}
await sleep(500); // 等待一段时间后再次检测
// 如果未识别到 ComfirmRo再次执行点击操作
await performClickOperations(filePath);
C_attempts++; // 增加尝试次数
}
if (!ComfirmRoResult) {
log.warn("未在规定时间内完成对话");
return; // 退出函数
}
// 只有在成功对齐并交互后,才执行后续的食材购买操作
// 记录已购买的食材
@@ -357,7 +431,7 @@ async function clickSelectedIngredients(selectedIngredients, filePath, npcNames)
await purchaseIngredient(ingredient);
purchasedIngredients.add(ingredient);
} else {
log.error(`未能识别到食材: ${ingredient}`);
// log.error(`未能识别到食材: ${ingredient}`);
allIngredientsFound = false; // 本轮有食材未找到
}
}
@@ -381,7 +455,7 @@ async function clickSelectedIngredients(selectedIngredients, filePath, npcNames)
// 如果成功购买了所有食材,记录成功信息
if (allIngredientsFound) {
log.info("成功购买了所有食材");
log.info("该处所需食材已完成购买");
} else {
log.error("未能购买所有食材,部分食材可能未找到或未成功购买。");
}