更改数字识别方式,回退龙蛋为打开背包识别 (#2876)

This commit is contained in:
古又
2026-02-14 23:19:24 +08:00
committed by GitHub
parent 760c0fa387
commit 9217fa1002
26 changed files with 174 additions and 71 deletions

View File

@@ -29,10 +29,6 @@
## 更新
### 版本:2.1
1.龙蛋数目由玩家填写,暂时去掉打开背包自动记录龙蛋功能
2.修改去纳塔悠悠集市的路线(中转一下)
3.添加糖雕添加雅珂达、哥伦比娅选项以及后退几步避免用户BGI开启的自动拾取误触调查点
4.转盘提供多种选择
5.去掉圣水中转路线,去掉快速调节时间操作
6.历史更新记录在records文件夹内
### 版本:2.1.2
更换识别方式,让龙蛋数量进行准确识别
回退为打开背包识别龙蛋

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

View File

@@ -6,12 +6,12 @@ let recordsNum = 0; // 写入内容次数
let sticksTime = false; // 判定是否可以上香
//六龙蛋位置
const coordinates = [
[565, 150],
[568, 723],
[1088, 161],
[874, 335],
[468, 574],
[1339, 358]
[565, 150], // 山
[568, 723], // 飞
[1088, 161], // 圣
[874, 335], // 太
[468, 574], // 献
[1339, 358] // 菲
];
// 通用方法区域
@@ -415,6 +415,7 @@ async function chcekDragonEggs() {
await checkExpire();
await sleep(1500);
await click(1250,50);
await sleep(50);
let DragonEggs = [0, 0, 0, 0, 0, 0];
let judgeEgg = 0;
// 判定是不是只有一页
@@ -423,16 +424,18 @@ async function chcekDragonEggs() {
for (let index = 0; index < 6; index++) {
let DragonEgg = await findImgIcon(`assets/RecognitionObject/DragonEgg${index}.png`, { min: 99, max: 1295 }, { min: 104, max: 967 }, true, 0.95);
if (DragonEgg.success) {
let ocrEggNum = await performOcr("",
{ min: DragonEgg.coordinates[0]-46, max: DragonEgg.coordinates[0]+34 }, { min: DragonEgg.coordinates[1]+56, max: DragonEgg.coordinates[1]+83 }, true);
// log.info(`第一次识别到的数字:${ocrEggNum.text}`);
if (ocrEggNum.text == "") {
await sleep(700);
ocrEggNum = await performOcr("",
{ min: DragonEgg.coordinates[0]-46, max: DragonEgg.coordinates[0]+34 }, { min: DragonEgg.coordinates[1]+56, max: DragonEgg.coordinates[1]+83 }, true);
// log.info(`第二次识别到的数字:${ocrEggNum.text}`);
};
DragonEggs[index] = Number(ocrEggNum.text);
// let ocrEggNum = await performOcr("",
// { min: DragonEgg.coordinates[0]-46, max: DragonEgg.coordinates[0]+34 }, { min: DragonEgg.coordinates[1]+56, max: DragonEgg.coordinates[1]+83 }, true);
// // log.info(`第一次识别到的数字:${ocrEggNum.text}`);
// if (ocrEggNum.text == "") {
// await sleep(700);
// ocrEggNum = await performOcr("",
// { min: DragonEgg.coordinates[0]-46, max: DragonEgg.coordinates[0]+34 }, { min: DragonEgg.coordinates[1]+56, max: DragonEgg.coordinates[1]+83 }, true);
// // log.info(`第二次识别到的数字:${ocrEggNum.text}`);
// };
let eggNum = await numberTemplateMatch('assets/backpackNumber', DragonEgg.coordinates[0]-46, DragonEgg.coordinates[1]+56, 80, 27);
log.info(`识别到的数字:${eggNum}`);
DragonEggs[index] = Number(eggNum);
}else{
DragonEggs[index] = 0;
};
@@ -443,19 +446,11 @@ async function chcekDragonEggs() {
for (let index = 0; index < 6; index++) {
let DragonEgg = await findImgIcon(`assets/RecognitionObject/DragonEgg${index}.png`, { min: 99, max: 1295 }, { min: 104, max: 967 }, true, 0.95);
if (DragonEgg.success) {
let ocrEggNum = await performOcr("",
{ min: DragonEgg.coordinates[0]-46, max: DragonEgg.coordinates[0]+34 }, { min: DragonEgg.coordinates[1]+56, max: DragonEgg.coordinates[1]+83 }, true);
// log.info(`第一次识别到的数字:${ocrEggNum.text}`);
if (ocrEggNum.text == "") {
await sleep(700);
ocrEggNum = await performOcr("",
{ min: DragonEgg.coordinates[0]-46, max: DragonEgg.coordinates[0]+34 }, { min: DragonEgg.coordinates[1]+56, max: DragonEgg.coordinates[1]+83 }, true);
// log.info(`第二次识别到的数字:${ocrEggNum.text}`);
let eggNum = await numberTemplateMatch('assets/backpackNumber', DragonEgg.coordinates[0]-46, DragonEgg.coordinates[1]+56, 80, 27);
if (eggNum == "") {
eggNum = 1;
};
if (ocrEggNum.text == "") {
ocrEggNum.text = 1;
};
DragonEggs[index] = ocrEggNum.text;
DragonEggs[index] = eggNum;
}else{
DragonEggs[index] = 0;
};
@@ -511,9 +506,111 @@ async function checkExpire() {
if (ocrExpire.text == "物品过期") {
log.info(`处理中=========`);
await click(980, 750);
await sleep(50);
};
};
// 多文本识别数字
/**
* 在指定区域内,用 0-9 的 PNG 模板做「多阈值 + 非极大抑制」数字识别,
* 最终把检测到的数字按左右顺序拼成一个整数返回。
*
* @param {string} numberPngFilePath - 存放 0.png ~ 9.png 的文件夹路径(不含文件名)
* @param {number} x - 待识别区域的左上角 x 坐标,默认 0
* @param {number} y - 待识别区域的左上角 y 坐标,默认 0
* @param {number} w - 待识别区域的宽度,默认 1920
* @param {number} h - 待识别区域的高度,默认 1080
* @param {number} maxThreshold - 模板匹配起始阈值,默认 0.95(最高可信度)
* @param {number} minThreshold - 模板匹配最低阈值,默认 0.8(最低可信度)
* @param {number} splitCount - 在 maxThreshold 与 minThreshold 之间做几次等间隔阈值递减,默认 3
* @param {number} maxOverlap - 非极大抑制时允许的最大重叠像素,默认 2只要 x 或 y 方向重叠大于该值即视为重复框
*
* @returns {number} 识别出的整数;若没有任何有效数字框则返回 -1
*
* @example
* const mora = await numberTemplateMatch('摩拉数字', 860, 70, 200, 40);
* if (mora >= 0) console.log(`当前摩拉:${mora}`);
*/
async function numberTemplateMatch(
numberPngFilePath,
x = 0, y = 0, w = 1920, h = 1080,
maxThreshold = 0.95,
minThreshold = 0.8,
splitCount = 3,
maxOverlap = 2
) {
let ros = [];
for (let i = 0; i <= 9; i++) {
ros[i] = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(`${numberPngFilePath}/${i}.png`), x, y, w, h);
};
function setThreshold(roArr, newThreshold) {
for (let i = 0; i < roArr.length; i++) {
roArr[i].Threshold = newThreshold;
roArr[i].InitTemplate();
};
};
const gameRegion = captureGameRegion();
const allCandidates = [];
/* 1. splitCount 次等间隔阈值递减 */
for (let k = 0; k < splitCount; k++) {
const curThr = maxThreshold - (maxThreshold - minThreshold) * k / Math.max(splitCount - 1, 1);
setThreshold(ros, curThr);
/* 2. 0-9 每个模板跑一遍,所有框都收 */
for (let digit = 0; digit <= 9; digit++) {
const res = gameRegion.findMulti(ros[digit]);
if (res.count === 0) continue;
for (let i = 0; i < res.count; i++) {
const box = res[i];
allCandidates.push({
digit: digit,
x: box.x,
y: box.y,
w: box.width,
h: box.height,
thr: curThr
});
};
};
};
gameRegion.dispose();
/* 3. 无结果提前返回 -1 */
if (allCandidates.length === 0) {
return -1;
};
/* 4. 非极大抑制(必须 x、y 两个方向重叠都 > maxOverlap 才视为重复) */
const adopted = [];
for (const c of allCandidates) {
let overlap = false;
for (const a of adopted) {
const xOverlap = Math.max(0, Math.min(c.x + c.w, a.x + a.w) - Math.max(c.x, a.x));
const yOverlap = Math.max(0, Math.min(c.y + c.h, a.y + a.h) - Math.max(c.y, a.y));
if (xOverlap > maxOverlap && yOverlap > maxOverlap) {
overlap = true;
break;
}
}
if (!overlap) {
adopted.push(c);
//log.info(`在 [${c.x},${c.y},${c.w},${c.h}] 找到数字 ${c.digit},匹配阈值=${c.thr}`);
}
}
/* 5. 按 x 排序,拼整数;仍无有效框时返回 -1 */
if (adopted.length === 0) return -1;
adopted.sort((a, b) => a.x - b.x);
return adopted.reduce((num, item) => num * 10 + item.digit, 0);
};
// 执行区
(async function() {
await fakeLog("AutoPickLitter脚本", true, true, 0);
@@ -540,6 +637,7 @@ async function checkExpire() {
await genshin.returnMainUi();
await pathingScript.runFile("assets/蒙德清泉镇路线.json");
await genshin.setTime(8,0);
await sleep(700);
//识别对话位置,并点击
let ocrResults = await performOcr("神奇的", dialogZone.x, dialogZone.y, false);
if (ocrResults.success) {
@@ -547,17 +645,12 @@ async function checkExpire() {
await performOcr("如何才", dialogZone.x, dialogZone.y, false);
await sleep(700);
let ocrOver = await performOcr("已",{ min: 1482, max: 1630 }, { min: 912, max: 957 }, false);
await sleep(700);
if (ocrOver.success) {
log.info("已售罄!!!");
} else {
let ocrMora = await performOcr("", { min: 1600, max: 1780 }, { min: 30, max: 60 }, true);
if (ocrMora == "") {
await sleep(700);
ocrMora = await performOcr("", { min: 1600, max: 1780 }, { min: 30, max: 60 }, true);
};
// 处理得到的数据
let onlyNumber = ocrMora.text.replace(/[^0-9]/g, "");
if (BigInt(onlyNumber) >= 300) {
const mora = await numberTemplateMatch('assets/moraNumber', 1600, 30, 180, 30);
if (BigInt(mora) >= 300) {
await sleep(800);
await click(1636,1019);
await sleep(1000);
@@ -806,7 +899,7 @@ async function checkExpire() {
await fakeLog("枫丹梅洛彼得堡福利餐", false, true, 0)
await genshin.returnMainUi();
await pathingScript.runFile("assets/枫丹梅洛彼得堡路线.json");
await sleep(1000);
await sleep(1500);
let ocrResults = await performOcr("布兰", dialogZone.x, dialogZone.y, false);
if (ocrResults.success) {
await sleep(700);
@@ -902,12 +995,11 @@ async function checkExpire() {
if(settings.eggs){
await fakeLog("纳塔悠悠集市龙蛋", false, true, 0)
// 保留打开背包识别龙蛋信息
// let nowDragonEggsNum = record.lastDragonEggsNum;
// if (record.lastDragonEggsNum == "【山之血0飞澜鲨鲨0圣龙君临0太阳的轰鸣0献给小酒杯0菲耶蒂娜0】" || settings.updateEggs) {
// nowDragonEggsNum = await chcekDragonEggs();
// settings.updateEggs = "false";
// };
let nowDragonEggsNum = settings.dragonEggsNum;
let nowDragonEggsNum = record.lastDragonEggsNum;
log.info(`上次的龙蛋信息:${nowDragonEggsNum}`);
if (record.lastDragonEggsNum == "【山之血0飞澜鲨鲨0圣龙君临0太阳的轰鸣0献给小酒杯0菲耶蒂娜0】" || settings.updateEggs) {
nowDragonEggsNum = await chcekDragonEggs();
};
let nowDragonEggs = nowDragonEggsNum.match(/\d+/g).map(Number);
await genshin.returnMainUi();
await pathingScript.runFile("assets/纳塔悠悠集市路线.json");
@@ -951,15 +1043,15 @@ async function checkExpire() {
}else {
// 平均模式
const now = new Date();
const weekNumber = now.getDay()
const weekNumber = now.getDay();
if (nowDragonEggs.every(num => num === nowDragonEggs[0])) {
// 所有元素相同时,按星期规则处理
if (weekNumber === 0) { // 周日:随机一个元素+1
if (weekNumber == 0) { // 周日:随机一个元素+1
figure = Math.floor(Math.random() * 6);
nowDragonEggs[figure]++;
} else { // 周一到周六第n个元素 +n1-6
const index = weekNumber - 1; // 周一对应索引0...周六对应索引5
nowDragonEggs[index]++;
figure = weekNumber - 1; // 周一对应索引0...周六对应索引5
nowDragonEggs[figure]++;
};
} else {
// 元素不同时:给低于平均数且最小的元素+1直到趋于平均
@@ -1239,6 +1331,7 @@ async function checkExpire() {
if (ocrResults1.success) {
await sleep(800);
await click(1012, 765);
await sleep(50);
await click(1012, 765);
};
};
@@ -1291,6 +1384,7 @@ async function checkExpire() {
if (ocrResults1.success) {
await sleep(800);
await click(1012, 765);
await sleep(50);
await click(1012, 765);
};
};

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "提瓦特杂事(收集)",
"version": "2.1",
"version": "2.1.2",
"tags": [
"玄学",
"收集",

View File

@@ -1,3 +1,14 @@
### 版本:2.1.1
修复龙蛋平均模式
### 版本:2.1
1.龙蛋数目由玩家填写,暂时去掉打开背包自动记录龙蛋功能
2.修改去纳塔悠悠集市的路线(中转一下)
3.添加糖雕添加雅珂达、哥伦比娅选项以及后退几步避免用户BGI开启的自动拾取误触调查点
4.转盘提供多种选择
5.去掉圣水中转路线,去掉快速调节时间操作
6.历史更新记录在records文件夹内
### 版本:2.0.4
1.修改多处龙蛋对应位置
2.固定缩放

View File

@@ -1,6 +1,14 @@
上次运行日期: 2026/01/18
上次运行日期: 2026/02/14
上次上香时间: 2026-01-18T11:07:47.147Z
背包龙蛋数目: 【山之血:8,飞澜鲨鲨:3,圣龙君临:4太阳的轰鸣3献给小酒杯8,菲耶蒂娜:4
背包龙蛋数目: 【山之血:1,飞澜鲨鲨:1,圣龙君临:3太阳的轰鸣3献给小酒杯2,菲耶蒂娜:5
>>>>>>>>>> 2026年02月14日
>>>>>>>>>> 2026年02月14日
>>>>>>>>>> 2026年02月14日
>>>>>>>>>> 2026年02月14日
>>>>>>>>>> 2026年02月14日
>>>>>>>>>> 2026年02月01日
>>>>>>>>>> 2026年01月31日
>>>>>>>>>> 2026年01月31日
>>>>>>>>>> 2026年01月18日
>>>>>>>>>> 2026年01月18日
转盘的运势: 「『旭日瞳瞳,驱散迷雾。』就是遇到困难也没关系,一定会得到帮助的意思!」

View File

@@ -55,12 +55,6 @@
],
"default": "随机模式"
},
{
"name": "dragonEggsNum",
"type": "input-text",
"label": "平均模式:自行输入自己背包各龙蛋数目\n顺序为【山-飞-圣-太-献-菲 】\n各龙蛋数目用【-】相连",
"default": "0-0-0-0-0-0"
},
{
"name": "pickupDragonEgg",
"type": "select",
@@ -74,12 +68,12 @@
"闪闪礼蛋·飞澜鲨鲨"
]
},
// {
// "name": "updateEggs",
// "type": "checkbox",
// "label": "是否更新背包各龙蛋数目(默认否)",
// "default": "false"
// },
{
"name": "updateEggs",
"type": "checkbox",
"label": "是否更新背包各龙蛋数目(默认否),勾选上后,下次运行前记得取消",
"default": "false"
},
{
"name": "turntable",
"type": "checkbox",