Skip to content

Commit 59e76fe

Browse files
authored
Merge pull request #558 from jw098/itemprinter
update read_number_waterfill(), Fix Itemprinter material detection
2 parents 3b4fe74 + 0bbeeee commit 59e76fe

File tree

6 files changed

+134
-16
lines changed

6 files changed

+134
-16
lines changed

SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.cpp

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
#include "OCR_RawOCR.h"
1818
#include "OCR_NumberReader.h"
1919

20-
// #include <iostream>
21-
// using std::cout;
22-
// using std::endl;
20+
#include <iostream>
21+
using std::cout;
22+
using std::endl;
2323

2424
namespace PokemonAutomation{
2525
namespace OCR{
@@ -39,6 +39,7 @@ std::string run_number_normalization(const std::string& input){
3939
{'9', '9'},
4040

4141
// Common misreads.
42+
{'|', '1'},
4243
{']', '1'},
4344
{'l', '1'},
4445
{'i', '1'},
@@ -47,6 +48,7 @@ std::string run_number_normalization(const std::string& input){
4748
{'S', '5'},
4849
{'s', '5'},
4950
{'/', '7'},
51+
{'g', '9'},
5052

5153
// Japanese OCR likes to do this.
5254
{U'🄋', '0'},
@@ -101,8 +103,10 @@ int read_number(Logger& logger, const ImageViewRGB32& image, Language language){
101103

102104
int read_number_waterfill(
103105
Logger& logger, const ImageViewRGB32& image,
104-
uint32_t rgb32_min, uint32_t rgb32_max,
105-
bool text_inside_range
106+
uint32_t rgb32_min, uint32_t rgb32_max,
107+
bool text_inside_range,
108+
uint32_t width_max,
109+
bool check_empty_string
106110
){
107111
using namespace Kernels::Waterfill;
108112

@@ -112,7 +116,8 @@ int read_number_waterfill(
112116
ImageRGB32 filtered = to_blackwhite_rgb32_range(image, rgb32_min, rgb32_max, text_inside_range);
113117

114118
// static int c = 0;
115-
// filtered.save("test-" + std::to_string(c++) + ".png");
119+
// filtered.save("zztest-" + std::to_string(c++) + ".png");
120+
// int i = 0;
116121

117122
PackedBinaryMatrix matrix = compress_rgb32_to_binary_range(filtered, 0xff000000, 0xff7f7f7f);
118123

@@ -122,20 +127,31 @@ int read_number_waterfill(
122127
auto iter = session->make_iterator(20);
123128
WaterfillObject object;
124129
while (map.size() < 16 && iter->find_next(object, true)){
130+
if (object.width() > width_max){
131+
logger.log("Skipped this filter: character exceeded max width.");
132+
return -1;
133+
}
125134
map.emplace(object.min_x, std::move(object));
126135
}
127136
}
128137

129138
std::string ocr_text;
130139
for (const auto& item : map){
131140
const WaterfillObject& object = item.second;
132-
ImageRGB32 cropped = extract_box_reference(filtered, object).copy();
141+
ImageRGB32 cropped = extract_box_reference(filtered, object).copy();
133142
PackedBinaryMatrix tmp(object.packed_matrix());
134143
filter_by_mask(tmp, cropped, Color(0xffffffff), true);
135144
ImageRGB32 padded = pad_image(cropped, cropped.width(), 0xffffffff);
136145
std::string ocr = OCR::ocr_read(Language::English, padded);
146+
// padded.save("zztest-cropped" + std::to_string(c) + "-" + std::to_string(i++) + ".png");
147+
// std::cout << ocr[0] << std::endl;
137148
if (!ocr.empty()){
138149
ocr_text += ocr[0];
150+
}else{
151+
if (check_empty_string){
152+
logger.log("Skipped this filter: empty string.");
153+
return -1;
154+
}
139155
}
140156
}
141157

@@ -152,6 +168,41 @@ int read_number_waterfill(
152168
return number;
153169
}
154170

171+
int read_number_waterfill(
172+
Logger& logger, const ImageViewRGB32& image,
173+
std::vector<std::pair<uint32_t, uint32_t>> filters,
174+
uint32_t width_max,
175+
bool text_inside_range
176+
){
177+
178+
179+
std::map<int, uint8_t> candidates;
180+
for (std::pair<uint32_t, uint32_t> filter : filters){
181+
182+
uint32_t rgb32_min = filter.first;
183+
uint32_t rgb32_max = filter.second;
184+
int candidate = read_number_waterfill(logger, image, rgb32_min, rgb32_max, text_inside_range, width_max, true);
185+
if (candidate != -1){
186+
candidates[candidate]++;
187+
}
188+
}
189+
190+
if (candidates.empty()){
191+
logger.log("No valid OCR candidates. Unable to read number.");
192+
return -1;
193+
}
194+
195+
std::pair<int, uint8_t> best;
196+
for (const auto& item : candidates){
197+
if (item.second > best.second){
198+
best = item;
199+
}
200+
}
201+
202+
logger.log("Best candidate: --------------------------> " + std::to_string(best.first));
203+
return best.first;
204+
}
205+
155206

156207

157208

SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define PokemonAutomation_CommonTools_OCR_NumberReader_H
99

1010
#include <stdint.h>
11+
#include <vector>
1112
#include "CommonFramework/Language.h"
1213

1314
namespace PokemonAutomation{
@@ -26,12 +27,26 @@ int read_number(Logger& logger, const ImageViewRGB32& image, Language language =
2627
// This version attempts to improve reliability by first isolating each number
2728
// via waterfill. Then it OCRs each number by itself and recombines them at the
2829
// end. This requires specifying the color range for the text.
30+
//
31+
// text_inside_range: binary filter is applied to the image so that any pixels within the color range will be turned black, and everything else will be white
32+
// width_max: return -1 if any character's width is greater than width_max (likely means that two characters are touching, and so are treated as one large character)
33+
// check_empty_string: if set to true, return -1 (and stop evaluation) if any character returns an empty string from OCR
2934
int read_number_waterfill(
3035
Logger& logger, const ImageViewRGB32& image,
3136
uint32_t rgb32_min, uint32_t rgb32_max,
32-
bool text_inside_range = true
37+
bool text_inside_range = true,
38+
uint32_t width_max = UINT32_MAX,
39+
bool check_empty_string = false
3340
);
3441

42+
// Try OCR with all the given color filters.
43+
// Return the best majority candidate
44+
int read_number_waterfill(
45+
Logger& logger, const ImageViewRGB32& image,
46+
std::vector<std::pair<uint32_t, uint32_t>> filters,
47+
uint32_t width_max,
48+
bool text_inside_range = true
49+
);
3550

3651

3752
}

SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ TestProgram::TestProgram()
219219
LockMode::LOCK_WHILE_RUNNING,
220220
false
221221
)
222+
, IMAGE_PATH(false, "Path to image for testing", LockMode::UNLOCK_WHILE_RUNNING, "default.png", "default.png")
222223
, STATIC_TEXT("Test text...")
223224
, SELECT("String Select", test_database(), LockMode::LOCK_WHILE_RUNNING, 0)
224225
// , PLAYER_LIST("Test Table", LockMode::UNLOCK_WHILE_RUNNING, "Notes")
@@ -258,6 +259,7 @@ TestProgram::TestProgram()
258259
PA_ADD_OPTION(BUTTON0);
259260
PA_ADD_OPTION(BUTTON1);
260261
PA_ADD_OPTION(LANGUAGE);
262+
PA_ADD_OPTION(IMAGE_PATH);
261263
PA_ADD_OPTION(STATIC_TEXT);
262264
PA_ADD_OPTION(SELECT);
263265
// PA_ADD_OPTION(PLAYER_LIST);
@@ -313,12 +315,31 @@ void TestProgram::program(MultiSwitchProgramEnvironment& env, CancellableScope&
313315

314316
// std::terminate();
315317

318+
#if 1
319+
// ImageRGB32 image(IMAGE_PATH);
320+
auto image = feed.snapshot();
321+
322+
ItemPrinterMaterialDetector detector(COLOR_RED, Language::English);
323+
324+
std::vector<ImageFloatBox> boxes = {
325+
// {0.485,0.176758,0.037,0.05}, {0.485,0.250977,0.037,0.05}, {0.485,0.325196,0.037,0.05}, {0.485,0.399415,0.037,0.05}, {0.485,0.473634,0.037,0.05}, {0.485,0.547853,0.037,0.05}, {0.485,0.622072,0.037,0.05}, {0.485,0.696291,0.037,0.05}, {0.485,0.77051,0.037,0.05}, {0.485,0.844729,0.037,0.05},
326+
{0.39,0.176758,0.025,0.05}, {0.39,0.250977,0.025,0.05}, {0.39,0.325196,0.025,0.05}, {0.39,0.399415,0.025,0.05}, {0.39,0.473634,0.025,0.05}, {0.39,0.547853,0.025,0.05}, {0.39,0.622072,0.025,0.05}, {0.39,0.696291,0.025,0.05}, {0.39,0.77051,0.025,0.05}, {0.39,0.844729,0.025,0.05},
327+
};
328+
for (ImageFloatBox box : boxes){
329+
detector.read_number(console.logger(), env.inference_dispatcher(), image, box);
330+
}
331+
332+
#endif
333+
334+
#if 0
335+
316336
ImageRGB32 image("20250323-011605651979.png");
317337

318338
DialogBoxDetector detector;
319339
detector.make_overlays(overlays);
320340
cout << detector.detect(image) << endl;
321341

342+
#endif
322343

323344

324345
#if 0

SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class TestProgram : public MultiSwitchProgramInstance, public ButtonListener{
6868

6969
OCR::LanguageOCROption LANGUAGE;
7070

71+
StringOption IMAGE_PATH;
72+
7173
StaticTextOption STATIC_TEXT;
7274

7375
StringSelectOption SELECT;

SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
1919
#include "PokemonSV_ItemPrinterMaterialDetector.h"
2020

21-
//#include <iostream>
21+
// #include <iostream>
2222

2323
namespace PokemonAutomation{
2424
namespace NintendoSwitch{
@@ -69,7 +69,9 @@ std::array<ImageFloatBox, 10> ItemPrinterMaterialDetector::Material_Boxes(ImageF
6969
for (size_t i = 0; i < 10; i++){
7070
double y = initial_y + i*y_spacing;
7171
material_boxes[i] = ImageFloatBox(x, y, width, height);
72+
// std::cout << "{" << x << "," << y << "," << width << "," << height << "}, ";
7273
}
74+
// std::cout << std::endl;
7375
return material_boxes;
7476
}
7577

@@ -98,9 +100,36 @@ int16_t ItemPrinterMaterialDetector::read_number(
98100

99101
int16_t number;
100102
if (is_dark_text_light_background){
101-
number = (int16_t)OCR::read_number_waterfill(logger, cropped, 0xff000000, 0xff808080);
103+
const std::vector<std::pair<uint32_t, uint32_t>> filters = {
104+
// {0xff000000, 0xffb0b0b0},
105+
{0xff000000, 0xffa0a0a0},
106+
{0xff000000, 0xff959595},
107+
{0xff000000, 0xff909090},
108+
{0xff000000, 0xff858585},
109+
{0xff000000, 0xff808080},
110+
// {0xff000000, 0xff707070},
111+
// {0xff000000, 0xff606060},
112+
// {0xff000000, 0xff505050},
113+
// {0xff000000, 0xff404040},
114+
// {0xff000000, 0xff303030},
115+
// {0xff000000, 0xff202020},
116+
// {0xff000000, 0xff101010},
117+
};
118+
number = (int16_t)OCR::read_number_waterfill(logger, cropped, filters, 24);
102119
}else{
103-
number = (int16_t)OCR::read_number_waterfill(logger, cropped, 0xff808080, 0xffffffff);
120+
const std::vector<std::pair<uint32_t, uint32_t>> filters = {
121+
{0xff808080, 0xffffffff},
122+
{0xff858585, 0xffffffff},
123+
{0xff909090, 0xffffffff},
124+
{0xff959595, 0xffffffff},
125+
{0xffa0a0a0, 0xffffffff},
126+
// {0xffb0b0b0, 0xffffffff},
127+
// {0xffc0c0c0, 0xffffffff},
128+
// {0xffd0d0d0, 0xffffffff},
129+
// {0xffe0e0e0, 0xffffffff},
130+
// {0xfff0f0f0, 0xffffffff},
131+
};
132+
number = (int16_t)OCR::read_number_waterfill(logger, cropped, filters, 24);
104133
}
105134

106135
if (number < 1 || number > 999){

SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ class ItemPrinterMaterialDetector{
7474
int8_t row_index
7575
) const;
7676

77-
78-
private:
7977
int16_t read_number(
8078
Logger& logger, AsyncDispatcher& dispatcher,
8179
const ImageViewRGB32& screen, const ImageFloatBox& box
@@ -85,9 +83,11 @@ class ItemPrinterMaterialDetector{
8583
private:
8684
Color m_color;
8785
Language m_language;
88-
std::array<ImageFloatBox, 10> m_box_mat_value;
89-
std::array<ImageFloatBox, 10> m_box_mat_quantity;
90-
std::array<ImageFloatBox, 10> m_box_mat_name;
86+
std::array<ImageFloatBox, 10> m_box_mat_value; // {0.39,0.176758,0.025,0.05}, {0.39,0.250977,0.025,0.05}, {0.39,0.325196,0.025,0.05}, {0.39,0.399415,0.025,0.05}, {0.39,0.473634,0.025,0.05}, {0.39,0.547853,0.025,0.05}, {0.39,0.622072,0.025,0.05}, {0.39,0.696291,0.025,0.05}, {0.39,0.77051,0.025,0.05}, {0.39,0.844729,0.025,0.05},
87+
std::array<ImageFloatBox, 10> m_box_mat_quantity; // {0.485,0.176758,0.037,0.05}, {0.485,0.250977,0.037,0.05}, {0.485,0.325196,0.037,0.05}, {0.485,0.399415,0.037,0.05}, {0.485,0.473634,0.037,0.05}, {0.485,0.547853,0.037,0.05}, {0.485,0.622072,0.037,0.05}, {0.485,0.696291,0.037,0.05}, {0.485,0.77051,0.037,0.05}, {0.485,0.844729,0.037,0.05},
88+
std::array<ImageFloatBox, 10> m_box_mat_name; // {0.09,0.176758,0.275,0.05}, {0.09,0.250977,0.275,0.05}, {0.09,0.325196,0.275,0.05}, {0.09,0.399415,0.275,0.05}, {0.09,0.473634,0.275,0.05}, {0.09,0.547853,0.275,0.05}, {0.09,0.622072,0.275,0.05}, {0.09,0.696291,0.275,0.05}, {0.09,0.77051,0.275,0.05}, {0.09,0.844729,0.275,0.05},
89+
90+
9191
};
9292

9393

0 commit comments

Comments
 (0)