diff --git a/Cpp_example/B02_Image_information_statistics/README.md b/Cpp_example/B02_Image_information_statistics/README.md index 124505f653f47f44a714144669eed1175f55b320..aba6ccc1d0774cb5130f7fae6a77b2f06e8b03e2 100755 --- a/Cpp_example/B02_Image_information_statistics/README.md +++ b/Cpp_example/B02_Image_information_statistics/README.md @@ -105,7 +105,7 @@ cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc); ### 3.1 流程图 - + ### 3.2 代码解释 - 读取图像文件 diff --git a/Cpp_example/D08_pp_humanseg/CMakeLists.txt b/Cpp_example/D08_pp_humanseg/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d9e44bb1910ccb77c42a130f4331ad1fdc12df1b --- /dev/null +++ b/Cpp_example/D08_pp_humanseg/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.10) + +project(pp_humanseg) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置rknpu2 +set(RKNPU2_BACKEND_BASE_DIR "${LockzhinerVisionModule_ROOT_PATH}/include/lockzhiner_vision_module/vision/deep_learning/runtime") +if(NOT EXISTS ${RKNPU2_BACKEND_BASE_DIR}) + message(FATAL_ERROR "RKNPU2 backend base dir missing: ${RKNPU2_BACKEND_BASE_DIR}") +endif() + + +add_executable(Test-pp_humanseg pp_humanseg.cc) +target_include_directories(Test-pp_humanseg PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} ${rknpu2_INCLUDE_DIRS} ${RKNPU2_BACKEND_BASE_DIR}) +target_link_libraries(Test-pp_humanseg PRIVATE ${OPENCV_LIBRARIES} ${NCNN_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-pp_humanseg + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/D08_pp_humanseg/README.md b/Cpp_example/D08_pp_humanseg/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7deb1303787d67d49636c41de493e14138946121 --- /dev/null +++ b/Cpp_example/D08_pp_humanseg/README.md @@ -0,0 +1,450 @@ +# 图像分割 +本章节在 Lockzhiner Vision Module 上基于paddleseg模型, 实现了一个PP-HumanSeg人像分割案例。 +## 1. 基本知识简介 +### 1.1 人像分割简介 +人像分割是一种基于计算机视觉的技术,通过深度学习算法精准识别图像或视频中的人物主体,将其与背景进行像素级分离。该技术可实时运行于移动端及嵌入式设备,广泛应用于虚拟背景、智能抠图、视频会议美颜等场景,支持复杂光照、多样姿态和遮挡情况下的高精度分割,兼顾处理速度与效果。 +### 1.2 人像分割常用方法 +目前对于实现人像分割任务的方法有很多,下面介绍几种常用的人像分割实现方法。 +- ​​传统算法(如GrabCut)​:基于颜色直方图与图割优化,适合简单背景,计算量小但精度有限。 +- U-Net系列:编码器-解码器结构,医学图像起家,适合精细边缘,需较高算力。 +- ​DeepLab系列:采用空洞卷积扩大感受野,擅长复杂场景,模型较大。 +- ​​BiSeNet​:双分支结构平衡速度与精度,实时分割首选,移动端友好。 +- ​​PP-HumanSeg​:百度自研轻量模型,专为人像优化,支持半监督训练,RKNN部署效率高。 + +这些方法各有优势,其中在工业部署方面PP-HumanSeg(精度与速度平衡)和BiSeNet(高性价比)更适合,可配合OpenCV后处理优化边缘。 + +--- + +## 2. C++ API 文档 +### 2.1 RKNPU2Backend 类 +#### 2.1.1 头文件 +```cpp +#include "rknpu2_backend/rknpu2_backend.h" +``` +- 作用:创建一个RKNPU2Backend类,用于实现对rknn模型的处理。 + +#### 2.1.2 构造类函数 +```cpp +ockzhiner_vision_module::vision::RKNPU2Backend backend; +``` +- 作用:创建一个RKNPU2Backen类型的对象实例,用于实现人像分割。 +- 参数说明: + - 无 +- 返回值: + - 无 + +#### 2.1.3 Initialize 函数 +```cpp +bool Initialize(const std::string &model_path, const std::string ¶m_path = "") override; +``` +- 作用:初始化 RKNN 模型,加载模型文件和可选参数文件,完成推理引擎的准备工作。 +- 参数说明: + - model_path:必需参数,RKNN 模型文件路径(.rknn 格式)。 + - param_path:可选参数,额外参数文件路径(某些场景下用于补充模型配置,默认空字符串)。 +- 返回值:返回ture/false,表示模型初始化是否成功。 + +#### 2.1.4 Run 函数 +```cpp +bool Run(); +``` +- 作用:执行模型推理计算,驱动输入数据通过模型计算得到输出结果。 +- 参数说明: + - 无 +- 返回值: + - true:推理执行成功。 + - false:推理失败(可能原因:输入数据未准备、内存不足等)。 + +#### 2.1.5 GetInputAttrs 函数 +```cpp +const std::vector& GetInputAttrs() const; +``` +- 作用:获取模型所有输入张量的属性信息(维度/形状、数据类型、量化参数等)。 +- 参数说明: + - 无 +- 返回值:常量引用形式的 rknn_tensor_attr 向量,包含输入张量属性。 + +#### 2.1.6 GetOutputAttrs 函数 +```cpp +const std::vector& GetInputMemories() const; +``` +- 作用:获取模型所有输出张量的属性信息。 +- 参数说明:无 +- 返回值:常量引用形式的 rknn_tensor_attr 向量,包含输出张量属性。 + +## 3. PP-Humanseg人像分割代码解析 +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化模型 +```cpp +backend.Initialize(model_path) +``` +- 获取输入输出属性 +```cpp +const auto& input_attrs = backend.GetInputAttrs(); +const auto& output_attrs = backend.GetOutputAttrs(); +``` +- 对输入图像进行推理 +```cpp +backend.Run() +``` +自定义函数说明 +- pp-humanseg输入预处理 +```cpp +cv::Mat preprocess(const cv::Mat& image, const std::vector& input_dims) +``` +- 作用:对输入图像进行预处理操作,包括 ​​尺寸调整​​、​​颜色空间转换​​ 和 ​​量化处理​​,使其符合 RKNN 模型的输入要求。 +- 参数说明: + - image:输入图像(BGR 格式的 cv::Mat 对象)。 + - input_dims:模型输入张量的维度定义(需满足 [1, H, W, 3] 的 NHWC 格式)。 +- 返回值: + - 返回预处理后的量化张量(cv::Mat,数据类型为 CV_8S)。 + - 若输入维度不合法,返回空矩阵(cv::Mat())并报错。 +- pp-humanseg输入后处理 +```cpp +cv::Mat postprocess(const rknn_tensor_mem* output_mem, + const std::vector& output_dims, + const cv::Size& target_size) +``` +- 作用:将模型输出的原始张量转换为高精度分割掩膜,包含 ​​概率解码​​、​​动态阈值分割​​、​​形态学优化​​和​​边缘增强​​等步骤,最终生成与原始图像尺寸匹配的二值化掩膜。 +- 参数说明: + - output_mem:模型输出的内存指针,包含量化后的原始数据。 + - output_dims:模型输出的维度信息,需满足 [1, 2, H, W] 的 NCHW 格式。 + - target_size:目标输出尺寸。 +- 返回值:返回优化后的二值化掩膜。 + +### 3.3 完整代码实现 +```cpp +#include +#include +#include +#include "rknpu2_backend/rknpu2_backend.h" +#include +#include +#include + +using namespace std::chrono; +// 输入和输出量化的scale和zeropoint +const float INPUT_SCALE = 1.0f / 255.0f; +const int INPUT_ZP = -128; +const float OUTPUT_SCALE = 0.034558f; +const int OUTPUT_ZP = -128; + +// 预处理函数 +cv::Mat preprocess(const cv::Mat& image, const std::vector& input_dims) { + // 确保输入维度为NHWC [1, H, W, 3] + if (input_dims.size() != 4 || input_dims[0] != 1 || input_dims[3] != 3) { + std::cerr << "Invalid input dimensions" << std::endl; + return cv::Mat(); + } + + const size_t input_h = input_dims[1]; + const size_t input_w = input_dims[2]; + + // Resize并转换颜色空间 + cv::Mat resized, rgb; + cv::resize(image, resized, cv::Size(input_w, input_h)); + cv::cvtColor(resized, rgb, cv::COLOR_BGR2RGB); + + // 量化到INT8 + cv::Mat quantized; + rgb.convertTo(quantized, CV_8S, 255.0 * INPUT_SCALE, INPUT_ZP); + + return quantized; +} + +// 后处理函数 +cv::Mat postprocess(const rknn_tensor_mem* output_mem, + const std::vector& output_dims, + const cv::Size& target_size) { + // 验证输出维度为NCHW [1, 2, H, W] + if (output_dims.size() != 4 || output_dims[0] != 1 || output_dims[1] != 2) { + std::cerr << "Invalid output dimensions" << std::endl; + return cv::Mat(); + } + + const int8_t* data = static_cast(output_mem->virt_addr); + const int h = output_dims[2]; + const int w = output_dims[3]; + + // ================= 1. 概率图生成优化 ================= + cv::Mat prob_map(h, w, CV_32FC1); + // 基于192x192模型的缩放补偿 + float spatial_weight = 1.0f - (h * w) / (192.0f * 192.0f); + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const int bg_idx = 0 * h * w + y * w + x; + const int fg_idx = 1 * h * w + y * w + x; + // 带饱和保护的反量化 + float bg_logit = std::clamp((data[bg_idx] - OUTPUT_ZP) * OUTPUT_SCALE, -10.0f, 10.0f); + float fg_logit = std::clamp((data[fg_idx] - OUTPUT_ZP) * OUTPUT_SCALE, -10.0f, 10.0f); + + // 空间注意力加权(中心区域增强) + float center_weight = 1.0f - (std::abs(x - w/2.0f)/(w/2.0f) + std::abs(y - h/2.0f)/(h/2.0f))/2.0f; + fg_logit *= (1.2f + 0.3f * center_weight * spatial_weight); + + // 稳健的Softmax计算 + float max_logit = std::max(bg_logit, fg_logit); + float exp_sum = expf(bg_logit - max_logit) + expf(fg_logit - max_logit); + prob_map.at(y, x) = expf(fg_logit - max_logit) / (exp_sum + 1e-8f); + } + } + + // ================= 2. 自适应阈值优化 ================= + cv::Mat binary_mask; + // 重点区域检测 + cv::Mat prob_roi = prob_map(cv::Rect(w/4, h/4, w/2, h/2)); + float center_mean = cv::mean(prob_roi)[0]; + float dynamic_thresh = std::clamp(0.45f - (center_mean - 0.5f) * 0.3f, 0.25f, 0.65f); + + cv::threshold(prob_map, binary_mask, dynamic_thresh, 255, cv::THRESH_BINARY); + binary_mask.convertTo(binary_mask, CV_8U); + + // ================= 3. 多尺度形态学处理 ================= + std::vector mask_pyramid; + // 构建金字塔 + cv::buildPyramid(binary_mask, mask_pyramid, 2); + + // 小尺度去噪 + cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3,3)); + cv::morphologyEx(mask_pyramid[1], mask_pyramid[1], cv::MORPH_OPEN, kernel1); + + // 中尺度填充 + cv::Mat kernel2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5,5)); + cv::morphologyEx(mask_pyramid[0], mask_pyramid[0], cv::MORPH_CLOSE, kernel2); + + // 金字塔重建 + cv::Mat refined_mask; + cv::pyrUp(mask_pyramid[1], refined_mask, mask_pyramid[0].size()); + cv::bitwise_and(refined_mask, mask_pyramid[0], refined_mask); + + // ================= 4. 智能边缘优化 ================= + cv::Mat edge_weights; + cv::distanceTransform(refined_mask, edge_weights, cv::DIST_L2, 3); + cv::normalize(edge_weights, edge_weights, 0, 1.0, cv::NORM_MINMAX); + + cv::Mat probabilistic_edges; + cv::Canny(refined_mask, probabilistic_edges, 50, 150); + probabilistic_edges.convertTo(probabilistic_edges, CV_32F, 1.0/255.0); + + cv::Mat final_edges; + cv::multiply(probabilistic_edges, edge_weights, final_edges); + final_edges.convertTo(final_edges, CV_8U, 255.0); + + // ================= 5. 多模态融合输出 ================= + cv::Mat resized_mask; + cv::resize(refined_mask, resized_mask, target_size, 0, 0, cv::INTER_LANCZOS4); + + cv::Mat final_mask; + cv::bilateralFilter(resized_mask, final_mask, 5, 15, 15); + + // 最终保边处理 + cv::Mat edge_mask_hr; + cv::resize(final_edges, edge_mask_hr, target_size, 0, 0, cv::INTER_NEAREST); + final_mask.setTo(255, edge_mask_hr > 128); + + return final_mask; +} + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + const std::string model_path = argv[1]; + const std::string image_path = argv[2]; + + // 初始化RKNN后端 + lockzhiner_vision_module::vision::RKNPU2Backend backend; + if (!backend.Initialize(model_path)) { + std::cerr << "Failed to initialize RKNN backend" << std::endl; + return -1; + } + + // 加载图像 + cv::Mat image = cv::imread(image_path); + if (image.empty()) { + std::cerr << "Failed to read image: " << image_path << std::endl; + return -1; + } + + // 获取输入属性 + const auto& input_attrs = backend.GetInputAttrs(); + if (input_attrs.empty()) { + std::cerr << "No input attributes found" << std::endl; + return -1; + } + const auto& input_attr = input_attrs[0]; + std::vector input_dims(input_attr.dims, input_attr.dims + input_attr.n_dims); + + // 预处理 + cv::Mat preprocessed = preprocess(image, input_dims); + if (preprocessed.empty()) { + std::cerr << "Preprocessing failed" << std::endl; + return -1; + } + + // 验证输入数据尺寸 + const size_t expected_input_size = input_attr.size_with_stride; + const size_t actual_input_size = preprocessed.total() * preprocessed.elemSize(); + if (expected_input_size != actual_input_size) { + std::cerr << "Input size mismatch! Expected: " << expected_input_size + << ", Actual: " << actual_input_size << std::endl; + return -1; + } + + // 拷贝输入数据 + const auto& input_memories = backend.GetInputMemories(); + if (input_memories.empty() || !input_memories[0]) { + std::cerr << "Invalid input memory" << std::endl; + return -1; + } + memcpy(input_memories[0]->virt_addr, preprocessed.data, actual_input_size); + + // 执行推理 + high_resolution_clock::time_point start_time = + high_resolution_clock::now(); + if (!backend.Run()) { + std::cerr << "Inference failed" << std::endl; + return -1; + } + + // 获取输出 + const auto& output_attrs = backend.GetOutputAttrs(); + if (output_attrs.empty()) { + std::cerr << "No output attributes found" << std::endl; + return -1; + } + const auto& output_memories = backend.GetOutputMemories(); + if (output_memories.empty() || !output_memories[0]) { + std::cerr << "Invalid output memory" << std::endl; + return -1; + } + + // 后处理 + const auto& output_attr = output_attrs[0]; + std::vector output_dims(output_attr.dims, output_attr.dims + output_attr.n_dims); + cv::Mat mask = postprocess(output_memories[0], output_dims, image.size()); + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + std::cout << "单张图片推理时间(ms): " << time_span.count() << std::endl; + // 生成结果 + cv::Mat result; + cv::bitwise_and(image, image, result, mask); + + // 保存结果 + const std::string output_path = "result.jpg"; + cv::imwrite(output_path, result); + std::cout << "Result saved to: " << output_path << std::endl; + + // 显示调试视图 + cv::imshow("Original", image); + cv::imshow("Mask", mask); + cv::imshow("Result", result); + cv::waitKey(0); + + return 0; +} +``` + +--- + +## 4. 编译调试 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时已经正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +cmake_minimum_required(VERSION 3.10) + +project(pp_humanseg) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置rknpu2 +set(RKNPU2_BACKEND_BASE_DIR "${LockzhinerVisionModule_ROOT_PATH}/include/lockzhiner_vision_module/vision/deep_learning/runtime") +if(NOT EXISTS ${RKNPU2_BACKEND_BASE_DIR}) + message(FATAL_ERROR "RKNPU2 backend base dir missing: ${RKNPU2_BACKEND_BASE_DIR}") +endif() + + +add_executable(Test-pp_humanseg pp_humanseg.cc) +target_include_directories(Test-pp_humanseg PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} ${rknpu2_INCLUDE_DIRS} ${RKNPU2_BACKEND_BASE_DIR}) +target_link_libraries(Test-pp_humanseg PRIVATE ${OPENCV_LIBRARIES} ${NCNN_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-pp_humanseg + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目。 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D08_pp_humanseg +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 执行结果 +### 5.1 运行前准备 +- 请确保你已经下载了 [凌智视觉模块人像分割模型](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.6/pp-humanseg.rknn) +### 5.2 运行过程 +```shell +chmod 777 Test-pp_humanseg +# 对人像进行分割 +./Test-pp_humanseg pp-humanseg.rknn image_path +``` +### 5.3 运行效果 +#### 5.3.1 人像分割结果 +- 原始图像 + +![title](./images/test.png) + +- 分割结果 + +![title](./images/result.jpg) + +#### 5.3.2 注意事项 +由于分割的模型很小,并且在模型转换过程中会有精度损失,所以在测试的时候尽量选择背景比较纯净的图像效果比较好。 + +--- + +## 6. 总结 +通过上述内容,我们成功实现了一个简单的人像分割的例子,包括: + +- 加载图像分割的rknn模型和待分割图像。 +- 图像预处理和模型推理。 +- 图像后处理并保存分割结果。 \ No newline at end of file diff --git a/Cpp_example/D08_pp_humanseg/images/result.jpg b/Cpp_example/D08_pp_humanseg/images/result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad3ed101c345e5a60c99fc2c96be208b7e081a3a Binary files /dev/null and b/Cpp_example/D08_pp_humanseg/images/result.jpg differ diff --git a/Cpp_example/D08_pp_humanseg/images/test.png b/Cpp_example/D08_pp_humanseg/images/test.png new file mode 100644 index 0000000000000000000000000000000000000000..a081a23c103bf0c8ea4b4d28fb7bd13a04507e95 Binary files /dev/null and b/Cpp_example/D08_pp_humanseg/images/test.png differ diff --git a/Cpp_example/D08_pp_humanseg/images/view.png b/Cpp_example/D08_pp_humanseg/images/view.png new file mode 100755 index 0000000000000000000000000000000000000000..ca3a5be8a1a172606292f53e4cefe665512448e1 Binary files /dev/null and b/Cpp_example/D08_pp_humanseg/images/view.png differ diff --git a/Cpp_example/D08_pp_humanseg/pp_humanseg.cc b/Cpp_example/D08_pp_humanseg/pp_humanseg.cc new file mode 100644 index 0000000000000000000000000000000000000000..a73c4749617e2933f9c7b718f0e599b02b703990 --- /dev/null +++ b/Cpp_example/D08_pp_humanseg/pp_humanseg.cc @@ -0,0 +1,232 @@ +#include +#include +#include +#include "rknpu2_backend/rknpu2_backend.h" +#include +#include +#include + +using namespace std::chrono; +// 输入和输出量化的scale和zeropoint +const float INPUT_SCALE = 1.0f / 255.0f; +const int INPUT_ZP = -128; +const float OUTPUT_SCALE = 0.034558f; +const int OUTPUT_ZP = -128; + +// 预处理函数 +cv::Mat preprocess(const cv::Mat& image, const std::vector& input_dims) { + // 确保输入维度为NHWC [1, H, W, 3] + if (input_dims.size() != 4 || input_dims[0] != 1 || input_dims[3] != 3) { + std::cerr << "Invalid input dimensions" << std::endl; + return cv::Mat(); + } + + const size_t input_h = input_dims[1]; + const size_t input_w = input_dims[2]; + + // Resize并转换颜色空间 + cv::Mat resized, rgb; + cv::resize(image, resized, cv::Size(input_w, input_h)); + cv::cvtColor(resized, rgb, cv::COLOR_BGR2RGB); + + // 量化到INT8 + cv::Mat quantized; + rgb.convertTo(quantized, CV_8S, 255.0 * INPUT_SCALE, INPUT_ZP); + + return quantized; +} + +// 后处理函数 +cv::Mat postprocess(const rknn_tensor_mem* output_mem, + const std::vector& output_dims, + const cv::Size& target_size) { + // 验证输出维度为NCHW [1, 2, H, W] + if (output_dims.size() != 4 || output_dims[0] != 1 || output_dims[1] != 2) { + std::cerr << "Invalid output dimensions" << std::endl; + return cv::Mat(); + } + + const int8_t* data = static_cast(output_mem->virt_addr); + const int h = output_dims[2]; + const int w = output_dims[3]; + + // ================= 1. 概率图生成优化 ================= + cv::Mat prob_map(h, w, CV_32FC1); + // 基于192x192模型的缩放补偿 + float spatial_weight = 1.0f - (h * w) / (192.0f * 192.0f); + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const int bg_idx = 0 * h * w + y * w + x; + const int fg_idx = 1 * h * w + y * w + x; + // 带饱和保护的反量化 + float bg_logit = std::clamp((data[bg_idx] - OUTPUT_ZP) * OUTPUT_SCALE, -10.0f, 10.0f); + float fg_logit = std::clamp((data[fg_idx] - OUTPUT_ZP) * OUTPUT_SCALE, -10.0f, 10.0f); + + // 空间注意力加权(中心区域增强) + float center_weight = 1.0f - (std::abs(x - w/2.0f)/(w/2.0f) + std::abs(y - h/2.0f)/(h/2.0f))/2.0f; + fg_logit *= (1.2f + 0.3f * center_weight * spatial_weight); + + // 稳健的Softmax计算 + float max_logit = std::max(bg_logit, fg_logit); + float exp_sum = expf(bg_logit - max_logit) + expf(fg_logit - max_logit); + prob_map.at(y, x) = expf(fg_logit - max_logit) / (exp_sum + 1e-8f); + } + } + + // ================= 2. 自适应阈值优化 ================= + cv::Mat binary_mask; + // 重点区域检测 + cv::Mat prob_roi = prob_map(cv::Rect(w/4, h/4, w/2, h/2)); + float center_mean = cv::mean(prob_roi)[0]; + float dynamic_thresh = std::clamp(0.45f - (center_mean - 0.5f) * 0.3f, 0.25f, 0.65f); + + cv::threshold(prob_map, binary_mask, dynamic_thresh, 255, cv::THRESH_BINARY); + binary_mask.convertTo(binary_mask, CV_8U); + + // ================= 3. 多尺度形态学处理 ================= + std::vector mask_pyramid; + // 构建金字塔 + cv::buildPyramid(binary_mask, mask_pyramid, 2); + + // 小尺度去噪 + cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3,3)); + cv::morphologyEx(mask_pyramid[1], mask_pyramid[1], cv::MORPH_OPEN, kernel1); + + // 中尺度填充 + cv::Mat kernel2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5,5)); + cv::morphologyEx(mask_pyramid[0], mask_pyramid[0], cv::MORPH_CLOSE, kernel2); + + // 金字塔重建 + cv::Mat refined_mask; + cv::pyrUp(mask_pyramid[1], refined_mask, mask_pyramid[0].size()); + cv::bitwise_and(refined_mask, mask_pyramid[0], refined_mask); + + // ================= 4. 智能边缘优化 ================= + cv::Mat edge_weights; + cv::distanceTransform(refined_mask, edge_weights, cv::DIST_L2, 3); + cv::normalize(edge_weights, edge_weights, 0, 1.0, cv::NORM_MINMAX); + + cv::Mat probabilistic_edges; + cv::Canny(refined_mask, probabilistic_edges, 50, 150); + probabilistic_edges.convertTo(probabilistic_edges, CV_32F, 1.0/255.0); + + cv::Mat final_edges; + cv::multiply(probabilistic_edges, edge_weights, final_edges); + final_edges.convertTo(final_edges, CV_8U, 255.0); + + // ================= 5. 多模态融合输出 ================= + cv::Mat resized_mask; + cv::resize(refined_mask, resized_mask, target_size, 0, 0, cv::INTER_LANCZOS4); + + cv::Mat final_mask; + cv::bilateralFilter(resized_mask, final_mask, 5, 15, 15); + + // 最终保边处理 + cv::Mat edge_mask_hr; + cv::resize(final_edges, edge_mask_hr, target_size, 0, 0, cv::INTER_NEAREST); + final_mask.setTo(255, edge_mask_hr > 128); + + return final_mask; +} + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + const std::string model_path = argv[1]; + const std::string image_path = argv[2]; + + // 初始化RKNN后端 + lockzhiner_vision_module::vision::RKNPU2Backend backend; + if (!backend.Initialize(model_path)) { + std::cerr << "Failed to initialize RKNN backend" << std::endl; + return -1; + } + + // 加载图像 + cv::Mat image = cv::imread(image_path); + if (image.empty()) { + std::cerr << "Failed to read image: " << image_path << std::endl; + return -1; + } + + // 获取输入属性 + const auto& input_attrs = backend.GetInputAttrs(); + if (input_attrs.empty()) { + std::cerr << "No input attributes found" << std::endl; + return -1; + } + const auto& input_attr = input_attrs[0]; + std::vector input_dims(input_attr.dims, input_attr.dims + input_attr.n_dims); + + // 预处理 + cv::Mat preprocessed = preprocess(image, input_dims); + if (preprocessed.empty()) { + std::cerr << "Preprocessing failed" << std::endl; + return -1; + } + + // 验证输入数据尺寸 + const size_t expected_input_size = input_attr.size_with_stride; + const size_t actual_input_size = preprocessed.total() * preprocessed.elemSize(); + if (expected_input_size != actual_input_size) { + std::cerr << "Input size mismatch! Expected: " << expected_input_size + << ", Actual: " << actual_input_size << std::endl; + return -1; + } + + // 拷贝输入数据 + const auto& input_memories = backend.GetInputMemories(); + if (input_memories.empty() || !input_memories[0]) { + std::cerr << "Invalid input memory" << std::endl; + return -1; + } + memcpy(input_memories[0]->virt_addr, preprocessed.data, actual_input_size); + + // 执行推理 + high_resolution_clock::time_point start_time = + high_resolution_clock::now(); + if (!backend.Run()) { + std::cerr << "Inference failed" << std::endl; + return -1; + } + + // 获取输出 + const auto& output_attrs = backend.GetOutputAttrs(); + if (output_attrs.empty()) { + std::cerr << "No output attributes found" << std::endl; + return -1; + } + const auto& output_memories = backend.GetOutputMemories(); + if (output_memories.empty() || !output_memories[0]) { + std::cerr << "Invalid output memory" << std::endl; + return -1; + } + + // 后处理 + const auto& output_attr = output_attrs[0]; + std::vector output_dims(output_attr.dims, output_attr.dims + output_attr.n_dims); + cv::Mat mask = postprocess(output_memories[0], output_dims, image.size()); + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + std::cout << "单张图片推理时间(ms): " << time_span.count() << std::endl; + // 生成结果 + cv::Mat result; + cv::bitwise_and(image, image, result, mask); + + // 保存结果 + const std::string output_path = "result.jpg"; + cv::imwrite(output_path, result); + std::cout << "Result saved to: " << output_path << std::endl; + + // 显示调试视图 + cv::imshow("Original", image); + cv::imshow("Mask", mask); + cv::imshow("Result", result); + cv::waitKey(0); + + return 0; +} diff --git a/Cpp_example/D09_plate_recognize/CMakeLists.txt b/Cpp_example/D09_plate_recognize/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..eb15d073c1318da74bdd2efada7e17b7194f3892 --- /dev/null +++ b/Cpp_example/D09_plate_recognize/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.10) + +project(plate_recognize) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# ncnn配置 +set(NCNN_ROOT_DIR "${PROJECT_ROOT_PATH}/third_party/ncnn-20240820-lockzhiner-vision-module") # 确保third_party层级存在 +message(STATUS "Checking ncnn headers in: ${NCNN_ROOT_DIR}/include/ncnn") + +# 验证头文件存在 +if(NOT EXISTS "${NCNN_ROOT_DIR}/include/ncnn/net.h") + message(FATAL_ERROR "ncnn headers not found. Confirm the directory contains ncnn: ${NCNN_ROOT_DIR}") +endif() + +set(NCNN_INCLUDE_DIRS "${NCNN_ROOT_DIR}/include") +set(NCNN_LIBRARIES "${NCNN_ROOT_DIR}/lib/libncnn.a") + +add_executable(Test-plate_recognize plate_recognize.cc) +target_include_directories(Test-plate_recognize PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} ${NCNN_INCLUDE_DIRS}) +target_link_libraries(Test-plate_recognize PRIVATE ${OPENCV_LIBRARIES} ${NCNN_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-plate_recognize + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/D09_plate_recognize/README.md b/Cpp_example/D09_plate_recognize/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5c8e4c127b25824f7b1959128b063b54cabc2eb3 --- /dev/null +++ b/Cpp_example/D09_plate_recognize/README.md @@ -0,0 +1,582 @@ +# 车牌识别 +本章节在 Lockzhiner Vision Module 上基于 PaddleDet 目标检测类和rcnn文本识别模型,实现了一个车牌识别案例。 +## 1. 基本知识简介 +### 1.1 车牌识别简介 +车牌识别是一种基于计算机视觉和深度学习的技术,通过图像处理、字符分割和光学字符识别算法,自动提取车辆牌照中的文字与数字信息。该技术可实时识别不同光照、角度和复杂背景下的车牌,广泛应用于智能交通管理、停车场收费系统、电子警察执法等场景。 +### 1.2 车牌识别流程 +- ​​车牌检测: + - ​深度学习​​:YOLO、Faster R-CNN等检测模型定位车牌区域。 + - ​​传统方法:颜色空间分析(蓝色/黄色车牌)+边缘检测。 +- ​​车牌矫正:透视变换调整倾斜角度,Hough直线检测旋转角度。 +- ​字符分割:​​垂直投影法分割字符,连通域分析处理粘连字符。 +- 字符识别: + - ​CRNN​​:CNN+RNN+CTC结构,端到端识别。 + - OCR模型​​:Tesseract、PaddleOCR等开源工具。 + +--- + +## 2. C++ API文档 +### 2.1 PaddleDetection 类 +#### 2.1.1 头文件 +```cpp +#include +``` +#### 2.1.2 构造函数 +```cpp +lockzhiner_vision_module::vision::PaddleDetection(); +``` +- 作用: + - 创建一个 PaddleDetection 对象,并初始化相关成员变量。 +- 参数: + - 无 +- 返回值: + - 无 +#### 2.1.3 Initialize函数 +```cpp +bool Initialize(const std::string& model_path); +``` +- 作用: + - 加载预训练的 PaddleDetection 模型。 +- 参数: + - model_path:模型路径,包含模型文件和参数文件。 +- 返回值: + - true:模型加载成功。 + - false:模型加载失败。 +#### 2.1.4 SetThreshold函数 +```cpp +void SetThreshold(float score_threshold = 0.5, float nms_threshold = 0.3); +``` +- 作用: + - 设置目标检测的置信度阈值和NMS阈值。 +- 参数: + - score_threshold:置信度阈值,默认值为0.5。 + - nms_threshold:NMS阈值,默认值为0.3。 +- 返回值: + - 无 +#### 2.1.5 Predict函数 +```cpp +std::vector Predict(const cv::Mat& image); +``` +- 作用: + - 使用加载的模型对输入图像进行目标检测,返回检测结果。 +- 参数: + - input_mat (const cv::Mat&): 输入的图像数据,通常是一个 cv::Mat 变量。 +- 返回值: + - 返回一个包含多个 DetectionResult 对象的向量,每个对象表示一个检测结果。 + +### 2.2 Net类 +#### 2.2.1 头文件 +```cpp +#include +``` +- 作用:用于声明Net类,使得Net类可以在当前文件中使用。 + +#### 2.2.2 构造类函数 +```cpp +ncnn::Net ocr_net; +``` +- 作用:创建一个Net类型的对象实例,用于加载模型和参数等。 +- 参数说明:无 +- 返回值:无 + +#### 2.3.3 load_param函数 +```cpp +int load_param(const DataReader& dr); +``` +- 参数说明: + - dr:传入的参数文件路径。 +- 返回值: + - 返回值为0表示加载参数文件成功。 + +#### 2.2.4 load_model函数 +```cpp +int load_model(const DataReader& dr); +``` +- 参数说明: + - dr:传入的模型文件路径。 +- 返回值:返回值为0表示加载模型成功。 + +#### 2.2.5 from_pixels函数 +```cpp +ncnn::Mat::from_pixels(plate_img.data, ncnn::Mat::PIXEL_BGR, plate_img.cols, + plate_img.rows); +``` +- ​​作用​​:将原始图像像素数据转换为 ncnn::Mat 对象,​​同时进行缩放操作​​,适配神经网络的输入尺寸要求。 +- 参数说明: + - plate_img.data:输入图像的像素数据指针。 + - ncnn::Mat::PIXEL_BGR:输入像素的颜色格式。 + - plate_img.cols, plate_img.rows:原始图像的宽度和高度。 +- 返回值​​:返回一个 ncnn::Mat 对象,包含缩放后的图像数据,格式为 ​​CHW 排列。 + +### 2.3 Extractor类 +#### 2.3.1 头文件 +```cpp +#include +``` +- 作用:用于声明Extractor类,使得Extractor类可以在当前文件中使用。 + +#### 2.3.2 构造类函数 +```cpp +ncnn::Extractor ex = ocr_net.create_extractor(); +``` +- 作用:从已经加载了神经网络模型的 net 中创建一个 Extractor 实例,用于执行车牌识别的推理任务。 +- 参数说明:无 +- 返回值:无 + +--- + +## 3. 车牌识别代码解析 +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化车牌检测模型 +```cpp +lockzhiner_vision_module::vision::PaddleDet detector; +if (!detector.Initialize(argv[1])) { + cerr << "Failed to load detection model: " << argv[1] << endl; + return 1; +} +``` +- 车牌检测模型推理 +```cpp +auto results = detector.Predict(image); +``` +- 可视化并显示推理结果 +```cpp +cv::Mat output_image = image.clone(); +for (const auto& det : results) { + cv::Rect rect( + static_cast(det.box.x), + static_cast(det.box.y), + static_cast(det.box.width), + static_cast(det.box.height) + ); + cv::rectangle( + output_image, + rect, + cv::Scalar(0, 255, 0), + 1, + cv::LINE_AA + ); +} +cv::imshow("Detection Result", output_image); +``` +- 加载字符识别参数和模型 +```cpp +ocr_net.load_param(param_path.c_str()) +ocr_net.load_model(model_path.c_str()) +``` +- 归一化处理 +```cpp +const float mean[3] = {127.5f, 127.5f, 127.5f}; +const float norm[3] = {0.0078125f, 0.0078125f, 0.0078125f}; +in.substract_mean_normalize(mean, norm); +``` +- 解码预测结果 +```cpp +string license; +vector preb; +for (int c = 0; c < feat.c; c++) { + const float* data = feat.channel(c); + for (int w = 0; w < feat.w; w++) { + float max_val = -FLT_MAX; + int max_idx = 0; + for (int h = 0; h < feat.h; h++) { + float val = data[w + h * feat.w]; + if (val > max_val) { + max_val = val; + max_idx = h; + } + } + preb.push_back(max_idx); + } +} + +// 后处理去重 +vector valid_labels; +int prev = -1; +for (size_t i = 0; i < preb.size(); ++i) { + if (preb[i] != 67 && preb[i] != prev) { + valid_labels.push_back(preb[i]); + } + prev = preb[i]; +} + +for (int idx : valid_labels) { + license += plate_chars[idx]; +} +``` +自定义函数说明 +- 车牌字符识别 +```cpp +string RecognizePlate(cv::Mat plate_img); +``` +- 作用:对分割出来的车牌进行识别。 +- 参数说明: + - plate_image:待识别的车牌图像。 +- 返回值:返回一串字符串类型的数据,表示识别到的车牌是什么。 + +### 3.3 完整代码实现 +```cpp +#include +#include +#include +#include +#include +#include +#include +#include "myfontface.h" + +using namespace std; +using namespace std::chrono; + +// OCR字符集配置 +const string plate_chars[68] = { + "京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", + "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂", "琼", "川", "贵", + "云", "藏", "陕", "甘", "青", "宁", "新", "0", "1", "2", "3", "4", + "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", + "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", "I", "O", "-"}; + +ncnn::Net ocr_net; + +bool InitOCRModel(const string& param_path, const string& model_path) { + return ocr_net.load_param(param_path.c_str()) == 0 && + ocr_net.load_model(model_path.c_str()) == 0; +} + +string RecognizePlate(cv::Mat plate_img) { + cv::resize(plate_img, plate_img, cv::Size(94, 24)); + + ncnn::Mat in = ncnn::Mat::from_pixels(plate_img.data, + ncnn::Mat::PIXEL_BGR, + plate_img.cols, + plate_img.rows); + + const float mean[3] = {127.5f, 127.5f, 127.5f}; + const float norm[3] = {0.0078125f, 0.0078125f, 0.0078125f}; + in.substract_mean_normalize(mean, norm); + + ncnn::Extractor ex = ocr_net.create_extractor(); + ex.input("input.1", in); + + ncnn::Mat feat; + ex.extract("131", feat); + + string license; + vector preb; + for (int c = 0; c < feat.c; c++) { + const float* data = feat.channel(c); + for (int w = 0; w < feat.w; w++) { + float max_val = -FLT_MAX; + int max_idx = 0; + for (int h = 0; h < feat.h; h++) { + float val = data[w + h * feat.w]; + if (val > max_val) { + max_val = val; + max_idx = h; + } + } + preb.push_back(max_idx); + } + } + + // 后处理去重 + vector valid_labels; + int prev = -1; + for (size_t i = 0; i < preb.size(); ++i) { + if (preb[i] != 67 && preb[i] != prev) { + valid_labels.push_back(preb[i]); + } + prev = preb[i]; + } + + for (int idx : valid_labels) { + license += plate_chars[idx]; + } + + return license.empty() ? "UNKNOWN" : license; +} + +int main(int argc, char** argv) { + // 参数验证 + if (argc < 4 || argc > 5) { + cerr << "Usage: " << argv[0] + << " [image_path]\n" + << "Example:\n" + << " Realtime: " << argv[0] << " det_model ocr.param ocr.bin\n" + << " Image: " << argv[0] << " det_model ocr.param ocr.bin test.jpg\n"; + return 1; + } + + // 初始化检测模型 + lockzhiner_vision_module::vision::PaddleDet detector; + if (!detector.Initialize(argv[1])) { + cerr << "Failed to load detection model: " << argv[1] << endl; + return 1; + } + + // 初始化OCR模型 + if (!InitOCRModel(argv[2], argv[3])) { + cerr << "Failed to load OCR model: " << argv[2] << " and " << argv[3] << endl; + return 1; + } + MyFontFace myfont; + // 设置文字参数 + double font_scale = 0.6; + int thickness = 1; + + // 图片处理模式 + if (argc == 5) { + cv::Mat image = cv::imread(argv[4]); + if (image.empty()) { + cerr << "Failed to read image: " << argv[4] << endl; + return 1; + } + + auto results = detector.Predict(image); + // 可视化并显示结果 + cv::Mat output_image = image.clone(); + for (const auto& det : results) { + cv::Rect rect( + static_cast(det.box.x), + static_cast(det.box.y), + static_cast(det.box.width), + static_cast(det.box.height) + ); + cv::rectangle( + output_image, + rect, + cv::Scalar(0, 255, 0), + 1, + cv::LINE_AA + ); + } + cout << "\n===== 检测到 " << results.size() << " 个车牌 =====" << endl; + + for (size_t i = 0; i < results.size(); ++i) { + const auto& det = results[i]; + cv::Rect roi(det.box.x, det.box.y, det.box.width, det.box.height); + roi &= cv::Rect(0, 0, image.cols, image.rows); + + if (roi.area() > 0) { + cv::Mat plate_img = image(roi); + cv::imshow("DetectionSeg Result", plate_img); + string plate_num = RecognizePlate(plate_img); + // 左上角偏移 + cv::Point text_org(roi.x + 2, roi.y - 2); + // 先绘制黑色背景提升可读性 + cv::putText(output_image, plate_num, text_org, + cv::Scalar(0, 0, 0), // 颜色 + myfont, // 字体对象 + font_scale, // 字体尺寸 + thickness + 2, // 线宽 + cv::PutTextFlags::PUT_TEXT_ALIGN_LEFT, // 对齐方式 + cv::Range(0, 0)); // 自动换行范围(0表示不换行) + cv::putText(output_image, plate_num, text_org, cv::Scalar(127, 0, 127), myfont, 10); + + cout << "车牌 " << i+1 << ":\n" + << " 位置: [" << roi.x << ", " << roi.y + << ", " << roi.width << "x" << roi.height << "]\n" + << " 置信度: " << det.score << "\n" + << " 识别结果: " << plate_num << "\n" << endl; + + cv::imshow("Detection Result", output_image); + } + } + cv::waitKey(0); + } + // 实时摄像头模式 + else { + // 初始化设备连接 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + if (!cap.isOpened()) { + cerr << "Error: Could not open camera" << endl; + return 1; + } + + cout << "实时识别模式启动 (按ESC退出)..." << endl; + + cv::Mat frame; + while (true) { + cap >> frame; + if (frame.empty()) continue; + + auto results = detector.Predict(frame); + + cv::Mat display_frame = frame.clone(); + for (const auto& det : results) { + cv::Rect rect( + static_cast(det.box.x), + static_cast(det.box.y), + static_cast(det.box.width), + static_cast(det.box.height) + ); + cv::rectangle( + display_frame, + rect, + cv::Scalar(0, 255, 0), + 1, + cv::LINE_AA + ); + } + + // 添加时间戳 + auto now = system_clock::now(); + time_t now_time = system_clock::to_time_t(now); + cout << "\n===== " << ctime(&now_time) + << "检测到 " << results.size() << " 个车牌 =====" << endl; + + for (const auto& det : results) { + cv::Rect roi(det.box.x, det.box.y, det.box.width, det.box.height); + roi &= cv::Rect(0, 0, frame.cols, frame.rows); + + if (roi.area() > 0) { + cv::Mat plate_img = frame(roi); + string plate_num = RecognizePlate(plate_img); + // 左上角偏移 + cv::Point text_org(roi.x + 2, roi.y - 2); + // 先绘制黑色背景提升可读性 + cv::putText(display_frame, plate_num, text_org, + cv::Scalar(0, 0, 0), // 颜色 + myfont, // 字体对象 + font_scale, // 字体尺寸 + thickness + 2, // 线宽 + cv::PutTextFlags::PUT_TEXT_ALIGN_LEFT, // 对齐方式 + cv::Range(0, 0)); // 自动换行范围(0表示不换行) + cv::putText(display_frame, plate_num, text_org, cv::Scalar(127, 0, 127), myfont, 10); + + cout << "[实时结果] 位置(" << roi.x << "," << roi.y + << ") 识别: " << plate_num + << " (置信度: " << det.score << ")" << endl; + } + } + edit.Print(display_frame); + // 退出检测 + if (cv::waitKey(1) == 27) break; + } + } + return 0; +} +``` + +--- + +## 4. 编译调试 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时已经正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +cmake_minimum_required(VERSION 3.10) + +project(plate_recognize) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# ncnn配置 +set(NCNN_ROOT_DIR "${PROJECT_ROOT_PATH}/third_party/ncnn-20240820-lockzhiner-vision-module") # 确保third_party层级存在 +message(STATUS "Checking ncnn headers in: ${NCNN_ROOT_DIR}/include/ncnn") + +# 验证头文件存在 +if(NOT EXISTS "${NCNN_ROOT_DIR}/include/ncnn/net.h") + message(FATAL_ERROR "ncnn headers not found. Confirm the directory contains ncnn: ${NCNN_ROOT_DIR}") +endif() + +set(NCNN_INCLUDE_DIRS "${NCNN_ROOT_DIR}/include") +set(NCNN_LIBRARIES "${NCNN_ROOT_DIR}/lib/libncnn.a") + +add_executable(Test-plate_recognize plate_recognize.cc) +target_include_directories(Test-plate_recognize PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} ${NCNN_INCLUDE_DIRS}) +target_link_libraries(Test-plate_recognize PRIVATE ${OPENCV_LIBRARIES} ${NCNN_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-plate_recognize + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D09_plate_recognize +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 执行结果 +### 5.1 运行前准备 +- 请确保你已经下载了 [凌智视觉模块车牌识别参数文件](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.6/lpr2d.param) +- 请确保你已经下载了 [凌智视觉模块车牌识别bin文件](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.6/lpr2d.bin) +- 请确保你已经下载了 [凌智视觉模块车牌检测模型](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.6/Plate_recognize.rknn) +### 5.2 运行过程 +```shell +chmode 777 Test-plate_recognize +# 对车牌图片进行识别 +./Test-plate_recognize Plate_recognize.rknn lpr2d.param lpr2d.bin image_path +# 摄像头实时识别 +./Test-plate_recognize Plate_recognize.rknn lpr2d.param lpr2d.bin +``` +### 5.3 运行结果 +- 原始图像 + +![](./images/D09_2.jpg) + +- 分割出来的车牌图像 + +![](./images/D09_3.png) + +- 识别结果 + +![](./images/D09_4.png) + +--- + +## 6. 总结 +通过上述内容,我们成功实现了一个车牌识别系统,包括: + +- 加载车牌检测模型和rcnn字符识别模型 +- 进行车牌检测并将车牌分割出来。 +- 将分割出来的车牌进行字符识别。 +- 保存识别结果。 \ No newline at end of file diff --git a/Cpp_example/D09_plate_recognize/images/D09_1.png b/Cpp_example/D09_plate_recognize/images/D09_1.png new file mode 100755 index 0000000000000000000000000000000000000000..4994498259dd36b8e87b94fca1de218221207c64 Binary files /dev/null and b/Cpp_example/D09_plate_recognize/images/D09_1.png differ diff --git a/Cpp_example/D09_plate_recognize/images/D09_2.jpg b/Cpp_example/D09_plate_recognize/images/D09_2.jpg new file mode 100755 index 0000000000000000000000000000000000000000..75ce9fb5adc1c8f9923c342728dc28a2c02684b0 Binary files /dev/null and b/Cpp_example/D09_plate_recognize/images/D09_2.jpg differ diff --git a/Cpp_example/D09_plate_recognize/images/D09_3.png b/Cpp_example/D09_plate_recognize/images/D09_3.png new file mode 100755 index 0000000000000000000000000000000000000000..1c4007b402da69573a1c15abbc1db966a6d08285 Binary files /dev/null and b/Cpp_example/D09_plate_recognize/images/D09_3.png differ diff --git a/Cpp_example/D09_plate_recognize/images/D09_4.png b/Cpp_example/D09_plate_recognize/images/D09_4.png new file mode 100755 index 0000000000000000000000000000000000000000..58bc46adcbf9c7975380a7916e746ce27914ba6d Binary files /dev/null and b/Cpp_example/D09_plate_recognize/images/D09_4.png differ diff --git a/Cpp_example/D09_plate_recognize/myfontface.h b/Cpp_example/D09_plate_recognize/myfontface.h new file mode 100755 index 0000000000000000000000000000000000000000..a6de8da1ccde4f8f564fcaa73e08a15b2d41c33f --- /dev/null +++ b/Cpp_example/D09_plate_recognize/myfontface.h @@ -0,0 +1,1323 @@ +#ifndef MYFONTFACE_H +#define MYFONTFACE_H + +#include +#include + +class MyFontFace : public cv::FontFace +{ +public: +MyFontFace() +{ +static const unsigned int font_glyph_unicode[31] = { + 20113, 20140, 20864, 21513, 23425, 24029, 26032, 26187, 26690, 27818, 27941, 27993, 28189, 28248, 29756, 29976, 30358, 31908, 33487, 33945, 34255, 35947, 36149, 36195, 36797, 37122, 38397, 38485, 38738, 40065, 40657 +}; +static const unsigned char font_glyph_bitmaps[31][40 * 20] = { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 169, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 154, 3, 0, 0, + 0, 0, 64, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, + 0, 0, 64, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, + 0, 0, 0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 196, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 125, 0, + 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 210, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 249, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 254, 223, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 192, 255, 111, 0, 0, 0, 148, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 249, 255, 10, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 254, 223, 2, 0, 0, 160, 255, 175, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 210, 255, 94, 0, 0, 0, 0, 251, 255, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 251, 255, 8, 0, 0, 0, 0, 210, 255, 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 191, 0, 0, 0, 0, 0, 64, 254, 239, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 255, 45, 0, 0, 0, 0, 0, 0, 230, 255, 78, 0, 0, 0, 0, + 0, 0, 0, 80, 254, 239, 4, 0, 0, 0, 0, 0, 0, 128, 255, 223, 2, 0, 0, 0, + 0, 0, 0, 231, 255, 111, 0, 0, 0, 0, 0, 0, 0, 64, 252, 255, 11, 0, 0, 0, + 0, 0, 193, 255, 255, 222, 221, 254, 255, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, + 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 221, 221, 172, 170, 154, 249, 255, 78, 0, 0, + 0, 0, 0, 220, 122, 85, 3, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 207, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 253, 77, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 112, 238, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 230, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 253, 223, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 191, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 148, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 73, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 112, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 7, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 171, 170, 170, 170, 170, 170, 170, 170, 170, 186, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 8, 0, 0, 0, 248, 223, 0, 0, 0, 128, 255, 11, 0, 0, 0, + 0, 0, 0, 144, 205, 6, 0, 0, 0, 248, 223, 0, 0, 0, 64, 169, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 212, 10, 0, 0, 248, 223, 0, 0, 182, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 175, 0, 0, 248, 223, 0, 64, 254, 175, 1, 0, 0, 0, 0, + 0, 0, 0, 16, 251, 255, 159, 0, 0, 248, 223, 0, 128, 255, 255, 126, 0, 0, 0, 0, + 0, 0, 0, 230, 255, 239, 6, 0, 0, 248, 223, 0, 0, 196, 255, 255, 59, 0, 0, 0, + 0, 0, 161, 255, 255, 43, 0, 0, 0, 248, 223, 0, 0, 0, 231, 255, 255, 9, 0, 0, + 0, 112, 254, 255, 126, 0, 0, 0, 0, 248, 223, 0, 0, 0, 48, 252, 255, 207, 4, 0, + 0, 229, 255, 207, 3, 0, 0, 0, 0, 250, 223, 0, 0, 0, 0, 128, 255, 239, 6, 0, + 0, 160, 239, 7, 0, 0, 215, 189, 202, 255, 207, 0, 0, 0, 0, 0, 196, 159, 0, 0, + 0, 32, 57, 0, 0, 0, 247, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 245, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 85, 85, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 80, 254, 11, 0, 0, 218, 59, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 255, 11, 0, 0, 253, 78, 0, 0, 80, 218, 191, 0, 0, + 0, 144, 255, 255, 255, 255, 255, 255, 11, 0, 0, 253, 78, 148, 236, 255, 255, 239, 4, 0, + 0, 144, 255, 255, 255, 255, 255, 255, 11, 0, 0, 253, 255, 255, 255, 255, 190, 72, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 255, 11, 0, 0, 253, 255, 239, 139, 4, 0, 217, 40, 0, + 0, 0, 0, 0, 0, 0, 96, 255, 11, 0, 0, 253, 110, 0, 0, 0, 0, 252, 111, 0, + 0, 0, 0, 100, 185, 237, 255, 255, 11, 0, 0, 253, 94, 0, 0, 0, 80, 254, 61, 0, + 0, 230, 255, 255, 255, 255, 255, 255, 11, 0, 0, 251, 239, 170, 170, 170, 235, 255, 11, 0, + 0, 228, 255, 255, 205, 105, 82, 255, 11, 0, 0, 228, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 128, 88, 0, 0, 0, 80, 254, 11, 0, 0, 16, 167, 220, 221, 221, 172, 6, 0, 0, + 0, 0, 0, 166, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 106, 0, 0, 0, + 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, + 0, 0, 0, 249, 175, 170, 170, 170, 170, 251, 191, 170, 170, 170, 170, 250, 159, 0, 0, 0, + 0, 0, 0, 249, 95, 0, 0, 0, 0, 248, 143, 0, 0, 0, 0, 245, 159, 0, 0, 0, + 0, 0, 0, 249, 127, 85, 85, 85, 85, 249, 159, 85, 85, 85, 85, 247, 159, 0, 0, 0, + 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, + 0, 0, 0, 249, 175, 170, 170, 170, 170, 251, 191, 170, 170, 170, 170, 250, 159, 0, 0, 0, + 0, 0, 0, 249, 95, 0, 0, 0, 0, 248, 143, 0, 0, 0, 0, 245, 159, 0, 0, 0, + 0, 0, 0, 249, 127, 85, 85, 85, 85, 249, 159, 85, 85, 85, 85, 247, 159, 0, 0, 0, + 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, + 0, 0, 0, 249, 223, 221, 253, 223, 221, 221, 221, 221, 253, 239, 221, 253, 159, 0, 0, 0, + 0, 0, 0, 83, 21, 0, 248, 95, 0, 0, 0, 0, 245, 159, 0, 81, 53, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 248, 95, 0, 0, 0, 0, 245, 159, 0, 0, 0, 0, 0, 0, + 0, 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, + 0, 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, + 0, 0, 0, 0, 0, 0, 248, 95, 0, 0, 0, 0, 245, 159, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 248, 95, 0, 0, 0, 0, 245, 159, 0, 0, 0, 0, 0, 0, + 0, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 82, 85, 85, 85, 85, 85, 134, 85, 85, 85, 85, 119, 85, 85, 85, 85, 85, 37, 0, + 0, 0, 0, 0, 0, 64, 234, 255, 10, 0, 0, 96, 254, 223, 90, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 200, 255, 255, 255, 60, 0, 0, 128, 253, 255, 255, 239, 107, 0, 0, 0, + 0, 130, 219, 255, 255, 255, 223, 24, 0, 0, 0, 0, 0, 165, 254, 255, 255, 223, 41, 0, + 0, 211, 255, 255, 239, 90, 0, 0, 0, 0, 0, 0, 0, 0, 16, 199, 255, 255, 62, 0, + 0, 128, 239, 107, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 218, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 179, 221, 221, 221, 221, 221, 221, 221, 254, 239, 221, 221, 221, 221, 221, 221, 221, 76, 0, + 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 112, 221, 221, 221, 221, 221, 221, 254, 239, 221, 221, 221, 221, 221, 221, 9, 0, 0, + 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, + 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 160, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 10, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 90, 85, 85, 85, 85, 85, 85, 85, 85, 165, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 90, 85, 85, 85, 85, 85, 85, 85, 85, 165, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 13, 0, 0, 0, + 0, 0, 0, 160, 221, 7, 0, 0, 0, 0, 0, 0, 0, 0, 112, 221, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 80, 252, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 239, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 219, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 189, 3, 0, + 0, 64, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 64, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 64, 254, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 64, 254, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 64, 254, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 64, 254, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 48, 219, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 189, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 147, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 106, 0, + 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 147, 170, 170, 170, 170, 170, 170, 170, 234, 255, 172, 170, 170, 170, 170, 170, 170, 106, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 2, 0, 32, 251, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 252, 255, 255, 255, 255, 239, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 251, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 249, 255, 255, 255, 239, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 167, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 205, 4, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 112, 254, 12, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 251, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 253, 159, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 253, 143, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 0, 253, 143, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 64, 254, 127, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 64, 254, 95, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 80, 255, 78, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 128, 255, 61, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 160, 255, 12, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 192, 255, 11, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 228, 255, 8, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 0, 249, 239, 5, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 48, 254, 207, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 160, 255, 143, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 0, 246, 255, 45, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 64, 254, 255, 6, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 210, 255, 175, 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 249, 255, 45, 0, 0, 0, 0, 0, 32, 85, 4, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 176, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 5, 0, 0, + 0, 32, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 154, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 80, 236, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 134, 0, 0, + 0, 0, 0, 0, 0, 252, 143, 0, 0, 0, 0, 0, 32, 85, 168, 220, 255, 239, 5, 0, + 0, 0, 0, 0, 0, 247, 207, 2, 0, 0, 64, 254, 255, 255, 255, 255, 255, 255, 9, 0, + 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 69, 254, 255, 255, 255, 255, 223, 172, 7, 0, + 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 69, 254, 174, 122, 69, 0, 0, 0, 0, 0, + 0, 96, 170, 170, 170, 170, 170, 170, 170, 154, 67, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 237, 4, 0, 0, 96, 206, 4, 64, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 160, 255, 11, 0, 0, 210, 255, 45, 64, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 253, 175, 0, 0, 249, 239, 4, 64, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 230, 239, 5, 80, 254, 111, 0, 64, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 159, 0, 210, 255, 10, 0, 64, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 110, 254, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 127, 254, 174, 170, 170, 170, 170, 170, 122, 0, + 0, 167, 170, 170, 170, 170, 170, 170, 170, 170, 89, 254, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 0, 0, 215, 108, 0, 0, 0, 64, 254, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 0, 0, 249, 143, 0, 0, 0, 64, 254, 13, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 249, 143, 0, 0, 0, 64, 254, 13, 0, 80, 255, 11, 0, 0, 0, + 0, 64, 85, 85, 85, 250, 159, 85, 85, 85, 65, 254, 13, 0, 80, 255, 11, 0, 0, 0, + 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 69, 254, 13, 0, 80, 255, 11, 0, 0, 0, + 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 69, 254, 13, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 249, 143, 0, 0, 0, 80, 255, 11, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 32, 5, 0, 249, 143, 0, 1, 0, 112, 255, 11, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 128, 239, 7, 249, 143, 212, 78, 0, 144, 255, 9, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 210, 255, 8, 249, 143, 251, 191, 0, 176, 255, 7, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 248, 223, 2, 249, 143, 228, 255, 8, 210, 239, 5, 0, 80, 255, 11, 0, 0, 0, + 0, 32, 253, 143, 0, 249, 143, 128, 255, 94, 246, 223, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 128, 255, 45, 0, 249, 143, 0, 252, 207, 250, 175, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 211, 255, 8, 0, 249, 143, 0, 229, 92, 254, 111, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 249, 207, 0, 0, 249, 143, 0, 48, 144, 255, 12, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 112, 109, 0, 0, 251, 143, 0, 0, 229, 255, 8, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 112, 221, 221, 255, 111, 0, 32, 253, 223, 2, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 80, 255, 255, 255, 45, 0, 112, 255, 127, 0, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 64, 254, 255, 207, 4, 0, 0, 250, 11, 0, 0, 0, 80, 255, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 2, 0, 0, 0, 48, 169, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 64, 169, 170, 170, 170, 170, 252, 191, 170, 170, 251, 223, 170, 170, 170, 170, 154, 4, 0, + 0, 0, 0, 230, 10, 0, 0, 249, 143, 0, 0, 248, 191, 0, 0, 64, 157, 0, 0, 0, + 0, 0, 96, 255, 143, 0, 0, 249, 143, 0, 0, 248, 191, 0, 0, 192, 255, 43, 0, 0, + 0, 0, 0, 250, 239, 6, 0, 249, 143, 0, 0, 248, 191, 0, 0, 250, 255, 8, 0, 0, + 0, 0, 0, 210, 255, 78, 0, 249, 143, 0, 0, 248, 191, 0, 128, 255, 175, 0, 0, 0, + 0, 0, 0, 80, 254, 191, 0, 249, 143, 0, 0, 248, 191, 0, 230, 255, 11, 0, 0, 0, + 0, 0, 0, 0, 248, 223, 5, 249, 143, 0, 0, 248, 191, 64, 254, 223, 2, 0, 0, 0, + 0, 0, 0, 0, 176, 44, 0, 249, 143, 0, 0, 248, 191, 0, 231, 45, 0, 0, 0, 0, + 0, 128, 170, 170, 170, 170, 170, 252, 191, 170, 170, 251, 223, 170, 170, 170, 170, 170, 57, 0, + 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 64, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 173, 170, 170, 170, 170, 170, 170, 170, 170, 218, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 222, 221, 221, 221, 221, 221, 221, 221, 221, 237, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 92, 85, 85, 85, 85, 85, 85, 85, 85, 197, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 128, 255, 92, 85, 85, 85, 85, 85, 85, 85, 85, 197, 255, 8, 0, 0, 0, + 0, 0, 0, 112, 254, 11, 0, 0, 0, 0, 0, 0, 0, 0, 176, 239, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 96, 220, 7, 0, 0, 0, 0, 0, 0, 217, 108, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 218, 221, 221, 221, 254, 223, 221, 221, 221, 125, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 253, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, + 0, 148, 170, 186, 255, 172, 170, 57, 252, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, + 0, 248, 255, 255, 255, 255, 255, 95, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 248, 255, 255, 255, 255, 255, 95, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 192, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 247, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 251, 255, 9, 0, 148, 170, 170, 170, 170, 253, 191, 170, 170, 170, 170, 57, 0, + 0, 0, 48, 254, 255, 186, 6, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 128, 255, 255, 255, 94, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 192, 255, 255, 253, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 246, 207, 255, 185, 255, 77, 0, 0, 0, 0, 217, 108, 0, 0, 0, 0, 0, 0, + 0, 0, 252, 158, 255, 41, 253, 223, 2, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 96, 255, 140, 255, 9, 228, 159, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 210, 255, 135, 255, 9, 80, 6, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 249, 223, 130, 255, 9, 0, 32, 169, 170, 170, 170, 253, 191, 170, 170, 170, 122, 0, 0, + 16, 253, 143, 128, 255, 9, 0, 64, 254, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 249, 12, 128, 255, 9, 0, 64, 254, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 213, 6, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 64, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 9, 64, 220, 221, 221, 221, 221, 254, 223, 221, 221, 221, 221, 157, 0, + 0, 0, 0, 128, 255, 9, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 128, 255, 9, 80, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 128, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 220, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 64, 3, 0, 0, 0, 0, 0, 0, 0, 179, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 228, 143, 0, 0, 0, 0, 0, 0, 64, 254, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 253, 255, 27, 0, 0, 0, 0, 0, 0, 248, 255, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 229, 255, 223, 4, 0, 0, 0, 0, 0, 176, 255, 111, 0, 0, 0, 0, 0, 0, + 0, 0, 48, 252, 255, 143, 0, 0, 0, 0, 0, 64, 254, 77, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 160, 255, 239, 5, 215, 221, 221, 221, 221, 238, 221, 221, 221, 221, 189, 3, 0, + 0, 0, 0, 0, 249, 143, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 0, 0, 112, 10, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 32, 2, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 192, 143, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 250, 255, 76, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 231, 255, 255, 8, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 64, 252, 255, 207, 3, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 0, 144, 255, 255, 11, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 0, 0, 230, 223, 2, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 0, 0, 64, 108, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 249, 239, 221, 221, 221, 221, 221, 221, 221, 253, 239, 4, 0, + 0, 0, 0, 144, 42, 0, 0, 251, 159, 0, 0, 0, 0, 0, 0, 0, 245, 239, 4, 0, + 0, 0, 0, 192, 255, 10, 0, 251, 143, 0, 0, 0, 0, 0, 0, 0, 81, 85, 0, 0, + 0, 0, 0, 228, 255, 11, 0, 253, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 255, 7, 64, 254, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 251, 239, 4, 96, 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 253, 191, 0, 144, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 255, 143, 0, 192, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 160, 255, 78, 0, 228, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 210, 255, 12, 0, 249, 239, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 246, 255, 9, 32, 253, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 251, 239, 5, 144, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 254, 207, 0, 228, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 159, 32, 253, 239, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 252, 111, 0, 249, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 8, 0, 112, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 230, 27, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 254, 223, 4, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 249, 255, 94, 0, 148, 170, 170, 170, 218, 255, 171, 170, 170, 170, 154, 3, 0, 0, + 0, 0, 128, 255, 239, 7, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, + 0, 0, 0, 247, 255, 143, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, + 0, 0, 0, 112, 255, 127, 0, 0, 0, 0, 176, 255, 8, 0, 0, 208, 255, 5, 0, 0, + 0, 0, 0, 0, 214, 7, 0, 0, 0, 0, 176, 255, 8, 0, 0, 208, 255, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 208, 255, 5, 0, 0, + 0, 0, 0, 0, 0, 217, 221, 221, 221, 221, 237, 255, 221, 221, 221, 253, 255, 221, 108, 0, + 0, 160, 142, 0, 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 248, 255, 27, 0, 217, 221, 221, 221, 221, 237, 255, 221, 221, 221, 253, 255, 221, 108, 0, + 0, 212, 255, 223, 4, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 208, 255, 5, 0, 0, + 0, 32, 252, 255, 110, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 208, 255, 5, 0, 0, + 0, 0, 160, 255, 255, 7, 0, 0, 0, 0, 176, 255, 8, 0, 0, 208, 255, 5, 0, 0, + 0, 0, 0, 249, 223, 2, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, + 0, 0, 0, 112, 60, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 148, 170, 170, 170, 218, 255, 171, 170, 170, 234, 255, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 160, 205, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 202, 6, 16, 85, 85, 85, 85, 197, 255, 89, 85, 85, 85, 85, 5, 0, 0, + 0, 0, 0, 253, 191, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 64, 254, 143, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 96, 255, 95, 16, 85, 85, 85, 85, 197, 255, 89, 85, 85, 85, 85, 5, 0, 0, + 0, 0, 144, 255, 45, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 176, 255, 11, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 211, 255, 9, 168, 170, 170, 170, 170, 218, 255, 171, 170, 170, 170, 170, 170, 57, 0, + 0, 0, 246, 255, 6, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 249, 223, 3, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 252, 207, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 253, 159, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 112, 255, 127, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 160, 255, 78, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 255, 12, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 7, 0, 0, 0, 0, 0, 0, 144, 205, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 198, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 212, 27, 0, 0, 0, 248, 159, 0, 0, 0, 0, 0, 0, 0, 16, 149, 4, 0, + 0, 48, 253, 207, 3, 0, 0, 248, 159, 0, 0, 82, 101, 169, 202, 237, 255, 255, 9, 0, + 0, 0, 247, 255, 78, 0, 0, 248, 159, 0, 0, 248, 255, 255, 255, 255, 255, 255, 13, 0, + 0, 0, 96, 254, 239, 6, 0, 248, 159, 0, 0, 248, 255, 255, 255, 255, 205, 138, 5, 0, + 0, 0, 0, 230, 255, 110, 0, 248, 159, 0, 0, 248, 207, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 254, 11, 0, 248, 159, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 166, 251, 255, 255, 255, 255, 191, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 251, 255, 255, 255, 255, 191, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 167, 170, 251, 207, 170, 122, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 144, 77, 0, 0, 0, 0, 248, 159, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 248, 239, 7, 0, 0, 0, 248, 159, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 230, 255, 159, 0, 0, 0, 248, 159, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 64, 254, 255, 11, 0, 0, 248, 159, 0, 0, 248, 255, 255, 255, 255, 255, 255, 126, 0, + 0, 0, 212, 255, 207, 2, 0, 248, 159, 0, 0, 248, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 0, 48, 253, 143, 0, 0, 248, 159, 0, 86, 248, 255, 255, 255, 255, 255, 255, 126, 0, + 0, 0, 0, 194, 9, 0, 0, 248, 191, 252, 159, 248, 191, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 250, 255, 255, 191, 248, 159, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 165, 254, 255, 255, 255, 125, 248, 159, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 0, 64, 254, 255, 255, 223, 23, 0, 248, 159, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 197, 39, 253, 255, 253, 159, 0, 0, 249, 159, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 248, 223, 221, 23, 248, 159, 0, 0, 249, 143, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 250, 207, 0, 0, 248, 159, 0, 0, 251, 127, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 252, 159, 0, 0, 248, 159, 0, 0, 252, 95, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 64, 254, 111, 0, 0, 248, 159, 0, 0, 253, 78, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 112, 255, 61, 0, 0, 248, 159, 0, 64, 254, 13, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 160, 255, 11, 0, 0, 248, 159, 0, 112, 255, 11, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 208, 255, 8, 0, 0, 248, 159, 0, 160, 255, 9, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 229, 255, 5, 0, 0, 248, 159, 0, 208, 255, 6, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 249, 223, 2, 0, 0, 248, 159, 0, 246, 223, 3, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 252, 191, 0, 0, 0, 249, 159, 0, 251, 191, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 80, 254, 143, 160, 171, 170, 254, 159, 80, 254, 111, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 144, 255, 78, 160, 255, 255, 255, 127, 192, 255, 12, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 80, 252, 13, 144, 255, 255, 255, 10, 229, 255, 7, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 80, 6, 64, 169, 170, 55, 0, 16, 187, 0, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 80, 1, 0, 0, 0, 0, 0, 0, 0, 232, 239, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 249, 77, 0, 0, 0, 0, 0, 0, 96, 254, 239, 5, 0, 0, 0, 0, 0, 0, + 0, 112, 255, 239, 7, 0, 0, 0, 0, 0, 228, 255, 255, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 231, 255, 159, 0, 0, 0, 0, 32, 253, 223, 253, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 254, 255, 44, 0, 0, 0, 210, 255, 78, 228, 255, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 211, 255, 191, 0, 0, 48, 253, 239, 4, 96, 254, 207, 2, 0, 0, 0, 0, + 0, 0, 0, 32, 252, 78, 0, 0, 228, 255, 94, 0, 0, 248, 255, 77, 0, 0, 0, 0, + 0, 0, 0, 0, 144, 4, 0, 112, 254, 239, 5, 0, 0, 128, 255, 239, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 250, 255, 77, 0, 0, 0, 0, 247, 255, 207, 3, 0, 0, + 0, 0, 2, 0, 0, 0, 214, 255, 255, 255, 255, 255, 255, 255, 239, 254, 255, 175, 1, 0, + 0, 160, 159, 0, 0, 177, 255, 255, 186, 255, 255, 255, 255, 255, 223, 195, 255, 255, 158, 0, + 0, 249, 255, 60, 16, 252, 255, 126, 64, 85, 85, 85, 85, 85, 69, 0, 248, 255, 110, 0, + 0, 194, 255, 239, 5, 230, 207, 4, 0, 0, 0, 0, 0, 0, 0, 0, 48, 251, 8, 0, + 0, 0, 249, 255, 143, 112, 88, 85, 85, 85, 85, 69, 0, 0, 0, 112, 205, 38, 0, 0, + 0, 0, 112, 255, 255, 9, 248, 255, 255, 255, 255, 191, 0, 84, 5, 144, 255, 8, 0, 0, + 0, 0, 0, 230, 191, 1, 248, 255, 255, 255, 255, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 64, 42, 0, 248, 159, 0, 0, 245, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 248, 159, 0, 0, 245, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 248, 159, 0, 0, 245, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 48, 0, 0, 248, 255, 255, 255, 255, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 211, 158, 2, 248, 255, 255, 255, 255, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 245, 223, 3, 248, 207, 170, 170, 250, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 248, 191, 0, 248, 159, 0, 0, 245, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 250, 159, 0, 248, 159, 0, 0, 245, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 0, 252, 111, 0, 248, 175, 85, 85, 247, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 64, 254, 62, 0, 248, 255, 255, 255, 255, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 96, 255, 12, 0, 248, 255, 255, 255, 255, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 144, 255, 9, 0, 248, 159, 0, 0, 245, 191, 0, 253, 78, 144, 255, 8, 0, 0, + 0, 0, 192, 255, 7, 0, 248, 159, 0, 0, 245, 191, 0, 84, 5, 144, 255, 8, 0, 0, + 0, 0, 228, 239, 4, 0, 248, 159, 0, 0, 245, 191, 0, 0, 0, 144, 255, 8, 0, 0, + 0, 0, 247, 223, 0, 0, 248, 159, 0, 0, 245, 191, 0, 0, 0, 160, 255, 8, 0, 0, + 0, 0, 250, 191, 0, 0, 248, 159, 67, 16, 250, 191, 112, 171, 170, 251, 255, 7, 0, 0, + 0, 0, 253, 143, 0, 0, 248, 159, 249, 255, 255, 143, 96, 255, 255, 255, 223, 2, 0, 0, + 0, 64, 254, 111, 0, 0, 248, 159, 246, 255, 255, 11, 64, 254, 255, 255, 93, 0, 0, 0, + 0, 0, 97, 57, 0, 0, 148, 106, 64, 85, 5, 0, 0, 16, 85, 21, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 192, 223, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 234, 6, 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 144, 255, 159, 0, 0, 0, 208, 239, 4, 0, 147, 170, 170, 170, 170, 170, 154, 2, 0, + 0, 32, 252, 255, 10, 0, 0, 208, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 177, 255, 207, 2, 0, 208, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 0, 250, 255, 61, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 160, 255, 45, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 0, 202, 216, 221, 253, 255, 221, 205, 247, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 248, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 0, 0, 249, 255, 255, 255, 255, 239, 247, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 160, 109, 0, 0, 0, 0, 247, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 247, 255, 9, 0, 0, 0, 250, 239, 4, 0, 245, 207, 85, 85, 85, 197, 239, 4, 0, + 0, 229, 255, 207, 2, 0, 0, 253, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 48, 252, 255, 77, 0, 96, 255, 239, 36, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 177, 255, 239, 6, 144, 255, 255, 238, 4, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 250, 223, 2, 210, 255, 255, 255, 11, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 144, 77, 0, 247, 239, 239, 252, 143, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 0, 0, 0, 252, 221, 239, 230, 239, 247, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 0, 0, 80, 254, 218, 239, 132, 255, 252, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 0, 0, 160, 255, 214, 239, 4, 187, 246, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 0, 169, 2, 229, 207, 208, 239, 4, 0, 245, 207, 85, 85, 85, 197, 239, 4, 0, + 0, 0, 0, 253, 127, 250, 143, 208, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 64, 254, 143, 254, 62, 208, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 96, 255, 205, 255, 9, 208, 239, 4, 0, 245, 207, 85, 85, 85, 197, 239, 4, 0, + 0, 0, 144, 255, 253, 239, 4, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 176, 255, 169, 175, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 210, 255, 7, 57, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 245, 239, 4, 0, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 248, 223, 0, 0, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 251, 191, 0, 0, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 32, 253, 143, 0, 0, 0, 208, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 96, 255, 111, 0, 0, 0, 208, 239, 4, 0, 245, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 144, 255, 61, 0, 0, 0, 208, 239, 4, 0, 245, 223, 170, 170, 170, 218, 239, 4, 0, + 0, 160, 255, 12, 0, 0, 0, 208, 239, 4, 0, 245, 191, 0, 0, 0, 176, 239, 4, 0, + 0, 0, 113, 6, 0, 0, 0, 192, 223, 3, 0, 229, 191, 0, 0, 0, 176, 223, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 238, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 12, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 254, 143, 0, 0, 0, 0, 0, 0, + 0, 148, 170, 170, 170, 170, 154, 3, 0, 0, 0, 0, 250, 223, 1, 0, 0, 0, 0, 0, + 0, 248, 255, 255, 255, 255, 255, 199, 221, 221, 221, 221, 253, 222, 221, 221, 221, 221, 125, 0, + 0, 248, 255, 255, 255, 255, 255, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 0, 0, 208, 239, 4, 0, 229, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 196, 221, 221, 221, 221, 221, 221, 221, 221, 59, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 191, 0, 0, 0, 0, 0, 0, 253, 78, 0, 0, + 0, 179, 221, 253, 255, 221, 189, 3, 245, 191, 0, 0, 0, 0, 0, 0, 253, 78, 0, 0, + 0, 228, 255, 255, 255, 255, 239, 4, 245, 191, 0, 0, 0, 0, 0, 0, 253, 78, 0, 0, + 0, 211, 255, 255, 255, 255, 223, 3, 245, 191, 0, 0, 0, 0, 0, 0, 253, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 191, 0, 0, 0, 0, 0, 0, 253, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 207, 85, 85, 85, 85, 85, 85, 253, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 245, 191, 0, 0, 249, 159, 0, 0, 253, 78, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 147, 122, 0, 0, 249, 159, 0, 0, 168, 41, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 55, 0, 0, 249, 159, 0, 96, 6, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 112, 255, 10, 0, 249, 159, 16, 250, 110, 0, 0, 0, + 0, 0, 0, 208, 239, 116, 235, 7, 210, 255, 11, 0, 249, 159, 32, 253, 239, 4, 0, 0, + 0, 0, 0, 212, 255, 255, 255, 8, 251, 223, 3, 0, 249, 159, 0, 228, 255, 45, 0, 0, + 0, 182, 253, 255, 255, 255, 255, 136, 255, 111, 0, 0, 249, 159, 0, 96, 254, 191, 0, 0, + 0, 251, 255, 255, 255, 173, 5, 230, 255, 10, 0, 0, 249, 159, 0, 0, 248, 255, 10, 0, + 0, 248, 255, 156, 4, 0, 64, 254, 223, 2, 0, 0, 249, 159, 0, 0, 176, 255, 110, 0, + 0, 115, 3, 0, 0, 0, 193, 255, 78, 0, 0, 0, 251, 159, 0, 0, 48, 253, 207, 2, + 0, 0, 0, 0, 0, 0, 64, 237, 6, 148, 86, 149, 255, 127, 0, 0, 0, 230, 27, 0, + 0, 0, 0, 0, 0, 0, 0, 66, 0, 246, 255, 255, 255, 61, 0, 0, 0, 48, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 239, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 170, 154, 22, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 215, 157, 0, 0, 0, 0, 0, 0, 0, 0, 217, 125, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 198, 221, 221, 254, 239, 221, 221, 221, 221, 221, 221, 221, 221, 254, 239, 221, 221, 108, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 207, 85, 85, 85, 85, 85, 85, 85, 85, 252, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 223, 170, 170, 170, 170, 170, 170, 170, 170, 253, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 207, 85, 85, 85, 85, 85, 85, 85, 85, 252, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 207, 85, 85, 85, 85, 85, 85, 85, 85, 252, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 251, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 215, 157, 0, 0, 0, 0, 0, 0, 0, 0, 167, 106, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 144, 154, 4, 0, 0, 0, 0, 0, 128, 142, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 192, 255, 8, 0, 0, 0, 0, 0, 248, 255, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 210, 239, 5, 0, 0, 0, 0, 0, 176, 255, 110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 223, 2, 0, 0, 0, 0, 0, 32, 253, 94, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 246, 191, 0, 0, 96, 170, 170, 170, 170, 234, 170, 170, 170, 170, 154, 3, 0, + 0, 96, 220, 253, 223, 221, 125, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, + 0, 128, 255, 255, 255, 255, 159, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, + 0, 128, 255, 255, 255, 255, 159, 144, 255, 8, 0, 0, 0, 0, 0, 0, 176, 255, 5, 0, + 0, 128, 255, 11, 0, 248, 159, 144, 255, 8, 0, 0, 0, 0, 0, 0, 176, 255, 5, 0, + 0, 128, 255, 11, 0, 248, 159, 144, 255, 8, 0, 0, 0, 0, 0, 0, 176, 255, 5, 0, + 0, 128, 255, 11, 0, 248, 159, 48, 85, 2, 0, 0, 0, 0, 0, 0, 64, 85, 1, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 160, 221, 221, 221, 221, 221, 221, 221, 221, 9, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 92, 85, 249, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 173, 170, 251, 159, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, + 0, 128, 255, 11, 0, 248, 159, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, + 0, 128, 255, 11, 0, 248, 159, 128, 170, 170, 253, 175, 170, 234, 255, 170, 170, 170, 7, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 0, 0, 251, 95, 0, 208, 255, 5, 0, 0, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 0, 0, 253, 94, 0, 208, 255, 5, 0, 0, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 0, 48, 254, 62, 0, 208, 255, 5, 0, 0, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 0, 96, 255, 13, 0, 208, 255, 5, 0, 0, 0, 0, + 0, 128, 255, 11, 0, 248, 159, 0, 0, 144, 255, 10, 0, 208, 255, 5, 0, 199, 7, 0, + 0, 128, 255, 222, 221, 253, 159, 0, 0, 210, 255, 7, 0, 208, 255, 5, 0, 248, 191, 0, + 0, 128, 255, 255, 255, 255, 159, 0, 0, 248, 223, 2, 0, 208, 255, 5, 0, 249, 175, 0, + 0, 128, 255, 255, 255, 255, 159, 0, 64, 254, 143, 0, 0, 208, 255, 5, 0, 250, 159, 0, + 0, 128, 255, 11, 0, 0, 0, 0, 212, 255, 45, 0, 0, 208, 255, 5, 0, 251, 143, 0, + 0, 128, 255, 11, 0, 0, 0, 80, 254, 239, 6, 0, 0, 208, 255, 6, 32, 253, 95, 0, + 0, 96, 220, 9, 0, 0, 32, 250, 255, 143, 0, 0, 0, 192, 255, 222, 237, 255, 78, 0, + 0, 0, 0, 0, 0, 0, 249, 255, 255, 7, 0, 0, 0, 144, 255, 255, 255, 255, 10, 0, + 0, 0, 0, 0, 0, 0, 229, 255, 77, 0, 0, 0, 0, 0, 250, 255, 255, 174, 0, 0, + 0, 0, 0, 0, 0, 0, 128, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 0, 0, 0, + 0, 0, 0, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 0, 69, 0, 0, 148, 106, 0, 0, 67, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 112, 255, 26, 0, 248, 159, 0, 96, 254, 8, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 64, 253, 239, 6, 248, 159, 0, 250, 255, 9, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 0, 144, 255, 11, 248, 159, 160, 255, 77, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 0, 0, 167, 0, 248, 159, 16, 155, 0, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 251, 255, 255, 255, 255, 255, 255, 255, 255, 191, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 251, 255, 255, 255, 255, 255, 255, 255, 255, 191, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 0, 0, 48, 251, 255, 191, 125, 0, 0, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 0, 0, 249, 255, 252, 255, 255, 126, 0, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 16, 233, 255, 126, 248, 159, 250, 255, 126, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 110, 252, 255, 207, 4, 248, 159, 32, 250, 255, 109, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 250, 239, 7, 0, 248, 159, 0, 64, 251, 175, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 160, 7, 0, 0, 82, 53, 0, 0, 96, 9, 228, 223, 0, 0, 0, + 0, 0, 0, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, 0, + 0, 0, 0, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, 0, + 0, 0, 0, 253, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 223, 0, 0, 0, + 0, 0, 0, 84, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 69, 0, 0, 0, + 0, 196, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 76, 0, + 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 81, 85, 85, 197, 255, 90, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 21, 0, + 0, 0, 0, 0, 228, 239, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 247, 223, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 3, 0, 0, 0, + 0, 0, 0, 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, + 0, 0, 0, 32, 219, 221, 221, 221, 221, 221, 221, 221, 221, 221, 237, 255, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 69, 0, 0, 0, 64, 252, 223, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 253, 255, 255, 255, 255, 239, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 117, 170, 105, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 198, 221, 221, 221, 221, 255, 223, 221, 221, 221, 221, 237, 255, 222, 221, 221, 221, 108, 0, + 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 255, 141, 255, 62, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 128, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 85, 85, 85, 85, 149, 255, 93, 85, 85, 85, 85, 85, 4, 0, 0, 0, 0, + 0, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, 0, + 0, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, 0, + 0, 0, 179, 221, 221, 221, 221, 237, 255, 222, 221, 221, 221, 237, 255, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 144, 255, 11, 0, 0, 0, 0, + 0, 0, 0, 128, 26, 0, 0, 208, 255, 8, 0, 0, 0, 176, 255, 91, 235, 7, 0, 0, + 0, 0, 0, 211, 239, 6, 0, 228, 255, 6, 0, 0, 0, 176, 255, 236, 255, 12, 0, 0, + 0, 0, 0, 250, 239, 6, 0, 247, 239, 4, 0, 0, 0, 176, 255, 170, 255, 111, 0, 0, + 0, 0, 80, 254, 175, 0, 0, 250, 223, 0, 0, 0, 0, 176, 255, 73, 254, 191, 0, 0, + 0, 0, 192, 255, 78, 0, 32, 253, 175, 0, 0, 0, 0, 176, 255, 9, 251, 239, 4, 0, + 0, 0, 248, 255, 8, 0, 112, 255, 127, 0, 0, 0, 0, 176, 255, 9, 246, 255, 9, 0, + 0, 64, 254, 207, 0, 0, 192, 255, 45, 0, 0, 0, 0, 192, 255, 9, 192, 255, 12, 0, + 0, 192, 255, 94, 0, 0, 247, 255, 9, 0, 0, 0, 0, 208, 255, 8, 144, 255, 111, 0, + 0, 249, 255, 8, 0, 32, 253, 223, 3, 0, 0, 0, 0, 208, 255, 8, 64, 254, 175, 0, + 0, 212, 191, 0, 0, 176, 255, 143, 0, 0, 0, 0, 0, 210, 255, 8, 0, 251, 159, 0, + 0, 16, 72, 0, 0, 250, 255, 11, 0, 0, 0, 0, 0, 228, 255, 5, 0, 101, 0, 0, + 0, 0, 0, 0, 144, 255, 239, 4, 0, 0, 0, 0, 0, 247, 255, 5, 0, 0, 0, 0, + 0, 0, 0, 16, 251, 255, 110, 0, 0, 0, 0, 0, 16, 252, 223, 3, 0, 0, 0, 0, + 0, 0, 0, 214, 255, 239, 6, 0, 180, 154, 85, 85, 215, 255, 175, 0, 0, 0, 0, 0, + 0, 0, 179, 255, 255, 77, 0, 0, 228, 255, 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, + 0, 144, 255, 255, 191, 2, 0, 0, 210, 255, 255, 255, 255, 239, 6, 0, 0, 0, 0, 0, + 0, 32, 253, 239, 7, 0, 0, 0, 192, 255, 255, 255, 223, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 214, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 211, 207, 0, 0, 0, 48, 253, 12, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 228, 223, 0, 0, 0, 64, 254, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 228, 223, 0, 0, 0, 64, 254, 13, 0, 0, 0, 0, 0, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 83, 85, 85, 85, 85, 230, 223, 85, 85, 85, 101, 254, 93, 85, 85, 85, 85, 69, 0, + 0, 0, 0, 0, 0, 0, 228, 223, 0, 0, 0, 64, 254, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 69, 0, 0, 0, 0, 85, 4, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, + 0, 128, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 8, 0, + 0, 128, 255, 11, 211, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 61, 176, 255, 8, 0, + 0, 0, 0, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, 0, 0, + 0, 0, 0, 0, 80, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 7, 0, + 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 16, 85, 85, 85, 85, 85, 101, 251, 255, 175, 86, 85, 85, 85, 85, 85, 85, 2, 0, + 0, 0, 0, 0, 0, 0, 130, 253, 255, 255, 8, 0, 0, 0, 0, 145, 93, 0, 0, 0, + 0, 0, 0, 0, 64, 234, 255, 255, 139, 254, 127, 0, 0, 0, 164, 255, 191, 0, 0, 0, + 0, 0, 64, 200, 255, 255, 223, 24, 32, 251, 239, 5, 0, 198, 255, 255, 191, 3, 0, 0, + 0, 178, 255, 255, 255, 158, 3, 32, 250, 255, 255, 60, 233, 255, 255, 141, 1, 0, 0, 0, + 0, 128, 255, 223, 41, 0, 80, 251, 255, 109, 252, 255, 255, 255, 77, 0, 0, 0, 0, 0, + 0, 0, 121, 1, 0, 129, 254, 255, 109, 0, 247, 255, 191, 253, 111, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 215, 255, 255, 92, 0, 145, 255, 239, 4, 246, 223, 3, 0, 0, 0, 0, + 0, 0, 147, 253, 255, 255, 74, 0, 128, 254, 255, 255, 6, 160, 255, 44, 0, 0, 0, 0, + 0, 193, 255, 255, 223, 24, 0, 145, 254, 255, 170, 255, 8, 32, 253, 207, 2, 0, 0, 0, + 0, 96, 255, 174, 4, 0, 164, 255, 255, 75, 144, 255, 8, 0, 228, 255, 109, 0, 0, 0, + 0, 0, 70, 0, 16, 216, 255, 255, 74, 0, 160, 255, 7, 0, 64, 254, 255, 59, 0, 0, + 0, 0, 0, 113, 252, 255, 239, 25, 0, 0, 211, 239, 5, 0, 0, 212, 255, 255, 92, 0, + 0, 64, 217, 255, 255, 207, 6, 0, 0, 64, 252, 207, 0, 0, 0, 16, 251, 255, 110, 0, + 0, 230, 255, 255, 141, 2, 210, 255, 255, 255, 255, 143, 0, 0, 0, 0, 96, 253, 8, 0, + 0, 160, 239, 57, 0, 0, 176, 255, 255, 255, 255, 9, 0, 0, 0, 0, 0, 32, 0, 0, + 0, 32, 2, 0, 0, 0, 112, 221, 221, 205, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 112, 205, 4, 0, 0, 215, 76, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 144, 255, 5, 0, 0, 249, 95, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 144, 255, 5, 0, 0, 249, 95, 0, 0, 0, 0, 0, 0, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 166, 170, 170, 170, 170, 202, 255, 170, 170, 170, 252, 175, 170, 170, 170, 170, 170, 122, 0, + 0, 0, 0, 0, 0, 0, 144, 255, 5, 0, 0, 249, 95, 144, 189, 3, 232, 8, 0, 0, + 0, 48, 219, 7, 0, 0, 144, 255, 5, 0, 0, 249, 95, 176, 239, 4, 250, 143, 0, 0, + 0, 64, 254, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 239, 4, 176, 175, 0, 0, + 0, 64, 254, 9, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 0, + 0, 64, 254, 9, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 64, 254, 9, 208, 207, 85, 85, 85, 85, 85, 85, 85, 165, 239, 86, 85, 85, 37, 0, + 0, 64, 254, 9, 208, 191, 0, 0, 0, 0, 0, 0, 0, 144, 239, 4, 0, 0, 0, 0, + 0, 64, 254, 9, 208, 191, 96, 170, 170, 170, 170, 170, 170, 134, 255, 5, 16, 2, 0, 0, + 0, 64, 254, 9, 208, 191, 144, 255, 255, 255, 255, 255, 255, 137, 255, 5, 128, 223, 5, 0, + 0, 64, 254, 255, 255, 191, 144, 255, 221, 221, 255, 222, 221, 135, 255, 5, 176, 239, 4, 0, + 0, 64, 254, 255, 255, 191, 144, 239, 4, 0, 253, 9, 0, 96, 255, 6, 210, 207, 0, 0, + 0, 0, 85, 85, 213, 191, 144, 239, 4, 0, 253, 9, 0, 80, 255, 8, 246, 159, 0, 0, + 0, 0, 0, 0, 208, 191, 144, 239, 4, 0, 253, 9, 0, 80, 254, 8, 250, 94, 0, 0, + 0, 0, 0, 0, 208, 191, 144, 239, 170, 170, 254, 172, 122, 64, 254, 57, 254, 12, 0, 0, + 0, 217, 221, 221, 253, 191, 144, 255, 255, 255, 255, 255, 191, 48, 253, 137, 255, 7, 0, 0, + 0, 251, 255, 255, 255, 191, 144, 255, 221, 221, 221, 237, 191, 0, 253, 219, 223, 2, 0, 0, + 0, 167, 252, 174, 234, 191, 144, 239, 4, 0, 0, 176, 191, 0, 252, 254, 143, 0, 0, 0, + 0, 0, 249, 78, 208, 191, 144, 239, 4, 0, 0, 176, 191, 0, 251, 255, 12, 0, 0, 0, + 0, 0, 249, 78, 208, 191, 144, 239, 4, 0, 0, 176, 191, 0, 249, 255, 6, 0, 0, 0, + 0, 0, 249, 78, 210, 191, 144, 255, 255, 255, 255, 255, 191, 0, 249, 175, 0, 128, 5, 0, + 0, 0, 251, 61, 228, 191, 144, 255, 255, 255, 255, 255, 191, 96, 254, 143, 0, 208, 191, 0, + 0, 0, 252, 13, 228, 159, 144, 239, 4, 0, 253, 185, 191, 230, 255, 191, 0, 211, 191, 0, + 0, 64, 254, 11, 245, 159, 144, 239, 4, 0, 253, 9, 96, 254, 222, 223, 2, 228, 159, 0, + 0, 128, 255, 9, 248, 143, 144, 239, 4, 0, 253, 9, 112, 255, 136, 255, 6, 245, 143, 0, + 0, 210, 239, 4, 251, 95, 144, 239, 4, 0, 253, 9, 0, 137, 64, 254, 11, 246, 127, 0, + 0, 250, 191, 80, 254, 45, 144, 255, 221, 221, 255, 222, 221, 10, 0, 251, 111, 248, 95, 0, + 0, 246, 78, 192, 255, 10, 144, 255, 255, 255, 255, 255, 255, 13, 0, 229, 239, 254, 61, 0, + 0, 128, 6, 210, 239, 4, 96, 170, 170, 170, 170, 170, 170, 8, 0, 128, 255, 255, 11, 0, + 0, 0, 0, 32, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 207, 3, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, 239, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 191, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 144, 221, 221, 221, 221, 221, 125, 0, 96, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 255, 255, 255, 255, 255, 159, 0, 228, 255, 255, 255, 255, 255, 95, 0, 0, 0, 0, + 0, 144, 221, 221, 221, 221, 255, 159, 32, 253, 255, 255, 255, 255, 255, 95, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 144, 255, 12, 212, 255, 109, 85, 85, 197, 255, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 229, 239, 85, 254, 239, 4, 0, 0, 250, 223, 2, 0, 0, 0, 0, + 0, 0, 160, 110, 32, 253, 143, 64, 253, 78, 0, 0, 96, 254, 110, 0, 0, 0, 0, 0, + 0, 0, 248, 239, 166, 255, 11, 0, 252, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 0, 160, 255, 255, 239, 4, 0, 253, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 0, 0, 250, 255, 143, 0, 0, 253, 174, 170, 170, 250, 223, 170, 170, 234, 191, 0, 0, + 0, 0, 0, 176, 255, 110, 0, 0, 253, 13, 0, 0, 248, 159, 0, 0, 228, 191, 0, 0, + 0, 0, 0, 32, 253, 207, 2, 0, 253, 13, 0, 0, 252, 94, 0, 0, 228, 191, 0, 0, + 0, 83, 85, 85, 214, 108, 85, 53, 253, 93, 85, 149, 255, 92, 85, 85, 230, 191, 0, 0, + 0, 249, 255, 255, 255, 255, 255, 159, 253, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 249, 255, 255, 255, 255, 255, 159, 253, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 0, 0, 144, 255, 8, 249, 111, 252, 12, 228, 255, 11, 0, 0, 0, 211, 191, 0, 0, + 0, 0, 0, 144, 255, 8, 253, 13, 0, 112, 254, 255, 143, 0, 0, 0, 144, 9, 0, 0, + 0, 0, 0, 144, 255, 104, 255, 9, 48, 251, 255, 235, 239, 6, 0, 48, 252, 143, 0, 0, + 0, 0, 0, 144, 255, 152, 239, 21, 249, 255, 126, 192, 255, 12, 0, 231, 255, 175, 0, 0, + 0, 0, 0, 144, 255, 8, 116, 233, 255, 207, 19, 251, 255, 143, 179, 255, 223, 6, 0, 0, + 0, 0, 0, 144, 255, 8, 248, 255, 239, 7, 195, 255, 253, 239, 255, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 8, 228, 255, 25, 96, 254, 191, 193, 255, 255, 239, 4, 0, 0, 0, + 0, 0, 0, 144, 255, 8, 112, 25, 48, 251, 255, 9, 210, 255, 157, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 8, 0, 16, 249, 255, 126, 32, 253, 255, 44, 253, 78, 0, 0, 0, + 0, 0, 0, 144, 255, 8, 64, 250, 255, 191, 3, 212, 255, 255, 45, 248, 175, 0, 0, 0, + 0, 0, 0, 144, 255, 8, 249, 255, 239, 7, 96, 254, 191, 253, 78, 210, 255, 8, 0, 0, + 0, 0, 0, 144, 255, 8, 176, 239, 8, 16, 250, 255, 10, 251, 78, 112, 255, 94, 0, 0, + 0, 0, 0, 144, 255, 8, 48, 6, 0, 214, 255, 143, 0, 251, 78, 0, 251, 239, 4, 0, + 0, 0, 0, 144, 255, 8, 0, 0, 197, 255, 223, 4, 0, 253, 78, 0, 210, 255, 126, 0, + 0, 0, 0, 176, 255, 8, 0, 214, 255, 255, 10, 0, 96, 255, 13, 0, 64, 254, 191, 0, + 0, 81, 1, 229, 255, 5, 128, 255, 255, 92, 169, 103, 250, 255, 10, 0, 0, 230, 11, 0, + 0, 228, 255, 255, 223, 3, 0, 250, 92, 48, 253, 255, 255, 239, 5, 0, 0, 32, 1, 0, + 0, 208, 255, 255, 143, 0, 0, 49, 0, 0, 252, 255, 255, 110, 0, 0, 0, 0, 0, 0, + 0, 112, 170, 154, 4, 0, 0, 0, 0, 0, 84, 85, 69, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 221, 221, 221, 221, 221, 254, 239, 221, 221, 221, 221, 221, 108, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 248, 191, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 248, 191, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 248, 191, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 251, 143, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 248, 223, 170, 170, 170, 170, 252, 207, 170, 170, 170, 170, 253, 143, 0, 0, 0, + 0, 0, 0, 148, 122, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 167, 73, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 166, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 122, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 172, 170, 170, 170, 170, 170, 170, 170, 170, 202, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 9, 0, 0, 0, 215, 125, 0, 0, 0, 144, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 9, 0, 0, 0, 249, 159, 0, 0, 0, 144, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 9, 0, 0, 0, 251, 159, 0, 0, 0, 144, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 9, 0, 0, 0, 252, 143, 0, 0, 0, 144, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 9, 0, 0, 64, 254, 95, 0, 0, 0, 144, 255, 9, 0, 0, 0, + 0, 0, 0, 144, 255, 9, 0, 0, 160, 255, 255, 107, 0, 0, 144, 255, 9, 0, 0, 0, + 0, 0, 0, 112, 221, 7, 0, 16, 250, 255, 255, 255, 174, 4, 96, 170, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 232, 255, 207, 252, 255, 255, 239, 41, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 113, 252, 255, 255, 44, 16, 216, 255, 255, 255, 125, 0, 0, 0, 0, + 0, 0, 32, 150, 236, 255, 255, 255, 142, 0, 0, 0, 164, 255, 255, 255, 175, 4, 0, 0, + 0, 64, 254, 255, 255, 255, 255, 141, 1, 0, 0, 0, 0, 112, 253, 255, 255, 191, 0, 0, + 0, 0, 251, 255, 255, 223, 57, 0, 0, 0, 0, 0, 0, 0, 64, 250, 255, 94, 0, 0, + 0, 0, 231, 189, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 195, 78, 0, 0, 0, 0, 0, 64, 238, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 239, 4, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 176, 255, 28, 0, 0, 0, 0, 250, 239, 170, 170, 170, 170, 73, 0, 0, + 0, 112, 170, 170, 170, 254, 171, 170, 154, 3, 96, 254, 255, 255, 255, 255, 255, 143, 0, 0, + 0, 176, 255, 255, 255, 255, 255, 255, 255, 5, 228, 255, 222, 221, 221, 221, 255, 127, 0, 0, + 0, 176, 255, 255, 255, 255, 255, 255, 239, 69, 253, 255, 127, 0, 0, 212, 255, 10, 0, 0, + 0, 0, 96, 190, 0, 0, 96, 174, 1, 211, 255, 234, 255, 9, 112, 254, 175, 0, 0, 0, + 0, 0, 176, 255, 10, 0, 192, 255, 10, 248, 175, 64, 253, 207, 251, 255, 8, 0, 0, 0, + 0, 0, 32, 253, 143, 0, 248, 223, 2, 128, 10, 0, 195, 255, 255, 93, 0, 0, 0, 0, + 0, 84, 85, 248, 159, 117, 254, 126, 85, 37, 0, 64, 250, 255, 255, 223, 40, 0, 0, 0, + 0, 251, 255, 255, 255, 255, 255, 255, 255, 143, 164, 254, 255, 207, 252, 255, 255, 206, 73, 0, + 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 175, 3, 64, 234, 255, 255, 143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 157, 85, 85, 85, 85, 167, 253, 45, 0, + 0, 0, 84, 85, 85, 85, 85, 85, 53, 165, 214, 255, 255, 255, 255, 255, 255, 9, 0, 0, + 0, 0, 253, 255, 255, 255, 255, 255, 159, 0, 192, 255, 255, 255, 255, 255, 255, 9, 0, 0, + 0, 0, 253, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, 249, 143, 0, 0, 0, 0, 0, + 0, 0, 253, 11, 0, 0, 0, 245, 159, 0, 0, 0, 0, 249, 143, 0, 0, 0, 0, 0, + 0, 0, 253, 11, 0, 0, 0, 245, 159, 196, 221, 221, 221, 254, 223, 221, 221, 221, 76, 0, + 0, 0, 253, 222, 221, 221, 221, 253, 159, 245, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 253, 255, 255, 255, 255, 255, 159, 147, 170, 170, 170, 170, 170, 170, 170, 170, 57, 0, + 0, 0, 253, 173, 170, 170, 170, 250, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 253, 11, 0, 0, 0, 245, 159, 0, 218, 221, 221, 221, 221, 221, 221, 125, 0, 0, + 0, 0, 253, 92, 85, 85, 85, 247, 159, 0, 253, 255, 255, 255, 255, 255, 255, 159, 0, 0, + 0, 0, 253, 255, 255, 255, 255, 255, 159, 0, 253, 93, 85, 85, 85, 85, 247, 159, 0, 0, + 0, 0, 253, 255, 255, 255, 255, 255, 159, 0, 253, 13, 0, 231, 126, 0, 245, 159, 0, 0, + 0, 0, 253, 11, 128, 255, 8, 245, 159, 0, 253, 13, 0, 248, 143, 0, 245, 159, 0, 0, + 0, 0, 0, 0, 128, 255, 8, 0, 0, 0, 253, 13, 0, 249, 111, 0, 245, 159, 0, 0, + 0, 84, 85, 85, 149, 255, 89, 85, 85, 37, 253, 13, 0, 250, 95, 0, 245, 159, 0, 0, + 0, 251, 255, 255, 255, 255, 255, 255, 255, 143, 253, 13, 0, 251, 78, 0, 245, 159, 0, 0, + 0, 251, 255, 255, 255, 255, 255, 255, 255, 126, 252, 12, 64, 254, 158, 2, 196, 125, 0, 0, + 0, 0, 0, 0, 128, 255, 8, 0, 0, 0, 0, 0, 195, 255, 255, 223, 24, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 8, 0, 0, 0, 64, 216, 255, 239, 254, 255, 255, 108, 0, 0, + 0, 0, 0, 0, 128, 255, 8, 0, 196, 253, 255, 255, 255, 77, 96, 251, 255, 255, 126, 0, + 0, 0, 0, 0, 128, 255, 8, 0, 176, 255, 255, 255, 125, 0, 0, 16, 232, 255, 110, 0, + 0, 0, 0, 0, 128, 255, 8, 0, 96, 238, 172, 5, 0, 0, 0, 0, 0, 181, 11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 48, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 231, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 250, 255, 8, 0, 144, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 189, 3, 0, + 0, 0, 176, 255, 110, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 48, 253, 223, 2, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 4, 0, + 0, 0, 0, 246, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 223, 3, 0, + 0, 0, 0, 160, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 60, 0, 0, + 0, 0, 0, 32, 253, 175, 0, 0, 0, 0, 0, 0, 0, 48, 252, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 198, 6, 0, 0, 0, 0, 0, 0, 0, 231, 255, 223, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 255, 255, 10, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 175, 1, 0, 0, 0, 0, 0, + 0, 166, 170, 170, 154, 3, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 249, 255, 255, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 249, 255, 255, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 192, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 0, 0, 0, 0, 229, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 96, 204, 170, 170, 254, 255, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 228, 255, 5, 0, 80, 254, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 249, 255, 7, 0, 48, 253, 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 177, 255, 255, 94, 0, 0, 168, 170, 170, 73, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 253, 255, 254, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 212, 255, 175, 211, 255, 239, 106, 2, 0, 0, 0, 0, 0, 0, 0, 80, 85, 69, 0, + 0, 248, 255, 10, 32, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 210, 175, 0, 0, 112, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 0, + 0, 128, 11, 0, 0, 0, 48, 168, 220, 221, 221, 221, 221, 221, 221, 221, 221, 221, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 255, 255, 255, 239, 4, 253, 255, 255, 255, 9, 198, 221, 221, 221, 221, 221, 9, 0, + 0, 80, 255, 255, 255, 239, 4, 253, 255, 255, 255, 9, 248, 255, 255, 255, 255, 255, 11, 0, + 0, 80, 255, 8, 144, 239, 4, 253, 11, 64, 254, 9, 248, 255, 255, 255, 255, 255, 11, 0, + 0, 80, 255, 8, 144, 239, 4, 253, 11, 64, 254, 9, 248, 191, 0, 0, 160, 255, 10, 0, + 0, 80, 255, 8, 144, 239, 4, 253, 11, 64, 254, 9, 248, 191, 0, 0, 210, 255, 6, 0, + 0, 80, 255, 8, 144, 239, 4, 253, 11, 64, 254, 9, 248, 191, 0, 0, 246, 207, 0, 0, + 0, 80, 255, 171, 202, 239, 4, 253, 173, 170, 254, 9, 248, 191, 0, 0, 250, 159, 0, 0, + 0, 80, 255, 255, 255, 239, 4, 253, 255, 255, 255, 9, 248, 191, 0, 48, 253, 78, 0, 0, + 0, 80, 255, 221, 237, 239, 4, 253, 222, 221, 255, 9, 248, 191, 0, 112, 255, 11, 0, 0, + 0, 80, 255, 8, 144, 239, 4, 253, 11, 64, 254, 9, 248, 191, 0, 176, 255, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 191, 0, 228, 223, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 191, 0, 249, 159, 0, 0, 0, + 0, 0, 228, 255, 255, 255, 255, 255, 255, 255, 143, 0, 248, 191, 32, 253, 127, 0, 0, 0, + 0, 0, 228, 255, 255, 255, 255, 255, 255, 255, 143, 0, 248, 191, 0, 250, 239, 6, 0, 0, + 0, 0, 80, 85, 85, 85, 85, 85, 85, 85, 37, 0, 248, 191, 0, 160, 255, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 191, 0, 0, 252, 175, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 191, 0, 0, 229, 239, 5, 0, + 0, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 248, 191, 0, 0, 160, 255, 10, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 248, 191, 0, 0, 80, 254, 45, 0, + 0, 148, 170, 252, 207, 170, 170, 170, 170, 170, 170, 122, 248, 191, 0, 0, 0, 253, 95, 0, + 0, 0, 0, 252, 111, 0, 0, 0, 0, 0, 0, 0, 248, 191, 0, 0, 0, 251, 127, 0, + 0, 0, 80, 254, 12, 0, 0, 0, 0, 0, 0, 0, 248, 191, 0, 0, 0, 251, 143, 0, + 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 10, 0, 248, 191, 0, 0, 0, 252, 127, 0, + 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 9, 0, 248, 191, 0, 0, 80, 254, 95, 0, + 0, 0, 96, 170, 170, 170, 170, 170, 186, 255, 9, 0, 248, 207, 122, 53, 215, 255, 45, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 8, 0, 248, 207, 254, 255, 255, 255, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 8, 0, 248, 191, 253, 255, 255, 191, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 7, 0, 248, 191, 233, 255, 206, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 5, 0, 248, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 87, 85, 117, 253, 223, 2, 0, 248, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 192, 255, 255, 255, 255, 143, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 160, 255, 255, 255, 255, 9, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 96, 170, 170, 170, 39, 0, 0, 0, 248, 191, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 160, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 251, 223, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 255, 77, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 0, 0, + 0, 0, 0, 128, 255, 223, 3, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, + 0, 0, 0, 0, 250, 255, 61, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, + 0, 0, 0, 0, 176, 255, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 223, 0, 0, + 0, 0, 0, 0, 0, 171, 0, 0, 0, 231, 159, 0, 0, 0, 0, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 248, 159, 0, 0, 0, 0, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 248, 159, 0, 0, 0, 0, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 248, 159, 0, 0, 0, 0, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 80, 85, 85, 85, 249, 175, 85, 85, 85, 21, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 207, 85, 85, 249, 175, 85, 85, 252, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 191, 0, 0, 248, 159, 0, 0, 251, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 191, 0, 0, 248, 159, 0, 0, 251, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 191, 0, 0, 248, 159, 0, 0, 251, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 191, 0, 0, 248, 159, 0, 0, 251, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 223, 170, 170, 251, 207, 170, 170, 253, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 228, 191, 0, 0, 248, 159, 0, 0, 37, 0, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 146, 122, 0, 0, 248, 159, 0, 213, 191, 0, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 248, 159, 0, 228, 255, 8, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 248, 159, 0, 128, 255, 78, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 248, 175, 85, 167, 254, 191, 0, 245, 223, 0, 0, + 0, 0, 253, 143, 197, 221, 255, 255, 255, 255, 255, 255, 255, 255, 255, 6, 245, 223, 0, 0, + 0, 0, 253, 143, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 45, 245, 223, 0, 0, + 0, 0, 253, 143, 228, 255, 255, 239, 221, 172, 154, 86, 37, 80, 254, 159, 245, 223, 0, 0, + 0, 0, 253, 143, 80, 3, 0, 0, 0, 0, 0, 0, 0, 0, 250, 76, 246, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 249, 223, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 0, 0, 0, 148, 88, 85, 165, 255, 191, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, 255, 255, 255, 110, 0, 0, + 0, 0, 253, 143, 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 218, 108, 0, 0, 0, 0, 0, 0, 0, 0, 128, 170, 220, 172, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 125, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, + 0, 48, 253, 255, 255, 255, 255, 9, 0, 0, 0, 0, 249, 159, 0, 0, 0, 0, 0, 0, + 0, 64, 254, 255, 255, 255, 255, 9, 0, 0, 0, 0, 249, 143, 0, 0, 0, 0, 0, 0, + 0, 64, 254, 255, 255, 255, 255, 9, 0, 0, 0, 0, 249, 143, 0, 0, 0, 0, 0, 0, + 0, 64, 254, 78, 0, 210, 255, 72, 220, 221, 221, 221, 254, 223, 221, 221, 221, 205, 6, 0, + 0, 64, 254, 78, 0, 246, 239, 84, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 64, 254, 78, 0, 249, 207, 80, 254, 255, 255, 255, 255, 255, 255, 255, 255, 239, 7, 0, + 0, 64, 254, 78, 0, 252, 143, 0, 0, 0, 0, 0, 251, 143, 0, 0, 0, 0, 0, 0, + 0, 64, 254, 78, 80, 254, 78, 0, 0, 1, 0, 0, 251, 143, 0, 0, 80, 0, 0, 0, + 0, 64, 254, 78, 144, 255, 12, 0, 212, 45, 0, 0, 251, 143, 0, 0, 250, 76, 0, 0, + 0, 64, 254, 78, 192, 255, 9, 16, 252, 191, 0, 0, 251, 143, 0, 96, 255, 159, 0, 0, + 0, 64, 254, 78, 228, 239, 5, 0, 230, 255, 10, 0, 251, 143, 0, 228, 255, 11, 0, 0, + 0, 64, 254, 78, 248, 207, 0, 0, 128, 255, 143, 0, 251, 143, 32, 253, 223, 2, 0, 0, + 0, 64, 254, 78, 248, 239, 6, 0, 0, 251, 239, 3, 251, 143, 193, 255, 78, 0, 0, 0, + 0, 64, 254, 78, 176, 255, 12, 0, 0, 228, 126, 0, 252, 111, 112, 239, 6, 0, 0, 0, + 0, 64, 254, 78, 64, 254, 143, 0, 0, 64, 3, 0, 253, 95, 0, 86, 0, 0, 0, 0, + 0, 64, 254, 78, 0, 249, 223, 2, 0, 0, 0, 0, 253, 95, 0, 0, 0, 0, 0, 0, + 0, 64, 254, 78, 0, 227, 255, 151, 170, 170, 170, 170, 254, 174, 170, 170, 170, 170, 73, 0, + 0, 64, 254, 78, 0, 160, 255, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 64, 254, 78, 0, 112, 255, 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 64, 254, 78, 0, 80, 255, 62, 0, 0, 0, 176, 255, 223, 2, 0, 0, 0, 0, 0, + 0, 64, 254, 78, 0, 64, 254, 78, 0, 0, 0, 227, 255, 255, 8, 0, 0, 0, 0, 0, + 0, 64, 254, 78, 0, 80, 254, 78, 0, 0, 0, 248, 207, 255, 45, 0, 0, 0, 0, 0, + 0, 64, 254, 78, 0, 128, 255, 13, 0, 0, 32, 253, 127, 252, 143, 0, 0, 0, 0, 0, + 0, 64, 254, 174, 105, 250, 255, 11, 0, 0, 160, 255, 45, 247, 239, 4, 0, 0, 0, 0, + 0, 64, 254, 222, 255, 255, 239, 5, 0, 0, 248, 255, 7, 192, 255, 11, 0, 0, 0, 0, + 0, 64, 254, 190, 255, 255, 110, 0, 0, 96, 254, 191, 0, 96, 254, 175, 0, 0, 0, 0, + 0, 64, 254, 94, 169, 73, 0, 0, 0, 247, 255, 61, 0, 0, 250, 255, 10, 0, 0, 0, + 0, 64, 254, 78, 0, 0, 0, 0, 160, 255, 239, 4, 0, 0, 176, 255, 207, 3, 0, 0, + 0, 64, 254, 78, 0, 0, 0, 80, 253, 255, 94, 0, 0, 0, 32, 253, 255, 126, 0, 0, + 0, 64, 254, 78, 0, 0, 48, 251, 255, 223, 4, 0, 0, 0, 0, 210, 255, 255, 109, 0, + 0, 64, 254, 78, 0, 0, 193, 255, 255, 44, 0, 0, 0, 0, 0, 16, 251, 255, 110, 0, + 0, 64, 254, 78, 0, 0, 80, 254, 143, 0, 0, 0, 0, 0, 0, 0, 128, 255, 8, 0, + 0, 48, 253, 61, 0, 0, 0, 201, 4, 0, 0, 0, 0, 0, 0, 0, 0, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 32, 85, 85, 85, 85, 85, 85, 85, 250, 207, 85, 85, 85, 85, 85, 85, 85, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 170, 170, 170, 170, 170, 170, 252, 223, 170, 170, 170, 170, 170, 138, 0, 0, 0, + 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, 0, + 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 211, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 61, 0, + 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, + 0, 146, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 41, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 112, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 7, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 172, 170, 170, 170, 170, 170, 170, 170, 170, 202, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 90, 85, 85, 85, 85, 85, 85, 85, 85, 165, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 186, 170, 170, 251, 255, 11, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 250, 255, 255, 255, 255, 7, 0, 0, 0, + 0, 0, 0, 176, 255, 9, 0, 0, 0, 0, 0, 248, 255, 255, 255, 142, 0, 0, 0, 0, + 0, 0, 0, 112, 170, 6, 0, 0, 0, 0, 0, 82, 85, 85, 53, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 228, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 253, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 194, 255, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 252, 255, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 212, 255, 44, 0, 0, 0, 0, 0, 250, 239, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 255, 191, 1, 0, 0, 0, 0, 160, 255, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 64, 252, 255, 223, 221, 221, 221, 221, 221, 253, 255, 221, 221, 221, 221, 125, 0, 0, 0, + 0, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, + 0, 176, 223, 250, 191, 170, 170, 170, 170, 251, 191, 170, 170, 170, 170, 251, 159, 0, 0, 0, + 0, 64, 24, 249, 143, 0, 0, 0, 0, 248, 143, 0, 0, 0, 0, 248, 159, 0, 0, 0, + 0, 0, 0, 249, 191, 170, 170, 170, 170, 251, 191, 170, 170, 170, 170, 251, 159, 0, 0, 0, + 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, + 0, 0, 0, 249, 191, 170, 170, 170, 170, 251, 191, 170, 170, 170, 170, 251, 159, 0, 0, 0, + 0, 0, 0, 249, 143, 0, 0, 0, 0, 248, 143, 0, 0, 0, 0, 248, 159, 0, 0, 0, + 0, 0, 0, 249, 191, 170, 170, 170, 170, 251, 191, 170, 170, 170, 170, 251, 159, 0, 0, 0, + 0, 0, 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, 0, 0, + 0, 0, 0, 249, 223, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 253, 159, 0, 0, 0, + 0, 0, 0, 83, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 53, 0, 0, 0, + 0, 81, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 21, 0, + 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 196, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 76, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 4, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 13, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 13, 0, 0, 0, + 0, 0, 0, 208, 239, 4, 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, 0, + 0, 0, 0, 208, 239, 86, 85, 85, 85, 85, 85, 85, 85, 85, 101, 254, 13, 0, 0, 0, + 0, 0, 0, 160, 189, 3, 0, 0, 0, 0, 0, 0, 0, 0, 48, 219, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 169, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 154, 4, 0, 0, + 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 112, 43, 0, 0, 249, 191, 0, 0, 200, 4, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 250, 223, 2, 0, 249, 191, 0, 64, 254, 143, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 228, 255, 11, 0, 249, 191, 0, 176, 255, 61, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 128, 255, 143, 0, 249, 191, 0, 248, 239, 6, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 0, 251, 239, 5, 249, 191, 64, 254, 159, 0, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 0, 211, 191, 2, 249, 191, 176, 255, 11, 0, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 0, 96, 7, 0, 249, 191, 0, 199, 3, 0, 176, 255, 8, 0, 0, + 0, 0, 128, 255, 173, 170, 170, 170, 170, 252, 223, 170, 170, 170, 170, 218, 255, 8, 0, 0, + 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 128, 255, 11, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 176, 255, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 144, 221, 221, 221, 221, 221, 221, 254, 239, 221, 221, 221, 221, 221, 221, 59, 0, 0, + 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 78, 0, 0, + 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 61, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 215, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 125, 0, + 0, 0, 32, 205, 6, 0, 80, 74, 0, 0, 80, 172, 0, 0, 0, 249, 10, 0, 0, 0, + 0, 0, 128, 255, 62, 0, 249, 191, 0, 0, 228, 255, 6, 0, 128, 255, 143, 0, 0, 0, + 0, 0, 210, 255, 10, 0, 229, 239, 5, 0, 160, 255, 45, 0, 0, 251, 255, 8, 0, 0, + 0, 0, 248, 239, 4, 0, 176, 255, 11, 0, 64, 254, 159, 0, 0, 210, 255, 110, 0, 0, + 0, 32, 253, 175, 0, 0, 96, 254, 111, 0, 0, 249, 239, 4, 0, 64, 254, 239, 4, 0, + 0, 144, 255, 78, 0, 0, 0, 252, 191, 0, 0, 211, 255, 10, 0, 0, 230, 255, 45, 0, + 0, 228, 255, 9, 0, 0, 0, 247, 175, 0, 0, 144, 175, 2, 0, 0, 128, 255, 10, 0, + 0, 32, 201, 2, 0, 0, 0, 114, 1, 0, 0, 16, 2, 0, 0, 0, 0, 138, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + } +}; +set_glyph(31, (const unsigned int*)font_glyph_unicode, (const unsigned char*)font_glyph_bitmaps); +} +}; + +#endif // MYFONTFACE_H diff --git a/Cpp_example/D09_plate_recognize/plate_recognize.cc b/Cpp_example/D09_plate_recognize/plate_recognize.cc new file mode 100644 index 0000000000000000000000000000000000000000..87ce6d8dbc92e921d462a8cbffad94e47fe37915 --- /dev/null +++ b/Cpp_example/D09_plate_recognize/plate_recognize.cc @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include +#include "myfontface.h" + +using namespace std; +using namespace std::chrono; + +// OCR字符集配置 +const string plate_chars[68] = { + "京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", + "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂", "琼", "川", "贵", + "云", "藏", "陕", "甘", "青", "宁", "新", "0", "1", "2", "3", "4", + "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", + "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", "I", "O", "-"}; + +ncnn::Net ocr_net; + +bool InitOCRModel(const string& param_path, const string& model_path) { + return ocr_net.load_param(param_path.c_str()) == 0 && + ocr_net.load_model(model_path.c_str()) == 0; +} + +string RecognizePlate(cv::Mat plate_img) { + cv::resize(plate_img, plate_img, cv::Size(94, 24)); + + ncnn::Mat in = ncnn::Mat::from_pixels(plate_img.data, + ncnn::Mat::PIXEL_BGR, + plate_img.cols, + plate_img.rows); + + const float mean[3] = {127.5f, 127.5f, 127.5f}; + const float norm[3] = {0.0078125f, 0.0078125f, 0.0078125f}; + in.substract_mean_normalize(mean, norm); + + ncnn::Extractor ex = ocr_net.create_extractor(); + ex.input("input.1", in); + + ncnn::Mat feat; + ex.extract("131", feat); + + string license; + vector preb; + for (int c = 0; c < feat.c; c++) { + const float* data = feat.channel(c); + for (int w = 0; w < feat.w; w++) { + float max_val = -FLT_MAX; + int max_idx = 0; + for (int h = 0; h < feat.h; h++) { + float val = data[w + h * feat.w]; + if (val > max_val) { + max_val = val; + max_idx = h; + } + } + preb.push_back(max_idx); + } + } + + // 后处理去重 + vector valid_labels; + int prev = -1; + for (size_t i = 0; i < preb.size(); ++i) { + if (preb[i] != 67 && preb[i] != prev) { + valid_labels.push_back(preb[i]); + } + prev = preb[i]; + } + + for (int idx : valid_labels) { + license += plate_chars[idx]; + } + + return license.empty() ? "UNKNOWN" : license; +} + +int main(int argc, char** argv) { + // 参数验证 + if (argc < 4 || argc > 5) { + cerr << "Usage: " << argv[0] + << " [image_path]\n" + << "Example:\n" + << " Realtime: " << argv[0] << " det_model ocr.param ocr.bin\n" + << " Image: " << argv[0] << " det_model ocr.param ocr.bin test.jpg\n"; + return 1; + } + + // 初始化检测模型 + lockzhiner_vision_module::vision::PaddleDet detector; + if (!detector.Initialize(argv[1])) { + cerr << "Failed to load detection model: " << argv[1] << endl; + return 1; + } + + // 初始化OCR模型 + if (!InitOCRModel(argv[2], argv[3])) { + cerr << "Failed to load OCR model: " << argv[2] << " and " << argv[3] << endl; + return 1; + } + MyFontFace myfont; + // 设置文字参数 + double font_scale = 0.6; + int thickness = 1; + + // 图片处理模式 + if (argc == 5) { + cv::Mat image = cv::imread(argv[4]); + if (image.empty()) { + cerr << "Failed to read image: " << argv[4] << endl; + return 1; + } + + auto results = detector.Predict(image); + // 可视化并显示结果 + cv::Mat output_image = image.clone(); + for (const auto& det : results) { + cv::Rect rect( + static_cast(det.box.x), + static_cast(det.box.y), + static_cast(det.box.width), + static_cast(det.box.height) + ); + cv::rectangle( + output_image, + rect, + cv::Scalar(0, 255, 0), + 1, + cv::LINE_AA + ); + } + cout << "\n===== 检测到 " << results.size() << " 个车牌 =====" << endl; + + for (size_t i = 0; i < results.size(); ++i) { + const auto& det = results[i]; + cv::Rect roi(det.box.x, det.box.y, det.box.width, det.box.height); + roi &= cv::Rect(0, 0, image.cols, image.rows); + + if (roi.area() > 0) { + cv::Mat plate_img = image(roi); + cv::imshow("DetectionSeg Result", plate_img); + string plate_num = RecognizePlate(plate_img); + // 左上角偏移 + cv::Point text_org(roi.x + 2, roi.y - 2); + // 先绘制黑色背景提升可读性 + cv::putText(output_image, plate_num, text_org, + cv::Scalar(0, 0, 0), // 颜色 + myfont, // 字体对象 + font_scale, // 字体尺寸 + thickness + 2, // 线宽 + cv::PutTextFlags::PUT_TEXT_ALIGN_LEFT, // 对齐方式 + cv::Range(0, 0)); // 自动换行范围(0表示不换行) + cv::putText(output_image, plate_num, text_org, cv::Scalar(127, 0, 127), myfont, 10); + + cout << "车牌 " << i+1 << ":\n" + << " 位置: [" << roi.x << ", " << roi.y + << ", " << roi.width << "x" << roi.height << "]\n" + << " 置信度: " << det.score << "\n" + << " 识别结果: " << plate_num << "\n" << endl; + + cv::imshow("Detection Result", output_image); + } + } + cv::waitKey(0); + } + // 实时摄像头模式 + else { + // 初始化设备连接 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + if (!cap.isOpened()) { + cerr << "Error: Could not open camera" << endl; + return 1; + } + + cout << "实时识别模式启动 (按ESC退出)..." << endl; + + cv::Mat frame; + while (true) { + cap >> frame; + if (frame.empty()) continue; + + auto results = detector.Predict(frame); + + cv::Mat display_frame = frame.clone(); + for (const auto& det : results) { + cv::Rect rect( + static_cast(det.box.x), + static_cast(det.box.y), + static_cast(det.box.width), + static_cast(det.box.height) + ); + cv::rectangle( + display_frame, + rect, + cv::Scalar(0, 255, 0), + 1, + cv::LINE_AA + ); + } + + // 添加时间戳 + auto now = system_clock::now(); + time_t now_time = system_clock::to_time_t(now); + cout << "\n===== " << ctime(&now_time) + << "检测到 " << results.size() << " 个车牌 =====" << endl; + + for (const auto& det : results) { + cv::Rect roi(det.box.x, det.box.y, det.box.width, det.box.height); + roi &= cv::Rect(0, 0, frame.cols, frame.rows); + + if (roi.area() > 0) { + cv::Mat plate_img = frame(roi); + string plate_num = RecognizePlate(plate_img); + // 左上角偏移 + cv::Point text_org(roi.x + 2, roi.y - 2); + // 先绘制黑色背景提升可读性 + cv::putText(display_frame, plate_num, text_org, + cv::Scalar(0, 0, 0), // 颜色 + myfont, // 字体对象 + font_scale, // 字体尺寸 + thickness + 2, // 线宽 + cv::PutTextFlags::PUT_TEXT_ALIGN_LEFT, // 对齐方式 + cv::Range(0, 0)); // 自动换行范围(0表示不换行) + cv::putText(display_frame, plate_num, text_org, cv::Scalar(127, 0, 127), myfont, 10); + + cout << "[实时结果] 位置(" << roi.x << "," << roi.y + << ") 识别: " << plate_num + << " (置信度: " << det.score << ")" << endl; + } + } + edit.Print(display_frame); + // 退出检测 + if (cv::waitKey(1) == 27) break; + } + } + return 0; +} \ No newline at end of file diff --git a/README.md b/README.md index 373bf127cf7977ad7ea00fb11112841be7f007a2..7ced98bf942edfeeb3b0edcb55745981f1bfa10a 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,19 @@ OCR(Optical Character Recognition,光学字符识别)是一种将图像中 * [凌智视觉模块 OCR 文字检测](./Cpp_example/D06_ocr_text_detection/README.md) * [凌智视觉模块 OCR 综合示例](./Cpp_example/D07_ocr_synthesis/README.md) -## 👍 C++ 开发案例 +### 👍 图像分割案例 + +图像分割是计算机视觉中的关键技术,通过像素级分类将图像划分为多个区域或对象,广泛应用于医学影像、自动驾驶等领域,助力智能分析与场景理解。 + +* [凌智视觉模块图像分割](./Cpp_example/D08_pp_humanseg/README.md) + +### 👍 车牌识别案例 + +车牌识别基于计算机视觉技术,通过图像处理和深度学习精准定位并提取车牌字符,应用于交通管理、停车场收费及安防系统,提升车辆识别效率与自动化水平。 + +* [凌智视觉模块车牌识别](./Cpp_example/D09_plate_recognize/README.md) + +## 🏀 C++ 开发案例 C++ 开发案例以A、B、C、D进行不同类别进行分类,方便初学者进行使用和二次开发。 * `A01 - A99`: 基础外设类 * `B01 - B99`: OpenCV基础函数类 @@ -163,6 +175,8 @@ C++ 开发案例以A、B、C、D进行不同类别进行分类,方便初学者 | D05 | 神经网络类 | OCR Text Recognition | [OCR文字识别](./Cpp_example/D05_ocr_text_recognition/README.md) | | D06 | 神经网络类 | OCR Text Detection | [OCR文字检测](./Cpp_example/D06_ocr_text_detection/README.md) | | D07 | 神经网络类 | OCR Synthesis | [OCR综合示例](./Cpp_example/D07_ocr_synthesis/README.md) | +| D08 | 神经网络类 | PPHumanSeg | [图像分割](./Cpp_example/D08_pp_humanseg/README.md) | +| D09 | 神经网络类 | Plate Recognition | [车牌识别](./Cpp_example/D09_plate_recognize/README.md) | ## 🐛 Bug反馈 @@ -198,4 +212,4 @@ C++ 开发案例以A、B、C、D进行不同类别进行分类,方便初学者 ## 📜 开源协议 -Lockzhiner Vision Module 全系列仓库遵循 [Apache License Version 2.0](./LICENSE)。 +Lockzhiner Vision Module 全系列仓库遵循 [Apache License Version 2.0](./LICENSE)。 \ No newline at end of file diff --git a/docs/introductory_tutorial/cpp_development_environment.md b/docs/introductory_tutorial/cpp_development_environment.md index 1327652dbec1d999371d28435aaed5463e4a21ba..b68b7fc6deff1ff1fcb1e3525ceb3c1616ffc79f 100644 --- a/docs/introductory_tutorial/cpp_development_environment.md +++ b/docs/introductory_tutorial/cpp_development_environment.md @@ -214,15 +214,35 @@ cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule git pull ``` -### 6.4 下载/更新 OpenCV Mobile 库 +### 6.4 下载/更新库 -执行以下命令来安装最新的 OpenCV Mobile 库(会删除旧版本) +执行以下命令来安装最新的库(会删除旧版本) ```bash cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule rm -rf opencv-mobile-4.10.0-lockzhiner-vision-module.zip -wget https://gitee.com/LockzhinerAI/opencv-mobile/releases/download/v0.0.0/opencv-mobile-4.10.0-lockzhiner-vision-module.zip +wget https://gitee.com/LockzhinerAI/opencv-mobile/releases/download/v29/opencv-mobile-4.10.0-lockzhiner-vision-module.zip unzip -qo opencv-mobile-4.10.0-lockzhiner-vision-module.zip -d third_party +# 下载 fmt +rm -rf fmt-11.0.2-lockzhiner-vision-module.zip +wget https://gitee.com/LockzhinerAI/fmt/releases/download/11.0.2/fmt-11.0.2-lockzhiner-vision-module.zip +unzip -qo fmt-11.0.2-lockzhiner-vision-module.zip -d third_party +# 下载 pybind11 +rm -rf pybind11-v2.13.5.zip +wget https://gitee.com/LockzhinerAI/pybind11/releases/download/v2.13.5/pybind11-v2.13.5.zip +unzip -qo pybind11-v2.13.5.zip -d third_party +# 下载 python3.11 +rm -rf python3.11-lockzhiner-vision-module.zip +wget https://gitee.com/LockzhinerAI/pybind11/releases/download/v2.13.5/python3.11-lockzhiner-vision-module.zip +unzip -qo python3.11-lockzhiner-vision-module.zip -d third_party +# 下载 zxing-cpp +rm -rf zxing-cpp-v2.2.1-lockzhiner-vision-module.zip +wget https://gitee.com/LockzhinerAI/zxing-cpp/releases/download/v2.2.1/zxing-cpp-v2.2.1-lockzhiner-vision-module.zip +unzip -qo zxing-cpp-v2.2.1-lockzhiner-vision-module.zip -d third_party +# 下载 NCNN +rm -rf ncnn-20240820-lockzhiner-vision-module.zip +wget https://gitee.com/LockzhinerAI/ncnn/releases/download/20240820/ncnn-20240820-lockzhiner-vision-module.zip +unzip -qo ncnn-20240820-lockzhiner-vision-module.zip -d third_party ``` ### 6.5 下载/更新 LockzhinerVisionModule SDK