Skip to content

Commit 30143d4

Browse files
author
Gin
committed
Add dummy LZA dialog box detection
1 parent 60c7cd5 commit 30143d4

File tree

13 files changed

+344
-10
lines changed

13 files changed

+344
-10
lines changed

SerialPrograms/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,8 @@ file(GLOB MAIN_SOURCES
14321432
Source/PokemonLGPE/Programs/ShinyHunting/PokemonLGPE_LegendaryReset.h
14331433
Source/PokemonLGPE/Programs/TestPrograms/PokemonLGPE_SoundListener.cpp
14341434
Source/PokemonLGPE/Programs/TestPrograms/PokemonLGPE_SoundListener.h
1435+
Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.cpp
1436+
Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.h
14351437
Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp
14361438
Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h
14371439
Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp
@@ -2220,6 +2222,8 @@ file(GLOB MAIN_SOURCES
22202222
Source/Tests/NintendoSwitch_Tests.h
22212223
Source/Tests/PokemonLA_Tests.cpp
22222224
Source/Tests/PokemonLA_Tests.h
2225+
Source/Tests/PokemonLZA_Tests.cpp
2226+
Source/Tests/PokemonLZA_Tests.h
22232227
Source/Tests/PokemonSV_Tests.cpp
22242228
Source/Tests/PokemonSV_Tests.h
22252229
Source/Tests/PokemonSwSh_Tests.cpp

SerialPrograms/Scripts/add_new_file.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
target_path = ""
2626
if len (sys.argv) > 1:
2727
target_path = sys.argv[1]
28+
print(f"Adding a new file path {target_path}")
2829

2930
# Sanitize target path:
3031
SerialPrograms_prefix = "SerialPrograms/"
@@ -76,26 +77,33 @@ def get_code_file_range(file_lines: List[str], starting_line: str, ending_line:
7677

7778
cmakelists_path = code_root_path + os.sep + "SerialPrograms" + os.sep + "CMakeLists.txt"
7879
pro_path = code_root_path + os.sep + "SerialPrograms" + os.sep + "SerialPrograms.pro"
80+
print(f"Found CMakeLists path {cmakelists_path}")
81+
print(f"Found QT Pro project file path {pro_path}")
7982

8083
file_lines = read_lines(cmakelists_path)
84+
old_file_lines = file_lines
8185

8286
code_file_start, code_file_end = get_code_file_range(file_lines, "file(GLOB MAIN_SOURCES", ")")
8387
code_file_lines = set(file_lines[code_file_start:code_file_end])
88+
old_num_code_files = len(code_file_lines)
89+
print(f"{old_num_code_files} lines from CMakeLists are for code file paths")
8490

8591
# Add new file
8692
if len(target_path) > 0:
8793
code_file_lines.add(" " + target_path)
8894

8995
code_file_lines = list(code_file_lines)
9096
code_file_lines.sort()
97+
if len(code_file_lines) == old_num_code_files:
98+
print("Warning: you are adding an existing file! The added file is ignored")
9199

92100
# print(f"\"{code_file_lines[0]}\"")
93101

94102
file_lines = file_lines[0:code_file_start] + code_file_lines + file_lines[code_file_end:]
95103
file_lines = [line + "\r\n" for line in file_lines]
96-
97104
with open(cmakelists_path, "w") as f:
98105
f.writelines(file_lines)
106+
print(f"Writed changes back to {cmakelists_path}")
99107

100108

101109
file_lines = read_lines(pro_path)
@@ -148,6 +156,7 @@ def process_pro_lines(file_lines: List[str], starting_line: str, new_path: str)
148156

149157
with open(pro_path, "w") as f:
150158
f.writelines(file_lines)
159+
print(f"Writed changes back to {pro_path}")
151160

152161

153162

SerialPrograms/Scripts/build_code_from_template.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616

1717
from typing import Dict
1818

19+
if len(sys.argv) == 1:
20+
print(
21+
"Usage:\n"
22+
f"python3 {sys.argv[0]} VisualDetector PokemonSV Map PokeCenter Icon"
23+
" - Generates PokemonSV_MapPokeCenterIconDetector.h and PokemonSV_MapPokeCenterIconDetector.cpp"
24+
)
25+
exit(0)
26+
1927
cur_folder_path = os.path.abspath(os.getcwd())
2028

2129
split_path = cur_folder_path.split(os.sep)

SerialPrograms/Scripts/image_viewer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def run(self):
220220
if len(self.highlight_list) > 0:
221221
self.cur_highlight_index = -1 if self.cur_highlight_index == len(self.highlight_list)-1 else self.cur_highlight_index+1
222222
print(f"Change highlight index to {self.cur_highlight_index}/{len(self.highlight_list)}")
223-
elif key == 127: # backspace, remove selected rectangle
223+
elif key == 127 or key == 8: # DEL or backspace (BS), remove selected rectangle
224224
mouse_down = False
225225
mouse_move_counter = 0
226226
if len(self.rects) > 0:

SerialPrograms/SerialPrograms.pro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,11 +1090,13 @@ SOURCES += \
10901090
Source/PokemonSwSh/Resources/PokemonSwSh_TypeMatchup.cpp \
10911091
Source/PokemonSwSh/Resources/PokemonSwSh_TypeSprites.cpp \
10921092
Source/PokemonSwSh/ShinyHuntTracker.cpp \
1093+
Source/PokemonZLA/Inference/PokemonZLA_DialogDetector.cpp \
10931094
Source/Tests/CommandLineTests.cpp \
10941095
Source/Tests/CommonFramework_Tests.cpp \
10951096
Source/Tests/Kernels_Tests.cpp \
10961097
Source/Tests/NintendoSwitch_Tests.cpp \
10971098
Source/Tests/PokemonLA_Tests.cpp \
1099+
Source/Tests/PokemonLZA_Tests.cpp \
10981100
Source/Tests/PokemonSV_Tests.cpp \
10991101
Source/Tests/PokemonSwSh_Tests.cpp \
11001102
Source/Tests/TestMap.cpp \
@@ -2310,11 +2312,13 @@ HEADERS += \
23102312
Source/PokemonSwSh/Resources/PokemonSwSh_TypeMatchup.h \
23112313
Source/PokemonSwSh/Resources/PokemonSwSh_TypeSprites.h \
23122314
Source/PokemonSwSh/ShinyHuntTracker.h \
2315+
Source/PokemonZLA/Inference/PokemonZLA_DialogDetector.h \
23132316
Source/Tests/CommandLineTests.h \
23142317
Source/Tests/CommonFramework_Tests.h \
23152318
Source/Tests/Kernels_Tests.h \
23162319
Source/Tests/NintendoSwitch_Tests.h \
23172320
Source/Tests/PokemonLA_Tests.h \
2321+
Source/Tests/PokemonLZA_Tests.h \
23182322
Source/Tests/PokemonSV_Tests.h \
23192323
Source/Tests/PokemonSwSh_Tests.h \
23202324
Source/Tests/TestMap.h \

SerialPrograms/Source/CommonTools/ImageMatch/WaterfillTemplateMatcher.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ WaterfillTemplateMatcher::WaterfillTemplateMatcher(
4040
dump_debug_image(
4141
global_logger_command_line(),
4242
"CommonFramework/WaterfillTemplateMatcher",
43-
"binary_image",
43+
"waterfill_template_image_black_background",
4444
binaryImage);
4545
}
4646
std::vector<WaterfillObject> objects = find_objects_inplace(matrix, min_area);
@@ -66,7 +66,11 @@ WaterfillTemplateMatcher::WaterfillTemplateMatcher(
6666
const auto exact_image = extract_box_reference(reference, *best);
6767
cout << "Build waterfil template matcher from " << full_path << ", W x H: " << exact_image.width()
6868
<< " x " << exact_image.height() << ", area ratio: " << m_area_ratio << ", Object area: " << best->area << endl;
69-
dump_debug_image(global_logger_command_line(), "CommonFramework/WaterfillTemplateMatcher", "matcher_exact_image", exact_image);
69+
dump_debug_image(
70+
global_logger_command_line(),
71+
"CommonFramework/WaterfillTemplateMatcher",
72+
"waterfill_template_matcher_reference_image",
73+
exact_image);
7074
}
7175
}
7276

@@ -150,7 +154,12 @@ double WaterfillTemplateMatcher::rmsd_original(const ImageViewRGB32& original_im
150154

151155
if (PreloadSettings::debug().IMAGE_TEMPLATE_MATCHING){
152156
cout << "rmsd_original()" << endl;
153-
dump_debug_image(global_logger_command_line(), "CommonFramework/WaterfillTemplateMatcher", "rmsd_original_input", extract_box_reference(original_image, object));
157+
dump_debug_image(
158+
global_logger_command_line(),
159+
"CommonFramework/WaterfillTemplateMatcher",
160+
"waterfill_template_matcher_rmsd_original_input",
161+
extract_box_reference(original_image, object)
162+
);
154163
}
155164

156165
if (!check_aspect_ratio(object.width(), object.height())){

SerialPrograms/Source/CommonTools/ImageMatch/WaterfillTemplateMatcher.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ class WaterfillTemplateMatcher{
6969
// See `double rmsd(const ImageViewRGB32& image) const` on the details of comparing the image against the template.
7070
virtual double rmsd_original(const ImageViewRGB32& original_image, const WaterfillObject& object) const;
7171

72+
// Return the image template mesh
73+
const ImageRGB32& image_template() const { return m_matcher->image_template(); }
74+
7275
protected:
7376
virtual bool check_image(const ImageViewRGB32& image) const{ return true; };
7477
bool check_aspect_ratio(size_t candidate_width, size_t candidate_height) const;

SerialPrograms/Source/CommonTools/Images/WaterfillUtilities.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ bool match_template_by_waterfill(
8080
std::function<bool(Kernels::Waterfill::WaterfillObject& object)> check_matched_object)
8181
{
8282
if (PreloadSettings::debug().IMAGE_TEMPLATE_MATCHING){
83+
dump_debug_image(
84+
global_logger_command_line(),
85+
"CommonFramework/WaterfillTemplateMatcher",
86+
"match_template_by_waterfill_input_image",
87+
image
88+
);
8389
std::cout << "Match template by waterfill, " << filters.size() << " filter(s), size range ("
8490
<< area_thresholds.first << ", ";
8591
if (area_thresholds.second == SIZE_MAX){
@@ -89,20 +95,22 @@ bool match_template_by_waterfill(
8995
}
9096
std::cout << ")" << std::endl;
9197
}
92-
auto matrices = compress_rgb32_to_binary_range(image, filters);
98+
std::vector<PokemonAutomation::PackedBinaryMatrix> matrices = compress_rgb32_to_binary_range(image, filters);
9399

94100
bool detected = false;
95101
bool stop_match = false;
96-
for (PokemonAutomation::PackedBinaryMatrix &matrix : matrices){
102+
for (size_t i_matrix = 0; i_matrix < matrices.size(); i_matrix++){
103+
PackedBinaryMatrix& matrix = matrices[i_matrix];
97104
if (PreloadSettings::debug().IMAGE_TEMPLATE_MATCHING){
98105
ImageRGB32 binaryImage = image.copy();
99106
filter_by_mask(matrix, binaryImage, Color(COLOR_BLACK), true);
100107
//filter_by_mask(matrix, binaryImage, Color(COLOR_WHITE), true);
101108
dump_debug_image(
102109
global_logger_command_line(),
103110
"CommonFramework/WaterfillTemplateMatcher",
104-
"binary_image",
105-
binaryImage);
111+
"match_template_by_waterfill_filtered_image_" + std::to_string(i_matrix),
112+
binaryImage
113+
);
106114
}
107115

108116
std::unique_ptr<Kernels::Waterfill::WaterfillSession> session = Kernels::Waterfill::make_WaterfillSession();
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/* Dialog Detector
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
*/
6+
7+
#include "CommonFramework/ImageTools/ImageStats.h"
8+
#include "CommonTools/Images/SolidColorTest.h"
9+
#include "PokemonLZA_DialogDetector.h"
10+
#include "Common/Cpp/AbstractLogger.h"
11+
#include "CommonTools/ImageMatch/WaterfillTemplateMatcher.h"
12+
#include "CommonTools/Images/WaterfillUtilities.h"
13+
#include "Kernels/Waterfill/Kernels_Waterfill_Types.h"
14+
#include "CommonFramework/VideoPipeline/VideoOverlay.h"
15+
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
16+
17+
//#include <iostream>
18+
//using std::cout;
19+
//using std::endl;
20+
21+
namespace PokemonAutomation{
22+
namespace NintendoSwitch{
23+
namespace PokemonLZA{
24+
25+
26+
class DialogTitleGreenLineMatcher : public ImageMatch::WaterfillTemplateMatcher{
27+
public:
28+
// The white background for the template file is of color range [r=240, g=255, b=230] to [255, 255, 255]
29+
// the green line is of color range [r=180,g=200,b=75] to [190, 214, 110]
30+
DialogTitleGreenLineMatcher() : WaterfillTemplateMatcher(
31+
"PokemonLZA/DialogBox/DialogBoxTitleGreenLine-Template.png", Color(180,200,70), Color(200, 220, 115), 50
32+
) {
33+
m_aspect_ratio_lower = 0.9;
34+
m_aspect_ratio_upper = 1.1;
35+
m_area_ratio_lower = 0.8;
36+
m_area_ratio_upper = 1.1;
37+
}
38+
39+
static const ImageMatch::WaterfillTemplateMatcher& instance() {
40+
static DialogTitleGreenLineMatcher matcher;
41+
return matcher;
42+
}
43+
};
44+
45+
class DialogBlackArrowMatcher : public ImageMatch::WaterfillTemplateMatcher{
46+
public:
47+
// The white background for the template file is of color range [r=240, g=255, b=230] to [255, 255, 255]
48+
// the black arrow color is about [r=36,g=38,b=51]
49+
DialogBlackArrowMatcher() : WaterfillTemplateMatcher(
50+
"PokemonLZA/DialogBox/DialogBoxBlackArrow-Template.png", Color(0,0,0), Color(90, 90, 90), 50
51+
) {
52+
m_aspect_ratio_lower = 0.9;
53+
m_aspect_ratio_upper = 1.1;
54+
m_area_ratio_lower = 0.85;
55+
m_area_ratio_upper = 1.1;
56+
}
57+
58+
static const ImageMatch::WaterfillTemplateMatcher& instance() {
59+
static DialogBlackArrowMatcher matcher;
60+
return matcher;
61+
}
62+
};
63+
64+
65+
66+
NormalDialogDetector::NormalDialogDetector(Logger& logger, VideoOverlay& overlay, bool stop_on_detected)
67+
: VisualInferenceCallback("NormalDialogDetector")
68+
, m_stop_on_detected(stop_on_detected)
69+
, m_detected(false)
70+
, m_title_green_line_box(0.224, 0.727, 0.016, 0.056)
71+
, m_black_arrow_box (0.727, 0.868, 0.037, 0.086)
72+
{}
73+
void NormalDialogDetector::make_overlays(VideoOverlaySet& items) const{
74+
items.add(COLOR_RED, m_title_green_line_box);
75+
items.add(COLOR_RED, m_black_arrow_box);
76+
}
77+
bool NormalDialogDetector::process_frame(const ImageViewRGB32& frame, WallClock timestamp){
78+
const double screen_rel_size = (frame.height() / 1080.0);
79+
const double screen_rel_size_2 = screen_rel_size * screen_rel_size;
80+
81+
bool found_green_title_line = false;
82+
bool found_black_arrow = false;
83+
84+
// Example green pixels from screenshots:
85+
// 194,230,70
86+
// 212,235,127
87+
// 176,212,62
88+
const std::vector<std::pair<uint32_t, uint32_t>> green_line_filters = {
89+
{combine_rgb(160,200,55), combine_rgb(220, 240, 130)}
90+
};
91+
92+
const double min_green_line_size_1080P = 100.0;
93+
const double green_line_rmsd_threshold = 50.0;
94+
const size_t min_green_line_size = size_t(screen_rel_size_2 * min_green_line_size_1080P);
95+
match_template_by_waterfill(
96+
extract_box_reference(frame, m_title_green_line_box),
97+
DialogTitleGreenLineMatcher::instance(),
98+
green_line_filters,
99+
{min_green_line_size, SIZE_MAX},
100+
green_line_rmsd_threshold,
101+
[&](Kernels::Waterfill::WaterfillObject& object) -> bool {
102+
found_green_title_line = true;
103+
return true;
104+
}
105+
);
106+
107+
// Example green pixels from screenshots:
108+
// [37,34,6] [20,15,55]
109+
// [39,42,58] [60,56,74] [50,49,63]
110+
const std::vector<std::pair<uint32_t, uint32_t>> black_arrow_filters = {
111+
{combine_rgb(0,0,0), combine_rgb(100, 100, 100)}
112+
};
113+
114+
const double min_black_arrow_size_1080P = 150.0;
115+
const double black_arrow_rmsd_threshold = 120.0;
116+
const size_t min_black_arrow_size = size_t(screen_rel_size_2 * min_black_arrow_size_1080P);
117+
match_template_by_waterfill(
118+
extract_box_reference(frame, m_black_arrow_box),
119+
DialogBlackArrowMatcher::instance(),
120+
black_arrow_filters,
121+
{min_black_arrow_size, SIZE_MAX},
122+
black_arrow_rmsd_threshold,
123+
[&](Kernels::Waterfill::WaterfillObject& object) -> bool {
124+
found_black_arrow = true;
125+
return true;
126+
}
127+
);
128+
129+
bool is_dialog_box = found_green_title_line & found_black_arrow;
130+
131+
if (is_dialog_box){
132+
m_detected.store(is_dialog_box);
133+
}
134+
135+
return is_dialog_box & m_stop_on_detected;
136+
137+
138+
// size_t hits = 0;
139+
140+
// const ImageStats title_top = image_stats(extract_box_reference(frame, m_title_top));
141+
// const ImageStats title_bottom = image_stats(extract_box_reference(frame, m_title_bottom));
142+
// const ImageStats title_left = image_stats(extract_box_reference(frame, m_title_left));
143+
// const ImageStats title_right = image_stats(extract_box_reference(frame, m_title_right));
144+
145+
// ImageStats top_white = image_stats(extract_box_reference(frame, m_top_white));
146+
// hits += is_white(top_white, 480, 30) ? 1 : 0;
147+
148+
// ImageStats bottom_white = image_stats(extract_box_reference(frame, m_bottom_white));
149+
// hits += is_white(bottom_white, 480, 30) ? 1 : 0;
150+
151+
// ImageStats left_white = image_stats(extract_box_reference(frame, m_left_white));
152+
// hits += is_white(left_white, 480, 30) ? 1 : 0;
153+
154+
// ImageStats right_white = image_stats(extract_box_reference(frame, m_right_white));
155+
// hits += is_white(right_white, 480, 30) ? 1 : 0;
156+
157+
// bool detected = hits == 5;
158+
// m_detected.store(detected, std::memory_order_release);
159+
160+
// return detected && m_stop_on_detected;
161+
}
162+
163+
164+
165+
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)