From 76bc3b3677062c0c1a6b9fa08ff20d12e470159c Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 4 Nov 2024 02:19:09 -0500 Subject: Some old refactoring + some new refactoring --- .clang-format | 2 + .gitignore | 3 + CMakeLists.txt | 26 ++- cardset.cpp | 66 +++++++ cardset.h | 46 +++++ designer.cpp | 69 +++++++ designer.h | 49 +++++ imagestore.cpp | 52 ++++++ imagestore.h | 18 ++ main.cpp | 46 +++++ prefix_search.h | 53 ++---- wizard.cpp | 569 ++++++++++++-------------------------------------------- wizard.h | 25 +++ 13 files changed, 532 insertions(+), 492 deletions(-) create mode 100644 .clang-format create mode 100644 cardset.cpp create mode 100644 cardset.h create mode 100644 designer.cpp create mode 100644 designer.h create mode 100644 imagestore.cpp create mode 100644 imagestore.h create mode 100644 main.cpp create mode 100644 wizard.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8de7fe6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +--- + BasedOnStyle: Google diff --git a/.gitignore b/.gitignore index 2f07f9e..96d50bf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ Makefile CMakeCache.txt CMakeFiles build +build-pluralize +build2024 +newbuild diff --git a/CMakeLists.txt b/CMakeLists.txt index f6380ee..ed3f7e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,25 +1,39 @@ cmake_minimum_required (VERSION 3.1) project (wizard) +set(CMAKE_BUILD_TYPE Debug) + find_package(PkgConfig) pkg_check_modules(tesseract tesseract REQUIRED) +pkg_check_modules(lept lept REQUIRED) pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED) - -add_subdirectory(vendor/curlcpp) +pkg_check_modules(tclap tclap REQUIRED) +pkg_check_modules(curlcpp curlcpp REQUIRED) include_directories( vendor/json vendor/hkutil ${tesseract_INCLUDE_DIRS} + ${lept_INCLUDE_DIRS} ${GraphicsMagick_INCLUDE_DIRS} - ${CURLCPP_SOURCE_DIR}/include) + ${curlcpp_INCLUDE_DIRS} + ${tclap_INCLUDE_DIRS} +) + +link_directories( + ${tesseract_LIBRARY_DIRS} + ${lept_LIBRARY_DIRS} + ${GraphicsMagick_LIBRARY_DIRS} + ${curlcpp_LIBRARY_DIRS} +) -add_executable(wizard wizard.cpp) +add_executable(wizard cardset.cpp imagestore.cpp designer.cpp wizard.cpp main.cpp) set_property(TARGET wizard PROPERTY CXX_STANDARD 14) set_property(TARGET wizard PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries( wizard ${tesseract_LIBRARIES} + ${lept_LIBRARIES} ${GraphicsMagick_LIBRARIES} - lept - curlcpp) + ${curlcpp_LIBRARIES} +) diff --git a/cardset.cpp b/cardset.cpp new file mode 100644 index 0000000..4003eed --- /dev/null +++ b/cardset.cpp @@ -0,0 +1,66 @@ +#include "cardset.h" + +#include + +#include +#include +#include + +cardset::cardset(std::string filename) { + std::ifstream in(filename, std::ios::in | std::ios::binary); + std::ostringstream contents; + contents << in.rdbuf(); + + 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; + } + + size_t cardId = cards_.size(); + cards_.emplace_back(cardId, cardJson["name"], + cardJson["image_uris"]["png"], frame, cardJson["id"]); + + std::string canon = hatkirby::lowercase(cardJson["name"]); + + for (int i = 0; i < canon.length(); i++) { + titles_.add(canon, {cardId, i}, i); + + chars_.insert(canon.at(i)); + } + } + } +} diff --git a/cardset.h b/cardset.h new file mode 100644 index 0000000..217d859 --- /dev/null +++ b/cardset.h @@ -0,0 +1,46 @@ +#ifndef CARDSET_H_09C0428F +#define CARDSET_H_09C0428F + +#include +#include +#include + +#include "prefix_search.h" + +using ps_type = prefix_search>; + +enum class card_frame { m2015, modern }; + +struct card { + size_t id; + std::string name; + std::string imageUri; + card_frame frame; + std::string uuid; + + card(size_t id, std::string name, std::string imageUri, card_frame frame, + std::string uuid) + : id(id), + name(std::move(name)), + imageUri(std::move(imageUri)), + frame(frame), + uuid(std::move(uuid)) {} +}; + +class cardset { + public: + explicit cardset(std::string filename); + + const card& getCard(size_t id) const { return cards_.at(id); } + + const ps_type& getTitles() const { return titles_; } + + const std::set& getCharacters() const { return chars_; } + + private: + std::vector cards_; + ps_type titles_; + std::set chars_; +}; + +#endif /* end of include guard: CARDSET_H_09C0428F */ diff --git a/designer.cpp b/designer.cpp new file mode 100644 index 0000000..024796f --- /dev/null +++ b/designer.cpp @@ -0,0 +1,69 @@ +#include "designer.h" + +std::list designer::generate(std::mt19937& rng) const { + std::list result; + size_t cur = 0; + + while (cur < text_.length()) { + const solution& curSol = get(cur); + const std::vector& posLens = curSol.lengths; + + std::uniform_int_distribution lenDist(0, posLens.size() - 1); + size_t len = posLens.at(lenDist(rng)); + + const ps_type& prefix = curSol.prefix; + std::uniform_int_distribution cardDist(0, prefix.getCount() - 1); + size_t cardIndex = cardDist(rng); + std::tuple pd = prefix.at(cardIndex); + + result.emplace_back(std::get<0>(pd), std::get<1>(pd), len); + + cur += len; + } + + return result; +} + +solution designer::calculate(size_t i) const { + if (i == text_.length()) { + return {titles_, {}, 0}; + } + + const ps_type& prefix = titles_.find(text_, i); + + bool foundScore = false; + size_t bestScore; + std::vector bestLens; + + for (int j = 1; (j <= prefix.getDepth()) && (i + j <= text_.length()); j++) { + const solution& subSol = get(i + j); + + if (subSol.score > 0 || (i + j == text_.length())) { + size_t tempScore = subSol.score + 1; + + if (!foundScore || tempScore < bestScore) { + foundScore = true; + bestScore = tempScore; + + bestLens.clear(); + bestLens.push_back(j); + } else if (tempScore == bestScore) { + bestLens.push_back(j); + } + } + } + + if (!foundScore) { + return {titles_, {}, 0}; + } else { + return {prefix, std::move(bestLens), bestScore}; + } +} + +const solution& designer::get(size_t i) const { + if (!solutions_.at(i)) { + solutions_[i] = std::make_unique(calculate(i)); + } + + return *solutions_.at(i); +} diff --git a/designer.h b/designer.h new file mode 100644 index 0000000..38ae3c3 --- /dev/null +++ b/designer.h @@ -0,0 +1,49 @@ +#ifndef DESIGNER_H_06F8DE64 +#define DESIGNER_H_06F8DE64 + +#include +#include +#include +#include +#include +#include + +#include "cardset.h" +#include "prefix_search.h" + +struct usage { + size_t cardId; + size_t strIndex; + size_t strLen; + + usage(size_t ci, size_t si, size_t sl) + : cardId(ci), strIndex(si), strLen(sl) {} +}; + +struct solution { + const ps_type& prefix; + std::vector lengths; + size_t score; +}; + +class designer { + public: + designer(std::string text, const ps_type& titles) + : text_(std::move(text)), + titles_(titles), + solutions_(text_.length() + 1) {} + + std::list generate(std::mt19937& rng) const; + + private: + const solution& get(size_t i) const; + + solution calculate(size_t i) const; + + const std::string text_; + const ps_type& titles_; + + mutable std::vector> solutions_; +}; + +#endif /* end of include guard: DESIGNER_H_06F8DE64 */ diff --git a/imagestore.cpp b/imagestore.cpp new file mode 100644 index 0000000..09bef92 --- /dev/null +++ b/imagestore.cpp @@ -0,0 +1,52 @@ +#include "imagestore.h" + +#include + +#include +#include + +Magick::Image imagestore::get(std::string key, std::string url) const { + std::string filename = path_ + "/" + key; + + Magick::Image pic; + + if (std::ifstream(filename)) { + pic.read(filename); + } else { + std::ostringstream imgbuf; + curl::curl_ios imgios(imgbuf); + curl::curl_easy imghandle(imgios); + + imghandle.add(url.c_str()); + imghandle.add(30); + imghandle.add(300); + + imghandle.perform(); + + if (imghandle.get_info().get() != 200) { + throw std::runtime_error("Could not download image"); + } + + std::string content_type = + imghandle.get_info().get(); + if (content_type.substr(0, 6) != "image/") { + throw std::runtime_error("Could not download image"); + } + + std::string imgstr = imgbuf.str(); + Magick::Blob img(imgstr.c_str(), imgstr.length()); + + try { + pic.read(img); + } catch (const Magick::ErrorOption& e) { + // Occurs when the the data downloaded from the server is malformed + std::cout << "Magick: " << e.what() << std::endl; + + throw std::runtime_error("Could not download image"); + } + + pic.write(filename); + } + + return pic; +} diff --git a/imagestore.h b/imagestore.h new file mode 100644 index 0000000..8c3fecf --- /dev/null +++ b/imagestore.h @@ -0,0 +1,18 @@ +#ifndef IMAGESTORE_H_80B1E49F +#define IMAGESTORE_H_80B1E49F + +#include + +#include + +class imagestore { + public: + explicit imagestore(std::string path) : path_(std::move(path)) {} + + Magick::Image get(std::string key, std::string url) const; + + private: + std::string path_; +}; + +#endif /* end of include guard: IMAGESTORE_H_80B1E49F */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a0ba7bb --- /dev/null +++ b/main.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include +#include + +#include "wizard.h" + +int main(int argc, char** argv) { + Magick::InitializeMagick(nullptr); + + std::random_device randomDevice; + std::mt19937 rng(5); // randomDevice()); + + try { + TCLAP::CmdLine cmd("Spelling things with MTG cards", ' ', "1.0"); + + TCLAP::ValueArg cardsPath("c", "cards", + "Path to the card definitions file", + true, "", "filename"); + cmd.add(cardsPath); + + TCLAP::ValueArg cachePath( + "i", "image-cache", "Path to store cached card image downloads", true, + "", "filename"); + cmd.add(cachePath); + + TCLAP::ValueArg outputPath( + "o", "output", "Path to write image output to", true, "", "filename"); + cmd.add(outputPath); + + TCLAP::ValueArg jsonPath( + "j", "json", "Path to write JSON output to", false, "", "filename"); + cmd.add(jsonPath); + + cmd.parse(argc, argv); + + wizard app(cardsPath.getValue(), cachePath.getValue(), + outputPath.getValue(), jsonPath.getValue(), rng); + + app.run(); + } catch (const TCLAP::ArgException& e) { + std::cerr << "Error: " << e.error() << " for arg " << e.argId() + << std::endl; + } +} diff --git a/prefix_search.h b/prefix_search.h index fe69ef7..39bc457 100644 --- a/prefix_search.h +++ b/prefix_search.h @@ -1,59 +1,43 @@ #ifndef PREFIX_SEARCH_H_A6672A1D #define PREFIX_SEARCH_H_A6672A1D +#include #include #include -#include template class prefix_search { -public: + public: + prefix_search(size_t depth = 0) : depth_(depth), count_(0) {} - prefix_search(size_t depth = 0) : depth_(depth), count_(0) - { - } + size_t getDepth() const { return depth_; } - size_t getDepth() const - { - return depth_; - } + size_t getCount() const { return count_; } - size_t getCount() const - { - return count_; - } - - void add(std::string& key, T val, size_t i = 0) - { + void add(std::string& key, T val, size_t i = 0) { count_++; - if (i == key.length()) - { + if (i == key.length()) { elements_.push_back(val); } else { char next = key.at(i); - if (!children_.count(next)) - { - children_.emplace(next, prefix_search { depth_ + 1 } ); + if (!children_.count(next)) { + children_.emplace(next, prefix_search{depth_ + 1}); } children_.at(next).add(key, val, i + 1); } } - T at(size_t i) const - { - if (i < elements_.size()) - { + T at(size_t i) const { + if (i < elements_.size()) { return elements_.at(i); } else { i -= elements_.size(); - for (const auto& mapping : children_) - { - if (mapping.second.count_ > i) - { + for (const auto& mapping : children_) { + if (mapping.second.count_ > i) { return mapping.second.at(i); } else { i -= mapping.second.count_; @@ -64,18 +48,15 @@ public: throw std::out_of_range("Index out of range"); } - const prefix_search& find(const std::string& val, size_t i) const - { - if (i == val.length() || !children_.count(val.at(i))) - { + const prefix_search& find(const std::string& val, size_t i) const { + if (i == val.length() || !children_.count(val.at(i))) { return *this; } - return children_.at(val.at(i)).find(val, i+1); + return children_.at(val.at(i)).find(val, i + 1); } -private: - + private: size_t depth_; size_t count_; std::unordered_map> children_; diff --git a/wizard.cpp b/wizard.cpp index e4b79e6..4eaefb3 100644 --- a/wizard.cpp +++ b/wizard.cpp @@ -1,443 +1,114 @@ +#include "wizard.h" +#include +#include +#include +#include -#include -#include -#include -#include #include -#include -#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include "prefix_search.h" - - - - +#include +#include +#include "designer.h" +#include "prefix_search.h" -std::string stripSpaces(std::string in) -{ - in.erase( - std::remove_if( - std::begin(in), - std::end(in), - ::isspace), - std::end(in)); +std::string stripSpaces(std::string in) { + in.erase(std::remove_if(std::begin(in), std::end(in), ::isspace), + std::end(in)); return in; } - - - - -using ps_type = prefix_search>; - -enum class card_frame { - m2015, - modern -}; - -struct card { - size_t id; - std::string name; - std::string imageUri; - card_frame frame; - - card( - size_t id, - std::string name, - std::string imageUri, - card_frame frame) : - id(id), - name(std::move(name)), - imageUri(std::move(imageUri)), - frame(frame) - { - } -}; - -struct usage { - size_t cardId; - size_t strIndex; - size_t strLen; - - usage( - size_t ci, - size_t si, - size_t sl) : - cardId(ci), - strIndex(si), - strLen(sl) - { - } -}; - -struct solution { - const ps_type& prefix; - std::vector lengths; - size_t score; -}; - -class designer { -public: - - designer( - std::string text, - const ps_type& titles) : - text_(std::move(text)), - titles_(titles), - solutions_(text_.length() + 1) - { - } - - std::list generate(std::mt19937& rng) const; - - -private: - - const solution& get(size_t i) const; - - solution calculate(size_t i) const; - - const std::string text_; - const ps_type& titles_; - - mutable std::vector> solutions_; -}; - - - - - - - - - -std::list designer::generate(std::mt19937& rng) const -{ - std::list result; - size_t cur = 0; - - while (cur < text_.length()) - { - const solution& curSol = get(cur); - const std::vector& posLens = curSol.lengths; - - std::uniform_int_distribution lenDist(0, posLens.size() - 1); - size_t len = posLens.at(lenDist(rng)); - - const ps_type& prefix = curSol.prefix; - std::uniform_int_distribution cardDist(0, prefix.getCount() - 1); - size_t cardIndex = cardDist(rng); - std::tuple pd = prefix.at(cardIndex); - - result.emplace_back(std::get<0>(pd), std::get<1>(pd), len); - - cur += len; - } - - return result; -} - - - - -solution designer::calculate(size_t i) const -{ - if (i == text_.length()) - { - return { - titles_, - {}, - 0 - }; - } - - const ps_type& prefix = titles_.find(text_, i); - - bool foundScore = false; - size_t bestScore; - std::vector bestLens; - - for (int j = 1; - (j <= prefix.getDepth()) && (i + j <= text_.length()); - j++) - { - const solution& subSol = get(i + j); - - if (subSol.score > 0 || (i + j == text_.length())) - { - size_t tempScore = subSol.score + 1; - - if (!foundScore || tempScore < bestScore) - { - foundScore = true; - bestScore = tempScore; - - bestLens.clear(); - bestLens.push_back(j); - } else if (tempScore == bestScore) - { - bestLens.push_back(j); - } - } - } - - if (!foundScore) - { - return { - titles_, - {}, - 0 - }; - } else { - return { - prefix, - std::move(bestLens), - bestScore - }; - } -} - -const solution& designer::get(size_t i) const -{ - if (!solutions_.at(i)) - { - solutions_[i] = std::make_unique(calculate(i)); - } - - return *solutions_.at(i); -} - - - - -Magick::Image downloadImage(const std::string& url) -{ - std::ostringstream imgbuf; - curl::curl_ios imgios(imgbuf); - curl::curl_easy imghandle(imgios); - - imghandle.add(url.c_str()); - imghandle.add(30); - imghandle.add(300); - - imghandle.perform(); - - if (imghandle.get_info().get() != 200) - { - throw std::runtime_error("Could not download image"); - } - - std::string content_type = imghandle.get_info().get(); - if (content_type.substr(0, 6) != "image/") - { - throw std::runtime_error("Could not download image"); - } - - std::string imgstr = imgbuf.str(); - Magick::Blob img(imgstr.c_str(), imgstr.length()); - - Magick::Image pic; - - try - { - pic.read(img); - } catch (const Magick::ErrorOption& e) - { - // Occurs when the the data downloaded from the server is malformed - std::cout << "Magick: " << e.what() << std::endl; - - throw std::runtime_error("Could not download image"); - } - - return pic; -} - - - - - class tesseract_deleter { -public: - - void operator()(tesseract::TessBaseAPI* ptr) const - { - ptr->End(); - } + public: + void operator()(tesseract::TessBaseAPI* ptr) const { ptr->End(); } }; using tesseract_ptr = - std::unique_ptr; + std::unique_ptr; class pix_deleter { -public: - - void operator()(Pix* ptr) const - { - pixDestroy(&ptr); - } + public: + void operator()(Pix* ptr) const { pixDestroy(&ptr); } }; using pix_ptr = std::unique_ptr; - - - - - -int main(int argc, char** argv) -{ - Magick::InitializeMagick(nullptr); - - std::random_device randomDevice; - std::mt19937 rng(randomDevice()); - - std::cout << "Compiling prefix search..." << std::endl; - - std::vector cards; - ps_type titles; - - std::set chars; - - { - std::ifstream in( - "/Users/hatkirby/Downloads/scryfall-default-cards.json", - std::ios::in | std::ios::binary); - std::ostringstream contents; - contents << in.rdbuf(); - - - 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") - { - 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); - - std::string canon = hatkirby::lowercase(cardJson["name"]); - - for (int i = 0; i < canon.length(); i++) - { - titles.add(canon, {cardId, i}, i); - - chars.insert(canon.at(i)); - } - } - } - } - +wizard::wizard(std::string cardsPath, std::string cachePath, + std::string outputPath, std::string jsonPath, std::mt19937& rng) + : cards_(cardsPath), + images_(cachePath), + outputPath_(outputPath), + jsonPath_(jsonPath), + rng_(rng) { std::cout << "Characters: "; - for (char ch : chars) - { + + for (char ch : cards_.getCharacters()) { std::cout << ch; } + std::cout << std::endl; +} + +void wizard::run() { + std::string text = "what the heck, it's just some gay guy"; + // getline(std::cin, text); + if (text.back() == '\r') { + text.pop_back(); + } std::cout << "Calculating card list..." << std::endl; - std::string text = "is it pretentious that i'm sending someone over 100 common and uncommon magic cards in an iphone six box"; std::string canonText = hatkirby::lowercase(text); - designer des(canonText, titles); - std::list res = des.generate(rng); + 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.at(u.cardId); + 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 << 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; - Magick::Image cardImg = downloadImage(theCard.imageUri); + Magick::Image cardImg = images_.get(theCard.uuid, theCard.imageUri); std::cout << "Reading text..." << std::endl; Magick::Image titleImg = cardImg; titleImg.magick("TIFF"); - //titleImg.threshold(MaxRGB / 2); + // 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 }; + 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.zoom({margin.width() * 5, margin.height() * 5}); - //titleImg.quantizeColorSpace(Magick::GRAYColorspace); - //titleImg.quantizeColors(2); - //titleImg.quantize(); + // titleImg.quantizeColorSpace(Magick::GRAYColorspace); + // titleImg.quantizeColors(2); + // titleImg.quantize(); titleImg.backgroundColor("white"); titleImg.matte(false); titleImg.resolutionUnits(Magick::PixelsPerInchResolution); - titleImg.density({ 300, 300 }); + titleImg.density({300, 300}); titleImg.type(Magick::GrayscaleType); titleImg.write("title.tif"); @@ -445,15 +116,13 @@ int main(int argc, char** argv) Magick::Blob titleBlob; titleImg.write(&titleBlob); - pix_ptr titlePix { pixReadMemTiff( - reinterpret_cast(titleBlob.data()), - titleBlob.length(), - 0) }; + pix_ptr titlePix{ + pixReadMemTiff(reinterpret_cast(titleBlob.data()), + titleBlob.length(), 0)}; - tesseract_ptr api { new tesseract::TessBaseAPI() }; + tesseract_ptr api{new tesseract::TessBaseAPI()}; - if (api->Init(nullptr, "eng")) - { + if (api->Init(nullptr, "eng")) { throw std::runtime_error("Could not initialize tesseract"); } @@ -463,48 +132,48 @@ int main(int argc, char** argv) tesseract::ResultIterator* ri = api->GetIterator(); tesseract::PageIteratorLevel level = tesseract::RIL_TEXTLINE; bool foundName = false; + size_t extraChars = 0; - if (ri) - { - do - { + if (ri) { + do { const char* line = ri->GetUTF8Text(level); - if (line) - { + if (line) { std::string lineStr(line); - //if (stripSpaces(hatkirby::lowercase(lineStr)).find(stripSpaces(hatkirby::lowercase((cardName)))) == 0) - { + 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; - }*/ + } /* else { + std::cout << "WRONG: " << lineStr << std::endl; + }*/ } } while (ri->Next(level)); } - if (foundName) - { + if (foundName) { level = tesseract::RIL_SYMBOL; + for (int i = 0; i < extraChars; i++) { + ri->Next(level); + } + std::vector> characters; size_t cur = 0; - do - { + do { int x1, y1, x2, y2; ri->BoundingBox(level, &x1, &y1, &x2, &y2); x1 /= 5; x2 /= 5; - if (cardName.at(cur) == ' ') - { - if (cur == 0) - { + if (cardName.at(cur) == ' ') { + if (cur == 0) { characters.emplace_back(0, x1); } else { const auto& prev = characters.back(); @@ -515,73 +184,73 @@ int main(int argc, char** argv) } characters.emplace_back(x1, x2); + std::cout << characters.size() << " " << x1 << "," << x2 << " (" + << ri->GetUTF8Text(level) << ")" << std::endl; cur++; } while (ri->Next(level) && (cur < cardName.length())); - if (cur != cardName.length()) - { + if (cur != cardName.length()) { throw std::runtime_error("Error detecting character bounds"); } - cardImg.crop({ - std::get<1>(characters[u.strIndex + u.strLen - 1]) - - std::get<0>(characters[u.strIndex]), - cardImg.rows(), - margin.xOff() + std::get<0>(characters[u.strIndex]), - 0 - }); - - cardImg.magick("PNG"); - cardImg.write("slice.png"); - } else { - std::cout << "Didn't find name" << std::endl; - } - - + Magick::Image bakImg = cardImg; + unsigned int left = margin.xOff() + std::get<0>(characters[u.strIndex]); + unsigned int right = + margin.xOff() + std::get<1>(characters[u.strIndex + u.strLen - 1]); + cardImg.crop({right - left, cardImg.rows(), left, 0}); + if (ui == 0) { + bakImg.crop({margin.xOff(), bakImg.rows(), 0, 0}); + cardImg.extent({cardImg.columns() + bakImg.columns(), cardImg.rows()}, + "black", Magick::GravityType::EastGravity); + cardImg.composite(bakImg, 0, 0); + } else if (ui == (res.size() - 1)) { + bakImg.crop( + {bakImg.columns() - margin.xOff() - std::get<1>(characters.back()), + bakImg.rows(), margin.xOff() + std::get<1>(characters.back()), 0}); + int xoff = cardImg.columns(); + cardImg.extent({cardImg.columns() + bakImg.columns(), cardImg.rows()}); + cardImg.composite(bakImg, xoff, 0); + } + cardImg.magick("PNG"); + cardImg.write("slice.png"); + } else { + std::cout << "Didn't find name" << std::endl; + } - if (!firstSlice) - { + if (!firstSlice) { firstSlice = true; endImage = cardImg; - } else { + if (theCard.frame == card_frame::m2015) { + // endImage.extent({endImage.columns(), endImage.rows() + 6}, + // Magick::SouthGravity); + } + } else { int xoff = endImage.columns(); 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; + // break; + ui++; } - endImage.magick("PNG"); - endImage.write("output.png"); - - - - - - + endImage.write(outputPath_); } - - diff --git a/wizard.h b/wizard.h new file mode 100644 index 0000000..9b27143 --- /dev/null +++ b/wizard.h @@ -0,0 +1,25 @@ +#ifndef WIZARD_H_1014B13E +#define WIZARD_H_1014B13E + +#include +#include + +#include "cardset.h" +#include "imagestore.h" + +class wizard { + public: + wizard(std::string cardsPath, std::string cachePath, std::string outputPath, + std::string jsonPath, std::mt19937& rng); + + void run(); + + private: + cardset cards_; + imagestore images_; + std::string outputPath_; + std::string jsonPath_; + std::mt19937& rng_; +}; + +#endif /* end of include guard: WIZARD_H_1014B13E */ -- cgit 1.4.1