From 4bc851544831e37b6173d0ad05806fd841e216fb Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 4 Nov 2024 20:42:36 -0500 Subject: Re-attempt 10 times, some tweaks to OCR, pre-filter card pool --- CMakeLists.txt | 2 + cards_filter.rb | 41 ++++++++++ cardset.cpp | 61 +++++--------- cardset.h | 6 +- wizard.cpp | 243 ++++++++++++++++++++++++++++++++------------------------ 5 files changed, 202 insertions(+), 151 deletions(-) create mode 100644 cards_filter.rb diff --git a/CMakeLists.txt b/CMakeLists.txt index 82e9d64..acc7ea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories( vendor/asio/asio/include vendor/base64/include ${tesseract_INCLUDE_DIRS} + #/Users/hatkirby/Repos/tesseract/include ${lept_INCLUDE_DIRS} ${GraphicsMagick_INCLUDE_DIRS} ${curlcpp_INCLUDE_DIRS} @@ -26,6 +27,7 @@ include_directories( link_directories( ${tesseract_LIBRARY_DIRS} + #/Users/hatkirby/Repos/tesseract/build/here/usr/local/lib ${lept_LIBRARY_DIRS} ${GraphicsMagick_LIBRARY_DIRS} ${curlcpp_LIBRARY_DIRS} diff --git a/cards_filter.rb b/cards_filter.rb new file mode 100644 index 0000000..aba66e3 --- /dev/null +++ b/cards_filter.rb @@ -0,0 +1,41 @@ +require 'json' + +input = File.open(ARGV[0]) do |file| + JSON.load(file) +end + +filtered = input.select do |cardJson| + cardJson["object"] == "card" && + # It needs to have a downloadable image + cardJson.count("image_uris") && + # Make sure we can support the card layout + ["normal", "leveler", "saga"].include?(cardJson["layout"]) && + # We only support modern and m15 frames + ["2015", "2003"].include?(cardJson["frame"]) && + # Digital cards look slightly different so ignore them + !cardJson["digital"] && + # Only use english printings + cardJson["lang"] == "en" && + # Currently not supporting silver bordered cards + cardJson["border_color"] != "silver" && + # It is hard to read the name of a planeswalker + !cardJson["type_line"].include?("Planeswalker") && + # This cuts out checklists and special tokens + cardJson["type_line"] != "Card" && + # Amonkhet invocations are impossible + cardJson["set"] != "mp2" && + # Unknown Event is not a real thing huh + cardJson["set"] != "da1" +end.map do |cardJson| + { + "id" => cardJson["id"], + "name" => cardJson["name"], + "imageUri" => cardJson["image_uris"]["png"], + "frame" => cardJson["frame"], + "artist" => cardJson["artist"] + } +end + +File.open(ARGV[1], 'w') do |file| + JSON.dump(filtered, file) +end diff --git a/cardset.cpp b/cardset.cpp index 4003eed..8097834 100644 --- a/cardset.cpp +++ b/cardset.cpp @@ -14,53 +14,28 @@ cardset::cardset(std::string filename) { nlohmann::json cardsJson = nlohmann::json::parse(contents.str()); for (const auto& cardJson : cardsJson) { - if ( - // The object needs to be a card - cardJson["object"] == "card" && - // It needs to have a downloadable image - cardJson.count("image_uris") && - // Make sure we can support the card layout - (cardJson["layout"] == "normal" || cardJson["layout"] == "leveler" || - cardJson["layout"] == "saga") && - // Digital cards look slightly different so ignore them - !cardJson["digital"] && - // Only use english printings - cardJson["lang"] == "en" && - // Currently not supporting silver bordered cards - cardJson["border_color"] != "silver" && - // It is hard to read the name of a planeswalker - cardJson["type_line"].get().find("Planeswalker") == - std::string::npos && - // This cuts out checklists and special tokens - cardJson["type_line"] != "Card" && - // Amonkhet invocations are impossible - cardJson["set"] != "mp2" && - // Unknown Event is not a real thing huh - cardJson["set"] != "da1" && - // Ignore cards with the special legendary flare - (!cardJson.count("frame_effects") || - !cardJson["frame_effects"].count("legendary"))) { - card_frame frame; - - if (cardJson["frame"] == "2015") { - frame = card_frame::m2015; - } else if (cardJson["frame"] == "2003") { - frame = card_frame::modern; - } else { - continue; - } + // We're pre-processing the cardset now to reduce the work done here (but + // mostly the space taken by the data). + card_frame frame; + + if (cardJson["frame"] == "2015") { + frame = card_frame::m2015; + } else if (cardJson["frame"] == "2003") { + frame = card_frame::modern; + } else { + continue; + } - size_t cardId = cards_.size(); - cards_.emplace_back(cardId, cardJson["name"], - cardJson["image_uris"]["png"], frame, cardJson["id"]); + size_t cardId = cards_.size(); + cards_.emplace_back(cardId, cardJson["name"], cardJson["imageUri"], frame, + cardJson["id"], cardJson["artist"]); - std::string canon = hatkirby::lowercase(cardJson["name"]); + std::string canon = hatkirby::lowercase(cardJson["name"]); - for (int i = 0; i < canon.length(); i++) { - titles_.add(canon, {cardId, i}, i); + for (int i = 0; i < canon.length(); i++) { + titles_.add(canon, {cardId, i}, i); - chars_.insert(canon.at(i)); - } + chars_.insert(canon.at(i)); } } } diff --git a/cardset.h b/cardset.h index 217d859..2e39f5c 100644 --- a/cardset.h +++ b/cardset.h @@ -17,14 +17,16 @@ struct card { std::string imageUri; card_frame frame; std::string uuid; + std::string artist; card(size_t id, std::string name, std::string imageUri, card_frame frame, - std::string uuid) + std::string uuid, std::string artist) : id(id), name(std::move(name)), imageUri(std::move(imageUri)), frame(frame), - uuid(std::move(uuid)) {} + uuid(std::move(uuid)), + artist(std::move(artist)) {} }; class cardset { diff --git a/wizard.cpp b/wizard.cpp index a1d7df4..c99ec79 100644 --- a/wizard.cpp +++ b/wizard.cpp @@ -55,6 +55,24 @@ void wizard::set_status_callback(status_callback_type callback) { } Magick::Image wizard::run() { + tesseract_ptr api{new tesseract::TessBaseAPI()}; + + if (api->Init(nullptr, "eng", tesseract::OEM_TESSERACT_LSTM_COMBINED)) { + throw std::runtime_error("Could not initialize tesseract"); + } + + std::string allChars; + for (char ch : cards_.getCharacters()) { + allChars.push_back(ch); + if (std::isalpha(ch)) { + allChars.push_back(std::toupper(ch)); + } + } + + if (!api->SetVariable("tessedit_char_whitelist", allChars.c_str())) { + throw std::runtime_error("Could not set tesseract allowlist"); + } + std::string text = text_; //"what the heck, it's just some gay guy"; // getline(std::cin, text); if (text.back() == '\r') { @@ -68,104 +86,109 @@ Magick::Image wizard::run() { std::string canonText = hatkirby::lowercase(text); designer des(canonText, cards_.getTitles()); - std::list res = des.generate(rng_); Magick::Image endImage; - bool firstSlice = false; - int ui = 0; - - for (const usage& u : res) { - const card& theCard = cards_.getCard(u.cardId); - const std::string& cardName = theCard.name; - - std::cout << cardName.substr(0, u.strIndex) << "[" - << cardName.substr(u.strIndex, u.strLen) << "]" - << cardName.substr(u.strIndex + u.strLen) << std::endl; - - std::cout << "Downloading image..." << std::endl; - if (status_callback_) { - status_callback_("Downloading image..."); - } - - Magick::Image cardImg = images_.get(theCard.uuid, theCard.imageUri); - - std::cout << "Reading text..." << std::endl; - if (status_callback_) { - status_callback_("Reading text..."); - } - - Magick::Image titleImg = cardImg; - titleImg.magick("TIFF"); - - // titleImg.threshold(MaxRGB / 2); - titleImg.write("pre.tif"); - - Magick::Geometry margin; - - if (theCard.frame == card_frame::m2015) { - margin = Magick::Geometry{595, 46, 57, 54}; - } else if (theCard.frame == card_frame::modern) { - margin = Magick::Geometry{581, 50, 63, 57}; - } - - titleImg.crop(margin); - titleImg.zoom({margin.width() * 5, margin.height() * 5}); - - // titleImg.quantizeColorSpace(Magick::GRAYColorspace); - // titleImg.quantizeColors(2); - // titleImg.quantize(); - titleImg.backgroundColor("white"); - titleImg.matte(false); - titleImg.resolutionUnits(Magick::PixelsPerInchResolution); - titleImg.density({300, 300}); - titleImg.type(Magick::GrayscaleType); + bool success = false; + for (int attempt = 0; attempt < 10; attempt++) { + endImage = {}; + + std::list res = des.generate(rng_); + + bool firstSlice = false; + int ui = 0; + + bool failure = false; + for (const usage& u : res) { + const card& theCard = cards_.getCard(u.cardId); + const std::string& cardName = theCard.name; + + std::cout << cardName.substr(0, u.strIndex) << "[" + << cardName.substr(u.strIndex, u.strLen) << "]" + << cardName.substr(u.strIndex + u.strLen) << std::endl; + + std::cout << "Downloading image..." << std::endl; + if (status_callback_) { + std::ostringstream status_stream; + status_stream << "Processing card " << (ui + 1) << "/" << res.size() + << " (attempt " << (attempt + 1) << ")..."; + status_callback_(status_stream.str()); + } - titleImg.write("title.tif"); + Magick::Image cardImg = images_.get(theCard.uuid, theCard.imageUri); - Magick::Blob titleBlob; - titleImg.write(&titleBlob); + std::cout << "Reading text..." << std::endl; - pix_ptr titlePix{ - pixReadMemTiff(reinterpret_cast(titleBlob.data()), - titleBlob.length(), 0)}; + Magick::Image titleImg = cardImg; + titleImg.magick("TIFF"); - tesseract_ptr api{new tesseract::TessBaseAPI()}; + // titleImg.threshold(MaxRGB / 2); + titleImg.write("pre.tif"); - if (api->Init(nullptr, "eng")) { - throw std::runtime_error("Could not initialize tesseract"); - } + Magick::Geometry margin; - api->SetImage(titlePix.get()); - api->Recognize(nullptr); + if (theCard.frame == card_frame::m2015) { + margin = Magick::Geometry{595, 50, 57, 54}; + } else if (theCard.frame == card_frame::modern) { + margin = Magick::Geometry{581, 50, 63, 57}; + } - tesseract::ResultIterator* ri = api->GetIterator(); - tesseract::PageIteratorLevel level = tesseract::RIL_TEXTLINE; - bool foundName = false; - size_t extraChars = 0; + titleImg.crop(margin); + titleImg.zoom({margin.width() * 5, margin.height() * 5}); + + // titleImg.quantizeColorSpace(Magick::GRAYColorspace); + // titleImg.quantizeColors(2); + // titleImg.quantize(); + titleImg.backgroundColor("white"); + titleImg.matte(false); + titleImg.resolutionUnits(Magick::PixelsPerInchResolution); + titleImg.density({300, 300}); + titleImg.type(Magick::GrayscaleType); + titleImg.gamma(3); + // titleImg.level(0.2, 0.8); + + titleImg.write("title.tif"); + + Magick::Blob titleBlob; + titleImg.write(&titleBlob); + + pix_ptr titlePix{pixReadMemTiff( + reinterpret_cast(titleBlob.data()), + titleBlob.length(), 0)}; + + api->SetImage(titlePix.get()); + api->Recognize(nullptr); + + tesseract::ResultIterator* ri = api->GetIterator(); + tesseract::PageIteratorLevel level = tesseract::RIL_TEXTLINE; + bool foundName = false; + size_t extraChars = 0; + + if (ri) { + do { + const char* line = ri->GetUTF8Text(level); + + if (line) { + std::string lineStr(line); + + size_t leadin = hatkirby::lowercase(lineStr).find( + hatkirby::lowercase((cardName))); + if (leadin != std::string::npos) { + foundName = true; + extraChars = leadin; + + break; + } /* else { + std::cout << "WRONG: " << lineStr << std::endl; + }*/ + } + } while (ri->Next(level)); + } - if (ri) { - do { - const char* line = ri->GetUTF8Text(level); - - if (line) { - std::string lineStr(line); - - size_t leadin = hatkirby::lowercase(lineStr).find( - hatkirby::lowercase((cardName))); - // if (leadin != std::string::npos) - { - foundName = true; - // extraChars = leadin; - - break; - } /* else { - std::cout << "WRONG: " << lineStr << std::endl; - }*/ - } - } while (ri->Next(level)); - } + if (!foundName) { + failure = true; + break; + } - if (foundName) { level = tesseract::RIL_SYMBOL; for (int i = 0; i < extraChars; i++) { @@ -233,32 +256,40 @@ Magick::Image wizard::run() { cardImg.magick("PNG"); cardImg.write("slice.png"); - } else { - std::cout << "Didn't find name" << std::endl; - } - if (!firstSlice) { - firstSlice = true; - endImage = cardImg; + if (!firstSlice) { + firstSlice = true; + endImage = cardImg; - if (theCard.frame == card_frame::m2015) { - // endImage.extent({endImage.columns(), endImage.rows() + 6}, - // Magick::SouthGravity); - } - } else { - int xoff = endImage.columns(); + if (theCard.frame == card_frame::m2015) { + // endImage.extent({endImage.columns(), endImage.rows() + 6}, + // Magick::SouthGravity); + } + } else { + int xoff = endImage.columns(); - endImage.backgroundColor("black"); + endImage.backgroundColor("black"); - endImage.extent({endImage.columns() + cardImg.columns(), cardImg.rows()}, - Magick::WestGravity); + endImage.extent( + {endImage.columns() + cardImg.columns(), cardImg.rows()}, + Magick::WestGravity); - endImage.composite(cardImg, xoff, - (theCard.frame == card_frame::m2015) ? 6 : 0); + endImage.composite(cardImg, xoff, + (theCard.frame == card_frame::m2015) ? 6 : 0); + } + + // break; + ui++; } - // break; - ui++; + if (!failure) { + success = true; + break; + } + } + + if (!success) { + throw std::domain_error("Could not generate card"); } endImage.magick("PNG"); -- cgit 1.4.1