@@ -88,38 +88,58 @@ void StatsReader::read_page1(
8888
8989 ImageViewRGB32 name_box = extract_box_reference (game_screen, jpn ? m_box_name_jpn : m_box_name);
9090
91- // remove shadow
92- ImageRGB32 name_filtered = filter_rgb32_range (
93- name_box, 0xff000000 , 0xffc7c7c7 , Color (0xffc3c3c3 ), true
94- );
95- // make text black
96- name_filtered = filter_rgb32_range (
97- name_filtered, 0xffc8c8c8 , 0xffffffff , Color (0xff000000 ), true
98- );
99-
100- ImageRGB32 name_ready = preprocess_for_ocr (
101- name_filtered, " name" , 7 , 2 , true ,
102- combine_rgb (0 , 0 , 0 ), jpn ? combine_rgb (160 , 160 , 160 ) : combine_rgb (120 , 120 , 120 )
103- );
104-
105- const std::vector<OCR ::TextColorRange> name_text_color_ranges{
106- {combine_rgb (0 , 0 , 0 ), combine_rgb (120 , 120 , 120 )}
107- };
10891
109- if (subset.size () > 0 ){
110- auto name_result = Pokemon::PokemonNameReader (subset).read_substring (
111- logger, language, name_ready, name_text_color_ranges
112- );
113- if (!name_result.results .empty ()){
114- stats.name = name_result.results .begin ()->second .token ;
92+ static const std::vector<int > WHITE_THRESHOLDS = { 180 , 200 , 220 , 230 , 240 };
93+ OCR ::StringMatchResult best_result;
94+ bool initialized = false ;
95+ for (int thresh : WHITE_THRESHOLDS ){
96+ ImageRGB32 name_filtered (name_box.width (), name_box.height ());
97+ for (size_t r = 0 ; r < name_box.height (); r++){
98+ for (size_t c = 0 ; c < name_box.width (); c++){
99+ Color pixel (name_box.pixel (c, r));
100+ if (pixel.red () > thresh && pixel.green () > thresh && pixel.blue () > thresh){
101+ name_filtered.pixel (c, r) = (uint32_t )0xff000000 ; // Black
102+ }else {
103+ name_filtered.pixel (c, r) = (uint32_t )0xffffffff ; // White
104+ }
105+ }
115106 }
116- } else {
117- auto name_result = Pokemon::PokemonNameReader::instance (). read_substring (
118- logger, language, name_ready, name_text_color_ranges
107+ ImageRGB32 name_ready = preprocess_for_ocr (
108+ name_filtered, " name " , 7 , 2 , true ,
109+ combine_rgb ( 0 , 0 , 0 ), jpn ? combine_rgb ( 160 , 160 , 160 ) : combine_rgb ( 140 , 140 , 140 )
119110 );
120- if (!name_result.results .empty ()){
121- stats.name = name_result.results .begin ()->second .token ;
111+
112+ const std::vector<OCR ::TextColorRange> name_text_color_ranges{
113+ {combine_rgb (0 , 0 , 0 ), combine_rgb (120 , 120 , 120 )}
114+ };
115+
116+ OCR ::StringMatchResult result;
117+ if (subset.size () > 0 ){
118+ auto name_result = Pokemon::PokemonNameReader (subset).read_substring (
119+ logger, language, name_ready, name_text_color_ranges
120+ );
121+ if (!name_result.results .empty ()){
122+ result = name_result;
123+ }
124+ }else {
125+ auto name_result = Pokemon::PokemonNameReader::instance ().read_substring (
126+ logger, language, name_ready, name_text_color_ranges
127+ );
128+ if (!name_result.results .empty ()){
129+ result = name_result;
130+ }
122131 }
132+ if (!result.results .empty ()){
133+ if (!initialized){
134+ best_result = result;
135+ initialized = true ;
136+ }else if (result.results .begin ()->first < best_result.results .begin ()->first ){
137+ best_result = result;
138+ }
139+ }
140+ }
141+ if (initialized && !best_result.results .empty ()){
142+ stats.name = best_result.results .begin ()->second .token ;
123143 }
124144
125145 // Detect gender by comparing red vs blue pixels
@@ -148,52 +168,29 @@ void StatsReader::read_page1(
148168 stats.gender = SummaryGender::Genderless;
149169 }
150170
151-
152-
153171 ImageViewRGB32 level_box = extract_box_reference (game_screen, jpn ? m_box_level_jpn : m_box_level);
154172
155- ImageRGB32 level_upscaled =
156- level_box.scale_to (level_box.width () * 4 , level_box.height () * 4 );
157- if (save_debug_images){
158- level_upscaled.save (" DebugDumps/ocr_level_upscaled.png" );
159- }
160-
161- // The level has a colored (lilac) background. The text is white, with a
162- // gray/black shadow. To bridge the gaps and make a solid black character on a
163- // white background: We want to turn BOTH the bright white text AND the dark
164- // shadow into BLACK pixels, and turn the mid-tone lilac background into
165- // WHITE. We can do this by keeping pixels that are very bright (text) or very
166- // dark (shadow).
167-
168- ImageRGB32 level_ready (level_upscaled.width (), level_upscaled.height ());
169- for (size_t r = 0 ; r < level_upscaled.height (); r++){
170- for (size_t c = 0 ; c < level_upscaled.width (); c++){
171- Color pixel (level_upscaled.pixel (c, r));
172- // If it's very bright (white text) OR very dark (shadow), it becomes
173- // black text. Otherwise (lilac background), it becomes white background.
174- if ((pixel.red () > 200 && pixel.green () > 200 && pixel.blue () > 200 ) ||
175- (pixel.red () < 100 && pixel.green () < 100 && pixel.blue () < 100 )){
176- level_ready.pixel (c, r) = (uint32_t )0xff000000 ; // Black
177- }else {
178- level_ready.pixel (c, r) = (uint32_t )0xffffffff ; // White
179- }
180- }
181- }
182-
183- if (save_debug_images){
184- level_ready.save (" DebugDumps/ocr_level_ready.png" );
185- }
186-
187173 if (!GlobalSettings::instance ().USE_PADDLE_OCR ){
188174 // The level uses white text with dark shadow on a lilac background.
189175 // The digit reader's binarizer captures dark pixels (<=190 on all channels)
190176 // but NOT the white text (all channels 255 -> excluded). This leaves the
191177 // shadow outline fragmented into many small disconnected blobs.
192178 // Preprocess: convert bright-white text pixels to black so the binarizer
193179 // merges text + shadow into one solid connected blob per digit.
194- ImageRGB32 preprocessed = filter_rgb32_range (
195- level_box, 0xffc8c8c8 , 0xffffffff , Color (0xff000000 ), true
196- );
180+ ImageRGB32 preprocessed = level_box.scale_to (level_box.width (), level_box.height ());
181+ for (size_t r = 0 ; r < level_box.height (); r++){
182+ for (size_t c = 0 ; c < level_box.width (); c++){
183+ Color pixel (level_box.pixel (c, r));
184+ // Try to detect lilac background first based on low green channel,
185+ // replacing it with a darker lilac color (for matching the template)
186+ // For other pixels, if it's bright it becomes black text.
187+ if ((pixel.blue () > pixel.green () + 25 ) && (pixel.red () > pixel.green () + 15 )){
188+ preprocessed.pixel (c, r) = (uint32_t )0xffd1b0f0 ; // from template
189+ }else if (pixel.red () > 200 && pixel.green () > 200 && pixel.blue () > 200 ){
190+ preprocessed.pixel (c, r) = (uint32_t )0xff000000 ; // Black
191+ }
192+ }
193+ }
197194 if (save_debug_images){
198195 preprocessed.save (" DebugDumps/ocr_level_preprocessed.png" );
199196 }
@@ -215,6 +212,37 @@ void StatsReader::read_page1(
215212 logger, level_digit_view, 230.0 , DigitTemplateType::LevelBox,
216213 " levelDigit" , 0x7F );
217214 }else {
215+ // The level has a colored (lilac) background. The text is white, with a
216+ // gray/black shadow. To bridge the gaps and make a solid black character on a
217+ // white background: We want to turn BOTH the bright white text AND the dark
218+ // shadow into BLACK pixels, and turn the mid-tone lilac background into
219+ // WHITE. We can do this by keeping pixels that are very bright (text) or very
220+ // dark (shadow).
221+ ImageRGB32 level_upscaled =
222+ level_box.scale_to (level_box.width () * 4 , level_box.height () * 4 );
223+ if (save_debug_images){
224+ level_upscaled.save (" DebugDumps/ocr_level_upscaled.png" );
225+ }
226+ ImageRGB32 level_ready (level_upscaled.width (), level_upscaled.height ());
227+ for (size_t r = 0 ; r < level_upscaled.height (); r++){
228+ for (size_t c = 0 ; c < level_upscaled.width (); c++){
229+ Color pixel (level_upscaled.pixel (c, r));
230+ // Try to detect lilac background first based on low green channel.
231+ // For other pixels, if it's very bright (white text) OR very dark (shadow),
232+ // it becomes black text. Otherwise, it becomes white background.
233+ if ((pixel.blue () > pixel.green () + 25 ) && (pixel.red () > pixel.green () + 15 )){
234+ level_ready.pixel (c, r) = (uint32_t )0xffffffff ; // White
235+ }else if ((pixel.red () > 200 && pixel.green () > 200 && pixel.blue () > 200 ) ||
236+ (pixel.red () < 100 && pixel.green () < 100 && pixel.blue () < 100 )){
237+ level_ready.pixel (c, r) = (uint32_t )0xff000000 ; // Black
238+ }else {
239+ level_ready.pixel (c, r) = (uint32_t )0xffffffff ; // White
240+ }
241+ }
242+ }
243+ if (save_debug_images){
244+ level_ready.save (" DebugDumps/ocr_level_ready.png" );
245+ }
218246 // Pass the binarized image to PaddleOCR
219247 stats.level = OCR::read_number (logger, level_ready, language);
220248 }
0 commit comments