feat.更新企鹅识别库4.2.2版本,新增对第十章磨难/普通的识别支持

This commit is contained in:
MistEO
2022-05-04 02:15:02 +08:00
parent 1df80a8707
commit 5e8b8063c7
9 changed files with 611 additions and 208 deletions

View File

@@ -128,6 +128,30 @@ namespace json
value& operator[](const std::string& key);
value& operator[](std::string&& key);
value operator|(const object& rhs)&;
value operator|(object&& rhs)&;
value operator|(const object& rhs)&&;
value operator|(object&& rhs)&&;
value& operator|=(const object& rhs);
value& operator|=(object&& rhs);
//value operator&(const object& rhs)&;
//value operator&(object&& rhs)&;
//value operator&(const object& rhs)&&;
//value operator&(object&& rhs)&&;
//value& operator&=(const object& rhs);
//value& operator&=(object&& rhs);
value operator+(const array& rhs)&;
value operator+(array&& rhs)&;
value operator+(const array& rhs)&&;
value operator+(array&& rhs)&&;
value& operator+=(const array& rhs);
value& operator+=(array&& rhs);
explicit operator bool() const { return as_boolean(); }
explicit operator int() const { return as_integer(); }
explicit operator long() const { return as_long(); }
@@ -141,14 +165,19 @@ namespace json
private:
static var_t deep_copy(const var_t& src);
template <typename... KeysThenDefaultValue, size_t... KeysIndexes>
decltype(auto) get(std::tuple<KeysThenDefaultValue...> keys_then_default_value, std::index_sequence<KeysIndexes...>) const;
decltype(auto) get(
std::tuple<KeysThenDefaultValue...> keys_then_default_value,
std::index_sequence<KeysIndexes...>) const;
template <typename T, typename FirstKey, typename... RestKeys>
decltype(auto) get_aux(T&& default_value, FirstKey&& first, RestKeys &&... rest) const;
template <typename T, typename UniqueKey>
decltype(auto) get_aux(T&& default_value, UniqueKey&& first) const;
const std::string& as_basic_type_str() const;
std::string& as_basic_type_str();
value_type _type = value_type::Null;
var_t _raw_data;
@@ -164,6 +193,7 @@ namespace json
{
public:
using raw_array = std::vector<value>;
using value_type = raw_array::value_type;
using iterator = raw_array::iterator;
using const_iterator = raw_array::const_iterator;
using reverse_iterator = raw_array::reverse_iterator;
@@ -225,6 +255,14 @@ namespace json
const value& operator[](size_t pos) const;
value& operator[](size_t pos);
array operator+(const array& rhs)&;
array operator+(array&& rhs)&;
array operator+(const array& rhs)&&;
array operator+(array&& rhs)&&;
array& operator+=(const array& rhs);
array& operator+=(array&& rhs);
array& operator=(const array&) = default;
array& operator=(array&&) noexcept = default;
@@ -243,13 +281,14 @@ namespace json
{
public:
using raw_object = std::unordered_map<std::string, value>;
using value_type = raw_object::value_type;
using iterator = raw_object::iterator;
using const_iterator = raw_object::const_iterator;
public:
object() = default;
object(const object& rhs) = default;
object(object&& rhs) = default;
object(object&& rhs) noexcept = default;
object(const raw_object& raw_obj);
object(raw_object&& raw_obj);
object(std::initializer_list<raw_object::value_type> init_list);
@@ -283,6 +322,7 @@ namespace json
const value& get(const std::string& key) const;
template <typename... Args> decltype(auto) emplace(Args &&...args);
template <typename... Args> decltype(auto) insert(Args &&...args);
void clear() noexcept;
bool earse(const std::string& key);
@@ -297,6 +337,22 @@ namespace json
value& operator[](const std::string& key);
value& operator[](std::string&& key);
object operator|(const object& rhs)&;
object operator|(object&& rhs)&;
object operator|(const object& rhs)&&;
object operator|(object&& rhs)&&;
object& operator|=(const object& rhs);
object& operator|=(object&& rhs);
//object operator&(const object& rhs)&;
//object operator&(object&& rhs)&;
//object operator&(const object& rhs)&&;
//object operator&(object&& rhs)&&;
//object& operator&=(const object& rhs);
//object& operator&=(object&& rhs);
object& operator=(const object&) = default;
object& operator=(object&&) = default;
@@ -454,20 +510,12 @@ namespace json
MEOJSON_INLINE const value& value::at(size_t pos) const
{
if (is_array()) {
return std::get<array_ptr>(_raw_data)->at(pos);
}
throw exception("Wrong Type or data empty");
return as_array().at(pos);
}
MEOJSON_INLINE const value& value::at(const std::string& key) const
{
if (is_object()) {
return std::get<object_ptr>(_raw_data)->at(key);
}
throw exception("Wrong Type or data empty");
return as_object().at(key);
}
template <typename... KeysThenDefaultValue>
@@ -533,7 +581,7 @@ namespace json
MEOJSON_INLINE bool value::as_boolean() const
{
if (is_boolean()) {
if (std::string b_str = std::get<std::string>(_raw_data);
if (const std::string& b_str = as_basic_type_str();
b_str == "true") {
return true;
}
@@ -552,7 +600,7 @@ namespace json
MEOJSON_INLINE int value::as_integer() const
{
if (is_number()) {
return std::stoi(std::get<std::string>(_raw_data));
return std::stoi(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -574,7 +622,7 @@ namespace json
MEOJSON_INLINE long value::as_long() const
{
if (is_number()) {
return std::stol(std::get<std::string>(_raw_data));
return std::stol(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -584,7 +632,7 @@ namespace json
MEOJSON_INLINE unsigned long value::as_unsigned_long() const
{
if (is_number()) {
return std::stoul(std::get<std::string>(_raw_data));
return std::stoul(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -594,7 +642,7 @@ namespace json
MEOJSON_INLINE long long value::as_long_long() const
{
if (is_number()) {
return std::stoll(std::get<std::string>(_raw_data));
return std::stoll(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -604,7 +652,7 @@ namespace json
MEOJSON_INLINE unsigned long long value::as_unsigned_long_long() const
{
if (is_number()) {
return std::stoull(std::get<std::string>(_raw_data));
return std::stoull(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -614,7 +662,7 @@ namespace json
MEOJSON_INLINE float value::as_float() const
{
if (is_number()) {
return std::stof(std::get<std::string>(_raw_data));
return std::stof(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -624,7 +672,7 @@ namespace json
MEOJSON_INLINE double value::as_double() const
{
if (is_number()) {
return std::stod(std::get<std::string>(_raw_data));
return std::stod(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -634,7 +682,7 @@ namespace json
MEOJSON_INLINE long double value::as_long_double() const
{
if (is_number()) {
return std::stold(std::get<std::string>(_raw_data));
return std::stold(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -644,7 +692,7 @@ namespace json
MEOJSON_INLINE const std::string value::as_string() const
{
if (is_string()) {
return escape_string(std::get<std::string>(_raw_data));
return escape_string(as_basic_type_str());
}
else {
throw exception("Wrong Type");
@@ -671,11 +719,11 @@ namespace json
MEOJSON_INLINE array& value::as_array()
{
if (is_array()) {
return *std::get<array_ptr>(_raw_data);
}
else if (empty()) {
if (empty()) {
*this = array();
}
if (is_array()) {
return *std::get<array_ptr>(_raw_data);
}
@@ -684,17 +732,26 @@ namespace json
MEOJSON_INLINE object& value::as_object()
{
if (is_object()) {
return *std::get<object_ptr>(_raw_data);
}
else if (empty()) {
if (empty()) {
*this = object();
}
if (is_object()) {
return *std::get<object_ptr>(_raw_data);
}
throw exception("Wrong Type or data empty");
}
MEOJSON_INLINE const std::string& value::as_basic_type_str() const
{
return std::get<std::string>(_raw_data);
}
MEOJSON_INLINE std::string& value::as_basic_type_str()
{
return std::get<std::string>(_raw_data);
}
template<typename... Args>
MEOJSON_INLINE decltype(auto) value::array_emplace(Args &&...args)
{
@@ -719,13 +776,13 @@ namespace json
return "null";
case value_type::Boolean:
case value_type::Number:
return std::get<std::string>(_raw_data);
return as_basic_type_str();
case value_type::String:
return '"' + std::get<std::string>(_raw_data) + '"';
return '"' + as_basic_type_str() + '"';
case value_type::Array:
return std::get<array_ptr>(_raw_data)->to_string();
return as_array().to_string();
case value_type::Object:
return std::get<object_ptr>(_raw_data)->to_string();
return as_object().to_string();
default:
throw exception("Unknown Value Type");
}
@@ -739,13 +796,13 @@ namespace json
return "null";
case value_type::Boolean:
case value_type::Number:
return std::get<std::string>(_raw_data);
return as_basic_type_str();
case value_type::String:
return '"' + std::get<std::string>(_raw_data) + '"';
return '"' + as_basic_type_str() + '"';
case value_type::Array:
return std::get<array_ptr>(_raw_data)->format(shift_str, basic_shift_count);
return as_array().format(shift_str, basic_shift_count);
case value_type::Object:
return std::get<object_ptr>(_raw_data)->format(shift_str, basic_shift_count);
return as_object().format(shift_str, basic_shift_count);
default:
throw exception("Unknown Value Type");
}
@@ -763,50 +820,130 @@ namespace json
MEOJSON_INLINE const value& value::operator[](size_t pos) const
{
if (is_array()) {
return std::get<array_ptr>(_raw_data)->operator[](pos);
}
// Array not support to create by operator[]
throw exception("Wrong Type");
return as_array()[pos];
}
MEOJSON_INLINE value& value::operator[](size_t pos)
{
if (is_array()) {
return std::get<array_ptr>(_raw_data)->operator[](pos);
}
// Array not support to create by operator[]
throw exception("Wrong Type");
return as_array()[pos];
}
MEOJSON_INLINE value& value::operator[](const std::string& key)
{
if (is_object()) {
return std::get<object_ptr>(_raw_data)->operator[](key);
}
// Create a new value by operator[]
else if (empty()) {
if (empty()) {
*this = object();
return std::get<object_ptr>(_raw_data)->operator[](key);
}
throw exception("Wrong Type");
return as_object()[key];
}
MEOJSON_INLINE value& value::operator[](std::string&& key)
{
if (is_object()) {
return std::get<object_ptr>(_raw_data)->operator[](key);
}
// Create a new value by operator[]
else if (empty()) {
if (empty()) {
*this = object();
return std::get<object_ptr>(_raw_data)->operator[](key);
}
throw exception("Wrong Type");
return as_object()[std::move(key)];
}
MEOJSON_INLINE value value::operator|(const object& rhs)&
{
return as_object() | rhs;
}
MEOJSON_INLINE value value::operator|(object&& rhs)&
{
return as_object() | std::move(rhs);
}
MEOJSON_INLINE value value::operator|(const object& rhs)&&
{
return std::move(as_object()) | rhs;
}
MEOJSON_INLINE value value::operator|(object&& rhs)&&
{
return std::move(as_object()) | std::move(rhs);
}
MEOJSON_INLINE value& value::operator|=(const object& rhs)
{
as_object() |= rhs;
return *this;
}
MEOJSON_INLINE value& value::operator|=(object&& rhs)
{
as_object() |= std::move(rhs);
return *this;
}
//MEOJSON_INLINE value value::operator&(const object& rhs)&
//{
// return as_object() & rhs;
//}
//MEOJSON_INLINE value value::operator&(object&& rhs)&
//{
// return as_object() & std::move(rhs);
//}
//MEOJSON_INLINE value value::operator&(const object& rhs)&&
//{
// return std::move(as_object()) & rhs;
//}
//MEOJSON_INLINE value value::operator&(object&& rhs)&&
//{
// return std::move(as_object()) & std::move(rhs);
//}
//MEOJSON_INLINE value& value::operator&=(const object& rhs)
//{
// as_object() &= rhs;
// return *this;
//}
//MEOJSON_INLINE value& value::operator&=(object&& rhs)
//{
// as_object() &= std::move(rhs);
// return *this;
//}
MEOJSON_INLINE value value::operator+(const array& rhs)&
{
return as_array() + rhs;
}
MEOJSON_INLINE value value::operator+(array&& rhs)&
{
return as_array() + std::move(rhs);
}
MEOJSON_INLINE value value::operator+(const array& rhs)&&
{
return std::move(as_array()) + rhs;
}
MEOJSON_INLINE value value::operator+(array&& rhs)&&
{
return std::move(as_array()) + std::move(rhs);
}
MEOJSON_INLINE value& value::operator+=(const array& rhs)
{
as_array() += rhs;
return *this;
}
MEOJSON_INLINE value& value::operator+=(array&& rhs)
{
as_array() += std::move(rhs);
return *this;
}
template <typename... Args>
@@ -885,10 +1022,9 @@ namespace json
static_assert(
std::is_constructible<json::value, typename ArrayType::value_type>::value,
"Parameter can't be used to construct a json::value");
_array_data.reserve(arr.size());
for (auto&& ele : arr) {
_array_data.emplace_back(std::move(ele));
}
_array_data.assign(
std::make_move_iterator(arr.begin()),
std::make_move_iterator(arr.end()));
}
MEOJSON_INLINE const value& array::at(size_t pos) const
@@ -1195,6 +1331,50 @@ namespace json
return _array_data[pos];
}
MEOJSON_INLINE array array::operator+(const array& rhs)&
{
array temp = *this;
temp._array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
return temp;
}
MEOJSON_INLINE array array::operator+(array&& rhs)&
{
array temp = *this;
temp._array_data.insert(_array_data.end(),
std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return temp;
}
MEOJSON_INLINE array array::operator+(const array& rhs)&&
{
_array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
return std::move(*this);
}
MEOJSON_INLINE array array::operator+(array&& rhs)&&
{
_array_data.insert(_array_data.end(),
std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return std::move(*this);
}
MEOJSON_INLINE array& array::operator+=(const array& rhs)
{
_array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
return *this;
}
MEOJSON_INLINE array& array::operator+=(array&& rhs)
{
_array_data.insert(_array_data.end(),
std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return *this;
}
// const raw_array &array::raw_data() const
// {
// return _array_data;
@@ -1211,6 +1391,11 @@ namespace json
return _object_data.emplace(std::forward<Args>(args)...);
}
template <typename... Args> decltype(auto) object::insert(Args &&...args)
{
return _object_data.insert(std::forward<Args>(args)...);
}
MEOJSON_INLINE std::ostream& operator<<(std::ostream& out, const array& arr)
{
// TODO: format output
@@ -1528,6 +1713,108 @@ namespace json
return _object_data[std::move(key)];
}
MEOJSON_INLINE object object::operator|(const object& rhs)&
{
object temp = *this;
temp._object_data.insert(rhs.begin(), rhs.end());
return temp;
}
MEOJSON_INLINE object object::operator|(object&& rhs)&
{
object temp = *this;
//temp._object_data.merge(std::move(rhs._object_data));
temp._object_data.insert(
std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return temp;
}
MEOJSON_INLINE object object::operator|(const object& rhs)&&
{
_object_data.insert(rhs.begin(), rhs.end());
return std::move(*this);
}
MEOJSON_INLINE object object::operator|(object&& rhs)&&
{
//_object_data.merge(std::move(rhs._object_data));
_object_data.insert(
std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return std::move(*this);
}
MEOJSON_INLINE object& object::operator|=(const object& rhs)
{
_object_data.insert(rhs.begin(), rhs.end());
return *this;
}
MEOJSON_INLINE object& object::operator|=(object&& rhs)
{
_object_data.insert(
std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return *this;
}
//MEOJSON_INLINE object object::operator&(const object& rhs)&
//{
// object temp;
// for (const auto& [key, value] : *this) {
// if (rhs.contains(key)) {
// temp.emplace(key, value);
// }
// }
// return temp;
//}
//MEOJSON_INLINE object object::operator&(object&& rhs)&
//{
// object temp;
// for (const auto& [key, value] : *this) {
// if (rhs.contains(key)) {
// temp.emplace(key, value);
// }
// }
// return temp;
//}
//MEOJSON_INLINE object object::operator&(const object& rhs)&&
//{
// object temp;
// for (auto&& [key, value] : *this) {
// if (rhs.contains(key)) {
// temp.emplace(key, std::move(value));
// }
// }
// return temp;
//}
//MEOJSON_INLINE object object::operator&(object&& rhs)&&
//{
// object temp;
// for (auto&& [key, value] : *this) {
// if (rhs.contains(key)) {
// temp.emplace(key, std::move(value));
// }
// }
// return temp;
//}
//MEOJSON_INLINE object& object::operator&=(const object& rhs)
//{
// *this = std::move(*this) & rhs;
// return *this;
//}
//MEOJSON_INLINE object& object::operator&=(object&& rhs)
//{
// *this = std::move(*this) & std::move(rhs);
// return *this;
//}
// const raw_object &object::raw_data() const
// {
// return _object_data;
@@ -1547,10 +1834,9 @@ namespace json
typename MapType::value_type>::value,
"Parameter can't be used to construct a "
"object::raw_object::value_type");
_object_data.reserve(map.size());
for (auto&& ele : map) {
_object_data.emplace(std::move(ele));
}
_object_data.insert(
std::make_move_iterator(map.begin()),
std::make_move_iterator(map.end()));
}
// *************************

View File

@@ -61,11 +61,11 @@ public:
static Resource instance;
return instance;
}
void add(std::string key, std::any src)
void add(const std::string& key, const std::any& src)
{
if (!key.empty())
{
_resource[std::move(key)] = std::move(src);
_resource.insert_or_assign(key, src);
}
}
template <typename ResourceType>

View File

@@ -16,6 +16,7 @@ using dict = nlohmann::ordered_json;
namespace penguin
{
enum CircleFlags
{
X = 0,
@@ -36,7 +37,7 @@ public:
_get_item_list();
return *this;
}
const dict report([[maybe_unused]] bool debug = false)
const dict report(bool debug = false)
{
dict _report = dict::object();
return _report;
@@ -112,7 +113,7 @@ private:
}
}
}
_item_diameter = static_cast<int>(round(cv::mean(item_circles)[R] * 2));
_item_diameter = round(cv::mean(item_circles)[R] * 2);
for (const cv::Vec3i& c : item_circles)
{
@@ -127,7 +128,7 @@ private:
// show_img(img_blur);
int radius = _item_diameter / 2;
int offset = static_cast<int>(radius * 1.2);
int offset = radius * 1.2;
ItemTemplates templs;
for (const cv::Vec3i& c : item_circles)
{
@@ -156,6 +157,7 @@ private:
// show_img(_img_ext);
}
};
} // namespace penguin
#endif // PENGUIN_DEPOT_HPP_

View File

@@ -15,6 +15,7 @@ using dict = nlohmann::ordered_json;
namespace penguin
{
enum class StatusFlags
{
NORMAL = 0,
@@ -162,10 +163,10 @@ public:
_templ_list.emplace_back(templ);
}
}
ItemTemplates(const std::string stage_code)
ItemTemplates(const std::string& stage_code, const std::string& difficulty = "NORMAL")
{
const auto& stage_drop =
resource.get<dict>("stage_index")[stage_code]["drops"];
resource.get<dict>("stage_index")[stage_code][difficulty]["drops"];
const auto& item_templs =
resource.get<std::map<std::string, cv::Mat>>("item_templs");
for (const auto& [_, itemId] : stage_drop.items())
@@ -822,8 +823,8 @@ private:
topleft_new.x *= coeff_multiinv;
topleft_new.y *= coeff_multiinv;
cv::Size size_new = cv::Size(
static_cast<int>(round(TEMPLATE_WIDTH * ((double)_diameter / TEMPLATE_DIAMETER))),
static_cast<int>(round(TEMPLATE_HEIGHT * ((double)_diameter / TEMPLATE_DIAMETER))));
round(TEMPLATE_WIDTH * ((double)_diameter / TEMPLATE_DIAMETER)),
round(TEMPLATE_HEIGHT * ((double)_diameter / TEMPLATE_DIAMETER)));
if (topleft_new.x + size_new.width > width)
{
size_new.width = width - topleft_new.x;
@@ -842,9 +843,9 @@ private:
}
void _get_quantity()
{
cv::Rect quantityrect = cv::Rect(0, static_cast<int>(round(height * _ITEM_QTY_Y_PROP)),
static_cast<int>(round(width * _ITEM_QTY_WIDTH_PROP)),
static_cast<int>(round(height * _ITEM_QTY_HEIGHT_PROP)));
cv::Rect quantityrect = cv::Rect(0, round(height * _ITEM_QTY_Y_PROP),
round(width * _ITEM_QTY_WIDTH_PROP),
round(height * _ITEM_QTY_HEIGHT_PROP));
cv::Mat quantityimg = _img(quantityrect);
_quantity.set_img(quantityimg);
_quantity.analyze();

View File

@@ -18,25 +18,19 @@
#include "depot.hpp"
#include "result.hpp"
static const std::string version = "4.1.1";
static const std::string version = "4.2.2";
static const std::string opencv_version = CV_VERSION;
cv::Mat decode(std::string JSarrayBuffer)
{
std::vector buf(std::make_move_iterator(JSarrayBuffer.begin()),
std::make_move_iterator(JSarrayBuffer.end()));
return cv::imdecode(buf, cv::IMREAD_COLOR);
}
cv::Mat decode(uint8_t* buffer, size_t size)
{
std::vector buf(buffer, buffer + size);
std::vector<uint8_t> buf(std::make_move_iterator(JSarrayBuffer.begin()),
std::make_move_iterator(JSarrayBuffer.end()));
return cv::imdecode(buf, cv::IMREAD_COLOR);
}
void load_server(std::string server)
{
penguin::server = std::move(server);
penguin::server = server;
}
void load_stage_index() // local
@@ -44,14 +38,13 @@ void load_stage_index() // local
dict stage_index;
std::ifstream f("../resources/json/stage_index.json");
f >> stage_index;
f.close();
penguin::resource.add("stage_index", std::move(stage_index));
penguin::resource.add("stage_index", stage_index);
}
void wload_stage_index(std::string stage_index) // wasm
{
auto& resource = penguin::resource;
resource.add("stage_index", dict::parse(std::move(stage_index)));
resource.add("stage_index", dict::parse(stage_index));
}
void load_hash_index() // local
@@ -59,14 +52,13 @@ void load_hash_index() // local
dict hash_index;
std::ifstream f("../resources/json/hash_index.json");
f >> hash_index;
f.close();
penguin::resource.add("hash_index", std::move(hash_index));
penguin::resource.add("hash_index", hash_index);
}
void wload_hash_index(std::string hash_index) // wasm
{
auto& resource = penguin::resource;
resource.add("hash_index", dict::parse(std::move(hash_index)));
resource.add("hash_index", dict::parse(hash_index));
}
void load_templs() // local
@@ -77,9 +69,9 @@ void load_templs() // local
{
std::string itemId = templ.path().stem().string();
cv::Mat templimg = cv::imread(templ.path().string());
item_templs[std::move(itemId)] = templimg; // cv::Mat is a shallow copy
item_templs.insert_or_assign(itemId, templimg);
}
penguin::resource.add("item_templs", std::move(item_templs));
penguin::resource.add("item_templs", item_templs);
}
void wload_templs(std::string itemId, std::string JSarrayBuffer) // wasm
@@ -91,7 +83,7 @@ void wload_templs(std::string itemId, std::string JSarrayBuffer) // wasm
}
auto& item_templs =
resource.get<std::map<std::string, cv::Mat>>("item_templs");
item_templs[std::move(itemId)] = decode(std::move(JSarrayBuffer));
item_templs.insert_or_assign(itemId, decode(std::move(JSarrayBuffer)));
}
const bool env_check()
@@ -103,9 +95,7 @@ class Recognizer
{
public:
Recognizer(std::string mode)
: _mode(std::move(mode))
{
}
: _mode(mode) {}
dict recognize(cv::Mat img, bool detail = false)
{
@@ -115,7 +105,7 @@ public:
}
std::string wrecognize(std::string JSarrayBuffer, bool detail, bool pretty_print)
{
_wset_img(std::move(JSarrayBuffer));
_wset_img(JSarrayBuffer);
_recognize();
return _wget_report(detail, pretty_print);
}

View File

@@ -14,7 +14,7 @@
#include "recognize.hpp"
using dict = nlohmann::ordered_json;
// extern void show_img(cv::Mat src);
extern void show_img(cv::Mat src);
namespace penguin
{ // result
@@ -123,13 +123,13 @@ private:
void _get_is_result()
{
auto& self = *this;
auto resultrect = cv::boundingRect(_img);
self._relate(resultrect.tl());
if (resultrect.empty())
auto result_rect = cv::boundingRect(_img);
self._relate(result_rect.tl());
if (result_rect.empty())
{
return;
}
_img = _img(resultrect);
_img = _img(result_rect);
auto result_img = _img;
_hash = shash(result_img, ResizeFlags::RESIZE_W32_H8);
std::string hash_std =
@@ -146,6 +146,90 @@ private:
}
};
class Widget_Stars : public Widget
{
public:
const bool is_3stars() const { return _stars == 3; }
Widget_Stars() = default;
Widget_Stars(Widget* const parent_widget)
: Widget("stars", parent_widget) {}
Widget_Stars(const cv::Mat& img, Widget* const parent_widget = nullptr)
: Widget("stars", parent_widget)
{
set_img(img);
}
void set_img(const cv::Mat& img)
{
Widget::set_img(img);
if (_img.channels() == 3)
{
cv::cvtColor(_img, _img, cv::COLOR_BGR2GRAY);
cv::threshold(_img, _img, 127, 255, cv::THRESH_BINARY);
}
}
Widget_Stars& analyze()
{
if (!_img.empty())
{
_get_is_3stars();
}
if (_stars != 3)
{
push_exception(ERROR, ExcSubtypeFlags::EXC_FALSE);
}
return *this;
}
const dict report(bool debug = false)
{
dict rpt = dict::object();
rpt.merge_patch(Widget::report(debug));
if (!debug)
{
rpt["count"] = _stars;
}
else
{
rpt["count"] = _stars;
}
return rpt;
}
private:
int _stars = 0;
void _get_is_3stars()
{
auto& self = *this;
auto star_img = _img;
auto sp_ = separate(star_img, DirectionFlags::RIGHT, 1);
auto laststar_range = separate(star_img, DirectionFlags::RIGHT, 1)[0];
auto laststar = star_img(cv::Range(0, height), laststar_range);
auto star_rect = cv::boundingRect(laststar);
if (star_rect.empty())
{
return;
}
auto sp = separate(star_img(cv::Rect(0, 0, width, height / 2)), DirectionFlags::LEFT);
for (auto it = sp.cbegin(); it != sp.cend();)
{
const auto& range = *it;
if (const auto length = range.end - range.start;
length < height * STAR_WIDTH_PROP)
{
it = sp.erase(it);
}
else
{
++it;
}
}
star_rect.x = sp.front().start;
star_rect.width = sp.back().end - star_rect.x;
_img = star_img = star_img(star_rect);
self._relate(star_rect.tl());
_stars = sp.size();
}
};
class Widget_Stage : public WidgetWithCandidate<std::vector<int>, int>
{
public:
@@ -181,7 +265,7 @@ public:
push_exception(ERROR, ExcSubtypeFlags::EXC_NOTFOUND);
}
else if (const auto& stage_index = resource.get<dict>("stage_index");
stage_index[_stage_code()]["existence"] == false)
stage_index[_stage_code()][_difficulty]["existence"] == false)
{
push_exception(ERROR, ExcSubtypeFlags::EXC_ILLEGAL);
}
@@ -229,10 +313,16 @@ public:
}
return rpt;
}
// for temporary workaround
void _set_difficulty(const std::string& difficulty)
{
_difficulty = difficulty;
}
private:
const int _CANDIDATES_COUNT = 5;
bool _existance = false;
std::string _difficulty = "NORMAL"; // for temporary workaround
std::vector<Widget_Character> _stage_chrs;
const std::string _stage_code() const
{
@@ -260,7 +350,7 @@ private:
if (const auto& stage_index = resource.get<dict>("stage_index");
stage_index.contains(stage_code))
{
return (std::string)stage_index[stage_code]["stageId"];
return (std::string)stage_index[stage_code][_difficulty]["stageId"];
}
else
{
@@ -359,36 +449,31 @@ private:
}
};
class Widget_Stars : public Widget
class Widget_Difficulty : public Widget
{
public:
const bool is_3stars() const { return _stars == 3; }
Widget_Stars() = default;
Widget_Stars(Widget* const parent_widget)
: Widget("stars", parent_widget) {}
Widget_Stars(const cv::Mat& img, Widget* const parent_widget = nullptr)
: Widget("stars", parent_widget)
const std::string difficulty() const { return _difficulty; }
Widget_Difficulty() = default;
Widget_Difficulty(Widget* const parent_widget)
: Widget("difficulty", parent_widget) {}
Widget_Difficulty(const cv::Mat& img, Widget* const parent_widget = nullptr)
: Widget("difficulty", parent_widget)
{
set_img(img);
}
void set_img(const cv::Mat& img)
{
Widget::set_img(img);
if (_img.channels() == 3)
{
cv::cvtColor(_img, _img, cv::COLOR_BGR2GRAY);
cv::threshold(_img, _img, 127, 255, cv::THRESH_BINARY);
}
}
Widget_Stars& analyze()
Widget_Difficulty& analyze()
{
if (!_img.empty())
{
_get_is_3stars();
_get_difficulty();
}
if (_stars != 3)
if (_difficulty.empty())
{
push_exception(ERROR, ExcSubtypeFlags::EXC_FALSE);
push_exception(ERROR, ExcSubtypeFlags::EXC_ILLEGAL);
}
return *this;
}
@@ -398,48 +483,59 @@ public:
rpt.merge_patch(Widget::report(debug));
if (!debug)
{
rpt["count"] = _stars;
rpt["difficulty"] = _difficulty;
}
else
{
rpt["count"] = _stars;
rpt["difficulty"] = _difficulty;
}
return rpt;
}
private:
int _stars = 0;
void _get_is_3stars()
std::string _difficulty;
void _get_difficulty()
{
auto& self = *this;
auto star_img = _img;
auto sp_ = separate(star_img, DirectionFlags::RIGHT, 1);
auto laststar_range = separate(star_img, DirectionFlags::RIGHT, 1)[0];
auto laststar = star_img(cv::Range(0, height), laststar_range);
auto starrect = cv::boundingRect(laststar);
if (starrect.empty())
auto img_bin = _img;
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 25, 255, cv::THRESH_BINARY);
auto diff_rect = cv::boundingRect(img_bin);
if (diff_rect.empty())
{
return;
}
auto sp = separate(star_img(cv::Rect(0, 0, width, height / 2)), DirectionFlags::LEFT);
for (auto it = sp.cbegin(); it != sp.cend();)
_img = _img(diff_rect);
auto& self = *this;
self._relate(diff_rect.tl());
int diff_count = 0;
if (!(height < width / 4))
{
const auto& range = *it;
if (auto length = range.end - range.start;
length < height * STAR_WIDTH_PROP)
{
it = sp.erase(it);
}
else
{
++it;
}
img_bin = _img;
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 200, 255, cv::THRESH_BINARY);
int inspction_line = 0.8 * cv::boundingRect(img_bin).y;
img_bin = _img;
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 64, 255, cv::THRESH_BINARY);
auto diff_img = img_bin(cv::Rect(0, inspction_line, width, 1));
diff_count = separate(diff_img, DirectionFlags::LEFT).size();
}
switch (diff_count)
{
case 0:
case 2:
_difficulty = "NORMAL";
break;
case 3:
_difficulty = "TOUGH";
break;
default:
break;
}
starrect.x = sp.front().start;
starrect.width = sp.back().end - starrect.x;
_img = star_img = star_img(starrect);
self._relate(starrect.tl());
_stars = static_cast<int>(sp.size());
}
};
@@ -506,7 +602,7 @@ private:
{
continue;
}
int dist = static_cast<int>(abs(kh - hsv[H]));
int dist = abs(kh - hsv[H]);
_candidates.emplace_back(vtype, dist);
}
std::sort(_candidates.begin(), _candidates.end(),
@@ -616,7 +712,7 @@ private:
while (droptextimg.rows > 0)
{
cv::Mat topline = droptextimg.row(0);
int meanval = static_cast<int>(cv::mean(topline)[0]);
int meanval = cv::mean(topline)[0];
if (meanval < 127)
{
break;
@@ -627,13 +723,13 @@ private:
self.y++;
}
}
auto droptextrect = cv::boundingRect(droptextimg);
if (droptextrect.empty())
auto droptext_rect = cv::boundingRect(droptextimg);
if (droptext_rect.empty())
{
return;
}
droptextimg = droptextimg(droptextrect);
self._relate(droptextrect.tl());
droptextimg = droptextimg(droptext_rect);
self._relate(droptext_rect.tl());
}
};
@@ -684,7 +780,7 @@ public:
}
private:
int _items_count = static_cast<int>(round(width / (height * W_H_PROP)));
int _items_count = round(width / (height * W_H_PROP));
Widget_DroptypeLine _line {this};
Widget_DroptypeText _text {this};
void _get_candidates()
@@ -719,9 +815,10 @@ public:
Widget_DropArea() = default;
Widget_DropArea(Widget* const parent_widget)
: Widget("dropArea", parent_widget) {}
Widget_DropArea(const cv::Mat& img, [[maybe_unused]] const std::string& stage, Widget* const parent_widget = nullptr)
Widget_DropArea(const cv::Mat& img, Widget* const parent_widget = nullptr)
: Widget(img, "dropArea", parent_widget) {}
Widget_DropArea& analyze(const std::string& stage)
Widget_DropArea& analyze(const std::string& stage,
const std::string& difficulty)
{
if (!_img.empty())
{
@@ -742,7 +839,7 @@ public:
widget_label = "droptypes";
push_exception(ERROR, ExcSubtypeFlags::EXC_ILLEGAL);
}
_get_drops(stage);
_get_drops(stage, difficulty);
}
else
{
@@ -762,13 +859,13 @@ public:
rpt["dropTypes"] = dict::array();
rpt["drops"] = dict::array();
size_t droptypes_count = _droptype_list.size(); // will move in "for" in C++20
for (size_t i = 0; i < droptypes_count; i++)
int droptypes_count = _droptype_list.size(); // will move in "for" in C++20
for (int i = 0; i < droptypes_count; i++)
{
rpt["dropTypes"].push_back(_droptype_list[i].report(debug));
}
size_t drops_count = _drop_list.size(); // will move in "for" in C++20
for (size_t i = 0; i < drops_count; i++)
int drops_count = _drop_list.size(); // will move in "for" in C++20
for (int i = 0; i < drops_count; i++)
{
rpt["drops"].push_back(
{{"dropType", Droptype2Str[_drop_list[i].droptype]}});
@@ -785,7 +882,7 @@ private:
auto _get_separate()
{
cv::Mat img_bin = _img;
int offset = static_cast<int>(round(height * 0.75));
int offset = round(height * 0.75);
img_bin.adjustROI(-offset, 0, 0, 0);
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::adaptiveThreshold(img_bin, img_bin, 255, cv::ADAPTIVE_THRESH_MEAN_C,
@@ -814,11 +911,12 @@ private:
}
int baseline_h = row + offset;
auto sp = separate(img_bin(cv::Rect(0, row, width, 1)), DirectionFlags::LEFT);
int item_diameter = static_cast<int>(height / DROP_AREA_HEIGHT_PROP * ITEM_DIAMETER_PROP);
int item_diameter = height / DROP_AREA_HEIGHT_PROP * ITEM_DIAMETER_PROP;
for (auto it = sp.cbegin(); it != sp.cend();)
{
const auto& range = *it;
if (auto length = range.end - range.start; length < item_diameter)
if (const auto length = range.end - range.start;
length < item_diameter)
{
it = sp.erase(it);
}
@@ -887,13 +985,13 @@ private:
_droptype_list = q.top();
}
void _get_drops(std::string stage)
void _get_drops(const std::string& stage, const std::string& difficulty)
{
if (_status == StatusFlags::HAS_ERROR || _status == StatusFlags::ERROR)
{
return;
}
int item_diameter = static_cast<int>(height / DROP_AREA_HEIGHT_PROP * ITEM_DIAMETER_PROP);
int item_diameter = height / DROP_AREA_HEIGHT_PROP * ITEM_DIAMETER_PROP;
ItemTemplates templs {stage};
for (const auto& droptype : _droptype_list)
{
@@ -926,13 +1024,13 @@ private:
int length = (droptype.width) / items_count;
for (int i = 0; i < items_count; i++)
{
std::string cur_label =
std::string label =
"drops." + std::to_string(_drop_list.size());
auto range =
cv::Range(droptype.x - x + length * i,
droptype.x - x + length * (i + 1));
auto dropimg = _img(cv::Range(0, droptype.y - y), range);
Widget_Item drop {dropimg, item_diameter, cur_label, this};
Widget_Item drop {dropimg, item_diameter, label, this};
drop.analyze(templs);
_drop_list.emplace_back(drop, type);
_drops_data.push_back({{"dropType", Droptype2Str[type]},
@@ -1108,10 +1206,10 @@ private:
}
const auto& bv = _baseline_v;
auto drop_area_img = _img(
cv::Range(static_cast<int>(bv.y + bv.height * DROP_AREA_Y_PROP), bv.y + bv.height),
cv::Range(static_cast<int>(bv.x + bv.height * DROP_AREA_X_PROP), width));
cv::Range(bv.y + bv.height * DROP_AREA_Y_PROP, bv.y + bv.height),
cv::Range(bv.x + bv.height * DROP_AREA_X_PROP, width));
_drop_area.set_img(drop_area_img);
_drop_area.analyze(_stage.stage_code());
_drop_area.analyze(_stage.stage_code(), "NORMAL");
}
};
@@ -1127,6 +1225,8 @@ public:
_get_result_label();
_get_stars();
_get_stage();
_get_difficulty();
_stage._set_difficulty(_difficutly.difficulty());
_get_drop_area();
return *this;
}
@@ -1146,6 +1246,7 @@ public:
{
rpt["resultLabel"] = _result_label.report()["isResult"];
rpt["stage"] = _stage.report();
rpt["difficulty"] = _difficutly.report()["difficulty"];
rpt["stars"] = _stars.report()["count"];
rpt["dropArea"] = _drop_area.report();
}
@@ -1153,6 +1254,7 @@ public:
{
rpt["resultLabel"] = _result_label.report(debug);
rpt["stage"] = _stage.report(debug);
rpt["difficulty"] = _difficutly.report(debug);
rpt["stars"] = _stars.report(debug);
rpt["dropArea"] = _drop_area.report(debug);
}
@@ -1161,8 +1263,9 @@ public:
private:
Widget _baseline_v {this};
Widget_Stage _stage {this};
Widget_Stars _stars {this};
Widget_Stage _stage {this};
Widget_Difficulty _difficutly {this};
Widget_ResultLabel _result_label {this};
Widget_DropArea _drop_area {this};
@@ -1172,11 +1275,7 @@ private:
{
return;
}
cv::Mat img_bin = _img(cv::Rect(
0,
static_cast<int>(0.2 * height),
static_cast<int>(0.2 * width),
static_cast<int>(0.4 * height)));
cv::Mat img_bin = _img(cv::Rect(0, 0.2 * height, 0.2 * width, 0.4 * height));
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 120, 255, cv::THRESH_BINARY);
@@ -1192,7 +1291,7 @@ private:
abs(img_temp.cols - last_height) <= 1 &&
abs(first_height - last_height) <= 1)
{
baseline_v_rect = cv::Rect(range.start, sp2.front().start + static_cast<int>(0.2 * height),
baseline_v_rect = cv::Rect(range.start, sp2.front().start + 0.2 * height,
img_temp.cols, sp2.back().end - sp2.front().start);
break;
}
@@ -1219,11 +1318,7 @@ private:
: x(x_), y(y_), width(width_), height(height_), area(area_) {}
};
cv::Mat img_bin = _img(cv::Rect(
0,
static_cast<int>(0.2 * height),
static_cast<int>(0.2 * width),
static_cast<int>(0.4 * height)));
cv::Mat img_bin = _img(cv::Rect(0, 0.2 * height, 0.2 * width, 0.4 * height));
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 120, 255, cv::THRESH_BINARY);
cv::Mat _;
@@ -1265,7 +1360,7 @@ private:
if (ccomps[0].x == ccomps[1].x)
{
baseline_v_rect = cv::Rect(ccomps[0].x,
ccomps[0].y + static_cast<int>(0.2 * height),
ccomps[0].y + 0.2 * height,
ccomps[0].width,
ccomps[1].y - ccomps[0].y + ccomps[1].height);
}
@@ -1277,7 +1372,7 @@ private:
{
if (ccomps[i].x == ccomps[i + 1].x)
baseline_v_rect = cv::Rect(ccomps[i].x,
ccomps[i].y + static_cast<int>(0.2 * height),
ccomps[i].y + 0.2 * height,
ccomps[i].width,
ccomps[i + 1].y - ccomps[i].y + ccomps[i + 1].height);
}
@@ -1302,8 +1397,9 @@ private:
return;
}
const auto& bv = _baseline_v;
auto result_img = _img(cv::Rect(bv.x + bv.width, bv.y,
static_cast<int>(1.6 * bv.height), bv.height));
int left_margin = bv.x + bv.width;
auto result_img = _img(cv::Range(bv.y, bv.y + bv.height / 2),
cv::Range(left_margin, left_margin + 1.5 * bv.height));
cv::Mat img_bin;
cv::cvtColor(result_img, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 200, 255, cv::THRESH_BINARY);
@@ -1319,8 +1415,9 @@ private:
return;
}
const auto& bv = _baseline_v;
auto star_img = _img(cv::Rect(bv.x + bv.width, bv.y,
static_cast<int>(1.2 * bv.height), bv.height));
int left_margin = bv.x + bv.width;
auto star_img = _img(cv::Range(bv.y + bv.height / 2, bv.y + bv.height),
cv::Range(left_margin, left_margin + 1.2 * bv.height));
cv::Mat img_bin;
cv::cvtColor(star_img, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 127, 255, cv::THRESH_BINARY);
@@ -1336,11 +1433,10 @@ private:
return;
}
const auto& bv = _baseline_v;
auto stage_img = _img(cv::Rect(
static_cast<int>(bv.x + bv.width + 0.43 * bv.height),
0,
static_cast<int>(1.6 * bv.height),
bv.y));
int left_margin = bv.x + bv.width;
auto stage_img = _img(cv::Range(0, bv.y),
cv::Range(left_margin + 0.43 * bv.height,
left_margin + 1.5 * bv.height));
cv::Mat img_bin;
cv::cvtColor(stage_img, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 200, 255, cv::THRESH_BINARY);
@@ -1349,6 +1445,24 @@ private:
_stage.set_img(stage_img);
_stage.analyze();
}
void _get_difficulty()
{
if (_status == StatusFlags::HAS_ERROR || _status == StatusFlags::ERROR)
{
return;
}
const auto& bv = _baseline_v;
int left_margin = bv.x + bv.width;
auto diff_img = _img(cv::Range(0, bv.y),
cv::Range(left_margin, left_margin + 0.43 * bv.height));
cv::Mat img_bin;
cv::cvtColor(diff_img, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 25, 255, cv::THRESH_BINARY);
diff_img = diff_img(separate(img_bin, DirectionFlags::TOP, 1)[0],
cv::Range(0, img_bin.cols));
_difficutly.set_img(diff_img);
_difficutly.analyze();
}
void _get_drop_area()
{
if (_status == StatusFlags::HAS_ERROR || _status == StatusFlags::ERROR)
@@ -1357,18 +1471,19 @@ private:
}
const auto& bv = _baseline_v;
cv::Mat img_bin = _img(cv::Range(bv.y + bv.height, height),
cv::Range(bv.x + bv.width, static_cast<int>(0.2 * width)));
cv::Range(bv.x + bv.width, 0.2 * width));
cv::cvtColor(img_bin, img_bin, cv::COLOR_BGR2GRAY);
cv::threshold(img_bin, img_bin, 127, 255, cv::THRESH_BINARY);
auto sp = separate(img_bin, DirectionFlags::TOP, 2);
int top_margin = bv.y + bv.height + sp[1].start;
auto drop_area_img = _img(
cv::Range(top_margin + static_cast<int>(bv.height * DROP_AREA_Y_PROP), top_margin + bv.height),
cv::Range(top_margin + bv.height * DROP_AREA_Y_PROP, top_margin + bv.height),
cv::Range(bv.x + bv.width, width));
_drop_area.set_img(drop_area_img);
_drop_area.analyze(_stage.stage_code());
_drop_area.analyze(_stage.stage_code(), _difficutly.difficulty());
}
};
} // namespace penguin
#endif // PENGUIN_RESULT_HPP_

View File

@@ -80,7 +80,7 @@ MAA 的意思是 MAA Assistant Arknights
- 图像识别库:[opencv](https://github.com/opencv/opencv.git)
- ~~文字识别库:[chineseocr_lite](https://github.com/DayBreak-u/chineseocr_lite.git)~~
- 文字识别库:[PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR)
- 关卡掉落识别:[企鹅物流识别](https://github.com/KumoSiunaus/penguin-stats-recognize-v3)
- 关卡掉落识别:[企鹅物流识别](https://github.com/penguin-statistics/recognizer)
- 地图格子识别:[Arknights-Tile-Pos](https://github.com/yuanyan3060/Arknights-Tile-Pos)
- C++ JSON库[meojson](https://github.com/MistEO/meojson.git)
- C++ 运算符解析器:[calculator](https://github.com/kimwalisch/calculator)

View File

@@ -64,14 +64,23 @@ bool asst::PenguinPack::load_json(const std::string& stage_path, const std::stri
stage_dst["drops"] = json::array(std::move(drops_vector));
stage_dst["existence"] = stage_info.at("existence").at(m_language).at("exist");
cvt_stage_json[std::move(key)] = std::move(stage_dst);
// 企鹅识别 4.2 新增的字段,为了区分第十章的 标准关卡 or 磨难关卡
json::value difficulty_json;
if (stage_dst["stageId"].as_string().find("tough_") == 0) {
difficulty_json["TOUGH"] = std::move(stage_dst);
}
else {
difficulty_json["NORMAL"] = std::move(stage_dst);
}
cvt_stage_json[std::move(key)] |= std::move(difficulty_json.as_object());
}
}
catch (json::exception& e) {
m_last_error = stage_path + " parsing error " + e.what();
return false;
}
::wload_stage_index(cvt_stage_json.to_string());
std::string cvt_string = cvt_stage_json.to_string();
::wload_stage_index(std::move(cvt_stage_json.to_string()));
::wload_hash_index(utils::load_file_without_bom(hash_path));
return true;

View File

@@ -12,7 +12,7 @@
- 图像识别库:[opencv](https://github.com/opencv/opencv.git)
- ~~文字识别库:[chineseocr_lite](https://github.com/DayBreak-u/chineseocr_lite.git)~~
- 文字识别库:[PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR)
- 关卡掉落识别:[企鹅物流识别](https://github.com/KumoSiunaus/penguin-stats-recognize-v3)
- 关卡掉落识别:[企鹅物流识别](https://github.com/penguin-statistics/recognizer)
- C++ JSON库[meojson](https://github.com/MistEO/meojson.git)
- C++ 运算符解析器:[calculator](https://github.com/kimwalisch/calculator)
- C++ base64编解码[cpp-base64](https://github.com/ReneNyffenegger/cpp-base64)