From d74cd30f452c0fdf3399b38996f421c3b61bccc0 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 22 Jun 2025 01:51:13 -0700 Subject: [PATCH] read_number_waterfill: add another parameter line_index for logging purposes, when multithreaded --- .../CommonTools/OCR/OCR_NumberReader.cpp | 27 ++++--- .../Source/CommonTools/OCR/OCR_NumberReader.h | 10 ++- .../PokemonSV_ItemPrinterMaterialDetector.cpp | 75 ++++++++++--------- .../PokemonSV_ItemPrinterMaterialDetector.h | 3 +- .../PokemonSV_ItemPrinterPrizeReader.cpp | 7 +- .../PokemonSV_ItemPrinterPrizeReader.h | 3 +- 6 files changed, 73 insertions(+), 52 deletions(-) diff --git a/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.cpp b/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.cpp index 9d1ee65fae..9291112345 100644 --- a/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.cpp +++ b/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.cpp @@ -102,19 +102,24 @@ int read_number(Logger& logger, const ImageViewRGB32& image, Language language){ int read_number_waterfill( Logger& logger, const ImageViewRGB32& image, uint32_t rgb32_min, uint32_t rgb32_max, - bool text_inside_range + bool text_inside_range, + int8_t line_index ){ std::string ocr_text = read_number_waterfill_no_normalization(logger, image, rgb32_min, rgb32_max, text_inside_range, UINT32_MAX, false); std::string normalized = run_number_normalization(ocr_text); + std::string line_index_str = ""; + if (line_index != -1){ + line_index_str = "Line " + std::to_string(line_index) + ": "; + } if (normalized.empty()){ - logger.log("OCR Text: \"" + ocr_text + "\" -> \"" + normalized + "\" -> Unable to read.", COLOR_RED); + logger.log(line_index_str + "OCR Text: \"" + ocr_text + "\" -> \"" + normalized + "\" -> Unable to read.", COLOR_RED); return -1; } int number = std::atoi(normalized.c_str()); - logger.log("OCR Text: \"" + ocr_text + "\" -> \"" + normalized + "\" -> " + std::to_string(number)); + logger.log(line_index_str + "OCR Text: \"" + ocr_text + "\" -> \"" + normalized + "\" -> " + std::to_string(number)); return number; } @@ -192,9 +197,13 @@ int read_number_waterfill_multifilter( std::vector> filters, uint32_t width_max, bool text_inside_range, - bool prioritize_numeric_only_results + bool prioritize_numeric_only_results, + int8_t line_index ){ - + std::string line_index_str = ""; + if (line_index != -1){ + line_index_str = "Line " + std::to_string(line_index) + ": "; + } std::map candidates; for (std::pair filter : filters){ @@ -210,7 +219,7 @@ int read_number_waterfill_multifilter( } int candidate = std::atoi(normalized.c_str()); - logger.log("OCR Text: \"" + ocr_text + "\" -> \"" + normalized + "\" -> " + std::to_string(candidate)); + logger.log(line_index_str + "OCR Text: \"" + ocr_text + "\" -> \"" + normalized + "\" -> " + std::to_string(candidate)); if (prioritize_numeric_only_results && is_digits(ocr_text)){ candidates[candidate] += 2; @@ -220,19 +229,19 @@ int read_number_waterfill_multifilter( } if (candidates.empty()){ - logger.log("No valid OCR candidates. Unable to read number.", COLOR_ORANGE); + logger.log(line_index_str + "No valid OCR candidates. Unable to read number.", COLOR_ORANGE); return -1; } std::pair best; for (const auto& item : candidates){ - logger.log("Candidate " + std::to_string(item.first) + ": " + std::to_string(item.second) + " votes"); + logger.log(line_index_str + "Candidate " + std::to_string(item.first) + ": " + std::to_string(item.second) + " votes"); if (item.second > best.second){ best = item; } } - logger.log("Best candidate: --------------------------> " + std::to_string(best.first)); + logger.log(line_index_str + "Best candidate: --------------------------> " + std::to_string(best.first)); return best.first; } diff --git a/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.h b/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.h index 25b59f4738..bc22ce6b2f 100644 --- a/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.h +++ b/SerialPrograms/Source/CommonTools/OCR/OCR_NumberReader.h @@ -27,10 +27,13 @@ int read_number(Logger& logger, const ImageViewRGB32& image, Language language = // This version attempts to improve reliability by first isolating each number // via waterfill. Then it OCRs each number by itself and recombines them at the // end. This requires specifying the color range for the text. +// +// line_index: specifies the current number's row. for logging purposes, when multithreaded. int read_number_waterfill( Logger& logger, const ImageViewRGB32& image, uint32_t rgb32_min, uint32_t rgb32_max, - bool text_inside_range = true + bool text_inside_range = true, + int8_t line_index = -1 ); // run OCR on each individual character in the string of numbers. @@ -53,12 +56,15 @@ int read_number_waterfill( // prioritize_numeric_only_results: // - if true: if OCR reads only numeric characters, the candidate gets 2 votes. If OCR reads non-numeric characters, the candidate gets only 1 vote. // - if false: all reads only get 1 vote +// +// line_index: specifies the current number's row. for logging purposes, when multithreaded. int read_number_waterfill_multifilter( Logger& logger, const ImageViewRGB32& image, std::vector> filters, uint32_t width_max, bool text_inside_range = true, - bool prioritize_numeric_only_results = true + bool prioritize_numeric_only_results = true, + int8_t line_index = -1 ); diff --git a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.cpp b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.cpp index 7bfef8d754..af21270cad 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.cpp +++ b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.cpp @@ -85,7 +85,8 @@ void ItemPrinterMaterialDetector::make_overlays(VideoOverlaySet& items) const{ int16_t ItemPrinterMaterialDetector::read_number( Logger& logger, AsyncDispatcher& dispatcher, - const ImageViewRGB32& screen, const ImageFloatBox& box + const ImageViewRGB32& screen, const ImageFloatBox& box, + int8_t row_index ) const{ ImageViewRGB32 cropped = extract_box_reference(screen, box); @@ -98,39 +99,41 @@ int16_t ItemPrinterMaterialDetector::read_number( bool is_dark_text_light_background = image_stats(filtered).average.sum() > 400; // std::cout << "Average sum of filtered: "<< std::to_string(image_stats(filtered).average.sum()) << std::endl; - int16_t number; - if (is_dark_text_light_background){ - const std::vector> filters = { - // {0xff000000, 0xffb0b0b0}, - {0xff000000, 0xffa0a0a0}, - {0xff000000, 0xff959595}, - {0xff000000, 0xff909090}, - {0xff000000, 0xff858585}, - {0xff000000, 0xff808080}, - // {0xff000000, 0xff707070}, - // {0xff000000, 0xff606060}, - // {0xff000000, 0xff505050}, - // {0xff000000, 0xff404040}, - // {0xff000000, 0xff303030}, - // {0xff000000, 0xff202020}, - // {0xff000000, 0xff101010}, - }; - number = (int16_t)OCR::read_number_waterfill_multifilter(logger, cropped, filters, 24); - }else{ - const std::vector> filters = { - {0xff808080, 0xffffffff}, - {0xff858585, 0xffffffff}, - {0xff909090, 0xffffffff}, - {0xff959595, 0xffffffff}, - {0xffa0a0a0, 0xffffffff}, - // {0xffb0b0b0, 0xffffffff}, - // {0xffc0c0c0, 0xffffffff}, - // {0xffd0d0d0, 0xffffffff}, - // {0xffe0e0e0, 0xffffffff}, - // {0xfff0f0f0, 0xffffffff}, - }; - number = (int16_t)OCR::read_number_waterfill_multifilter(logger, cropped, filters, 24); - } + const std::vector> filters = + [&](){ + if (is_dark_text_light_background){ + return std::vector>{ + // {0xff000000, 0xffb0b0b0}, + {0xff000000, 0xffa0a0a0}, + {0xff000000, 0xff959595}, + {0xff000000, 0xff909090}, + {0xff000000, 0xff858585}, + {0xff000000, 0xff808080}, + // {0xff000000, 0xff707070}, + // {0xff000000, 0xff606060}, + // {0xff000000, 0xff505050}, + // {0xff000000, 0xff404040}, + // {0xff000000, 0xff303030}, + // {0xff000000, 0xff202020}, + // {0xff000000, 0xff101010}, + }; + }else{ + return std::vector>{ + {0xff808080, 0xffffffff}, + {0xff858585, 0xffffffff}, + {0xff909090, 0xffffffff}, + {0xff959595, 0xffffffff}, + {0xffa0a0a0, 0xffffffff}, + // {0xffb0b0b0, 0xffffffff}, + // {0xffc0c0c0, 0xffffffff}, + // {0xffd0d0d0, 0xffffffff}, + // {0xffe0e0e0, 0xffffffff}, + // {0xfff0f0f0, 0xffffffff}, + }; + } + }(); + + int16_t number = (int16_t)OCR::read_number_waterfill_multifilter(logger, cropped, filters, 24, true, true, row_index); if (number < 1 || number > 999){ number = -1; @@ -225,7 +228,7 @@ std::vector ItemPrinterMaterialDetector::find_material_value_row_index( std::vector> tasks(total_rows); for (int8_t row_index = 0; row_index < total_rows; row_index++){ tasks[row_index] = dispatcher.dispatch([&, row_index]{ - int16_t value = read_number(stream.logger(), dispatcher, snapshot, m_box_mat_value[row_index]); + int16_t value = read_number(stream.logger(), dispatcher, snapshot, m_box_mat_value[row_index], row_index); if (value == material_value){ WriteSpinLock lg(lock); row_indexes.push_back(row_index); @@ -246,7 +249,7 @@ int16_t ItemPrinterMaterialDetector::detect_material_quantity( ) const{ context.wait_for_all_requests(); VideoSnapshot snapshot = stream.video().snapshot(); - int16_t value = read_number(stream.logger(), dispatcher, snapshot, m_box_mat_quantity[row_index]); + int16_t value = read_number(stream.logger(), dispatcher, snapshot, m_box_mat_quantity[row_index], row_index); return value; } diff --git a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.h b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.h index f99c4ea356..b41f8b5217 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.h +++ b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterMaterialDetector.h @@ -76,7 +76,8 @@ class ItemPrinterMaterialDetector{ int16_t read_number( Logger& logger, AsyncDispatcher& dispatcher, - const ImageViewRGB32& screen, const ImageFloatBox& box + const ImageViewRGB32& screen, const ImageFloatBox& box, + int8_t row_index ) const; diff --git a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.cpp b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.cpp index 8853403390..e71e0bbad6 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.cpp +++ b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.cpp @@ -164,7 +164,7 @@ std::array ItemPrinterPrizeReader::read_quantity( // filtered.save("DebugDumps/test"+ std::to_string(i) +".png"); tasks[i] = dispatcher.dispatch([&, i]{ - results[i] = read_number(logger, screen, boxes[i]); + results[i] = read_number(logger, screen, boxes[i], (int8_t)i); }); } @@ -179,11 +179,12 @@ std::array ItemPrinterPrizeReader::read_quantity( int16_t ItemPrinterPrizeReader::read_number( Logger& logger, const ImageViewRGB32& screen, - const ImageFloatBox& box + const ImageFloatBox& box, + int8_t line_index ) const{ ImageViewRGB32 cropped = extract_box_reference(screen, box); - int16_t number = (int16_t)OCR::read_number_waterfill(logger, cropped, 0xff808000, 0xffffffff); + int16_t number = (int16_t)OCR::read_number_waterfill(logger, cropped, 0xff808000, 0xffffffff, true, line_index); if (number < 1 || number > 40){ number = 1; // default to 1 if we can't read the prize quantity diff --git a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.h b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.h index 9d40bb1cd2..86508a6cf6 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.h +++ b/SerialPrograms/Source/PokemonSV/Inference/ItemPrinter/PokemonSV_ItemPrinterPrizeReader.h @@ -43,7 +43,8 @@ class ItemPrinterPrizeReader{ int16_t read_number( Logger& logger, const ImageViewRGB32& screen, - const ImageFloatBox& box + const ImageFloatBox& box, + int8_t line_index = -1 ) const; double average_sum_filtered(const ImageViewRGB32& screen, const ImageFloatBox& box) const;