From 9e591fba3775455845c0775b9124c3db33912968 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Fri, 5 Aug 2022 09:13:44 +0300 Subject: [PATCH 01/99] Add support for av1 and update fourcc mapping to deal with all combinations of expected codecs. --- modules/cudacodec/src/ffmpeg_video_source.cpp | 16 ++++++++++++++-- modules/cudacodec/test/test_video.cpp | 13 ++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/modules/cudacodec/src/ffmpeg_video_source.cpp b/modules/cudacodec/src/ffmpeg_video_source.cpp index 436a18eb39..815e5c8ce6 100644 --- a/modules/cudacodec/src/ffmpeg_video_source.cpp +++ b/modules/cudacodec/src/ffmpeg_video_source.cpp @@ -72,7 +72,9 @@ Codec FourccToCodec(int codec) switch (codec) { case CV_FOURCC_MACRO('m', 'p', 'e', 'g'): // fallthru + case CV_FOURCC_MACRO('m', 'p', 'g', '1'): // fallthru case CV_FOURCC_MACRO('M', 'P', 'G', '1'): return MPEG1; + case CV_FOURCC_MACRO('m', 'p', 'g', '2'): // fallthru case CV_FOURCC_MACRO('M', 'P', 'G', '2'): return MPEG2; case CV_FOURCC_MACRO('X', 'V', 'I', 'D'): // fallthru case CV_FOURCC_MACRO('m', 'p', '4', 'v'): // fallthru @@ -85,8 +87,18 @@ Codec FourccToCodec(int codec) case CV_FOURCC_MACRO('h', '2', '6', '5'): // fallthru case CV_FOURCC_MACRO('h', 'e', 'v', 'c'): return HEVC; case CV_FOURCC_MACRO('M', 'J', 'P', 'G'): return JPEG; - case CV_FOURCC_MACRO('V', 'P', '8', '0'): return VP8; - case CV_FOURCC_MACRO('V', 'P', '9', '0'): return VP9; + case CV_FOURCC_MACRO('v', 'p', '8', '0'): // fallthru + case CV_FOURCC_MACRO('V', 'P', '8', '0'): // fallthru + case CV_FOURCC_MACRO('v', 'p', '0', '8'): // fallthru + case CV_FOURCC_MACRO('V', 'P', '0', '8'): return VP8; + case CV_FOURCC_MACRO('v', 'p', '9', '0'): // fallthru + case CV_FOURCC_MACRO('V', 'P', '9', '0'): // fallthru + case CV_FOURCC_MACRO('V', 'P', '0', '9'): // fallthru + case CV_FOURCC_MACRO('v', 'p', '0', '9'): return VP9; + case CV_FOURCC_MACRO('a', 'v', '1', '0'): // fallthru + case CV_FOURCC_MACRO('A', 'V', '1', '0'): // fallthru + case CV_FOURCC_MACRO('a', 'v', '0', '1'): // fallthru + case CV_FOURCC_MACRO('A', 'V', '0', '1'): return AV1; default: break; } diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index e37833ef8b..216d0bc0e5 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -185,6 +185,11 @@ CUDA_TEST_P(Video, Reader) if (GET_PARAM(1) == "cv/video/768x576.avi" && !videoio_registry::hasBackend(CAP_FFMPEG)) throw SkipTestException("FFmpeg backend not found"); +#ifdef _WIN32 // handle old FFmpeg backend + if (GET_PARAM(1) == "/cv/tracking/faceocc2/data/faceocc2.webm") + throw SkipTestException("Feature not yet supported by Windows FFmpeg shared library!"); +#endif + const std::vector> formatsToChannels = { {cudacodec::ColorFormat::GRAY,1}, {cudacodec::ColorFormat::BGR,3}, @@ -196,7 +201,7 @@ CUDA_TEST_P(Video, Reader) cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); cv::cudacodec::FormatInfo fmt = reader->format(); cv::cuda::GpuMat frame; - for (int i = 0; i < 100; i++) + for (int i = 0; i < 10; i++) { // request a different colour format for each frame const std::pair< cudacodec::ColorFormat, int>& formatToChannels = formatsToChannels[i % formatsToChannels.size()]; @@ -426,8 +431,10 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckSet, testing::Combine( ALL_DEVICES, testing::Values("highgui/video/big_buck_bunny.mp4"))); -#define VIDEO_SRC_R "highgui/video/big_buck_bunny.mp4", "cv/video/768x576.avi", "cv/video/1920x1080.avi", "highgui/video/big_buck_bunny.avi", \ - "highgui/video/big_buck_bunny.h264", "highgui/video/big_buck_bunny.h265", "highgui/video/big_buck_bunny.mpg" +#define VIDEO_SRC_R "highgui/video/big_buck_bunny.mp4", "cv/video/768x576.avi", "cv/video/1920x1080.avi", "highgui/video/big_buck_bunny.avi", \ + "highgui/video/big_buck_bunny.h264", "highgui/video/big_buck_bunny.h265", "highgui/video/big_buck_bunny.mpg", \ + "highgui/video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", "highgui/video/sample_322x242_15frames.yuv420p.libaom-av1.mp4", \ + "cv/tracking/faceocc2/data/faceocc2.webm" INSTANTIATE_TEST_CASE_P(CUDA_Codec, Video, testing::Combine( ALL_DEVICES, testing::Values(VIDEO_SRC_R))); From badef606c2ce0f4f7ba6bfa7880b27add8dc9451 Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Mon, 8 Aug 2022 21:54:35 +0200 Subject: [PATCH 02/99] Remove unused size of get_subbox_chart_physical() The argument size of get_subbox_chart_physical() is unused. get_subbox_chart_physical() is called only in one place. The local variable h could overflow the cast to int and lead to a SIGILL in some environments. --- modules/mcc/src/checker_detector.cpp | 7 ++----- modules/mcc/src/checker_detector.hpp | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/mcc/src/checker_detector.cpp b/modules/mcc/src/checker_detector.cpp index f5ce44ed56..b84c885d6e 100644 --- a/modules/mcc/src/checker_detector.cpp +++ b/modules/mcc/src/checker_detector.cpp @@ -802,8 +802,7 @@ void CCheckerDetectorImpl:: // get physical char box model std::vector chartPhy; - cv::Size size_box_phy; - get_subbox_chart_physical(points, chartPhy, size_box_phy); + get_subbox_chart_physical(points, chartPhy); // Find the perspective transformation that brings current chart to rectangular form Matx33f ccT = cv::getPerspectiveTransform(points, chartPhy); @@ -1101,7 +1100,7 @@ void CCheckerDetectorImpl:: } void CCheckerDetectorImpl:: - get_subbox_chart_physical(const std::vector &points, std::vector &chartPhy, cv::Size &size) + get_subbox_chart_physical(const std::vector &points, std::vector &chartPhy) { float w, h; cv::Point2f v1 = points[1] - points[0]; @@ -1117,8 +1116,6 @@ void CCheckerDetectorImpl:: chartPhy[1] = cv::Point2f(w, 0); chartPhy[2] = cv::Point2f(w, h); chartPhy[3] = cv::Point2f(0, h); - - size = cv::Size((int)w, (int)h); } void CCheckerDetectorImpl:: diff --git a/modules/mcc/src/checker_detector.hpp b/modules/mcc/src/checker_detector.hpp index 83fc55ad76..75b1644a51 100644 --- a/modules/mcc/src/checker_detector.hpp +++ b/modules/mcc/src/checker_detector.hpp @@ -164,8 +164,7 @@ class CCheckerDetectorImpl : public CCheckerDetector private: // methods aux void get_subbox_chart_physical( const std::vector &points, - std::vector &chartPhy, - cv::Size &size); + std::vector &chartPhy); void reduce_array( const std::vector &x, From cd97cad52dd3586efaed73f772c148cd7ecfe257 Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Mon, 8 Aug 2022 22:41:05 +0200 Subject: [PATCH 03/99] Use T as temp var type in polyanticlockwise() To match the input data type. Cast inner computation to double to prevent int overflows for any input type. --- modules/mcc/src/common.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/mcc/src/common.hpp b/modules/mcc/src/common.hpp index aa4c383f55..224459eb1a 100644 --- a/modules/mcc/src/common.hpp +++ b/modules/mcc/src/common.hpp @@ -79,10 +79,10 @@ void polyanticlockwise(std::vector &points) // Sort the points in anti-clockwise order // Trace a line between the first and second point. // If the third point is at the right side, then the points are anti-clockwise - cv::Point v1 = points[1] - points[0]; - cv::Point v2 = points[2] - points[0]; + T v1 = points[1] - points[0]; + T v2 = points[2] - points[0]; - double o = (v1.x * v2.y) - (v1.y * v2.x); + double o = ((double)v1.x * v2.y) - ((double)v1.y * v2.x); if (o < 0.0) //if the third point is in the left side, then sort in anti-clockwise order std::swap(points[1], points[3]); @@ -93,10 +93,10 @@ void polyclockwise(std::vector &points) // Sort the points in clockwise order // Trace a line between the first and second point. // If the third point is at the right side, then the points are clockwise - cv::Point v1 = points[1] - points[0]; - cv::Point v2 = points[2] - points[0]; + T v1 = points[1] - points[0]; + T v2 = points[2] - points[0]; - double o = (v1.x * v2.y) - (v1.y * v2.x); + double o = ((double)v1.x * v2.y) - ((double)v1.y * v2.x); if (o > 0.0) //if the third point is in the left side, then sort in clockwise order std::swap(points[1], points[3]); From 9685382eb6eae24b5183fa2761e68ce7816f50be Mon Sep 17 00:00:00 2001 From: Kishor Date: Sun, 21 Aug 2022 11:32:36 +0530 Subject: [PATCH 04/99] Set window_size to 1 if round down results in 0. Without this fix, we will get a floating point error in calCoherence method due to divide by zeor --- modules/barcode/src/detector/bardetect.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/barcode/src/detector/bardetect.cpp b/modules/barcode/src/detector/bardetect.cpp index 72990b3cc8..ec0806970c 100644 --- a/modules/barcode/src/detector/bardetect.cpp +++ b/modules/barcode/src/detector/bardetect.cpp @@ -86,6 +86,9 @@ void Detect::localization() for (const float scale:SCALE_LIST) { window_size = cvRound(min_side * scale); + if(window_size == 0) { + window_size = 1; + } calCoherence(window_size); barcodeErode(); regionGrowing(window_size); From 4e4e3c34657246755452c2110ff559e6762a68cd Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Wed, 24 Aug 2022 00:41:19 +0530 Subject: [PATCH 05/99] sfm: Fix redefinition error in 32 bit mingw-w64 environment sincos function is defined by mingw-w64 for both 32 bit and 64 bit environments. Previously, sincos function was hidden for 64 bit mingw-w64 with __MINGW64__ macro. This change also hides the sincos definition for 32 bit mingw-w64 with __MINGW32__ macro. --- modules/sfm/src/libmv_light/libmv/numeric/numeric.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sfm/src/libmv_light/libmv/numeric/numeric.h b/modules/sfm/src/libmv_light/libmv/numeric/numeric.h index dde7e816f1..9e7927e0bb 100644 --- a/modules/sfm/src/libmv_light/libmv/numeric/numeric.h +++ b/modules/sfm/src/libmv_light/libmv/numeric/numeric.h @@ -33,7 +33,7 @@ #include #include -#if !defined(__MINGW64__) +#if !defined(__MINGW32__) # if defined(_WIN32) || defined(__APPLE__) || \ defined(__FreeBSD__) || defined(__NetBSD__) static void sincos(double x, double *sinx, double *cosx) { @@ -41,7 +41,7 @@ static void sincos(double x, double *sinx, double *cosx) { *cosx = cos(x); } # endif -#endif // !__MINGW64__ +#endif // !__MINGW32__ #if (defined(_WIN32)) && !defined(__MINGW32__) inline long lround(double d) { From 66809ea92952cda61f55c2e4ce70ae73c1c3c7ac Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Thu, 25 Aug 2022 14:41:57 +0300 Subject: [PATCH 06/99] Fix memory leak caused by incorrect use of smart pointer. --- modules/cudacodec/src/frame_queue.cpp | 26 ++++++++++++++++++++++++-- modules/cudacodec/src/frame_queue.hpp | 13 +++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/modules/cudacodec/src/frame_queue.cpp b/modules/cudacodec/src/frame_queue.cpp index 6fecff6b8c..fdbe50f8dd 100644 --- a/modules/cudacodec/src/frame_queue.cpp +++ b/modules/cudacodec/src/frame_queue.cpp @@ -46,10 +46,32 @@ #ifdef HAVE_NVCUVID RawPacket::RawPacket(const unsigned char* _data, const size_t _size, const bool _containsKeyFrame) : size(_size), containsKeyFrame(_containsKeyFrame) { - data = cv::makePtr(new unsigned char[size]); - memcpy(*data, _data, size); + data = new unsigned char[size]; + memcpy(data, _data, size); }; +RawPacket::~RawPacket() { + if (data) delete[] data; +} + +RawPacket::RawPacket(const RawPacket& other) : RawPacket(other.data, other.size, other.containsKeyFrame) { +} + +RawPacket::RawPacket(RawPacket&& other) noexcept : data(std::exchange(other.data, nullptr)), size(std::exchange(other.size, 0)), + containsKeyFrame(std::exchange(other.containsKeyFrame, false)) { +} + +RawPacket& RawPacket::operator=(const RawPacket& other) { + return *this = RawPacket(other); +} + +RawPacket& RawPacket::operator=(RawPacket&& other) noexcept { + std::swap(data, other.data); + size = other.size; + containsKeyFrame = other.containsKeyFrame; + return *this; +} + cv::cudacodec::detail::FrameQueue::~FrameQueue() { if (isFrameInUse_) delete[] isFrameInUse_; diff --git a/modules/cudacodec/src/frame_queue.hpp b/modules/cudacodec/src/frame_queue.hpp index b6e0b06bcc..d7f0696af5 100644 --- a/modules/cudacodec/src/frame_queue.hpp +++ b/modules/cudacodec/src/frame_queue.hpp @@ -50,11 +50,16 @@ class RawPacket { public: RawPacket(const unsigned char* _data, const size_t _size = 0, const bool _containsKeyFrame = false); - unsigned char* Data() const { return *data; } - size_t size; - bool containsKeyFrame; + ~RawPacket(); + RawPacket(const RawPacket& other); + RawPacket(RawPacket&& other) noexcept; + RawPacket& operator=(const RawPacket& other); + RawPacket& operator=(RawPacket&& other) noexcept; + unsigned char* Data() const { return data; } + size_t size = 0; + bool containsKeyFrame = false; private: - cv::Ptr data = 0; + unsigned char* data = 0; }; namespace cv { namespace cudacodec { namespace detail { From 087a1004656896a037e119d4fae491b05fa00b1a Mon Sep 17 00:00:00 2001 From: Mathijs de Groot Date: Fri, 26 Aug 2022 23:25:50 +0200 Subject: [PATCH 07/99] Merge pull request #3313 from Qubiz:Qubiz-find-hdr-parser-path-using-OpenCV_SOURCE_DIR matlab: Use OpenCV_SOURCE_DIR instead of CMAKE_SOURCE_DIR to find the modules root * Set HDR_PARSER_PATH using OpenCV_SOURCE_DIR var This sets the `HDR_PARSER_PATH` variable using `OpenCV_SOURCE_DIR` instead of using `CMAKE_SOURCE_DIR`. This allows for more flexibility in how the user's project can be structured. * Find OpenCV module root using OpenCV_SOURCE_DIR --- modules/matlab/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/matlab/CMakeLists.txt b/modules/matlab/CMakeLists.txt index 011a81cbd5..b18d025438 100644 --- a/modules/matlab/CMakeLists.txt +++ b/modules/matlab/CMakeLists.txt @@ -120,7 +120,7 @@ execute_process(COMMAND git log -1 --pretty=%H OUTPUT_VARIABLE GIT_COMMIT ERROR_ string(REGEX REPLACE "(\r?\n)+$" "" GIT_COMMIT "${GIT_COMMIT}") # set the path to the C++ header and doc parser, and template engine -set(HDR_PARSER_PATH ${CMAKE_SOURCE_DIR}/modules/python/src2) +set(HDR_PARSER_PATH ${OpenCV_SOURCE_DIR}/modules/python/src2) # set mex compiler options prepend("-I" MEX_INCLUDE_DIRS ${CMAKE_BINARY_DIR}) @@ -234,7 +234,7 @@ add_custom_command( COMMAND ${PYTHON_DEFAULT_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab.py --hdrparser ${HDR_PARSER_PATH} - --moduleroot ${CMAKE_SOURCE_DIR}/modules ${OPENCV_EXTRA_MODULES_PATH} + --moduleroot ${OpenCV_SOURCE_DIR}/modules ${OPENCV_EXTRA_MODULES_PATH} --modules ${opencv_modules} --extra ${opencv_extra_hdrs} --outdir ${CMAKE_CURRENT_BINARY_DIR} From 2b0bbb581dd9791a043155711ae75a117635cde3 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Fri, 29 Jul 2022 00:15:18 +0300 Subject: [PATCH 08/99] add scaleFactor/setScaleFactor/getScaleFactor add test add dnn data search --- modules/wechat_qrcode/CMakeLists.txt | 2 +- .../include/opencv2/wechat_qrcode.hpp | 19 +++++++++-- modules/wechat_qrcode/src/wechat_qrcode.cpp | 20 ++++++++--- modules/wechat_qrcode/test/test_main.cpp | 17 +++++++++- modules/wechat_qrcode/test/test_qrcode.cpp | 33 +++++++++++++++++++ 5 files changed, 83 insertions(+), 8 deletions(-) diff --git a/modules/wechat_qrcode/CMakeLists.txt b/modules/wechat_qrcode/CMakeLists.txt index d38d9cce75..a15c43377d 100644 --- a/modules/wechat_qrcode/CMakeLists.txt +++ b/modules/wechat_qrcode/CMakeLists.txt @@ -1,5 +1,5 @@ set(the_description "WeChat QR code Detector") -ocv_define_module(wechat_qrcode opencv_core opencv_imgproc opencv_dnn WRAP java objc python js) +ocv_define_module(wechat_qrcode opencv_core opencv_imgproc opencv_objdetect opencv_dnn WRAP java objc python js) # iconv support isn't automatic on some systems if(CMAKE_VERSION VERSION_GREATER "3.11") diff --git a/modules/wechat_qrcode/include/opencv2/wechat_qrcode.hpp b/modules/wechat_qrcode/include/opencv2/wechat_qrcode.hpp index 7400d2b088..676104cd02 100644 --- a/modules/wechat_qrcode/include/opencv2/wechat_qrcode.hpp +++ b/modules/wechat_qrcode/include/opencv2/wechat_qrcode.hpp @@ -48,8 +48,23 @@ class CV_EXPORTS_W WeChatQRCode { * empty if not found. * @return list of decoded string. */ - CV_WRAP std::vector detectAndDecode(InputArray img, - OutputArrayOfArrays points = noArray()); + CV_WRAP std::vector detectAndDecode(InputArray img, OutputArrayOfArrays points = noArray()); + + /** + * @brief set scale factor + * QR code detector use neural network to detect QR. + * Before running the neural network, the input image is pre-processed by scaling. + * By default, the input image is scaled to an image with an area of 160000 pixels. + * The scale factor allows to use custom scale the input image: + * width = scaleFactor*width + * height = scaleFactor*width + * + * scaleFactor valuse must be > 0 and <= 1, otherwise the scaleFactor value is set to -1 + * and use default scaled to an image with an area of 160000 pixels. + */ + CV_WRAP void setScaleFactor(float _scalingFactor); + + CV_WRAP float getScaleFactor(); protected: class Impl; diff --git a/modules/wechat_qrcode/src/wechat_qrcode.cpp b/modules/wechat_qrcode/src/wechat_qrcode.cpp index 7a4037c9c6..df5e8bbcc9 100644 --- a/modules/wechat_qrcode/src/wechat_qrcode.cpp +++ b/modules/wechat_qrcode/src/wechat_qrcode.cpp @@ -43,6 +43,7 @@ class WeChatQRCode::Impl { std::shared_ptr detector_; std::shared_ptr super_resolution_model_; bool use_nn_detector_, use_nn_sr_; + float scaleFactor = -1.f; }; WeChatQRCode::WeChatQRCode(const String& detector_prototxt_path, @@ -109,6 +110,17 @@ vector WeChatQRCode::detectAndDecode(InputArray img, OutputArrayOfArrays points.assign(tmp_points); } return ret; +} + +void WeChatQRCode::setScaleFactor(float _scaleFactor) { + if (_scaleFactor > 0 && _scaleFactor <= 1.f) + p->scaleFactor = _scaleFactor; + else + p->scaleFactor = -1.f; +}; + +float WeChatQRCode::getScaleFactor() { + return p->scaleFactor; }; vector WeChatQRCode::Impl::decode(const Mat& img, vector& candidate_points, @@ -173,11 +185,11 @@ int WeChatQRCode::Impl::applyDetector(const Mat& img, vector& points) { int img_w = img.cols; int img_h = img.rows; + const float targetArea = 400.f * 400.f; // hard code input size - int minInputSize = 400; - float resizeRatio = sqrt(img_w * img_h * 1.0 / (minInputSize * minInputSize)); - int detect_width = img_w / resizeRatio; - int detect_height = img_h / resizeRatio; + const float tmpScaleFactor = scaleFactor == -1.f ? sqrt(targetArea / (img_w * img_h)) : scaleFactor; + int detect_width = img_w * tmpScaleFactor; + int detect_height = img_h * tmpScaleFactor; points = detector_->forward(img, detect_width, detect_height); diff --git a/modules/wechat_qrcode/test/test_main.cpp b/modules/wechat_qrcode/test/test_main.cpp index 47f2ebac93..02b3a26f50 100644 --- a/modules/wechat_qrcode/test/test_main.cpp +++ b/modules/wechat_qrcode/test/test_main.cpp @@ -11,4 +11,19 @@ #include #endif -CV_TEST_MAIN("cv") +static +void initTests() +{ +#ifdef HAVE_OPENCV_DNN + const char* extraTestDataPath = +#ifdef WINRT + NULL; +#else + getenv("OPENCV_DNN_TEST_DATA_PATH"); +#endif + if (extraTestDataPath) + cvtest::addDataSearchPath(extraTestDataPath); +#endif // HAVE_OPENCV_DNN +} + +CV_TEST_MAIN("cv", initTests()) diff --git a/modules/wechat_qrcode/test/test_qrcode.cpp b/modules/wechat_qrcode/test/test_qrcode.cpp index 5de65338f0..cbd1b2fb35 100644 --- a/modules/wechat_qrcode/test/test_qrcode.cpp +++ b/modules/wechat_qrcode/test/test_qrcode.cpp @@ -6,6 +6,7 @@ // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. #include "test_precomp.hpp" +#include "opencv2/objdetect.hpp" namespace opencv_test { namespace { @@ -289,5 +290,37 @@ INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Monitor, testing::ValuesIn(qrcode INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Curved, testing::ValuesIn(qrcode_images_curved)); // INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Multi, testing::ValuesIn(qrcode_images_multiple)); +TEST(Objdetect_QRCode_Big, regression) { + string path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, path_sr_caffemodel; + string model_version = "_2021-01"; + path_detect_prototxt = findDataFile("dnn/wechat"+model_version+"/detect.prototxt", false); + path_detect_caffemodel = findDataFile("dnn/wechat"+model_version+"/detect.caffemodel", false); + path_sr_prototxt = findDataFile("dnn/wechat"+model_version+"/sr.prototxt", false); + path_sr_caffemodel = findDataFile("dnn/wechat"+model_version+"/sr.caffemodel", false); + + auto detector = wechat_qrcode::WeChatQRCode(path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, + path_sr_caffemodel); + + const cv::String expect_msg = "OpenCV"; + QRCodeEncoder::Params params; + params.version = 4; // 33x33 + Ptr qrcode_enc = cv::QRCodeEncoder::create(params); + Mat qrImage; + qrcode_enc->encode(expect_msg, qrImage); + Mat largeImage(4032, 3024, CV_8UC1); + const int pixInBlob = 4; + Size qrSize = Size((21+(params.version-1)*4)*pixInBlob,(21+(params.version-1)*4)*pixInBlob); + Mat roiImage = largeImage(Rect((largeImage.cols - qrSize.width)/2, (largeImage.rows - qrSize.height)/2, + qrSize.width, qrSize.height)); + cv::resize(qrImage, roiImage, qrSize, 1., 1., INTER_NEAREST); + + vector points; + detector.setScaleFactor(0.25f); + auto decoded_info = detector.detectAndDecode(largeImage, points); + ASSERT_EQ(1ull, decoded_info.size()); + ASSERT_EQ(expect_msg, decoded_info[0]); +} + + } // namespace } // namespace opencv_test From cee17e4a7e2b1ad69d3c4b67bf086c7c774f2d58 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 31 Aug 2022 11:05:27 +0300 Subject: [PATCH 09/99] fix output qr corners (accuracy+order), add test --- modules/wechat_qrcode/src/decodermgr.cpp | 10 +++- modules/wechat_qrcode/src/decodermgr.hpp | 2 +- modules/wechat_qrcode/src/wechat_qrcode.cpp | 16 +++++-- modules/wechat_qrcode/test/test_qrcode.cpp | 52 +++++++++++++++++++++ 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/modules/wechat_qrcode/src/decodermgr.cpp b/modules/wechat_qrcode/src/decodermgr.cpp index 06706eed2f..d4b8edf6f4 100644 --- a/modules/wechat_qrcode/src/decodermgr.cpp +++ b/modules/wechat_qrcode/src/decodermgr.cpp @@ -18,7 +18,7 @@ using zxing::Result; using zxing::UnicomBlock; namespace cv { namespace wechat_qrcode { -int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result) { +int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, vector& points) { int width = src.cols; int height = src.rows; if (width <= 20 || height <= 20) @@ -46,6 +46,14 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result) { int ret = TryDecode(source, zx_result); if (!ret) { result = zx_result->getText()->getText(); + auto result_points = zx_result->getResultPoints(); + for(int i = 0; i < result_points->size() / 4; i++) { + const int ind = i * 4; + for (int j = 1; j < 4; j++) + points.emplace_back(result_points[ind+j]->getX(), result_points[ind+j]->getY()); + + points.emplace_back(result_points[ind]->getX(), result_points[ind]->getY()); + } return ret; } // try different binarizers diff --git a/modules/wechat_qrcode/src/decodermgr.hpp b/modules/wechat_qrcode/src/decodermgr.hpp index 10ac16e7c7..c2f29dbe2f 100644 --- a/modules/wechat_qrcode/src/decodermgr.hpp +++ b/modules/wechat_qrcode/src/decodermgr.hpp @@ -26,7 +26,7 @@ class DecoderMgr { DecoderMgr() { reader_ = new zxing::qrcode::QRCodeReader(); }; ~DecoderMgr(){}; - int decodeImage(cv::Mat src, bool use_nn_detector, string& result); + int decodeImage(cv::Mat src, bool use_nn_detector, string& result, vector& points); private: zxing::Ref qbarUicomBlock_; diff --git a/modules/wechat_qrcode/src/wechat_qrcode.cpp b/modules/wechat_qrcode/src/wechat_qrcode.cpp index df5e8bbcc9..7745279413 100644 --- a/modules/wechat_qrcode/src/wechat_qrcode.cpp +++ b/modules/wechat_qrcode/src/wechat_qrcode.cpp @@ -131,8 +131,8 @@ vector WeChatQRCode::Impl::decode(const Mat& img, vector& candidate vector decode_results; for (auto& point : candidate_points) { Mat cropped_img; + Align aligner; if (use_nn_detector_) { - Align aligner; cropped_img = cropObj(img, point, aligner); } else { cropped_img = img; @@ -144,9 +144,19 @@ vector WeChatQRCode::Impl::decode(const Mat& img, vector& candidate super_resolution_model_->processImageScale(cropped_img, cur_scale, use_nn_sr_); string result; DecoderMgr decodemgr; - auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, result); - + vector points_qr; + auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, result, points_qr); if (ret == 0) { + for (auto&& pt: points_qr) { + pt /= cur_scale; + } + + if (use_nn_detector_) + points_qr = aligner.warpBack(points_qr); + for (int i = 0; i < 4; ++i) { + point.at(i, 0) = points_qr[i].x; + point.at(i, 1) = points_qr[i].y; + } decode_results.push_back(result); points.push_back(point); break; diff --git a/modules/wechat_qrcode/test/test_qrcode.cpp b/modules/wechat_qrcode/test/test_qrcode.cpp index cbd1b2fb35..25ab24895b 100644 --- a/modules/wechat_qrcode/test/test_qrcode.cpp +++ b/modules/wechat_qrcode/test/test_qrcode.cpp @@ -284,6 +284,58 @@ TEST_P(Objdetect_QRCode_Multi, regression) { } } +TEST(Objdetect_QRCode_points_position, rotate45) { + string path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, path_sr_caffemodel; + string model_version = "_2021-01"; + path_detect_prototxt = findDataFile("dnn/wechat"+model_version+"/detect.prototxt", false); + path_detect_caffemodel = findDataFile("dnn/wechat"+model_version+"/detect.caffemodel", false); + path_sr_prototxt = findDataFile("dnn/wechat"+model_version+"/sr.prototxt", false); + path_sr_caffemodel = findDataFile("dnn/wechat"+model_version+"/sr.caffemodel", false); + + auto detector = wechat_qrcode::WeChatQRCode(path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, + path_sr_caffemodel); + + const cv::String expect_msg = "OpenCV"; + QRCodeEncoder::Params params; + params.version = 5; // 37x37 + Ptr qrcode_enc = cv::QRCodeEncoder::create(params); + Mat qrImage; + qrcode_enc->encode(expect_msg, qrImage); + Mat image(800, 800, CV_8UC1); + const int pixInBlob = 4; + Size qrSize = Size((21+(params.version-1)*4)*pixInBlob,(21+(params.version-1)*4)*pixInBlob); + Rect2f rec((image.cols - qrSize.width)/2, (image.rows - qrSize.height)/2, qrSize.width, qrSize.height); + vector goldCorners = {rec.x, rec.y, + rec.x+rec.width, rec.y, + rec.x+rec.width, rec.y+rec.height, + rec.x, rec.y+rec.height}; + Mat roiImage = image(rec); + cv::resize(qrImage, roiImage, qrSize, 1., 1., INTER_NEAREST); + + vector points1; + auto decoded_info1 = detector.detectAndDecode(image, points1); + ASSERT_EQ(1ull, decoded_info1.size()); + ASSERT_EQ(expect_msg, decoded_info1[0]); + EXPECT_NEAR(0, cvtest::norm(Mat(goldCorners), points1[0].reshape(1, 8), NORM_INF), 8.); + + const double angle = 45; + Point2f pc(image.cols/2.f, image.rows/2.f); + Mat rot = getRotationMatrix2D(pc, angle, 1.); + warpAffine(image, image, rot, image.size()); + vector rotateGoldCorners; + for (int i = 0; i < static_cast(goldCorners.size()); i+= 2) { + rotateGoldCorners.push_back(rot.at(0, 0) * goldCorners[i] + + rot.at(0, 1) * goldCorners[i+1] + rot.at(0, 2)); + rotateGoldCorners.push_back(rot.at(1, 0) * goldCorners[i] + + rot.at(1, 1) * goldCorners[i+1] + rot.at(1, 2)); + } + vector points2; + auto decoded_info2 = detector.detectAndDecode(image, points2); + ASSERT_EQ(1ull, decoded_info2.size()); + ASSERT_EQ(expect_msg, decoded_info2[0]); + EXPECT_NEAR(0, cvtest::norm(Mat(rotateGoldCorners), points2[0].reshape(1, 8), NORM_INF), 11.); +} + INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode, testing::ValuesIn(qrcode_images_name)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Close, testing::ValuesIn(qrcode_images_close)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Monitor, testing::ValuesIn(qrcode_images_monitor)); From 0be54f24f27934e4907235a4ff3c32028e4a5f99 Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Thu, 1 Sep 2022 11:34:43 +0200 Subject: [PATCH 10/99] Untemplate polyanticlockwise() and polyclockwise() --- modules/mcc/src/common.hpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/modules/mcc/src/common.hpp b/modules/mcc/src/common.hpp index 224459eb1a..175a8e4808 100644 --- a/modules/mcc/src/common.hpp +++ b/modules/mcc/src/common.hpp @@ -73,32 +73,28 @@ void unique(const std::vector &A, std::vector &U) U.push_back(Tm[i]); } -template -void polyanticlockwise(std::vector &points) +void polyanticlockwise(std::vector &points) { // Sort the points in anti-clockwise order // Trace a line between the first and second point. // If the third point is at the right side, then the points are anti-clockwise - T v1 = points[1] - points[0]; - T v2 = points[2] - points[0]; - - double o = ((double)v1.x * v2.y) - ((double)v1.y * v2.x); + cv::Point2f v1 = points[1] - points[0]; + cv::Point2f v2 = points[2] - points[0]; - if (o < 0.0) //if the third point is in the left side, then sort in anti-clockwise order + //if the third point is in the left side, then sort in anti-clockwise order + if ((v1.x * v2.y) - (v1.y * v2.x) < 0.0) std::swap(points[1], points[3]); } -template -void polyclockwise(std::vector &points) +void polyclockwise(std::vector &points) { // Sort the points in clockwise order // Trace a line between the first and second point. // If the third point is at the right side, then the points are clockwise - T v1 = points[1] - points[0]; - T v2 = points[2] - points[0]; - - double o = ((double)v1.x * v2.y) - ((double)v1.y * v2.x); + cv::Point2f v1 = points[1] - points[0]; + cv::Point2f v2 = points[2] - points[0]; - if (o > 0.0) //if the third point is in the left side, then sort in clockwise order + //if the third point is in the left side, then sort in clockwise order + if ((v1.x * v2.y) - (v1.y * v2.x) > 0.0) std::swap(points[1], points[3]); } // Does lexical cast of the input argument to string From 94ca0c5fc0221f8642cd81d862ccc2f2a8a9c070 Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Mon, 5 Sep 2022 10:23:32 +0200 Subject: [PATCH 11/99] Move poly*clockwise() definition to common.cpp --- modules/mcc/src/common.cpp | 25 +++++++++++++++++++++++++ modules/mcc/src/common.hpp | 27 +++------------------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/modules/mcc/src/common.cpp b/modules/mcc/src/common.cpp index 71cba1e8bc..1f44273202 100644 --- a/modules/mcc/src/common.cpp +++ b/modules/mcc/src/common.cpp @@ -86,5 +86,30 @@ mace_center(const std::vector &ps) return center; } +void polyanticlockwise(std::vector &points) +{ + // Sort the points in anti-clockwise order + // Trace a line between the first and second point. + // If the third point is at the right side, then the points are anti-clockwise + cv::Point2f v1 = points[1] - points[0]; + cv::Point2f v2 = points[2] - points[0]; + + //if the third point is in the left side, then sort in anti-clockwise order + if ((v1.x * v2.y) - (v1.y * v2.x) < 0.0) + std::swap(points[1], points[3]); +} +void polyclockwise(std::vector &points) +{ + // Sort the points in clockwise order + // Trace a line between the first and second point. + // If the third point is at the right side, then the points are clockwise + cv::Point2f v1 = points[1] - points[0]; + cv::Point2f v2 = points[2] - points[0]; + + //if the third point is in the left side, then sort in clockwise order + if ((v1.x * v2.y) - (v1.y * v2.x) > 0.0) + std::swap(points[1], points[3]); +} + } // namespace mcc } // namespace cv diff --git a/modules/mcc/src/common.hpp b/modules/mcc/src/common.hpp index 175a8e4808..998f8a4c51 100644 --- a/modules/mcc/src/common.hpp +++ b/modules/mcc/src/common.hpp @@ -73,30 +73,9 @@ void unique(const std::vector &A, std::vector &U) U.push_back(Tm[i]); } -void polyanticlockwise(std::vector &points) -{ - // Sort the points in anti-clockwise order - // Trace a line between the first and second point. - // If the third point is at the right side, then the points are anti-clockwise - cv::Point2f v1 = points[1] - points[0]; - cv::Point2f v2 = points[2] - points[0]; - - //if the third point is in the left side, then sort in anti-clockwise order - if ((v1.x * v2.y) - (v1.y * v2.x) < 0.0) - std::swap(points[1], points[3]); -} -void polyclockwise(std::vector &points) -{ - // Sort the points in clockwise order - // Trace a line between the first and second point. - // If the third point is at the right side, then the points are clockwise - cv::Point2f v1 = points[1] - points[0]; - cv::Point2f v2 = points[2] - points[0]; - - //if the third point is in the left side, then sort in clockwise order - if ((v1.x * v2.y) - (v1.y * v2.x) > 0.0) - std::swap(points[1], points[3]); -} +void polyanticlockwise(std::vector &points); +void polyclockwise(std::vector &points); + // Does lexical cast of the input argument to string template std::string ToString(const T &value) From 3559e761a38d7759516ab33742cb69f83f5eb11c Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 5 Sep 2022 17:32:23 +0300 Subject: [PATCH 12/99] fixed bug and cast warnings --- modules/aruco/src/aruco_detector.cpp | 2 +- modules/aruco/src/charuco.cpp | 2 +- modules/aruco/test/test_boarddetection.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/aruco/src/aruco_detector.cpp b/modules/aruco/src/aruco_detector.cpp index 45f7cc101b..684e403fd4 100644 --- a/modules/aruco/src/aruco_detector.cpp +++ b/modules/aruco/src/aruco_detector.cpp @@ -63,7 +63,7 @@ bool DetectorParameters::readDetectorParameters(const FileNode& fn) { bool DetectorParameters::writeDetectorParameters(const Ptr& fs) { - if (fs.empty() && !fs->isOpened()) + if (fs.empty() || !fs->isOpened()) return false; return readWrite(*this, nullptr, fs); } diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index a61e0e7056..ba10a7eae4 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -428,7 +428,7 @@ void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, // try to find the rest of markers in the diamond vector< int > acceptedIdxs; Ptr _b = _charucoDiamondLayout.staticCast(); - Ptr refineParameters = makePtr(minRepDistance, -1, false); + Ptr refineParameters = makePtr(minRepDistance, -1.f, false); ArucoDetector detector(dictionary, DetectorParameters::create(), refineParameters); detector.refineDetectedMarkers(grey, _b, currentMarker, currentMarkerId, candidates, noArray(), noArray(), acceptedIdxs); diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index 148c6d440d..5a65c225a7 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -170,7 +170,7 @@ class CV_ArucoRefine : public cvtest::BaseTest { params->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) params->useAruco3Detection = true; - Ptr refineParams = makePtr(10, 3., true); + Ptr refineParams = makePtr(10.f, 3.f, true); detector = aruco::ArucoDetector(dictionary, params, refineParams); } From 432cda837026fcdf29f4d4dc65d4d73b3183a2a2 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 6 Sep 2022 10:55:22 +0300 Subject: [PATCH 13/99] fix cast warnings --- modules/wechat_qrcode/test/test_qrcode.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/wechat_qrcode/test/test_qrcode.cpp b/modules/wechat_qrcode/test/test_qrcode.cpp index 25ab24895b..0420b5b24f 100644 --- a/modules/wechat_qrcode/test/test_qrcode.cpp +++ b/modules/wechat_qrcode/test/test_qrcode.cpp @@ -304,7 +304,10 @@ TEST(Objdetect_QRCode_points_position, rotate45) { Mat image(800, 800, CV_8UC1); const int pixInBlob = 4; Size qrSize = Size((21+(params.version-1)*4)*pixInBlob,(21+(params.version-1)*4)*pixInBlob); - Rect2f rec((image.cols - qrSize.width)/2, (image.rows - qrSize.height)/2, qrSize.width, qrSize.height); + Rect2f rec(static_cast((image.cols - qrSize.width)/2), + static_cast((image.rows - qrSize.height)/2), + static_cast(qrSize.width), + static_cast(qrSize.height)); vector goldCorners = {rec.x, rec.y, rec.x+rec.width, rec.y, rec.x+rec.width, rec.y+rec.height, @@ -324,10 +327,10 @@ TEST(Objdetect_QRCode_points_position, rotate45) { warpAffine(image, image, rot, image.size()); vector rotateGoldCorners; for (int i = 0; i < static_cast(goldCorners.size()); i+= 2) { - rotateGoldCorners.push_back(rot.at(0, 0) * goldCorners[i] + - rot.at(0, 1) * goldCorners[i+1] + rot.at(0, 2)); - rotateGoldCorners.push_back(rot.at(1, 0) * goldCorners[i] + - rot.at(1, 1) * goldCorners[i+1] + rot.at(1, 2)); + rotateGoldCorners.push_back(static_cast(rot.at(0, 0) * goldCorners[i] + + rot.at(0, 1) * goldCorners[i+1] + rot.at(0, 2))); + rotateGoldCorners.push_back(static_cast(rot.at(1, 0) * goldCorners[i] + + rot.at(1, 1) * goldCorners[i+1] + rot.at(1, 2))); } vector points2; auto decoded_info2 = detector.detectAndDecode(image, points2); From b865387a8bfbe8dac1006d5af955a4267fe5bca2 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Thu, 8 Sep 2022 00:31:12 +0300 Subject: [PATCH 14/99] Update edge_drawing.py --- modules/ximgproc/samples/edge_drawing.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/ximgproc/samples/edge_drawing.py b/modules/ximgproc/samples/edge_drawing.py index 43b3334471..8c8d6add3b 100644 --- a/modules/ximgproc/samples/edge_drawing.py +++ b/modules/ximgproc/samples/edge_drawing.py @@ -46,6 +46,7 @@ def main(): # Detect edges # you should call this before detectLines() and detectEllipses() ed.detectEdges(gray) + segments = ed.getSegments() lines = ed.detectLines() ellipses = ed.detectEllipses() @@ -67,14 +68,16 @@ def main(): #Draw detected circles and ellipses if ellipses is not None: # Check if circles and ellipses have been found and only then iterate over these and add them to the image - ellipses = np.uint16(np.around(ellipses)) for i in range(len(ellipses)): + center = (int(ellipses[i][0][0]), int(ellipses[i][0][1])) + axes = (int(ellipses[i][0][2])+int(ellipses[i][0][3]),int(ellipses[i][0][2])+int(ellipses[i][0][4])) + angle = ellipses[i][0][5] color = (0, 0, 255) if ellipses[i][0][2] == 0: color = (0, 255, 0) - cv.ellipse(esrc, (ellipses[i][0][0], ellipses[i][0][1]), (ellipses[i][0][2]+ellipses[i][0][3],ellipses[i][0][2]+ellipses[i][0][4]),ellipses[i][0][5],0, 360, color, 2, cv.LINE_AA) + cv.ellipse(esrc, center, axes, angle,0, 360, color, 2, cv.LINE_AA) - cv.imshow("detected ellipses", esrc) + cv.imshow("detected circles and ellipses", esrc) cv.waitKey(0) print('Done') From 4476b9b1bfbd7509914b2dee44e6ef006077a39a Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 12 Sep 2022 10:09:12 +0300 Subject: [PATCH 15/99] remove upscale, add tiny qr test --- modules/wechat_qrcode/src/wechat_qrcode.cpp | 2 +- modules/wechat_qrcode/test/test_qrcode.cpp | 29 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/wechat_qrcode/src/wechat_qrcode.cpp b/modules/wechat_qrcode/src/wechat_qrcode.cpp index 7745279413..3836ea4678 100644 --- a/modules/wechat_qrcode/src/wechat_qrcode.cpp +++ b/modules/wechat_qrcode/src/wechat_qrcode.cpp @@ -197,7 +197,7 @@ int WeChatQRCode::Impl::applyDetector(const Mat& img, vector& points) { const float targetArea = 400.f * 400.f; // hard code input size - const float tmpScaleFactor = scaleFactor == -1.f ? sqrt(targetArea / (img_w * img_h)) : scaleFactor; + const float tmpScaleFactor = scaleFactor == -1.f ? min(1.f, sqrt(targetArea / (img_w * img_h))) : scaleFactor; int detect_width = img_w * tmpScaleFactor; int detect_height = img_h * tmpScaleFactor; diff --git a/modules/wechat_qrcode/test/test_qrcode.cpp b/modules/wechat_qrcode/test/test_qrcode.cpp index 0420b5b24f..c7d807e2ac 100644 --- a/modules/wechat_qrcode/test/test_qrcode.cpp +++ b/modules/wechat_qrcode/test/test_qrcode.cpp @@ -376,6 +376,35 @@ TEST(Objdetect_QRCode_Big, regression) { ASSERT_EQ(expect_msg, decoded_info[0]); } +TEST(Objdetect_QRCode_Tiny, regression) { + string path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, path_sr_caffemodel; + string model_version = "_2021-01"; + path_detect_prototxt = findDataFile("dnn/wechat"+model_version+"/detect.prototxt", false); + path_detect_caffemodel = findDataFile("dnn/wechat"+model_version+"/detect.caffemodel", false); + path_sr_prototxt = findDataFile("dnn/wechat"+model_version+"/sr.prototxt", false); + path_sr_caffemodel = findDataFile("dnn/wechat"+model_version+"/sr.caffemodel", false); + + auto detector = wechat_qrcode::WeChatQRCode(path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, + path_sr_caffemodel); + + const cv::String expect_msg = "OpenCV"; + QRCodeEncoder::Params params; + params.version = 4; // 33x33 + Ptr qrcode_enc = cv::QRCodeEncoder::create(params); + Mat qrImage; + qrcode_enc->encode(expect_msg, qrImage); + Mat tinyImage(80, 80, CV_8UC1); + const int pixInBlob = 2; + Size qrSize = Size((21+(params.version-1)*4)*pixInBlob,(21+(params.version-1)*4)*pixInBlob); + Mat roiImage = tinyImage(Rect((tinyImage.cols - qrSize.width)/2, (tinyImage.rows - qrSize.height)/2, + qrSize.width, qrSize.height)); + cv::resize(qrImage, roiImage, qrSize, 1., 1., INTER_NEAREST); + + vector points; + auto decoded_info = detector.detectAndDecode(tinyImage, points); + ASSERT_EQ(1ull, decoded_info.size()); + ASSERT_EQ(expect_msg, decoded_info[0]); +} } // namespace } // namespace opencv_test From 98c9d36722f7f9d3a7389c68f6ea7e795542d281 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 12 Sep 2022 10:34:20 +0300 Subject: [PATCH 16/99] enable 2 tests, enable dnn detector --- modules/wechat_qrcode/test/test_qrcode.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/modules/wechat_qrcode/test/test_qrcode.cpp b/modules/wechat_qrcode/test/test_qrcode.cpp index 0420b5b24f..cc7114bb8f 100644 --- a/modules/wechat_qrcode/test/test_qrcode.cpp +++ b/modules/wechat_qrcode/test/test_qrcode.cpp @@ -41,9 +41,9 @@ std::string qrcode_images_curved[] = {"curved_1.jpg", /*"curved_2.jpg", "curved_ "curved_4.jpg",*/ "curved_5.jpg", "curved_6.jpg", /*"curved_7.jpg", "curved_8.jpg"*/}; -// std::string qrcode_images_multiple[] = {"2_qrcodes.png", "3_close_qrcodes.png", "3_qrcodes.png", -// "4_qrcodes.png", "5_qrcodes.png", "6_qrcodes.png", -// "7_qrcodes.png", "8_close_qrcodes.png"}; +std::string qrcode_images_multiple[] = {/*"2_qrcodes.png",*/ "3_close_qrcodes.png", /*"3_qrcodes.png", + "4_qrcodes.png", "5_qrcodes.png", "6_qrcodes.png",*/ + "7_qrcodes.png"/*, "8_close_qrcodes.png"*/}; typedef testing::TestWithParam Objdetect_QRCode; TEST_P(Objdetect_QRCode, regression) { @@ -237,18 +237,20 @@ typedef testing::TestWithParam Objdetect_QRCode_Multi; TEST_P(Objdetect_QRCode_Multi, regression) { const std::string name_current_image = GetParam(); const std::string root = "qrcode/multiple/"; + string path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, path_sr_caffemodel; + string model_version = "_2021-01"; + path_detect_prototxt = findDataFile("dnn/wechat"+model_version+"/detect.prototxt", false); + path_detect_caffemodel = findDataFile("dnn/wechat"+model_version+"/detect.caffemodel", false); + path_sr_prototxt = findDataFile("dnn/wechat"+model_version+"/sr.prototxt", false); + path_sr_caffemodel = findDataFile("dnn/wechat"+model_version+"/sr.caffemodel", false); std::string image_path = findDataFile(root + name_current_image); Mat src = imread(image_path); ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; vector points; - // can not find the model file - // so we temporarily comment it out - // auto detector = wechat_qrcode::WeChatQRCode( - // findDataFile("detect.prototxt", false), findDataFile("detect.caffemodel", false), - // findDataFile("sr.prototxt", false), findDataFile("sr.caffemodel", false)); - auto detector = wechat_qrcode::WeChatQRCode(); + auto detector = wechat_qrcode::WeChatQRCode(path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, + path_sr_caffemodel); vector decoded_info = detector.detectAndDecode(src, points); const std::string dataset_config = findDataFile(root + "dataset_config.json"); @@ -343,7 +345,7 @@ INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode, testing::ValuesIn(qrcode_images_ INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Close, testing::ValuesIn(qrcode_images_close)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Monitor, testing::ValuesIn(qrcode_images_monitor)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Curved, testing::ValuesIn(qrcode_images_curved)); -// INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Multi, testing::ValuesIn(qrcode_images_multiple)); +INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Multi, testing::ValuesIn(qrcode_images_multiple)); TEST(Objdetect_QRCode_Big, regression) { string path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, path_sr_caffemodel; From 657335352a9e69fc40e1b150496a19c33c506e61 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Mon, 12 Sep 2022 14:54:54 +0300 Subject: [PATCH 17/99] Replace internal raw pointer with vect. --- modules/cudacodec/src/frame_queue.cpp | 28 ++------------------------ modules/cudacodec/src/frame_queue.hpp | 14 +++++-------- modules/cudacodec/src/video_reader.cpp | 5 +++-- 3 files changed, 10 insertions(+), 37 deletions(-) diff --git a/modules/cudacodec/src/frame_queue.cpp b/modules/cudacodec/src/frame_queue.cpp index fdbe50f8dd..d73e04eb18 100644 --- a/modules/cudacodec/src/frame_queue.cpp +++ b/modules/cudacodec/src/frame_queue.cpp @@ -45,32 +45,8 @@ #ifdef HAVE_NVCUVID -RawPacket::RawPacket(const unsigned char* _data, const size_t _size, const bool _containsKeyFrame) : size(_size), containsKeyFrame(_containsKeyFrame) { - data = new unsigned char[size]; - memcpy(data, _data, size); -}; - -RawPacket::~RawPacket() { - if (data) delete[] data; -} - -RawPacket::RawPacket(const RawPacket& other) : RawPacket(other.data, other.size, other.containsKeyFrame) { -} - -RawPacket::RawPacket(RawPacket&& other) noexcept : data(std::exchange(other.data, nullptr)), size(std::exchange(other.size, 0)), - containsKeyFrame(std::exchange(other.containsKeyFrame, false)) { -} - -RawPacket& RawPacket::operator=(const RawPacket& other) { - return *this = RawPacket(other); -} - -RawPacket& RawPacket::operator=(RawPacket&& other) noexcept { - std::swap(data, other.data); - size = other.size; - containsKeyFrame = other.containsKeyFrame; - return *this; -} +RawPacket::RawPacket(const unsigned char* data_, const size_t size, const bool containsKeyFrame_) : + data(data_,data_ + size), containsKeyFrame(containsKeyFrame_) {}; cv::cudacodec::detail::FrameQueue::~FrameQueue() { if (isFrameInUse_) diff --git a/modules/cudacodec/src/frame_queue.hpp b/modules/cudacodec/src/frame_queue.hpp index d7f0696af5..840b23c5dd 100644 --- a/modules/cudacodec/src/frame_queue.hpp +++ b/modules/cudacodec/src/frame_queue.hpp @@ -50,16 +50,12 @@ class RawPacket { public: RawPacket(const unsigned char* _data, const size_t _size = 0, const bool _containsKeyFrame = false); - ~RawPacket(); - RawPacket(const RawPacket& other); - RawPacket(RawPacket&& other) noexcept; - RawPacket& operator=(const RawPacket& other); - RawPacket& operator=(RawPacket&& other) noexcept; - unsigned char* Data() const { return data; } - size_t size = 0; - bool containsKeyFrame = false; + const unsigned char* Data() const noexcept { return data.data(); } + size_t Size() const noexcept { return data.size(); } + bool ContainsKeyFrame() const noexcept { return containsKeyFrame; } private: - unsigned char* data = 0; + std::vector data; + bool containsKeyFrame = false; }; namespace cv { namespace cudacodec { namespace detail { diff --git a/modules/cudacodec/src/video_reader.cpp b/modules/cudacodec/src/video_reader.cpp index 903defaf37..f321ed4f18 100644 --- a/modules/cudacodec/src/video_reader.cpp +++ b/modules/cudacodec/src/video_reader.cpp @@ -254,7 +254,8 @@ namespace if (idx >= rawPacketsBaseIdx && idx < rawPacketsBaseIdx + rawPackets.size()) { if (!frame.isMat()) CV_Error(Error::StsUnsupportedFormat, "Raw data is stored on the host and must be retrieved using a cv::Mat"); - Mat tmp(1, rawPackets.at(idx - rawPacketsBaseIdx).size, CV_8UC1, rawPackets.at(idx - rawPacketsBaseIdx).Data(), rawPackets.at(idx - rawPacketsBaseIdx).size); + const size_t i = idx - rawPacketsBaseIdx; + Mat tmp(1, rawPackets.at(i).Size(), CV_8UC1, const_cast(rawPackets.at(i).Data()), rawPackets.at(i).Size()); frame.getMatRef() = tmp; } } @@ -299,7 +300,7 @@ namespace case VideoReaderProps::PROP_LRF_HAS_KEY_FRAME: { const int iPacket = propertyVal - rawPacketsBaseIdx; if (videoSource_->RawModeEnabled() && iPacket >= 0 && iPacket < rawPackets.size()) { - propertyVal = rawPackets.at(iPacket).containsKeyFrame; + propertyVal = rawPackets.at(iPacket).ContainsKeyFrame(); return true; } else From 14b5933e8c51abd50a11b545a8808b07ee51f0eb Mon Sep 17 00:00:00 2001 From: sitong lian <563052165@qq.com> Date: Tue, 6 Sep 2022 23:58:19 +0800 Subject: [PATCH 18/99] fix the bug that cannot detect multi qrcode when use_nn_detector is false --- .vscode/settings.json | 6 ++ .../samples/qrcode_example_without_nn.cpp | 67 +++++++++++++++++++ .../samples/qrcode_without_nn.py | 50 ++++++++++++++ modules/wechat_qrcode/src/decodermgr.cpp | 36 +++++----- modules/wechat_qrcode/src/decodermgr.hpp | 6 +- modules/wechat_qrcode/src/wechat_qrcode.cpp | 28 ++++---- .../src/zxing/qrcode/qrcode_reader.cpp | 37 +++++----- .../src/zxing/qrcode/qrcode_reader.hpp | 6 +- modules/wechat_qrcode/src/zxing/reader.cpp | 2 +- modules/wechat_qrcode/src/zxing/reader.hpp | 4 +- 10 files changed, 187 insertions(+), 55 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 modules/wechat_qrcode/samples/qrcode_example_without_nn.cpp create mode 100644 modules/wechat_qrcode/samples/qrcode_without_nn.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..553817fbf8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cquery.cacheDirectory": "${workspaceFolder}/.vscode/cquery_cached_index/", + "files.associations": { + "vector": "cpp" + } +} \ No newline at end of file diff --git a/modules/wechat_qrcode/samples/qrcode_example_without_nn.cpp b/modules/wechat_qrcode/samples/qrcode_example_without_nn.cpp new file mode 100644 index 0000000000..7428d0dd6b --- /dev/null +++ b/modules/wechat_qrcode/samples/qrcode_example_without_nn.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +using namespace std; +using namespace cv; + +#include + +int main(int argc, char* argv[]) { + cout << endl << argv[0] << endl << endl; + cout << "A demo program of WeChat QRCode Detector: " << endl; + + Mat img; + int camIdx = -1; + if (argc > 1) { + bool live = strcmp(argv[1], "-camera") == 0; + if (live) { + camIdx = argc > 2 ? atoi(argv[2]) : 0; + } else { + img = imread(argv[1]); + } + } else { + cout << " Usage: " << argv[0] << " " << endl; + return 0; + } + // The model is downloaded to ${CMAKE_BINARY_DIR}/downloads/wechat_qrcode if cmake runs without warnings, + // otherwise you can download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode. + Ptr detector; + + try { + detector = makePtr("", "", "", ""); + } catch (const std::exception& e) { + cout << + "\n---------------------------------------------------------------\n" + "Failed to initialize WeChatQRCode.\n" + "---------------------------------------------------------------\n"; + cout << e.what() << endl; + return 0; + } + string prevstr = ""; + vector points; + + if (camIdx < 0) { + auto res = detector->detectAndDecode(img, points); + for (const auto& t : res) cout << t << endl; + } else { + VideoCapture cap(camIdx); + for(;;) { + cap >> img; + if (img.empty()) + break; + auto res = detector->detectAndDecode(img, points); + for (const auto& t : res) { + if (t != prevstr) + cout << t << endl; + } + if (!res.empty()) + prevstr = res.back(); + imshow("image", img); + if (waitKey(30) >= 0) + break; + } + } + return 0; +} \ No newline at end of file diff --git a/modules/wechat_qrcode/samples/qrcode_without_nn.py b/modules/wechat_qrcode/samples/qrcode_without_nn.py new file mode 100644 index 0000000000..9b8feb5205 --- /dev/null +++ b/modules/wechat_qrcode/samples/qrcode_without_nn.py @@ -0,0 +1,50 @@ +import cv2 +import sys + +print(sys.argv[0]) +print('A demo program of WeChat QRCode Detector:') +camIdx = -1 +if len(sys.argv) > 1: + if sys.argv[1] == "-camera": + camIdx = int(sys.argv[2]) if len(sys.argv)>2 else 0 + img = cv2.imread(sys.argv[1]) +else: + print(" Usage: " + sys.argv[0] + " ") + exit(0) + +# For python API generator, it follows the template: {module_name}_{class_name}, +# so it is a little weird. +# The model is downloaded to ${CMAKE_BINARY_DIR}/downloads/wechat_qrcode if cmake runs without warnings, +# otherwise you can download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode. +try: + detector = cv2.wechat_qrcode_WeChatQRCode( + "", "", "", "") +except: + print("---------------------------------------------------------------") + print("Failed to initialize WeChatQRCode.") + print("---------------------------------------------------------------") + exit(0) + +prevstr = "" + +if camIdx < 0: + res, points = detector.detectAndDecode(img) + print(res,points) +else: + cap = cv2.VideoCapture(camIdx) + while True: + res, img = cap.read() + if img is None: + break + res, points = detector.detectAndDecode(img) + for t in res: + if t != prevstr: + print(t) + if res: + prevstr = res[-1] + cv2.imshow("image", img) + if cv2.waitKey(30) >= 0: + break + # When everything done, release the capture + cap.release() + cv2.destroyAllWindows() diff --git a/modules/wechat_qrcode/src/decodermgr.cpp b/modules/wechat_qrcode/src/decodermgr.cpp index d4b8edf6f4..47a9ec775e 100644 --- a/modules/wechat_qrcode/src/decodermgr.cpp +++ b/modules/wechat_qrcode/src/decodermgr.cpp @@ -18,7 +18,7 @@ using zxing::Result; using zxing::UnicomBlock; namespace cv { namespace wechat_qrcode { -int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, vector& points) { +int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, vector& results, vector>& zxing_points) { int width = src.cols; int height = src.rows; if (width <= 20 || height <= 20) @@ -28,7 +28,7 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, v zxing::ArrayRef scaled_img_zx = zxing::ArrayRef(new zxing::Array(scaled_img_data)); - zxing::Ref zx_result; + vector> zx_results; decode_hints_.setUseNNDetector(use_nn_detector); @@ -43,16 +43,20 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, v } else { source->reset(scaled_img_zx.data(), width, height); } - int ret = TryDecode(source, zx_result); + int ret = TryDecode(source, zx_results); if (!ret) { - result = zx_result->getText()->getText(); - auto result_points = zx_result->getResultPoints(); - for(int i = 0; i < result_points->size() / 4; i++) { - const int ind = i * 4; - for (int j = 1; j < 4; j++) - points.emplace_back(result_points[ind+j]->getX(), result_points[ind+j]->getY()); - - points.emplace_back(result_points[ind]->getX(), result_points[ind]->getY()); + for(unsigned int i=0; igetText()->getText()); + vector tmp_qr_points; + auto tmp_zx_points = zx_results[i]->getResultPoints(); + for(int i = 0; i < tmp_zx_points->size() / 4; i++) { + const int ind = i * 4; + for (int j = 1; j < 4; j++){ + tmp_qr_points.emplace_back(tmp_zx_points[ind+j]->getX(), tmp_zx_points[ind+j]->getY()); + } + tmp_qr_points.emplace_back(tmp_zx_points[ind]->getX(), tmp_zx_points[ind]->getY()); + } + zxing_points.push_back(tmp_qr_points); } return ret; } @@ -62,7 +66,7 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, v return -1; } -int DecoderMgr::TryDecode(Ref source, Ref& result) { +int DecoderMgr::TryDecode(Ref source, vector>& results) { int res = -1; string cell_result; @@ -71,17 +75,17 @@ int DecoderMgr::TryDecode(Ref source, Ref& result) { zxing::Ref binary_bitmap(new BinaryBitmap(binarizer)); binary_bitmap->m_poUnicomBlock = qbarUicomBlock_; - result = Decode(binary_bitmap, decode_hints_); - res = (result == NULL) ? 1 : 0; + results = Decode(binary_bitmap, decode_hints_); + res = (results.size() == 0) ? 1 : 0; if (res == 0) { - result->setBinaryMethod(int(binarizer_mgr_.GetCurBinarizer())); + results[0]->setBinaryMethod(int(binarizer_mgr_.GetCurBinarizer())); } return res; } -Ref DecoderMgr::Decode(Ref image, DecodeHints hints) { +vector> DecoderMgr::Decode(Ref image, DecodeHints hints) { return reader_->decode(image, hints); } } // namespace wechat_qrcode diff --git a/modules/wechat_qrcode/src/decodermgr.hpp b/modules/wechat_qrcode/src/decodermgr.hpp index c2f29dbe2f..1e04203b21 100644 --- a/modules/wechat_qrcode/src/decodermgr.hpp +++ b/modules/wechat_qrcode/src/decodermgr.hpp @@ -26,7 +26,7 @@ class DecoderMgr { DecoderMgr() { reader_ = new zxing::qrcode::QRCodeReader(); }; ~DecoderMgr(){}; - int decodeImage(cv::Mat src, bool use_nn_detector, string& result, vector& points); + int decodeImage(cv::Mat src, bool use_nn_detector, vector& result, vector>& zxing_points); private: zxing::Ref qbarUicomBlock_; @@ -35,10 +35,10 @@ class DecoderMgr { zxing::Ref reader_; BinarizerMgr binarizer_mgr_; - zxing::Ref Decode(zxing::Ref image, + vector> Decode(zxing::Ref image, zxing::DecodeHints hints); - int TryDecode(zxing::Ref source, zxing::Ref& result); + int TryDecode(zxing::Ref source, vector>& result); }; } // namespace wechat_qrcode diff --git a/modules/wechat_qrcode/src/wechat_qrcode.cpp b/modules/wechat_qrcode/src/wechat_qrcode.cpp index 3836ea4678..596a72269c 100644 --- a/modules/wechat_qrcode/src/wechat_qrcode.cpp +++ b/modules/wechat_qrcode/src/wechat_qrcode.cpp @@ -144,21 +144,23 @@ vector WeChatQRCode::Impl::decode(const Mat& img, vector& candidate super_resolution_model_->processImageScale(cropped_img, cur_scale, use_nn_sr_); string result; DecoderMgr decodemgr; - vector points_qr; - auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, result, points_qr); + vector> zxing_points; + auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, decode_results, zxing_points); if (ret == 0) { - for (auto&& pt: points_qr) { - pt /= cur_scale; + for(unsigned int i=0; i points_qr = zxing_points[i]; + for (auto&& pt: points_qr) { + pt /= cur_scale; + } + + if (use_nn_detector_) + points_qr = aligner.warpBack(points_qr); + for (int j = 0; j < 4; ++j) { + point.at(j, 0) = points_qr[j].x; + point.at(j, 1) = points_qr[j].y; + } + points.push_back(point); } - - if (use_nn_detector_) - points_qr = aligner.warpBack(points_qr); - for (int i = 0; i < 4; ++i) { - point.at(i, 0) = points_qr[i].x; - point.at(i, 1) = points_qr[i].y; - } - decode_results.push_back(result); - points.push_back(point); break; } } diff --git a/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp b/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp index 64a6c08c9f..c5b5262635 100644 --- a/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp +++ b/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp @@ -34,33 +34,33 @@ QRCodeReader::QRCodeReader() : decoder_() { smoothMaxMultiple_ = 40; } -Ref QRCodeReader::decode(Ref image) { return decode(image, DecodeHints()); } +vector> QRCodeReader::decode(Ref image) { return decode(image, DecodeHints()); } -Ref QRCodeReader::decode(Ref image, DecodeHints hints) { +vector> QRCodeReader::decode(Ref image, DecodeHints hints) { // Binarize image using the Histogram Binarized method and be binarized ErrorHandler err_handler; + vector> result_list; Ref imageBitMatrix = image->getBlackMatrix(err_handler); - if (err_handler.ErrCode() || imageBitMatrix == NULL) return Ref(); + if (err_handler.ErrCode() || imageBitMatrix == NULL) return result_list; - Ref rst = decodeMore(image, imageBitMatrix, hints, err_handler); - if (err_handler.ErrCode() || rst == NULL) { + vector> rst = decodeMore(image, imageBitMatrix, hints, err_handler); + if (err_handler.ErrCode() || rst.empty()) { // black white mirro!!! Ref invertedMatrix = image->getInvertedMatrix(err_handler); - if (err_handler.ErrCode() || invertedMatrix == NULL) return Ref(); - Ref tmp_rst = decodeMore(image, invertedMatrix, hints, err_handler); - if (err_handler.ErrCode() || tmp_rst == NULL) return Ref(); - return tmp_rst; + if (err_handler.ErrCode() || invertedMatrix == NULL) return result_list; + vector> tmp_rst = decodeMore(image, invertedMatrix, hints, err_handler); + if (err_handler.ErrCode() || tmp_rst.empty()) return tmp_rst; } return rst; } -Ref QRCodeReader::decodeMore(Ref image, Ref imageBitMatrix, +vector> QRCodeReader::decodeMore(Ref image, Ref imageBitMatrix, DecodeHints hints, ErrorHandler &err_handler) { nowHints_ = hints; std::string ept; - - if (imageBitMatrix == NULL) return Ref(); + vector> result_list; + if (imageBitMatrix == NULL) return result_list; image->m_poUnicomBlock->Init(); image->m_poUnicomBlock->Reset(imageBitMatrix); @@ -88,7 +88,7 @@ Ref QRCodeReader::decodeMore(Ref image, Ref ima Ref patternInfo = detector->getFinderPatternInfo(i); setPatternFix(patternInfo->getPossibleFix()); if (patternInfo->getAnglePossibleFix() < 0.6 && i) continue; - + bool patternFoundFlag = false; int possibleAlignmentCount = 0; possibleAlignmentCount = detector->getPossibleAlignmentCount(i); if (possibleAlignmentCount < 0) continue; @@ -99,6 +99,7 @@ Ref QRCodeReader::decodeMore(Ref image, Ref ima vector needTryVariousDeimensions(possibleAlignmentCount, false); for (int j = 0; j < possibleAlignmentCount; j++) { + if (patternFoundFlag){break;} ArrayRef > points; err_handler.Reset(); Ref detectorResult = @@ -142,11 +143,12 @@ Ref QRCodeReader::decodeMore(Ref image, Ref ima decoderResult->getCharset(), decoderResult->getQRCodeVersion(), decoderResult->getEcLevel(), decoderResult->getCharsetMode())); setSuccFix(points); - - return result; + result_list.push_back(result); + patternFoundFlag = true; } // try different dimentions for (int j = 0; j < possibleAlignmentCount; j++) { + if (patternFoundFlag){break;} err_handler.Reset(); ArrayRef > points; if (needTryVariousDeimensions[j]) { @@ -188,13 +190,14 @@ Ref QRCodeReader::decodeMore(Ref image, Ref ima decoderResult->getEcLevel(), decoderResult->getCharsetMode())); setSuccFix(points); - return result; + result_list.push_back(result); + patternFoundFlag = true; } } } } } - return Ref(); + return result_list; } vector QRCodeReader::getPossibleDimentions(int detectDimension) { diff --git a/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.hpp b/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.hpp index bb0ccf61e7..613286c2c6 100644 --- a/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.hpp +++ b/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.hpp @@ -75,10 +75,10 @@ class QRCodeReader : public Reader { virtual ~QRCodeReader(); string name() override { return "qrcode"; } - Ref decode(Ref image) override; - Ref decode(Ref image, DecodeHints hints) override; + vector> decode(Ref image) override; + vector> decode(Ref image, DecodeHints hints) override; - Ref decodeMore(Ref image, Ref imageBitMatrix, + vector> decodeMore(Ref image, Ref imageBitMatrix, DecodeHints hints, ErrorHandler& err_handler); private: diff --git a/modules/wechat_qrcode/src/zxing/reader.cpp b/modules/wechat_qrcode/src/zxing/reader.cpp index da54ccb6b8..7fa6b65f97 100644 --- a/modules/wechat_qrcode/src/zxing/reader.cpp +++ b/modules/wechat_qrcode/src/zxing/reader.cpp @@ -14,7 +14,7 @@ namespace zxing { Reader::~Reader() {} -Ref Reader::decode(Ref image) { return decode(image, DecodeHints()); } +vector> Reader::decode(Ref image) { return decode(image, DecodeHints()); } unsigned int Reader::getDecodeID() { return 0; } diff --git a/modules/wechat_qrcode/src/zxing/reader.hpp b/modules/wechat_qrcode/src/zxing/reader.hpp index cb3e7eaf3f..e958013cad 100644 --- a/modules/wechat_qrcode/src/zxing/reader.hpp +++ b/modules/wechat_qrcode/src/zxing/reader.hpp @@ -23,8 +23,8 @@ class Reader : public Counted { Reader() {} public: - virtual Ref decode(Ref image); - virtual Ref decode(Ref image, DecodeHints hints) = 0; + virtual vector> decode(Ref image); + virtual vector> decode(Ref image, DecodeHints hints) = 0; virtual ~Reader(); virtual string name(); From f30e80e026074fcbf190acd47270bf3fcf7fcb00 Mon Sep 17 00:00:00 2001 From: sitong lian <563052165@qq.com> Date: Wed, 7 Sep 2022 00:03:50 +0800 Subject: [PATCH 19/99] fix the bug that cannot detect multi qrcode when use_nn_detector is false --- .vscode/settings.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 553817fbf8..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cquery.cacheDirectory": "${workspaceFolder}/.vscode/cquery_cached_index/", - "files.associations": { - "vector": "cpp" - } -} \ No newline at end of file From 4c2283fa9952c241584936d64103cb059910408a Mon Sep 17 00:00:00 2001 From: sitong lian <563052165@qq.com> Date: Wed, 7 Sep 2022 00:19:14 +0800 Subject: [PATCH 20/99] fix the bug that cannot detect multi qrcode when use_nn_detector is false --- .vscode/settings.json | 3 +++ modules/wechat_qrcode/src/decodermgr.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..f5d622ff17 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cquery.cacheDirectory": "${workspaceFolder}/.vscode/cquery_cached_index/" +} \ No newline at end of file diff --git a/modules/wechat_qrcode/src/decodermgr.cpp b/modules/wechat_qrcode/src/decodermgr.cpp index 47a9ec775e..9f8ad419a4 100644 --- a/modules/wechat_qrcode/src/decodermgr.cpp +++ b/modules/wechat_qrcode/src/decodermgr.cpp @@ -45,10 +45,10 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, vector& r } int ret = TryDecode(source, zx_results); if (!ret) { - for(unsigned int i=0; igetText()->getText()); + for(unsigned int k=0; kgetText()->getText()); vector tmp_qr_points; - auto tmp_zx_points = zx_results[i]->getResultPoints(); + auto tmp_zx_points = zx_results[k]->getResultPoints(); for(int i = 0; i < tmp_zx_points->size() / 4; i++) { const int ind = i * 4; for (int j = 1; j < 4; j++){ From 5d9067933e59436f48066bcb9326b8972a68ae74 Mon Sep 17 00:00:00 2001 From: sitong lian <563052165@qq.com> Date: Wed, 7 Sep 2022 00:22:33 +0800 Subject: [PATCH 21/99] fix the bug that cannot detect multi qrcode when use_nn_detector is false --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index f5d622ff17..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cquery.cacheDirectory": "${workspaceFolder}/.vscode/cquery_cached_index/" -} \ No newline at end of file From 587207bd9bca9c555e4549ae8f8d4fad0de8275c Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Thu, 25 Aug 2022 14:27:11 +0300 Subject: [PATCH 22/99] =?UTF-8?q?=EF=BB=BFInitail=20commit=20adding=20nvde?= =?UTF-8?q?c=20scaling=20and=20cropping=20to=20cudacodec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cudacodec/include/opencv2/cudacodec.hpp | 10 ++++ modules/cudacodec/src/video_decoder.cpp | 8 +++ modules/cudacodec/src/video_decoder.hpp | 14 ++++- modules/cudacodec/src/video_parser.cpp | 15 ++++-- modules/cudacodec/src/video_reader.cpp | 14 +++-- modules/cudacodec/test/test_precomp.hpp | 2 + modules/cudacodec/test/test_video.cpp | 54 ++++++++++++++++++- 7 files changed, 106 insertions(+), 11 deletions(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 54ec5bed0a..5aacba0ab9 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -309,6 +309,9 @@ struct CV_EXPORTS_W_SIMPLE FormatInfo CV_PROP_RW double fps; CV_PROP_RW int ulNumDecodeSurfaces;//!< Maximum number of internal decode surfaces. CV_PROP_RW DeinterlaceMode deinterlaceMode; + CV_PROP_RW cv::Size targetSz;//!< Post-processed size of the output frame. + CV_PROP_RW cv::Rect srcRoi;//!< Region of interest decoded from video source. + CV_PROP_RW cv::Rect targetRoi;//!< Region of interest in the output frame containing the decoded frame. }; /** @brief cv::cudacodec::VideoReader generic properties identifier. @@ -516,6 +519,10 @@ surfaces it requires for correct functionality and optimal video memory usage bu overall application. The optimal number of decode surfaces (in terms of performance and memory utilization) should be decided by experimentation for each application, but it cannot go below the number determined by NVDEC. @param rawMode Allow the raw encoded data which has been read up until the last call to grab() to be retrieved by calling retrieve(rawData,RAW_DATA_IDX). +@param targetSz Post-processed size (width/height should be multiples of 2) of the output frame, defaults to the size of the encoded video source. +@param srcRoi Region of interest (x/width should be multiples of 4 and y/height multiples of 2) decoded from video source, defaults to the full frame. +@param targetRoi Region of interest (x/width should be multiples of 4 and y/height multiples of 2) within the output frame to copy and resize the decoded frame to, +defaults to the full frame. */ struct CV_EXPORTS_W_SIMPLE VideoReaderInitParams { CV_WRAP VideoReaderInitParams() : udpSource(false), allowFrameDrop(false), minNumDecodeSurfaces(0), rawMode(0) {}; @@ -523,6 +530,9 @@ struct CV_EXPORTS_W_SIMPLE VideoReaderInitParams { CV_PROP_RW bool allowFrameDrop; CV_PROP_RW int minNumDecodeSurfaces; CV_PROP_RW bool rawMode; + CV_PROP_RW cv::Size targetSz; + CV_PROP_RW cv::Rect srcRoi; + CV_PROP_RW cv::Rect targetRoi; }; /** @brief Creates video reader. diff --git a/modules/cudacodec/src/video_decoder.cpp b/modules/cudacodec/src/video_decoder.cpp index 69845a7a0b..f828b08c15 100644 --- a/modules/cudacodec/src/video_decoder.cpp +++ b/modules/cudacodec/src/video_decoder.cpp @@ -148,6 +148,14 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat) createInfo_.ulTargetHeight = videoFormat.height; createInfo_.ulMaxWidth = videoFormat.ulMaxWidth; createInfo_.ulMaxHeight = videoFormat.ulMaxHeight; + createInfo_.display_area.left = videoFormat.displayArea.x; + createInfo_.display_area.right = videoFormat.displayArea.x + videoFormat.displayArea.width; + createInfo_.display_area.top = videoFormat.displayArea.y; + createInfo_.display_area.bottom = videoFormat.displayArea.y + videoFormat.displayArea.height; + createInfo_.target_rect.left = videoFormat.targetRoi.x; + createInfo_.target_rect.right = videoFormat.targetRoi.x + videoFormat.targetRoi.width; + createInfo_.target_rect.top = videoFormat.targetRoi.y; + createInfo_.target_rect.bottom = videoFormat.targetRoi.y + videoFormat.targetRoi.height; createInfo_.ulNumOutputSurfaces = 2; createInfo_.ulCreationFlags = videoCreateFlags; createInfo_.vidLock = lock_; diff --git a/modules/cudacodec/src/video_decoder.hpp b/modules/cudacodec/src/video_decoder.hpp index 98d8e65254..76d731f207 100644 --- a/modules/cudacodec/src/video_decoder.hpp +++ b/modules/cudacodec/src/video_decoder.hpp @@ -49,10 +49,17 @@ namespace cv { namespace cudacodec { namespace detail { class VideoDecoder { public: - VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, CUcontext ctx, CUvideoctxlock lock) : ctx_(ctx), lock_(lock), decoder_(0) + VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, cv::Size targetSz, cv::Rect srcRoi, cv::Rect targetRoi, CUcontext ctx, CUvideoctxlock lock) : + ctx_(ctx), lock_(lock), decoder_(0) { videoFormat_.codec = codec; videoFormat_.ulNumDecodeSurfaces = minNumDecodeSurfaces; + // alignment enforced by nvcuvid, likely due to chroma subsampling + videoFormat_.targetSz.width = targetSz.width - targetSz.width % 2; videoFormat_.targetSz.height = targetSz.height - targetSz.height % 2; + videoFormat_.srcRoi.x = srcRoi.x - srcRoi.x % 4; videoFormat_.srcRoi.width = srcRoi.width - srcRoi.width % 4; + videoFormat_.srcRoi.y = srcRoi.y - srcRoi.y % 2; videoFormat_.srcRoi.height = srcRoi.height - srcRoi.height % 2; + videoFormat_.targetRoi.x = targetRoi.x - targetRoi.x % 4; videoFormat_.targetRoi.width = targetRoi.width - targetRoi.width % 4; + videoFormat_.targetRoi.y = targetRoi.y - targetRoi.y % 2; videoFormat_.targetRoi.height = targetRoi.height - targetRoi.height % 2; } ~VideoDecoder() @@ -66,6 +73,9 @@ class VideoDecoder // Get the code-type currently used. cudaVideoCodec codec() const { return static_cast(videoFormat_.codec); } int nDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; } + cv::Size getTargetSz() const { return videoFormat_.targetSz; } + cv::Rect getSrcRoi() const { return videoFormat_.srcRoi; } + cv::Rect getTargetRoi() const { return videoFormat_.targetRoi; } unsigned long frameWidth() const { return videoFormat_.ulWidth; } unsigned long frameHeight() const { return videoFormat_.ulHeight; } @@ -89,7 +99,7 @@ class VideoDecoder cuSafeCall( cuvidMapVideoFrame(decoder_, picIdx, &ptr, &pitch, &videoProcParams) ); - return cuda::GpuMat(frameHeight() * 3 / 2, frameWidth(), CV_8UC1, (void*) ptr, pitch); + return cuda::GpuMat(targetHeight() * 3 / 2, targetWidth(), CV_8UC1, (void*) ptr, pitch); } void unmapFrame(cuda::GpuMat& frame) diff --git a/modules/cudacodec/src/video_parser.cpp b/modules/cudacodec/src/video_parser.cpp index feda982c5b..8bccd065a8 100644 --- a/modules/cudacodec/src/video_parser.cpp +++ b/modules/cudacodec/src/video_parser.cpp @@ -120,10 +120,19 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa newFormat.nBitDepthMinus8 = format->bit_depth_luma_minus8; newFormat.ulWidth = format->coded_width; newFormat.ulHeight = format->coded_height; - newFormat.width = format->coded_width; - newFormat.height = format->coded_height; - newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom)); newFormat.fps = format->frame_rate.numerator / static_cast(format->frame_rate.denominator); + newFormat.targetSz = thiz->videoDecoder_->getTargetSz(); + newFormat.width = newFormat.targetSz.width ? newFormat.targetSz.width : format->coded_width; + newFormat.height = newFormat.targetSz.height ? newFormat.targetSz.height : format->coded_height; + newFormat.srcRoi = thiz->videoDecoder_->getSrcRoi(); + if (newFormat.srcRoi.empty()) { + format->display_area.right = format->coded_width; + format->display_area.bottom = format->coded_height; + newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom)); + } + else + newFormat.displayArea = newFormat.srcRoi; + newFormat.targetRoi = thiz->videoDecoder_->getTargetRoi(); newFormat.ulNumDecodeSurfaces = min(!thiz->allowFrameDrop_ ? max(thiz->videoDecoder_->nDecodeSurfaces(), static_cast(format->min_num_decode_surfaces)) : format->min_num_decode_surfaces * 2, 32); if (format->progressive_sequence) diff --git a/modules/cudacodec/src/video_reader.cpp b/modules/cudacodec/src/video_reader.cpp index 903defaf37..5eea8199ef 100644 --- a/modules/cudacodec/src/video_reader.cpp +++ b/modules/cudacodec/src/video_reader.cpp @@ -86,7 +86,8 @@ namespace class VideoReaderImpl : public VideoReader { public: - explicit VideoReaderImpl(const Ptr& source, const int minNumDecodeSurfaces, const bool allowFrameDrop = false , const bool udpSource = false); + explicit VideoReaderImpl(const Ptr& source, const int minNumDecodeSurfaces, const bool allowFrameDrop = false , const bool udpSource = false, + const Size targetSz = Size(), const Rect srcRoi = Rect(), const Rect targetRoi = Rect()); ~VideoReaderImpl(); bool nextFrame(GpuMat& frame, Stream& stream) CV_OVERRIDE; @@ -131,7 +132,8 @@ namespace return videoSource_->format(); } - VideoReaderImpl::VideoReaderImpl(const Ptr& source, const int minNumDecodeSurfaces, const bool allowFrameDrop, const bool udpSource) : + VideoReaderImpl::VideoReaderImpl(const Ptr& source, const int minNumDecodeSurfaces, const bool allowFrameDrop, const bool udpSource, + const Size targetSz, const Rect srcRoi, const Rect targetRoi) : videoSource_(source), lock_(0) { @@ -143,7 +145,7 @@ namespace cuSafeCall( cuCtxGetCurrent(&ctx) ); cuSafeCall( cuvidCtxLockCreate(&lock_, ctx) ); frameQueue_.reset(new FrameQueue()); - videoDecoder_.reset(new VideoDecoder(videoSource_->format().codec, minNumDecodeSurfaces, ctx, lock_)); + videoDecoder_.reset(new VideoDecoder(videoSource_->format().codec, minNumDecodeSurfaces, targetSz, srcRoi, targetRoi, ctx, lock_)); videoParser_.reset(new VideoParser(videoDecoder_, frameQueue_, allowFrameDrop, udpSource)); videoSource_->setVideoParser(videoParser_); videoSource_->start(); @@ -357,13 +359,15 @@ Ptr cv::cudacodec::createVideoReader(const String& filename, const videoSource.reset(new CuvidVideoSource(filename)); } - return makePtr(videoSource, params.minNumDecodeSurfaces, params.allowFrameDrop, params.udpSource); + return makePtr(videoSource, params.minNumDecodeSurfaces, params.allowFrameDrop, params.udpSource, params.targetSz, + params.srcRoi, params.targetRoi); } Ptr cv::cudacodec::createVideoReader(const Ptr& source, const VideoReaderInitParams params) { Ptr videoSource(new RawVideoSourceWrapper(source, params.rawMode)); - return makePtr(videoSource, params.minNumDecodeSurfaces); + return makePtr(videoSource, params.minNumDecodeSurfaces, params.allowFrameDrop, params.udpSource, params.targetSz, + params.srcRoi, params.targetRoi); } #endif // HAVE_NVCUVID diff --git a/modules/cudacodec/test/test_precomp.hpp b/modules/cudacodec/test/test_precomp.hpp index dd58482521..7d38b1142f 100644 --- a/modules/cudacodec/test/test_precomp.hpp +++ b/modules/cudacodec/test/test_precomp.hpp @@ -47,6 +47,8 @@ #include "opencv2/ts/cuda_test.hpp" #include "opencv2/cudacodec.hpp" +#include "opencv2/cudawarping.hpp" +#include "opencv2/cudaarithm.hpp" #include "cvconfig.h" diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index 216d0bc0e5..b194ffa2f8 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -54,6 +54,10 @@ PARAM_TEST_CASE(CheckExtraData, cv::cuda::DeviceInfo, check_extra_data_params_t) { }; +PARAM_TEST_CASE(Scaling, cv::cuda::DeviceInfo, std::string, Size2f, Rect2f, Rect2f) +{ +}; + PARAM_TEST_CASE(Video, cv::cuda::DeviceInfo, std::string) { }; @@ -177,6 +181,47 @@ CUDA_TEST_P(CheckKeyFrame, Reader) } } +CUDA_TEST_P(Scaling, Reader) +{ + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../" + GET_PARAM(1); + const Size2f targetSzIn = GET_PARAM(2); + const Rect2f srcRoiIn = GET_PARAM(3); + const Rect2f targetRoiIn = GET_PARAM(4); + + GpuMat frameOr; + { + cv::Ptr readerGs = cv::cudacodec::createVideoReader(inputFile); + readerGs->set(cudacodec::ColorFormat::GRAY); + ASSERT_TRUE(readerGs->nextFrame(frameOr)); + } + + cudacodec::VideoReaderInitParams params; + params.targetSz = Size(frameOr.cols * targetSzIn.width, frameOr.rows * targetSzIn.height); + params.srcRoi = Rect(frameOr.cols * srcRoiIn.x, frameOr.rows * srcRoiIn.y, frameOr.cols * srcRoiIn.width, frameOr.rows * srcRoiIn.height); + params.targetRoi = Rect(params.targetSz.width * targetRoiIn.x, params.targetSz.height * targetRoiIn.y, params.targetSz.width * targetRoiIn.width, + params.targetSz.height * targetRoiIn.height); + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, params); + reader->set(cudacodec::ColorFormat::GRAY); + GpuMat frame; + ASSERT_TRUE(reader->nextFrame(frame)); + const cudacodec::FormatInfo format = reader->format(); + Size targetSzOut; + targetSzOut.width = params.targetSz.width - params.targetSz.width % 2; targetSzOut.height = params.targetSz.height - params.targetSz.height % 2; + Rect srcRoiOut, targetRoiOut; + srcRoiOut.x = params.srcRoi.x - params.srcRoi.x % 4; srcRoiOut.width = params.srcRoi.width - params.srcRoi.width % 4; + srcRoiOut.y = params.srcRoi.y - params.srcRoi.y % 2; srcRoiOut.height = params.srcRoi.height - params.srcRoi.height % 2; + targetRoiOut.x = params.targetRoi.x - params.targetRoi.x % 4; targetRoiOut.width = params.targetRoi.width - params.targetRoi.width % 4; + targetRoiOut.y = params.targetRoi.y - params.targetRoi.y % 2; targetRoiOut.height = params.targetRoi.height - params.targetRoi.height % 2; + ASSERT_TRUE(format.valid && format.targetSz == targetSzOut && format.srcRoi == srcRoiOut && format.targetRoi == targetRoiOut); + ASSERT_TRUE(frame.size() == targetSzOut); + GpuMat frameGs; + cv::cuda::resize(frameOr(srcRoiOut), frameGs, targetRoiOut.size(), 0, 0, INTER_AREA); + // assert on mean absolute error due to different resize algorithms + const double mae = cv::cuda::norm(frameGs, frame(targetRoiOut), NORM_L1)/frameGs.size().area(); + ASSERT_LT(mae, 2.35); +} + CUDA_TEST_P(Video, Reader) { cv::cuda::setDevice(GET_PARAM(0).deviceID()); @@ -431,7 +476,14 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckSet, testing::Combine( ALL_DEVICES, testing::Values("highgui/video/big_buck_bunny.mp4"))); -#define VIDEO_SRC_R "highgui/video/big_buck_bunny.mp4", "cv/video/768x576.avi", "cv/video/1920x1080.avi", "highgui/video/big_buck_bunny.avi", \ +#define VIDEO_SRC_SCALING "highgui/video/big_buck_bunny.mp4" +#define TARGET_SZ Size2f(1,1), Size2f(0.8,0.9), Size2f(2.3,1.8) +#define SRC_ROI Rect2f(0,0,1,1), Rect2f(0.25,0.25,0.5,0.5) +#define TARGET_ROI Rect2f(0,0,1,1), Rect2f(0.2,0.3,0.6,0.7) +INSTANTIATE_TEST_CASE_P(CUDA_Codec, Scaling, testing::Combine( + ALL_DEVICES, testing::Values(VIDEO_SRC_SCALING), testing::Values(TARGET_SZ), testing::Values(SRC_ROI), testing::Values(TARGET_ROI))); + +#define VIDEO_SRC_R "highgui/video/big_buck_bunny.mp4", "cv/video/768x576.avi", "cv/video/1920x1080.avi", "highgui/video/big_buck_bunny.avi", \ "highgui/video/big_buck_bunny.h264", "highgui/video/big_buck_bunny.h265", "highgui/video/big_buck_bunny.mpg", \ "highgui/video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", "highgui/video/sample_322x242_15frames.yuv420p.libaom-av1.mp4", \ "cv/tracking/faceocc2/data/faceocc2.webm" From 1b92ec7a5d2cdf7b874f7689729c815c91c480e5 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Sun, 18 Sep 2022 09:48:21 +0300 Subject: [PATCH 23/99] Add missing cmake target. --- modules/cudacodec/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cudacodec/CMakeLists.txt b/modules/cudacodec/CMakeLists.txt index 071404ecc7..c1b9ee936c 100644 --- a/modules/cudacodec/CMakeLists.txt +++ b/modules/cudacodec/CMakeLists.txt @@ -6,7 +6,7 @@ set(the_description "CUDA-accelerated Video Encoding/Decoding") ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 /wd4324 /wd4512 -Wundef -Wshadow) -ocv_add_module(cudacodec opencv_core opencv_videoio OPTIONAL opencv_cudev WRAP python) +ocv_add_module(cudacodec opencv_core opencv_videoio opencv_cudaarithm OPTIONAL opencv_cudev WRAP python) ocv_module_include_directories() ocv_glob_module_sources() From 14157b140ea83777dede37cd7dbeb75c64fc7a7a Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 19 Sep 2022 23:13:05 +0300 Subject: [PATCH 24/99] fixed duplicate corners and style --- modules/wechat_qrcode/src/decodermgr.cpp | 6 ++--- modules/wechat_qrcode/src/wechat_qrcode.cpp | 27 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/wechat_qrcode/src/decodermgr.cpp b/modules/wechat_qrcode/src/decodermgr.cpp index 9f8ad419a4..1e93aa15c0 100644 --- a/modules/wechat_qrcode/src/decodermgr.cpp +++ b/modules/wechat_qrcode/src/decodermgr.cpp @@ -45,14 +45,14 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, vector& r } int ret = TryDecode(source, zx_results); if (!ret) { - for(unsigned int k=0; kgetText()->getText()); vector tmp_qr_points; auto tmp_zx_points = zx_results[k]->getResultPoints(); - for(int i = 0; i < tmp_zx_points->size() / 4; i++) { + for (int i = 0; i < tmp_zx_points->size() / 4; i++) { const int ind = i * 4; for (int j = 1; j < 4; j++){ - tmp_qr_points.emplace_back(tmp_zx_points[ind+j]->getX(), tmp_zx_points[ind+j]->getY()); + tmp_qr_points.emplace_back(tmp_zx_points[ind + j]->getX(), tmp_zx_points[ind + j]->getY()); } tmp_qr_points.emplace_back(tmp_zx_points[ind]->getX(), tmp_zx_points[ind]->getY()); } diff --git a/modules/wechat_qrcode/src/wechat_qrcode.cpp b/modules/wechat_qrcode/src/wechat_qrcode.cpp index 596a72269c..f4bec7c2b3 100644 --- a/modules/wechat_qrcode/src/wechat_qrcode.cpp +++ b/modules/wechat_qrcode/src/wechat_qrcode.cpp @@ -144,10 +144,10 @@ vector WeChatQRCode::Impl::decode(const Mat& img, vector& candidate super_resolution_model_->processImageScale(cropped_img, cur_scale, use_nn_sr_); string result; DecoderMgr decodemgr; - vector> zxing_points; + vector> zxing_points, check_points; auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, decode_results, zxing_points); if (ret == 0) { - for(unsigned int i=0; i points_qr = zxing_points[i]; for (auto&& pt: points_qr) { pt /= cur_scale; @@ -159,7 +159,28 @@ vector WeChatQRCode::Impl::decode(const Mat& img, vector& candidate point.at(j, 0) = points_qr[j].x; point.at(j, 1) = points_qr[j].y; } - points.push_back(point); + // try to find duplicate qr corners + bool isDuplicate = false; + for (const auto &tmp_points: check_points) { + const float eps = 10.f; + for (size_t j = 0; j < tmp_points.size(); j++) { + if (abs(tmp_points[j].x - points_qr[j].x) < eps && + abs(tmp_points[j].y - points_qr[j].y) < eps) { + isDuplicate = true; + } + else { + isDuplicate = false; + break; + } + } + } + if (isDuplicate == false) { + points.push_back(point); + check_points.push_back(points_qr); + } + else { + decode_results.erase(decode_results.begin() + i, decode_results.begin() + i + 1); + } } break; } From cfcfd5f4114a6014d93345a82ea397c2cc1aeb40 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 19 Sep 2022 23:13:42 +0300 Subject: [PATCH 25/99] add multi detect test --- modules/wechat_qrcode/test/test_qrcode.cpp | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/modules/wechat_qrcode/test/test_qrcode.cpp b/modules/wechat_qrcode/test/test_qrcode.cpp index 49a34978ab..d59932b812 100644 --- a/modules/wechat_qrcode/test/test_qrcode.cpp +++ b/modules/wechat_qrcode/test/test_qrcode.cpp @@ -408,5 +408,52 @@ TEST(Objdetect_QRCode_Tiny, regression) { ASSERT_EQ(expect_msg, decoded_info[0]); } + +typedef testing::TestWithParam Objdetect_QRCode_Easy_Multi; +TEST_P(Objdetect_QRCode_Easy_Multi, regression) { + string path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, path_sr_caffemodel; + string model_path = GetParam(); + + if (!model_path.empty()) { + path_detect_prototxt = findDataFile(model_path + "/detect.prototxt", false); + path_detect_caffemodel = findDataFile(model_path + "/detect.caffemodel", false); + path_sr_prototxt = findDataFile(model_path + "/sr.prototxt", false); + path_sr_caffemodel = findDataFile(model_path + "/sr.caffemodel", false); + } + + auto detector = wechat_qrcode::WeChatQRCode(path_detect_prototxt, path_detect_caffemodel, path_sr_prototxt, + path_sr_caffemodel); + + const cv::String expect_msg1 = "OpenCV1", expect_msg2 = "OpenCV2"; + QRCodeEncoder::Params params; + params.version = 4; // 33x33 + Ptr qrcode_enc = cv::QRCodeEncoder::create(params); + Mat qrImage1, qrImage2; + qrcode_enc->encode(expect_msg1, qrImage1); + qrcode_enc->encode(expect_msg2, qrImage2); + const int pixInBlob = 2; + const int offset = 14; + const int qr_size = (params.version - 1) * 4 + 21; + Mat tinyImage = Mat::zeros(qr_size*pixInBlob+offset, (qr_size*pixInBlob+offset)*2, CV_8UC1); + Size qrSize = Size(qrImage1.cols, qrImage1.rows); + + Mat roiImage = tinyImage(Rect((tinyImage.cols/2 - qrSize.width)/2, (tinyImage.rows - qrSize.height)/2, + qrSize.width, qrSize.height)); + cv::resize(qrImage1, roiImage, qrSize, 1., 1., INTER_NEAREST); + + roiImage = tinyImage(Rect((tinyImage.cols/2 - qrSize.width)/2+tinyImage.cols/2, (tinyImage.rows - qrSize.height)/2, + qrSize.width, qrSize.height)); + cv::resize(qrImage2, roiImage, qrSize, 1., 1., INTER_NEAREST); + + vector points; + auto decoded_info = detector.detectAndDecode(tinyImage, points); + ASSERT_EQ(2ull, decoded_info.size()); + ASSERT_TRUE((expect_msg1 == decoded_info[0] && expect_msg2 == decoded_info[1]) || + (expect_msg1 == decoded_info[1] && expect_msg2 == decoded_info[0])); +} + +std::string qrcode_model_path[] = {"", "dnn/wechat_2021-01"}; +INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Easy_Multi, testing::ValuesIn(qrcode_model_path)); + } // namespace } // namespace opencv_test From a3afb879cab6898a6be787ff6fd2f1cc04beb63f Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 20 Sep 2022 11:37:53 +0300 Subject: [PATCH 26/99] add return for nn_detector --- modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp b/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp index c5b5262635..b6c4c4f71c 100644 --- a/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp +++ b/modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp @@ -145,6 +145,9 @@ vector> QRCodeReader::decodeMore(Ref image, Ref> QRCodeReader::decodeMore(Ref image, Ref Date: Wed, 21 Sep 2022 20:11:29 +0300 Subject: [PATCH 27/99] Add other missing target. --- modules/cudacodec/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cudacodec/CMakeLists.txt b/modules/cudacodec/CMakeLists.txt index c1b9ee936c..9281024e8b 100644 --- a/modules/cudacodec/CMakeLists.txt +++ b/modules/cudacodec/CMakeLists.txt @@ -6,7 +6,7 @@ set(the_description "CUDA-accelerated Video Encoding/Decoding") ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 /wd4324 /wd4512 -Wundef -Wshadow) -ocv_add_module(cudacodec opencv_core opencv_videoio opencv_cudaarithm OPTIONAL opencv_cudev WRAP python) +ocv_add_module(cudacodec opencv_core opencv_videoio opencv_cudaarithm opencv_cudawarping OPTIONAL opencv_cudev WRAP python) ocv_module_include_directories() ocv_glob_module_sources() From f9d92f2bb503b57d1fd2ae5f869ea7e660923dea Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 23 Sep 2022 10:37:12 +0200 Subject: [PATCH 28/99] adding python smoke test for MSDDetector --- .../include/opencv2/xfeatures2d.hpp | 2 +- .../misc/python/test/test_descriptors.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 modules/xfeatures2d/misc/python/test/test_descriptors.py diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index ca958c8b13..12eddbcbe9 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -282,7 +282,7 @@ class CV_EXPORTS_W MSDDetector : public Feature2D { public: - static Ptr create(int m_patch_radius = 3, int m_search_area_radius = 5, + CV_WRAP static Ptr create(int m_patch_radius = 3, int m_search_area_radius = 5, int m_nms_radius = 5, int m_nms_scale_radius = 0, float m_th_saliency = 250.0f, int m_kNN = 4, float m_scale_factor = 1.25f, int m_n_scales = -1, bool m_compute_orientation = false); }; diff --git a/modules/xfeatures2d/misc/python/test/test_descriptors.py b/modules/xfeatures2d/misc/python/test/test_descriptors.py new file mode 100644 index 0000000000..30278c7fab --- /dev/null +++ b/modules/xfeatures2d/misc/python/test/test_descriptors.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# Python 2/3 compatibility +from __future__ import print_function + +import os +import numpy as np +import cv2 as cv + +from tests_common import NewOpenCVTests + +class MSDDetector_test(NewOpenCVTests): + + def test_create(self): + + msd = cv.xfeatures2d.MSDDetector_create() + self.assertFalse(msd is None) + + img1 = np.zeros((100, 100, 3), dtype=np.uint8) + kp1_, des1_ = msd.detectAndCompute(img1, None) + + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() From 6fb484c07df60f381abea772d5813e6a058aa0da Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 23 Sep 2022 11:48:49 +0200 Subject: [PATCH 29/99] adding python smoke test for MSDDetector --- modules/xfeatures2d/misc/python/test/test_descriptors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/xfeatures2d/misc/python/test/test_descriptors.py b/modules/xfeatures2d/misc/python/test/test_descriptors.py index 30278c7fab..ca8bbcbc02 100644 --- a/modules/xfeatures2d/misc/python/test/test_descriptors.py +++ b/modules/xfeatures2d/misc/python/test/test_descriptors.py @@ -17,7 +17,7 @@ def test_create(self): self.assertFalse(msd is None) img1 = np.zeros((100, 100, 3), dtype=np.uint8) - kp1_, des1_ = msd.detectAndCompute(img1, None) + kp1_ = msd.detect(img1, None) if __name__ == '__main__': From 4057993c1a8d54484bcdc51225cda21d7c873310 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 24 Aug 2022 12:02:26 +0200 Subject: [PATCH 30/99] xphoto fix inpaint fsr --- modules/xphoto/src/inpainting_fsr.impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/xphoto/src/inpainting_fsr.impl.hpp b/modules/xphoto/src/inpainting_fsr.impl.hpp index f47e2c79e1..b9ebb348fa 100644 --- a/modules/xphoto/src/inpainting_fsr.impl.hpp +++ b/modules/xphoto/src/inpainting_fsr.impl.hpp @@ -748,7 +748,7 @@ icvDetermineProcessingOrder( static -void inpaint_fsr(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType) +void inpaint_fsr(Mat src, const Mat &mask, Mat &dst, const int algorithmType) { CV_Assert(algorithmType == xphoto::INPAINT_FSR_BEST || algorithmType == xphoto::INPAINT_FSR_FAST); CV_Check(src.channels(), src.channels() == 1 || src.channels() == 3, ""); @@ -767,7 +767,7 @@ void inpaint_fsr(const Mat &src, const Mat &mask, Mat &dst, const int algorithmT CV_Error(Error::StsUnsupportedFormat, "Unsupported source image format!"); break; } - src.convertTo(src, CV_8U, 1/257.0); + src.convertTo(src, CV_8U, 1/256.0); break; } case CV_32FC1: From 9ccca571086acaf6ce4e18c528f2e52318849896 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Fri, 7 Oct 2022 17:57:05 +0300 Subject: [PATCH 31/99] Update cudacodec::VideoWriter to work with the latest Nvidia Video Codec SDK --- modules/cudacodec/CMakeLists.txt | 7 +- .../cudacodec/include/opencv2/cudacodec.hpp | 347 +++-- .../misc/python/pyopencv_cudacodec.hpp | 10 - .../misc/python/test/test_cudacodec.py | 23 +- modules/cudacodec/perf/perf_video.cpp | 122 +- modules/cudacodec/src/NvEncoder.cpp | 779 ++++++++++++ modules/cudacodec/src/NvEncoder.h | 377 ++++++ modules/cudacodec/src/NvEncoderCuda.cpp | 196 +++ modules/cudacodec/src/NvEncoderCuda.h | 75 ++ modules/cudacodec/src/precomp.hpp | 50 +- modules/cudacodec/src/video_writer.cpp | 1117 +++++------------ modules/cudacodec/test/test_video.cpp | 361 +++++- 12 files changed, 2432 insertions(+), 1032 deletions(-) delete mode 100644 modules/cudacodec/misc/python/pyopencv_cudacodec.hpp create mode 100644 modules/cudacodec/src/NvEncoder.cpp create mode 100644 modules/cudacodec/src/NvEncoder.h create mode 100644 modules/cudacodec/src/NvEncoderCuda.cpp create mode 100644 modules/cudacodec/src/NvEncoderCuda.h diff --git a/modules/cudacodec/CMakeLists.txt b/modules/cudacodec/CMakeLists.txt index 9281024e8b..1720d9c7cd 100644 --- a/modules/cudacodec/CMakeLists.txt +++ b/modules/cudacodec/CMakeLists.txt @@ -18,12 +18,11 @@ if(HAVE_NVCUVID) endif() if(HAVE_NVCUVENC) - if(WIN32) - list(APPEND extra_libs ${CUDA_nvcuvenc_LIBRARY}) - endif() + list(APPEND extra_libs ${CUDA_CUDA_LIBRARY} ${CUDA_nvencodeapi_LIBRARY}) + ocv_add_module(cudacodec opencv_cudaimgproc) endif() ocv_create_module(${extra_libs}) ocv_add_accuracy_tests() -ocv_add_perf_tests() +ocv_add_perf_tests() \ No newline at end of file diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 5aacba0ab9..6dc25af4a5 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -66,114 +66,153 @@ using namespace cuda; // Stream ////////////////////////////////// Video Encoding ////////////////////////////////// -// Works only under Windows. -// Supports only H264 video codec and AVI files. +/** @brief Codecs supported by Video writer, refer to Nvidia's GPU Support Matrix to confirm your GPU supports them. +*/ +enum class VideoWriterCodec +{ + H264 = 0, + HEVC = 1, + NumCodecs = 2 +}; + +/** @brief OpenCV color formats. +*/ +enum COLOR_FORMAT_CV { + UNDEFINED = 0, + BGR = 1, //!< Default OpenCV color format. + RGB = 2, + BGRA = 3, + RGBA = 4, + GRAY = 5 +}; -enum SurfaceFormat +/** @brief Nvidia Video Codec SDK surface formats. +*/ +enum ENC_BUFFER_FORMAT { - SF_UYVY = 0, - SF_YUY2, - SF_YV12, - SF_NV12, - SF_IYUV, - SF_BGR, - SF_GRAY = SF_BGR + BF_UNDEFINED = 0x00000000, //!< Undefined buffer format. + BF_NV12 = 0x00000001, //!< Semi-Planar YUV [Y plane followed by interleaved UV plane]. + BF_YV12 = 0x00000010, //!< Planar YUV [Y plane followed by V and U planes]. + BF_IYUV = 0x00000100, //!< Planar YUV [Y plane followed by U and V planes]. + BF_YUV444 = 0x00001000, //!< Planar YUV [Y plane followed by U and V planes]. + BF_YUV420_10BIT = 0x00010000, //!< 10 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. + BF_YUV444_10BIT = 0x00100000, //!< 10 bit Planar YUV444 [Y plane followed by U and V planes]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. + BF_ARGB = 0x01000000, //!< 8 bit Packed A8R8G8B8. This is a word-ordered format where a pixel is represented by a 32-bit word with B in the lowest 8 bits, G in the next 8 bits, R in the 8 bits after that and A in the highest 8 bits. + BF_ARGB10 = 0x02000000, //!< 10 bit Packed A2R10G10B10. This is a word-ordered format where a pixel is represented by a 32-bit word with B in the lowest 10 bits, G in the next 10 bits, R in the 10 bits after that and A in the highest 2 bits. + BF_AYUV = 0x04000000, //!< 8 bit Packed A8Y8U8V8. This is a word-ordered format where a pixel is represented by a 32-bit word with V in the lowest 8 bits, U in the next 8 bits, Y in the 8 bits after that and A in the highest 8 bits. + BF_ABGR = 0x10000000, //!< 8 bit Packed A8B8G8R8. This is a word-ordered format where a pixel is represented by a 32-bit word with R in the lowest 8 bits, G in the next 8 bits, B in the 8 bits after that and A in the highest 8 bits. + BF_ABGR10 = 0x20000000, //!< 10 bit Packed A2B10G10R10. This is a word-ordered format where a pixel is represented by a 32-bit word with R in the lowest 10 bits, G in the next 10 bits, B in the 10 bits after that and A in the highest 2 bits. }; -/** @brief Different parameters for CUDA video encoder. - */ -struct CV_EXPORTS_W EncoderParams +/** @brief Rate Control Modes. +*/ +enum ENC_PARAMS_RC_MODE { + ENC_PARAMS_RC_CONSTQP = 0x0, //!< Constant QP mode. + ENC_PARAMS_RC_VBR = 0x1, //!< Variable bitrate mode. + ENC_PARAMS_RC_CBR = 0x2 //!< Constant bitrate mode. +}; + +/** @brief Multi Pass Encoding. +*/ +enum ENC_MULTI_PASS { - int P_Interval; //!< NVVE_P_INTERVAL, - int IDR_Period; //!< NVVE_IDR_PERIOD, - int DynamicGOP; //!< NVVE_DYNAMIC_GOP, - int RCType; //!< NVVE_RC_TYPE, - int AvgBitrate; //!< NVVE_AVG_BITRATE, - int PeakBitrate; //!< NVVE_PEAK_BITRATE, - int QP_Level_Intra; //!< NVVE_QP_LEVEL_INTRA, - int QP_Level_InterP; //!< NVVE_QP_LEVEL_INTER_P, - int QP_Level_InterB; //!< NVVE_QP_LEVEL_INTER_B, - int DeblockMode; //!< NVVE_DEBLOCK_MODE, - int ProfileLevel; //!< NVVE_PROFILE_LEVEL, - int ForceIntra; //!< NVVE_FORCE_INTRA, - int ForceIDR; //!< NVVE_FORCE_IDR, - int ClearStat; //!< NVVE_CLEAR_STAT, - int DIMode; //!< NVVE_SET_DEINTERLACE, - int Presets; //!< NVVE_PRESETS, - int DisableCabac; //!< NVVE_DISABLE_CABAC, - int NaluFramingType; //!< NVVE_CONFIGURE_NALU_FRAMING_TYPE - int DisableSPSPPS; //!< NVVE_DISABLE_SPS_PPS - - EncoderParams(); - /** @brief Constructors. - - @param configFile Config file name. - - Creates default parameters or reads parameters from config file. - */ - explicit EncoderParams(const String& configFile); + ENC_MULTI_PASS_DISABLED = 0x0, //!< Single Pass. + ENC_TWO_PASS_QUARTER_RESOLUTION = 0x1, //!< Two Pass encoding is enabled where first Pass is quarter resolution. + ENC_TWO_PASS_FULL_RESOLUTION = 0x2, //!< Two Pass encoding is enabled where first Pass is full resolution. +}; - /** @brief Reads parameters from config file. - @param configFile Config file name. - */ - void load(const String& configFile); - /** @brief Saves parameters to config file. +/** @brief Supported Encoder Profiles. +*/ +enum ENC_PROFILE { + ENC_CODEC_PROFILE_AUTOSELECT = 0, + ENC_H264_PROFILE_BASELINE = 1, + ENC_H264_PROFILE_MAIN = 2, + ENC_H264_PROFILE_HIGH = 3, + ENC_H264_PROFILE_HIGH_444 = 4, + ENC_H264_PROFILE_STEREO = 5, + ENC_H264_PROFILE_PROGRESSIVE_HIGH = 6, + ENC_H264_PROFILE_CONSTRAINED_HIGH = 7, + ENC_HEVC_PROFILE_MAIN = 8, + ENC_HEVC_PROFILE_MAIN10 = 9, + ENC_HEVC_PROFILE_FREXT = 10 +}; - @param configFile Config file name. - */ - void save(const String& configFile) const; +/** @brief Nvidia Encoding Presets. Performance degrades and quality improves as we move from P1 to P7. +*/ +enum ENC_PRESET { + ENC_PRESET_P1 = 1, + ENC_PRESET_P2 = 2, + ENC_PRESET_P3 = 3, + ENC_PRESET_P4 = 4, + ENC_PRESET_P5 = 5, + ENC_PRESET_P6 = 6, + ENC_PRESET_P7 = 7 }; -/** @brief Callbacks for CUDA video encoder. - */ -class CV_EXPORTS_W EncoderCallBack +/** @brief Tuning information. +*/ +enum ENC_TUNING_INFO { + ENC_TUNING_INFO_UNDEFINED = 0, //!< Undefined tuningInfo. Invalid value for encoding. + ENC_TUNING_INFO_HIGH_QUALITY = 1, //!< Tune presets for latency tolerant encoding. + ENC_TUNING_INFO_LOW_LATENCY = 2, //!< Tune presets for low latency streaming. + ENC_TUNING_INFO_ULTRA_LOW_LATENCY = 3, //!< Tune presets for ultra low latency streaming. + ENC_TUNING_INFO_LOSSLESS = 4, //!< Tune presets for lossless encoding. + ENC_TUNING_INFO_COUNT +}; + +/** Quantization Parameter for each type of frame when using ENC_PARAMS_RC_MODE::ENC_PARAMS_RC_CONSTQP. +*/ +struct CV_EXPORTS_W_SIMPLE ENC_QP { -public: - enum PicType - { - IFRAME = 1, - PFRAME = 2, - BFRAME = 3 - }; + CV_PROP_RW uint32_t qpInterP; //!< Specifies QP value for P-frame. + CV_PROP_RW uint32_t qpInterB; //!< Specifies QP value for B-frame. + CV_PROP_RW uint32_t qpIntra; //!< Specifies QP value for Intra Frame. +}; - virtual ~EncoderCallBack() {} +/** @brief Different parameters for CUDA video encoder. +*/ +struct CV_EXPORTS_W_SIMPLE EncoderParams +{ +public: + CV_WRAP EncoderParams(); + CV_PROP_RW ENC_PRESET nvPreset; + CV_PROP_RW ENC_TUNING_INFO tuningInfo; + CV_PROP_RW ENC_PROFILE encodingProfile; + CV_PROP_RW ENC_PARAMS_RC_MODE rateControlMode; + CV_PROP_RW ENC_MULTI_PASS multiPassEncoding; + CV_PROP_RW ENC_QP constQp; //!< QP's for ENC_PARAMS_RC_CONSTQP. + CV_PROP_RW int averageBitRate; //!< target bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CBR. + CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CONSTQP. + CV_PROP_RW float targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with ENC_PARAMS_RC_VBR. + CV_PROP_RW int gopLength; +}; +CV_EXPORTS bool operator==(const EncoderParams& lhs, const EncoderParams& rhs); - /** @brief Callback function to signal the start of bitstream that is to be encoded. +/** @brief Interface for encoder callbacks. - Callback must allocate buffer for CUDA encoder and return pointer to it and it's size. - */ - virtual uchar* acquireBitStream(int* bufferSize) = 0; +User can implement own multiplexing by implementing this interface. +*/ +class CV_EXPORTS_W EncoderCallBack { +public: + /** @brief Callback function to signal that the encoded bitstream for one or more frames is ready. - /** @brief Callback function to signal that the encoded bitstream is ready to be written to file. + @param vPacket The raw bitstream for one or more frames. */ - virtual void releaseBitStream(unsigned char* data, int size) = 0; - - /** @brief Callback function to signal that the encoding operation on the frame has started. - - @param frameNumber - @param picType Specify frame type (I-Frame, P-Frame or B-Frame). - */ - CV_WRAP virtual void onBeginFrame(int frameNumber, EncoderCallBack::PicType picType) = 0; + virtual void onEncoded(std::vector> vPacket) = 0; - /** @brief Callback function signals that the encoding operation on the frame has finished. + /** @brief Callback function to that the encoding has finished. + * */ + virtual void onEncodingFinished() = 0; - @param frameNumber - @param picType Specify frame type (I-Frame, P-Frame or B-Frame). - */ - CV_WRAP virtual void onEndFrame(int frameNumber, EncoderCallBack::PicType picType) = 0; + virtual ~EncoderCallBack() {} }; /** @brief Video writer interface. - -The implementation uses H264 video codec. - -@note Currently only Windows platform is supported. - @note - An example on how to use the videoWriter class can be found at opencv_source_code/samples/gpu/video_writer.cpp - */ +*/ class CV_EXPORTS_W VideoWriter { public: @@ -181,66 +220,134 @@ class CV_EXPORTS_W VideoWriter /** @brief Writes the next video frame. - @param frame The written frame. - @param lastFrame Indicates that it is end of stream. The parameter can be ignored. + @param frame The framet to be written. - The method write the specified image to video file. The image must have the same size and the same + The method encodes the specified image to a video stream. The image must have the same size and the same surface format as has been specified when opening the video writer. - */ - CV_WRAP virtual void write(InputArray frame, bool lastFrame = false) = 0; + */ + CV_WRAP virtual void write(InputArray frame) = 0; + /** @brief Retrieve the encoding parameters. + */ CV_WRAP virtual EncoderParams getEncoderParams() const = 0; + + /** @brief Waits until the encoding process has finished before calling EncoderCallBack::onEncodingFinished(). + */ + CV_WRAP virtual void close() = 0; }; /** @brief Creates video writer. -@param fileName Name of the output video file. Only AVI file format is supported. +@param fileName Name of the output video file. Only raw h264 or hevc files are supported. @param frameSize Size of the input video frames. +@param codec Codec. @param fps Framerate of the created video stream. -@param format Surface format of input frames ( SF_UYVY , SF_YUY2 , SF_YV12 , SF_NV12 , -SF_IYUV , SF_BGR or SF_GRAY). BGR or gray frames will be converted to YV12 format before -encoding, frames with other formats will be used as is. +@param colorFormat OpenCv color format of the frames to be encoded. +@param stream Stream for frame pre-processing. +*/ +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, + const double fps = 25.0, const COLOR_FORMAT_CV colorFormat = BGR, const Stream& stream = Stream::Null()); -The constructors initialize video writer. FFMPEG is used to write videos. User can implement own -multiplexing with cudacodec::EncoderCallBack . - */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, Size frameSize, double fps, SurfaceFormat format = SF_BGR); -/** @overload -@param fileName Name of the output video file. Only AVI file format is supported. +/** @brief Creates video writer. + +@param fileName Name of the output video file. Only raw h264 or hevc files are supported. @param frameSize Size of the input video frames. +@param codec Codec. @param fps Framerate of the created video stream. -@param params Encoder parameters. See cudacodec::EncoderParams . -@param format Surface format of input frames ( SF_UYVY , SF_YUY2 , SF_YV12 , SF_NV12 , -SF_IYUV , SF_BGR or SF_GRAY). BGR or gray frames will be converted to YV12 format before -encoding, frames with other formats will be used as is. +@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. +@param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, Size frameSize, double fps, const EncoderParams& params, SurfaceFormat format = SF_BGR); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, + const double fps = 25.0, const ENC_BUFFER_FORMAT bufferFormat = BF_ARGB, const Stream& stream = Stream::Null()); -/** @overload -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you -want to work with raw video stream. +/** @brief Creates video writer. + +@param fileName Name of the output video file. Only raw h264 or hevc files are supported. @param frameSize Size of the input video frames. +@param codec Codec. @param fps Framerate of the created video stream. -@param format Surface format of input frames ( SF_UYVY , SF_YUY2 , SF_YV12 , SF_NV12 , -SF_IYUV , SF_BGR or SF_GRAY). BGR or gray frames will be converted to YV12 format before -encoding, frames with other formats will be used as is. +@param colorFormat OpenCv color format of the frames to be encoded. +@param params Additional encoding parameters. +@param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, Size frameSize, double fps, SurfaceFormat format = SF_BGR); -/** @overload -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you -want to work with raw video stream. +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, + const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); + + +/** @brief Creates video writer. + +@param fileName Name of the output video file. Only raw h264 or hevc files are supported. +@param frameSize Size of the input video frames. +@param codec Codec. +@param fps Framerate of the created video stream. +@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. +@param params Additional encoding parameters. +@param stream Stream for frame pre-processing. +*/ +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, + const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); + +/** @brief Creates video writer. + +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param frameSize Size of the input video frames. +@param codec Codec. +@param fps Framerate of the created video stream. +@param colorFormat OpenCv color format of the frames to be encoded. +@param stream Stream for frame pre-processing. + +The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallBack. +*/ +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, + const double fps = 25.0, const COLOR_FORMAT_CV colorFormat = BGR, const Stream& stream = Stream::Null()); + +/** @brief Creates video writer. + +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. @param frameSize Size of the input video frames. +@param codec Codec. @param fps Framerate of the created video stream. -@param params Encoder parameters. See cudacodec::EncoderParams. -@param format Surface format of input frames ( SF_UYVY , SF_YUY2 , SF_YV12 , SF_NV12 , -SF_IYUV , SF_BGR or SF_GRAY). BGR or gray frames will be converted to YV12 format before -encoding, frames with other formats will be used as is. +@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. +@param stream Stream for frame pre-processing. + +The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallBack. +*/ +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, + const double fps = 25.0, const ENC_BUFFER_FORMAT bufferFormat = BF_ARGB, const Stream& stream = Stream::Null()); + +/** @brief Creates video writer. + +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param frameSize Size of the input video frames. +@param codec Codec. +@param fps Framerate of the created video stream. +@param colorFormat OpenCv color format of the frames to be encoded. +@param params Additional encoding parameters. +@param stream Stream for frame pre-processing. + +The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallBack. +*/ +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, + const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); + +/** @brief Creates video writer. + +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param frameSize Size of the input video frames. +@param codec Codec. +@param fps Framerate of the created video stream. +@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. +@param params Additional encoding parameters. +@param stream Stream for frame pre-processing. + +The constructors initialize video writer. User can implement own their multiplexing with cudacodec::EncoderCallBack. */ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, Size frameSize, double fps, const EncoderParams& params, SurfaceFormat format = SF_BGR); +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, + const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); ////////////////////////////////// Video Decoding ////////////////////////////////////////// -/** @brief Video codecs supported by cudacodec::VideoReader . +/** @brief Video codecs supported by cudacodec::VideoReader. */ enum Codec { diff --git a/modules/cudacodec/misc/python/pyopencv_cudacodec.hpp b/modules/cudacodec/misc/python/pyopencv_cudacodec.hpp deleted file mode 100644 index 15fd43de42..0000000000 --- a/modules/cudacodec/misc/python/pyopencv_cudacodec.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifdef HAVE_OPENCV_CUDACODEC - -#include "opencv2/cudacodec.hpp" - -typedef cudacodec::EncoderCallBack::PicType EncoderCallBack_PicType; - -CV_PY_TO_CLASS(cudacodec::EncoderParams); -CV_PY_FROM_CLASS(cudacodec::EncoderParams); - -#endif diff --git a/modules/cudacodec/misc/python/test/test_cudacodec.py b/modules/cudacodec/misc/python/test/test_cudacodec.py index dc9f7a40aa..21d3d33a57 100644 --- a/modules/cudacodec/misc/python/test/test_cudacodec.py +++ b/modules/cudacodec/misc/python/test/test_cudacodec.py @@ -2,7 +2,7 @@ import os import cv2 as cv import numpy as np - +import tempfile from tests_common import NewOpenCVTests, unittest class cudacodec_test(NewOpenCVTests): @@ -79,12 +79,25 @@ def test_writer_existence(self): #Test at least the existence of wrapped functions for now try: - _writer = cv.cudacodec.createVideoWriter("tmp", (128, 128), 30) + _, fname = tempfile.mkstemp(suffix=".h264") + encoder_params_in = cv.cudacodec.EncoderParams() + encoder_params_in.gopLength = 10 + stream = cv.cuda.Stream() + sz = (1920,1080) + writer = cv.cudacodec.createVideoWriter(fname, sz, cv.cudacodec.VideoWriterCodec_H264, 30, cv.cudacodec.ColorFormat_BGR, + encoder_params_in, stream) + blankFrameIn = cv.cuda.GpuMat(sz,cv.CV_8UC3) + writer.write(blankFrameIn) + writer.close() + encoder_params_out = writer.getEncoderParams() + self.assert_true(encoder_params_in.gopLength == encoder_params_out.gopLength) + cap = cv.VideoCapture(fname,cv.CAP_FFMPEG) + self.assert_true(cap.isOpened()) + ret, blankFrameOut = cap.read() + self.assert_true(ret and blankFrameOut.shape == blankFrameIn.download().shape) except cv.error as e: self.assertEqual(e.code, cv.Error.StsNotImplemented) - self.skipTest("NVCUVENC is not installed") - - self.assertTrue(True) #It is sufficient that no exceptions have been there + self.skipTest("Either NVCUVENC or a GPU hardware encoder is missing or the encoding profile is not supported.") if __name__ == '__main__': NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudacodec/perf/perf_video.cpp b/modules/cudacodec/perf/perf_video.cpp index af7b2f67c9..ba0f7b7d2c 100644 --- a/modules/cudacodec/perf/perf_video.cpp +++ b/modules/cudacodec/perf/perf_video.cpp @@ -45,7 +45,7 @@ namespace opencv_test { namespace { -#if defined(HAVE_NVCUVID) +#if defined(HAVE_NVCUVID) || defined(HAVE_NVCUVENC) #if defined(HAVE_FFMPEG_WRAPPER) // should this be set in preprocessor or in cvconfig.h #define VIDEO_SRC Values("cv/video/768x576.avi", "cv/video/1920x1080.avi") @@ -54,6 +54,8 @@ namespace opencv_test { namespace { #define VIDEO_SRC Values( "cv/video/1920x1080.avi") #endif +#if defined(HAVE_NVCUVID) + DEF_PARAM_TEST_1(FileName, string); ////////////////////////////////////////////////////// @@ -93,63 +95,93 @@ PERF_TEST_P(FileName, VideoReader, VIDEO_SRC) ////////////////////////////////////////////////////// // VideoWriter -#if defined(HAVE_NVCUVID) && defined(_WIN32) +#if defined(HAVE_NVCUVENC) -PERF_TEST_P(FileName, VideoWriter, VIDEO_SRC) -{ - declare.time(30); - - const string inputFile = perf::TestBase::getDataPath(GetParam()); - const string outputFile = cv::tempfile(".avi"); +DEF_PARAM_TEST(WriteToFile, string, cv::cudacodec::COLOR_FORMAT_CV, cv::cudacodec::VideoWriterCodec); - const double FPS = 25.0; +#define COLOR_FORMAT Values(cv::cudacodec::COLOR_FORMAT_CV::BGR, cv::cudacodec::COLOR_FORMAT_CV::RGB, cv::cudacodec::COLOR_FORMAT_CV::BGRA, \ +cv::cudacodec::COLOR_FORMAT_CV::RGBA, cv::cudacodec::COLOR_FORMAT_CV::GRAY) +#define CODEC Values(cv::cudacodec::VideoWriterCodec::H264, cv::cudacodec::VideoWriterCodec::HEVC) +PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) +{ + declare.time(30); + const string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); + const cv::cudacodec::COLOR_FORMAT_CV surfaceFormat = GET_PARAM(1); + const cudacodec::VideoWriterCodec codec = GET_PARAM(2); + const double fps = 25; + const int nFrames = 20; cv::VideoCapture reader(inputFile); - ASSERT_TRUE( reader.isOpened() ); - - cv::Mat frame; - - if (PERF_RUN_CUDA()) - { - cv::Ptr d_writer; - - cv::cuda::GpuMat d_frame; - - for (int i = 0; i < 10; ++i) - { - reader >> frame; - ASSERT_FALSE(frame.empty()); - - d_frame.upload(frame); - - if (d_writer.empty()) - d_writer = cv::cudacodec::createVideoWriter(outputFile, frame.size(), FPS); - - startTimer(); next(); - d_writer->write(d_frame); + ASSERT_TRUE(reader.isOpened()); + Mat frameBgr; + if (PERF_RUN_CUDA()) { + const std::string ext = codec == cudacodec::VideoWriterCodec::H264 ? ".h264" : ".hevc"; + const string outputFile = cv::tempfile(ext.c_str()); + std::vector frames; + cv::Mat frameNewSf; + cuda::Stream stream; + ColorConversionCodes conversionCode = COLOR_COLORCVT_MAX; + switch (surfaceFormat) { + case cudacodec::COLOR_FORMAT_CV::RGB: + conversionCode = COLOR_BGR2RGB; + break; + case cudacodec::COLOR_FORMAT_CV::BGRA: + conversionCode = COLOR_BGR2BGRA; + break; + case cudacodec::COLOR_FORMAT_CV::RGBA: + conversionCode = COLOR_BGR2RGBA; + break; + case cudacodec::COLOR_FORMAT_CV::GRAY: + conversionCode = COLOR_BGR2GRAY; + default: + break; + } + for (int i = 0; i < nFrames; i++) { + reader >> frameBgr; + ASSERT_FALSE(frameBgr.empty()); + if (conversionCode == COLOR_COLORCVT_MAX) + frameNewSf = frameBgr; + else + cv::cvtColor(frameBgr, frameNewSf, conversionCode); + GpuMat frame; frame.upload(frameNewSf, stream); + frames.push_back(frame); + } + stream.waitForCompletion(); + cv::Ptr d_writer = cv::cudacodec::createVideoWriter(outputFile, frameBgr.size(), codec, fps, surfaceFormat, stream); + for (int i = 0; i < nFrames - 1; ++i) { + startTimer(); + d_writer->write(frames[i]); stopTimer(); } + startTimer(); + d_writer->write(frames[nFrames - 1]); + d_writer->close(); + stopTimer(); } - else - { + else { + if (surfaceFormat != cv::cudacodec::COLOR_FORMAT_CV::BGR || codec != cv::cudacodec::VideoWriterCodec::H264) + throw PerfSkipTestException(); cv::VideoWriter writer; - - for (int i = 0; i < 10; ++i) - { - reader >> frame; - ASSERT_FALSE(frame.empty()); - + const string outputFile = cv::tempfile(".avi"); + for (int i = 0; i < nFrames-1; ++i) { + reader >> frameBgr; + ASSERT_FALSE(frameBgr.empty()); if (!writer.isOpened()) - writer.open(outputFile, VideoWriter::fourcc('X', 'V', 'I', 'D'), FPS, frame.size()); - - startTimer(); next(); - writer.write(frame); + writer.open(outputFile, VideoWriter::fourcc('x', '2', '6', '4'), fps, frameBgr.size()); + startTimer(); + writer.write(frameBgr); stopTimer(); } + reader >> frameBgr; + ASSERT_FALSE(frameBgr.empty()); + startTimer(); + writer.write(frameBgr); + writer.release(); + stopTimer(); } - - SANITY_CHECK(frame); + SANITY_CHECK(frameBgr); } +#endif #endif }} // namespace diff --git a/modules/cudacodec/src/NvEncoder.cpp b/modules/cudacodec/src/NvEncoder.cpp new file mode 100644 index 0000000000..0a3f12079c --- /dev/null +++ b/modules/cudacodec/src/NvEncoder.cpp @@ -0,0 +1,779 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "precomp.hpp" + +#if defined(HAVE_NVCUVENC) +#include "NvEncoder.h" + +namespace cv { namespace cudacodec { +#ifndef _WIN32 +#include +static inline bool operator==(const GUID& guid1, const GUID& guid2) { + return !memcmp(&guid1, &guid2, sizeof(GUID)); +} + +static inline bool operator!=(const GUID& guid1, const GUID& guid2) { + return !(guid1 == guid2); +} +#endif + +NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay) : + m_hEncoder(nullptr), + m_pDevice(pDevice), + m_eDeviceType(eDeviceType), + m_nWidth(nWidth), + m_nHeight(nHeight), + m_nMaxEncodeWidth(nWidth), + m_nMaxEncodeHeight(nHeight), + m_eBufferFormat(eBufferFormat), + m_nExtraOutputDelay(nExtraOutputDelay) +{ + LoadNvEncApi(); + + if (!m_nvenc.nvEncOpenEncodeSession) + { + m_nEncoderBuffer = 0; + NVENC_THROW_ERROR("EncodeAPI not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER }; + encodeSessionExParams.device = m_pDevice; + encodeSessionExParams.deviceType = m_eDeviceType; + encodeSessionExParams.apiVersion = NVENCAPI_VERSION; + void* hEncoder = NULL; + NVENC_API_CALL(m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder)); + m_hEncoder = hEncoder; +} + +void NvEncoder::LoadNvEncApi() +{ + + uint32_t version = 0; + uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version)); + if (currentVersion > version) + { + NVENC_THROW_ERROR("Current Driver Version does not support this NvEncodeAPI version, please upgrade driver", NV_ENC_ERR_INVALID_VERSION); + } + + + m_nvenc = { NV_ENCODE_API_FUNCTION_LIST_VER }; + NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc)); +} + +NvEncoder::~NvEncoder() +{ + DestroyHWEncoder(); +} + +void NvEncoder::CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, GUID presetGuid, NV_ENC_TUNING_INFO tuningInfo) +{ + if (!m_hEncoder) + { + NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_NO_ENCODE_DEVICE); + return; + } + + if (pIntializeParams == nullptr || pIntializeParams->encodeConfig == nullptr) + { + NVENC_THROW_ERROR("pInitializeParams and pInitializeParams->encodeConfig can't be NULL", NV_ENC_ERR_INVALID_PTR); + } + + memset(pIntializeParams->encodeConfig, 0, sizeof(NV_ENC_CONFIG)); + auto pEncodeConfig = pIntializeParams->encodeConfig; + memset(pIntializeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS)); + pIntializeParams->encodeConfig = pEncodeConfig; + + + pIntializeParams->encodeConfig->version = NV_ENC_CONFIG_VER; + pIntializeParams->version = NV_ENC_INITIALIZE_PARAMS_VER; + + pIntializeParams->encodeGUID = codecGuid; + pIntializeParams->presetGUID = presetGuid; + pIntializeParams->encodeWidth = m_nWidth; + pIntializeParams->encodeHeight = m_nHeight; + pIntializeParams->darWidth = m_nWidth; + pIntializeParams->darHeight = m_nHeight; + pIntializeParams->frameRateNum = 30; + pIntializeParams->frameRateDen = 1; + pIntializeParams->enablePTD = 1; + pIntializeParams->reportSliceOffsets = 0; + pIntializeParams->enableSubFrameWrite = 0; + pIntializeParams->maxEncodeWidth = m_nWidth; + pIntializeParams->maxEncodeHeight = m_nHeight; + pIntializeParams->enableMEOnlyMode = false; + pIntializeParams->enableOutputInVidmem = false; +#if defined(_WIN32) + pIntializeParams->enableEncodeAsync = GetCapabilityValue(codecGuid, NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT); +#endif + pIntializeParams->tuningInfo = tuningInfo; + NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } }; + m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, tuningInfo, &presetConfig); + memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); + + if (pIntializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID) + { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + { + pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC = 3; + } + pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.idrPeriod = pIntializeParams->encodeConfig->gopLength; + } + else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) + { + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) ? 2 : 0; + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + { + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC = 3; + } + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = pIntializeParams->encodeConfig->gopLength; + } + + return; +} + +void NvEncoder::CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncoderParams) +{ + if (!m_hEncoder) + { + NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + if (!pEncoderParams) + { + NVENC_THROW_ERROR("Invalid NV_ENC_INITIALIZE_PARAMS ptr", NV_ENC_ERR_INVALID_PTR); + } + + if (pEncoderParams->encodeWidth == 0 || pEncoderParams->encodeHeight == 0) + { + NVENC_THROW_ERROR("Invalid encoder width and height", NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeGUID != NV_ENC_CODEC_H264_GUID && pEncoderParams->encodeGUID != NV_ENC_CODEC_HEVC_GUID) + { + NVENC_THROW_ERROR("Invalid codec guid", NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) + { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + { + NVENC_THROW_ERROR("10-bit format isn't supported by H264 encoder", NV_ENC_ERR_INVALID_PARAM); + } + } + + // set other necessary params if not set yet + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) + { + if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444) && + (pEncoderParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC != 3)) + { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) + { + bool yuv10BitFormat = (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) ? true : false; + if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 != 2) + { + NVENC_THROW_ERROR("Invalid PixelBitdepth", NV_ENC_ERR_INVALID_PARAM); + } + + if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) && + (pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC != 3)) + { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + } + + memcpy(&m_initializeParams, pEncoderParams, sizeof(m_initializeParams)); + m_initializeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; + + if (pEncoderParams->encodeConfig) + { + memcpy(&m_encodeConfig, pEncoderParams->encodeConfig, sizeof(m_encodeConfig)); + m_encodeConfig.version = NV_ENC_CONFIG_VER; + } + else + { + NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } }; + m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, pEncoderParams->encodeGUID, pEncoderParams->presetGUID, pEncoderParams->tuningInfo, &presetConfig); + memcpy(&m_encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); + } + m_initializeParams.encodeConfig = &m_encodeConfig; + NVENC_API_CALL(m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams)); + m_bEncoderInitialized = true; + m_nWidth = m_initializeParams.encodeWidth; + m_nHeight = m_initializeParams.encodeHeight; + m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; + m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; + + m_nEncoderBuffer = m_encodeConfig.frameIntervalP + m_encodeConfig.rcParams.lookaheadDepth + m_nExtraOutputDelay; + m_nOutputDelay = m_nEncoderBuffer - 1; + + m_vpCompletionEvent.resize(m_nEncoderBuffer, nullptr); + +#if defined(_WIN32) + for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) + { + m_vpCompletionEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); + NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER }; + eventParams.completionEvent = m_vpCompletionEvent[i]; + m_nvenc.nvEncRegisterAsyncEvent(m_hEncoder, &eventParams); + } +#endif + + m_vMappedInputBuffers.resize(m_nEncoderBuffer, nullptr); + m_vBitstreamOutputBuffer.resize(m_nEncoderBuffer, nullptr); + InitializeBitstreamBuffer(); + AllocateInputBuffers(m_nEncoderBuffer); +} + +void NvEncoder::DestroyEncoder() +{ + if (!m_hEncoder) + { + return; + } + + ReleaseInputBuffers(); + + DestroyHWEncoder(); +} + +void NvEncoder::DestroyHWEncoder() +{ + if (!m_hEncoder) + { + return; + } + +#if defined(_WIN32) + for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) + { + if (m_vpCompletionEvent[i]) + { + NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER }; + eventParams.completionEvent = m_vpCompletionEvent[i]; + m_nvenc.nvEncUnregisterAsyncEvent(m_hEncoder, &eventParams); + CloseHandle(m_vpCompletionEvent[i]); + } + } + m_vpCompletionEvent.clear(); +#endif + + DestroyBitstreamBuffer(); + + m_nvenc.nvEncDestroyEncoder(m_hEncoder); + + m_hEncoder = nullptr; + + m_bEncoderInitialized = false; +} + +const NvEncInputFrame* NvEncoder::GetNextInputFrame() +{ + int i = m_iToSend % m_nEncoderBuffer; + return &m_vInputFrames[i]; +} + +void NvEncoder::MapResources(uint32_t bfrIdx) +{ + NV_ENC_MAP_INPUT_RESOURCE mapInputResource = { NV_ENC_MAP_INPUT_RESOURCE_VER }; + + mapInputResource.registeredResource = m_vRegisteredResources[bfrIdx]; + NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); + m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource; +} + +void NvEncoder::EncodeFrame(std::vector>& vPacket, NV_ENC_PIC_PARAMS* pPicParams) +{ + vPacket.clear(); + if (!IsHWEncoderInitialized()) + { + NVENC_THROW_ERROR("Encoder device not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + int bfrIdx = m_iToSend % m_nEncoderBuffer; + + MapResources(bfrIdx); + + NVENCSTATUS nvStatus = DoEncode(m_vMappedInputBuffers[bfrIdx], m_vBitstreamOutputBuffer[bfrIdx], pPicParams); + + if (nvStatus == NV_ENC_SUCCESS || nvStatus == NV_ENC_ERR_NEED_MORE_INPUT) + { + m_iToSend++; + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, true); + } + else + { + NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); + } +} + +void NvEncoder::GetSequenceParams(std::vector& seqParams) +{ + uint8_t spsppsData[1024]; // Assume maximum spspps data is 1KB or less + memset(spsppsData, 0, sizeof(spsppsData)); + NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER }; + uint32_t spsppsSize = 0; + + payload.spsppsBuffer = spsppsData; + payload.inBufferSize = sizeof(spsppsData); + payload.outSPSPPSPayloadSize = &spsppsSize; + NVENC_API_CALL(m_nvenc.nvEncGetSequenceParams(m_hEncoder, &payload)); + seqParams.clear(); + seqParams.insert(seqParams.end(), &spsppsData[0], &spsppsData[spsppsSize]); +} + +NVENCSTATUS NvEncoder::DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR outputBuffer, NV_ENC_PIC_PARAMS* pPicParams) +{ + NV_ENC_PIC_PARAMS picParams = {}; + if (pPicParams) + { + picParams = *pPicParams; + } + picParams.version = NV_ENC_PIC_PARAMS_VER; + picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + picParams.inputBuffer = inputBuffer; + picParams.bufferFmt = GetPixelFormat(); + picParams.inputWidth = GetEncodeWidth(); + picParams.inputHeight = GetEncodeHeight(); + picParams.outputBitstream = outputBuffer; + picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENCSTATUS nvStatus = m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams); + + return nvStatus; +} + +void NvEncoder::SendEOS() +{ + NV_ENC_PIC_PARAMS picParams = { NV_ENC_PIC_PARAMS_VER }; + picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS; + picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams)); +} + +void NvEncoder::EndEncode(std::vector>& vPacket) +{ + vPacket.clear(); + if (!IsHWEncoderInitialized()) + { + NVENC_THROW_ERROR("Encoder device not initialized", NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + } + + SendEOS(); + + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, false); +} + +void NvEncoder::GetEncodedPacket(std::vector& vOutputBuffer, std::vector>& vPacket, bool bOutputDelay) +{ + unsigned i = 0; + int iEnd = bOutputDelay ? m_iToSend - m_nOutputDelay : m_iToSend; + for (; m_iGot < iEnd; m_iGot++) + { + WaitForCompletionEvent(m_iGot % m_nEncoderBuffer); + NV_ENC_LOCK_BITSTREAM lockBitstreamData = { NV_ENC_LOCK_BITSTREAM_VER }; + lockBitstreamData.outputBitstream = vOutputBuffer[m_iGot % m_nEncoderBuffer]; + lockBitstreamData.doNotWait = false; + NVENC_API_CALL(m_nvenc.nvEncLockBitstream(m_hEncoder, &lockBitstreamData)); + + uint8_t* pData = (uint8_t*)lockBitstreamData.bitstreamBufferPtr; + if (vPacket.size() < i + 1) + { + vPacket.push_back(std::vector()); + } + vPacket[i].clear(); + vPacket[i].insert(vPacket[i].end(), &pData[0], &pData[lockBitstreamData.bitstreamSizeInBytes]); + i++; + + NVENC_API_CALL(m_nvenc.nvEncUnlockBitstream(m_hEncoder, lockBitstreamData.outputBitstream)); + + if (m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer]) + { + NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer])); + m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer] = nullptr; + } + } +} + +bool NvEncoder::Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams) +{ + NVENC_API_CALL(m_nvenc.nvEncReconfigureEncoder(m_hEncoder, const_cast(pReconfigureParams))); + + memcpy(&m_initializeParams, &(pReconfigureParams->reInitEncodeParams), sizeof(m_initializeParams)); + if (pReconfigureParams->reInitEncodeParams.encodeConfig) + { + memcpy(&m_encodeConfig, pReconfigureParams->reInitEncodeParams.encodeConfig, sizeof(m_encodeConfig)); + } + + m_nWidth = m_initializeParams.encodeWidth; + m_nHeight = m_initializeParams.encodeHeight; + m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; + m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; + + return true; +} + +NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource(void* pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage, + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint) +{ + NV_ENC_REGISTER_RESOURCE registerResource = { NV_ENC_REGISTER_RESOURCE_VER }; + registerResource.resourceType = eResourceType; + registerResource.resourceToRegister = pBuffer; + registerResource.width = width; + registerResource.height = height; + registerResource.pitch = pitch; + registerResource.bufferFormat = bufferFormat; + registerResource.bufferUsage = bufferUsage; + registerResource.pInputFencePoint = pInputFencePoint; + registerResource.pOutputFencePoint = pOutputFencePoint; + NVENC_API_CALL(m_nvenc.nvEncRegisterResource(m_hEncoder, ®isterResource)); + + return registerResource.registeredResource; +} + +void NvEncoder::RegisterInputResources(std::vector inputframes, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, bool bReferenceFrame) +{ + for (uint32_t i = 0; i < inputframes.size(); ++i) + { + NV_ENC_REGISTERED_PTR registeredPtr = RegisterResource(inputframes[i], eResourceType, width, height, pitch, bufferFormat, NV_ENC_INPUT_IMAGE); + + std::vector _chromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(bufferFormat, pitch, height, _chromaOffsets); + NvEncInputFrame inputframe = {}; + inputframe.inputPtr = (void*)inputframes[i]; + inputframe.chromaOffsets[0] = 0; + inputframe.chromaOffsets[1] = 0; + for (uint32_t ch = 0; ch < _chromaOffsets.size(); ch++) + { + inputframe.chromaOffsets[ch] = _chromaOffsets[ch]; + } + inputframe.numChromaPlanes = NvEncoder::GetNumChromaPlanes(bufferFormat); + inputframe.pitch = pitch; + inputframe.chromaPitch = NvEncoder::GetChromaPitch(bufferFormat, pitch); + inputframe.bufferFormat = bufferFormat; + inputframe.resourceType = eResourceType; + + if (bReferenceFrame) + { + m_vRegisteredResourcesForReference.push_back(registeredPtr); + m_vReferenceFrames.push_back(inputframe); + } + else + { + m_vRegisteredResources.push_back(registeredPtr); + m_vInputFrames.push_back(inputframe); + } + } +} + +void NvEncoder::FlushEncoder() +{ + try + { + std::vector> vPacket; + EndEncode(vPacket); + } + catch (...) + { + + } +} + +void NvEncoder::UnregisterInputResources() +{ + FlushEncoder(); + + m_vMappedRefBuffers.clear(); + + for (uint32_t i = 0; i < m_vMappedInputBuffers.size(); ++i) + { + if (m_vMappedInputBuffers[i]) + { + m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[i]); + } + } + m_vMappedInputBuffers.clear(); + + for (uint32_t i = 0; i < m_vRegisteredResources.size(); ++i) + { + if (m_vRegisteredResources[i]) + { + m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResources[i]); + } + } + m_vRegisteredResources.clear(); + + + for (uint32_t i = 0; i < m_vRegisteredResourcesForReference.size(); ++i) + { + if (m_vRegisteredResourcesForReference[i]) + { + m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResourcesForReference[i]); + } + } + m_vRegisteredResourcesForReference.clear(); + +} + + +void NvEncoder::WaitForCompletionEvent(int iEvent) +{ +#if defined(_WIN32) + // Check if we are in async mode. If not, don't wait for event; + NV_ENC_CONFIG sEncodeConfig = { 0 }; + NV_ENC_INITIALIZE_PARAMS sInitializeParams = { 0 }; + sInitializeParams.encodeConfig = &sEncodeConfig; + GetInitializeParams(&sInitializeParams); + + if (0U == sInitializeParams.enableEncodeAsync) + { + return; + } +#ifdef DEBUG + WaitForSingleObject(m_vpCompletionEvent[iEvent], INFINITE); +#else + // wait for 20s which is infinite on terms of gpu time + if (WaitForSingleObject(m_vpCompletionEvent[iEvent], 20000) == WAIT_FAILED) + { + NVENC_THROW_ERROR("Failed to encode frame", NV_ENC_ERR_GENERIC); + } +#endif +#endif +} + +uint32_t NvEncoder::GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t width) +{ + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_YUV444: + return width; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return width * 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return width * 4; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +uint32_t NvEncoder::GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat) +{ + switch (bufferFormat) + { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 1; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + } +} + +uint32_t NvEncoder::GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaPitch) +{ + switch (bufferFormat) + { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return lumaPitch; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + return (lumaPitch + 1) / 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + } +} + +void NvEncoder::GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch, const uint32_t height, std::vector& chromaOffsets) +{ + chromaOffsets.clear(); + switch (bufferFormat) + { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + chromaOffsets.push_back(pitch * height); + return; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + chromaOffsets.push_back(pitch * height); + chromaOffsets.push_back(chromaOffsets[0] + (NvEncoder::GetChromaPitch(bufferFormat, pitch) * GetChromaHeight(bufferFormat, height))); + return; + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + chromaOffsets.push_back(pitch * height); + chromaOffsets.push_back(chromaOffsets[0] + (pitch * height)); + return; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return; + } +} + +uint32_t NvEncoder::GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaHeight) +{ + switch (bufferFormat) + { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return (lumaHeight + 1) / 2; + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return lumaHeight; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +uint32_t NvEncoder::GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth) +{ + switch (bufferFormat) + { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + return (lumaWidth + 1) / 2; + case NV_ENC_BUFFER_FORMAT_NV12: + return lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 2 * lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV444: + return lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2 * lumaWidth; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + + +int NvEncoder::GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery) +{ + if (!m_hEncoder) + { + return 0; + } + NV_ENC_CAPS_PARAM capsParam = { NV_ENC_CAPS_PARAM_VER }; + capsParam.capsToQuery = capsToQuery; + int v; + m_nvenc.nvEncGetEncodeCaps(m_hEncoder, guidCodec, &capsParam, &v); + return v; +} + +int NvEncoder::GetFrameSize() const +{ + switch (GetPixelFormat()) + { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_NV12: + return GetEncodeWidth() * (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 2 * GetEncodeWidth() * (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); + case NV_ENC_BUFFER_FORMAT_YUV444: + return GetEncodeWidth() * GetEncodeHeight() * 3; + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2 * GetEncodeWidth() * GetEncodeHeight() * 3; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 4 * GetEncodeWidth() * GetEncodeHeight(); + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +void NvEncoder::GetInitializeParams(NV_ENC_INITIALIZE_PARAMS* pInitializeParams) +{ + if (!pInitializeParams || !pInitializeParams->encodeConfig) + { + NVENC_THROW_ERROR("Both pInitializeParams and pInitializeParams->encodeConfig can't be NULL", NV_ENC_ERR_INVALID_PTR); + } + NV_ENC_CONFIG* pEncodeConfig = pInitializeParams->encodeConfig; + *pEncodeConfig = m_encodeConfig; + *pInitializeParams = m_initializeParams; + pInitializeParams->encodeConfig = pEncodeConfig; +} + +void NvEncoder::InitializeBitstreamBuffer() +{ + for (int i = 0; i < m_nEncoderBuffer; i++) + { + NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER }; + NVENC_API_CALL(m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer)); + m_vBitstreamOutputBuffer[i] = createBitstreamBuffer.bitstreamBuffer; + } +} + +void NvEncoder::DestroyBitstreamBuffer() +{ + for (uint32_t i = 0; i < m_vBitstreamOutputBuffer.size(); i++) + { + if (m_vBitstreamOutputBuffer[i]) + { + m_nvenc.nvEncDestroyBitstreamBuffer(m_hEncoder, m_vBitstreamOutputBuffer[i]); + } + } + + m_vBitstreamOutputBuffer.clear(); +} +}} +#endif \ No newline at end of file diff --git a/modules/cudacodec/src/NvEncoder.h b/modules/cudacodec/src/NvEncoder.h new file mode 100644 index 0000000000..c8c281e95a --- /dev/null +++ b/modules/cudacodec/src/NvEncoder.h @@ -0,0 +1,377 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_NVENCODER_HPP +#define OPENCV_NVENCODER_HPP +#include +#include "nvEncodeAPI.h" +#include +#include +#include +#include +#include +#include + +namespace cv { namespace cudacodec { + +#define NVENC_THROW_ERROR( errorStr, errorCode ) \ +do \ +{ \ +cv::String msg = cv::format("%s [Code = %d]", errorStr, errorCode); \ +cv::error(cv::Error::GpuApiCallError, msg, __FUNCTION__, __FILE__, __LINE__); \ +} while (0) + + +#define NVENC_API_CALL( nvencAPI ) \ +do \ +{ \ +NVENCSTATUS errorCode = nvencAPI; \ +if( errorCode != NV_ENC_SUCCESS) \ +{ \ +cv::String msg = cv::format("NVENC returned error [Code = %d]", errorCode); \ +cv::error(cv::Error::GpuApiCallError, msg, __FUNCTION__, __FILE__, __LINE__); \ +} \ +} while (0) + +struct NvEncInputFrame +{ + void* inputPtr = nullptr; + uint32_t chromaOffsets[2]; + uint32_t numChromaPlanes; + uint32_t pitch; + uint32_t chromaPitch; + NV_ENC_BUFFER_FORMAT bufferFormat; + NV_ENC_INPUT_RESOURCE_TYPE resourceType; +}; + +/** +* @brief Shared base class for different encoder interfaces. +*/ +class NvEncoder +{ +public: + /** + * @brief This function is used to initialize the encoder session. + * Application must call this function to initialize the encoder, before + * starting to encode any frames. + */ + virtual void CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncodeParams); + + /** + * @brief This function is used to destroy the encoder session. + * Application must call this function to destroy the encoder session and + * clean up any allocated resources. The application must call EndEncode() + * function to get any queued encoded frames before calling DestroyEncoder(). + */ + virtual void DestroyEncoder(); + + /** + * @brief This function is used to reconfigure an existing encoder session. + * Application can use this function to dynamically change the bitrate, + * resolution and other QOS parameters. If the application changes the + * resolution, it must set NV_ENC_RECONFIGURE_PARAMS::forceIDR. + */ + bool Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams); + + /** + * @brief This function is used to get the next available input buffer. + * Applications must call this function to obtain a pointer to the next + * input buffer. The application must copy the uncompressed data to the + * input buffer and then call EncodeFrame() function to encode it. + */ + const NvEncInputFrame* GetNextInputFrame(); + + + /** + * @brief This function is used to encode a frame. + * Applications must call EncodeFrame() function to encode the uncompressed + * data, which has been copied to an input buffer obtained from the + * GetNextInputFrame() function. + */ + virtual void EncodeFrame(std::vector>& vPacket, NV_ENC_PIC_PARAMS* pPicParams = nullptr); + + /** + * @brief This function to flush the encoder queue. + * The encoder might be queuing frames for B picture encoding or lookahead; + * the application must call EndEncode() to get all the queued encoded frames + * from the encoder. The application must call this function before destroying + * an encoder session. + */ + virtual void EndEncode(std::vector>& vPacket); + + /** + * @brief This function is used to query hardware encoder capabilities. + * Applications can call this function to query capabilities like maximum encode + * dimensions, support for lookahead or the ME-only mode etc. + */ + int GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery); + + /** + * @brief This function is used to get the current device on which encoder is running. + */ + void* GetDevice() const { return m_pDevice; } + + /** + * @brief This function is used to get the current device type which encoder is running. + */ + NV_ENC_DEVICE_TYPE GetDeviceType() const { return m_eDeviceType; } + + /** + * @brief This function is used to get the current encode width. + * The encode width can be modified by Reconfigure() function. + */ + int GetEncodeWidth() const { return m_nWidth; } + + /** + * @brief This function is used to get the current encode height. + * The encode height can be modified by Reconfigure() function. + */ + int GetEncodeHeight() const { return m_nHeight; } + + /** + * @brief This function is used to get the current frame size based on pixel format. + */ + int GetFrameSize() const; + + /** + * @brief This function is used to initialize config parameters based on + * given codec and preset guids. + * The application can call this function to get the default configuration + * for a certain preset. The application can either use these parameters + * directly or override them with application-specific settings before + * using them in CreateEncoder() function. + */ + void CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, GUID presetGuid, NV_ENC_TUNING_INFO tuningInfo = NV_ENC_TUNING_INFO_UNDEFINED); + + /** + * @brief This function is used to get the current initialization parameters, + * which had been used to configure the encoder session. + * The initialization parameters are modified if the application calls + * Reconfigure() function. + */ + void GetInitializeParams(NV_ENC_INITIALIZE_PARAMS* pInitializeParams); + + /** + * @brief This function is used to get sequence and picture parameter headers. + * Application can call this function after encoder is initialized to get SPS and PPS + * nalus for the current encoder instance. The sequence header data might change when + * application calls Reconfigure() function. + */ + void GetSequenceParams(std::vector& seqParams); + + /** + * @brief NvEncoder class virtual destructor. + */ + virtual ~NvEncoder(); + +public: + /** + * @brief This a static function to get chroma offsets for YUV planar formats. + */ + static void GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch, + const uint32_t height, std::vector& chromaOffsets); + /** + * @brief This a static function to get the chroma plane pitch for YUV planar formats. + */ + static uint32_t GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaPitch); + + /** + * @brief This a static function to get the number of chroma planes for YUV planar formats. + */ + static uint32_t GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat); + + /** + * @brief This a static function to get the chroma plane width in bytes for YUV planar formats. + */ + static uint32_t GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth); + + /** + * @brief This a static function to get the chroma planes height in bytes for YUV planar formats. + */ + static uint32_t GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaHeight); + + + /** + * @brief This a static function to get the width in bytes for the frame. + * For YUV planar format this is the width in bytes of the luma plane. + */ + static uint32_t GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t width); + + /** + * @brief This function returns the number of allocated buffers. + */ + uint32_t GetEncoderBufferCount() const { return m_nEncoderBuffer; } +protected: + + /** + * @brief NvEncoder class constructor. + * NvEncoder class constructor cannot be called directly by the application. + */ + NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, uint32_t nOutputDelay); + + /** + * @brief This function is used to check if hardware encoder is properly initialized. + */ + bool IsHWEncoderInitialized() const { return m_hEncoder != NULL && m_bEncoderInitialized; } + + /** + * @brief This function is used to register CUDA, D3D or OpenGL input buffers with NvEncodeAPI. + * This is non public function and is called by derived class for allocating + * and registering input buffers. + */ + void RegisterInputResources(std::vector inputframes, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, bool bReferenceFrame = false); + + /** + * @brief This function is used to unregister resources which had been previously registered for encoding + * using RegisterInputResources() function. + */ + void UnregisterInputResources(); + + /** + * @brief This function is used to register CUDA, D3D or OpenGL input or output buffers with NvEncodeAPI. + */ + NV_ENC_REGISTERED_PTR RegisterResource(void* pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage = NV_ENC_INPUT_IMAGE, + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint = NULL, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint = NULL); + + /** + * @brief This function returns maximum width used to open the encoder session. + * All encode input buffers are allocated using maximum dimensions. + */ + uint32_t GetMaxEncodeWidth() const { return m_nMaxEncodeWidth; } + + /** + * @brief This function returns maximum height used to open the encoder session. + * All encode input buffers are allocated using maximum dimensions. + */ + uint32_t GetMaxEncodeHeight() const { return m_nMaxEncodeHeight; } + + /** + * @brief This function returns the completion event. + */ + void* GetCompletionEvent(uint32_t eventIdx) { return (m_vpCompletionEvent.size() == m_nEncoderBuffer) ? m_vpCompletionEvent[eventIdx] : nullptr; } + + /** + * @brief This function returns the current pixel format. + */ + NV_ENC_BUFFER_FORMAT GetPixelFormat() const { return m_eBufferFormat; } + + /** + * @brief This function is used to submit the encode commands to the + * NVENC hardware. + */ + NVENCSTATUS DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR outputBuffer, NV_ENC_PIC_PARAMS* pPicParams); + + /** + * @brief This function is used to submit the encode commands to the + * NVENC hardware for ME only mode. + */ + //NVENCSTATUS DoMotionEstimation(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_INPUT_PTR inputBufferForReference, NV_ENC_OUTPUT_PTR outputBuffer); + + /** + * @brief This function is used to map the input buffers to NvEncodeAPI. + */ + void MapResources(uint32_t bfrIdx); + + /** + * @brief This function is used to wait for completion of encode command. + */ + void WaitForCompletionEvent(int iEvent); + + /** + * @brief This function is used to send EOS to HW encoder. + */ + void SendEOS(); + +private: + /** + * @brief This is a private function which is used to check if there is any + buffering done by encoder. + * The encoder generally buffers data to encode B frames or for lookahead + * or pipelining. + */ + bool IsZeroDelay() { return m_nOutputDelay == 0; } + + /** + * @brief This is a private function which is used to load the encode api shared library. + */ + void LoadNvEncApi(); + + /** + * @brief This is a private function which is used to get the output packets + * from the encoder HW. + * This is called by DoEncode() function. If there is buffering enabled, + * this may return without any output data. + */ + void GetEncodedPacket(std::vector& vOutputBuffer, std::vector>& vPacket, bool bOutputDelay); + + /** + * @brief This is a private function which is used to initialize the bitstream buffers. + * This is only used in the encoding mode. + */ + void InitializeBitstreamBuffer(); + + /** + * @brief This is a private function which is used to destroy the bitstream buffers. + * This is only used in the encoding mode. + */ + void DestroyBitstreamBuffer(); + + /** + * @brief This is a private function which is used to destroy HW encoder. + */ + void DestroyHWEncoder(); + + /** + * @brief This function is used to flush the encoder queue. + */ + void FlushEncoder(); + +private: + /** + * @brief This is a pure virtual function which is used to allocate input buffers. + * The derived classes must implement this function. + */ + virtual void AllocateInputBuffers(int32_t numInputBuffers) = 0; + + /** + * @brief This is a pure virtual function which is used to destroy input buffers. + * The derived classes must implement this function. + */ + virtual void ReleaseInputBuffers() = 0; + +protected: + void* m_hEncoder = nullptr; + NV_ENCODE_API_FUNCTION_LIST m_nvenc; + std::vector m_vInputFrames; + std::vector m_vRegisteredResources; + std::vector m_vReferenceFrames; + std::vector m_vRegisteredResourcesForReference; + std::vector m_vMappedInputBuffers; + std::vector m_vMappedRefBuffers; + std::vector m_vpCompletionEvent; + + int32_t m_iToSend = 0; + int32_t m_iGot = 0; + int32_t m_nEncoderBuffer = 0; + int32_t m_nOutputDelay = 0; + +private: + void* m_pDevice; + NV_ENC_DEVICE_TYPE m_eDeviceType; + uint32_t m_nWidth; + uint32_t m_nHeight; + uint32_t m_nMaxEncodeWidth = 0; + uint32_t m_nMaxEncodeHeight = 0; + NV_ENC_BUFFER_FORMAT m_eBufferFormat; + NV_ENC_INITIALIZE_PARAMS m_initializeParams = {}; + NV_ENC_CONFIG m_encodeConfig = {}; + bool m_bEncoderInitialized = false; + uint32_t m_nExtraOutputDelay = 3; // To ensure encode and graphics can work in parallel, m_nExtraOutputDelay should be set to at least 1 + std::vector m_vBitstreamOutputBuffer; +}; +}} +#endif \ No newline at end of file diff --git a/modules/cudacodec/src/NvEncoderCuda.cpp b/modules/cudacodec/src/NvEncoderCuda.cpp new file mode 100644 index 0000000000..195ffde15b --- /dev/null +++ b/modules/cudacodec/src/NvEncoderCuda.cpp @@ -0,0 +1,196 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "precomp.hpp" + +#if defined(HAVE_NVCUVENC) +#include "NvEncoderCuda.h" + +namespace cv { namespace cudacodec { +NvEncoderCuda::NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay) : + NvEncoder(NV_ENC_DEVICE_TYPE_CUDA, cuContext, nWidth, nHeight, eBufferFormat, nExtraOutputDelay), + m_cuContext(cuContext) +{ + if (!m_hEncoder) + { + NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_INVALID_DEVICE); + } + + if (!m_cuContext) + { + NVENC_THROW_ERROR("Invalid Cuda Context", NV_ENC_ERR_INVALID_DEVICE); + } +} + +NvEncoderCuda::~NvEncoderCuda() +{ + ReleaseCudaResources(); +} + +void NvEncoderCuda::AllocateInputBuffers(int32_t numInputBuffers) +{ + if (!IsHWEncoderInitialized()) + { + NVENC_THROW_ERROR("Encoder intialization failed", NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + } + + cuSafeCall(cuCtxPushCurrent(m_cuContext)); + std::vector inputFrames; + for (int i = 0; i < numInputBuffers; i++) + { + CUdeviceptr pDeviceFrame; + uint32_t chromaHeight = GetNumChromaPlanes(GetPixelFormat()) * GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); + if (GetPixelFormat() == NV_ENC_BUFFER_FORMAT_YV12 || GetPixelFormat() == NV_ENC_BUFFER_FORMAT_IYUV) + chromaHeight = GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); + cuSafeCall(cuMemAllocPitch((CUdeviceptr*)&pDeviceFrame, + &m_cudaPitch, + GetWidthInBytes(GetPixelFormat(), GetMaxEncodeWidth()), + GetMaxEncodeHeight() + chromaHeight, 16)); + inputFrames.push_back((void*)pDeviceFrame); + } + cuSafeCall(cuCtxPopCurrent(NULL)); + + RegisterInputResources(inputFrames, + NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR, + GetMaxEncodeWidth(), + GetMaxEncodeHeight(), + (int)m_cudaPitch, + GetPixelFormat(), + false); +} + +void NvEncoderCuda::SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream) +{ + NVENC_API_CALL(m_nvenc.nvEncSetIOCudaStreams(m_hEncoder, inputStream, outputStream)); +} + +void NvEncoderCuda::ReleaseInputBuffers() +{ + ReleaseCudaResources(); +} + +void NvEncoderCuda::ReleaseCudaResources() +{ + if (!m_hEncoder) + { + return; + } + + if (!m_cuContext) + { + return; + } + + UnregisterInputResources(); + + cuCtxPushCurrent(m_cuContext); + + for (uint32_t i = 0; i < m_vInputFrames.size(); ++i) + { + if (m_vInputFrames[i].inputPtr) + { + cuMemFree(reinterpret_cast(m_vInputFrames[i].inputPtr)); + } + } + m_vInputFrames.clear(); + + for (uint32_t i = 0; i < m_vReferenceFrames.size(); ++i) + { + if (m_vReferenceFrames[i].inputPtr) + { + cuMemFree(reinterpret_cast(m_vReferenceFrames[i].inputPtr)); + } + } + m_vReferenceFrames.clear(); + + cuCtxPopCurrent(NULL); + m_cuContext = nullptr; +} + +void NvEncoderCuda::CopyToDeviceFrame(CUcontext device, + void* pSrcFrame, + uint32_t nSrcPitch, + CUdeviceptr pDstFrame, + uint32_t dstPitch, + int width, + int height, + CUmemorytype srcMemoryType, + NV_ENC_BUFFER_FORMAT pixelFormat, + const uint32_t dstChromaOffsets[], + uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy, + CUstream stream) +{ + if (srcMemoryType != CU_MEMORYTYPE_HOST && srcMemoryType != CU_MEMORYTYPE_DEVICE) + { + NVENC_THROW_ERROR("Invalid source memory type for copy", NV_ENC_ERR_INVALID_PARAM); + } + + cuSafeCall(cuCtxPushCurrent(device)); + + uint32_t srcPitch = nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); + CUDA_MEMCPY2D m = { 0 }; + m.srcMemoryType = srcMemoryType; + if (srcMemoryType == CU_MEMORYTYPE_HOST) + { + m.srcHost = pSrcFrame; + } + else + { + m.srcDevice = (CUdeviceptr)pSrcFrame; + } + m.srcPitch = srcPitch; + m.dstMemoryType = CU_MEMORYTYPE_DEVICE; + m.dstDevice = pDstFrame; + m.dstPitch = dstPitch; + m.WidthInBytes = NvEncoder::GetWidthInBytes(pixelFormat, width); + m.Height = height; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) + { + cuSafeCall(cuMemcpy2DUnaligned(&m)); + } + else + { + cuSafeCall(stream == NULL ? cuMemcpy2D(&m) : cuMemcpy2DAsync(&m, stream)); + } + + std::vector srcChromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(pixelFormat, srcPitch, height, srcChromaOffsets); + uint32_t chromaHeight = NvEncoder::GetChromaHeight(pixelFormat, height); + uint32_t destChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, dstPitch); + uint32_t srcChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, srcPitch); + uint32_t chromaWidthInBytes = NvEncoder::GetChromaWidthInBytes(pixelFormat, width); + + for (uint32_t i = 0; i < numChromaPlanes; ++i) + { + if (chromaHeight) + { + if (srcMemoryType == CU_MEMORYTYPE_HOST) + { + m.srcHost = ((uint8_t*)pSrcFrame + srcChromaOffsets[i]); + } + else + { + m.srcDevice = (CUdeviceptr)((uint8_t*)pSrcFrame + srcChromaOffsets[i]); + } + m.srcPitch = srcChromaPitch; + + m.dstDevice = (CUdeviceptr)((uint8_t*)pDstFrame + dstChromaOffsets[i]); + m.dstPitch = destChromaPitch; + m.WidthInBytes = chromaWidthInBytes; + m.Height = chromaHeight; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) + { + cuSafeCall(cuMemcpy2DUnaligned(&m)); + } + else + { + cuSafeCall(stream == NULL ? cuMemcpy2D(&m) : cuMemcpy2DAsync(&m, stream)); + } + } + } + cuSafeCall(cuCtxPopCurrent(NULL)); +} +}} +#endif \ No newline at end of file diff --git a/modules/cudacodec/src/NvEncoderCuda.h b/modules/cudacodec/src/NvEncoderCuda.h new file mode 100644 index 0000000000..55788dc7f9 --- /dev/null +++ b/modules/cudacodec/src/NvEncoderCuda.h @@ -0,0 +1,75 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_NVENCODERCUDA_HPP +#define OPENCV_NVENCODERCUDA_HPP +#include +#include +#include +#include +#include "NvEncoder.h" + +namespace cv { namespace cudacodec { + +/** +* @brief Encoder for CUDA device memory. +*/ +class NvEncoderCuda : public NvEncoder +{ +public: + NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay = 3); + virtual ~NvEncoderCuda(); + + /** + * @brief This is a static function to copy input data from host memory to device memory. + * This function assumes YUV plane is a single contiguous memory segment. + */ + static void CopyToDeviceFrame(CUcontext device, + void* pSrcFrame, + uint32_t nSrcPitch, + CUdeviceptr pDstFrame, + uint32_t dstPitch, + int width, + int height, + CUmemorytype srcMemoryType, + NV_ENC_BUFFER_FORMAT pixelFormat, + const uint32_t dstChromaOffsets[], + uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy = false, + CUstream stream = NULL); + + /** + * @brief This function sets input and output CUDA streams + */ + void SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream); + +protected: + /** + * @brief This function is used to release the input buffers allocated for encoding. + * This function is an override of virtual function NvEncoder::ReleaseInputBuffers(). + */ + virtual void ReleaseInputBuffers() override; + +private: + /** + * @brief This function is used to allocate input buffers for encoding. + * This function is an override of virtual function NvEncoder::AllocateInputBuffers(). + */ + virtual void AllocateInputBuffers(int32_t numInputBuffers) override; + +private: + /** + * @brief This is a private function to release CUDA device memory used for encoding. + */ + void ReleaseCudaResources(); + +protected: + CUcontext m_cuContext; + +private: + size_t m_cudaPitch = 0; +}; +}} +#endif \ No newline at end of file diff --git a/modules/cudacodec/src/precomp.hpp b/modules/cudacodec/src/precomp.hpp index ca3c68958d..99a788a012 100644 --- a/modules/cudacodec/src/precomp.hpp +++ b/modules/cudacodec/src/precomp.hpp @@ -57,32 +57,38 @@ #include "opencv2/core/private.cuda.hpp" #include -#ifdef HAVE_NVCUVID - #if defined(HAVE_DYNLINK_NVCUVID_HEADER) - #include - #elif defined(HAVE_NVCUVID_HEADER) - #include - #endif - - #ifdef _WIN32 +#if defined(HAVE_NVCUVID) || defined(HAVE_NVCUVENC) + #if _WIN32 #define NOMINMAX - #include - #ifdef HAVE_NVCUVENC - #include - #endif - #else - #include - #include #endif + #if defined(HAVE_NVCUVID) + #if defined(HAVE_DYNLINK_NVCUVID_HEADER) + #include + #elif defined(HAVE_NVCUVID_HEADER) + #include + #endif - #include "thread.hpp" - #include "video_source.hpp" - #include "ffmpeg_video_source.hpp" - #include "cuvid_video_source.hpp" - #include "frame_queue.hpp" - #include "video_decoder.hpp" - #include "video_parser.hpp" + #ifdef _WIN32 + #include + #else + #include + #include + #endif + #include "thread.hpp" + #include "video_source.hpp" + #include "ffmpeg_video_source.hpp" + #include "cuvid_video_source.hpp" + #include "frame_queue.hpp" + #include "video_decoder.hpp" + #include "video_parser.hpp" + #endif + #if defined(HAVE_NVCUVENC) + #include + #include + #include "NvEncoderCuda.h" + #include + #endif #endif #endif /* OPENCV_PRECOMP_H */ diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index ce3b68fb2a..c33c89bdae 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -43,874 +43,393 @@ #include "precomp.hpp" -using namespace cv; +namespace cv { namespace cudacodec { using namespace cv::cuda; -using namespace cv::cudacodec; -#if !defined(HAVE_NVCUVENC) || !defined(_WIN32) +#if !defined(HAVE_NVCUVENC) -cv::cudacodec::EncoderParams::EncoderParams() { throw_no_cuda(); } -cv::cudacodec::EncoderParams::EncoderParams(const String&) { throw_no_cuda(); } -void cv::cudacodec::EncoderParams::load(const String&) { throw_no_cuda(); } -void cv::cudacodec::EncoderParams::save(const String&) const { throw_no_cuda(); } +Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const COLOR_FORMAT_CV, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr cv::cudacodec::createVideoWriter(const String&, Size, double, SurfaceFormat) { throw_no_cuda(); return Ptr(); } -Ptr cv::cudacodec::createVideoWriter(const String&, Size, double, const EncoderParams&, SurfaceFormat) { throw_no_cuda(); return Ptr(); } +#else // !defined HAVE_NVCUVENC -Ptr cv::cudacodec::createVideoWriter(const Ptr&, Size, double, SurfaceFormat) { throw_no_cuda(); return Ptr(); } -Ptr cv::cudacodec::createVideoWriter(const Ptr&, Size, double, const EncoderParams&, SurfaceFormat) { throw_no_cuda(); return Ptr(); } - -#else // !defined HAVE_NVCUVENC || !defined _WIN32 - -void RGB_to_YV12(const GpuMat& src, GpuMat& dst); - -/////////////////////////////////////////////////////////////////////////// -// VideoWriterImpl - -namespace +EncoderParams::EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT), + rateControlMode(ENC_PARAMS_RC_VBR), multiPassEncoding(ENC_MULTI_PASS_DISABLED), constQp({ 0,0,0 }), averageBitRate(0), maxBitRate(0), + targetQuality(30), gopLength(0) { - class NVEncoderWrapper - { - public: - NVEncoderWrapper() : encoder_(0) - { - int err; - - err = NVGetHWEncodeCaps(); - if (err) - CV_Error(Error::GpuNotSupported, "No CUDA capability present"); - - // Create the Encoder API Interface - err = NVCreateEncoder(&encoder_); - CV_Assert( err == 0 ); - } - - ~NVEncoderWrapper() - { - if (encoder_) - NVDestroyEncoder(encoder_); - } - - operator NVEncoder() const - { - return encoder_; - } - - private: - NVEncoder encoder_; - }; - - enum CodecType - { - MPEG1, // not supported yet - MPEG2, // not supported yet - MPEG4, // not supported yet - H264 - }; - - class VideoWriterImpl : public VideoWriter - { - public: - VideoWriterImpl(const Ptr& callback, Size frameSize, double fps, SurfaceFormat format, CodecType codec = H264); - VideoWriterImpl(const Ptr& callback, Size frameSize, double fps, const EncoderParams& params, SurfaceFormat format, CodecType codec = H264); - - void write(InputArray frame, bool lastFrame = false); - - EncoderParams getEncoderParams() const; - - private: - void initEncoder(double fps); - void setEncodeParams(const EncoderParams& params); - void initGpuMemory(); - void initCallBacks(); - void createHWEncoder(); - - Ptr callback_; - Size frameSize_; - - CodecType codec_; - SurfaceFormat inputFormat_; - NVVE_SurfaceFormat surfaceFormat_; +}; - NVEncoderWrapper encoder_; - - GpuMat videoFrame_; - CUvideoctxlock cuCtxLock_; - - // CallBacks - - static unsigned char* NVENCAPI HandleAcquireBitStream(int* pBufferSize, void* pUserdata); - static void NVENCAPI HandleReleaseBitStream(int nBytesInBuffer, unsigned char* cb, void* pUserdata); - static void NVENCAPI HandleOnBeginFrame(const NVVE_BeginFrameInfo* pbfi, void* pUserdata); - static void NVENCAPI HandleOnEndFrame(const NVVE_EndFrameInfo* pefi, void* pUserdata); - }; +bool operator==(const EncoderParams& lhs, const EncoderParams& rhs) +{ + return std::tie(lhs.nvPreset, lhs.tuningInfo, lhs.encodingProfile, lhs.rateControlMode, lhs.multiPassEncoding, lhs.constQp.qpInterB, lhs.constQp.qpInterP, lhs.constQp.qpIntra, + lhs.averageBitRate, lhs.maxBitRate, lhs.targetQuality, lhs.gopLength) == std::tie(rhs.nvPreset, rhs.tuningInfo, rhs.encodingProfile, rhs.rateControlMode, rhs.multiPassEncoding, rhs.constQp.qpInterB, rhs.constQp.qpInterP, rhs.constQp.qpIntra, + rhs.averageBitRate, rhs.maxBitRate, rhs.targetQuality, rhs.gopLength); +}; - VideoWriterImpl::VideoWriterImpl(const Ptr& callback, Size frameSize, double fps, SurfaceFormat format, CodecType codec) : - callback_(callback), - frameSize_(frameSize), - codec_(codec), - inputFormat_(format), - cuCtxLock_(0) - { - surfaceFormat_ = (inputFormat_ == SF_BGR ? YV12 : static_cast(inputFormat_)); +class RawVideoWriter : public EncoderCallBack +{ +public: + RawVideoWriter(String fileName); + ~RawVideoWriter(); + void onEncoded(std::vector> vPacket); + void onEncodingFinished(); +private: + std::ofstream fpOut; +}; + +RawVideoWriter::RawVideoWriter(String fileName) { + fpOut = std::ofstream(fileName, std::ios::out | std::ios::binary); + if (!fpOut) + CV_Error(Error::StsError, "Failed to open video file " + fileName + " for writing!"); +} - initEncoder(fps); +void RawVideoWriter::onEncodingFinished() { + fpOut.close(); +} - initGpuMemory(); +RawVideoWriter::~RawVideoWriter() { + onEncodingFinished(); +} - initCallBacks(); +void RawVideoWriter::onEncoded(std::vector> vPacket) { + for (auto& packet : vPacket) + fpOut.write(reinterpret_cast(packet.data()), packet.size()); +} - createHWEncoder(); +class VideoWriterImpl : public VideoWriter +{ +public: + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV surfaceFormatCv, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT surfaceFormatNv, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV surfaceFormatCv, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT surfaceFormatNv, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); + ~VideoWriterImpl(); + void write(InputArray frame); + EncoderParams getEncoderParams() const; + void close(); +private: + void Init(const VideoWriterCodec codec, const double fps, const Size frameSz); + void InitializeEncoder(NvEncoderCuda* const pEnc, const GUID codec, const double fps); + void CopyToNvSurface(const InputArray src); + + Ptr encoderCallBack; + COLOR_FORMAT_CV surfaceFormatCv = COLOR_FORMAT_CV::UNDEFINED; + ENC_BUFFER_FORMAT surfaceFormatNv = ENC_BUFFER_FORMAT::BF_UNDEFINED; + EncoderParams encoderParams; + Stream stream = Stream::Null(); + Ptr pEnc; + std::vector> vPacket; + int nSrcChannels = -1; + CUcontext cuContext; +}; + +ENC_BUFFER_FORMAT NvSurfaceFormat(const COLOR_FORMAT_CV format) { + switch (format) { + case BGR: return BF_ARGB; + case RGB: return BF_ABGR; + case BGRA: return BF_ARGB; + case RGBA: return BF_ABGR; + case GRAY: return BF_NV12; + default: return BF_UNDEFINED; } +} - VideoWriterImpl::VideoWriterImpl(const Ptr& callback, Size frameSize, double fps, const EncoderParams& params, SurfaceFormat format, CodecType codec) : - callback_(callback), - frameSize_(frameSize), - codec_(codec), - inputFormat_(format), - cuCtxLock_(0) - { - surfaceFormat_ = (inputFormat_ == SF_BGR ? YV12 : static_cast(inputFormat_)); - - initEncoder(fps); - - setEncodeParams(params); - - initGpuMemory(); - - initCallBacks(); - - createHWEncoder(); +int NChannels(const COLOR_FORMAT_CV format) { + switch (format) { + case BGR: + case RGB: return 3; + case RGBA: + case BGRA: return 4; + case GRAY: return 1; + default: return 0; } +} - void VideoWriterImpl::initEncoder(double fps) - { - int err; - - // Set codec - - static const unsigned long codecs_id[] = - { - NV_CODEC_TYPE_MPEG1, NV_CODEC_TYPE_MPEG2, NV_CODEC_TYPE_MPEG4, NV_CODEC_TYPE_H264, NV_CODEC_TYPE_VC1 - }; - err = NVSetCodec(encoder_, codecs_id[codec_]); - if (err) - CV_Error(Error::StsNotImplemented, "Codec format is not supported"); - - // Set default params - - err = NVSetDefaultParam(encoder_); - CV_Assert( err == 0 ); - - // Set some common params - - int inputSize[] = { frameSize_.width, frameSize_.height }; - err = NVSetParamValue(encoder_, NVVE_IN_SIZE, &inputSize); - CV_Assert( err == 0 ); - err = NVSetParamValue(encoder_, NVVE_OUT_SIZE, &inputSize); - CV_Assert( err == 0 ); - - int aspectRatio[] = { frameSize_.width, frameSize_.height, ASPECT_RATIO_DAR }; - err = NVSetParamValue(encoder_, NVVE_ASPECT_RATIO, &aspectRatio); - CV_Assert( err == 0 ); - - // FPS - - int frame_rate = static_cast(fps + 0.5); - int frame_rate_base = 1; - while (fabs(static_cast(frame_rate) / frame_rate_base) - fps > 0.001) - { - frame_rate_base *= 10; - frame_rate = static_cast(fps*frame_rate_base + 0.5); - } - int FrameRate[] = { frame_rate, frame_rate_base }; - err = NVSetParamValue(encoder_, NVVE_FRAME_RATE, &FrameRate); - CV_Assert( err == 0 ); - - // Select device for encoding +int NChannels(const ENC_BUFFER_FORMAT format) { + if (format == ENC_BUFFER_FORMAT::BF_ARGB || format == ENC_BUFFER_FORMAT::BF_ABGR) return 4; + else return 1; +} - int gpuID = getDevice(); - err = NVSetParamValue(encoder_, NVVE_FORCE_GPU_SELECTION, &gpuID); - CV_Assert( err == 0 ); +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV surfaceFormatCv_, const EncoderParams& encoderParams_, const Stream& stream_) : + encoderCallBack(encoderCallBack_), surfaceFormatCv(surfaceFormatCv_), encoderParams(encoderParams_), stream(stream_) +{ + surfaceFormatNv = NvSurfaceFormat(surfaceFormatCv); + if (surfaceFormatNv == BF_UNDEFINED) { + String msg = cv::format("Unsupported input surface format: %i", surfaceFormatCv); + CV_LOG_WARNING(NULL, msg); + CV_Error(Error::StsUnsupportedFormat, msg); } + nSrcChannels = NChannels(surfaceFormatCv); + Init(codec, fps, frameSz); +} - void VideoWriterImpl::setEncodeParams(const EncoderParams& params) - { - int err; - - int P_Interval = params.P_Interval; - err = NVSetParamValue(encoder_, NVVE_P_INTERVAL, &P_Interval); - CV_Assert( err == 0 ); - - int IDR_Period = params.IDR_Period; - err = NVSetParamValue(encoder_, NVVE_IDR_PERIOD, &IDR_Period); - CV_Assert( err == 0 ); - - int DynamicGOP = params.DynamicGOP; - err = NVSetParamValue(encoder_, NVVE_DYNAMIC_GOP, &DynamicGOP); - CV_Assert( err == 0 ); - - NVVE_RateCtrlType RCType = static_cast(params.RCType); - err = NVSetParamValue(encoder_, NVVE_RC_TYPE, &RCType); - CV_Assert( err == 0 ); - - int AvgBitrate = params.AvgBitrate; - err = NVSetParamValue(encoder_, NVVE_AVG_BITRATE, &AvgBitrate); - CV_Assert( err == 0 ); - - int PeakBitrate = params.PeakBitrate; - err = NVSetParamValue(encoder_, NVVE_PEAK_BITRATE, &PeakBitrate); - CV_Assert( err == 0 ); - - int QP_Level_Intra = params.QP_Level_Intra; - err = NVSetParamValue(encoder_, NVVE_QP_LEVEL_INTRA, &QP_Level_Intra); - CV_Assert( err == 0 ); - - int QP_Level_InterP = params.QP_Level_InterP; - err = NVSetParamValue(encoder_, NVVE_QP_LEVEL_INTER_P, &QP_Level_InterP); - CV_Assert( err == 0 ); - - int QP_Level_InterB = params.QP_Level_InterB; - err = NVSetParamValue(encoder_, NVVE_QP_LEVEL_INTER_B, &QP_Level_InterB); - CV_Assert( err == 0 ); - - int DeblockMode = params.DeblockMode; - err = NVSetParamValue(encoder_, NVVE_DEBLOCK_MODE, &DeblockMode); - CV_Assert( err == 0 ); - - int ProfileLevel = params.ProfileLevel; - err = NVSetParamValue(encoder_, NVVE_PROFILE_LEVEL, &ProfileLevel); - CV_Assert( err == 0 ); - - int ForceIntra = params.ForceIntra; - err = NVSetParamValue(encoder_, NVVE_FORCE_INTRA, &ForceIntra); - CV_Assert( err == 0 ); - - int ForceIDR = params.ForceIDR; - err = NVSetParamValue(encoder_, NVVE_FORCE_IDR, &ForceIDR); - CV_Assert( err == 0 ); - - int ClearStat = params.ClearStat; - err = NVSetParamValue(encoder_, NVVE_CLEAR_STAT, &ClearStat); - CV_Assert( err == 0 ); - - NVVE_DI_MODE DIMode = static_cast(params.DIMode); - err = NVSetParamValue(encoder_, NVVE_SET_DEINTERLACE, &DIMode); - CV_Assert( err == 0 ); +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT surfaceFormatNv_, const EncoderParams& encoderParams_, const Stream& stream_) : + encoderCallBack(encoderCallBack_), surfaceFormatNv(surfaceFormatNv_), encoderParams(encoderParams_), stream(stream_) +{ + CV_Assert(surfaceFormatNv != BF_UNDEFINED); + nSrcChannels = NChannels(surfaceFormatNv); + Init(codec, fps, frameSz); +} - if (params.Presets != -1) - { - NVVE_PRESETS_TARGET Presets = static_cast(params.Presets); - err = NVSetParamValue(encoder_, NVVE_PRESETS, &Presets); - CV_Assert( err == 0 ); - } +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack, const Size frameSz, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV surfaceFormatCv, const Stream& stream) : + VideoWriterImpl(encoderCallBack, frameSz, codec, fps, surfaceFormatCv, EncoderParams(), stream) +{ +} - int DisableCabac = params.DisableCabac; - err = NVSetParamValue(encoder_, NVVE_DISABLE_CABAC, &DisableCabac); - CV_Assert( err == 0 ); +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack, const Size frameSz, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT surfaceFormatNv, const Stream& stream) : + VideoWriterImpl(encoderCallBack, frameSz, codec, fps, surfaceFormatNv, EncoderParams(), stream) +{ +} - int NaluFramingType = params.NaluFramingType; - err = NVSetParamValue(encoder_, NVVE_CONFIGURE_NALU_FRAMING_TYPE, &NaluFramingType); - CV_Assert( err == 0 ); +void VideoWriterImpl::close() { + pEnc->EndEncode(vPacket); + encoderCallBack->onEncoded(vPacket); + encoderCallBack->onEncodingFinished(); +} - int DisableSPSPPS = params.DisableSPSPPS; - err = NVSetParamValue(encoder_, NVVE_DISABLE_SPS_PPS, &DisableSPSPPS); - CV_Assert( err == 0 ); - } +VideoWriterImpl::~VideoWriterImpl() { + close(); +} - EncoderParams VideoWriterImpl::getEncoderParams() const - { - int err; - - EncoderParams params; - - int P_Interval; - err = NVGetParamValue(encoder_, NVVE_P_INTERVAL, &P_Interval); - CV_Assert( err == 0 ); - params.P_Interval = P_Interval; - - int IDR_Period; - err = NVGetParamValue(encoder_, NVVE_IDR_PERIOD, &IDR_Period); - CV_Assert( err == 0 ); - params.IDR_Period = IDR_Period; - - int DynamicGOP; - err = NVGetParamValue(encoder_, NVVE_DYNAMIC_GOP, &DynamicGOP); - CV_Assert( err == 0 ); - params.DynamicGOP = DynamicGOP; - - NVVE_RateCtrlType RCType; - err = NVGetParamValue(encoder_, NVVE_RC_TYPE, &RCType); - CV_Assert( err == 0 ); - params.RCType = RCType; - - int AvgBitrate; - err = NVGetParamValue(encoder_, NVVE_AVG_BITRATE, &AvgBitrate); - CV_Assert( err == 0 ); - params.AvgBitrate = AvgBitrate; - - int PeakBitrate; - err = NVGetParamValue(encoder_, NVVE_PEAK_BITRATE, &PeakBitrate); - CV_Assert( err == 0 ); - params.PeakBitrate = PeakBitrate; - - int QP_Level_Intra; - err = NVGetParamValue(encoder_, NVVE_QP_LEVEL_INTRA, &QP_Level_Intra); - CV_Assert( err == 0 ); - params.QP_Level_Intra = QP_Level_Intra; - - int QP_Level_InterP; - err = NVGetParamValue(encoder_, NVVE_QP_LEVEL_INTER_P, &QP_Level_InterP); - CV_Assert( err == 0 ); - params.QP_Level_InterP = QP_Level_InterP; - - int QP_Level_InterB; - err = NVGetParamValue(encoder_, NVVE_QP_LEVEL_INTER_B, &QP_Level_InterB); - CV_Assert( err == 0 ); - params.QP_Level_InterB = QP_Level_InterB; - - int DeblockMode; - err = NVGetParamValue(encoder_, NVVE_DEBLOCK_MODE, &DeblockMode); - CV_Assert( err == 0 ); - params.DeblockMode = DeblockMode; - - int ProfileLevel; - err = NVGetParamValue(encoder_, NVVE_PROFILE_LEVEL, &ProfileLevel); - CV_Assert( err == 0 ); - params.ProfileLevel = ProfileLevel; - - int ForceIntra; - err = NVGetParamValue(encoder_, NVVE_FORCE_INTRA, &ForceIntra); - CV_Assert( err == 0 ); - params.ForceIntra = ForceIntra; - - int ForceIDR; - err = NVGetParamValue(encoder_, NVVE_FORCE_IDR, &ForceIDR); - CV_Assert( err == 0 ); - params.ForceIDR = ForceIDR; - - int ClearStat; - err = NVGetParamValue(encoder_, NVVE_CLEAR_STAT, &ClearStat); - CV_Assert( err == 0 ); - params.ClearStat = ClearStat; - - NVVE_DI_MODE DIMode; - err = NVGetParamValue(encoder_, NVVE_SET_DEINTERLACE, &DIMode); - CV_Assert( err == 0 ); - params.DIMode = DIMode; - - params.Presets = -1; - - int DisableCabac; - err = NVGetParamValue(encoder_, NVVE_DISABLE_CABAC, &DisableCabac); - CV_Assert( err == 0 ); - params.DisableCabac = DisableCabac; - - int NaluFramingType; - err = NVGetParamValue(encoder_, NVVE_CONFIGURE_NALU_FRAMING_TYPE, &NaluFramingType); - CV_Assert( err == 0 ); - params.NaluFramingType = NaluFramingType; - - int DisableSPSPPS; - err = NVGetParamValue(encoder_, NVVE_DISABLE_SPS_PPS, &DisableSPSPPS); - CV_Assert( err == 0 ); - params.DisableSPSPPS = DisableSPSPPS; - - return params; +GUID CodecGuid(const VideoWriterCodec codec) { + switch (codec) { + case VideoWriterCodec::H264: return NV_ENC_CODEC_H264_GUID; + case VideoWriterCodec::HEVC: return NV_ENC_CODEC_HEVC_GUID; + default: break; } + std::string msg = "Unknown codec: cudacodec::VideoWriter only supports VideoWriterCodec::H264 and VideoWriterCodec::HEVC"; + CV_LOG_WARNING(NULL, msg); + CV_Error(Error::StsUnsupportedFormat, msg); +} - void VideoWriterImpl::initGpuMemory() - { - int err; - - // initialize context - GpuMat temp(1, 1, CV_8U); - temp.release(); - - static const int bpp[] = - { - 16, // UYVY, 4:2:2 - 16, // YUY2, 4:2:2 - 12, // YV12, 4:2:0 - 12, // NV12, 4:2:0 - 12, // IYUV, 4:2:0 - }; - - CUcontext cuContext; - cuSafeCall( cuCtxGetCurrent(&cuContext) ); - - // Allocate the CUDA memory Pitched Surface - if (surfaceFormat_ == UYVY || surfaceFormat_ == YUY2) - videoFrame_.create(frameSize_.height, (frameSize_.width * bpp[surfaceFormat_]) / 8, CV_8UC1); - else - videoFrame_.create((frameSize_.height * bpp[surfaceFormat_]) / 8, frameSize_.width, CV_8UC1); - - // Create the Video Context Lock (used for synchronization) - cuSafeCall( cuvidCtxLockCreate(&cuCtxLock_, cuContext) ); - - // If we are using GPU Device Memory with NVCUVENC, it is necessary to create a - // CUDA Context with a Context Lock cuvidCtxLock. The Context Lock needs to be passed to NVCUVENC - - int iUseDeviceMem = 1; - err = NVSetParamValue(encoder_, NVVE_DEVICE_MEMORY_INPUT, &iUseDeviceMem); - CV_Assert( err == 0 ); - - err = NVSetParamValue(encoder_, NVVE_DEVICE_CTX_LOCK, &cuCtxLock_); - CV_Assert( err == 0 ); +void VideoWriterImpl::Init(const VideoWriterCodec codec, const double fps, const Size frameSz) { + // init context + GpuMat temp(1, 1, CV_8UC1); + temp.release(); + cuSafeCall(cuCtxGetCurrent(&cuContext)); + CV_Assert(nSrcChannels != 0); + const GUID codecGuid = CodecGuid(codec); + try { + pEnc = new NvEncoderCuda(cuContext, frameSz.width, frameSz.height, (NV_ENC_BUFFER_FORMAT)surfaceFormatNv); + InitializeEncoder(pEnc, codecGuid, fps); + const cudaStream_t cudaStream = cuda::StreamAccessor::getStream(stream); + pEnc->SetIOCudaStreams((NV_ENC_CUSTREAM_PTR)&cudaStream, (NV_ENC_CUSTREAM_PTR)&cudaStream); } - - void VideoWriterImpl::initCallBacks() + catch (cv::Exception& e) { - NVVE_CallbackParams cb; - memset(&cb, 0, sizeof(NVVE_CallbackParams)); - - cb.pfnacquirebitstream = HandleAcquireBitStream; - cb.pfnonbeginframe = HandleOnBeginFrame; - cb.pfnonendframe = HandleOnEndFrame; - cb.pfnreleasebitstream = HandleReleaseBitStream; - - NVRegisterCB(encoder_, cb, this); + String msg = String("Error initializing Nvidia Encoder. Refer to Nvidia's GPU Support Matrix to confirm your GPU supports hardware encoding, ") + + String("codec and surface format and check the encoder documentation to verify your choice of encoding paramaters are supported.") + + e.msg; + CV_Error(Error::GpuApiCallError, msg); } + const Size encoderFrameSz(pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight()); + CV_Assert(frameSz == encoderFrameSz); +} - void VideoWriterImpl::createHWEncoder() - { - int err; - - // Create the NVIDIA HW resources for Encoding on NVIDIA hardware - err = NVCreateHWEncoder(encoder_); - CV_Assert( err == 0 ); +void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen) { + CV_Assert(fps >= 0); + int frame_rate = (int)(fps + 0.5); + int frame_rate_base = 1; + while (fabs(((double)frame_rate / frame_rate_base) - fps) > 0.001) { + frame_rate_base *= 10; + frame_rate = (int)(fps * frame_rate_base + 0.5); } + frameRateNum = frame_rate; + frameRateDen = frame_rate_base; +} - // UYVY/YUY2 are both 4:2:2 formats (16bpc) - // Luma, U, V are interleaved, chroma is subsampled (w/2,h) - void copyUYVYorYUY2Frame(Size frameSize, const GpuMat& src, GpuMat& dst) - { - // Source is YUVY/YUY2 4:2:2, the YUV data in a packed and interleaved - - // YUV Copy setup - CUDA_MEMCPY2D stCopyYUV422; - memset(&stCopyYUV422, 0, sizeof(CUDA_MEMCPY2D)); - - stCopyYUV422.srcXInBytes = 0; - stCopyYUV422.srcY = 0; - stCopyYUV422.srcMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyYUV422.srcHost = 0; - stCopyYUV422.srcDevice = (CUdeviceptr) src.data; - stCopyYUV422.srcArray = 0; - stCopyYUV422.srcPitch = src.step; - - stCopyYUV422.dstXInBytes = 0; - stCopyYUV422.dstY = 0; - stCopyYUV422.dstMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyYUV422.dstHost = 0; - stCopyYUV422.dstDevice = (CUdeviceptr) dst.data; - stCopyYUV422.dstArray = 0; - stCopyYUV422.dstPitch = dst.step; - - stCopyYUV422.WidthInBytes = frameSize.width * 2; - stCopyYUV422.Height = frameSize.height; - - // DMA Luma/Chroma - cuSafeCall( cuMemcpy2D(&stCopyYUV422) ); +GUID EncodingProfileGuid(const ENC_PROFILE encodingProfile) { + switch (encodingProfile) { + case(ENC_CODEC_PROFILE_AUTOSELECT): return NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID; + case(ENC_H264_PROFILE_BASELINE): return NV_ENC_H264_PROFILE_BASELINE_GUID; + case(ENC_H264_PROFILE_MAIN): return NV_ENC_H264_PROFILE_MAIN_GUID; + case(ENC_H264_PROFILE_HIGH): return NV_ENC_H264_PROFILE_HIGH_GUID; + case(ENC_H264_PROFILE_HIGH_444): return NV_ENC_H264_PROFILE_HIGH_444_GUID; + case(ENC_H264_PROFILE_STEREO): return NV_ENC_H264_PROFILE_STEREO_GUID; + case(ENC_H264_PROFILE_PROGRESSIVE_HIGH): return NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID; + case(ENC_H264_PROFILE_CONSTRAINED_HIGH): return NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID; + case(ENC_HEVC_PROFILE_MAIN): return NV_ENC_HEVC_PROFILE_MAIN_GUID; + case(ENC_HEVC_PROFILE_MAIN10): return NV_ENC_HEVC_PROFILE_MAIN10_GUID; + case(ENC_HEVC_PROFILE_FREXT): return NV_ENC_HEVC_PROFILE_FREXT_GUID; + default: break; } + std::string msg = "Unknown Encoding Profile."; + CV_LOG_WARNING(NULL, msg); + CV_Error(Error::StsUnsupportedFormat, msg); +} - // YV12/IYUV are both 4:2:0 planar formats (12bpc) - // Luma, U, V chroma planar (12bpc), chroma is subsampled (w/2,h/2) - void copyYV12orIYUVFrame(Size frameSize, const GpuMat& src, GpuMat& dst) - { - // Source is YV12/IYUV, this native format is converted to NV12 format by the video encoder - - // (1) luma copy setup - CUDA_MEMCPY2D stCopyLuma; - memset(&stCopyLuma, 0, sizeof(CUDA_MEMCPY2D)); - - stCopyLuma.srcXInBytes = 0; - stCopyLuma.srcY = 0; - stCopyLuma.srcMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyLuma.srcHost = 0; - stCopyLuma.srcDevice = (CUdeviceptr) src.data; - stCopyLuma.srcArray = 0; - stCopyLuma.srcPitch = src.step; - - stCopyLuma.dstXInBytes = 0; - stCopyLuma.dstY = 0; - stCopyLuma.dstMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyLuma.dstHost = 0; - stCopyLuma.dstDevice = (CUdeviceptr) dst.data; - stCopyLuma.dstArray = 0; - stCopyLuma.dstPitch = dst.step; - - stCopyLuma.WidthInBytes = frameSize.width; - stCopyLuma.Height = frameSize.height; - - // (2) chroma copy setup, U/V can be done together - CUDA_MEMCPY2D stCopyChroma; - memset(&stCopyChroma, 0, sizeof(CUDA_MEMCPY2D)); - - stCopyChroma.srcXInBytes = 0; - stCopyChroma.srcY = frameSize.height << 1; // U/V chroma offset - stCopyChroma.srcMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyChroma.srcHost = 0; - stCopyChroma.srcDevice = (CUdeviceptr) src.data; - stCopyChroma.srcArray = 0; - stCopyChroma.srcPitch = src.step >> 1; // chroma is subsampled by 2 (but it has U/V are next to each other) - - stCopyChroma.dstXInBytes = 0; - stCopyChroma.dstY = frameSize.height << 1; // chroma offset (srcY*srcPitch now points to the chroma planes) - stCopyChroma.dstMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyChroma.dstHost = 0; - stCopyChroma.dstDevice = (CUdeviceptr) dst.data; - stCopyChroma.dstArray = 0; - stCopyChroma.dstPitch = dst.step >> 1; - - stCopyChroma.WidthInBytes = frameSize.width >> 1; - stCopyChroma.Height = frameSize.height; // U/V are sent together - - // DMA Luma - cuSafeCall( cuMemcpy2D(&stCopyLuma) ); - - // DMA Chroma channels (UV side by side) - cuSafeCall( cuMemcpy2D(&stCopyChroma) ); +GUID EncodingPresetGuid(const ENC_PRESET nvPreset) { + switch (nvPreset) { + case ENC_PRESET_P1: return NV_ENC_PRESET_P1_GUID; + case ENC_PRESET_P2: return NV_ENC_PRESET_P2_GUID; + case ENC_PRESET_P3: return NV_ENC_PRESET_P3_GUID; + case ENC_PRESET_P4: return NV_ENC_PRESET_P4_GUID; + case ENC_PRESET_P5: return NV_ENC_PRESET_P5_GUID; + case ENC_PRESET_P6: return NV_ENC_PRESET_P6_GUID; + case ENC_PRESET_P7: return NV_ENC_PRESET_P7_GUID; + default: break; } + std::string msg = "Unknown Nvidia Encoding Preset."; + CV_LOG_WARNING(NULL, msg); + CV_Error(Error::StsUnsupportedFormat, msg); +} - // NV12 is 4:2:0 format (12bpc) - // Luma followed by U/V chroma interleaved (12bpc), chroma is subsampled (w/2,h/2) - void copyNV12Frame(Size frameSize, const GpuMat& src, GpuMat& dst) - { - // Source is NV12 in pitch linear memory - // Because we are assume input is NV12 (if we take input in the native format), the encoder handles NV12 as a native format in pitch linear memory - - // Luma/Chroma can be done in a single transfer - CUDA_MEMCPY2D stCopyNV12; - memset(&stCopyNV12, 0, sizeof(CUDA_MEMCPY2D)); - - stCopyNV12.srcXInBytes = 0; - stCopyNV12.srcY = 0; - stCopyNV12.srcMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyNV12.srcHost = 0; - stCopyNV12.srcDevice = (CUdeviceptr) src.data; - stCopyNV12.srcArray = 0; - stCopyNV12.srcPitch = src.step; - - stCopyNV12.dstXInBytes = 0; - stCopyNV12.dstY = 0; - stCopyNV12.dstMemoryType = CU_MEMORYTYPE_DEVICE; - stCopyNV12.dstHost = 0; - stCopyNV12.dstDevice = (CUdeviceptr) dst.data; - stCopyNV12.dstArray = 0; - stCopyNV12.dstPitch = dst.step; - - stCopyNV12.WidthInBytes = frameSize.width; - stCopyNV12.Height = (frameSize.height * 3) >> 1; - - // DMA Luma/Chroma - cuSafeCall( cuMemcpy2D(&stCopyNV12) ); - } +bool Equal(const GUID& g1, const GUID& g2) { + if (std::tie(g1.Data1, g1.Data2, g1.Data3, g1.Data4) == std::tie(g2.Data1, g2.Data2, g2.Data3, g2.Data4)) + return true; + return false; +} - void VideoWriterImpl::write(InputArray _frame, bool lastFrame) - { - GpuMat frame = _frame.getGpuMat(); +void VideoWriterImpl::InitializeEncoder(NvEncoderCuda* const pEnc, const GUID codec, const double fps) +{ + NV_ENC_INITIALIZE_PARAMS initializeParams = { NV_ENC_INITIALIZE_PARAMS_VER }; + NV_ENC_CONFIG encodeConfig = { NV_ENC_CONFIG_VER }; + initializeParams.encodeConfig = &encodeConfig; + pEnc->CreateDefaultEncoderParams(&initializeParams, codec, EncodingPresetGuid(encoderParams.nvPreset), (NV_ENC_TUNING_INFO)encoderParams.tuningInfo); + FrameRate(fps, initializeParams.frameRateNum, initializeParams.frameRateDen); + initializeParams.encodeConfig->profileGUID = EncodingProfileGuid(encoderParams.encodingProfile); + initializeParams.encodeConfig->rcParams.rateControlMode = (NV_ENC_PARAMS_RC_MODE)(encoderParams.rateControlMode + encoderParams.multiPassEncoding); + initializeParams.encodeConfig->rcParams.constQP = { encoderParams.constQp.qpInterB, encoderParams.constQp.qpInterB,encoderParams.constQp.qpInterB }; + initializeParams.encodeConfig->rcParams.averageBitRate = encoderParams.averageBitRate; + initializeParams.encodeConfig->rcParams.maxBitRate = encoderParams.maxBitRate; + initializeParams.encodeConfig->rcParams.targetQuality = encoderParams.targetQuality; + initializeParams.encodeConfig->gopLength = encoderParams.gopLength; + if (Equal(codec, NV_ENC_CODEC_H264_GUID)) + initializeParams.encodeConfig->encodeCodecConfig.h264Config.idrPeriod = encoderParams.gopLength; + else if (Equal(codec, NV_ENC_CODEC_HEVC_GUID)) + initializeParams.encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = encoderParams.gopLength; + pEnc->CreateEncoder(&initializeParams); +} - if (inputFormat_ == SF_BGR) - { - CV_Assert( frame.size() == frameSize_ ); - CV_Assert( frame.type() == CV_8UC1 || frame.type() == CV_8UC3 || frame.type() == CV_8UC4 ); - } - else - { - CV_Assert( frame.size() == videoFrame_.size() ); - CV_Assert( frame.type() == videoFrame_.type() ); +void VideoWriterImpl::CopyToNvSurface(const InputArray src) +{ + const NvEncInputFrame* encoderInputFrame = pEnc->GetNextInputFrame(); + CV_Assert(src.isGpuMat() || src.isMat()); + if (surfaceFormatCv != COLOR_FORMAT_CV::UNDEFINED) + CV_Assert(src.size() == Size(pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight())); + Npp8u* dst = (Npp8u*)encoderInputFrame->inputPtr; + if (surfaceFormatCv == COLOR_FORMAT_CV::BGR || surfaceFormatCv == COLOR_FORMAT_CV::RGB) { + GpuMat srcDevice; + if (src.isGpuMat()) + srcDevice = src.getGpuMat(); + else { + if (stream) + srcDevice.upload(src, stream); + else + srcDevice.upload(src); } - - NVVE_EncodeFrameParams efparams; - efparams.Width = frameSize_.width; - efparams.Height = frameSize_.height; - efparams.Pitch = static_cast(videoFrame_.step); - efparams.SurfFmt = surfaceFormat_; - efparams.PictureStruc = FRAME_PICTURE; - efparams.topfieldfirst = 0; - efparams.repeatFirstField = 0; - efparams.progressiveFrame = (surfaceFormat_ == NV12) ? 1 : 0; - efparams.bLast = lastFrame; - efparams.picBuf = 0; // Must be set to NULL in order to support device memory input - - // Don't forget we need to lock/unlock between memcopies - cuSafeCall( cuvidCtxLock(cuCtxLock_, 0) ); - - if (inputFormat_ == SF_BGR) - { - RGB_to_YV12(frame, videoFrame_); + Npp8u* pDst[3] = { dst, &dst[encoderInputFrame->pitch * pEnc->GetEncodeHeight()], &dst[encoderInputFrame->pitch * pEnc->GetEncodeHeight() * 2] }; + const NppiSize oSizeROI = { pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight() }; + if (surfaceFormatCv == COLOR_FORMAT_CV::BGR) { + GpuMat dstGpuMat(pEnc->GetEncodeHeight(), pEnc->GetEncodeWidth(), CV_8UC4, dst, encoderInputFrame->pitch); + cuda::cvtColor(srcDevice, dstGpuMat, COLOR_BGR2BGRA, 0, stream); } - else - { - switch (surfaceFormat_) - { - case UYVY: // UYVY (4:2:2) - case YUY2: // YUY2 (4:2:2) - copyUYVYorYUY2Frame(frameSize_, frame, videoFrame_); - break; - - case YV12: // YV12 (4:2:0), Y V U - case IYUV: // IYUV (4:2:0), Y U V - copyYV12orIYUVFrame(frameSize_, frame, videoFrame_); - break; - - case NV12: // NV12 (4:2:0) - copyNV12Frame(frameSize_, frame, videoFrame_); - break; - } + else { + GpuMat dstGpuMat(pEnc->GetEncodeHeight(), pEnc->GetEncodeWidth(), CV_8UC4, dst, encoderInputFrame->pitch); + cuda::cvtColor(srcDevice, dstGpuMat, COLOR_RGB2RGBA, 0, stream); } - - cuSafeCall( cuvidCtxUnlock(cuCtxLock_, 0) ); - - int err = NVEncodeFrame(encoder_, &efparams, 0, videoFrame_.data); - CV_Assert( err == 0 ); } - - unsigned char* NVENCAPI VideoWriterImpl::HandleAcquireBitStream(int* pBufferSize, void* pUserdata) - { - VideoWriterImpl* thiz = static_cast(pUserdata); - - return thiz->callback_->acquireBitStream(pBufferSize); - } - - void NVENCAPI VideoWriterImpl::HandleReleaseBitStream(int nBytesInBuffer, unsigned char* cb, void* pUserdata) - { - VideoWriterImpl* thiz = static_cast(pUserdata); - - thiz->callback_->releaseBitStream(cb, nBytesInBuffer); - } - - void NVENCAPI VideoWriterImpl::HandleOnBeginFrame(const NVVE_BeginFrameInfo* pbfi, void* pUserdata) - { - VideoWriterImpl* thiz = static_cast(pUserdata); - - thiz->callback_->onBeginFrame(pbfi->nFrameNumber, static_cast(pbfi->nPicType)); - } - - void NVENCAPI VideoWriterImpl::HandleOnEndFrame(const NVVE_EndFrameInfo* pefi, void* pUserdata) - { - VideoWriterImpl* thiz = static_cast(pUserdata); - - thiz->callback_->onEndFrame(pefi->nFrameNumber, static_cast(pefi->nPicType)); - } - - /////////////////////////////////////////////////////////////////////////// - // FFMPEG - - class EncoderCallBackFFMPEG : public EncoderCallBack - { - public: - EncoderCallBackFFMPEG(const String& fileName, Size frameSize, double fps); - ~EncoderCallBackFFMPEG(); - - unsigned char* acquireBitStream(int* bufferSize); - void releaseBitStream(unsigned char* data, int size); - void onBeginFrame(int frameNumber, PicType picType); - void onEndFrame(int frameNumber, PicType picType); - - private: - static bool init_MediaStream_FFMPEG(); - - struct OutputMediaStream_FFMPEG* stream_; - std::vector buf_; - bool isKeyFrame_; - - static Create_OutputMediaStream_FFMPEG_Plugin create_OutputMediaStream_FFMPEG_p; - static Release_OutputMediaStream_FFMPEG_Plugin release_OutputMediaStream_FFMPEG_p; - static Write_OutputMediaStream_FFMPEG_Plugin write_OutputMediaStream_FFMPEG_p; - }; - - Create_OutputMediaStream_FFMPEG_Plugin EncoderCallBackFFMPEG::create_OutputMediaStream_FFMPEG_p = 0; - Release_OutputMediaStream_FFMPEG_Plugin EncoderCallBackFFMPEG::release_OutputMediaStream_FFMPEG_p = 0; - Write_OutputMediaStream_FFMPEG_Plugin EncoderCallBackFFMPEG::write_OutputMediaStream_FFMPEG_p = 0; - - bool EncoderCallBackFFMPEG::init_MediaStream_FFMPEG() - { - static bool initialized = false; - - if (!initialized) - { - #if defined(_WIN32) - const char* module_name = "opencv_ffmpeg" - CVAUX_STR(CV_VERSION_MAJOR) CVAUX_STR(CV_VERSION_MINOR) CVAUX_STR(CV_VERSION_REVISION) - #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__) - "_64" - #endif - ".dll"; - - static HMODULE cvFFOpenCV = LoadLibrary(module_name); - - if (cvFFOpenCV) - { - create_OutputMediaStream_FFMPEG_p = - (Create_OutputMediaStream_FFMPEG_Plugin)GetProcAddress(cvFFOpenCV, "create_OutputMediaStream_FFMPEG"); - release_OutputMediaStream_FFMPEG_p = - (Release_OutputMediaStream_FFMPEG_Plugin)GetProcAddress(cvFFOpenCV, "release_OutputMediaStream_FFMPEG"); - write_OutputMediaStream_FFMPEG_p = - (Write_OutputMediaStream_FFMPEG_Plugin)GetProcAddress(cvFFOpenCV, "write_OutputMediaStream_FFMPEG"); - - initialized = create_OutputMediaStream_FFMPEG_p != 0 && release_OutputMediaStream_FFMPEG_p != 0 && write_OutputMediaStream_FFMPEG_p != 0; - } - #elif defined(HAVE_FFMPEG) - create_OutputMediaStream_FFMPEG_p = create_OutputMediaStream_FFMPEG; - release_OutputMediaStream_FFMPEG_p = release_OutputMediaStream_FFMPEG; - write_OutputMediaStream_FFMPEG_p = write_OutputMediaStream_FFMPEG; - - initialized = true; - #endif + else if (surfaceFormatCv == COLOR_FORMAT_CV::GRAY) { + const cudaMemcpyKind memcpyKind = src.isGpuMat() ? cudaMemcpyDeviceToDevice : cudaMemcpyHostToDevice; + const void* srcPtr = src.isGpuMat() ? src.getGpuMat().data : src.getMat().data; + const size_t srcPitch = src.isGpuMat() ? src.getGpuMat().step : src.getMat().step; + const uint32_t chromaHeight = NvEncoder::GetChromaHeight(NV_ENC_BUFFER_FORMAT_NV12, pEnc->GetEncodeHeight()); + if (stream) { + cudaMemcpy2DAsync(dst, encoderInputFrame->pitch, srcPtr, srcPitch, pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight(), memcpyKind, + cuda::StreamAccessor::getStream(stream)); + cudaMemset2DAsync(&dst[encoderInputFrame->pitch * pEnc->GetEncodeHeight()], encoderInputFrame->pitch, 128, pEnc->GetEncodeWidth(), chromaHeight, + cuda::StreamAccessor::getStream(stream)); + } + else { + cudaMemcpy2D(dst, encoderInputFrame->pitch, srcPtr, srcPitch, pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight(), memcpyKind); + cudaMemset2D(&dst[encoderInputFrame->pitch * pEnc->GetEncodeHeight()], encoderInputFrame->pitch, 128, pEnc->GetEncodeWidth(), chromaHeight); } - - return initialized; - } - - EncoderCallBackFFMPEG::EncoderCallBackFFMPEG(const String& fileName, Size frameSize, double fps) : - stream_(0), isKeyFrame_(false) - { - int buf_size = std::max(frameSize.area() * 4, 1024 * 1024); - buf_.resize(buf_size); - - CV_Assert( init_MediaStream_FFMPEG() ); - - stream_ = create_OutputMediaStream_FFMPEG_p(fileName.c_str(), frameSize.width, frameSize.height, fps); - CV_Assert( stream_ != 0 ); - } - - EncoderCallBackFFMPEG::~EncoderCallBackFFMPEG() - { - release_OutputMediaStream_FFMPEG_p(stream_); - } - - unsigned char* EncoderCallBackFFMPEG::acquireBitStream(int* bufferSize) - { - *bufferSize = static_cast(buf_.size()); - return &buf_[0]; - } - - void EncoderCallBackFFMPEG::releaseBitStream(unsigned char* data, int size) - { - write_OutputMediaStream_FFMPEG_p(stream_, data, size, isKeyFrame_); } - - void EncoderCallBackFFMPEG::onBeginFrame(int frameNumber, PicType picType) - { - CV_UNUSED(frameNumber); - isKeyFrame_ = (picType == IFRAME); - } - - void EncoderCallBackFFMPEG::onEndFrame(int frameNumber, PicType picType) - { - CV_UNUSED(frameNumber); - CV_UNUSED(picType); + else { + void* srcPtr = src.isGpuMat() ? src.getGpuMat().data : src.getMat().data; + const CUmemorytype cuMemoryType = src.isGpuMat() ? CU_MEMORYTYPE_DEVICE : CU_MEMORYTYPE_HOST; + NvEncoderCuda::CopyToDeviceFrame(cuContext, srcPtr, src.step(), (CUdeviceptr)encoderInputFrame->inputPtr, (int)encoderInputFrame->pitch, pEnc->GetEncodeWidth(), + pEnc->GetEncodeHeight(), cuMemoryType, encoderInputFrame->bufferFormat, encoderInputFrame->chromaOffsets, encoderInputFrame->numChromaPlanes, + false, cuda::StreamAccessor::getStream(stream)); } } -/////////////////////////////////////////////////////////////////////////// -// EncoderParams +void VideoWriterImpl::write(const InputArray frame) { + CV_Assert(frame.channels() == nSrcChannels); + CopyToNvSurface(frame); + pEnc->EncodeFrame(vPacket); + encoderCallBack->onEncoded(vPacket); +}; -cv::cudacodec::EncoderParams::EncoderParams() +EncoderParams VideoWriterImpl::getEncoderParams() const { + return encoderParams; +}; + +Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV colorFormat, const Stream& stream) { - P_Interval = 3; - IDR_Period = 15; - DynamicGOP = 0; - RCType = 1; - AvgBitrate = 4000000; - PeakBitrate = 10000000; - QP_Level_Intra = 25; - QP_Level_InterP = 28; - QP_Level_InterB = 31; - DeblockMode = 1; - ProfileLevel = 65357; - ForceIntra = 0; - ForceIDR = 0; - ClearStat = 0; - DIMode = 1; - Presets = 2; - DisableCabac = 0; - NaluFramingType = 0; - DisableSPSPPS = 0; + Ptr rawVideoWriter = new RawVideoWriter(fileName); + return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, stream); } -cv::cudacodec::EncoderParams::EncoderParams(const String& configFile) +Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream) { - load(configFile); + Ptr rawVideoWriter = new RawVideoWriter(fileName); + return createVideoWriter(rawVideoWriter, frameSize, codec, fps, bufferFormat, stream); } -void cv::cudacodec::EncoderParams::load(const String& configFile) +Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream) { - FileStorage fs(configFile, FileStorage::READ); - CV_Assert( fs.isOpened() ); - - read(fs["P_Interval" ], P_Interval, 3); - read(fs["IDR_Period" ], IDR_Period, 15); - read(fs["DynamicGOP" ], DynamicGOP, 0); - read(fs["RCType" ], RCType, 1); - read(fs["AvgBitrate" ], AvgBitrate, 4000000); - read(fs["PeakBitrate" ], PeakBitrate, 10000000); - read(fs["QP_Level_Intra" ], QP_Level_Intra, 25); - read(fs["QP_Level_InterP"], QP_Level_InterP, 28); - read(fs["QP_Level_InterB"], QP_Level_InterB, 31); - read(fs["DeblockMode" ], DeblockMode, 1); - read(fs["ProfileLevel" ], ProfileLevel, 65357); - read(fs["ForceIntra" ], ForceIntra, 0); - read(fs["ForceIDR" ], ForceIDR, 0); - read(fs["ClearStat" ], ClearStat, 0); - read(fs["DIMode" ], DIMode, 1); - read(fs["Presets" ], Presets, 2); - read(fs["DisableCabac" ], DisableCabac, 0); - read(fs["NaluFramingType"], NaluFramingType, 0); - read(fs["DisableSPSPPS" ], DisableSPSPPS, 0); + Ptr rawVideoWriter = new RawVideoWriter(fileName); + return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, params, stream); } -void cv::cudacodec::EncoderParams::save(const String& configFile) const +Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream) { - FileStorage fs(configFile, FileStorage::WRITE); - CV_Assert( fs.isOpened() ); - - write(fs, "P_Interval" , P_Interval); - write(fs, "IDR_Period" , IDR_Period); - write(fs, "DynamicGOP" , DynamicGOP); - write(fs, "RCType" , RCType); - write(fs, "AvgBitrate" , AvgBitrate); - write(fs, "PeakBitrate" , PeakBitrate); - write(fs, "QP_Level_Intra" , QP_Level_Intra); - write(fs, "QP_Level_InterP", QP_Level_InterP); - write(fs, "QP_Level_InterB", QP_Level_InterB); - write(fs, "DeblockMode" , DeblockMode); - write(fs, "ProfileLevel" , ProfileLevel); - write(fs, "ForceIntra" , ForceIntra); - write(fs, "ForceIDR" , ForceIDR); - write(fs, "ClearStat" , ClearStat); - write(fs, "DIMode" , DIMode); - write(fs, "Presets" , Presets); - write(fs, "DisableCabac" , DisableCabac); - write(fs, "NaluFramingType", NaluFramingType); - write(fs, "DisableSPSPPS" , DisableSPSPPS); + Ptr rawVideoWriter = new RawVideoWriter(fileName); + return createVideoWriter(rawVideoWriter, frameSize, codec, fps, bufferFormat, params, stream); } -/////////////////////////////////////////////////////////////////////////// -// createVideoWriter - -Ptr cv::cudacodec::createVideoWriter(const String& fileName, Size frameSize, double fps, SurfaceFormat format) +Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV colorFormat, const Stream& stream) { - Ptr encoderCallback(new EncoderCallBackFFMPEG(fileName, frameSize, fps)); - return createVideoWriter(encoderCallback, frameSize, fps, format); + return makePtr(encoderCallBack, frameSize, codec, fps, colorFormat, stream); } -Ptr cv::cudacodec::createVideoWriter(const String& fileName, Size frameSize, double fps, const EncoderParams& params, SurfaceFormat format) +Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream) { - Ptr encoderCallback(new EncoderCallBackFFMPEG(fileName, frameSize, fps)); - return createVideoWriter(encoderCallback, frameSize, fps, params, format); + return makePtr(encoderCallBack, frameSize, codec, fps, bufferFormat, stream); } -Ptr cv::cudacodec::createVideoWriter(const Ptr& encoderCallback, Size frameSize, double fps, SurfaceFormat format) +Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, + const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream) { - return makePtr(encoderCallback, frameSize, fps, format); + return makePtr(encoderCallBack, frameSize, codec, fps, colorFormat, params, stream); } -Ptr cv::cudacodec::createVideoWriter(const Ptr& encoderCallback, Size frameSize, double fps, const EncoderParams& params, SurfaceFormat format) +Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, + const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream) { - return makePtr(encoderCallback, frameSize, fps, params, format); + return makePtr(encoderCallBack, frameSize, codec, fps, bufferFormat, params, stream); } +#endif // !defined HAVE_NVCUVENC -#endif // !defined HAVE_NVCUVENC || !defined _WIN32 || defined HAVE_FFMPEG_WRAPPER +}} diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index b194ffa2f8..6271243f59 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -144,7 +144,7 @@ CUDA_TEST_P(CheckExtraData, Reader) ASSERT_TRUE(reader->grab()); cv::Mat extraData; const bool newData = reader->retrieve(extraData, extraDataIdx); - ASSERT_TRUE(newData && sz || !newData && !sz); + ASSERT_TRUE((newData && sz) || (!newData && !sz)); ASSERT_EQ(extraData.total(), sz); } @@ -174,7 +174,7 @@ CUDA_TEST_P(CheckKeyFrame, Reader) nPackages++; double containsKeyFrame = i; ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_LRF_HAS_KEY_FRAME, containsKeyFrame)); - ASSERT_TRUE(nPackages == 1 && containsKeyFrame || nPackages == 2 && !containsKeyFrame) << "nPackage: " << i; + ASSERT_TRUE((nPackages == 1 && containsKeyFrame) || (nPackages == 2 && !containsKeyFrame)) << "nPackage: " << i; if (nPackages >= maxNPackagesToCheck) break; } @@ -423,54 +423,361 @@ CUDA_TEST_P(CheckInitParams, Reader) #endif // HAVE_NVCUVID -#if defined(_WIN32) && defined(HAVE_NVCUVENC) +#if defined(HAVE_NVCUVID) && defined(HAVE_NVCUVENC) +struct TransCode : testing::TestWithParam +{ + cv::cuda::DeviceInfo devInfo; + virtual void SetUp() + { + devInfo = GetParam(); + cv::cuda::setDevice(devInfo.deviceID()); + } +}; + + +CUDA_TEST_P(TransCode, H264ToH265) +{ + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.h264"; + constexpr cv::cudacodec::ENC_BUFFER_FORMAT surfaceFormatNv = cv::cudacodec::ENC_BUFFER_FORMAT::BF_NV12; + constexpr double fps = 25; + const cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::HEVC; + const std::string ext = ".h265"; + const std::string outputFile = cv::tempfile(ext.c_str()); + constexpr int nFrames = 5; + Size frameSz; + { + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); + cv::cudacodec::FormatInfo fmt = reader->format(); + reader->set(cudacodec::ColorFormat::YUV); + cv::Ptr writer; + cv::cuda::GpuMat frame; + cv::cuda::Stream stream; + for (int i = 0; i < nFrames; ++i) { + ASSERT_TRUE(reader->nextFrame(frame, stream)); + if (!fmt.valid) { + fmt = reader->format(); + ASSERT_TRUE(fmt.valid); + } + ASSERT_FALSE(frame.empty()); + Mat tst; frame.download(tst); + if (writer.empty()) { + frameSz = Size(fmt.width, fmt.height); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, surfaceFormatNv, stream); + } + writer->write(frame); + } + } + + { + cv::VideoCapture cap(outputFile); + ASSERT_TRUE(cap.isOpened()); + const int width = cap.get(CAP_PROP_FRAME_WIDTH); + const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + ASSERT_EQ(frameSz, Size(width, height)); + ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); + Mat frame; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + } + } +} + +INSTANTIATE_TEST_CASE_P(CUDA_Codec, TransCode, ALL_DEVICES); +#endif + +#if defined(HAVE_NVCUVENC) + ////////////////////////////////////////////////////// // VideoWriter -CUDA_TEST_P(Video, Writer) +//========================================================================== + +bool CvtColor(const Mat& in, Mat& out, const cudacodec::ENC_BUFFER_FORMAT surfaceFormatNv) { + switch (surfaceFormatNv) { + case(cudacodec::ENC_BUFFER_FORMAT::BF_ARGB): + cv::cvtColor(in, out, COLOR_BGR2BGRA); + return true; + case(cudacodec::ENC_BUFFER_FORMAT::BF_ABGR): + cv::cvtColor(in, out, COLOR_BGR2RGBA); + return true; + default: + return false; + } +} + +PARAM_TEST_CASE(WriteNv, cuda::DeviceInfo, bool, cudacodec::ENC_BUFFER_FORMAT) +{ +}; + +CUDA_TEST_P(WriteNv, Writer) { cv::cuda::setDevice(GET_PARAM(0).deviceID()); + const bool deviceSrc = GET_PARAM(1); + const cv::cudacodec::ENC_BUFFER_FORMAT surfaceFormatNv = GET_PARAM(2); + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; + constexpr double fps = 25; + const cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::H264; + const std::string ext = ".h264"; + const std::string outputFile = cv::tempfile(ext.c_str()); + constexpr int nFrames = 5; + Size frameSz; + { + cv::VideoCapture cap(inputFile); + ASSERT_TRUE(cap.isOpened()); + cv::Ptr writer; + cv::Mat frame, frameNewSf; + cv::cuda::GpuMat dFrame; + cv::cuda::Stream stream; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (writer.empty()) { + frameSz = frame.size(); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, surfaceFormatNv, stream); + } + CvtColor(frame, frameNewSf, surfaceFormatNv); + if (deviceSrc) { + dFrame.upload(frameNewSf); + writer->write(dFrame); + } + else + writer->write(frameNewSf); + } + } + + { + cv::VideoCapture cap(outputFile); + ASSERT_TRUE(cap.isOpened()); + const int width = cap.get(CAP_PROP_FRAME_WIDTH); + const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + ASSERT_EQ(frameSz, Size(width, height)); + ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); + Mat frame; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + } + } +} - const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "video/" + GET_PARAM(1); +#define DEVICE_SRC true, false +#define SURFACE_FORMAT_NV cv::cudacodec::ENC_BUFFER_FORMAT::BF_ARGB, cv::cudacodec::ENC_BUFFER_FORMAT::BF_ARGB +INSTANTIATE_TEST_CASE_P(CUDA_Codec, WriteNv, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(SURFACE_FORMAT_NV))); + +void CvtColor(const Mat& in, Mat& out, const cudacodec::COLOR_FORMAT_CV surfaceFormatCv) { + switch (surfaceFormatCv) { + case(cudacodec::COLOR_FORMAT_CV::RGB): + return cv::cvtColor(in, out, COLOR_BGR2RGB); + case(cudacodec::COLOR_FORMAT_CV::BGRA): + return cv::cvtColor(in, out, COLOR_BGR2BGRA); + case(cudacodec::COLOR_FORMAT_CV::RGBA): + return cv::cvtColor(in, out, COLOR_BGR2RGBA); + case(cudacodec::COLOR_FORMAT_CV::GRAY): + return cv::cvtColor(in, out, COLOR_BGR2GRAY); + default: + in.copyTo(out); + } +} - std::string outputFile = cv::tempfile(".avi"); - const double FPS = 25.0; +PARAM_TEST_CASE(WriteCv, cv::cuda::DeviceInfo, bool, cv::cudacodec::VideoWriterCodec, double, cv::cudacodec::COLOR_FORMAT_CV) +{ +}; + +CUDA_TEST_P(WriteCv, Writer) +{ + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; + const bool deviceSrc = GET_PARAM(1); + const cudacodec::VideoWriterCodec codec = GET_PARAM(2); + const double fps = GET_PARAM(3); + const cv::cudacodec::COLOR_FORMAT_CV surfaceFormatCv = GET_PARAM(4); + const std::string ext = codec == cudacodec::VideoWriterCodec::H264 ? ".h264" : ".hevc"; + const std::string outputFile = cv::tempfile(ext.c_str()); + constexpr int nFrames = 5; + Size frameSz; + { + cv::VideoCapture cap(inputFile); + ASSERT_TRUE(cap.isOpened()); + cv::Ptr writer; + cv::Mat frame, frameNewSf; + cv::cuda::GpuMat dFrame; + cv::cuda::Stream stream; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + if (writer.empty()) { + frameSz = frame.size(); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, surfaceFormatCv, stream); + } + CvtColor(frame, frameNewSf, surfaceFormatCv); + if (deviceSrc) { + dFrame.upload(frameNewSf); + writer->write(dFrame); + } + else + writer->write(frameNewSf); + } + } - cv::VideoCapture reader(inputFile); - ASSERT_TRUE(reader.isOpened()); + { + cv::VideoCapture cap(outputFile); + ASSERT_TRUE(cap.isOpened()); + const int width = cap.get(CAP_PROP_FRAME_WIDTH); + const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + ASSERT_EQ(frameSz, Size(width, height)); + ASSERT_TRUE(abs(fps - cap.get(CAP_PROP_FPS)) < 0.5); + Mat frame; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + } + } +} - cv::Ptr d_writer; +#define DEVICE_SRC true, false +#define FPS 10, 29.7 +#define CODEC cv::cudacodec::VideoWriterCodec::H264, cv::cudacodec::VideoWriterCodec::HEVC +#define SURFACE_FORMAT_CV cv::cudacodec::COLOR_FORMAT_CV::BGR, cv::cudacodec::COLOR_FORMAT_CV::RGB, cv::cudacodec::COLOR_FORMAT_CV::BGRA, \ +cv::cudacodec::COLOR_FORMAT_CV::RGBA, cv::cudacodec::COLOR_FORMAT_CV::GRAY +INSTANTIATE_TEST_CASE_P(CUDA_Codec, WriteCv, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(CODEC), testing::Values(FPS), + testing::Values(SURFACE_FORMAT_CV))); - cv::Mat frame; - cv::cuda::GpuMat d_frame; - for (int i = 0; i < 10; ++i) +struct EncoderParamsBase : testing::TestWithParam +{ + cv::cuda::DeviceInfo devInfo; + cv::cudacodec::EncoderParams params; + virtual void SetUp() { - reader >> frame; - ASSERT_FALSE(frame.empty()); + devInfo = GetParam(); + cv::cuda::setDevice(devInfo.deviceID()); + // Fixed params for CBR test + params.nvPreset = cv::cudacodec::ENC_PRESET::ENC_PRESET_P7; + params.tuningInfo = cv::cudacodec::ENC_TUNING_INFO::ENC_TUNING_INFO_HIGH_QUALITY; + params.encodingProfile = cv::cudacodec::ENC_PROFILE::ENC_H264_PROFILE_MAIN; + params.rateControlMode = cv::cudacodec::ENC_PARAMS_RC_MODE::ENC_PARAMS_RC_CBR; + params.multiPassEncoding = cv::cudacodec::ENC_MULTI_PASS::ENC_TWO_PASS_FULL_RESOLUTION; + params.averageBitRate = 1e6; + params.maxBitRate = 0; + params.targetQuality = 0; + params.gopLength = 5; + } +}; - d_frame.upload(frame); +struct EncoderParamsCv : EncoderParamsBase +{ +}; - if (d_writer.empty()) - d_writer = cv::cudacodec::createVideoWriter(outputFile, frame.size(), FPS); +CUDA_TEST_P(EncoderParamsCv, Writer) +{ + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; + constexpr double fps = 25.0; + constexpr cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::H264; + const std::string ext = ".h264"; + const std::string outputFile = cv::tempfile(ext.c_str()); + Size frameSz; + constexpr int nFrames = 5; + { + cv::VideoCapture reader(inputFile); + ASSERT_TRUE(reader.isOpened()); + const cv::cudacodec::COLOR_FORMAT_CV format = cv::cudacodec::COLOR_FORMAT_CV::BGR; + cv::Ptr writer; + cv::Mat frame; + cv::cuda::GpuMat dFrame; + cv::cuda::Stream stream; + for (int i = 0; i < nFrames; ++i) { + reader >> frame; + ASSERT_FALSE(frame.empty()); + dFrame.upload(frame); + if (writer.empty()) { + frameSz = frame.size(); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, format, params, stream); + cv::cudacodec::EncoderParams paramsOut = writer->getEncoderParams(); + ASSERT_EQ(params, paramsOut); + } + writer->write(dFrame); + } + } - d_writer->write(d_frame); + { + cv::VideoCapture cap(outputFile); + ASSERT_TRUE(cap.isOpened()); + const int width = cap.get(CAP_PROP_FRAME_WIDTH); + const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + ASSERT_EQ(frameSz, Size(width, height)); + ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); + const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG); + Mat frame; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) + ASSERT_TRUE(i % params.gopLength == 0); + } } +} - reader.release(); - d_writer.release(); +INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParamsCv, ALL_DEVICES); - reader.open(outputFile); - ASSERT_TRUE(reader.isOpened()); +struct EncoderParamsNv : EncoderParamsBase +{ +}; - for (int i = 0; i < 5; ++i) +CUDA_TEST_P(EncoderParamsNv, Writer) +{ + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; + constexpr double fps = 25.0; + constexpr cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::H264; + const std::string ext = ".h264"; + const std::string outputFile = cv::tempfile(ext.c_str()); + Size frameSz; + constexpr int nFrames = 5; { - reader >> frame; - ASSERT_FALSE(frame.empty()); + cv::VideoCapture reader(inputFile); + ASSERT_TRUE(reader.isOpened()); + const cv::cudacodec::ENC_BUFFER_FORMAT format = cv::cudacodec::ENC_BUFFER_FORMAT::BF_ARGB; + cv::Ptr writer; + cv::Mat frame, frameNewSf; + cv::cuda::GpuMat dFrame; + cv::cuda::Stream stream; + for (int i = 0; i < nFrames; ++i) { + reader >> frame; + ASSERT_FALSE(frame.empty()); + cvtColor(frame, frameNewSf, COLOR_BGR2BGRA); + dFrame.upload(frameNewSf); + if (writer.empty()) { + frameSz = frame.size(); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, format, params, stream); + cv::cudacodec::EncoderParams paramsOut = writer->getEncoderParams(); + ASSERT_EQ(params, paramsOut); + } + writer->write(dFrame); + } + } + + { + cv::VideoCapture cap(outputFile); + ASSERT_TRUE(cap.isOpened()); + const int width = cap.get(CAP_PROP_FRAME_WIDTH); + const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + ASSERT_EQ(frameSz, Size(width, height)); + ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); + const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG); + Mat frame; + for (int i = 0; i < nFrames; ++i) { + cap >> frame; + ASSERT_FALSE(frame.empty()); + if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) + ASSERT_TRUE(i % params.gopLength == 0); + } } } -#endif // _WIN32, HAVE_NVCUVENC +INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParamsNv, ALL_DEVICES); + +#endif // HAVE_NVCUVENC INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckSet, testing::Combine( ALL_DEVICES, From 22261ec9760881c084273c86da9081eafeb6059e Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Sun, 9 Oct 2022 16:50:57 +0300 Subject: [PATCH 32/99] Fix documentation warning --- .../cudacodec/include/opencv2/cudacodec.hpp | 30 ++++----- modules/cudacodec/src/video_writer.cpp | 66 +++++++++---------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 6dc25af4a5..cb8b725dd8 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -193,7 +193,7 @@ CV_EXPORTS bool operator==(const EncoderParams& lhs, const EncoderParams& rhs); User can implement own multiplexing by implementing this interface. */ -class CV_EXPORTS_W EncoderCallBack { +class CV_EXPORTS_W EncoderCallback { public: /** @brief Callback function to signal that the encoded bitstream for one or more frames is ready. @@ -205,7 +205,7 @@ class CV_EXPORTS_W EncoderCallBack { * */ virtual void onEncodingFinished() = 0; - virtual ~EncoderCallBack() {} + virtual ~EncoderCallback() {} }; /** @brief Video writer interface. @@ -231,7 +231,7 @@ class CV_EXPORTS_W VideoWriter */ CV_WRAP virtual EncoderParams getEncoderParams() const = 0; - /** @brief Waits until the encoding process has finished before calling EncoderCallBack::onEncodingFinished(). + /** @brief Waits until the encoding process has finished before calling EncoderCallback::onEncodingFinished(). */ CV_WRAP virtual void close() = 0; }; @@ -289,35 +289,35 @@ CV_EXPORTS_W Ptr createVideoWriter(const String& fileNam /** @brief Creates video writer. -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. @param frameSize Size of the input video frames. @param codec Codec. @param fps Framerate of the created video stream. @param colorFormat OpenCv color format of the frames to be encoded. @param stream Stream for frame pre-processing. -The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallBack. +The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallback. */ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, const double fps = 25.0, const COLOR_FORMAT_CV colorFormat = BGR, const Stream& stream = Stream::Null()); /** @brief Creates video writer. -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. @param frameSize Size of the input video frames. @param codec Codec. @param fps Framerate of the created video stream. @param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. @param stream Stream for frame pre-processing. -The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallBack. +The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallback. */ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, const double fps = 25.0, const ENC_BUFFER_FORMAT bufferFormat = BF_ARGB, const Stream& stream = Stream::Null()); /** @brief Creates video writer. -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. @param frameSize Size of the input video frames. @param codec Codec. @param fps Framerate of the created video stream. @@ -325,14 +325,14 @@ CV_EXPORTS_W Ptr createVideoWriter(const Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); /** @brief Creates video writer. -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallBack . Use it if you want to work with the raw video stream. +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. @param frameSize Size of the input video frames. @param codec Codec. @param fps Framerate of the created video stream. @@ -340,9 +340,9 @@ CV_EXPORTS_W Ptr createVideoWriter(const Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); ////////////////////////////////// Video Decoding ////////////////////////////////////////// diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index c33c89bdae..ecf07445b1 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -52,10 +52,10 @@ Ptr createVideoWriter(const String&, const Size, const V Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const COLOR_FORMAT_CV, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const COLOR_FORMAT_CV, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } #else // !defined HAVE_NVCUVENC @@ -72,7 +72,7 @@ bool operator==(const EncoderParams& lhs, const EncoderParams& rhs) rhs.averageBitRate, rhs.maxBitRate, rhs.targetQuality, rhs.gopLength); }; -class RawVideoWriter : public EncoderCallBack +class RawVideoWriter : public EncoderCallback { public: RawVideoWriter(String fileName); @@ -105,13 +105,13 @@ void RawVideoWriter::onEncoded(std::vector> vPacket) { class VideoWriterImpl : public VideoWriter { public: - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV surfaceFormatCv, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT surfaceFormatNv, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV surfaceFormatCv, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT surfaceFormatNv, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); ~VideoWriterImpl(); void write(InputArray frame); @@ -122,7 +122,7 @@ class VideoWriterImpl : public VideoWriter void InitializeEncoder(NvEncoderCuda* const pEnc, const GUID codec, const double fps); void CopyToNvSurface(const InputArray src); - Ptr encoderCallBack; + Ptr encoderCallback; COLOR_FORMAT_CV surfaceFormatCv = COLOR_FORMAT_CV::UNDEFINED; ENC_BUFFER_FORMAT surfaceFormatNv = ENC_BUFFER_FORMAT::BF_UNDEFINED; EncoderParams encoderParams; @@ -160,9 +160,9 @@ int NChannels(const ENC_BUFFER_FORMAT format) { else return 1; } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV surfaceFormatCv_, const EncoderParams& encoderParams_, const Stream& stream_) : - encoderCallBack(encoderCallBack_), surfaceFormatCv(surfaceFormatCv_), encoderParams(encoderParams_), stream(stream_) + encoderCallback(encoderCallBack_), surfaceFormatCv(surfaceFormatCv_), encoderParams(encoderParams_), stream(stream_) { surfaceFormatNv = NvSurfaceFormat(surfaceFormatCv); if (surfaceFormatNv == BF_UNDEFINED) { @@ -174,31 +174,31 @@ VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, c Init(codec, fps, frameSz); } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT surfaceFormatNv_, const EncoderParams& encoderParams_, const Stream& stream_) : - encoderCallBack(encoderCallBack_), surfaceFormatNv(surfaceFormatNv_), encoderParams(encoderParams_), stream(stream_) + encoderCallback(encoderCallBack_), surfaceFormatNv(surfaceFormatNv_), encoderParams(encoderParams_), stream(stream_) { CV_Assert(surfaceFormatNv != BF_UNDEFINED); nSrcChannels = NChannels(surfaceFormatNv); Init(codec, fps, frameSz); } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack, const Size frameSz, const VideoWriterCodec codec, const double fps, +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV surfaceFormatCv, const Stream& stream) : - VideoWriterImpl(encoderCallBack, frameSz, codec, fps, surfaceFormatCv, EncoderParams(), stream) + VideoWriterImpl(encoderCallback, frameSz, codec, fps, surfaceFormatCv, EncoderParams(), stream) { } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack, const Size frameSz, const VideoWriterCodec codec, const double fps, +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT surfaceFormatNv, const Stream& stream) : - VideoWriterImpl(encoderCallBack, frameSz, codec, fps, surfaceFormatNv, EncoderParams(), stream) + VideoWriterImpl(encoderCallback, frameSz, codec, fps, surfaceFormatNv, EncoderParams(), stream) { } void VideoWriterImpl::close() { pEnc->EndEncode(vPacket); - encoderCallBack->onEncoded(vPacket); - encoderCallBack->onEncodingFinished(); + encoderCallback->onEncoded(vPacket); + encoderCallback->onEncodingFinished(); } VideoWriterImpl::~VideoWriterImpl() { @@ -372,7 +372,7 @@ void VideoWriterImpl::write(const InputArray frame) { CV_Assert(frame.channels() == nSrcChannels); CopyToNvSurface(frame); pEnc->EncodeFrame(vPacket); - encoderCallBack->onEncoded(vPacket); + encoderCallback->onEncoded(vPacket); }; EncoderParams VideoWriterImpl::getEncoderParams() const { @@ -382,53 +382,53 @@ EncoderParams VideoWriterImpl::getEncoderParams() const { Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV colorFormat, const Stream& stream) { - Ptr rawVideoWriter = new RawVideoWriter(fileName); + Ptr rawVideoWriter = new RawVideoWriter(fileName); return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, stream); } Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream) { - Ptr rawVideoWriter = new RawVideoWriter(fileName); + Ptr rawVideoWriter = new RawVideoWriter(fileName); return createVideoWriter(rawVideoWriter, frameSize, codec, fps, bufferFormat, stream); } Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream) { - Ptr rawVideoWriter = new RawVideoWriter(fileName); + Ptr rawVideoWriter = new RawVideoWriter(fileName); return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, params, stream); } Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream) { - Ptr rawVideoWriter = new RawVideoWriter(fileName); + Ptr rawVideoWriter = new RawVideoWriter(fileName); return createVideoWriter(rawVideoWriter, frameSize, codec, fps, bufferFormat, params, stream); } -Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, +Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV colorFormat, const Stream& stream) { - return makePtr(encoderCallBack, frameSize, codec, fps, colorFormat, stream); + return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, stream); } -Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, +Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream) { - return makePtr(encoderCallBack, frameSize, codec, fps, bufferFormat, stream); + return makePtr(encoderCallback, frameSize, codec, fps, bufferFormat, stream); } -Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, +Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream) { - return makePtr(encoderCallBack, frameSize, codec, fps, colorFormat, params, stream); + return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, params, stream); } -Ptr createVideoWriter(const Ptr& encoderCallBack, const Size frameSize, const VideoWriterCodec codec, const double fps, +Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream) { - return makePtr(encoderCallBack, frameSize, codec, fps, bufferFormat, params, stream); + return makePtr(encoderCallback, frameSize, codec, fps, bufferFormat, params, stream); } #endif // !defined HAVE_NVCUVENC From e1bd43452860badc19cfd2771a1a92c7bb27b5d9 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Mon, 10 Oct 2022 18:36:23 +0300 Subject: [PATCH 33/99] Address cmake errors, build warnings and use codec supported by windows ffmpeg.dll in perf tests. --- modules/cudacodec/CMakeLists.txt | 26 ++++++--- .../cudacodec/include/opencv2/cudacodec.hpp | 10 ++-- modules/cudacodec/perf/perf_video.cpp | 2 +- modules/cudacodec/src/NvEncoder.cpp | 43 +++++++++------ modules/cudacodec/src/NvEncoderCuda.cpp | 2 +- modules/cudacodec/src/video_writer.cpp | 25 ++++++--- modules/cudacodec/test/test_video.cpp | 55 ++++++++++--------- 7 files changed, 96 insertions(+), 67 deletions(-) diff --git a/modules/cudacodec/CMakeLists.txt b/modules/cudacodec/CMakeLists.txt index 1720d9c7cd..6ff9f1ae9d 100644 --- a/modules/cudacodec/CMakeLists.txt +++ b/modules/cudacodec/CMakeLists.txt @@ -6,20 +6,30 @@ set(the_description "CUDA-accelerated Video Encoding/Decoding") ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 /wd4324 /wd4512 -Wundef -Wshadow) -ocv_add_module(cudacodec opencv_core opencv_videoio opencv_cudaarithm opencv_cudawarping OPTIONAL opencv_cudev WRAP python) +set(required_dependencies opencv_core opencv_videoio opencv_cudaarithm opencv_cudawarping) +if(HAVE_NVCUVENC) + list(APPEND required_dependencies opencv_cudaimgproc) +endif() + +ocv_add_module(cudacodec ${required_dependencies} OPTIONAL opencv_cudev WRAP python) ocv_module_include_directories() ocv_glob_module_sources() set(extra_libs "") -if(HAVE_NVCUVID) - list(APPEND extra_libs ${CUDA_CUDA_LIBRARY} ${CUDA_nvcuvid_LIBRARY}) -endif() - -if(HAVE_NVCUVENC) - list(APPEND extra_libs ${CUDA_CUDA_LIBRARY} ${CUDA_nvencodeapi_LIBRARY}) - ocv_add_module(cudacodec opencv_cudaimgproc) +if(HAVE_NVCUVID OR HAVE_NVCUVENC) + list(APPEND extra_libs ${CUDA_CUDA_LIBRARY}) + if(HAVE_NVCUVID) + list(APPEND extra_libs ${CUDA_nvcuvid_LIBRARY}) + endif() + if(HAVE_NVCUVENC) + if(WIN32) + list(APPEND extra_libs ${CUDA_nvencodeapi_LIBRARY}) + else() + list(APPEND extra_libs ${CUDA_nvidia-encode_LIBRARY}) + endif() + endif() endif() ocv_create_module(${extra_libs}) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index cb8b725dd8..7a17e02922 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -184,7 +184,7 @@ struct CV_EXPORTS_W_SIMPLE EncoderParams CV_PROP_RW ENC_QP constQp; //!< QP's for ENC_PARAMS_RC_CONSTQP. CV_PROP_RW int averageBitRate; //!< target bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CBR. CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CONSTQP. - CV_PROP_RW float targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with ENC_PARAMS_RC_VBR. + CV_PROP_RW uint8_t targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with ENC_PARAMS_RC_VBR. CV_PROP_RW int gopLength; }; CV_EXPORTS bool operator==(const EncoderParams& lhs, const EncoderParams& rhs); @@ -257,8 +257,8 @@ CV_EXPORTS_W Ptr createVideoWriter(const String& fileNam @param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, - const double fps = 25.0, const ENC_BUFFER_FORMAT bufferFormat = BF_ARGB, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, + const double fps, const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream = Stream::Null()); /** @brief Creates video writer. @@ -312,8 +312,8 @@ CV_EXPORTS_W Ptr createVideoWriter(const Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, - const double fps = 25.0, const ENC_BUFFER_FORMAT bufferFormat = BF_ARGB, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, + const double fps, const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream = Stream::Null()); /** @brief Creates video writer. diff --git a/modules/cudacodec/perf/perf_video.cpp b/modules/cudacodec/perf/perf_video.cpp index ba0f7b7d2c..992760630b 100644 --- a/modules/cudacodec/perf/perf_video.cpp +++ b/modules/cudacodec/perf/perf_video.cpp @@ -167,7 +167,7 @@ PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) reader >> frameBgr; ASSERT_FALSE(frameBgr.empty()); if (!writer.isOpened()) - writer.open(outputFile, VideoWriter::fourcc('x', '2', '6', '4'), fps, frameBgr.size()); + writer.open(outputFile, VideoWriter::fourcc('X', 'V', 'I', 'D'), fps, frameBgr.size()); startTimer(); writer.write(frameBgr); stopTimer(); diff --git a/modules/cudacodec/src/NvEncoder.cpp b/modules/cudacodec/src/NvEncoder.cpp index 0a3f12079c..78b76c78e5 100644 --- a/modules/cudacodec/src/NvEncoder.cpp +++ b/modules/cudacodec/src/NvEncoder.cpp @@ -38,7 +38,8 @@ NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWi NVENC_THROW_ERROR("EncodeAPI not found", NV_ENC_ERR_NO_ENCODE_DEVICE); } - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER }; + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = {}; + encodeSessionExParams.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; encodeSessionExParams.device = m_pDevice; encodeSessionExParams.deviceType = m_eDeviceType; encodeSessionExParams.apiVersion = NVENCAPI_VERSION; @@ -58,8 +59,8 @@ void NvEncoder::LoadNvEncApi() NVENC_THROW_ERROR("Current Driver Version does not support this NvEncodeAPI version, please upgrade driver", NV_ENC_ERR_INVALID_VERSION); } - - m_nvenc = { NV_ENCODE_API_FUNCTION_LIST_VER }; + m_nvenc = {}; + m_nvenc.version = NV_ENCODE_API_FUNCTION_LIST_VER; NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc)); } @@ -109,7 +110,9 @@ void NvEncoder::CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeP pIntializeParams->enableEncodeAsync = GetCapabilityValue(codecGuid, NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT); #endif pIntializeParams->tuningInfo = tuningInfo; - NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } }; + NV_ENC_PRESET_CONFIG presetConfig = {}; + presetConfig.version = NV_ENC_PRESET_CONFIG_VER; + presetConfig.presetCfg.version = NV_ENC_CONFIG_VER; m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, tuningInfo, &presetConfig); memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); @@ -200,7 +203,9 @@ void NvEncoder::CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncoderParams) } else { - NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } }; + NV_ENC_PRESET_CONFIG presetConfig = {}; + presetConfig.version = NV_ENC_PRESET_CONFIG_VER; + presetConfig.presetCfg.version = NV_ENC_CONFIG_VER; m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, pEncoderParams->encodeGUID, pEncoderParams->presetGUID, pEncoderParams->tuningInfo, &presetConfig); memcpy(&m_encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); } @@ -283,8 +288,8 @@ const NvEncInputFrame* NvEncoder::GetNextInputFrame() void NvEncoder::MapResources(uint32_t bfrIdx) { - NV_ENC_MAP_INPUT_RESOURCE mapInputResource = { NV_ENC_MAP_INPUT_RESOURCE_VER }; - + NV_ENC_MAP_INPUT_RESOURCE mapInputResource = {}; + mapInputResource.version = NV_ENC_MAP_INPUT_RESOURCE_VER; mapInputResource.registeredResource = m_vRegisteredResources[bfrIdx]; NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource; @@ -319,7 +324,8 @@ void NvEncoder::GetSequenceParams(std::vector& seqParams) { uint8_t spsppsData[1024]; // Assume maximum spspps data is 1KB or less memset(spsppsData, 0, sizeof(spsppsData)); - NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER }; + NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = {}; + payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER; uint32_t spsppsSize = 0; payload.spsppsBuffer = spsppsData; @@ -352,7 +358,9 @@ NVENCSTATUS NvEncoder::DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR void NvEncoder::SendEOS() { - NV_ENC_PIC_PARAMS picParams = { NV_ENC_PIC_PARAMS_VER }; + NV_ENC_PIC_PARAMS picParams = {}; + picParams.version = NV_ENC_PIC_PARAMS_VER; + picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS; picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams)); @@ -378,7 +386,8 @@ void NvEncoder::GetEncodedPacket(std::vector& vOutputBuffer, for (; m_iGot < iEnd; m_iGot++) { WaitForCompletionEvent(m_iGot % m_nEncoderBuffer); - NV_ENC_LOCK_BITSTREAM lockBitstreamData = { NV_ENC_LOCK_BITSTREAM_VER }; + NV_ENC_LOCK_BITSTREAM lockBitstreamData = {}; + lockBitstreamData.version = NV_ENC_LOCK_BITSTREAM_VER; lockBitstreamData.outputBitstream = vOutputBuffer[m_iGot % m_nEncoderBuffer]; lockBitstreamData.doNotWait = false; NVENC_API_CALL(m_nvenc.nvEncLockBitstream(m_hEncoder, &lockBitstreamData)); @@ -424,7 +433,8 @@ NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource(void* pBuffer, NV_ENC_INPUT_RE int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage, NV_ENC_FENCE_POINT_D3D12* pInputFencePoint, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint) { - NV_ENC_REGISTER_RESOURCE registerResource = { NV_ENC_REGISTER_RESOURCE_VER }; + NV_ENC_REGISTER_RESOURCE registerResource = {}; + registerResource.version = NV_ENC_REGISTER_RESOURCE_VER; registerResource.resourceType = eResourceType; registerResource.resourceToRegister = pBuffer; registerResource.width = width; @@ -569,7 +579,6 @@ uint32_t NvEncoder::GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, con return width * 4; default: NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); - return 0; } } @@ -646,7 +655,6 @@ void NvEncoder::GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat return; default: NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); - return; } } @@ -670,7 +678,6 @@ uint32_t NvEncoder::GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, con return 0; default: NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); - return 0; } } @@ -697,7 +704,6 @@ uint32_t NvEncoder::GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferForma return 0; default: NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); - return 0; } } @@ -708,7 +714,8 @@ int NvEncoder::GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery) { return 0; } - NV_ENC_CAPS_PARAM capsParam = { NV_ENC_CAPS_PARAM_VER }; + NV_ENC_CAPS_PARAM capsParam = {}; + capsParam.version = NV_ENC_CAPS_PARAM_VER; capsParam.capsToQuery = capsToQuery; int v; m_nvenc.nvEncGetEncodeCaps(m_hEncoder, guidCodec, &capsParam, &v); @@ -737,7 +744,6 @@ int NvEncoder::GetFrameSize() const return 4 * GetEncodeWidth() * GetEncodeHeight(); default: NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); - return 0; } } @@ -757,7 +763,8 @@ void NvEncoder::InitializeBitstreamBuffer() { for (int i = 0; i < m_nEncoderBuffer; i++) { - NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER }; + NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = {}; + createBitstreamBuffer.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER; NVENC_API_CALL(m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer)); m_vBitstreamOutputBuffer[i] = createBitstreamBuffer.bitstreamBuffer; } diff --git a/modules/cudacodec/src/NvEncoderCuda.cpp b/modules/cudacodec/src/NvEncoderCuda.cpp index 195ffde15b..9aae90c272 100644 --- a/modules/cudacodec/src/NvEncoderCuda.cpp +++ b/modules/cudacodec/src/NvEncoderCuda.cpp @@ -130,7 +130,7 @@ void NvEncoderCuda::CopyToDeviceFrame(CUcontext device, cuSafeCall(cuCtxPushCurrent(device)); uint32_t srcPitch = nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); - CUDA_MEMCPY2D m = { 0 }; + CUDA_MEMCPY2D m = {}; m.srcMemoryType = srcMemoryType; if (srcMemoryType == CU_MEMORYTYPE_HOST) { diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index ecf07445b1..9ac3b57ad8 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -59,6 +59,15 @@ Ptr createVideoWriter(const Ptr&, const #else // !defined HAVE_NVCUVENC +ENC_BUFFER_FORMAT NvSurfaceFormat(const COLOR_FORMAT_CV format); +int NChannels(const COLOR_FORMAT_CV format); +int NChannels(const ENC_BUFFER_FORMAT format); +GUID CodecGuid(const VideoWriterCodec codec); +void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen); +GUID EncodingProfileGuid(const ENC_PROFILE encodingProfile); +GUID EncodingPresetGuid(const ENC_PRESET nvPreset); +bool Equal(const GUID& g1, const GUID& g2); + EncoderParams::EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT), rateControlMode(ENC_PARAMS_RC_VBR), multiPassEncoding(ENC_MULTI_PASS_DISABLED), constQp({ 0,0,0 }), averageBitRate(0), maxBitRate(0), targetQuality(30), gopLength(0) @@ -119,7 +128,7 @@ class VideoWriterImpl : public VideoWriter void close(); private: void Init(const VideoWriterCodec codec, const double fps, const Size frameSz); - void InitializeEncoder(NvEncoderCuda* const pEnc, const GUID codec, const double fps); + void InitializeEncoder(const GUID codec, const double fps); void CopyToNvSurface(const InputArray src); Ptr encoderCallback; @@ -225,7 +234,7 @@ void VideoWriterImpl::Init(const VideoWriterCodec codec, const double fps, const const GUID codecGuid = CodecGuid(codec); try { pEnc = new NvEncoderCuda(cuContext, frameSz.width, frameSz.height, (NV_ENC_BUFFER_FORMAT)surfaceFormatNv); - InitializeEncoder(pEnc, codecGuid, fps); + InitializeEncoder(codecGuid, fps); const cudaStream_t cudaStream = cuda::StreamAccessor::getStream(stream); pEnc->SetIOCudaStreams((NV_ENC_CUSTREAM_PTR)&cudaStream, (NV_ENC_CUSTREAM_PTR)&cudaStream); } @@ -294,10 +303,12 @@ bool Equal(const GUID& g1, const GUID& g2) { return false; } -void VideoWriterImpl::InitializeEncoder(NvEncoderCuda* const pEnc, const GUID codec, const double fps) +void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps) { - NV_ENC_INITIALIZE_PARAMS initializeParams = { NV_ENC_INITIALIZE_PARAMS_VER }; - NV_ENC_CONFIG encodeConfig = { NV_ENC_CONFIG_VER }; + NV_ENC_INITIALIZE_PARAMS initializeParams = {}; + initializeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; + NV_ENC_CONFIG encodeConfig = {}; + encodeConfig.version = NV_ENC_CONFIG_VER; initializeParams.encodeConfig = &encodeConfig; pEnc->CreateDefaultEncoderParams(&initializeParams, codec, EncodingPresetGuid(encoderParams.nvPreset), (NV_ENC_TUNING_INFO)encoderParams.tuningInfo); FrameRate(fps, initializeParams.frameRateNum, initializeParams.frameRateDen); @@ -332,8 +343,6 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) else srcDevice.upload(src); } - Npp8u* pDst[3] = { dst, &dst[encoderInputFrame->pitch * pEnc->GetEncodeHeight()], &dst[encoderInputFrame->pitch * pEnc->GetEncodeHeight() * 2] }; - const NppiSize oSizeROI = { pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight() }; if (surfaceFormatCv == COLOR_FORMAT_CV::BGR) { GpuMat dstGpuMat(pEnc->GetEncodeHeight(), pEnc->GetEncodeWidth(), CV_8UC4, dst, encoderInputFrame->pitch); cuda::cvtColor(srcDevice, dstGpuMat, COLOR_BGR2BGRA, 0, stream); @@ -362,7 +371,7 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) else { void* srcPtr = src.isGpuMat() ? src.getGpuMat().data : src.getMat().data; const CUmemorytype cuMemoryType = src.isGpuMat() ? CU_MEMORYTYPE_DEVICE : CU_MEMORYTYPE_HOST; - NvEncoderCuda::CopyToDeviceFrame(cuContext, srcPtr, src.step(), (CUdeviceptr)encoderInputFrame->inputPtr, (int)encoderInputFrame->pitch, pEnc->GetEncodeWidth(), + NvEncoderCuda::CopyToDeviceFrame(cuContext, srcPtr, static_cast(src.step()), (CUdeviceptr)encoderInputFrame->inputPtr, (int)encoderInputFrame->pitch, pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight(), cuMemoryType, encoderInputFrame->bufferFormat, encoderInputFrame->chromaOffsets, encoderInputFrame->numChromaPlanes, false, cuda::StreamAccessor::getStream(stream)); } diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index 6271243f59..39a243d5e7 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -143,7 +143,7 @@ CUDA_TEST_P(CheckExtraData, Reader) ASSERT_EQ(extraDataIdx, 1 ); ASSERT_TRUE(reader->grab()); cv::Mat extraData; - const bool newData = reader->retrieve(extraData, extraDataIdx); + const bool newData = reader->retrieve(extraData, static_cast(extraDataIdx)); ASSERT_TRUE((newData && sz) || (!newData && !sz)); ASSERT_EQ(extraData.total(), sz); } @@ -170,7 +170,7 @@ CUDA_TEST_P(CheckKeyFrame, Reader) ASSERT_TRUE(reader->grab()); double N = -1; ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB,N)); - for (int i = rawIdxBase; i < N + rawIdxBase; i++) { + for (int i = static_cast(rawIdxBase); i < static_cast(N + rawIdxBase); i++) { nPackages++; double containsKeyFrame = i; ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_LRF_HAS_KEY_FRAME, containsKeyFrame)); @@ -197,10 +197,11 @@ CUDA_TEST_P(Scaling, Reader) } cudacodec::VideoReaderInitParams params; - params.targetSz = Size(frameOr.cols * targetSzIn.width, frameOr.rows * targetSzIn.height); - params.srcRoi = Rect(frameOr.cols * srcRoiIn.x, frameOr.rows * srcRoiIn.y, frameOr.cols * srcRoiIn.width, frameOr.rows * srcRoiIn.height); - params.targetRoi = Rect(params.targetSz.width * targetRoiIn.x, params.targetSz.height * targetRoiIn.y, params.targetSz.width * targetRoiIn.width, - params.targetSz.height * targetRoiIn.height); + params.targetSz = Size(static_cast(frameOr.cols * targetSzIn.width), static_cast(frameOr.rows * targetSzIn.height)); + params.srcRoi = Rect(static_cast(frameOr.cols * srcRoiIn.x), static_cast(frameOr.rows * srcRoiIn.y), static_cast(frameOr.cols * srcRoiIn.width), + static_cast(frameOr.rows * srcRoiIn.height)); + params.targetRoi = Rect(static_cast(params.targetSz.width * targetRoiIn.x), static_cast(params.targetSz.height * targetRoiIn.y), + static_cast(params.targetSz.width * targetRoiIn.width), static_cast(params.targetSz.height * targetRoiIn.height)); cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, params); reader->set(cudacodec::ColorFormat::GRAY); GpuMat frame; @@ -256,7 +257,7 @@ CUDA_TEST_P(Video, Reader) ASSERT_TRUE(reader->nextFrame(frame)); if(!fmt.valid) fmt = reader->format(); - const int height = formatToChannels.first == cudacodec::ColorFormat::YUV ? 1.5 * fmt.height : fmt.height; + const int height = formatToChannels.first == cudacodec::ColorFormat::YUV ? static_cast(1.5 * fmt.height) : fmt.height; ASSERT_TRUE(frame.cols == fmt.width && frame.rows == height); ASSERT_FALSE(frame.empty()); ASSERT_TRUE(frame.channels() == formatToChannels.second); @@ -291,9 +292,9 @@ CUDA_TEST_P(VideoReadRaw, Reader) double N = -1; ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB,N)); ASSERT_TRUE(N >= 0) << N << " < 0"; - for (int i = rawIdxBase; i <= N + rawIdxBase; i++) { + for (int j = static_cast(rawIdxBase); j <= static_cast(N + rawIdxBase); j++) { Mat rawPackets; - reader->retrieve(rawPackets, i); + reader->retrieve(rawPackets, j); file.write((char*)rawPackets.data, rawPackets.total()); } } @@ -315,7 +316,7 @@ CUDA_TEST_P(VideoReadRaw, Reader) { ASSERT_TRUE(readerReference->nextFrame(reference)); ASSERT_TRUE(readerActual->grab()); - ASSERT_TRUE(readerActual->retrieve(actual, decodedFrameIdx)); + ASSERT_TRUE(readerActual->retrieve(actual, static_cast(decodedFrameIdx))); actual.download(actualHost); reference.download(referenceHost); ASSERT_TRUE(cvtest::norm(actualHost, referenceHost, NORM_INF) == 0); @@ -471,8 +472,8 @@ CUDA_TEST_P(TransCode, H264ToH265) { cv::VideoCapture cap(outputFile); ASSERT_TRUE(cap.isOpened()); - const int width = cap.get(CAP_PROP_FRAME_WIDTH); - const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); + const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); ASSERT_EQ(frameSz, Size(width, height)); ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); Mat frame; @@ -550,8 +551,8 @@ CUDA_TEST_P(WriteNv, Writer) { cv::VideoCapture cap(outputFile); ASSERT_TRUE(cap.isOpened()); - const int width = cap.get(CAP_PROP_FRAME_WIDTH); - const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); + const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); ASSERT_EQ(frameSz, Size(width, height)); ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); Mat frame; @@ -624,8 +625,8 @@ CUDA_TEST_P(WriteCv, Writer) { cv::VideoCapture cap(outputFile); ASSERT_TRUE(cap.isOpened()); - const int width = cap.get(CAP_PROP_FRAME_WIDTH); - const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); + const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); ASSERT_EQ(frameSz, Size(width, height)); ASSERT_TRUE(abs(fps - cap.get(CAP_PROP_FPS)) < 0.5); Mat frame; @@ -659,7 +660,7 @@ struct EncoderParamsBase : testing::TestWithParam params.encodingProfile = cv::cudacodec::ENC_PROFILE::ENC_H264_PROFILE_MAIN; params.rateControlMode = cv::cudacodec::ENC_PARAMS_RC_MODE::ENC_PARAMS_RC_CBR; params.multiPassEncoding = cv::cudacodec::ENC_MULTI_PASS::ENC_TWO_PASS_FULL_RESOLUTION; - params.averageBitRate = 1e6; + params.averageBitRate = 1000000; params.maxBitRate = 0; params.targetQuality = 0; params.gopLength = 5; @@ -704,8 +705,8 @@ CUDA_TEST_P(EncoderParamsCv, Writer) { cv::VideoCapture cap(outputFile); ASSERT_TRUE(cap.isOpened()); - const int width = cap.get(CAP_PROP_FRAME_WIDTH); - const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); + const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); ASSERT_EQ(frameSz, Size(width, height)); ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG); @@ -713,8 +714,9 @@ CUDA_TEST_P(EncoderParamsCv, Writer) for (int i = 0; i < nFrames; ++i) { cap >> frame; ASSERT_FALSE(frame.empty()); - if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) + if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) { ASSERT_TRUE(i % params.gopLength == 0); + } } } } @@ -760,8 +762,8 @@ CUDA_TEST_P(EncoderParamsNv, Writer) { cv::VideoCapture cap(outputFile); ASSERT_TRUE(cap.isOpened()); - const int width = cap.get(CAP_PROP_FRAME_WIDTH); - const int height = cap.get(CAP_PROP_FRAME_HEIGHT); + const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); + const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); ASSERT_EQ(frameSz, Size(width, height)); ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG); @@ -769,8 +771,9 @@ CUDA_TEST_P(EncoderParamsNv, Writer) for (int i = 0; i < nFrames; ++i) { cap >> frame; ASSERT_FALSE(frame.empty()); - if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) + if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) { ASSERT_TRUE(i % params.gopLength == 0); + } } } } @@ -784,9 +787,9 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckSet, testing::Combine( testing::Values("highgui/video/big_buck_bunny.mp4"))); #define VIDEO_SRC_SCALING "highgui/video/big_buck_bunny.mp4" -#define TARGET_SZ Size2f(1,1), Size2f(0.8,0.9), Size2f(2.3,1.8) -#define SRC_ROI Rect2f(0,0,1,1), Rect2f(0.25,0.25,0.5,0.5) -#define TARGET_ROI Rect2f(0,0,1,1), Rect2f(0.2,0.3,0.6,0.7) +#define TARGET_SZ Size2f(1,1), Size2f(0.8f,0.9f), Size2f(2.3f,1.8f) +#define SRC_ROI Rect2f(0,0,1,1), Rect2f(0.25f,0.25f,0.5f,0.5f) +#define TARGET_ROI Rect2f(0,0,1,1), Rect2f(0.2f,0.3f,0.6f,0.7f) INSTANTIATE_TEST_CASE_P(CUDA_Codec, Scaling, testing::Combine( ALL_DEVICES, testing::Values(VIDEO_SRC_SCALING), testing::Values(TARGET_SZ), testing::Values(SRC_ROI), testing::Values(TARGET_ROI))); From be91fa885fbb216147b5de5a5602dba654347429 Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Wed, 12 Oct 2022 14:53:42 +0300 Subject: [PATCH 34/99] Workflow Ubuntu 20.04 x64 with CUDA support (4.x) --- .github/workflows/PR-4.x.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index dedc3767f9..9d2a733c55 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -12,6 +12,10 @@ jobs: Ubuntu2004-x64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-U20.yaml@main + Ubuntu2004-x64-CUDA: + if: "${{ contains(github.event.pull_request.labels.*.name, 'category: cuda') }}" + uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-U20-Cuda.yaml@main + Windows10-x64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-W10.yaml@main From 4fa9c71169c5ecd04a4a469332059ad682b66a44 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Fri, 14 Oct 2022 18:02:46 +0300 Subject: [PATCH 35/99] Four factory method version. --- .../cudacodec/include/opencv2/cudacodec.hpp | 103 ++-------- .../misc/python/test/test_cudacodec.py | 5 +- modules/cudacodec/perf/perf_video.cpp | 30 +-- modules/cudacodec/src/video_writer.cpp | 189 +++++++----------- modules/cudacodec/test/test_video.cpp | 185 +++-------------- 5 files changed, 141 insertions(+), 371 deletions(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 7a17e02922..46e699a47f 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -68,40 +68,28 @@ using namespace cuda; // Stream /** @brief Codecs supported by Video writer, refer to Nvidia's GPU Support Matrix to confirm your GPU supports them. */ -enum class VideoWriterCodec +enum class CODEC_VW { H264 = 0, HEVC = 1, NumCodecs = 2 }; -/** @brief OpenCV color formats. +/** @brief Video writer color formats. */ -enum COLOR_FORMAT_CV { +enum COLOR_FORMAT_VW { UNDEFINED = 0, BGR = 1, //!< Default OpenCV color format. RGB = 2, BGRA = 3, RGBA = 4, - GRAY = 5 -}; + GRAY = 5, -/** @brief Nvidia Video Codec SDK surface formats. -*/ -enum ENC_BUFFER_FORMAT -{ - BF_UNDEFINED = 0x00000000, //!< Undefined buffer format. - BF_NV12 = 0x00000001, //!< Semi-Planar YUV [Y plane followed by interleaved UV plane]. - BF_YV12 = 0x00000010, //!< Planar YUV [Y plane followed by V and U planes]. - BF_IYUV = 0x00000100, //!< Planar YUV [Y plane followed by U and V planes]. - BF_YUV444 = 0x00001000, //!< Planar YUV [Y plane followed by U and V planes]. - BF_YUV420_10BIT = 0x00010000, //!< 10 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. - BF_YUV444_10BIT = 0x00100000, //!< 10 bit Planar YUV444 [Y plane followed by U and V planes]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. - BF_ARGB = 0x01000000, //!< 8 bit Packed A8R8G8B8. This is a word-ordered format where a pixel is represented by a 32-bit word with B in the lowest 8 bits, G in the next 8 bits, R in the 8 bits after that and A in the highest 8 bits. - BF_ARGB10 = 0x02000000, //!< 10 bit Packed A2R10G10B10. This is a word-ordered format where a pixel is represented by a 32-bit word with B in the lowest 10 bits, G in the next 10 bits, R in the 10 bits after that and A in the highest 2 bits. - BF_AYUV = 0x04000000, //!< 8 bit Packed A8Y8U8V8. This is a word-ordered format where a pixel is represented by a 32-bit word with V in the lowest 8 bits, U in the next 8 bits, Y in the 8 bits after that and A in the highest 8 bits. - BF_ABGR = 0x10000000, //!< 8 bit Packed A8B8G8R8. This is a word-ordered format where a pixel is represented by a 32-bit word with R in the lowest 8 bits, G in the next 8 bits, B in the 8 bits after that and A in the highest 8 bits. - BF_ABGR10 = 0x20000000, //!< 10 bit Packed A2B10G10R10. This is a word-ordered format where a pixel is represented by a 32-bit word with R in the lowest 10 bits, G in the next 10 bits, B in the 10 bits after that and A in the highest 2 bits. + NV_NV12 = 6, //!< Nvidia Buffer Format - Semi-Planar YUV [Y plane followed by interleaved UV plane]. + NV_YV12 = 7, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by V and U planes]. + NV_IYUV = 8, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes]. + NV_YUV444 = 9, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes]. + NV_AYUV = 10 //!< Nvidia Buffer Format - 8 bit Packed A8Y8U8V8. This is a word-ordered format where a pixel is represented by a 32-bit word with V in the lowest 8 bits, U in the next 8 bits, Y in the 8 bits after that and A in the highest 8 bits. }; /** @brief Rate Control Modes. @@ -233,7 +221,7 @@ class CV_EXPORTS_W VideoWriter /** @brief Waits until the encoding process has finished before calling EncoderCallback::onEncodingFinished(). */ - CV_WRAP virtual void close() = 0; + CV_WRAP virtual void release() = 0; }; /** @brief Creates video writer. @@ -245,20 +233,8 @@ class CV_EXPORTS_W VideoWriter @param colorFormat OpenCv color format of the frames to be encoded. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, - const double fps = 25.0, const COLOR_FORMAT_CV colorFormat = BGR, const Stream& stream = Stream::Null()); - -/** @brief Creates video writer. - -@param fileName Name of the output video file. Only raw h264 or hevc files are supported. -@param frameSize Size of the input video frames. -@param codec Codec. -@param fps Framerate of the created video stream. -@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. -@param stream Stream for frame pre-processing. -*/ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, - const double fps, const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec = CODEC_VW::H264, + const double fps = 25.0, const COLOR_FORMAT_VW colorFormat = BGR, const Stream& stream = Stream::Null()); /** @brief Creates video writer. @@ -270,22 +246,8 @@ CV_EXPORTS_W Ptr createVideoWriter(const String& fileNam @param params Additional encoding parameters. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, - const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); - - -/** @brief Creates video writer. - -@param fileName Name of the output video file. Only raw h264 or hevc files are supported. -@param frameSize Size of the input video frames. -@param codec Codec. -@param fps Framerate of the created video stream. -@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. -@param params Additional encoding parameters. -@param stream Stream for frame pre-processing. -*/ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, - const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, + const double fps, const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); /** @brief Creates video writer. @@ -298,22 +260,8 @@ CV_EXPORTS_W Ptr createVideoWriter(const String& fileNam The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallback. */ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec = VideoWriterCodec::H264, - const double fps = 25.0, const COLOR_FORMAT_CV colorFormat = BGR, const Stream& stream = Stream::Null()); - -/** @brief Creates video writer. - -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. -@param frameSize Size of the input video frames. -@param codec Codec. -@param fps Framerate of the created video stream. -@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. -@param stream Stream for frame pre-processing. - -The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallback. -*/ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, - const double fps, const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec = CODEC_VW::H264, + const double fps = 25.0, const COLOR_FORMAT_VW colorFormat = BGR, const Stream& stream = Stream::Null()); /** @brief Creates video writer. @@ -327,23 +275,8 @@ CV_EXPORTS_W Ptr createVideoWriter(const Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, - const double fps, const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); - -/** @brief Creates video writer. - -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. -@param frameSize Size of the input video frames. -@param codec Codec. -@param fps Framerate of the created video stream. -@param bufferFormat Nvidia Video Codec SDK buffer format of the frames to be encoded. -@param params Additional encoding parameters. -@param stream Stream for frame pre-processing. - -The constructors initialize video writer. User can implement own their multiplexing with cudacodec::EncoderCallback. -*/ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, - const double fps, const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec, + const double fps, const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); ////////////////////////////////// Video Decoding ////////////////////////////////////////// diff --git a/modules/cudacodec/misc/python/test/test_cudacodec.py b/modules/cudacodec/misc/python/test/test_cudacodec.py index 21d3d33a57..cef78a895e 100644 --- a/modules/cudacodec/misc/python/test/test_cudacodec.py +++ b/modules/cudacodec/misc/python/test/test_cudacodec.py @@ -79,7 +79,8 @@ def test_writer_existence(self): #Test at least the existence of wrapped functions for now try: - _, fname = tempfile.mkstemp(suffix=".h264") + fd, fname = tempfile.mkstemp(suffix=".h264") + os.close(fd) encoder_params_in = cv.cudacodec.EncoderParams() encoder_params_in.gopLength = 10 stream = cv.cuda.Stream() @@ -99,5 +100,7 @@ def test_writer_existence(self): self.assertEqual(e.code, cv.Error.StsNotImplemented) self.skipTest("Either NVCUVENC or a GPU hardware encoder is missing or the encoding profile is not supported.") + os.remove(fname) + if __name__ == '__main__': NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudacodec/perf/perf_video.cpp b/modules/cudacodec/perf/perf_video.cpp index 992760630b..f893706731 100644 --- a/modules/cudacodec/perf/perf_video.cpp +++ b/modules/cudacodec/perf/perf_video.cpp @@ -97,41 +97,41 @@ PERF_TEST_P(FileName, VideoReader, VIDEO_SRC) #if defined(HAVE_NVCUVENC) -DEF_PARAM_TEST(WriteToFile, string, cv::cudacodec::COLOR_FORMAT_CV, cv::cudacodec::VideoWriterCodec); +DEF_PARAM_TEST(WriteToFile, string, cv::cudacodec::COLOR_FORMAT_VW, cv::cudacodec::CODEC_VW); -#define COLOR_FORMAT Values(cv::cudacodec::COLOR_FORMAT_CV::BGR, cv::cudacodec::COLOR_FORMAT_CV::RGB, cv::cudacodec::COLOR_FORMAT_CV::BGRA, \ -cv::cudacodec::COLOR_FORMAT_CV::RGBA, cv::cudacodec::COLOR_FORMAT_CV::GRAY) -#define CODEC Values(cv::cudacodec::VideoWriterCodec::H264, cv::cudacodec::VideoWriterCodec::HEVC) +#define COLOR_FORMAT Values(cv::cudacodec::COLOR_FORMAT_VW::BGR, cv::cudacodec::COLOR_FORMAT_VW::RGB, cv::cudacodec::COLOR_FORMAT_VW::BGRA, \ +cv::cudacodec::COLOR_FORMAT_VW::RGBA, cv::cudacodec::COLOR_FORMAT_VW::GRAY) +#define CODEC Values(cv::cudacodec::CODEC_VW::H264, cv::cudacodec::CODEC_VW::HEVC) PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) { declare.time(30); const string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); - const cv::cudacodec::COLOR_FORMAT_CV surfaceFormat = GET_PARAM(1); - const cudacodec::VideoWriterCodec codec = GET_PARAM(2); + const cv::cudacodec::COLOR_FORMAT_VW surfaceFormat = GET_PARAM(1); + const cudacodec::CODEC_VW codec = GET_PARAM(2); const double fps = 25; const int nFrames = 20; cv::VideoCapture reader(inputFile); ASSERT_TRUE(reader.isOpened()); Mat frameBgr; if (PERF_RUN_CUDA()) { - const std::string ext = codec == cudacodec::VideoWriterCodec::H264 ? ".h264" : ".hevc"; + const std::string ext = codec == cudacodec::CODEC_VW::H264 ? ".h264" : ".hevc"; const string outputFile = cv::tempfile(ext.c_str()); std::vector frames; cv::Mat frameNewSf; cuda::Stream stream; ColorConversionCodes conversionCode = COLOR_COLORCVT_MAX; switch (surfaceFormat) { - case cudacodec::COLOR_FORMAT_CV::RGB: + case cudacodec::COLOR_FORMAT_VW::RGB: conversionCode = COLOR_BGR2RGB; break; - case cudacodec::COLOR_FORMAT_CV::BGRA: + case cudacodec::COLOR_FORMAT_VW::BGRA: conversionCode = COLOR_BGR2BGRA; break; - case cudacodec::COLOR_FORMAT_CV::RGBA: + case cudacodec::COLOR_FORMAT_VW::RGBA: conversionCode = COLOR_BGR2RGBA; break; - case cudacodec::COLOR_FORMAT_CV::GRAY: + case cudacodec::COLOR_FORMAT_VW::GRAY: conversionCode = COLOR_BGR2GRAY; default: break; @@ -155,11 +155,13 @@ PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) } startTimer(); d_writer->write(frames[nFrames - 1]); - d_writer->close(); + d_writer->release(); stopTimer(); + + ASSERT_EQ(0, remove(outputFile.c_str())); } else { - if (surfaceFormat != cv::cudacodec::COLOR_FORMAT_CV::BGR || codec != cv::cudacodec::VideoWriterCodec::H264) + if (surfaceFormat != cv::cudacodec::COLOR_FORMAT_VW::BGR || codec != cv::cudacodec::CODEC_VW::H264) throw PerfSkipTestException(); cv::VideoWriter writer; const string outputFile = cv::tempfile(".avi"); @@ -178,6 +180,8 @@ PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) writer.write(frameBgr); writer.release(); stopTimer(); + + ASSERT_EQ(0, remove(outputFile.c_str())); } SANITY_CHECK(frameBgr); } diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index 9ac3b57ad8..3076881c10 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -48,21 +48,16 @@ using namespace cv::cuda; #if !defined(HAVE_NVCUVENC) -Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const String&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const COLOR_FORMAT_CV, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec codec, const double, const ENC_BUFFER_FORMAT, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const COLOR_FORMAT_CV, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const VideoWriterCodec, const double, const ENC_BUFFER_FORMAT, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const CODEC_VW codec, const double, const COLOR_FORMAT_VW, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const Ptr&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } #else // !defined HAVE_NVCUVENC -ENC_BUFFER_FORMAT NvSurfaceFormat(const COLOR_FORMAT_CV format); -int NChannels(const COLOR_FORMAT_CV format); -int NChannels(const ENC_BUFFER_FORMAT format); -GUID CodecGuid(const VideoWriterCodec codec); +NV_ENC_BUFFER_FORMAT EncBufferFormat(const COLOR_FORMAT_VW colorFormat); +int NChannels(const COLOR_FORMAT_VW colorFormat); +GUID CodecGuid(const CODEC_VW codec); void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen); GUID EncodingProfileGuid(const ENC_PROFILE encodingProfile); GUID EncodingPresetGuid(const ENC_PRESET nvPreset); @@ -114,118 +109,105 @@ void RawVideoWriter::onEncoded(std::vector> vPacket) { class VideoWriterImpl : public VideoWriter { public: - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV surfaceFormatCv, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT surfaceFormatNv, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV surfaceFormatCv, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT surfaceFormatNv, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); ~VideoWriterImpl(); void write(InputArray frame); EncoderParams getEncoderParams() const; - void close(); + void release(); private: - void Init(const VideoWriterCodec codec, const double fps, const Size frameSz); + void Init(const CODEC_VW codec, const double fps, const Size frameSz); void InitializeEncoder(const GUID codec, const double fps); void CopyToNvSurface(const InputArray src); Ptr encoderCallback; - COLOR_FORMAT_CV surfaceFormatCv = COLOR_FORMAT_CV::UNDEFINED; - ENC_BUFFER_FORMAT surfaceFormatNv = ENC_BUFFER_FORMAT::BF_UNDEFINED; + COLOR_FORMAT_VW colorFormat = COLOR_FORMAT_VW::UNDEFINED; + NV_ENC_BUFFER_FORMAT surfaceFormat = NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_UNDEFINED; EncoderParams encoderParams; Stream stream = Stream::Null(); Ptr pEnc; std::vector> vPacket; - int nSrcChannels = -1; + int nSrcChannels = 0; CUcontext cuContext; }; -ENC_BUFFER_FORMAT NvSurfaceFormat(const COLOR_FORMAT_CV format) { - switch (format) { - case BGR: return BF_ARGB; - case RGB: return BF_ABGR; - case BGRA: return BF_ARGB; - case RGBA: return BF_ABGR; - case GRAY: return BF_NV12; - default: return BF_UNDEFINED; +NV_ENC_BUFFER_FORMAT EncBufferFormat(const COLOR_FORMAT_VW colorFormat) { + switch (colorFormat) { + case BGR: return NV_ENC_BUFFER_FORMAT_ARGB; + case RGB: return NV_ENC_BUFFER_FORMAT_ABGR; + case BGRA: return NV_ENC_BUFFER_FORMAT_ARGB; + case RGBA: return NV_ENC_BUFFER_FORMAT_ABGR; + case GRAY: + case NV_NV12: return NV_ENC_BUFFER_FORMAT_NV12; + case NV_YV12: return NV_ENC_BUFFER_FORMAT_YV12; + case NV_IYUV: return NV_ENC_BUFFER_FORMAT_IYUV; + case NV_YUV444: return NV_ENC_BUFFER_FORMAT_YUV444; + case NV_AYUV: return NV_ENC_BUFFER_FORMAT_AYUV; + default: return NV_ENC_BUFFER_FORMAT_UNDEFINED; } } -int NChannels(const COLOR_FORMAT_CV format) { - switch (format) { +int NChannels(const COLOR_FORMAT_VW colorFormat) { + switch (colorFormat) { case BGR: - case RGB: return 3; + case RGB: + case NV_IYUV: + case NV_YUV444: return 3; case RGBA: - case BGRA: return 4; - case GRAY: return 1; + case BGRA: + case NV_AYUV: return 4; + case GRAY: + case NV_NV12: + case NV_YV12: return 1; default: return 0; } } -int NChannels(const ENC_BUFFER_FORMAT format) { - if (format == ENC_BUFFER_FORMAT::BF_ARGB || format == ENC_BUFFER_FORMAT::BF_ABGR) return 4; - else return 1; -} - -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV surfaceFormatCv_, const EncoderParams& encoderParams_, const Stream& stream_) : - encoderCallback(encoderCallBack_), surfaceFormatCv(surfaceFormatCv_), encoderParams(encoderParams_), stream(stream_) +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat_, const EncoderParams& encoderParams_, const Stream& stream_) : + encoderCallback(encoderCallBack_), colorFormat(colorFormat_), encoderParams(encoderParams_), stream(stream_) { - surfaceFormatNv = NvSurfaceFormat(surfaceFormatCv); - if (surfaceFormatNv == BF_UNDEFINED) { - String msg = cv::format("Unsupported input surface format: %i", surfaceFormatCv); + CV_Assert(colorFormat != UNDEFINED); + surfaceFormat = EncBufferFormat(colorFormat); + if (surfaceFormat == NV_ENC_BUFFER_FORMAT_UNDEFINED) { + String msg = cv::format("Unsupported input surface format: %i", colorFormat); CV_LOG_WARNING(NULL, msg); CV_Error(Error::StsUnsupportedFormat, msg); } - nSrcChannels = NChannels(surfaceFormatCv); + nSrcChannels = NChannels(colorFormat); Init(codec, fps, frameSz); } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT surfaceFormatNv_, const EncoderParams& encoderParams_, const Stream& stream_) : - encoderCallback(encoderCallBack_), surfaceFormatNv(surfaceFormatNv_), encoderParams(encoderParams_), stream(stream_) +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const Stream& stream) : + VideoWriterImpl(encoderCallback, frameSz, codec, fps, colorFormat, EncoderParams(), stream) { - CV_Assert(surfaceFormatNv != BF_UNDEFINED); - nSrcChannels = NChannels(surfaceFormatNv); - Init(codec, fps, frameSz); } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV surfaceFormatCv, const Stream& stream) : - VideoWriterImpl(encoderCallback, frameSz, codec, fps, surfaceFormatCv, EncoderParams(), stream) -{ -} - -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT surfaceFormatNv, const Stream& stream) : - VideoWriterImpl(encoderCallback, frameSz, codec, fps, surfaceFormatNv, EncoderParams(), stream) -{ -} - -void VideoWriterImpl::close() { +void VideoWriterImpl::release() { pEnc->EndEncode(vPacket); encoderCallback->onEncoded(vPacket); encoderCallback->onEncodingFinished(); } VideoWriterImpl::~VideoWriterImpl() { - close(); + release(); } -GUID CodecGuid(const VideoWriterCodec codec) { +GUID CodecGuid(const CODEC_VW codec) { switch (codec) { - case VideoWriterCodec::H264: return NV_ENC_CODEC_H264_GUID; - case VideoWriterCodec::HEVC: return NV_ENC_CODEC_HEVC_GUID; + case CODEC_VW::H264: return NV_ENC_CODEC_H264_GUID; + case CODEC_VW::HEVC: return NV_ENC_CODEC_HEVC_GUID; default: break; } - std::string msg = "Unknown codec: cudacodec::VideoWriter only supports VideoWriterCodec::H264 and VideoWriterCodec::HEVC"; + std::string msg = "Unknown codec: cudacodec::VideoWriter only supports CODEC_VW::H264 and CODEC_VW::HEVC"; CV_LOG_WARNING(NULL, msg); CV_Error(Error::StsUnsupportedFormat, msg); } -void VideoWriterImpl::Init(const VideoWriterCodec codec, const double fps, const Size frameSz) { +void VideoWriterImpl::Init(const CODEC_VW codec, const double fps, const Size frameSz) { // init context GpuMat temp(1, 1, CV_8UC1); temp.release(); @@ -233,7 +215,7 @@ void VideoWriterImpl::Init(const VideoWriterCodec codec, const double fps, const CV_Assert(nSrcChannels != 0); const GUID codecGuid = CodecGuid(codec); try { - pEnc = new NvEncoderCuda(cuContext, frameSz.width, frameSz.height, (NV_ENC_BUFFER_FORMAT)surfaceFormatNv); + pEnc = new NvEncoderCuda(cuContext, frameSz.width, frameSz.height, surfaceFormat); InitializeEncoder(codecGuid, fps); const cudaStream_t cudaStream = cuda::StreamAccessor::getStream(stream); pEnc->SetIOCudaStreams((NV_ENC_CUSTREAM_PTR)&cudaStream, (NV_ENC_CUSTREAM_PTR)&cudaStream); @@ -326,14 +308,20 @@ void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps) pEnc->CreateEncoder(&initializeParams); } +inline bool CvFormat(const COLOR_FORMAT_VW cf) { + if (cf == BGR || cf == RGB || cf == BGRA || cf == RGBA || cf == GRAY) + return true; + return false; +} + void VideoWriterImpl::CopyToNvSurface(const InputArray src) { const NvEncInputFrame* encoderInputFrame = pEnc->GetNextInputFrame(); CV_Assert(src.isGpuMat() || src.isMat()); - if (surfaceFormatCv != COLOR_FORMAT_CV::UNDEFINED) + if (CvFormat(colorFormat)) CV_Assert(src.size() == Size(pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight())); Npp8u* dst = (Npp8u*)encoderInputFrame->inputPtr; - if (surfaceFormatCv == COLOR_FORMAT_CV::BGR || surfaceFormatCv == COLOR_FORMAT_CV::RGB) { + if (colorFormat == COLOR_FORMAT_VW::BGR || colorFormat == COLOR_FORMAT_VW::RGB) { GpuMat srcDevice; if (src.isGpuMat()) srcDevice = src.getGpuMat(); @@ -343,7 +331,7 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) else srcDevice.upload(src); } - if (surfaceFormatCv == COLOR_FORMAT_CV::BGR) { + if (colorFormat == COLOR_FORMAT_VW::BGR) { GpuMat dstGpuMat(pEnc->GetEncodeHeight(), pEnc->GetEncodeWidth(), CV_8UC4, dst, encoderInputFrame->pitch); cuda::cvtColor(srcDevice, dstGpuMat, COLOR_BGR2BGRA, 0, stream); } @@ -352,7 +340,7 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) cuda::cvtColor(srcDevice, dstGpuMat, COLOR_RGB2RGBA, 0, stream); } } - else if (surfaceFormatCv == COLOR_FORMAT_CV::GRAY) { + else if (colorFormat == COLOR_FORMAT_VW::GRAY) { const cudaMemcpyKind memcpyKind = src.isGpuMat() ? cudaMemcpyDeviceToDevice : cudaMemcpyHostToDevice; const void* srcPtr = src.isGpuMat() ? src.getGpuMat().data : src.getMat().data; const size_t srcPitch = src.isGpuMat() ? src.getGpuMat().step : src.getMat().step; @@ -388,57 +376,32 @@ EncoderParams VideoWriterImpl::getEncoderParams() const { return encoderParams; }; -Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV colorFormat, const Stream& stream) +Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const Stream& stream) { Ptr rawVideoWriter = new RawVideoWriter(fileName); return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, stream); } -Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream) -{ - Ptr rawVideoWriter = new RawVideoWriter(fileName); - return createVideoWriter(rawVideoWriter, frameSize, codec, fps, bufferFormat, stream); -} - -Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream) +Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream) { Ptr rawVideoWriter = new RawVideoWriter(fileName); return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, params, stream); } -Ptr createVideoWriter(const String& fileName, const Size frameSize, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream) -{ - Ptr rawVideoWriter = new RawVideoWriter(fileName); - return createVideoWriter(rawVideoWriter, frameSize, codec, fps, bufferFormat, params, stream); -} - -Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV colorFormat, const Stream& stream) +Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const Stream& stream) { return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, stream); } -Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT bufferFormat, const Stream& stream) -{ - return makePtr(encoderCallback, frameSize, codec, fps, bufferFormat, stream); -} - -Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, - const COLOR_FORMAT_CV colorFormat, const EncoderParams& params, const Stream& stream) +Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec, const double fps, + const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream) { return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, params, stream); } -Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const VideoWriterCodec codec, const double fps, - const ENC_BUFFER_FORMAT bufferFormat, const EncoderParams& params, const Stream& stream) -{ - return makePtr(encoderCallback, frameSize, codec, fps, bufferFormat, params, stream); -} #endif // !defined HAVE_NVCUVENC }} diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index 39a243d5e7..1d61e9fc78 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -439,9 +439,9 @@ struct TransCode : testing::TestWithParam CUDA_TEST_P(TransCode, H264ToH265) { const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.h264"; - constexpr cv::cudacodec::ENC_BUFFER_FORMAT surfaceFormatNv = cv::cudacodec::ENC_BUFFER_FORMAT::BF_NV12; + constexpr cv::cudacodec::COLOR_FORMAT_VW colorFormat = cv::cudacodec::COLOR_FORMAT_VW::NV_NV12; constexpr double fps = 25; - const cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::HEVC; + const cudacodec::CODEC_VW codec = cudacodec::CODEC_VW::HEVC; const std::string ext = ".h265"; const std::string outputFile = cv::tempfile(ext.c_str()); constexpr int nFrames = 5; @@ -463,7 +463,7 @@ CUDA_TEST_P(TransCode, H264ToH265) Mat tst; frame.download(tst); if (writer.empty()) { frameSz = Size(fmt.width, fmt.height); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, surfaceFormatNv, stream); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, stream); } writer->write(frame); } @@ -494,107 +494,34 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, TransCode, ALL_DEVICES); //========================================================================== -bool CvtColor(const Mat& in, Mat& out, const cudacodec::ENC_BUFFER_FORMAT surfaceFormatNv) { - switch (surfaceFormatNv) { - case(cudacodec::ENC_BUFFER_FORMAT::BF_ARGB): - cv::cvtColor(in, out, COLOR_BGR2BGRA); - return true; - case(cudacodec::ENC_BUFFER_FORMAT::BF_ABGR): - cv::cvtColor(in, out, COLOR_BGR2RGBA); - return true; - default: - return false; - } -} - -PARAM_TEST_CASE(WriteNv, cuda::DeviceInfo, bool, cudacodec::ENC_BUFFER_FORMAT) -{ -}; - -CUDA_TEST_P(WriteNv, Writer) -{ - cv::cuda::setDevice(GET_PARAM(0).deviceID()); - const bool deviceSrc = GET_PARAM(1); - const cv::cudacodec::ENC_BUFFER_FORMAT surfaceFormatNv = GET_PARAM(2); - const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; - constexpr double fps = 25; - const cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::H264; - const std::string ext = ".h264"; - const std::string outputFile = cv::tempfile(ext.c_str()); - constexpr int nFrames = 5; - Size frameSz; - { - cv::VideoCapture cap(inputFile); - ASSERT_TRUE(cap.isOpened()); - cv::Ptr writer; - cv::Mat frame, frameNewSf; - cv::cuda::GpuMat dFrame; - cv::cuda::Stream stream; - for (int i = 0; i < nFrames; ++i) { - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (writer.empty()) { - frameSz = frame.size(); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, surfaceFormatNv, stream); - } - CvtColor(frame, frameNewSf, surfaceFormatNv); - if (deviceSrc) { - dFrame.upload(frameNewSf); - writer->write(dFrame); - } - else - writer->write(frameNewSf); - } - } - - { - cv::VideoCapture cap(outputFile); - ASSERT_TRUE(cap.isOpened()); - const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); - const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); - ASSERT_EQ(frameSz, Size(width, height)); - ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); - Mat frame; - for (int i = 0; i < nFrames; ++i) { - cap >> frame; - ASSERT_FALSE(frame.empty()); - } - } -} - -#define DEVICE_SRC true, false -#define SURFACE_FORMAT_NV cv::cudacodec::ENC_BUFFER_FORMAT::BF_ARGB, cv::cudacodec::ENC_BUFFER_FORMAT::BF_ARGB -INSTANTIATE_TEST_CASE_P(CUDA_Codec, WriteNv, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(SURFACE_FORMAT_NV))); - -void CvtColor(const Mat& in, Mat& out, const cudacodec::COLOR_FORMAT_CV surfaceFormatCv) { +void CvtColor(const Mat& in, Mat& out, const cudacodec::COLOR_FORMAT_VW surfaceFormatCv) { switch (surfaceFormatCv) { - case(cudacodec::COLOR_FORMAT_CV::RGB): + case(cudacodec::COLOR_FORMAT_VW::RGB): return cv::cvtColor(in, out, COLOR_BGR2RGB); - case(cudacodec::COLOR_FORMAT_CV::BGRA): + case(cudacodec::COLOR_FORMAT_VW::BGRA): return cv::cvtColor(in, out, COLOR_BGR2BGRA); - case(cudacodec::COLOR_FORMAT_CV::RGBA): + case(cudacodec::COLOR_FORMAT_VW::RGBA): return cv::cvtColor(in, out, COLOR_BGR2RGBA); - case(cudacodec::COLOR_FORMAT_CV::GRAY): + case(cudacodec::COLOR_FORMAT_VW::GRAY): return cv::cvtColor(in, out, COLOR_BGR2GRAY); default: in.copyTo(out); } } -PARAM_TEST_CASE(WriteCv, cv::cuda::DeviceInfo, bool, cv::cudacodec::VideoWriterCodec, double, cv::cudacodec::COLOR_FORMAT_CV) +PARAM_TEST_CASE(Write, cv::cuda::DeviceInfo, bool, cv::cudacodec::CODEC_VW, double, cv::cudacodec::COLOR_FORMAT_VW) { }; -CUDA_TEST_P(WriteCv, Writer) +CUDA_TEST_P(Write, Writer) { cv::cuda::setDevice(GET_PARAM(0).deviceID()); const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; const bool deviceSrc = GET_PARAM(1); - const cudacodec::VideoWriterCodec codec = GET_PARAM(2); + const cudacodec::CODEC_VW codec = GET_PARAM(2); const double fps = GET_PARAM(3); - const cv::cudacodec::COLOR_FORMAT_CV surfaceFormatCv = GET_PARAM(4); - const std::string ext = codec == cudacodec::VideoWriterCodec::H264 ? ".h264" : ".hevc"; + const cv::cudacodec::COLOR_FORMAT_VW colorFormat = GET_PARAM(4); + const std::string ext = codec == cudacodec::CODEC_VW::H264 ? ".h264" : ".hevc"; const std::string outputFile = cv::tempfile(ext.c_str()); constexpr int nFrames = 5; Size frameSz; @@ -610,9 +537,9 @@ CUDA_TEST_P(WriteCv, Writer) ASSERT_FALSE(frame.empty()); if (writer.empty()) { frameSz = frame.size(); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, surfaceFormatCv, stream); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, stream); } - CvtColor(frame, frameNewSf, surfaceFormatCv); + CvtColor(frame, frameNewSf, colorFormat); if (deviceSrc) { dFrame.upload(frameNewSf); writer->write(dFrame); @@ -639,14 +566,14 @@ CUDA_TEST_P(WriteCv, Writer) #define DEVICE_SRC true, false #define FPS 10, 29.7 -#define CODEC cv::cudacodec::VideoWriterCodec::H264, cv::cudacodec::VideoWriterCodec::HEVC -#define SURFACE_FORMAT_CV cv::cudacodec::COLOR_FORMAT_CV::BGR, cv::cudacodec::COLOR_FORMAT_CV::RGB, cv::cudacodec::COLOR_FORMAT_CV::BGRA, \ -cv::cudacodec::COLOR_FORMAT_CV::RGBA, cv::cudacodec::COLOR_FORMAT_CV::GRAY -INSTANTIATE_TEST_CASE_P(CUDA_Codec, WriteCv, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(CODEC), testing::Values(FPS), - testing::Values(SURFACE_FORMAT_CV))); +#define CODEC cv::cudacodec::CODEC_VW::H264, cv::cudacodec::CODEC_VW::HEVC +#define COLOR_FORMAT cv::cudacodec::COLOR_FORMAT_VW::BGR, cv::cudacodec::COLOR_FORMAT_VW::RGB, cv::cudacodec::COLOR_FORMAT_VW::BGRA, \ +cv::cudacodec::COLOR_FORMAT_VW::RGBA, cv::cudacodec::COLOR_FORMAT_VW::GRAY +INSTANTIATE_TEST_CASE_P(CUDA_Codec, Write, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(CODEC), testing::Values(FPS), + testing::Values(COLOR_FORMAT))); -struct EncoderParamsBase : testing::TestWithParam +struct EncoderParams : testing::TestWithParam { cv::cuda::DeviceInfo devInfo; cv::cudacodec::EncoderParams params; @@ -667,15 +594,12 @@ struct EncoderParamsBase : testing::TestWithParam } }; -struct EncoderParamsCv : EncoderParamsBase -{ -}; -CUDA_TEST_P(EncoderParamsCv, Writer) +CUDA_TEST_P(EncoderParams, Writer) { const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; constexpr double fps = 25.0; - constexpr cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::H264; + constexpr cudacodec::CODEC_VW codec = cudacodec::CODEC_VW::H264; const std::string ext = ".h264"; const std::string outputFile = cv::tempfile(ext.c_str()); Size frameSz; @@ -683,7 +607,7 @@ CUDA_TEST_P(EncoderParamsCv, Writer) { cv::VideoCapture reader(inputFile); ASSERT_TRUE(reader.isOpened()); - const cv::cudacodec::COLOR_FORMAT_CV format = cv::cudacodec::COLOR_FORMAT_CV::BGR; + const cv::cudacodec::COLOR_FORMAT_VW colorFormat = cv::cudacodec::COLOR_FORMAT_VW::BGR; cv::Ptr writer; cv::Mat frame; cv::cuda::GpuMat dFrame; @@ -694,64 +618,7 @@ CUDA_TEST_P(EncoderParamsCv, Writer) dFrame.upload(frame); if (writer.empty()) { frameSz = frame.size(); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, format, params, stream); - cv::cudacodec::EncoderParams paramsOut = writer->getEncoderParams(); - ASSERT_EQ(params, paramsOut); - } - writer->write(dFrame); - } - } - - { - cv::VideoCapture cap(outputFile); - ASSERT_TRUE(cap.isOpened()); - const int width = static_cast(cap.get(CAP_PROP_FRAME_WIDTH)); - const int height = static_cast(cap.get(CAP_PROP_FRAME_HEIGHT)); - ASSERT_EQ(frameSz, Size(width, height)); - ASSERT_EQ(fps, cap.get(CAP_PROP_FPS)); - const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG); - Mat frame; - for (int i = 0; i < nFrames; ++i) { - cap >> frame; - ASSERT_FALSE(frame.empty()); - if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) { - ASSERT_TRUE(i % params.gopLength == 0); - } - } - } -} - -INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParamsCv, ALL_DEVICES); - -struct EncoderParamsNv : EncoderParamsBase -{ -}; - -CUDA_TEST_P(EncoderParamsNv, Writer) -{ - const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; - constexpr double fps = 25.0; - constexpr cudacodec::VideoWriterCodec codec = cudacodec::VideoWriterCodec::H264; - const std::string ext = ".h264"; - const std::string outputFile = cv::tempfile(ext.c_str()); - Size frameSz; - constexpr int nFrames = 5; - { - cv::VideoCapture reader(inputFile); - ASSERT_TRUE(reader.isOpened()); - const cv::cudacodec::ENC_BUFFER_FORMAT format = cv::cudacodec::ENC_BUFFER_FORMAT::BF_ARGB; - cv::Ptr writer; - cv::Mat frame, frameNewSf; - cv::cuda::GpuMat dFrame; - cv::cuda::Stream stream; - for (int i = 0; i < nFrames; ++i) { - reader >> frame; - ASSERT_FALSE(frame.empty()); - cvtColor(frame, frameNewSf, COLOR_BGR2BGRA); - dFrame.upload(frameNewSf); - if (writer.empty()) { - frameSz = frame.size(); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, format, params, stream); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, params, stream); cv::cudacodec::EncoderParams paramsOut = writer->getEncoderParams(); ASSERT_EQ(params, paramsOut); } @@ -778,7 +645,7 @@ CUDA_TEST_P(EncoderParamsNv, Writer) } } -INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParamsNv, ALL_DEVICES); +INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParams, ALL_DEVICES); #endif // HAVE_NVCUVENC From fbd6827e719c29eea2241890944b7b45d846dacf Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Fri, 14 Oct 2022 18:23:36 +0300 Subject: [PATCH 36/99] Two factory methods. --- .../cudacodec/include/opencv2/cudacodec.hpp | 39 +++---------------- modules/cudacodec/perf/perf_video.cpp | 2 +- modules/cudacodec/src/video_writer.cpp | 30 ++++---------- modules/cudacodec/test/test_video.cpp | 6 +-- 4 files changed, 18 insertions(+), 59 deletions(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 46e699a47f..a28a3c593b 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -231,10 +231,11 @@ class CV_EXPORTS_W VideoWriter @param codec Codec. @param fps Framerate of the created video stream. @param colorFormat OpenCv color format of the frames to be encoded. +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback. Required for working with the encoded video stream. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec = CODEC_VW::H264, - const double fps = 25.0, const COLOR_FORMAT_VW colorFormat = BGR, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec = CODEC_VW::H264, const double fps = 25.0, + const COLOR_FORMAT_VW colorFormat = BGR, Ptr encoderCallback = 0, const Stream& stream = Stream::Null()); /** @brief Creates video writer. @@ -244,39 +245,11 @@ CV_EXPORTS_W Ptr createVideoWriter(const String& fileNam @param fps Framerate of the created video stream. @param colorFormat OpenCv color format of the frames to be encoded. @param params Additional encoding parameters. +@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback. Required for working with the encoded video stream. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, - const double fps, const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); - -/** @brief Creates video writer. - -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. -@param frameSize Size of the input video frames. -@param codec Codec. -@param fps Framerate of the created video stream. -@param colorFormat OpenCv color format of the frames to be encoded. -@param stream Stream for frame pre-processing. - -The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallback. -*/ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec = CODEC_VW::H264, - const double fps = 25.0, const COLOR_FORMAT_VW colorFormat = BGR, const Stream& stream = Stream::Null()); - -/** @brief Creates video writer. - -@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback . Use it if you want to work with the raw video stream. -@param frameSize Size of the input video frames. -@param codec Codec. -@param fps Framerate of the created video stream. -@param colorFormat OpenCv color format of the frames to be encoded. -@param params Additional encoding parameters. -@param stream Stream for frame pre-processing. - -The constructors initialize video writer. User can implement their own multiplexing with cudacodec::EncoderCallback. -*/ -CV_EXPORTS_W Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec, - const double fps, const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, const COLOR_FORMAT_VW colorFormat, + const EncoderParams& params, Ptr encoderCallback = 0, const Stream& stream = Stream::Null()); ////////////////////////////////// Video Decoding ////////////////////////////////////////// diff --git a/modules/cudacodec/perf/perf_video.cpp b/modules/cudacodec/perf/perf_video.cpp index f893706731..713a6062f5 100644 --- a/modules/cudacodec/perf/perf_video.cpp +++ b/modules/cudacodec/perf/perf_video.cpp @@ -147,7 +147,7 @@ PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) frames.push_back(frame); } stream.waitForCompletion(); - cv::Ptr d_writer = cv::cudacodec::createVideoWriter(outputFile, frameBgr.size(), codec, fps, surfaceFormat, stream); + cv::Ptr d_writer = cv::cudacodec::createVideoWriter(outputFile, frameBgr.size(), codec, fps, surfaceFormat, 0, stream); for (int i = 0; i < nFrames - 1; ++i) { startTimer(); d_writer->write(frames[i]); diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index 3076881c10..6e2b82f8b8 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -48,10 +48,8 @@ using namespace cv::cuda; #if !defined(HAVE_NVCUVENC) -Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const CODEC_VW codec, const double, const COLOR_FORMAT_VW, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const Ptr&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const EncoderParams&, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const Ptr, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const EncoderParams&, const Ptr, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } #else // !defined HAVE_NVCUVENC @@ -376,29 +374,17 @@ EncoderParams VideoWriterImpl::getEncoderParams() const { return encoderParams; }; -Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const Stream& stream) -{ - Ptr rawVideoWriter = new RawVideoWriter(fileName); - return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, stream); -} - -Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream) -{ - Ptr rawVideoWriter = new RawVideoWriter(fileName); - return createVideoWriter(rawVideoWriter, frameSize, codec, fps, colorFormat, params, stream); -} - -Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const Stream& stream) +Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, const COLOR_FORMAT_VW colorFormat, + Ptr encoderCallback, const Stream& stream) { + encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName); return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, stream); } -Ptr createVideoWriter(const Ptr& encoderCallback, const Size frameSize, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const EncoderParams& params, const Stream& stream) +Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, const COLOR_FORMAT_VW colorFormat, + const EncoderParams& params, Ptr encoderCallback, const Stream& stream) { + encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName); return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, params, stream); } diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index 1d61e9fc78..a1260a6dae 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -463,7 +463,7 @@ CUDA_TEST_P(TransCode, H264ToH265) Mat tst; frame.download(tst); if (writer.empty()) { frameSz = Size(fmt.width, fmt.height); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, stream); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, 0, stream); } writer->write(frame); } @@ -537,7 +537,7 @@ CUDA_TEST_P(Write, Writer) ASSERT_FALSE(frame.empty()); if (writer.empty()) { frameSz = frame.size(); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, stream); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, 0, stream); } CvtColor(frame, frameNewSf, colorFormat); if (deviceSrc) { @@ -618,7 +618,7 @@ CUDA_TEST_P(EncoderParams, Writer) dFrame.upload(frame); if (writer.empty()) { frameSz = frame.size(); - writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, params, stream); + writer = cv::cudacodec::createVideoWriter(outputFile, frameSz, codec, fps, colorFormat, params, 0, stream); cv::cudacodec::EncoderParams paramsOut = writer->getEncoderParams(); ASSERT_EQ(params, paramsOut); } From a8864db902e3da164dac50fd7b9a1c091bfe055a Mon Sep 17 00:00:00 2001 From: Augustin Manecy Date: Wed, 7 Jul 2021 00:01:40 +0200 Subject: [PATCH 37/99] Add read/write functions to xfeatures2d and normalize naming convention In read function, check before if node is empty to avoid erasing default value in case of missing parameter. Add getters/setters to complete cpp/java/python API (needed for Java Tests.) fix warning due to double to float conversion in freak --- .../include/opencv2/xfeatures2d.hpp | 146 ++++++++++++++- .../include/opencv2/xfeatures2d/nonfree.hpp | 2 + .../test/DAISYDescriptorExtractorTest.java | 67 +++++++ .../test/FREAKDescriptorExtractorTest.java | 64 +++++++ .../java/test/HARRISFeatureDetectorTest.java | 65 +++++++ .../test/LATCHDescriptorExtractorTest.java | 64 +++++++ .../test/LUCIDDescriptorExtractorTest.java | 62 ++++++ .../java/test/MSDFeatureDetectorTest.java | 69 +++++++ .../java/test/STARFeatureDetectorTest.java | 128 +++++++++++++ .../test/SURFDescriptorExtractorTest.java | 115 ++++++++++++ .../java/test/SURFFeatureDetectorTest.java | 176 ++++++++++++++++++ modules/xfeatures2d/src/beblid.cpp | 16 +- modules/xfeatures2d/src/boostdesc.cpp | 48 ++++- modules/xfeatures2d/src/brief.cpp | 85 ++++++--- modules/xfeatures2d/src/daisy.cpp | 72 +++++++ modules/xfeatures2d/src/freak.cpp | 44 +++++ .../src/harris_lapace_detector.cpp | 54 ++++-- modules/xfeatures2d/src/latch.cpp | 123 ++++++------ modules/xfeatures2d/src/lucid.cpp | 48 +++++ modules/xfeatures2d/src/msd.cpp | 71 +++++++ modules/xfeatures2d/src/stardetector.cpp | 50 +++++ modules/xfeatures2d/src/surf.cpp | 5 + modules/xfeatures2d/src/surf.hpp | 27 +++ modules/xfeatures2d/src/vgg.cpp | 35 ++++ 24 files changed, 1535 insertions(+), 101 deletions(-) create mode 100644 modules/xfeatures2d/misc/java/test/DAISYDescriptorExtractorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/FREAKDescriptorExtractorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/HARRISFeatureDetectorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/LATCHDescriptorExtractorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/LUCIDDescriptorExtractorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/MSDFeatureDetectorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/STARFeatureDetectorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/SURFDescriptorExtractorTest.java create mode 100644 modules/xfeatures2d/misc/java/test/SURFFeatureDetectorTest.java diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 8248ccdefa..cf0980ddf3 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -102,6 +102,20 @@ class CV_EXPORTS_W FREAK : public Feature2D float patternScale = 22.0f, int nOctaves = 4, const std::vector& selectedPairs = std::vector()); + + CV_WRAP virtual void setOrientationNormalized(bool orientationNormalized) = 0; + CV_WRAP virtual bool getOrientationNormalized() const = 0; + + CV_WRAP virtual void setScaleNormalized(bool scaleNormalized) = 0; + CV_WRAP virtual bool getScaleNormalized() const = 0; + + CV_WRAP virtual void setPatternScale(double patternScale) = 0; + CV_WRAP virtual double getPatternScale() const = 0; + + CV_WRAP virtual void setNOctaves(int nOctaves) = 0; + CV_WRAP virtual int getNOctaves() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; @@ -115,6 +129,23 @@ class CV_EXPORTS_W StarDetector : public Feature2D int lineThresholdProjected=10, int lineThresholdBinarized=8, int suppressNonmaxSize=5); + + CV_WRAP virtual void setMaxSize(int _maxSize) = 0; + CV_WRAP virtual int getMaxSize() const = 0; + + CV_WRAP virtual void setResponseThreshold(int _responseThreshold) = 0; + CV_WRAP virtual int getResponseThreshold() const = 0; + + CV_WRAP virtual void setLineThresholdProjected(int _lineThresholdProjected) = 0; + CV_WRAP virtual int getLineThresholdProjected() const = 0; + + CV_WRAP virtual void setLineThresholdBinarized(int _lineThresholdBinarized) = 0; + CV_WRAP virtual int getLineThresholdBinarized() const = 0; + + CV_WRAP virtual void setSuppressNonmaxSize(int _suppressNonmaxSize) = 0; + CV_WRAP virtual int getSuppressNonmaxSize() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /* @@ -131,6 +162,14 @@ class CV_EXPORTS_W BriefDescriptorExtractor : public Feature2D { public: CV_WRAP static Ptr create( int bytes = 32, bool use_orientation = false ); + + CV_WRAP virtual void setDescriptorSize(int bytes) = 0; + CV_WRAP virtual int getDescriptorSize() const = 0; + + CV_WRAP virtual void setUseOrientation(bool use_orientation) = 0; + CV_WRAP virtual bool getUseOrientation() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /** @brief Class implementing the locally uniform comparison image descriptor, described in @cite LUCID @@ -148,6 +187,14 @@ class CV_EXPORTS_W LUCID : public Feature2D * @param blur_kernel kernel for blurring image prior to descriptor construction, where 1=3x3, 2=5x5, 3=7x7 and so forth */ CV_WRAP static Ptr create(const int lucid_kernel = 1, const int blur_kernel = 2); + + CV_WRAP virtual void setLucidKernel(int lucid_kernel) = 0; + CV_WRAP virtual int getLucidKernel() const = 0; + + CV_WRAP virtual void setBlurKernel(int blur_kernel) = 0; + CV_WRAP virtual int getBlurKernel() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; @@ -177,6 +224,20 @@ class CV_EXPORTS_W LATCH : public Feature2D { public: CV_WRAP static Ptr create(int bytes = 32, bool rotationInvariance = true, int half_ssd_size = 3, double sigma = 2.0); + + CV_WRAP virtual void setBytes(int bytes) = 0; + CV_WRAP virtual int getBytes() const = 0; + + CV_WRAP virtual void setRotationInvariance(bool rotationInvariance) = 0; + CV_WRAP virtual bool getRotationInvariance() const = 0; + + CV_WRAP virtual void setHalfSSDsize(int half_ssd_size) = 0; + CV_WRAP virtual int getHalfSSDsize() const = 0; + + CV_WRAP virtual void setSigma(double sigma) = 0; + CV_WRAP virtual double getSigma() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /** @brief Class implementing BEBLID (Boosted Efficient Binary Local Image Descriptor), @@ -222,6 +283,11 @@ class CV_EXPORTS_W BEBLID : public Feature2D BEBLID::SIZE_512_BITS or BEBLID::SIZE_256_BITS. */ CV_WRAP static Ptr create(float scale_factor, int n_bits = BEBLID::SIZE_512_BITS); + + CV_WRAP virtual void setScaleFactor(float scale_factor) = 0; + CV_WRAP virtual float getScaleFactor() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /** @brief Class implementing TEBLID (Triplet-based Efficient Binary Local Image Descriptor), @@ -265,6 +331,8 @@ class CV_EXPORTS_W TEBLID : public Feature2D TEBLID::SIZE_256_BITS or TEBLID::SIZE_512_BITS. */ CV_WRAP static Ptr create(float scale_factor, int n_bits = TEBLID::SIZE_256_BITS); + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /** @brief Class implementing DAISY descriptor, described in @cite Tola10 @@ -294,6 +362,32 @@ class CV_EXPORTS_W DAISY : public Feature2D int q_hist = 8, DAISY::NormalizationType norm = DAISY::NRM_NONE, InputArray H = noArray(), bool interpolation = true, bool use_orientation = false ); + CV_WRAP virtual void setRadius(float radius) = 0; + CV_WRAP virtual float getRadius() const = 0; + + CV_WRAP virtual void setQRadius(int q_radius) = 0; + CV_WRAP virtual int getQRadius() const = 0; + + CV_WRAP virtual void setQTheta(int q_theta) = 0; + CV_WRAP virtual int getQTheta() const = 0; + + CV_WRAP virtual void setQHist(int q_hist) = 0; + CV_WRAP virtual int getQHist() const = 0; + + CV_WRAP virtual void setNorm(int norm) = 0; + CV_WRAP virtual int getNorm() const = 0; + + CV_WRAP virtual void setH(InputArray H) = 0; + CV_WRAP virtual cv::Mat getH() const = 0; + + CV_WRAP virtual void setInterpolation(bool interpolation) = 0; + CV_WRAP virtual bool getInterpolation() const = 0; + + CV_WRAP virtual void setUseOrientation(bool use_orientation) = 0; + CV_WRAP virtual bool getUseOrientation() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; + /** @overload * @param image image to extract descriptors * @param keypoints of interest within image @@ -371,9 +465,38 @@ class CV_EXPORTS_W MSDDetector : public Feature2D { public: - CV_WRAP static Ptr create(int m_patch_radius = 3, int m_search_area_radius = 5, + CV_WRAP static Ptr create(int m_patch_radius = 3, int m_search_area_radius = 5, int m_nms_radius = 5, int m_nms_scale_radius = 0, float m_th_saliency = 250.0f, int m_kNN = 4, float m_scale_factor = 1.25f, int m_n_scales = -1, bool m_compute_orientation = false); + + CV_WRAP virtual void setPatchRadius(int patch_radius) = 0; + CV_WRAP virtual int getPatchRadius() const = 0; + + CV_WRAP virtual void setSearchAreaRadius(int use_orientation) = 0; + CV_WRAP virtual int getSearchAreaRadius() const = 0; + + CV_WRAP virtual void setNmsRadius(int nms_radius) = 0; + CV_WRAP virtual int getNmsRadius() const = 0; + + CV_WRAP virtual void setNmsScaleRadius(int nms_scale_radius) = 0; + CV_WRAP virtual int getNmsScaleRadius() const = 0; + + CV_WRAP virtual void setThSaliency(float th_saliency) = 0; + CV_WRAP virtual float getThSaliency() const = 0; + + CV_WRAP virtual void setKNN(int kNN) = 0; + CV_WRAP virtual int getKNN() const = 0; + + CV_WRAP virtual void setScaleFactor(float scale_factor) = 0; + CV_WRAP virtual float getScaleFactor() const = 0; + + CV_WRAP virtual void setNScales(int use_orientation) = 0; + CV_WRAP virtual int getNScales() const = 0; + + CV_WRAP virtual void setComputeOrientation(bool compute_orientation) = 0; + CV_WRAP virtual bool getComputeOrientation() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /** @brief Class implementing VGG (Oxford Visual Geometry Group) descriptor trained end to end @@ -406,6 +529,8 @@ class CV_EXPORTS_W VGG : public Feature2D bool img_normalize = true, bool use_scale_orientation = true, float scale_factor = 6.25f, bool dsc_normalize = false ); + CV_WRAP String getDefaultName() const CV_OVERRIDE; + CV_WRAP virtual void setSigma(const float isigma) = 0; CV_WRAP virtual float getSigma() const = 0; @@ -462,6 +587,8 @@ class CV_EXPORTS_W BoostDesc : public Feature2D CV_WRAP static Ptr create( int desc = BoostDesc::BINBOOST_256, bool use_scale_orientation = true, float scale_factor = 6.25f ); + CV_WRAP String getDefaultName() const CV_OVERRIDE; + CV_WRAP virtual void setUseScaleOrientation(const bool use_scale_orientation) = 0; CV_WRAP virtual bool getUseScaleOrientation() const = 0; @@ -977,6 +1104,23 @@ class CV_EXPORTS_W HarrisLaplaceFeatureDetector : public Feature2D float DOG_thresh=0.01f, int maxCorners=5000, int num_layers=4); + + CV_WRAP virtual void setNumOctaves(int numOctaves_) = 0; + CV_WRAP virtual int getNumOctaves() const = 0; + + CV_WRAP virtual void setCornThresh(float corn_thresh_) = 0; + CV_WRAP virtual float getCornThresh() const = 0; + + CV_WRAP virtual void setDOGThresh(float DOG_thresh_) = 0; + CV_WRAP virtual float getDOGThresh() const = 0; + + CV_WRAP virtual void setMaxCorners(int maxCorners_) = 0; + CV_WRAP virtual int getMaxCorners() const = 0; + + CV_WRAP virtual void setNumLayers(int num_layers_) = 0; + CV_WRAP virtual int getNumLayers() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; /** diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d/nonfree.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d/nonfree.hpp index 472d822807..8eb11aa665 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d/nonfree.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d/nonfree.hpp @@ -109,6 +109,8 @@ class CV_EXPORTS_W SURF : public Feature2D CV_WRAP virtual void setUpright(bool upright) = 0; CV_WRAP virtual bool getUpright() const = 0; + + CV_WRAP String getDefaultName() const CV_OVERRIDE; }; typedef SURF SurfFeatureDetector; diff --git a/modules/xfeatures2d/misc/java/test/DAISYDescriptorExtractorTest.java b/modules/xfeatures2d/misc/java/test/DAISYDescriptorExtractorTest.java new file mode 100644 index 0000000000..97d3063cbb --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/DAISYDescriptorExtractorTest.java @@ -0,0 +1,67 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.xfeatures2d.DAISY; + +public class DAISYDescriptorExtractorTest extends OpenCVTestCase { + + DAISY extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = DAISY.create(); // default (15, 3, 8, 8, 100, noArray, true, false) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.DAISY\"\nradius: 16.\nq_radius: 4\nq_theta: 9\nq_hist: 10\nnorm_type: 101\nenable_interpolation: 0\nuse_orientation: 1\n"); + + extractor.read(filename); + + assertEquals(16.0f, extractor.getRadius()); + assertEquals(4, extractor.getQRadius()); + assertEquals(9, extractor.getQTheta()); + assertEquals(10, extractor.getQHist()); + assertEquals(101, extractor.getNorm()); + assertEquals(false, extractor.getInterpolation()); + assertEquals(true, extractor.getUseOrientation()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.DAISY\"\nradius: 15.\nq_radius: 3\nq_theta: 8\nq_hist: 8\nnorm_type: 100\nenable_interpolation: 1\nuse_orientation: 0\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/FREAKDescriptorExtractorTest.java b/modules/xfeatures2d/misc/java/test/FREAKDescriptorExtractorTest.java new file mode 100644 index 0000000000..4793a48783 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/FREAKDescriptorExtractorTest.java @@ -0,0 +1,64 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.xfeatures2d.FREAK; + +public class FREAKDescriptorExtractorTest extends OpenCVTestCase { + + FREAK extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = FREAK.create(); // default (true,true,22,4) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.FREAK\"\norientationNormalized: 0\nscaleNormalized: 0\npatternScale: 23.\nnOctaves: 5\n"); + + extractor.read(filename); + + assertEquals(false, extractor.getOrientationNormalized()); + assertEquals(false, extractor.getScaleNormalized()); + assertEquals(23.0, extractor.getPatternScale()); + assertEquals(5, extractor.getNOctaves()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.FREAK\"\norientationNormalized: 1\nscaleNormalized: 1\npatternScale: 22.\nnOctaves: 4\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/HARRISFeatureDetectorTest.java b/modules/xfeatures2d/misc/java/test/HARRISFeatureDetectorTest.java new file mode 100644 index 0000000000..2f45a88b21 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/HARRISFeatureDetectorTest.java @@ -0,0 +1,65 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.xfeatures2d.HarrisLaplaceFeatureDetector; + +public class HARRISFeatureDetectorTest extends OpenCVTestCase { + + HarrisLaplaceFeatureDetector detector; + + @Override + protected void setUp() throws Exception { + super.setUp(); + detector = HarrisLaplaceFeatureDetector.create(); // default constructor have (6, 0.01, 0.01, 5000, 4) + } + + public void testCreate() { + assertNotNull(detector); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.HARRIS-LAPLACE\"\nnumOctaves: 5\ncorn_thresh: 0.02\nDOG_thresh: 0.03\nmaxCorners: 4000\nnum_layers: 2\n"); + detector.read(filename); + + assertEquals(5, detector.getNumOctaves()); + assertEquals(0.02f, detector.getCornThresh()); + assertEquals(0.03f, detector.getDOGThresh()); + assertEquals(4000, detector.getMaxCorners()); + assertEquals(2, detector.getNumLayers()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.HARRIS-LAPLACE\"\nnumOctaves: 6\ncorn_thresh: 9.9999997764825821e-03\nDOG_thresh: 9.9999997764825821e-03\nmaxCorners: 5000\nnum_layers: 4\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/LATCHDescriptorExtractorTest.java b/modules/xfeatures2d/misc/java/test/LATCHDescriptorExtractorTest.java new file mode 100644 index 0000000000..10802d2199 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/LATCHDescriptorExtractorTest.java @@ -0,0 +1,64 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.xfeatures2d.LATCH; + +public class LATCHDescriptorExtractorTest extends OpenCVTestCase { + + LATCH extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = LATCH.create(); // default (32,true,3,2.0) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.LATCH\"\ndescriptorSize: 64\nrotationInvariance: 0\nhalf_ssd_size: 5\nsigma: 3.\n"); + + extractor.read(filename); + + assertEquals(64, extractor.getBytes()); + assertEquals(false, extractor.getRotationInvariance()); + assertEquals(5, extractor.getHalfSSDsize()); + assertEquals(3.0, extractor.getSigma()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.LATCH\"\ndescriptorSize: 32\nrotationInvariance: 1\nhalf_ssd_size: 3\nsigma: 2.\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/LUCIDDescriptorExtractorTest.java b/modules/xfeatures2d/misc/java/test/LUCIDDescriptorExtractorTest.java new file mode 100644 index 0000000000..295fc215f5 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/LUCIDDescriptorExtractorTest.java @@ -0,0 +1,62 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.xfeatures2d.LUCID; + +public class LUCIDDescriptorExtractorTest extends OpenCVTestCase { + + LUCID extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = LUCID.create(); // default (1,2) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.LUCID\"\nlucid_kernel: 2\nblur_kernel: 3\n"); + + extractor.read(filename); + + assertEquals(2, extractor.getLucidKernel()); + assertEquals(3, extractor.getBlurKernel()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.LUCID\"\nlucid_kernel: 1\nblur_kernel: 2\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/MSDFeatureDetectorTest.java b/modules/xfeatures2d/misc/java/test/MSDFeatureDetectorTest.java new file mode 100644 index 0000000000..8e37e0138c --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/MSDFeatureDetectorTest.java @@ -0,0 +1,69 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.xfeatures2d.MSDDetector; + +public class MSDFeatureDetectorTest extends OpenCVTestCase { + + MSDDetector detector; + + @Override + protected void setUp() throws Exception { + super.setUp(); + detector = MSDDetector.create(); // default (3,5,5,0,250.4,',1.25,-1,false) + } + + public void testCreate() { + assertNotNull(detector); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.MSD\"\npatch_radius: 4\nsearch_area_radius: 6\nnms_radius: 7\nnms_scale_radius: 1\nth_saliency: 251.\nkNN: 2\nscale_factor: 1.26\nn_scales: 3\ncompute_orientation: 1\n"); + + detector.read(filename); + + assertEquals(4, detector.getPatchRadius()); + assertEquals(6, detector.getSearchAreaRadius()); + assertEquals(7, detector.getNmsRadius()); + assertEquals(1, detector.getNmsScaleRadius()); + assertEquals(251.0f, detector.getThSaliency()); + assertEquals(2, detector.getKNN()); + assertEquals(1.26f, detector.getScaleFactor()); + assertEquals(3, detector.getNScales()); + assertEquals(true, detector.getComputeOrientation()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.MSD\"\npatch_radius: 3\nsearch_area_radius: 5\nnms_radius: 5\nnms_scale_radius: 0\nth_saliency: 250.\nkNN: 4\nscale_factor: 1.2500000000000000e+00\nn_scales: -1\ncompute_orientation: 0\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/STARFeatureDetectorTest.java b/modules/xfeatures2d/misc/java/test/STARFeatureDetectorTest.java new file mode 100644 index 0000000000..88d3dade89 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/STARFeatureDetectorTest.java @@ -0,0 +1,128 @@ +package org.opencv.test.features2d; + +import java.util.Arrays; + +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.KeyPoint; +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.imgproc.Imgproc; +import org.opencv.xfeatures2d.StarDetector; + +public class STARFeatureDetectorTest extends OpenCVTestCase { + + StarDetector detector; + int matSize; + KeyPoint[] truth; + + private Mat getMaskImg() { + Mat mask = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); + Mat right = mask.submat(0, matSize, matSize / 2, matSize); + right.setTo(new Scalar(0)); + return mask; + } + + private Mat getTestImg() { + Scalar color = new Scalar(0); + int center = matSize / 2; + int radius = 6; + int offset = 40; + + Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); + Imgproc.circle(img, new Point(center - offset, center), radius, color, -1); + Imgproc.circle(img, new Point(center + offset, center), radius, color, -1); + Imgproc.circle(img, new Point(center, center - offset), radius, color, -1); + Imgproc.circle(img, new Point(center, center + offset), radius, color, -1); + Imgproc.circle(img, new Point(center, center), radius, color, -1); + return img; + } + + protected void setUp() throws Exception { + super.setUp(); + detector = createClassInstance(XFEATURES2D+"StarDetector", DEFAULT_FACTORY, null, null); + matSize = 200; + truth = new KeyPoint[] { + new KeyPoint( 95, 80, 22, -1, 31.5957f, 0, -1), + new KeyPoint(105, 80, 22, -1, 31.5957f, 0, -1), + new KeyPoint( 80, 95, 22, -1, 31.5957f, 0, -1), + new KeyPoint(120, 95, 22, -1, 31.5957f, 0, -1), + new KeyPoint(100, 100, 8, -1, 30.f, 0, -1), + new KeyPoint( 80, 105, 22, -1, 31.5957f, 0, -1), + new KeyPoint(120, 105, 22, -1, 31.5957f, 0, -1), + new KeyPoint( 95, 120, 22, -1, 31.5957f, 0, -1), + new KeyPoint(105, 120, 22, -1, 31.5957f, 0, -1) + }; + } + + public void testCreate() { + assertNotNull(detector); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + Mat img = getTestImg(); + MatOfKeyPoint keypoints = new MatOfKeyPoint(); + + detector.detect(img, keypoints); + + assertListKeyPointEquals(Arrays.asList(truth), keypoints.toList(), EPS); + } + + public void testDetectMatListOfKeyPointMat() { + Mat img = getTestImg(); + Mat mask = getMaskImg(); + MatOfKeyPoint keypoints = new MatOfKeyPoint(); + + detector.detect(img, keypoints, mask); + + assertListKeyPointEquals(Arrays.asList(truth[0], truth[2], truth[5], truth[7]), keypoints.toList(), EPS); + } + + public void testEmpty() { +// assertFalse(detector.empty()); + fail("Not yet implemented"); + } + + public void testReadYml() { + Mat img = getTestImg(); + + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); + detector.detect(img, keypoints1); + + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.STAR\"\nmaxSize: 45\nresponseThreshold: 150\nlineThresholdProjected: 10\nlineThresholdBinarized: 8\nsuppressNonmaxSize: 5\n"); + detector.read(filename); + + assertEquals(45, detector.getMaxSize()); + assertEquals(150, detector.getResponseThreshold()); + assertEquals(10, detector.getLineThresholdProjected()); + assertEquals(8, detector.getLineThresholdBinarized()); + assertEquals(5, detector.getSuppressNonmaxSize()); + + MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); + detector.detect(img, keypoints2); + + assertTrue(keypoints2.total() <= keypoints1.total()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.STAR\"\nmaxSize: 45\nresponseThreshold: 30\nlineThresholdProjected: 10\nlineThresholdBinarized: 8\nsuppressNonmaxSize: 5\n"; + assertEquals(truth, readFile(filename)); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/SURFDescriptorExtractorTest.java b/modules/xfeatures2d/misc/java/test/SURFDescriptorExtractorTest.java new file mode 100644 index 0000000000..e2bac918d8 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/SURFDescriptorExtractorTest.java @@ -0,0 +1,115 @@ +package org.opencv.test.features2d; + +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.KeyPoint; +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.imgproc.Imgproc; +import org.opencv.xfeatures2d.SURF; + +public class SURFDescriptorExtractorTest extends OpenCVTestCase { + + SURF extractor; + int matSize; + + private Mat getTestImg() { + Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); + Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2); + Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2); + + return cross; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Class[] cParams = {double.class, int.class, int.class, boolean.class, boolean.class}; + Object[] oValues = {100, 2, 4, true, false}; + extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, cParams, oValues); + + matSize = 100; + } + + public void testComputeListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testComputeMatListOfKeyPointMat() { + KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1); + MatOfKeyPoint keypoints = new MatOfKeyPoint(point); + Mat img = getTestImg(); + Mat descriptors = new Mat(); + + extractor.compute(img, keypoints, descriptors); + + Mat truth = new Mat(1, 128, CvType.CV_32FC1) { + { + put(0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.058821894, 0.058821894, -0.045962855, 0.046261817, 0.0085156476, + 0.0085754395, -0.0064509804, 0.0064509804, 0.00044069235, 0.00044069235, 0, 0, 0.00025723741, + 0.00025723741, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.00025723741, 0.00025723741, -0.00044069235, + 0.00044069235, 0, 0, 0.36278215, 0.36278215, -0.24688604, 0.26173124, 0.052068226, 0.052662034, + -0.032815345, 0.032815345, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0064523756, + 0.0064523756, 0.0082002236, 0.0088908644, -0.059001274, 0.059001274, 0.045789491, 0.04648013, + 0.11961588, 0.22789426, -0.01322381, 0.18291828, -0.14042182, 0.23973691, 0.073782086, 0.23769434, + -0.027880307, 0.027880307, 0.049587864, 0.049587864, -0.33991757, 0.33991757, 0.21437603, 0.21437603, + -0.0020763327, 0.0020763327, 0.006245892, 0.006245892, -0.04067041, 0.04067041, 0.019361559, + 0.019361559, 0, 0, -0.0035977389, 0.0035977389, 0, 0, -0.00099993451, 0.00099993451, 0.040670406, + 0.040670406, -0.019361559, 0.019361559, 0.006245892, 0.006245892, -0.0020763327, 0.0020763327, + -0.00034532088, 0.00034532088, 0, 0, 0, 0, 0.00034532088, 0.00034532088, -0.00099993451, + 0.00099993451, 0, 0, 0, 0, 0.0035977389, 0.0035977389 + ); + } + }; + + assertMatEqual(truth, descriptors, EPS); + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDescriptorSize() { + assertEquals(128, extractor.descriptorSize()); + } + + public void testDescriptorType() { + assertEquals(CvType.CV_32F, extractor.descriptorType()); + } + + public void testEmpty() { +// assertFalse(extractor.empty()); + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.SURF\"\nhessianThreshold: 100.\nextended: 1\nupright: 0\nnOctaves: 2\nnOctaveLayers: 4\n"); + + extractor.read(filename); + + assertEquals(128, extractor.descriptorSize()); + assertEquals(true, extractor.getExtended()); + assertEquals(false, extractor.getUpright()); + assertEquals(2, extractor.getNOctaves()); + assertEquals(4, extractor.getNOctaveLayers()); + assertEquals(100., extractor.getHessianThreshold()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.SURF\"\nhessianThreshold: 100.\nextended: 1\nupright: 0\nnOctaves: 2\nnOctaveLayers: 4\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/misc/java/test/SURFFeatureDetectorTest.java b/modules/xfeatures2d/misc/java/test/SURFFeatureDetectorTest.java new file mode 100644 index 0000000000..6e46371b96 --- /dev/null +++ b/modules/xfeatures2d/misc/java/test/SURFFeatureDetectorTest.java @@ -0,0 +1,176 @@ +package org.opencv.test.features2d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.KeyPoint; +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.imgproc.Imgproc; +import org.opencv.xfeatures2d.SURF; + +public class SURFFeatureDetectorTest extends OpenCVTestCase { + + SURF detector; + int matSize; + KeyPoint[] truth; + + private Mat getMaskImg() { + Mat mask = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); + Mat right = mask.submat(0, matSize, matSize / 2, matSize); + right.setTo(new Scalar(0)); + return mask; + } + + private Mat getTestImg() { + Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); + Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2); + Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2); + + return cross; + } + + private void order(List points) { + Collections.sort(points, new Comparator() { + public int compare(KeyPoint p1, KeyPoint p2) { + if (p1.angle < p2.angle) + return -1; + if (p1.angle > p2.angle) + return 1; + return 0; + } + }); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + detector = createClassInstance(XFEATURES2D + "SURF", DEFAULT_FACTORY, null, null); + matSize = 100; + truth = new KeyPoint[] { + new KeyPoint(55.775578f, 55.775578f, 16, 80.245735f, 8617.8633f, 0, -1), + new KeyPoint(44.224422f, 55.775578f, 16, 170.24574f, 8617.8633f, 0, -1), + new KeyPoint(44.224422f, 44.224422f, 16, 260.24573f, 8617.8633f, 0, -1), + new KeyPoint(55.775578f, 44.224422f, 16, 350.24573f, 8617.8633f, 0, -1) + }; + } + + public void testCreate() { + assertNotNull(detector); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + + setProperty(detector, "hessianThreshold", "double", 8000); + setProperty(detector, "nOctaves", "int", 3); + setProperty(detector, "nOctaveLayers", "int", 4); + setProperty(detector, "upright", "boolean", false); + setProperty(detector, "extended", "boolean", true); + + List keypoints = new ArrayList(); + Mat cross = getTestImg(); + List crosses = new ArrayList(3); + crosses.add(cross); + crosses.add(cross); + crosses.add(cross); + + detector.detect(crosses, keypoints); + + assertEquals(3, keypoints.size()); + + for (MatOfKeyPoint mkp : keypoints) { + List lkp = mkp.toList(); + order(lkp); + assertListKeyPointEquals(Arrays.asList(truth), lkp, EPS); + } + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + + setProperty(detector, "hessianThreshold", "double", 8000); + setProperty(detector, "nOctaves", "int", 3); + setProperty(detector, "nOctaveLayers", "int", 4); + setProperty(detector, "upright", "boolean", false); + + MatOfKeyPoint keypoints = new MatOfKeyPoint(); + Mat cross = getTestImg(); + + detector.detect(cross, keypoints); + + List lkp = keypoints.toList(); + order(lkp); + assertListKeyPointEquals(Arrays.asList(truth), lkp, EPS); + } + + public void testDetectMatListOfKeyPointMat() { + + setProperty(detector, "hessianThreshold", "double", 8000); + setProperty(detector, "nOctaves", "int", 3); + setProperty(detector, "nOctaveLayers", "int", 4); + setProperty(detector, "upright", "boolean", false); + setProperty(detector, "extended", "boolean", true); + + Mat img = getTestImg(); + Mat mask = getMaskImg(); + MatOfKeyPoint keypoints = new MatOfKeyPoint(); + + detector.detect(img, keypoints, mask); + + List lkp = keypoints.toList(); + order(lkp); + assertListKeyPointEquals(Arrays.asList(truth[1], truth[2]), lkp, EPS); + } + + public void testEmpty() { +// assertFalse(detector.empty()); + fail("Not yet implemented"); + } + + public void testReadYml() { + Mat cross = getTestImg(); + + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); + detector.detect(cross, keypoints1); + + String filename = OpenCVTestRunner.getTempFileName("xml"); + writeFile(filename, "\n\nFeature2D.SURF\n8000.\n1\n0\n3\n4\n\n"); + + detector.read(filename); + + assertEquals(128, detector.descriptorSize()); + assertEquals(8000., detector.getHessianThreshold()); + assertEquals(true, detector.getExtended()); + assertEquals(false, detector.getUpright()); + assertEquals(3, detector.getNOctaves()); + assertEquals(4, detector.getNOctaveLayers()); + + MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); + detector.detect(cross, keypoints2); + + assertTrue(keypoints2.total() <= keypoints1.total()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.SURF\"\nhessianThreshold: 100.\nextended: 0\nupright: 0\nnOctaves: 4\nnOctaveLayers: 3\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/xfeatures2d/src/beblid.cpp b/modules/xfeatures2d/src/beblid.cpp index 8dec556a49..00812cd10d 100644 --- a/modules/xfeatures2d/src/beblid.cpp +++ b/modules/xfeatures2d/src/beblid.cpp @@ -58,6 +58,9 @@ class BEBLID_Impl CV_FINAL: public BEBLID // returns the default norm type int defaultNorm() const CV_OVERRIDE { return cv::NORM_HAMMING; } + void setScaleFactor(float scale_factor) CV_OVERRIDE { scale_factor_ = scale_factor; } + float getScaleFactor() const { return scale_factor_;} + // compute descriptors given keypoints void compute(InputArray image, vector &keypoints, OutputArray descriptors) CV_OVERRIDE; @@ -347,7 +350,7 @@ void BEBLID_Impl::compute(InputArray _image, vector &key // constructor template BEBLID_Impl::BEBLID_Impl(float scale_factor, const std::vector& wl_params) - : wl_params_(wl_params), scale_factor_(scale_factor),patch_size_(32, 32) + : wl_params_(wl_params), scale_factor_(scale_factor), patch_size_(32, 32) { } @@ -466,5 +469,16 @@ Ptr BEBLID::create(float scale_factor, int n_bits) CV_Error(Error::StsBadArg, "n_bits should be either BEBLID::SIZE_512_BITS or BEBLID::SIZE_256_BITS"); } } + +String BEBLID::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".BEBLID"); +} + +String TEBLID::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".TEBLID"); +} + } // END NAMESPACE XFEATURES2D } // END NAMESPACE CV diff --git a/modules/xfeatures2d/src/boostdesc.cpp b/modules/xfeatures2d/src/boostdesc.cpp index fe672a7324..0e74459042 100644 --- a/modules/xfeatures2d/src/boostdesc.cpp +++ b/modules/xfeatures2d/src/boostdesc.cpp @@ -81,26 +81,29 @@ class BoostDesc_Impl CV_FINAL : public BoostDesc float scale_factor = 6.25f ); // destructor - virtual ~BoostDesc_Impl() CV_OVERRIDE; + ~BoostDesc_Impl() CV_OVERRIDE; + + void read( const FileNode& fn ) CV_OVERRIDE; + void write( FileStorage& fs ) const CV_OVERRIDE; // returns the descriptor length in bytes - virtual int descriptorSize() const CV_OVERRIDE { return m_descriptor_size; } + int descriptorSize() const CV_OVERRIDE { return m_descriptor_size; } // returns the descriptor type - virtual int descriptorType() const CV_OVERRIDE { return m_descriptor_type; } + int descriptorType() const CV_OVERRIDE { return m_descriptor_type; } // returns the default norm type - virtual int defaultNorm() const CV_OVERRIDE { return m_descriptor_norm; } + int defaultNorm() const CV_OVERRIDE { return m_descriptor_norm; } // compute descriptors given keypoints - virtual void compute( InputArray image, vector& keypoints, OutputArray descriptors ) CV_OVERRIDE; + void compute( InputArray image, vector& keypoints, OutputArray descriptors ) CV_OVERRIDE; // getter / setter - virtual void setUseScaleOrientation(const bool use_scale_orientation) CV_OVERRIDE { m_use_scale_orientation = use_scale_orientation; } - virtual bool getUseScaleOrientation() const CV_OVERRIDE { return m_use_scale_orientation; } + void setUseScaleOrientation(const bool use_scale_orientation) CV_OVERRIDE { m_use_scale_orientation = use_scale_orientation; } + bool getUseScaleOrientation() const CV_OVERRIDE { return m_use_scale_orientation; } - virtual void setScaleFactor(const float scale_factor) CV_OVERRIDE { m_scale_factor = scale_factor; } - virtual float getScaleFactor() const CV_OVERRIDE { return m_scale_factor; } + void setScaleFactor(const float scale_factor) CV_OVERRIDE { m_scale_factor = scale_factor; } + float getScaleFactor() const CV_OVERRIDE { return m_scale_factor; } protected: @@ -731,6 +734,28 @@ BoostDesc_Impl::~BoostDesc_Impl() { } +void BoostDesc_Impl::read (const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["desc_type"].empty()) + fn["desc_type"] >> m_desc_type; + if (!fn["scale_factor"].empty()) + fn["scale_factor"] >> m_scale_factor; + if (!fn["use_scale_orientation"].empty()) + fn["use_scale_orientation"] >> m_use_scale_orientation; +} + +void BoostDesc_Impl::write (FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "desc_type" << m_desc_type; + fs << "scale_factor" << m_scale_factor; + fs << "use_scale_orientation" << m_use_scale_orientation; + } +} + #endif // OPENCV_XFEATURES2D_HAS_BOOST_DATA Ptr BoostDesc::create( int desc, bool use_scale_orientation, float scale_factor ) @@ -743,6 +768,11 @@ Ptr BoostDesc::create( int desc, bool use_scale_orientation, float sc #endif } +String BoostDesc::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".BOOST"); +} + } // END NAMESPACE XFEATURES2D } // END NAMESPACE CV diff --git a/modules/xfeatures2d/src/brief.cpp b/modules/xfeatures2d/src/brief.cpp index a430234648..43c92077e2 100644 --- a/modules/xfeatures2d/src/brief.cpp +++ b/modules/xfeatures2d/src/brief.cpp @@ -63,14 +63,20 @@ class BriefDescriptorExtractorImpl : public BriefDescriptorExtractor // bytes is a length of descriptor in bytes. It can be equal 16, 32 or 64 bytes. BriefDescriptorExtractorImpl( int bytes = 32, bool use_orientation = false ); - virtual void read( const FileNode& ) CV_OVERRIDE; - virtual void write( FileStorage& ) const CV_OVERRIDE; + void read( const FileNode& ) CV_OVERRIDE; + void write( FileStorage& ) const CV_OVERRIDE; - virtual int descriptorSize() const CV_OVERRIDE; - virtual int descriptorType() const CV_OVERRIDE; - virtual int defaultNorm() const CV_OVERRIDE; + int descriptorSize() const CV_OVERRIDE; + int descriptorType() const CV_OVERRIDE; + int defaultNorm() const CV_OVERRIDE; - virtual void compute(InputArray image, std::vector& keypoints, OutputArray descriptors) CV_OVERRIDE; + void setDescriptorSize(int bytes) CV_OVERRIDE; + int getDescriptorSize() const CV_OVERRIDE { return bytes_;} + + void setUseOrientation(bool use_orientation) CV_OVERRIDE { use_orientation_ = use_orientation; } + bool getUseOrientation() const CV_OVERRIDE { return use_orientation_; }; + + void compute(InputArray image, std::vector& keypoints, OutputArray descriptors) CV_OVERRIDE; protected: typedef void(*PixelTestFn)(InputArray, const std::vector&, OutputArray, bool use_orientation ); @@ -85,6 +91,11 @@ Ptr BriefDescriptorExtractor::create( int bytes, bool return makePtr(bytes, use_orientation ); } +String BriefDescriptorExtractor::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".BRIEF"); +} + inline int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x, bool use_orientation, Matx21f R) { static const int HALF_KERNEL = BriefDescriptorExtractorImpl::KERNEL_SIZE / 2; @@ -168,10 +179,27 @@ static void pixelTests64(InputArray _sum, const std::vector& keypoints } BriefDescriptorExtractorImpl::BriefDescriptorExtractorImpl(int bytes, bool use_orientation) : - bytes_(bytes), test_fn_(NULL) + bytes_(bytes), use_orientation_(use_orientation), test_fn_(NULL) { - use_orientation_ = use_orientation; + switch (bytes) + { + case 16: + test_fn_ = pixelTests16; + break; + case 32: + test_fn_ = pixelTests32; + break; + case 64: + test_fn_ = pixelTests64; + break; + default: + CV_Error(Error::StsBadArg, "bytes must be 16, 32, or 64"); + } +} +void BriefDescriptorExtractorImpl::setDescriptorSize(int bytes) +{ + bytes_ = bytes; switch (bytes) { case 16: @@ -205,27 +233,38 @@ int BriefDescriptorExtractorImpl::defaultNorm() const void BriefDescriptorExtractorImpl::read( const FileNode& fn) { - int dSize = fn["descriptorSize"]; - switch (dSize) + // if node is empty, keep previous value + if (!fn["descriptorSize"].empty()) { - case 16: - test_fn_ = pixelTests16; - break; - case 32: - test_fn_ = pixelTests32; - break; - case 64: - test_fn_ = pixelTests64; - break; - default: - CV_Error(Error::StsBadArg, "descriptorSize must be 16, 32, or 64"); + int dSize = fn["descriptorSize"]; + switch (dSize) + { + case 16: + test_fn_ = pixelTests16; + break; + case 32: + test_fn_ = pixelTests32; + break; + case 64: + test_fn_ = pixelTests64; + break; + default: + CV_Error(Error::StsBadArg, "descriptorSize must be 16, 32, or 64"); + } + bytes_ = dSize; } - bytes_ = dSize; + if (!fn["use_orientation"].empty()) + fn["use_orientation"] >> use_orientation_; } void BriefDescriptorExtractorImpl::write( FileStorage& fs) const { - fs << "descriptorSize" << bytes_; + if ( fs.isOpened() ) + { + fs << "name" << getDefaultName(); + fs << "descriptorSize" << bytes_; + fs << "use_orientation" << use_orientation_; + } } void BriefDescriptorExtractorImpl::compute(InputArray image, diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 4c99297575..ff4dccd414 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -103,6 +103,46 @@ class DAISY_Impl CV_FINAL : public DAISY virtual ~DAISY_Impl() CV_OVERRIDE; + void read( const FileNode& fn) CV_OVERRIDE; + void write( FileStorage& fs) const CV_OVERRIDE; + + void setRadius(float radius) CV_OVERRIDE { m_rad = radius; } + float getRadius() const CV_OVERRIDE { return m_rad; } + + void setQRadius(int q_radius) CV_OVERRIDE { m_rad_q_no = q_radius; } + int getQRadius() const CV_OVERRIDE { return m_rad_q_no; } + + void setQTheta(int q_theta) CV_OVERRIDE { m_th_q_no = q_theta; } + int getQTheta() const CV_OVERRIDE { return m_th_q_no; } + + void setQHist(int q_hist) CV_OVERRIDE { m_hist_th_q_no = q_hist; } + int getQHist() const CV_OVERRIDE { return m_hist_th_q_no; } + + void setNorm(int norm) CV_OVERRIDE { + switch(norm) + { + case NRM_NONE: + case NRM_PARTIAL: + case NRM_FULL: + case NRM_SIFT: + break; + default: + CV_Error(cv::Error::StsBadArg, "norm should be one of {NRM_NONE, NRM_PARTIAL, NRM_FULL, NRM_SIFT}"); + return; + } + m_nrm_type = (DAISY::NormalizationType)norm; + } + int getNorm() const CV_OVERRIDE { return (int)m_nrm_type; } + + void setH(InputArray H) CV_OVERRIDE { m_h_matrix = H.getMat(); } + cv::Mat getH() const CV_OVERRIDE { return m_h_matrix; } + + void setInterpolation(bool interpolation) CV_OVERRIDE { m_enable_interpolation = interpolation; } + bool getInterpolation() const CV_OVERRIDE { return m_enable_interpolation; } + + void setUseOrientation(bool use_orientation) CV_OVERRIDE { m_use_orientation = use_orientation; } + bool getUseOrientation() const CV_OVERRIDE { return m_use_orientation; } + /** returns the descriptor length in bytes */ virtual int descriptorSize() const CV_OVERRIDE { // +1 is for center pixel @@ -1611,12 +1651,44 @@ DAISY_Impl::~DAISY_Impl() release_auxiliary(); } +void DAISY_Impl::read( const FileNode& fn) +{ + fn["radius"] >> m_rad; + fn["q_radius"] >> m_rad_q_no; + fn["q_theta"] >> m_th_q_no; + fn["q_hist"] >> m_hist_th_q_no; + int norm_type; + fn["norm_type"] >> norm_type; + setNorm(norm_type); + fn["enable_interpolation"] >> m_enable_interpolation; + fn["use_orientation"] >> m_use_orientation; +} +void DAISY_Impl::write( FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "radius" << m_rad; + fs << "q_radius" << m_rad_q_no; + fs << "q_theta" << m_th_q_no; + fs << "q_hist" << m_hist_th_q_no; + fs << "norm_type" << (int)m_nrm_type; + fs << "enable_interpolation" << m_enable_interpolation; + fs << "use_orientation" << m_use_orientation; + } +} + Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, DAISY::NormalizationType norm, InputArray H, bool interpolation, bool use_orientation) { return makePtr(radius, q_radius, q_theta, q_hist, norm, H, interpolation, use_orientation); } +String DAISY::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".DAISY"); +} + } // END NAMESPACE XFEATURES2D } // END NAMESPACE CV diff --git a/modules/xfeatures2d/src/freak.cpp b/modules/xfeatures2d/src/freak.cpp index 054841d4ee..793836b0ed 100644 --- a/modules/xfeatures2d/src/freak.cpp +++ b/modules/xfeatures2d/src/freak.cpp @@ -67,6 +67,21 @@ class FREAK_Impl CV_FINAL : public FREAK virtual ~FREAK_Impl(); + void read( const FileNode& fn) CV_OVERRIDE; + void write( FileStorage& fs) const CV_OVERRIDE; + + void setOrientationNormalized(bool _orientationNormalized) CV_OVERRIDE {orientationNormalized = _orientationNormalized;} + bool getOrientationNormalized() const CV_OVERRIDE { return orientationNormalized; } + + void setScaleNormalized(bool _scaleNormalized) CV_OVERRIDE {scaleNormalized = _scaleNormalized;} + bool getScaleNormalized() const CV_OVERRIDE { return scaleNormalized; } + + void setPatternScale(double _patternScale) CV_OVERRIDE {patternScale = _patternScale;} + double getPatternScale() const CV_OVERRIDE { return patternScale; } + + void setNOctaves(int _nOctaves) CV_OVERRIDE {nOctaves = _nOctaves;} + int getNOctaves() const CV_OVERRIDE { return nOctaves; } + /** returns the descriptor length in bytes */ virtual int descriptorSize() const CV_OVERRIDE; @@ -815,6 +830,30 @@ FREAK_Impl::~FREAK_Impl() { } +void FREAK_Impl::read( const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["orientationNormalized"].empty()) + fn["orientationNormalized"] >> orientationNormalized; + if (!fn["scaleNormalized"].empty()) + fn["scaleNormalized"] >> scaleNormalized; + if (!fn["patternScale"].empty()) + fn["patternScale"] >> patternScale; + if (!fn["nOctaves"].empty()) + fn["nOctaves"] >> nOctaves; +} +void FREAK_Impl::write( FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "orientationNormalized" << orientationNormalized; + fs << "scaleNormalized" << scaleNormalized; + fs << "patternScale" << patternScale; + fs << "nOctaves" << nOctaves; + } +} + int FREAK_Impl::descriptorSize() const { return FREAK::NB_PAIRS / 8; // descriptor length in bytes @@ -840,5 +879,10 @@ Ptr FREAK::create(bool orientationNormalized, patternScale, nOctaves, selectedPairs); } +String FREAK::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".FREAK"); +} + } } // END NAMESPACE CV diff --git a/modules/xfeatures2d/src/harris_lapace_detector.cpp b/modules/xfeatures2d/src/harris_lapace_detector.cpp index 6a6c78581c..1e9dc7c388 100644 --- a/modules/xfeatures2d/src/harris_lapace_detector.cpp +++ b/modules/xfeatures2d/src/harris_lapace_detector.cpp @@ -325,8 +325,23 @@ class HarrisLaplaceFeatureDetector_Impl CV_FINAL : public HarrisLaplaceFeatureDe int maxCorners=5000, int num_layers=4 ); - virtual void read( const FileNode& fn ) CV_OVERRIDE; - virtual void write( FileStorage& fs ) const CV_OVERRIDE; + void read( const FileNode& fn ) CV_OVERRIDE; + void write( FileStorage& fs ) const CV_OVERRIDE; + + void setNumOctaves(int numOctaves_) CV_OVERRIDE {numOctaves = numOctaves_;} + int getNumOctaves() const CV_OVERRIDE {return numOctaves;} + + void setCornThresh(float corn_thresh_) CV_OVERRIDE {corn_thresh = corn_thresh_;} + float getCornThresh() const CV_OVERRIDE {return corn_thresh;} + + void setDOGThresh(float DOG_thresh_) CV_OVERRIDE {DOG_thresh = DOG_thresh_;} + float getDOGThresh() const CV_OVERRIDE {return DOG_thresh;} + + void setMaxCorners(int maxCorners_) CV_OVERRIDE {maxCorners = maxCorners_;} + int getMaxCorners() const CV_OVERRIDE {return maxCorners;} + + void setNumLayers(int num_layers_) CV_OVERRIDE {num_layers = num_layers_; CV_Assert(num_layers == 2 || num_layers==4);} + int getNumLayers() const CV_OVERRIDE {return num_layers;} protected: void detect( InputArray image, std::vector& keypoints, InputArray mask=noArray() ) CV_OVERRIDE; @@ -348,6 +363,11 @@ Ptr HarrisLaplaceFeatureDetector::create( return makePtr(numOctaves, corn_thresh, DOG_thresh, maxCorners, num_layers); } +CV_WRAP String HarrisLaplaceFeatureDetector::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".HARRIS-LAPLACE"); +} + HarrisLaplaceFeatureDetector_Impl::HarrisLaplaceFeatureDetector_Impl( int _numOctaves, float _corn_thresh, @@ -366,20 +386,30 @@ HarrisLaplaceFeatureDetector_Impl::HarrisLaplaceFeatureDetector_Impl( void HarrisLaplaceFeatureDetector_Impl::read (const FileNode& fn) { - numOctaves = fn["numOctaves"]; - corn_thresh = fn["corn_thresh"]; - DOG_thresh = fn["DOG_thresh"]; - maxCorners = fn["maxCorners"]; - num_layers = fn["num_layers"]; + // if node is empty, keep previous value + if (!fn["numOctaves"].empty()) + fn["numOctaves"] >> numOctaves; + if (!fn["corn_thresh"].empty()) + fn["corn_thresh"] >> corn_thresh; + if (!fn["corn_thresh"].empty()) + fn["DOG_thresh"] >> DOG_thresh; + if (!fn["maxCorners"].empty()) + fn["maxCorners"] >> maxCorners; + if (!fn["num_layers"].empty()) + fn["num_layers"] >> num_layers; } void HarrisLaplaceFeatureDetector_Impl::write (FileStorage& fs) const { - fs << "numOctaves" << numOctaves; - fs << "corn_thresh" << corn_thresh; - fs << "DOG_thresh" << DOG_thresh; - fs << "maxCorners" << maxCorners; - fs << "num_layers" << num_layers; + if ( fs.isOpened() ) + { + fs << "name" << getDefaultName(); + fs << "numOctaves" << numOctaves; + fs << "corn_thresh" << corn_thresh; + fs << "DOG_thresh" << DOG_thresh; + fs << "maxCorners" << maxCorners; + fs << "num_layers" << num_layers; + } } /* diff --git a/modules/xfeatures2d/src/latch.cpp b/modules/xfeatures2d/src/latch.cpp index b80e7bc22d..da5041fa0a 100644 --- a/modules/xfeatures2d/src/latch.cpp +++ b/modules/xfeatures2d/src/latch.cpp @@ -68,6 +68,18 @@ namespace cv virtual void read( const FileNode& ) CV_OVERRIDE; virtual void write( FileStorage& ) const CV_OVERRIDE; + void setBytes(int bytes) CV_OVERRIDE; + int getBytes() const CV_OVERRIDE { return bytes_; } + + void setRotationInvariance(bool rotationInvariance) CV_OVERRIDE { rotationInvariance_ = rotationInvariance; } + bool getRotationInvariance() const CV_OVERRIDE { return rotationInvariance_; } + + void setHalfSSDsize(int half_ssd_size) CV_OVERRIDE { half_ssd_size_ = half_ssd_size; } + int getHalfSSDsize() const CV_OVERRIDE { return half_ssd_size_; } + + void setSigma(double sigma) CV_OVERRIDE { sigma_ = sigma; } + double getSigma() const CV_OVERRIDE { return sigma_; } + virtual int descriptorSize() const CV_OVERRIDE; virtual int descriptorType() const CV_OVERRIDE; virtual int defaultNorm() const CV_OVERRIDE; @@ -91,6 +103,10 @@ namespace cv { return makePtr(bytes, rotationInvariance, half_ssd_size, sigma); } + String LATCH::getDefaultName() const + { + return (Feature2D::getDefaultName() + ".LATCH"); + } void CalcuateSums(int count, const std::vector &points, bool rotationInvariance, const Mat &grayImage, const KeyPoint &pt, int &suma, int &sumc, float cos_theta, float sin_theta, int half_ssd_size); @@ -404,37 +420,42 @@ namespace cv + void LATCHDescriptorExtractorImpl::setBytes(int bytes) + { + switch (bytes) + { + case 1: + test_fn_ = pixelTests1; + break; + case 2: + test_fn_ = pixelTests2; + break; + case 4: + test_fn_ = pixelTests4; + break; + case 8: + test_fn_ = pixelTests8; + break; + case 16: + test_fn_ = pixelTests16; + break; + case 32: + test_fn_ = pixelTests32; + break; + case 64: + test_fn_ = pixelTests64; + break; + default: + CV_Error(Error::StsBadArg, "descriptorSize must be 1,2, 4, 8, 16, 32, or 64"); + } + bytes_ = bytes; + setSamplingPoints(); + } + LATCHDescriptorExtractorImpl::LATCHDescriptorExtractorImpl(int bytes, bool rotationInvariance, int half_ssd_size, double sigma) : bytes_(bytes), test_fn_(NULL), rotationInvariance_(rotationInvariance), half_ssd_size_(half_ssd_size), sigma_(sigma) { - switch (bytes) - { - case 1: - test_fn_ = pixelTests1; - break; - case 2: - test_fn_ = pixelTests2; - break; - case 4: - test_fn_ = pixelTests4; - break; - case 8: - test_fn_ = pixelTests8; - break; - case 16: - test_fn_ = pixelTests16; - break; - case 32: - test_fn_ = pixelTests32; - break; - case 64: - test_fn_ = pixelTests64; - break; - default: - CV_Error(Error::StsBadArg, "descriptorSize must be 1,2, 4, 8, 16, 32, or 64"); - } - - setSamplingPoints(); + setBytes(bytes_); } int LATCHDescriptorExtractorImpl::descriptorSize() const @@ -454,39 +475,31 @@ namespace cv void LATCHDescriptorExtractorImpl::read(const FileNode& fn) { - int dSize = fn["descriptorSize"]; - switch (dSize) + // if node is empty, keep previous value + if (!fn["descriptorSize"].empty()) { - case 1: - test_fn_ = pixelTests1; - break; - case 2: - test_fn_ = pixelTests2; - break; - case 4: - test_fn_ = pixelTests4; - break; - case 8: - test_fn_ = pixelTests8; - break; - case 16: - test_fn_ = pixelTests16; - break; - case 32: - test_fn_ = pixelTests32; - break; - case 64: - test_fn_ = pixelTests64; - break; - default: - CV_Error(Error::StsBadArg, "descriptorSize must be 1,2, 4, 8, 16, 32, or 64"); + int bytes = fn["descriptorSize"]; + setBytes(bytes); } - bytes_ = dSize; + if (!fn["rotationInvariance"].empty()) + fn["rotationInvariance"] >> rotationInvariance_; + if (!fn["half_ssd_size"].empty()) + fn["half_ssd_size"] >> half_ssd_size_; + if (!fn["sigma"].empty()) + fn["sigma"] >> sigma_; } void LATCHDescriptorExtractorImpl::write(FileStorage& fs) const { - fs << "descriptorSize" << bytes_; + if ( fs.isOpened() ) + { + fs << "name" << getDefaultName(); + fs << "descriptorSize" << bytes_; + fs << "rotationInvariance" << rotationInvariance_; + fs << "half_ssd_size" << half_ssd_size_; + fs << "sigma" << sigma_; + + } } void LATCHDescriptorExtractorImpl::compute(InputArray _image, diff --git a/modules/xfeatures2d/src/lucid.cpp b/modules/xfeatures2d/src/lucid.cpp index 51d468dc60..18a68b6db5 100644 --- a/modules/xfeatures2d/src/lucid.cpp +++ b/modules/xfeatures2d/src/lucid.cpp @@ -61,6 +61,27 @@ namespace cv { */ LUCIDImpl(const int lucid_kernel = 1, const int blur_kernel = 2); + void read( const FileNode& fn) CV_OVERRIDE; + void write( FileStorage& fs) const CV_OVERRIDE; + + /** + * @param lucid_kernel kernel for descriptor construction, where 1=3x3, 2=5x5, 3=7x7 and so forth + */ + void setLucidKernel(int lucid_kernel) CV_OVERRIDE { l_kernel = lucid_kernel; } + /** + * @return lucid kernel, where 1=3x3, 2=5x5, 3=7x7 and so forth + */ + int getLucidKernel() const CV_OVERRIDE { return l_kernel; } + + /** + * @param blur_kernel kernel for blurring image prior to descriptor construction, where 1=3x3, 2=5x5, 3=7x7 and so forth + */ + void setBlurKernel(int blur_kernel) CV_OVERRIDE { b_kernel = blur_kernel*2+1; } + /** + * @return blur kernel, where 1=3x3, 2=5x5, 3=7x7 and so forth + */ + int getBlurKernel() const CV_OVERRIDE { return (b_kernel-1)/2; } + /** returns the descriptor length */ virtual int descriptorSize() const CV_OVERRIDE; @@ -85,6 +106,28 @@ namespace cv { b_kernel = blur_kernel*2+1; } + void LUCIDImpl::read( const FileNode& fn) + { + // if node is empty, keep previous value + if (!fn["lucid_kernel"].empty()) + fn["lucid_kernel"] >> l_kernel; + if (!fn["blur_kernel"].empty()) + { + int _b_kernel; + fn["blur_kernel"] >> _b_kernel; + b_kernel = _b_kernel*2+1; + } + } + void LUCIDImpl::write( FileStorage& fs) const + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "lucid_kernel" << l_kernel; + fs << "blur_kernel" << (b_kernel-1)/2; + } + } + int LUCIDImpl::descriptorSize() const { return (l_kernel*2+1)*(l_kernel*2+1)*3; } @@ -143,5 +186,10 @@ namespace cv { if (_desc.needed()) sort(desc, _desc, SORT_EVERY_ROW | SORT_ASCENDING); } + + String LUCID::getDefaultName() const + { + return (Feature2D::getDefaultName() + ".LUCID"); + } } } // END NAMESPACE CV diff --git a/modules/xfeatures2d/src/msd.cpp b/modules/xfeatures2d/src/msd.cpp index 76292410b6..c63815af96 100644 --- a/modules/xfeatures2d/src/msd.cpp +++ b/modules/xfeatures2d/src/msd.cpp @@ -135,6 +135,72 @@ namespace cv { } + void setPatchRadius(int patch_radius) CV_OVERRIDE { m_patch_radius = patch_radius; } + int getPatchRadius() const CV_OVERRIDE { return m_patch_radius; } + + void setSearchAreaRadius(int search_area_radius) CV_OVERRIDE { m_search_area_radius = search_area_radius; } + int getSearchAreaRadius() const CV_OVERRIDE { return m_search_area_radius; } + + void setNmsRadius(int nms_radius) CV_OVERRIDE { m_nms_radius = nms_radius; } + int getNmsRadius() const CV_OVERRIDE { return m_nms_radius; } + + void setNmsScaleRadius(int nms_scale_radius) CV_OVERRIDE { m_nms_scale_radius = nms_scale_radius; } + int getNmsScaleRadius() const CV_OVERRIDE { return m_nms_scale_radius; } + + void setThSaliency(float th_saliency) CV_OVERRIDE { m_th_saliency = th_saliency; } + float getThSaliency() const CV_OVERRIDE { return m_th_saliency; } + + void setKNN(int kNN) CV_OVERRIDE { m_kNN = kNN; } + int getKNN() const CV_OVERRIDE { return m_kNN; } + + void setScaleFactor(float scale_factor) CV_OVERRIDE { m_scale_factor = scale_factor; } + float getScaleFactor() const CV_OVERRIDE { return m_scale_factor; } + + void setNScales(int n_scales) CV_OVERRIDE { m_n_scales = n_scales; } + int getNScales() const CV_OVERRIDE { return m_n_scales; } + + void setComputeOrientation(bool compute_orientation) CV_OVERRIDE { m_compute_orientation = compute_orientation; } + bool getComputeOrientation() const CV_OVERRIDE { return m_compute_orientation; } + + void read( const FileNode& fn) CV_OVERRIDE + { + // if node is empty, keep previous value + if (!fn["patch_radius"].empty()) + fn["patch_radius"] >> m_patch_radius; + if (!fn["search_area_radius"].empty()) + fn["search_area_radius"] >> m_search_area_radius; + if (!fn["nms_radius"].empty()) + fn["nms_radius"] >> m_nms_radius; + if (!fn["nms_scale_radius"].empty()) + fn["nms_scale_radius"] >> m_nms_scale_radius; + if (!fn["th_saliency"].empty()) + fn["th_saliency"] >> m_th_saliency; + if (!fn["kNN"].empty()) + fn["kNN"] >> m_kNN; + if (!fn["scale_factor"].empty()) + fn["scale_factor"] >> m_scale_factor; + if (!fn["n_scales"].empty()) + fn["n_scales"] >> m_n_scales; + if (!fn["compute_orientation"].empty()) + fn["compute_orientation"] >> m_compute_orientation; + } + void write( FileStorage& fs) const CV_OVERRIDE + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "patch_radius" << m_patch_radius; + fs << "search_area_radius" << m_search_area_radius; + fs << "nms_radius" << m_nms_radius; + fs << "nms_scale_radius" << m_nms_scale_radius; + fs << "th_saliency" << m_th_saliency; + fs << "kNN" << m_kNN; + fs << "scale_factor" << m_scale_factor; + fs << "n_scales" << m_n_scales; + fs << "compute_orientation" << m_compute_orientation; + } + } + void detect(InputArray _image, std::vector& keypoints, InputArray _mask) CV_OVERRIDE { m_mask = _mask.getMat(); @@ -713,5 +779,10 @@ namespace cv m_n_scales, m_compute_orientation); } + String MSDDetector::getDefaultName() const + { + return (Feature2D::getDefaultName() + ".MSD"); + } + } } diff --git a/modules/xfeatures2d/src/stardetector.cpp b/modules/xfeatures2d/src/stardetector.cpp index bcd4cccb61..c608045215 100644 --- a/modules/xfeatures2d/src/stardetector.cpp +++ b/modules/xfeatures2d/src/stardetector.cpp @@ -60,6 +60,24 @@ class StarDetectorImpl : public StarDetector int _lineThresholdBinarized=8, int _suppressNonmaxSize=5); + void setMaxSize(int _maxSize) CV_OVERRIDE { maxSize = _maxSize; } + int getMaxSize() const CV_OVERRIDE { return maxSize; } + + void setResponseThreshold(int _responseThreshold) CV_OVERRIDE { responseThreshold = _responseThreshold; } + int getResponseThreshold() const CV_OVERRIDE { return responseThreshold; } + + void setLineThresholdProjected(int _lineThresholdProjected) CV_OVERRIDE { lineThresholdProjected = _lineThresholdProjected; } + int getLineThresholdProjected() const CV_OVERRIDE { return lineThresholdProjected; } + + void setLineThresholdBinarized(int _lineThresholdBinarized) CV_OVERRIDE { lineThresholdBinarized = _lineThresholdBinarized; } + int getLineThresholdBinarized() const CV_OVERRIDE { return lineThresholdBinarized; } + + void setSuppressNonmaxSize(int _suppressNonmaxSize) CV_OVERRIDE { suppressNonmaxSize = _suppressNonmaxSize; } + int getSuppressNonmaxSize() const CV_OVERRIDE { return suppressNonmaxSize; } + + void read( const FileNode& fn ) CV_OVERRIDE; + void write( FileStorage& fs ) const CV_OVERRIDE; + void detect( InputArray image, std::vector& keypoints, InputArray mask=noArray() ) CV_OVERRIDE; protected: @@ -82,6 +100,38 @@ Ptr StarDetector::create(int _maxSize, _suppressNonmaxSize); } +void StarDetectorImpl::read( const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["maxSize"].empty()) + fn["maxSize"] >> maxSize; + if (!fn["responseThreshold"].empty()) + fn["responseThreshold"] >> responseThreshold; + if (!fn["lineThresholdProjected"].empty()) + fn["lineThresholdProjected"] >> lineThresholdProjected; + if (!fn["lineThresholdBinarized"].empty()) + fn["lineThresholdBinarized"] >> lineThresholdBinarized; + if (!fn["suppressNonmaxSize"].empty()) + fn["suppressNonmaxSize"] >> suppressNonmaxSize; +} +void StarDetectorImpl::write( FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "maxSize" << maxSize; + fs << "responseThreshold" << responseThreshold; + fs << "lineThresholdProjected" << lineThresholdProjected; + fs << "lineThresholdBinarized" << lineThresholdBinarized; + fs << "suppressNonmaxSize" << suppressNonmaxSize; + } +} + +String StarDetector::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".STAR"); +} + template static void computeIntegralImages( const Mat& matI, Mat& matS, Mat& matT, Mat& _FT, diff --git a/modules/xfeatures2d/src/surf.cpp b/modules/xfeatures2d/src/surf.cpp index 7629af7a36..ea04e9e03d 100644 --- a/modules/xfeatures2d/src/surf.cpp +++ b/modules/xfeatures2d/src/surf.cpp @@ -1020,6 +1020,11 @@ Ptr SURF::create(double _threshold, int _nOctaves, int _nOctaveLayers, boo return makePtr(_threshold, _nOctaves, _nOctaveLayers, _extended, _upright); } +String SURF::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".SURF"); +} + #else // ! #ifdef OPENCV_ENABLE_NONFREE Ptr SURF::create(double, int, int, bool, bool) diff --git a/modules/xfeatures2d/src/surf.hpp b/modules/xfeatures2d/src/surf.hpp index b507d8f877..3293850ec9 100644 --- a/modules/xfeatures2d/src/surf.hpp +++ b/modules/xfeatures2d/src/surf.hpp @@ -23,6 +23,33 @@ class SURF_Impl : public SURF int nOctaves = 4, int nOctaveLayers = 2, bool extended = true, bool upright = false); + void read( const FileNode& fn) + { + // if node is empty, keep previous value + if (!fn["hessianThreshold"].empty()) + fn["hessianThreshold"] >> hessianThreshold; + if (!fn["extended"].empty()) + fn["extended"] >> extended; + if (!fn["upright"].empty()) + fn["upright"] >> upright; + if (!fn["nOctaves"].empty()) + fn["nOctaves"] >> nOctaves; + if (!fn["nOctaveLayers"].empty()) + fn["nOctaveLayers"] >> nOctaveLayers; + } + void write( FileStorage& fs) const + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "hessianThreshold" << hessianThreshold; + fs << "extended" << extended; + fs << "upright" << upright; + fs << "nOctaves" << nOctaves; + fs << "nOctaveLayers" << nOctaveLayers; + } + } + //! returns the descriptor size in float's (64 or 128) CV_WRAP int descriptorSize() const CV_OVERRIDE; diff --git a/modules/xfeatures2d/src/vgg.cpp b/modules/xfeatures2d/src/vgg.cpp index 506f5d0c13..19184979b4 100644 --- a/modules/xfeatures2d/src/vgg.cpp +++ b/modules/xfeatures2d/src/vgg.cpp @@ -84,6 +84,9 @@ class VGG_Impl CV_FINAL : public VGG // destructor virtual ~VGG_Impl() CV_OVERRIDE; + void read( const FileNode& fn ) CV_OVERRIDE; + void write( FileStorage& fs ) const CV_OVERRIDE; + // returns the descriptor length in bytes virtual int descriptorSize() const CV_OVERRIDE { return m_descriptor_size; } @@ -529,6 +532,38 @@ VGG_Impl::~VGG_Impl() { } +void VGG_Impl::read (const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["isigma"].empty()) + fn["isigma"] >> m_isigma; + if (!fn["scale_factor"].empty()) + fn["scale_factor"] >> m_scale_factor; + if (!fn["img_normalize"].empty()) + fn["img_normalize"] >> m_img_normalize; + if (!fn["use_scale_orientation"].empty()) + fn["use_scale_orientation"] >> m_use_scale_orientation; + if (!fn["dsc_normalize"].empty()) + fn["dsc_normalize"] >> m_dsc_normalize; +} + +void VGG_Impl::write (FileStorage& fs) const +{ + if ( fs.isOpened() ) + { + fs << "isigma" << m_isigma; + fs << "scale_factor" << m_scale_factor; + fs << "img_normalize" << m_img_normalize; + fs << "use_scale_orientation" << m_use_scale_orientation; + fs << "dsc_normalize" << m_dsc_normalize; + } +} + +String VGG::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".VGG"); +} + #endif Ptr VGG::create( int desc, float isigma, bool img_normalize, bool use_scale_orientation, From 11b056b26387dd92eba0477fb06455657927e59b Mon Sep 17 00:00:00 2001 From: Zhanbo Huang Date: Mon, 24 Oct 2022 22:44:24 +0800 Subject: [PATCH 38/99] Merge pull request #3322 from MisakiCoca:ellipse --- modules/ximgproc/README.md | 1 + modules/ximgproc/doc/ximgproc.bib | 11 + modules/ximgproc/include/opencv2/ximgproc.hpp | 1 + .../opencv2/ximgproc/find_ellipses.hpp | 38 + modules/ximgproc/perf/perf_find_ellipses.cpp | 27 + modules/ximgproc/samples/find_ellipses.cpp | 50 + modules/ximgproc/src/find_ellipses.cpp | 1988 +++++++++++++++++ modules/ximgproc/test/test_find_ellipses.cpp | 43 + 8 files changed, 2159 insertions(+) create mode 100644 modules/ximgproc/include/opencv2/ximgproc/find_ellipses.hpp create mode 100644 modules/ximgproc/perf/perf_find_ellipses.cpp create mode 100644 modules/ximgproc/samples/find_ellipses.cpp create mode 100644 modules/ximgproc/src/find_ellipses.cpp create mode 100644 modules/ximgproc/test/test_find_ellipses.cpp diff --git a/modules/ximgproc/README.md b/modules/ximgproc/README.md index 59e744e023..d4ec453d1d 100644 --- a/modules/ximgproc/README.md +++ b/modules/ximgproc/README.md @@ -16,3 +16,4 @@ Extended Image Processing - Pei&Lin Normalization - Ridge Detection Filter - Binary morphology on run-length encoded images +- Ellipse Detector diff --git a/modules/ximgproc/doc/ximgproc.bib b/modules/ximgproc/doc/ximgproc.bib index c521b69aa9..f081f54d3c 100644 --- a/modules/ximgproc/doc/ximgproc.bib +++ b/modules/ximgproc/doc/ximgproc.bib @@ -389,3 +389,14 @@ @article{loke2021accelerated year={2021}, publisher={Springer} } + +@article{jia2017fast, + title={A fast ellipse detector using projective invariant pruning}, + author={Jia, Qi and Fan, Xin and Luo, Zhongxuan and Song, Lianbo and Qiu, Tie}, + journal={IEEE Transactions on Image Processing}, + volume={26}, + number={8}, + pages={3665--3679}, + year={2017}, + publisher={IEEE} +} \ No newline at end of file diff --git a/modules/ximgproc/include/opencv2/ximgproc.hpp b/modules/ximgproc/include/opencv2/ximgproc.hpp index d4d5387fb6..dca0443c0a 100644 --- a/modules/ximgproc/include/opencv2/ximgproc.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc.hpp @@ -62,6 +62,7 @@ #include "ximgproc/edgepreserving_filter.hpp" #include "ximgproc/color_match.hpp" #include "ximgproc/radon_transform.hpp" +#include "ximgproc/find_ellipses.hpp" /** @defgroup ximgproc Extended Image Processing diff --git a/modules/ximgproc/include/opencv2/ximgproc/find_ellipses.hpp b/modules/ximgproc/include/opencv2/ximgproc/find_ellipses.hpp new file mode 100644 index 0000000000..2251e52bcc --- /dev/null +++ b/modules/ximgproc/include/opencv2/ximgproc/find_ellipses.hpp @@ -0,0 +1,38 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef __OPENCV_FIND_ELLIPSES_HPP__ +#define __OPENCV_FIND_ELLIPSES_HPP__ + +#include + +namespace cv { +namespace ximgproc { + +//! @addtogroup ximgproc +//! @{ + +/** +@brief Finds ellipses fastly in an image using projective invariant pruning. +* +* The function detects ellipses in images using projective invariant pruning. +* For more details about this implementation, please see +* [JIA2017FAST] Jia, Qi et al, (2017). +* A Fast Ellipse Detector using Projective Invariant Pruning. IEEE Transactions on Image Processing. +* +@param image input image, could be gray or color. +@param ellipses output vector of found ellipses. each vector is encoded as five float $x, y, a, b, radius, score$. +@param scoreThreshold float, the threshold of ellipse score. +@param reliabilityThreshold float, the threshold of reliability. +@param centerDistanceThreshold float, the threshold of center distance. +*/ +CV_EXPORTS_W void findEllipses( + InputArray image, OutputArray ellipses, + float scoreThreshold = 0.7f, float reliabilityThreshold = 0.5f, + float centerDistanceThreshold = 0.05f +); +//! @} ximgproc +} +} +#endif \ No newline at end of file diff --git a/modules/ximgproc/perf/perf_find_ellipses.cpp b/modules/ximgproc/perf/perf_find_ellipses.cpp new file mode 100644 index 0000000000..0c948fe021 --- /dev/null +++ b/modules/ximgproc/perf/perf_find_ellipses.cpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { + +typedef tuple FindEllipsesTestParam; +typedef TestBaseWithParam FindEllipsesTest; + +PERF_TEST_P(FindEllipsesTest, perf, Combine(SZ_TYPICAL, Values(CV_8U), Values(1, 3))) +{ + FindEllipsesTestParam params = GetParam(); + Size sz = get<0>(params); + int matType = get<1>(params); + int srcCn = get<2>(params); + + Mat src(sz, CV_MAKE_TYPE(matType, srcCn)); + Mat dst(sz, CV_32FC(6)); + + declare.in(src, WARMUP_RNG).out(dst); + + TEST_CYCLE() findEllipses(src, dst, 0.7f, 0.5f, 0.05f); + + SANITY_CHECK_NOTHING(); +} +}} // namespace \ No newline at end of file diff --git a/modules/ximgproc/samples/find_ellipses.cpp b/modules/ximgproc/samples/find_ellipses.cpp new file mode 100644 index 0000000000..783c9f1a2b --- /dev/null +++ b/modules/ximgproc/samples/find_ellipses.cpp @@ -0,0 +1,50 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include +#include +#include +#include +#include + +using namespace cv; + +int main() { + + // load image + Mat img = imread(samples::findFile("stuff.jpg"), IMREAD_COLOR); + + // check if image is loaded + if (img.empty()) { + std::cout << "fail to open image" << std::endl; + return EXIT_FAILURE; + } + + // create output array + std::vector ells; + + // test ellipse detection + cv::ximgproc::findEllipses(img, ells, 0.4f, 0.7f, 0.02f); + + // print output + for (unsigned i = 0; i < ells.size(); i++) { + Vec6f ell = ells[i]; + std::cout << ell << std::endl; + Scalar color(0, 0, 255); + // draw ellipse on image + ellipse( + img, + Point(cvRound(ell[0]), cvRound(ell[1])), + Size(cvRound(ell[2]), cvRound(ell[3])), + ell[5] * 180 / CV_PI, 0.0, 360.0, color, 3 + ); + } + + // show image + imshow("result", img); + waitKey(); + + // end + return 0; +} diff --git a/modules/ximgproc/src/find_ellipses.cpp b/modules/ximgproc/src/find_ellipses.cpp new file mode 100644 index 0000000000..62bf074f7f --- /dev/null +++ b/modules/ximgproc/src/find_ellipses.cpp @@ -0,0 +1,1988 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" +#include +#include +#include + +namespace cv { +namespace ximgproc { + +// typedef +typedef std::vector VP; +typedef std::vector VVP; +typedef unsigned int uint; + +// ellipse format +struct Ellipse { + Point2f center; + float a, b; + float radius; + float score; + + Ellipse() { + center = Point2f(0, 0); + a = 0.f, b = 0.f; + radius = 0.f, score = 0.f; + }; + + Ellipse(Point2f _center, float _a, float _b, float _radius, + float _score = 0.f) { + center = _center; + this->a = _a, this->b = _b; + this->radius = _radius, this->score = _score; + }; + + Ellipse(float _center_x, float _center_y, float _a, float _b, float _radius, + float _score = 0.f) { + center = Point2f(_center_x, _center_y); + this->a = _a, this->b = _b; + this->radius = _radius, this->score = _score; + }; + + Ellipse(const Ellipse &other) { + center = other.center; + a = other.a, b = other.b; + radius = other.radius, score = other.score; + }; + + bool operator<(const Ellipse &other) const { + if (score == other.score) { + float lhs_e = b / a; + float rhs_e = other.b / other.a; + if (lhs_e == rhs_e) + return false; + return lhs_e > rhs_e; + } + return score > other.score; + }; + + virtual ~Ellipse() = default; +}; + +static int inline signal(float val) { + return val > 0.f ? 1 : -1; +} + +static bool sortPoint(const Point &lhs, const Point &rhs) { + if (lhs.x == rhs.x) { + return lhs.y < rhs.y; + } + return lhs.x < rhs.x; +} + +static Point2f lineCrossPoint(Point2f l1p1, Point2f l1p2, Point2f l2p1, Point2f l2p2) { + Point2f crossPoint; + float k1, k2, b1, b2; + if (l1p1.x == l1p2.x && l2p1.x == l2p2.x) { + crossPoint = Point2f(0, 0); + return crossPoint; + } + if (l1p1.x == l1p2.x) { + crossPoint.x = l1p1.x; + k2 = (l2p2.y - l2p1.y) / (l2p2.x - l2p1.x); + b2 = l2p1.y - k2 * l2p1.x; + crossPoint.y = k2 * crossPoint.x + b2; + return crossPoint; + } + if (l2p1.x == l2p2.x) { + crossPoint.x = l2p1.x; + k2 = (l1p2.y - l1p1.y) / (l1p2.x - l1p1.x); + b2 = l1p1.y - k2 * l1p1.x; + crossPoint.y = k2 * crossPoint.x + b2; + return crossPoint; + } + + k1 = (l1p2.y - l1p1.y) / (l1p2.x - l1p1.x); + k2 = (l2p2.y - l2p1.y) / (l2p2.x - l2p1.x); + b1 = l1p1.y - k1 * l1p1.x; + b2 = l2p1.y - k2 * l2p1.x; + if (k1 == k2) + crossPoint = Point2f(0, 0); + else { + crossPoint.x = (b2 - b1) / (k1 - k2); + crossPoint.y = k1 * crossPoint.x + b1; + } + return crossPoint; +} + +static void pointToMat(Point2f p1, Point2f p2, Mat& mat) { + mat.at(0, 0) = p1.x; + mat.at(0, 1) = p1.y; + mat.at(1, 0) = p2.x; + mat.at(1, 1) = p2.y; +} + +static float valueOfPoints(Point2f p3, Point2f p2, Point2f p1, Point2f p4, Point2f p5, Point2f p6) { + float result = 1; + + Point2f v = lineCrossPoint(p1, p2, p3, p4); + Point2f w = lineCrossPoint(p5, p6, p3, p4); + Point2f u = lineCrossPoint(p5, p6, p1, p2); + + Mat B = Mat(2, 2, CV_32F); + Mat C = Mat(2, 2, CV_32F); + + pointToMat(u, v, B); + pointToMat(p1, p2, C); + Mat A = C * B.inv(); + result *= + A.at(0, 0) * A.at(1, 0) / (A.at(0, 1) * A.at(1, 1)); + + pointToMat(p3, p4, C); + pointToMat(v, w, B); + A = C * B.inv(); + result *= + A.at(0, 0) * A.at(1, 0) / (A.at(0, 1) * A.at(1, 1)); + + pointToMat(p5, p6, C); + pointToMat(w, u, B); + A = C * B.inv(); + result *= + A.at(0, 0) * A.at(1, 0) / (A.at(0, 1) * A.at(1, 1)); + return result; +} + +static float inline pointDistance2(const Point &A, const Point &B) { + return float(((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y))); +} + +static float getMinAnglePI(float alpha, float beta) { + auto pi = float(CV_PI); + auto pi2 = float(2.0 * CV_PI); + + // normalize data in [0, 2*pi] + float a = fmod(alpha + pi2, pi2); + float b = fmod(beta + pi2, pi2); + + // normalize data in [0, pi] + if (a > pi) + a -= pi; + if (b > pi) + b -= pi; + + if (a > b) + swap(a, b); + + float diff1 = b - a; + float diff2 = pi - diff1; + + return min(diff1, diff2); +} + +// ellipse data +struct EllipseData { + bool isValid; + float ta, tb; + float ra, rb; + Point2f Ma, Mb, Cab; + std::vector Sa, Sb; +}; + +// implement of ellipse detector +class EllipseDetectorImpl { + + // preprocessing - Gaussian Smooth. + Size _kernelSize; // size of the Gaussian filter + double _sigma; // sigma of the Gaussian filter + + // selection strategy - Step 1 - Discard noisy or straight arcs. + int _minEdgeLength; // minimum edge size + float _minOrientedRectSide; // minimum size of the oriented bounding box containing the arc + + // selection strategy - Step 2 - Remove according to mutual convexity. + float _positionThreshold; + + // selection strategy - Step 3 - Number of points considered for slope estimation when estimating the center. + unsigned _uNs; // find at most Ns parallel chords + + // selection strategy - Step 3 - Discard pairs of arcs if their estimated center is not close enough + float _maxCenterDistance; // maximum distance in pixel between 2 center points + float _maxCenterDistance2; // pow(_maxCenterDistance, 2) + + // validation - Points within a threshold are considered to lie on the ellipse contour. + float _maxDistanceToEllipseContour; // maximum distance between a point and the contour + + // validation - Assign a score. + float _minScore; // minimum score to confirm a detection + float _minReliability; // minimum auxiliary score to confirm a detection + + // auxiliary variables + Size _imgSize; // input image size + + int ACC_N_SIZE, ACC_R_SIZE, ACC_A_SIZE; // size of accumulator + int *accN, *accR, *accA; // pointer to accumulator + +public: + float countsOfFindEllipse; + float countsOfGetFastCenter; + + EllipseDetectorImpl(); + + ~EllipseDetectorImpl() = default; + + // detect the ellipses in the gray image + void detect(Mat1b &image, std::vector &ellipses); + + // set the parameters of the detector + void setParameters(float maxCenterDistance, float minScore, float minReliability); + +private: + // keys for hash table + static const ushort PAIR_12 = 0x00; + static const ushort PAIR_23 = 0x01; + static const ushort PAIR_34 = 0x02; + static const ushort PAIR_14 = 0x03; + + static uint inline generateKey(uchar pair, ushort u, ushort v); + + void preProcessing(Mat1b &image, Mat1b &dp, Mat1b &dn); + + void clusterEllipses(std::vector &ellipses); + + static float + getMedianSlope(std::vector &med, Point2f ¢ers, std::vector &slopes); + + void getFastCenter(std::vector &e1, std::vector &e2, EllipseData &data); + + void detectEdges13(Mat1b &DP, VVP &points_1, VVP &points_3); + + void detectEdges24(Mat1b &DN, VVP &points_2, VVP &points_4); + + void + findEllipses(Point2f ¢er, VP &edge_i, VP &edge_j, VP &edge_k, EllipseData &data_ij, + EllipseData &data_ik, std::vector &ellipses); + + static Point2f getCenterCoordinates(EllipseData &data_ij, EllipseData &data_ik); + + void + getTriplets124(VVP &pi, VVP &pj, VVP &pk, std::unordered_map &data, + std::vector &ellipses); + + void + getTriplets231(VVP &pi, VVP &pj, VVP &pk, std::unordered_map &data, + std::vector &ellipses); + + void + getTriplets342(VVP &pi, VVP &pj, VVP &pk, std::unordered_map &data, + std::vector &ellipses); + + void + getTriplets413(VVP &pi, VVP &pj, VVP &pk, std::unordered_map &data, + std::vector &ellipses); + + static void labeling(Mat1b &image, VVP &segments, int minLength); +}; + +EllipseDetectorImpl::EllipseDetectorImpl() { + // Default Parameters Settings + _kernelSize = Size(5, 5); + _sigma = 1.0; + _positionThreshold = 1.0f; + _maxCenterDistance = 100.0f * 0.05f; + _maxCenterDistance2 = _maxCenterDistance * _maxCenterDistance; + _minEdgeLength = 16; + _minOrientedRectSide = 3.0f; + _maxDistanceToEllipseContour = 0.1f; + _minScore = 0.7f; + _minReliability = 0.5; + _uNs = 16; +} + +void EllipseDetectorImpl::setParameters(float maxCenterDistance, float minScore, + float minReliability) { + _maxCenterDistance = maxCenterDistance; + _minScore = minScore; + _minReliability = minReliability; + + _maxCenterDistance2 = _maxCenterDistance * _maxCenterDistance; +} + +uint inline EllipseDetectorImpl::generateKey(uchar pair, ushort u, ushort v) { + return (pair << 30) + (u << 15) + v; +} + +float EllipseDetectorImpl::getMedianSlope(std::vector &med, Point2f ¢ers, + std::vector &slopes) { + // med : vector of points + // centers : centroid of the points in med + // slopes : vector of the slopes + + unsigned pointCount = med.size(); + // CV_Assert(pointCount >= 2); + + unsigned halfSize = pointCount >> 1; + unsigned quarterSize = halfSize >> 1; + + std::vector xx, yy; + slopes.reserve(halfSize); + xx.reserve(pointCount); + yy.reserve(pointCount); + + for (unsigned i = 0; i < halfSize; i++) { + Point2f &p1 = med[i]; + Point2f &p2 = med[halfSize + i]; + + xx.push_back(p1.x); + xx.push_back(p2.x); + yy.push_back(p1.y); + yy.push_back(p2.y); + + float den = (p2.x - p1.x); + float num = (p2.y - p1.y); + + if (den == 0) den = 0.00001f; + + slopes.push_back(num / den); + } + + nth_element(slopes.begin(), slopes.begin() + quarterSize, slopes.end()); + nth_element(xx.begin(), xx.begin() + halfSize, xx.end()); + nth_element(yy.begin(), yy.begin() + halfSize, yy.end()); + centers.x = xx[halfSize]; + centers.y = yy[halfSize]; + + return slopes[quarterSize]; +} + +void EllipseDetectorImpl::getFastCenter(std::vector &e1, std::vector &e2, + EllipseData &data) { + countsOfGetFastCenter++; + data.isValid = true; + + auto size_1 = unsigned(e1.size()); + auto size_2 = unsigned(e2.size()); + + unsigned hsize_1 = size_1 >> 1; + unsigned hsize_2 = size_2 >> 1; + + Point &med1 = e1[hsize_1]; + Point &med2 = e2[hsize_2]; + + Point2f M12, M34; + float q2, q4; + + { + auto dx_ref = float(e1[0].x - med2.x); + auto dy_ref = float(e1[0].y - med2.y); + + if (dx_ref == 0) + dx_ref = 0.00001f; + + float m_ref = dy_ref / dx_ref; + data.ra = m_ref; + + // find points with same slope as reference + std::vector med; + med.reserve(hsize_2); + + unsigned minPoints = (_uNs < hsize_2) ? _uNs : hsize_2; + + std::vector indexes(minPoints); + if (_uNs < hsize_2) { + unsigned iSzBin = hsize_2 / unsigned(_uNs); + unsigned iIdx = hsize_2 + (iSzBin / 2); + + for (unsigned i = 0; i < _uNs; i++) { + indexes[i] = iIdx; + iIdx += iSzBin; + } + } else + iota(indexes.begin(), indexes.end(), hsize_2); + + for (uint ii = 0; ii < minPoints; ii++) { + uint i = indexes[ii]; + + auto x1 = float(e2[i].x); + auto y1 = float(e2[i].y); + + uint begin = 0; + uint end = size_1 - 1; + + auto xb = float(e1[begin].x); + auto yb = float(e1[begin].y); + float res_begin = ((xb - x1) * dy_ref) - ((yb - y1) * dx_ref); + int sign_begin = signal(res_begin); + if (sign_begin == 0) { + med.emplace_back((xb + x1) * 0.5f, (yb + y1) * 0.5f); + continue; + } + + auto xe = float(e1[end].x); + auto ye = float(e1[end].y); + float res_end = ((xe - x1) * dy_ref) - ((ye - y1) * dx_ref); + int sign_end = signal(res_end); + if (sign_end == 0) { + med.emplace_back((xe + x1) * 0.5f, (ye + y1) * 0.5f); + continue; + } + + if ((sign_begin + sign_end) != 0) + continue; + + + // search parallel arc + uint j = (begin + end) >> 1; + while (end - begin > 2) { + auto x2 = float(e1[j].x); + auto y2 = float(e1[j].y); + float res = ((x2 - x1) * dy_ref) - ((y2 - y1) * dx_ref); + int sign_res = signal(res); + + if (sign_res == 0) { + med.emplace_back((x2 + x1) * 0.5f, (y2 + y1) * 0.5f); + break; + } + + if (sign_res + sign_begin == 0) { + sign_end = sign_res; + end = j; + } else { + sign_begin = sign_res; + begin = j; + } + j = (begin + end) >> 1; + } + + med.emplace_back((e1[j].x + x1) * 0.5f, (e1[j].y + y1) * 0.5f); + } + + if (med.size() < 2) { + data.isValid = false; + return; + } + + q2 = getMedianSlope(med, M12, data.Sa); + } + + { + auto dx_ref = float(med1.x - e2[0].x); + auto dy_ref = float(med1.y - e2[0].y); + + if (dx_ref == 0) + dx_ref = 0.00001f; + + float m_ref = dy_ref / dx_ref; + data.rb = m_ref; + + // find points with same slope as reference + std::vector med; + med.reserve(hsize_1); + + uint minPoints = (_uNs < hsize_1) ? _uNs : hsize_1; + + std::vector indexes(minPoints); + if (_uNs < hsize_1) { + unsigned iSzBin = hsize_1 / unsigned(_uNs); + unsigned iIdx = hsize_1 + (iSzBin / 2); + + for (unsigned i = 0; i < _uNs; i++) { + indexes[i] = iIdx; + iIdx += iSzBin; + } + } else + iota(indexes.begin(), indexes.end(), hsize_1); + + + for (uint ii = 0; ii < minPoints; ii++) { + uint i = indexes[ii]; + + auto x1 = float(e1[i].x); + auto y1 = float(e1[i].y); + + uint begin = 0; + uint end = size_2 - 1; + + auto xb = float(e2[begin].x); + auto yb = float(e2[begin].y); + float res_begin = ((xb - x1) * dy_ref) - ((yb - y1) * dx_ref); + int sign_begin = signal(res_begin); + if (sign_begin == 0) { + med.emplace_back((xb + x1) * 0.5f, (yb + y1) * 0.5f); + continue; + } + + auto xe = float(e2[end].x); + auto ye = float(e2[end].y); + float res_end = ((xe - x1) * dy_ref) - ((ye - y1) * dx_ref); + int sign_end = signal(res_end); + if (sign_end == 0) { + med.emplace_back((xe + x1) * 0.5f, (ye + y1) * 0.5f); + continue; + } + + if ((sign_begin + sign_end) != 0) + continue; + + uint j = (begin + end) >> 1; + + while (end - begin > 2) { + auto x2 = float(e2[j].x); + auto y2 = float(e2[j].y); + float res = ((x2 - x1) * dy_ref) - ((y2 - y1) * dx_ref); + int sign_res = signal(res); + + if (sign_res == 0) { + med.emplace_back((x2 + x1) * 0.5f, (y2 + y1) * 0.5f); + break; + } + + if (sign_res + sign_begin == 0) { + sign_end = sign_res; + end = j; + } else { + sign_begin = sign_res; + begin = j; + } + j = (begin + end) >> 1; + } + + med.emplace_back((e2[j].x + x1) * 0.5f, (e2[j].y + y1) * 0.5f); + } + + if (med.size() < 2) { + data.isValid = false; + return; + } + q4 = getMedianSlope(med, M34, data.Sb); + } + + if (q2 == q4) { + data.isValid = false; + return; + } + + float invDen = 1 / (q2 - q4); + data.Cab.x = (M34.y - q4 * M34.x - M12.y + q2 * M12.x) * invDen; + data.Cab.y = (q2 * M34.y - q4 * M12.y + q2 * q4 * (M12.x - M34.x)) * invDen; + data.ta = q2; + data.tb = q4; + data.Ma = M12; + data.Mb = M34; +} + +Point2f +EllipseDetectorImpl::getCenterCoordinates(EllipseData &data_ij, EllipseData &data_ik) { + float xx[7]; + float yy[7]; + + xx[0] = data_ij.Cab.x; + xx[1] = data_ik.Cab.x; + yy[0] = data_ij.Cab.y; + yy[1] = data_ik.Cab.y; + + { + // 1-1 + float q2 = data_ij.ta; + float q4 = data_ik.ta; + Point2f &M12 = data_ij.Ma; + Point2f &M34 = data_ik.Ma; + + float invDen = 1 / (q2 - q4); + xx[2] = (M34.y - q4 * M34.x - M12.y + q2 * M12.x) * invDen; + yy[2] = (q2 * M34.y - q4 * M12.y + q2 * q4 * (M12.x - M34.x)) * invDen; + } + + { + // 1-2 + float q2 = data_ij.ta; + float q4 = data_ik.tb; + Point2f &M12 = data_ij.Ma; + Point2f &M34 = data_ik.Mb; + + float invDen = 1 / (q2 - q4); + xx[3] = (M34.y - q4 * M34.x - M12.y + q2 * M12.x) * invDen; + yy[3] = (q2 * M34.y - q4 * M12.y + q2 * q4 * (M12.x - M34.x)) * invDen; + } + + { + // 2-2 + float q2 = data_ij.tb; + float q4 = data_ik.tb; + Point2f &M12 = data_ij.Mb; + Point2f &M34 = data_ik.Mb; + + float invDen = 1 / (q2 - q4); + xx[4] = (M34.y - q4 * M34.x - M12.y + q2 * M12.x) * invDen; + yy[4] = (q2 * M34.y - q4 * M12.y + q2 * q4 * (M12.x - M34.x)) * invDen; + } + + { + // 2-1 + float q2 = data_ij.tb; + float q4 = data_ik.ta; + Point2f &M12 = data_ij.Mb; + Point2f &M34 = data_ik.Ma; + + float invDen = 1 / (q2 - q4); + xx[5] = (M34.y - q4 * M34.x - M12.y + q2 * M12.x) * invDen; + yy[5] = (q2 * M34.y - q4 * M12.y + q2 * q4 * (M12.x - M34.x)) * invDen; + } + + xx[6] = (xx[0] + xx[1]) * 0.5f; + yy[6] = (yy[0] + yy[1]) * 0.5f; + + + // median + std::nth_element(xx, xx + 3, xx + 7); + std::nth_element(yy, yy + 3, yy + 7); + float xc = xx[3]; + float yc = yy[3]; + + return {xc, yc}; +} + + +void EllipseDetectorImpl::labeling(Mat1b &image, VVP &segments, int minLength) { + const int RG_STACK_SIZE = 2048; + int stackInt[RG_STACK_SIZE]; + Point stackPoint[RG_STACK_SIZE]; + + Mat_ image_clone = image.clone(); + Size imgSize = image.size(); + int h = imgSize.height, w = imgSize.width; + + Point point; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + if ((image_clone(y, x)) != 0) { + // for each point + int curInt = 0; + int i = x + y * w; + stackInt[curInt] = i; + curInt++; + + // clear point list + int curPoint = 0; + while (curInt > 0) { + curInt--; + i = stackInt[curInt]; + int x2 = i % w; + int y2 = i / w; + + point.x = x2; + point.y = y2; + + if ((image_clone(y2, x2))) { + stackPoint[curPoint] = point; + curPoint++; + image_clone(y2, x2) = 0; + } + + // 4 directions + if (x2 > 0 && (image_clone(y2, x2 - 1) != 0)) { + stackInt[curInt] = i - 1; + curInt++; + } + if (y2 > 0 && (image_clone(y2 - 1, x2) != 0)) { + stackInt[curInt] = i - w; + curInt++; + } + if (y2 < h - 1 && (image_clone(y2 + 1, x2) != 0)) { + stackInt[curInt] = i + w; + curInt++; + } + if (x2 < w - 1 && (image_clone(y2, x2 + 1) != 0)) { + stackInt[curInt] = i + 1; + curInt++; + } + + // 8 directions + if (x2 > 0 && y2 > 0 && (image_clone(y2 - 1, x2 - 1) != 0)) { + stackInt[curInt] = i - w - 1; + curInt++; + } + if (x2 > 0 && y2 < h - 1 && (image_clone(y2 + 1, x2 - 1) != 0)) { + stackInt[curInt] = i + w - 1; + curInt++; + } + if (x2 < w - 1 && y2 > 0 && (image_clone(y2 - 1, x2 + 1) != 0)) { + stackInt[curInt] = i - w + 1; + curInt++; + } + if (x2 < w - 1 && y2 < h - 1 && (image_clone(y2 + 1, x2 + 1) != 0)) { + stackInt[curInt] = i + w + 1; + curInt++; + } + } + + if (curPoint >= minLength) { + std::vector component; + component.reserve(curPoint); + for (int j = 0; j < curPoint; j++) + component.push_back(stackPoint[j]); + segments.push_back(component); + } + } + } + } +} + +void EllipseDetectorImpl::detectEdges13(Mat1b &DP, VVP &points_1, VVP &points_3) { + // vector of connected edge points + VVP contours; + int countEdges = 0; + // labeling 8-connected edge points, discarding edge too small + labeling(DP, contours, _minEdgeLength); // label point on the same arc + int contourSize = int(contours.size()); + + // for each edge + for (int i = 0; i < contourSize; i++) { + VP &edgeSegment = contours[i]; + + // selection strategy - constraint on axes aspect ratio + RotatedRect oriented = minAreaRect(edgeSegment); + float orMin = min(oriented.size.width, oriented.size.height); + + if (orMin < _minOrientedRectSide) { + countEdges++; + continue; + } + + // order edge points of the same arc + sort(edgeSegment.begin(), edgeSegment.end(), sortPoint); + int edgeSegmentSize = unsigned(edgeSegment.size()); + + // get extrema of the arc + Point &left = edgeSegment[0]; + Point &right = edgeSegment[edgeSegmentSize - 1]; + + // find convexity + int countTop = 0; + int lx = left.x; + for (int k = 1; k < edgeSegmentSize; ++k) { + if (edgeSegment[k].x == lx) + continue; + countTop += (edgeSegment[k].y - left.y); + lx = edgeSegment[k].x; + } + + int width = abs(right.x - left.x) + 1; + int height = abs(right.y - left.y) + 1; + int countBottom = (width * height) - edgeSegmentSize - countTop; + + if (countBottom > countTop) + points_1.push_back(edgeSegment); + else if (countBottom < countTop) + points_3.push_back(edgeSegment); + } +} + +void EllipseDetectorImpl::detectEdges24(Mat1b &DN, VVP &points_2, VVP &points_4) { + // vector of connected edge points + VVP contours; + int countEdges = 0; + // labeling 8-connected edge points, discarding edge too small + labeling(DN, contours, _minEdgeLength); // label point on the same arc + int contourSize = int(contours.size()); + + // for each edge + for (int i = 0; i < contourSize; i++) { + VP &edgeSegment = contours[i]; + + // selection strategy - constraint on axes aspect ratio + RotatedRect oriented = minAreaRect(edgeSegment); + float orMin = min(oriented.size.width, oriented.size.height); + + if (orMin < _minOrientedRectSide) { + countEdges++; + continue; + } + + // order edge points of the same arc + sort(edgeSegment.begin(), edgeSegment.end(), sortPoint); + int edgeSegmentSize = unsigned(edgeSegment.size()); + + // get extrema of the arc + Point &left = edgeSegment[0]; + Point &right = edgeSegment[edgeSegmentSize - 1]; + + // find convexity + int countBottom = 0; + int lx = left.x; + for (int k = 0; k < edgeSegmentSize; ++k) { + if (edgeSegment[k].x == lx) + continue; + countBottom += (left.y - edgeSegment[k].y); + lx = edgeSegment[k].x; + } + + int width = abs(right.x - left.x) + 1; + int height = abs(right.y - left.y) + 1; + int countTop = (width * height) - edgeSegmentSize - countBottom; + + if (countBottom > countTop) + points_2.push_back(edgeSegment); + else if (countBottom < countTop) + points_4.push_back(edgeSegment); + } +} + +#define T124 pjf,pjm,pjl,pif,pim,pil +#define T231 pil,pim,pif,pjf,pjm,pjl +#define T342 pif,pim,pil,pjf,pjm,pjl +#define T413 pif,pim,pil,pjl,pjm,pjf + +void EllipseDetectorImpl::getTriplets124(VVP &pi, VVP &pj, VVP &pk, + std::unordered_map &data, + std::vector &ellipses) { + // get arcs length + auto sz_i = ushort(pi.size()); + auto sz_j = ushort(pj.size()); + auto sz_k = ushort(pk.size()); + + // for each edge i + for (ushort i = 0; i < sz_i; i++) { + VP &edge_i = pi[i]; + auto sz_ei = ushort(edge_i.size()); + + Point &pif = edge_i[0]; + Point &pim = edge_i[sz_ei / 2]; + Point &pil = edge_i[sz_ei - 1]; + + // 1 -> reverse 1 + VP rev_i(edge_i.size()); + reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin()); + + // for each edge j + for (ushort j = 0; j < sz_j; ++j) { + VP &edge_j = pj[j]; + auto sz_ej = ushort(edge_j.size()); + + Point &pjf = edge_j[0]; + Point &pjm = edge_j[sz_ej / 2]; + Point &pjl = edge_j[sz_ej - 1]; + + // constraints on position + if (pjl.x > pif.x + _positionThreshold) + continue; + + // constraints on CNC + const float CNC_THRESHOLD = 0.3f; + if (fabs(valueOfPoints(T124) - 1) > CNC_THRESHOLD) + continue; + + uint key_ij = generateKey(PAIR_12, i, j); + + // for each edge k + for (ushort k = 0; k < sz_k; ++k) { + VP &edge_k = pk[k]; + auto sz_ek = ushort(edge_k.size()); + + Point &pkl = edge_k[sz_ek - 1]; + + // constraints on position + if (pkl.y < pil.y - _positionThreshold) + continue; + + uint key_ik = generateKey(PAIR_14, i, k); + + // find centers + EllipseData data_ij, data_ik; + + // if the data for the pair i-j have not been computed yet + if (data.count(key_ij) == 0) { + getFastCenter(edge_j, rev_i, data_ij); + // insert computed data in the hash table + data.insert(std::pair(key_ij, data_ij)); + } else { + // otherwise, just lookup the data in the hash table + data_ij = data.at(key_ij); + } + + // if the data for the pair i-k have not been computed yet + if (data.count(key_ik) == 0) { + getFastCenter(edge_i, edge_k, data_ik); + // insert computed data in the hash table + data.insert(std::pair(key_ik, data_ik)); + } else { + // otherwise, just lookup the data in the hash table + data_ik = data.at(key_ik); + } + + // invalid centers + if (!data_ij.isValid || !data_ik.isValid) + continue; + + // selection strategy - Step 3. + // the computed centers are not close enough + if (pointDistance2(data_ij.Cab, data_ik.Cab) > _maxCenterDistance2) + continue; + + // find ellipse parameters + // get the coordinates of the center (xc, yc) + Point2f center = getCenterCoordinates(data_ij, data_ik); + // find remaining parameters (A, B, rho) + findEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ellipses); + } + } + } +} + +void EllipseDetectorImpl::getTriplets231(VVP &pi, VVP &pj, VVP &pk, + std::unordered_map &data, + std::vector &ellipses) { + // get arc length + auto sz_i = ushort(pi.size()); + auto sz_j = ushort(pj.size()); + auto sz_k = ushort(pk.size()); + + // for each edge i + for (ushort i = 0; i < sz_i; i++) { + VP &edge_i = pi[i]; + auto sz_ei = ushort(edge_i.size()); + + Point &pif = edge_i[0]; + Point &pim = edge_i[sz_ei / 2]; + Point &pil = edge_i[sz_ei - 1]; + + // 2 -> reverse 2 + VP rev_i(edge_i.size()); + reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin()); + + // for each edge j + for (ushort j = 0; j < sz_j; ++j) { + VP &edge_j = pj[j]; + auto sz_ej = ushort(edge_j.size()); + + Point &pjf = edge_j[0]; + Point &pjm = edge_j[sz_ej / 2]; + Point &pjl = edge_j[sz_ej - 1]; + + // constraints on position + if (pjf.y < pif.y - _positionThreshold) + continue; + + // constraints on CNC + const float CNC_THRESHOLD = 0.3f; + if (fabs(valueOfPoints(T231) - 1) > CNC_THRESHOLD) + continue; + + // 3 -> reverse 3 + VP rev_j(edge_j.size()); + reverse_copy(edge_j.begin(), edge_j.end(), rev_j.begin()); + + uint key_ij = generateKey(PAIR_23, i, j); + + // for each edge k + for (ushort k = 0; k < sz_k; ++k) { + VP &edge_k = pk[k]; + + Point &pkf = edge_k[0]; + + // constraints on position + if (pkf.x < pil.x - _positionThreshold) + continue; + + uint key_ik = generateKey(PAIR_12, k, i); + + // find centers + EllipseData data_ij, data_ik; + + // if the data for the pair i-j have not been computed yet + if (data.count(key_ij) == 0) { + getFastCenter(rev_i, rev_j, data_ij); + // insert computed date in the hash table + data.insert(std::pair(key_ij, data_ij)); + } else { + // otherwise, just lookup the data in the hash table + data_ij = data.at(key_ij); + } + + // if the data for the pair i-k have not been computed yet + if (data.count(key_ik) == 0) { + // 1 -> reverse 1 + VP rev_k(edge_k.size()); + reverse_copy(edge_k.begin(), edge_k.end(), rev_k.begin()); + + getFastCenter(edge_i, rev_k, data_ik); + data.insert(std::pair(key_ik, data_ik)); + } else { + // otherwise, just lookup the data in the hash table + data_ik = data.at(key_ik); + } + + // invalid centers + if (!data_ij.isValid || !data_ik.isValid) + continue; + + // selection strategy - Step 3. + // the computed centers are not close enough + if (pointDistance2(data_ij.Cab, data_ik.Cab) > _maxCenterDistance2) + continue; + + // find ellipse parameters + // get the coordinates of the center (xc, yc) + Point2f center = getCenterCoordinates(data_ij, data_ik); + // find remaining parameters (A, B, rho) + findEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ellipses); + } + } + } +} + +void EllipseDetectorImpl::getTriplets342(VVP &pi, VVP &pj, VVP &pk, + std::unordered_map &data, + std::vector &ellipses) { + // get arcs length + auto sz_i = ushort(pi.size()); + auto sz_j = ushort(pj.size()); + auto sz_k = ushort(pk.size()); + + // for each edge i + for (ushort i = 0; i < sz_i; i++) { + VP &edge_i = pi[i]; + auto sz_ei = ushort(edge_i.size()); + + Point &pif = edge_i[0]; + Point &pim = edge_i[sz_ei / 2]; + Point &pil = edge_i[sz_ei - 1]; + + // 3 -> reverse 3 + VP rev_i(edge_i.size()); + reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin()); + + // for each edge j + for (ushort j = 0; j < sz_j; ++j) { + VP &edge_j = pj[j]; + auto sz_ej = ushort(edge_j.size()); + + Point &pjf = edge_j[0]; + Point &pjm = edge_j[sz_ej / 2]; + Point &pjl = edge_j[sz_ej - 1]; + + // constraints on position + if (pjf.x < pil.x - _positionThreshold) + continue; + + // constraints on CNC + const float CNC_THRESHOLD = 0.3f; + if (fabs(valueOfPoints(T342) - 1) > CNC_THRESHOLD) + continue; + + // 4 -> reverse 4 + VP rev_j(edge_j.size()); + reverse_copy(edge_j.begin(), edge_j.end(), rev_j.begin()); + + uint key_ij = generateKey(PAIR_34, i, j); + + // for each edge k + for (ushort k = 0; k < sz_k; ++k) { + VP &edge_k = pk[k]; + + Point &pkf = edge_k[0]; + + // constraints on position + if (pkf.y > pif.y + _positionThreshold) + continue; + + uint key_ik = generateKey(PAIR_23, k, i); + + // find centers + EllipseData data_ij, data_ik; + + // if the data for the pair i-j have not been computed yet + if (data.count(key_ij) == 0) { + getFastCenter(edge_i, rev_j, data_ij); + // insert computed data in the hash table + data.insert(std::pair(key_ij, data_ij)); + } else { + // otherwise, just lookup the data in the hash table + data_ij = data.at(key_ij); + } + + // if the data for the pair i-k have not been computed yet + if (data.count(key_ik) == 0) { + // 2 -> reverse 2 + VP rev_k(edge_k.size()); + reverse_copy(edge_k.begin(), edge_k.end(), rev_k.begin()); + + getFastCenter(rev_i, rev_k, data_ik); + data.insert(std::pair(key_ik, data_ik)); + } else { + // otherwise, just lookup the data in the hash table + data_ik = data.at(key_ik); + } + + // invalid centers + if (!data_ij.isValid || !data_ik.isValid) + continue; + + // selection strategy - Step 3. + // the computed centers are not close enough + if (pointDistance2(data_ij.Cab, data_ik.Cab) > _maxCenterDistance2) + continue; + + // find ellipse parameters + // get the coordinates of the center (xc, yc) + Point2f center = getCenterCoordinates(data_ij, data_ik); + // find remaining parameters (A, B, rho) + findEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ellipses); + } + } + + } +} + +void EllipseDetectorImpl::getTriplets413(VVP &pi, VVP &pj, VVP &pk, + std::unordered_map &data, + std::vector &ellipses) { + // get arch length + auto sz_i = ushort(pi.size()); + auto sz_j = ushort(pj.size()); + auto sz_k = ushort(pk.size()); + + // for each edge i + for (ushort i = 0; i < sz_i; i++) { + VP &edge_i = pi[i]; + auto sz_ei = ushort(edge_i.size()); + + Point &pif = edge_i[0]; + Point &pim = edge_i[sz_ei / 2]; + Point &pil = edge_i[sz_ei - 1]; + + // 4 -> reverse 4 + VP rev_i(edge_i.size()); + reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin()); + + // for each edge j + for (ushort j = 0; j < sz_j; ++j) { + VP &edge_j = pj[j]; + auto sz_ej = ushort(edge_j.size()); + + Point &pjf = edge_j[0]; + Point &pjm = edge_j[sz_ej / 2]; + Point &pjl = edge_j[sz_ej - 1]; + + // constraints on position + if (pjl.y > pil.y + _positionThreshold) + continue; + + // constraints on CNC + const float CNC_THRESHOLD = 0.3f; + if (fabs(valueOfPoints(T413) - 1) > CNC_THRESHOLD) + continue; + + uint key_ij = generateKey(PAIR_14, j, i); + + // for each edge k + for (ushort k = 0; k < sz_k; ++k) { + VP &edge_k = pk[k]; + auto sz_ek = ushort(edge_k.size()); + + Point &pkl = edge_k[sz_ek - 1]; + + // constraints on position + if (pkl.x > pif.x + _positionThreshold) + continue; + + uint key_ik = generateKey(PAIR_34, k, i); + + // find centers + EllipseData data_ij, data_ik; + + // if the data for the pair i-j have not been computed yet + if (data.count(key_ij) == 0) { + getFastCenter(edge_i, edge_j, data_ij); + // insert computed date in the hash table + data.insert(std::pair(key_ij, data_ij)); + } else { + // otherwise, just lookup the data in the hash table + data_ij = data.at(key_ij); + } + + // if the data for the pair i-k have not been computed yet + if (data.count(key_ik) == 0) { + getFastCenter(rev_i, edge_k, data_ik); + data.insert(std::pair(key_ik, data_ik)); + } else { + // otherwise, just lookup the data in the hash table + data_ik = data.at(key_ik); + } + + // invalid centers + if (!data_ij.isValid || !data_ik.isValid) + continue; + + // selection strategy - Step 3. + // the computed centers are not close enough + if (pointDistance2(data_ij.Cab, data_ik.Cab) > _maxCenterDistance2) + continue; + + // find ellipse parameters + // get the coordinates of the center (xc, yc) + Point2f center = getCenterCoordinates(data_ij, data_ik); + // find remaining parameters (A, B, rho) + findEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ellipses); + + } + } + } +} + +void EllipseDetectorImpl::preProcessing(Mat1b &image, Mat1b &dp, Mat1b &dn) { + // smooth image + GaussianBlur(image, image, _kernelSize, _sigma); + + // temp variables + Mat1b edges;// edge mask + Mat1s dx, dy; // sobel derivatives + + // detect edges + Sobel(image, dx, CV_16S, 1, 0, 3, 1, 0, BORDER_REPLICATE); + Sobel(image, dy, CV_16S, 0, 1, 3, 1, 0, BORDER_REPLICATE); + + // calculate magnitude of gradient + Size imgSize = image.size(); + edges.create(imgSize); + Mat1f magGrad(imgSize.height, imgSize.width, 0.f); + float maxGrad(0); + for (int i = 0; i < imgSize.height; i++) { + auto *tmpMag = magGrad.ptr(i); + for (int j = 0; j < imgSize.width; j++) { + auto val = float(abs(dx[i][j]) + abs(dy[i][j])); + tmpMag[j] = val; + maxGrad = (val > maxGrad) ? val : maxGrad; + } + } + + // set magic numbers + const int NUM_BINS = 64; + const double PERCENT_OF_PIXEL_NOT_EDGES = 0.9f, RATIO_THRESHOLD = 0.3f; + + // compute histogram + int binSize = cvFloor(maxGrad / float(NUM_BINS) + 0.5f) + 1; + binSize = max(1, binSize); + int bins[NUM_BINS] = {0}; + for (int i = 0; i < imgSize.height; i++) { + auto *tmpMag = magGrad.ptr(i); + for (int j = 0; j < imgSize.width; j++) + bins[int(tmpMag[j]) / binSize]++; + } + + // select the thresholds + float total = 0.f; + auto target = float(imgSize.height * imgSize.width * PERCENT_OF_PIXEL_NOT_EDGES); + int lowTh, highTh(0); + + while (total < target) { + total += bins[highTh]; + highTh++; + } + highTh = cvFloor(highTh * binSize); + lowTh = cvFloor(RATIO_THRESHOLD * float(highTh)); + + // buffer + int *magBuffer[3]; + void *buffer = malloc((imgSize.width + 2) * (imgSize.height + 2) + + (imgSize.width + 2) * 3 * sizeof(int)); + magBuffer[0] = (int *) buffer; + magBuffer[1] = magBuffer[0] + imgSize.width + 2; + magBuffer[2] = magBuffer[1] + imgSize.width + 2; + uchar *map = (uchar *) (magBuffer[2] + imgSize.width + 2); + ptrdiff_t mapStep = imgSize.width + 2; + + int maxSize = MAX(1 << 10, imgSize.width * imgSize.height / 10); + std::vector stack; + stack.resize(maxSize); + uchar **stackTop = 0, **stackBottom = 0; + stackTop = stackBottom = &stack[0]; + + memset(magBuffer[0], 0, (imgSize.width + 2) * sizeof(int)); + memset(map, 1, mapStep); + memset(map + mapStep * (imgSize.height + 1), 1, mapStep); + + Mat magRow; + magRow.create(1, imgSize.width, CV_32F); + + // calculate magnitude and angle of gradient, perform non-maxima suppression. + // fill the map with one of the following values: + // 0 - the pixel might belong to an edge + // 1 - the pixel can not belong to an edge + // 2 - the pixel does belong to an edge + for (int i = 0; i <= imgSize.height; i++) { + int *tmpMag = magBuffer[(i > 0) + 1] + 1; + const short *tmpDx = (short *) (dx[i]); + const short *tmpDy = (short *) (dy[i]); + uchar *tmpMap; + int prevFlag = 0; + + if (i < imgSize.height) { + tmpMag[-1] = tmpMag[imgSize.width] = 0; + for (int j = 0; j < imgSize.width; j++) + tmpMag[j] = abs(tmpDx[j]) + abs(tmpDy[j]); + } else + memset(tmpMag - 1, 0, (imgSize.width + 2) * sizeof(int)); + + // at the very beginning we do not have a complete ring + // buffer of 3 magnitude rows for non-maxima suppression + if (i == 0) + continue; + + tmpMap = map + mapStep * i + 1; + tmpMap[-1] = tmpMap[imgSize.width] = 1; + + tmpMag = magBuffer[1] + 1; // take the central row + tmpDx = (short *) (dx[i - 1]); + tmpDy = (short *) (dy[i - 1]); + + ptrdiff_t magStep1, magStep2; + magStep1 = magBuffer[2] - magBuffer[1]; + magStep2 = magBuffer[0] - magBuffer[1]; + + if ((stackTop - stackBottom) + imgSize.width > maxSize) { + int stackSize = (int) (stackTop - stackBottom); + maxSize = MAX(maxSize * 3 / 2, maxSize + 8); + stack.resize(maxSize); + stackBottom = &stack[0]; + stackTop = stackBottom + stackSize; + } + + const int CANNY_SHIFT = 15; + const float TAN22_5 = 0.4142135623730950488016887242097; // tan(22.5) = sqrt(2) - 1 + const int TG22 = (int) (TAN22_5 * (1 << CANNY_SHIFT) + 0.5); + + // #define CANNY_PUSH(d) *(d) = (uchar)2, *stack_top++ = (d) + // #define CANNY_POP(d) ((d) = *--stack_top) + + for (int j = 0; j < imgSize.width; j++) { + int x = tmpDx[j], y = tmpDy[j]; + int s = x ^ y; + int m = tmpMag[j]; + x = abs(x), y = abs(y); + if (m > lowTh) { + int tg22x = x * TG22; + int tg67x = tg22x + ((x + x) << CANNY_SHIFT); + + y <<= CANNY_SHIFT; + if (y < tg22x) { + if (m > tmpMag[j - 1] && m >= tmpMag[j + 1]) { + if (m > highTh && !prevFlag && tmpMap[j - mapStep] != 2) { + tmpMap[j] = (uchar) 2; + *stackTop++ = (tmpMap + j); + prevFlag = 1; + } else + tmpMap[j] = (uchar) 0; + continue; + } + } else if (y > tg67x) { + if (m > tmpMag[j + magStep2] && m >= tmpMag[j + magStep1]) { + if (m > highTh && !prevFlag && tmpMap[j - mapStep] != 2) { + tmpMap[j] = (uchar) 2; + *stackTop++ = (tmpMap + j); + prevFlag = 1; + } else + tmpMap[j] = (uchar) 0; + continue; + } + } else { + s = s < 0 ? -1 : 1; + if (m > tmpMag[j + magStep2 - s] && m > tmpMag[j + magStep1 + s]) { + if (m > highTh && !prevFlag && tmpMap[j - mapStep] != 2) { + tmpMap[j] = (uchar) 2; + *stackTop++ = (tmpMap + j); + prevFlag = 1; + } else + tmpMap[j] = (uchar) 0; + continue; + } + } + } + prevFlag = 0; + tmpMap[j] = (uchar) 1; + } + + // scroll the ring buffer + tmpMag = magBuffer[0]; + magBuffer[0] = magBuffer[1]; + magBuffer[1] = magBuffer[2]; + magBuffer[2] = tmpMag; + } + + // track the edges (hysteresis thresholding) + while (stackTop > stackBottom) { + uchar *m; + if ((stackTop - stackBottom) + 8 > maxSize) { + int stackSize = (int) (stackTop - stackBottom); + maxSize = MAX(maxSize * 3 / 2, maxSize + 8); + stack.resize(maxSize); + stackBottom = &stack[0]; + stackTop = stackBottom + stackSize; + } + + m = *--stackTop; + if (!m[-1]) { + *(m - 1) = (uchar) 2; + *stackTop++ = m - 1; + } + if (!m[1]) { + *(m + 1) = (uchar) 2; + *stackTop++ = m + 1; + } + if (!m[-mapStep - 1]) { + *(m - mapStep - 1) = (uchar) 2; + *stackTop++ = m - mapStep - 1; + } + if (!m[-mapStep]) { + *(m - mapStep) = (uchar) 2; + *stackTop++ = m - mapStep; + } + if (!m[-mapStep + 1]) { + *(m - mapStep + 1) = (uchar) 2; + *stackTop++ = m - mapStep + 1; + } + if (!m[mapStep - 1]) { + *(m + mapStep - 1) = (uchar) 2; + *stackTop++ = m + mapStep - 1; + } + if (!m[mapStep]) { + *(m + mapStep) = (uchar) 2; + *stackTop++ = m + mapStep; + } + if (!m[mapStep + 1]) { + *(m + mapStep + 1) = (uchar) 2; + *stackTop++ = m + mapStep + 1; + } + } + + // final pass, form the final image + for (int i = 0; i < imgSize.height; i++) { + const uchar *tmpMap = map + mapStep * (i + 1) + 1; + uchar *tmpDst = edges[i]; + for (int j = 0; j < imgSize.width; j++) + tmpDst[j] = (uchar) -(tmpMap[j] >> 1); + } + + // for each edge points, compute the edge direction + for (int i = 0; i < imgSize.height; i++) { + auto *tmpDx = dx.ptr(i); + auto *tmpDy = dy.ptr(i); + auto *tmpE = edges.ptr(i); + auto *tmpDp = dp.ptr(i); + auto *tmpDn = dn.ptr(i); + + for (int j = 0; j < imgSize.width; j++) { + if (!((tmpE[j] <= 0) || (tmpDx[j] == 0) || (tmpDy[j] == 0))) { + // angle of the tangent + float phi = -(float(tmpDx[j])) / float(tmpDy[j]); + // along positive or negative diagonal + if (phi > 0) + tmpDp[j] = (uchar) 255; + else if (phi < 0) + tmpDn[j] = (uchar) 255; + } + } + } +} + +void EllipseDetectorImpl::detect(Mat1b &image, std::vector &ellipses) { + countsOfFindEllipse = 0, countsOfGetFastCenter = 0; + + // set the image size + _imgSize = image.size(); + + // initialize temporary data structures + Mat1b dp = Mat1b::zeros(_imgSize); // arcs along positive diagonal + Mat1b dn = Mat1b::zeros(_imgSize); // arcs along negative diagonal + + // initialize accumulator dimensions + ACC_N_SIZE = 101, ACC_R_SIZE = 180, ACC_A_SIZE = max(_imgSize.height, _imgSize.width); + + // allocate accumulators + accN = new int[ACC_N_SIZE], accR = new int[ACC_R_SIZE], accA = new int[ACC_A_SIZE]; + + // other temporary + VVP points_1, points_2, points_3, points_4; // vector of points, one for each convexity class + std::unordered_map centers; // hash map for reusing already computed EllipseData + + // preprocessing + // find edge point with coarse convexity along positive (dp) or negative (dn) diagonal + preProcessing(image, dp, dn); + + // detect edge and find convexity + detectEdges13(dp, points_1, points_3); + detectEdges24(dn, points_2, points_4); + + // find triplets + getTriplets124(points_1, points_2, points_4, centers, ellipses); + getTriplets231(points_2, points_3, points_1, centers, ellipses); + getTriplets342(points_3, points_4, points_2, centers, ellipses); + getTriplets413(points_4, points_1, points_3, centers, ellipses); + + // sort by score + sort(ellipses.begin(), ellipses.end()); + + // free accumulator memory + delete[]accN; + delete[]accR; + delete[]accA; + + // cluster detections + clusterEllipses(ellipses); +} + +void EllipseDetectorImpl::findEllipses(Point2f ¢er, VP &edge_i, VP &edge_j, VP &edge_k, + EllipseData &data_ij, EllipseData &data_ik, + std::vector &ellipses) { + countsOfFindEllipse++; + // find ellipse parameters + + // 0-initialize accumulators + memset(accN, 0, sizeof(int) * ACC_N_SIZE); + memset(accR, 0, sizeof(int) * ACC_R_SIZE); + memset(accA, 0, sizeof(int) * ACC_A_SIZE); + + + // get size of the 4 vectors of slopes (2 pairs of arcs) + int sz_ij1 = int(data_ij.Sa.size()); + int sz_ij2 = int(data_ij.Sb.size()); + int sz_ik1 = int(data_ik.Sa.size()); + int sz_ik2 = int(data_ik.Sb.size()); + + // get the size of the 3 arcs + size_t sz_ei = edge_i.size(); + size_t sz_ej = edge_j.size(); + size_t sz_ek = edge_k.size(); + + // center of the estimated ellipse + float a0 = center.x; + float b0 = center.y; + + // estimation of remaining parameters + // uses 4 combinations of parameters. See Table 1 and Sect [3.2.3] of the paper. + // ij1 and ik + { + float q1 = data_ij.ra; + float q3 = data_ik.ra; + float q5 = data_ik.rb; + + for (int ij1 = 0; ij1 < sz_ij1; ij1++) { + float q2 = data_ij.Sa[ij1]; + + float q1xq2 = q1 * q2; + // ij1 and ik1 + for (int ik1 = 0; ik1 < sz_ik1; ik1++) { + float q4 = data_ik.Sa[ik1]; + + float q3xq4 = q3 * q4; + + // see Eq. [13-18] in the paper + + float a = (q1xq2 - q3xq4); // gama + float b = (q3xq4 + 1) * (q1 + q2) - (q1xq2 + 1) * (q3 + q4); // beta + float Kp = (-b + sqrt(b * b + 4 * a * a)) / (2 * a); // K+ + float zplus = ((q1 - Kp) * (q2 - Kp)) / ((1 + q1 * Kp) * (1 + q2 * Kp)); + + // available or not + if (zplus >= 0.0f) continue; + + float Np = sqrt(-zplus); // N+ + float rho = atan(Kp); // rho tmp + int rhoDeg; + if (Np > 1.f) { + // inverse and convert to [0, 180) + Np = 1.f / Np; + rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; + } else { + // convert to [0, 180) + rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; + } + + int iNp = cvRound(Np * 100); + + if (0 <= iNp && iNp < ACC_N_SIZE && 0 <= rhoDeg && rhoDeg < ACC_R_SIZE) { + ++accN[iNp]; // increment N accumulator + ++accR[rhoDeg]; // increment R accumulator + } + } + + // ij1 and ik2 + for (int ik2 = 0; ik2 < sz_ik2; ik2++) { + float q4 = data_ik.Sb[ik2]; + + float q5xq4 = q5 * q4; + + // See Eq. [13-18] in the paper + + float a = (q1xq2 - q5xq4); + float b = (q5xq4 + 1) * (q1 + q2) - (q1xq2 + 1) * (q5 + q4); + float Kp = (-b + sqrt(b * b + 4 * a * a)) / (2 * a); + float zplus = ((q1 - Kp) * (q2 - Kp)) / ((1 + q1 * Kp) * (1 + q2 * Kp)); + + if (zplus >= 0.0f) + continue; + + float Np = sqrt(-zplus); + float rho = atan(Kp); + int rhoDeg; + if (Np > 1.f) { + // inverse and convert to [0, 180) + Np = 1.f / Np; + rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; + } else { + // convert to [0, 180) + rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; + } + + int iNp = cvRound(Np * 100); // [0, 100] + + if (0 <= iNp && iNp < ACC_N_SIZE && 0 <= rhoDeg && rhoDeg < ACC_R_SIZE) { + ++accN[iNp]; // increment N accumulator + ++accR[rhoDeg]; // increment R accumulator + } + } + + } + } + + //ij2 and ik + { + float q1 = data_ij.rb; + float q3 = data_ik.rb; + float q5 = data_ik.ra; + + for (int ij2 = 0; ij2 < sz_ij2; ij2++) { + float q2 = data_ij.Sb[ij2]; + + float q1xq2 = q1 * q2; + //ij2 and ik2 + for (int ik2 = 0; ik2 < sz_ik2; ik2++) { + float q4 = data_ik.Sb[ik2]; + + float q3xq4 = q3 * q4; + + // See Eq. [13-18] in the paper + + float a = (q1xq2 - q3xq4); + float b = (q3xq4 + 1) * (q1 + q2) - (q1xq2 + 1) * (q3 + q4); + float Kp = (-b + sqrt(b * b + 4 * a * a)) / (2 * a); + float zplus = ((q1 - Kp) * (q2 - Kp)) / ((1 + q1 * Kp) * (1 + q2 * Kp)); + + if (zplus >= 0.0f) + continue; + + float Np = sqrt(-zplus); + float rho = atan(Kp); + int rhoDeg; + if (Np > 1.f) { + // inverse and convert to [0, 180) + Np = 1.f / Np; + rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; + } else { + // convert to [0, 180) + rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; + } + + int iNp = cvRound(Np * 100); // [0, 100] + + if (0 <= iNp && iNp < ACC_N_SIZE && 0 <= rhoDeg && rhoDeg < ACC_R_SIZE) { + ++accN[iNp]; // increment N accumulator + ++accR[rhoDeg]; // increment R accumulator + } + } + + // ij2 and ik1 + for (int ik1 = 0; ik1 < sz_ik1; ik1++) { + float q4 = data_ik.Sa[ik1]; + + float q5xq4 = q5 * q4; + + // See Eq. [13-18] in the paper + + float a = (q1xq2 - q5xq4); + float b = (q5xq4 + 1) * (q1 + q2) - (q1xq2 + 1) * (q5 + q4); + float Kp = (-b + sqrt(b * b + 4 * a * a)) / (2 * a); + float zplus = ((q1 - Kp) * (q2 - Kp)) / ((1 + q1 * Kp) * (1 + q2 * Kp)); + + if (zplus >= 0.0f) { + continue; + } + + float Np = sqrt(-zplus); + float rho = atan(Kp); + int rhoDeg; + if (Np > 1.f) { + // inverse and convert to [0, 180) + Np = 1.f / Np; + rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; + } else { + // convert to [0, 180) + rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; + } + + int iNp = cvRound(Np * 100); // [0, 100] + + if (0 <= iNp && iNp < ACC_N_SIZE && 0 <= rhoDeg && rhoDeg < ACC_R_SIZE) { + ++accN[iNp]; // increment N accumulator + ++accR[rhoDeg]; // increment R accumulator + } + } + } + } + + // find peak in N and K accumulator + int iN = std::distance(accN, std::max_element(accN, accN + ACC_N_SIZE)); + int iK = std::distance(accR, std::max_element(accR, accR + ACC_R_SIZE)) + 90; + + // recover real values + auto fK = float(iK); + float Np = float(iN) * 0.01f; + float rho = fK * float(CV_PI) / 180.f; // deg 2 rad + float Kp = tan(rho); + + // estimate A. See Eq. [19 - 22] in Sect [3.2.3] of the paper + for (ushort l = 0; l < sz_ei; ++l) { + Point &pp = edge_i[l]; + float sk = 1.f / sqrt(Kp * Kp + 1.f); // cos rho + float x0 = ((pp.x - a0) * sk) + (((pp.y - b0) * Kp) * sk); + float y0 = -(((pp.x - a0) * Kp) * sk) + ((pp.y - b0) * sk); + float Ax = sqrt((x0 * x0 * Np * Np + y0 * y0) / ((Np * Np) * (1.f + Kp * Kp))); + int A = cvRound(abs(Ax / cos(rho))); + if ((0 <= A) && (A < ACC_A_SIZE)) + ++accA[A]; + } + + for (ushort l = 0; l < sz_ej; ++l) { + Point &pp = edge_j[l]; + float sk = 1.f / sqrt(Kp * Kp + 1.f); + float x0 = ((pp.x - a0) * sk) + (((pp.y - b0) * Kp) * sk); + float y0 = -(((pp.x - a0) * Kp) * sk) + ((pp.y - b0) * sk); + float Ax = sqrt((x0 * x0 * Np * Np + y0 * y0) / ((Np * Np) * (1.f + Kp * Kp))); + int A = cvRound(abs(Ax / cos(rho))); + if ((0 <= A) && (A < ACC_A_SIZE)) + ++accA[A]; + } + + for (ushort l = 0; l < sz_ek; ++l) { + Point &pp = edge_k[l]; + float sk = 1.f / sqrt(Kp * Kp + 1.f); + float x0 = ((pp.x - a0) * sk) + (((pp.y - b0) * Kp) * sk); + float y0 = -(((pp.x - a0) * Kp) * sk) + ((pp.y - b0) * sk); + float Ax = sqrt((x0 * x0 * Np * Np + y0 * y0) / ((Np * Np) * (1.f + Kp * Kp))); + int A = cvRound(abs(Ax / cos(rho))); + if ((0 <= A) && (A < ACC_A_SIZE)) + ++accA[A]; + } + + // find peak in A accumulator + int A = std::distance(accA, std::max_element(accA, accA + ACC_A_SIZE)); + auto fA = float(A); + + // find B value. See Eq [23] in the paper + float fB = abs(fA * Np); + + // got all ellipse parameters + Ellipse ell(a0, b0, fA, fB, fmod(rho + float(CV_PI) * 2.f, float(CV_PI))); + + // get the score. See Sect [3.3.1] in the paper + + // find the number of edge pixel lying on the ellipse + float _cos = cos(-ell.radius); + float _sin = sin(-ell.radius); + + float invA2 = 1.f / (ell.a * ell.a); + float invB2 = 1.f / (ell.b * ell.b); + + float invNofPoints = 1.f / float(sz_ei + sz_ej + sz_ek); + int counter_on_perimeter = 0; + + for (ushort l = 0; l < sz_ei; ++l) { + float tx = float(edge_i[l].x) - ell.center.x; + float ty = float(edge_i[l].y) - ell.center.y; + float rx = (tx * _cos - ty * _sin); + float ry = (tx * _sin + ty * _cos); + + float h = (rx * rx) * invA2 + (ry * ry) * invB2; + if (abs(h - 1.f) < _maxDistanceToEllipseContour) + ++counter_on_perimeter; + } + + for (ushort l = 0; l < sz_ej; ++l) { + float tx = float(edge_j[l].x) - ell.center.x; + float ty = float(edge_j[l].y) - ell.center.y; + float rx = (tx * _cos - ty * _sin); + float ry = (tx * _sin + ty * _cos); + + float h = (rx * rx) * invA2 + (ry * ry) * invB2; + if (abs(h - 1.f) < _maxDistanceToEllipseContour) + ++counter_on_perimeter; + } + + for (ushort l = 0; l < sz_ek; ++l) { + float tx = float(edge_k[l].x) - ell.center.x; + float ty = float(edge_k[l].y) - ell.center.y; + float rx = (tx * _cos - ty * _sin); + float ry = (tx * _sin + ty * _cos); + + float h = (rx * rx) * invA2 + (ry * ry) * invB2; + if (abs(h - 1.f) < _maxDistanceToEllipseContour) + ++counter_on_perimeter; + } + + // no points found on the ellipse + if (counter_on_perimeter <= 0) + return; + + // compute score + float score = float(counter_on_perimeter) * invNofPoints; + if (score < _minScore) + return; + + // compute reliability + float di, dj, dk; + { + Point2f p1(float(edge_i[0].x), float(edge_i[0].y)); + Point2f p2(float(edge_i[sz_ei - 1].x), float(edge_i[sz_ei - 1].y)); + p1.x -= ell.center.x; + p1.y -= ell.center.y; + p2.x -= ell.center.x; + p2.y -= ell.center.y; + Point2f r1((p1.x * _cos - p1.y * _sin), (p1.x * _sin + p1.y * _cos)); + Point2f r2((p2.x * _cos - p2.y * _sin), (p2.x * _sin + p2.y * _cos)); + di = abs(r2.x - r1.x) + abs(r2.y - r1.y); + } + { + Point2f p1(float(edge_j[0].x), float(edge_j[0].y)); + Point2f p2(float(edge_j[sz_ej - 1].x), float(edge_j[sz_ej - 1].y)); + p1.x -= ell.center.x; + p1.y -= ell.center.y; + p2.x -= ell.center.x; + p2.y -= ell.center.y; + Point2f r1((p1.x * _cos - p1.y * _sin), (p1.x * _sin + p1.y * _cos)); + Point2f r2((p2.x * _cos - p2.y * _sin), (p2.x * _sin + p2.y * _cos)); + dj = abs(r2.x - r1.x) + abs(r2.y - r1.y); + } + { + Point2f p1(float(edge_k[0].x), float(edge_k[0].y)); + Point2f p2(float(edge_k[sz_ek - 1].x), float(edge_k[sz_ek - 1].y)); + p1.x -= ell.center.x; + p1.y -= ell.center.y; + p2.x -= ell.center.x; + p2.y -= ell.center.y; + Point2f r1((p1.x * _cos - p1.y * _sin), (p1.x * _sin + p1.y * _cos)); + Point2f r2((p2.x * _cos - p2.y * _sin), (p2.x * _sin + p2.y * _cos)); + dk = abs(r2.x - r1.x) + abs(r2.y - r1.y); + } + + // this allows to get rid of thick edges + float rel = min(1.f, ((di + dj + dk) / (3 * (ell.a + ell.b)))); + + if (rel < _minReliability) + return; + + // assign the new score + ell.score = (score + rel) * 0.5f; + + // the tentative detection has been confirmed + ellipses.emplace_back(ell); +} + +void EllipseDetectorImpl::clusterEllipses(std::vector &ellipses) { + const float aDistanceThreshold = 0.1f, bDistanceThreshold = 0.1f; + const float rDistanceThreshold = 0.1f; + const float DcRatioThreshold = 0.1f, rCircleThreshold = 0.9f; + + int ellipseCount = int(ellipses.size()); + if (ellipseCount == 0) + return; + + // the first ellipse is assigned to a cluster + std::vector clusters; + clusters.emplace_back(ellipses[0]); + + bool foundCluster = false; + for (int i = 0; i < ellipseCount; i++) { + int clusterSize = int(clusters.size()); + + Ellipse &e1 = ellipses[i]; + float ba_e1 = e1.b / e1.a; + + foundCluster = false; + for (int j = 0; j < clusterSize; j++) { + Ellipse &e2 = clusters[j]; + float ba_e2 = e2.b / e2.a; + + float cDistanceThreshold = min(e1.b, e2.b) * DcRatioThreshold; + cDistanceThreshold = cDistanceThreshold * cDistanceThreshold; + + // filter centers + float cDistance = ((e1.center.x - e2.center.x) * (e1.center.x - e2.center.x) + + (e1.center.y - e2.center.y) * (e1.center.y - e2.center.y)); + if (cDistance > cDistanceThreshold) + continue; + + // filter a + float aDistance = abs(e1.a - e2.a) / max(e1.a, e2.a); + if (aDistance > aDistanceThreshold) + continue; + + // filter b + float bDistance = abs(e1.b - e2.b) / min(e1.b, e2.b); + if (bDistance > bDistanceThreshold) + continue; + + // filter angle + float rDistance = getMinAnglePI(e1.radius, e2.radius) / float(CV_PI); + if ((rDistance > rDistanceThreshold) && (ba_e1 < rCircleThreshold) && + (ba_e2 < rCircleThreshold)) + continue; + + // same cluster as e2 + foundCluster = true; + break; + } + + // create a new cluster + if (!foundCluster) + clusters.push_back(e1); + } + clusters.swap(ellipses); +} + +// find ellipses in images +void findEllipses( + InputArray image, OutputArray ellipses, + float scoreThreshold, float reliabilityThreshold, + float centerDistanceThreshold) { + + // check image empty and type + CV_Assert( + !image.empty() && (image.isMat() || image.isUMat())); + + // check ellipses type + int type = CV_32FC(6); + if (ellipses.fixedType()) { + type = ellipses.type(); + CV_CheckType(type, type == CV_32FC(6), "Wrong type of output ellipses"); + } + + // set class parameters + Size imgSize = image.size(); + float maxCenterDistance = + sqrt(float(imgSize.width * imgSize.width + imgSize.height * imgSize.height)) * + centerDistanceThreshold; + EllipseDetectorImpl edi; + edi.setParameters(maxCenterDistance, scoreThreshold, reliabilityThreshold); + + // detect - ellipse format + std::vector ellipseResults; + Mat1b grayImage = image.getMat(); + if (image.channels() != 1) + cvtColor(image, grayImage, COLOR_BGR2GRAY); + edi.detect(grayImage, ellipseResults); + + // convert - ellipse format to std::vector + auto ellipseSize = unsigned(ellipseResults.size()); + Mat _ellipses(1, ellipseSize, CV_32FC(6)); + for (unsigned i = 0; i < ellipseSize; i++) { + Ellipse tmpEll = ellipseResults[i]; + Vec6f tmpVec(tmpEll.center.x, tmpEll.center.y, tmpEll.a, tmpEll.b, tmpEll.score, + tmpEll.radius); + _ellipses.at(i) = tmpVec; + } + _ellipses.copyTo(ellipses); +} +} // namespace ximgproc +} // namespace cv \ No newline at end of file diff --git a/modules/ximgproc/test/test_find_ellipses.cpp b/modules/ximgproc/test/test_find_ellipses.cpp new file mode 100644 index 0000000000..9e57d89579 --- /dev/null +++ b/modules/ximgproc/test/test_find_ellipses.cpp @@ -0,0 +1,43 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" + +using namespace cv; + +namespace opencv_test { namespace { + + +TEST(FindEllipsesTest, EllipsesOnly) +{ + std::string picture_name = "cv/imgproc/stuff.jpg"; + std::string filename = cvtest::TS::ptr()->get_data_path() + picture_name; + Mat src = imread(filename, IMREAD_GRAYSCALE); + EXPECT_FALSE(src.empty()) << "Invalid test image: " << filename; + + std::vector ells; + ximgproc::findEllipses(src, ells, 0.7f, 0.75f, 0.02f); + + // number check + EXPECT_EQ(ells.size(), size_t(3)) << "Should find 3 ellipses"; + + // position check + // target centers + Point2f center_1(226.9, 57.2); + Point2f center_2(393.1, 187.0); + Point2f center_3(208.5, 307.5); + // matching + for (auto ell: ells) { + bool has_match = false; + for (auto c: {center_1, center_2, center_3}) { + Point2f diff = c - Point2f(ell[0], ell[1]); + float distance = sqrt(diff.x * diff.x + diff.y * diff.y); + if (distance < 5.0) { + has_match = true; + break; + } + } + EXPECT_TRUE(has_match) << "Wrong ellipse center:" << Point2f(ell[0], ell[1]); + } +} +}} \ No newline at end of file From d3b58df32f665ba6099c587db23b035665443ee7 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Wed, 26 Oct 2022 17:50:36 +0300 Subject: [PATCH 39/99] Remove temporary files from accuracy tests. --- modules/cudacodec/test/test_video.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index a1260a6dae..d4d42b1698 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -482,6 +482,7 @@ CUDA_TEST_P(TransCode, H264ToH265) ASSERT_FALSE(frame.empty()); } } + ASSERT_EQ(0, remove(outputFile.c_str())); } INSTANTIATE_TEST_CASE_P(CUDA_Codec, TransCode, ALL_DEVICES); @@ -562,6 +563,7 @@ CUDA_TEST_P(Write, Writer) ASSERT_FALSE(frame.empty()); } } + ASSERT_EQ(0, remove(outputFile.c_str())); } #define DEVICE_SRC true, false @@ -643,6 +645,7 @@ CUDA_TEST_P(EncoderParams, Writer) } } } + ASSERT_EQ(0, remove(outputFile.c_str())); } INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParams, ALL_DEVICES); From 86790142c13518df9cbbad9bf58346a61f073c29 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Thu, 27 Oct 2022 14:55:04 +0300 Subject: [PATCH 40/99] Extend Codec and ColorFormat enums instead of using VideoWriter specific ones and use camel-case for enum names. --- .../cudacodec/include/opencv2/cudacodec.hpp | 129 ++++++++---------- .../misc/python/test/test_cudacodec.py | 6 +- modules/cudacodec/perf/perf_video.cpp | 24 ++-- modules/cudacodec/src/video_reader.cpp | 17 ++- modules/cudacodec/src/video_writer.cpp | 106 +++++++------- modules/cudacodec/test/test_video.cpp | 55 ++++---- 6 files changed, 166 insertions(+), 171 deletions(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index a28a3c593b..12d06de7c4 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -66,35 +66,56 @@ using namespace cuda; // Stream ////////////////////////////////// Video Encoding ////////////////////////////////// -/** @brief Codecs supported by Video writer, refer to Nvidia's GPU Support Matrix to confirm your GPU supports them. -*/ -enum class CODEC_VW +/** @brief Video codecs supported by cudacodec::VideoReader and cudacodec::VideoWriter. +@note + - Support will depend on your hardware, refer to the Nvidia Video Codec SDK Video Encode and Decode GPU Support Matrix for details. + */ +enum Codec { - H264 = 0, - HEVC = 1, - NumCodecs = 2 + MPEG1 = 0, + MPEG2, + MPEG4, + VC1, + H264, + JPEG, + H264_SVC, + H264_MVC, + HEVC, + VP8, + VP9, + AV1, + NumCodecs, + + Uncompressed_YUV420 = (('I' << 24) | ('Y' << 16) | ('U' << 8) | ('V')), //!< Y,U,V (4:2:0) + Uncompressed_YV12 = (('Y' << 24) | ('V' << 16) | ('1' << 8) | ('2')), //!< Y,V,U (4:2:0) + Uncompressed_NV12 = (('N' << 24) | ('V' << 16) | ('1' << 8) | ('2')), //!< Y,UV (4:2:0) + Uncompressed_YUYV = (('Y' << 24) | ('U' << 16) | ('Y' << 8) | ('V')), //!< YUYV/YUY2 (4:2:2) + Uncompressed_UYVY = (('U' << 24) | ('Y' << 16) | ('V' << 8) | ('Y')) //!< UYVY (4:2:2) }; -/** @brief Video writer color formats. +/** @brief ColorFormat for the frame returned by VideoReader::nextFrame()/VideoReader::retrieve() or used to initialize a VideoWriter. */ -enum COLOR_FORMAT_VW { +enum class ColorFormat { UNDEFINED = 0, - BGR = 1, //!< Default OpenCV color format. - RGB = 2, - BGRA = 3, - RGBA = 4, - GRAY = 5, - - NV_NV12 = 6, //!< Nvidia Buffer Format - Semi-Planar YUV [Y plane followed by interleaved UV plane]. - NV_YV12 = 7, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by V and U planes]. - NV_IYUV = 8, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes]. - NV_YUV444 = 9, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes]. - NV_AYUV = 10 //!< Nvidia Buffer Format - 8 bit Packed A8Y8U8V8. This is a word-ordered format where a pixel is represented by a 32-bit word with V in the lowest 8 bits, U in the next 8 bits, Y in the 8 bits after that and A in the highest 8 bits. + BGRA = 1, //!< OpenCV color format, can be used with both VideoReader and VideoWriter. + BGR = 2, //!< OpenCV color format, can be used with both VideoReader and VideoWriter. + GRAY = 3, //!< OpenCV color format, can be used with both VideoReader and VideoWriter. + NV_NV12 = 4, //!< Nvidia color format - equivalent to YUV - Semi-Planar YUV [Y plane followed by interleaved UV plane], can be used with both VideoReader and VideoWriter. + + RGB = 5, //!< OpenCV color format, can only be used with VideoWriter. + RGBA = 6, //!< OpenCV color format, can only be used with VideoWriter. + NV_YV12 = 8, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by V and U planes], use with VideoReader, can only be used with VideoWriter. + NV_IYUV = 9, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes], use with VideoReader, can only be used with VideoWriter. + NV_YUV444 = 10, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes], use with VideoReader, can only be used with VideoWriter. + NV_AYUV = 11, //!< Nvidia Buffer Format - 8 bit Packed A8Y8U8V8. This is a word-ordered format where a pixel is represented by a 32-bit word with V in the lowest 8 bits, U in the next 8 bits, Y in the 8 bits after that and A in the highest 8 bits, can only be used with VideoWriter. +#ifndef CV_DOXYGEN + PROP_NOT_SUPPORTED +#endif }; /** @brief Rate Control Modes. */ -enum ENC_PARAMS_RC_MODE { +enum EncodeParamsRcMode { ENC_PARAMS_RC_CONSTQP = 0x0, //!< Constant QP mode. ENC_PARAMS_RC_VBR = 0x1, //!< Variable bitrate mode. ENC_PARAMS_RC_CBR = 0x2 //!< Constant bitrate mode. @@ -102,7 +123,7 @@ enum ENC_PARAMS_RC_MODE { /** @brief Multi Pass Encoding. */ -enum ENC_MULTI_PASS +enum EncodeMultiPass { ENC_MULTI_PASS_DISABLED = 0x0, //!< Single Pass. ENC_TWO_PASS_QUARTER_RESOLUTION = 0x1, //!< Two Pass encoding is enabled where first Pass is quarter resolution. @@ -112,7 +133,7 @@ enum ENC_MULTI_PASS /** @brief Supported Encoder Profiles. */ -enum ENC_PROFILE { +enum EncodeProfile { ENC_CODEC_PROFILE_AUTOSELECT = 0, ENC_H264_PROFILE_BASELINE = 1, ENC_H264_PROFILE_MAIN = 2, @@ -128,7 +149,7 @@ enum ENC_PROFILE { /** @brief Nvidia Encoding Presets. Performance degrades and quality improves as we move from P1 to P7. */ -enum ENC_PRESET { +enum EncodePreset { ENC_PRESET_P1 = 1, ENC_PRESET_P2 = 2, ENC_PRESET_P3 = 3, @@ -140,7 +161,7 @@ enum ENC_PRESET { /** @brief Tuning information. */ -enum ENC_TUNING_INFO { +enum EncodeTuningInfo { ENC_TUNING_INFO_UNDEFINED = 0, //!< Undefined tuningInfo. Invalid value for encoding. ENC_TUNING_INFO_HIGH_QUALITY = 1, //!< Tune presets for latency tolerant encoding. ENC_TUNING_INFO_LOW_LATENCY = 2, //!< Tune presets for low latency streaming. @@ -151,7 +172,7 @@ enum ENC_TUNING_INFO { /** Quantization Parameter for each type of frame when using ENC_PARAMS_RC_MODE::ENC_PARAMS_RC_CONSTQP. */ -struct CV_EXPORTS_W_SIMPLE ENC_QP +struct CV_EXPORTS_W_SIMPLE EncodeQp { CV_PROP_RW uint32_t qpInterP; //!< Specifies QP value for P-frame. CV_PROP_RW uint32_t qpInterB; //!< Specifies QP value for B-frame. @@ -164,12 +185,12 @@ struct CV_EXPORTS_W_SIMPLE EncoderParams { public: CV_WRAP EncoderParams(); - CV_PROP_RW ENC_PRESET nvPreset; - CV_PROP_RW ENC_TUNING_INFO tuningInfo; - CV_PROP_RW ENC_PROFILE encodingProfile; - CV_PROP_RW ENC_PARAMS_RC_MODE rateControlMode; - CV_PROP_RW ENC_MULTI_PASS multiPassEncoding; - CV_PROP_RW ENC_QP constQp; //!< QP's for ENC_PARAMS_RC_CONSTQP. + CV_PROP_RW EncodePreset nvPreset; + CV_PROP_RW EncodeTuningInfo tuningInfo; + CV_PROP_RW EncodeProfile encodingProfile; + CV_PROP_RW EncodeParamsRcMode rateControlMode; + CV_PROP_RW EncodeMultiPass multiPassEncoding; + CV_PROP_RW EncodeQp constQp; //!< QP's for ENC_PARAMS_RC_CONSTQP. CV_PROP_RW int averageBitRate; //!< target bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CBR. CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CONSTQP. CV_PROP_RW uint8_t targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with ENC_PARAMS_RC_VBR. @@ -234,8 +255,8 @@ class CV_EXPORTS_W VideoWriter @param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback. Required for working with the encoded video stream. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec = CODEC_VW::H264, const double fps = 25.0, - const COLOR_FORMAT_VW colorFormat = BGR, Ptr encoderCallback = 0, const Stream& stream = Stream::Null()); +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const Codec codec = Codec::H264, const double fps = 25.0, + const ColorFormat colorFormat = ColorFormat::BGR, Ptr encoderCallback = 0, const Stream& stream = Stream::Null()); /** @brief Creates video writer. @@ -248,36 +269,11 @@ CV_EXPORTS_W Ptr createVideoWriter(const String& fileNam @param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback. Required for working with the encoded video stream. @param stream Stream for frame pre-processing. */ -CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, const COLOR_FORMAT_VW colorFormat, +CV_EXPORTS_W Ptr createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat, const EncoderParams& params, Ptr encoderCallback = 0, const Stream& stream = Stream::Null()); ////////////////////////////////// Video Decoding ////////////////////////////////////////// -/** @brief Video codecs supported by cudacodec::VideoReader. - */ -enum Codec -{ - MPEG1 = 0, - MPEG2, - MPEG4, - VC1, - H264, - JPEG, - H264_SVC, - H264_MVC, - HEVC, - VP8, - VP9, - AV1, - NumCodecs, - - Uncompressed_YUV420 = (('I'<<24)|('Y'<<16)|('U'<<8)|('V')), //!< Y,U,V (4:2:0) - Uncompressed_YV12 = (('Y'<<24)|('V'<<16)|('1'<<8)|('2')), //!< Y,V,U (4:2:0) - Uncompressed_NV12 = (('N'<<24)|('V'<<16)|('1'<<8)|('2')), //!< Y,UV (4:2:0) - Uncompressed_YUYV = (('Y'<<24)|('U'<<16)|('Y'<<8)|('V')), //!< YUYV/YUY2 (4:2:2) - Uncompressed_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')) //!< UYVY (4:2:2) -}; - /** @brief Chroma formats supported by cudacodec::VideoReader. */ enum ChromaFormat @@ -344,18 +340,6 @@ enum class VideoReaderProps { #endif }; -/** @brief ColorFormat for the frame returned by nextFrame()/retrieve(). -*/ -enum class ColorFormat { - BGRA = 1, - BGR = 2, - GRAY = 3, - YUV = 4, -#ifndef CV_DOXYGEN - PROP_NOT_SUPPORTED -#endif -}; - /** @brief Video reader interface. @note @@ -451,8 +435,9 @@ class CV_EXPORTS_W VideoReader /** @brief Set the desired ColorFormat for the frame returned by nextFrame()/retrieve(). @param colorFormat Value of the ColorFormat. + @return `true` unless the colorFormat is not supported. */ - CV_WRAP virtual void set(const ColorFormat colorFormat) = 0; + CV_WRAP virtual bool set(const ColorFormat colorFormat) = 0; /** @brief Returns the specified VideoReader property diff --git a/modules/cudacodec/misc/python/test/test_cudacodec.py b/modules/cudacodec/misc/python/test/test_cudacodec.py index cef78a895e..3f41c3bbed 100644 --- a/modules/cudacodec/misc/python/test/test_cudacodec.py +++ b/modules/cudacodec/misc/python/test/test_cudacodec.py @@ -85,11 +85,11 @@ def test_writer_existence(self): encoder_params_in.gopLength = 10 stream = cv.cuda.Stream() sz = (1920,1080) - writer = cv.cudacodec.createVideoWriter(fname, sz, cv.cudacodec.VideoWriterCodec_H264, 30, cv.cudacodec.ColorFormat_BGR, - encoder_params_in, stream) + writer = cv.cudacodec.createVideoWriter(fname, sz, cv.cudacodec.H264, 30, cv.cudacodec.ColorFormat_BGR, + encoder_params_in, stream=stream) blankFrameIn = cv.cuda.GpuMat(sz,cv.CV_8UC3) writer.write(blankFrameIn) - writer.close() + writer.release() encoder_params_out = writer.getEncoderParams() self.assert_true(encoder_params_in.gopLength == encoder_params_out.gopLength) cap = cv.VideoCapture(fname,cv.CAP_FFMPEG) diff --git a/modules/cudacodec/perf/perf_video.cpp b/modules/cudacodec/perf/perf_video.cpp index 713a6062f5..bb4e9a4a77 100644 --- a/modules/cudacodec/perf/perf_video.cpp +++ b/modules/cudacodec/perf/perf_video.cpp @@ -97,41 +97,41 @@ PERF_TEST_P(FileName, VideoReader, VIDEO_SRC) #if defined(HAVE_NVCUVENC) -DEF_PARAM_TEST(WriteToFile, string, cv::cudacodec::COLOR_FORMAT_VW, cv::cudacodec::CODEC_VW); +DEF_PARAM_TEST(WriteToFile, string, cv::cudacodec::ColorFormat, cv::cudacodec::Codec); -#define COLOR_FORMAT Values(cv::cudacodec::COLOR_FORMAT_VW::BGR, cv::cudacodec::COLOR_FORMAT_VW::RGB, cv::cudacodec::COLOR_FORMAT_VW::BGRA, \ -cv::cudacodec::COLOR_FORMAT_VW::RGBA, cv::cudacodec::COLOR_FORMAT_VW::GRAY) -#define CODEC Values(cv::cudacodec::CODEC_VW::H264, cv::cudacodec::CODEC_VW::HEVC) +#define COLOR_FORMAT Values(cv::cudacodec::ColorFormat::BGR, cv::cudacodec::ColorFormat::RGB, cv::cudacodec::ColorFormat::BGRA, \ +cv::cudacodec::ColorFormat::RGBA, cv::cudacodec::ColorFormat::GRAY) +#define CODEC Values(cv::cudacodec::Codec::H264, cv::cudacodec::Codec::HEVC) PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) { declare.time(30); const string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); - const cv::cudacodec::COLOR_FORMAT_VW surfaceFormat = GET_PARAM(1); - const cudacodec::CODEC_VW codec = GET_PARAM(2); + const cv::cudacodec::ColorFormat surfaceFormat = GET_PARAM(1); + const cudacodec::Codec codec = GET_PARAM(2); const double fps = 25; const int nFrames = 20; cv::VideoCapture reader(inputFile); ASSERT_TRUE(reader.isOpened()); Mat frameBgr; if (PERF_RUN_CUDA()) { - const std::string ext = codec == cudacodec::CODEC_VW::H264 ? ".h264" : ".hevc"; + const std::string ext = codec == cudacodec::Codec::H264 ? ".h264" : ".hevc"; const string outputFile = cv::tempfile(ext.c_str()); std::vector frames; cv::Mat frameNewSf; cuda::Stream stream; ColorConversionCodes conversionCode = COLOR_COLORCVT_MAX; switch (surfaceFormat) { - case cudacodec::COLOR_FORMAT_VW::RGB: + case cudacodec::ColorFormat::RGB: conversionCode = COLOR_BGR2RGB; break; - case cudacodec::COLOR_FORMAT_VW::BGRA: + case cudacodec::ColorFormat::BGRA: conversionCode = COLOR_BGR2BGRA; break; - case cudacodec::COLOR_FORMAT_VW::RGBA: + case cudacodec::ColorFormat::RGBA: conversionCode = COLOR_BGR2RGBA; break; - case cudacodec::COLOR_FORMAT_VW::GRAY: + case cudacodec::ColorFormat::GRAY: conversionCode = COLOR_BGR2GRAY; default: break; @@ -161,7 +161,7 @@ PERF_TEST_P(WriteToFile, VideoWriter, Combine(VIDEO_SRC, COLOR_FORMAT, CODEC)) ASSERT_EQ(0, remove(outputFile.c_str())); } else { - if (surfaceFormat != cv::cudacodec::COLOR_FORMAT_VW::BGR || codec != cv::cudacodec::CODEC_VW::H264) + if (surfaceFormat != cv::cudacodec::ColorFormat::BGR || codec != cv::cudacodec::Codec::H264) throw PerfSkipTestException(); cv::VideoWriter writer; const string outputFile = cv::tempfile(".avi"); diff --git a/modules/cudacodec/src/video_reader.cpp b/modules/cudacodec/src/video_reader.cpp index c484f23b23..a566bd4de7 100644 --- a/modules/cudacodec/src/video_reader.cpp +++ b/modules/cudacodec/src/video_reader.cpp @@ -54,6 +54,7 @@ Ptr cv::cudacodec::createVideoReader(const Ptr&, co #else // HAVE_NVCUVID void nv12ToBgra(const GpuMat& decodedFrame, GpuMat& outFrame, int width, int height, cudaStream_t stream); +bool ValidColorFormat(const ColorFormat colorFormat); void videoDecPostProcessFrame(const GpuMat& decodedFrame, GpuMat& outFrame, int width, int height, const ColorFormat colorFormat, Stream stream) @@ -74,7 +75,7 @@ void videoDecPostProcessFrame(const GpuMat& decodedFrame, GpuMat& outFrame, int outFrame.create(height, width, CV_8UC1); cudaMemcpy2DAsync(outFrame.ptr(), outFrame.step, decodedFrame.ptr(), decodedFrame.step, width, height, cudaMemcpyDeviceToDevice, StreamAccessor::getStream(stream)); } - else if (colorFormat == ColorFormat::YUV) { + else if (colorFormat == ColorFormat::NV_NV12) { decodedFrame.copyTo(outFrame, stream); } } @@ -100,7 +101,7 @@ namespace bool set(const VideoReaderProps propertyId, const double propertyVal) CV_OVERRIDE; - void set(const ColorFormat _colorFormat) CV_OVERRIDE; + bool set(const ColorFormat colorFormat_) CV_OVERRIDE; bool get(const VideoReaderProps propertyId, double& propertyVal) const CV_OVERRIDE; bool getVideoReaderProps(const VideoReaderProps propertyId, double& propertyValOut, double propertyValIn) const CV_OVERRIDE; @@ -273,8 +274,16 @@ namespace return false; } - void VideoReaderImpl::set(const ColorFormat _colorFormat) { - colorFormat = _colorFormat; + bool ValidColorFormat(const ColorFormat colorFormat) { + if (colorFormat == ColorFormat::BGRA || colorFormat == ColorFormat::BGR || colorFormat == ColorFormat::GRAY || colorFormat == ColorFormat::NV_NV12) + return true; + return false; + } + + bool VideoReaderImpl::set(const ColorFormat colorFormat_) { + if (!ValidColorFormat(colorFormat_)) return false; + colorFormat = colorFormat_; + return true; } bool VideoReaderImpl::get(const VideoReaderProps propertyId, double& propertyVal) const { diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index 6e2b82f8b8..cd184580ef 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -48,17 +48,17 @@ using namespace cv::cuda; #if !defined(HAVE_NVCUVENC) -Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const Ptr, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } -Ptr createVideoWriter(const String&, const Size, const CODEC_VW, const double, const COLOR_FORMAT_VW, const EncoderParams&, const Ptr, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const Codec, const double, const ColorFormat, const Ptr, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } +Ptr createVideoWriter(const String&, const Size, const Codec, const double, const ColorFormat, const EncoderParams&, const Ptr, const cv::cuda::Stream&) { throw_no_cuda(); return Ptr(); } #else // !defined HAVE_NVCUVENC -NV_ENC_BUFFER_FORMAT EncBufferFormat(const COLOR_FORMAT_VW colorFormat); -int NChannels(const COLOR_FORMAT_VW colorFormat); -GUID CodecGuid(const CODEC_VW codec); +NV_ENC_BUFFER_FORMAT EncBufferFormat(const ColorFormat colorFormat); +int NChannels(const ColorFormat colorFormat); +GUID CodecGuid(const Codec codec); void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen); -GUID EncodingProfileGuid(const ENC_PROFILE encodingProfile); -GUID EncodingPresetGuid(const ENC_PRESET nvPreset); +GUID EncodingProfileGuid(const EncodeProfile encodingProfile); +GUID EncodingPresetGuid(const EncodePreset nvPreset); bool Equal(const GUID& g1, const GUID& g2); EncoderParams::EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT), @@ -107,21 +107,21 @@ void RawVideoWriter::onEncoded(std::vector> vPacket) { class VideoWriterImpl : public VideoWriter { public: - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const Stream& stream = Stream::Null()); - VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const Codec codec, const double fps, + const ColorFormat colorFormat, const Stream& stream = Stream::Null()); + VideoWriterImpl(const Ptr& videoWriter, const Size frameSize, const Codec codec, const double fps, + const ColorFormat colorFormat, const EncoderParams& encoderParams, const Stream& stream = Stream::Null()); ~VideoWriterImpl(); void write(InputArray frame); EncoderParams getEncoderParams() const; void release(); private: - void Init(const CODEC_VW codec, const double fps, const Size frameSz); + void Init(const Codec codec, const double fps, const Size frameSz); void InitializeEncoder(const GUID codec, const double fps); void CopyToNvSurface(const InputArray src); Ptr encoderCallback; - COLOR_FORMAT_VW colorFormat = COLOR_FORMAT_VW::UNDEFINED; + ColorFormat colorFormat = ColorFormat::UNDEFINED; NV_ENC_BUFFER_FORMAT surfaceFormat = NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_UNDEFINED; EncoderParams encoderParams; Stream stream = Stream::Null(); @@ -131,43 +131,43 @@ class VideoWriterImpl : public VideoWriter CUcontext cuContext; }; -NV_ENC_BUFFER_FORMAT EncBufferFormat(const COLOR_FORMAT_VW colorFormat) { +NV_ENC_BUFFER_FORMAT EncBufferFormat(const ColorFormat colorFormat) { switch (colorFormat) { - case BGR: return NV_ENC_BUFFER_FORMAT_ARGB; - case RGB: return NV_ENC_BUFFER_FORMAT_ABGR; - case BGRA: return NV_ENC_BUFFER_FORMAT_ARGB; - case RGBA: return NV_ENC_BUFFER_FORMAT_ABGR; - case GRAY: - case NV_NV12: return NV_ENC_BUFFER_FORMAT_NV12; - case NV_YV12: return NV_ENC_BUFFER_FORMAT_YV12; - case NV_IYUV: return NV_ENC_BUFFER_FORMAT_IYUV; - case NV_YUV444: return NV_ENC_BUFFER_FORMAT_YUV444; - case NV_AYUV: return NV_ENC_BUFFER_FORMAT_AYUV; + case ColorFormat::BGR: return NV_ENC_BUFFER_FORMAT_ARGB; + case ColorFormat::RGB: return NV_ENC_BUFFER_FORMAT_ABGR; + case ColorFormat::BGRA: return NV_ENC_BUFFER_FORMAT_ARGB; + case ColorFormat::RGBA: return NV_ENC_BUFFER_FORMAT_ABGR; + case ColorFormat::GRAY: + case ColorFormat::NV_NV12: return NV_ENC_BUFFER_FORMAT_NV12; + case ColorFormat::NV_YV12: return NV_ENC_BUFFER_FORMAT_YV12; + case ColorFormat::NV_IYUV: return NV_ENC_BUFFER_FORMAT_IYUV; + case ColorFormat::NV_YUV444: return NV_ENC_BUFFER_FORMAT_YUV444; + case ColorFormat::NV_AYUV: return NV_ENC_BUFFER_FORMAT_AYUV; default: return NV_ENC_BUFFER_FORMAT_UNDEFINED; } } -int NChannels(const COLOR_FORMAT_VW colorFormat) { +int NChannels(const ColorFormat colorFormat) { switch (colorFormat) { - case BGR: - case RGB: - case NV_IYUV: - case NV_YUV444: return 3; - case RGBA: - case BGRA: - case NV_AYUV: return 4; - case GRAY: - case NV_NV12: - case NV_YV12: return 1; + case ColorFormat::BGR: + case ColorFormat::RGB: + case ColorFormat::NV_IYUV: + case ColorFormat::NV_YUV444: return 3; + case ColorFormat::RGBA: + case ColorFormat::BGRA: + case ColorFormat::NV_AYUV: return 4; + case ColorFormat::GRAY: + case ColorFormat::NV_NV12: + case ColorFormat::NV_YV12: return 1; default: return 0; } } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat_, const EncoderParams& encoderParams_, const Stream& stream_) : +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, const Size frameSz, const Codec codec, const double fps, + const ColorFormat colorFormat_, const EncoderParams& encoderParams_, const Stream& stream_) : encoderCallback(encoderCallBack_), colorFormat(colorFormat_), encoderParams(encoderParams_), stream(stream_) { - CV_Assert(colorFormat != UNDEFINED); + CV_Assert(colorFormat != ColorFormat::UNDEFINED); surfaceFormat = EncBufferFormat(colorFormat); if (surfaceFormat == NV_ENC_BUFFER_FORMAT_UNDEFINED) { String msg = cv::format("Unsupported input surface format: %i", colorFormat); @@ -178,8 +178,8 @@ VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallBack_, c Init(codec, fps, frameSz); } -VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const CODEC_VW codec, const double fps, - const COLOR_FORMAT_VW colorFormat, const Stream& stream) : +VideoWriterImpl::VideoWriterImpl(const Ptr& encoderCallback, const Size frameSz, const Codec codec, const double fps, + const ColorFormat colorFormat, const Stream& stream) : VideoWriterImpl(encoderCallback, frameSz, codec, fps, colorFormat, EncoderParams(), stream) { } @@ -194,10 +194,10 @@ VideoWriterImpl::~VideoWriterImpl() { release(); } -GUID CodecGuid(const CODEC_VW codec) { +GUID CodecGuid(const Codec codec) { switch (codec) { - case CODEC_VW::H264: return NV_ENC_CODEC_H264_GUID; - case CODEC_VW::HEVC: return NV_ENC_CODEC_HEVC_GUID; + case Codec::H264: return NV_ENC_CODEC_H264_GUID; + case Codec::HEVC: return NV_ENC_CODEC_HEVC_GUID; default: break; } std::string msg = "Unknown codec: cudacodec::VideoWriter only supports CODEC_VW::H264 and CODEC_VW::HEVC"; @@ -205,7 +205,7 @@ GUID CodecGuid(const CODEC_VW codec) { CV_Error(Error::StsUnsupportedFormat, msg); } -void VideoWriterImpl::Init(const CODEC_VW codec, const double fps, const Size frameSz) { +void VideoWriterImpl::Init(const Codec codec, const double fps, const Size frameSz) { // init context GpuMat temp(1, 1, CV_8UC1); temp.release(); @@ -241,7 +241,7 @@ void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen) frameRateDen = frame_rate_base; } -GUID EncodingProfileGuid(const ENC_PROFILE encodingProfile) { +GUID EncodingProfileGuid(const EncodeProfile encodingProfile) { switch (encodingProfile) { case(ENC_CODEC_PROFILE_AUTOSELECT): return NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID; case(ENC_H264_PROFILE_BASELINE): return NV_ENC_H264_PROFILE_BASELINE_GUID; @@ -261,7 +261,7 @@ GUID EncodingProfileGuid(const ENC_PROFILE encodingProfile) { CV_Error(Error::StsUnsupportedFormat, msg); } -GUID EncodingPresetGuid(const ENC_PRESET nvPreset) { +GUID EncodingPresetGuid(const EncodePreset nvPreset) { switch (nvPreset) { case ENC_PRESET_P1: return NV_ENC_PRESET_P1_GUID; case ENC_PRESET_P2: return NV_ENC_PRESET_P2_GUID; @@ -306,8 +306,8 @@ void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps) pEnc->CreateEncoder(&initializeParams); } -inline bool CvFormat(const COLOR_FORMAT_VW cf) { - if (cf == BGR || cf == RGB || cf == BGRA || cf == RGBA || cf == GRAY) +inline bool CvFormat(const ColorFormat cf) { + if (cf == ColorFormat::BGR || cf == ColorFormat::RGB || cf == ColorFormat::BGRA || cf == ColorFormat::RGBA || cf == ColorFormat::GRAY) return true; return false; } @@ -319,7 +319,7 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) if (CvFormat(colorFormat)) CV_Assert(src.size() == Size(pEnc->GetEncodeWidth(), pEnc->GetEncodeHeight())); Npp8u* dst = (Npp8u*)encoderInputFrame->inputPtr; - if (colorFormat == COLOR_FORMAT_VW::BGR || colorFormat == COLOR_FORMAT_VW::RGB) { + if (colorFormat == ColorFormat::BGR || colorFormat == ColorFormat::RGB) { GpuMat srcDevice; if (src.isGpuMat()) srcDevice = src.getGpuMat(); @@ -329,7 +329,7 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) else srcDevice.upload(src); } - if (colorFormat == COLOR_FORMAT_VW::BGR) { + if (colorFormat == ColorFormat::BGR) { GpuMat dstGpuMat(pEnc->GetEncodeHeight(), pEnc->GetEncodeWidth(), CV_8UC4, dst, encoderInputFrame->pitch); cuda::cvtColor(srcDevice, dstGpuMat, COLOR_BGR2BGRA, 0, stream); } @@ -338,7 +338,7 @@ void VideoWriterImpl::CopyToNvSurface(const InputArray src) cuda::cvtColor(srcDevice, dstGpuMat, COLOR_RGB2RGBA, 0, stream); } } - else if (colorFormat == COLOR_FORMAT_VW::GRAY) { + else if (colorFormat == ColorFormat::GRAY) { const cudaMemcpyKind memcpyKind = src.isGpuMat() ? cudaMemcpyDeviceToDevice : cudaMemcpyHostToDevice; const void* srcPtr = src.isGpuMat() ? src.getGpuMat().data : src.getMat().data; const size_t srcPitch = src.isGpuMat() ? src.getGpuMat().step : src.getMat().step; @@ -374,14 +374,14 @@ EncoderParams VideoWriterImpl::getEncoderParams() const { return encoderParams; }; -Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, const COLOR_FORMAT_VW colorFormat, +Ptr createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat, Ptr encoderCallback, const Stream& stream) { encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName); return makePtr(encoderCallback, frameSize, codec, fps, colorFormat, stream); } -Ptr createVideoWriter(const String& fileName, const Size frameSize, const CODEC_VW codec, const double fps, const COLOR_FORMAT_VW colorFormat, +Ptr createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat, const EncoderParams& params, Ptr encoderCallback, const Stream& stream) { encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName); diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index d4d42b1698..b9b4e9f25c 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -192,7 +192,7 @@ CUDA_TEST_P(Scaling, Reader) GpuMat frameOr; { cv::Ptr readerGs = cv::cudacodec::createVideoReader(inputFile); - readerGs->set(cudacodec::ColorFormat::GRAY); + ASSERT_TRUE(readerGs->set(cudacodec::ColorFormat::GRAY)); ASSERT_TRUE(readerGs->nextFrame(frameOr)); } @@ -203,7 +203,7 @@ CUDA_TEST_P(Scaling, Reader) params.targetRoi = Rect(static_cast(params.targetSz.width * targetRoiIn.x), static_cast(params.targetSz.height * targetRoiIn.y), static_cast(params.targetSz.width * targetRoiIn.width), static_cast(params.targetSz.height * targetRoiIn.height)); cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, params); - reader->set(cudacodec::ColorFormat::GRAY); + ASSERT_TRUE(reader->set(cudacodec::ColorFormat::GRAY)); GpuMat frame; ASSERT_TRUE(reader->nextFrame(frame)); const cudacodec::FormatInfo format = reader->format(); @@ -240,24 +240,25 @@ CUDA_TEST_P(Video, Reader) {cudacodec::ColorFormat::GRAY,1}, {cudacodec::ColorFormat::BGR,3}, {cudacodec::ColorFormat::BGRA,4}, - {cudacodec::ColorFormat::YUV,1} + {cudacodec::ColorFormat::NV_NV12,1} }; std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../" + GET_PARAM(1); cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); + ASSERT_FALSE(reader->set(cudacodec::ColorFormat::RGB)); cv::cudacodec::FormatInfo fmt = reader->format(); cv::cuda::GpuMat frame; for (int i = 0; i < 10; i++) { // request a different colour format for each frame const std::pair< cudacodec::ColorFormat, int>& formatToChannels = formatsToChannels[i % formatsToChannels.size()]; - reader->set(formatToChannels.first); + ASSERT_TRUE(reader->set(formatToChannels.first)); double colorFormat; ASSERT_TRUE(reader->get(cudacodec::VideoReaderProps::PROP_COLOR_FORMAT, colorFormat) && static_cast(colorFormat) == formatToChannels.first); ASSERT_TRUE(reader->nextFrame(frame)); if(!fmt.valid) fmt = reader->format(); - const int height = formatToChannels.first == cudacodec::ColorFormat::YUV ? static_cast(1.5 * fmt.height) : fmt.height; + const int height = formatToChannels.first == cudacodec::ColorFormat::NV_NV12 ? static_cast(1.5 * fmt.height) : fmt.height; ASSERT_TRUE(frame.cols == fmt.width && frame.rows == height); ASSERT_FALSE(frame.empty()); ASSERT_TRUE(frame.channels() == formatToChannels.second); @@ -439,9 +440,9 @@ struct TransCode : testing::TestWithParam CUDA_TEST_P(TransCode, H264ToH265) { const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.h264"; - constexpr cv::cudacodec::COLOR_FORMAT_VW colorFormat = cv::cudacodec::COLOR_FORMAT_VW::NV_NV12; + constexpr cv::cudacodec::ColorFormat colorFormat = cv::cudacodec::ColorFormat::NV_NV12; constexpr double fps = 25; - const cudacodec::CODEC_VW codec = cudacodec::CODEC_VW::HEVC; + const cudacodec::Codec codec = cudacodec::Codec::HEVC; const std::string ext = ".h265"; const std::string outputFile = cv::tempfile(ext.c_str()); constexpr int nFrames = 5; @@ -449,7 +450,7 @@ CUDA_TEST_P(TransCode, H264ToH265) { cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); cv::cudacodec::FormatInfo fmt = reader->format(); - reader->set(cudacodec::ColorFormat::YUV); + reader->set(cudacodec::ColorFormat::NV_NV12); cv::Ptr writer; cv::cuda::GpuMat frame; cv::cuda::Stream stream; @@ -495,22 +496,22 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, TransCode, ALL_DEVICES); //========================================================================== -void CvtColor(const Mat& in, Mat& out, const cudacodec::COLOR_FORMAT_VW surfaceFormatCv) { +void CvtColor(const Mat& in, Mat& out, const cudacodec::ColorFormat surfaceFormatCv) { switch (surfaceFormatCv) { - case(cudacodec::COLOR_FORMAT_VW::RGB): + case(cudacodec::ColorFormat::RGB): return cv::cvtColor(in, out, COLOR_BGR2RGB); - case(cudacodec::COLOR_FORMAT_VW::BGRA): + case(cudacodec::ColorFormat::BGRA): return cv::cvtColor(in, out, COLOR_BGR2BGRA); - case(cudacodec::COLOR_FORMAT_VW::RGBA): + case(cudacodec::ColorFormat::RGBA): return cv::cvtColor(in, out, COLOR_BGR2RGBA); - case(cudacodec::COLOR_FORMAT_VW::GRAY): + case(cudacodec::ColorFormat::GRAY): return cv::cvtColor(in, out, COLOR_BGR2GRAY); default: in.copyTo(out); } } -PARAM_TEST_CASE(Write, cv::cuda::DeviceInfo, bool, cv::cudacodec::CODEC_VW, double, cv::cudacodec::COLOR_FORMAT_VW) +PARAM_TEST_CASE(Write, cv::cuda::DeviceInfo, bool, cv::cudacodec::Codec, double, cv::cudacodec::ColorFormat) { }; @@ -519,10 +520,10 @@ CUDA_TEST_P(Write, Writer) cv::cuda::setDevice(GET_PARAM(0).deviceID()); const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; const bool deviceSrc = GET_PARAM(1); - const cudacodec::CODEC_VW codec = GET_PARAM(2); + const cudacodec::Codec codec = GET_PARAM(2); const double fps = GET_PARAM(3); - const cv::cudacodec::COLOR_FORMAT_VW colorFormat = GET_PARAM(4); - const std::string ext = codec == cudacodec::CODEC_VW::H264 ? ".h264" : ".hevc"; + const cv::cudacodec::ColorFormat colorFormat = GET_PARAM(4); + const std::string ext = codec == cudacodec::Codec::H264 ? ".h264" : ".hevc"; const std::string outputFile = cv::tempfile(ext.c_str()); constexpr int nFrames = 5; Size frameSz; @@ -568,9 +569,9 @@ CUDA_TEST_P(Write, Writer) #define DEVICE_SRC true, false #define FPS 10, 29.7 -#define CODEC cv::cudacodec::CODEC_VW::H264, cv::cudacodec::CODEC_VW::HEVC -#define COLOR_FORMAT cv::cudacodec::COLOR_FORMAT_VW::BGR, cv::cudacodec::COLOR_FORMAT_VW::RGB, cv::cudacodec::COLOR_FORMAT_VW::BGRA, \ -cv::cudacodec::COLOR_FORMAT_VW::RGBA, cv::cudacodec::COLOR_FORMAT_VW::GRAY +#define CODEC cv::cudacodec::Codec::H264, cv::cudacodec::Codec::HEVC +#define COLOR_FORMAT cv::cudacodec::ColorFormat::BGR, cv::cudacodec::ColorFormat::RGB, cv::cudacodec::ColorFormat::BGRA, \ +cv::cudacodec::ColorFormat::RGBA, cv::cudacodec::ColorFormat::GRAY INSTANTIATE_TEST_CASE_P(CUDA_Codec, Write, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(CODEC), testing::Values(FPS), testing::Values(COLOR_FORMAT))); @@ -584,11 +585,11 @@ struct EncoderParams : testing::TestWithParam devInfo = GetParam(); cv::cuda::setDevice(devInfo.deviceID()); // Fixed params for CBR test - params.nvPreset = cv::cudacodec::ENC_PRESET::ENC_PRESET_P7; - params.tuningInfo = cv::cudacodec::ENC_TUNING_INFO::ENC_TUNING_INFO_HIGH_QUALITY; - params.encodingProfile = cv::cudacodec::ENC_PROFILE::ENC_H264_PROFILE_MAIN; - params.rateControlMode = cv::cudacodec::ENC_PARAMS_RC_MODE::ENC_PARAMS_RC_CBR; - params.multiPassEncoding = cv::cudacodec::ENC_MULTI_PASS::ENC_TWO_PASS_FULL_RESOLUTION; + params.nvPreset = cv::cudacodec::EncodePreset::ENC_PRESET_P7; + params.tuningInfo = cv::cudacodec::EncodeTuningInfo::ENC_TUNING_INFO_HIGH_QUALITY; + params.encodingProfile = cv::cudacodec::EncodeProfile::ENC_H264_PROFILE_MAIN; + params.rateControlMode = cv::cudacodec::EncodeParamsRcMode::ENC_PARAMS_RC_CBR; + params.multiPassEncoding = cv::cudacodec::EncodeMultiPass::ENC_TWO_PASS_FULL_RESOLUTION; params.averageBitRate = 1000000; params.maxBitRate = 0; params.targetQuality = 0; @@ -601,7 +602,7 @@ CUDA_TEST_P(EncoderParams, Writer) { const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4"; constexpr double fps = 25.0; - constexpr cudacodec::CODEC_VW codec = cudacodec::CODEC_VW::H264; + constexpr cudacodec::Codec codec = cudacodec::Codec::H264; const std::string ext = ".h264"; const std::string outputFile = cv::tempfile(ext.c_str()); Size frameSz; @@ -609,7 +610,7 @@ CUDA_TEST_P(EncoderParams, Writer) { cv::VideoCapture reader(inputFile); ASSERT_TRUE(reader.isOpened()); - const cv::cudacodec::COLOR_FORMAT_VW colorFormat = cv::cudacodec::COLOR_FORMAT_VW::BGR; + const cv::cudacodec::ColorFormat colorFormat = cv::cudacodec::ColorFormat::BGR; cv::Ptr writer; cv::Mat frame; cv::cuda::GpuMat dFrame; From ea1fe9763540513c00efff2053e1d08863092088 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:24:19 +0300 Subject: [PATCH 41/99] Remove slash. --- modules/cudacodec/include/opencv2/cudacodec.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 12d06de7c4..7a77251681 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -93,7 +93,7 @@ enum Codec Uncompressed_UYVY = (('U' << 24) | ('Y' << 16) | ('V' << 8) | ('Y')) //!< UYVY (4:2:2) }; -/** @brief ColorFormat for the frame returned by VideoReader::nextFrame()/VideoReader::retrieve() or used to initialize a VideoWriter. +/** @brief ColorFormat for the frame returned by VideoReader::nextFrame() and VideoReader::retrieve() or used to initialize a VideoWriter. */ enum class ColorFormat { UNDEFINED = 0, From 1754d03123052f17245b28b8499f531e20cb606d Mon Sep 17 00:00:00 2001 From: Benjamin Buch Date: Wed, 16 Nov 2022 12:18:46 +0100 Subject: [PATCH 42/99] Merge pull request #3285 from bebuch:4.x make CVV module work with Qt6 * make CVV module work with Qt6 * workaround non-existence of Qt::SplitBehavior for Qt versions less 5.15 * workaround Qt5/Qt6 differences * workaround differences between Qt 5.11 and later versions * fix regressions for Qt 5.5 --- modules/cvv/CMakeLists.txt | 16 +++--- .../cvv/src/qtutil/matchview/matchscene.cpp | 2 +- .../matchview/singlecolorkeypointpen.cpp | 2 +- .../qtutil/matchview/singlecolormatchpen.cpp | 2 +- .../qtutil/matchview/zoomableproxyobject.cpp | 6 +++ modules/cvv/src/qtutil/zoomableimage.cpp | 10 +++- modules/cvv/src/qtutil/zoomableimage.hpp | 2 +- modules/cvv/src/stfl/stfl_engine.hpp | 53 +++++++++++-------- 8 files changed, 60 insertions(+), 33 deletions(-) diff --git a/modules/cvv/CMakeLists.txt b/modules/cvv/CMakeLists.txt index c264ef1ead..645e7b3944 100644 --- a/modules/cvv/CMakeLists.txt +++ b/modules/cvv/CMakeLists.txt @@ -5,16 +5,20 @@ endif() set(the_description "Debug visualization framework") ocv_add_module(cvv opencv_core opencv_imgproc opencv_features2d WRAP python) - ocv_warnings_disable(CMAKE_CXX_FLAGS -Wshadow -Wmissing-declarations) -# Qt5 +# Qt +set(CVV_QT_MODULES Core Gui Widgets) +if(QT_VERSION_MAJOR EQUAL 6) + list(APPEND CVV_QT_MODULES Core5Compat) +endif() + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${CVV_QT_MODULES}) + set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) -foreach(dt5_dep Core Gui Widgets) - add_definitions(${Qt5${dt5_dep}_DEFINITIONS}) - include_directories(${Qt5${dt5_dep}_INCLUDE_DIRS}) - list(APPEND CVV_LIBRARIES ${Qt5${dt5_dep}_LIBRARIES}) +foreach(module ${CVV_QT_MODULES}) + list(APPEND CVV_LIBRARIES ${Qt${QT_VERSION_MAJOR}${module}_LIBRARIES}) endforeach() ocv_glob_module_sources() diff --git a/modules/cvv/src/qtutil/matchview/matchscene.cpp b/modules/cvv/src/qtutil/matchview/matchscene.cpp index bc68fcd62b..5d2a983444 100644 --- a/modules/cvv/src/qtutil/matchview/matchscene.cpp +++ b/modules/cvv/src/qtutil/matchview/matchscene.cpp @@ -153,7 +153,7 @@ void MatchScene::rightClick(const QPoint &pos) pmap = rightImage_->visibleImage(); } }else{ - pmap = QPixmap::grabWidget(graphicView_->viewport()); + pmap = graphicView_->viewport()->grab(); } pmap.save(fileName, 0, 100); } diff --git a/modules/cvv/src/qtutil/matchview/singlecolorkeypointpen.cpp b/modules/cvv/src/qtutil/matchview/singlecolorkeypointpen.cpp index 0c994ff697..0dd17c4730 100644 --- a/modules/cvv/src/qtutil/matchview/singlecolorkeypointpen.cpp +++ b/modules/cvv/src/qtutil/matchview/singlecolorkeypointpen.cpp @@ -15,7 +15,7 @@ SingleColorKeyPen::SingleColorKeyPen(std::vector, QWidget *parent) auto layout = util::make_unique(); auto button = util::make_unique("Color Dialog"); - layout->setMargin(0); + layout->setContentsMargins(QMargins()); connect(colordia_, SIGNAL(currentColorChanged(const QColor &)), this, SLOT(updateColor(const QColor &))); diff --git a/modules/cvv/src/qtutil/matchview/singlecolormatchpen.cpp b/modules/cvv/src/qtutil/matchview/singlecolormatchpen.cpp index 28dfc5b2ac..a847050ba2 100644 --- a/modules/cvv/src/qtutil/matchview/singlecolormatchpen.cpp +++ b/modules/cvv/src/qtutil/matchview/singlecolormatchpen.cpp @@ -23,7 +23,7 @@ SingleColorMatchPen::SingleColorMatchPen(std::vector, QWidget *paren connect(button.get(), SIGNAL(clicked(bool)), this, SLOT(colorButtonClicked())); - layout->setMargin(0); + layout->setContentsMargins(QMargins()); layout->addWidget(button.release()); setLayout(layout.release()); diff --git a/modules/cvv/src/qtutil/matchview/zoomableproxyobject.cpp b/modules/cvv/src/qtutil/matchview/zoomableproxyobject.cpp index f314497bae..60b1b11839 100644 --- a/modules/cvv/src/qtutil/matchview/zoomableproxyobject.cpp +++ b/modules/cvv/src/qtutil/matchview/zoomableproxyobject.cpp @@ -19,10 +19,16 @@ ZoomableProxyObject::ZoomableProxyObject(ZoomableImage *zoom) void ZoomableProxyObject::wheelEvent(QGraphicsSceneWheelEvent *event) { QPoint delta{ event->delta(), 0 }; + QWheelEvent newEvent{ event->pos(), event->screenPos(), delta, delta, +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + event->buttons(), event->modifiers(), + Qt::NoScrollPhase, true }; +#else event->delta(), event->orientation(), event->buttons(), event->modifiers() }; +#endif image_->wheelEvent(&newEvent); } diff --git a/modules/cvv/src/qtutil/zoomableimage.cpp b/modules/cvv/src/qtutil/zoomableimage.cpp index ce62daab61..ffc7e6f55d 100644 --- a/modules/cvv/src/qtutil/zoomableimage.cpp +++ b/modules/cvv/src/qtutil/zoomableimage.cpp @@ -205,7 +205,7 @@ ZoomableImage::ZoomableImage(const cv::Mat &mat, QWidget *parent) view_->setFocusPolicy(Qt::NoFocus); auto layout = util::make_unique(); layout->addWidget(view.release()); - layout->setMargin(0); + layout->setContentsMargins(QMargins()); setLayout(layout.release()); setMat(mat_); // rightklick @@ -398,7 +398,13 @@ QPointF ZoomableImage::mapImagePointToParent(QPointF point) const void ZoomableImage::mouseMoveEvent(QMouseEvent * event) { - QPointF imgPos=view_->mapToScene(view_->mapFromGlobal(event->globalPos())); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QPoint pos = event->globalPosition().toPoint(); +#else + QPoint pos = event->globalPos(); +#endif + + QPointF imgPos=view_->mapToScene(view_->mapFromGlobal(pos)); bool inImage=(imgPos.x()>=0) &&(imgPos.y()>=0) &&(imgPos.x()<=imageWidth()) diff --git a/modules/cvv/src/qtutil/zoomableimage.hpp b/modules/cvv/src/qtutil/zoomableimage.hpp index 785a238ab6..1340b626e7 100644 --- a/modules/cvv/src/qtutil/zoomableimage.hpp +++ b/modules/cvv/src/qtutil/zoomableimage.hpp @@ -208,7 +208,7 @@ class ZoomableImage : public QWidget */ QPixmap visibleImage() const { - return QPixmap::grabWidget(view_->viewport()); + return view_->viewport()->grab(); } /** diff --git a/modules/cvv/src/stfl/stfl_engine.hpp b/modules/cvv/src/stfl/stfl_engine.hpp index bb1dc04be5..1d1139c24d 100644 --- a/modules/cvv/src/stfl/stfl_engine.hpp +++ b/modules/cvv/src/stfl/stfl_engine.hpp @@ -22,6 +22,17 @@ #include "element_group.hpp" #include "../qtutil/util.hpp" + +// WORKAROUND: +// - Qt::SplitBehavior introduced in 5.14, QString::SplitBehavior was removed in Qt6 +// - Support required part of Qt::SplitBehavior from 5.15 for older Qt versions +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) +namespace Qt { + static constexpr QString::SplitBehavior SkipEmptyParts = QString::SkipEmptyParts; +} +#endif + + namespace cvv { namespace stfl @@ -162,7 +173,7 @@ template class STFLEngine } QList elemList; QStringList cmdStrings = - query.split("#", QString::SkipEmptyParts); + query.split("#", Qt::SkipEmptyParts); elemList = executeFilters(elements, cmdStrings); elemList = executeSortCmds(elemList, cmdStrings); auto groups = executeGroupCmds(elemList, cmdStrings); @@ -549,7 +560,7 @@ template class STFLEngine { using namespace std::placeholders; QStringList arr = - cmdString.split(" ", QString::SkipEmptyParts); + cmdString.split(" ", Qt::SkipEmptyParts); QString cmd; if (arr.empty()) { @@ -570,7 +581,7 @@ template class STFLEngine else if (isFilterCSCmd(cmd)) { QStringList arguments = arr.join("").split( - ",", QString::SkipEmptyParts); + ",", Qt::SkipEmptyParts); std::for_each(arguments.begin(), arguments.end(), [](QString &str) { str.replace("\\,", ","); }); @@ -609,7 +620,7 @@ template class STFLEngine for (QString cmdString : cmdStrings) { QStringList arr = - cmdString.split(" ", QString::SkipEmptyParts); + cmdString.split(" ", Qt::SkipEmptyParts); if (arr.size() < 2) { continue; @@ -619,7 +630,7 @@ template class STFLEngine { arr.removeFirst(); } - arr = arr.join(" ").split(",", QString::SkipEmptyParts); + arr = arr.join(" ").split(",", Qt::SkipEmptyParts); for (QString cmdPart : arr) { cmdPart = cmdPart.trimmed(); @@ -647,7 +658,7 @@ template class STFLEngine if (sortCmd.second) { auto sortFunc = sortFuncs[sortCmd.first]; - qStableSort(resList.begin(), resList.end(), + std::stable_sort(resList.begin(), resList.end(), [&](const Element &elem1, const Element &elem2) { return sortFunc(elem1, elem2); }); @@ -655,7 +666,7 @@ template class STFLEngine else { auto sortFunc = sortFuncs[sortCmd.first]; - qStableSort(resList.begin(), resList.end(), + std::stable_sort(resList.begin(), resList.end(), [&](const Element &elem1, const Element &elem2) { return sortFunc(elem2, elem1); }); @@ -675,7 +686,7 @@ template class STFLEngine for (QString cmdString : cmdStrings) { QStringList arr = - cmdString.split(" ", QString::SkipEmptyParts); + cmdString.split(" ", Qt::SkipEmptyParts); if (arr.size() < 2) { continue; @@ -689,7 +700,7 @@ template class STFLEngine { arr.removeFirst(); } - arr = arr.join("").split(",", QString::SkipEmptyParts); + arr = arr.join("").split(",", Qt::SkipEmptyParts); for (QString cmdPart : arr) { QStringList cmdPartList = cmdPart.split(" "); @@ -720,7 +731,7 @@ template class STFLEngine for (auto it = groups.begin(); it != groups.end(); ++it) { ElementGroup elementGroup( - it->first.split("\\|", QString::SkipEmptyParts), + it->first.split("\\|", Qt::SkipEmptyParts), it->second); groupList.push_back(elementGroup); } @@ -733,7 +744,7 @@ template class STFLEngine for (QString cmdString : cmdStrings) { QStringList arr = - cmdString.split(" ", QString::SkipEmptyParts); + cmdString.split(" ", Qt::SkipEmptyParts); if (arr.isEmpty()) { continue; @@ -743,7 +754,7 @@ template class STFLEngine { continue; } - arr = arr.join("").split(",", QString::SkipEmptyParts); + arr = arr.join("").split(",", Qt::SkipEmptyParts); additionalCommandFuncs[cmd](arr, groups); } } @@ -762,11 +773,11 @@ template class STFLEngine if (cmd == "group" || cmd == "sort") { int frontCut = - std::min(1 + (hasByString ? 1 : 0), tokens.size()); - tokens = cmdQuery.split(" ", QString::SkipEmptyParts) + std::min(size_t(hasByString ? 2 : 1), size_t(tokens.size())); + tokens = cmdQuery.split(" ", Qt::SkipEmptyParts) .mid(frontCut, tokens.size()); QStringList args = tokens.join(" ").split( - ",", QString::SkipEmptyParts); + ",", Qt::SkipEmptyParts); args.removeDuplicates(); for (auto &arg : args) { @@ -806,7 +817,7 @@ template class STFLEngine else { QStringList args = rejoined.split( - ",", QString::SkipEmptyParts); + ",", Qt::SkipEmptyParts); if (isFilterCmd(cmd)) { suggs = getSuggestionsForFilterCSCmd(cmd, args); @@ -902,7 +913,7 @@ template class STFLEngine QStringList getSuggestionsForFilterCmd(const QString &cmd, const QString &argument) { - QStringList pool(filterPool[cmd].toList()); + QStringList pool(filterPool[cmd].values()); return sortStringsByStringEquality(pool, argument); } @@ -918,7 +929,7 @@ template class STFLEngine { last = args[args.size() - 1]; } - QStringList pool(filterCSPool[cmd].toList()); + QStringList pool(filterCSPool[cmd].values()); QStringList list = sortStringsByStringEquality(pool, last); for (QString &item : list) { @@ -1022,7 +1033,7 @@ template class STFLEngine void addQueryToStore(QString query) { QStringList storedCmds = getStoredCmds(); - QStringList cmds = query.split("#", QString::SkipEmptyParts); + QStringList cmds = query.split("#", Qt::SkipEmptyParts); cmds.removeDuplicates(); for (QString cmd : cmds) { @@ -1082,12 +1093,12 @@ template class STFLEngine { QMap weightedStrings; auto compareWithWords = - compareWith.split(" ", QString::SkipEmptyParts); + compareWith.split(" ", Qt::SkipEmptyParts); for (const QString &str : strings) { int strEqu = 0xFFFFFF; // infinity... for (auto word : - str.split(" ", QString::SkipEmptyParts)) + str.split(" ", Qt::SkipEmptyParts)) { auto wordA = word.leftJustified(15, ' '); for (const auto &word2 : compareWithWords) From 9b87bed1e25fb89008f9f3a149176b42470c1c44 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 22 Nov 2022 10:14:18 +0200 Subject: [PATCH 43/99] Move EncoderParams constructor and add build/run instructions/comments --- modules/cudacodec/include/opencv2/cudacodec.hpp | 14 +++++++++++++- modules/cudacodec/src/video_writer.cpp | 6 ------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 7a77251681..d61673cd71 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -184,7 +184,10 @@ struct CV_EXPORTS_W_SIMPLE EncodeQp struct CV_EXPORTS_W_SIMPLE EncoderParams { public: - CV_WRAP EncoderParams(); + CV_WRAP EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT), + rateControlMode(ENC_PARAMS_RC_VBR), multiPassEncoding(ENC_MULTI_PASS_DISABLED), constQp({ 0,0,0 }), averageBitRate(0), maxBitRate(0), + targetQuality(30), gopLength(0) {}; + CV_PROP_RW EncodePreset nvPreset; CV_PROP_RW EncodeTuningInfo tuningInfo; CV_PROP_RW EncodeProfile encodingProfile; @@ -218,6 +221,11 @@ class CV_EXPORTS_W EncoderCallback { }; /** @brief Video writer interface. + +Available when built with WITH_NVCUVENC=ON while Nvidia's Video Codec SDK is installed. + +Encoding support is dependent on the GPU, refer to the Nvidia Video Codec SDK Video Encode and Decode GPU Support Matrix for details. + @note - An example on how to use the videoWriter class can be found at opencv_source_code/samples/gpu/video_writer.cpp @@ -342,6 +350,10 @@ enum class VideoReaderProps { /** @brief Video reader interface. +Available when built with WITH_NVCUVID=ON while Nvidia's Video Codec SDK is installed. + +Decoding support is dependent on the GPU, refer to the Nvidia Video Codec SDK Video Encode and Decode GPU Support Matrix for details. + @note - An example on how to use the videoReader class can be found at opencv_source_code/samples/gpu/video_reader.cpp diff --git a/modules/cudacodec/src/video_writer.cpp b/modules/cudacodec/src/video_writer.cpp index cd184580ef..db3e2e3630 100644 --- a/modules/cudacodec/src/video_writer.cpp +++ b/modules/cudacodec/src/video_writer.cpp @@ -61,12 +61,6 @@ GUID EncodingProfileGuid(const EncodeProfile encodingProfile); GUID EncodingPresetGuid(const EncodePreset nvPreset); bool Equal(const GUID& g1, const GUID& g2); -EncoderParams::EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT), - rateControlMode(ENC_PARAMS_RC_VBR), multiPassEncoding(ENC_MULTI_PASS_DISABLED), constQp({ 0,0,0 }), averageBitRate(0), maxBitRate(0), - targetQuality(30), gopLength(0) -{ -}; - bool operator==(const EncoderParams& lhs, const EncoderParams& rhs) { return std::tie(lhs.nvPreset, lhs.tuningInfo, lhs.encodingProfile, lhs.rateControlMode, lhs.multiPassEncoding, lhs.constQp.qpInterB, lhs.constQp.qpInterP, lhs.constQp.qpIntra, From 9b9ec60b4b461b431785b1aba91c4009789071f6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 22 Nov 2022 10:56:10 +0200 Subject: [PATCH 44/99] Fix bug in cudacodec preventing it from building against the Nvidia Video Codec SDK 12 --- modules/cudacodec/src/NvEncoder.cpp | 3 +-- modules/cudacodec/src/NvEncoder.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/cudacodec/src/NvEncoder.cpp b/modules/cudacodec/src/NvEncoder.cpp index 78b76c78e5..249f6f1c61 100644 --- a/modules/cudacodec/src/NvEncoder.cpp +++ b/modules/cudacodec/src/NvEncoder.cpp @@ -431,7 +431,7 @@ bool NvEncoder::Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams) NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource(void* pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage, - NV_ENC_FENCE_POINT_D3D12* pInputFencePoint, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint) + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint) { NV_ENC_REGISTER_RESOURCE registerResource = {}; registerResource.version = NV_ENC_REGISTER_RESOURCE_VER; @@ -443,7 +443,6 @@ NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource(void* pBuffer, NV_ENC_INPUT_RE registerResource.bufferFormat = bufferFormat; registerResource.bufferUsage = bufferUsage; registerResource.pInputFencePoint = pInputFencePoint; - registerResource.pOutputFencePoint = pOutputFencePoint; NVENC_API_CALL(m_nvenc.nvEncRegisterResource(m_hEncoder, ®isterResource)); return registerResource.registeredResource; diff --git a/modules/cudacodec/src/NvEncoder.h b/modules/cudacodec/src/NvEncoder.h index c8c281e95a..dd13d2c150 100644 --- a/modules/cudacodec/src/NvEncoder.h +++ b/modules/cudacodec/src/NvEncoder.h @@ -235,7 +235,7 @@ class NvEncoder */ NV_ENC_REGISTERED_PTR RegisterResource(void* pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage = NV_ENC_INPUT_IMAGE, - NV_ENC_FENCE_POINT_D3D12* pInputFencePoint = NULL, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint = NULL); + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint = NULL); /** * @brief This function returns maximum width used to open the encoder session. From d6102ef65f9ccdb37bba0ea9b8254951eb89dc39 Mon Sep 17 00:00:00 2001 From: Ping Wu Date: Sun, 27 Nov 2022 22:23:55 +0800 Subject: [PATCH 45/99] Update dictionary.hpp (#3297) In Macbeth colorchecker, the RGB value of 21.neutral 6.5(.44*) and 23.neutral 3.5(.1.05*) seems to be wrong, while Lab values are correct. Citation: https://babelcolor.com/index_htm_files/RGB%20Coordinates%20of%20the%20Macbeth%20ColorChecker.pdf --- modules/mcc/src/dictionary.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mcc/src/dictionary.hpp b/modules/mcc/src/dictionary.hpp index 85044d9f12..bae1514631 100644 --- a/modules/mcc/src/dictionary.hpp +++ b/modules/mcc/src/dictionary.hpp @@ -62,9 +62,9 @@ const float CChartClassicModelColors[24][9] = { {8.0f, 133.0f, 161.0f, 51.038f, -28.631f, -28.638f, 5.00f, 5.00f, 8.0f}, //18. cyan {243.0f, 243.0f, 242.0f, 96.539f, -0.425f, 1.186f, 0.00f, 9.50f, 0.0f}, //19. white(.05*) {200.0f, 200.0f, 200.0f, 81.257f, -0.638f, -0.335f, 0.00f, 8.00f, 0.0f}, //20. neutral 8(.23*) - {150.0f, 160.0f, 160.0f, 66.766f, -0.734f, -0.504f, 0.00f, 6.50f, 0.0f}, //21. neutral 6.5(.44*) + {160.0f, 160.0f, 160.0f, 66.766f, -0.734f, -0.504f, 0.00f, 6.50f, 0.0f}, //21. neutral 6.5(.44*) {122.0f, 122.0f, 121.0f, 50.867f, -0.153f, -0.270f, 0.00f, 5.00f, 0.0f}, //22. neutral 5(.70*) - {58.0f, 85.0f, 85.0f, 35.656f, -0.421f, -1.231f, 0.00f, 3.50f, 0.0f}, //23. neutral 3.5(.1.05*) + {85.0f, 85.0f, 85.0f, 35.656f, -0.421f, -1.231f, 0.00f, 3.50f, 0.0f}, //23. neutral 3.5(.1.05*) {52.0f, 52.0f, 52.0f, 20.461f, -0.079f, -0.973f, 0.00f, 2.00f, 0.0f}, //24. black(1.50*) }; From 4a1b89d33b5d33fa61d9437702cee796406c8f79 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 11 Oct 2022 02:04:20 +0300 Subject: [PATCH 46/99] move testCharucoCornersCollinear to CharucoBoard move ArucoTutorial tests to new cpp file move estimatePoseBoard() to board class removed aruco_detector add matchImagePoints add getters to CharucoBoard --- modules/aruco/CMakeLists.txt | 2 +- modules/aruco/include/opencv2/aruco.hpp | 130 +- .../include/opencv2/aruco/aruco_calib.hpp | 124 + .../opencv2/aruco/aruco_calib_pose.hpp | 286 - modules/aruco/include/opencv2/aruco/board.hpp | 243 - .../aruco/include/opencv2/aruco/charuco.hpp | 10 +- .../include/opencv2/aruco/dictionary.hpp | 187 - .../aruco/include/opencv2/aruco_detector.hpp | 436 - modules/aruco/misc/java/test/ArucoTest.java | 3 +- modules/aruco/perf/perf_aruco.cpp | 1 + modules/aruco/perf/perf_precomp.hpp | 3 +- modules/aruco/samples/aruco_dict_utils.cpp | 2 +- .../aruco/samples/aruco_samples_utility.hpp | 2 +- modules/aruco/samples/calibrate_camera.cpp | 4 +- modules/aruco/samples/create_board.cpp | 2 +- modules/aruco/samples/create_marker.cpp | 2 +- modules/aruco/samples/detect_board.cpp | 6 +- .../aruco/samples/detect_board_charuco.cpp | 4 +- modules/aruco/samples/detect_markers.cpp | 3 +- .../src/apriltag/apriltag_quad_thresh.cpp | 1666 -- .../src/apriltag/apriltag_quad_thresh.hpp | 119 - .../predefined_dictionaries_apriltag.hpp | 14900 ------------ modules/aruco/src/apriltag/unionfind.hpp | 133 - modules/aruco/src/apriltag/zarray.hpp | 150 - modules/aruco/src/apriltag/zmaxheap.cpp | 207 - modules/aruco/src/apriltag/zmaxheap.hpp | 42 - modules/aruco/src/aruco.cpp | 149 + modules/aruco/src/aruco_calib.cpp | 95 + modules/aruco/src/aruco_calib_pose.cpp | 257 - modules/aruco/src/aruco_detector.cpp | 1261 - modules/aruco/src/aruco_utils.cpp | 50 - modules/aruco/src/aruco_utils.hpp | 44 - modules/aruco/src/board.cpp | 467 - modules/aruco/src/charuco.cpp | 24 +- modules/aruco/src/dictionary.cpp | 465 - modules/aruco/src/precomp.hpp | 1 - modules/aruco/src/predefined_dictionaries.hpp | 20127 ---------------- modules/aruco/test/test_aruco_tutorial.cpp | 216 + modules/aruco/test/test_arucodetection.cpp | 814 - modules/aruco/test/test_boarddetection.cpp | 2 +- modules/aruco/test/test_charucodetection.cpp | 135 +- 41 files changed, 751 insertions(+), 42023 deletions(-) create mode 100644 modules/aruco/include/opencv2/aruco/aruco_calib.hpp delete mode 100644 modules/aruco/include/opencv2/aruco/aruco_calib_pose.hpp delete mode 100644 modules/aruco/include/opencv2/aruco/board.hpp delete mode 100644 modules/aruco/include/opencv2/aruco/dictionary.hpp delete mode 100644 modules/aruco/include/opencv2/aruco_detector.hpp delete mode 100644 modules/aruco/src/apriltag/apriltag_quad_thresh.cpp delete mode 100644 modules/aruco/src/apriltag/apriltag_quad_thresh.hpp delete mode 100644 modules/aruco/src/apriltag/predefined_dictionaries_apriltag.hpp delete mode 100644 modules/aruco/src/apriltag/unionfind.hpp delete mode 100644 modules/aruco/src/apriltag/zarray.hpp delete mode 100644 modules/aruco/src/apriltag/zmaxheap.cpp delete mode 100644 modules/aruco/src/apriltag/zmaxheap.hpp create mode 100644 modules/aruco/src/aruco_calib.cpp delete mode 100644 modules/aruco/src/aruco_calib_pose.cpp delete mode 100644 modules/aruco/src/aruco_detector.cpp delete mode 100644 modules/aruco/src/aruco_utils.cpp delete mode 100644 modules/aruco/src/aruco_utils.hpp delete mode 100644 modules/aruco/src/board.cpp delete mode 100644 modules/aruco/src/dictionary.cpp delete mode 100644 modules/aruco/src/predefined_dictionaries.hpp create mode 100644 modules/aruco/test/test_aruco_tutorial.cpp delete mode 100644 modules/aruco/test/test_arucodetection.cpp diff --git a/modules/aruco/CMakeLists.txt b/modules/aruco/CMakeLists.txt index ab97d3f99d..9050eeb192 100644 --- a/modules/aruco/CMakeLists.txt +++ b/modules/aruco/CMakeLists.txt @@ -1,5 +1,5 @@ set(the_description "ArUco Marker Detection") -ocv_define_module(aruco opencv_core opencv_imgproc opencv_calib3d WRAP python java objc js) +ocv_define_module(aruco opencv_core opencv_imgproc opencv_calib3d opencv_objdetect WRAP python java objc js) ocv_include_directories(${CMAKE_CURRENT_BINARY_DIR}) ocv_add_testdata(samples/ contrib/aruco diff --git a/modules/aruco/include/opencv2/aruco.hpp b/modules/aruco/include/opencv2/aruco.hpp index 3213c59508..0968a4a754 100644 --- a/modules/aruco/include/opencv2/aruco.hpp +++ b/modules/aruco/include/opencv2/aruco.hpp @@ -1,25 +1,25 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_ARUCO_HPP__ -#define __OPENCV_ARUCO_HPP__ +#ifndef OPENCV_ARUCO_HPP +#define OPENCV_ARUCO_HPP -#include "opencv2/aruco_detector.hpp" -#include "opencv2/aruco/aruco_calib_pose.hpp" +#include "opencv2/objdetect/aruco_detector.hpp" +#include "opencv2/aruco/aruco_calib.hpp" namespace cv { namespace aruco { /** -@deprecated Use class ArucoDetector +@deprecated Use class ArucoDetector::detectMarkers */ CV_EXPORTS_W void detectMarkers(InputArray image, const Ptr &dictionary, OutputArrayOfArrays corners, OutputArray ids, const Ptr ¶meters = DetectorParameters::create(), OutputArrayOfArrays rejectedImgPoints = noArray()); /** -@deprecated Use class ArucoDetector +@deprecated Use class ArucoDetector::refineDetectedMarkers */ CV_EXPORTS_W void refineDetectedMarkers(InputArray image,const Ptr &board, InputOutputArrayOfArrays detectedCorners, @@ -29,6 +29,124 @@ CV_EXPORTS_W void refineDetectedMarkers(InputArray image,const Ptr &boar bool checkAllOrders = true, OutputArray recoveredIdxs = noArray(), const Ptr ¶meters = DetectorParameters::create()); +/** +@deprecated Use Board::draw +*/ +CV_EXPORTS_W void drawPlanarBoard(const Ptr &board, Size outSize, OutputArray img, int marginSize, + int borderBits); + +/** +@deprecated Use Board::matchImagePoints +*/ +CV_EXPORTS_W void getBoardObjectAndImagePoints(const Ptr &board, InputArrayOfArrays detectedCorners, + InputArray detectedIds, OutputArray objPoints, OutputArray imgPoints); + + +/** + * @brief Pose estimation for a board of markers + * + * @param corners vector of already detected markers corners. For each marker, its four corners + * are provided, (e.g std::vector > ). For N detected markers, the + * dimensions of this array should be Nx4. The order of the corners should be clockwise. + * @param ids list of identifiers for each marker in corners + * @param board layout of markers in the board. The layout is composed by the marker identifiers + * and the positions of each marker corner in the board reference system. + * @param cameraMatrix input 3x3 floating-point camera matrix + * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ + * @param distCoeffs vector of distortion coefficients + * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements + * @param rvec Output vector (e.g. cv::Mat) corresponding to the rotation vector of the board + * (see cv::Rodrigues). Used as initial guess if not empty. + * @param tvec Output vector (e.g. cv::Mat) corresponding to the translation vector of the board. + * @param useExtrinsicGuess defines whether initial guess for \b rvec and \b tvec will be used or not. + * Used as initial guess if not empty. + * + * This function receives the detected markers and returns the pose of a marker board composed + * by those markers. + * A Board of marker has a single world coordinate system which is defined by the board layout. + * The returned transformation is the one that transforms points from the board coordinate system + * to the camera coordinate system. + * Input markers that are not included in the board layout are ignored. + * The function returns the number of markers from the input employed for the board pose estimation. + * Note that returning a 0 means the pose has not been estimated. + * @sa use cv::drawFrameAxes to get world coordinate system axis for object points + */ +CV_EXPORTS_W int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr &board, + InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess = false); + +/** + * @brief Pose estimation for a ChArUco board given some of their corners + * @param charucoCorners vector of detected charuco corners + * @param charucoIds list of identifiers for each corner in charucoCorners + * @param board layout of ChArUco board. + * @param cameraMatrix input 3x3 floating-point camera matrix + * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ + * @param distCoeffs vector of distortion coefficients + * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements + * @param rvec Output vector (e.g. cv::Mat) corresponding to the rotation vector of the board + * (see cv::Rodrigues). + * @param tvec Output vector (e.g. cv::Mat) corresponding to the translation vector of the board. + * @param useExtrinsicGuess defines whether initial guess for \b rvec and \b tvec will be used or not. + * + * This function estimates a Charuco board pose from some detected corners. + * The function checks if the input corners are enough and valid to perform pose estimation. + * If pose estimation is valid, returns true, else returns false. + * @sa use cv::drawFrameAxes to get world coordinate system axis for object points + */ +CV_EXPORTS_W bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, + const Ptr &board, InputArray cameraMatrix, + InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess = false); + +/** + * @brief Pose estimation for single markers + * + * @param corners vector of already detected markers corners. For each marker, its four corners + * are provided, (e.g std::vector > ). For N detected markers, + * the dimensions of this array should be Nx4. The order of the corners should be clockwise. + * @sa detectMarkers + * @param markerLength the length of the markers' side. The returning translation vectors will + * be in the same unit. Normally, unit is meters. + * @param cameraMatrix input 3x3 floating-point camera matrix + * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ + * @param distCoeffs vector of distortion coefficients + * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements + * @param rvecs array of output rotation vectors (@sa Rodrigues) (e.g. std::vector). + * Each element in rvecs corresponds to the specific marker in imgPoints. + * @param tvecs array of output translation vectors (e.g. std::vector). + * Each element in tvecs corresponds to the specific marker in imgPoints. + * @param objPoints array of object points of all the marker corners + * @param estimateParameters set the origin of coordinate system and the coordinates of the four corners of the marker + * (default estimateParameters.pattern = PatternPositionType::ARUCO_CCW_CENTER, estimateParameters.useExtrinsicGuess = false, + * estimateParameters.solvePnPMethod = SOLVEPNP_ITERATIVE). + * + * This function receives the detected markers and returns their pose estimation respect to + * the camera individually. So for each marker, one rotation and translation vector is returned. + * The returned transformation is the one that transforms points from each marker coordinate system + * to the camera coordinate system. + * The marker coordinate system is centered on the middle (by default) or on the top-left corner of the marker, + * with the Z axis perpendicular to the marker plane. + * estimateParameters defines the coordinates of the four corners of the marker in its own coordinate system (by default) are: + * (-markerLength/2, markerLength/2, 0), (markerLength/2, markerLength/2, 0), + * (markerLength/2, -markerLength/2, 0), (-markerLength/2, -markerLength/2, 0) + * @sa use cv::drawFrameAxes to get world coordinate system axis for object points + * @sa @ref tutorial_aruco_detection + * @sa EstimateParameters + * @sa PatternPositionType + */ +CV_EXPORTS_W void estimatePoseSingleMarkers(InputArrayOfArrays corners, float markerLength, + InputArray cameraMatrix, InputArray distCoeffs, + OutputArray rvecs, OutputArray tvecs, OutputArray objPoints = noArray(), + const Ptr& estimateParameters = EstimateParameters::create()); + + +/** +@deprecated Use CharucoBoard::testCharucoCornersCollinear + */ +CV_EXPORTS_W bool testCharucoCornersCollinear(const Ptr &board, InputArray charucoIds); + + } } diff --git a/modules/aruco/include/opencv2/aruco/aruco_calib.hpp b/modules/aruco/include/opencv2/aruco/aruco_calib.hpp new file mode 100644 index 0000000000..ed5efa7b12 --- /dev/null +++ b/modules/aruco/include/opencv2/aruco/aruco_calib.hpp @@ -0,0 +1,124 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html +#ifndef OPENCV_ARUCO_CALIB_POSE_HPP +#define OPENCV_ARUCO_CALIB_POSE_HPP +#include + +namespace cv { +namespace aruco { + +//! @addtogroup aruco +//! @{ + +/** + * @brief Calibrate a camera using aruco markers + * + * @param corners vector of detected marker corners in all frames. + * The corners should have the same format returned by detectMarkers (see #detectMarkers). + * @param ids list of identifiers for each marker in corners + * @param counter number of markers in each frame so that corners and ids can be split + * @param board Marker Board layout + * @param imageSize Size of the image used only to initialize the intrinsic camera matrix. + * @param cameraMatrix Output 3x3 floating-point camera matrix + * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS + * and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be + * initialized before calling the function. + * @param distCoeffs Output vector of distortion coefficients + * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements + * @param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each board view + * (e.g. std::vector>). That is, each k-th rotation vector together with the corresponding + * k-th translation vector (see the next output parameter description) brings the board pattern + * from the model coordinate space (in which object points are specified) to the world coordinate + * space, that is, a real position of the board pattern in the k-th pattern view (k=0.. *M* -1). + * @param tvecs Output vector of translation vectors estimated for each pattern view. + * @param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters. + * Order of deviations values: + * \f$(f_x, f_y, c_x, c_y, k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6 , s_1, s_2, s_3, + * s_4, \tau_x, \tau_y)\f$ If one of parameters is not estimated, it's deviation is equals to zero. + * @param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters. + * Order of deviations values: \f$(R_1, T_1, \dotsc , R_M, T_M)\f$ where M is number of pattern views, + * \f$R_i, T_i\f$ are concatenated 1x3 vectors. + * @param perViewErrors Output vector of average re-projection errors estimated for each pattern view. + * @param flags flags Different flags for the calibration process (see #calibrateCamera for details). + * @param criteria Termination criteria for the iterative optimization algorithm. + * + * This function calibrates a camera using an Aruco Board. The function receives a list of + * detected markers from several views of the Board. The process is similar to the chessboard + * calibration in calibrateCamera(). The function returns the final re-projection error. + */ +CV_EXPORTS_AS(calibrateCameraArucoExtended) +double calibrateCameraAruco(InputArrayOfArrays corners, InputArray ids, InputArray counter, const Ptr &board, + Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs, + OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, OutputArray stdDeviationsIntrinsics, + OutputArray stdDeviationsExtrinsics, OutputArray perViewErrors, int flags = 0, + const TermCriteria& criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON)); + +/** @overload + * @brief It's the same function as #calibrateCameraAruco but without calibration error estimation. + */ +CV_EXPORTS_W double calibrateCameraAruco(InputArrayOfArrays corners, InputArray ids, InputArray counter, + const Ptr &board, Size imageSize, InputOutputArray cameraMatrix, + InputOutputArray distCoeffs, OutputArrayOfArrays rvecs = noArray(), + OutputArrayOfArrays tvecs = noArray(), int flags = 0, + const TermCriteria& criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, + 30, DBL_EPSILON)); + + +/** + * @brief Calibrate a camera using Charuco corners + * + * @param charucoCorners vector of detected charuco corners per frame + * @param charucoIds list of identifiers for each corner in charucoCorners per frame + * @param board Marker Board layout + * @param imageSize input image size + * @param cameraMatrix Output 3x3 floating-point camera matrix + * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS + * and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be + * initialized before calling the function. + * @param distCoeffs Output vector of distortion coefficients + * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements + * @param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each board view + * (e.g. std::vector>). That is, each k-th rotation vector together with the corresponding + * k-th translation vector (see the next output parameter description) brings the board pattern + * from the model coordinate space (in which object points are specified) to the world coordinate + * space, that is, a real position of the board pattern in the k-th pattern view (k=0.. *M* -1). + * @param tvecs Output vector of translation vectors estimated for each pattern view. + * @param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters. + * Order of deviations values: + * \f$(f_x, f_y, c_x, c_y, k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6 , s_1, s_2, s_3, + * s_4, \tau_x, \tau_y)\f$ If one of parameters is not estimated, it's deviation is equals to zero. + * @param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters. + * Order of deviations values: \f$(R_1, T_1, \dotsc , R_M, T_M)\f$ where M is number of pattern views, + * \f$R_i, T_i\f$ are concatenated 1x3 vectors. + * @param perViewErrors Output vector of average re-projection errors estimated for each pattern view. + * @param flags flags Different flags for the calibration process (see #calibrateCamera for details). + * @param criteria Termination criteria for the iterative optimization algorithm. + * + * This function calibrates a camera using a set of corners of a Charuco Board. The function + * receives a list of detected corners and its identifiers from several views of the Board. + * The function returns the final re-projection error. + */ +CV_EXPORTS_AS(calibrateCameraCharucoExtended) +double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, + const Ptr &board, Size imageSize, InputOutputArray cameraMatrix, + InputOutputArray distCoeffs, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, + OutputArray stdDeviationsIntrinsics, OutputArray stdDeviationsExtrinsics, + OutputArray perViewErrors, int flags = 0, const TermCriteria& criteria = TermCriteria( + TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON)); + +/** + * @brief It's the same function as #calibrateCameraCharuco but without calibration error estimation. + */ +CV_EXPORTS_W double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, + const Ptr &board, Size imageSize, + InputOutputArray cameraMatrix, InputOutputArray distCoeffs, + OutputArrayOfArrays rvecs = noArray(), + OutputArrayOfArrays tvecs = noArray(), int flags = 0, + const TermCriteria& criteria=TermCriteria(TermCriteria::COUNT + + TermCriteria::EPS, 30, DBL_EPSILON)); +//! @} + +} +} +#endif diff --git a/modules/aruco/include/opencv2/aruco/aruco_calib_pose.hpp b/modules/aruco/include/opencv2/aruco/aruco_calib_pose.hpp deleted file mode 100644 index 7f6624ccde..0000000000 --- a/modules/aruco/include/opencv2/aruco/aruco_calib_pose.hpp +++ /dev/null @@ -1,286 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_ARUCO_CALIB_POSE_HPP__ -#define __OPENCV_ARUCO_CALIB_POSE_HPP__ -#include -#include - -namespace cv { -namespace aruco { - -//! @addtogroup aruco -//! @{ - -/** @brief rvec/tvec define the right handed coordinate system of the marker. - * PatternPos defines center this system and axes direction. - * Axis X (red color) - first coordinate, axis Y (green color) - second coordinate, - * axis Z (blue color) - third coordinate. - * @sa estimatePoseSingleMarkers(), @ref tutorial_aruco_detection - */ -enum PatternPos { - /** @brief The marker coordinate system is centered on the middle of the marker. - * The coordinates of the four corners (CCW order) of the marker in its own coordinate system are: - * (-markerLength/2, markerLength/2, 0), (markerLength/2, markerLength/2, 0), - * (markerLength/2, -markerLength/2, 0), (-markerLength/2, -markerLength/2, 0). - * - * These pattern points define this coordinate system: - * ![Image with axes drawn](images/singlemarkersaxes.jpg) - */ - ARUCO_CCW_CENTER, - /** @brief The marker coordinate system is centered on the top-left corner of the marker. - * The coordinates of the four corners (CW order) of the marker in its own coordinate system are: - * (0, 0, 0), (markerLength, 0, 0), - * (markerLength, markerLength, 0), (0, markerLength, 0). - * - * These pattern points define this coordinate system: - * ![Image with axes drawn](images/singlemarkersaxes2.jpg) - * - * These pattern dots are convenient to use with a chessboard/ChArUco board. - */ - ARUCO_CW_TOP_LEFT_CORNER -}; - -/** @brief Pose estimation parameters - * @param pattern Defines center this system and axes direction (default PatternPos::ARUCO_CCW_CENTER). - * @param useExtrinsicGuess Parameter used for SOLVEPNP_ITERATIVE. If true (1), the function uses the provided - * rvec and tvec values as initial approximations of the rotation and translation vectors, respectively, and further - * optimizes them (default false). - * @param solvePnPMethod Method for solving a PnP problem: see @ref calib3d_solvePnP_flags (default SOLVEPNP_ITERATIVE). - * @sa PatternPos, solvePnP(), @ref tutorial_aruco_detection - */ -struct CV_EXPORTS_W EstimateParameters { - CV_PROP_RW PatternPos pattern; - CV_PROP_RW bool useExtrinsicGuess; - CV_PROP_RW SolvePnPMethod solvePnPMethod; - - EstimateParameters(): pattern(ARUCO_CCW_CENTER), useExtrinsicGuess(false), - solvePnPMethod(SOLVEPNP_ITERATIVE) {} - - CV_WRAP static Ptr create() { - return makePtr(); - } -}; - - -/** - * @brief Pose estimation for single markers - * - * @param corners vector of already detected markers corners. For each marker, its four corners - * are provided, (e.g std::vector > ). For N detected markers, - * the dimensions of this array should be Nx4. The order of the corners should be clockwise. - * @sa detectMarkers - * @param markerLength the length of the markers' side. The returning translation vectors will - * be in the same unit. Normally, unit is meters. - * @param cameraMatrix input 3x3 floating-point camera matrix - * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ - * @param distCoeffs vector of distortion coefficients - * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements - * @param rvecs array of output rotation vectors (@sa Rodrigues) (e.g. std::vector). - * Each element in rvecs corresponds to the specific marker in imgPoints. - * @param tvecs array of output translation vectors (e.g. std::vector). - * Each element in tvecs corresponds to the specific marker in imgPoints. - * @param objPoints array of object points of all the marker corners - * @param estimateParameters set the origin of coordinate system and the coordinates of the four corners of the marker - * (default estimateParameters.pattern = PatternPos::ARUCO_CCW_CENTER, estimateParameters.useExtrinsicGuess = false, - * estimateParameters.solvePnPMethod = SOLVEPNP_ITERATIVE). - * - * This function receives the detected markers and returns their pose estimation respect to - * the camera individually. So for each marker, one rotation and translation vector is returned. - * The returned transformation is the one that transforms points from each marker coordinate system - * to the camera coordinate system. - * The marker coordinate system is centered on the middle (by default) or on the top-left corner of the marker, - * with the Z axis perpendicular to the marker plane. - * estimateParameters defines the coordinates of the four corners of the marker in its own coordinate system (by default) are: - * (-markerLength/2, markerLength/2, 0), (markerLength/2, markerLength/2, 0), - * (markerLength/2, -markerLength/2, 0), (-markerLength/2, -markerLength/2, 0) - * @sa use cv::drawFrameAxes to get world coordinate system axis for object points - * @sa @ref tutorial_aruco_detection - * @sa EstimateParameters - * @sa PatternPos - */ -CV_EXPORTS_W void estimatePoseSingleMarkers(InputArrayOfArrays corners, float markerLength, - InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvecs, OutputArray tvecs, OutputArray objPoints = noArray(), - const Ptr& estimateParameters = EstimateParameters::create()); - -/** - * @brief Pose estimation for a board of markers - * - * @param corners vector of already detected markers corners. For each marker, its four corners - * are provided, (e.g std::vector > ). For N detected markers, the - * dimensions of this array should be Nx4. The order of the corners should be clockwise. - * @param ids list of identifiers for each marker in corners - * @param board layout of markers in the board. The layout is composed by the marker identifiers - * and the positions of each marker corner in the board reference system. - * @param cameraMatrix input 3x3 floating-point camera matrix - * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ - * @param distCoeffs vector of distortion coefficients - * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements - * @param rvec Output vector (e.g. cv::Mat) corresponding to the rotation vector of the board - * (see cv::Rodrigues). Used as initial guess if not empty. - * @param tvec Output vector (e.g. cv::Mat) corresponding to the translation vector of the board. - * @param useExtrinsicGuess defines whether initial guess for \b rvec and \b tvec will be used or not. - * Used as initial guess if not empty. - * - * This function receives the detected markers and returns the pose of a marker board composed - * by those markers. - * A Board of marker has a single world coordinate system which is defined by the board layout. - * The returned transformation is the one that transforms points from the board coordinate system - * to the camera coordinate system. - * Input markers that are not included in the board layout are ignored. - * The function returns the number of markers from the input employed for the board pose estimation. - * Note that returning a 0 means the pose has not been estimated. - * @sa use cv::drawFrameAxes to get world coordinate system axis for object points - */ -CV_EXPORTS_W int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr &board, - InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, - InputOutputArray tvec, bool useExtrinsicGuess = false); - -/** - * @brief Given a board configuration and a set of detected markers, returns the corresponding - * image points and object points to call solvePnP - * - * @param board Marker board layout. - * @param detectedCorners List of detected marker corners of the board. - * @param detectedIds List of identifiers for each marker. - * @param objPoints Vector of vectors of board marker points in the board coordinate space. - * @param imgPoints Vector of vectors of the projections of board marker corner points. -*/ -CV_EXPORTS_W void getBoardObjectAndImagePoints(const Ptr &board, InputArrayOfArrays detectedCorners, - InputArray detectedIds, OutputArray objPoints, OutputArray imgPoints); - -/** - * @brief Calibrate a camera using aruco markers - * - * @param corners vector of detected marker corners in all frames. - * The corners should have the same format returned by detectMarkers (see #detectMarkers). - * @param ids list of identifiers for each marker in corners - * @param counter number of markers in each frame so that corners and ids can be split - * @param board Marker Board layout - * @param imageSize Size of the image used only to initialize the intrinsic camera matrix. - * @param cameraMatrix Output 3x3 floating-point camera matrix - * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS - * and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be - * initialized before calling the function. - * @param distCoeffs Output vector of distortion coefficients - * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements - * @param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each board view - * (e.g. std::vector>). That is, each k-th rotation vector together with the corresponding - * k-th translation vector (see the next output parameter description) brings the board pattern - * from the model coordinate space (in which object points are specified) to the world coordinate - * space, that is, a real position of the board pattern in the k-th pattern view (k=0.. *M* -1). - * @param tvecs Output vector of translation vectors estimated for each pattern view. - * @param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters. - * Order of deviations values: - * \f$(f_x, f_y, c_x, c_y, k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6 , s_1, s_2, s_3, - * s_4, \tau_x, \tau_y)\f$ If one of parameters is not estimated, it's deviation is equals to zero. - * @param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters. - * Order of deviations values: \f$(R_1, T_1, \dotsc , R_M, T_M)\f$ where M is number of pattern views, - * \f$R_i, T_i\f$ are concatenated 1x3 vectors. - * @param perViewErrors Output vector of average re-projection errors estimated for each pattern view. - * @param flags flags Different flags for the calibration process (see #calibrateCamera for details). - * @param criteria Termination criteria for the iterative optimization algorithm. - * - * This function calibrates a camera using an Aruco Board. The function receives a list of - * detected markers from several views of the Board. The process is similar to the chessboard - * calibration in calibrateCamera(). The function returns the final re-projection error. - */ -CV_EXPORTS_AS(calibrateCameraArucoExtended) -double calibrateCameraAruco(InputArrayOfArrays corners, InputArray ids, InputArray counter, const Ptr &board, - Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs, - OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, OutputArray stdDeviationsIntrinsics, - OutputArray stdDeviationsExtrinsics, OutputArray perViewErrors, int flags = 0, - const TermCriteria& criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON)); - -/** @overload - * @brief It's the same function as #calibrateCameraAruco but without calibration error estimation. - */ -CV_EXPORTS_W double calibrateCameraAruco(InputArrayOfArrays corners, InputArray ids, InputArray counter, - const Ptr &board, Size imageSize, InputOutputArray cameraMatrix, - InputOutputArray distCoeffs, OutputArrayOfArrays rvecs = noArray(), - OutputArrayOfArrays tvecs = noArray(), int flags = 0, - const TermCriteria& criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, - 30, DBL_EPSILON)); - -/** - * @brief Pose estimation for a ChArUco board given some of their corners - * @param charucoCorners vector of detected charuco corners - * @param charucoIds list of identifiers for each corner in charucoCorners - * @param board layout of ChArUco board. - * @param cameraMatrix input 3x3 floating-point camera matrix - * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ - * @param distCoeffs vector of distortion coefficients - * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements - * @param rvec Output vector (e.g. cv::Mat) corresponding to the rotation vector of the board - * (see cv::Rodrigues). - * @param tvec Output vector (e.g. cv::Mat) corresponding to the translation vector of the board. - * @param useExtrinsicGuess defines whether initial guess for \b rvec and \b tvec will be used or not. - * - * This function estimates a Charuco board pose from some detected corners. - * The function checks if the input corners are enough and valid to perform pose estimation. - * If pose estimation is valid, returns true, else returns false. - * @sa use cv::drawFrameAxes to get world coordinate system axis for object points - */ -CV_EXPORTS_W bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, - const Ptr &board, InputArray cameraMatrix, - InputArray distCoeffs, InputOutputArray rvec, - InputOutputArray tvec, bool useExtrinsicGuess = false); - -/** - * @brief Calibrate a camera using Charuco corners - * - * @param charucoCorners vector of detected charuco corners per frame - * @param charucoIds list of identifiers for each corner in charucoCorners per frame - * @param board Marker Board layout - * @param imageSize input image size - * @param cameraMatrix Output 3x3 floating-point camera matrix - * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS - * and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be - * initialized before calling the function. - * @param distCoeffs Output vector of distortion coefficients - * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements - * @param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each board view - * (e.g. std::vector>). That is, each k-th rotation vector together with the corresponding - * k-th translation vector (see the next output parameter description) brings the board pattern - * from the model coordinate space (in which object points are specified) to the world coordinate - * space, that is, a real position of the board pattern in the k-th pattern view (k=0.. *M* -1). - * @param tvecs Output vector of translation vectors estimated for each pattern view. - * @param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters. - * Order of deviations values: - * \f$(f_x, f_y, c_x, c_y, k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6 , s_1, s_2, s_3, - * s_4, \tau_x, \tau_y)\f$ If one of parameters is not estimated, it's deviation is equals to zero. - * @param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters. - * Order of deviations values: \f$(R_1, T_1, \dotsc , R_M, T_M)\f$ where M is number of pattern views, - * \f$R_i, T_i\f$ are concatenated 1x3 vectors. - * @param perViewErrors Output vector of average re-projection errors estimated for each pattern view. - * @param flags flags Different flags for the calibration process (see #calibrateCamera for details). - * @param criteria Termination criteria for the iterative optimization algorithm. - * - * This function calibrates a camera using a set of corners of a Charuco Board. The function - * receives a list of detected corners and its identifiers from several views of the Board. - * The function returns the final re-projection error. - */ -CV_EXPORTS_AS(calibrateCameraCharucoExtended) -double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, - const Ptr &board, Size imageSize, InputOutputArray cameraMatrix, - InputOutputArray distCoeffs, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, - OutputArray stdDeviationsIntrinsics, OutputArray stdDeviationsExtrinsics, - OutputArray perViewErrors, int flags = 0, const TermCriteria& criteria = TermCriteria( - TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON)); - -/** - * @brief It's the same function as #calibrateCameraCharuco but without calibration error estimation. - */ -CV_EXPORTS_W double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, - const Ptr &board, Size imageSize, - InputOutputArray cameraMatrix, InputOutputArray distCoeffs, - OutputArrayOfArrays rvecs = noArray(), - OutputArrayOfArrays tvecs = noArray(), int flags = 0, - const TermCriteria& criteria=TermCriteria(TermCriteria::COUNT + - TermCriteria::EPS, 30, DBL_EPSILON)); -//! @} - -} -} -#endif diff --git a/modules/aruco/include/opencv2/aruco/board.hpp b/modules/aruco/include/opencv2/aruco/board.hpp deleted file mode 100644 index 85c8369e91..0000000000 --- a/modules/aruco/include/opencv2/aruco/board.hpp +++ /dev/null @@ -1,243 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_ARUCO_BOARD_HPP__ -#define __OPENCV_ARUCO_BOARD_HPP__ - -#include -#include - -namespace cv { -namespace aruco { -//! @addtogroup aruco -//! @{ - -class Dictionary; - -/** - * @brief Board of markers - * - * A board is a set of markers in the 3D space with a common coordinate system. - * The common form of a board of marker is a planar (2D) board, however any 3D layout can be used. - * A Board object is composed by: - * - The object points of the marker corners, i.e. their coordinates respect to the board system. - * - The dictionary which indicates the type of markers of the board - * - The identifier of all the markers in the board. - */ -class CV_EXPORTS_W Board { -public: - CV_WRAP Board(); - - /** @brief Provide way to create Board by passing necessary data. Specially needed in Python. - * @param objPoints array of object points of all the marker corners in the board - * @param dictionary the dictionary of markers employed for this board - * @param ids vector of the identifiers of the markers in the board - */ - CV_WRAP static Ptr create(InputArrayOfArrays objPoints, const Ptr &dictionary, InputArray ids); - - /** @brief Set ids vector - * @param ids vector of the identifiers of the markers in the board (should be the same size - * as objPoints) - * - * Recommended way to set ids vector, which will fail if the size of ids does not match size - * of objPoints. - */ - CV_WRAP void setIds(InputArray ids); - - /** @brief change id for ids[index] - * @param index - element index in ids - * @param newId - new value for ids[index], should be less than Dictionary size - */ - CV_WRAP void changeId(int index, int newId); - /** @brief return ids - */ - CV_WRAP const std::vector& getIds() const; - - /** @brief set dictionary - */ - CV_WRAP void setDictionary(const Ptr &dictionary); - - /** @brief return dictionary - */ - CV_WRAP Ptr getDictionary() const; - - /** @brief set objPoints - */ - CV_WRAP void setObjPoints(const std::vector > &objPoints); - - /** @brief get objPoints - */ - CV_WRAP const std::vector >& getObjPoints() const; - - /** @brief get rightBottomBorder - */ - CV_WRAP const Point3f& getRightBottomBorder() const; - -protected: - /** @brief array of object points of all the marker corners in the board each marker include its 4 corners in this order: - * - objPoints[i][0] - left-top point of i-th marker - * - objPoints[i][1] - right-top point of i-th marker - * - objPoints[i][2] - right-bottom point of i-th marker - * - objPoints[i][3] - left-bottom point of i-th marker - * - * Markers are placed in a certain order - row by row, left to right in every row. - * For M markers, the size is Mx4. - */ - CV_PROP std::vector > objPoints; - - /// the dictionary of markers employed for this board - CV_PROP Ptr dictionary; - - /// coordinate of the bottom right corner of the board, is set when calling the function create() - CV_PROP Point3f rightBottomBorder; - - /** @brief vector of the identifiers of the markers in the board (same size than objPoints) - * The identifiers refers to the board dictionary - */ - CV_PROP_RW std::vector ids; -}; - -/** - * @brief Draw a planar board - * @sa drawPlanarBoard - * - * @param board layout of the board that will be drawn. The board should be planar, - * z coordinate is ignored - * @param outSize size of the output image in pixels. - * @param img output image with the board. The size of this image will be outSize - * and the board will be on the center, keeping the board proportions. - * @param marginSize minimum margins (in pixels) of the board in the output image - * @param borderBits width of the marker borders. - * - * This function return the image of a planar board, ready to be printed. It assumes - * the Board layout specified is planar by ignoring the z coordinates of the object points. - */ -CV_EXPORTS_W void drawPlanarBoard(const Ptr &board, Size outSize, OutputArray img, - int marginSize = 0, int borderBits = 1); - -/** - * @brief Planar board with grid arrangement of markers - * More common type of board. All markers are placed in the same plane in a grid arrangement. - * The board can be drawn using drawPlanarBoard() function (@sa drawPlanarBoard) - */ - -class CV_EXPORTS_W GridBoard : public Board { -public: - CV_WRAP GridBoard(); - /** - * @brief Draw a GridBoard - * - * @param outSize size of the output image in pixels. - * @param img output image with the board. The size of this image will be outSize - * and the board will be on the center, keeping the board proportions. - * @param marginSize minimum margins (in pixels) of the board in the output image - * @param borderBits width of the marker borders. - * - * This function return the image of the GridBoard, ready to be printed. - */ - CV_WRAP void draw(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1); - - /** - * @brief Create a GridBoard object - * - * @param markersX number of markers in X direction - * @param markersY number of markers in Y direction - * @param markerLength marker side length (normally in meters) - * @param markerSeparation separation between two markers (same unit as markerLength) - * @param dictionary dictionary of markers indicating the type of markers - * @param firstMarker id of first marker in dictionary to use on board. - * @return the output GridBoard object - * - * This functions creates a GridBoard object given the number of markers in each direction and - * the marker size and marker separation. - */ - CV_WRAP static Ptr create(int markersX, int markersY, float markerLength, float markerSeparation, - const Ptr &dictionary, int firstMarker = 0); - - CV_WRAP Size getGridSize() const; - CV_WRAP float getMarkerLength() const; - CV_WRAP float getMarkerSeparation() const; - -protected: - struct GridImpl; - Ptr gridImpl; - friend class CharucoBoard; -}; - -/** - * @brief ChArUco board - * Specific class for ChArUco boards. A ChArUco board is a planar board where the markers are placed - * inside the white squares of a chessboard. The benefits of ChArUco boards is that they provide - * both, ArUco markers versatility and chessboard corner precision, which is important for - * calibration and pose estimation. - * This class also allows the easy creation and drawing of ChArUco boards. - */ -class CV_EXPORTS_W CharucoBoard : public Board { -public: - CV_WRAP CharucoBoard(); - - // vector of chessboard 3D corners precalculated - CV_PROP std::vector chessboardCorners; - - // for each charuco corner, nearest marker id and nearest marker corner id of each marker - CV_PROP std::vector > nearestMarkerIdx; - CV_PROP std::vector > nearestMarkerCorners; - - /** @brief Draw a ChArUco board - * - * @param outSize size of the output image in pixels. - * @param img output image with the board. The size of this image will be outSize - * and the board will be on the center, keeping the board proportions. - * @param marginSize minimum margins (in pixels) of the board in the output image - * @param borderBits width of the marker borders. - * - * This function return the image of the ChArUco board, ready to be printed. - */ - CV_WRAP void draw(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1); - - - /** @brief Create a CharucoBoard object - * @param squaresX number of chessboard squares in X direction - * @param squaresY number of chessboard squares in Y direction - * @param squareLength chessboard square side length (normally in meters) - * @param markerLength marker side length (same unit than squareLength) - * @param dictionary dictionary of markers indicating the type of markers. - * The first markers in the dictionary are used to fill the white chessboard squares. - * @return the output CharucoBoard object - * - * This functions creates a CharucoBoard object given the number of squares in each direction - * and the size of the markers and chessboard squares. - */ - CV_WRAP static Ptr create(int squaresX, int squaresY, float squareLength, - float markerLength, const Ptr &dictionary); - - CV_WRAP Size getChessboardSize() const; - CV_WRAP float getSquareLength() const; - CV_WRAP float getMarkerLength() const; - -protected: - struct CharucoImpl; - Ptr charucoImpl; -}; - -/** - * @brief test whether the ChArUco markers are collinear - * - * @param board layout of ChArUco board. - * @param charucoIds list of identifiers for each corner in charucoCorners per frame. - * @return bool value, 1 (true) if detected corners form a line, 0 (false) if they do not. - * solvePnP, calibration functions will fail if the corners are collinear (true). - * - * The number of ids in charucoIDs should be <= the number of chessboard corners in the board. - * This functions checks whether the charuco corners are on a straight line (returns true, if so), or not (false). - * Axis parallel, as well as diagonal and other straight lines detected. Degenerate cases: - * for number of charucoIDs <= 2,the function returns true. - */ -CV_EXPORTS_W bool testCharucoCornersCollinear(const Ptr &board, InputArray charucoIds); - -//! @} - -} -} - -#endif diff --git a/modules/aruco/include/opencv2/aruco/charuco.hpp b/modules/aruco/include/opencv2/aruco/charuco.hpp index d961c7ef00..2f1a2334e0 100644 --- a/modules/aruco/include/opencv2/aruco/charuco.hpp +++ b/modules/aruco/include/opencv2/aruco/charuco.hpp @@ -1,14 +1,14 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_CHARUCO_HPP__ -#define __OPENCV_CHARUCO_HPP__ +#ifndef OPENCV_CHARUCO_HPP +#define OPENCV_CHARUCO_HPP #include #include #include -#include -#include +#include +#include namespace cv { @@ -90,7 +90,7 @@ CV_EXPORTS_W void detectCharucoDiamond(InputArray image, InputArrayOfArrays mark OutputArrayOfArrays diamondCorners, OutputArray diamondIds, InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), - Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME::DICT_4X4_50)); + Ptr dictionary = getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME::DICT_4X4_50)); diff --git a/modules/aruco/include/opencv2/aruco/dictionary.hpp b/modules/aruco/include/opencv2/aruco/dictionary.hpp deleted file mode 100644 index cc692cf489..0000000000 --- a/modules/aruco/include/opencv2/aruco/dictionary.hpp +++ /dev/null @@ -1,187 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_DICTIONARY_HPP__ -#define __OPENCV_DICTIONARY_HPP__ - -#include - -namespace cv { -namespace aruco { - -//! @addtogroup aruco -//! @{ - - -/** - * @brief Dictionary/Set of markers. It contains the inner codification - * - * bytesList contains the marker codewords where - * - bytesList.rows is the dictionary size - * - each marker is encoded using `nbytes = ceil(markerSize*markerSize/8.)` - * - each row contains all 4 rotations of the marker, so its length is `4*nbytes` - * - * `bytesList.ptr(i)[k*nbytes + j]` is then the j-th byte of i-th marker, in its k-th rotation. - */ -class CV_EXPORTS_W Dictionary { - - public: - CV_PROP_RW Mat bytesList; // marker code information - CV_PROP_RW int markerSize; // number of bits per dimension - CV_PROP_RW int maxCorrectionBits; // maximum number of bits that can be corrected - - - Dictionary(const Mat &_bytesList = Mat(), int _markerSize = 0, int _maxcorr = 0); - - - /** - * Dictionary(const Dictionary &_dictionary); - */ - Dictionary(const Ptr &dictionary); - - - /** @brief returns generateCustomDictionary(nMarkers, markerSize, randomSeed) - * @see generateCustomDictionary - */ - CV_WRAP_AS(create) static Ptr create(int nMarkers, int markerSize, int randomSeed=0); - - - /** @brief returns generateCustomDictionary(nMarkers, markerSize, baseDictionary, randomSeed) - * @see generateCustomDictionary - */ - CV_WRAP_AS(create_from) static Ptr create(int nMarkers, int markerSize, - const Ptr &baseDictionary, int randomSeed=0); - - /** - * @brief Read a new dictionary from FileNode. Format:\n - * nmarkers: 35\n - * markersize: 6\n - * maxCorrectionBits: 5\n - * marker_0: "101011111011111001001001101100000000"\n - * ...\n - * marker_34: "011111010000111011111110110101100101" - */ - CV_WRAP bool readDictionary(const cv::FileNode& fn); - - /** - * @brief Write a dictionary to FileStorage. Format is the same as in readDictionary(). - */ - CV_WRAP void writeDictionary(Ptr& fs); - - /** - * @see getPredefinedDictionary - */ - CV_WRAP static Ptr get(int dict); - - /** - * @brief Given a matrix of bits. Returns whether if marker is identified or not. - * It returns by reference the correct id (if any) and the correct rotation - */ - CV_WRAP bool identify(const Mat &onlyBits, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate) const; - - /** - * @brief Returns the distance of the input bits to the specific id. If allRotations is true, - * the four posible bits rotation are considered - */ - CV_WRAP int getDistanceToId(InputArray bits, int id, bool allRotations = true) const; - - - /** - * @brief Draw a canonical marker image - */ - CV_WRAP void drawMarker(int id, int sidePixels, OutputArray _img, int borderBits = 1) const; - - - /** - * @brief Transform matrix of bits to list of bytes in the 4 rotations - */ - CV_WRAP static Mat getByteListFromBits(const Mat &bits); - - - /** - * @brief Transform list of bytes to matrix of bits - */ - CV_WRAP static Mat getBitsFromByteList(const Mat &byteList, int markerSize); -}; - - - - -/** - * @brief Predefined markers dictionaries/sets - * Each dictionary indicates the number of bits and the number of markers contained - * - DICT_ARUCO_ORIGINAL: standard ArUco Library Markers. 1024 markers, 5x5 bits, 0 minimum - distance - */ -enum PREDEFINED_DICTIONARY_NAME { - DICT_4X4_50 = 0, ///< 4x4 bits, minimum hamming distance between any two codes = 4, 50 codes - DICT_4X4_100, ///< 4x4 bits, minimum hamming distance between any two codes = 3, 100 codes - DICT_4X4_250, ///< 4x4 bits, minimum hamming distance between any two codes = 3, 250 codes - DICT_4X4_1000, ///< 4x4 bits, minimum hamming distance between any two codes = 2, 1000 codes - DICT_5X5_50, ///< 5x5 bits, minimum hamming distance between any two codes = 8, 50 codes - DICT_5X5_100, ///< 5x5 bits, minimum hamming distance between any two codes = 7, 100 codes - DICT_5X5_250, ///< 5x5 bits, minimum hamming distance between any two codes = 6, 250 codes - DICT_5X5_1000, ///< 5x5 bits, minimum hamming distance between any two codes = 5, 1000 codes - DICT_6X6_50, ///< 6x6 bits, minimum hamming distance between any two codes = 13, 50 codes - DICT_6X6_100, ///< 6x6 bits, minimum hamming distance between any two codes = 12, 100 codes - DICT_6X6_250, ///< 6x6 bits, minimum hamming distance between any two codes = 11, 250 codes - DICT_6X6_1000, ///< 6x6 bits, minimum hamming distance between any two codes = 9, 1000 codes - DICT_7X7_50, ///< 7x7 bits, minimum hamming distance between any two codes = 19, 50 codes - DICT_7X7_100, ///< 7x7 bits, minimum hamming distance between any two codes = 18, 100 codes - DICT_7X7_250, ///< 7x7 bits, minimum hamming distance between any two codes = 17, 250 codes - DICT_7X7_1000, ///< 7x7 bits, minimum hamming distance between any two codes = 14, 1000 codes - DICT_ARUCO_ORIGINAL, ///< 6x6 bits, minimum hamming distance between any two codes = 3, 1024 codes - DICT_APRILTAG_16h5, ///< 4x4 bits, minimum hamming distance between any two codes = 5, 30 codes - DICT_APRILTAG_25h9, ///< 5x5 bits, minimum hamming distance between any two codes = 9, 35 codes - DICT_APRILTAG_36h10, ///< 6x6 bits, minimum hamming distance between any two codes = 10, 2320 codes - DICT_APRILTAG_36h11 ///< 6x6 bits, minimum hamming distance between any two codes = 11, 587 codes -}; - - -/** - * @brief Returns one of the predefined dictionaries defined in PREDEFINED_DICTIONARY_NAME - */ -CV_EXPORTS Ptr getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME name); - - -/** - * @brief Returns one of the predefined dictionaries referenced by DICT_*. - */ -CV_EXPORTS_W Ptr getPredefinedDictionary(int dict); - - -/** - * @see generateCustomDictionary - */ -CV_EXPORTS_AS(custom_dictionary) Ptr generateCustomDictionary( - int nMarkers, - int markerSize, - int randomSeed=0); - - -/** - * @brief Generates a new customizable marker dictionary - * - * @param nMarkers number of markers in the dictionary - * @param markerSize number of bits per dimension of each markers - * @param baseDictionary Include the markers in this dictionary at the beginning (optional) - * @param randomSeed a user supplied seed for theRNG() - * - * This function creates a new dictionary composed by nMarkers markers and each markers composed - * by markerSize x markerSize bits. If baseDictionary is provided, its markers are directly - * included and the rest are generated based on them. If the size of baseDictionary is higher - * than nMarkers, only the first nMarkers in baseDictionary are taken and no new marker is added. - */ -CV_EXPORTS_AS(custom_dictionary_from) Ptr generateCustomDictionary( - int nMarkers, - int markerSize, - const Ptr &baseDictionary, - int randomSeed=0); - - - -//! @} -} -} - -#endif diff --git a/modules/aruco/include/opencv2/aruco_detector.hpp b/modules/aruco/include/opencv2/aruco_detector.hpp deleted file mode 100644 index f3342ebe76..0000000000 --- a/modules/aruco/include/opencv2/aruco_detector.hpp +++ /dev/null @@ -1,436 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_ARUCO_DETECTOR_HPP__ -#define __OPENCV_ARUCO_DETECTOR_HPP__ -#include -#include - -/** - * @defgroup aruco ArUco Marker Detection - * This module is dedicated to square fiducial markers (also known as Augmented Reality Markers) - * These markers are useful for easy, fast and robust camera pose estimation. - * - * The main functionality of ArucoDetector class is: - * - Detection of markers in an image - * - * There are even more functionalities implemented in charuco.hpp and aruco_calib_pose.hpp: - * - Pose estimation from a single marker or from a board/set of markers - * - Detection of ChArUco board for high subpixel accuracy - * - Camera calibration from both, ArUco boards and ChArUco boards. - * - Detection of ChArUco diamond markers - * The samples directory includes easy examples of how to use the module. - * - * The implementation is based on the ArUco Library by R. Muñoz-Salinas and S. Garrido-Jurado @cite Aruco2014. - * - * Markers can also be detected based on the AprilTag 2 @cite wang2016iros fiducial detection method. - * - * @sa S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014. - * "Automatic generation and detection of highly reliable fiducial markers under occlusion". - * Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005 - * - * @sa http://www.uco.es/investiga/grupos/ava/node/26 - * - * This module has been originally developed by Sergio Garrido-Jurado as a project - * for Google Summer of Code 2015 (GSoC 15). - * - * -*/ - -namespace cv { -namespace aruco { - -//! @addtogroup aruco -//! @{ - -enum CornerRefineMethod{ - CORNER_REFINE_NONE, ///< Tag and corners detection based on the ArUco approach - CORNER_REFINE_SUBPIX, ///< ArUco approach and refine the corners locations using corner subpixel accuracy - CORNER_REFINE_CONTOUR, ///< ArUco approach and refine the corners locations using the contour-points line fitting - CORNER_REFINE_APRILTAG, ///< Tag and corners detection based on the AprilTag 2 approach @cite wang2016iros -}; - -/** - * @brief struct DetectorParameters is used by ArucoDetector - */ -struct CV_EXPORTS_W DetectorParameters { - DetectorParameters() { - adaptiveThreshWinSizeMin = 3; - adaptiveThreshWinSizeMax = 23; - adaptiveThreshWinSizeStep = 10; - adaptiveThreshConstant = 7; - minMarkerPerimeterRate = 0.03; - maxMarkerPerimeterRate = 4.; - polygonalApproxAccuracyRate = 0.03; - minCornerDistanceRate = 0.05; - minDistanceToBorder = 3; - minMarkerDistanceRate = 0.05; - cornerRefinementMethod = CORNER_REFINE_NONE; - cornerRefinementWinSize = 5; - cornerRefinementMaxIterations = 30; - cornerRefinementMinAccuracy = 0.1; - markerBorderBits = 1; - perspectiveRemovePixelPerCell = 4; - perspectiveRemoveIgnoredMarginPerCell = 0.13; - maxErroneousBitsInBorderRate = 0.35; - minOtsuStdDev = 5.0; - errorCorrectionRate = 0.6; - aprilTagQuadDecimate = 0.0; - aprilTagQuadSigma = 0.0; - aprilTagMinClusterPixels = 5; - aprilTagMaxNmaxima = 10; - aprilTagCriticalRad = (float)(10* CV_PI /180); - aprilTagMaxLineFitMse = 10.0; - aprilTagMinWhiteBlackDiff = 5; - aprilTagDeglitch = 0; - detectInvertedMarker = false; - useAruco3Detection = false; - minSideLengthCanonicalImg = 32; - minMarkerLengthRatioOriginalImg = 0.0; - }; - - /** @brief Create a new set of DetectorParameters with default values. - */ - CV_WRAP static Ptr create() { - Ptr params = makePtr(); - return params; - } - - /** - * @brief Read a new set of DetectorParameters from FileNode (use FileStorage.root()). - */ - CV_WRAP bool readDetectorParameters(const FileNode& fn); - - /** - * @brief Write a set of DetectorParameters to FileStorage - */ - CV_WRAP bool writeDetectorParameters(const Ptr& fs); - - /// minimum window size for adaptive thresholding before finding contours (default 3). - CV_PROP_RW int adaptiveThreshWinSizeMin; - - /// maximum window size for adaptive thresholding before finding contours (default 23). - CV_PROP_RW int adaptiveThreshWinSizeMax; - - /// increments from adaptiveThreshWinSizeMin to adaptiveThreshWinSizeMax during the thresholding (default 10). - CV_PROP_RW int adaptiveThreshWinSizeStep; - - /// constant for adaptive thresholding before finding contours (default 7) - CV_PROP_RW double adaptiveThreshConstant; - - /** @brief determine minimum perimeter for marker contour to be detected. This is defined as a rate respect to the - * maximum dimension of the input image (default 0.03). - */ - CV_PROP_RW double minMarkerPerimeterRate; - - /** @brief determine maximum perimeter for marker contour to be detected. This is defined as a rate respect to - * the maximum dimension of the input image (default 4.0). - */ - CV_PROP_RW double maxMarkerPerimeterRate; - - /// minimum accuracy during the polygonal approximation process to determine which contours are squares. (default 0.03) - CV_PROP_RW double polygonalApproxAccuracyRate; - - /// minimum distance between corners for detected markers relative to its perimeter (default 0.05) - CV_PROP_RW double minCornerDistanceRate; - - /// minimum distance of any corner to the image border for detected markers (in pixels) (default 3) - CV_PROP_RW int minDistanceToBorder; - - /** @brief minimum mean distance beetween two marker corners to be considered imilar, so that the - * smaller one is removed. The rate is relative to the smaller perimeter of the two markers (default 0.05). - */ - CV_PROP_RW double minMarkerDistanceRate; - - /** @brief default CORNER_REFINE_NONE. - * 0:CORNER_REFINE_NONE, no refinement. - * 1: CORNER_REFINE_SUBPIX, do subpixel refinement. - * 2: CORNER_REFINE_CONTOUR use contour-Points, - * 3: CORNER_REFINE_APRILTAG use the AprilTag2 approach). - */ - CV_PROP_RW int cornerRefinementMethod; - - /// window size for the corner refinement process (in pixels) (default 5). - CV_PROP_RW int cornerRefinementWinSize; - - /// maximum number of iterations for stop criteria of the corner refinement process (default 30). - CV_PROP_RW int cornerRefinementMaxIterations; - - /// minimum error for the stop cristeria of the corner refinement process (default: 0.1) - CV_PROP_RW double cornerRefinementMinAccuracy; - - /// number of bits of the marker border, i.e. marker border width (default 1). - CV_PROP_RW int markerBorderBits; - - /// number of bits (per dimension) for each cell of the marker when removing the perspective (default 4). - CV_PROP_RW int perspectiveRemovePixelPerCell; - - /** @brief width of the margin of pixels on each cell not considered for the - * determination of the cell bit. Represents the rate respect to the total size of the cell, i.e. - * perspectiveRemovePixelPerCell (default 0.13) - */ - CV_PROP_RW double perspectiveRemoveIgnoredMarginPerCell; - - /** @brief maximum number of accepted erroneous bits in the border (i.e. number of allowed - * white bits in the border). Represented as a rate respect to the total number of bits per marker (default 0.35). - */ - CV_PROP_RW double maxErroneousBitsInBorderRate; - - /** @brief minimun standard deviation in pixels values during the decodification step to apply Otsu - * thresholding (otherwise, all the bits are set to 0 or 1 depending on mean higher than 128 or not) (default 5.0) - */ - CV_PROP_RW double minOtsuStdDev; - - /// error correction rate respect to the maximun error correction capability for each dictionary (default 0.6). - CV_PROP_RW double errorCorrectionRate; - - /** @brief April :: User-configurable parameters. - * detection of quads can be done on a lower-resolution image, improving speed at a cost of - * pose accuracy and a slight decrease in detection rate. Decoding the binary payload is still - */ - CV_PROP_RW float aprilTagQuadDecimate; - - /// what Gaussian blur should be applied to the segmented image (used for quad detection?) - CV_PROP_RW float aprilTagQuadSigma; - - // April :: Internal variables - /// reject quads containing too few pixels (default 5). - CV_PROP_RW int aprilTagMinClusterPixels; - - /// how many corner candidates to consider when segmenting a group of pixels into a quad (default 10). - CV_PROP_RW int aprilTagMaxNmaxima; - - /** @brief reject quads where pairs of edges have angles that are close to straight or close to 180 degrees. - * Zero means that no quads are rejected. (In radians) (default 10*PI/180) - */ - CV_PROP_RW float aprilTagCriticalRad; - - /// when fitting lines to the contours, what is the maximum mean squared error - CV_PROP_RW float aprilTagMaxLineFitMse; - - /** @brief when we build our model of black & white pixels, we add an extra check that the - * white model must be (overall) brighter than the black model. How much brighter? (in pixel values, [0,255]). - * (default 5) - */ - CV_PROP_RW int aprilTagMinWhiteBlackDiff; - - /// should the thresholded image be deglitched? Only useful for very noisy images (default 0). - CV_PROP_RW int aprilTagDeglitch; - - /** @brief to check if there is a white marker. In order to generate a "white" marker just invert a - * normal marker by using a tilde, ~markerImage. (default false) - */ - CV_PROP_RW bool detectInvertedMarker; - - /** @brief new Aruco functionality proposed in the paper: - * Romero-Ramirez et al: Speeded up detection of squared fiducial markers (2018) - * https://www.researchgate.net/publication/325787310_Speeded_Up_Detection_of_Squared_Fiducial_Markers - */ - - /// to enable the new and faster Aruco detection strategy. - CV_PROP_RW bool useAruco3Detection; - - /// minimum side length of a marker in the canonical image. Latter is the binarized image in which contours are searched. - CV_PROP_RW int minSideLengthCanonicalImg; - - /// range [0,1], eq (2) from paper. The parameter tau_i has a direct influence on the processing speed. - CV_PROP_RW float minMarkerLengthRatioOriginalImg; -}; - -/** - * @brief struct RefineParameters is used by ArucoDetector - */ -struct CV_EXPORTS_W RefineParameters { - RefineParameters() { - minRepDistance = 10.f; - errorCorrectionRate = 3.f; - checkAllOrders = true; - } - - RefineParameters(float _minRepDistance, float _errorCorrectionRate, bool _checkAllOrders): - minRepDistance(_minRepDistance), errorCorrectionRate(_errorCorrectionRate), checkAllOrders(_checkAllOrders) {} - - CV_WRAP static Ptr create(float _minRepDistance = 10.f, float _errorCorrectionRate = 3.f, - bool _checkAllOrders = true) { - return makePtr(_minRepDistance, _errorCorrectionRate, _checkAllOrders); - } - - - /** - * @brief Read a new set of RefineParameters from FileNode (use FileStorage.root()). - */ - CV_WRAP bool readRefineParameters(const FileNode& fn); - - /** @brief Write a set of RefineParameters to FileStorage - */ - CV_WRAP bool writeRefineParameters(const Ptr& fs); - - /** @brief minRepDistance minimum distance between the corners of the rejected candidate and the reprojected marker in - * order to consider it as a correspondence. - */ - CV_PROP_RW float minRepDistance; - /** @brief minRepDistance rate of allowed erroneous bits respect to the error correction - * capability of the used dictionary. -1 ignores the error correction step. - */ - CV_PROP_RW float errorCorrectionRate; - /** @brief checkAllOrders consider the four posible corner orders in the rejectedCorners array. - * If it set to false, only the provided corner order is considered (default true). - */ - CV_PROP_RW bool checkAllOrders; -}; - -/** - * @brief The main functionality of ArucoDetector class is detection of markers in an image with detectMarkers() method. - * After detecting some markers in the image, you can try to find undetected markers from this dictionary with - * refineDetectedMarkers() method. - * @see DetectorParameters, RefineParameters - */ -class CV_EXPORTS_W ArucoDetector : public Algorithm -{ -public: - /// dictionary indicates the type of markers that will be searched - CV_PROP_RW Ptr dictionary; - - /// marker detection parameters, check DetectorParameters docs to see available settings - CV_PROP_RW Ptr params; - - /// marker refine parameters - CV_PROP_RW Ptr refineParams; - - /** - * @brief Basic ArucoDetector constructor - * @param _dictionary indicates the type of markers that will be searched - * @param _params marker detection parameters - * @param _refineParams marker refine detection parameters - */ - CV_WRAP ArucoDetector(const Ptr &_dictionary = getPredefinedDictionary(DICT_4X4_50), - const Ptr &_params = DetectorParameters::create(), - const Ptr &_refineParams = RefineParameters::create()): - dictionary(_dictionary), params(_params), refineParams(_refineParams) {} - - CV_WRAP static Ptr create(const Ptr &_dictionary, const Ptr &_params) { - return makePtr(_dictionary, _params); - } - - /** - * @brief Basic marker detection - * - * @param image input image - * @param corners vector of detected marker corners. For each marker, its four corners - * are provided, (e.g std::vector > ). For N detected markers, - * the dimensions of this array is Nx4. The order of the corners is clockwise. - * @param ids vector of identifiers of the detected markers. The identifier is of type int - * (e.g. std::vector). For N detected markers, the size of ids is also N. - * The identifiers have the same order than the markers in the imgPoints array. - * @param rejectedImgPoints contains the imgPoints of those squares whose inner code has not a - * correct codification. Useful for debugging purposes. - * - * Performs marker detection in the input image. Only markers included in the specific dictionary - * are searched. For each detected marker, it returns the 2D position of its corner in the image - * and its corresponding identifier. - * Note that this function does not perform pose estimation. - * @note The function does not correct lens distortion or takes it into account. It's recommended to undistort - * input image with corresponging camera model, if camera parameters are known - * @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard - */ - CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids, - OutputArrayOfArrays rejectedImgPoints = noArray()); - - /** - * @brief Refind not detected markers based on the already detected and the board layout - * - * @param image input image - * @param board layout of markers in the board. - * @param detectedCorners vector of already detected marker corners. - * @param detectedIds vector of already detected marker identifiers. - * @param rejectedCorners vector of rejected candidates during the marker detection process. - * @param cameraMatrix optional input 3x3 floating-point camera matrix - * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ - * @param distCoeffs optional vector of distortion coefficients - * \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements - * @param recoveredIdxs Optional array to returns the indexes of the recovered candidates in the - * original rejectedCorners array. - * - * This function tries to find markers that were not detected in the basic detecMarkers function. - * First, based on the current detected marker and the board layout, the function interpolates - * the position of the missing markers. Then it tries to find correspondence between the reprojected - * markers and the rejected candidates based on the minRepDistance and errorCorrectionRate - * parameters. - * If camera parameters and distortion coefficients are provided, missing markers are reprojected - * using projectPoint function. If not, missing marker projections are interpolated using global - * homography, and all the marker corners in the board must have the same Z coordinate. - */ - CV_WRAP void refineDetectedMarkers(InputArray image, const Ptr &board, - InputOutputArrayOfArrays detectedCorners, - InputOutputArray detectedIds, InputOutputArrayOfArrays rejectedCorners, - InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), - OutputArray recoveredIdxs = noArray()); - - /** @brief Stores algorithm parameters in a file storage - */ - virtual void write(FileStorage& fs) const override { - Ptr pfs = makePtr(fs); - dictionary->writeDictionary(pfs); - params->writeDetectorParameters(pfs); - refineParams->writeRefineParameters(pfs); - } - - /** @brief simplified API for language bindings - * @overload - */ - CV_WRAP void write(const String& fileName) const { - FileStorage fs(fileName, FileStorage::WRITE); - write(fs); - } - - /** @brief Reads algorithm parameters from a file storage - */ - CV_WRAP virtual void read(const FileNode& fn) override { - dictionary->readDictionary(fn); - params->readDetectorParameters(fn); - refineParams->readRefineParameters(fn); - } -}; - -/** - * @brief Draw detected markers in image - * - * @param image input/output image. It must have 1 or 3 channels. The number of channels is not - * altered. - * @param corners positions of marker corners on input image. - * (e.g std::vector > ). For N detected markers, the dimensions of - * this array should be Nx4. The order of the corners should be clockwise. - * @param ids vector of identifiers for markers in markersCorners . - * Optional, if not provided, ids are not painted. - * @param borderColor color of marker borders. Rest of colors (text color and first corner color) - * are calculated based on this one to improve visualization. - * - * Given an array of detected marker corners and its corresponding ids, this functions draws - * the markers in the image. The marker borders are painted and the markers identifiers if provided. - * Useful for debugging purposes. - * - */ -CV_EXPORTS_W void drawDetectedMarkers(InputOutputArray image, InputArrayOfArrays corners, - InputArray ids = noArray(), Scalar borderColor = Scalar(0, 255, 0)); - -/** - * @brief Draw a canonical marker image - * - * @param dictionary dictionary of markers indicating the type of markers - * @param id identifier of the marker that will be returned. It has to be a valid id - * in the specified dictionary. - * @param sidePixels size of the image in pixels - * @param img output image with the marker - * @param borderBits width of the marker border. - * - * This function returns a marker image in its canonical form (i.e. ready to be printed) - */ -CV_EXPORTS_W void drawMarker(const Ptr &dictionary, int id, int sidePixels, OutputArray img, - int borderBits = 1); - -//! @} - -} -} - -#endif diff --git a/modules/aruco/misc/java/test/ArucoTest.java b/modules/aruco/misc/java/test/ArucoTest.java index 6e70eab175..a0a575410b 100644 --- a/modules/aruco/misc/java/test/ArucoTest.java +++ b/modules/aruco/misc/java/test/ArucoTest.java @@ -8,6 +8,7 @@ import org.opencv.core.Mat; import org.opencv.core.CvType; import org.opencv.aruco.*; +import org.opencv.objdetect.*; public class ArucoTest extends OpenCVTestCase { @@ -37,7 +38,7 @@ public void testArucoDetector() { Mat markerImage = new Mat(); int id = 1, offset = 5, size = 40; - Aruco.drawMarker(dictionary, id, size, markerImage, detectorParameters.get_markerBorderBits()); + Objdetect.drawMarker(dictionary, id, size, markerImage, detectorParameters.get_markerBorderBits()); Mat image = new Mat(markerImage.rows() + 2*offset, markerImage.cols() + 2*offset, CvType.CV_8UC1, new Scalar(255)); diff --git a/modules/aruco/perf/perf_aruco.cpp b/modules/aruco/perf/perf_aruco.cpp index 6cde729ff2..a24792d32a 100644 --- a/modules/aruco/perf/perf_aruco.cpp +++ b/modules/aruco/perf/perf_aruco.cpp @@ -2,6 +2,7 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html #include "perf_precomp.hpp" +#include "opencv2/calib3d.hpp" namespace opencv_test { using namespace perf; diff --git a/modules/aruco/perf/perf_precomp.hpp b/modules/aruco/perf/perf_precomp.hpp index a72903624c..8c0d716c39 100644 --- a/modules/aruco/perf/perf_precomp.hpp +++ b/modules/aruco/perf/perf_precomp.hpp @@ -5,7 +5,6 @@ #define __OPENCV_PERF_PRECOMP_HPP__ #include "opencv2/ts.hpp" -#include "opencv2/aruco_detector.hpp" -#include "opencv2/calib3d.hpp" +#include "opencv2/objdetect/aruco_detector.hpp" #endif diff --git a/modules/aruco/samples/aruco_dict_utils.cpp b/modules/aruco/samples/aruco_dict_utils.cpp index ab32f4f58a..beee9a2996 100644 --- a/modules/aruco/samples/aruco_dict_utils.cpp +++ b/modules/aruco/samples/aruco_dict_utils.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include using namespace cv; diff --git a/modules/aruco/samples/aruco_samples_utility.hpp b/modules/aruco/samples/aruco_samples_utility.hpp index ebdbcc1d7e..6387bfc3ef 100644 --- a/modules/aruco/samples/aruco_samples_utility.hpp +++ b/modules/aruco/samples/aruco_samples_utility.hpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/modules/aruco/samples/calibrate_camera.cpp b/modules/aruco/samples/calibrate_camera.cpp index bf056b1527..bf92a1daf5 100644 --- a/modules/aruco/samples/calibrate_camera.cpp +++ b/modules/aruco/samples/calibrate_camera.cpp @@ -39,8 +39,8 @@ the use of this software, even if advised of the possibility of such damage. #include #include -#include -#include +#include +#include #include #include #include diff --git a/modules/aruco/samples/create_board.cpp b/modules/aruco/samples/create_board.cpp index 242688e5cb..1b311a3cf4 100644 --- a/modules/aruco/samples/create_board.cpp +++ b/modules/aruco/samples/create_board.cpp @@ -38,7 +38,7 @@ the use of this software, even if advised of the possibility of such damage. #include -#include +#include #include #include "aruco_samples_utility.hpp" diff --git a/modules/aruco/samples/create_marker.cpp b/modules/aruco/samples/create_marker.cpp index 73ce21880f..bd5b4ec988 100644 --- a/modules/aruco/samples/create_marker.cpp +++ b/modules/aruco/samples/create_marker.cpp @@ -38,7 +38,7 @@ the use of this software, even if advised of the possibility of such damage. #include -#include +#include #include #include "aruco_samples_utility.hpp" diff --git a/modules/aruco/samples/detect_board.cpp b/modules/aruco/samples/detect_board.cpp index 7f9c3d1ebc..1c8e5a0e69 100644 --- a/modules/aruco/samples/detect_board.cpp +++ b/modules/aruco/samples/detect_board.cpp @@ -38,8 +38,7 @@ the use of this software, even if advised of the possibility of such damage. #include -#include -#include +#include #include #include #include "aruco_samples_utility.hpp" @@ -179,8 +178,7 @@ int main(int argc, char *argv[]) { // estimate board pose int markersOfBoardDetected = 0; if(ids.size() > 0) - markersOfBoardDetected = - aruco::estimatePoseBoard(corners, ids, board, camMatrix, distCoeffs, rvec, tvec); + markersOfBoardDetected = estimatePoseBoard(corners, ids, board, camMatrix, distCoeffs, rvec, tvec); double currentTime = ((double)getTickCount() - tick) / getTickFrequency(); totalTime += currentTime; diff --git a/modules/aruco/samples/detect_board_charuco.cpp b/modules/aruco/samples/detect_board_charuco.cpp index f413cef9fb..68977b727f 100644 --- a/modules/aruco/samples/detect_board_charuco.cpp +++ b/modules/aruco/samples/detect_board_charuco.cpp @@ -182,8 +182,8 @@ int main(int argc, char *argv[]) { // estimate charuco board pose bool validPose = false; if(camMatrix.total() != 0) - validPose = aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, charucoboard, - camMatrix, distCoeffs, rvec, tvec); + validPose = estimatePoseCharucoBoard(charucoCorners, charucoIds, charucoboard, + camMatrix, distCoeffs, rvec, tvec); diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index de38c2a1b2..6f882b82ac 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -38,8 +38,7 @@ the use of this software, even if advised of the possibility of such damage. #include -#include -#include +#include #include #include "aruco_samples_utility.hpp" diff --git a/modules/aruco/src/apriltag/apriltag_quad_thresh.cpp b/modules/aruco/src/apriltag/apriltag_quad_thresh.cpp deleted file mode 100644 index fcc038b25f..0000000000 --- a/modules/aruco/src/apriltag/apriltag_quad_thresh.cpp +++ /dev/null @@ -1,1666 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copyright (C) 2013-2016, The Regents of The University of Michigan. -// -// This software was developed in the APRIL Robotics Lab under the -// direction of Edwin Olson, ebolson@umich.edu. This software may be -// available under alternative licensing terms; contact the address above. -// -// The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, -// either expressed or implied, of the Regents of The University of Michigan. - -// limitation: image size must be <32768 in width and height. This is -// because we use a fixed-point 16 bit integer representation with one -// fractional bit. - -#include "../precomp.hpp" -#include "apriltag_quad_thresh.hpp" - -//#define APRIL_DEBUG -#ifdef APRIL_DEBUG - #include "opencv2/imgcodecs.hpp" - #include -#endif - -namespace cv { -namespace aruco { - -static void ptsort_(struct pt *pts, int sz); // forward delaration - -static inline -void ptsort(struct pt *pts, int sz) -{ -#define MAYBE_SWAP(arr,apos,bpos) \ - if (arr[apos].theta > arr[bpos].theta) { \ - tmp = arr[apos]; arr[apos] = arr[bpos]; arr[bpos] = tmp; \ - }; - - if (sz <= 1) - return; - - if (sz == 2) { - struct pt tmp; - MAYBE_SWAP(pts, 0, 1); - return; - } - - // NB: Using less-branch-intensive sorting networks here on the - // hunch that it's better for performance. - if (sz == 3) { // 3 element bubble sort is optimal - struct pt tmp; - MAYBE_SWAP(pts, 0, 1); - MAYBE_SWAP(pts, 1, 2); - MAYBE_SWAP(pts, 0, 1); - return; - } - - if (sz == 4) { // 4 element optimal sorting network. - struct pt tmp; - MAYBE_SWAP(pts, 0, 1); // sort each half, like a merge sort - MAYBE_SWAP(pts, 2, 3); - MAYBE_SWAP(pts, 0, 2); // minimum value is now at 0. - MAYBE_SWAP(pts, 1, 3); // maximum value is now at end. - MAYBE_SWAP(pts, 1, 2); // that only leaves the middle two. - return; - } - - if (sz == 5) { - // this 9-step swap is optimal for a sorting network, but two - // steps slower than a generic sort. - struct pt tmp; - MAYBE_SWAP(pts, 0, 1); // sort each half (3+2), like a merge sort - MAYBE_SWAP(pts, 3, 4); - MAYBE_SWAP(pts, 1, 2); - MAYBE_SWAP(pts, 0, 1); - MAYBE_SWAP(pts, 0, 3); // minimum element now at 0 - MAYBE_SWAP(pts, 2, 4); // maximum element now at end - MAYBE_SWAP(pts, 1, 2); // now resort the three elements 1-3. - MAYBE_SWAP(pts, 2, 3); - MAYBE_SWAP(pts, 1, 2); - return; - } - -#undef MAYBE_SWAP - - ptsort_(pts, sz); -} - -void ptsort_(struct pt *pts, int sz) -{ - // a merge sort with temp storage. - - // Use stack storage if it's not too big. - cv::AutoBuffer _tmp_stack(sz); - memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz); - - int asz = sz/2; - int bsz = sz - asz; - - struct pt *as = &_tmp_stack[0]; - struct pt *bs = &_tmp_stack[asz]; - - ptsort(as, asz); - ptsort(bs, bsz); - -#define MERGE(apos,bpos) \ - if (as[apos].theta < bs[bpos].theta) \ - pts[outpos++] = as[apos++]; \ - else \ - pts[outpos++] = bs[bpos++]; - - int apos = 0, bpos = 0, outpos = 0; - while (apos + 8 < asz && bpos + 8 < bsz) { - MERGE(apos,bpos); MERGE(apos,bpos); MERGE(apos,bpos); MERGE(apos,bpos); - MERGE(apos,bpos); MERGE(apos,bpos); MERGE(apos,bpos); MERGE(apos,bpos); - } - - while (apos < asz && bpos < bsz) { - MERGE(apos,bpos); - } - - if (apos < asz) - memcpy(&pts[outpos], &as[apos], (asz-apos)*sizeof(struct pt)); - if (bpos < bsz) - memcpy(&pts[outpos], &bs[bpos], (bsz-bpos)*sizeof(struct pt)); - -#undef MERGE -} - -/** - * lfps contains *cumulative* moments for N points, with - * index j reflecting points [0,j] (inclusive). - * fit a line to the points [i0, i1] (inclusive). i0, i1 are both (0, sz) - * if i1 < i0, we treat this as a wrap around. - */ -void fit_line(struct line_fit_pt *lfps, int sz, int i0, int i1, double *lineparm, double *err, double *mse){ - CV_Assert(i0 != i1); - CV_Assert(i0 >= 0 && i1 >= 0 && i0 < sz && i1 < sz); - - double Mx, My, Mxx, Myy, Mxy, W; - int N; // how many points are included in the set? - - if (i0 < i1) { - N = i1 - i0 + 1; - - Mx = lfps[i1].Mx; - My = lfps[i1].My; - Mxx = lfps[i1].Mxx; - Mxy = lfps[i1].Mxy; - Myy = lfps[i1].Myy; - W = lfps[i1].W; - - if (i0 > 0) { - Mx -= lfps[i0-1].Mx; - My -= lfps[i0-1].My; - Mxx -= lfps[i0-1].Mxx; - Mxy -= lfps[i0-1].Mxy; - Myy -= lfps[i0-1].Myy; - W -= lfps[i0-1].W; - } - - } else { - // i0 > i1, e.g. [15, 2]. Wrap around. - CV_Assert(i0 > 0); - - Mx = lfps[sz-1].Mx - lfps[i0-1].Mx; - My = lfps[sz-1].My - lfps[i0-1].My; - Mxx = lfps[sz-1].Mxx - lfps[i0-1].Mxx; - Mxy = lfps[sz-1].Mxy - lfps[i0-1].Mxy; - Myy = lfps[sz-1].Myy - lfps[i0-1].Myy; - W = lfps[sz-1].W - lfps[i0-1].W; - - Mx += lfps[i1].Mx; - My += lfps[i1].My; - Mxx += lfps[i1].Mxx; - Mxy += lfps[i1].Mxy; - Myy += lfps[i1].Myy; - W += lfps[i1].W; - - N = sz - i0 + i1 + 1; - } - - CV_Assert(N >= 2); - - double Ex = Mx / W; - double Ey = My / W; - double Cxx = Mxx / W - Ex*Ex; - double Cxy = Mxy / W - Ex*Ey; - double Cyy = Myy / W - Ey*Ey; - - double nx, ny; - - if (1) { - // on iOS about 5% of total CPU spent in these trig functions. - // 85 ms per frame on 5S, example.pnm - // - // XXX this was using the double-precision atan2. Was there a case where - // we needed that precision? Seems doubtful. - float normal_theta = float(.5f * (CV_PI / 180)) * cv::fastAtan2((float)(-2*Cxy), (float)(Cyy - Cxx)); - nx = cosf(normal_theta); - ny = sinf(normal_theta); - } else { - // 73.5 ms per frame on 5S, example.pnm - double ty = -2*Cxy; - double tx = (Cyy - Cxx); - double mag = ty*ty + tx*tx; - - if (mag == 0) { - nx = 1; - ny = 0; - } else { - float norm = sqrtf((float)(ty*ty + tx*tx)); - tx /= norm; - - // ty is now sin(2theta) - // tx is now cos(2theta). We want sin(theta) and cos(theta) - - // due to precision err, tx could still have slightly too large magnitude. - if (tx > 1) { - ny = 0; - nx = 1; - } else if (tx < -1) { - ny = 1; - nx = 0; - } else { - // half angle formula - ny = sqrtf((1.0f - (float)tx)*0.5f); - nx = sqrtf((1.0f + (float)tx)*0.5f); - - // pick a consistent branch cut - if (ty < 0) - ny = - ny; - } - } - } - - if (lineparm) { - lineparm[0] = Ex; - lineparm[1] = Ey; - lineparm[2] = nx; - lineparm[3] = ny; - } - - // sum of squared errors = - // - // SUM_i ((p_x - ux)*nx + (p_y - uy)*ny)^2 - // SUM_i nx*nx*(p_x - ux)^2 + 2nx*ny(p_x -ux)(p_y-uy) + ny*ny*(p_y-uy)*(p_y-uy) - // nx*nx*SUM_i((p_x -ux)^2) + 2nx*ny*SUM_i((p_x-ux)(p_y-uy)) + ny*ny*SUM_i((p_y-uy)^2) - // - // nx*nx*N*Cxx + 2nx*ny*N*Cxy + ny*ny*N*Cyy - - // sum of squared errors - if (err) - *err = nx*nx*N*Cxx + 2*nx*ny*N*Cxy + ny*ny*N*Cyy; - - // mean squared error - if (mse) - *mse = nx*nx*Cxx + 2*nx*ny*Cxy + ny*ny*Cyy; -} - -int err_compare_descending(const void *_a, const void *_b){ - const double *a = (const double*)_a; - const double *b = (const double*)_b; - - return ((*a) < (*b)) ? 1 : -1; -} - -/** - 1. Identify A) white points near a black point and B) black points near a white point. - - 2. Find the connected components within each of the classes above, - yielding clusters of "white-near-black" and - "black-near-white". (These two classes are kept separate). Each - segment has a unique id. - - 3. For every pair of "white-near-black" and "black-near-white" - clusters, find the set of points that are in one and adjacent to the - other. In other words, a "boundary" layer between the two - clusters. (This is actually performed by iterating over the pixels, - rather than pairs of clusters.) Critically, this helps keep nearby - edges from becoming connected. - **/ -int quad_segment_maxima(const Ptr &td, int sz, struct line_fit_pt *lfps, int indices[4]){ - - // ksz: when fitting points, how many points on either side do we consider? - // (actual "kernel" width is 2ksz). - // - // This value should be about: 0.5 * (points along shortest edge). - // - // If all edges were equally-sized, that would give a value of - // sz/8. We make it somewhat smaller to account for tags at high - // aspects. - - // XXX Tunable. Maybe make a multiple of JPEG block size to increase robustness - // to JPEG compression artifacts? - //int ksz = imin(20, sz / 12); - int ksz = 20 < (sz/12)? 20: (sz/12); - - // can't fit a quad if there are too few points. - if (ksz < 2) - return 0; - - // printf("sz %5d, ksz %3d\n", sz, ksz); - - std::vector errs(sz); - - for (int i = 0; i < sz; i++) { - fit_line(lfps, sz, (i + sz - ksz) % sz, (i + ksz) % sz, NULL, &errs[i], NULL); - } - - // apply a low-pass filter to errs - if (1) { - std::vector y(sz); - - // how much filter to apply? - - // XXX Tunable - double sigma = 1; // was 3 - - // cutoff = exp(-j*j/(2*sigma*sigma)); - // log(cutoff) = -j*j / (2*sigma*sigma) - // log(cutoff)*2*sigma*sigma = -j*j; - - // how big a filter should we use? We make our kernel big - // enough such that we represent any values larger than - // 'cutoff'. - - // XXX Tunable (though not super useful to change) - double cutoff = 0.05; - int fsz = cvFloor(sqrt(-log(cutoff)*2*sigma*sigma)) + 1; - fsz = 2*fsz + 1; - - // For default values of cutoff = 0.05, sigma = 3, - // we have fsz = 17. - std::vector f(fsz); - - for (int i = 0; i < fsz; i++) { - int j = i - fsz / 2; - f[i] = (float)exp(-j*j/(2*sigma*sigma)); - } - - for (int iy = 0; iy < sz; iy++) { - double acc = 0; - - for (int i = 0; i < fsz; i++) { - acc += errs[(iy + i - fsz / 2 + sz) % sz] * f[i]; - } - y[iy] = acc; - } - copy(y.begin(), y.end(), errs.begin()); - } - - std::vector maxima(sz); - std::vector maxima_errs(sz); - int nmaxima = 0; - - for (int i = 0; i < sz; i++) { - if (errs[i] > errs[(i+1)%sz] && errs[i] > errs[(i+sz-1)%sz]) { - maxima[nmaxima] = i; - maxima_errs[nmaxima] = errs[i]; - nmaxima++; - } - } - // if we didn't get at least 4 maxima, we can't fit a quad. - if (nmaxima < 4) - return 0; - - // select only the best maxima if we have too many - int max_nmaxima = td->aprilTagMaxNmaxima; - - if (nmaxima > max_nmaxima) { - std::vector maxima_errs_copy(maxima_errs.begin(), maxima_errs.begin()+nmaxima); - - // throw out all but the best handful of maxima. Sorts descending. - qsort(maxima_errs_copy.data(), nmaxima, sizeof(double), err_compare_descending); - - double maxima_thresh = maxima_errs_copy[max_nmaxima]; - int out = 0; - for (int in = 0; in < nmaxima; in++) { - if (maxima_errs[in] <= maxima_thresh) - continue; - maxima[out++] = maxima[in]; - } - nmaxima = out; - } - - int best_indices[4]; - double best_error = HUGE_VALF; - - double err01, err12, err23, err30; - double mse01, mse12, mse23, mse30; - double params01[4], params12[4], params23[4], params30[4]; - - // disallow quads where the angle is less than a critical value. - double max_dot = cos(td->aprilTagCriticalRad); //25*M_PI/180); - - for (int m0 = 0; m0 < nmaxima - 3; m0++) { - int i0 = maxima[m0]; - - for (int m1 = m0+1; m1 < nmaxima - 2; m1++) { - int i1 = maxima[m1]; - - fit_line(lfps, sz, i0, i1, params01, &err01, &mse01); - - if (mse01 > td->aprilTagMaxLineFitMse) - continue; - - for (int m2 = m1+1; m2 < nmaxima - 1; m2++) { - int i2 = maxima[m2]; - - fit_line(lfps, sz, i1, i2, params12, &err12, &mse12); - if (mse12 > td->aprilTagMaxLineFitMse) - continue; - - double dot = params01[2]*params12[2] + params01[3]*params12[3]; - if (fabs(dot) > max_dot) - continue; - - for (int m3 = m2+1; m3 < nmaxima; m3++) { - int i3 = maxima[m3]; - - fit_line(lfps, sz, i2, i3, params23, &err23, &mse23); - if (mse23 > td->aprilTagMaxLineFitMse) - continue; - - fit_line(lfps, sz, i3, i0, params30, &err30, &mse30); - if (mse30 > td->aprilTagMaxLineFitMse) - continue; - - double err = err01 + err12 + err23 + err30; - if (err < best_error) { - best_error = err; - best_indices[0] = i0; - best_indices[1] = i1; - best_indices[2] = i2; - best_indices[3] = i3; - } - } - } - } - } - - if (best_error == HUGE_VALF) - return 0; - - for (int i = 0; i < 4; i++) - indices[i] = best_indices[i]; - - if (best_error / sz < td->aprilTagMaxLineFitMse) - return 1; - return 0; -} - -/** - * returns 0 if the cluster looks bad. - */ -int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){ - //int sz = zarray_size(cluster); - - zmaxheap_t *heap = zmaxheap_create(sizeof(struct remove_vertex*)); - - // We will initially allocate sz rvs. We then have two types of - // iterations: some iterations that are no-ops in terms of - // allocations, and those that remove a vertex and allocate two - // more children. This will happen at most (sz-4) times. Thus we - // need: sz + 2*(sz-4) entries. - - int rvalloc_pos = 0; - int rvalloc_size = 3*sz; - cv::AutoBuffer rvalloc_(std::max(1, rvalloc_size)); - memset(rvalloc_.data(), 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill - struct remove_vertex *rvalloc = rvalloc_.data(); - cv::AutoBuffer segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill - memset(segs_.data(), 0, sizeof(segs_[0]) * segs_.size()); - struct segment *segs = segs_.data(); - - // populate with initial entries - for (int i = 0; i < sz; i++) { - struct remove_vertex *rv = &rvalloc[rvalloc_pos++]; - rv->i = i; - if (i == 0) { - rv->left = sz-1; - rv->right = 1; - } else { - rv->left = i-1; - rv->right = (i+1) % sz; - } - - fit_line(lfps, sz, rv->left, rv->right, NULL, NULL, &rv->err); - - //TODO is finite CV_Assert(): - CV_DbgAssert (!cvIsNaN(-rv->err) && "zmaxheap_add: Trying to add non-finite number to heap. NaN's prohibited, could allow INF with testing"); - zmaxheap_add(heap, &rv, (float)-rv->err); - - segs[i].left = rv->left; - segs[i].right = rv->right; - segs[i].is_vertex = 1; - } - - int nvertices = sz; - - while (nvertices > 4) { - CV_Assert(rvalloc_pos < rvalloc_size); - - struct remove_vertex *rv; - float err; - - int res = zmaxheap_remove_max(heap, &rv, &err); - if (!res) - return 0; - CV_Assert(res); - - // is this remove_vertex valid? (Or has one of the left/right - // vertices changes since we last looked?) - if (!segs[rv->i].is_vertex || - !segs[rv->left].is_vertex || - !segs[rv->right].is_vertex) { - continue; - } - - // we now merge. - CV_Assert(segs[rv->i].is_vertex); - - segs[rv->i].is_vertex = 0; - segs[rv->left].right = rv->right; - segs[rv->right].left = rv->left; - - // create the join to the left - if (1) { - struct remove_vertex *child = &rvalloc[rvalloc_pos++]; - child->i = rv->left; - child->left = segs[rv->left].left; - child->right = rv->right; - - fit_line(lfps, sz, child->left, child->right, NULL, NULL, &child->err); - - //TODO is finite CV_Assert(): - CV_DbgAssert (!cvIsNaN(-child->err) && "zmaxheap_add: Trying to add non-finite number to heap. NaN's prohibited, could allow INF with testing"); - zmaxheap_add(heap, &child, (float)-child->err); - } - - // create the join to the right - if (1) { - struct remove_vertex *child = &rvalloc[rvalloc_pos++]; - child->i = rv->right; - child->left = rv->left; - child->right = segs[rv->right].right; - - fit_line(lfps, sz, child->left, child->right, NULL, NULL, &child->err); - - //TODO is finite CV_Assert(): - CV_DbgAssert (!cvIsNaN(-child->err) && "zmaxheap_add: Trying to add non-finite number to heap. NaN's prohibited, could allow INF with testing"); - zmaxheap_add(heap, &child, (float)-child->err); - } - - // we now have one less vertex - nvertices--; - } - - zmaxheap_destroy(heap); - - int idx = 0; - for (int i = 0; i < sz; i++) { - if (segs[i].is_vertex) { - indices[idx++] = i; - } - } - - return 1; -} - -#define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) unionfind_connect(uf, y*w + x, y*w + dy*w + x + dx); -static void do_unionfind_line(unionfind_t *uf, Mat &im, int w, int s, int y){ - CV_Assert(y+1 < im.rows); - CV_Assert(!im.empty()); - - for (int x = 1; x < w - 1; x++) { - uint8_t v = im.data[y*s + x]; - - if (v == 127) - continue; - - // (dx,dy) pairs for 8 connectivity: - // (REFERENCE) (1, 0) - // (-1, 1) (0, 1) (1, 1) - // - DO_UNIONFIND(1, 0); - DO_UNIONFIND(0, 1); - if (v == 255) { - DO_UNIONFIND(-1, 1); - DO_UNIONFIND(1, 1); - } - } -} -#undef DO_UNIONFIND - -/** - * return 1 if the quad looks okay, 0 if it should be discarded - * quad - **/ -int fit_quad(const Ptr &_params, const Mat im, zarray_t *cluster, struct sQuad *quad){ - CV_Assert(cluster != NULL); - - int res = 0; - - int sz = _zarray_size(cluster); - if (sz < 4) // can't fit a quad to less than 4 points - return 0; - - ///////////////////////////////////////////////////////////// - // Step 1. Sort points so they wrap around the center of the - // quad. We will constrain our quad fit to simply partition this - // ordered set into 4 groups. - - // compute a bounding box so that we can order the points - // according to their angle WRT the center. - int32_t xmax = 0, xmin = INT32_MAX, ymax = 0, ymin = INT32_MAX; - - for (int pidx = 0; pidx < sz; pidx++) { - struct pt *p; - _zarray_get_volatile(cluster, pidx, &p); - - //(a > b) ? a : b; - //xmax = imax(xmax, p->x); - //xmin = imin(xmin, p->x); - //ymax = imax(ymax, p->y); - //ymin = imin(ymin, p->y); - - xmax = xmax > p->x? xmax : p->x; - xmin = xmin < p->x? xmin : p->x; - - ymax = ymax > p->y? ymax : p->y; - ymin = ymin < p->y? ymin : p->y; - } - - // add some noise to (cx,cy) so that pixels get a more diverse set - // of theta estimates. This will help us remove more points. - // (Only helps a small amount. The actual noise values here don't - // matter much at all, but we want them [-1, 1]. (XXX with - // fixed-point, should range be bigger?) - double cx = (xmin + xmax) * 0.5 + 0.05118; - double cy = (ymin + ymax) * 0.5 + -0.028581; - - double dot = 0; - - for (int pidx = 0; pidx < sz; pidx++) { - struct pt *p; - _zarray_get_volatile(cluster, pidx, &p); - - double dx = p->x - cx; - double dy = p->y - cy; - - p->theta = cv::fastAtan2((float)dy, (float)dx) * (float)(CV_PI/180); - - dot += dx*p->gx + dy*p->gy; - } - - // Ensure that the black border is inside the white border. - if (dot < 0) - return 0; - - // we now sort the points according to theta. This is a preparatory - // step for segmenting them into four lines. - if (1) { - // zarray_sort(cluster, pt_compare_theta); - ptsort((struct pt*) cluster->data, sz); - - // remove duplicate points. (A byproduct of our segmentation system.) - if (1) { - int outpos = 1; - - struct pt *last; - _zarray_get_volatile(cluster, 0, &last); - - for (int i = 1; i < sz; i++) { - - struct pt *p; - _zarray_get_volatile(cluster, i, &p); - - if (p->x != last->x || p->y != last->y) { - - if (i != outpos) { - struct pt *out; - _zarray_get_volatile(cluster, outpos, &out); - memcpy(out, p, sizeof(struct pt)); - } - - outpos++; - } - - last = p; - } - - cluster->size = outpos; - sz = outpos; - } - - } else { - // This is a counting sort in which we retain at most one - // point for every bucket; the bucket index is computed from - // theta. Since a good quad completes a complete revolution, - // there's reason to think that we should get a good - // distribution of thetas. We might "lose" a few points due - // to collisions, but this shouldn't affect quality very much. - - // XXX tunable. Increase to reduce the likelihood of "losing" - // points due to collisions. - int nbuckets = 4*sz; - -#define ASSOC 2 - std::vector > v(nbuckets, std::vector(ASSOC)); - - // put each point into a bucket. - for (int i = 0; i < sz; i++) { - struct pt *p; - _zarray_get_volatile(cluster, i, &p); - - CV_Assert(p->theta >= -CV_PI && p->theta <= CV_PI); - - int bucket = cvFloor((nbuckets - 1) * (p->theta + CV_PI) / (2*CV_PI)); - CV_Assert(bucket >= 0 && bucket < nbuckets); - - for (int j = 0; j < ASSOC; j++) { - if (v[bucket][j].theta == 0) { - v[bucket][j] = *p; - break; - } - } - } - - // collect the points from the buckets and put them back into the array. - int outsz = 0; - for (int i = 0; i < nbuckets; i++) { - for (int j = 0; j < ASSOC; j++) { - if (v[i][j].theta != 0) { - _zarray_set(cluster, outsz, &v[i][j], NULL); - outsz++; - } - } - } - - _zarray_truncate(cluster, outsz); - sz = outsz; - } - - if (sz < 4) - return 0; - - ///////////////////////////////////////////////////////////// - // Step 2. Precompute statistics that allow line fit queries to be - // efficiently computed for any contiguous range of indices. - - cv::AutoBuffer lfps_(sz); - memset(lfps_.data(), 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill - struct line_fit_pt *lfps = lfps_.data(); - - for (int i = 0; i < sz; i++) { - struct pt *p; - _zarray_get_volatile(cluster, i, &p); - - if (i > 0) { - memcpy(&lfps[i], &lfps[i-1], sizeof(struct line_fit_pt)); - } - - if (0) { - // we now undo our fixed-point arithmetic. - double delta = 0.5; - double x = p->x * .5 + delta; - double y = p->y * .5 + delta; - double W; - - for (int dy = -1; dy <= 1; dy++) { - int iy = cvFloor(y + dy); - - if (iy < 0 || iy + 1 >= im.rows) - continue; - - for (int dx = -1; dx <= 1; dx++) { - int ix = cvFloor(x + dx); - - if (ix < 0 || ix + 1 >= im.cols) - continue; - - int grad_x = im.data[iy * im.cols + ix + 1] - - im.data[iy * im.cols + ix - 1]; - - int grad_y = im.data[(iy+1) * im.cols + ix] - - im.data[(iy-1) * im.cols + ix]; - - W = sqrtf(float(grad_x*grad_x + grad_y*grad_y)) + 1; - - // double fx = x + dx, fy = y + dy; - double fx = ix + .5, fy = iy + .5; - lfps[i].Mx += W * fx; - lfps[i].My += W * fy; - lfps[i].Mxx += W * fx * fx; - lfps[i].Mxy += W * fx * fy; - lfps[i].Myy += W * fy * fy; - lfps[i].W += W; - } - } - } else { - // we now undo our fixed-point arithmetic. - double delta = 0.5; // adjust for pixel center bias - double x = p->x * .5 + delta; - double y = p->y * .5 + delta; - int ix = cvFloor(x), iy = cvFloor(y); - double W = 1; - - if (ix > 0 && ix+1 < im.cols && iy > 0 && iy+1 < im.rows) { - int grad_x = im.data[iy * im.cols + ix + 1] - - im.data[iy * im.cols + ix - 1]; - - int grad_y = im.data[(iy+1) * im.cols + ix] - - im.data[(iy-1) * im.cols + ix]; - - // XXX Tunable. How to shape the gradient magnitude? - W = sqrt(grad_x*grad_x + grad_y*grad_y) + 1; - } - - double fx = x, fy = y; - lfps[i].Mx += W * fx; - lfps[i].My += W * fy; - lfps[i].Mxx += W * fx * fx; - lfps[i].Mxy += W * fx * fy; - lfps[i].Myy += W * fy * fy; - lfps[i].W += W; - } - } - - int indices[4]; - if (1) { - if (!quad_segment_maxima(_params, _zarray_size(cluster), lfps, indices)) - goto finish; - } else { - if (!quad_segment_agg(sz, lfps, indices)) - goto finish; - } - - // printf("%d %d %d %d\n", indices[0], indices[1], indices[2], indices[3]); - - if (0) { - // no refitting here; just use those points as the vertices. - // Note, this is useful for debugging, but pretty bad in - // practice since this code path also omits several - // plausibility checks that save us tons of time in quad - // decoding. - for (int i = 0; i < 4; i++) { - struct pt *p; - _zarray_get_volatile(cluster, indices[i], &p); - - quad->p[i][0] = (float)(.5*p->x); // undo fixed-point arith. - quad->p[i][1] = (float)(.5*p->y); - } - - res = 1; - - } else { - double lines[4][4]; - - for (int i = 0; i < 4; i++) { - int i0 = indices[i]; - int i1 = indices[(i+1)&3]; - - if (0) { - // if there are enough points, skip the points near the corners - // (because those tend not to be very good.) - if (i1-i0 > 8) { - int t = (i1-i0)/6; - if (t < 0) - t = -t; - - i0 = (i0 + t) % sz; - i1 = (i1 + sz - t) % sz; - } - } - - double err; - fit_line(lfps, sz, i0, i1, lines[i], NULL, &err); - - if (err > _params->aprilTagMaxLineFitMse) { - res = 0; - goto finish; - } - } - - for (int i = 0; i < 4; i++) { - // solve for the intersection of lines (i) and (i+1)&3. - // p0 + lambda0*u0 = p1 + lambda1*u1, where u0 and u1 - // are the line directions. - // - // lambda0*u0 - lambda1*u1 = (p1 - p0) - // - // rearrange (solve for lambdas) - // - // [u0_x -u1_x ] [lambda0] = [ p1_x - p0_x ] - // [u0_y -u1_y ] [lambda1] [ p1_y - p0_y ] - // - // remember that lines[i][0,1] = p, lines[i][2,3] = NORMAL vector. - // We want the unit vector, so we need the perpendiculars. Thus, below - // we have swapped the x and y components and flipped the y components. - - const int i1 = (i + 1) & 3; - double A00 = lines[i][3], A01 = -lines[i1][3]; - double A10 = -lines[i][2], A11 = lines[i1][2]; - double B0 = -lines[i][0] + lines[i1][0]; - double B1 = -lines[i][1] + lines[i1][1]; - - double det = A00 * A11 - A10 * A01; - if (fabs(det) < 0.001) { - res = 0; - goto finish; - } - - // inverse. - double det_inv = 1.0 / det; - double W00 = A11 * det_inv, W01 = -A01 * det_inv; - - // solve - double L0 = W00*B0 + W01*B1; - - // compute intersection - quad->p[i][0] = (float)(lines[i][0] + L0*A00); - quad->p[i][1] = (float)(lines[i][1] + L0*A10); - -#if !defined(NDEBUG) - { - // we should get the same intersection starting - // from point p1 and moving L1*u1. - double W10 = -A10 * det_inv, W11 = A00 * det_inv; - double L1 = W10*B0 + W11*B1; - - double x = lines[i1][0] - L1*A01; - double y = lines[i1][1] - L1*A11; - - CV_Assert(fabs(x - quad->p[i][0]) < 0.001); - CV_Assert(fabs(y - quad->p[i][1]) < 0.001); - } -#endif // NDEBUG - - res = 1; - } - } - - // reject quads that are too small - if (1) { - double area = 0; - - // get area of triangle formed by points 0, 1, 2, 0 - double length[3], p; - for (int i = 0; i < 3; i++) { - int idxa = i; // 0, 1, 2, - int idxb = (i+1) % 3; // 1, 2, 0 - //length[i] = sqrt( - // sq(quad->p[idxb][0] - quad->p[idxa][0]) - // + sq(quad->p[idxb][1] - quad->p[idxa][1])); - double sq1 = quad->p[idxb][0] - quad->p[idxa][0]; - sq1 = sq1 * sq1; - double sq2 = quad->p[idxb][1] - quad->p[idxa][1]; - sq2 = sq2 * sq2; - length[i] = sqrt(sq1 + sq2); - } - p = (length[0] + length[1] + length[2]) / 2; - - area += sqrt(p*(p-length[0])*(p-length[1])*(p-length[2])); - - // get area of triangle formed by points 2, 3, 0, 2 - for (int i = 0; i < 3; i++) { - int idxs[] = { 2, 3, 0, 2 }; - int idxa = idxs[i]; - int idxb = idxs[i+1]; - //length[i] = sqrt( - // sq(quad->p[idxb][0] - quad->p[idxa][0]) - // + sq(quad->p[idxb][1] - quad->p[idxa][1])); - double sq1 = quad->p[idxb][0] - quad->p[idxa][0]; - sq1 = sq1 * sq1; - double sq2 = quad->p[idxb][1] - quad->p[idxa][1]; - sq2 = sq2 * sq2; - length[i] = sqrt(sq1 + sq2); - } - p = (length[0] + length[1] + length[2]) / 2; - - area += sqrt(p*(p-length[0])*(p-length[1])*(p-length[2])); - - // we don't actually know the family yet (quad detection is generic.) - // This threshold is based on a 6x6 tag (which is actually 8x8) - // int d = fam->d + fam->black_border*2; - int d = 8; - if (area < d*d) { - res = 0; - goto finish; - } - } - - // reject quads whose cumulative angle change isn't equal to 2PI - if(1){ - double total = 0; - - for (int i = 0; i < 4; i++) { - int i0 = i, i1 = (i+1)&3, i2 = (i+2)&3; - - double theta0 = atan2f(quad->p[i0][1] - quad->p[i1][1], - quad->p[i0][0] - quad->p[i1][0]); - double theta1 = atan2f(quad->p[i2][1] - quad->p[i1][1], - quad->p[i2][0] - quad->p[i1][0]); - - double dtheta = theta0 - theta1; - if (dtheta < 0) - dtheta += 2*CV_PI; - - if (dtheta < _params->aprilTagCriticalRad || dtheta > (CV_PI - _params->aprilTagCriticalRad)) - res = 0; - - total += dtheta; - } - - // looking for 2PI - if (total < 6.2 || total > 6.4) { - res = 0; - goto finish; - } - } - - finish: - - return res; -} - - -static void do_quad(int nCidx0, int nCidx1, zarray_t &nClusters, int nW, int nH, zarray_t *nquads, const Ptr &td, const Mat im){ - - CV_Assert(nquads != NULL); - - //struct quad_task *task = (struct quad_task*) p; - - //zarray_t *clusters = nClusters; - zarray_t *quads = nquads; - int w = nW, h = nH; - - for (int cidx = nCidx0; cidx < nCidx1; cidx++) { - - zarray_t *cluster; - _zarray_get(&nClusters, cidx, &cluster); - - if (_zarray_size(cluster) < td->aprilTagMinClusterPixels) - continue; - - // a cluster should contain only boundary points around the - // tag. it cannot be bigger than the whole screen. (Reject - // large connected blobs that will be prohibitively slow to - // fit quads to.) A typical point along an edge is added three - // times (because it has 3 neighbors). The maximum perimeter - // is 2w+2h. - if (_zarray_size(cluster) > 3*(2*w+2*h)) { - continue; - } - - struct sQuad quad; - memset(&quad, 0, sizeof(struct sQuad)); - - if (fit_quad(td, im, cluster, &quad)) { - //pthread_mutex_lock(&td.mutex); - _zarray_add(quads, &quad); - //pthread_mutex_unlock(&td.mutex); - } - } -} - -void threshold(const Mat mIm, const Ptr ¶meters, Mat& mThresh){ - int w = mIm.cols, h = mIm.rows; - int s = (unsigned) mIm.step; - CV_Assert(w < 32768); - CV_Assert(h < 32768); - - CV_Assert(mThresh.step == (unsigned)s); - - // The idea is to find the maximum and minimum values in a - // window around each pixel. If it's a contrast-free region - // (max-min is small), don't try to binarize. Otherwise, - // threshold according to (max+min)/2. - // - // Mark low-contrast regions with value 127 so that we can skip - // future work on these areas too. - - // however, computing max/min around every pixel is needlessly - // expensive. We compute max/min for tiles. To avoid artifacts - // that arise when high-contrast features appear near a tile - // edge (and thus moving from one tile to another results in a - // large change in max/min value), the max/min values used for - // any pixel are computed from all 3x3 surrounding tiles. Thus, - // the max/min sampling area for nearby pixels overlap by at least - // one tile. - // - // The important thing is that the windows be large enough to - // capture edge transitions; the tag does not need to fit into - // a tile. - - // XXX Tunable. Generally, small tile sizes--- so long as they're - // large enough to span a single tag edge--- seem to be a winner. - const int tilesz = 4; - - // the last (possibly partial) tiles along each row and column will - // just use the min/max value from the last full tile. - int tw = w / tilesz; - int th = h / tilesz; - - uint8_t *im_max = (uint8_t*)calloc(tw*th, sizeof(uint8_t)); - uint8_t *im_min = (uint8_t*)calloc(tw*th, sizeof(uint8_t)); - - - // first, collect min/max statistics for each tile - for (int ty = 0; ty < th; ty++) { - for (int tx = 0; tx < tw; tx++) { - uint8_t max = 0, min = 255; - - for (int dy = 0; dy < tilesz; dy++) { - - for (int dx = 0; dx < tilesz; dx++) { - - uint8_t v = mIm.data[(ty*tilesz+dy)*s + tx*tilesz + dx]; - if (v < min) - min = v; - if (v > max) - max = v; - } - } - im_max[ty*tw+tx] = max; - im_min[ty*tw+tx] = min; - } - } - - // second, apply 3x3 max/min convolution to "blur" these values - // over larger areas. This reduces artifacts due to abrupt changes - // in the threshold value. - uint8_t *im_max_tmp = (uint8_t*)calloc(tw*th, sizeof(uint8_t)); - uint8_t *im_min_tmp = (uint8_t*)calloc(tw*th, sizeof(uint8_t)); - - for (int ty = 0; ty < th; ty++) { - for (int tx = 0; tx < tw; tx++) { - uint8_t max = 0, min = 255; - - for (int dy = -1; dy <= 1; dy++) { - if (ty+dy < 0 || ty+dy >= th) - continue; - for (int dx = -1; dx <= 1; dx++) { - if (tx+dx < 0 || tx+dx >= tw) - continue; - - uint8_t m = im_max[(ty+dy)*tw+tx+dx]; - if (m > max) - max = m; - m = im_min[(ty+dy)*tw+tx+dx]; - if (m < min) - min = m; - } - } - - im_max_tmp[ty*tw + tx] = max; - im_min_tmp[ty*tw + tx] = min; - } - } - free(im_max); - free(im_min); - im_max = im_max_tmp; - im_min = im_min_tmp; - - for (int ty = 0; ty < th; ty++) { - for (int tx = 0; tx < tw; tx++) { - - int min_ = im_min[ty*tw + tx]; - int max_ = im_max[ty*tw + tx]; - - // low contrast region? (no edges) - if (max_ - min_ < parameters->aprilTagMinWhiteBlackDiff) { - for (int dy = 0; dy < tilesz; dy++) { - int y = ty*tilesz + dy; - - for (int dx = 0; dx < tilesz; dx++) { - int x = tx*tilesz + dx; - - //threshim->buf[y*s+x] = 127; - mThresh.data[y*s+x] = 127; - } - } - continue; - } - - // otherwise, actually threshold this tile. - - // argument for biasing towards dark; specular highlights - // can be substantially brighter than white tag parts - uint8_t thresh = saturate_cast((max_ + min_) / 2); - - for (int dy = 0; dy < tilesz; dy++) { - int y = ty*tilesz + dy; - - for (int dx = 0; dx < tilesz; dx++) { - int x = tx*tilesz + dx; - - uint8_t v = mIm.data[y*s+x]; - mThresh.data[y*s+x] = (v > thresh) ? 255 : 0; - } - } - } - } - - // we skipped over the non-full-sized tiles above. Fix those now. - for (int y = 0; y < h; y++) { - - // what is the first x coordinate we need to process in this row? - - int x0; - - if (y >= th*tilesz) { - x0 = 0; // we're at the bottom; do the whole row. - } else { - x0 = tw*tilesz; // we only need to do the right most part. - } - - // compute tile coordinates and clamp. - int ty = y / tilesz; - if (ty >= th) - ty = th - 1; - - for (int x = x0; x < w; x++) { - int tx = x / tilesz; - if (tx >= tw) - tx = tw - 1; - - int max = im_max[ty*tw + tx]; - int min = im_min[ty*tw + tx]; - int thresh = min + (max - min) / 2; - - uint8_t v = mIm.data[y*s+x]; - if (v > thresh){ - mThresh.data[y*s+x] = 255; - } - else{ - mThresh.data[y*s+x] = 0; - } - } - } - free(im_min); - free(im_max); - - // this is a dilate/erode deglitching scheme that does not improve - // anything as far as I can tell. - if (parameters->aprilTagDeglitch) { - Mat tmp(h,w, mIm.type()); - for (int y = 1; y + 1 < h; y++) { - for (int x = 1; x + 1 < w; x++) { - uint8_t max = 0; - for (int dy = -1; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++) { - uint8_t v = mThresh.data[(y+dy)*s + x + dx]; - if (v > max) - max = v; - } - } - tmp.data[y*s+x] = max; - } - } - - for (int y = 1; y + 1 < h; y++) { - for (int x = 1; x + 1 < w; x++) { - uint8_t min = 255; - for (int dy = -1; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++) { - uint8_t v = tmp.data[(y+dy)*s + x + dx]; - if (v < min) - min = v; - } - } - mThresh.data[y*s+x] = min; - } - } - } - -} - -#ifdef APRIL_DEBUG -static void _darken(const Mat &im){ - for (int y = 0; y < im.rows; y++) { - for (int x = 0; x < im.cols; x++) { - im.data[im.cols*y+x] /= 2; - } - } -} -#endif - -zarray_t *apriltag_quad_thresh(const Ptr ¶meters, const Mat & mImg, std::vector > &contours){ - - //////////////////////////////////////////////////////// - // step 1. threshold the image, creating the edge image. - - int w = mImg.cols, h = mImg.rows; - - Mat thold(h, w, mImg.type()); - threshold(mImg, parameters, thold); - - int ts = thold.cols; - -#ifdef APRIL_DEBUG - imwrite("2.2 debug_threshold.pnm", thold); -#endif - - //////////////////////////////////////////////////////// - // step 2. find connected components. - - unionfind_t *uf = unionfind_create(w * h); - - // TODO PARALLELIZE - for (int y = 0; y < h - 1; y++) { - do_unionfind_line(uf, thold, w, ts, y); - } - - // XXX sizing?? - int nclustermap = 2*w*h - 1; - - struct uint64_zarray_entry **clustermap = (struct uint64_zarray_entry**)calloc(nclustermap, sizeof(struct uint64_zarray_entry*)); - - for (int y = 1; y < h-1; y++) { - for (int x = 1; x < w-1; x++) { - - uint8_t v0 = thold.data[y*ts + x]; - if (v0 == 127) - continue; - - // XXX don't query this until we know we need it? - uint64_t rep0 = unionfind_get_representative(uf, y*w + x); - - // whenever we find two adjacent pixels such that one is - // white and the other black, we add the point half-way - // between them to a cluster associated with the unique - // ids of the white and black regions. - // - // We additionally compute the gradient direction (i.e., which - // direction was the white pixel?) Note: if (v1-v0) == 255, then - // (dx,dy) points towards the white pixel. if (v1-v0) == -255, then - // (dx,dy) points towards the black pixel. p.gx and p.gy will thus - // be -255, 0, or 255. - // - // Note that any given pixel might be added to multiple - // different clusters. But in the common case, a given - // pixel will be added multiple times to the same cluster, - // which increases the size of the cluster and thus the - // computational costs. - // - // A possible optimization would be to combine entries - // within the same cluster. - -#define DO_CONN(dx, dy) \ - if (1) { \ - uint8_t v1 = thold.data[y*ts + dy*ts + x + dx]; \ - \ - if (v0 + v1 == 255) { \ - uint64_t rep1 = unionfind_get_representative(uf, y*w + dy*w + x + dx); \ - uint64_t clusterid; \ - if (rep0 < rep1) \ - clusterid = (rep1 << 32) + rep0; \ - else \ - clusterid = (rep0 << 32) + rep1; \ - \ - /* XXX lousy hash function */ \ - uint32_t clustermap_bucket = u64hash_2(clusterid) % nclustermap; \ - struct uint64_zarray_entry *entry = clustermap[clustermap_bucket]; \ - while (entry && entry->id != clusterid) { \ - entry = entry->next; \ - } \ - \ - if (!entry) { \ - entry = (struct uint64_zarray_entry*)calloc(1, sizeof(struct uint64_zarray_entry)); \ - entry->id = clusterid; \ - entry->cluster = _zarray_create(sizeof(struct pt)); \ - entry->next = clustermap[clustermap_bucket]; \ - clustermap[clustermap_bucket] = entry; \ - } \ - \ - struct pt p; \ - p.x = saturate_cast(2*x + dx); \ - p.y = saturate_cast(2*y + dy); \ - p.gx = saturate_cast(dx*((int) v1-v0)); \ - p.gy = saturate_cast(dy*((int) v1-v0)); \ - _zarray_add(entry->cluster, &p); \ - } \ - } - - // do 4 connectivity. NB: Arguments must be [-1, 1] or we'll overflow .gx, .gy - DO_CONN(1, 0); - DO_CONN(0, 1); - - // do 8 connectivity - DO_CONN(-1, 1); - DO_CONN(1, 1); -} -} -#undef DO_CONN - -#ifdef APRIL_DEBUG -Mat out = Mat::zeros(h, w, CV_8UC3); - -uint32_t *colors = (uint32_t*) calloc(w*h, sizeof(*colors)); - -for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - uint32_t v = unionfind_get_representative(uf, y*w+x); - - if (unionfind_get_set_size(uf, v) < parameters->aprilTagMinClusterPixels) - continue; - - uint32_t color = colors[v]; - uint8_t r = color >> 16, - g = color >> 8, - b = color; - - if (color == 0) { - const int bias = 50; - r = bias + (random() % (200-bias)); - g = bias + (random() % (200-bias)); - b = bias + (random() % (200-bias)); - colors[v] = (r << 16) | (g << 8) | b; - } - out.at(y, x)[0]=b; - out.at(y, x)[1]=g; - out.at(y, x)[2]=r; - } -} -free(colors); -imwrite("2.3 debug_segmentation.pnm", out); -out = Mat::zeros(h, w, CV_8UC3); -#endif - - //////////////////////////////////////////////////////// - // step 3. process each connected component. - zarray_t *clusters = _zarray_create(sizeof(zarray_t*)); //, uint64_zarray_hash_size(clustermap)); - CV_Assert(clusters != NULL); - - for (int i = 0; i < nclustermap; i++) { - for (struct uint64_zarray_entry *entry = clustermap[i]; entry; entry = entry->next) { - // XXX reject clusters here? - _zarray_add(clusters, &entry->cluster); - } - } - -#ifdef APRIL_DEBUG -for (int i = 0; i < _zarray_size(clusters); i++) { - zarray_t *cluster; - _zarray_get(clusters, i, &cluster); - - uint32_t r, g, b; - - const int bias = 50; - r = bias + (random() % (200-bias)); - g = bias + (random() % (200-bias)); - b = bias + (random() % (200-bias)); - - for (int j = 0; j < _zarray_size(cluster); j++) { - struct pt *p; - _zarray_get_volatile(cluster, j, &p); - - int x = p->x / 2; - int y = p->y / 2; - out.at(y, x)[0]=b; - out.at(y, x)[1]=g; - out.at(y, x)[2]=r; - } -} - -imwrite("2.4 debug_clusters.pnm", out); -out = Mat::zeros(h, w, CV_8UC3); -#endif - - for (int i = 0; i < _zarray_size(clusters); i++) { - zarray_t *cluster; - _zarray_get(clusters, i, &cluster); - - std::vector cnt; - for (int j = 0; j < _zarray_size(cluster); j++) { - struct pt *p; - _zarray_get_volatile(cluster, j, &p); - - Point pnt(p->x, p->y); - cnt.push_back(pnt); - } - contours.push_back(cnt); - } - - for (int i = 0; i < nclustermap; i++) { - struct uint64_zarray_entry *entry = clustermap[i]; - while (entry) { - struct uint64_zarray_entry *tmp = entry->next; - free(entry); - entry = tmp; - } - } - free(clustermap); - - zarray_t *quads = _zarray_create(sizeof(struct sQuad)); - - //int chunksize = 1 + sz / (APRILTAG_TASKS_PER_THREAD_TARGET * numberOfThreads); - int chunksize = std::max(1, h / (10 * getNumThreads())); - int sz = _zarray_size(clusters); - - // TODO PARALLELIZE - for (int i = 0; i < sz; i += chunksize) { - int min = sz < (i+chunksize)? sz: (i+chunksize); - do_quad(i, min, *clusters, w, h, quads, parameters, mImg); - } - -#ifdef APRIL_DEBUG -mImg.copyTo(out); -_darken(out); -_darken(out); -srandom(0); - -for (int i = 0; i < _zarray_size(quads); i++) { - struct sQuad *quad; - _zarray_get_volatile(quads, i, &quad); - - float rgb[3]; - int bias = 100; - - for (int i = 0; i < 3; i++) - rgb[i] = bias + (random() % (255-bias)); - - line(out, Point(quad->p[0][0], quad->p[0][1]), Point(quad->p[1][0], quad->p[1][1]), rgb[i]); - line(out, Point(quad->p[1][0], quad->p[1][1]), Point(quad->p[2][0], quad->p[2][1]), rgb[i]); - line(out, Point(quad->p[2][0], quad->p[2][1]), Point(quad->p[3][0], quad->p[3][1]), rgb[i]); - line(out, Point(quad->p[3][0], quad->p[3][1]), Point(quad->p[0][0], quad->p[0][1]), rgb[i]); -} -imwrite("2.5 debug_lines.pnm", out); -#endif - - unionfind_destroy(uf); - - for (int i = 0; i < _zarray_size(clusters); i++) { - zarray_t *cluster; - _zarray_get(clusters, i, &cluster); - _zarray_destroy(cluster); - } - _zarray_destroy(clusters); - return quads; -} - -void _apriltag(Mat im_orig, const Ptr & _params, std::vector > &candidates, - std::vector > &contours){ - - /////////////////////////////////////////////////////////// - /// Step 1. Detect quads according to requested image decimation - /// and blurring parameters. - Mat quad_im; - - if (_params->aprilTagQuadDecimate > 1){ - resize(im_orig, quad_im, Size(), 1/_params->aprilTagQuadDecimate, 1/_params->aprilTagQuadDecimate, INTER_AREA); - } - else { - im_orig.copyTo(quad_im); - } - - // Apply a Blur - if (_params->aprilTagQuadSigma != 0) { - // compute a reasonable kernel width by figuring that the - // kernel should go out 2 std devs. - // - // max sigma ksz - // 0.499 1 (disabled) - // 0.999 3 - // 1.499 5 - // 1.999 7 - - float sigma = fabsf((float) _params->aprilTagQuadSigma); - - int ksz = cvFloor(4 * sigma); // 2 std devs in each direction - ksz |= 1; // make odd number - - if (ksz > 1) { - if (_params->aprilTagQuadSigma > 0) - GaussianBlur(quad_im, quad_im, Size(ksz, ksz), sigma, sigma, BORDER_REPLICATE); - else { - Mat orig; - quad_im.copyTo(orig); - GaussianBlur(quad_im, quad_im, Size(ksz, ksz), sigma, sigma, BORDER_REPLICATE); - - // SHARPEN the image by subtracting the low frequency components. - for (int y = 0; y < orig.rows; y++) { - for (int x = 0; x < orig.cols; x++) { - int vorig = orig.data[y*orig.step + x]; - int vblur = quad_im.data[y*quad_im.step + x]; - - int v = 2*vorig - vblur; - if (v < 0) - v = 0; - if (v > 255) - v = 255; - - quad_im.data[y*quad_im.step + x] = (uint8_t) v; - } - } - } - } - } - -#ifdef APRIL_DEBUG - imwrite("1.1 debug_preprocess.pnm", quad_im); -#endif - - /////////////////////////////////////////////////////////// - /// Step 2. do the Threshold :: get the set of candidate quads - zarray_t *quads = apriltag_quad_thresh(_params, quad_im, contours); - - CV_Assert(quads != NULL); - - // adjust centers of pixels so that they correspond to the - // original full-resolution image. - if (_params->aprilTagQuadDecimate > 1) { - for (int i = 0; i < _zarray_size(quads); i++) { - struct sQuad *q; - _zarray_get_volatile(quads, i, &q); - for (int j = 0; j < 4; j++) { - q->p[j][0] *= _params->aprilTagQuadDecimate; - q->p[j][1] *= _params->aprilTagQuadDecimate; - } - } - } - -#ifdef APRIL_DEBUG - Mat im_quads = im_orig.clone(); - im_quads = im_quads*0.5; - srandom(0); - - for (int i = 0; i < _zarray_size(quads); i++) { - struct sQuad *quad; - _zarray_get_volatile(quads, i, &quad); - - const int bias = 100; - int color = bias + (random() % (255-bias)); - - line(im_quads, Point(quad->p[0][0], quad->p[0][1]), Point(quad->p[1][0], quad->p[1][1]), color, 1); - line(im_quads, Point(quad->p[1][0], quad->p[1][1]), Point(quad->p[2][0], quad->p[2][1]), color, 1); - line(im_quads, Point(quad->p[2][0], quad->p[2][1]), Point(quad->p[3][0], quad->p[3][1]), color, 1); - line(im_quads, Point(quad->p[3][0], quad->p[3][1]), Point(quad->p[0][0], quad->p[0][1]), color, 1); - } - imwrite("1.2 debug_quads_raw.pnm", im_quads); -#endif - - //////////////////////////////////////////////////////////////// - /// Step 3. Save the output :: candidate corners - for (int i = 0; i < _zarray_size(quads); i++) { - struct sQuad *quad; - _zarray_get_volatile(quads, i, &quad); - - std::vector corners; - corners.push_back(Point2f(quad->p[3][0], quad->p[3][1])); //pA - corners.push_back(Point2f(quad->p[0][0], quad->p[0][1])); //pB - corners.push_back(Point2f(quad->p[1][0], quad->p[1][1])); //pC - corners.push_back(Point2f(quad->p[2][0], quad->p[2][1])); //pD - - candidates.push_back(corners); - } - - _zarray_destroy(quads); -} - -}} diff --git a/modules/aruco/src/apriltag/apriltag_quad_thresh.hpp b/modules/aruco/src/apriltag/apriltag_quad_thresh.hpp deleted file mode 100644 index 2b8647ca15..0000000000 --- a/modules/aruco/src/apriltag/apriltag_quad_thresh.hpp +++ /dev/null @@ -1,119 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copyright (C) 2013-2016, The Regents of The University of Michigan. -// -// This software was developed in the APRIL Robotics Lab under the -// direction of Edwin Olson, ebolson@umich.edu. This software may be -// available under alternative licensing terms; contact the address above. -// -// The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, -// either expressed or implied, of the Regents of The University of Michigan. - -// limitation: image size must be <32768 in width and height. This is -// because we use a fixed-point 16 bit integer representation with one -// fractional bit. - -#ifndef _OPENCV_APRIL_QUAD_THRESH_HPP_ -#define _OPENCV_APRIL_QUAD_THRESH_HPP_ - -#include -#include "opencv2/aruco_detector.hpp" -#include "unionfind.hpp" -#include "zmaxheap.hpp" -#include "zarray.hpp" - -namespace cv { -namespace aruco { - -static inline uint32_t u64hash_2(uint64_t x) { - return uint32_t((2654435761UL * x) >> 32); -} - -struct uint64_zarray_entry{ - uint64_t id; - zarray_t *cluster; - - struct uint64_zarray_entry *next; -}; - -struct pt{ - // Note: these represent 2*actual value. - uint16_t x, y; - float theta; - int16_t gx, gy; -}; - -struct remove_vertex{ - int i; // which vertex to remove? - int left, right; // left vertex, right vertex - - double err; -}; - -struct segment{ - int is_vertex; - - // always greater than zero, but right can be > size, which denotes - // a wrap around back to the beginning of the points. and left < right. - int left, right; -}; - -struct line_fit_pt{ - double Mx, My; - double Mxx, Myy, Mxy; - double W; // total weight -}; - -/** - * lfps contains *cumulative* moments for N points, with - * index j reflecting points [0,j] (inclusive). - * fit a line to the points [i0, i1] (inclusive). i0, i1 are both (0, sz) - * if i1 < i0, we treat this as a wrap around. - */ -void fit_line(struct line_fit_pt *lfps, int sz, int i0, int i1, double *lineparm, double *err, double *mse); - -int err_compare_descending(const void *_a, const void *_b); - -/** - 1. Identify A) white points near a black point and B) black points near a white point. - - 2. Find the connected components within each of the classes above, - yielding clusters of "white-near-black" and - "black-near-white". (These two classes are kept separate). Each - segment has a unique id. - - 3. For every pair of "white-near-black" and "black-near-white" - clusters, find the set of points that are in one and adjacent to the - other. In other words, a "boundary" layer between the two - clusters. (This is actually performed by iterating over the pixels, - rather than pairs of clusters.) Critically, this helps keep nearby - edges from becoming connected. - **/ -int quad_segment_maxima(const Ptr &td, int sz, struct line_fit_pt *lfps, int indices[4]); - -/** - * returns 0 if the cluster looks bad. - */ -int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]); - -/** - * return 1 if the quad looks okay, 0 if it should be discarded - * quad - **/ -int fit_quad(const Ptr &_params, const Mat im, zarray_t *cluster, struct sQuad *quad); - - -void threshold(const Mat mIm, const Ptr ¶meters, Mat& mThresh); - - -zarray_t *apriltag_quad_thresh(const Ptr ¶meters, const Mat & mImg, - std::vector > &contours); - -void _apriltag(Mat im_orig, const Ptr & _params, std::vector > &candidates, - std::vector > &contours); - -}} -#endif diff --git a/modules/aruco/src/apriltag/predefined_dictionaries_apriltag.hpp b/modules/aruco/src/apriltag/predefined_dictionaries_apriltag.hpp deleted file mode 100644 index cad9802f53..0000000000 --- a/modules/aruco/src/apriltag/predefined_dictionaries_apriltag.hpp +++ /dev/null @@ -1,14900 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. - -namespace { - -/** - * Dictionaries are stored as a list of bytes in its four rotations. - * On each rotation, the marker is divided in bytes assuming a row-major order. - * This format allows a faster marker identification. - * For a dictionary composed by M markers of NxN bits, the structure dimensions should be: - * const char name[nMarkers][4rotations][nBytes], or more specifically: - * const char name[M][4][ceil(NxN/8)] - * The element [i][j][k] represents the k-th byte of the i-th marker in the dictionary - * in its j-th rotation. - * Each rotation implies a 90 degree rotation of the marker in anticlockwise direction. - */ -static unsigned char DICT_APRILTAG_16h5_BYTES[][4][2] = -{ - { {216, 196}, - {128, 190}, - {35, 27}, - {125, 1} - }, - { {165, 116}, - {106, 120}, - {46, 165}, - {30, 86} - }, - { {86, 44}, - {134, 209}, - {52, 106}, - {139, 97} - }, - { {157, 162}, - {195, 78}, - {69, 185}, - {114, 195} - }, - { {101, 158}, - {105, 211}, - {121, 166}, - {203, 150} - }, - { {214, 254}, - {167, 251}, - {127, 107}, - {223, 229} - }, - { {26, 205}, - {148, 55}, - {179, 88}, - {236, 41} - }, - { {162, 231}, - {31, 58}, - {231, 69}, - {92, 248} - }, - { {154, 127}, - {183, 61}, - {254, 89}, - {188, 237} - }, - { {182, 168}, - {142, 75}, - {21, 109}, - {210, 113} - }, - { {208, 28}, - {160, 153}, - {56, 11}, - {153, 5} - }, - { {213, 15}, - {209, 217}, - {240, 171}, - {155, 139} - }, - { {33, 176}, - {106, 2}, - {13, 132}, - {64, 86} - }, - { {108, 226}, - {11, 230}, - {71, 54}, - {103, 208} - }, - { {78, 49}, - {54, 196}, - {140, 114}, - {35, 108} - }, - { {8, 245}, - {50, 54}, - {175, 16}, - {108, 76} - }, - { {60, 144}, - {168, 70}, - {9, 60}, - {98, 21} - }, - { {45, 201}, - {88, 103}, - {147, 180}, - {230, 26} - }, - { {192, 165}, - {18, 154}, - {165, 3}, - {89, 72} - }, - { {241, 98}, - {203, 168}, - {70, 143}, - {21, 211} - }, - { {236, 135}, - {25, 222}, - {225, 55}, - {123, 152} - }, - { {169, 234}, - {75, 47}, - {87, 149}, - {244, 210} - }, - { {66, 251}, - {55, 163}, - {223, 66}, - {197, 236} - }, - { {184, 56}, - {170, 13}, - {28, 29}, - {176, 85} - }, - { {59, 151}, - {253, 22}, - {233, 220}, - {104, 191} - }, - { {181, 206}, - {201, 123}, - {115, 173}, - {222, 147} - }, - { {250, 181}, - {190, 158}, - {173, 95}, - {121, 125} - }, - { {12, 171}, - {19, 71}, - {213, 48}, - {226, 200} - }, - { {83, 224}, - {198, 162}, - {7, 202}, - {69, 99} - }, - { {116, 245}, - {186, 242}, - {175, 46}, - {79, 93} - } -}; - - - -static unsigned char DICT_APRILTAG_25h9_BYTES[][4][4] = -{ - { {143, 211, 170, 1}, - {234, 146, 237, 1}, - {170, 229, 248, 1}, - {219, 164, 171, 1} - }, - { {109, 139, 39, 1}, - {170, 251, 21, 0}, - {242, 104, 219, 0}, - {84, 111, 170, 1} - }, - { {22, 208, 222, 1}, - {94, 6, 244, 1}, - {189, 133, 180, 0}, - {151, 176, 61, 0} - }, - { {179, 147, 87, 1}, - {62, 118, 217, 0}, - {245, 100, 230, 1}, - {77, 183, 62, 0} - }, - { {115, 40, 116, 1}, - {28, 189, 146, 0}, - {151, 10, 103, 0}, - {36, 222, 156, 0} - }, - { {221, 18, 172, 0}, - {164, 145, 124, 1}, - {26, 164, 93, 1}, - {159, 68, 146, 1} - }, - { {103, 234, 238, 0}, - {98, 191, 182, 1}, - {59, 171, 243, 0}, - {182, 254, 163, 0} - }, - { {213, 186, 96, 1}, - {46, 157, 78, 0}, - {131, 46, 213, 1}, - {57, 92, 186, 0} - }, - { {231, 69, 93, 1}, - {89, 117, 157, 1}, - {221, 81, 115, 1}, - {220, 215, 77, 0} - }, - { {17, 101, 123, 0}, - {85, 214, 3, 1}, - {111, 83, 68, 0}, - {224, 53, 213, 0} - }, - { {228, 155, 129, 0}, - {34, 105, 109, 0}, - {64, 236, 147, 1}, - {91, 75, 34, 0} - }, - { {130, 77, 138, 0}, - {65, 10, 169, 1}, - {40, 217, 32, 1}, - {202, 168, 65, 0} - }, - { {117, 19, 219, 0}, - {52, 119, 101, 1}, - {109, 228, 87, 0}, - {211, 119, 22, 0} - }, - { {152, 112, 167, 1}, - {204, 194, 122, 0}, - {242, 135, 12, 1}, - {47, 33, 153, 1} - }, - { {75, 152, 26, 1}, - {154, 27, 192, 1}, - {172, 12, 233, 0}, - {129, 236, 44, 1} - }, - { {65, 65, 182, 1}, - {88, 147, 49, 0}, - {182, 193, 65, 0}, - {70, 100, 141, 0} - }, - { {172, 7, 198, 1}, - {169, 38, 61, 0}, - {177, 240, 26, 1}, - {94, 50, 74, 1} - }, - { {129, 217, 185, 0}, - {82, 216, 105, 1}, - {78, 205, 192, 1}, - {203, 13, 165, 0} - }, - { {35, 119, 206, 1}, - {105, 54, 243, 1}, - {185, 247, 98, 0}, - {231, 182, 75, 0} - }, - { {144, 92, 117, 0}, - {85, 204, 88, 0}, - {87, 29, 4, 1}, - {13, 25, 213, 0} - }, - { {138, 62, 250, 0}, - {177, 142, 234, 1}, - {47, 190, 40, 1}, - {171, 184, 198, 1} - }, - { {63, 236, 133, 1}, - {207, 120, 182, 0}, - {208, 155, 254, 0}, - {54, 143, 121, 1} - }, - { {70, 169, 59, 0}, - {18, 203, 135, 1}, - {110, 74, 177, 0}, - {240, 233, 164, 0} - }, - { {162, 116, 1, 1}, - {73, 96, 202, 0}, - {192, 23, 34, 1}, - {41, 131, 73, 0} - }, - { {52, 42, 195, 1}, - {44, 110, 38, 0}, - {225, 170, 22, 0}, - {50, 59, 26, 0} - }, - { {37, 240, 225, 0}, - {66, 244, 102, 0}, - {67, 135, 210, 0}, - {51, 23, 161, 0} - }, - { {144, 226, 137, 0}, - {102, 64, 42, 1}, - {72, 163, 132, 1}, - {170, 1, 51, 0} - }, - { {15, 33, 102, 1}, - {136, 150, 151, 0}, - {179, 66, 120, 0}, - {116, 180, 136, 1} - }, - { {165, 79, 19, 0}, - {113, 122, 13, 0}, - {100, 121, 82, 1}, - {88, 47, 71, 0} - }, - { {221, 124, 193, 0}, - {197, 93, 110, 0}, - {65, 159, 93, 1}, - {59, 93, 81, 1} - }, - { {254, 142, 154, 0}, - {183, 43, 172, 1}, - {44, 184, 191, 1}, - {154, 234, 118, 1} - }, - { {10, 11, 60, 1}, - {184, 136, 145, 1}, - {158, 104, 40, 0}, - {196, 136, 142, 1} - }, - { {139, 80, 95, 0}, - {208, 86, 216, 1}, - {125, 5, 104, 1}, - {141, 181, 5, 1} - }, - { {13, 198, 237, 1}, - {235, 212, 52, 1}, - {219, 177, 216, 0}, - {150, 21, 235, 1} - }, - { {115, 137, 157, 0}, - {22, 121, 177, 1}, - {92, 200, 231, 0}, - {198, 207, 52, 0} - } -}; - - - -static unsigned char DICT_APRILTAG_36h10_BYTES[][4][5] = -{ - { {225, 101, 73, 83, 8}, - {49, 6, 165, 238, 1}, - {28, 169, 42, 104, 7}, - {135, 122, 86, 8, 12} - }, - { {50, 53, 132, 160, 4}, - {65, 139, 98, 33, 2}, - {32, 82, 26, 196, 12}, - {72, 68, 109, 24, 2} - }, - { {107, 237, 252, 94, 4}, - {43, 167, 244, 249, 13}, - {39, 163, 251, 125, 6}, - {185, 242, 254, 93, 4} - }, - { {217, 189, 115, 45, 4}, - {123, 106, 82, 246, 12}, - {43, 76, 235, 217, 11}, - {54, 244, 165, 109, 14} - }, - { {87, 115, 222, 38, 12}, - {233, 207, 79, 209, 1}, - {54, 71, 188, 238, 10}, - {136, 191, 47, 57, 7} - }, - { {245, 43, 80, 249, 12}, - {161, 42, 235, 222, 10}, - {57, 240, 173, 74, 15}, - {87, 189, 117, 72, 5} - }, - { {46, 91, 153, 155, 12}, - {210, 166, 237, 21, 11}, - {61, 153, 157, 167, 4}, - {218, 139, 118, 84, 11} - }, - { {127, 167, 237, 114, 2}, - {179, 155, 190, 233, 5}, - {68, 235, 126, 95, 14}, - {169, 119, 221, 156, 13} - }, - { {72, 191, 207, 28, 10}, - {123, 243, 157, 160, 8}, - {83, 143, 63, 209, 2}, - {16, 91, 156, 253, 14} - }, - { {235, 64, 193, 206, 10}, - {58, 180, 33, 203, 3}, - {87, 56, 48, 45, 7}, - {205, 56, 66, 213, 12} - }, - { {215, 152, 253, 186, 6}, - {240, 185, 214, 211, 15}, - {101, 219, 241, 158, 11}, - {252, 182, 185, 208, 15} - }, - { {92, 212, 54, 176, 14}, - {194, 93, 211, 176, 6}, - {112, 214, 194, 179, 10}, - {96, 220, 187, 164, 3} - }, - { {114, 66, 21, 239, 14}, - {24, 61, 107, 157, 3}, - {127, 122, 132, 36, 14}, - {203, 157, 107, 193, 8} - }, - { {200, 18, 236, 76, 1}, - {110, 161, 12, 138, 4}, - {131, 35, 116, 129, 3}, - {37, 19, 8, 87, 6} - }, - { {27, 178, 99, 54, 1}, - {127, 72, 154, 65, 5}, - {134, 204, 100, 221, 8}, - {168, 37, 145, 47, 14} - }, - { {185, 202, 170, 229, 1}, - {14, 204, 62, 78, 14}, - {138, 117, 85, 57, 13}, - {119, 39, 195, 55, 0} - }, - { {55, 38, 64, 234, 9}, - {165, 40, 43, 105, 3}, - {149, 112, 38, 78, 12}, - {201, 109, 65, 74, 5} - }, - { {141, 86, 137, 137, 9}, - {214, 164, 13, 102, 2}, - {153, 25, 22, 171, 1}, - {70, 107, 2, 86, 11} - }, - { {220, 110, 245, 96, 5}, - {183, 141, 74, 186, 12}, - {160, 106, 247, 99, 11}, - {53, 213, 43, 30, 13} - }, - { {81, 225, 58, 109, 5}, - {13, 110, 86, 220, 4}, - {171, 101, 200, 120, 10}, - {35, 182, 167, 107, 0} - }, - { {40, 201, 94, 212, 13}, - {46, 71, 245, 24, 10}, - {178, 183, 169, 49, 4}, - {81, 138, 254, 39, 4} - }, - { {155, 25, 144, 102, 13}, - {78, 138, 67, 91, 9}, - {182, 96, 153, 141, 9}, - {157, 172, 37, 23, 2} - }, - { {198, 37, 228, 219, 13}, - {165, 163, 193, 175, 7}, - {189, 178, 122, 70, 3}, - {239, 88, 60, 90, 5} - }, - { {238, 221, 198, 163, 3}, - {230, 215, 50, 167, 11}, - {204, 86, 59, 183, 7}, - {222, 84, 206, 182, 7} - }, - { {168, 123, 189, 36, 7}, - {95, 151, 110, 18, 12}, - {226, 75, 221, 225, 5}, - {52, 135, 110, 159, 10} - }, - { {38, 247, 86, 43, 7}, - {229, 119, 122, 53, 1}, - {237, 70, 174, 246, 4}, - {138, 197, 238, 234, 7} - }, - { {119, 95, 17, 146, 15}, - {212, 30, 233, 241, 11}, - {244, 152, 143, 174, 14}, - {216, 249, 119, 130, 11} - }, - { {227, 232, 42, 118, 0}, - {9, 68, 182, 203, 13}, - {6, 229, 65, 124, 7}, - {189, 54, 210, 41, 0} - }, - { {219, 116, 9, 26, 8}, - {83, 44, 133, 227, 1}, - {21, 137, 2, 237, 11}, - {140, 122, 19, 76, 10} - }, - { {166, 92, 117, 179, 8}, - {240, 5, 163, 55, 15}, - {28, 218, 227, 166, 5}, - {254, 204, 90, 0, 15} - }, - { {247, 162, 51, 66, 4}, - {145, 72, 120, 219, 5}, - {36, 44, 196, 94, 15}, - {173, 177, 225, 40, 9} - }, - { {45, 210, 218, 190, 4}, - {234, 228, 254, 80, 3}, - {39, 213, 180, 187, 4}, - {192, 167, 242, 117, 7} - }, - { {158, 138, 87, 237, 4}, - {186, 105, 90, 31, 10}, - {43, 126, 165, 23, 9}, - {95, 133, 169, 101, 13} - }, - { {192, 186, 16, 180, 12}, - {73, 0, 219, 146, 10}, - {50, 208, 133, 208, 3}, - {84, 157, 176, 9, 2} - }, - { {19, 70, 233, 230, 12}, - {56, 140, 79, 105, 7}, - {54, 121, 118, 44, 8}, - {233, 111, 35, 17, 12} - }, - { {177, 22, 100, 69, 12}, - {104, 9, 105, 110, 4}, - {58, 34, 102, 136, 13}, - {39, 105, 105, 1, 6} - }, - { {74, 118, 173, 59, 12}, - {83, 165, 207, 165, 5}, - {61, 203, 86, 229, 2}, - {170, 95, 58, 92, 10} - }, - { {133, 254, 70, 49, 2}, - {225, 85, 154, 102, 8}, - {72, 198, 39, 250, 1}, - {22, 101, 154, 168, 7} - }, - { {173, 105, 121, 110, 10}, - {187, 54, 39, 90, 13}, - {87, 105, 233, 107, 5}, - {181, 174, 70, 205, 13} - }, - { {32, 21, 210, 100, 6}, - {104, 210, 98, 56, 0}, - {98, 100, 186, 128, 4}, - {1, 196, 100, 177, 6} - }, - { {147, 181, 91, 6, 6}, - {121, 90, 84, 115, 1}, - {102, 13, 170, 220, 9}, - {140, 226, 165, 169, 14} - }, - { {202, 157, 31, 171, 6}, - {82, 115, 86, 183, 11}, - {109, 95, 139, 149, 3}, - {222, 214, 172, 228, 10} - }, - { {187, 35, 104, 210, 14}, - {35, 26, 237, 75, 7}, - {116, 177, 108, 77, 13}, - {237, 43, 117, 140, 4} - }, - { {52, 107, 195, 95, 14}, - {185, 254, 233, 12, 9}, - {127, 172, 61, 98, 12}, - {147, 9, 119, 249, 13} - }, - { {160, 207, 113, 248, 9}, - {52, 38, 187, 58, 14}, - {145, 248, 239, 48, 5}, - {117, 205, 214, 66, 12} - }, - { {83, 31, 184, 90, 9}, - {68, 170, 141, 249, 13}, - {149, 161, 223, 140, 10}, - {185, 251, 21, 82, 2} - }, - { {241, 127, 53, 41, 9}, - {85, 47, 43, 246, 12}, - {153, 74, 207, 232, 15}, - {54, 253, 79, 74, 10} - }, - { {152, 80, 211, 144, 5}, - {118, 204, 192, 18, 2}, - {160, 156, 176, 161, 9}, - {68, 128, 51, 54, 14} - }, - { {123, 240, 90, 34, 5}, - {103, 76, 118, 209, 1}, - {164, 69, 160, 253, 14}, - {136, 182, 227, 46, 6} - }, - { {197, 168, 151, 126, 5}, - {157, 225, 210, 218, 9}, - {167, 238, 145, 90, 3}, - {149, 180, 184, 123, 9} - }, - { {22, 216, 30, 157, 5}, - {204, 109, 212, 21, 10}, - {171, 151, 129, 182, 8}, - {90, 130, 187, 99, 3} - }, - { {180, 132, 224, 47, 5}, - {172, 168, 114, 38, 5}, - {175, 64, 114, 18, 13}, - {166, 68, 225, 83, 5} - }, - { {237, 52, 164, 150, 13}, - {207, 129, 225, 226, 7}, - {182, 146, 82, 203, 7}, - {228, 120, 120, 31, 3} - }, - { {9, 146, 15, 177, 3}, - {86, 81, 158, 68, 2}, - {200, 223, 4, 153, 0}, - {66, 39, 152, 166, 10} - }, - { {251, 90, 185, 188, 11}, - {94, 188, 175, 211, 14}, - {211, 217, 213, 173, 15}, - {124, 191, 83, 215, 10} - }, - { {37, 6, 52, 30, 11}, - {140, 49, 169, 112, 5}, - {215, 130, 198, 10, 4}, - {160, 233, 88, 195, 1} - }, - { {224, 145, 168, 72, 15}, - {68, 178, 117, 138, 4}, - {241, 33, 88, 144, 7}, - {37, 26, 228, 210, 2} - }, - { {194, 115, 147, 11, 8}, - {81, 230, 9, 151, 1}, - {29, 12, 156, 228, 3}, - {142, 153, 6, 120, 10} - }, - { {16, 43, 26, 208, 4}, - {1, 74, 204, 24, 10}, - {32, 181, 141, 64, 8}, - {81, 131, 53, 40, 0} - }, - { {56, 215, 41, 175, 4}, - {90, 46, 126, 36, 7}, - {47, 89, 78, 177, 12}, - {226, 71, 231, 69, 10} - }, - { {214, 63, 130, 165, 12}, - {201, 202, 75, 167, 10}, - {58, 84, 31, 198, 11}, - {94, 93, 37, 57, 3} - }, - { {93, 176, 247, 42, 2}, - {243, 249, 18, 208, 5}, - {69, 78, 240, 219, 10}, - {160, 180, 137, 252, 15} - }, - { {115, 36, 212, 252, 10}, - {41, 185, 163, 249, 2}, - {83, 242, 178, 76, 14}, - {73, 252, 89, 217, 4} - }, - { {71, 220, 230, 148, 6}, - {232, 213, 208, 225, 14}, - {98, 150, 115, 190, 2}, - {120, 112, 186, 177, 7} - }, - { {221, 26, 76, 114, 14}, - {226, 25, 207, 202, 9}, - {116, 227, 37, 139, 11}, - {149, 63, 57, 132, 7} - }, - { {154, 222, 49, 181, 1}, - {94, 12, 154, 55, 14}, - {138, 216, 199, 181, 9}, - {126, 197, 147, 7, 10} - }, - { {76, 25, 234, 75, 9}, - {230, 226, 5, 140, 13}, - {157, 37, 121, 131, 2}, - {179, 26, 4, 118, 7} - }, - { {129, 149, 32, 65, 5}, - {68, 2, 80, 110, 4}, - {168, 32, 74, 152, 1}, - {39, 96, 164, 2, 2} - }, - { {208, 173, 68, 239, 5}, - {45, 43, 82, 174, 11}, - {175, 114, 43, 80, 11}, - {215, 84, 173, 75, 4} - }, - { {169, 3, 2, 54, 13}, - {14, 66, 235, 66, 1}, - {182, 196, 12, 9, 5}, - {132, 45, 116, 39, 0} - }, - { {90, 99, 243, 101, 13}, - {63, 206, 75, 157, 4}, - {186, 108, 252, 101, 10}, - {43, 157, 39, 63, 12} - }, - { {167, 56, 140, 168, 15}, - {197, 177, 103, 67, 10}, - {241, 83, 17, 206, 5}, - {92, 46, 104, 218, 3} - }, - { {193, 252, 209, 46, 0}, - {121, 164, 18, 242, 9}, - {7, 72, 179, 248, 3}, - {148, 244, 130, 89, 14} - }, - { {18, 162, 88, 245, 0}, - {41, 8, 158, 29, 2}, - {10, 241, 164, 84, 8}, - {75, 135, 145, 9, 4} - }, - { {176, 210, 149, 151, 0}, - {88, 141, 184, 22, 3}, - {14, 154, 148, 176, 13}, - {198, 129, 219, 17, 10} - }, - { {172, 158, 132, 125, 4}, - {202, 161, 250, 46, 8}, - {43, 226, 23, 147, 5}, - {23, 69, 248, 85, 3} - }, - { {112, 105, 55, 71, 12}, - {25, 79, 97, 156, 13}, - {62, 46, 201, 96, 14}, - {179, 152, 111, 41, 8} - }, - { {68, 181, 20, 179, 2}, - {193, 19, 146, 180, 3}, - {76, 210, 138, 210, 2}, - {194, 212, 156, 136, 3} - }, - { {231, 77, 237, 224, 10}, - {176, 151, 39, 235, 14}, - {80, 123, 123, 46, 7}, - {125, 126, 78, 144, 13} - }, - { {53, 29, 98, 66, 10}, - {224, 90, 33, 104, 13}, - {84, 36, 107, 138, 12}, - {177, 104, 69, 160, 7} - }, - { {142, 125, 171, 62, 10}, - {219, 246, 135, 35, 13}, - {87, 205, 91, 231, 1}, - {188, 78, 22, 253, 11} - }, - { {82, 219, 5, 133, 6}, - {88, 31, 88, 133, 10}, - {106, 26, 13, 180, 10}, - {90, 17, 175, 129, 10} - }, - { {240, 135, 244, 59, 6}, - {32, 187, 250, 182, 5}, - {109, 194, 254, 16, 15}, - {166, 213, 253, 208, 4} - }, - { {78, 144, 152, 110, 1}, - {206, 160, 22, 153, 1}, - {135, 97, 144, 151, 2}, - {137, 150, 128, 87, 3} - }, - { {210, 100, 39, 89, 9}, - {21, 109, 129, 175, 4}, - {153, 174, 66, 100, 11}, - {47, 88, 27, 106, 8} - }, - { {36, 194, 245, 253, 5}, - {188, 165, 250, 28, 6}, - {171, 250, 244, 50, 4}, - {99, 133, 250, 83, 13} - }, - { {50, 142, 228, 41, 3}, - {36, 185, 58, 37, 12}, - {201, 66, 119, 20, 12}, - {58, 69, 201, 210, 4} - }, - { {107, 62, 162, 144, 11}, - {71, 208, 169, 225, 14}, - {208, 148, 87, 205, 6}, - {120, 121, 80, 190, 2} - }, - { {217, 65, 43, 194, 11}, - {22, 94, 5, 202, 7}, - {212, 61, 72, 41, 11}, - {229, 58, 7, 166, 8} - }, - { {156, 165, 116, 197, 7}, - {175, 27, 80, 62, 6}, - {234, 50, 234, 83, 9}, - {103, 192, 173, 143, 5} - }, - { {17, 109, 219, 74, 15}, - {53, 254, 69, 120, 9}, - {245, 45, 187, 104, 8}, - {145, 234, 39, 250, 12} - }, - { {174, 87, 71, 134, 8}, - {250, 71, 41, 35, 3}, - {22, 30, 46, 167, 5}, - {204, 73, 78, 37, 15} - }, - { {114, 224, 117, 97, 4}, - {49, 13, 114, 157, 4}, - {40, 106, 224, 116, 14}, - {43, 148, 235, 8, 12} - }, - { {198, 150, 180, 86, 6}, - {200, 145, 216, 187, 5}, - {102, 162, 214, 150, 3}, - {173, 209, 184, 145, 3} - }, - { {168, 197, 195, 205, 1}, - {62, 230, 48, 46, 2}, - {139, 60, 58, 49, 5}, - {71, 64, 198, 119, 12} - }, - { {91, 21, 74, 119, 1}, - {110, 74, 134, 237, 1}, - {142, 229, 42, 141, 10}, - {139, 118, 21, 39, 6} - }, - { {205, 163, 184, 200, 5}, - {135, 162, 92, 218, 6}, - {161, 49, 220, 91, 3}, - {101, 179, 164, 94, 1} - }, - { {74, 204, 221, 214, 7}, - {62, 149, 212, 185, 11}, - {230, 187, 179, 53, 2}, - {217, 210, 186, 151, 12} - }, - { {102, 82, 239, 170, 15}, - {244, 245, 111, 129, 7}, - {245, 95, 116, 166, 6}, - {232, 31, 106, 242, 15} - }, - { {173, 218, 37, 160, 0}, - {210, 5, 58, 66, 14}, - {0, 90, 69, 187, 5}, - {116, 37, 202, 4, 11} - }, - { {252, 230, 67, 94, 0}, - {187, 108, 184, 170, 1}, - {7, 172, 38, 115, 15}, - {133, 81, 211, 109, 13} - }, - { {187, 145, 56, 91, 8}, - {66, 42, 181, 95, 5}, - {29, 161, 200, 157, 13}, - {175, 170, 213, 68, 2} - }, - { {52, 217, 147, 145, 4}, - {208, 206, 240, 20, 10}, - {40, 156, 153, 178, 12}, - {82, 128, 247, 48, 11} - }, - { {160, 29, 41, 117, 12}, - {88, 2, 231, 46, 12}, - {58, 233, 75, 128, 5}, - {55, 78, 116, 1, 10} - }, - { {197, 91, 119, 176, 10}, - {240, 87, 139, 210, 14}, - {80, 222, 237, 170, 3}, - {116, 189, 30, 160, 15} - }, - { {180, 103, 48, 193, 10}, - {129, 30, 41, 62, 6}, - {88, 48, 206, 98, 13}, - {103, 201, 71, 136, 1} - }, - { {79, 215, 217, 189, 10}, - {250, 182, 159, 245, 2}, - {91, 217, 190, 191, 2}, - {74, 255, 150, 213, 15} - }, - { {62, 239, 157, 76, 6}, - {155, 191, 124, 57, 8}, - {99, 43, 159, 119, 12}, - {25, 195, 239, 221, 9} - }, - { {211, 16, 230, 71, 6}, - {104, 217, 64, 207, 5}, - {110, 38, 112, 140, 11}, - {175, 48, 41, 177, 6} - }, - { {150, 212, 131, 130, 1}, - {212, 204, 16, 35, 3}, - {132, 28, 18, 182, 9}, - {204, 64, 131, 50, 11} - }, - { {190, 66, 191, 212, 9}, - {158, 205, 173, 27, 6}, - {146, 191, 212, 39, 13}, - {109, 139, 91, 55, 9} - }, - { {66, 18, 49, 102, 9}, - {92, 0, 11, 153, 5}, - {150, 104, 196, 132, 2}, - {169, 157, 0, 3, 10} - }, - { {51, 202, 85, 219, 9}, - {52, 45, 185, 93, 11}, - {157, 186, 165, 60, 12}, - {219, 169, 219, 66, 12} - }, - { {165, 14, 174, 0, 13}, - {132, 193, 109, 98, 12}, - {176, 7, 87, 10, 5}, - {52, 107, 104, 50, 1} - }, - { {86, 110, 32, 220, 13}, - {141, 44, 201, 169, 14}, - {179, 176, 71, 102, 10}, - {121, 89, 51, 75, 1} - }, - { {126, 177, 2, 164, 3}, - {207, 90, 50, 129, 2}, - {194, 84, 8, 215, 14}, - {72, 20, 197, 175, 3} - }, - { {175, 51, 166, 149, 7}, - {207, 211, 232, 71, 6}, - {234, 150, 92, 207, 5}, - {110, 33, 124, 191, 3} - }, - { {144, 39, 133, 201, 15}, - {21, 187, 73, 46, 2}, - {249, 58, 30, 64, 9}, - {71, 73, 45, 218, 8} - }, - { {26, 175, 114, 194, 0}, - {35, 74, 24, 57, 15}, - {4, 52, 239, 85, 8}, - {249, 193, 133, 44, 4} - }, - { {140, 72, 88, 22, 8}, - {170, 4, 133, 18, 9}, - {22, 129, 161, 35, 1}, - {148, 138, 18, 5, 5} - }, - { {221, 184, 28, 187, 8}, - {195, 41, 151, 214, 11}, - {29, 211, 129, 219, 11}, - {214, 190, 153, 76, 3} - }, - { {41, 92, 192, 64, 12}, - {98, 132, 97, 104, 8}, - {48, 32, 51, 169, 4}, - {17, 104, 98, 20, 6} - }, - { {204, 21, 5, 90, 14}, - {210, 51, 193, 170, 1}, - {117, 170, 10, 131, 3}, - {133, 88, 60, 196, 11} - }, - { {218, 107, 24, 244, 9}, - {15, 14, 143, 155, 10}, - {146, 241, 141, 101, 11}, - {93, 159, 23, 15, 0} - }, - { {167, 135, 108, 37, 9}, - {172, 3, 63, 103, 4}, - {154, 67, 110, 30, 5}, - {46, 111, 204, 3, 5} - }, - { {246, 183, 42, 152, 5}, - {197, 106, 252, 163, 6}, - {161, 149, 78, 214, 15}, - {108, 83, 245, 106, 3} - }, - { {176, 8, 245, 30, 13}, - {60, 169, 225, 18, 13}, - {183, 138, 241, 0, 13}, - {180, 136, 121, 83, 12} - }, - { {75, 168, 124, 205, 13}, - {47, 33, 85, 221, 14}, - {187, 51, 225, 93, 2}, - {123, 186, 168, 79, 4} - }, - { {132, 228, 215, 70, 3}, - {189, 213, 16, 58, 1}, - {198, 46, 178, 114, 1}, - {133, 192, 138, 187, 13} - }, - { {108, 145, 86, 98, 0}, - {226, 67, 50, 152, 1}, - {4, 102, 168, 147, 6}, - {129, 148, 204, 36, 7} - }, - { {161, 89, 232, 111, 0}, - {104, 166, 38, 78, 13}, - {15, 97, 121, 168, 5}, - {183, 38, 70, 81, 6} - }, - { {240, 101, 172, 214, 8}, - {9, 143, 165, 170, 7}, - {22, 179, 90, 96, 15}, - {229, 90, 95, 25, 0} - }, - { {151, 176, 34, 145, 10}, - {193, 88, 145, 71, 6}, - {88, 148, 64, 222, 9}, - {110, 40, 145, 168, 3} - }, - { {191, 36, 1, 206, 6}, - {155, 56, 96, 107, 3}, - {103, 56, 2, 79, 13}, - {205, 96, 97, 205, 9} - }, - { {164, 202, 31, 226, 1}, - {148, 69, 62, 26, 11}, - {132, 127, 133, 50, 5}, - {213, 135, 202, 34, 9} - }, - { {17, 222, 135, 135, 9}, - {92, 205, 25, 100, 11}, - {158, 30, 23, 184, 8}, - {210, 105, 139, 51, 10} - }, - { {57, 73, 184, 51, 5}, - {6, 142, 230, 84, 13}, - {172, 193, 217, 41, 12}, - {178, 166, 119, 22, 0} - }, - { {134, 25, 53, 96, 13}, - {212, 3, 67, 27, 12}, - {176, 106, 201, 134, 1}, - {61, 140, 44, 2, 11} - }, - { {200, 75, 66, 10, 11}, - {38, 118, 9, 130, 9}, - {213, 4, 45, 33, 3}, - {148, 25, 6, 230, 4} - }, - { {81, 136, 22, 43, 15}, - {4, 121, 83, 212, 9}, - {253, 70, 129, 24, 10}, - {146, 188, 169, 226, 0} - }, - { {198, 116, 204, 159, 0}, - {233, 165, 132, 167, 3}, - {15, 147, 50, 230, 3}, - {206, 82, 26, 89, 7} - }, - { {168, 22, 147, 66, 12}, - {82, 192, 105, 58, 1}, - {52, 44, 150, 129, 5}, - {133, 201, 96, 52, 10} - }, - { {91, 182, 26, 190, 12}, - {75, 104, 223, 241, 3}, - {55, 213, 134, 221, 10}, - {200, 255, 177, 109, 2} - }, - { {64, 57, 11, 138, 10}, - {81, 114, 5, 128, 11}, - {85, 29, 9, 192, 2}, - {208, 26, 4, 232, 10} - }, - { {49, 229, 119, 99, 10}, - {49, 95, 51, 124, 5}, - {92, 110, 234, 120, 12}, - {163, 236, 207, 168, 12} - }, - { {138, 181, 190, 128, 6}, - {67, 211, 84, 51, 6}, - {96, 23, 218, 213, 1}, - {108, 194, 172, 188, 2} - }, - { {232, 127, 242, 172, 9}, - {111, 230, 43, 178, 14}, - {147, 84, 255, 225, 7}, - {116, 221, 70, 127, 6} - }, - { {133, 144, 118, 85, 9}, - {236, 65, 145, 94, 4}, - {154, 166, 224, 154, 1}, - {39, 168, 152, 35, 7} - }, - { {173, 4, 85, 41, 5}, - {182, 33, 98, 118, 0}, - {169, 74, 162, 11, 5}, - {6, 228, 104, 70, 13} - }, - { {94, 100, 156, 243, 5}, - {135, 141, 198, 189, 3}, - {172, 243, 146, 103, 10}, - {203, 214, 59, 30, 1} - }, - { {113, 188, 174, 157, 13}, - {77, 233, 245, 228, 14}, - {187, 151, 83, 216, 14}, - {114, 122, 249, 123, 2} - }, - { {202, 194, 32, 47, 13}, - {14, 36, 91, 135, 5}, - {191, 64, 68, 53, 3}, - {174, 29, 162, 71, 0} - }, - { {237, 109, 12, 225, 0}, - {131, 7, 38, 238, 10}, - {8, 115, 11, 107, 7}, - {87, 118, 78, 12, 1} - }, - { {211, 179, 54, 142, 8}, - {73, 107, 25, 211, 7}, - {23, 22, 204, 220, 11}, - {236, 185, 141, 105, 2} - }, - { {88, 251, 152, 132, 4}, - {75, 142, 92, 144, 10}, - {34, 17, 157, 241, 10}, - {80, 147, 167, 29, 2} - }, - { {66, 176, 73, 175, 12}, - {121, 32, 87, 133, 3}, - {63, 89, 32, 212, 2}, - {202, 30, 160, 73, 14} - }, - { {165, 172, 222, 241, 10}, - {161, 209, 183, 126, 10}, - {88, 247, 179, 90, 5}, - {87, 238, 216, 184, 5} - }, - { {126, 74, 98, 45, 6}, - {170, 124, 106, 133, 12}, - {107, 68, 101, 39, 14}, - {58, 21, 99, 229, 5} - }, - { {93, 233, 95, 78, 9}, - {191, 111, 21, 216, 9}, - {151, 47, 169, 123, 10}, - {145, 186, 143, 111, 13} - }, - { {34, 197, 24, 23, 9}, - {12, 6, 181, 53, 1}, - {158, 129, 138, 52, 4}, - {138, 202, 214, 3, 0} - }, - { {115, 117, 108, 186, 5}, - {101, 47, 230, 225, 7}, - {165, 211, 106, 236, 14}, - {232, 118, 127, 74, 6} - }, - { {80, 47, 87, 216, 11}, - {53, 123, 137, 184, 10}, - {209, 190, 175, 64, 10}, - {81, 217, 29, 234, 12} - }, - { {154, 160, 25, 211, 11}, - {23, 24, 149, 31, 3}, - {220, 185, 128, 85, 9}, - {207, 138, 145, 142, 8} - }, - { {61, 180, 129, 166, 15}, - {223, 152, 115, 96, 3}, - {246, 88, 18, 219, 12}, - {192, 108, 225, 159, 11} - }, - { {39, 218, 159, 156, 8}, - {216, 229, 189, 81, 10}, - {19, 159, 149, 190, 4}, - {88, 171, 218, 113, 11} - }, - { {58, 103, 134, 237, 6}, - {11, 255, 106, 45, 2}, - {107, 118, 30, 101, 12}, - {75, 69, 111, 253, 0} - }, - { {132, 55, 15, 143, 6}, - {217, 115, 76, 38, 3}, - {111, 31, 14, 194, 1}, - {198, 67, 44, 233, 11} - }, - { {53, 66, 74, 6, 5}, - {172, 76, 108, 64, 1}, - {166, 5, 36, 42, 12}, - {128, 35, 99, 35, 5} - }, - { {131, 37, 139, 194, 7}, - {21, 210, 68, 107, 3}, - {228, 61, 26, 76, 1}, - {205, 98, 36, 186, 8} - }, - { {6, 72, 246, 57, 4}, - {160, 229, 194, 21, 12}, - {41, 198, 241, 38, 0}, - {58, 132, 58, 112, 5} - }, - { {46, 148, 213, 97, 12}, - {242, 129, 115, 61, 0}, - {56, 106, 178, 151, 4}, - {11, 204, 232, 20, 15} - }, - { {72, 50, 160, 103, 2}, - {75, 144, 10, 140, 5}, - {78, 96, 84, 193, 2}, - {163, 21, 0, 157, 2} - }, - { {228, 117, 33, 147, 1}, - {213, 6, 160, 166, 7}, - {140, 152, 74, 226, 7}, - {230, 80, 86, 10, 11} - }, - { {110, 253, 140, 158, 9}, - {207, 167, 181, 161, 11}, - {151, 147, 27, 247, 6}, - {216, 90, 222, 95, 3} - }, - { {0, 211, 242, 119, 9}, - {108, 198, 155, 28, 5}, - {158, 228, 252, 176, 0}, - {163, 141, 150, 51, 6} - }, - { {99, 216, 68, 68, 11}, - {108, 21, 49, 201, 8}, - {210, 34, 33, 188, 6}, - {25, 56, 202, 131, 6} - }, - { {249, 28, 183, 98, 7}, - {86, 217, 98, 250, 13}, - {228, 110, 211, 137, 15}, - {181, 244, 105, 182, 10} - }, - { {148, 34, 208, 61, 7}, - {173, 184, 202, 22, 0}, - {235, 192, 180, 66, 9}, - {6, 133, 49, 219, 5} - }, - { {187, 200, 20, 31, 6}, - {10, 61, 240, 87, 9}, - {111, 130, 129, 61, 13}, - {158, 160, 251, 197, 0} - }, - { {69, 152, 237, 76, 14}, - {248, 177, 85, 200, 12}, - {115, 43, 113, 154, 2}, - {49, 58, 168, 209, 15} - }, - { {83, 226, 244, 224, 9}, - {37, 141, 27, 217, 6}, - {144, 114, 244, 124, 10}, - {105, 189, 139, 26, 4} - }, - { {241, 178, 125, 130, 9}, - {117, 9, 61, 210, 7}, - {148, 27, 228, 216, 15}, - {228, 187, 201, 10, 14} - }, - { {42, 202, 178, 62, 9}, - {14, 228, 187, 17, 13}, - {151, 196, 213, 53, 4}, - {184, 141, 210, 119, 0} - }, - { {123, 122, 214, 143, 9}, - {111, 237, 41, 213, 11}, - {159, 22, 181, 237, 14}, - {218, 185, 75, 127, 6} - }, - { {130, 225, 42, 209, 13}, - {5, 70, 213, 15, 6}, - {184, 181, 72, 116, 1}, - {111, 10, 182, 42, 0} - }, - { {37, 245, 188, 164, 11}, - {205, 151, 55, 112, 6}, - {210, 83, 218, 250, 4}, - {96, 238, 206, 155, 3} - }, - { {150, 173, 51, 250, 11}, - {149, 122, 147, 59, 15}, - {213, 252, 203, 86, 9}, - {253, 204, 149, 234, 9} - }, - { {116, 221, 218, 89, 11}, - {228, 254, 181, 188, 8}, - {217, 165, 187, 178, 14}, - {19, 218, 215, 242, 7} - }, - { {190, 51, 16, 82, 7}, - {199, 26, 232, 27, 1}, - {228, 160, 140, 199, 13}, - {141, 129, 117, 142, 3} - }, - { {175, 106, 142, 227, 12}, - {131, 197, 111, 79, 11}, - {60, 119, 21, 111, 5}, - {223, 47, 106, 60, 1} - }, - { {80, 157, 15, 85, 14}, - {88, 91, 213, 172, 8}, - {122, 175, 11, 144, 10}, - {19, 90, 189, 161, 10} - }, - { {90, 168, 135, 202, 13}, - {23, 233, 81, 137, 11}, - {181, 62, 17, 85, 10}, - {217, 24, 169, 126, 8} - }, - { {248, 216, 14, 113, 13}, - {70, 77, 247, 142, 8}, - {184, 231, 1, 177, 15}, - {23, 30, 251, 38, 2} - }, - { {18, 41, 212, 58, 0}, - {33, 171, 130, 17, 9}, - {5, 194, 185, 68, 8}, - {152, 132, 29, 88, 4} - }, - { {58, 213, 230, 98, 8}, - {98, 207, 51, 41, 5}, - {20, 102, 122, 181, 12}, - {169, 76, 207, 52, 6} - }, - { {68, 63, 156, 94, 2}, - {201, 179, 140, 184, 9}, - {71, 163, 159, 194, 2}, - {145, 211, 28, 217, 3} - }, - { {53, 224, 26, 247, 2}, - {137, 92, 182, 92, 3}, - {78, 245, 128, 122, 12}, - {195, 166, 211, 169, 1} - }, - { {82, 84, 77, 114, 6}, - {112, 29, 198, 169, 1}, - {100, 235, 34, 164, 10}, - {137, 86, 59, 128, 14} - }, - { {8, 222, 33, 65, 9}, - {86, 4, 25, 44, 12}, - {152, 40, 71, 177, 0}, - {51, 73, 130, 6, 10} - }, - { {151, 233, 242, 229, 5}, - {173, 206, 82, 95, 14}, - {170, 116, 249, 126, 9}, - {127, 164, 167, 59, 5} - }, - { {117, 185, 123, 135, 5}, - {253, 74, 116, 212, 15}, - {174, 29, 233, 218, 14}, - {242, 178, 229, 43, 15} - }, - { {28, 149, 63, 106, 13}, - {214, 107, 87, 56, 5}, - {181, 111, 202, 147, 8}, - {161, 206, 173, 102, 11} - }, - { {72, 136, 208, 174, 15}, - {46, 176, 83, 144, 11}, - {247, 80, 177, 17, 2}, - {208, 156, 160, 215, 4} - }, - { {35, 82, 133, 125, 8}, - {88, 165, 171, 77, 0}, - {27, 234, 20, 172, 4}, - {11, 45, 90, 81, 10} - }, - { {145, 10, 12, 159, 8}, - {8, 41, 141, 70, 11}, - {31, 147, 5, 8, 9}, - {214, 43, 25, 65, 0} - }, - { {114, 106, 253, 204, 4}, - {57, 173, 108, 153, 14}, - {35, 59, 245, 100, 14}, - {121, 147, 107, 89, 12} - }, - { {70, 182, 223, 184, 12}, - {241, 225, 223, 177, 2}, - {49, 223, 182, 214, 2}, - {72, 223, 184, 120, 15} - }, - { {55, 30, 152, 201, 12}, - {192, 168, 109, 125, 10}, - {57, 49, 151, 142, 12}, - {91, 235, 97, 80, 3} - }, - { {242, 133, 64, 180, 10}, - {40, 26, 179, 163, 2}, - {82, 208, 42, 20, 15}, - {76, 92, 213, 129, 4} - }, - { {155, 53, 4, 69, 10}, - {75, 27, 1, 111, 0}, - {90, 34, 10, 205, 9}, - {15, 104, 13, 141, 2} - }, - { {138, 236, 145, 248, 13}, - {23, 164, 211, 59, 10}, - {177, 248, 147, 117, 1}, - {93, 204, 178, 94, 8} - }, - { {173, 85, 173, 77, 15}, - {222, 183, 101, 110, 4}, - {251, 43, 90, 171, 5}, - {39, 106, 110, 215, 11} - }, - { {42, 24, 202, 208, 2}, - {98, 208, 164, 9, 10}, - {64, 181, 49, 133, 4}, - {89, 2, 80, 180, 6} - }, - { {237, 2, 60, 165, 10}, - {138, 17, 47, 214, 6}, - {90, 83, 196, 11, 7}, - {102, 191, 72, 133, 1} - }, - { {190, 137, 112, 155, 1}, - {166, 42, 176, 23, 15}, - {141, 144, 233, 23, 13}, - {254, 128, 213, 70, 5} - }, - { {66, 233, 185, 200, 9}, - {21, 166, 21, 153, 14}, - {145, 57, 217, 116, 2}, - {121, 154, 134, 90, 8} - }, - { {224, 185, 52, 114, 9}, - {69, 3, 179, 154, 13}, - {148, 226, 201, 208, 7}, - {181, 156, 220, 10, 2} - }, - { {135, 32, 220, 54, 11}, - {173, 145, 135, 83, 1}, - {214, 195, 176, 78, 1}, - {140, 174, 24, 155, 5} - }, - { {93, 132, 96, 137, 7}, - {166, 56, 80, 228, 6}, - {233, 16, 98, 27, 10}, - {98, 112, 161, 198, 5} - }, - { {144, 204, 205, 2, 15}, - {52, 157, 85, 34, 9}, - {244, 11, 51, 48, 9}, - {148, 74, 171, 146, 12} - }, - { {140, 134, 212, 168, 8}, - {162, 161, 27, 50, 2}, - {17, 82, 182, 19, 1}, - {68, 205, 136, 84, 5} - }, - { {120, 169, 76, 36, 12}, - {43, 11, 119, 128, 8}, - {50, 67, 41, 81, 14}, - {16, 30, 237, 13, 4} - }, - { {182, 101, 251, 203, 12}, - {177, 238, 101, 63, 7}, - {61, 61, 250, 102, 13}, - {239, 202, 103, 120, 13} - }, - { {39, 167, 32, 26, 6}, - {129, 50, 248, 97, 5}, - {101, 128, 78, 94, 4}, - {168, 97, 244, 200, 1} - }, - { {193, 202, 69, 13, 5}, - {60, 37, 88, 198, 8}, - {171, 10, 37, 56, 3}, - {22, 49, 170, 67, 12} - }, - { {75, 38, 242, 6, 13}, - {47, 192, 73, 241, 5}, - {182, 4, 246, 77, 2}, - {168, 249, 32, 63, 4} - }, - { {146, 37, 46, 39, 11}, - {13, 91, 7, 39, 5}, - {222, 71, 74, 68, 9}, - {174, 78, 13, 171, 0} - }, - { {240, 175, 98, 24, 8}, - {33, 106, 185, 162, 12}, - {17, 132, 111, 80, 15}, - {52, 89, 213, 104, 4} - }, - { {50, 161, 124, 11, 6}, - {33, 59, 116, 21, 5}, - {109, 3, 232, 84, 12}, - {170, 130, 237, 200, 4} - }, - { {107, 9, 58, 162, 14}, - {2, 82, 103, 209, 15}, - {116, 85, 201, 13, 6}, - {248, 190, 100, 164, 0} - }, - { {134, 231, 189, 205, 5}, - {157, 167, 92, 63, 6}, - {171, 59, 222, 118, 1}, - {111, 195, 174, 91, 9} - }, - { {70, 76, 128, 181, 11}, - {140, 148, 131, 165, 10}, - {218, 208, 19, 38, 2}, - {90, 92, 18, 147, 1} - }, - { {110, 146, 188, 233, 7}, - {198, 177, 126, 157, 6}, - {233, 115, 212, 151, 6}, - {107, 151, 232, 214, 3} - }, - { {10, 141, 210, 173, 4}, - {42, 226, 82, 53, 10}, - {43, 84, 187, 21, 0}, - {90, 196, 164, 117, 4} - }, - { {133, 26, 254, 234, 9}, - {228, 225, 15, 90, 15}, - {149, 119, 245, 138, 1}, - {245, 175, 8, 114, 7} - }, - { {102, 122, 112, 73, 9}, - {229, 36, 41, 157, 12}, - {153, 32, 229, 230, 6}, - {59, 153, 66, 74, 7} - }, - { {113, 49, 97, 102, 13}, - {125, 10, 99, 200, 5}, - {182, 104, 104, 200, 14}, - {161, 60, 101, 11, 14} - }, - { {197, 119, 160, 82, 15}, - {197, 150, 201, 234, 5}, - {244, 160, 94, 234, 3}, - {165, 121, 54, 154, 3} - }, - { {224, 70, 76, 187, 12}, - {32, 37, 239, 166, 3}, - {61, 211, 38, 32, 7}, - {198, 95, 122, 64, 4} - }, - { {244, 9, 85, 141, 10}, - {184, 59, 33, 150, 10}, - {91, 26, 169, 2, 15}, - {86, 152, 77, 193, 13} - }, - { {34, 79, 148, 169, 9}, - {4, 167, 43, 53, 10}, - {153, 82, 159, 36, 4}, - {90, 205, 78, 82, 0} - }, - { {229, 88, 200, 244, 13}, - {236, 132, 231, 202, 10}, - {178, 241, 49, 170, 7}, - {85, 62, 114, 19, 7} - }, - { {243, 18, 209, 192, 11}, - {116, 152, 41, 219, 2}, - {208, 56, 180, 140, 15}, - {77, 185, 65, 146, 14} - }, - { {41, 114, 88, 124, 11}, - {111, 52, 175, 88, 0}, - {211, 225, 164, 233, 4}, - {1, 175, 82, 207, 6} - }, - { {65, 179, 38, 98, 4}, - {65, 67, 90, 200, 5}, - {36, 102, 76, 216, 2}, - {161, 53, 172, 40, 2} - }, - { {218, 87, 244, 214, 12}, - {106, 143, 201, 187, 7}, - {54, 178, 254, 165, 11}, - {237, 217, 63, 21, 6} - }, - { {186, 1, 109, 186, 9}, - {54, 43, 167, 3, 7}, - {149, 219, 104, 5, 13}, - {236, 14, 93, 70, 12} - }, - { {53, 137, 198, 176, 5}, - {164, 203, 242, 64, 10}, - {160, 214, 57, 26, 12}, - {80, 36, 253, 50, 5} - }, - { {223, 69, 8, 189, 5}, - {142, 46, 198, 231, 2}, - {171, 209, 10, 47, 11}, - {78, 118, 55, 71, 1} - }, - { {50, 82, 34, 250, 8}, - {64, 108, 171, 9, 7}, - {21, 244, 68, 164, 12}, - {233, 13, 83, 96, 2} - }, - { {87, 54, 121, 125, 4}, - {249, 40, 206, 253, 4}, - {43, 233, 230, 206, 10}, - {43, 247, 49, 73, 15} - }, - { {100, 77, 74, 190, 10}, - {168, 118, 167, 160, 11}, - {87, 213, 43, 34, 6}, - {208, 94, 86, 225, 5} - }, - { {13, 189, 14, 79, 10}, - {203, 115, 21, 108, 9}, - {95, 39, 11, 219, 0}, - {147, 106, 140, 237, 3} - }, - { {20, 18, 155, 246, 13}, - {220, 200, 207, 24, 3}, - {182, 253, 148, 130, 8}, - {193, 143, 49, 51, 11} - }, - { {160, 137, 141, 158, 1}, - {28, 163, 180, 2, 11}, - {135, 155, 25, 16, 5}, - {212, 2, 220, 83, 8} - }, - { {116, 80, 182, 226, 11}, - {196, 221, 35, 152, 7}, - {212, 118, 208, 162, 14}, - {225, 156, 75, 178, 3} - }, - { {224, 228, 28, 54, 7}, - {13, 21, 246, 178, 1}, - {230, 195, 130, 112, 7}, - {132, 214, 250, 139, 0} - }, - { {105, 149, 101, 253, 11}, - {126, 51, 179, 236, 6}, - {219, 250, 106, 153, 6}, - {99, 124, 220, 199, 14} - }, - { {103, 160, 233, 22, 8}, - {185, 128, 181, 193, 5}, - {22, 137, 112, 94, 6}, - {168, 58, 208, 25, 13} - }, - { {25, 252, 180, 77, 12}, - {75, 173, 81, 124, 12}, - {59, 34, 211, 249, 8}, - {51, 232, 171, 93, 2} - }, - { {240, 254, 74, 108, 6}, - {105, 124, 126, 170, 8}, - {99, 101, 39, 240, 15}, - {21, 87, 227, 233, 6} - }, - { {78, 229, 220, 11, 14}, - {163, 183, 85, 181, 1}, - {125, 3, 186, 119, 2}, - {138, 218, 174, 220, 5} - }, - { {178, 90, 177, 231, 7}, - {92, 156, 106, 31, 15}, - {238, 120, 213, 164, 13}, - {255, 133, 99, 147, 10} - }, - { {84, 197, 166, 143, 13}, - {140, 239, 81, 164, 7}, - {191, 22, 90, 50, 10}, - {226, 88, 175, 115, 1} - }, - { {247, 21, 47, 212, 3}, - {220, 91, 164, 235, 6}, - {194, 191, 74, 142, 15}, - {109, 114, 93, 163, 11} - }, - { {79, 145, 125, 232, 10}, - {242, 51, 23, 217, 6}, - {81, 123, 232, 159, 2}, - {105, 190, 140, 196, 15} - }, - { {9, 53, 24, 238, 6}, - {75, 50, 70, 120, 3}, - {103, 113, 138, 201, 0}, - {193, 230, 36, 205, 2} - }, - { {170, 77, 229, 141, 6}, - {58, 183, 96, 39, 14}, - {107, 26, 123, 37, 5}, - {126, 64, 110, 213, 12} - }, - { {251, 189, 163, 100, 14}, - {91, 218, 115, 235, 12}, - {114, 108, 91, 221, 15}, - {61, 124, 229, 189, 10} - }, - { {194, 140, 79, 157, 13}, - {60, 97, 213, 167, 10}, - {187, 159, 35, 20, 3}, - {94, 90, 184, 99, 12} - }, - { {221, 221, 90, 236, 8}, - {234, 110, 23, 250, 10}, - {19, 117, 171, 187, 11}, - {85, 254, 135, 101, 7} - }, - { {154, 123, 45, 105, 4}, - {83, 47, 78, 15, 12}, - {41, 107, 77, 229, 9}, - {63, 7, 47, 76, 10} - }, - { {218, 46, 220, 145, 1}, - {39, 137, 140, 183, 10}, - {136, 147, 183, 69, 11}, - {94, 211, 25, 30, 4} - }, - { {18, 21, 66, 25, 5}, - {100, 106, 192, 37, 0}, - {169, 132, 42, 132, 8}, - {10, 64, 53, 98, 6} - }, - { {58, 253, 126, 65, 13}, - {103, 79, 117, 61, 12}, - {184, 39, 235, 245, 12}, - {59, 202, 239, 46, 6} - }, - { {210, 145, 48, 33, 12}, - {64, 10, 83, 151, 4}, - {56, 64, 200, 148, 11}, - {46, 156, 165, 0, 2} - }, - { {136, 91, 123, 237, 6}, - {122, 118, 78, 30, 14}, - {107, 125, 237, 161, 1}, - {119, 135, 38, 229, 14} - }, - { {255, 26, 148, 3, 13}, - {198, 137, 105, 215, 9}, - {188, 2, 149, 143, 15}, - {158, 185, 105, 22, 3} - }, - { {138, 210, 5, 218, 10}, - {82, 53, 153, 11, 3}, - {85, 186, 4, 181, 1}, - {205, 9, 154, 196, 10} - }, - { {124, 254, 229, 86, 14}, - {251, 157, 249, 168, 13}, - {118, 170, 119, 243, 14}, - {177, 89, 251, 157, 15} - }, - { {15, 29, 188, 45, 9}, - {206, 163, 7, 117, 12}, - {155, 67, 219, 143, 0}, - {58, 238, 12, 87, 3} - }, - { {173, 125, 51, 119, 9}, - {223, 70, 163, 126, 13}, - {158, 236, 203, 235, 5}, - {183, 236, 86, 47, 11} - }, - { {94, 35, 218, 148, 5}, - {175, 202, 204, 145, 2}, - {162, 149, 188, 71, 10}, - {72, 147, 53, 63, 5} - }, - { {202, 135, 100, 114, 13}, - {38, 3, 219, 171, 5}, - {180, 226, 110, 21, 3}, - {173, 93, 188, 6, 4} - }, - { {42, 52, 86, 226, 7}, - {103, 81, 98, 57, 3}, - {228, 118, 162, 197, 4}, - {201, 196, 104, 174, 6} - }, - { {179, 128, 104, 125, 6}, - {40, 56, 246, 79, 4}, - {107, 225, 96, 28, 13}, - {47, 38, 241, 193, 4} - }, - { {73, 224, 165, 31, 6}, - {27, 181, 208, 196, 5}, - {111, 138, 80, 121, 2}, - {162, 48, 186, 221, 8} - }, - { {144, 226, 83, 62, 9}, - {61, 108, 155, 18, 1}, - {151, 204, 164, 112, 9}, - {132, 141, 147, 107, 12} - }, - { {221, 1, 6, 99, 13}, - {134, 75, 67, 206, 1}, - {188, 102, 8, 11, 11}, - {135, 60, 45, 38, 1} - }, - { {118, 160, 92, 144, 8}, - {161, 9, 181, 145, 2}, - {16, 147, 160, 86, 14}, - {72, 154, 217, 8, 5} - }, - { {14, 217, 3, 223, 6}, - {218, 118, 208, 13, 11}, - {111, 188, 9, 183, 0}, - {219, 0, 182, 229, 11} - }, - { {43, 112, 183, 113, 13}, - {87, 197, 227, 93, 4}, - {184, 238, 208, 237, 4}, - {43, 172, 122, 62, 10} - }, - { {78, 140, 226, 119, 3}, - {174, 208, 146, 173, 13}, - {206, 228, 115, 23, 2}, - {187, 84, 144, 183, 5} - }, - { {164, 109, 4, 140, 4}, - {137, 39, 96, 34, 10}, - {35, 18, 11, 98, 5}, - {84, 64, 110, 73, 1} - }, - { {174, 72, 136, 91, 10}, - {130, 180, 165, 15, 9}, - {93, 161, 17, 39, 5}, - {159, 10, 82, 212, 1} - }, - { {10, 163, 70, 143, 3}, - {47, 115, 24, 5, 3}, - {207, 22, 44, 85, 0}, - {202, 1, 140, 239, 4} - }, - { {106, 225, 111, 19, 7}, - {55, 87, 244, 133, 5}, - {236, 143, 104, 117, 6}, - {170, 18, 254, 174, 12} - }, - { {149, 35, 13, 186, 13}, - {149, 43, 207, 66, 3}, - {181, 219, 12, 74, 9}, - {196, 47, 61, 74, 9} - }, - { {228, 136, 181, 77, 4}, - {152, 161, 112, 158, 12}, - {43, 42, 209, 18, 7}, - {55, 144, 232, 81, 9} - }, - { {121, 10, 75, 108, 10}, - {58, 120, 47, 200, 8}, - {83, 109, 37, 9, 14}, - {17, 63, 65, 229, 12} - }, - { {64, 35, 127, 147, 9}, - {53, 67, 141, 148, 7}, - {156, 159, 236, 64, 2}, - {226, 155, 28, 42, 12} - }, - { {45, 191, 41, 200, 3}, - {215, 50, 60, 104, 14}, - {193, 57, 79, 219, 4}, - {113, 99, 196, 206, 11} - }, - { {202, 214, 76, 54, 0}, - {106, 5, 158, 163, 1}, - {6, 195, 38, 181, 3}, - {140, 87, 154, 5, 6} - }, - { {211, 175, 89, 151, 10}, - {57, 26, 157, 247, 11}, - {94, 153, 175, 92, 11}, - {222, 251, 149, 137, 12} - }, - { {157, 46, 167, 143, 5}, - {159, 233, 72, 102, 15}, - {175, 30, 87, 75, 9}, - {246, 97, 41, 127, 9} - }, - { {34, 211, 98, 6, 7}, - {108, 86, 120, 1, 5}, - {230, 4, 108, 180, 4}, - {168, 1, 230, 163, 6} - }, - { {204, 169, 43, 134, 6}, - {155, 82, 84, 130, 15}, - {102, 29, 73, 83, 3}, - {244, 18, 164, 173, 9} - }, - { {85, 43, 159, 167, 1}, - {157, 203, 14, 212, 11}, - {142, 95, 157, 74, 10}, - {210, 183, 13, 59, 9} - }, - { {203, 178, 173, 205, 11}, - {95, 177, 29, 207, 6}, - {219, 59, 84, 221, 3}, - {111, 59, 136, 223, 10} - }, - { {68, 250, 70, 198, 7}, - {237, 85, 88, 136, 11}, - {230, 54, 37, 242, 2}, - {209, 17, 170, 171, 7} - }, - { {231, 38, 143, 185, 7}, - {149, 241, 238, 231, 2}, - {233, 223, 22, 78, 7}, - {78, 119, 120, 250, 9} - }, - { {36, 175, 23, 170, 2}, - {145, 115, 58, 48, 11}, - {69, 94, 143, 82, 4}, - {208, 197, 204, 232, 9} - }, - { {67, 232, 194, 173, 10}, - {41, 244, 19, 197, 10}, - {91, 84, 49, 124, 2}, - {90, 60, 130, 249, 4} - }, - { {145, 205, 26, 71, 13}, - {12, 78, 85, 126, 9}, - {190, 37, 139, 56, 9}, - {151, 234, 167, 35, 0} - }, - { {104, 5, 2, 194, 10}, - {2, 82, 33, 168, 3}, - {84, 52, 10, 1, 6}, - {193, 88, 68, 164, 0} - }, - { {5, 213, 122, 29, 10}, - {232, 118, 149, 116, 4}, - {91, 133, 234, 186, 0}, - {34, 234, 150, 225, 7} - }, - { {88, 51, 178, 20, 8}, - {75, 202, 137, 144, 4}, - {18, 132, 220, 193, 10}, - {32, 153, 21, 61, 2} - }, - { {207, 23, 24, 178, 4}, - {194, 2, 206, 243, 3}, - {36, 209, 142, 143, 3}, - {204, 247, 52, 4, 3} - }, - { {86, 20, 124, 139, 2}, - {224, 57, 4, 181, 7}, - {77, 19, 226, 134, 10}, - {234, 210, 9, 192, 7} - }, - { {254, 81, 189, 45, 1}, - {222, 175, 38, 151, 4}, - {139, 75, 216, 167, 15}, - {46, 150, 79, 87, 11} - }, - { {48, 238, 165, 168, 4}, - {17, 173, 122, 32, 14}, - {33, 90, 87, 112, 12}, - {112, 69, 235, 88, 8} - }, - { {94, 27, 78, 166, 2}, - {234, 91, 14, 129, 11}, - {70, 87, 45, 135, 10}, - {216, 23, 13, 165, 7} - }, - { {37, 57, 142, 52, 3}, - {205, 211, 166, 64, 8}, - {194, 199, 25, 202, 4}, - {16, 38, 92, 187, 3} - }, - { {71, 238, 53, 187, 2}, - {145, 53, 154, 245, 15}, - {77, 218, 199, 126, 2}, - {250, 245, 154, 200, 9} - }, - { {105, 226, 147, 152, 11}, - {23, 244, 185, 208, 2}, - {209, 156, 148, 121, 6}, - {64, 185, 210, 254, 8} - }, - { {203, 235, 3, 139, 4}, - {19, 102, 88, 199, 11}, - {45, 28, 13, 125, 3}, - {222, 49, 166, 108, 8} - }, - { {151, 33, 193, 117, 9}, - {189, 138, 131, 79, 0}, - {154, 232, 56, 78, 9}, - {15, 44, 21, 27, 13} - }, - { {117, 81, 72, 151, 9}, - {236, 14, 165, 196, 3}, - {158, 145, 40, 170, 14}, - {194, 58, 87, 3, 7} - }, - { {87, 243, 99, 229, 3}, - {253, 94, 26, 205, 6}, - {202, 124, 108, 254, 10}, - {107, 53, 135, 171, 15} - }, - { {174, 100, 20, 104, 0}, - {131, 37, 34, 59, 0}, - {1, 98, 130, 103, 5}, - {13, 196, 74, 76, 1} - }, - { {126, 147, 161, 155, 5}, - {214, 170, 248, 133, 7}, - {173, 152, 92, 151, 14}, - {234, 17, 245, 86, 11} - }, - { {169, 131, 209, 245, 2}, - {58, 146, 186, 94, 2}, - {74, 248, 188, 25, 5}, - {71, 165, 212, 149, 12} - }, - { {100, 91, 1, 175, 9}, - {220, 38, 43, 132, 11}, - {159, 88, 13, 162, 6}, - {210, 29, 70, 67, 11} - }, - { {13, 167, 121, 86, 5}, - {191, 2, 220, 120, 5}, - {166, 169, 238, 91, 0}, - {161, 227, 180, 15, 13} - }, - { {114, 16, 159, 147, 13}, - {84, 201, 229, 149, 3}, - {188, 159, 144, 132, 14}, - {202, 154, 121, 50, 10} - }, - { {141, 12, 71, 198, 11}, - {190, 81, 1, 106, 11}, - {214, 62, 35, 11, 1}, - {213, 104, 8, 167, 13} - }, - { {109, 229, 76, 66, 3}, - {167, 23, 52, 232, 1}, - {196, 35, 42, 123, 6}, - {129, 114, 206, 142, 5} - }, - { {241, 35, 191, 102, 11}, - {29, 219, 47, 218, 5}, - {214, 111, 220, 72, 15}, - {165, 191, 77, 187, 8} - }, - { {180, 231, 234, 99, 7}, - {165, 222, 126, 46, 5}, - {236, 101, 126, 114, 13}, - {167, 71, 231, 186, 5} - }, - { {229, 224, 226, 108, 5}, - {173, 228, 114, 202, 4}, - {163, 100, 112, 122, 7}, - {37, 52, 226, 123, 5} - }, - { {90, 49, 4, 158, 15}, - {79, 59, 193, 129, 3}, - {247, 146, 8, 197, 10}, - {200, 24, 61, 207, 2} - }, - { {137, 248, 81, 23, 11}, - {127, 20, 145, 86, 9}, - {222, 136, 161, 249, 1}, - {150, 168, 146, 143, 14} - }, - { {111, 187, 159, 46, 10}, - {219, 243, 63, 209, 9}, - {87, 79, 157, 223, 6}, - {152, 191, 204, 253, 11} - }, - { {80, 175, 172, 250, 6}, - {1, 187, 222, 168, 15}, - {101, 243, 95, 80, 10}, - {241, 87, 189, 216, 0} - }, - { {181, 113, 7, 57, 6}, - {209, 127, 226, 70, 0}, - {105, 206, 8, 234, 13}, - {6, 36, 127, 232, 11} - }, - { {121, 119, 47, 9, 2}, - {83, 127, 44, 228, 4}, - {73, 15, 78, 233, 14}, - {34, 115, 79, 236, 10} - }, - { {177, 29, 65, 73, 3}, - {116, 58, 32, 110, 8}, - {201, 40, 43, 136, 13}, - {23, 96, 69, 194, 14} - }, - { {29, 227, 35, 74, 14}, - {147, 126, 89, 72, 5}, - {117, 44, 76, 123, 8}, - {161, 41, 167, 236, 9} - }, - { {179, 148, 34, 108, 13}, - {76, 104, 115, 107, 4}, - {179, 100, 66, 156, 13}, - {45, 108, 225, 99, 2} - }, - { {44, 126, 92, 173, 1}, - {239, 37, 46, 52, 10}, - {139, 83, 167, 227, 4}, - {82, 199, 74, 79, 7} - }, - { {95, 115, 48, 151, 13}, - {207, 14, 201, 213, 7}, - {190, 144, 204, 239, 10}, - {234, 185, 55, 15, 3} - }, - { {69, 251, 224, 160, 13}, - {229, 134, 91, 192, 14}, - {176, 80, 125, 250, 2}, - {112, 61, 166, 26, 7} - }, - { {9, 122, 74, 176, 0}, - {99, 68, 142, 64, 10}, - {0, 213, 37, 233, 0}, - {80, 39, 18, 44, 6} - }, - { {179, 119, 146, 163, 10}, - {65, 222, 43, 119, 3}, - {92, 84, 158, 236, 13}, - {206, 237, 71, 184, 2} - }, - { {98, 35, 175, 220, 7}, - {29, 243, 236, 137, 6}, - {227, 191, 92, 68, 6}, - {105, 19, 124, 251, 8} - }, - { {108, 155, 99, 108, 9}, - {254, 98, 59, 136, 12}, - {147, 108, 109, 147, 6}, - {49, 29, 196, 103, 15} - }, - { {155, 78, 142, 46, 9}, - {14, 237, 15, 99, 9}, - {151, 71, 23, 45, 9}, - {156, 111, 11, 119, 0} - }, - { {249, 195, 194, 21, 3}, - {46, 222, 184, 198, 0}, - {202, 132, 60, 57, 15}, - {6, 49, 215, 183, 4} - }, - { {119, 43, 15, 234, 11}, - {149, 123, 47, 201, 11}, - {213, 127, 13, 78, 14}, - {217, 63, 77, 234, 9} - }, - { {102, 250, 154, 171, 4}, - {193, 228, 126, 149, 11}, - {45, 85, 149, 246, 6}, - {218, 151, 226, 120, 3} - }, - { {116, 98, 77, 223, 13}, - {189, 45, 237, 140, 3}, - {191, 187, 36, 98, 14}, - {195, 27, 123, 75, 13} - }, - { {25, 222, 78, 102, 4}, - {106, 77, 94, 104, 9}, - {38, 103, 39, 185, 8}, - {145, 103, 171, 37, 6} - }, - { {148, 41, 230, 53, 14}, - {169, 219, 195, 6, 12}, - {122, 198, 121, 66, 9}, - {54, 12, 61, 185, 5} - }, - { {76, 236, 163, 246, 4}, - {155, 196, 210, 168, 15}, - {38, 252, 83, 115, 2}, - {241, 84, 178, 61, 9} - }, - { {164, 180, 6, 106, 6}, - {193, 113, 114, 42, 1}, - {101, 102, 2, 210, 5}, - {133, 68, 232, 232, 3} - }, - { {30, 86, 88, 215, 4}, - {234, 12, 204, 61, 3}, - {46, 177, 166, 167, 8}, - {203, 195, 51, 5, 7} - }, - { {219, 73, 192, 161, 2}, - {34, 158, 2, 199, 10}, - {72, 80, 57, 45, 11}, - {94, 52, 7, 148, 4} - }, - { {79, 81, 144, 218, 7}, - {198, 182, 192, 217, 3}, - {229, 176, 152, 175, 2}, - {201, 176, 54, 214, 3} - }, - { {1, 67, 44, 173, 5}, - {12, 39, 78, 68, 6}, - {171, 83, 76, 40, 0}, - {98, 39, 46, 67, 0} - }, - { {82, 252, 40, 115, 9}, - {69, 12, 151, 173, 13}, - {156, 225, 67, 244, 10}, - {187, 94, 147, 10, 2} - }, - { {67, 147, 62, 210, 15}, - {68, 83, 221, 217, 7}, - {244, 183, 204, 156, 2}, - {233, 187, 188, 162, 2} - }, - { {107, 7, 29, 172, 0}, - {26, 35, 46, 241, 2}, - {3, 91, 142, 13, 6}, - {72, 247, 76, 69, 8} - }, - { {81, 13, 171, 142, 1}, - {28, 234, 4, 224, 15}, - {135, 29, 91, 8, 10}, - {240, 114, 5, 115, 8} - }, - { {80, 201, 26, 157, 10}, - {8, 126, 149, 148, 10}, - {91, 149, 137, 48, 10}, - {82, 154, 151, 225, 0} - }, - { {95, 229, 148, 45, 0}, - {139, 175, 18, 245, 0}, - {11, 66, 154, 127, 10}, - {10, 244, 143, 93, 1} - }, - { {88, 194, 177, 178, 10}, - {18, 156, 155, 144, 7}, - {84, 216, 212, 49, 10}, - {224, 157, 147, 148, 8} - }, - { {137, 65, 77, 139, 14}, - {50, 55, 69, 70, 3}, - {125, 27, 40, 41, 1}, - {198, 42, 46, 196, 12} - }, - { {132, 10, 38, 120, 5}, - {132, 97, 202, 10, 12}, - {161, 230, 69, 2, 1}, - {53, 5, 56, 98, 1} - }, - { {185, 96, 91, 1, 13}, - {55, 76, 101, 86, 0}, - {184, 13, 160, 105, 13}, - {6, 170, 99, 46, 12} - }, - { {20, 186, 220, 247, 7}, - {237, 153, 222, 28, 11}, - {238, 243, 181, 210, 8}, - {211, 135, 185, 155, 7} - }, - { {246, 219, 203, 198, 4}, - {248, 206, 124, 139, 11}, - {38, 61, 61, 182, 15}, - {221, 19, 231, 49, 15} - }, - { {192, 171, 73, 169, 0}, - {49, 34, 30, 134, 10}, - {9, 89, 45, 80, 3}, - {86, 23, 132, 72, 12} - }, - { {153, 38, 27, 157, 2}, - {27, 120, 140, 118, 2}, - {75, 157, 134, 73, 9}, - {70, 227, 17, 237, 8} - }, - { {97, 25, 251, 19, 6}, - {112, 210, 228, 212, 13}, - {108, 141, 249, 136, 6}, - {178, 178, 116, 176, 14} - }, - { {136, 247, 2, 236, 5}, - {79, 102, 90, 42, 2}, - {163, 116, 14, 241, 1}, - {69, 69, 166, 111, 2} - }, - { {59, 109, 198, 146, 3}, - {39, 223, 160, 97, 11}, - {196, 150, 59, 109, 12}, - {216, 96, 95, 190, 4} - }, - { {78, 98, 149, 182, 0}, - {155, 133, 138, 145, 3}, - {6, 218, 148, 103, 2}, - {200, 149, 26, 29, 9} - }, - { {217, 253, 38, 220, 10}, - {75, 127, 145, 234, 14}, - {83, 182, 75, 249, 11}, - {117, 120, 159, 237, 2} - }, - { {185, 170, 215, 180, 13}, - {63, 201, 251, 82, 10}, - {178, 222, 181, 89, 13}, - {84, 173, 249, 63, 12} - }, - { {77, 237, 86, 136, 15}, - {167, 119, 81, 240, 10}, - {241, 22, 171, 123, 2}, - {80, 248, 174, 238, 5} - }, - { {32, 85, 135, 53, 1}, - {92, 199, 162, 36, 0}, - {138, 206, 26, 160, 4}, - {2, 68, 94, 51, 10} - }, - { {229, 194, 49, 87, 1}, - {156, 4, 184, 222, 5}, - {142, 168, 196, 58, 7}, - {167, 177, 210, 3, 9} - }, - { {203, 185, 136, 37, 6}, - {75, 146, 86, 199, 8}, - {106, 65, 25, 221, 3}, - {30, 54, 164, 157, 2} - }, - { {43, 23, 42, 185, 9}, - {70, 98, 175, 101, 6}, - {153, 213, 78, 141, 4}, - {106, 111, 84, 102, 2} - }, - { {171, 204, 25, 105, 3}, - {22, 52, 54, 127, 8}, - {201, 105, 131, 61, 5}, - {31, 230, 194, 198, 8} - }, - { {136, 158, 50, 170, 15}, - {70, 112, 91, 50, 15}, - {245, 84, 199, 145, 1}, - {244, 205, 160, 230, 2} - }, - { {13, 103, 151, 181, 12}, - {155, 199, 203, 116, 2}, - {58, 222, 158, 107, 0}, - {66, 237, 62, 61, 9} - }, - { {110, 236, 61, 135, 6}, - {155, 21, 116, 181, 15}, - {110, 27, 195, 119, 6}, - {250, 210, 234, 141, 9} - }, - { {163, 18, 150, 137, 14}, - {64, 241, 105, 87, 2}, - {121, 22, 148, 140, 5}, - {78, 169, 104, 240, 2} - }, - { {49, 39, 126, 125, 11}, - {45, 123, 175, 124, 4}, - {219, 231, 238, 72, 12}, - {35, 239, 93, 235, 4} - }, - { {197, 59, 33, 41, 14}, - {209, 50, 75, 198, 12}, - {121, 72, 77, 202, 3}, - {54, 61, 36, 200, 11} - }, - { {14, 92, 116, 153, 15}, - {230, 53, 193, 53, 14}, - {249, 146, 227, 167, 0}, - {122, 200, 58, 198, 7} - }, - { {206, 216, 31, 33, 1}, - {214, 69, 22, 151, 8}, - {136, 79, 129, 183, 3}, - {30, 150, 138, 38, 11} - }, - { {17, 190, 238, 22, 3}, - {109, 217, 156, 96, 13}, - {198, 135, 119, 216, 8}, - {176, 99, 153, 187, 6} - }, - { {91, 246, 119, 192, 6}, - {115, 93, 88, 249, 6}, - {96, 62, 230, 253, 10}, - {105, 241, 171, 172, 14} - }, - { {205, 49, 157, 20, 14}, - {219, 147, 197, 210, 0}, - {114, 139, 152, 203, 3}, - {4, 186, 60, 157, 11} - }, - { {33, 104, 14, 72, 13}, - {5, 101, 101, 72, 8}, - {177, 39, 1, 104, 4}, - {17, 42, 106, 106, 0} - }, - { {40, 39, 132, 250, 11}, - {7, 179, 171, 40, 3}, - {213, 242, 30, 65, 4}, - {193, 77, 92, 222, 0} - }, - { {168, 244, 166, 26, 0}, - {67, 229, 176, 34, 5}, - {5, 134, 82, 241, 5}, - {164, 64, 218, 124, 2} - }, - { {49, 118, 81, 59, 4}, - {113, 44, 234, 116, 1}, - {45, 200, 166, 232, 12}, - {130, 229, 115, 72, 14} - }, - { {247, 105, 206, 110, 2}, - {169, 255, 38, 203, 9}, - {71, 103, 57, 110, 15}, - {157, 54, 79, 249, 5} - }, - { {83, 146, 163, 177, 13}, - {84, 200, 219, 197, 6}, - {184, 220, 84, 156, 10}, - {106, 61, 177, 50, 10} - }, - { {88, 101, 173, 108, 15}, - {31, 191, 71, 168, 4}, - {243, 107, 90, 97, 10}, - {33, 94, 47, 223, 8} - }, - { {190, 115, 121, 11, 0}, - {243, 46, 44, 23, 5}, - {13, 9, 236, 231, 13}, - {174, 131, 71, 76, 15} - }, - { {90, 232, 238, 91, 11}, - {39, 253, 149, 141, 13}, - {221, 167, 113, 117, 10}, - {187, 26, 155, 254, 4} - }, - { {139, 106, 57, 122, 0}, - {19, 36, 142, 91, 13}, - {5, 233, 197, 109, 1}, - {189, 167, 18, 76, 8} - }, - { {163, 233, 94, 124, 14}, - {41, 119, 247, 91, 8}, - {115, 231, 169, 124, 5}, - {29, 174, 254, 233, 4} - }, - { {91, 159, 159, 132, 13}, - {94, 203, 93, 241, 10}, - {178, 31, 159, 157, 10}, - {88, 251, 173, 55, 10} - }, - { {47, 75, 147, 199, 11}, - {158, 214, 41, 93, 11}, - {222, 60, 157, 47, 4}, - {219, 169, 70, 183, 9} - }, - { {76, 117, 32, 169, 6}, - {195, 54, 66, 164, 6}, - {105, 80, 74, 227, 2}, - {98, 84, 38, 204, 3} - }, - { {98, 163, 2, 254, 14}, - {9, 114, 251, 137, 3}, - {119, 244, 12, 84, 6}, - {201, 29, 244, 233, 0} - }, - { {103, 190, 132, 139, 7}, - {197, 177, 120, 229, 11}, - {237, 18, 23, 222, 6}, - {218, 113, 232, 218, 3} - }, - { {183, 81, 31, 133, 12}, - {216, 79, 101, 87, 2}, - {58, 31, 136, 174, 13}, - {78, 170, 111, 33, 11} - }, - { {24, 89, 92, 70, 7}, - {110, 31, 68, 24, 9}, - {230, 35, 169, 161, 8}, - {145, 130, 47, 135, 6} - }, - { {21, 230, 55, 117, 0}, - {153, 77, 154, 124, 4}, - {10, 238, 198, 122, 8}, - {35, 229, 155, 41, 9} - }, - { {152, 85, 193, 87, 13}, - {126, 142, 193, 46, 1}, - {190, 168, 58, 161, 9}, - {135, 72, 55, 23, 14} - }, - { {221, 31, 87, 116, 9}, - {254, 75, 139, 250, 8}, - {146, 238, 175, 139, 11}, - {21, 253, 29, 39, 15} - }, - { {116, 139, 16, 186, 5}, - {132, 42, 250, 144, 11}, - {165, 208, 141, 18, 14}, - {208, 149, 245, 66, 1} - }, - { {28, 253, 149, 152, 1}, - {215, 175, 144, 48, 10}, - {129, 154, 155, 243, 8}, - {80, 192, 159, 94, 11} - }, - { {99, 111, 43, 206, 2}, - {25, 118, 44, 233, 15}, - {71, 61, 79, 108, 6}, - {249, 115, 70, 233, 8} - }, - { {175, 140, 36, 152, 13}, - {134, 33, 241, 99, 14}, - {177, 146, 67, 31, 5}, - {124, 104, 248, 70, 1} - }, - { {33, 45, 73, 54, 5}, - {61, 2, 230, 96, 9}, - {166, 201, 43, 72, 4}, - {144, 102, 116, 11, 12} - }, - { {173, 161, 22, 32, 14}, - {131, 83, 115, 82, 0}, - {112, 70, 136, 91, 5}, - {4, 172, 236, 172, 1} - }, - { {239, 49, 13, 89, 13}, - {215, 35, 229, 207, 0}, - {185, 171, 8, 207, 7}, - {15, 58, 124, 78, 11} - }, - { {155, 50, 155, 65, 14}, - {83, 216, 77, 95, 0}, - {120, 45, 148, 205, 9}, - {15, 171, 33, 188, 10} - }, - { {95, 76, 1, 168, 15}, - {150, 60, 67, 225, 10}, - {241, 88, 3, 47, 10}, - {88, 124, 35, 198, 9} - }, - { {110, 151, 175, 238, 0}, - {218, 227, 62, 169, 7}, - {7, 127, 94, 151, 6}, - {233, 87, 204, 117, 11} - }, - { {129, 68, 91, 180, 8}, - {56, 68, 135, 114, 2}, - {18, 221, 162, 40, 1}, - {68, 238, 18, 33, 12} - }, - { {196, 142, 130, 78, 7}, - {140, 240, 88, 170, 9}, - {231, 36, 23, 18, 3}, - {149, 81, 160, 243, 1} - }, - { {225, 39, 182, 160, 12}, - {1, 195, 107, 242, 6}, - {48, 86, 222, 72, 7}, - {100, 253, 108, 56, 0} - }, - { {88, 201, 151, 188, 7}, - {30, 255, 210, 144, 10}, - {227, 222, 153, 49, 10}, - {80, 148, 191, 247, 8} - }, - { {37, 121, 224, 205, 7}, - {237, 182, 96, 76, 14}, - {235, 48, 121, 234, 4}, - {115, 32, 102, 219, 7} - }, - { {107, 93, 4, 34, 7}, - {70, 23, 98, 225, 9}, - {228, 66, 11, 173, 6}, - {152, 116, 110, 134, 2} - }, - { {109, 39, 67, 156, 2}, - {187, 114, 168, 224, 2}, - {67, 156, 46, 75, 6}, - {64, 113, 84, 237, 13} - }, - { {168, 101, 37, 190, 2}, - {27, 55, 162, 34, 7}, - {71, 218, 74, 97, 5}, - {228, 68, 94, 205, 8} - }, - { {197, 187, 243, 235, 0}, - {241, 226, 26, 222, 15}, - {13, 124, 253, 218, 3}, - {247, 181, 132, 120, 15} - }, - { {224, 138, 63, 86, 10}, - {24, 81, 189, 154, 13}, - {86, 175, 197, 16, 7}, - {181, 155, 216, 161, 8} - }, - { {209, 250, 49, 233, 7}, - {85, 60, 90, 222, 14}, - {233, 120, 197, 248, 11}, - {119, 181, 163, 202, 10} - }, - { {102, 72, 123, 117, 10}, - {184, 84, 167, 157, 12}, - {90, 237, 225, 38, 6}, - {59, 158, 82, 161, 13} - }, - { {152, 233, 237, 143, 5}, - {63, 175, 84, 6, 15}, - {175, 27, 121, 113, 9}, - {246, 2, 175, 95, 12} - }, - { {156, 76, 216, 209, 15}, - {166, 156, 197, 62, 10}, - {248, 177, 179, 35, 9}, - {87, 202, 51, 150, 5} - }, - { {219, 162, 17, 122, 14}, - {19, 56, 219, 219, 1}, - {117, 232, 132, 93, 11}, - {141, 189, 177, 204, 8} - }, - { {252, 157, 45, 143, 13}, - {222, 43, 117, 166, 15}, - {191, 27, 75, 147, 15}, - {246, 90, 237, 71, 11} - }, - { {42, 216, 44, 171, 15}, - {70, 53, 119, 5, 15}, - {253, 83, 65, 181, 4}, - {250, 14, 234, 198, 2} - }, - { {203, 77, 206, 196, 5}, - {46, 199, 68, 235, 10}, - {162, 55, 59, 45, 3}, - {93, 114, 46, 55, 4} - }, - { {212, 230, 126, 105, 10}, - {161, 125, 31, 190, 4}, - {89, 103, 230, 114, 11}, - {39, 223, 139, 232, 5} - }, - { {182, 118, 99, 52, 5}, - {253, 76, 234, 35, 4}, - {162, 204, 102, 230, 13}, - {44, 69, 115, 43, 15} - }, - { {129, 161, 64, 232, 13}, - {37, 34, 83, 74, 2}, - {177, 112, 40, 88, 1}, - {69, 44, 164, 74, 4} - }, - { {36, 248, 181, 42, 13}, - {213, 165, 115, 16, 13}, - {181, 74, 209, 242, 4}, - {176, 140, 234, 90, 11} - }, - { {33, 212, 75, 137, 7}, - {116, 116, 116, 100, 2}, - {233, 29, 34, 184, 4}, - {66, 98, 226, 226, 14} - }, - { {143, 130, 58, 23, 11}, - {142, 80, 157, 87, 5}, - {222, 133, 196, 31, 1}, - {174, 171, 144, 167, 1} - }, - { {246, 113, 26, 188, 10}, - {201, 126, 167, 147, 2}, - {83, 213, 136, 230, 15}, - {76, 158, 87, 233, 3} - }, - { {63, 94, 2, 121, 7}, - {198, 124, 234, 109, 8}, - {233, 228, 7, 175, 12}, - {27, 101, 115, 230, 3} - }, - { {205, 212, 228, 99, 15}, - {230, 149, 83, 238, 5}, - {252, 98, 114, 187, 3}, - {167, 124, 170, 150, 7} - }, - { {89, 70, 5, 49, 5}, - {22, 13, 202, 228, 0}, - {168, 202, 6, 41, 10}, - {2, 117, 59, 6, 8} - }, - { {32, 186, 102, 97, 13}, - {101, 65, 123, 12, 12}, - {184, 102, 101, 208, 4}, - {51, 13, 232, 42, 6} - }, - { {151, 191, 26, 90, 0}, - {193, 106, 156, 123, 9}, - {5, 165, 143, 222, 9}, - {157, 227, 149, 104, 3} - }, - { {126, 66, 218, 161, 8}, - {162, 204, 47, 149, 2}, - {24, 85, 180, 39, 14}, - {74, 159, 67, 52, 5} - }, - { {98, 148, 45, 213, 5}, - {92, 1, 244, 173, 6}, - {170, 187, 66, 148, 6}, - {107, 82, 248, 3, 10} - }, - { {177, 229, 50, 4, 7}, - {13, 94, 112, 114, 4}, - {226, 4, 202, 120, 13}, - {36, 224, 231, 171, 0} - }, - { {222, 100, 233, 20, 12}, - {187, 140, 197, 163, 4}, - {50, 137, 114, 103, 11}, - {44, 90, 51, 29, 13} - }, - { {71, 94, 177, 229, 0}, - {216, 132, 10, 253, 14}, - {10, 120, 215, 174, 2}, - {123, 245, 2, 17, 11} - }, - { {25, 165, 108, 166, 9}, - {47, 11, 23, 96, 7}, - {150, 83, 106, 89, 8}, - {224, 110, 141, 15, 4} - }, - { {149, 5, 117, 79, 13}, - {188, 43, 65, 126, 5}, - {191, 42, 234, 10, 9}, - {167, 232, 45, 67, 13} - }, - { {54, 171, 61, 147, 0}, - {145, 11, 188, 21, 15}, - {12, 155, 205, 86, 12}, - {250, 131, 221, 8, 9} - }, - { {87, 132, 157, 120, 1}, - {148, 169, 150, 249, 0}, - {129, 235, 146, 30, 10}, - {9, 246, 153, 82, 9} - }, - { {164, 135, 244, 206, 4}, - {168, 163, 120, 58, 7}, - {39, 50, 254, 18, 5}, - {229, 193, 236, 81, 5} - }, - { {177, 1, 150, 215, 4}, - {8, 203, 224, 94, 3}, - {46, 182, 152, 8, 13}, - {199, 160, 125, 49, 0} - }, - { {202, 139, 220, 248, 6}, - {34, 179, 222, 155, 10}, - {97, 243, 189, 21, 3}, - {93, 151, 188, 212, 4} - }, - { {241, 104, 15, 187, 1}, - {21, 109, 166, 198, 11}, - {141, 223, 1, 104, 15}, - {214, 54, 91, 106, 8} - }, - { {119, 235, 139, 132, 9}, - {157, 206, 61, 193, 10}, - {146, 29, 29, 126, 14}, - {88, 59, 199, 59, 9} - }, - { {2, 83, 18, 189, 4}, - {72, 102, 202, 21, 2}, - {43, 212, 140, 164, 0}, - {74, 133, 54, 97, 2} - }, - { {29, 42, 141, 92, 1}, - {159, 169, 140, 72, 8}, - {131, 171, 21, 75, 8}, - {17, 35, 25, 95, 9} - }, - { {39, 117, 108, 47, 10}, - {233, 55, 39, 101, 5}, - {95, 67, 106, 238, 4}, - {170, 110, 78, 201, 7} - }, - { {61, 80, 83, 188, 9}, - {254, 108, 163, 80, 2}, - {147, 220, 160, 171, 12}, - {64, 172, 83, 103, 15} - }, - { {120, 181, 174, 182, 4}, - {75, 203, 246, 160, 7}, - {38, 215, 90, 209, 14}, - {224, 86, 253, 61, 2} - }, - { {42, 93, 133, 78, 1}, - {94, 167, 32, 41, 9}, - {135, 42, 27, 165, 4}, - {153, 64, 78, 87, 10} - }, - { {0, 239, 149, 206, 12}, - {25, 167, 89, 56, 11}, - {55, 58, 159, 112, 0}, - {209, 201, 174, 89, 8} - }, - { {198, 248, 201, 176, 10}, - {241, 148, 151, 131, 10}, - {80, 217, 49, 246, 3}, - {92, 30, 146, 152, 15} - }, - { {52, 138, 244, 180, 8}, - {168, 137, 187, 16, 14}, - {18, 210, 245, 18, 12}, - {112, 141, 217, 17, 5} - }, - { {154, 221, 191, 118, 3}, - {94, 223, 150, 59, 13}, - {198, 239, 219, 181, 9}, - {189, 198, 159, 183, 10} - }, - { {68, 183, 153, 113, 1}, - {213, 130, 158, 188, 0}, - {136, 233, 158, 210, 2}, - {3, 215, 148, 26, 11} - }, - { {53, 31, 237, 192, 9}, - {244, 139, 45, 104, 14}, - {144, 59, 127, 138, 12}, - {113, 107, 77, 18, 15} - }, - { {240, 132, 133, 185, 5}, - {20, 169, 242, 166, 2}, - {169, 218, 18, 16, 15}, - {70, 84, 249, 82, 8} - }, - { {130, 188, 93, 116, 5}, - {125, 1, 214, 59, 8}, - {162, 235, 163, 212, 1}, - {29, 198, 184, 11, 14} - }, - { {58, 134, 179, 151, 15}, - {30, 216, 249, 53, 7}, - {254, 156, 214, 21, 12}, - {234, 201, 241, 183, 8} - }, - { {140, 232, 137, 29, 15}, - {159, 180, 213, 6, 8}, - {251, 137, 17, 115, 1}, - {22, 10, 178, 223, 9} - }, - { {63, 123, 205, 83, 8}, - {243, 143, 173, 77, 9}, - {28, 171, 61, 239, 12}, - {155, 43, 95, 28, 15} - }, - { {180, 13, 145, 70, 7}, - {156, 154, 96, 58, 9}, - {230, 40, 155, 2, 13}, - {149, 192, 101, 147, 9} - }, - { {109, 172, 86, 182, 6}, - {171, 81, 242, 240, 11}, - {102, 214, 163, 91, 6}, - {208, 244, 248, 173, 5} - }, - { {88, 149, 105, 0, 13}, - {118, 10, 85, 160, 4}, - {176, 9, 106, 145, 10}, - {32, 90, 165, 6, 14} - }, - { {76, 163, 63, 89, 3}, - {151, 115, 156, 156, 4}, - {201, 175, 204, 83, 2}, - {35, 147, 156, 238, 9} - }, - { {53, 167, 215, 80, 1}, - {181, 203, 184, 120, 0}, - {128, 174, 190, 90, 12}, - {1, 225, 221, 58, 13} - }, - { {22, 233, 116, 244, 11}, - {173, 31, 147, 25, 14}, - {210, 242, 233, 118, 8}, - {121, 140, 159, 139, 5} - }, - { {106, 219, 126, 99, 3}, - {102, 87, 62, 157, 13}, - {204, 103, 237, 181, 6}, - {187, 151, 206, 166, 6} - }, - { {172, 254, 199, 138, 5}, - {247, 229, 120, 34, 11}, - {165, 30, 55, 243, 5}, - {212, 65, 234, 126, 15} - }, - { {195, 109, 112, 53, 6}, - {41, 22, 194, 247, 12}, - {106, 192, 235, 108, 3}, - {62, 244, 54, 137, 4} - }, - { {133, 82, 84, 245, 6}, - {232, 21, 202, 94, 2}, - {106, 242, 164, 170, 1}, - {71, 165, 58, 129, 7} - }, - { {211, 112, 148, 110, 7}, - {77, 189, 66, 219, 1}, - {231, 98, 144, 236, 11}, - {141, 180, 43, 219, 2} - }, - { {157, 177, 206, 126, 12}, - {235, 235, 215, 74, 1}, - {55, 231, 56, 219, 9}, - {133, 46, 189, 125, 7} - }, - { {163, 136, 217, 180, 14}, - {56, 144, 247, 83, 10}, - {114, 217, 177, 28, 5}, - {92, 174, 240, 145, 12} - }, - { {98, 252, 134, 118, 14}, - {73, 213, 243, 169, 9}, - {118, 230, 19, 244, 6}, - {153, 92, 250, 185, 2} - }, - { {209, 228, 206, 15, 2}, - {41, 253, 20, 230, 1}, - {79, 7, 50, 120, 11}, - {134, 114, 139, 249, 4} - }, - { {224, 197, 119, 135, 0}, - {56, 71, 48, 182, 7}, - {14, 30, 234, 48, 7}, - {230, 208, 206, 33, 12} - }, - { {136, 137, 241, 121, 7}, - {54, 178, 210, 30, 12}, - {233, 232, 249, 17, 1}, - {55, 132, 180, 214, 12} - }, - { {175, 103, 124, 158, 0}, - {171, 39, 172, 115, 7}, - {7, 147, 238, 111, 5}, - {236, 227, 94, 77, 5} - }, - { {250, 135, 32, 206, 8}, - {10, 42, 57, 171, 7}, - {23, 48, 78, 21, 15}, - {237, 89, 197, 69, 0} - }, - { {32, 136, 108, 197, 14}, - {40, 17, 117, 12, 14}, - {122, 51, 97, 16, 4}, - {115, 10, 232, 129, 4} - }, - { {26, 153, 204, 140, 14}, - {106, 187, 85, 1, 10}, - {115, 19, 57, 149, 8}, - {88, 10, 173, 213, 6} - }, - { {136, 165, 101, 91, 1}, - {55, 35, 144, 46, 5}, - {141, 170, 106, 81, 1}, - {167, 64, 156, 78, 12} - }, - { {214, 247, 236, 2, 5}, - {229, 143, 92, 163, 5}, - {164, 3, 126, 246, 11}, - {172, 83, 175, 26, 7} - }, - { {244, 246, 145, 8, 4}, - {209, 172, 120, 178, 0}, - {33, 8, 150, 242, 15}, - {4, 209, 227, 88, 11} - }, - { {76, 10, 202, 63, 5}, - {174, 224, 206, 132, 9}, - {175, 197, 53, 3, 2}, - {146, 23, 48, 119, 5} - }, - { {31, 5, 164, 12, 7}, - {142, 187, 64, 97, 4}, - {227, 2, 90, 15, 8}, - {40, 96, 45, 215, 1} - }, - { {250, 93, 132, 124, 6}, - {74, 191, 226, 171, 8}, - {99, 226, 27, 165, 15}, - {29, 84, 127, 213, 2} - }, - { {19, 29, 173, 215, 12}, - {88, 139, 197, 109, 15}, - {62, 187, 91, 140, 8}, - {251, 106, 61, 17, 10} - }, - { {195, 96, 214, 57, 15}, - {37, 245, 195, 215, 0}, - {249, 198, 176, 108, 3}, - {14, 188, 58, 250, 4} - }, - { {126, 199, 122, 149, 3}, - {174, 94, 188, 181, 6}, - {202, 149, 238, 55, 14}, - {106, 211, 215, 167, 5} - }, - { {45, 212, 161, 248, 1}, - {214, 164, 178, 104, 6}, - {129, 248, 82, 187, 4}, - {97, 100, 210, 86, 11} - }, - { {196, 27, 169, 22, 11}, - {220, 146, 141, 130, 13}, - {214, 137, 93, 130, 3}, - {180, 27, 20, 147, 11} - }, - { {174, 83, 48, 32, 14}, - {194, 22, 107, 19, 4}, - {112, 64, 204, 167, 5}, - {44, 141, 102, 132, 3} - }, - { {137, 195, 118, 107, 2}, - {34, 119, 26, 94, 5}, - {77, 102, 236, 57, 1}, - {167, 165, 142, 228, 4} - }, - { {17, 167, 104, 69, 2}, - {41, 26, 28, 108, 4}, - {74, 33, 110, 88, 8}, - {35, 99, 133, 137, 4} - }, - { {115, 24, 54, 184, 10}, - {64, 121, 163, 209, 14}, - {81, 214, 193, 140, 14}, - {120, 188, 89, 224, 2} - }, - { {152, 195, 136, 238, 2}, - {10, 190, 30, 10, 3}, - {71, 113, 28, 49, 9}, - {197, 7, 135, 213, 0} - }, - { {50, 158, 254, 117, 6}, - {104, 217, 254, 61, 12}, - {106, 231, 247, 148, 12}, - {59, 199, 249, 177, 6} - }, - { {235, 41, 103, 213, 2}, - {59, 83, 160, 207, 14}, - {74, 190, 105, 77, 7}, - {127, 48, 92, 173, 12} - }, - { {118, 246, 246, 147, 13}, - {229, 205, 249, 181, 7}, - {188, 150, 246, 246, 14}, - {234, 217, 251, 58, 7} - }, - { {75, 54, 229, 94, 10}, - {123, 177, 137, 233, 5}, - {87, 170, 118, 205, 2}, - {169, 121, 24, 221, 14} - }, - { {18, 100, 241, 41, 15}, - {53, 188, 67, 53, 4}, - {249, 72, 242, 100, 8}, - {42, 204, 35, 218, 12} - }, - { {195, 216, 179, 216, 12}, - {80, 228, 209, 219, 14}, - {49, 188, 209, 188, 3}, - {125, 184, 178, 112, 10} - }, - { {184, 252, 222, 50, 9}, - {103, 205, 183, 50, 9}, - {148, 199, 179, 241, 13}, - {148, 206, 219, 62, 6} - }, - { {53, 97, 247, 116, 12}, - {185, 207, 227, 88, 4}, - {50, 238, 248, 106, 12}, - {33, 172, 127, 57, 13} - }, - { {116, 255, 31, 22, 6}, - {217, 95, 252, 176, 9}, - {102, 143, 143, 242, 14}, - {144, 211, 255, 169, 11} - }, - { {29, 80, 232, 123, 6}, - {226, 188, 198, 76, 5}, - {109, 225, 112, 171, 8}, - {163, 38, 51, 212, 7} - }, - { {71, 201, 216, 44, 9}, - {172, 166, 23, 209, 8}, - {147, 65, 185, 62, 2}, - {24, 190, 134, 83, 5} - }, - { {198, 131, 135, 46, 9}, - {156, 227, 27, 131, 1}, - {151, 78, 28, 22, 3}, - {140, 29, 140, 115, 9} - }, - { {182, 245, 65, 140, 14}, - {249, 62, 113, 35, 2}, - {115, 24, 42, 246, 13}, - {76, 72, 231, 201, 15} - }, - { {209, 45, 250, 27, 10}, - {33, 250, 133, 246, 13}, - {93, 133, 251, 72, 11}, - {182, 250, 21, 248, 4} - }, - { {123, 222, 164, 209, 14}, - {66, 157, 249, 237, 14}, - {120, 178, 87, 189, 14}, - {123, 121, 251, 148, 2} - }, - { {148, 215, 11, 206, 8}, - {216, 110, 29, 42, 3}, - {23, 61, 14, 178, 9}, - {197, 75, 135, 97, 11} - }, - { {139, 161, 235, 181, 10}, - {59, 210, 151, 71, 6}, - {90, 221, 120, 93, 1}, - {110, 46, 148, 189, 12} - }, - { {2, 76, 23, 111, 5}, - {28, 101, 66, 61, 9}, - {175, 110, 131, 36, 0}, - {155, 196, 42, 99, 8} - }, - { {54, 104, 158, 115, 11}, - {133, 221, 167, 29, 9}, - {220, 231, 145, 102, 12}, - {155, 142, 91, 186, 1} - }, - { {208, 33, 104, 228, 4}, - {41, 10, 70, 138, 6}, - {34, 113, 104, 64, 11}, - {101, 22, 37, 9, 4} - }, - { {155, 143, 16, 45, 14}, - {10, 58, 91, 119, 8}, - {123, 64, 143, 29, 9}, - {30, 237, 165, 197, 0} - }, - { {126, 132, 92, 39, 11}, - {174, 25, 55, 181, 1}, - {222, 67, 162, 23, 14}, - {138, 222, 201, 135, 5} - }, - { {118, 223, 8, 118, 12}, - {200, 14, 255, 169, 9}, - {54, 225, 15, 182, 14}, - {153, 95, 247, 1, 3} - }, - { {251, 188, 21, 149, 7}, - {95, 25, 240, 247, 10}, - {234, 154, 131, 221, 15}, - {94, 240, 249, 143, 10} - }, - { {70, 128, 175, 59, 2}, - {144, 241, 150, 133, 5}, - {77, 207, 80, 22, 2}, - {170, 22, 152, 240, 9} - }, - { {209, 94, 13, 85, 9}, - {92, 13, 141, 238, 8}, - {154, 171, 7, 168, 11}, - {23, 123, 27, 3, 10} - }, - { {140, 34, 108, 129, 4}, - {163, 1, 76, 6, 6}, - {40, 19, 100, 67, 1}, - {102, 3, 40, 12, 5} - }, - { {118, 43, 180, 156, 1}, - {141, 171, 168, 145, 14}, - {131, 146, 221, 70, 14}, - {120, 145, 93, 91, 1} - }, - { {115, 86, 122, 41, 0}, - {96, 108, 46, 245, 4}, - {9, 69, 230, 172, 14}, - {42, 247, 67, 96, 6} - }, - { {65, 92, 222, 11, 1}, - {100, 229, 4, 244, 9}, - {141, 7, 179, 168, 2}, - {146, 242, 10, 114, 6} - }, - { {9, 152, 244, 128, 11}, - {102, 145, 17, 80, 14}, - {208, 18, 241, 153, 0}, - {112, 168, 136, 150, 6} - }, - { {227, 53, 25, 14, 13}, - {93, 34, 101, 243, 1}, - {183, 9, 138, 204, 7}, - {140, 250, 100, 75, 10} - }, - { {54, 133, 243, 244, 3}, - {188, 218, 178, 57, 6}, - {194, 252, 250, 22, 12}, - {105, 196, 213, 179, 13} - }, - { {149, 140, 227, 231, 8}, - {184, 200, 19, 110, 15}, - {30, 124, 115, 26, 9}, - {247, 108, 129, 49, 13} - }, - { {119, 22, 87, 104, 15}, - {244, 121, 107, 249, 0}, - {241, 110, 166, 142, 14}, - {9, 253, 105, 226, 15} - }, - { {192, 211, 150, 95, 4}, - {72, 231, 216, 158, 1}, - {47, 166, 156, 176, 3}, - {135, 145, 190, 113, 2} - }, - { {212, 159, 135, 115, 2}, - {208, 219, 154, 174, 9}, - {76, 238, 31, 146, 11}, - {151, 85, 157, 176, 11} - }, - { {69, 191, 48, 71, 8}, - {201, 2, 25, 252, 13}, - {30, 32, 207, 218, 2}, - {179, 249, 132, 9, 3} - }, - { {178, 247, 139, 151, 9}, - {93, 206, 189, 39, 3}, - {158, 157, 30, 244, 13}, - {206, 75, 215, 59, 10} - }, - { {182, 81, 172, 193, 7}, - {196, 159, 100, 15, 6}, - {232, 51, 88, 166, 13}, - {111, 2, 111, 146, 3} - }, - { {18, 188, 225, 89, 10}, - {113, 184, 145, 45, 12}, - {89, 168, 115, 212, 8}, - {59, 72, 145, 216, 14} - }, - { {142, 109, 66, 22, 5}, - {175, 70, 192, 35, 9}, - {166, 132, 43, 103, 1}, - {156, 64, 54, 47, 5} - }, - { {247, 219, 218, 17, 6}, - {224, 222, 252, 215, 8}, - {104, 133, 189, 190, 15}, - {30, 179, 247, 176, 7} - }, - { {95, 40, 2, 124, 14}, - {139, 120, 195, 201, 8}, - {115, 228, 1, 79, 10}, - {25, 60, 49, 237, 1} - }, - { {2, 18, 169, 157, 14}, - {88, 176, 205, 5, 6}, - {123, 153, 84, 132, 0}, - {106, 11, 48, 209, 10} - }, - { {191, 254, 168, 18, 10}, - {195, 156, 189, 99, 13}, - {84, 129, 87, 255, 13}, - {188, 107, 211, 156, 3} - }, - { {186, 49, 92, 161, 1}, - {103, 11, 38, 23, 2}, - {136, 83, 168, 197, 13}, - {78, 134, 77, 14, 6} - }, - { {7, 242, 225, 29, 7}, - {253, 180, 216, 69, 4}, - {235, 136, 116, 254, 0}, - {42, 33, 178, 219, 15} - }, - { {234, 79, 43, 164, 13}, - {30, 70, 111, 163, 14}, - {178, 93, 79, 37, 7}, - {124, 95, 102, 39, 8} - }, - { {165, 190, 34, 214, 12}, - {201, 64, 249, 106, 15}, - {54, 180, 71, 218, 5}, - {245, 105, 240, 41, 3} - }, - { {241, 169, 225, 184, 14}, - {49, 186, 243, 194, 14}, - {113, 216, 121, 88, 15}, - {116, 60, 245, 216, 12} - }, - { {202, 168, 155, 183, 7}, - {31, 208, 214, 151, 11}, - {238, 221, 145, 85, 3}, - {222, 150, 176, 191, 8} - }, - { {84, 134, 196, 245, 2}, - {168, 153, 154, 172, 2}, - {74, 242, 54, 18, 10}, - {67, 85, 153, 145, 5} - }, - { {53, 121, 76, 30, 4}, - {233, 47, 228, 64, 9}, - {39, 131, 41, 234, 12}, - {144, 34, 127, 73, 7} - }, - { {110, 216, 72, 137, 8}, - {226, 36, 53, 133, 10}, - {25, 17, 33, 183, 6}, - {90, 26, 194, 68, 7} - }, - { {186, 232, 137, 103, 6}, - {27, 156, 118, 15, 9}, - {110, 105, 17, 117, 13}, - {159, 6, 227, 157, 8} - }, - { {148, 192, 40, 189, 0}, - {136, 44, 150, 6, 6}, - {11, 209, 64, 50, 9}, - {102, 6, 147, 65, 1} - }, - { {115, 76, 162, 183, 6}, - {8, 220, 226, 229, 15}, - {110, 212, 83, 44, 14}, - {250, 116, 115, 177, 0} - }, - { {47, 241, 60, 122, 11}, - {199, 55, 183, 89, 5}, - {213, 227, 200, 255, 4}, - {169, 174, 222, 206, 3} - }, - { {204, 2, 99, 100, 7}, - {190, 80, 74, 138, 4}, - {226, 108, 100, 3, 3}, - {37, 21, 32, 167, 13} - }, - { {222, 144, 69, 41, 2}, - {242, 57, 18, 135, 0}, - {73, 74, 32, 151, 11}, - {14, 20, 137, 196, 15} - }, - { {2, 43, 108, 122, 15}, - {37, 51, 207, 9, 13}, - {245, 227, 109, 68, 0}, - {185, 15, 60, 202, 4} - }, - { {64, 75, 24, 30, 13}, - {12, 38, 205, 144, 9}, - {183, 129, 141, 32, 2}, - {144, 155, 54, 67, 0} - }, - { {44, 49, 42, 198, 5}, - {207, 66, 100, 8, 7}, - {166, 53, 72, 195, 4}, - {225, 2, 100, 47, 3} - }, - { {191, 189, 15, 65, 6}, - {211, 91, 116, 111, 8}, - {104, 47, 11, 223, 13}, - {31, 98, 237, 172, 11} - }, - { {232, 58, 99, 203, 12}, - {115, 96, 105, 142, 15}, - {61, 60, 101, 193, 7}, - {247, 25, 96, 108, 14} - }, - { {231, 89, 156, 35, 2}, - {192, 151, 38, 215, 9}, - {76, 67, 153, 174, 7}, - {158, 182, 78, 144, 3} - }, - { {255, 230, 113, 141, 6}, - {187, 60, 120, 247, 6}, - {107, 24, 230, 127, 15}, - {110, 241, 227, 205, 13} - }, - { {197, 136, 65, 238, 2}, - {184, 48, 18, 202, 11}, - {71, 120, 33, 26, 3}, - {213, 52, 128, 193, 13} - }, - { {171, 180, 217, 16, 2}, - {115, 144, 180, 115, 0}, - {64, 137, 178, 221, 5}, - {12, 226, 208, 156, 14} - }, - { {10, 23, 123, 120, 4}, - {114, 98, 206, 57, 4}, - {33, 237, 238, 133, 0}, - {41, 199, 52, 100, 14} - }, - { {78, 123, 115, 156, 14}, - {251, 118, 201, 145, 14}, - {115, 156, 237, 231, 2}, - {120, 153, 54, 237, 15} - }, - { {34, 227, 251, 26, 1}, - {53, 230, 188, 17, 5}, - {133, 141, 252, 116, 4}, - {168, 131, 214, 122, 12} - }, - { {54, 93, 186, 61, 2}, - {200, 254, 166, 53, 12}, - {75, 197, 219, 166, 12}, - {58, 198, 87, 241, 3} - }, - { {59, 220, 189, 56, 4}, - {82, 173, 246, 113, 12}, - {33, 203, 211, 189, 12}, - {56, 230, 251, 84, 10} - }, - { {200, 199, 188, 19, 5}, - {6, 135, 220, 182, 5}, - {172, 131, 222, 49, 3}, - {166, 211, 190, 22, 0} - }, - { {91, 16, 250, 200, 6}, - {98, 248, 68, 217, 6}, - {97, 53, 240, 141, 10}, - {105, 178, 33, 244, 6} - }, - { {208, 249, 92, 92, 11}, - {109, 63, 149, 154, 8}, - {211, 163, 169, 240, 11}, - {21, 154, 159, 203, 6} - }, - { {153, 6, 142, 77, 7}, - {14, 249, 76, 110, 0}, - {235, 39, 22, 9, 9}, - {7, 99, 41, 247, 0} - }, - { {200, 39, 17, 208, 2}, - {19, 18, 136, 186, 2}, - {64, 184, 142, 65, 3}, - {69, 209, 20, 140, 8} - }, - { {164, 84, 34, 73, 9}, - {196, 100, 33, 46, 4}, - {153, 36, 66, 162, 5}, - {39, 72, 66, 98, 3} - }, - { {169, 103, 185, 198, 3}, - {31, 150, 44, 122, 7}, - {198, 57, 222, 105, 5}, - {229, 227, 70, 159, 8} - }, - { {227, 227, 61, 193, 6}, - {17, 23, 124, 223, 6}, - {104, 59, 204, 124, 7}, - {111, 179, 238, 136, 8} - }, - { {242, 227, 250, 36, 6}, - {41, 222, 126, 147, 4}, - {98, 69, 252, 116, 15}, - {44, 151, 231, 185, 4} - }, - { {171, 214, 110, 194, 10}, - {98, 85, 61, 107, 7}, - {84, 55, 102, 189, 5}, - {237, 107, 202, 164, 6} - }, - { {150, 166, 172, 174, 12}, - {137, 169, 95, 35, 7}, - {55, 83, 86, 86, 9}, - {236, 79, 169, 89, 1} - }, - { {17, 94, 218, 37, 15}, - {108, 220, 79, 116, 8}, - {250, 69, 183, 168, 8}, - {18, 239, 35, 179, 6} - }, - { {224, 72, 217, 79, 9}, - {60, 164, 37, 158, 9}, - {159, 41, 177, 32, 7}, - {151, 154, 66, 83, 12} - }, - { {62, 162, 162, 69, 13}, - {143, 200, 121, 13, 4}, - {186, 36, 84, 87, 12}, - {43, 9, 225, 63, 1} - }, - { {147, 122, 113, 157, 10}, - {121, 60, 137, 87, 14}, - {91, 152, 229, 236, 9}, - {126, 169, 19, 201, 14} - }, - { {109, 101, 53, 32, 4}, - {147, 7, 98, 240, 4}, - {32, 74, 202, 107, 6}, - {32, 244, 110, 12, 9} - }, - { {218, 6, 151, 74, 11}, - {22, 249, 9, 187, 1}, - {213, 46, 150, 5, 11}, - {141, 217, 9, 246, 8} - }, - { {20, 176, 37, 32, 10}, - {209, 25, 19, 0, 4}, - {80, 74, 64, 210, 8}, - {32, 12, 137, 136, 11} - }, - { {122, 150, 135, 109, 5}, - {94, 233, 122, 173, 0}, - {171, 110, 22, 149, 14}, - {11, 85, 233, 119, 10} - }, - { {221, 120, 18, 100, 5}, - {207, 76, 66, 218, 8}, - {162, 100, 129, 235, 11}, - {21, 180, 35, 47, 3} - }, - { {130, 195, 122, 162, 9}, - {36, 70, 31, 19, 7}, - {148, 85, 236, 52, 1}, - {236, 143, 134, 34, 4} - }, - { {151, 25, 250, 15, 4}, - {232, 234, 68, 87, 13}, - {47, 5, 249, 142, 9}, - {190, 162, 37, 113, 7} - }, - { {58, 108, 193, 181, 7}, - {63, 156, 226, 37, 10}, - {234, 216, 51, 101, 12}, - {90, 68, 115, 159, 12} - }, - { {97, 33, 177, 73, 13}, - {21, 162, 97, 220, 4}, - {185, 40, 216, 72, 6}, - {35, 184, 100, 90, 8} - }, - { {208, 123, 6, 29, 14}, - {73, 127, 201, 134, 8}, - {123, 134, 13, 224, 11}, - {22, 25, 63, 233, 2} - }, - { {69, 190, 247, 38, 9}, - {253, 193, 27, 240, 13}, - {150, 78, 247, 218, 2}, - {176, 253, 136, 59, 15} - }, - { {157, 70, 148, 66, 4}, - {130, 141, 72, 122, 1}, - {36, 34, 150, 43, 9}, - {133, 225, 43, 20, 1} - }, - { {77, 251, 186, 241, 4}, - {195, 198, 222, 220, 14}, - {40, 245, 221, 251, 2}, - {115, 183, 182, 60, 3} - }, - { {200, 236, 99, 163, 1}, - {55, 68, 18, 166, 15}, - {140, 92, 99, 113, 3}, - {246, 84, 130, 46, 12} - }, - { {41, 38, 49, 96, 9}, - {23, 0, 43, 120, 4}, - {144, 104, 198, 73, 4}, - {33, 237, 64, 14, 8} - }, - { {34, 234, 181, 80, 10}, - {17, 149, 185, 25, 12}, - {80, 170, 213, 116, 4}, - {57, 137, 218, 152, 8} - }, - { {12, 83, 62, 124, 9}, - {206, 103, 143, 24, 4}, - {147, 231, 204, 163, 0}, - {33, 143, 30, 103, 3} - }, - { {241, 209, 122, 65, 9}, - {100, 78, 53, 222, 4}, - {152, 37, 232, 184, 15}, - {39, 186, 199, 34, 6} - }, - { {190, 41, 45, 21, 12}, - {155, 11, 229, 7, 12}, - {58, 139, 73, 71, 13}, - {62, 10, 125, 13, 9} - }, - { {173, 236, 230, 5, 15}, - {175, 213, 113, 102, 12}, - {250, 6, 115, 123, 5}, - {54, 104, 234, 191, 5} - }, - { {23, 198, 147, 157, 3}, - {156, 252, 152, 117, 2}, - {203, 156, 150, 62, 8}, - {74, 225, 147, 243, 9} - }, - { {76, 195, 212, 88, 9}, - {166, 167, 153, 152, 0}, - {145, 162, 188, 51, 2}, - {1, 153, 158, 86, 5} - }, - { {154, 214, 102, 33, 4}, - {98, 77, 90, 39, 4}, - {40, 70, 102, 181, 9}, - {46, 69, 171, 36, 6} - }, - { {19, 133, 65, 215, 3}, - {60, 26, 144, 109, 3}, - {206, 184, 42, 28, 8}, - {203, 96, 149, 131, 12} - }, - { {86, 15, 151, 244, 5}, - {156, 203, 202, 185, 10}, - {162, 254, 159, 6, 10}, - {89, 213, 61, 51, 9} - }, - { {166, 192, 165, 246, 0}, - {152, 133, 178, 11, 7}, - {6, 250, 80, 54, 5}, - {237, 4, 218, 17, 9} - }, - { {95, 226, 229, 89, 8}, - {179, 173, 153, 205, 4}, - {25, 170, 116, 127, 10}, - {43, 57, 155, 92, 13} - }, - { {67, 190, 136, 118, 5}, - {77, 128, 222, 233, 9}, - {166, 225, 23, 220, 2}, - {153, 119, 176, 27, 2} - }, - { {8, 144, 129, 205, 4}, - {90, 160, 80, 12, 2}, - {43, 56, 16, 145, 0}, - {67, 0, 160, 85, 10} - }, - { {77, 234, 23, 239, 15}, - {159, 117, 91, 220, 11}, - {255, 126, 133, 123, 2}, - {211, 189, 170, 239, 9} - }, - { {124, 58, 173, 18, 0}, - {211, 137, 172, 128, 13}, - {4, 139, 85, 195, 14}, - {176, 19, 89, 28, 11} - }, - { {67, 86, 162, 174, 9}, - {76, 228, 11, 225, 7}, - {151, 84, 86, 172, 2}, - {232, 125, 2, 115, 2} - }, - { {236, 183, 0, 75, 5}, - {199, 34, 120, 174, 1}, - {173, 32, 14, 211, 7}, - {135, 81, 228, 78, 3} - }, - { {247, 97, 178, 178, 7}, - {133, 222, 226, 211, 7}, - {228, 212, 216, 110, 15}, - {236, 180, 119, 186, 1} - }, - { {137, 36, 99, 124, 13}, - {63, 96, 195, 106, 4}, - {179, 236, 98, 73, 1}, - {37, 108, 48, 111, 12} - }, - { {114, 58, 98, 179, 1}, - {101, 72, 170, 133, 15}, - {140, 212, 101, 196, 14}, - {250, 21, 81, 42, 6} - }, - { {41, 180, 125, 69, 1}, - {127, 1, 52, 124, 4}, - {138, 43, 226, 217, 4}, - {35, 226, 200, 15, 14} - }, - { {18, 107, 239, 149, 10}, - {57, 223, 141, 5, 14}, - {90, 159, 125, 100, 8}, - {122, 11, 31, 185, 12} - }, - { {161, 94, 96, 121, 10}, - {96, 52, 171, 110, 12}, - {89, 224, 103, 168, 5}, - {55, 109, 82, 192, 6} - }, - { {126, 76, 172, 58, 13}, - {134, 173, 231, 161, 13}, - {181, 195, 83, 39, 14}, - {184, 94, 123, 86, 1} - }, - { {114, 142, 21, 50, 3}, - {20, 25, 186, 177, 9}, - {196, 202, 135, 20, 14}, - {152, 213, 217, 130, 8} - }, - { {50, 5, 228, 151, 0}, - {40, 139, 160, 37, 7}, - {14, 146, 122, 4, 12}, - {234, 64, 93, 17, 4} - }, - { {20, 99, 87, 142, 15}, - {189, 127, 73, 16, 3}, - {247, 30, 172, 98, 8}, - {192, 137, 47, 235, 13} - }, - { {70, 114, 172, 144, 8}, - {193, 133, 141, 129, 6}, - {16, 147, 84, 230, 2}, - {104, 27, 26, 24, 3} - }, - { {218, 88, 222, 192, 8}, - {98, 205, 5, 155, 10}, - {16, 55, 177, 165, 11}, - {93, 154, 11, 52, 6} - }, - { {201, 31, 228, 54, 4}, - {106, 131, 202, 226, 13}, - {38, 194, 127, 137, 3}, - {180, 117, 60, 21, 6} - }, - { {161, 197, 254, 154, 12}, - {32, 231, 245, 114, 7}, - {53, 151, 250, 56, 5}, - {228, 234, 254, 112, 4} - }, - { {247, 70, 210, 71, 7}, - {172, 220, 104, 255, 1}, - {238, 36, 182, 46, 15}, - {143, 241, 99, 179, 5} - }, - { {56, 73, 248, 205, 2}, - {42, 190, 36, 28, 14}, - {75, 49, 249, 33, 12}, - {115, 130, 71, 213, 4} - }, - { {186, 114, 36, 10, 6}, - {67, 61, 104, 3, 5}, - {101, 2, 68, 229, 13}, - {172, 1, 107, 204, 2} - }, - { {100, 162, 99, 127, 3}, - {189, 112, 186, 140, 5}, - {207, 236, 100, 82, 6}, - {163, 21, 208, 235, 13} - }, - { {132, 80, 221, 43, 3}, - {244, 181, 6, 22, 1}, - {205, 75, 176, 162, 1}, - {134, 134, 10, 210, 15} - }, - { {233, 134, 163, 18, 6}, - {18, 208, 248, 226, 5}, - {100, 140, 86, 25, 7}, - {164, 113, 240, 180, 8} - }, - { {31, 106, 10, 155, 13}, - {135, 108, 205, 69, 11}, - {189, 149, 5, 111, 8}, - {218, 43, 51, 110, 1} - }, - { {193, 131, 19, 193, 9}, - {20, 66, 25, 222, 2}, - {152, 60, 140, 24, 3}, - {71, 185, 132, 34, 8} - }, - { {68, 244, 102, 59, 11}, - {229, 117, 147, 164, 5}, - {221, 198, 98, 242, 2}, - {162, 92, 154, 234, 7} - }, - { {1, 157, 216, 198, 10}, - {104, 146, 21, 120, 11}, - {86, 49, 187, 152, 0}, - {209, 234, 132, 145, 6} - }, - { {207, 106, 252, 4, 8}, - {171, 133, 13, 211, 12}, - {18, 3, 245, 111, 3}, - {60, 187, 10, 29, 5} - }, - { {146, 174, 195, 5, 6}, - {57, 216, 88, 39, 8}, - {106, 12, 55, 84, 9}, - {30, 65, 161, 185, 12} - }, - { {37, 21, 93, 154, 5}, - {244, 35, 228, 112, 3}, - {165, 155, 170, 138, 4}, - {192, 226, 124, 66, 15} - }, - { {22, 10, 92, 69, 6}, - {168, 25, 76, 29, 8}, - {106, 35, 165, 6, 8}, - {27, 131, 41, 129, 5} - }, - { {42, 195, 144, 107, 5}, - {6, 166, 122, 29, 1}, - {173, 96, 156, 53, 4}, - {139, 133, 230, 86, 0} - }, - { {46, 122, 182, 246, 15}, - {207, 213, 235, 25, 15}, - {246, 246, 213, 231, 4}, - {249, 141, 122, 191, 3} - }, - { {158, 250, 147, 214, 12}, - {219, 204, 217, 27, 11}, - {54, 188, 149, 247, 9}, - {221, 137, 179, 61, 11} - }, - { {70, 52, 147, 36, 2}, - {217, 208, 2, 177, 0}, - {66, 76, 146, 198, 2}, - {8, 212, 0, 185, 11} - }, - { {10, 253, 84, 150, 12}, - {107, 7, 209, 49, 11}, - {54, 146, 171, 245, 0}, - {216, 200, 190, 13, 6} - }, - { {176, 98, 61, 37, 0}, - {25, 13, 46, 22, 4}, - {10, 75, 196, 96, 13}, - {38, 135, 75, 9, 8} - }, - { {145, 210, 194, 45, 8}, - {104, 236, 27, 70, 0}, - {27, 68, 52, 184, 9}, - {6, 45, 131, 113, 6} - }, - { {180, 12, 140, 215, 2}, - {136, 153, 164, 46, 11}, - {78, 179, 19, 2, 13}, - {215, 66, 89, 145, 1} - }, - { {159, 137, 21, 197, 9}, - {158, 11, 17, 95, 10}, - {154, 58, 137, 31, 9}, - {95, 168, 141, 7, 9} - }, - { {231, 171, 31, 92, 9}, - {157, 99, 189, 219, 8}, - {147, 175, 141, 94, 7}, - {29, 187, 220, 107, 9} - }, - { {64, 95, 208, 111, 7}, - {108, 182, 74, 188, 9}, - {239, 96, 191, 160, 2}, - {147, 213, 38, 211, 6} - }, - { {107, 30, 197, 224, 6}, - {114, 145, 106, 233, 10}, - {96, 122, 55, 141, 6}, - {89, 117, 104, 148, 14} - }, - { {172, 27, 229, 165, 10}, - {250, 147, 43, 6, 14}, - {90, 90, 125, 131, 5}, - {118, 13, 76, 149, 15} - }, - { {230, 47, 3, 108, 10}, - {153, 114, 43, 171, 8}, - {83, 108, 15, 70, 7}, - {29, 93, 68, 233, 9} - }, - { {127, 229, 102, 248, 8}, - {163, 111, 179, 233, 6}, - {17, 246, 106, 127, 14}, - {105, 124, 223, 108, 5} - }, - { {163, 194, 62, 50, 14}, - {0, 85, 255, 83, 5}, - {116, 199, 196, 60, 5}, - {172, 175, 250, 160, 0} - }, - { {148, 62, 117, 118, 6}, - {249, 25, 202, 58, 13}, - {102, 234, 231, 194, 9}, - {181, 197, 57, 137, 15} - }, - { {52, 186, 30, 90, 15}, - {197, 121, 253, 24, 9}, - {245, 167, 133, 210, 12}, - {145, 139, 249, 234, 3} - }, - { {3, 165, 18, 156, 11}, - {13, 114, 145, 113, 2}, - {211, 148, 138, 92, 0}, - {72, 232, 148, 235, 0} - }, - { {37, 82, 144, 131, 7}, - {196, 148, 104, 84, 3}, - {236, 16, 148, 170, 4}, - {194, 161, 98, 146, 3} - }, - { {152, 56, 107, 83, 15}, - {119, 88, 197, 14, 13}, - {252, 173, 97, 193, 9}, - {183, 10, 49, 174, 14} - }, - { {81, 8, 229, 57, 2}, - {48, 185, 130, 196, 12}, - {73, 202, 113, 8, 10}, - {50, 52, 25, 208, 12} - }, - { {109, 47, 236, 46, 14}, - {171, 179, 111, 224, 13}, - {119, 67, 127, 75, 6}, - {176, 127, 108, 221, 5} - }, - { {82, 188, 16, 202, 10}, - {65, 56, 17, 185, 11}, - {85, 48, 131, 212, 10}, - {217, 216, 129, 200, 2} - }, - { {179, 40, 54, 67, 10}, - {1, 89, 33, 95, 13}, - {92, 38, 193, 76, 13}, - {191, 168, 73, 168, 0} - }, - { {119, 53, 50, 215, 6}, - {201, 90, 224, 253, 7}, - {110, 180, 202, 206, 14}, - {235, 240, 117, 169, 3} - }, - { {60, 107, 123, 81, 7}, - {183, 94, 236, 28, 12}, - {232, 173, 237, 99, 12}, - {51, 131, 119, 174, 13} - }, - { {58, 210, 85, 76, 8}, - {122, 45, 57, 25, 0}, - {19, 42, 164, 181, 12}, - {9, 137, 203, 69, 14} - }, - { {121, 133, 192, 110, 10}, - {42, 186, 51, 232, 1}, - {87, 96, 58, 25, 14}, - {129, 124, 197, 213, 4} - }, - { {197, 187, 154, 199, 7}, - {205, 210, 92, 222, 11}, - {238, 53, 157, 218, 3}, - {215, 179, 164, 187, 3} - }, - { {12, 197, 122, 240, 11}, - {166, 86, 151, 56, 6}, - {208, 245, 234, 51, 0}, - {97, 206, 150, 166, 5} - }, - { {248, 208, 240, 214, 2}, - {106, 156, 176, 154, 7}, - {70, 176, 240, 177, 15}, - {229, 144, 211, 149, 6} - }, - { {129, 63, 211, 226, 11}, - {117, 210, 11, 122, 11}, - {212, 124, 191, 200, 1}, - {213, 237, 4, 186, 14} - }, - { {13, 236, 216, 183, 12}, - {171, 132, 215, 116, 11}, - {62, 209, 179, 123, 0}, - {210, 238, 178, 29, 5} - }, - { {27, 74, 227, 19, 2}, - {50, 220, 136, 69, 13}, - {76, 140, 117, 45, 8}, - {186, 33, 19, 180, 12} - }, - { {210, 32, 64, 67, 5}, - {37, 8, 64, 143, 1}, - {172, 32, 32, 68, 11}, - {143, 16, 33, 10, 4} - }, - { {20, 134, 143, 224, 4}, - {144, 201, 94, 40, 2}, - {32, 127, 22, 18, 8}, - {65, 71, 169, 48, 9} - }, - { {129, 156, 58, 252, 10}, - {72, 112, 151, 122, 14}, - {83, 245, 195, 152, 1}, - {117, 238, 144, 225, 2} - }, - { {69, 204, 88, 112, 14}, - {160, 20, 215, 248, 8}, - {112, 225, 163, 58, 2}, - {17, 254, 178, 128, 5} - }, - { {85, 209, 44, 27, 7}, - {196, 63, 212, 196, 5}, - {237, 131, 72, 186, 10}, - {162, 50, 191, 194, 3} - }, - { {124, 82, 159, 25, 14}, - {210, 253, 237, 148, 0}, - {121, 143, 148, 163, 14}, - {2, 155, 123, 244, 11} - }, - { {112, 145, 54, 145, 1}, - {68, 75, 176, 148, 6}, - {136, 150, 200, 144, 14}, - {98, 144, 221, 34, 2} - }, - { {252, 90, 66, 132, 10}, - {234, 92, 41, 130, 10}, - {82, 20, 37, 163, 15}, - {84, 25, 67, 165, 7} - }, - { {87, 151, 148, 193, 3}, - {196, 155, 24, 253, 2}, - {200, 50, 158, 158, 10}, - {75, 241, 141, 146, 3} - }, - { {165, 209, 196, 248, 2}, - {224, 183, 178, 74, 2}, - {65, 242, 56, 186, 5}, - {69, 36, 222, 208, 7} - }, - { {129, 60, 188, 144, 8}, - {65, 129, 133, 114, 14}, - {16, 147, 211, 200, 1}, - {116, 234, 24, 24, 2} - }, - { {9, 113, 70, 213, 3}, - {111, 87, 128, 76, 2}, - {202, 182, 40, 233, 0}, - {67, 32, 30, 175, 6} - }, - { {106, 207, 210, 216, 5}, - {38, 230, 248, 185, 10}, - {161, 180, 191, 53, 6}, - {89, 209, 246, 118, 4} - }, - { {115, 244, 119, 116, 9}, - {125, 77, 179, 249, 4}, - {146, 238, 226, 252, 14}, - {41, 252, 219, 43, 14} - }, - { {245, 157, 172, 185, 10}, - {192, 187, 183, 230, 14}, - {89, 211, 91, 154, 15}, - {118, 126, 221, 208, 3} - }, - { {202, 249, 118, 232, 4}, - {99, 103, 82, 155, 14}, - {33, 118, 233, 245, 3}, - {125, 148, 174, 108, 6} - }, - { {145, 223, 213, 147, 14}, - {112, 159, 217, 118, 11}, - {124, 154, 191, 184, 9}, - {214, 233, 191, 144, 14} - }, - { {151, 224, 139, 96, 13}, - {149, 204, 87, 75, 0}, - {176, 109, 16, 126, 9}, - {13, 46, 163, 58, 9} - }, - { {171, 24, 195, 41, 7}, - {118, 240, 98, 71, 8}, - {233, 76, 49, 141, 5}, - {30, 36, 96, 246, 14} - }, - { {12, 117, 233, 207, 4}, - {251, 166, 68, 44, 7}, - {47, 57, 122, 227, 0}, - {227, 66, 38, 93, 15} - }, - { {18, 88, 90, 126, 11}, - {108, 124, 135, 25, 9}, - {215, 229, 161, 164, 8}, - {153, 142, 19, 227, 6} - }, - { {226, 61, 82, 49, 9}, - {101, 66, 163, 183, 8}, - {152, 196, 171, 196, 7}, - {30, 220, 84, 42, 6} - }, - { {148, 62, 206, 46, 10}, - {233, 249, 15, 34, 9}, - {87, 71, 55, 194, 9}, - {148, 79, 9, 249, 7} - }, - { {77, 128, 42, 254, 11}, - {142, 112, 151, 200, 7}, - {215, 245, 64, 27, 2}, - {225, 62, 144, 231, 1} - }, - { {99, 222, 29, 74, 1}, - {84, 37, 60, 249, 9}, - {133, 43, 135, 188, 6}, - {153, 243, 202, 66, 10} - }, - { {234, 149, 240, 24, 8}, - {98, 162, 177, 179, 4}, - {17, 128, 250, 149, 7}, - {44, 216, 212, 84, 6} - }, - { {20, 204, 99, 128, 10}, - {176, 92, 17, 32, 14}, - {80, 28, 99, 50, 8}, - {112, 72, 131, 160, 13} - }, - { {200, 176, 49, 75, 15}, - {87, 48, 81, 158, 5}, - {253, 40, 192, 209, 3}, - {167, 152, 160, 206, 10} - }, - { {158, 5, 82, 110, 15}, - {174, 122, 67, 59, 1}, - {247, 100, 170, 7, 9}, - {141, 204, 37, 231, 5} - }, - { {90, 49, 246, 81, 3}, - {103, 219, 128, 157, 4}, - {200, 166, 248, 197, 10}, - {43, 144, 29, 190, 6} - }, - { {2, 64, 141, 174, 9}, - {28, 165, 7, 1, 3}, - {151, 91, 16, 36, 0}, - {200, 14, 10, 83, 8} - }, - { {3, 39, 204, 189, 6}, - {41, 179, 206, 101, 2}, - {107, 211, 62, 76, 0}, - {74, 103, 60, 217, 4} - }, - { {57, 199, 200, 136, 5}, - {38, 174, 124, 96, 2}, - {161, 17, 62, 57, 12}, - {64, 99, 231, 86, 4} - }, - { {29, 241, 95, 61, 5}, - {255, 111, 214, 84, 0}, - {171, 207, 168, 251, 8}, - {2, 166, 191, 111, 15} - }, - { {53, 248, 87, 146, 8}, - {241, 77, 177, 80, 11}, - {20, 158, 161, 250, 12}, - {208, 168, 219, 40, 15} - }, - { {246, 170, 194, 38, 15}, - {173, 216, 123, 131, 9}, - {246, 68, 53, 86, 15}, - {156, 29, 225, 187, 5} - }, - { {231, 224, 91, 154, 9}, - {181, 100, 181, 211, 3}, - {149, 157, 160, 126, 7}, - {204, 186, 210, 106, 13} - }, - { {35, 84, 88, 78, 2}, - {104, 52, 36, 121, 1}, - {71, 33, 162, 172, 4}, - {137, 226, 66, 193, 6} - }, - { {168, 240, 123, 208, 12}, - {115, 68, 245, 26, 6}, - {48, 189, 224, 241, 5}, - {101, 138, 242, 44, 14} - }, - { {31, 123, 242, 62, 3}, - {239, 254, 138, 81, 13}, - {199, 196, 253, 239, 8}, - {184, 165, 23, 255, 7} - }, - { {166, 34, 215, 254, 13}, - {189, 225, 235, 27, 3}, - {183, 254, 180, 70, 5}, - {205, 141, 120, 123, 13} - }, - { {60, 224, 174, 102, 10}, - {139, 221, 55, 8, 5}, - {86, 103, 80, 115, 12}, - {161, 14, 203, 189, 1} - }, - { {157, 234, 252, 246, 9}, - {175, 141, 159, 90, 15}, - {150, 243, 245, 123, 9}, - {245, 175, 155, 31, 5} - }, - { {118, 28, 15, 110, 1}, - {220, 105, 38, 169, 9}, - {135, 111, 3, 134, 14}, - {153, 86, 73, 99, 11} - }, - { {23, 229, 15, 201, 12}, - {145, 111, 85, 109, 2}, - {57, 63, 10, 126, 8}, - {75, 106, 175, 104, 9} - }, - { {107, 215, 2, 212, 12}, - {74, 70, 249, 233, 2}, - {50, 180, 14, 189, 6}, - {73, 121, 246, 37, 2} - }, - { {86, 44, 136, 80, 8}, - {129, 136, 133, 169, 8}, - {16, 161, 19, 70, 10}, - {25, 90, 17, 24, 1} - }, - { {117, 18, 248, 29, 15}, - {236, 184, 237, 212, 4}, - {251, 129, 244, 138, 14}, - {34, 187, 113, 211, 7} - }, - { {126, 225, 73, 21, 0}, - {187, 14, 180, 133, 0}, - {10, 137, 40, 119, 14}, - {10, 18, 215, 13, 13} - }, - { {16, 118, 198, 246, 1}, - {109, 205, 138, 40, 3}, - {134, 246, 54, 224, 8}, - {193, 69, 27, 59, 6} - }, - { {212, 235, 35, 223, 2}, - {153, 126, 152, 142, 15}, - {79, 188, 77, 114, 11}, - {247, 17, 151, 233, 9} - }, - { {40, 207, 171, 134, 10}, - {26, 214, 61, 32, 15}, - {86, 29, 95, 49, 4}, - {240, 75, 198, 181, 8} - }, - { {172, 20, 113, 133, 3}, - {254, 16, 32, 54, 6}, - {202, 24, 226, 131, 5}, - {102, 192, 64, 135, 15} - }, - { {175, 152, 175, 103, 14}, - {218, 209, 119, 79, 13}, - {126, 111, 81, 159, 5}, - {191, 46, 232, 181, 11} - }, - { {193, 158, 114, 157, 1}, - {108, 96, 152, 246, 14}, - {139, 148, 231, 152, 3}, - {118, 241, 144, 99, 6} - }, - { {207, 95, 181, 152, 6}, - {210, 183, 200, 243, 14}, - {97, 154, 223, 175, 3}, - {124, 241, 62, 212, 11} - }, - { {213, 221, 187, 147, 2}, - {208, 222, 148, 246, 15}, - {76, 157, 219, 186, 11}, - {246, 242, 151, 176, 11} - }, - { {79, 134, 37, 109, 3}, - {158, 49, 26, 237, 4}, - {203, 106, 70, 31, 2}, - {43, 117, 136, 199, 9} - }, - { {106, 36, 71, 111, 0}, - {59, 97, 34, 173, 1}, - {15, 110, 34, 69, 6}, - {139, 84, 72, 109, 12} - }, - { {128, 45, 147, 26, 1}, - {21, 226, 128, 50, 9}, - {133, 140, 155, 64, 1}, - {148, 192, 20, 122, 8} - }, - { {29, 205, 131, 180, 9}, - {158, 206, 147, 96, 10}, - {146, 220, 27, 59, 8}, - {80, 108, 151, 55, 9} - }, - { {218, 134, 193, 229, 12}, - {58, 136, 91, 175, 2}, - {58, 120, 54, 21, 11}, - {79, 93, 161, 21, 12} - }, - { {230, 246, 109, 46, 3}, - {253, 53, 62, 163, 5}, - {199, 75, 102, 246, 7}, - {172, 87, 202, 203, 15} - }, - { {61, 137, 16, 149, 7}, - {142, 26, 240, 84, 10}, - {234, 144, 137, 27, 12}, - {82, 160, 245, 135, 1} - }, - { {210, 121, 65, 88, 12}, - {113, 46, 193, 139, 8}, - {49, 168, 41, 228, 11}, - {29, 24, 55, 72, 14} - }, - { {160, 175, 28, 189, 14}, - {9, 51, 255, 54, 10}, - {123, 211, 143, 80, 5}, - {86, 207, 252, 201, 0} - }, - { {1, 87, 110, 34, 13}, - {100, 71, 79, 96, 5}, - {180, 71, 110, 168, 0}, - {160, 111, 46, 34, 6} - }, - { {87, 248, 236, 245, 8}, - {233, 141, 151, 205, 14}, - {26, 243, 113, 254, 10}, - {123, 62, 155, 25, 7} - }, - { {106, 255, 41, 18, 1}, - {87, 6, 188, 161, 13}, - {132, 137, 79, 245, 6}, - {184, 83, 214, 14, 10} - }, - { {251, 33, 174, 202, 0}, - {3, 235, 36, 203, 7}, - {5, 55, 88, 77, 15}, - {237, 50, 77, 124, 0} - }, - { {11, 139, 189, 114, 11}, - {22, 147, 159, 89, 13}, - {212, 235, 221, 29, 0}, - {185, 175, 156, 150, 8} - }, - { {212, 152, 51, 194, 13}, - {212, 72, 81, 154, 15}, - {180, 60, 193, 146, 11}, - {245, 152, 161, 34, 11} - }, - { {225, 75, 0, 230, 14}, - {8, 22, 107, 202, 11}, - {118, 112, 13, 40, 7}, - {213, 61, 102, 129, 0} - }, - { {26, 95, 155, 85, 5}, - {94, 206, 204, 61, 8}, - {170, 173, 159, 165, 8}, - {27, 195, 55, 55, 10} - }, - { {240, 142, 136, 165, 15}, - {12, 152, 127, 166, 10}, - {250, 81, 23, 16, 15}, - {86, 95, 225, 147, 0} - }, - { {167, 153, 98, 167, 6}, - {232, 82, 114, 71, 15}, - {110, 84, 105, 158, 5}, - {254, 36, 228, 161, 7} - }, - { {117, 137, 204, 235, 6}, - {160, 187, 118, 204, 11}, - {109, 115, 57, 26, 14}, - {211, 54, 237, 208, 5} - }, - { {146, 223, 143, 67, 12}, - {80, 207, 93, 47, 9}, - {60, 47, 31, 180, 9}, - {159, 75, 175, 48, 10} - }, - { {174, 86, 155, 223, 2}, - {218, 244, 172, 63, 3}, - {79, 189, 150, 167, 5}, - {207, 195, 82, 245, 11} - }, - { {210, 12, 103, 74, 4}, - {48, 105, 64, 171, 13}, - {37, 46, 99, 4, 11}, - {189, 80, 41, 96, 12} - }, - { {40, 70, 65, 78, 7}, - {62, 52, 104, 40, 1}, - {231, 40, 38, 33, 4}, - {129, 65, 98, 199, 12} - }, - { {108, 201, 54, 127, 1}, - {142, 103, 178, 156, 13}, - {143, 230, 201, 51, 6}, - {179, 148, 222, 103, 1} - }, - { {194, 238, 156, 100, 12}, - {9, 133, 95, 187, 8}, - {50, 99, 151, 116, 3}, - {29, 223, 170, 25, 0} - }, - { {36, 163, 220, 7, 0}, - {169, 131, 60, 20, 1}, - {14, 3, 188, 82, 4}, - {130, 131, 204, 25, 5} - }, - { {41, 169, 53, 183, 11}, - {31, 19, 179, 84, 15}, - {222, 218, 201, 89, 4}, - {242, 172, 220, 143, 8} - }, - { {105, 9, 143, 242, 0}, - {18, 195, 166, 200, 11}, - {4, 255, 25, 9, 6}, - {209, 54, 92, 52, 8} - }, - { {221, 179, 45, 144, 2}, - {211, 27, 156, 194, 6}, - {64, 155, 76, 219, 11}, - {100, 51, 157, 140, 11} - }, - { {17, 24, 73, 168, 13}, - {116, 40, 71, 64, 10}, - {177, 89, 33, 136, 8}, - {80, 46, 33, 66, 14} - }, - { {182, 179, 217, 164, 5}, - {253, 138, 126, 19, 2}, - {162, 89, 188, 214, 13}, - {76, 135, 229, 27, 15} - }, - { {160, 207, 230, 60, 2}, - {40, 247, 186, 34, 12}, - {67, 198, 127, 48, 5}, - {52, 69, 222, 241, 4} - }, - { {55, 59, 166, 59, 8}, - {193, 235, 171, 69, 13}, - {29, 198, 93, 206, 12}, - {186, 45, 93, 120, 3} - }, - { {161, 242, 208, 206, 8}, - {105, 164, 57, 90, 3}, - {23, 48, 180, 248, 5}, - {197, 169, 194, 89, 6} - }, - { {86, 143, 18, 81, 15}, - {132, 90, 217, 189, 8}, - {248, 164, 143, 22, 10}, - {27, 217, 181, 162, 1} - }, - { {194, 231, 12, 94, 10}, - {9, 55, 157, 171, 1}, - {87, 163, 14, 116, 3}, - {141, 91, 158, 201, 0} - }, - { {156, 25, 191, 90, 0}, - {210, 235, 132, 26, 13}, - {5, 175, 217, 131, 9}, - {181, 130, 29, 116, 11} - }, - { {117, 207, 171, 65, 6}, - {144, 222, 124, 236, 12}, - {104, 45, 95, 58, 14}, - {51, 115, 231, 176, 9} - }, - { {126, 204, 41, 109, 0}, - {154, 44, 54, 173, 12}, - {11, 105, 67, 55, 14}, - {59, 86, 195, 69, 9} - }, - { {243, 91, 227, 26, 14}, - {112, 254, 233, 195, 13}, - {117, 140, 125, 172, 15}, - {188, 57, 119, 240, 14} - }, - { {190, 117, 133, 241, 8}, - {211, 143, 163, 47, 2}, - {24, 250, 26, 231, 13}, - {79, 76, 95, 28, 11} - }, - { {33, 154, 217, 93, 10}, - {120, 176, 189, 92, 8}, - {91, 169, 181, 152, 4}, - {19, 171, 208, 209, 14} - }, - { {187, 162, 184, 116, 12}, - {11, 136, 255, 91, 4}, - {50, 225, 212, 93, 13}, - {45, 175, 241, 29, 0} - }, - { {24, 170, 15, 78, 6}, - {27, 121, 92, 8, 9}, - {103, 47, 5, 81, 8}, - {145, 3, 169, 237, 8} - }, - { {141, 163, 255, 65, 12}, - {179, 195, 93, 94, 4}, - {56, 47, 252, 91, 1}, - {39, 171, 172, 60, 13} - }, - { {204, 241, 13, 15, 1}, - {223, 39, 20, 134, 1}, - {143, 11, 8, 243, 3}, - {134, 18, 142, 79, 11} - }, - { {15, 249, 178, 82, 13}, - {199, 198, 209, 89, 13}, - {180, 164, 217, 255, 0}, - {185, 168, 182, 62, 3} - }, - { {1, 159, 5, 220, 10}, - {88, 51, 153, 104, 10}, - {83, 186, 15, 152, 0}, - {81, 105, 156, 193, 10} - }, - { {223, 138, 224, 53, 4}, - {170, 136, 218, 199, 12}, - {42, 192, 117, 31, 11}, - {62, 53, 177, 21, 5} - }, - { {206, 123, 160, 13, 5}, - {207, 166, 72, 135, 12}, - {171, 0, 93, 231, 3}, - {62, 17, 38, 95, 3} - }, - { {217, 125, 237, 88, 6}, - {115, 191, 196, 234, 12}, - {97, 171, 123, 233, 11}, - {53, 114, 63, 220, 14} - }, - { {181, 147, 30, 12, 6}, - {200, 123, 124, 82, 0}, - {99, 7, 140, 154, 13}, - {4, 163, 237, 225, 3} - }, - { {18, 139, 154, 110, 4}, - {8, 234, 94, 25, 9}, - {39, 101, 157, 20, 8}, - {153, 135, 165, 113, 0} - }, - { {242, 22, 60, 166, 3}, - {76, 25, 46, 179, 7}, - {198, 83, 198, 132, 15}, - {236, 215, 73, 131, 2} - }, - { {232, 10, 7, 115, 7}, - {22, 81, 234, 142, 9}, - {236, 238, 5, 1, 7}, - {151, 21, 120, 166, 8} - }, - { {122, 115, 30, 194, 4}, - {67, 79, 108, 153, 3}, - {36, 55, 140, 229, 14}, - {201, 147, 111, 44, 2} - }, - { {53, 218, 126, 240, 11}, - {228, 93, 191, 88, 14}, - {208, 247, 229, 186, 12}, - {113, 175, 219, 162, 7} - }, - { {153, 130, 253, 45, 1}, - {62, 169, 30, 86, 4}, - {139, 75, 244, 25, 9}, - {38, 167, 137, 87, 12} - }, - { {223, 169, 37, 94, 13}, - {159, 43, 209, 203, 13}, - {183, 170, 73, 95, 11}, - {189, 56, 189, 79, 9} - }, - { {165, 30, 214, 188, 15}, - {236, 241, 235, 114, 10}, - {243, 214, 183, 138, 5}, - {84, 237, 120, 243, 7} - }, - { {36, 8, 213, 209, 9}, - {180, 129, 161, 28, 10}, - {152, 186, 177, 2, 4}, - {83, 136, 88, 18, 13} - }, - { {181, 248, 233, 45, 6}, - {249, 188, 118, 70, 12}, - {107, 73, 113, 250, 13}, - {54, 38, 227, 217, 15} - }, - { {145, 54, 188, 78, 0}, - {73, 169, 12, 122, 5}, - {7, 35, 214, 200, 9}, - {165, 227, 9, 89, 2} - }, - { {174, 88, 224, 232, 5}, - {230, 164, 98, 11, 14}, - {161, 112, 113, 167, 5}, - {125, 4, 98, 86, 7} - }, - { {47, 128, 197, 170, 11}, - {182, 177, 51, 65, 3}, - {213, 90, 48, 31, 4}, - {200, 44, 200, 214, 13} - }, - { {218, 73, 123, 17, 9}, - {54, 78, 133, 151, 12}, - {152, 141, 233, 37, 11}, - {62, 154, 23, 38, 12} - }, - { {162, 246, 228, 83, 14}, - {97, 149, 249, 47, 5}, - {124, 162, 118, 244, 5}, - {175, 73, 250, 152, 6} - }, - { {33, 39, 109, 132, 12}, - {57, 3, 109, 96, 6}, - {50, 27, 110, 72, 4}, - {96, 107, 108, 9, 12} - }, - { {107, 121, 40, 104, 12}, - {67, 38, 103, 201, 12}, - {49, 97, 73, 237, 6}, - {57, 62, 102, 76, 2} - }, - { {157, 218, 93, 177, 13}, - {246, 13, 223, 86, 10}, - {184, 219, 165, 187, 9}, - {86, 175, 187, 6, 15} - }, - { {47, 207, 178, 181, 5}, - {142, 198, 250, 117, 14}, - {170, 212, 223, 63, 4}, - {122, 229, 246, 55, 1} - }, - { {175, 32, 71, 30, 8}, - {187, 97, 161, 67, 1}, - {23, 142, 32, 79, 5}, - {140, 40, 88, 109, 13} - }, - { {234, 199, 75, 58, 9}, - {54, 102, 191, 163, 1}, - {149, 205, 46, 53, 7}, - {140, 95, 214, 102, 12} - }, - { {204, 229, 29, 252, 14}, - {155, 55, 215, 186, 2}, - {115, 251, 138, 115, 3}, - {69, 222, 190, 205, 9} - }, - { {97, 48, 133, 50, 1}, - {85, 129, 162, 192, 1}, - {132, 202, 16, 200, 6}, - {128, 52, 88, 26, 10} - }, - { {180, 146, 25, 41, 1}, - {212, 40, 62, 22, 0}, - {137, 73, 132, 146, 13}, - {6, 135, 193, 66, 11} - }, - { {193, 207, 189, 157, 10}, - {24, 183, 157, 246, 14}, - {91, 155, 223, 56, 3}, - {118, 251, 158, 209, 8} - }, - { {8, 221, 26, 54, 6}, - {74, 86, 214, 48, 9}, - {102, 197, 139, 177, 0}, - {144, 198, 182, 165, 2} - }, - { {120, 67, 92, 168, 15}, - {38, 63, 111, 144, 2}, - {241, 83, 172, 33, 14}, - {64, 159, 111, 198, 4} - }, - { {32, 220, 186, 83, 14}, - {64, 212, 245, 60, 13}, - {124, 165, 211, 176, 4}, - {179, 202, 242, 176, 2} - }, - { {227, 132, 165, 236, 6}, - {24, 177, 114, 235, 6}, - {99, 122, 82, 28, 7}, - {109, 116, 232, 209, 8} - }, - { {131, 145, 171, 122, 13}, - {84, 226, 215, 75, 5}, - {181, 237, 88, 156, 1}, - {173, 46, 180, 114, 10} - }, - { {114, 38, 60, 80, 11}, - {5, 25, 173, 185, 4}, - {208, 163, 198, 68, 14}, - {41, 219, 89, 138, 0} - }, - { {242, 174, 11, 171, 12}, - {17, 104, 127, 167, 11}, - {61, 93, 7, 84, 15}, - {222, 95, 225, 104, 8} - }, - { {46, 136, 9, 214, 3}, - {158, 16, 180, 9, 11}, - {198, 185, 1, 23, 4}, - {217, 2, 208, 135, 9} - }, - { {183, 203, 246, 151, 12}, - {168, 207, 249, 87, 15}, - {62, 150, 253, 62, 13}, - {254, 169, 255, 49, 5} - }, - { {49, 251, 244, 57, 14}, - {97, 191, 251, 84, 12}, - {121, 194, 253, 248, 12}, - {50, 173, 255, 216, 6} - }, - { {142, 199, 82, 154, 6}, - {162, 118, 216, 51, 3}, - {101, 148, 174, 55, 1}, - {204, 193, 182, 228, 5} - }, - { {94, 235, 242, 10, 12}, - {163, 238, 89, 145, 13}, - {53, 4, 253, 119, 10}, - {184, 153, 167, 124, 5} - }, - { {227, 59, 129, 212, 12}, - {89, 130, 233, 203, 10}, - {50, 184, 29, 204, 7}, - {93, 57, 116, 25, 10} - }, - { {172, 124, 242, 222, 10}, - {235, 244, 161, 58, 15}, - {87, 180, 243, 227, 5}, - {245, 200, 82, 253, 7} - }, - { {78, 1, 1, 147, 2}, - {146, 18, 128, 133, 3}, - {76, 152, 8, 7, 2}, - {202, 16, 20, 132, 9} - }, - { {201, 144, 131, 96, 5}, - {86, 192, 82, 202, 0}, - {160, 108, 16, 153, 3}, - {5, 52, 160, 54, 10} - }, - { {245, 102, 105, 160, 13}, - {181, 12, 111, 226, 6}, - {176, 89, 102, 106, 15}, - {100, 127, 99, 10, 13} - }, - { {44, 3, 150, 212, 14}, - {138, 211, 233, 24, 2}, - {114, 182, 156, 3, 4}, - {65, 137, 124, 181, 1} - }, - { {118, 193, 117, 204, 7}, - {188, 63, 112, 153, 6}, - {227, 58, 232, 54, 14}, - {105, 144, 239, 195, 13} - }, - { {176, 251, 83, 107, 5}, - {117, 110, 122, 30, 9}, - {173, 108, 173, 240, 13}, - {151, 133, 231, 106, 14} - }, - { {135, 53, 206, 169, 8}, - {225, 227, 7, 103, 2}, - {25, 87, 58, 206, 1}, - {78, 110, 12, 120, 7} - }, - { {231, 139, 244, 106, 11}, - {164, 179, 59, 219, 13}, - {213, 98, 253, 30, 7}, - {189, 189, 204, 210, 5} - }, - { {185, 178, 1, 226, 1}, - {87, 8, 58, 74, 3}, - {132, 120, 4, 217, 13}, - {197, 37, 193, 14, 10} - }, - { {44, 230, 244, 199, 9}, - {175, 133, 57, 60, 7}, - {158, 50, 246, 115, 4}, - {227, 201, 202, 31, 5} - }, - { {240, 26, 58, 172, 4}, - {72, 104, 110, 146, 14}, - {35, 85, 197, 128, 15}, - {116, 151, 97, 97, 2} - }, - { {107, 29, 238, 143, 4}, - {106, 227, 100, 229, 15}, - {47, 23, 123, 141, 6}, - {250, 114, 108, 117, 6} - }, - { {10, 99, 89, 22, 11}, - {63, 22, 141, 17, 1}, - {214, 137, 172, 101, 0}, - {136, 139, 22, 143, 12} - }, - { {61, 80, 237, 142, 0}, - {250, 173, 36, 64, 7}, - {7, 27, 112, 171, 12}, - {224, 34, 75, 85, 15} - }, - { {12, 234, 137, 42, 4}, - {147, 164, 94, 0, 9}, - {37, 73, 21, 115, 0}, - {144, 7, 162, 92, 9} - }, - { {146, 159, 44, 154, 3}, - {68, 59, 156, 35, 15}, - {197, 147, 79, 148, 9}, - {252, 67, 157, 194, 2} - }, - { {144, 185, 171, 44, 8}, - {89, 234, 23, 2, 12}, - {19, 77, 89, 208, 9}, - {52, 14, 133, 121, 10} - }, - { {96, 223, 140, 227, 4}, - {64, 135, 126, 172, 11}, - {44, 115, 31, 176, 6}, - {211, 87, 238, 16, 2} - }, - { {211, 163, 99, 9, 11}, - {53, 122, 25, 199, 4}, - {217, 12, 108, 92, 11}, - {46, 57, 133, 234, 12} - }, - { {37, 61, 159, 195, 3}, - {213, 211, 36, 124, 11}, - {204, 63, 155, 202, 4}, - {211, 226, 76, 186, 11} - }, - { {48, 25, 30, 15, 13}, - {76, 107, 101, 20, 9}, - {191, 7, 137, 128, 12}, - {146, 138, 109, 99, 2} - }, - { {90, 151, 28, 16, 1}, - {70, 11, 156, 177, 0}, - {128, 131, 142, 149, 10}, - {8, 211, 157, 6, 2} - }, - { {42, 15, 107, 51, 10}, - {50, 82, 175, 37, 13}, - {92, 205, 111, 5, 4}, - {186, 79, 84, 164, 12} - }, - { {106, 117, 209, 236, 0}, - {123, 166, 34, 185, 2}, - {3, 120, 186, 229, 6}, - {73, 212, 70, 93, 14} - }, - { {30, 157, 187, 139, 1}, - {214, 234, 20, 53, 15}, - {141, 29, 219, 151, 8}, - {250, 194, 133, 118, 11} - }, - { {47, 58, 225, 220, 9}, - {255, 160, 169, 73, 14}, - {147, 184, 117, 207, 4}, - {121, 41, 80, 95, 15} - }, - { {99, 208, 151, 52, 7}, - {92, 213, 242, 209, 0}, - {226, 206, 144, 188, 6}, - {8, 180, 250, 179, 10} - }, - { {216, 157, 137, 67, 7}, - {86, 154, 84, 174, 9}, - {236, 41, 27, 145, 11}, - {151, 82, 165, 150, 10} - }, - { {131, 189, 136, 80, 11}, - {69, 146, 149, 107, 8}, - {208, 161, 27, 220, 1}, - {29, 106, 148, 154, 2} - }, - { {34, 78, 125, 138, 14}, - {48, 53, 109, 49, 15}, - {117, 27, 231, 36, 4}, - {248, 203, 106, 192, 12} - }, - { {121, 75, 63, 158, 10}, - {26, 127, 173, 208, 15}, - {87, 159, 205, 41, 14}, - {240, 187, 95, 229, 8} - }, - { {141, 51, 167, 110, 15}, - {223, 243, 75, 74, 5}, - {247, 110, 92, 203, 1}, - {165, 45, 44, 255, 11} - }, - { {33, 133, 188, 205, 7}, - {12, 179, 116, 124, 6}, - {235, 51, 218, 24, 4}, - {99, 226, 236, 211, 0} - }, - { {240, 218, 227, 80, 3}, - {116, 220, 184, 138, 12}, - {192, 172, 117, 176, 15}, - {53, 17, 211, 178, 14} - }, - { {204, 223, 42, 42, 8}, - {194, 102, 31, 162, 13}, - {21, 69, 79, 179, 3}, - {180, 95, 134, 100, 3} - }, - { {56, 130, 84, 157, 1}, - {46, 41, 184, 20, 2}, - {139, 146, 164, 17, 12}, - {66, 129, 217, 71, 4} - }, - { {110, 45, 44, 91, 14}, - {131, 51, 229, 173, 13}, - {125, 163, 75, 71, 6}, - {187, 90, 124, 204, 1} - }, - { {122, 43, 200, 47, 2}, - {43, 186, 46, 133, 9}, - {79, 65, 61, 69, 14}, - {154, 23, 69, 221, 4} - }, - { {87, 219, 131, 112, 8}, - {208, 206, 155, 201, 8}, - {16, 236, 29, 190, 10}, - {25, 61, 151, 48, 11} - }, - { {201, 217, 206, 10, 6}, - {98, 247, 84, 194, 9}, - {101, 7, 57, 185, 3}, - {148, 50, 174, 244, 6} - }, - { {140, 81, 7, 168, 12}, - {210, 103, 67, 2, 2}, - {49, 94, 8, 163, 1}, - {68, 12, 46, 100, 11} - }, - { {189, 143, 171, 242, 11}, - {150, 218, 191, 106, 15}, - {212, 253, 95, 27, 13}, - {245, 111, 213, 182, 9} - }, - { {100, 133, 26, 10, 13}, - {132, 98, 117, 176, 1}, - {181, 5, 138, 18, 6}, - {128, 218, 228, 98, 1} - }, - { {220, 109, 58, 176, 4}, - {131, 78, 198, 178, 14}, - {32, 213, 203, 99, 11}, - {116, 214, 55, 44, 1} - }, - { {231, 251, 25, 168, 3}, - {213, 54, 62, 211, 10}, - {193, 89, 141, 254, 7}, - {92, 183, 198, 202, 11} - }, - { {177, 204, 84, 80, 5}, - {36, 13, 240, 122, 8}, - {160, 162, 163, 56, 13}, - {21, 224, 251, 2, 4} - }, - { {97, 110, 245, 148, 7}, - {61, 149, 232, 240, 14}, - {226, 154, 247, 104, 6}, - {112, 241, 122, 155, 12} - }, - { {147, 83, 11, 252, 1}, - {92, 110, 142, 75, 2}, - {131, 253, 12, 172, 9}, - {77, 39, 23, 99, 10} - }, - { {27, 41, 1, 82, 8}, - {19, 10, 129, 73, 9}, - {20, 168, 9, 77, 8}, - {153, 40, 21, 12, 8} - }, - { {197, 245, 7, 104, 1}, - {213, 103, 18, 234, 0}, - {129, 110, 10, 250, 3}, - {5, 116, 142, 106, 11} - }, - { {52, 186, 247, 153, 2}, - {241, 249, 184, 20, 14}, - {73, 158, 245, 210, 12}, - {114, 129, 217, 248, 15} - }, - { {169, 176, 189, 187, 13}, - {87, 161, 247, 86, 7}, - {189, 219, 208, 217, 5}, - {230, 174, 248, 94, 10} - }, - { {149, 57, 193, 12, 5}, - {253, 170, 64, 66, 8}, - {163, 8, 57, 202, 9}, - {20, 32, 37, 91, 15} - }, - { {50, 223, 204, 191, 2}, - {104, 191, 190, 37, 11}, - {79, 211, 63, 180, 12}, - {218, 71, 223, 209, 6} - }, - { {237, 234, 172, 111, 7}, - {143, 181, 126, 206, 13}, - {239, 99, 85, 123, 7}, - {183, 55, 234, 223, 1} - }, - { {77, 86, 15, 172, 10}, - {218, 117, 15, 224, 2}, - {83, 95, 6, 171, 2}, - {64, 127, 10, 229, 11} - }, - { {116, 172, 190, 43, 0}, - {129, 233, 54, 180, 13}, - {13, 71, 211, 82, 14}, - {178, 214, 201, 120, 1} - }, - { {35, 182, 120, 236, 5}, - {109, 32, 126, 121, 6}, - {163, 113, 230, 220, 4}, - {105, 231, 224, 75, 6} - }, - { {232, 210, 137, 53, 1}, - {94, 132, 190, 134, 0}, - {138, 201, 20, 177, 7}, - {6, 23, 210, 23, 10} - }, - { {235, 150, 185, 93, 6}, - {90, 176, 252, 255, 4}, - {107, 169, 214, 157, 7}, - {47, 243, 240, 213, 10} - }, - { {85, 145, 91, 105, 0}, - {240, 106, 22, 220, 0}, - {9, 109, 168, 154, 10}, - {3, 182, 133, 96, 15} - }, - { {40, 186, 189, 218, 6}, - {83, 177, 252, 24, 15}, - {101, 187, 213, 209, 4}, - {241, 131, 248, 220, 10} - }, - { {35, 163, 75, 130, 11}, - {53, 82, 61, 65, 3}, - {212, 29, 44, 92, 4}, - {200, 43, 196, 170, 12} - }, - { {193, 172, 20, 84, 10}, - {9, 17, 145, 250, 8}, - {82, 162, 131, 88, 3}, - {21, 248, 152, 137, 0} - }, - { {100, 182, 175, 131, 2}, - {209, 209, 60, 164, 7}, - {76, 31, 86, 210, 6}, - {226, 83, 200, 184, 11} - }, - { {82, 25, 155, 54, 1}, - {92, 202, 134, 145, 9}, - {134, 205, 153, 132, 10}, - {152, 150, 21, 51, 10} - }, - { {99, 183, 61, 54, 4}, - {89, 3, 254, 241, 5}, - {38, 203, 206, 220, 6}, - {168, 247, 252, 9, 10} - }, - { {192, 85, 112, 85, 8}, - {104, 6, 129, 190, 4}, - {26, 160, 234, 160, 3}, - {39, 216, 22, 1, 6} - }, - { {52, 181, 171, 169, 13}, - {213, 234, 119, 36, 6}, - {185, 93, 90, 210, 12}, - {98, 78, 229, 122, 11} - }, - { {4, 214, 173, 116, 7}, - {220, 149, 222, 40, 4}, - {226, 235, 86, 178, 0}, - {33, 71, 186, 147, 11} - }, - { {58, 185, 185, 47, 15}, - {95, 186, 119, 21, 13}, - {255, 73, 217, 213, 12}, - {186, 142, 229, 223, 10} - }, - { {30, 193, 204, 115, 3}, - {166, 159, 150, 13, 1}, - {204, 227, 56, 55, 8}, - {139, 6, 159, 150, 5} - }, - { {227, 95, 151, 12, 12}, - {88, 231, 105, 243, 8}, - {51, 14, 159, 172, 7}, - {28, 249, 110, 113, 10} - }, - { {82, 233, 202, 244, 7}, - {45, 222, 214, 137, 10}, - {226, 245, 57, 116, 10}, - {89, 22, 183, 187, 4} - }, - { {171, 99, 139, 22, 6}, - {27, 214, 236, 67, 1}, - {102, 141, 28, 109, 5}, - {140, 35, 118, 189, 8} - }, - { {138, 229, 208, 83, 7}, - {39, 150, 208, 63, 1}, - {236, 160, 186, 117, 1}, - {143, 192, 182, 158, 4} - }, - { {103, 173, 81, 43, 1}, - {181, 34, 50, 245, 9}, - {141, 72, 171, 94, 6}, - {154, 244, 196, 74, 13} - }, - { {179, 24, 122, 4, 11}, - {108, 88, 37, 83, 12}, - {210, 5, 225, 140, 13}, - {60, 170, 65, 163, 6} - }, - { {60, 198, 137, 195, 8}, - {146, 140, 61, 44, 3}, - {28, 57, 22, 51, 12}, - {195, 75, 195, 20, 9} - }, - { {250, 116, 11, 175, 7}, - {95, 124, 102, 167, 3}, - {239, 93, 2, 229, 15}, - {206, 86, 99, 239, 10} - }, - { {4, 250, 221, 72, 11}, - {245, 181, 29, 24, 8}, - {209, 43, 181, 242, 0}, - {17, 139, 138, 218, 15} - }, - { {153, 138, 132, 196, 12}, - {10, 137, 89, 74, 10}, - {50, 50, 21, 25, 9}, - {85, 41, 169, 21, 0} - }, - { {63, 252, 218, 241, 1}, - {231, 204, 182, 125, 10}, - {136, 245, 179, 255, 12}, - {91, 230, 211, 62, 7} - }, - { {148, 250, 34, 140, 9}, - {205, 108, 25, 2, 14}, - {147, 20, 69, 242, 9}, - {116, 9, 131, 107, 3} - }, - { {228, 237, 60, 145, 13}, - {133, 7, 245, 182, 14}, - {184, 147, 203, 114, 7}, - {118, 218, 254, 10, 1} - }, - { {3, 55, 138, 20, 12}, - {73, 194, 205, 97, 0}, - {50, 133, 30, 204, 0}, - {8, 107, 52, 57, 2} - }, - { {216, 27, 34, 208, 1}, - {70, 74, 136, 138, 14}, - {128, 180, 77, 129, 11}, - {117, 17, 21, 38, 2} - }, - { {25, 91, 18, 85, 10}, - {74, 94, 137, 92, 8}, - {90, 164, 141, 169, 8}, - {19, 169, 23, 165, 2} - }, - { {33, 72, 120, 182, 2}, - {40, 20, 166, 80, 15}, - {70, 209, 225, 40, 4}, - {240, 166, 82, 129, 4} - }, - { {40, 93, 236, 19, 2}, - {98, 151, 164, 36, 13}, - {76, 131, 123, 161, 4}, - {178, 66, 94, 148, 6} - }, - { {123, 100, 52, 77, 7}, - {15, 61, 96, 253, 4}, - {235, 34, 194, 109, 14}, - {43, 240, 107, 207, 0} - }, - { {98, 75, 151, 138, 7}, - {20, 247, 104, 145, 11}, - {229, 30, 157, 36, 6}, - {216, 145, 110, 242, 8} - }, - { {103, 119, 188, 11, 1}, - {197, 167, 44, 245, 5}, - {141, 3, 222, 238, 6}, - {170, 243, 78, 90, 3} - }, - { {144, 144, 113, 245, 10}, - {120, 24, 147, 30, 6}, - {90, 248, 224, 144, 9}, - {103, 140, 145, 129, 14} - }, - { {37, 206, 233, 123, 13}, - {180, 164, 255, 108, 13}, - {189, 233, 119, 58, 4}, - {179, 111, 242, 82, 13} - }, - { {253, 83, 136, 111, 13}, - {206, 174, 111, 206, 1}, - {191, 97, 28, 171, 15}, - {135, 63, 103, 87, 3} - }, - { {231, 105, 53, 36, 11}, - {157, 23, 35, 211, 12}, - {210, 74, 201, 110, 7}, - {60, 188, 78, 139, 9} - }, - { {24, 201, 137, 127, 9}, - {30, 174, 151, 12, 9}, - {159, 233, 25, 49, 8}, - {147, 14, 151, 87, 8} - }, - { {101, 96, 64, 176, 10}, - {161, 20, 163, 192, 2}, - {80, 208, 32, 106, 6}, - {64, 60, 82, 136, 5} - }, - { {56, 46, 157, 194, 13}, - {23, 137, 109, 56, 11}, - {180, 59, 151, 65, 12}, - {209, 203, 105, 30, 8} - }, - { {205, 242, 177, 37, 13}, - {223, 132, 91, 214, 4}, - {186, 72, 212, 251, 3}, - {38, 189, 162, 31, 11} - }, - { {9, 78, 179, 61, 6}, - {26, 244, 202, 116, 12}, - {107, 204, 215, 41, 0}, - {50, 229, 50, 245, 8} - }, - { {57, 223, 214, 61, 1}, - {110, 239, 186, 116, 8}, - {139, 198, 191, 185, 12}, - {18, 229, 223, 119, 6} - }, - { {249, 54, 192, 90, 7}, - {103, 184, 232, 234, 1}, - {229, 160, 54, 201, 15}, - {133, 113, 113, 222, 6} - }, - { {119, 243, 69, 1, 5}, - {245, 15, 120, 197, 0}, - {168, 10, 44, 254, 14}, - {10, 49, 239, 10, 15} - }, - { {229, 170, 108, 152, 3}, - {165, 49, 188, 194, 14}, - {193, 147, 101, 90, 7}, - {116, 51, 216, 202, 5} - }, - { {176, 232, 167, 19, 14}, - {17, 221, 241, 6, 13}, - {124, 142, 81, 112, 13}, - {182, 8, 251, 184, 8} - }, - { {221, 219, 120, 40, 6}, - {226, 62, 94, 210, 12}, - {97, 65, 237, 187, 11}, - {52, 183, 167, 196, 7} - }, - { {59, 9, 157, 107, 2}, - {18, 187, 38, 93, 9}, - {77, 107, 153, 13, 12}, - {155, 166, 77, 212, 8} - }, - { {64, 234, 217, 35, 3}, - {53, 148, 30, 148, 9}, - {204, 73, 181, 112, 2}, - {146, 151, 130, 154, 12} - }, - { {26, 247, 252, 44, 4}, - {107, 175, 94, 49, 4}, - {35, 67, 254, 245, 8}, - {40, 199, 175, 93, 6} - }, - { {60, 154, 211, 10, 11}, - {246, 248, 57, 16, 9}, - {213, 12, 181, 147, 12}, - {144, 137, 193, 246, 15} - }, - { {179, 6, 81, 36, 2}, - {56, 24, 42, 115, 0}, - {66, 72, 166, 12, 13}, - {12, 229, 65, 129, 12} - }, - { {50, 189, 243, 128, 9}, - {117, 202, 49, 49, 14}, - {144, 28, 251, 212, 12}, - {120, 200, 197, 58, 14} - }, - { {108, 225, 50, 82, 6}, - {131, 86, 240, 152, 5}, - {100, 164, 200, 115, 6}, - {161, 144, 246, 172, 1} - }, - { {87, 93, 59, 77, 8}, - {216, 110, 5, 253, 12}, - {27, 45, 203, 174, 10}, - {59, 250, 7, 97, 11} - }, - { {50, 250, 120, 150, 11}, - {109, 28, 189, 17, 15}, - {214, 145, 229, 244, 12}, - {248, 139, 211, 139, 6} - }, - { {201, 37, 74, 207, 11}, - {47, 114, 5, 238, 3}, - {223, 53, 42, 73, 3}, - {199, 122, 4, 239, 4} - }, - { {91, 227, 88, 153, 8}, - {35, 46, 157, 213, 2}, - {25, 145, 172, 125, 10}, - {74, 187, 151, 76, 4} - }, - { {162, 249, 225, 35, 2}, - {113, 150, 50, 7, 13}, - {76, 72, 121, 244, 5}, - {190, 4, 198, 152, 14} - }, - { {119, 181, 82, 205, 9}, - {237, 106, 49, 253, 2}, - {155, 52, 170, 222, 14}, - {75, 248, 197, 107, 7} - }, - { {176, 219, 205, 212, 10}, - {120, 159, 189, 10, 10}, - {82, 187, 61, 176, 13}, - {85, 11, 223, 145, 14} - }, - { {203, 147, 21, 157, 14}, - {90, 51, 217, 215, 2}, - {123, 154, 140, 157, 3}, - {78, 185, 188, 197, 10} - }, - { {126, 39, 103, 62, 11}, - {191, 123, 171, 161, 5}, - {215, 206, 110, 71, 14}, - {168, 93, 93, 239, 13} - }, - { {27, 68, 157, 3, 7}, - {22, 157, 68, 117, 1}, - {236, 11, 146, 45, 8}, - {138, 226, 43, 150, 8} - }, - { {214, 213, 57, 224, 1}, - {212, 14, 22, 187, 6}, - {128, 121, 202, 182, 11}, - {109, 214, 135, 2, 11} - }, - { {153, 17, 59, 93, 5}, - {94, 106, 196, 94, 4}, - {171, 173, 200, 137, 9}, - {39, 162, 53, 103, 10} - }, - { {237, 201, 222, 175, 0}, - {170, 231, 54, 214, 11}, - {15, 87, 185, 59, 7}, - {214, 182, 206, 117, 5} - }, - { {152, 79, 114, 58, 0}, - {34, 110, 138, 50, 13}, - {5, 196, 239, 33, 9}, - {180, 197, 23, 100, 4} - }, - { {146, 177, 194, 94, 11}, - {109, 250, 145, 11, 1}, - {215, 164, 56, 212, 9}, - {141, 8, 149, 251, 6} - }, - { {233, 83, 55, 170, 15}, - {86, 119, 107, 210, 7}, - {245, 94, 204, 169, 7}, - {228, 189, 110, 230, 10} - }, - { {184, 249, 145, 224, 11}, - {87, 158, 51, 26, 10}, - {208, 120, 153, 241, 13}, - {85, 140, 199, 158, 10} - }, - { {54, 190, 41, 6, 4}, - {217, 8, 124, 33, 13}, - {38, 9, 71, 214, 12}, - {184, 67, 225, 9, 11} - }, - { {109, 88, 75, 223, 15}, - {254, 116, 229, 204, 11}, - {255, 189, 33, 171, 6}, - {211, 58, 114, 231, 15} - }, - { {124, 73, 255, 32, 11}, - {182, 223, 39, 144, 12}, - {208, 79, 249, 35, 14}, - {48, 158, 79, 182, 13} - }, - { {204, 4, 237, 38, 9}, - {190, 129, 7, 162, 5}, - {150, 75, 114, 3, 3}, - {164, 94, 8, 23, 13} - }, - { {126, 191, 195, 88, 12}, - {243, 234, 249, 169, 8}, - {49, 172, 63, 215, 14}, - {25, 89, 245, 124, 15} - }, - { {35, 71, 74, 52, 11}, - {44, 86, 175, 97, 0}, - {210, 197, 46, 44, 4}, - {8, 111, 86, 163, 4} - }, - { {247, 2, 101, 153, 14}, - {176, 57, 233, 199, 6}, - {121, 154, 100, 14, 15}, - {110, 57, 121, 192, 13} - }, - { {183, 134, 11, 177, 9}, - {148, 72, 191, 103, 2}, - {152, 221, 6, 30, 13}, - {78, 111, 209, 34, 9} - }, - { {29, 237, 84, 211, 10}, - {163, 31, 145, 124, 11}, - {92, 178, 171, 123, 8}, - {211, 232, 159, 140, 5} - }, - { {19, 129, 28, 244, 14}, - {8, 27, 215, 89, 2}, - {114, 243, 136, 28, 8}, - {73, 174, 189, 129, 0} - }, - { {38, 136, 183, 166, 7}, - {156, 209, 114, 17, 15}, - {230, 94, 209, 22, 4}, - {248, 132, 232, 179, 9} - }, - { {218, 189, 5, 242, 12}, - {83, 11, 211, 171, 11}, - {52, 250, 11, 213, 11}, - {221, 92, 189, 12, 10} - }, - { {179, 196, 25, 242, 6}, - {16, 28, 246, 123, 3}, - {100, 249, 130, 60, 13}, - {205, 230, 243, 128, 8} - }, - { {3, 88, 126, 158, 12}, - {104, 101, 197, 81, 15}, - {55, 151, 225, 172, 0}, - {248, 170, 58, 97, 6} - }, - { {138, 226, 81, 223, 4}, - {59, 36, 216, 31, 3}, - {47, 184, 164, 117, 1}, - {207, 129, 178, 77, 12} - }, - { {116, 44, 226, 207, 6}, - {169, 248, 96, 172, 15}, - {111, 52, 115, 66, 14}, - {243, 80, 97, 249, 5} - }, - { {80, 87, 235, 180, 4}, - {120, 206, 206, 160, 6}, - {34, 221, 126, 160, 10}, - {96, 87, 55, 49, 14} - }, - { {234, 122, 109, 152, 13}, - {119, 37, 237, 131, 14}, - {177, 155, 101, 229, 7}, - {124, 27, 122, 78, 14} - }, - { {81, 124, 23, 242, 2}, - {81, 93, 130, 248, 11}, - {68, 254, 131, 232, 10}, - {209, 244, 27, 168, 10} - }, - { {116, 206, 76, 238, 0}, - {168, 45, 62, 168, 11}, - {7, 115, 39, 50, 14}, - {209, 87, 203, 65, 5} - }, - { {235, 112, 71, 178, 6}, - {115, 85, 226, 195, 3}, - {100, 222, 32, 237, 7}, - {204, 52, 122, 172, 14} - }, - { {44, 21, 121, 54, 0}, - {250, 2, 166, 48, 5}, - {6, 201, 234, 131, 4}, - {160, 198, 84, 5, 15} - }, - { {10, 102, 96, 115, 1}, - {39, 4, 138, 45, 5}, - {140, 224, 102, 101, 0}, - {171, 69, 18, 14, 4} - }, - { {15, 250, 22, 84, 3}, - {207, 85, 152, 89, 8}, - {194, 166, 133, 255, 0}, - {25, 161, 154, 175, 3} - }, - { {248, 13, 152, 242, 6}, - {2, 154, 230, 186, 11}, - {100, 241, 155, 1, 15}, - {213, 214, 117, 148, 0} - }, - { {17, 81, 213, 137, 2}, - {112, 191, 0, 84, 2}, - {73, 26, 184, 168, 8}, - {66, 160, 15, 208, 14} - }, - { {90, 127, 72, 72, 2}, - {99, 62, 12, 169, 8}, - {65, 33, 47, 229, 10}, - {25, 83, 7, 204, 6} - }, - { {220, 246, 137, 159, 6}, - {219, 188, 220, 166, 3}, - {111, 153, 22, 243, 11}, - {198, 83, 179, 221, 11} - }, - { {142, 116, 239, 41, 6}, - {243, 245, 70, 39, 4}, - {105, 79, 114, 231, 1}, - {46, 70, 42, 252, 15} - }, - { {122, 205, 165, 235, 5}, - {22, 175, 114, 173, 15}, - {173, 122, 91, 53, 14}, - {251, 84, 239, 86, 8} - }, - { {73, 74, 158, 70, 15}, - {14, 213, 77, 216, 9}, - {246, 39, 149, 41, 2}, - {145, 187, 42, 183, 0} - }, - { {92, 188, 59, 49, 3}, - {215, 88, 150, 180, 12}, - {200, 205, 195, 211, 10}, - {50, 214, 145, 174, 11} - }, - { {24, 172, 214, 61, 15}, - {47, 249, 211, 52, 8}, - {251, 198, 179, 81, 8}, - {18, 204, 185, 255, 4} - }, - { {128, 152, 198, 222, 6}, - {104, 241, 208, 10, 11}, - {103, 182, 49, 144, 1}, - {213, 0, 184, 241, 6} - }, - { {171, 158, 93, 173, 10}, - {122, 49, 63, 119, 10}, - {91, 91, 167, 157, 5}, - {94, 239, 200, 197, 14} - }, - { {187, 215, 181, 242, 15}, - {86, 159, 251, 123, 7}, - {244, 250, 222, 189, 13}, - {237, 237, 255, 150, 10} - }, - { {231, 64, 19, 227, 5}, - {148, 68, 98, 223, 3}, - {172, 124, 128, 46, 7}, - {207, 180, 98, 34, 9} - }, - { {185, 180, 142, 161, 12}, - {67, 201, 119, 102, 2}, - {56, 87, 18, 217, 13}, - {70, 110, 233, 60, 2} - }, - { {45, 174, 203, 210, 6}, - {179, 208, 252, 104, 11}, - {100, 189, 55, 91, 4}, - {209, 99, 240, 188, 13} - }, - { {14, 251, 136, 89, 3}, - {199, 182, 156, 13, 8}, - {201, 161, 29, 247, 0}, - {27, 3, 150, 222, 3} - }, - { {140, 253, 170, 193, 1}, - {199, 198, 20, 46, 14}, - {136, 53, 91, 243, 1}, - {119, 66, 134, 62, 3} - }, - { {80, 221, 255, 96, 6}, - {112, 223, 86, 184, 12}, - {96, 111, 251, 176, 10}, - {49, 214, 175, 176, 14} - }, - { {171, 67, 201, 91, 13}, - {54, 166, 237, 79, 1}, - {189, 169, 60, 45, 5}, - {143, 43, 118, 86, 12} - }, - { {80, 242, 159, 163, 14}, - {81, 221, 95, 148, 3}, - {124, 95, 148, 240, 10}, - {194, 159, 171, 184, 10} - }, - { {128, 166, 71, 246, 14}, - {57, 81, 219, 42, 3}, - {118, 254, 38, 80, 1}, - {197, 77, 184, 169, 12} - }, - { {60, 168, 157, 27, 10}, - {147, 185, 181, 20, 9}, - {93, 139, 145, 83, 12}, - {146, 138, 217, 220, 9} - }, - { {24, 77, 221, 97, 1}, - {54, 143, 6, 60, 8}, - {136, 107, 187, 33, 8}, - {19, 198, 15, 22, 12} - }, - { {83, 127, 71, 115, 5}, - {117, 79, 202, 237, 9}, - {172, 238, 47, 236, 10}, - {155, 117, 63, 42, 14} - }, - { {164, 149, 152, 181, 4}, - {200, 130, 246, 54, 2}, - {42, 209, 154, 146, 5}, - {70, 198, 244, 17, 3} - }, - { {58, 161, 233, 220, 1}, - {63, 170, 180, 9, 6}, - {131, 185, 120, 85, 12}, - {105, 2, 213, 95, 12} - }, - { {72, 193, 6, 108, 11}, - {14, 119, 19, 136, 0}, - {211, 102, 8, 49, 2}, - {1, 28, 142, 231, 0} - }, - { {227, 219, 6, 53, 1}, - {76, 71, 186, 199, 8}, - {138, 198, 13, 188, 7}, - {30, 53, 222, 35, 2} - }, - { {26, 241, 58, 17, 0}, - {67, 78, 148, 21, 4}, - {8, 133, 200, 245, 8}, - {42, 130, 151, 44, 2} - }, - { {137, 31, 189, 213, 1}, - {94, 131, 140, 126, 14}, - {138, 187, 223, 137, 1}, - {119, 227, 28, 23, 10} - }, - { {142, 68, 6, 133, 7}, - {142, 85, 64, 39, 2}, - {234, 22, 2, 39, 1}, - {78, 64, 42, 167, 1} - }, - { {135, 127, 42, 107, 5}, - {197, 102, 78, 111, 13}, - {173, 101, 79, 238, 1}, - {191, 103, 38, 106, 3} - }, - { {122, 152, 134, 35, 6}, - {66, 217, 114, 133, 9}, - {108, 70, 17, 149, 14}, - {154, 20, 233, 180, 2} - }, - { {228, 197, 141, 119, 10}, - {152, 151, 183, 174, 1}, - {94, 235, 26, 50, 7}, - {135, 94, 222, 145, 9} - }, - { {70, 156, 46, 177, 8}, - {192, 65, 151, 165, 14}, - {24, 215, 67, 150, 2}, - {122, 94, 152, 32, 3} - }, - { {165, 170, 62, 63, 8}, - {137, 97, 191, 86, 13}, - {31, 199, 197, 90, 5}, - {182, 175, 216, 105, 1} - }, - { {14, 105, 46, 93, 5}, - {143, 103, 196, 13, 12}, - {171, 167, 73, 103, 0}, - {59, 2, 62, 111, 1} - }, - { {6, 57, 165, 147, 9}, - {213, 131, 129, 5, 15}, - {156, 154, 89, 198, 0}, - {250, 8, 28, 26, 11} - }, - { {96, 63, 93, 167, 6}, - {121, 19, 110, 180, 11}, - {110, 91, 175, 192, 6}, - {210, 215, 108, 137, 14} - }, - { {72, 219, 238, 150, 9}, - {110, 199, 157, 128, 15}, - {150, 151, 125, 177, 2}, - {240, 27, 158, 55, 6} - }, - { {95, 215, 109, 233, 5}, - {246, 47, 94, 237, 6}, - {169, 123, 110, 191, 10}, - {107, 119, 175, 70, 15} - }, - { {101, 185, 180, 188, 6}, - {201, 179, 242, 208, 14}, - {99, 210, 217, 218, 6}, - {112, 180, 252, 217, 3} - }, - { {6, 179, 0, 213, 15}, - {205, 18, 217, 13, 2}, - {250, 176, 12, 214, 0}, - {75, 9, 180, 139, 3} - }, - { {76, 98, 251, 222, 11}, - {191, 244, 141, 152, 7}, - {215, 189, 244, 99, 2}, - {225, 155, 18, 255, 13} - }, - { {154, 177, 225, 193, 15}, - {119, 154, 81, 15, 6}, - {248, 56, 120, 213, 9}, - {111, 8, 165, 158, 14} - }, - { {116, 98, 52, 56, 13}, - {133, 45, 235, 144, 4}, - {177, 194, 196, 98, 14}, - {32, 157, 123, 74, 1} - }, - { {229, 34, 138, 114, 8}, - {129, 192, 175, 202, 1}, - {20, 229, 20, 74, 7}, - {133, 63, 80, 56, 1} - }, - { {20, 217, 9, 82, 15}, - {212, 30, 213, 8, 9}, - {244, 169, 9, 178, 8}, - {145, 10, 183, 130, 11} - }, - { {218, 24, 89, 233, 7}, - {118, 56, 70, 159, 10}, - {233, 121, 161, 133, 11}, - {95, 150, 33, 198, 14} - }, - { {38, 80, 144, 16, 9}, - {196, 132, 161, 17, 0}, - {144, 128, 144, 166, 4}, - {8, 136, 82, 18, 3} - }, - { {25, 235, 51, 121, 9}, - {23, 110, 155, 92, 12}, - {153, 236, 205, 121, 8}, - {51, 173, 151, 110, 8} - }, - { {105, 80, 30, 18, 12}, - {66, 69, 229, 208, 1}, - {52, 135, 128, 169, 6}, - {128, 186, 122, 36, 2} - }, - { {150, 139, 94, 59, 15}, - {164, 123, 223, 23, 9}, - {253, 199, 173, 22, 9}, - {158, 143, 189, 226, 5} - }, - { {218, 116, 48, 6, 5}, - {79, 12, 64, 179, 5}, - {166, 0, 194, 229, 11}, - {172, 208, 35, 15, 2} - }, - { {25, 179, 22, 144, 13}, - {71, 75, 217, 80, 2}, - {176, 150, 140, 217, 8}, - {64, 169, 189, 46, 2} - }, - { {25, 3, 122, 37, 8}, - {42, 74, 15, 84, 4}, - {26, 69, 236, 9, 8}, - {34, 175, 5, 37, 4} - }, - { {45, 131, 24, 137, 9}, - {134, 34, 61, 84, 2}, - {153, 17, 140, 27, 4}, - {66, 171, 196, 70, 1} - }, - { {90, 179, 165, 245, 1}, - {95, 139, 154, 141, 6}, - {138, 250, 92, 213, 10}, - {107, 21, 157, 31, 10} - }, - { {122, 166, 126, 219, 8}, - {35, 105, 189, 189, 7}, - {29, 183, 230, 85, 14}, - {235, 219, 217, 108, 4} - }, - { {74, 39, 236, 111, 9}, - {47, 163, 15, 173, 5}, - {159, 99, 126, 69, 2}, - {171, 95, 12, 95, 4} - }, - { {199, 32, 161, 106, 9}, - {149, 160, 3, 203, 5}, - {149, 104, 80, 78, 3}, - {173, 60, 0, 90, 9} - }, - { {70, 101, 77, 162, 0}, - {177, 7, 6, 161, 3}, - {4, 91, 42, 102, 2}, - {200, 86, 14, 8, 13} - }, - { {69, 170, 40, 84, 15}, - {141, 16, 221, 200, 12}, - {242, 161, 69, 90, 2}, - {49, 59, 176, 139, 1} - }, - { {174, 121, 83, 131, 6}, - {243, 86, 96, 23, 11}, - {108, 28, 169, 231, 5}, - {222, 128, 102, 172, 15} - }, - { {156, 98, 82, 80, 12}, - {163, 76, 201, 26, 0}, - {48, 164, 164, 99, 9}, - {5, 137, 51, 44, 5} - }, - { {12, 88, 52, 54, 3}, - {206, 21, 130, 16, 13}, - {198, 194, 193, 163, 0}, - {176, 132, 26, 135, 3} - }, - { {74, 45, 0, 174, 1}, - {15, 34, 2, 161, 11}, - {135, 80, 11, 69, 2}, - {216, 84, 4, 79, 0} - }, - { {116, 221, 28, 199, 10}, - {200, 31, 53, 188, 11}, - {94, 51, 139, 178, 14}, - {211, 218, 207, 129, 3} - }, - { {255, 184, 139, 185, 11}, - {215, 248, 183, 199, 10}, - {217, 221, 17, 223, 15}, - {94, 62, 209, 254, 11} - }, - { {72, 11, 173, 35, 1}, - {22, 131, 14, 132, 13}, - {140, 75, 93, 1, 2}, - {178, 23, 12, 22, 8} - }, - { {7, 41, 136, 152, 1}, - {133, 162, 132, 65, 10}, - {129, 145, 25, 78, 0}, - {88, 34, 20, 90, 1} - }, - { {230, 29, 184, 139, 11}, - {196, 178, 37, 183, 15}, - {221, 17, 219, 134, 7}, - {254, 218, 68, 210, 3} - }, - { {115, 138, 131, 230, 3}, - {28, 216, 58, 201, 11}, - {198, 124, 21, 28, 14}, - {217, 53, 193, 179, 8} - }, - { {39, 0, 88, 211, 10}, - {160, 16, 165, 93, 3}, - {92, 177, 160, 14, 4}, - {203, 170, 80, 128, 5} - }, - { {99, 133, 237, 25, 0}, - {48, 163, 180, 229, 4}, - {9, 139, 122, 28, 6}, - {42, 114, 220, 80, 12} - }, - { {43, 58, 207, 150, 12}, - {123, 193, 237, 65, 11}, - {54, 159, 53, 205, 4}, - {216, 43, 120, 61, 14} - }, - { {219, 208, 152, 69, 3}, - {78, 156, 20, 223, 0}, - {202, 33, 144, 189, 11}, - {15, 178, 131, 151, 2} - }, - { {157, 205, 210, 135, 6}, - {170, 222, 80, 118, 11}, - {110, 20, 187, 59, 9}, - {214, 224, 167, 181, 5} - }, - { {49, 244, 150, 171, 1}, - {69, 237, 50, 116, 3}, - {141, 86, 146, 248, 12}, - {194, 228, 203, 122, 2} - }, - { {31, 47, 77, 37, 3}, - {191, 27, 14, 101, 8}, - {202, 75, 47, 79, 8}, - {26, 103, 13, 143, 13} - }, - { {238, 22, 15, 225, 14}, - {210, 81, 111, 175, 2}, - {120, 127, 6, 135, 7}, - {79, 95, 104, 164, 11} - }, - { {10, 161, 221, 53, 1}, - {63, 131, 150, 21, 0}, - {138, 203, 184, 85, 0}, - {10, 134, 156, 31, 12} - }, - { {86, 173, 47, 172, 10}, - {153, 123, 23, 161, 14}, - {83, 95, 75, 86, 10}, - {120, 94, 141, 233, 9} - }, - { {211, 126, 245, 50, 15}, - {117, 157, 203, 243, 13}, - {244, 202, 247, 236, 11}, - {188, 253, 59, 154, 14} - }, - { {26, 77, 168, 98, 11}, - {6, 158, 7, 41, 13}, - {212, 97, 91, 37, 8}, - {185, 78, 7, 150, 0} - }, - { {14, 14, 83, 226, 12}, - {178, 64, 75, 57, 11}, - {52, 124, 167, 7, 0}, - {217, 205, 32, 36, 13} - }, - { {186, 19, 25, 227, 12}, - {82, 10, 111, 31, 3}, - {60, 121, 140, 133, 13}, - {207, 143, 101, 4, 10} - }, - { {166, 38, 10, 6, 2}, - {137, 80, 44, 35, 1}, - {70, 5, 6, 70, 5}, - {140, 67, 64, 169, 1} - }, - { {59, 165, 11, 14, 1}, - {31, 106, 52, 97, 1}, - {135, 13, 10, 93, 12}, - {136, 98, 197, 111, 8} - }, - { {38, 36, 208, 138, 6}, - {161, 176, 96, 49, 3}, - {101, 16, 178, 70, 4}, - {200, 192, 96, 216, 5} - }, - { {34, 170, 93, 184, 11}, - {53, 49, 191, 17, 10}, - {209, 219, 165, 84, 4}, - {88, 143, 216, 202, 12} - }, - { {204, 89, 30, 234, 11}, - {198, 119, 7, 154, 11}, - {213, 119, 137, 163, 3}, - {213, 158, 14, 230, 3} - }, - { {93, 163, 232, 36, 7}, - {175, 154, 94, 192, 4}, - {226, 65, 124, 91, 10}, - {32, 55, 165, 159, 5} - }, - { {49, 70, 31, 250, 13}, - {20, 109, 239, 120, 3}, - {181, 255, 134, 40, 12}, - {193, 239, 123, 98, 8} - }, - { {225, 228, 77, 220, 5}, - {61, 37, 244, 234, 2}, - {163, 187, 34, 120, 7}, - {69, 114, 250, 75, 12} - }, - { {44, 242, 109, 104, 5}, - {247, 37, 126, 8, 4}, - {161, 107, 100, 243, 4}, - {33, 7, 234, 78, 15} - }, - { {188, 184, 23, 132, 2}, - {219, 89, 48, 18, 10}, - {66, 30, 129, 211, 13}, - {84, 128, 201, 173, 11} - }, - { {229, 105, 162, 218, 12}, - {129, 230, 225, 202, 15}, - {53, 180, 89, 106, 7}, - {245, 56, 118, 120, 1} - }, - { {126, 187, 69, 235, 1}, - {247, 43, 58, 141, 11}, - {141, 122, 45, 215, 14}, - {219, 21, 205, 78, 15} - }, - { {95, 193, 180, 208, 10}, - {130, 159, 145, 217, 6}, - {80, 178, 216, 63, 10}, - {105, 184, 159, 148, 1} - }, - { {67, 231, 225, 87, 9}, - {61, 134, 153, 237, 5}, - {158, 168, 126, 124, 2}, - {171, 121, 150, 27, 12} - }, - { {74, 231, 170, 10, 11}, - {7, 246, 29, 161, 5}, - {213, 5, 94, 117, 2}, - {168, 91, 134, 254, 0} - }, - { {165, 185, 85, 105, 15}, - {245, 51, 115, 94, 8}, - {249, 106, 169, 218, 5}, - {23, 172, 236, 202, 15} - }, - { {224, 84, 118, 144, 13}, - {100, 69, 225, 178, 6}, - {176, 150, 226, 160, 7}, - {100, 216, 122, 34, 6} - }, - { {210, 45, 14, 153, 2}, - {1, 123, 132, 167, 10}, - {73, 151, 11, 68, 11}, - {94, 82, 29, 232, 0} - }, - { {24, 168, 223, 230, 1}, - {63, 201, 22, 24, 11}, - {134, 127, 177, 81, 8}, - {209, 134, 137, 63, 12} - }, - { {69, 219, 175, 11, 10}, - {208, 247, 29, 196, 13}, - {93, 15, 93, 186, 2}, - {178, 59, 142, 240, 11} - }, - { {120, 139, 137, 141, 8}, - {26, 170, 61, 132, 10}, - {27, 25, 29, 17, 14}, - {82, 27, 197, 85, 8} - }, - { {241, 221, 156, 23, 1}, - {76, 143, 180, 246, 9}, - {142, 131, 155, 184, 15}, - {150, 242, 223, 19, 2} - }, - { {168, 83, 82, 179, 8}, - {98, 70, 171, 22, 3}, - {28, 212, 172, 161, 5}, - {198, 141, 86, 36, 6} - }, - { {168, 106, 55, 142, 9}, - {31, 101, 41, 18, 15}, - {151, 30, 197, 97, 5}, - {244, 137, 74, 111, 8} - }, - { {189, 126, 84, 235, 14}, - {227, 61, 107, 126, 11}, - {125, 114, 167, 235, 13}, - {215, 237, 107, 204, 7} - }, - { {223, 220, 164, 141, 1}, - {206, 173, 16, 231, 14}, - {139, 18, 83, 191, 11}, - {126, 112, 139, 87, 3} - }, - { {164, 184, 123, 136, 7}, - {245, 112, 116, 18, 14}, - {225, 29, 225, 210, 5}, - {116, 130, 224, 234, 15} - }, - { {156, 22, 106, 229, 0}, - {234, 72, 14, 46, 6}, - {10, 117, 102, 131, 9}, - {103, 71, 1, 37, 7} - }, - { {7, 66, 172, 242, 15}, - {132, 149, 207, 73, 7}, - {244, 243, 84, 46, 0}, - {233, 47, 58, 146, 1} - }, - { {76, 120, 158, 87, 8}, - {203, 197, 133, 156, 9}, - {30, 167, 145, 227, 2}, - {147, 154, 26, 61, 3} - }, - { {154, 239, 33, 84, 12}, - {27, 14, 217, 43, 12}, - {50, 168, 79, 117, 9}, - {61, 73, 183, 13, 8} - }, - { {114, 33, 143, 98, 6}, - {17, 219, 102, 137, 1}, - {100, 111, 24, 68, 14}, - {137, 22, 109, 184, 8} - }, - { {144, 22, 79, 84, 2}, - {120, 89, 140, 42, 0}, - {66, 175, 38, 128, 9}, - {5, 67, 25, 161, 14} - }, - { {255, 38, 45, 47, 8}, - {155, 41, 47, 231, 5}, - {31, 75, 70, 79, 15}, - {174, 127, 73, 77, 9} - }, - { {155, 75, 33, 101, 11}, - {30, 30, 11, 79, 12}, - {218, 104, 77, 45, 9}, - {63, 45, 7, 135, 8} - }, - { {157, 250, 242, 161, 11}, - {231, 220, 27, 86, 14}, - {216, 84, 245, 251, 9}, - {118, 173, 131, 190, 7} - }, - { {88, 151, 203, 43, 2}, - {114, 250, 30, 164, 1}, - {77, 77, 62, 145, 10}, - {130, 87, 133, 244, 14} - }, - { {71, 47, 124, 245, 10}, - {169, 19, 143, 253, 14}, - {90, 243, 239, 78, 2}, - {123, 255, 28, 137, 5} - }, - { {231, 58, 247, 133, 1}, - {253, 193, 40, 215, 14}, - {138, 30, 245, 206, 7}, - {126, 177, 72, 59, 15} - }, - { {207, 215, 242, 172, 4}, - {234, 230, 90, 243, 6}, - {35, 84, 254, 191, 3}, - {108, 245, 166, 117, 7} - }, - { {34, 145, 90, 60, 13}, - {108, 98, 247, 17, 0}, - {179, 197, 168, 148, 4}, - {8, 142, 244, 99, 6} - }, - { {34, 133, 70, 209, 9}, - {36, 67, 177, 45, 2}, - {152, 182, 42, 20, 4}, - {75, 72, 220, 34, 4} - }, - { {114, 208, 93, 189, 9}, - {124, 45, 183, 149, 2}, - {155, 219, 160, 180, 14}, - {74, 158, 219, 67, 14} - }, - { {66, 217, 149, 82, 0}, - {80, 135, 144, 153, 9}, - {4, 170, 153, 180, 2}, - {153, 144, 158, 16, 10} - }, - { {138, 133, 121, 144, 1}, - {54, 2, 148, 51, 6}, - {128, 153, 234, 21, 1}, - {108, 194, 148, 6, 12} - }, - { {197, 198, 42, 24, 4}, - {128, 100, 220, 226, 4}, - {33, 133, 70, 58, 3}, - {36, 115, 178, 96, 1} - }, - { {233, 151, 88, 80, 13}, - {102, 2, 253, 250, 0}, - {176, 161, 174, 153, 7}, - {5, 251, 244, 6, 6} - }, - { {14, 234, 177, 70, 7}, - {159, 148, 88, 25, 13}, - {230, 40, 213, 119, 0}, - {185, 129, 162, 159, 9} - }, - { {145, 53, 173, 2, 13}, - {85, 139, 69, 98, 5}, - {180, 11, 90, 200, 9}, - {164, 106, 45, 26, 10} - }, - { {190, 141, 200, 78, 4}, - {170, 170, 116, 43, 9}, - {39, 33, 59, 23, 13}, - {157, 66, 229, 85, 5} - }, - { {173, 173, 184, 75, 2}, - {131, 178, 52, 126, 13}, - {77, 33, 219, 91, 5}, - {183, 226, 196, 220, 1} - }, - { {239, 18, 224, 176, 8}, - {226, 128, 171, 195, 6}, - {16, 208, 116, 143, 7}, - {108, 61, 80, 20, 7} - }, - { {110, 30, 54, 228, 4}, - {202, 65, 106, 185, 14}, - {34, 118, 199, 135, 6}, - {121, 213, 104, 37, 3} - }, - { {182, 167, 125, 25, 13}, - {181, 43, 253, 55, 4}, - {185, 139, 238, 86, 13}, - {46, 203, 253, 74, 13} - }, - { {23, 86, 83, 139, 13}, - {244, 108, 73, 117, 3}, - {189, 28, 166, 174, 8}, - {202, 233, 35, 98, 15} - }, - { {95, 18, 213, 36, 1}, - {254, 137, 10, 209, 0}, - {130, 74, 180, 143, 10}, - {8, 181, 9, 23, 15} - }, - { {70, 117, 172, 246, 3}, - {205, 151, 134, 169, 7}, - {198, 243, 90, 230, 2}, - {233, 86, 30, 155, 3} - }, - { {60, 106, 94, 42, 12}, - {163, 109, 111, 16, 9}, - {53, 71, 165, 99, 12}, - {144, 143, 107, 108, 5} - }, - { {36, 27, 3, 248, 15}, - {212, 114, 235, 8, 10}, - {241, 252, 13, 130, 4}, - {81, 13, 116, 226, 11} - }, - { {174, 183, 170, 241, 10}, - {195, 210, 191, 47, 6}, - {88, 245, 94, 215, 5}, - {111, 79, 212, 188, 3} - }, - { {128, 123, 11, 149, 5}, - {93, 70, 204, 6, 10}, - {170, 157, 13, 224, 1}, - {86, 3, 54, 43, 10} - }, - { {6, 123, 185, 142, 2}, - {217, 182, 12, 17, 15}, - {71, 25, 221, 230, 0}, - {248, 131, 6, 217, 11} - }, - { {187, 205, 97, 202, 11}, - {54, 62, 49, 107, 15}, - {213, 56, 107, 61, 13}, - {253, 104, 199, 198, 12} - }, - { {102, 111, 155, 27, 2}, - {145, 246, 172, 181, 9}, - {77, 141, 159, 102, 6}, - {154, 211, 86, 248, 9} - }, - { {220, 128, 108, 119, 5}, - {174, 9, 214, 142, 5}, - {174, 227, 96, 19, 11}, - {167, 22, 185, 7, 5} - }, - { {244, 76, 119, 37, 13}, - {188, 77, 99, 182, 12}, - {186, 78, 227, 34, 15}, - {54, 220, 107, 35, 13} - }, - { {114, 219, 75, 25, 1}, - {116, 110, 188, 133, 8}, - {137, 141, 45, 180, 14}, - {26, 19, 215, 98, 14} - }, - { {16, 18, 108, 127, 9}, - {108, 41, 143, 12, 5}, - {159, 227, 100, 128, 8}, - {163, 15, 25, 67, 6} - }, - { {162, 1, 124, 141, 13}, - {44, 35, 101, 23, 6}, - {187, 19, 232, 4, 5}, - {110, 138, 108, 67, 4} - }, - { {92, 43, 98, 199, 13}, - {175, 74, 73, 140, 15}, - {190, 52, 109, 67, 10}, - {243, 25, 37, 47, 5} - }, - { {246, 17, 249, 50, 13}, - {244, 138, 231, 147, 5}, - {180, 201, 248, 134, 15}, - {172, 158, 117, 18, 15} - }, - { {29, 202, 246, 89, 3}, - {166, 253, 152, 92, 12}, - {201, 166, 245, 59, 8}, - {51, 161, 155, 246, 5} - }, - { {246, 222, 90, 183, 0}, - {232, 76, 190, 183, 11}, - {14, 213, 167, 182, 15}, - {222, 215, 211, 33, 7} - }, - { {2, 227, 95, 133, 4}, - {57, 71, 92, 21, 2}, - {42, 31, 172, 116, 0}, - {74, 131, 174, 41, 12} - }, - { {154, 82, 200, 49, 13}, - {102, 140, 207, 7, 0}, - {184, 193, 52, 165, 9}, - {14, 15, 51, 22, 6} - }, - { {201, 156, 238, 248, 13}, - {102, 225, 215, 234, 14}, - {177, 247, 115, 153, 3}, - {117, 126, 184, 118, 6} - }, - { {164, 0, 185, 178, 3}, - {148, 144, 166, 18, 7}, - {196, 217, 208, 2, 5}, - {228, 134, 80, 146, 9} - }, - { {83, 189, 160, 149, 3}, - {77, 154, 144, 229, 14}, - {202, 144, 91, 220, 10}, - {122, 112, 149, 155, 2} - }, - { {229, 156, 80, 168, 12}, - {224, 32, 115, 242, 10}, - {49, 80, 163, 154, 7}, - {84, 252, 224, 64, 7} - }, - { {171, 95, 148, 193, 7}, - {70, 151, 104, 127, 10}, - {232, 50, 159, 173, 5}, - {95, 225, 110, 150, 2} - }, - { {154, 1, 159, 43, 13}, - {22, 235, 71, 23, 1}, - {189, 79, 152, 5, 9}, - {142, 142, 45, 118, 8} - }, - { {179, 111, 58, 61, 12}, - {9, 110, 239, 119, 12}, - {59, 197, 207, 108, 13}, - {62, 239, 119, 105, 0} - }, - { {192, 187, 244, 25, 3}, - {101, 179, 152, 150, 12}, - {201, 130, 253, 208, 3}, - {54, 145, 156, 218, 6} - }, - { {20, 123, 78, 197, 0}, - {233, 79, 12, 12, 10}, - {10, 55, 45, 226, 8}, - {83, 3, 15, 41, 7} - }, - { {90, 199, 71, 252, 14}, - {58, 127, 219, 169, 2}, - {115, 254, 46, 53, 10}, - {73, 93, 191, 229, 12} - }, - { {55, 33, 111, 157, 0}, - {185, 107, 164, 69, 6}, - {11, 159, 104, 78, 12}, - {106, 34, 93, 105, 13} - }, - { {1, 111, 252, 42, 3}, - {37, 183, 14, 112, 13}, - {197, 67, 255, 104, 0}, - {176, 231, 14, 218, 4} - }, - { {89, 15, 175, 61, 8}, - {26, 235, 143, 228, 12}, - {27, 207, 95, 9, 10}, - {50, 127, 29, 117, 8} - }, - { {153, 1, 84, 216, 10}, - {34, 59, 129, 90, 2}, - {81, 178, 168, 9, 9}, - {69, 168, 29, 196, 4} - }, - { {67, 238, 76, 167, 7}, - {45, 21, 94, 229, 11}, - {238, 83, 39, 124, 2}, - {218, 119, 170, 139, 4} - }, - { {54, 90, 193, 205, 0}, - {248, 172, 40, 13, 10}, - {11, 56, 53, 166, 12}, - {91, 1, 67, 81, 15} - }, - { {131, 166, 230, 183, 5}, - {45, 193, 218, 103, 7}, - {174, 214, 118, 92, 1}, - {238, 101, 184, 59, 4} - }, - { {19, 38, 191, 52, 9}, - {29, 201, 143, 113, 4}, - {146, 207, 214, 76, 8}, - {40, 239, 25, 59, 8} - }, - { {75, 112, 99, 4, 11}, - {127, 84, 1, 193, 4}, - {210, 12, 96, 237, 2}, - {40, 56, 2, 175, 14} - }, - { {138, 249, 189, 185, 10}, - {83, 183, 151, 23, 14}, - {89, 219, 217, 245, 1}, - {126, 142, 158, 220, 10} - }, - { {210, 150, 211, 86, 13}, - {124, 200, 217, 187, 1}, - {182, 172, 182, 148, 11}, - {141, 217, 177, 51, 14} - }, - { {191, 171, 59, 228, 2}, - {155, 90, 62, 91, 14}, - {66, 125, 205, 95, 13}, - {125, 167, 197, 173, 9} - }, - { {174, 86, 204, 98, 6}, - {226, 149, 110, 43, 1}, - {100, 99, 54, 167, 5}, - {141, 71, 106, 148, 7} - }, - { {178, 87, 177, 0, 1}, - {84, 142, 40, 51, 4}, - {128, 8, 222, 164, 13}, - {44, 193, 71, 18, 10} - }, - { {62, 59, 212, 245, 0}, - {235, 139, 170, 29, 10}, - {10, 242, 189, 199, 12}, - {91, 133, 93, 29, 7} - }, - { {81, 226, 98, 81, 13}, - {37, 76, 217, 204, 4}, - {184, 164, 100, 120, 10}, - {35, 57, 179, 42, 4} - }, - { {109, 172, 163, 64, 13}, - {151, 192, 113, 232, 12}, - {176, 44, 83, 91, 6}, - {49, 120, 224, 62, 9} - }, - { {140, 97, 186, 38, 3}, - {143, 214, 6, 18, 5}, - {198, 69, 216, 99, 1}, - {164, 134, 6, 191, 1} - }, - { {188, 245, 45, 34, 0}, - {211, 15, 54, 34, 5}, - {4, 75, 74, 243, 13}, - {164, 70, 207, 12, 11} - }, - { {211, 103, 251, 121, 3}, - {53, 254, 142, 255, 4}, - {201, 237, 254, 108, 11}, - {47, 247, 23, 250, 12} - }, - { {84, 87, 19, 84, 12}, - {216, 78, 201, 184, 0}, - {50, 172, 142, 162, 10}, - {1, 217, 55, 33, 11} - }, - { {252, 69, 102, 73, 14}, - {162, 127, 97, 174, 4}, - {121, 38, 106, 35, 15}, - {39, 88, 111, 228, 5} - }, - { {201, 116, 9, 180, 3}, - {95, 20, 134, 226, 2}, - {194, 217, 2, 233, 3}, - {68, 118, 18, 143, 10} - }, - { {219, 223, 58, 117, 6}, - {74, 94, 222, 255, 12}, - {106, 229, 207, 189, 11}, - {63, 247, 183, 165, 2} - }, - { {124, 198, 54, 99, 4}, - {130, 77, 122, 188, 5}, - {44, 102, 198, 51, 14}, - {163, 213, 235, 36, 1} - }, - { {199, 37, 21, 237, 7}, - {157, 51, 66, 255, 2}, - {235, 122, 138, 78, 3}, - {79, 244, 44, 203, 9} - }, - { {243, 129, 78, 141, 3}, - {44, 123, 52, 199, 2}, - {203, 23, 40, 28, 15}, - {78, 50, 205, 227, 4} - }, - { {1, 97, 236, 83, 14}, - {33, 151, 197, 76, 5}, - {124, 163, 120, 104, 0}, - {163, 42, 62, 152, 4} - }, - { {185, 10, 129, 53, 14}, - {26, 152, 235, 70, 8}, - {122, 200, 21, 9, 13}, - {22, 45, 113, 149, 8} - }, - { {206, 10, 5, 154, 5}, - {150, 33, 200, 131, 11}, - {165, 154, 5, 7, 3}, - {220, 17, 56, 70, 9} - }, - { {177, 143, 157, 57, 3}, - {20, 187, 190, 118, 8}, - {201, 203, 159, 24, 13}, - {22, 231, 221, 210, 8} - }, - { {148, 176, 8, 30, 13}, - {205, 40, 213, 2, 1}, - {183, 129, 0, 210, 9}, - {132, 10, 177, 75, 3} - }, - { {80, 66, 146, 213, 15}, - {12, 220, 201, 156, 2}, - {250, 180, 148, 32, 10}, - {67, 153, 51, 179, 0} - }, - { {35, 150, 114, 80, 11}, - {100, 80, 185, 121, 4}, - {208, 164, 230, 156, 4}, - {41, 233, 208, 162, 6} - }, - { {68, 239, 204, 54, 11}, - {173, 151, 159, 160, 9}, - {214, 195, 63, 114, 2}, - {144, 95, 158, 155, 5} - }, - { {31, 253, 132, 177, 6}, - {195, 159, 210, 101, 10}, - {104, 210, 27, 255, 8}, - {90, 100, 191, 156, 3} - }, - { {197, 49, 181, 191, 8}, - {217, 163, 131, 214, 7}, - {31, 218, 216, 202, 3}, - {230, 188, 28, 89, 11} - }, - { {22, 150, 178, 17, 4}, - {192, 200, 216, 53, 4}, - {40, 132, 214, 150, 8}, - {42, 193, 177, 48, 3} - }, - { {38, 79, 248, 144, 13}, - {164, 134, 237, 49, 14}, - {176, 145, 255, 38, 4}, - {120, 203, 118, 18, 5} - }, - { {246, 196, 223, 173, 7}, - {188, 253, 118, 183, 2}, - {235, 95, 178, 54, 15}, - {78, 214, 235, 243, 13} - }, - { {156, 24, 84, 188, 5}, - {238, 41, 194, 18, 10}, - {163, 210, 161, 131, 9}, - {84, 132, 57, 71, 7} - }, - { {19, 208, 219, 228, 10}, - {120, 220, 23, 89, 2}, - {82, 125, 176, 188, 8}, - {73, 174, 131, 177, 14} - }, - { {249, 179, 22, 59, 4}, - {67, 107, 250, 214, 1}, - {45, 198, 140, 217, 15}, - {134, 181, 253, 108, 2} - }, - { {81, 149, 114, 252, 7}, - {108, 122, 210, 248, 6}, - {227, 244, 234, 152, 10}, - {97, 244, 181, 227, 6} - }, - { {242, 39, 249, 70, 5}, - {61, 138, 108, 187, 5}, - {166, 41, 254, 68, 15}, - {173, 211, 101, 27, 12} - }, - { {102, 190, 189, 206, 13}, - {221, 161, 125, 185, 15}, - {183, 59, 215, 214, 6}, - {249, 219, 232, 91, 11} - }, - { {214, 222, 191, 213, 4}, - {216, 205, 220, 191, 14}, - {42, 191, 215, 182, 11}, - {127, 211, 187, 49, 11} - }, - { {42, 6, 160, 45, 8}, - {10, 160, 43, 37, 4}, - {27, 64, 86, 5, 4}, - {42, 77, 64, 85, 0} - }, - { {20, 53, 95, 118, 11}, - {253, 91, 135, 56, 1}, - {214, 239, 170, 194, 8}, - {129, 206, 29, 171, 15} - }, - { {10, 147, 60, 139, 0}, - {66, 35, 28, 21, 7}, - {13, 19, 204, 149, 0}, - {234, 131, 140, 68, 2} - }, - { {41, 181, 114, 31, 7}, - {111, 114, 240, 116, 5}, - {239, 132, 234, 217, 4}, - {162, 224, 244, 239, 6} - }, - { {10, 176, 119, 109, 10}, - {123, 113, 19, 29, 4}, - {91, 110, 224, 213, 0}, - {43, 140, 136, 237, 14} - }, - { {73, 99, 125, 47, 0}, - {59, 39, 14, 212, 5}, - {15, 75, 236, 105, 2}, - {162, 183, 14, 77, 12} - }, - { {129, 17, 157, 135, 11}, - {92, 147, 5, 86, 3}, - {222, 27, 152, 136, 1}, - {198, 170, 12, 147, 10} - }, - { {140, 179, 51, 58, 10}, - {211, 114, 155, 18, 5}, - {85, 204, 204, 211, 1}, - {164, 141, 148, 236, 11} - }, - { {178, 29, 155, 97, 0}, - {80, 202, 38, 63, 8}, - {8, 109, 155, 132, 13}, - {31, 198, 69, 48, 10} - }, - { {188, 118, 206, 213, 10}, - {235, 221, 173, 46, 2}, - {90, 183, 54, 227, 13}, - {71, 75, 91, 189, 7} - }, - { {160, 15, 75, 230, 8}, - {56, 66, 47, 42, 11}, - {22, 125, 47, 0, 5}, - {213, 79, 68, 33, 12} - }, - { {68, 168, 183, 24, 8}, - {145, 225, 145, 144, 12}, - {17, 142, 209, 82, 2}, - {48, 152, 152, 120, 9} - }, - { {192, 147, 189, 209, 12}, - {80, 131, 221, 158, 6}, - {56, 187, 220, 144, 3}, - {103, 155, 188, 16, 10} - }, - { {67, 88, 203, 57, 12}, - {112, 228, 199, 197, 8}, - {57, 205, 49, 172, 2}, - {26, 62, 50, 112, 14} - }, - { {138, 61, 137, 8, 12}, - {83, 162, 69, 35, 8}, - {49, 9, 27, 197, 1}, - {28, 74, 36, 92, 10} - }, - { {252, 190, 21, 102, 11}, - {223, 25, 59, 186, 9}, - {214, 106, 135, 211, 15}, - {149, 221, 201, 143, 11} - }, - { {203, 103, 181, 191, 5}, - {31, 167, 202, 247, 7}, - {175, 218, 222, 109, 3}, - {238, 245, 62, 95, 8} - }, - { {233, 7, 248, 189, 4}, - {42, 162, 238, 246, 6}, - {43, 209, 254, 9, 7}, - {102, 247, 116, 85, 4} - }, - { {75, 186, 42, 243, 10}, - {67, 80, 159, 205, 15}, - {92, 245, 69, 221, 2}, - {251, 63, 144, 172, 2} - }, - { {113, 234, 245, 7, 10}, - {57, 157, 57, 212, 13}, - {94, 10, 245, 120, 14}, - {178, 185, 203, 153, 12} - }, - { {147, 43, 182, 127, 6}, - {9, 251, 202, 95, 13}, - {111, 230, 221, 76, 9}, - {191, 165, 61, 249, 0} - }, - { {83, 241, 61, 70, 1}, - {93, 15, 20, 217, 5}, - {134, 43, 200, 252, 10}, - {169, 178, 143, 11, 10} - }, - { {99, 45, 7, 61, 3}, - {29, 115, 162, 229, 8}, - {203, 206, 11, 76, 6}, - {26, 116, 92, 235, 8} - }, - { {187, 21, 254, 209, 0}, - {98, 203, 164, 127, 6}, - {8, 183, 250, 141, 13}, - {111, 226, 93, 52, 6} - }, - { {249, 117, 16, 139, 12}, - {67, 46, 97, 246, 3}, - {61, 16, 138, 233, 15}, - {198, 248, 103, 76, 2} - }, - { {82, 137, 180, 183, 15}, - {12, 155, 211, 149, 15}, - {254, 210, 217, 20, 10}, - {250, 156, 189, 147, 0} - }, - { {27, 210, 12, 213, 13}, - {78, 13, 221, 77, 2}, - {186, 179, 4, 189, 8}, - {75, 43, 187, 7, 2} - }, - { {177, 242, 231, 215, 11}, - {125, 221, 185, 78, 7}, - {222, 190, 116, 248, 13}, - {231, 41, 219, 187, 14} - }, - { {59, 199, 234, 23, 8}, - {42, 206, 189, 101, 5}, - {30, 133, 126, 61, 12}, - {170, 107, 215, 53, 4} - }, - { {129, 160, 44, 14, 10}, - {9, 49, 21, 66, 5}, - {87, 3, 64, 88, 1}, - {164, 42, 136, 201, 0} - }, - { {175, 47, 221, 40, 8}, - {179, 163, 47, 115, 8}, - {17, 75, 191, 79, 5}, - {28, 239, 76, 92, 13} - }, - { {102, 160, 63, 60, 5}, - {157, 97, 246, 145, 4}, - {163, 207, 192, 86, 6}, - {40, 150, 248, 107, 9} - }, - { {97, 107, 54, 64, 2}, - {1, 87, 40, 216, 12}, - {64, 38, 205, 104, 6}, - {49, 177, 78, 168, 0} - }, - { {179, 110, 223, 102, 4}, - {57, 205, 110, 123, 9}, - {38, 111, 183, 108, 13}, - {157, 231, 107, 57, 12} - }, - { {221, 206, 234, 142, 13}, - {174, 236, 93, 226, 15}, - {183, 21, 119, 59, 11}, - {244, 123, 163, 119, 5} - }, - { {191, 161, 228, 191, 4}, - {171, 171, 242, 71, 7}, - {47, 210, 120, 95, 13}, - {238, 36, 253, 93, 5} - }, - { {181, 50, 205, 129, 2}, - {241, 153, 44, 70, 2}, - {72, 27, 52, 202, 13}, - {70, 35, 73, 152, 15} - }, - { {93, 75, 26, 56, 8}, - {130, 110, 143, 208, 8}, - {17, 197, 141, 43, 10}, - {16, 191, 23, 100, 1} - }, - { {178, 246, 24, 165, 12}, - {73, 12, 127, 55, 2}, - {58, 81, 134, 244, 13}, - {78, 207, 227, 9, 2} - }, - { {56, 237, 90, 165, 11}, - {47, 94, 55, 52, 10}, - {218, 85, 171, 113, 12}, - {82, 206, 199, 175, 4} - }, - { {73, 219, 225, 19, 5}, - {118, 134, 216, 196, 13}, - {172, 136, 125, 185, 2}, - {178, 49, 182, 22, 14} - }, - { {73, 170, 182, 189, 9}, - {15, 225, 155, 212, 14}, - {155, 214, 213, 89, 2}, - {114, 189, 152, 127, 0} - }, - { {84, 139, 234, 26, 2}, - {160, 250, 156, 128, 13}, - {69, 133, 125, 18, 10}, - {176, 19, 149, 240, 5} - }, - { {157, 15, 70, 19, 1}, - {166, 75, 136, 102, 9}, - {140, 134, 47, 11, 9}, - {150, 97, 29, 38, 5} - }, - { {102, 97, 19, 14, 1}, - {157, 102, 32, 145, 1}, - {135, 12, 136, 102, 6}, - {136, 144, 70, 107, 9} - }, - { {248, 9, 47, 37, 2}, - {26, 91, 38, 134, 12}, - {74, 79, 73, 1, 15}, - {54, 22, 77, 165, 8} - }, - { {11, 201, 149, 46, 14}, - {26, 183, 83, 81, 9}, - {119, 74, 153, 61, 0}, - {152, 172, 174, 213, 8} - }, - { {184, 19, 104, 249, 3}, - {102, 58, 174, 14, 6}, - {201, 241, 108, 129, 13}, - {103, 7, 85, 198, 6} - }, - { {247, 151, 168, 45, 7}, - {204, 186, 126, 231, 4}, - {235, 65, 94, 158, 15}, - {46, 119, 229, 211, 3} - }, - { {64, 198, 85, 224, 12}, - {48, 5, 91, 184, 2}, - {48, 122, 166, 48, 2}, - {65, 221, 170, 0, 12} - }, - { {119, 132, 246, 36, 5}, - {172, 201, 114, 241, 4}, - {162, 70, 242, 30, 14}, - {40, 244, 233, 51, 5} - }, - { {67, 149, 31, 37, 8}, - {88, 67, 23, 245, 0}, - {26, 79, 138, 156, 2}, - {10, 254, 140, 33, 10} - }, - { {45, 159, 144, 243, 0}, - {194, 130, 186, 124, 11}, - {12, 240, 159, 155, 4}, - {211, 229, 212, 20, 3} - }, - { {146, 221, 176, 205, 11}, - {76, 190, 17, 63, 14}, - {219, 48, 219, 180, 9}, - {127, 200, 135, 211, 2} - }, - { {246, 199, 216, 26, 12}, - {160, 174, 253, 179, 1}, - {53, 129, 190, 54, 15}, - {140, 219, 247, 80, 5} - }, - { {24, 194, 89, 137, 6}, - {50, 60, 92, 20, 2}, - {105, 25, 164, 49, 8}, - {66, 131, 163, 196, 12} - }, - { {191, 94, 230, 119, 1}, - {238, 205, 170, 111, 13}, - {142, 230, 119, 175, 13}, - {191, 101, 91, 55, 7} - }, - { {154, 185, 210, 41, 10}, - {99, 250, 19, 23, 8}, - {89, 68, 185, 213, 9}, - {30, 140, 133, 252, 6} - }, - { {85, 134, 127, 171, 1}, - {180, 105, 30, 244, 7}, - {141, 95, 230, 26, 10}, - {226, 247, 137, 98, 13} - }, - { {184, 210, 232, 62, 14}, - {106, 188, 255, 2, 5}, - {119, 193, 116, 177, 13}, - {164, 15, 243, 213, 6} - }, - { {8, 95, 31, 243, 10}, - {82, 87, 143, 60, 11}, - {92, 255, 143, 161, 0}, - {211, 207, 30, 164, 10} - }, - { {18, 244, 23, 56, 3}, - {85, 125, 146, 49, 0}, - {193, 206, 130, 244, 8}, - {8, 196, 155, 234, 10} - }, - { {166, 113, 141, 223, 14}, - {217, 183, 229, 15, 3}, - {127, 187, 24, 230, 5}, - {207, 10, 126, 217, 11} - }, - { {192, 142, 206, 109, 0}, - {40, 225, 30, 174, 8}, - {11, 103, 55, 16, 3}, - {23, 87, 136, 113, 4} - }, - { {109, 149, 12, 180, 7}, - {206, 19, 246, 224, 2}, - {226, 211, 10, 155, 6}, - {64, 118, 252, 135, 3} - }, - { {150, 47, 248, 18, 6}, - {161, 154, 204, 51, 13}, - {100, 129, 255, 70, 9}, - {188, 195, 53, 152, 5} - }, - { {100, 178, 210, 193, 10}, - {225, 208, 57, 156, 2}, - {88, 52, 180, 210, 6}, - {67, 153, 192, 184, 7} - }, - { {171, 232, 165, 221, 12}, - {27, 165, 241, 79, 14}, - {59, 186, 81, 125, 5}, - {127, 40, 250, 93, 8} - }, - { {1, 1, 118, 119, 7}, - {44, 83, 194, 92, 5}, - {238, 230, 232, 8, 0}, - {163, 164, 60, 163, 4} - }, - { {122, 87, 242, 127, 10}, - {106, 254, 171, 189, 5}, - {95, 228, 254, 165, 14}, - {171, 221, 87, 245, 6} - }, - { {49, 50, 151, 33, 7}, - {85, 217, 106, 84, 0}, - {232, 78, 148, 200, 12}, - {2, 165, 105, 186, 10} - }, - { {84, 80, 181, 69, 13}, - {220, 141, 65, 156, 4}, - {186, 42, 208, 162, 10}, - {35, 152, 43, 19, 11} - }, - { {11, 87, 84, 254, 5}, - {110, 39, 202, 121, 3}, - {167, 242, 174, 173, 0}, - {201, 229, 62, 71, 6} - }, - { {182, 45, 55, 60, 6}, - {153, 123, 226, 51, 12}, - {99, 206, 203, 70, 13}, - {60, 196, 125, 233, 9} - }, - { {148, 78, 188, 41, 1}, - {132, 173, 14, 54, 12}, - {137, 67, 215, 34, 9}, - {54, 199, 11, 82, 1} - }, - { {212, 209, 225, 203, 8}, - {240, 174, 17, 142, 7}, - {29, 56, 120, 178, 11}, - {231, 24, 135, 80, 15} - }, - { {41, 127, 114, 212, 15}, - {111, 86, 233, 120, 14}, - {242, 180, 239, 233, 4}, - {113, 233, 118, 175, 6} - }, - { {185, 41, 143, 89, 11}, - {23, 251, 165, 78, 8}, - {217, 175, 25, 73, 13}, - {23, 42, 93, 254, 8} - }, - { {156, 178, 65, 173, 13}, - {255, 40, 91, 6, 2}, - {187, 88, 36, 211, 9}, - {70, 13, 161, 79, 15} - }, - { {9, 10, 241, 177, 13}, - {54, 128, 203, 84, 14}, - {184, 216, 245, 9, 0}, - {114, 173, 48, 22, 12} - }, - { {16, 185, 135, 171, 7}, - {85, 251, 82, 4, 11}, - {237, 94, 25, 208, 8}, - {210, 4, 173, 250, 10} - }, - { {126, 194, 225, 64, 6}, - {178, 156, 120, 137, 4}, - {96, 40, 116, 55, 14}, - {41, 17, 227, 148, 13} - }, - { {67, 167, 86, 127, 1}, - {45, 99, 154, 253, 1}, - {143, 230, 174, 92, 2}, - {139, 245, 156, 107, 4} - }, - { {26, 3, 90, 19, 6}, - {34, 90, 204, 21, 1}, - {108, 133, 172, 5, 8}, - {138, 131, 53, 164, 4} - }, - { {29, 135, 214, 9, 13}, - {166, 235, 89, 116, 0}, - {185, 6, 190, 27, 8}, - {2, 233, 173, 118, 5} - }, - { {166, 252, 66, 185, 15}, - {229, 116, 243, 39, 10}, - {249, 212, 35, 246, 5}, - {94, 76, 242, 234, 7} - }, - { {107, 129, 183, 247, 6}, - {26, 211, 242, 221, 7}, - {110, 254, 216, 29, 6}, - {235, 180, 252, 181, 8} - }, - { {69, 254, 109, 129, 14}, - {241, 21, 93, 228, 14}, - {120, 27, 103, 250, 2}, - {114, 123, 170, 136, 15} - }, - { {53, 236, 43, 184, 7}, - {149, 124, 246, 96, 14}, - {225, 221, 67, 122, 12}, - {112, 102, 243, 234, 9} - }, - { {49, 88, 4, 149, 14}, - {72, 29, 225, 68, 10}, - {122, 146, 1, 168, 12}, - {82, 40, 123, 129, 2} - }, - { {152, 19, 34, 45, 15}, - {78, 122, 75, 6, 4}, - {251, 68, 76, 129, 9}, - {38, 13, 37, 231, 2} - }, - { {154, 159, 90, 231, 3}, - {110, 90, 30, 63, 11}, - {206, 117, 175, 149, 9}, - {223, 199, 133, 167, 6} - }, - { {212, 231, 111, 182, 1}, - {189, 79, 158, 162, 7}, - {134, 223, 110, 114, 11}, - {228, 87, 159, 43, 13} - }, - { {229, 90, 150, 106, 14}, - {192, 245, 107, 218, 9}, - {117, 102, 149, 170, 7}, - {149, 189, 106, 240, 3} - }, - { {79, 187, 212, 222, 13}, - {239, 163, 217, 217, 11}, - {183, 178, 189, 223, 2}, - {217, 185, 188, 95, 7} - }, - { {121, 157, 133, 152, 15}, - {86, 187, 241, 224, 10}, - {241, 154, 27, 153, 14}, - {80, 120, 253, 214, 10} - }, - { {9, 112, 158, 165, 9}, - {79, 197, 7, 84, 2}, - {154, 87, 144, 233, 0}, - {66, 174, 10, 63, 2} - }, - { {3, 87, 205, 203, 8}, - {112, 167, 13, 109, 3}, - {29, 59, 62, 172, 0}, - {203, 107, 14, 80, 14} - }, - { {212, 185, 186, 58, 5}, - {197, 234, 214, 146, 13}, - {165, 197, 217, 210, 11}, - {180, 150, 181, 122, 3} - }, - { {24, 64, 161, 74, 8}, - {18, 172, 1, 8, 5}, - {21, 40, 80, 33, 8}, - {161, 8, 3, 84, 8} - }, - { {118, 93, 35, 164, 0}, - {216, 78, 34, 161, 14}, - {2, 92, 75, 166, 14}, - {120, 84, 71, 33, 11} - }, - { {215, 210, 250, 82, 11}, - {228, 220, 157, 219, 5}, - {212, 165, 244, 190, 11}, - {173, 187, 147, 178, 7} - }, - { {112, 131, 115, 144, 14}, - {48, 90, 249, 144, 6}, - {112, 156, 236, 16, 14}, - {96, 153, 245, 160, 12} - }, - { {54, 219, 219, 178, 9}, - {244, 206, 191, 17, 11}, - {148, 221, 189, 182, 12}, - {216, 143, 215, 50, 15} - }, - { {238, 65, 132, 139, 9}, - {134, 167, 33, 135, 3}, - {157, 18, 24, 39, 7}, - {206, 24, 78, 86, 1} - }, - { {82, 197, 48, 152, 6}, - {0, 62, 208, 177, 6}, - {97, 144, 202, 52, 10}, - {104, 208, 183, 192, 0} - }, - { {252, 86, 183, 22, 2}, - {218, 221, 168, 178, 5}, - {70, 142, 214, 163, 15}, - {164, 209, 91, 181, 11} - }, - { {116, 165, 189, 128, 1}, - {149, 139, 52, 176, 6}, - {128, 27, 218, 82, 14}, - {96, 210, 205, 26, 9} - }, - { {149, 143, 205, 134, 4}, - {184, 139, 92, 98, 11}, - {38, 27, 63, 26, 9}, - {212, 99, 173, 17, 13} - }, - { {217, 178, 146, 205, 0}, - {75, 232, 24, 222, 2}, - {11, 52, 148, 217, 11}, - {71, 177, 129, 125, 2} - }, - { {167, 235, 71, 193, 10}, - {177, 87, 57, 79, 10}, - {88, 62, 45, 126, 5}, - {95, 41, 206, 168, 13} - }, - { {79, 1, 94, 2, 9}, - {166, 67, 5, 209, 1}, - {148, 7, 168, 15, 2}, - {136, 186, 12, 38, 5} - }, - { {106, 36, 9, 113, 10}, - {19, 16, 167, 173, 0}, - {88, 233, 2, 69, 6}, - {11, 94, 80, 140, 8} - }, - { {206, 68, 49, 8, 9}, - {150, 36, 1, 179, 4}, - {145, 8, 194, 39, 3}, - {44, 216, 2, 70, 9} - }, - { {49, 255, 30, 233, 10}, - {65, 127, 63, 124, 10}, - {89, 119, 143, 248, 12}, - {83, 239, 207, 232, 2} - }, - { {20, 10, 7, 54, 11}, - {156, 89, 139, 0, 9}, - {214, 206, 5, 2, 8}, - {144, 13, 25, 163, 9} - }, - { {70, 59, 86, 20, 0}, - {233, 67, 136, 145, 8}, - {2, 134, 173, 198, 2}, - {24, 145, 28, 41, 7} - }, - { {59, 6, 12, 183, 14}, - {10, 25, 239, 101, 3}, - {126, 211, 6, 13, 12}, - {202, 111, 121, 133, 0} - }, - { {183, 192, 126, 222, 10}, - {168, 125, 181, 91, 7}, - {87, 183, 224, 62, 13}, - {237, 170, 219, 225, 5} - }, - { {131, 167, 59, 199, 12}, - {25, 66, 93, 127, 7}, - {62, 61, 206, 92, 1}, - {239, 235, 164, 41, 8} - }, - { {169, 68, 61, 125, 8}, - {26, 37, 167, 126, 4}, - {27, 235, 194, 41, 5}, - {39, 238, 90, 69, 8} - }, - { {89, 62, 133, 40, 0}, - {83, 169, 10, 224, 8}, - {1, 74, 23, 201, 10}, - {16, 117, 9, 92, 10} - }, - { {91, 223, 9, 190, 3}, - {94, 62, 158, 225, 11}, - {199, 217, 15, 189, 10}, - {216, 119, 151, 199, 10} - }, - { {90, 199, 81, 168, 1}, - {54, 46, 26, 177, 2}, - {129, 88, 174, 53, 10}, - {72, 213, 135, 70, 12} - }, - { {172, 197, 14, 185, 6}, - {130, 119, 246, 38, 2}, - {105, 215, 10, 51, 5}, - {70, 70, 254, 228, 1} - }, - { {140, 158, 159, 124, 11}, - {222, 241, 159, 58, 8}, - {211, 239, 151, 147, 1}, - {21, 207, 152, 247, 11} - }, - { {94, 223, 124, 158, 10}, - {234, 63, 157, 177, 15}, - {87, 147, 239, 183, 10}, - {248, 219, 159, 197, 7} - }, - { {194, 247, 145, 150, 7}, - {93, 150, 216, 179, 3}, - {230, 152, 158, 244, 3}, - {204, 209, 182, 155, 10} - }, - { {104, 128, 250, 139, 3}, - {38, 240, 52, 148, 7}, - {205, 21, 240, 17, 6}, - {226, 146, 192, 246, 4} - }, - { {164, 118, 61, 185, 15}, - {213, 53, 239, 54, 6}, - {249, 219, 198, 226, 5}, - {102, 207, 122, 202, 11} - }, - { {232, 136, 149, 37, 11}, - {30, 145, 51, 150, 8}, - {218, 74, 145, 17, 7}, - {22, 156, 200, 151, 8} - }, - { {94, 137, 11, 42, 0}, - {146, 106, 22, 129, 9}, - {5, 77, 9, 23, 10}, - {152, 22, 133, 100, 9} - }, - { {168, 252, 52, 240, 6}, - {67, 21, 242, 58, 14}, - {96, 242, 195, 241, 5}, - {117, 196, 250, 140, 2} - }, - { {102, 80, 220, 156, 14}, - {232, 181, 229, 145, 2}, - {115, 147, 176, 166, 6}, - {72, 154, 122, 209, 7} - }, - { {217, 235, 112, 198, 6}, - {43, 30, 88, 218, 15}, - {102, 48, 237, 121, 11}, - {245, 177, 167, 141, 4} - }, - { {6, 57, 26, 27, 7}, - {197, 114, 196, 21, 9}, - {237, 133, 137, 198, 0}, - {154, 130, 52, 234, 3} - }, - { {100, 213, 236, 219, 14}, - {224, 183, 245, 172, 7}, - {125, 179, 122, 178, 6}, - {227, 90, 254, 208, 7} - }, - { {80, 23, 140, 98, 3}, - {68, 155, 14, 168, 1}, - {196, 99, 30, 128, 10}, - {129, 87, 13, 146, 2} - }, - { {128, 175, 91, 93, 8}, - {57, 98, 157, 62, 8}, - {27, 173, 175, 80, 1}, - {23, 203, 148, 105, 12} - }, - { {178, 110, 247, 189, 9}, - {61, 237, 171, 55, 14}, - {155, 222, 247, 100, 13}, - {126, 205, 91, 123, 12} - }, - { {210, 217, 41, 11, 11}, - {84, 62, 21, 135, 13}, - {221, 9, 73, 180, 11}, - {190, 26, 135, 194, 10} - }, - { {142, 230, 136, 246, 1}, - {143, 132, 158, 43, 3}, - {134, 241, 22, 119, 1}, - {205, 71, 146, 31, 1} - }, - { {144, 133, 126, 179, 5}, - {36, 75, 214, 54, 7}, - {172, 215, 234, 16, 9}, - {230, 198, 189, 34, 4} - }, - { {214, 244, 185, 51, 14}, - {209, 156, 215, 183, 5}, - {124, 201, 210, 246, 11}, - {174, 222, 179, 152, 11} - }, - { {68, 17, 154, 175, 12}, - {200, 226, 71, 148, 3}, - {63, 85, 152, 130, 2}, - {194, 158, 36, 113, 3} - }, - { {48, 177, 90, 93, 2}, - {105, 122, 180, 28, 0}, - {75, 165, 168, 208, 12}, - {3, 130, 213, 233, 6} - }, - { {66, 57, 254, 118, 6}, - {105, 211, 198, 153, 13}, - {102, 231, 249, 196, 2}, - {185, 150, 60, 185, 6} - }, - { {108, 223, 228, 140, 12}, - {234, 167, 121, 160, 14}, - {51, 18, 127, 179, 6}, - {112, 89, 238, 85, 7} - }, - { {110, 159, 250, 19, 0}, - {226, 194, 188, 181, 13}, - {12, 133, 255, 151, 6}, - {186, 211, 212, 52, 7} - }, - { {251, 180, 205, 219, 9}, - {119, 169, 181, 239, 3}, - {157, 187, 50, 221, 15}, - {207, 122, 217, 94, 14} - }, - { {106, 94, 54, 11, 12}, - {66, 101, 105, 181, 13}, - {61, 6, 199, 165, 6}, - {186, 217, 106, 100, 2} - }, - { {187, 128, 110, 3, 5}, - {38, 73, 116, 71, 5}, - {172, 7, 96, 29, 13}, - {174, 34, 233, 38, 4} - }, - { {151, 235, 197, 191, 10}, - {185, 191, 155, 71, 11}, - {95, 218, 61, 126, 9}, - {222, 45, 159, 217, 13} - }, - { {15, 117, 226, 80, 2}, - {227, 214, 128, 105, 4}, - {64, 164, 122, 239, 0}, - {41, 96, 22, 188, 7} - }, - { {11, 49, 73, 156, 8}, - {123, 34, 133, 65, 2}, - {19, 153, 40, 205, 0}, - {72, 42, 20, 77, 14} - }, - { {215, 174, 213, 193, 8}, - {177, 137, 25, 255, 10}, - {24, 58, 183, 94, 11}, - {95, 249, 137, 24, 13} - }, - { {210, 248, 182, 187, 9}, - {69, 237, 147, 151, 15}, - {157, 214, 209, 244, 11}, - {254, 156, 155, 122, 2} - }, - { {34, 55, 245, 42, 9}, - {117, 163, 43, 49, 5}, - {149, 74, 254, 196, 4}, - {168, 205, 76, 90, 14} - }, - { {68, 127, 202, 153, 4}, - {225, 230, 204, 164, 10}, - {41, 149, 63, 226, 2}, - {82, 83, 54, 120, 7} - }, - { {141, 146, 126, 41, 15}, - {230, 113, 95, 86, 4}, - {249, 71, 228, 155, 1}, - {38, 175, 168, 230, 7} - }, - { {13, 54, 100, 46, 5}, - {239, 33, 74, 96, 5}, - {167, 66, 102, 203, 0}, - {160, 101, 40, 79, 7} - }, - { {213, 191, 237, 221, 12}, - {249, 171, 221, 238, 14}, - {59, 187, 127, 218, 11}, - {119, 123, 189, 89, 15} - }, - { {139, 198, 212, 229, 1}, - {46, 133, 26, 127, 2}, - {138, 114, 182, 61, 1}, - {79, 229, 138, 23, 4} - }, - { {221, 244, 124, 110, 1}, - {239, 45, 22, 250, 5}, - {135, 99, 226, 251, 11}, - {165, 246, 139, 79, 7} - }, - { {69, 159, 45, 147, 1}, - {212, 3, 156, 228, 15}, - {140, 155, 79, 154, 2}, - {242, 115, 156, 2, 11} - }, - { {134, 21, 175, 172, 5}, - {220, 227, 70, 35, 6}, - {163, 95, 90, 134, 1}, - {108, 70, 44, 115, 11} - }, - { {52, 174, 42, 121, 9}, - {133, 104, 191, 44, 12}, - {153, 229, 71, 82, 12}, - {51, 79, 209, 106, 1} - }, - { {246, 135, 29, 191, 4}, - {152, 43, 254, 183, 3}, - {47, 219, 142, 22, 15}, - {206, 215, 253, 65, 9} - }, - { {22, 183, 193, 11, 4}, - {241, 170, 88, 37, 1}, - {45, 8, 62, 214, 8}, - {138, 65, 165, 88, 15} - }, - { {122, 54, 203, 160, 5}, - {119, 200, 110, 161, 2}, - {160, 93, 54, 197, 14}, - {72, 87, 97, 62, 14} - }, - { {127, 66, 16, 98, 10}, - {130, 28, 43, 217, 1}, - {84, 96, 132, 47, 14}, - {137, 189, 67, 132, 1} - }, - { {103, 112, 117, 206, 8}, - {249, 37, 33, 217, 7}, - {23, 58, 224, 238, 6}, - {233, 184, 74, 73, 15} - }, - { {167, 70, 60, 209, 12}, - {128, 5, 237, 127, 6}, - {56, 179, 198, 46, 5}, - {111, 235, 122, 0, 1} - }, - { {153, 117, 131, 110, 2}, - {91, 254, 2, 106, 1}, - {71, 108, 26, 233, 9}, - {133, 100, 7, 253, 10} - }, - { {81, 145, 211, 21, 9}, - {124, 202, 145, 212, 0}, - {154, 140, 184, 152, 10}, - {2, 184, 149, 51, 14} - }, - { {53, 76, 2, 185, 8}, - {128, 108, 163, 100, 10}, - {25, 212, 3, 42, 12}, - {82, 108, 83, 96, 1} - }, - { {208, 27, 128, 57, 5}, - {68, 170, 202, 134, 8}, - {169, 192, 29, 128, 11}, - {22, 21, 53, 82, 2} - }, - { {123, 39, 171, 229, 3}, - {31, 218, 46, 237, 6}, - {202, 125, 94, 77, 14}, - {107, 119, 69, 191, 8} - }, - { {168, 140, 171, 169, 4}, - {18, 224, 118, 38, 14}, - {41, 93, 83, 17, 5}, - {118, 70, 224, 116, 8} - }, - { {138, 112, 46, 100, 13}, - {79, 69, 71, 11, 4}, - {178, 103, 64, 229, 1}, - {45, 14, 42, 47, 2} - }, - { {17, 144, 216, 79, 13}, - {108, 168, 85, 92, 1}, - {191, 33, 176, 152, 8}, - {131, 170, 161, 83, 6} - }, - { {168, 56, 156, 230, 8}, - {75, 129, 39, 26, 11}, - {22, 115, 145, 193, 5}, - {213, 142, 72, 29, 2} - }, - { {99, 172, 245, 8, 11}, - {53, 177, 49, 241, 12}, - {209, 10, 243, 92, 6}, - {56, 248, 200, 218, 12} - }, - { {153, 224, 174, 41, 11}, - {7, 253, 23, 70, 4}, - {217, 71, 80, 121, 9}, - {38, 46, 139, 254, 0} - }, - { {232, 139, 97, 157, 3}, - {62, 50, 184, 134, 14}, - {203, 152, 109, 17, 7}, - {118, 17, 212, 199, 12} - }, - { {17, 175, 187, 184, 0}, - {17, 234, 158, 112, 14}, - {1, 221, 223, 88, 8}, - {112, 231, 149, 120, 8} - }, - { {98, 250, 27, 120, 14}, - {81, 116, 255, 153, 8}, - {113, 237, 133, 244, 6}, - {25, 159, 242, 232, 10} - }, - { {241, 171, 236, 42, 5}, - {37, 171, 126, 194, 13}, - {165, 67, 125, 88, 15}, - {180, 55, 237, 90, 4} - }, - { {164, 120, 185, 64, 2}, - {209, 148, 36, 26, 12}, - {64, 41, 209, 226, 5}, - {53, 130, 66, 152, 11} - }, - { {78, 147, 112, 26, 3}, - {230, 50, 152, 145, 5}, - {197, 128, 236, 151, 2}, - {168, 145, 148, 198, 7} - }, - { {93, 31, 190, 33, 6}, - {194, 219, 78, 244, 12}, - {104, 71, 223, 139, 10}, - {50, 247, 45, 180, 3} - }, - { {222, 153, 178, 148, 7}, - {206, 218, 208, 147, 14}, - {226, 148, 217, 151, 11}, - {124, 144, 181, 183, 3} - }, - { {119, 8, 209, 116, 7}, - {188, 152, 226, 217, 8}, - {226, 232, 177, 14, 14}, - {25, 180, 113, 147, 13} - }, - { {187, 192, 140, 154, 8}, - {2, 173, 181, 67, 3}, - {21, 147, 16, 61, 13}, - {204, 42, 219, 84, 0} - }, - { {234, 28, 8, 68, 4}, - {74, 0, 100, 171, 8}, - {34, 33, 3, 133, 7}, - {29, 82, 96, 5, 2} - }, - { {184, 154, 172, 7, 8}, - {74, 137, 61, 6, 13}, - {30, 3, 85, 145, 13}, - {182, 11, 201, 21, 2} - }, - { {151, 79, 121, 58, 13}, - {180, 46, 207, 115, 13}, - {181, 201, 239, 46, 9}, - {188, 239, 55, 66, 13} - }, - { {124, 55, 168, 31, 9}, - {207, 170, 173, 164, 5}, - {159, 129, 94, 195, 14}, - {162, 91, 85, 95, 3} - }, - { {132, 5, 69, 184, 11}, - {180, 51, 131, 34, 2}, - {209, 218, 42, 2, 1}, - {68, 76, 28, 194, 13} - }, - { {137, 136, 232, 242, 10}, - {34, 144, 151, 74, 15}, - {84, 241, 113, 25, 1}, - {245, 46, 144, 148, 4} - }, - { {122, 5, 47, 138, 7}, - {22, 123, 100, 161, 7}, - {229, 31, 74, 5, 14}, - {232, 82, 109, 230, 8} - }, - { {192, 107, 231, 168, 3}, - {53, 247, 10, 130, 14}, - {193, 94, 125, 96, 3}, - {116, 21, 14, 250, 12} - }, - { {225, 214, 143, 229, 13}, - {92, 197, 127, 238, 2}, - {186, 127, 22, 184, 7}, - {71, 127, 234, 51, 10} - }, - { {135, 193, 94, 208, 1}, - {164, 71, 148, 91, 2}, - {128, 183, 168, 62, 1}, - {77, 162, 158, 34, 5} - }, - { {177, 141, 71, 4, 14}, - {56, 91, 113, 98, 8}, - {114, 14, 43, 24, 13}, - {20, 104, 237, 161, 12} - }, - { {185, 96, 48, 55, 10}, - {11, 28, 163, 86, 5}, - {94, 192, 192, 105, 13}, - {166, 172, 83, 141, 0} - }, - { {57, 22, 249, 242, 5}, - {118, 136, 238, 120, 7}, - {164, 249, 246, 137, 12}, - {225, 231, 113, 22, 14} - }, - { {91, 245, 56, 225, 10}, - {67, 30, 23, 253, 6}, - {88, 113, 202, 253, 10}, - {107, 254, 135, 140, 2} - }, - { {69, 2, 140, 137, 3}, - {132, 177, 12, 196, 2}, - {201, 19, 20, 10, 2}, - {66, 51, 8, 210, 1} - }, - { {139, 247, 79, 21, 14}, - {123, 87, 221, 103, 0}, - {122, 143, 46, 253, 1}, - {14, 107, 190, 173, 14} - }, - { {52, 169, 38, 14, 14}, - {137, 123, 113, 0, 13}, - {119, 6, 73, 82, 12}, - {176, 8, 237, 233, 1} - }, - { {254, 161, 180, 87, 2}, - {139, 155, 176, 159, 5}, - {78, 162, 216, 87, 15}, - {175, 144, 221, 157, 1} - }, - { {170, 172, 74, 33, 2}, - {35, 80, 54, 39, 8}, - {72, 69, 35, 85, 5}, - {30, 70, 192, 172, 4} - }, - { {199, 149, 142, 146, 10}, - {192, 211, 149, 227, 3}, - {84, 151, 26, 158, 3}, - {204, 122, 156, 176, 3} - }, - { {77, 114, 150, 13, 7}, - {207, 245, 72, 212, 0}, - {235, 6, 148, 235, 2}, - {2, 177, 42, 255, 3} - }, - { {198, 113, 60, 138, 13}, - {197, 39, 69, 147, 7}, - {181, 19, 200, 230, 3}, - {236, 154, 46, 74, 3} - }, - { {26, 219, 8, 147, 10}, - {66, 30, 157, 5, 11}, - {92, 145, 13, 181, 8}, - {218, 11, 151, 132, 2} - }, - { {235, 178, 94, 21, 3}, - {111, 81, 188, 215, 0}, - {202, 135, 164, 221, 7}, - {14, 179, 216, 175, 6} - }, - { {26, 140, 101, 49, 13}, - {54, 9, 211, 37, 12}, - {184, 202, 99, 21, 8}, - {58, 76, 185, 6, 12} - }, - { {184, 194, 144, 210, 13}, - {6, 140, 249, 26, 3}, - {180, 176, 148, 49, 13}, - {197, 137, 243, 22, 0} - }, - { {237, 10, 185, 59, 12}, - {146, 160, 239, 214, 13}, - {61, 201, 213, 11, 7}, - {182, 191, 112, 84, 9} - }, - { {53, 64, 254, 171, 14}, - {160, 253, 103, 84, 7}, - {125, 87, 240, 42, 12}, - {226, 174, 107, 240, 5} - }, - { {77, 141, 169, 163, 10}, - {146, 146, 23, 228, 15}, - {92, 89, 91, 27, 2}, - {242, 126, 132, 148, 9} - }, - { {25, 111, 83, 7, 9}, - {63, 78, 9, 116, 9}, - {158, 12, 175, 105, 8}, - {146, 233, 7, 47, 12} - }, - { {170, 161, 45, 108, 8}, - {27, 35, 55, 11, 4}, - {19, 107, 72, 85, 5}, - {45, 14, 204, 77, 8} - }, - { {212, 51, 219, 225, 6}, - {241, 218, 78, 158, 2}, - {104, 125, 188, 194, 11}, - {71, 151, 37, 184, 15} - }, - { {41, 68, 117, 210, 10}, - {50, 21, 161, 120, 7}, - {84, 186, 226, 41, 4}, - {225, 232, 90, 132, 12} - }, - { {222, 189, 217, 145, 6}, - {243, 154, 212, 183, 10}, - {104, 153, 187, 215, 11}, - {94, 210, 181, 156, 15} - }, - { {123, 98, 241, 8, 1}, - {55, 172, 40, 209, 4}, - {129, 8, 244, 109, 14}, - {40, 177, 67, 94, 12} - }, - { {16, 243, 23, 219, 8}, - {81, 111, 153, 28, 3}, - {29, 190, 140, 240, 8}, - {195, 137, 159, 104, 10} - }, - { {184, 117, 135, 64, 5}, - {87, 207, 96, 42, 0}, - {160, 46, 26, 225, 13}, - {5, 64, 111, 62, 10} - }, - { {154, 182, 79, 175, 0}, - {123, 105, 30, 39, 3}, - {15, 95, 38, 213, 9}, - {206, 71, 137, 109, 14} - }, - { {212, 4, 243, 160, 1}, - {180, 200, 2, 178, 6}, - {128, 92, 242, 2, 11}, - {100, 212, 1, 50, 13} - }, - { {225, 106, 123, 127, 5}, - {61, 100, 238, 222, 13}, - {175, 237, 229, 104, 7}, - {183, 183, 114, 107, 12} - }, - { {12, 218, 188, 26, 8}, - {194, 165, 157, 16, 13}, - {21, 131, 213, 179, 0}, - {176, 139, 154, 84, 3} - }, - { {4, 96, 183, 149, 11}, - {157, 213, 129, 20, 6}, - {218, 158, 208, 98, 0}, - {98, 136, 26, 187, 9} - }, - { {28, 206, 196, 143, 3}, - {174, 189, 24, 36, 11}, - {207, 18, 55, 51, 8}, - {210, 65, 139, 215, 5} - }, - { {35, 222, 229, 165, 12}, - {120, 133, 123, 101, 14}, - {58, 90, 119, 188, 4}, - {122, 109, 234, 17, 14} - }, - { {99, 5, 255, 232, 5}, - {52, 227, 102, 249, 6}, - {161, 127, 250, 12, 6}, - {105, 246, 108, 114, 12} - }, - { {225, 195, 39, 185, 4}, - {16, 103, 250, 198, 6}, - {41, 222, 76, 56, 7}, - {102, 53, 254, 96, 8} - }, - { {140, 43, 164, 30, 0}, - {139, 163, 136, 2, 13}, - {7, 130, 93, 67, 1}, - {180, 1, 28, 93, 1} - }, - { {28, 217, 222, 180, 0}, - {234, 207, 150, 16, 10}, - {2, 215, 185, 179, 8}, - {80, 134, 159, 53, 7} - }, - { {85, 100, 240, 183, 1}, - {173, 140, 130, 244, 7}, - {142, 208, 242, 106, 10}, - {226, 244, 19, 27, 5} - }, - { {176, 199, 42, 118, 10}, - {8, 94, 191, 42, 5}, - {86, 229, 78, 48, 13}, - {165, 79, 215, 161, 0} - }, - { {231, 13, 80, 70, 9}, - {172, 2, 33, 251, 9}, - {150, 32, 171, 14, 7}, - {157, 248, 68, 3, 5} - }, - { {50, 149, 124, 126, 3}, - {108, 59, 182, 57, 5}, - {199, 227, 234, 148, 12}, - {169, 198, 221, 195, 6} - }, - { {71, 130, 81, 20, 11}, - {188, 16, 153, 209, 0}, - {210, 136, 164, 30, 2}, - {8, 185, 144, 131, 13} - }, - { {120, 242, 17, 22, 15}, - {95, 28, 249, 144, 1}, - {246, 136, 132, 241, 14}, - {128, 153, 243, 143, 10} - }, - { {144, 251, 64, 119, 10}, - {105, 30, 155, 14, 9}, - {94, 224, 45, 240, 9}, - {151, 13, 151, 137, 6} - }, - { {162, 111, 219, 64, 3}, - {53, 214, 44, 59, 8}, - {192, 45, 191, 100, 5}, - {29, 195, 70, 186, 12} - }, - { {36, 36, 50, 53, 7}, - {141, 80, 226, 52, 4}, - {234, 196, 194, 66, 4}, - {34, 196, 112, 171, 1} - }, - { {135, 138, 180, 213, 3}, - {140, 145, 152, 95, 14}, - {202, 178, 213, 30, 1}, - {127, 161, 152, 147, 1} - }, - { {238, 169, 194, 236, 0}, - {171, 226, 50, 139, 10}, - {3, 116, 57, 87, 7}, - {93, 20, 196, 125, 5} - }, - { {194, 91, 81, 134, 10}, - {120, 22, 9, 147, 11}, - {86, 24, 173, 164, 3}, - {220, 153, 6, 129, 14} - }, - { {154, 26, 220, 22, 4}, - {106, 137, 204, 19, 9}, - {38, 131, 181, 133, 9}, - {156, 131, 57, 21, 6} - }, - { {115, 131, 224, 101, 1}, - {44, 138, 58, 205, 4}, - {138, 96, 124, 28, 14}, - {43, 53, 197, 19, 4} - }, - { {25, 137, 191, 169, 14}, - {18, 251, 87, 84, 14}, - {121, 95, 217, 25, 8}, - {114, 174, 173, 244, 8} - }, - { {29, 136, 219, 124, 13}, - {190, 232, 215, 88, 8}, - {179, 237, 177, 27, 8}, - {17, 174, 177, 119, 13} - }, - { {58, 57, 201, 98, 1}, - {119, 138, 38, 9, 9}, - {132, 105, 57, 197, 12}, - {153, 6, 69, 30, 14} - }, - { {196, 95, 111, 141, 1}, - {252, 103, 12, 166, 14}, - {139, 31, 111, 162, 3}, - {118, 83, 14, 99, 15} - }, - { {127, 212, 244, 88, 13}, - {230, 173, 241, 249, 4}, - {177, 162, 242, 191, 14}, - {41, 248, 251, 86, 7} - }, - { {235, 94, 5, 198, 13}, - {94, 5, 105, 235, 11}, - {182, 58, 7, 173, 7}, - {221, 121, 106, 7, 10} - }, - { {0, 222, 87, 31, 10}, - {120, 117, 153, 52, 9}, - {95, 142, 167, 176, 0}, - {146, 201, 154, 225, 14} - }, - { {48, 182, 57, 119, 2}, - {89, 24, 190, 60, 5}, - {78, 233, 198, 208, 12}, - {163, 199, 209, 137, 10} - }, - { {217, 17, 79, 9, 9}, - {118, 107, 5, 198, 0}, - {153, 15, 40, 137, 11}, - {6, 58, 13, 102, 14} - }, - { {11, 138, 78, 254, 13}, - {46, 97, 223, 73, 11}, - {183, 247, 37, 29, 0}, - {217, 47, 184, 103, 4} - }, - { {150, 200, 148, 195, 5}, - {132, 141, 80, 31, 11}, - {172, 50, 145, 54, 9}, - {223, 128, 171, 18, 1} - }, - { {222, 86, 207, 27, 9}, - {246, 237, 141, 167, 1}, - {157, 143, 54, 167, 11}, - {142, 91, 27, 118, 15} - }, - { {233, 20, 44, 234, 11}, - {70, 49, 39, 234, 7}, - {213, 115, 66, 137, 7}, - {229, 126, 72, 198, 2} - }, - { {166, 193, 149, 63, 7}, - {156, 183, 242, 23, 1}, - {239, 202, 152, 54, 5}, - {142, 132, 254, 211, 9} - }, - { {112, 249, 99, 53, 11}, - {125, 94, 179, 132, 12}, - {218, 204, 105, 240, 14}, - {50, 28, 215, 171, 14} - }, - { {60, 210, 112, 229, 12}, - {234, 12, 123, 28, 6}, - {58, 112, 228, 179, 12}, - {99, 141, 227, 5, 7} - }, - { {67, 44, 58, 151, 8}, - {9, 64, 133, 245, 15}, - {30, 149, 195, 76, 2}, - {250, 250, 16, 41, 0} - }, - { {96, 54, 213, 83, 2}, - {113, 145, 168, 188, 1}, - {76, 170, 182, 192, 6}, - {131, 209, 88, 152, 14} - }, - { {135, 118, 248, 196, 14}, - {233, 148, 77, 123, 6}, - {114, 49, 246, 238, 1}, - {109, 235, 34, 153, 7} - }, - { {32, 85, 144, 158, 12}, - {72, 166, 225, 48, 3}, - {55, 144, 154, 160, 4}, - {192, 200, 118, 81, 2} - }, - { {51, 149, 147, 18, 0}, - {80, 202, 176, 113, 1}, - {4, 140, 154, 156, 12}, - {136, 224, 213, 48, 10} - }, - { {61, 129, 11, 57, 10}, - {146, 122, 183, 68, 0}, - {89, 205, 8, 27, 12}, - {2, 46, 213, 228, 9} - }, - { {39, 67, 30, 187, 2}, - {128, 119, 174, 85, 3}, - {77, 215, 140, 46, 4}, - {202, 167, 94, 224, 1} - }, - { {240, 133, 185, 233, 2}, - {16, 186, 54, 190, 6}, - {73, 121, 218, 16, 15}, - {103, 214, 197, 208, 8} - }, - { {163, 11, 146, 226, 0}, - {0, 194, 42, 91, 11}, - {4, 116, 157, 12, 5}, - {221, 165, 68, 48, 0} - }, - { {220, 20, 37, 232, 5}, - {214, 41, 66, 170, 6}, - {161, 122, 66, 131, 11}, - {101, 84, 41, 70, 11} - }, - { {180, 171, 135, 231, 11}, - {157, 219, 59, 14, 11}, - {222, 126, 29, 82, 13}, - {215, 13, 205, 187, 9} - }, - { {143, 183, 62, 39, 0}, - {203, 67, 30, 119, 5}, - {14, 71, 206, 223, 1}, - {174, 231, 140, 45, 3} - }, - { {255, 211, 18, 95, 2}, - {202, 126, 184, 223, 1}, - {79, 164, 140, 191, 15}, - {143, 177, 215, 229, 3} - }, - { {5, 76, 149, 162, 10}, - {144, 149, 3, 112, 11}, - {84, 90, 147, 42, 0}, - {208, 236, 10, 144, 9} - }, - { {29, 121, 189, 67, 6}, - {211, 159, 68, 92, 13}, - {108, 43, 217, 235, 8}, - {179, 162, 47, 156, 11} - }, - { {49, 220, 189, 155, 11}, - {84, 189, 181, 116, 15}, - {221, 155, 211, 184, 12}, - {242, 234, 219, 210, 10} - }, - { {93, 131, 76, 204, 2}, - {170, 59, 28, 200, 2}, - {67, 51, 44, 27, 10}, - {65, 51, 141, 197, 5} - }, - { {184, 110, 95, 128, 10}, - {51, 93, 45, 50, 10}, - {80, 31, 167, 97, 13}, - {84, 203, 75, 172, 12} - }, - { {221, 181, 2, 55, 14}, - {203, 90, 211, 230, 1}, - {126, 196, 10, 219, 11}, - {134, 124, 181, 173, 3} - }, - { {204, 212, 249, 32, 2}, - {242, 148, 22, 178, 4}, - {64, 73, 242, 179, 3}, - {36, 214, 130, 148, 15} - }, - { {222, 175, 20, 123, 3}, - {135, 59, 154, 191, 9}, - {205, 226, 143, 87, 11}, - {159, 213, 157, 206, 1} - }, - { {157, 32, 19, 22, 15}, - {159, 88, 193, 82, 1}, - {246, 140, 128, 75, 9}, - {132, 168, 49, 175, 9} - }, - { {124, 189, 240, 175, 1}, - {239, 170, 50, 180, 15}, - {143, 80, 251, 211, 14}, - {242, 212, 197, 95, 7} - }, - { {34, 195, 216, 114, 10}, - {32, 150, 191, 25, 1}, - {84, 225, 188, 52, 4}, - {137, 143, 214, 144, 4} - }, - { {69, 199, 100, 214, 12}, - {168, 7, 217, 232, 7}, - {54, 178, 110, 58, 2}, - {225, 121, 190, 1, 5} - }, - { {141, 147, 90, 116, 0}, - {234, 66, 158, 90, 0}, - {2, 229, 172, 155, 1}, - {5, 167, 148, 37, 7} - }, - { {177, 161, 236, 16, 8}, - {33, 139, 181, 66, 4}, - {16, 131, 120, 88, 13}, - {36, 42, 221, 24, 4} - }, - { {208, 121, 53, 51, 6}, - {81, 31, 194, 150, 13}, - {108, 202, 201, 224, 11}, - {182, 148, 63, 136, 10} - }, - { {74, 81, 93, 164, 6}, - {122, 23, 70, 145, 2}, - {98, 91, 168, 165, 2}, - {72, 150, 46, 133, 14} - }, - { {58, 45, 19, 35, 5}, - {23, 74, 98, 53, 9}, - {172, 76, 139, 69, 12}, - {154, 196, 101, 46, 8} - }, - { {236, 65, 230, 22, 1}, - {174, 199, 160, 130, 5}, - {134, 134, 120, 35, 7}, - {164, 16, 94, 55, 5} - }, - { {127, 172, 78, 24, 2}, - {163, 121, 180, 225, 8}, - {65, 135, 35, 95, 14}, - {24, 114, 217, 236, 5} - }, - { {115, 198, 100, 107, 13}, - {36, 45, 123, 237, 5}, - {189, 98, 102, 60, 14}, - {171, 125, 235, 66, 4} - }, - { {77, 251, 76, 21, 7}, - {239, 23, 220, 196, 8}, - {234, 131, 45, 251, 2}, - {18, 51, 190, 143, 7} - }, - { {229, 15, 78, 90, 11}, - {164, 115, 173, 234, 9}, - {213, 167, 47, 10, 7}, - {149, 123, 92, 226, 5} - }, - { {122, 161, 57, 166, 0}, - {27, 10, 54, 145, 7}, - {6, 89, 200, 85, 14}, - {232, 150, 197, 13, 8} - }, - { {18, 122, 254, 87, 13}, - {109, 205, 205, 29, 13}, - {190, 167, 245, 228, 8}, - {187, 139, 59, 59, 6} - }, - { {168, 230, 106, 159, 15}, - {47, 116, 253, 38, 7}, - {255, 149, 102, 113, 5}, - {230, 75, 242, 239, 4} - }, - { {89, 155, 96, 189, 8}, - {106, 42, 155, 196, 14}, - {27, 208, 109, 153, 10}, - {114, 61, 149, 69, 6} - }, - { {49, 234, 54, 102, 9}, - {13, 77, 59, 88, 13}, - {150, 102, 197, 120, 12}, - {177, 173, 203, 43, 0} - }, - { {55, 203, 228, 166, 3}, - {172, 159, 58, 65, 15}, - {198, 82, 125, 62, 12}, - {248, 37, 207, 147, 5} - }, - { {134, 60, 248, 42, 13}, - {229, 160, 71, 51, 13}, - {181, 65, 243, 198, 1}, - {188, 206, 32, 90, 7} - }, - { {14, 181, 149, 30, 6}, - {219, 179, 208, 49, 1}, - {103, 138, 154, 215, 0}, - {136, 192, 188, 221, 11} - }, - { {197, 251, 85, 118, 12}, - {249, 7, 219, 218, 9}, - {54, 234, 173, 250, 3}, - {149, 189, 190, 9, 15} - }, - { {123, 128, 210, 239, 5}, - {46, 232, 114, 221, 3}, - {175, 116, 176, 29, 14}, - {203, 180, 225, 119, 4} - }, - { {195, 179, 195, 50, 10}, - {113, 210, 155, 195, 1}, - {84, 204, 60, 220, 3}, - {140, 61, 148, 184, 14} - }, - { {103, 62, 26, 116, 11}, - {205, 80, 175, 249, 8}, - {210, 229, 135, 206, 6}, - {25, 255, 80, 171, 3} - }, - { {146, 201, 210, 150, 1}, - {44, 206, 144, 19, 11}, - {134, 148, 185, 52, 9}, - {220, 128, 151, 51, 4} - }, - { {127, 25, 25, 12, 11}, - {222, 58, 37, 209, 8}, - {211, 9, 137, 143, 14}, - {24, 186, 69, 199, 11} - }, - { {144, 112, 164, 195, 10}, - {65, 157, 1, 14, 7}, - {92, 50, 80, 224, 9}, - {231, 8, 11, 152, 2} - }, - { {113, 67, 83, 178, 1}, - {52, 78, 170, 208, 3}, - {132, 220, 172, 40, 14}, - {192, 181, 87, 34, 12} - }, - { {251, 75, 181, 78, 15}, - {30, 191, 105, 219, 13}, - {247, 42, 221, 45, 15}, - {189, 185, 111, 215, 8} - }, - { {197, 129, 104, 9, 8}, - {160, 34, 21, 198, 4}, - {25, 1, 104, 26, 3}, - {38, 58, 132, 64, 5} - }, - { {43, 241, 86, 121, 0}, - {99, 103, 178, 93, 0}, - {9, 230, 168, 253, 4}, - {11, 164, 222, 108, 6} - }, - { {139, 50, 134, 244, 8}, - {75, 193, 139, 75, 2}, - {18, 246, 20, 205, 1}, - {77, 45, 24, 61, 2} - }, - { {35, 88, 1, 235, 6}, - {80, 52, 98, 77, 11}, - {109, 120, 1, 172, 4}, - {219, 36, 98, 192, 10} - }, - { {42, 83, 143, 50, 8}, - {82, 199, 175, 1, 1}, - {20, 207, 28, 165, 4}, - {136, 15, 94, 52, 10} - }, - { {16, 109, 126, 84, 8}, - {41, 79, 133, 56, 12}, - {18, 167, 235, 96, 8}, - {49, 202, 31, 41, 4} - }, - { {70, 253, 197, 231, 10}, - {249, 151, 19, 173, 11}, - {94, 122, 59, 246, 2}, - {219, 92, 142, 153, 15} - }, - { {69, 106, 10, 93, 0}, - {137, 100, 140, 204, 8}, - {11, 165, 5, 106, 2}, - {19, 51, 18, 105, 1} - }, - { {217, 50, 39, 125, 10}, - {91, 121, 139, 206, 4}, - {91, 238, 68, 201, 11}, - {39, 61, 25, 237, 10} - }, - { {97, 59, 235, 152, 1}, - {117, 226, 172, 192, 14}, - {129, 157, 125, 200, 6}, - {112, 51, 84, 122, 14} - }, - { {191, 19, 181, 70, 0}, - {218, 139, 40, 91, 5}, - {6, 42, 220, 143, 13}, - {173, 161, 77, 21, 11} - }, - { {130, 186, 168, 203, 13}, - {69, 160, 93, 15, 15}, - {189, 49, 85, 212, 1}, - {255, 11, 160, 90, 2} - }, - { {81, 205, 99, 27, 3}, - {52, 126, 144, 228, 13}, - {205, 140, 107, 56, 10}, - {178, 112, 151, 226, 12} - }, - { {112, 171, 240, 76, 7}, - {45, 186, 120, 152, 12}, - {227, 32, 253, 80, 14}, - {49, 145, 229, 219, 4} - }, - { {170, 102, 185, 24, 7}, - {23, 180, 236, 51, 4}, - {225, 137, 214, 101, 5}, - {44, 195, 114, 222, 8} - }, - { {140, 20, 60, 97, 6}, - {194, 17, 70, 62, 4}, - {104, 99, 194, 131, 1}, - {39, 198, 40, 132, 3} - }, - { {241, 160, 157, 75, 0}, - {17, 169, 52, 222, 1}, - {13, 43, 144, 88, 15}, - {135, 178, 201, 88, 8} - }, - { {95, 27, 95, 220, 4}, - {250, 107, 204, 217, 10}, - {35, 191, 173, 143, 10}, - {89, 179, 61, 101, 15} - }, - { {104, 72, 189, 153, 8}, - {18, 165, 165, 148, 14}, - {25, 155, 209, 33, 6}, - {114, 154, 90, 84, 8} - }, - { {235, 245, 28, 134, 10}, - {75, 23, 53, 243, 3}, - {86, 19, 138, 253, 7}, - {204, 250, 206, 141, 2} - }, - { {201, 92, 23, 53, 15}, - {94, 85, 195, 246, 8}, - {250, 206, 131, 169, 3}, - {22, 252, 58, 167, 10} - }, - { {164, 229, 178, 170, 9}, - {133, 230, 51, 50, 7}, - {149, 84, 218, 114, 5}, - {228, 204, 198, 122, 1} - }, - { {137, 187, 40, 109, 9}, - {79, 34, 31, 78, 12}, - {155, 97, 77, 217, 1}, - {55, 47, 132, 79, 2} - }, - { {252, 83, 211, 81, 9}, - {246, 206, 169, 158, 0}, - {152, 172, 188, 163, 15}, - {7, 153, 87, 54, 15} - }, - { {132, 250, 176, 250, 11}, - {197, 180, 155, 26, 15}, - {213, 240, 213, 242, 1}, - {245, 141, 146, 218, 3} - }, - { {135, 156, 9, 173, 7}, - {220, 48, 86, 103, 10}, - {235, 89, 3, 158, 1}, - {94, 102, 160, 195, 11} - }, - { {84, 19, 120, 184, 10}, - {224, 58, 143, 144, 6}, - {81, 209, 236, 130, 10}, - {96, 159, 21, 192, 7} - }, - { {93, 207, 118, 108, 5}, - {174, 111, 90, 248, 12}, - {163, 102, 239, 59, 10}, - {49, 245, 175, 103, 5} - }, - { {15, 128, 112, 181, 8}, - {170, 0, 147, 85, 6}, - {26, 208, 224, 31, 0}, - {106, 172, 144, 5, 5} - }, - { {208, 181, 28, 15, 14}, - {73, 59, 85, 182, 1}, - {127, 3, 138, 208, 11}, - {134, 218, 173, 201, 2} - }, - { {132, 202, 160, 119, 12}, - {136, 132, 219, 14, 13}, - {62, 224, 85, 50, 1}, - {183, 13, 178, 17, 1} - }, - { {237, 211, 180, 253, 12}, - {202, 167, 251, 222, 6}, - {59, 242, 220, 187, 7}, - {103, 189, 254, 85, 3} - }, - { {54, 161, 151, 8, 4}, - {145, 235, 112, 17, 0}, - {33, 14, 152, 86, 12}, - {8, 128, 237, 120, 9} - }, - { {140, 65, 87, 242, 7}, - {182, 87, 194, 26, 3}, - {228, 254, 168, 35, 1}, - {197, 132, 62, 166, 13} - }, - { {236, 146, 34, 145, 13}, - {198, 64, 249, 134, 6}, - {184, 148, 68, 147, 7}, - {102, 25, 240, 38, 3} - }, - { {44, 51, 74, 17, 10}, - {227, 82, 173, 4, 0}, - {88, 133, 44, 195, 4}, - {2, 11, 84, 172, 7} - }, - { {149, 119, 148, 119, 8}, - {201, 143, 139, 126, 1}, - {30, 226, 158, 234, 9}, - {135, 237, 31, 25, 3} - }, - { {79, 158, 57, 137, 13}, - {214, 32, 93, 245, 14}, - {185, 25, 199, 159, 2}, - {122, 251, 160, 70, 11} - }, - { {181, 203, 34, 59, 11}, - {132, 126, 187, 70, 13}, - {221, 196, 77, 58, 13}, - {182, 45, 215, 226, 1} - }, - { {238, 226, 189, 59, 11}, - {151, 181, 191, 151, 5}, - {221, 203, 212, 119, 7}, - {174, 159, 218, 222, 9} - }, - { {133, 212, 25, 195, 12}, - {208, 4, 85, 126, 3}, - {60, 57, 130, 186, 1}, - {199, 234, 162, 0, 11} - }, - { {186, 35, 215, 19, 11}, - {55, 219, 169, 23, 1}, - {220, 142, 188, 69, 13}, - {142, 137, 93, 190, 12} - }, - { {141, 72, 238, 157, 1}, - {174, 229, 132, 70, 14}, - {139, 151, 113, 43, 1}, - {118, 34, 26, 119, 5} - }, - { {120, 69, 116, 14, 9}, - {46, 47, 33, 176, 5}, - {151, 2, 234, 33, 14}, - {160, 216, 79, 71, 4} - }, - { {100, 231, 234, 47, 8}, - {169, 230, 63, 164, 5}, - {31, 69, 126, 114, 6}, - {162, 95, 198, 121, 5} - }, - { {100, 118, 246, 62, 4}, - {233, 229, 234, 176, 5}, - {39, 198, 246, 226, 6}, - {160, 213, 122, 121, 7} - }, - { {14, 238, 158, 149, 0}, - {139, 197, 156, 53, 10}, - {10, 151, 151, 119, 0}, - {90, 195, 154, 61, 1} - }, - { {220, 175, 168, 110, 11}, - {143, 186, 31, 170, 13}, - {215, 97, 95, 83, 11}, - {181, 95, 133, 223, 1} - }, - { {135, 109, 56, 242, 14}, - {129, 22, 199, 123, 15}, - {116, 241, 203, 110, 1}, - {253, 238, 54, 136, 1} - }, - { {59, 235, 39, 76, 5}, - {31, 111, 120, 73, 12}, - {163, 46, 77, 125, 12}, - {57, 33, 239, 111, 8} - }, - { {184, 191, 89, 23, 4}, - {123, 10, 252, 54, 9}, - {46, 137, 175, 209, 13}, - {150, 195, 245, 13, 14} - }, - { {171, 32, 236, 13, 0}, - {43, 161, 36, 71, 4}, - {11, 3, 112, 77, 5}, - {46, 34, 72, 93, 4} - }, - { {13, 85, 26, 83, 3}, - {198, 86, 132, 124, 1}, - {204, 165, 138, 171, 0}, - {131, 226, 22, 166, 3} - }, - { {164, 73, 46, 167, 8}, - {136, 71, 39, 6, 15}, - {30, 87, 73, 34, 5}, - {246, 14, 78, 33, 1} - }, - { {93, 108, 92, 103, 6}, - {171, 29, 70, 252, 9}, - {110, 99, 163, 107, 10}, - {147, 246, 43, 141, 5} - }, - { {153, 4, 153, 70, 10}, - {26, 152, 5, 122, 1}, - {86, 41, 146, 9, 9}, - {133, 234, 1, 149, 8} - }, - { {10, 157, 128, 226, 6}, - {66, 146, 82, 41, 11}, - {100, 112, 27, 149, 0}, - {217, 68, 164, 148, 2} - }, - { {105, 134, 183, 41, 15}, - {22, 241, 123, 244, 4}, - {249, 78, 214, 25, 6}, - {34, 253, 232, 246, 8} - }, - { {143, 173, 130, 44, 13}, - {143, 226, 83, 99, 8}, - {179, 68, 27, 95, 1}, - {28, 108, 164, 127, 1} - }, - { {226, 67, 95, 38, 3}, - {60, 87, 46, 147, 1}, - {198, 79, 172, 36, 7}, - {140, 151, 78, 163, 12} - }, - { {209, 155, 215, 78, 11}, - {124, 251, 25, 218, 9}, - {215, 46, 189, 152, 11}, - {149, 185, 141, 243, 14} - }, - { {196, 218, 25, 133, 15}, - {220, 20, 93, 150, 10}, - {250, 25, 133, 178, 3}, - {86, 155, 162, 131, 11} - }, - { {26, 193, 30, 251, 4}, - {2, 111, 214, 29, 3}, - {45, 247, 136, 53, 8}, - {203, 134, 191, 100, 0} - }, - { {17, 45, 28, 17, 1}, - {5, 11, 132, 116, 8}, - {136, 131, 139, 72, 8}, - {18, 226, 29, 10, 0} - }, - { {45, 76, 8, 144, 6}, - {130, 20, 228, 96, 10}, - {96, 145, 3, 43, 4}, - {80, 98, 114, 132, 1} - }, - { {108, 235, 79, 250, 4}, - {179, 103, 254, 136, 11}, - {37, 255, 45, 115, 6}, - {209, 23, 254, 108, 13} - }, - { {171, 203, 198, 115, 11}, - {38, 215, 187, 79, 9}, - {220, 230, 61, 61, 5}, - {159, 45, 222, 182, 4} - }, - { {40, 32, 233, 173, 13}, - {63, 160, 103, 4, 6}, - {187, 89, 112, 65, 4}, - {98, 14, 96, 95, 12} - }, - { {127, 250, 209, 178, 2}, - {243, 156, 186, 209, 11}, - {68, 216, 181, 255, 14}, - {216, 181, 211, 156, 15} - }, - { {27, 102, 106, 88, 7}, - {39, 124, 204, 105, 4}, - {225, 165, 102, 109, 8}, - {41, 99, 51, 238, 4} - }, - { {164, 137, 72, 44, 7}, - {172, 50, 118, 2, 8}, - {227, 65, 41, 18, 5}, - {20, 6, 228, 195, 5} - }, - { {141, 42, 28, 71, 0}, - {139, 1, 12, 94, 9}, - {14, 35, 133, 75, 1}, - {151, 163, 8, 13, 1} - }, - { {125, 182, 84, 216, 0}, - {227, 41, 184, 248, 2}, - {1, 178, 166, 219, 14}, - {65, 241, 217, 76, 7} - }, - { {179, 222, 10, 136, 11}, - {68, 124, 61, 99, 10}, - {209, 21, 7, 188, 13}, - {92, 107, 195, 226, 2} - }, - { {214, 120, 202, 34, 1}, - {229, 204, 6, 131, 9}, - {132, 69, 49, 230, 11}, - {156, 22, 3, 58, 7} - }, - { {136, 97, 82, 97, 11}, - {39, 86, 3, 30, 0}, - {216, 100, 168, 97, 1}, - {7, 140, 6, 174, 4} - }, - { {0, 238, 90, 84, 7}, - {45, 84, 220, 56, 8}, - {226, 165, 167, 112, 0}, - {17, 195, 178, 171, 4} - }, - { {84, 105, 30, 139, 4}, - {129, 111, 68, 148, 11}, - {45, 23, 137, 98, 10}, - {210, 146, 47, 104, 1} - }, - { {128, 254, 13, 106, 8}, - {81, 37, 31, 42, 9}, - {21, 107, 7, 240, 1}, - {149, 79, 138, 72, 10} - }, - { {195, 127, 154, 209, 2}, - {65, 214, 140, 255, 10}, - {72, 181, 159, 236, 3}, - {95, 243, 22, 184, 2} - }, - { {57, 235, 155, 203, 12}, - {19, 238, 125, 92, 11}, - {61, 61, 157, 121, 12}, - {211, 171, 231, 124, 8} - }, - { {156, 205, 246, 33, 8}, - {162, 207, 19, 54, 12}, - {24, 70, 251, 51, 9}, - {54, 204, 143, 52, 5} - }, - { {83, 192, 184, 251, 11}, - {4, 188, 151, 221, 7}, - {221, 241, 208, 60, 10}, - {235, 190, 147, 210, 0} - }, - { {159, 93, 81, 153, 0}, - {242, 46, 128, 119, 10}, - {9, 152, 171, 175, 9}, - {94, 224, 23, 68, 15} - }, - { {208, 152, 110, 185, 3}, - {100, 121, 150, 134, 14}, - {201, 215, 97, 144, 11}, - {118, 22, 153, 226, 6} - }, - { {21, 133, 42, 206, 6}, - {136, 122, 84, 104, 7}, - {103, 53, 74, 26, 8}, - {225, 98, 165, 225, 1} - }, - { {144, 212, 121, 19, 3}, - {116, 28, 148, 54, 5}, - {204, 137, 226, 176, 9}, - {166, 194, 147, 130, 14} - }, - { {44, 22, 172, 202, 12}, - {194, 161, 109, 40, 7}, - {53, 51, 86, 131, 4}, - {225, 75, 104, 84, 3} - }, - { {112, 226, 233, 181, 14}, - {57, 156, 255, 132, 6}, - {122, 217, 116, 112, 14}, - {98, 31, 243, 153, 12} - }, - { {36, 160, 106, 178, 15}, - {165, 80, 247, 0, 7}, - {244, 213, 96, 82, 4}, - {224, 14, 240, 170, 5} - }, - { {114, 1, 237, 44, 10}, - {56, 187, 39, 129, 4}, - {83, 75, 120, 4, 14}, - {40, 30, 77, 209, 12} - }, - { {76, 99, 9, 66, 8}, - {147, 6, 13, 136, 1}, - {20, 41, 12, 99, 2}, - {129, 27, 6, 12, 9} - }, - { {6, 185, 203, 230, 13}, - {253, 194, 87, 9, 11}, - {182, 125, 57, 214, 0}, - {217, 14, 164, 59, 15} - }, - { {215, 246, 104, 218, 12}, - {225, 44, 221, 235, 7}, - {53, 177, 102, 254, 11}, - {237, 123, 179, 72, 7} - }, - { {129, 109, 246, 113, 1}, - {37, 199, 130, 126, 12}, - {136, 230, 251, 104, 1}, - {55, 228, 30, 58, 4} - }, - { {88, 117, 65, 240, 5}, - {119, 14, 194, 168, 2}, - {160, 248, 42, 225, 10}, - {65, 84, 55, 14, 14} - }, - { {74, 115, 6, 33, 0}, - {67, 71, 10, 133, 0}, - {8, 70, 12, 229, 2}, - {10, 21, 14, 44, 2} - }, - { {72, 9, 86, 13, 1}, - {46, 99, 0, 148, 8}, - {139, 6, 169, 1, 2}, - {18, 144, 12, 103, 4} - }, - { {59, 153, 72, 245, 11}, - {110, 26, 183, 77, 10}, - {218, 241, 41, 157, 12}, - {91, 46, 213, 135, 6} - }, - { {54, 150, 213, 148, 6}, - {248, 153, 248, 49, 2}, - {98, 154, 182, 150, 12}, - {72, 193, 249, 145, 15} - }, - { {81, 34, 220, 136, 12}, - {33, 169, 77, 208, 2}, - {49, 19, 180, 72, 10}, - {64, 187, 41, 88, 4} - }, - { {168, 69, 58, 219, 0}, - {2, 102, 164, 62, 7}, - {13, 181, 202, 33, 5}, - {231, 194, 86, 100, 0} - }, - { {180, 120, 208, 7, 12}, - {233, 140, 97, 22, 9}, - {62, 0, 177, 226, 13}, - {150, 136, 99, 25, 7} - }, - { {213, 175, 225, 90, 1}, - {181, 170, 152, 234, 13}, - {133, 168, 127, 90, 11}, - {181, 113, 149, 90, 13} - }, - { {4, 255, 169, 156, 13}, - {221, 166, 221, 32, 14}, - {179, 153, 95, 242, 0}, - {112, 75, 182, 91, 11} - }, - { {178, 170, 134, 106, 1}, - {5, 233, 58, 11, 9}, - {133, 102, 21, 84, 13}, - {157, 5, 201, 122, 0} - }, - { {33, 202, 57, 14, 7}, - {28, 52, 124, 80, 13}, - {231, 9, 197, 56, 4}, - {176, 163, 226, 195, 8} - }, - { {217, 149, 56, 136, 0}, - {66, 42, 20, 242, 6}, - {1, 17, 202, 153, 11}, - {100, 242, 133, 68, 2} - }, - { {158, 66, 134, 121, 2}, - {130, 253, 138, 15, 0}, - {73, 230, 20, 39, 9}, - {15, 5, 27, 244, 1} - }, - { {35, 126, 230, 40, 15}, - {101, 245, 107, 97, 12}, - {241, 70, 119, 236, 4}, - {56, 109, 106, 250, 6} - }, - { {223, 217, 194, 111, 1}, - {238, 238, 18, 207, 9}, - {143, 100, 57, 191, 11}, - {159, 52, 135, 119, 7} - }, - { {134, 37, 95, 146, 14}, - {177, 83, 197, 51, 3}, - {116, 159, 170, 70, 1}, - {204, 202, 60, 168, 13} - }, - { {89, 24, 159, 35, 8}, - {82, 201, 7, 212, 9}, - {28, 79, 145, 137, 10}, - {146, 190, 9, 52, 10} - }, - { {83, 168, 9, 113, 7}, - {21, 24, 214, 205, 8}, - {232, 233, 1, 92, 10}, - {27, 54, 177, 138, 8} - }, - { {8, 171, 22, 118, 15}, - {15, 83, 219, 24, 9}, - {246, 230, 141, 81, 0}, - {145, 141, 188, 175, 0} - }, - { {249, 140, 152, 28, 13}, - {14, 168, 245, 242, 8}, - {179, 129, 147, 25, 15}, - {20, 250, 241, 87, 0} - }, - { {249, 136, 239, 149, 11}, - {62, 217, 181, 198, 14}, - {218, 159, 113, 25, 15}, - {118, 58, 217, 183, 12} - }, - { {31, 2, 254, 30, 14}, - {170, 249, 205, 81, 5}, - {119, 135, 244, 15, 8}, - {168, 171, 57, 245, 5} - }, - { {104, 234, 254, 163, 13}, - {39, 197, 127, 148, 15}, - {188, 87, 245, 113, 6}, - {242, 159, 234, 62, 4} - }, - { {126, 145, 79, 240, 6}, - {242, 91, 246, 137, 2}, - {96, 255, 40, 151, 14}, - {73, 22, 253, 164, 15} - }, - { {44, 149, 20, 92, 13}, - {206, 35, 241, 56, 0}, - {179, 162, 138, 147, 4}, - {1, 200, 252, 71, 3} - }, - { {62, 147, 44, 208, 8}, - {194, 11, 189, 9, 6}, - {16, 179, 76, 151, 12}, - {105, 11, 221, 4, 3} - }, - { {183, 92, 175, 40, 3}, - {212, 253, 38, 99, 12}, - {193, 79, 83, 174, 13}, - {60, 102, 75, 242, 11} - }, - { {186, 245, 114, 60, 11}, - {111, 126, 179, 51, 4}, - {211, 196, 234, 245, 13}, - {44, 204, 215, 239, 6} - }, - { {121, 203, 18, 140, 13}, - {14, 110, 121, 208, 10}, - {179, 20, 141, 57, 14}, - {80, 185, 231, 103, 0} - }, - { {64, 87, 46, 30, 0}, - {72, 103, 140, 160, 5}, - {7, 135, 78, 160, 2}, - {160, 83, 30, 97, 2} - }, - { {82, 212, 150, 97, 15}, - {68, 221, 83, 189, 0}, - {248, 102, 146, 180, 10}, - {11, 220, 171, 178, 2} - }, - { {177, 112, 92, 13, 10}, - {105, 61, 37, 86, 0}, - {91, 3, 160, 232, 13}, - {6, 170, 75, 201, 6} - }, - { {99, 49, 219, 103, 3}, - {125, 210, 38, 221, 1}, - {206, 109, 184, 204, 6}, - {139, 182, 68, 187, 14} - }, - { {16, 154, 188, 196, 1}, - {76, 137, 28, 24, 14}, - {130, 51, 213, 144, 8}, - {113, 131, 137, 19, 2} - }, - { {96, 60, 124, 104, 15}, - {101, 49, 103, 184, 12}, - {241, 99, 227, 192, 6}, - {49, 222, 104, 202, 6} - }, - { {115, 50, 161, 255, 11}, - {93, 184, 171, 205, 7}, - {223, 248, 84, 204, 14}, - {235, 61, 81, 219, 10} - }, - { {61, 143, 96, 230, 4}, - {170, 10, 122, 104, 15}, - {38, 112, 111, 27, 12}, - {241, 101, 229, 5, 5} - }, - { {202, 97, 172, 50, 9}, - {7, 135, 135, 131, 5}, - {148, 195, 88, 101, 3}, - {172, 30, 30, 30, 0} - }, - { {132, 25, 50, 28, 14}, - {200, 114, 193, 18, 12}, - {115, 132, 201, 130, 1}, - {52, 136, 52, 225, 3} - }, - { {171, 167, 29, 138, 7}, - {23, 51, 124, 115, 3}, - {229, 27, 142, 93, 5}, - {204, 227, 236, 206, 8} - }, - { {220, 105, 76, 91, 7}, - {167, 63, 196, 142, 9}, - {237, 163, 41, 99, 11}, - {151, 18, 63, 206, 5} - }, - { {244, 156, 252, 224, 5}, - {228, 137, 118, 186, 14}, - {160, 115, 243, 146, 15}, - {117, 214, 233, 18, 7} - }, - { {200, 69, 243, 60, 3}, - {62, 246, 130, 178, 4}, - {195, 204, 250, 33, 3}, - {36, 212, 22, 247, 12} - }, - { {104, 74, 251, 82, 12}, - {50, 196, 237, 152, 13}, - {52, 173, 245, 33, 6}, - {177, 155, 114, 52, 12} - }, - { {214, 20, 91, 29, 3}, - {252, 120, 132, 183, 0}, - {203, 141, 162, 134, 11}, - {14, 210, 17, 227, 15} - }, - { {16, 253, 223, 173, 12}, - {121, 239, 87, 52, 10}, - {59, 95, 187, 240, 8}, - {82, 206, 175, 121, 14} - }, - { {31, 139, 72, 20, 8}, - {170, 10, 157, 65, 8}, - {18, 129, 45, 31, 8}, - {24, 43, 149, 5, 5} - }, - { {156, 50, 163, 233, 3}, - {215, 248, 10, 14, 6}, - {201, 124, 84, 195, 9}, - {103, 5, 1, 254, 11} - }, - { {157, 108, 124, 56, 7}, - {167, 61, 198, 114, 12}, - {225, 195, 227, 107, 9}, - {52, 230, 59, 206, 5} - }, - { {183, 3, 177, 221, 5}, - {156, 170, 232, 95, 6}, - {171, 184, 220, 14, 13}, - {111, 161, 117, 83, 9} - }, - { {111, 221, 59, 152, 0}, - {210, 102, 180, 241, 14}, - {1, 157, 203, 191, 6}, - {120, 242, 214, 100, 11} - }, - { {178, 62, 176, 218, 4}, - {65, 168, 232, 59, 15}, - {37, 176, 215, 196, 13}, - {253, 193, 113, 88, 2} - }, - { {115, 173, 103, 162, 0}, - {49, 75, 50, 225, 15}, - {4, 94, 107, 92, 14}, - {248, 116, 205, 40, 12} - }, - { {83, 97, 3, 190, 8}, - {25, 110, 131, 193, 3}, - {23, 220, 8, 108, 10}, - {200, 60, 23, 105, 8} - }, - { {214, 122, 66, 252, 3}, - {237, 124, 138, 139, 10}, - {195, 244, 37, 230, 11}, - {93, 21, 19, 235, 7} - }, - { {31, 40, 119, 191, 6}, - {187, 121, 194, 85, 15}, - {111, 222, 225, 79, 8}, - {250, 164, 57, 237, 13} - }, - { {45, 103, 170, 137, 12}, - {131, 230, 109, 100, 6}, - {57, 21, 94, 107, 4}, - {98, 107, 102, 124, 1} - }, - { {168, 55, 137, 133, 0}, - {91, 130, 44, 38, 2}, - {10, 25, 30, 193, 5}, - {70, 67, 68, 29, 10} - }, - { {47, 235, 63, 25, 12}, - {147, 103, 253, 85, 12}, - {57, 143, 205, 127, 4}, - {58, 171, 254, 108, 9} - }, - { {41, 160, 212, 118, 5}, - {47, 129, 242, 88, 1}, - {166, 226, 176, 89, 4}, - {129, 164, 248, 31, 4} - }, - { {65, 177, 230, 167, 15}, - {109, 211, 83, 196, 7}, - {254, 86, 120, 216, 2}, - {226, 60, 172, 187, 6} - }, - { {248, 93, 126, 140, 7}, - {110, 127, 100, 178, 14}, - {227, 23, 235, 161, 15}, - {116, 210, 111, 231, 6} - }, - { {133, 48, 143, 35, 4}, - {209, 193, 70, 70, 1}, - {44, 79, 16, 202, 1}, - {134, 38, 40, 56, 11} - }, - { {121, 131, 156, 71, 5}, - {14, 139, 124, 220, 1}, - {174, 35, 156, 25, 14}, - {131, 179, 237, 23, 0} - }, - { {75, 124, 102, 113, 8}, - {99, 69, 131, 237, 12}, - {24, 230, 99, 237, 2}, - {59, 124, 26, 44, 6} - }, - { {231, 237, 2, 5, 2}, - {137, 86, 48, 231, 8}, - {74, 4, 11, 126, 7}, - {30, 112, 198, 169, 1} - }, - { {57, 33, 5, 61, 13}, - {31, 43, 227, 68, 0}, - {187, 202, 8, 73, 12}, - {2, 44, 125, 79, 8} - }, - { {46, 92, 218, 7, 3}, - {238, 212, 36, 53, 9}, - {206, 5, 179, 167, 4}, - {154, 194, 66, 183, 7} - }, - { {191, 65, 151, 115, 14}, - {146, 223, 227, 95, 1}, - {124, 238, 152, 47, 13}, - {143, 172, 127, 180, 9} - }, - { {229, 242, 167, 113, 14}, - {209, 213, 251, 206, 4}, - {120, 238, 84, 250, 7}, - {39, 61, 250, 184, 11} - }, - { {189, 13, 222, 45, 14}, - {170, 251, 103, 118, 8}, - {123, 71, 187, 11, 13}, - {22, 238, 109, 245, 5} - }, - { {104, 73, 143, 23, 14}, - {26, 215, 229, 132, 9}, - {126, 143, 25, 33, 6}, - {146, 26, 126, 181, 8} - }, - { {183, 56, 155, 230, 11}, - {221, 216, 39, 91, 11}, - {214, 125, 145, 206, 13}, - {221, 174, 65, 187, 11} - }, - { {41, 127, 247, 175, 7}, - {127, 247, 106, 116, 15}, - {239, 94, 255, 233, 4}, - {242, 229, 110, 255, 14} - }, - { {177, 201, 139, 131, 7}, - {20, 222, 116, 70, 11}, - {236, 29, 25, 56, 13}, - {214, 34, 231, 178, 8} - }, - { {112, 239, 122, 3, 2}, - {33, 94, 60, 180, 13}, - {76, 5, 239, 112, 14}, - {178, 211, 199, 168, 4} - }, - { {155, 66, 34, 76, 0}, - {10, 108, 8, 75, 4}, - {3, 36, 68, 45, 9}, - {45, 33, 3, 101, 0} - }, - { {45, 220, 24, 13, 15}, - {206, 52, 117, 116, 8}, - {251, 1, 131, 187, 4}, - {18, 234, 226, 199, 3} - }, - { {10, 189, 5, 237, 15}, - {95, 51, 83, 45, 10}, - {251, 122, 11, 213, 0}, - {91, 76, 172, 207, 10} - }, - { {225, 190, 133, 89, 1}, - {85, 161, 184, 238, 8}, - {137, 170, 23, 216, 7}, - {23, 113, 216, 90, 10} - }, - { {145, 183, 86, 114, 7}, - {101, 91, 218, 122, 1}, - {228, 230, 174, 216, 9}, - {133, 229, 189, 170, 6} - }, - { {45, 22, 243, 125, 11}, - {254, 240, 171, 124, 4}, - {219, 236, 246, 139, 4}, - {35, 237, 80, 247, 15} - }, - { {39, 176, 238, 193, 6}, - {225, 209, 116, 77, 6}, - {104, 55, 112, 222, 4}, - {107, 34, 232, 184, 7} - }, - { {108, 133, 130, 249, 13}, - {134, 226, 243, 172, 2}, - {185, 244, 26, 19, 6}, - {67, 92, 244, 118, 1} - }, - { {115, 83, 40, 110, 3}, - {76, 62, 46, 201, 5}, - {199, 97, 76, 172, 14}, - {169, 55, 71, 195, 2} - }, - { {97, 193, 148, 57, 1}, - {4, 167, 178, 212, 0}, - {137, 194, 152, 56, 6}, - {2, 180, 222, 82, 0} - }, - { {123, 144, 243, 154, 9}, - {118, 232, 177, 209, 7}, - {149, 156, 240, 157, 14}, - {232, 184, 209, 118, 14} - }, - { {199, 152, 208, 205, 5}, - {236, 160, 80, 223, 10}, - {171, 48, 177, 158, 3}, - {95, 176, 160, 83, 7} - }, - { {40, 109, 39, 77, 11}, - {31, 119, 33, 44, 12}, - {219, 46, 75, 97, 4}, - {51, 72, 78, 239, 8} - }, - { {217, 203, 12, 182, 4}, - {10, 15, 222, 194, 11}, - {38, 211, 13, 57, 11}, - {212, 55, 191, 5, 0} - }, - { {74, 39, 247, 51, 6}, - {51, 211, 202, 181, 5}, - {108, 206, 254, 69, 2}, - {170, 213, 60, 188, 12} - }, - { {58, 192, 163, 225, 10}, - {18, 220, 51, 13, 6}, - {88, 124, 80, 53, 12}, - {107, 12, 195, 180, 8} - }, - { {7, 221, 220, 229, 7}, - {236, 151, 86, 125, 10}, - {234, 115, 187, 190, 0}, - {91, 230, 174, 147, 7} - }, - { {136, 88, 155, 155, 11}, - {86, 244, 133, 22, 11}, - {221, 157, 145, 161, 1}, - {214, 138, 18, 246, 10} - }, - { {138, 23, 163, 27, 11}, - {86, 242, 137, 39, 5}, - {221, 140, 94, 133, 1}, - {174, 73, 20, 246, 10} - }, - { {238, 5, 82, 209, 7}, - {166, 82, 224, 191, 2}, - {232, 180, 170, 7, 7}, - {79, 208, 116, 166, 5} - }, - { {187, 95, 31, 51, 1}, - {86, 79, 174, 119, 9}, - {140, 207, 143, 173, 13}, - {158, 231, 95, 38, 10} - }, - { {208, 60, 187, 190, 2}, - {89, 248, 134, 178, 15}, - {71, 221, 211, 192, 11}, - {244, 214, 17, 249, 10} - }, - { {152, 244, 149, 172, 6}, - {91, 189, 82, 50, 2}, - {99, 90, 146, 241, 9}, - {68, 196, 171, 221, 10} - }, - { {197, 139, 251, 112, 15}, - {180, 210, 223, 218, 12}, - {240, 237, 253, 26, 3}, - {53, 191, 180, 178, 13} - }, - { {122, 253, 6, 239, 8}, - {75, 111, 51, 173, 11}, - {31, 118, 11, 245, 14}, - {219, 92, 207, 109, 2} - }, - { {81, 109, 127, 193, 5}, - {53, 79, 68, 252, 14}, - {168, 63, 235, 104, 10}, - {115, 242, 47, 42, 12} - }, - { {187, 149, 178, 239, 2}, - {74, 250, 50, 127, 7}, - {79, 116, 218, 157, 13}, - {239, 228, 197, 245, 2} - }, - { {74, 73, 174, 244, 10}, - {10, 215, 135, 137, 14}, - {82, 247, 89, 37, 2}, - {121, 30, 30, 181, 0} - }, - { {121, 22, 119, 209, 9}, - {118, 73, 169, 252, 6}, - {152, 190, 230, 137, 14}, - {99, 249, 89, 38, 14} - }, - { {171, 195, 19, 250, 3}, - {22, 118, 186, 91, 3}, - {197, 252, 140, 61, 5}, - {205, 165, 214, 230, 8} - }, - { {97, 72, 188, 236, 1}, - {12, 165, 38, 216, 14}, - {131, 115, 209, 40, 6}, - {113, 182, 74, 83, 0} - }, - { {189, 91, 56, 250, 15}, - {198, 62, 239, 90, 15}, - {245, 241, 205, 171, 13}, - {245, 175, 119, 198, 3} - }, - { {167, 203, 22, 91, 5}, - {132, 103, 248, 95, 9}, - {173, 166, 141, 62, 5}, - {159, 161, 254, 98, 1} - }, - { {122, 215, 157, 221, 12}, - {90, 175, 253, 189, 2}, - {59, 187, 158, 181, 14}, - {75, 219, 255, 85, 10} - }, - { {30, 33, 123, 48, 10}, - {179, 90, 135, 17, 4}, - {80, 205, 232, 71, 8}, - {40, 142, 21, 172, 13} - }, - { {7, 106, 159, 128, 15}, - {149, 213, 77, 81, 10}, - {240, 31, 149, 110, 0}, - {88, 171, 42, 186, 9} - }, - { {66, 20, 244, 73, 5}, - {100, 161, 64, 189, 4}, - {169, 34, 242, 132, 2}, - {43, 208, 40, 82, 6} - }, - { {90, 77, 21, 204, 4}, - {26, 47, 64, 185, 10}, - {35, 58, 139, 37, 10}, - {89, 208, 47, 69, 8} - }, - { {42, 237, 24, 136, 14}, - {3, 54, 117, 49, 10}, - {113, 17, 139, 117, 4}, - {88, 202, 230, 204, 0} - }, - { {74, 162, 225, 254, 13}, - {63, 160, 219, 137, 7}, - {183, 248, 116, 85, 2}, - {233, 29, 176, 95, 12} - }, - { {132, 142, 174, 178, 2}, - {128, 209, 158, 34, 15}, - {68, 215, 87, 18, 1}, - {244, 71, 152, 176, 1} - }, - { {51, 40, 220, 225, 13}, - {37, 137, 103, 93, 10}, - {184, 115, 177, 76, 12}, - {91, 174, 105, 26, 4} - }, - { {58, 219, 131, 61, 10}, - {90, 254, 187, 5, 8}, - {91, 204, 29, 181, 12}, - {26, 13, 215, 245, 10} - }, - { {115, 169, 29, 159, 13}, - {29, 43, 245, 213, 11}, - {191, 155, 137, 92, 14}, - {218, 186, 253, 75, 8} - }, - { {4, 109, 218, 103, 10}, - {169, 214, 7, 60, 9}, - {94, 101, 187, 98, 0}, - {147, 206, 6, 185, 5} - }, - { {58, 203, 120, 189, 13}, - {46, 46, 255, 21, 14}, - {187, 209, 237, 53, 12}, - {122, 143, 247, 71, 4} - }, - { {206, 153, 231, 26, 12}, - {242, 227, 209, 131, 13}, - {53, 142, 121, 151, 3}, - {188, 24, 188, 116, 15} - }, - { {165, 37, 165, 84, 6}, - {153, 147, 224, 106, 4}, - {98, 170, 90, 74, 5}, - {37, 96, 124, 153, 9} - }, - { {240, 63, 168, 232, 1}, - {69, 170, 46, 170, 14}, - {129, 113, 95, 192, 15}, - {117, 87, 69, 90, 2} - }, - { {118, 229, 192, 251, 1}, - {165, 174, 178, 173, 3}, - {141, 240, 58, 118, 14}, - {203, 84, 215, 90, 5} - }, - { {227, 99, 237, 110, 12}, - {57, 167, 111, 203, 5}, - {55, 107, 124, 108, 7}, - {173, 63, 110, 89, 12} - }, - { {116, 249, 37, 224, 5}, - {213, 15, 114, 136, 14}, - {160, 122, 73, 242, 14}, - {113, 20, 239, 10, 11} - }, - { {198, 217, 168, 122, 2}, - {192, 182, 150, 139, 13}, - {69, 225, 89, 182, 3}, - {189, 22, 150, 208, 3} - }, - { {13, 96, 234, 117, 11}, - {175, 212, 135, 76, 4}, - {218, 229, 112, 107, 0}, - {35, 46, 18, 191, 5} - }, - { {51, 227, 74, 122, 13}, - {37, 110, 255, 73, 1}, - {181, 229, 44, 124, 12}, - {137, 47, 247, 106, 4} - }, - { {113, 122, 201, 96, 4}, - {113, 140, 110, 200, 8}, - {32, 105, 53, 232, 14}, - {17, 55, 99, 24, 14} - }, - { {152, 138, 48, 249, 12}, - {2, 40, 219, 30, 14}, - {57, 240, 197, 17, 9}, - {119, 141, 177, 68, 0} - }, - { {46, 163, 26, 179, 12}, - {131, 66, 255, 21, 3}, - {60, 213, 140, 87, 4}, - {202, 143, 244, 44, 1} - }, - { {172, 182, 25, 142, 10}, - {219, 48, 61, 50, 3}, - {87, 25, 134, 211, 5}, - {196, 203, 192, 205, 11} - }, - { {240, 101, 182, 107, 5}, - {5, 239, 98, 190, 5}, - {173, 102, 218, 96, 15}, - {167, 212, 111, 122, 0} - }, - { {168, 198, 88, 212, 0}, - {42, 4, 188, 58, 2}, - {2, 177, 166, 49, 5}, - {69, 195, 210, 5, 4} - }, - { {204, 173, 140, 128, 11}, - {135, 147, 21, 162, 10}, - {208, 19, 27, 83, 3}, - {84, 90, 140, 158, 1} - }, - { {163, 169, 133, 130, 14}, - {17, 147, 113, 67, 11}, - {116, 26, 25, 92, 5}, - {220, 40, 236, 152, 8} - }, - { {236, 44, 102, 29, 0}, - {171, 97, 160, 166, 12}, - {11, 134, 99, 67, 7}, - {54, 80, 88, 109, 5} - }, - { {25, 228, 167, 179, 8}, - {19, 205, 147, 100, 7}, - {28, 222, 82, 121, 8}, - {226, 108, 155, 60, 8} - }, - { {141, 26, 103, 29, 6}, - {250, 113, 200, 70, 12}, - {107, 142, 101, 139, 1}, - {54, 33, 56, 229, 15} - }, - { {178, 43, 3, 194, 13}, - {21, 74, 105, 11, 11}, - {180, 60, 13, 68, 13}, - {221, 9, 101, 42, 8} - }, - { {233, 107, 129, 42, 2}, - {19, 182, 42, 194, 9}, - {69, 72, 29, 105, 7}, - {148, 53, 70, 220, 8} - }, - { {175, 188, 151, 185, 4}, - {211, 225, 242, 119, 10}, - {41, 222, 147, 223, 5}, - {94, 228, 248, 124, 11} - }, - { {46, 29, 146, 160, 8}, - {194, 194, 35, 49, 10}, - {16, 84, 155, 135, 4}, - {88, 204, 68, 52, 3} - }, - { {20, 26, 214, 160, 3}, - {228, 217, 10, 16, 10}, - {192, 86, 181, 130, 8}, - {80, 133, 9, 178, 7} - }, - { {181, 12, 21, 252, 0}, - {152, 41, 162, 122, 10}, - {3, 250, 131, 10, 13}, - {85, 228, 89, 65, 9} - }, - { {95, 40, 182, 146, 9}, - {135, 201, 129, 209, 15}, - {148, 150, 209, 79, 10}, - {248, 184, 25, 62, 1} - }, - { {5, 73, 160, 42, 15}, - {132, 182, 67, 64, 13}, - {245, 64, 89, 42, 0}, - {176, 44, 38, 210, 1} - }, - { {170, 29, 19, 140, 15}, - {94, 114, 97, 51, 10}, - {243, 28, 139, 133, 5}, - {92, 200, 100, 231, 10} - }, - { {214, 197, 212, 242, 6}, - {160, 159, 210, 187, 3}, - {100, 242, 186, 54, 11}, - {205, 212, 191, 144, 5} - }, - { {175, 39, 99, 231, 15}, - {191, 82, 107, 111, 7}, - {254, 124, 110, 79, 5}, - {239, 109, 100, 175, 13} - }, - { {13, 30, 84, 39, 10}, - {234, 17, 11, 116, 9}, - {94, 66, 167, 139, 0}, - {146, 237, 8, 133, 7} - }, - { {105, 13, 29, 67, 9}, - {22, 3, 37, 252, 9}, - {156, 43, 139, 9, 6}, - {147, 250, 76, 6, 8} - }, - { {142, 118, 82, 211, 11}, - {231, 84, 137, 63, 3}, - {220, 180, 166, 231, 1}, - {207, 201, 18, 174, 7} - }, - { {183, 231, 13, 198, 11}, - {157, 31, 61, 107, 3}, - {214, 59, 14, 126, 13}, - {205, 107, 207, 139, 9} - }, - { {91, 61, 61, 135, 11}, - {95, 27, 5, 245, 15}, - {222, 27, 203, 205, 10}, - {250, 250, 13, 143, 10} - }, - { {79, 134, 142, 113, 6}, - {130, 209, 222, 237, 0}, - {104, 231, 22, 31, 2}, - {11, 119, 184, 180, 1} - }, - { {177, 31, 187, 167, 12}, - {88, 202, 111, 118, 15}, - {62, 93, 223, 136, 13}, - {246, 239, 101, 49, 10} - }, - { {128, 174, 197, 152, 2}, - {49, 177, 152, 34, 10}, - {65, 154, 55, 80, 1}, - {84, 65, 152, 216, 12} - }, - { {213, 231, 198, 181, 15}, - {173, 223, 219, 230, 2}, - {250, 214, 62, 122, 11}, - {70, 125, 191, 187, 5} - }, - { {93, 45, 129, 118, 3}, - {159, 154, 130, 232, 9}, - {198, 232, 27, 75, 10}, - {145, 116, 21, 159, 9} - }, - { {203, 168, 201, 76, 1}, - {63, 160, 20, 203, 8}, - {131, 41, 49, 93, 3}, - {29, 50, 128, 95, 12} - }, - { {103, 230, 75, 17, 3}, - {181, 84, 188, 229, 0}, - {200, 141, 38, 126, 6}, - {10, 115, 210, 170, 13} - }, - { {250, 67, 80, 92, 14}, - {42, 62, 233, 155, 0}, - {115, 160, 172, 37, 15}, - {13, 153, 119, 197, 4} - }, - { {240, 150, 174, 222, 7}, - {76, 249, 252, 170, 7}, - {231, 183, 86, 144, 15}, - {229, 83, 249, 243, 2} - }, - { {71, 218, 157, 211, 11}, - {212, 149, 157, 221, 11}, - {220, 187, 149, 190, 2}, - {219, 187, 154, 146, 11} - }, - { {154, 5, 75, 133, 14}, - {58, 90, 69, 39, 2}, - {122, 29, 42, 5, 9}, - {78, 74, 37, 165, 12} - }, - { {120, 108, 53, 241, 11}, - {23, 29, 163, 188, 14}, - {216, 250, 195, 97, 14}, - {115, 220, 91, 142, 8} - }, - { {147, 175, 244, 140, 6}, - {41, 187, 88, 115, 14}, - {99, 18, 255, 92, 9}, - {124, 225, 173, 217, 4} - }, - { {42, 176, 175, 52, 11}, - {95, 209, 183, 1, 4}, - {210, 207, 80, 213, 4}, - {40, 14, 216, 191, 10} - }, - { {240, 11, 250, 99, 1}, - {36, 202, 46, 158, 13}, - {140, 101, 253, 0, 15}, - {183, 151, 69, 50, 4} - }, - { {76, 166, 198, 133, 5}, - {175, 193, 88, 164, 2}, - {170, 22, 54, 83, 2}, - {66, 81, 168, 63, 5} - }, - { {186, 20, 227, 14, 1}, - {126, 232, 32, 35, 5}, - {135, 12, 114, 133, 13}, - {172, 64, 65, 119, 14} - }, - { {33, 208, 242, 145, 8}, - {96, 196, 177, 84, 6}, - {24, 148, 240, 184, 4}, - {98, 168, 210, 48, 6} - }, - { {195, 163, 48, 100, 2}, - {9, 18, 26, 219, 4}, - {66, 96, 204, 92, 3}, - {45, 181, 132, 137, 0} - }, - { {55, 157, 233, 153, 7}, - {244, 186, 244, 101, 14}, - {233, 153, 123, 158, 12}, - {122, 98, 245, 210, 15} - }, - { {34, 132, 160, 94, 1}, - {12, 160, 176, 41, 5}, - {135, 160, 82, 20, 4}, - {169, 64, 208, 83, 0} - }, - { {111, 213, 214, 14, 1}, - {238, 231, 48, 241, 1}, - {135, 6, 186, 191, 6}, - {136, 240, 206, 119, 7} - }, - { {203, 84, 111, 171, 13}, - {118, 101, 71, 231, 7}, - {189, 95, 98, 173, 3}, - {238, 126, 42, 102, 14} - }, - { {156, 42, 145, 229, 8}, - {155, 136, 11, 30, 10}, - {26, 120, 149, 67, 9}, - {87, 141, 1, 29, 9} - }, - { {61, 210, 148, 126, 15}, - {206, 189, 251, 88, 1}, - {247, 226, 148, 187, 12}, - {129, 173, 251, 215, 3} - }, - { {100, 83, 228, 51, 13}, - {228, 135, 235, 132, 5}, - {188, 194, 124, 162, 6}, - {162, 29, 126, 18, 7} - }, - { {43, 198, 86, 24, 12}, - {34, 101, 249, 113, 0}, - {49, 134, 166, 61, 4}, - {8, 233, 250, 100, 4} - }, - { {37, 39, 183, 207, 14}, - {153, 243, 105, 124, 7}, - {127, 62, 222, 74, 4}, - {227, 233, 108, 249, 9} - }, - { {22, 36, 199, 38, 13}, - {189, 201, 67, 33, 1}, - {182, 78, 50, 70, 8}, - {136, 76, 41, 59, 13} - }, - { {202, 176, 164, 248, 14}, - {67, 177, 211, 139, 6}, - {113, 242, 80, 213, 3}, - {109, 28, 184, 220, 2} - }, - { {193, 236, 138, 99, 14}, - {1, 212, 87, 238, 9}, - {124, 101, 19, 120, 3}, - {151, 126, 162, 184, 0} - }, - { {82, 142, 30, 232, 13}, - {4, 105, 95, 185, 10}, - {177, 119, 135, 20, 10}, - {89, 223, 169, 98, 0} - }, - { {254, 163, 79, 210, 9}, - {183, 75, 189, 139, 3}, - {148, 191, 44, 87, 15}, - {205, 27, 221, 46, 13} - }, - { {92, 145, 249, 82, 6}, - {242, 154, 212, 152, 5}, - {100, 169, 248, 147, 10}, - {161, 146, 181, 148, 15} - }, - { {42, 103, 218, 12, 8}, - {43, 230, 45, 49, 0}, - {19, 5, 190, 101, 4}, - {8, 203, 70, 125, 4} - }, - { {111, 147, 7, 201, 2}, - {210, 115, 56, 205, 2}, - {73, 62, 12, 159, 6}, - {75, 49, 204, 228, 11} - }, - { {27, 127, 101, 218, 0}, - {115, 47, 136, 105, 15}, - {5, 186, 111, 237, 8}, - {249, 97, 31, 76, 14} - }, - { {96, 94, 48, 82, 3}, - {68, 20, 168, 184, 13}, - {196, 160, 199, 160, 6}, - {177, 209, 82, 130, 2} - }, - { {187, 3, 116, 51, 0}, - {34, 11, 170, 87, 5}, - {12, 194, 236, 13, 13}, - {174, 165, 93, 4, 4} - }, - { {245, 150, 230, 89, 11}, - {228, 249, 185, 238, 4}, - {217, 166, 118, 154, 15}, - {39, 121, 217, 242, 7} - }, - { {146, 81, 227, 36, 14}, - {120, 222, 67, 3, 4}, - {114, 76, 120, 164, 9}, - {44, 12, 39, 177, 14} - }, - { {37, 129, 147, 161, 3}, - {148, 210, 50, 84, 2}, - {200, 92, 152, 26, 4}, - {66, 164, 196, 178, 9} - }, - { {179, 35, 202, 164, 8}, - {41, 202, 47, 67, 2}, - {18, 85, 60, 76, 13}, - {76, 47, 69, 57, 4} - }, - { {141, 73, 29, 60, 1}, - {158, 39, 134, 82, 8}, - {131, 203, 137, 43, 1}, - {20, 166, 30, 71, 9} - }, - { {227, 51, 36, 195, 13}, - {69, 3, 105, 207, 7}, - {188, 50, 76, 204, 7}, - {239, 57, 108, 10, 2} - }, - { {184, 54, 252, 47, 7}, - {111, 185, 110, 54, 5}, - {239, 67, 246, 193, 13}, - {166, 199, 105, 223, 6} - }, - { {237, 209, 46, 107, 4}, - {194, 103, 118, 206, 5}, - {45, 103, 72, 187, 7}, - {167, 54, 238, 100, 3} - }, - { {235, 71, 59, 247, 10}, - {26, 86, 175, 255, 7}, - {94, 253, 206, 45, 7}, - {239, 255, 86, 165, 8} - }, - { {20, 242, 111, 82, 9}, - {245, 77, 157, 8, 5}, - {148, 175, 100, 242, 8}, - {161, 11, 155, 42, 15} - }, - { {205, 127, 26, 185, 15}, - {199, 118, 207, 246, 10}, - {249, 213, 143, 235, 3}, - {86, 255, 54, 238, 3} - }, - { {198, 53, 226, 175, 3}, - {237, 242, 2, 167, 7}, - {207, 84, 122, 198, 3}, - {238, 84, 4, 251, 7} - }, - { {215, 249, 12, 215, 3}, - {205, 31, 148, 207, 11}, - {206, 179, 9, 254, 11}, - {223, 50, 159, 139, 3} - }, - { {83, 21, 215, 208, 12}, - {112, 203, 193, 249, 2}, - {48, 190, 186, 140, 10}, - {73, 248, 61, 48, 14} - }, - { {86, 18, 116, 213, 1}, - {236, 9, 136, 157, 6}, - {138, 178, 228, 134, 10}, - {107, 145, 25, 3, 7} - }, - { {181, 26, 186, 149, 2}, - {200, 216, 172, 86, 14}, - {74, 149, 213, 138, 13}, - {118, 163, 81, 177, 3} - }, - { {98, 62, 7, 58, 4}, - {81, 97, 234, 161, 9}, - {37, 206, 7, 196, 6}, - {152, 85, 120, 104, 10} - }, - { {235, 252, 157, 72, 14}, - {83, 181, 117, 251, 8}, - {113, 43, 147, 253, 7}, - {29, 250, 234, 220, 10} - }, - { {129, 51, 86, 47, 14}, - {105, 115, 75, 86, 1}, - {127, 70, 172, 200, 1}, - {134, 173, 44, 233, 6} - }, - { {125, 43, 146, 2, 6}, - {131, 218, 104, 208, 9}, - {100, 4, 157, 75, 14}, - {144, 177, 101, 188, 1} - }, - { {144, 85, 192, 202, 6}, - {96, 190, 64, 42, 3}, - {101, 48, 58, 160, 9}, - {197, 64, 39, 208, 6} - }, - { {205, 132, 181, 92, 15}, - {158, 177, 209, 250, 4}, - {243, 170, 210, 27, 3}, - {37, 248, 184, 215, 9} - }, - { {11, 54, 92, 9, 4}, - {99, 33, 76, 117, 0}, - {41, 3, 166, 205, 0}, - {10, 227, 40, 76, 6} - }, - { {190, 98, 7, 5, 10}, - {155, 93, 41, 7, 0}, - {90, 14, 4, 103, 13}, - {14, 9, 75, 173, 9} - }, - { {39, 38, 95, 35, 12}, - {177, 65, 111, 117, 1}, - {60, 79, 166, 78, 4}, - {138, 239, 104, 40, 13} - }, - { {100, 138, 166, 99, 10}, - {128, 209, 59, 140, 13}, - {92, 102, 85, 18, 6}, - {179, 29, 200, 176, 1} - }, - { {254, 101, 69, 2, 13}, - {183, 15, 97, 163, 1}, - {180, 10, 42, 103, 15}, - {140, 88, 111, 14, 13} - }, - { {167, 99, 68, 48, 13}, - {165, 7, 235, 67, 0}, - {176, 194, 44, 110, 5}, - {12, 45, 126, 10, 5} - }, - { {217, 192, 126, 10, 8}, - {34, 109, 21, 210, 5}, - {21, 7, 224, 57, 11}, - {164, 186, 139, 100, 4} - }, - { {29, 104, 212, 20, 13}, - {175, 141, 193, 80, 8}, - {178, 130, 177, 107, 8}, - {16, 168, 59, 31, 5} - }, - { {203, 197, 40, 112, 3}, - {6, 22, 150, 235, 4}, - {192, 225, 74, 61, 3}, - {45, 118, 150, 134, 0} - }, - { {228, 40, 28, 188, 9}, - {141, 33, 167, 146, 10}, - {147, 211, 129, 66, 7}, - {84, 158, 88, 75, 1} - }, - { {225, 221, 170, 221, 6}, - {72, 246, 244, 238, 14}, - {107, 181, 91, 184, 7}, - {119, 114, 246, 241, 2} - }, - { {244, 239, 90, 230, 13}, - {173, 78, 127, 186, 11}, - {182, 117, 175, 114, 15}, - {213, 223, 231, 43, 5} - }, - { {122, 159, 45, 115, 15}, - {86, 27, 255, 173, 13}, - {252, 235, 79, 149, 14}, - {187, 95, 253, 134, 10} - }, - { {189, 215, 111, 86, 6}, - {250, 95, 252, 106, 5}, - {102, 175, 110, 187, 13}, - {165, 99, 255, 165, 15} - }, - { {79, 139, 34, 80, 4}, - {130, 66, 216, 201, 12}, - {32, 164, 77, 31, 2}, - {57, 49, 180, 36, 1} - }, - { {104, 156, 225, 61, 6}, - {122, 176, 242, 164, 12}, - {107, 200, 115, 145, 6}, - {50, 84, 240, 213, 14} - }, - { {23, 125, 226, 145, 13}, - {229, 206, 193, 101, 14}, - {184, 148, 123, 238, 8}, - {122, 104, 55, 58, 7} - }, - { {101, 27, 125, 109, 2}, - {248, 51, 46, 220, 12}, - {75, 107, 237, 138, 6}, - {51, 183, 76, 193, 15} - }, - { {230, 30, 244, 128, 14}, - {224, 145, 105, 179, 14}, - {112, 18, 247, 134, 7}, - {124, 217, 104, 144, 7} - }, - { {10, 131, 197, 183, 14}, - {58, 147, 219, 5, 3}, - {126, 218, 60, 21, 0}, - {202, 13, 188, 149, 12} - }, - { {113, 216, 247, 205, 5}, - {124, 237, 112, 220, 14}, - {171, 62, 241, 184, 14}, - {115, 176, 235, 115, 14} - }, - { {119, 102, 40, 235, 6}, - {129, 60, 110, 237, 7}, - {109, 113, 70, 110, 14}, - {235, 119, 99, 200, 1} - }, - { {164, 71, 35, 16, 2}, - {144, 86, 168, 34, 4}, - {64, 140, 78, 34, 5}, - {36, 65, 86, 160, 9} - }, - { {141, 28, 30, 136, 7}, - {198, 113, 68, 114, 10}, - {225, 23, 131, 139, 1}, - {84, 226, 40, 230, 3} - }, - { {174, 5, 207, 117, 0}, - {186, 195, 166, 47, 0}, - {10, 239, 58, 7, 5}, - {15, 70, 92, 53, 13} - }, - { {242, 185, 238, 5, 10}, - {105, 219, 53, 135, 12}, - {90, 7, 121, 212, 15}, - {62, 26, 205, 185, 6} - }, - { {150, 184, 220, 80, 14}, - {225, 153, 213, 27, 8}, - {112, 163, 177, 214, 9}, - {29, 138, 185, 152, 7} - }, - { {191, 140, 179, 54, 4}, - {154, 200, 242, 115, 13}, - {38, 204, 211, 31, 13}, - {188, 228, 241, 53, 9} - }, - { {77, 139, 178, 107, 7}, - {134, 242, 90, 220, 13}, - {237, 100, 221, 27, 2}, - {179, 181, 164, 246, 1} - }, - { {48, 111, 43, 129, 9}, - {21, 78, 45, 36, 14}, - {152, 29, 79, 96, 12}, - {114, 75, 71, 42, 8} - }, - { {134, 223, 39, 101, 6}, - {216, 87, 90, 47, 12}, - {106, 110, 79, 182, 1}, - {63, 69, 174, 161, 11} - }, - { {201, 63, 168, 244, 10}, - {75, 146, 143, 234, 14}, - {82, 241, 95, 201, 3}, - {117, 127, 20, 157, 2} - }, - { {178, 101, 205, 76, 0}, - {57, 175, 36, 43, 0}, - {3, 43, 58, 100, 13}, - {13, 66, 79, 89, 12} - }, - { {230, 107, 232, 211, 7}, - {165, 150, 236, 143, 15}, - {236, 177, 125, 102, 7}, - {255, 19, 118, 154, 5} - }, - { {172, 157, 252, 25, 5}, - {230, 163, 244, 54, 12}, - {169, 131, 251, 147, 5}, - {54, 194, 252, 86, 7} - }, - { {177, 19, 196, 190, 12}, - {104, 171, 235, 66, 3}, - {55, 210, 60, 136, 13}, - {196, 45, 125, 81, 6} - }, - { {211, 223, 9, 161, 13}, - {84, 14, 95, 231, 10}, - {184, 89, 15, 188, 11}, - {94, 127, 167, 2, 10} - }, - { {238, 69, 143, 67, 7}, - {150, 215, 100, 175, 1}, - {236, 47, 26, 39, 7}, - {143, 82, 110, 182, 9} - }, - { {103, 202, 12, 176, 9}, - {132, 5, 191, 193, 10}, - {144, 211, 5, 62, 6}, - {88, 63, 218, 2, 1} - }, - { {179, 49, 95, 108, 5}, - {125, 107, 102, 91, 0}, - {163, 111, 168, 204, 13}, - {13, 166, 109, 107, 14} - }, - { {51, 113, 11, 210, 2}, - {81, 94, 164, 73, 3}, - {68, 189, 8, 236, 12}, - {201, 34, 87, 168, 10} - }, - { {155, 233, 139, 184, 6}, - {19, 254, 214, 67, 10}, - {97, 221, 25, 125, 9}, - {92, 38, 183, 252, 8} - }, - { {65, 191, 62, 12, 5}, - {77, 99, 92, 240, 12}, - {163, 7, 207, 216, 2}, - {48, 243, 172, 107, 2} - }, - { {39, 180, 57, 11, 8}, - {209, 32, 53, 117, 5}, - {29, 9, 194, 222, 4}, - {170, 234, 192, 72, 11} - }, - { {22, 194, 235, 111, 10}, - {184, 252, 31, 13, 5}, - {95, 109, 116, 54, 8}, - {171, 15, 131, 241, 13} - }, - { {180, 216, 190, 76, 8}, - {200, 237, 53, 26, 12}, - {19, 39, 209, 178, 13}, - {53, 138, 203, 113, 3} - }, - { {157, 108, 51, 41, 10}, - {147, 124, 3, 118, 12}, - {89, 76, 195, 107, 9}, - {54, 236, 3, 236, 9} - }, - { {62, 93, 56, 87, 15}, - {206, 30, 229, 61, 13}, - {254, 161, 203, 167, 12}, - {187, 202, 119, 135, 3} - }, - { {180, 100, 140, 48, 10}, - {129, 157, 167, 34, 0}, - {80, 195, 18, 98, 13}, - {4, 78, 91, 152, 1} - }, - { {93, 101, 141, 153, 11}, - {151, 191, 133, 228, 2}, - {217, 155, 26, 107, 10}, - {66, 122, 31, 222, 9} - }, - { {75, 57, 30, 253, 8}, - {75, 99, 135, 221, 10}, - {27, 247, 137, 205, 2}, - {91, 190, 28, 109, 2} - }, - { {159, 102, 246, 244, 7}, - {175, 221, 202, 123, 6}, - {226, 246, 246, 111, 9}, - {109, 229, 59, 191, 5} - }, - { {99, 251, 122, 188, 8}, - {105, 102, 191, 209, 14}, - {19, 213, 237, 252, 6}, - {120, 191, 214, 105, 6} - }, - { {48, 213, 149, 195, 13}, - {84, 143, 113, 60, 3}, - {188, 58, 154, 176, 12}, - {195, 200, 239, 18, 10} - }, - { {44, 228, 218, 91, 8}, - {163, 228, 181, 60, 1}, - {29, 165, 178, 115, 4}, - {131, 202, 210, 124, 5} - }, - { {123, 79, 47, 122, 4}, - {18, 111, 238, 233, 13}, - {37, 239, 79, 45, 14}, - {185, 119, 127, 100, 8} - }, - { {80, 165, 170, 211, 11}, - {5, 218, 149, 172, 7}, - {220, 181, 90, 80, 10}, - {227, 90, 149, 186, 0} - }, - { {53, 99, 219, 99, 9}, - {181, 206, 47, 92, 1}, - {156, 109, 188, 106, 12}, - {131, 175, 71, 58, 13} - }, - { {202, 17, 193, 89, 1}, - {118, 162, 128, 143, 0}, - {137, 168, 56, 133, 3}, - {15, 16, 20, 86, 14} - }, - { {119, 153, 15, 22, 8}, - {216, 75, 181, 193, 9}, - {22, 143, 9, 158, 14}, - {152, 58, 221, 33, 11} - }, - { {6, 232, 6, 138, 11}, - {133, 117, 17, 1, 11}, - {213, 22, 1, 118, 0}, - {216, 8, 138, 234, 1} - }, - { {88, 242, 76, 249, 1}, - {103, 45, 158, 140, 2}, - {137, 243, 36, 241, 10}, - {67, 23, 155, 78, 6} - }, - { {87, 131, 120, 211, 4}, - {160, 10, 220, 221, 7}, - {44, 177, 236, 30, 10}, - {235, 179, 181, 0, 5} - }, - { {102, 199, 182, 12, 6}, - {136, 247, 120, 177, 4}, - {99, 6, 222, 54, 6}, - {40, 209, 238, 241, 1} - }, - { {120, 143, 69, 72, 0}, - {50, 43, 56, 168, 8}, - {1, 42, 47, 17, 14}, - {17, 81, 205, 68, 12} - }, - { {233, 239, 208, 58, 13}, - {39, 166, 251, 242, 9}, - {181, 192, 191, 121, 7}, - {148, 253, 246, 94, 4} - }, - { {39, 212, 15, 163, 10}, - {208, 85, 55, 101, 3}, - {92, 95, 2, 190, 4}, - {202, 110, 202, 160, 11} - }, - { {125, 42, 249, 128, 10}, - {179, 152, 45, 208, 14}, - {80, 25, 245, 75, 14}, - {112, 187, 65, 156, 13} - }, - { {86, 144, 82, 117, 6}, - {232, 88, 210, 157, 0}, - {106, 228, 160, 150, 10}, - {11, 148, 177, 161, 7} - }, - { {30, 225, 231, 61, 2}, - {187, 255, 146, 5, 4}, - {75, 206, 120, 119, 8}, - {42, 4, 159, 253, 13} - }, - { {254, 157, 166, 210, 9}, - {198, 203, 177, 171, 15}, - {148, 182, 91, 151, 15}, - {253, 88, 221, 54, 3} - }, - { {75, 222, 118, 252, 11}, - {110, 117, 155, 249, 14}, - {211, 246, 231, 189, 2}, - {121, 253, 154, 231, 6} - }, - { {50, 21, 75, 223, 8}, - {120, 106, 165, 45, 3}, - {31, 189, 42, 132, 12}, - {203, 74, 85, 97, 14} - }, - { {141, 94, 72, 11, 6}, - {226, 52, 76, 102, 9}, - {109, 1, 39, 171, 1}, - {150, 99, 34, 196, 7} - }, - { {85, 153, 228, 23, 12}, - {232, 139, 209, 196, 13}, - {62, 130, 121, 154, 10}, - {178, 56, 189, 17, 7} - }, - { {127, 178, 100, 131, 2}, - {227, 25, 56, 197, 7}, - {76, 18, 100, 223, 14}, - {234, 49, 201, 140, 7} - }, - { {6, 201, 57, 111, 3}, - {156, 54, 22, 29, 13}, - {207, 105, 201, 54, 0}, - {187, 134, 134, 195, 9} - }, - { {66, 202, 124, 45, 2}, - {40, 53, 30, 149, 12}, - {75, 67, 229, 52, 2}, - {58, 151, 138, 193, 4} - }, - { {78, 192, 248, 186, 12}, - {162, 164, 215, 145, 7}, - {53, 209, 240, 55, 2}, - {232, 158, 178, 84, 5} - }, - { {45, 113, 131, 30, 11}, - {223, 246, 161, 64, 1}, - {215, 140, 24, 235, 4}, - {128, 40, 86, 255, 11} - }, - { {128, 130, 234, 70, 4}, - {40, 192, 92, 10, 5}, - {38, 37, 116, 16, 1}, - {165, 3, 160, 49, 4} - }, - { {172, 206, 58, 52, 3}, - {142, 84, 190, 50, 12}, - {194, 197, 199, 51, 5}, - {52, 199, 210, 167, 1} - }, - { {88, 160, 32, 181, 13}, - {15, 8, 211, 132, 6}, - {186, 208, 64, 81, 10}, - {98, 28, 177, 15, 0} - }, - { {114, 159, 129, 35, 8}, - {80, 138, 59, 165, 9}, - {28, 72, 31, 148, 14}, - {154, 93, 197, 16, 10} - }, - { {123, 5, 90, 20, 12}, - {42, 74, 229, 241, 0}, - {50, 133, 170, 13, 14}, - {8, 250, 117, 37, 4} - }, - { {150, 248, 124, 233, 1}, - {229, 45, 22, 31, 14}, - {137, 115, 225, 246, 9}, - {127, 134, 139, 74, 7} - }, - { {41, 99, 28, 75, 10}, - {3, 55, 45, 92, 1}, - {93, 35, 140, 105, 4}, - {131, 171, 78, 204, 0} - }, - { {157, 226, 24, 237, 14}, - {139, 60, 95, 94, 2}, - {123, 113, 132, 123, 9}, - {71, 175, 163, 205, 1} - }, - { {58, 230, 175, 66, 4}, - {19, 205, 124, 41, 5}, - {36, 47, 86, 117, 12}, - {169, 67, 235, 60, 8} - }, - { {180, 56, 174, 163, 3}, - {197, 217, 38, 6, 15}, - {204, 87, 81, 194, 13}, - {246, 6, 73, 186, 3} - }, - { {208, 70, 61, 144, 7}, - {20, 29, 204, 178, 6}, - {224, 155, 198, 32, 11}, - {100, 211, 59, 130, 8} - }, - { {187, 32, 151, 39, 0}, - {27, 201, 34, 87, 1}, - {14, 78, 144, 77, 13}, - {142, 164, 73, 61, 8} - }, - { {229, 143, 37, 62, 0}, - {152, 35, 186, 226, 13}, - {7, 202, 79, 26, 7}, - {180, 117, 220, 65, 9} - }, - { {194, 117, 158, 53, 4}, - {73, 199, 198, 183, 0}, - {42, 199, 154, 228, 3}, - {14, 214, 62, 57, 2} - }, - { {10, 66, 188, 132, 7}, - {14, 149, 76, 17, 6}, - {226, 19, 212, 37, 0}, - {104, 131, 42, 151, 0} - }, - { {173, 67, 236, 130, 11}, - {166, 151, 45, 66, 7}, - {212, 19, 124, 43, 5}, - {228, 43, 78, 150, 5} - }, - { {61, 136, 115, 251, 11}, - {182, 120, 179, 92, 15}, - {221, 252, 225, 27, 12}, - {243, 172, 209, 230, 13} - }, - { {102, 161, 166, 184, 11}, - {133, 243, 179, 129, 6}, - {209, 214, 88, 86, 6}, - {104, 28, 220, 250, 1} - }, - { {221, 141, 175, 200, 4}, - {146, 235, 84, 234, 14}, - {33, 63, 91, 27, 11}, - {117, 114, 173, 116, 9} - }, - { {21, 7, 218, 203, 3}, - {164, 250, 12, 124, 3}, - {205, 53, 190, 10, 8}, - {195, 227, 5, 242, 5} - }, - { {137, 254, 190, 99, 3}, - {71, 213, 30, 126, 13}, - {204, 103, 215, 249, 1}, - {183, 231, 138, 190, 2} - }, - { {25, 7, 189, 144, 10}, - {18, 155, 141, 112, 6}, - {80, 155, 222, 9, 8}, - {96, 235, 29, 148, 8} - }, - { {98, 241, 188, 178, 14}, - {65, 151, 247, 145, 7}, - {116, 211, 216, 244, 6}, - {232, 158, 254, 152, 2} - }, - { {14, 147, 144, 200, 10}, - {194, 178, 25, 25, 2}, - {81, 48, 156, 151, 0}, - {73, 137, 132, 212, 3} - }, - { {102, 251, 212, 124, 10}, - {233, 183, 187, 153, 8}, - {83, 226, 189, 246, 6}, - {25, 157, 222, 217, 7} - }, - { {203, 127, 203, 186, 5}, - {119, 230, 206, 227, 11}, - {165, 221, 63, 237, 3}, - {220, 119, 54, 126, 14} - }, - { {72, 77, 150, 114, 5}, - {6, 199, 194, 184, 9}, - {164, 230, 155, 33, 2}, - {145, 212, 62, 54, 0} - }, - { {182, 201, 156, 184, 13}, - {132, 175, 247, 19, 10}, - {177, 211, 153, 54, 13}, - {92, 142, 255, 82, 1} - }, - { {214, 73, 19, 109, 6}, - {152, 126, 66, 159, 8}, - {107, 108, 137, 38, 11}, - {31, 148, 39, 225, 9} - }, - { {13, 229, 2, 166, 2}, - {139, 86, 18, 96, 3}, - {70, 84, 10, 123, 0}, - {192, 100, 134, 173, 1} - }, - { {249, 154, 61, 119, 4}, - {90, 9, 254, 222, 13}, - {46, 235, 197, 153, 15}, - {183, 183, 249, 5, 10} - }, - { {124, 20, 82, 167, 4}, - {234, 72, 98, 180, 3}, - {46, 84, 162, 131, 14}, - {194, 212, 97, 37, 7} - }, - { {57, 81, 76, 89, 15}, - {102, 63, 229, 76, 0}, - {249, 163, 40, 169, 12}, - {3, 42, 127, 198, 6} - }, - { {202, 63, 57, 87, 14}, - {91, 18, 205, 191, 13}, - {126, 169, 207, 197, 3}, - {191, 219, 52, 141, 10} - }, - { {219, 212, 95, 243, 10}, - {114, 93, 151, 255, 3}, - {92, 255, 162, 189, 11}, - {207, 254, 155, 164, 14} - }, - { {113, 246, 139, 38, 2}, - {89, 220, 62, 224, 1}, - {70, 77, 22, 248, 14}, - {128, 119, 195, 185, 10} - }, - { {61, 34, 41, 13, 7}, - {159, 56, 108, 68, 4}, - {235, 9, 68, 75, 12}, - {34, 35, 97, 207, 9} - }, - { {154, 77, 130, 207, 12}, - {10, 238, 65, 47, 11}, - {63, 52, 27, 37, 9}, - {223, 72, 39, 117, 0} - }, - { {16, 141, 72, 139, 2}, - {32, 58, 20, 36, 11}, - {77, 17, 43, 16, 8}, - {210, 66, 133, 192, 4} - }, - { {66, 66, 157, 48, 14}, - {16, 149, 207, 145, 0}, - {112, 203, 148, 36, 2}, - {8, 159, 58, 144, 8} - }, - { {191, 242, 183, 46, 9}, - {223, 237, 59, 83, 5}, - {151, 78, 212, 255, 13}, - {172, 173, 203, 127, 11} - }, - { {137, 69, 178, 16, 8}, - {2, 198, 129, 114, 4}, - {16, 132, 218, 41, 1}, - {36, 232, 22, 52, 0} - }, - { {156, 83, 206, 106, 1}, - {230, 239, 14, 10, 1}, - {133, 103, 60, 163, 9}, - {133, 7, 15, 118, 7} - }, - { {246, 110, 46, 178, 11}, - {133, 93, 175, 163, 15}, - {212, 215, 71, 102, 15}, - {252, 95, 91, 170, 1} - }, - { {123, 135, 22, 82, 6}, - {2, 91, 248, 249, 1}, - {100, 166, 142, 29, 14}, - {137, 241, 253, 164, 0} - }, - { {117, 48, 198, 172, 7}, - {237, 249, 98, 192, 2}, - {227, 86, 48, 202, 14}, - {64, 52, 105, 251, 7} - }, - { {219, 148, 76, 224, 4}, - {98, 9, 86, 235, 2}, - {32, 115, 34, 157, 11}, - {77, 118, 169, 4, 6} - }, - { {64, 156, 92, 188, 6}, - {104, 49, 214, 176, 10}, - {99, 211, 163, 144, 2}, - {80, 214, 184, 193, 6} - }, - { {96, 119, 25, 168, 12}, - {81, 38, 111, 176, 2}, - {49, 89, 142, 224, 6}, - {64, 223, 102, 72, 10} - }, - { {100, 136, 107, 16, 1}, - {180, 64, 180, 128, 12}, - {128, 141, 97, 18, 6}, - {48, 18, 208, 34, 13} - }, - { {33, 158, 169, 148, 0}, - {88, 128, 188, 96, 14}, - {2, 153, 87, 152, 4}, - {112, 99, 208, 17, 10} - }, - { {146, 100, 250, 242, 11}, - {37, 220, 135, 59, 7}, - {212, 245, 242, 100, 9}, - {237, 206, 19, 186, 4} - }, - { {214, 14, 81, 55, 9}, - {188, 8, 139, 183, 9}, - {158, 200, 167, 6, 11}, - {158, 221, 17, 3, 13} - }, - { {6, 232, 25, 146, 12}, - {145, 4, 213, 17, 11}, - {52, 153, 129, 118, 0}, - {216, 138, 178, 8, 9} - }, - { {101, 196, 213, 201, 3}, - {180, 181, 48, 252, 2}, - {201, 58, 178, 58, 6}, - {67, 240, 202, 210, 13} - }, - { {91, 1, 85, 227, 4}, - {50, 11, 66, 221, 3}, - {44, 122, 168, 13, 10}, - {203, 180, 45, 4, 12} - }, - { {93, 64, 143, 102, 1}, - {158, 205, 6, 200, 1}, - {134, 111, 16, 43, 10}, - {129, 54, 11, 55, 9} - }, - { {61, 239, 243, 168, 13}, - {183, 238, 123, 112, 14}, - {177, 92, 255, 123, 12}, - {112, 237, 231, 126, 13} - }, - { {83, 37, 200, 86, 6}, - {41, 154, 196, 233, 1}, - {102, 161, 58, 76, 10}, - {137, 114, 53, 153, 4} - }, - { {18, 10, 174, 82, 10}, - {0, 217, 141, 9, 13}, - {84, 167, 85, 4, 8}, - {185, 11, 25, 176, 0} - }, - { {155, 29, 191, 200, 11}, - {86, 251, 5, 123, 14}, - {209, 63, 219, 141, 9}, - {125, 234, 13, 246, 10} - }, - { {207, 183, 171, 86, 8}, - {219, 194, 157, 235, 5}, - {22, 173, 94, 223, 3}, - {173, 123, 148, 61, 11} - }, - { {42, 141, 220, 32, 7}, - {38, 147, 118, 49, 8}, - {224, 67, 187, 21, 4}, - {24, 198, 236, 150, 4} - }, - { {142, 106, 174, 19, 3}, - {135, 213, 140, 7, 13}, - {204, 135, 85, 103, 1}, - {190, 3, 26, 190, 1} - }, - { {171, 253, 77, 159, 3}, - {127, 55, 180, 103, 11}, - {207, 155, 43, 253, 5}, - {222, 98, 222, 207, 14} - }, - { {45, 9, 17, 6, 12}, - {154, 2, 97, 80, 9}, - {54, 8, 137, 11, 4}, - {144, 168, 100, 5, 9} - }, - { {168, 207, 127, 12, 12}, - {58, 103, 125, 50, 12}, - {51, 15, 239, 49, 5}, - {52, 203, 238, 101, 12} - }, - { {166, 83, 153, 76, 7}, - {220, 182, 108, 27, 0}, - {227, 41, 156, 166, 5}, - {13, 131, 102, 211, 11} - }, - { {135, 166, 224, 109, 0}, - {169, 160, 26, 111, 4}, - {11, 96, 118, 94, 1}, - {47, 101, 128, 89, 5} - }, - { {59, 232, 221, 151, 0}, - {59, 141, 180, 85, 11}, - {14, 155, 177, 125, 12}, - {218, 162, 219, 29, 12} - }, - { {209, 178, 226, 250, 9}, - {101, 232, 155, 202, 7}, - {149, 244, 116, 216, 11}, - {229, 61, 145, 122, 6} - }, - { {0, 188, 184, 78, 7}, - {77, 176, 84, 56, 13}, - {231, 33, 211, 208, 0}, - {177, 194, 160, 219, 2} - }, - { {166, 167, 158, 98, 11}, - {133, 211, 63, 59, 1}, - {212, 103, 158, 86, 5}, - {141, 207, 204, 186, 1} - }, - { {200, 134, 91, 1, 3}, - {54, 80, 28, 182, 0}, - {200, 13, 166, 17, 3}, - {6, 211, 128, 166, 12} - }, - { {12, 255, 119, 177, 9}, - {247, 71, 155, 52, 14}, - {152, 222, 239, 243, 0}, - {114, 205, 158, 46, 15} - }, - { {142, 29, 220, 148, 3}, - {238, 147, 132, 51, 10}, - {194, 147, 187, 135, 1}, - {92, 194, 28, 151, 7} - }, - { {18, 244, 200, 158, 7}, - {109, 188, 212, 33, 3}, - {231, 145, 50, 244, 8}, - {200, 66, 179, 219, 6} - }, - { {221, 245, 180, 168, 13}, - {199, 175, 83, 242, 6}, - {177, 82, 218, 251, 11}, - {100, 252, 175, 94, 3} - }, - { {97, 194, 86, 250, 10}, - {32, 117, 187, 216, 3}, - {85, 246, 164, 56, 6}, - {193, 189, 218, 224, 4} - }, - { {34, 2, 81, 216, 5}, - {52, 32, 232, 25, 2}, - {161, 184, 164, 4, 4}, - {73, 129, 112, 66, 12} - }, - { {160, 222, 24, 47, 2}, - {72, 52, 62, 54, 9}, - {79, 65, 135, 176, 5}, - {150, 199, 194, 193, 2} - }, - { {203, 116, 62, 141, 0}, - {75, 101, 4, 247, 6}, - {11, 23, 194, 237, 3}, - {110, 242, 10, 109, 2} - }, - { {32, 93, 174, 186, 9}, - {68, 231, 167, 32, 15}, - {149, 215, 91, 160, 4}, - {240, 78, 94, 114, 2} - }, - { {182, 151, 72, 253, 13}, - {236, 42, 255, 47, 2}, - {187, 241, 46, 150, 13}, - {79, 79, 245, 67, 7} - }, - { {244, 165, 139, 213, 6}, - {153, 218, 244, 174, 2}, - {106, 189, 26, 82, 15}, - {71, 82, 245, 185, 9} - }, - { {137, 147, 208, 12, 15}, - {110, 178, 89, 82, 0}, - {243, 0, 188, 153, 1}, - {4, 169, 164, 215, 6} - }, - { {43, 161, 206, 80, 15}, - {39, 211, 245, 73, 0}, - {240, 167, 56, 93, 4}, - {9, 42, 252, 190, 4} - }, - { {84, 31, 38, 148, 10}, - {200, 91, 137, 160, 14}, - {82, 150, 79, 130, 10}, - {112, 89, 29, 161, 3} - }, - { {250, 73, 70, 153, 5}, - {38, 111, 224, 135, 10}, - {169, 150, 41, 37, 15}, - {94, 16, 127, 102, 4} - }, - { {143, 119, 77, 200, 6}, - {243, 55, 76, 107, 2}, - {97, 59, 46, 239, 1}, - {77, 99, 46, 204, 15} - }, - { {21, 73, 52, 191, 10}, - {136, 63, 131, 84, 15}, - {95, 210, 201, 42, 8}, - {242, 172, 31, 193, 1} - }, - { {160, 179, 125, 121, 0}, - {113, 35, 190, 30, 4}, - {9, 235, 236, 208, 5}, - {39, 135, 220, 72, 14} - }, - { {94, 29, 64, 6, 12}, - {234, 10, 65, 161, 9}, - {54, 0, 43, 135, 10}, - {152, 88, 37, 5, 7} - }, - { {247, 121, 227, 64, 9}, - {245, 206, 33, 203, 12}, - {144, 44, 121, 238, 15}, - {61, 56, 71, 58, 15} - }, - { {126, 60, 103, 41, 12}, - {243, 105, 99, 165, 12}, - {57, 78, 99, 199, 14}, - {58, 92, 105, 108, 15} - }, - { {38, 10, 20, 204, 8}, - {136, 33, 41, 25, 10}, - {19, 50, 133, 6, 4}, - {89, 137, 72, 65, 1} - }, - { {210, 180, 127, 62, 15}, - {125, 121, 215, 179, 5}, - {247, 207, 226, 212, 11}, - {172, 222, 185, 235, 14} - }, - { {52, 74, 22, 15, 2}, - {136, 125, 40, 20, 9}, - {79, 6, 133, 34, 12}, - {146, 129, 75, 225, 1} - }, - { {240, 88, 87, 246, 9}, - {124, 77, 163, 154, 11}, - {150, 254, 161, 160, 15}, - {213, 156, 91, 35, 14} - }, - { {200, 222, 73, 90, 3}, - {118, 52, 156, 170, 9}, - {197, 169, 39, 177, 3}, - {149, 83, 146, 198, 14} - }, - { {41, 152, 52, 89, 7}, - {70, 49, 240, 92, 12}, - {233, 162, 193, 153, 4}, - {51, 160, 248, 198, 2} - }, - { {192, 28, 220, 163, 10}, - {96, 145, 7, 182, 11}, - {92, 83, 179, 128, 3}, - {214, 222, 8, 144, 6} - }, - { {17, 134, 214, 198, 15}, - {44, 217, 89, 120, 3}, - {246, 54, 182, 24, 8}, - {193, 233, 169, 179, 4} - }, - { {199, 106, 99, 224, 0}, - {177, 68, 10, 203, 14}, - {0, 124, 101, 110, 3}, - {125, 53, 2, 40, 13} - }, - { {250, 63, 143, 86, 4}, - {91, 203, 236, 171, 9}, - {38, 175, 31, 197, 15}, - {157, 83, 125, 61, 10} - }, - { {30, 208, 47, 67, 7}, - {214, 93, 84, 13, 5}, - {236, 47, 64, 183, 8}, - {171, 2, 171, 166, 11} - }, - { {167, 132, 152, 85, 15}, - {140, 144, 245, 127, 0}, - {250, 161, 146, 30, 5}, - {15, 234, 240, 147, 1} - }, - { {12, 209, 94, 200, 6}, - {226, 119, 84, 24, 2}, - {97, 55, 168, 179, 0}, - {65, 130, 174, 228, 7} - }, - { {222, 242, 230, 20, 0}, - {235, 205, 152, 131, 4}, - {2, 134, 116, 247, 11}, - {44, 17, 155, 61, 7} - }, - { {172, 114, 24, 123, 12}, - {195, 36, 239, 30, 1}, - {61, 225, 132, 227, 5}, - {135, 143, 114, 76, 3} - }, - { {103, 201, 15, 119, 7}, - {156, 87, 246, 205, 9}, - {238, 239, 9, 62, 6}, - {155, 54, 254, 163, 9} - }, - { {7, 91, 205, 34, 11}, - {244, 151, 15, 65, 9}, - {212, 75, 61, 174, 0}, - {152, 47, 14, 146, 15} - }, - { {201, 190, 213, 237, 13}, - {127, 161, 91, 254, 10}, - {187, 122, 183, 217, 3}, - {87, 253, 168, 95, 14} - }, - { {106, 147, 24, 117, 4}, - {74, 2, 254, 157, 0}, - {42, 225, 140, 149, 6}, - {11, 151, 244, 5, 2} - }, - { {107, 212, 146, 123, 3}, - {70, 244, 178, 253, 1}, - {205, 228, 146, 189, 6}, - {139, 244, 210, 246, 2} - }, - { {118, 132, 7, 115, 8}, - {144, 73, 179, 173, 1}, - {28, 238, 2, 22, 14}, - {139, 92, 217, 32, 9} - }, - { {181, 12, 26, 120, 15}, - {132, 120, 231, 122, 8}, - {241, 229, 131, 10, 13}, - {21, 238, 113, 226, 1} - }, - { {153, 76, 54, 246, 14}, - {10, 93, 195, 122, 15}, - {118, 246, 195, 41, 9}, - {245, 236, 59, 165, 0} - }, - { {33, 245, 36, 87, 11}, - {77, 23, 177, 108, 5}, - {222, 162, 74, 248, 4}, - {163, 104, 222, 139, 2} - }, - { {97, 170, 231, 238, 4}, - {57, 225, 122, 200, 15}, - {39, 126, 117, 88, 6}, - {241, 53, 232, 121, 12} - }, - { {36, 1, 140, 232, 5}, - {132, 163, 102, 8, 2}, - {161, 115, 24, 2, 4}, - {65, 6, 108, 82, 1} - }, - { {97, 41, 206, 35, 10}, - {33, 211, 39, 196, 9}, - {92, 71, 57, 72, 6}, - {146, 62, 76, 184, 4} - }, - { {164, 226, 123, 199, 10}, - {185, 84, 61, 30, 7}, - {94, 61, 228, 114, 5}, - {231, 139, 194, 169, 13} - }, - { {182, 249, 62, 111, 6}, - {201, 127, 118, 31, 13}, - {111, 103, 201, 246, 13}, - {191, 134, 239, 233, 3} - }, - { {184, 158, 66, 250, 4}, - {98, 104, 250, 42, 11}, - {37, 244, 39, 145, 13}, - {213, 69, 241, 100, 6} - }, - { {205, 168, 178, 163, 12}, - {131, 192, 83, 214, 15}, - {60, 84, 209, 91, 3}, - {246, 188, 160, 60, 1} - }, - { {76, 57, 177, 159, 7}, - {223, 178, 192, 148, 15}, - {239, 152, 217, 195, 2}, - {242, 144, 52, 223, 11} - }, - { {185, 129, 38, 244, 2}, - {10, 91, 178, 74, 6}, - {66, 246, 72, 25, 13}, - {101, 36, 221, 165, 0} - }, - { {14, 11, 250, 182, 8}, - {170, 194, 143, 17, 15}, - {22, 213, 253, 7, 0}, - {248, 143, 20, 53, 5} - }, - { {242, 177, 157, 60, 6}, - {89, 187, 246, 147, 0}, - {99, 203, 152, 212, 15}, - {12, 150, 253, 217, 10} - }, - { {62, 75, 92, 90, 2}, - {162, 63, 172, 25, 9}, - {69, 163, 173, 39, 12}, - {153, 131, 95, 196, 5} - }, - { {53, 13, 105, 45, 8}, - {184, 42, 39, 100, 12}, - {27, 73, 107, 10, 12}, - {50, 110, 69, 65, 13} - }, - { {180, 35, 142, 37, 5}, - {141, 203, 110, 6, 0}, - {170, 71, 28, 66, 13}, - {6, 7, 109, 59, 1} - }, - { {69, 98, 201, 154, 0}, - {177, 164, 140, 192, 3}, - {5, 153, 52, 106, 2}, - {192, 51, 18, 88, 13} - }, - { {180, 56, 177, 59, 0}, - {209, 168, 162, 22, 13}, - {13, 200, 209, 194, 13}, - {182, 132, 81, 88, 11} - }, - { {131, 20, 145, 146, 14}, - {80, 144, 193, 115, 3}, - {116, 152, 146, 140, 1}, - {204, 232, 48, 144, 10} - }, - { {255, 9, 118, 5, 7}, - {174, 91, 96, 215, 12}, - {234, 6, 233, 15, 15}, - {62, 176, 109, 167, 5} - }, - { {76, 85, 220, 22, 13}, - {238, 135, 197, 176, 1}, - {182, 131, 186, 163, 2}, - {128, 218, 62, 23, 7} - }, - { {104, 114, 128, 201, 15}, - {71, 180, 105, 140, 2}, - {249, 48, 20, 225, 6}, - {67, 25, 98, 222, 2} - }, - { {118, 171, 174, 160, 4}, - {129, 203, 126, 129, 14}, - {32, 87, 93, 86, 14}, - {120, 23, 237, 56, 1} - }, - { {145, 170, 76, 41, 14}, - {33, 57, 95, 70, 8}, - {121, 67, 37, 88, 9}, - {22, 47, 169, 200, 4} - }, - { {172, 153, 124, 202, 8}, - {226, 35, 53, 26, 15}, - {21, 51, 233, 147, 5}, - {245, 138, 204, 68, 7} - }, - { {14, 211, 209, 214, 1}, - {254, 134, 152, 25, 3}, - {134, 184, 188, 183, 0}, - {201, 129, 150, 23, 15} - }, - { {186, 245, 248, 135, 3}, - {111, 158, 52, 55, 7}, - {206, 17, 250, 245, 13}, - {238, 194, 199, 159, 6} - }, - { {24, 203, 208, 54, 12}, - {42, 142, 219, 16, 9}, - {54, 192, 189, 49, 8}, - {144, 141, 183, 21, 4} - }, - { {230, 136, 49, 122, 7}, - {148, 48, 242, 155, 13}, - {229, 232, 193, 22, 7}, - {189, 148, 240, 194, 9} - }, - { {38, 93, 140, 24, 7}, - {196, 183, 228, 33, 8}, - {225, 131, 27, 166, 4}, - {24, 66, 126, 210, 3} - }, - { {37, 253, 177, 16, 7}, - {213, 150, 240, 112, 12}, - {224, 136, 219, 250, 4}, - {48, 224, 246, 154, 11} - }, - { {46, 105, 226, 33, 9}, - {167, 198, 35, 5, 12}, - {152, 68, 121, 103, 4}, - {58, 12, 70, 62, 5} - }, - { {148, 212, 15, 127, 5}, - {220, 109, 214, 46, 1}, - {175, 239, 2, 178, 9}, - {135, 70, 187, 99, 11} - }, - { {104, 227, 24, 161, 2}, - {3, 22, 62, 148, 2}, - {72, 81, 140, 113, 6}, - {66, 151, 198, 140, 0} - }, - { {115, 38, 168, 12, 2}, - {9, 184, 44, 225, 4}, - {67, 1, 86, 76, 14}, - {40, 115, 65, 217, 0} - }, - { {8, 225, 19, 56, 4}, - {19, 102, 210, 16, 0}, - {33, 204, 136, 113, 0}, - {0, 132, 182, 108, 8} - }, - { {30, 74, 153, 58, 11}, - {150, 188, 143, 17, 9}, - {213, 201, 149, 39, 8}, - {152, 143, 19, 214, 9} - }, - { {56, 123, 230, 193, 11}, - {103, 223, 41, 12, 14}, - {216, 54, 125, 225, 12}, - {115, 9, 79, 190, 6} - }, - { {23, 164, 136, 165, 2}, - {137, 152, 22, 101, 2}, - {74, 81, 18, 94, 8}, - {74, 102, 129, 153, 1} - }, - { {100, 67, 135, 200, 8}, - {144, 231, 41, 136, 2}, - {17, 62, 28, 34, 6}, - {65, 25, 78, 112, 9} - }, - { {110, 7, 130, 143, 7}, - {142, 242, 104, 165, 3}, - {239, 20, 30, 7, 6}, - {202, 81, 100, 247, 1} - }, - { {184, 128, 233, 2, 2}, - {50, 152, 52, 2, 5}, - {68, 9, 112, 17, 13}, - {164, 2, 193, 148, 12} - }, - { {62, 14, 185, 83, 2}, - {146, 152, 172, 61, 13}, - {76, 169, 215, 7, 12}, - {187, 195, 81, 148, 9} - }, - { {143, 140, 17, 176, 2}, - {146, 16, 146, 115, 10}, - {64, 216, 131, 31, 1}, - {92, 228, 144, 132, 9} - }, - { {27, 141, 195, 155, 13}, - {54, 234, 209, 101, 11}, - {189, 156, 59, 29, 8}, - {218, 104, 181, 118, 12} - }, - { {238, 130, 108, 30, 9}, - {174, 33, 189, 131, 5}, - {151, 131, 100, 23, 7}, - {172, 27, 216, 71, 5} - }, - { {149, 96, 110, 240, 8}, - {161, 77, 135, 74, 6}, - {16, 247, 96, 106, 9}, - {101, 46, 27, 40, 5} - }, - { {80, 228, 38, 86, 2}, - {9, 93, 144, 168, 5}, - {70, 166, 66, 112, 10}, - {161, 80, 155, 169, 0} - }, - { {212, 77, 143, 180, 2}, - {152, 223, 134, 162, 10}, - {66, 223, 27, 34, 11}, - {84, 86, 31, 177, 9} - }, - { {5, 79, 54, 9, 7}, - {132, 119, 72, 116, 12}, - {233, 6, 207, 42, 0}, - {50, 225, 46, 226, 1} - }, - { {165, 246, 91, 233, 9}, - {245, 100, 63, 126, 2}, - {153, 125, 166, 250, 5}, - {71, 239, 194, 106, 15} - }, - { {246, 67, 68, 234, 5}, - {164, 47, 106, 139, 3}, - {165, 114, 44, 38, 15}, - {205, 21, 111, 66, 5} - }, - { {85, 81, 175, 40, 13}, - {212, 239, 71, 192, 4}, - {177, 79, 88, 170, 10}, - {32, 62, 47, 114, 11} - }, - { {27, 94, 104, 63, 5}, - {110, 44, 206, 101, 13}, - {175, 193, 103, 173, 8}, - {186, 103, 51, 71, 6} - }, - { {6, 118, 167, 222, 12}, - {217, 229, 201, 41, 7}, - {55, 190, 86, 230, 0}, - {233, 73, 58, 121, 11} - }, - { {70, 176, 31, 231, 11}, - {221, 81, 23, 157, 3}, - {222, 127, 128, 214, 2}, - {203, 158, 136, 171, 11} - }, - { {38, 80, 223, 195, 0}, - {240, 197, 36, 29, 3}, - {12, 63, 176, 166, 4}, - {203, 130, 74, 48, 15} - }, - { {52, 1, 83, 23, 3}, - {188, 90, 160, 20, 1}, - {206, 140, 168, 2, 12}, - {130, 128, 85, 163, 13} - }, - { {185, 183, 76, 10, 12}, - {99, 43, 125, 98, 1}, - {53, 3, 46, 217, 13}, - {132, 107, 237, 76, 6} - }, - { {103, 124, 93, 21, 5}, - {253, 5, 228, 245, 8}, - {170, 139, 163, 238, 6}, - {26, 242, 122, 11, 15} - }, - { {62, 14, 116, 210, 9}, - {166, 9, 169, 57, 15}, - {148, 178, 231, 7, 12}, - {249, 201, 89, 6, 5} - }, - { {135, 91, 221, 123, 4}, - {240, 167, 206, 95, 9}, - {45, 235, 189, 174, 1}, - {159, 167, 62, 80, 15} - }, - { {243, 250, 71, 202, 15}, - {117, 125, 121, 203, 11}, - {245, 62, 37, 252, 15}, - {221, 57, 235, 234, 14} - }, - { {215, 194, 170, 201, 7}, - {132, 252, 92, 207, 6}, - {233, 53, 84, 62, 11}, - {111, 51, 163, 242, 1} - }, - { {9, 112, 244, 98, 8}, - {99, 133, 3, 88, 5}, - {20, 98, 240, 233, 0}, - {161, 172, 10, 28, 6} - }, - { {49, 139, 231, 50, 10}, - {48, 219, 187, 64, 13}, - {84, 206, 125, 24, 12}, - {176, 45, 221, 176, 12} - }, - { {250, 59, 245, 187, 4}, - {115, 171, 234, 151, 15}, - {45, 218, 253, 197, 15}, - {254, 149, 125, 92, 14} - }, - { {119, 186, 106, 0, 14}, - {225, 88, 125, 193, 12}, - {112, 5, 101, 222, 14}, - {56, 59, 225, 168, 7} - }, - { {245, 7, 16, 43, 15}, - {132, 58, 107, 246, 1}, - {253, 64, 142, 10, 15}, - {134, 253, 101, 194, 1} - }, - { {55, 185, 16, 190, 8}, - {201, 42, 179, 81, 11}, - {23, 208, 137, 222, 12}, - {216, 172, 213, 73, 3} - }, - { {154, 36, 87, 204, 1}, - {63, 105, 0, 59, 2}, - {131, 62, 162, 69, 9}, - {77, 192, 9, 111, 12} - }, - { {86, 238, 203, 19, 12}, - {177, 204, 221, 165, 9}, - {60, 141, 55, 118, 10}, - {154, 91, 179, 56, 13} - }, - { {19, 93, 119, 167, 4}, - {120, 79, 66, 117, 15}, - {46, 94, 235, 172, 8}, - {250, 228, 47, 33, 14} - }, - { {13, 146, 253, 48, 4}, - {242, 129, 222, 80, 4}, - {32, 203, 244, 155, 0}, - {32, 167, 184, 20, 15} - }, - { {237, 122, 230, 34, 3}, - {231, 213, 42, 194, 13}, - {196, 70, 117, 235, 7}, - {180, 53, 74, 190, 7} - }, - { {141, 125, 109, 37, 12}, - {251, 7, 71, 102, 12}, - {58, 75, 107, 235, 1}, - {54, 110, 46, 13, 15} - }, - { {120, 62, 207, 103, 3}, - {127, 217, 46, 172, 9}, - {206, 111, 55, 193, 14}, - {147, 87, 73, 191, 14} - }, - { {62, 116, 93, 252, 6}, - {251, 61, 230, 57, 2}, - {99, 251, 162, 231, 12}, - {73, 198, 123, 205, 15} - }, - { {188, 170, 218, 5, 10}, - {171, 216, 61, 22, 8}, - {90, 5, 181, 83, 13}, - {22, 139, 193, 189, 5} - }, - { {13, 162, 38, 20, 8}, - {139, 65, 153, 64, 4}, - {18, 134, 68, 91, 0}, - {32, 41, 152, 45, 1} - }, - { {134, 217, 94, 7, 10}, - {232, 87, 21, 23, 9}, - {94, 7, 169, 182, 1}, - {158, 138, 142, 161, 7} - }, - { {37, 126, 156, 27, 14}, - {193, 181, 237, 116, 9}, - {125, 131, 151, 234, 4}, - {146, 235, 122, 216, 3} - }, - { {221, 222, 210, 16, 1}, - {230, 204, 152, 242, 8}, - {128, 132, 183, 187, 11}, - {20, 241, 147, 54, 7} - }, - { {233, 1, 205, 160, 13}, - {54, 131, 103, 194, 2}, - {176, 91, 56, 9, 7}, - {68, 62, 108, 22, 12} - }, - { {119, 152, 12, 57, 4}, - {192, 41, 246, 197, 8}, - {41, 195, 1, 158, 14}, - {26, 54, 249, 64, 3} - }, - { {209, 14, 75, 139, 7}, - {52, 120, 76, 230, 11}, - {237, 29, 39, 8, 11}, - {214, 115, 33, 226, 12} - }, - { {226, 178, 81, 251, 9}, - {117, 32, 187, 159, 3}, - {157, 248, 164, 212, 7}, - {207, 157, 208, 74, 14} - }, - { {108, 39, 137, 18, 7}, - {151, 146, 236, 160, 1}, - {228, 137, 30, 67, 6}, - {128, 83, 116, 158, 9} - }, - { {78, 213, 70, 229, 4}, - {234, 71, 82, 173, 2}, - {42, 118, 42, 183, 2}, - {75, 84, 174, 37, 7} - }, - { {240, 6, 14, 244, 9}, - {12, 73, 175, 170, 2}, - {146, 247, 6, 0, 15}, - {69, 95, 89, 35, 0} - }, - { {179, 69, 148, 196, 9}, - {12, 143, 33, 123, 2}, - {146, 50, 154, 44, 13}, - {77, 232, 79, 19, 0} - }, - { {183, 99, 0, 71, 12}, - {137, 14, 105, 79, 1}, - {62, 32, 12, 110, 13}, - {143, 41, 103, 9, 1} - }, - { {124, 196, 229, 3, 1}, - {182, 141, 48, 164, 5}, - {140, 10, 114, 51, 14}, - {162, 80, 203, 22, 13} - }, - { {86, 1, 102, 16, 6}, - {160, 91, 192, 129, 4}, - {96, 134, 104, 6, 10}, - {40, 16, 61, 160, 5} - }, - { {82, 75, 187, 242, 15}, - {20, 222, 207, 153, 15}, - {244, 253, 221, 36, 10}, - {249, 159, 55, 178, 8} - }, - { {142, 50, 98, 30, 15}, - {239, 112, 201, 3, 5}, - {247, 132, 100, 199, 1}, - {172, 9, 48, 239, 7} - }, - { {66, 68, 75, 195, 9}, - {52, 68, 5, 173, 3}, - {156, 61, 34, 36, 2}, - {203, 90, 2, 34, 12} - }, - { {139, 101, 126, 225, 12}, - {35, 71, 71, 127, 6}, - {56, 119, 234, 109, 1}, - {111, 238, 46, 44, 4} - }, - { {53, 42, 182, 84, 7}, - {141, 217, 232, 88, 12}, - {226, 166, 213, 74, 12}, - {49, 161, 121, 187, 1} - }, - { {231, 165, 75, 233, 2}, - {177, 114, 54, 239, 2}, - {73, 125, 42, 94, 7}, - {79, 118, 196, 232, 13} - }, - { {120, 30, 244, 21, 3}, - {110, 153, 168, 180, 12}, - {202, 130, 247, 129, 14}, - {50, 209, 89, 151, 6} - }, - { {50, 242, 74, 129, 2}, - {97, 92, 60, 5, 2}, - {72, 21, 36, 244, 12}, - {74, 3, 195, 168, 6} - }, - { {40, 17, 16, 119, 11}, - {78, 18, 163, 28, 1}, - {222, 224, 136, 129, 4}, - {131, 140, 84, 135, 2} - }, - { {37, 203, 65, 84, 13}, - {188, 6, 249, 72, 8}, - {178, 168, 45, 58, 4}, - {17, 41, 246, 3, 13} - }, - { {167, 103, 245, 103, 1}, - {189, 135, 42, 127, 5}, - {142, 106, 254, 110, 5}, - {175, 229, 78, 27, 13} - }, - { {91, 165, 158, 70, 11}, - {15, 219, 21, 249, 1}, - {214, 39, 154, 93, 10}, - {137, 250, 141, 191, 0} - }, - { {88, 61, 62, 106, 2}, - {67, 123, 6, 184, 13}, - {69, 103, 203, 193, 10}, - {177, 214, 13, 236, 2} - }, - { {191, 77, 67, 125, 10}, - {186, 126, 163, 111, 8}, - {91, 236, 43, 47, 13}, - {31, 108, 87, 229, 13} - }, - { {160, 164, 162, 247, 2}, - {9, 208, 178, 46, 7}, - {78, 244, 82, 80, 5}, - {231, 68, 208, 185, 0} - }, - { {93, 52, 153, 184, 6}, - {211, 184, 198, 240, 2}, - {97, 217, 146, 203, 10}, - {64, 246, 49, 220, 11} - }, - { {211, 7, 214, 157, 5}, - {44, 235, 200, 247, 2}, - {171, 150, 190, 12, 11}, - {78, 241, 61, 115, 4} - }, - { {155, 172, 247, 16, 14}, - {51, 217, 209, 115, 12}, - {112, 142, 243, 93, 9}, - {60, 232, 185, 188, 12} - }, - { {196, 108, 246, 252, 13}, - {173, 229, 195, 186, 14}, - {179, 246, 243, 98, 3}, - {117, 220, 58, 123, 5} - }, - { {184, 1, 141, 118, 12}, - {26, 139, 231, 10, 1}, - {54, 235, 24, 1, 13}, - {133, 14, 125, 21, 8} - }, - { {232, 151, 127, 181, 13}, - {126, 67, 255, 182, 6}, - {186, 223, 238, 145, 7}, - {102, 223, 252, 39, 14} - }, - { {11, 60, 141, 188, 5}, - {95, 161, 198, 97, 10}, - {163, 219, 19, 205, 0}, - {88, 102, 56, 95, 10} - }, - { {20, 20, 133, 139, 8}, - {208, 169, 1, 36, 3}, - {29, 26, 18, 130, 8}, - {194, 72, 9, 80, 11} - }, - { {115, 104, 16, 10, 4}, - {1, 44, 96, 209, 9}, - {37, 0, 129, 108, 14}, - {152, 176, 99, 72, 0} - }, - { {18, 132, 134, 239, 3}, - {12, 249, 18, 45, 3}, - {207, 118, 18, 20, 8}, - {203, 68, 137, 243, 0} - }, - { {210, 252, 86, 125, 0}, - {105, 109, 146, 191, 8}, - {11, 230, 163, 244, 11}, - {31, 212, 155, 105, 6} - }, - { {91, 207, 66, 130, 14}, - {34, 94, 89, 225, 11}, - {116, 20, 47, 61, 10}, - {216, 121, 167, 164, 4} - }, - { {73, 183, 123, 167, 3}, - {127, 82, 30, 244, 7}, - {206, 93, 238, 217, 2}, - {226, 247, 132, 175, 14} - }, - { {97, 229, 12, 169, 15}, - {5, 55, 119, 228, 2}, - {249, 83, 10, 120, 6}, - {66, 126, 238, 202, 0} - }, - { {28, 221, 19, 226, 0}, - {210, 78, 18, 56, 11}, - {4, 124, 139, 179, 8}, - {209, 196, 135, 36, 11} - }, - { {241, 23, 253, 83, 15}, - {116, 155, 237, 254, 5}, - {252, 171, 254, 136, 15}, - {167, 251, 125, 146, 14} - }, - { {141, 51, 92, 19, 13}, - {231, 3, 205, 86, 1}, - {188, 131, 172, 203, 1}, - {134, 171, 60, 14, 7} - }, - { {248, 223, 155, 48, 4}, - {82, 206, 254, 178, 8}, - {32, 205, 159, 177, 15}, - {20, 215, 247, 52, 10} - }, - { {205, 115, 59, 196, 0}, - {219, 70, 12, 218, 6}, - {2, 61, 204, 235, 3}, - {101, 179, 6, 45, 11} - }, - { {35, 29, 150, 151, 9}, - {76, 195, 161, 117, 11}, - {158, 150, 155, 140, 4}, - {218, 232, 92, 51, 2} - }, - { {44, 151, 177, 71, 6}, - {218, 146, 120, 60, 5}, - {110, 40, 222, 147, 4}, - {163, 193, 228, 149, 11} - }, - { {230, 105, 156, 80, 1}, - {133, 135, 164, 155, 8}, - {128, 163, 153, 102, 7}, - {29, 146, 94, 26, 1} - }, - { {181, 161, 208, 86, 14}, - {169, 154, 241, 90, 1}, - {118, 160, 184, 90, 13}, - {133, 168, 245, 153, 5} - }, - { {60, 125, 33, 88, 4}, - {211, 46, 224, 40, 12}, - {33, 168, 75, 227, 12}, - {49, 64, 119, 76, 11} - }, - { {173, 20, 11, 20, 5}, - {222, 64, 228, 98, 0}, - {162, 141, 2, 139, 5}, - {4, 98, 112, 39, 11} - }, - { {251, 9, 110, 114, 3}, - {38, 91, 166, 203, 13}, - {196, 231, 105, 13, 15}, - {189, 54, 93, 166, 4} - }, - { {23, 132, 105, 191, 13}, - {188, 40, 215, 101, 7}, - {191, 217, 98, 30, 8}, - {234, 110, 177, 67, 13} - }, - { {36, 48, 61, 47, 6}, - {217, 49, 102, 20, 5}, - {111, 75, 192, 194, 4}, - {162, 134, 104, 201, 11} - }, - { {235, 214, 231, 21, 5}, - {126, 197, 248, 231, 4}, - {170, 142, 118, 189, 7}, - {46, 113, 250, 55, 14} - }, - { {147, 91, 170, 215, 11}, - {76, 222, 141, 79, 15}, - {222, 181, 93, 172, 9}, - {255, 43, 23, 179, 2} - }, - { {209, 95, 34, 205, 13}, - {76, 110, 73, 238, 14}, - {187, 52, 79, 168, 11}, - {119, 121, 39, 99, 2} - }, - { {55, 178, 213, 83, 15}, - {245, 153, 249, 93, 1}, - {252, 170, 180, 222, 12}, - {139, 169, 249, 154, 15} - }, - { {211, 101, 213, 98, 8}, - {49, 143, 3, 251, 1}, - {20, 106, 186, 108, 11}, - {141, 252, 15, 24, 12} - }, - { {29, 118, 183, 146, 15}, - {215, 221, 201, 112, 7}, - {244, 158, 214, 235, 8}, - {224, 233, 59, 190, 11} - }, - { {87, 185, 126, 171, 11}, - {229, 123, 23, 213, 15}, - {221, 87, 233, 222, 10}, - {250, 190, 141, 234, 7} - }, - { {9, 11, 171, 164, 4}, - {26, 194, 78, 64, 14}, - {34, 93, 93, 9, 0}, - {112, 39, 36, 53, 8} - }, - { {163, 131, 97, 118, 11}, - {60, 18, 187, 75, 5}, - {214, 232, 108, 28, 5}, - {173, 45, 212, 131, 12} - }, - { {156, 250, 125, 156, 6}, - {251, 61, 220, 18, 14}, - {99, 155, 229, 243, 9}, - {116, 131, 187, 205, 15} - }, - { {76, 134, 94, 122, 15}, - {166, 113, 223, 184, 1}, - {245, 231, 166, 19, 2}, - {129, 223, 184, 230, 5} - }, - { {249, 13, 22, 148, 11}, - {14, 91, 161, 242, 10}, - {210, 150, 139, 9, 15}, - {84, 248, 93, 167, 0} - }, - { {120, 93, 12, 212, 1}, - {78, 15, 164, 168, 10}, - {130, 179, 11, 161, 14}, - {81, 82, 95, 7, 2} - }, - { {51, 98, 95, 192, 1}, - {53, 77, 44, 89, 2}, - {128, 63, 164, 108, 12}, - {73, 163, 75, 42, 12} - }, - { {0, 213, 240, 148, 3}, - {108, 150, 144, 48, 6}, - {194, 144, 250, 176, 0}, - {96, 192, 150, 147, 6} - }, - { {167, 185, 175, 176, 8}, - {209, 195, 183, 67, 14}, - {16, 223, 89, 222, 5}, - {124, 46, 220, 56, 11} - }, - { {223, 59, 151, 88, 10}, - {211, 251, 137, 219, 8}, - {81, 174, 157, 207, 11}, - {29, 185, 29, 252, 11} - }, - { {182, 138, 31, 89, 2}, - {144, 121, 188, 31, 8}, - {73, 175, 133, 22, 13}, - {31, 131, 217, 224, 9} - }, - { {103, 50, 106, 212, 0}, - {233, 64, 172, 201, 6}, - {2, 181, 100, 206, 6}, - {105, 51, 80, 41, 7} - }, - { {7, 184, 90, 134, 3}, - {237, 80, 20, 81, 11}, - {198, 21, 161, 222, 0}, - {216, 162, 128, 171, 7} - }, - { {37, 38, 25, 247, 15}, - {157, 16, 239, 124, 3}, - {254, 249, 134, 74, 4}, - {195, 239, 112, 139, 9} - }, - { {130, 206, 71, 104, 3}, - {52, 117, 26, 43, 8}, - {193, 110, 39, 52, 1}, - {29, 69, 138, 226, 12} - }, - { {108, 230, 33, 121, 12}, - {147, 36, 251, 172, 4}, - {57, 232, 70, 115, 6}, - {35, 93, 242, 76, 9} - }, - { {98, 230, 221, 55, 8}, - {57, 133, 191, 181, 1}, - {30, 203, 182, 116, 6}, - {138, 223, 218, 25, 12} - }, - { {62, 92, 11, 17, 10}, - {210, 92, 165, 37, 8}, - {88, 141, 3, 167, 12}, - {26, 74, 83, 164, 11} - }, - { {57, 245, 112, 80, 8}, - {99, 14, 177, 120, 4}, - {16, 160, 234, 249, 12}, - {33, 232, 215, 12, 6} - }, - { {214, 87, 51, 142, 6}, - {216, 126, 72, 179, 7}, - {103, 28, 206, 166, 11}, - {236, 209, 39, 225, 11} - }, - { {192, 98, 168, 92, 12}, - {9, 164, 205, 138, 4}, - {51, 161, 84, 96, 3}, - {37, 27, 50, 89, 0} - }, - { {131, 220, 0, 222, 5}, - {76, 36, 208, 107, 11}, - {167, 176, 3, 188, 1}, - {221, 96, 178, 67, 2} - }, - { {177, 33, 195, 250, 3}, - {53, 250, 162, 74, 3}, - {197, 252, 56, 72, 13}, - {197, 36, 85, 250, 12} - }, - { {227, 149, 10, 113, 14}, - {64, 82, 247, 239, 0}, - {120, 229, 10, 156, 7}, - {15, 126, 244, 160, 2} - }, - { {113, 29, 253, 190, 8}, - {120, 171, 167, 240, 15}, - {23, 219, 251, 136, 14}, - {240, 254, 93, 81, 14} - }, - { {130, 171, 112, 191, 2}, - {41, 50, 154, 23, 15}, - {79, 208, 237, 84, 1}, - {254, 133, 148, 201, 4} - }, - { {93, 123, 132, 66, 1}, - {199, 143, 8, 200, 9}, - {132, 34, 29, 235, 10}, - {145, 49, 15, 30, 3} - }, - { {103, 239, 52, 200, 12}, - {129, 39, 121, 249, 14}, - {49, 50, 207, 126, 6}, - {121, 249, 238, 72, 1} - }, - { {202, 53, 231, 230, 13}, - {127, 195, 67, 171, 7}, - {182, 126, 122, 197, 3}, - {237, 92, 44, 63, 14} - }, - { {36, 1, 77, 111, 11}, - {188, 51, 39, 12, 1}, - {223, 107, 40, 2, 4}, - {131, 14, 76, 195, 13} - }, - { {183, 239, 157, 161, 14}, - {145, 159, 127, 119, 10}, - {120, 91, 159, 126, 13}, - {94, 239, 239, 152, 9} - }, - { {176, 76, 172, 188, 7}, - {12, 189, 230, 34, 14}, - {227, 211, 83, 32, 13}, - {116, 70, 123, 211, 0} - }, - { {193, 166, 3, 154, 8}, - {17, 96, 153, 226, 3}, - {21, 156, 6, 88, 3}, - {196, 121, 144, 104, 8} - }, - { {197, 1, 82, 163, 10}, - {160, 82, 3, 214, 3}, - {92, 84, 168, 10, 3}, - {198, 188, 4, 160, 5} - }, - { {220, 202, 43, 43, 7}, - {150, 124, 94, 134, 13}, - {237, 77, 69, 51, 11}, - {182, 23, 163, 230, 9} - }, - { {88, 6, 54, 143, 3}, - {14, 121, 8, 180, 7}, - {207, 22, 198, 1, 10}, - {226, 209, 9, 231, 0} - }, - { {139, 223, 114, 151, 10}, - {106, 86, 153, 119, 15}, - {94, 148, 239, 189, 1}, - {254, 233, 150, 165, 6} - }, - { {118, 72, 225, 171, 10}, - {176, 188, 35, 133, 15}, - {93, 88, 113, 38, 14}, - {250, 28, 67, 208, 13} - }, - { {224, 20, 244, 247, 7}, - {108, 145, 226, 190, 7}, - {238, 242, 242, 128, 7}, - {231, 212, 120, 147, 6} - }, - { {113, 170, 60, 249, 0}, - {1, 41, 190, 220, 14}, - {9, 243, 197, 88, 14}, - {115, 183, 217, 72, 0} - }, - { {156, 171, 243, 108, 14}, - {187, 250, 91, 26, 12}, - {115, 108, 253, 83, 9}, - {53, 141, 165, 253, 13} - }, - { {135, 84, 73, 90, 15}, - {244, 52, 197, 107, 1}, - {245, 169, 34, 174, 1}, - {141, 106, 50, 194, 15} - }, - { {154, 153, 213, 71, 2}, - {122, 155, 16, 31, 9}, - {78, 42, 185, 149, 9}, - {159, 128, 141, 149, 14} - }, - { {231, 44, 187, 69, 6}, - {153, 208, 100, 255, 12}, - {106, 45, 211, 78, 7}, - {63, 242, 96, 185, 9} - }, - { {196, 176, 203, 147, 1}, - {245, 192, 148, 134, 3}, - {140, 157, 48, 210, 3}, - {198, 18, 144, 58, 15} - }, - { {137, 237, 4, 93, 7}, - {15, 55, 208, 110, 8}, - {235, 162, 11, 121, 1}, - {23, 96, 190, 207, 0} - }, - { {199, 169, 10, 58, 11}, - {133, 114, 151, 195, 9}, - {213, 197, 9, 94, 3}, - {156, 62, 148, 234, 1} - }, - { {65, 90, 235, 196, 2}, - {120, 212, 12, 200, 14}, - {66, 61, 117, 168, 2}, - {113, 51, 2, 177, 14} - }, - { {127, 85, 215, 48, 2}, - {242, 223, 162, 241, 0}, - {64, 206, 186, 175, 14}, - {8, 244, 95, 180, 15} - }, - { {17, 75, 16, 122, 6}, - {0, 62, 202, 88, 9}, - {101, 224, 141, 40, 8}, - {145, 165, 55, 192, 0} - }, - { {230, 235, 111, 39, 4}, - {185, 71, 126, 135, 13}, - {46, 79, 109, 118, 7}, - {190, 23, 238, 41, 13} - }, - { {132, 244, 176, 135, 15}, - {205, 148, 81, 54, 7}, - {254, 16, 210, 242, 1}, - {230, 200, 162, 155, 3} - }, - { {91, 222, 44, 8, 8}, - {66, 45, 29, 225, 12}, - {17, 3, 71, 189, 10}, - {56, 123, 139, 68, 2} - }, - { {205, 253, 149, 194, 7}, - {215, 151, 80, 250, 11}, - {228, 58, 155, 251, 3}, - {213, 240, 174, 158, 11} - }, - { {51, 193, 37, 123, 3}, - {20, 63, 178, 77, 5}, - {205, 234, 72, 60, 12}, - {171, 36, 223, 194, 8} - }, - { {99, 27, 71, 141, 15}, - {124, 115, 105, 197, 10}, - {251, 30, 45, 140, 6}, - {90, 57, 108, 227, 14} - }, - { {229, 239, 80, 81, 11}, - {165, 22, 185, 254, 8}, - {216, 160, 175, 122, 7}, - {23, 249, 214, 138, 5} - }, - { {65, 141, 160, 190, 12}, - {8, 162, 211, 224, 15}, - {55, 208, 91, 24, 2}, - {240, 124, 180, 81, 0} - }, - { {193, 184, 36, 139, 1}, - {69, 33, 16, 198, 15}, - {141, 18, 65, 216, 3}, - {246, 48, 136, 74, 2} - }, - { {118, 222, 87, 133, 11}, - {252, 93, 57, 181, 10}, - {218, 30, 167, 182, 14}, - {90, 217, 203, 163, 15} - }, - { {219, 201, 87, 218, 6}, - {50, 127, 208, 219, 11}, - {101, 190, 169, 61, 11}, - {221, 176, 191, 228, 12} - }, - { {202, 225, 244, 5, 4}, - {43, 135, 80, 151, 4}, - {42, 2, 248, 117, 3}, - {46, 144, 174, 29, 4} - }, - { {145, 76, 36, 42, 2}, - {0, 61, 2, 98, 13}, - {69, 66, 67, 40, 9}, - {180, 100, 11, 192, 0} - }, - { {69, 163, 208, 168, 2}, - {161, 178, 26, 208, 2}, - {65, 80, 188, 90, 2}, - {64, 181, 132, 216, 5} - }, - { {136, 154, 125, 93, 13}, - {126, 33, 221, 30, 12}, - {187, 171, 229, 145, 1}, - {55, 139, 184, 71, 14} - }, - { {154, 61, 179, 211, 2}, - {83, 218, 128, 63, 15}, - {76, 188, 219, 197, 9}, - {255, 192, 21, 188, 10} - }, - { {208, 44, 134, 112, 15}, - {5, 217, 195, 170, 8}, - {240, 230, 19, 64, 11}, - {21, 92, 57, 186, 0} - }, - { {21, 123, 52, 80, 12}, - {193, 15, 201, 88, 12}, - {48, 162, 205, 234, 8}, - {49, 169, 63, 8, 3} - }, - { {137, 13, 55, 91, 10}, - {18, 115, 129, 126, 13}, - {93, 174, 203, 9, 1}, - {183, 232, 28, 228, 8} - }, - { {137, 27, 201, 59, 10}, - {114, 178, 143, 70, 9}, - {93, 201, 61, 137, 1}, - {150, 47, 20, 212, 14} - }, - { {138, 2, 216, 10, 3}, - {38, 176, 12, 19, 1}, - {197, 1, 180, 5, 1}, - {140, 131, 0, 214, 4} - }, - { {30, 93, 243, 65, 14}, - {242, 222, 65, 61, 12}, - {120, 44, 251, 167, 8}, - {59, 200, 39, 180, 15} - }, - { {120, 109, 137, 131, 2}, - {19, 158, 36, 164, 11}, - {76, 25, 27, 97, 14}, - {210, 82, 71, 156, 8} - }, - { {172, 247, 228, 126, 13}, - {239, 167, 251, 42, 5}, - {183, 226, 126, 243, 5}, - {165, 77, 254, 95, 7} - }, - { {28, 22, 226, 90, 0}, - {226, 232, 136, 40, 5}, - {5, 164, 118, 131, 8}, - {161, 65, 17, 116, 7} - }, - { {46, 169, 4, 13, 9}, - {143, 35, 49, 5, 8}, - {155, 2, 9, 87, 4}, - {26, 8, 204, 79, 1} - }, - { {243, 38, 138, 238, 5}, - {13, 232, 110, 235, 3}, - {167, 117, 22, 76, 15}, - {205, 119, 97, 123, 0} - }, - { {128, 232, 75, 204, 11}, - {61, 116, 21, 10, 10}, - {211, 61, 33, 112, 1}, - {85, 10, 130, 235, 12} - }, - { {162, 190, 211, 245, 12}, - {121, 192, 251, 63, 10}, - {58, 252, 183, 212, 5}, - {95, 205, 240, 57, 14} - }, - { {179, 172, 4, 251, 2}, - {1, 57, 178, 111, 11}, - {77, 242, 3, 92, 13}, - {223, 100, 217, 200, 0} - }, - { {132, 89, 129, 25, 2}, - {208, 182, 128, 6, 8}, - {73, 136, 25, 162, 1}, - {22, 0, 22, 208, 11} - }, - { {96, 167, 159, 201, 4}, - {17, 227, 124, 188, 2}, - {41, 63, 158, 80, 6}, - {67, 211, 236, 120, 8} - }, - { {19, 143, 250, 197, 9}, - {44, 202, 29, 125, 14}, - {154, 53, 255, 28, 8}, - {123, 235, 133, 51, 4} - }, - { {214, 125, 112, 144, 3}, - {229, 30, 128, 179, 14}, - {192, 144, 235, 230, 11}, - {124, 208, 23, 138, 7} - }, - { {43, 138, 160, 242, 5}, - {6, 128, 250, 73, 15}, - {164, 240, 85, 29, 4}, - {249, 37, 240, 22, 0} - }, - { {41, 131, 176, 59, 10}, - {2, 178, 187, 84, 5}, - {93, 192, 220, 25, 4}, - {162, 173, 212, 212, 0} - }, - { {140, 215, 81, 28, 8}, - {250, 38, 153, 50, 0}, - {19, 136, 174, 179, 1}, - {4, 201, 150, 69, 15} - }, - { {47, 222, 77, 2, 12}, - {242, 5, 125, 97, 9}, - {52, 11, 39, 191, 4}, - {152, 107, 234, 4, 15} - }, - { {213, 4, 207, 5, 12}, - {184, 201, 69, 230, 0}, - {58, 15, 50, 10, 11}, - {6, 122, 41, 49, 13} - }, - { {186, 231, 214, 153, 0}, - {35, 239, 184, 55, 2}, - {9, 150, 190, 117, 13}, - {78, 193, 223, 124, 4} - }, - { {156, 243, 126, 164, 13}, - {239, 79, 95, 18, 6}, - {178, 87, 236, 243, 9}, - {100, 143, 175, 47, 7} - }, - { {143, 79, 97, 177, 14}, - {178, 22, 203, 103, 14}, - {120, 216, 111, 47, 1}, - {126, 109, 54, 132, 13} - }, - { {137, 255, 175, 206, 6}, - {91, 247, 92, 106, 15}, - {103, 63, 95, 249, 1}, - {245, 99, 174, 253, 10} - }, - { {124, 126, 177, 202, 2}, - {211, 188, 40, 184, 15}, - {69, 56, 215, 227, 14}, - {241, 209, 67, 220, 11} - }, - { {150, 194, 61, 167, 11}, - {156, 29, 31, 23, 7}, - {222, 91, 196, 54, 9}, - {238, 143, 139, 131, 9} - } -}; - - - -static unsigned char DICT_APRILTAG_36h11_BYTES[][4][5] = -{ - { {33, 161, 70, 186, 11}, - {37, 115, 179, 64, 3}, - {213, 214, 40, 88, 4}, - {192, 44, 220, 234, 4} - }, - { {146, 209, 143, 233, 11}, - {84, 255, 23, 15, 2}, - {217, 127, 24, 180, 9}, - {79, 14, 143, 242, 10} - }, - { {112, 137, 1, 75, 11}, - {20, 58, 49, 140, 9}, - {221, 40, 9, 16, 14}, - {147, 24, 197, 194, 8} - }, - { {25, 57, 121, 226, 7}, - {119, 26, 70, 88, 15}, - {228, 121, 233, 201, 8}, - {241, 166, 37, 142, 14} - }, - { {68, 21, 61, 61, 7}, - {220, 51, 198, 180, 4}, - {235, 203, 202, 130, 2}, - {34, 214, 60, 195, 11} - }, - { {53, 205, 91, 140, 15}, - {188, 126, 117, 112, 10}, - {243, 29, 171, 58, 12}, - {80, 234, 231, 227, 13} - }, - { {161, 11, 165, 106, 0}, - {16, 163, 42, 74, 13}, - {5, 106, 93, 8, 5}, - {181, 37, 76, 80, 8} - }, - { {43, 135, 74, 96, 8}, - {34, 66, 63, 105, 0}, - {16, 101, 46, 29, 4}, - {9, 111, 196, 36, 4} - }, - { {181, 127, 184, 212, 4}, - {201, 142, 236, 122, 14}, - {34, 177, 223, 234, 13}, - {117, 227, 119, 25, 3} - }, - { {78, 32, 181, 166, 4}, - {155, 129, 66, 145, 7}, - {38, 90, 208, 71, 2}, - {232, 148, 40, 29, 9} - }, - { {97, 216, 151, 242, 12}, - {80, 197, 243, 216, 11}, - {52, 254, 145, 184, 6}, - {209, 188, 250, 48, 10} - }, - { {171, 52, 105, 255, 12}, - {123, 32, 231, 111, 7}, - {63, 249, 98, 205, 5}, - {239, 110, 112, 77, 14} - }, - { {89, 76, 164, 92, 2}, - {10, 189, 128, 232, 12}, - {67, 162, 83, 41, 10}, - {49, 112, 27, 213, 0} - }, - { {250, 28, 45, 46, 2}, - {90, 57, 38, 163, 13}, - {71, 75, 67, 133, 15}, - {188, 86, 73, 197, 10} - }, - { {151, 194, 75, 151, 2}, - {184, 92, 156, 71, 3}, - {78, 157, 36, 62, 9}, - {206, 35, 147, 161, 13} - }, - { {117, 146, 134, 36, 10}, - {200, 217, 59, 192, 0}, - {82, 70, 20, 154, 14}, - {0, 61, 201, 177, 3} - }, - { {28, 170, 254, 153, 10}, - {163, 249, 157, 20, 14}, - {89, 151, 245, 83, 8}, - {114, 139, 153, 252, 5} - }, - { {50, 54, 221, 193, 6}, - {113, 153, 108, 61, 2}, - {104, 59, 182, 196, 12}, - {75, 195, 105, 152, 14} - }, - { {136, 78, 82, 125, 6}, - {42, 116, 202, 62, 8}, - {107, 228, 167, 33, 1}, - {23, 197, 50, 229, 4} - }, - { {87, 113, 168, 199, 14}, - {201, 158, 69, 205, 7}, - {126, 49, 88, 238, 10}, - {235, 58, 39, 153, 3} - }, - { {245, 41, 37, 184, 1}, - {149, 43, 162, 194, 14}, - {129, 218, 73, 74, 15}, - {116, 52, 93, 74, 9} - }, - { {46, 89, 204, 26, 1}, - {230, 167, 164, 1, 9}, - {133, 131, 57, 167, 4}, - {152, 2, 94, 86, 7} - }, - { {127, 165, 138, 179, 1}, - {135, 202, 182, 229, 3}, - {140, 213, 26, 95, 14}, - {202, 118, 213, 62, 1} - }, - { {235, 67, 56, 79, 9}, - {14, 38, 45, 223, 5}, - {159, 33, 204, 45, 7}, - {175, 187, 70, 71, 0} - }, - { {57, 19, 213, 52, 5}, - {126, 139, 234, 80, 0}, - {162, 202, 188, 137, 12}, - {0, 165, 125, 23, 14} - }, - { {215, 155, 26, 59, 5}, - {196, 106, 222, 215, 9}, - {173, 197, 141, 158, 11}, - {158, 183, 181, 98, 3} - }, - { {13, 251, 231, 104, 13}, - {247, 231, 91, 72, 12}, - {177, 110, 125, 251, 0}, - {49, 45, 174, 126, 15} - }, - { {92, 215, 160, 49, 13}, - {198, 142, 219, 164, 4}, - {184, 192, 94, 179, 10}, - {34, 93, 183, 22, 3} - }, - { {185, 200, 247, 164, 11}, - {62, 221, 51, 82, 14}, - {210, 94, 241, 57, 13}, - {116, 172, 203, 183, 12} - }, - { {70, 152, 126, 6, 11}, - {236, 81, 21, 145, 13}, - {214, 7, 225, 150, 2}, - {184, 154, 136, 163, 7} - }, - { {110, 12, 93, 82, 7}, - {182, 17, 228, 185, 9}, - {228, 171, 163, 7, 6}, - {153, 210, 120, 134, 13} - }, - { {91, 197, 103, 187, 4}, - {50, 111, 210, 229, 7}, - {45, 222, 106, 61, 10}, - {234, 116, 191, 100, 12} - }, - { {30, 99, 2, 188, 2}, - {139, 126, 138, 1, 2}, - {67, 212, 12, 103, 8}, - {72, 5, 23, 237, 1} - }, - { {138, 199, 208, 70, 10}, - {42, 150, 25, 59, 1}, - {86, 32, 190, 53, 1}, - {141, 201, 134, 149, 4} - }, - { {166, 95, 226, 50, 6}, - {224, 214, 234, 35, 13}, - {100, 196, 127, 166, 5}, - {188, 69, 118, 176, 7} - }, - { {84, 0, 235, 97, 6}, - {176, 216, 70, 140, 4}, - {104, 109, 112, 2, 10}, - {35, 22, 33, 176, 13} - }, - { {247, 160, 102, 131, 6}, - {161, 89, 112, 199, 7}, - {108, 22, 96, 94, 15}, - {238, 48, 233, 168, 5} - }, - { {45, 208, 175, 223, 6}, - {218, 245, 244, 76, 7}, - {111, 191, 80, 187, 4}, - {227, 34, 250, 245, 11} - }, - { {59, 156, 182, 11, 1}, - {70, 233, 48, 117, 13}, - {141, 6, 211, 157, 12}, - {186, 224, 201, 118, 2} - }, - { {173, 106, 28, 175, 9}, - {143, 37, 47, 86, 11}, - {159, 83, 133, 107, 5}, - {214, 175, 74, 79, 1} - }, - { {147, 182, 46, 251, 5}, - {69, 105, 222, 111, 7}, - {173, 247, 70, 220, 9}, - {239, 103, 185, 106, 2} - }, - { {187, 33, 13, 147, 13}, - {23, 11, 229, 71, 3}, - {188, 155, 8, 77, 13}, - {206, 42, 125, 14, 8} - }, - { {222, 229, 80, 148, 11}, - {175, 30, 145, 179, 2}, - {210, 144, 170, 119, 11}, - {76, 216, 151, 143, 5} - }, - { {160, 205, 20, 121, 11}, - {4, 55, 179, 62, 8}, - {217, 226, 139, 48, 5}, - {23, 204, 222, 194, 0} - }, - { {42, 35, 171, 178, 7}, - {23, 210, 238, 1, 7}, - {228, 221, 92, 69, 4}, - {232, 7, 116, 190, 8} - }, - { {79, 103, 244, 181, 15}, - {175, 151, 203, 245, 6}, - {250, 210, 254, 111, 2}, - {106, 253, 62, 159, 5} - }, - { {88, 168, 21, 129, 8}, - {19, 9, 17, 148, 10}, - {24, 26, 129, 81, 10}, - {82, 152, 137, 12, 8} - }, - { {116, 52, 39, 214, 4}, - {217, 73, 224, 168, 7}, - {38, 190, 66, 194, 14}, - {225, 80, 121, 41, 11} - }, - { {29, 236, 64, 135, 4}, - {171, 12, 80, 100, 11}, - {46, 16, 35, 123, 8}, - {210, 96, 163, 13, 5} - }, - { {137, 42, 179, 160, 2}, - {19, 208, 10, 82, 14}, - {64, 92, 213, 73, 1}, - {116, 165, 0, 188, 8} - }, - { {56, 213, 116, 46, 14}, - {106, 63, 115, 48, 5}, - {119, 66, 234, 177, 12}, - {160, 204, 239, 197, 6} - }, - { {12, 67, 86, 122, 1}, - {166, 103, 138, 24, 1}, - {133, 230, 172, 35, 0}, - {129, 133, 30, 102, 5} - }, - { {93, 179, 17, 203, 1}, - {215, 42, 24, 220, 3}, - {141, 56, 140, 219, 10}, - {195, 177, 133, 78, 11} - }, - { {201, 87, 202, 31, 9}, - {110, 230, 141, 230, 1}, - {159, 133, 62, 169, 3}, - {134, 123, 22, 119, 6} - }, - { {162, 36, 87, 130, 3}, - {53, 81, 32, 51, 3}, - {196, 30, 162, 68, 5}, - {204, 192, 72, 170, 12} - }, - { {41, 172, 233, 143, 3}, - {63, 176, 52, 100, 15}, - {207, 25, 115, 89, 4}, - {242, 98, 192, 223, 12} - }, - { {204, 197, 44, 82, 8}, - {130, 7, 149, 170, 5}, - {20, 163, 74, 51, 3}, - {165, 90, 158, 4, 1} - }, - { {218, 139, 53, 120, 12}, - {18, 43, 219, 155, 12}, - {49, 234, 205, 21, 11}, - {61, 157, 189, 68, 8} - }, - { {213, 180, 185, 227, 6}, - {209, 152, 86, 254, 7}, - {108, 121, 210, 218, 11}, - {231, 246, 161, 152, 11} - }, - { {172, 156, 221, 60, 14}, - {250, 177, 247, 50, 8}, - {115, 203, 179, 147, 5}, - {20, 206, 248, 213, 15} - }, - { {203, 218, 168, 57, 1}, - {70, 164, 158, 199, 12}, - {137, 193, 85, 189, 3}, - {62, 55, 146, 86, 2} - }, - { {25, 134, 37, 107, 1}, - {22, 41, 26, 108, 5}, - {141, 106, 70, 25, 8}, - {163, 101, 137, 70, 8} - }, - { {186, 230, 204, 136, 9}, - {39, 173, 61, 35, 2}, - {145, 19, 54, 117, 13}, - {76, 75, 203, 94, 4} - }, - { {223, 145, 177, 14, 5}, - {222, 170, 80, 211, 5}, - {167, 8, 216, 159, 11}, - {172, 176, 165, 87, 11} - }, - { {161, 169, 213, 167, 5}, - {61, 131, 114, 86, 11}, - {174, 90, 185, 88, 5}, - {214, 164, 236, 27, 12} - }, - { {43, 101, 26, 169, 13}, - {7, 102, 103, 117, 2}, - {185, 85, 138, 109, 4}, - {74, 238, 102, 110, 0} - }, - { {77, 92, 46, 25, 13}, - {198, 101, 197, 228, 12}, - {185, 135, 67, 171, 2}, - {50, 122, 58, 102, 3} - }, - { {91, 22, 55, 54, 11}, - {94, 89, 139, 241, 5}, - {214, 206, 198, 141, 10}, - {168, 253, 25, 167, 10} - }, - { {247, 83, 214, 10, 8}, - {224, 239, 41, 211, 1}, - {21, 6, 188, 174, 15}, - {140, 185, 79, 112, 7} - }, - { {94, 132, 179, 125, 2}, - {154, 248, 146, 189, 4}, - {75, 236, 210, 23, 10}, - {43, 212, 145, 245, 9} - }, - { {252, 228, 58, 159, 2}, - {139, 124, 180, 182, 7}, - {79, 149, 194, 115, 15}, - {230, 210, 211, 237, 1} - }, - { {187, 146, 109, 26, 6}, - {114, 57, 252, 67, 5}, - {101, 139, 100, 157, 13}, - {172, 35, 249, 196, 14} - }, - { {237, 141, 35, 110, 5}, - {158, 98, 114, 234, 13}, - {167, 108, 75, 27, 7}, - {181, 116, 228, 103, 9} - }, - { {106, 248, 70, 243, 7}, - {103, 85, 242, 141, 11}, - {236, 246, 33, 245, 6}, - {219, 20, 250, 174, 6} - }, - { {234, 86, 165, 19, 8}, - {82, 133, 169, 167, 5}, - {28, 138, 86, 165, 7}, - {174, 89, 90, 20, 10} - }, - { {93, 73, 113, 70, 12}, - {186, 14, 65, 216, 13}, - {54, 40, 233, 43, 10}, - {177, 184, 39, 5, 13} - }, - { {71, 3, 96, 236, 10}, - {168, 50, 11, 201, 6}, - {83, 112, 108, 14, 2}, - {105, 61, 4, 193, 5} - }, - { {140, 75, 205, 103, 10}, - {186, 151, 15, 14, 9}, - {94, 107, 61, 35, 1}, - {151, 15, 14, 149, 13} - }, - { {41, 95, 57, 1, 14}, - {82, 22, 109, 116, 12}, - {120, 9, 207, 169, 4}, - {50, 235, 102, 132, 10} - }, - { {246, 181, 119, 233, 7}, - {245, 123, 114, 191, 6}, - {233, 126, 234, 214, 15}, - {111, 212, 237, 234, 15} - }, - { {82, 6, 2, 245, 10}, - {8, 88, 139, 173, 2}, - {90, 244, 6, 4, 10}, - {75, 93, 17, 161, 0} - }, - { {181, 17, 148, 146, 14}, - {192, 155, 225, 82, 3}, - {116, 146, 152, 138, 13}, - {196, 168, 125, 144, 3} - }, - { {72, 226, 153, 171, 7}, - {23, 180, 94, 148, 3}, - {237, 89, 148, 113, 2}, - {194, 151, 162, 222, 8} - }, - { {183, 159, 87, 55, 10}, - {248, 91, 187, 119, 9}, - {94, 206, 175, 158, 13}, - {158, 237, 221, 161, 15} - }, - { {168, 20, 198, 131, 14}, - {98, 209, 97, 38, 3}, - {124, 22, 50, 129, 5}, - {198, 72, 104, 180, 6} - }, - { {45, 169, 0, 88, 3}, - {135, 50, 176, 72, 8}, - {193, 160, 9, 91, 4}, - {17, 32, 212, 206, 1} - }, - { {212, 171, 164, 121, 7}, - {133, 187, 218, 142, 12}, - {233, 226, 93, 82, 11}, - {55, 21, 189, 218, 1} - }, - { {152, 42, 14, 101, 12}, - {11, 73, 79, 14, 8}, - {58, 103, 5, 65, 9}, - {23, 15, 41, 45, 0} - }, - { {244, 128, 204, 193, 5}, - {164, 137, 116, 142, 2}, - {168, 51, 48, 18, 15}, - {71, 18, 233, 18, 5} - }, - { {11, 32, 234, 115, 2}, - {35, 208, 134, 77, 5}, - {76, 229, 112, 77, 0}, - {171, 38, 16, 188, 4} - }, - { {169, 80, 103, 16, 10}, - {114, 85, 161, 66, 4}, - {80, 142, 96, 169, 5}, - {36, 40, 90, 164, 14} - }, - { {4, 134, 226, 238, 1}, - {172, 224, 26, 40, 7}, - {135, 116, 118, 18, 0}, - {225, 69, 128, 115, 5} - }, - { {176, 169, 114, 97, 5}, - {37, 74, 114, 30, 12}, - {168, 100, 233, 80, 13}, - {55, 132, 229, 42, 4} - }, - { {231, 63, 58, 37, 7}, - {205, 82, 110, 247, 12}, - {234, 69, 207, 206, 7}, - {62, 247, 100, 171, 3} - }, - { {23, 226, 5, 185, 8}, - {145, 45, 155, 69, 2}, - {25, 218, 4, 126, 8}, - {74, 45, 155, 72, 9} - }, - { {236, 154, 178, 178, 4}, - {194, 192, 250, 146, 15}, - {36, 212, 213, 147, 7}, - {244, 149, 240, 52, 3} - }, - { {217, 51, 158, 7, 6}, - {75, 219, 76, 214, 1}, - {110, 7, 156, 201, 11}, - {134, 179, 45, 189, 2} - }, - { {235, 136, 116, 249, 9}, - {38, 33, 179, 223, 14}, - {153, 242, 225, 29, 7}, - {127, 188, 216, 70, 4} - }, - { {148, 32, 165, 92, 12}, - {153, 169, 193, 10, 4}, - {51, 170, 80, 66, 9}, - {37, 8, 57, 89, 9} - }, - { {227, 100, 255, 153, 2}, - {49, 245, 164, 247, 6}, - {73, 159, 242, 108, 7}, - {110, 242, 90, 248, 12} - }, - { {138, 76, 184, 240, 10}, - {2, 148, 135, 59, 14}, - {80, 241, 211, 37, 1}, - {125, 206, 18, 148, 0} - }, - { {247, 42, 238, 118, 6}, - {169, 217, 238, 203, 13}, - {102, 231, 117, 78, 15}, - {189, 55, 121, 185, 5} - }, - { {115, 204, 107, 86, 9}, - {60, 76, 181, 233, 13}, - {150, 173, 99, 60, 14}, - {185, 122, 211, 35, 12} - }, - { {201, 156, 166, 165, 9}, - {78, 193, 19, 230, 14}, - {154, 86, 83, 153, 3}, - {118, 124, 136, 55, 2} - }, - { {54, 106, 12, 83, 5}, - {133, 13, 236, 13, 9}, - {172, 163, 5, 102, 12}, - {155, 3, 123, 10, 1} - }, - { {80, 158, 81, 148, 3}, - {124, 24, 152, 176, 10}, - {194, 152, 167, 144, 10}, - {80, 209, 145, 131, 14} - }, - { {186, 109, 34, 125, 1}, - {15, 110, 162, 47, 12}, - {139, 228, 75, 101, 13}, - {63, 68, 87, 111, 0} - }, - { {210, 172, 241, 99, 11}, - {53, 152, 19, 191, 13}, - {220, 104, 243, 84, 11}, - {191, 220, 129, 154, 12} - }, - { {217, 137, 254, 246, 8}, - {42, 203, 151, 218, 15}, - {22, 247, 249, 25, 11}, - {245, 190, 157, 53, 4} - }, - { {100, 23, 7, 187, 2}, - {208, 115, 170, 164, 3}, - {77, 222, 14, 130, 6}, - {194, 85, 92, 224, 11} - }, - { {249, 189, 49, 42, 11}, - {87, 58, 51, 242, 13}, - {213, 72, 203, 217, 15}, - {180, 252, 197, 206, 10} - }, - { {119, 115, 156, 32, 7}, - {197, 159, 110, 209, 0}, - {224, 67, 156, 238, 14}, - {8, 183, 111, 154, 3} - }, - { {104, 191, 141, 27, 15}, - {87, 179, 253, 164, 9}, - {253, 139, 31, 209, 6}, - {146, 91, 252, 222, 10} - }, - { {222, 221, 132, 166, 14}, - {202, 159, 83, 163, 11}, - {118, 82, 27, 183, 11}, - {220, 92, 175, 149, 3} - }, - { {66, 234, 236, 73, 11}, - {37, 181, 29, 141, 12}, - {217, 35, 117, 116, 2}, - {59, 27, 138, 218, 4} - }, - { {48, 43, 104, 213, 3}, - {45, 26, 172, 12, 14}, - {202, 177, 109, 64, 12}, - {115, 3, 85, 139, 4} - }, - { {78, 187, 147, 12, 14}, - {219, 242, 89, 145, 8}, - {115, 12, 157, 215, 2}, - {24, 153, 164, 253, 11} - }, - { {67, 156, 93, 155, 5}, - {116, 33, 212, 245, 11}, - {173, 155, 163, 156, 2}, - {218, 242, 184, 66, 14} - }, - { {225, 252, 146, 40, 13}, - {69, 228, 115, 242, 8}, - {177, 68, 147, 248, 7}, - {20, 252, 226, 122, 2} - }, - { {248, 166, 168, 212, 10}, - {11, 152, 189, 170, 6}, - {82, 177, 86, 81, 15}, - {101, 91, 209, 157, 0} - }, - { {181, 57, 2, 161, 8}, - {193, 74, 35, 70, 10}, - {24, 84, 9, 202, 13}, - {86, 44, 69, 40, 3} - }, - { {177, 99, 71, 94, 14}, - {57, 127, 233, 74, 1}, - {119, 174, 44, 104, 13}, - {133, 41, 127, 233, 12} - }, - { {173, 47, 90, 242, 9}, - {167, 66, 175, 122, 11}, - {148, 245, 175, 75, 5}, - {213, 239, 84, 46, 5} - }, - { {170, 181, 220, 133, 0}, - {107, 131, 52, 55, 2}, - {10, 19, 186, 213, 5}, - {78, 194, 204, 29, 6} - }, - { {179, 236, 37, 120, 6}, - {17, 61, 242, 107, 12}, - {97, 234, 67, 124, 13}, - {61, 100, 251, 200, 8} - }, - { {71, 153, 196, 64, 5}, - {228, 131, 80, 201, 8}, - {160, 34, 57, 158, 2}, - {25, 48, 172, 18, 7} - }, - { {139, 198, 141, 192, 4}, - {18, 133, 92, 107, 2}, - {32, 59, 22, 61, 1}, - {77, 99, 170, 20, 8} - }, - { {112, 47, 21, 221, 14}, - {25, 59, 233, 188, 10}, - {123, 186, 143, 64, 14}, - {83, 217, 125, 201, 8} - }, - { {180, 34, 147, 218, 9}, - {149, 232, 169, 26, 3}, - {149, 188, 148, 66, 13}, - {197, 137, 81, 122, 9} - }, - { {26, 202, 208, 56, 2}, - {34, 188, 154, 17, 8}, - {65, 192, 181, 53, 8}, - {24, 133, 147, 212, 4} - }, - { {129, 51, 99, 151, 1}, - {125, 66, 136, 70, 7}, - {142, 156, 108, 200, 1}, - {230, 33, 20, 43, 14} - }, - { {14, 105, 42, 215, 0}, - {139, 70, 132, 13, 15}, - {14, 181, 73, 103, 0}, - {251, 2, 22, 45, 1} - }, - { {138, 208, 92, 91, 9}, - {102, 37, 149, 31, 1}, - {157, 163, 160, 181, 1}, - {143, 138, 154, 70, 6} - }, - { {133, 230, 84, 193, 11}, - {165, 21, 25, 126, 2}, - {216, 50, 166, 122, 1}, - {71, 233, 138, 138, 5} - }, - { {61, 10, 194, 179, 11}, - {166, 216, 171, 68, 11}, - {220, 212, 53, 11, 12}, - {210, 45, 81, 182, 5} - }, - { {75, 191, 160, 85, 2}, - {75, 146, 152, 237, 12}, - {74, 160, 95, 221, 2}, - {59, 113, 148, 157, 2} - }, - { {114, 45, 222, 48, 6}, - {33, 219, 230, 177, 8}, - {96, 199, 187, 68, 14}, - {24, 214, 125, 184, 4} - }, - { {54, 157, 26, 205, 2}, - {200, 122, 52, 61, 10}, - {75, 53, 139, 150, 12}, - {91, 194, 197, 225, 3} - }, - { {162, 217, 148, 230, 0}, - {72, 135, 50, 27, 11}, - {6, 114, 153, 180, 5}, - {221, 132, 206, 17, 2} - }, - { {95, 173, 145, 57, 13}, - {151, 170, 211, 245, 8}, - {185, 200, 155, 95, 10}, - {26, 252, 181, 94, 9} - }, - { {87, 83, 90, 233, 6}, - {224, 126, 78, 221, 2}, - {105, 117, 172, 174, 10}, - {75, 183, 39, 224, 7} - }, - { {95, 94, 241, 212, 6}, - {250, 156, 200, 249, 14}, - {98, 184, 247, 175, 10}, - {121, 241, 51, 149, 15} - }, - { {235, 195, 230, 172, 12}, - {42, 231, 123, 195, 6}, - {51, 86, 124, 61, 7}, - {108, 61, 238, 117, 4} - }, - { {10, 153, 200, 53, 13}, - {110, 130, 215, 5, 8}, - {186, 193, 57, 149, 0}, - {26, 14, 180, 23, 6} - }, - { {84, 99, 228, 154, 5}, - {165, 175, 200, 128, 7}, - {165, 146, 124, 98, 10}, - {224, 17, 63, 90, 5} - }, - { {37, 103, 104, 147, 10}, - {161, 22, 173, 100, 7}, - {92, 145, 110, 106, 4}, - {226, 107, 86, 136, 5} - }, - { {171, 237, 55, 6, 4}, - {27, 71, 112, 115, 13}, - {38, 14, 203, 125, 5}, - {188, 224, 238, 45, 8} - }, - { {89, 189, 222, 217, 4}, - {99, 235, 212, 252, 10}, - {41, 183, 187, 217, 10}, - {83, 242, 189, 124, 6} - }, - { {100, 250, 4, 192, 9}, - {197, 5, 57, 136, 10}, - {144, 50, 5, 242, 6}, - {81, 25, 202, 10, 3} - }, - { {37, 14, 68, 231, 5}, - {172, 1, 106, 108, 11}, - {174, 114, 39, 10, 4}, - {211, 101, 104, 3, 5} - }, - { {229, 138, 55, 75, 12}, - {144, 97, 121, 222, 13}, - {61, 46, 197, 26, 7}, - {183, 185, 232, 96, 9} - }, - { {36, 202, 165, 116, 1}, - {156, 133, 186, 8, 12}, - {130, 234, 85, 50, 4}, - {49, 5, 218, 19, 9} - }, - { {228, 44, 246, 88, 8}, - {161, 225, 161, 186, 12}, - {17, 166, 243, 66, 7}, - {53, 216, 88, 120, 5} - }, - { {110, 162, 56, 83, 8}, - {131, 0, 189, 157, 5}, - {28, 161, 196, 87, 6}, - {171, 155, 208, 12, 1} - }, - { {247, 88, 19, 2, 5}, - {212, 76, 96, 211, 9}, - {164, 12, 129, 174, 15}, - {156, 176, 99, 34, 11} - }, - { {12, 235, 51, 187, 13}, - {151, 102, 219, 20, 15}, - {189, 220, 205, 115, 0}, - {242, 141, 182, 110, 9} - }, - { {120, 106, 232, 103, 0}, - {43, 140, 46, 140, 13}, - {14, 97, 117, 97, 14}, - {179, 23, 67, 29, 4} - }, - { {240, 222, 140, 150, 9}, - {76, 141, 189, 162, 11}, - {150, 147, 23, 176, 15}, - {212, 91, 219, 19, 2} - }, - { {38, 200, 30, 254, 6}, - {136, 117, 246, 25, 11}, - {103, 247, 129, 54, 4}, - {217, 134, 250, 225, 1} - }, - { {105, 244, 137, 8, 6}, - {83, 180, 116, 224, 0}, - {97, 9, 18, 249, 6}, - {0, 114, 226, 220, 10} - }, - { {63, 191, 146, 164, 13}, - {207, 202, 123, 113, 10}, - {178, 84, 159, 223, 12}, - {88, 237, 229, 63, 3} - }, - { {214, 85, 90, 118, 0}, - {232, 78, 134, 187, 1}, - {6, 229, 170, 166, 11}, - {141, 214, 23, 33, 7} - }, - { {156, 202, 226, 54, 13}, - {174, 204, 219, 2, 13}, - {182, 196, 117, 51, 9}, - {180, 13, 179, 55, 5} - }, - { {178, 86, 193, 106, 3}, - {116, 188, 42, 43, 1}, - {197, 104, 54, 164, 13}, - {141, 69, 67, 210, 14} - }, - { {215, 138, 199, 34, 1}, - {180, 201, 26, 195, 9}, - {132, 78, 53, 30, 11}, - {156, 53, 137, 50, 13} - }, - { {45, 193, 254, 38, 2}, - {170, 215, 54, 80, 5}, - {70, 71, 248, 59, 4}, - {160, 166, 206, 181, 5} - }, - { {175, 32, 81, 9, 0}, - {179, 32, 32, 87, 0}, - {9, 8, 160, 79, 5}, - {14, 160, 64, 76, 13} - }, - { {147, 44, 8, 175, 14}, - {9, 56, 71, 103, 11}, - {127, 81, 3, 76, 9}, - {222, 110, 33, 201, 0} - }, - { {83, 254, 202, 119, 5}, - {109, 204, 222, 237, 9}, - {174, 229, 55, 252, 10}, - {155, 119, 179, 59, 6} - }, - { {93, 41, 36, 151, 10}, - {139, 27, 129, 196, 15}, - {94, 146, 73, 75, 10}, - {242, 56, 29, 141, 1} - }, - { {47, 16, 26, 34, 11}, - {198, 80, 39, 81, 1}, - {212, 69, 128, 143, 4}, - {136, 174, 64, 166, 3} - }, - { {198, 173, 22, 177, 4}, - {129, 67, 210, 183, 10}, - {40, 214, 139, 86, 3}, - {94, 212, 188, 40, 1} - }, - { {210, 236, 164, 178, 0}, - {1, 141, 146, 163, 15}, - {4, 210, 83, 116, 11}, - {252, 84, 155, 24, 0} - }, - { {166, 250, 96, 61, 4}, - {233, 36, 250, 7, 12}, - {43, 192, 101, 246, 5}, - {62, 5, 242, 73, 7} - }, - { {48, 87, 195, 182, 9}, - {124, 206, 171, 32, 3}, - {150, 220, 62, 160, 12}, - {192, 77, 87, 51, 14} - }, - { {232, 23, 181, 77, 2}, - {90, 179, 40, 190, 4}, - {75, 42, 222, 129, 7}, - {39, 209, 76, 213, 10} - }, - { {64, 171, 252, 62, 0}, - {41, 163, 158, 144, 13}, - {7, 195, 253, 80, 2}, - {176, 151, 156, 89, 4} - }, - { {245, 58, 209, 110, 7}, - {253, 184, 106, 218, 9}, - {231, 104, 181, 202, 15}, - {149, 181, 97, 219, 15} - }, - { {54, 243, 160, 110, 8}, - {201, 174, 59, 9, 5}, - {23, 96, 92, 246, 12}, - {169, 13, 199, 89, 3} - }, - { {39, 120, 46, 65, 8}, - {193, 69, 37, 77, 12}, - {24, 39, 65, 238, 4}, - {59, 42, 74, 40, 3} - }, - { {62, 144, 98, 20, 6}, - {234, 88, 240, 1, 4}, - {98, 132, 96, 151, 12}, - {40, 0, 241, 165, 7} - }, - { {1, 253, 122, 212, 14}, - {105, 86, 213, 120, 14}, - {114, 181, 235, 248, 0}, - {113, 234, 182, 169, 6} - }, - { {200, 118, 91, 198, 4}, - {123, 68, 76, 186, 3}, - {38, 61, 166, 225, 3}, - {197, 211, 34, 45, 14} - }, - { {173, 61, 130, 21, 15}, - {207, 210, 225, 102, 8}, - {250, 132, 27, 203, 5}, - {22, 104, 116, 191, 3} - }, - { {150, 49, 122, 39, 14}, - {233, 90, 71, 23, 5}, - {126, 69, 232, 198, 9}, - {174, 142, 37, 169, 7} - }, - { {5, 248, 19, 202, 6}, - {209, 116, 80, 88, 11}, - {101, 60, 129, 250, 0}, - {209, 160, 162, 232, 11} - }, - { {14, 122, 220, 34, 14}, - {227, 149, 79, 17, 9}, - {116, 67, 181, 231, 0}, - {152, 143, 42, 156, 7} - }, - { {191, 82, 196, 165, 0}, - {234, 141, 42, 71, 2}, - {10, 82, 52, 175, 13}, - {78, 37, 75, 21, 7} - }, - { {114, 79, 106, 226, 14}, - {32, 94, 111, 169, 15}, - {116, 117, 111, 36, 14}, - {249, 95, 103, 160, 4} - }, - { {84, 15, 215, 234, 8}, - {176, 235, 11, 184, 11}, - {21, 126, 191, 2, 10}, - {209, 221, 13, 112, 13} - }, - { {156, 150, 47, 116, 1}, - {222, 73, 158, 42, 4}, - {130, 239, 70, 147, 9}, - {37, 71, 153, 39, 11} - }, - { {38, 64, 66, 176, 1}, - {164, 68, 162, 1, 2}, - {128, 212, 32, 38, 4}, - {72, 4, 82, 34, 5} - }, - { {25, 124, 183, 117, 4}, - {91, 205, 194, 124, 12}, - {42, 238, 211, 233, 8}, - {51, 228, 59, 61, 10} - }, - { {31, 43, 184, 80, 13}, - {135, 138, 205, 89, 12}, - {176, 161, 221, 79, 8}, - {57, 171, 53, 30, 1} - }, - { {200, 125, 108, 42, 13}, - {103, 39, 71, 162, 13}, - {181, 67, 107, 225, 3}, - {180, 94, 46, 78, 6} - }, - { {220, 141, 45, 237, 14}, - {154, 59, 87, 174, 14}, - {123, 123, 75, 19, 11}, - {119, 94, 173, 197, 9} - }, - { {203, 107, 1, 115, 8}, - {19, 6, 139, 207, 9}, - {28, 232, 13, 109, 3}, - {159, 61, 22, 12, 8} - }, - { {190, 66, 16, 54, 8}, - {138, 12, 171, 19, 1}, - {22, 192, 132, 39, 13}, - {140, 141, 83, 5, 1} - }, - { {187, 70, 34, 167, 7}, - {14, 92, 106, 103, 7}, - {238, 84, 70, 45, 13}, - {238, 101, 99, 167, 0} - }, - { {115, 50, 38, 239, 8}, - {73, 105, 43, 205, 7}, - {31, 118, 68, 204, 14}, - {235, 61, 73, 105, 2} - }, - { {73, 51, 173, 227, 9}, - {87, 131, 15, 204, 7}, - {156, 123, 92, 201, 2}, - {227, 63, 12, 30, 10} - }, - { {31, 28, 137, 108, 7}, - {222, 184, 70, 105, 8}, - {227, 105, 19, 143, 8}, - {25, 102, 33, 215, 11} - }, - { {137, 118, 101, 171, 10}, - {115, 53, 11, 102, 7}, - {93, 90, 102, 233, 1}, - {230, 109, 10, 204, 14} - }, - { {146, 244, 107, 161, 2}, - {113, 92, 22, 39, 6}, - {72, 93, 98, 244, 9}, - {110, 70, 131, 168, 14} - }, - { {41, 121, 129, 118, 7}, - {95, 150, 226, 72, 9}, - {230, 232, 25, 233, 4}, - {145, 36, 118, 159, 10} - }, - { {173, 112, 44, 248, 13}, - {199, 37, 231, 74, 6}, - {177, 243, 64, 235, 5}, - {101, 46, 122, 78, 3} - }, - { {71, 49, 152, 185, 0}, - {193, 162, 134, 213, 2}, - {9, 209, 152, 206, 2}, - {74, 182, 20, 88, 3} - }, - { {236, 46, 142, 6, 11}, - {143, 209, 45, 162, 9}, - {214, 7, 23, 67, 7}, - {148, 91, 72, 191, 1} - }, - { {54, 161, 214, 152, 0}, - {161, 235, 176, 17, 2}, - {1, 150, 184, 86, 12}, - {72, 128, 221, 120, 5} - }, - { {165, 206, 134, 80, 15}, - {132, 213, 249, 106, 8}, - {240, 166, 23, 58, 5}, - {21, 105, 250, 178, 1} - }, - { {99, 120, 112, 52, 8}, - {105, 4, 163, 209, 12}, - {18, 192, 225, 236, 6}, - {56, 188, 82, 9, 6} - }, - { {243, 171, 65, 212, 6}, - {57, 26, 248, 203, 10}, - {98, 184, 45, 92, 15}, - {93, 49, 245, 137, 12} - }, - { {230, 3, 42, 139, 5}, - {132, 98, 108, 135, 7}, - {173, 21, 76, 6, 7}, - {238, 19, 100, 98, 1} - }, - { {76, 17, 238, 199, 3}, - {238, 211, 4, 140, 7}, - {206, 55, 120, 131, 2}, - {227, 18, 12, 183, 7} - }, - { {38, 29, 229, 13, 6}, - {248, 179, 96, 37, 12}, - {107, 10, 123, 134, 4}, - {58, 64, 108, 209, 15} - }, - { {41, 102, 22, 227, 2}, - {3, 85, 42, 124, 3}, - {76, 118, 134, 105, 4}, - {195, 229, 74, 172, 0} - }, - { {55, 207, 241, 160, 10}, - {176, 158, 59, 113, 14}, - {80, 88, 255, 62, 12}, - {120, 237, 199, 144, 13} - }, - { {196, 87, 132, 122, 11}, - {196, 183, 139, 170, 1}, - {213, 226, 30, 162, 3}, - {133, 93, 30, 210, 3} - }, - { {119, 110, 82, 255, 9}, - {173, 108, 171, 253, 11}, - {159, 244, 167, 110, 14}, - {219, 253, 83, 107, 5} - }, - { {152, 8, 25, 187, 9}, - {22, 40, 135, 22, 11}, - {157, 217, 129, 1, 9}, - {214, 142, 17, 70, 8} - }, - { {80, 203, 43, 93, 8}, - {24, 110, 157, 140, 12}, - {27, 173, 77, 48, 10}, - {51, 27, 151, 97, 8} - }, - { {139, 105, 24, 6, 3}, - {15, 22, 4, 83, 9}, - {198, 1, 137, 109, 1}, - {156, 162, 6, 143, 0} - }, - { {18, 217, 194, 206, 9}, - {108, 238, 17, 9, 11}, - {151, 52, 57, 180, 8}, - {217, 8, 135, 115, 6} - }, - { {21, 140, 46, 115, 8}, - {128, 73, 151, 108, 13}, - {28, 231, 67, 26, 8}, - {179, 110, 153, 32, 1} - }, - { {107, 132, 1, 94, 10}, - {26, 48, 177, 233, 1}, - {87, 168, 2, 29, 6}, - {137, 120, 208, 197, 8} - }, - { {103, 105, 93, 185, 15}, - {181, 55, 231, 213, 10}, - {249, 219, 169, 110, 6}, - {90, 190, 126, 202, 13} - }, - { {62, 114, 116, 92, 1}, - {239, 45, 168, 25, 4}, - {131, 162, 228, 231, 12}, - {41, 129, 91, 79, 7} - }, - { {1, 202, 26, 87, 1}, - {12, 68, 156, 92, 9}, - {142, 165, 133, 56, 0}, - {147, 163, 146, 35, 0} - }, - { {34, 231, 3, 111, 5}, - {29, 102, 122, 45, 1}, - {175, 108, 14, 116, 4}, - {139, 69, 230, 107, 8} - }, - { {213, 220, 237, 174, 1}, - {252, 173, 22, 226, 15}, - {135, 91, 115, 186, 11}, - {244, 118, 139, 83, 15} - }, - { {128, 149, 50, 197, 11}, - {76, 82, 17, 62, 6}, - {218, 52, 202, 144, 1}, - {103, 200, 132, 163, 2} - }, - { {247, 228, 233, 213, 4}, - {185, 140, 244, 239, 6}, - {42, 185, 114, 126, 15}, - {111, 114, 243, 25, 13} - }, - { {89, 216, 79, 156, 11}, - {126, 125, 149, 192, 10}, - {211, 159, 33, 185, 10}, - {80, 58, 155, 231, 14} - }, - { {244, 46, 208, 133, 1}, - {173, 136, 40, 182, 10}, - {138, 16, 183, 66, 15}, - {86, 209, 65, 27, 5} - }, - { {93, 180, 169, 31, 10}, - {219, 184, 149, 228, 5}, - {95, 137, 82, 219, 10}, - {162, 122, 145, 221, 11} - }, - { {13, 88, 229, 177, 15}, - {246, 149, 195, 68, 14}, - {248, 218, 113, 171, 0}, - {114, 44, 58, 150, 15} - }, - { {105, 251, 159, 164, 3}, - {95, 215, 62, 208, 10}, - {194, 95, 157, 249, 6}, - {80, 183, 206, 191, 10} - }, - { {41, 186, 81, 246, 0}, - {123, 0, 186, 88, 11}, - {6, 248, 165, 217, 4}, - {209, 165, 208, 13, 14} - }, - { {88, 124, 103, 66, 7}, - {119, 93, 64, 168, 13}, - {228, 46, 99, 225, 10}, - {177, 80, 43, 174, 14} - }, - { {216, 185, 2, 30, 13}, - {79, 106, 209, 130, 9}, - {183, 132, 9, 209, 11}, - {148, 24, 181, 111, 2} - }, - { {187, 89, 68, 73, 14}, - {98, 63, 97, 79, 8}, - {121, 34, 41, 173, 13}, - {31, 40, 111, 196, 6} - }, - { {170, 35, 31, 53, 10}, - {27, 83, 175, 23, 0}, - {90, 207, 140, 69, 5}, - {14, 143, 92, 173, 8} - }, - { {13, 149, 148, 63, 4}, - {202, 163, 210, 116, 1}, - {47, 194, 154, 155, 0}, - {130, 228, 188, 85, 3} - }, - { {222, 196, 97, 37, 7}, - {190, 28, 82, 167, 4}, - {234, 72, 98, 55, 11}, - {46, 84, 163, 135, 13} - }, - { {5, 161, 105, 48, 13}, - {181, 2, 215, 64, 4}, - {176, 201, 104, 90, 0}, - {32, 46, 180, 10, 13} - }, - { {226, 81, 46, 181, 8}, - {72, 71, 167, 135, 6}, - {26, 215, 72, 164, 7}, - {110, 30, 94, 33, 2} - }, - { {171, 72, 226, 220, 0}, - {42, 228, 160, 75, 14}, - {3, 180, 113, 45, 5}, - {125, 32, 82, 117, 4} - }, - { {12, 182, 132, 148, 15}, - {207, 145, 217, 32, 2}, - {242, 146, 22, 211, 0}, - {64, 73, 184, 159, 3} - }, - { {146, 122, 107, 91, 1}, - {117, 108, 140, 15, 13}, - {141, 173, 101, 228, 9}, - {191, 3, 19, 106, 14} - }, - { {187, 144, 243, 69, 0}, - {122, 200, 48, 95, 4}, - {10, 44, 240, 157, 13}, - {47, 160, 193, 53, 14} - }, - { {99, 46, 143, 173, 0}, - {25, 225, 46, 229, 10}, - {11, 95, 23, 76, 6}, - {90, 119, 72, 121, 8} - }, - { {194, 238, 175, 42, 7}, - {21, 245, 94, 163, 13}, - {229, 79, 87, 116, 3}, - {188, 87, 170, 250, 8} - }, - { {30, 178, 175, 57, 13}, - {215, 233, 223, 5, 4}, - {185, 207, 84, 215, 8}, - {42, 15, 185, 126, 11} - }, - { {81, 21, 72, 197, 2}, - {104, 26, 4, 236, 2}, - {74, 49, 42, 136, 10}, - {67, 114, 5, 129, 6} - }, - { {70, 147, 213, 222, 4}, - {248, 163, 216, 153, 3}, - {39, 186, 188, 150, 2}, - {201, 145, 188, 81, 15} - }, - { {136, 142, 145, 97, 13}, - {22, 128, 91, 62, 8}, - {184, 104, 151, 17, 1}, - {23, 205, 160, 22, 8} - }, - { {104, 131, 111, 244, 15}, - {62, 83, 255, 136, 6}, - {242, 255, 108, 17, 6}, - {97, 31, 252, 167, 12} - }, - { {81, 189, 46, 68, 1}, - {77, 75, 20, 232, 12}, - {130, 39, 75, 216, 10}, - {49, 114, 141, 43, 2} - }, - { {122, 10, 196, 14, 9}, - {46, 169, 41, 129, 9}, - {151, 2, 53, 5, 14}, - {152, 25, 73, 87, 4} - }, - { {139, 183, 98, 180, 10}, - {107, 82, 155, 99, 6}, - {82, 212, 110, 221, 1}, - {108, 109, 148, 173, 6} - }, - { {28, 175, 131, 161, 10}, - {147, 218, 27, 36, 10}, - {88, 92, 31, 83, 8}, - {82, 77, 133, 188, 9} - }, - { {194, 112, 11, 187, 13}, - {85, 100, 199, 135, 3}, - {189, 221, 0, 228, 3}, - {206, 30, 50, 106, 10} - }, - { {251, 46, 208, 154, 10}, - {35, 184, 169, 243, 11}, - {85, 144, 183, 77, 15}, - {220, 249, 81, 220, 4} - }, - { {248, 82, 2, 148, 14}, - {74, 92, 233, 130, 2}, - {114, 148, 4, 161, 15}, - {68, 25, 115, 165, 2} - }, - { {122, 161, 4, 202, 6}, - {3, 59, 112, 137, 3}, - {101, 50, 8, 85, 14}, - {201, 16, 237, 204, 0} - }, - { {106, 150, 210, 188, 11}, - {110, 240, 187, 177, 2}, - {211, 212, 182, 149, 6}, - {72, 221, 208, 247, 6} - }, - { {40, 242, 236, 117, 14}, - {107, 149, 255, 12, 4}, - {122, 227, 116, 241, 4}, - {35, 15, 250, 157, 6} - }, - { {96, 112, 101, 235, 4}, - {113, 37, 98, 140, 7}, - {45, 122, 96, 224, 6}, - {227, 20, 106, 72, 14} - }, - { {122, 217, 179, 168, 12}, - {82, 238, 115, 145, 14}, - {49, 92, 217, 181, 14}, - {120, 156, 231, 116, 10} - }, - { {53, 114, 214, 203, 15}, - {229, 253, 105, 92, 3}, - {253, 54, 180, 234, 12}, - {195, 169, 107, 250, 7} - }, - { {212, 38, 239, 184, 15}, - {181, 249, 207, 162, 6}, - {241, 223, 118, 66, 11}, - {100, 95, 57, 250, 13} - }, - { {100, 238, 77, 186, 10}, - {177, 53, 191, 160, 11}, - {85, 219, 39, 114, 6}, - {208, 95, 218, 200, 13} - }, - { {152, 61, 122, 143, 1}, - {111, 106, 4, 54, 15}, - {143, 21, 235, 193, 9}, - {246, 194, 5, 111, 6} - }, - { {140, 163, 31, 73, 9}, - {151, 99, 29, 30, 0}, - {153, 47, 140, 83, 1}, - {7, 139, 140, 110, 9} - }, - { {2, 84, 87, 180, 6}, - {120, 85, 194, 49, 2}, - {98, 222, 162, 164, 0}, - {72, 196, 58, 161, 14} - }, - { {213, 58, 228, 20, 9}, - {237, 137, 137, 194, 12}, - {146, 130, 117, 202, 11}, - {52, 57, 25, 27, 7} - }, - { {157, 60, 100, 57, 6}, - {227, 57, 194, 102, 12}, - {105, 194, 99, 203, 9}, - {54, 100, 57, 204, 7} - }, - { {121, 83, 50, 238, 5}, - {78, 110, 106, 216, 7}, - {167, 116, 204, 169, 14}, - {225, 181, 103, 103, 2} - }, - { {112, 136, 77, 102, 4}, - {56, 9, 118, 136, 9}, - {38, 107, 33, 16, 14}, - {145, 22, 233, 1, 12} - }, - { {29, 211, 82, 141, 8}, - {234, 110, 25, 84, 2}, - {27, 20, 172, 187, 8}, - {66, 169, 135, 101, 7} - }, - { {216, 229, 29, 36, 6}, - {27, 31, 86, 178, 0}, - {98, 75, 138, 113, 11}, - {4, 214, 175, 141, 8} - }, - { {214, 2, 156, 163, 8}, - {128, 137, 15, 151, 3}, - {28, 83, 148, 6, 11}, - {206, 159, 9, 16, 1} - }, - { {217, 74, 182, 16, 12}, - {2, 205, 201, 210, 12}, - {48, 134, 213, 41, 11}, - {52, 185, 59, 52, 0} - }, - { {118, 107, 184, 136, 0}, - {129, 174, 44, 145, 14}, - {1, 17, 221, 102, 14}, - {120, 147, 71, 88, 1} - }, - { {253, 12, 107, 49, 9}, - {182, 72, 167, 230, 12}, - {152, 205, 99, 11, 15}, - {54, 126, 81, 38, 13} - }, - { {59, 255, 139, 157, 1}, - {95, 238, 188, 101, 10}, - {139, 157, 31, 253, 12}, - {90, 99, 215, 127, 10} - }, - { {231, 134, 244, 212, 14}, - {168, 145, 249, 251, 6}, - {114, 178, 246, 30, 7}, - {109, 249, 248, 145, 5} - }, - { {212, 230, 185, 183, 9}, - {157, 140, 159, 182, 7}, - {158, 217, 214, 114, 11}, - {230, 223, 147, 27, 9} - }, - { {207, 168, 187, 54, 9}, - {159, 192, 151, 211, 13}, - {150, 205, 209, 95, 3}, - {188, 190, 144, 63, 9} - }, - { {32, 164, 54, 108, 6}, - {9, 113, 114, 56, 4}, - {99, 102, 194, 80, 4}, - {33, 196, 232, 233, 0} - }, - { {92, 239, 185, 83, 6}, - {147, 158, 220, 188, 13}, - {108, 169, 223, 115, 10}, - {179, 211, 183, 156, 9} - }, - { {15, 6, 182, 96, 5}, - {134, 193, 74, 121, 4}, - {160, 102, 214, 15, 0}, - {41, 229, 40, 54, 1} - }, - { {187, 81, 210, 94, 7}, - {110, 254, 224, 91, 1}, - {231, 164, 184, 173, 13}, - {141, 160, 119, 247, 6} - }, - { {247, 226, 98, 100, 1}, - {173, 76, 58, 203, 4}, - {130, 100, 100, 126, 15}, - {45, 53, 195, 43, 5} - }, - { {129, 253, 169, 133, 5}, - {93, 134, 84, 102, 14}, - {170, 25, 91, 248, 1}, - {118, 98, 166, 27, 10} - }, - { {34, 65, 219, 197, 9}, - {60, 198, 37, 29, 2}, - {154, 61, 184, 36, 4}, - {75, 138, 70, 51, 12} - }, - { {86, 251, 75, 40, 0}, - {241, 110, 30, 129, 8}, - {1, 77, 45, 246, 10}, - {24, 23, 135, 104, 15} - }, - { {237, 166, 76, 109, 0}, - {171, 33, 62, 238, 0}, - {11, 99, 38, 91, 7}, - {7, 119, 200, 77, 5} - }, - { {206, 123, 8, 15, 4}, - {203, 38, 76, 135, 9}, - {47, 1, 13, 231, 3}, - {158, 19, 38, 77, 3} - }, - { {97, 146, 238, 126, 9}, - {108, 225, 191, 200, 5}, - {151, 231, 116, 152, 6}, - {161, 63, 216, 115, 6} - }, - { {148, 63, 245, 18, 4}, - {241, 139, 200, 50, 13}, - {36, 138, 255, 194, 9}, - {180, 193, 61, 24, 15} - }, - { {136, 179, 190, 173, 15}, - {79, 243, 95, 22, 6}, - {251, 87, 220, 209, 1}, - {102, 143, 172, 255, 2} - }, - { {155, 39, 229, 141, 0}, - {59, 171, 8, 103, 6}, - {11, 26, 126, 77, 9}, - {110, 97, 13, 93, 12} - }, - { {190, 11, 69, 253, 2}, - {186, 59, 170, 15, 10}, - {75, 250, 45, 7, 13}, - {95, 5, 93, 197, 13} - }, - { {105, 251, 58, 114, 10}, - {67, 86, 191, 216, 13}, - {84, 229, 205, 249, 6}, - {177, 191, 214, 172, 2} - }, - { {97, 56, 58, 131, 2}, - {65, 80, 36, 212, 15}, - {76, 21, 193, 200, 6}, - {242, 178, 64, 168, 2} - }, - { {102, 136, 99, 155, 3}, - {180, 112, 176, 133, 15}, - {205, 156, 97, 22, 6}, - {250, 16, 208, 226, 13} - }, - { {84, 208, 28, 153, 2}, - {192, 61, 148, 148, 2}, - {73, 147, 128, 178, 10}, - {66, 146, 155, 192, 3} - }, - { {144, 177, 12, 2, 10}, - {65, 27, 21, 2, 1}, - {84, 3, 8, 208, 9}, - {132, 10, 141, 136, 2} - }, - { {246, 103, 252, 109, 1}, - {173, 175, 46, 191, 4}, - {139, 99, 254, 102, 15}, - {47, 215, 79, 91, 5} - }, - { {122, 205, 117, 129, 3}, - {54, 31, 48, 181, 14}, - {200, 26, 235, 53, 14}, - {122, 208, 207, 134, 12} - }, - { {58, 189, 7, 246, 1}, - {95, 75, 178, 41, 11}, - {134, 254, 11, 213, 12}, - {217, 68, 221, 47, 10} - }, - { {84, 48, 18, 71, 3}, - {205, 88, 0, 156, 1}, - {206, 36, 128, 194, 10}, - {131, 144, 1, 171, 3} - }, - { {6, 119, 148, 213, 8}, - {201, 135, 137, 61, 2}, - {26, 178, 158, 230, 0}, - {75, 201, 30, 25, 3} - }, - { {168, 85, 177, 32, 1}, - {86, 134, 34, 50, 4}, - {128, 72, 218, 161, 5}, - {36, 196, 70, 22, 10} - }, - { {156, 180, 215, 253, 11}, - {255, 249, 147, 62, 2}, - {219, 254, 178, 211, 9}, - {71, 204, 153, 255, 15} - }, - { {49, 161, 147, 230, 10}, - {25, 218, 51, 88, 3}, - {86, 124, 152, 88, 12}, - {193, 172, 197, 185, 8} - }, - { {61, 163, 183, 109, 4}, - {155, 235, 122, 92, 4}, - {43, 110, 220, 91, 12}, - {35, 165, 237, 125, 9} - }, - { {65, 83, 241, 224, 4}, - {112, 134, 74, 216, 6}, - {32, 120, 252, 168, 2}, - {97, 181, 38, 16, 14} - }, - { {85, 68, 51, 91, 5}, - {148, 108, 192, 252, 5}, - {173, 172, 194, 42, 10}, - {163, 240, 51, 98, 9} - }, - { {146, 246, 18, 73, 13}, - {69, 108, 89, 63, 0}, - {185, 36, 134, 244, 9}, - {15, 201, 163, 106, 2} - }, - { {38, 154, 53, 248, 10}, - {208, 49, 187, 25, 14}, - {81, 250, 197, 150, 4}, - {121, 141, 216, 192, 11} - }, - { {4, 38, 119, 137, 6}, - {177, 113, 72, 52, 6}, - {105, 30, 230, 66, 0}, - {98, 193, 40, 232, 13} - }, - { {148, 135, 205, 198, 15}, - {188, 155, 93, 42, 3}, - {246, 59, 62, 18, 9}, - {197, 75, 173, 147, 13} - }, - { {78, 167, 231, 217, 12}, - {179, 227, 217, 173, 6}, - {57, 190, 126, 87, 2}, - {107, 89, 188, 124, 13} - }, - { {46, 54, 115, 69, 11}, - {255, 80, 41, 61, 4}, - {218, 44, 230, 199, 4}, - {43, 201, 64, 175, 15} - }, - { {58, 94, 143, 48, 4}, - {82, 205, 238, 33, 8}, - {32, 207, 23, 165, 12}, - {24, 71, 123, 52, 10} - }, - { {118, 217, 250, 167, 7}, - {236, 222, 118, 149, 15}, - {238, 85, 249, 182, 14}, - {250, 150, 231, 179, 7} - }, - { {100, 228, 234, 29, 15}, - {173, 244, 245, 164, 4}, - {251, 133, 114, 114, 6}, - {34, 90, 242, 251, 5} - }, - { {18, 167, 180, 52, 13}, - {13, 139, 219, 49, 4}, - {178, 194, 222, 84, 8}, - {40, 205, 189, 27, 0} - }, - { {73, 115, 217, 85, 3}, - {127, 150, 140, 220, 0}, - {202, 169, 188, 233, 2}, - {3, 179, 22, 159, 14} - }, - { {205, 180, 24, 151, 5}, - {207, 0, 212, 246, 3}, - {174, 145, 130, 219, 3}, - {198, 242, 176, 15, 3} - }, - { {139, 218, 118, 210, 14}, - {98, 85, 217, 91, 15}, - {116, 182, 229, 189, 1}, - {253, 169, 186, 164, 6} - }, - { {245, 74, 77, 236, 8}, - {184, 45, 47, 202, 10}, - {19, 123, 37, 42, 15}, - {85, 63, 75, 65, 13} - }, - { {129, 22, 22, 248, 10}, - {64, 113, 139, 122, 2}, - {81, 246, 134, 136, 1}, - {69, 237, 24, 224, 2} - }, - { {64, 54, 244, 230, 11}, - {109, 145, 11, 184, 7}, - {214, 114, 246, 192, 2}, - {225, 221, 8, 155, 6} - }, - { {39, 54, 40, 42, 6}, - {193, 48, 110, 97, 5}, - {101, 65, 70, 206, 4}, - {168, 103, 96, 200, 3} - }, - { {164, 19, 210, 41, 15}, - {228, 242, 107, 22, 0}, - {249, 68, 188, 130, 5}, - {6, 141, 100, 242, 7} - }, - { {144, 31, 228, 193, 7}, - {100, 155, 72, 46, 14}, - {232, 50, 127, 128, 9}, - {119, 65, 45, 146, 6} - }, - { {154, 191, 206, 86, 14}, - {107, 219, 221, 43, 9}, - {118, 167, 63, 213, 9}, - {157, 75, 189, 189, 6} - }, - { {45, 50, 91, 158, 15}, - {255, 112, 237, 80, 3}, - {247, 157, 164, 203, 4}, - {192, 171, 112, 239, 15} - }, - { {217, 144, 31, 106, 9}, - {86, 105, 23, 218, 1}, - {149, 111, 128, 153, 11}, - {133, 190, 137, 102, 10} - }, - { {58, 166, 79, 173, 14}, - {59, 121, 127, 37, 2}, - {123, 95, 38, 85, 12}, - {74, 79, 233, 237, 12} - }, - { {101, 40, 141, 96, 11}, - {149, 145, 39, 200, 8}, - {208, 107, 17, 74, 6}, - {17, 62, 72, 154, 9} - }, - { {10, 83, 233, 96, 11}, - {118, 150, 15, 9, 4}, - {208, 105, 124, 165, 0}, - {41, 15, 6, 150, 14} - }, - { {233, 224, 156, 62, 4}, - {11, 165, 246, 210, 1}, - {39, 195, 144, 121, 7}, - {132, 182, 250, 93, 0} - }, - { {77, 155, 120, 99, 13}, - {230, 2, 95, 220, 13}, - {188, 97, 237, 155, 2}, - {179, 191, 164, 6, 7} - }, - { {217, 139, 180, 131, 3}, - {6, 155, 24, 214, 15}, - {204, 18, 221, 25, 11}, - {246, 177, 141, 150, 0} - }, - { {52, 177, 129, 237, 7}, - {221, 186, 114, 12, 2}, - {235, 120, 24, 210, 12}, - {67, 4, 229, 219, 11} - }, - { {91, 55, 63, 73, 0}, - {83, 107, 12, 253, 4}, - {9, 47, 206, 205, 10}, - {43, 243, 13, 108, 10} - }, - { {133, 114, 183, 185, 1}, - {213, 229, 138, 86, 6}, - {137, 222, 212, 234, 1}, - {102, 165, 26, 122, 11} - }, - { {44, 93, 5, 106, 4}, - {210, 39, 98, 40, 9}, - {37, 106, 11, 163, 4}, - {145, 68, 110, 68, 11} - }, - { {228, 103, 18, 150, 13}, - {141, 70, 233, 178, 3}, - {182, 148, 142, 98, 7}, - {196, 217, 118, 43, 1} - }, - { {42, 8, 214, 247, 8}, - {42, 193, 163, 29, 11}, - {30, 246, 177, 5, 4}, - {219, 140, 88, 53, 4} - }, - { {69, 41, 73, 198, 1}, - {189, 2, 4, 200, 11}, - {134, 57, 41, 74, 2}, - {209, 50, 4, 11, 13} - }, - { {37, 1, 236, 91, 7}, - {164, 179, 228, 76, 5}, - {237, 163, 120, 10, 4}, - {163, 34, 124, 210, 5} - }, - { {184, 245, 71, 121, 12}, - {115, 111, 243, 46, 0}, - {57, 238, 42, 241, 13}, - {7, 76, 255, 108, 14} - }, - { {163, 183, 234, 75, 3}, - {101, 242, 60, 111, 5}, - {205, 37, 126, 220, 5}, - {175, 99, 196, 250, 6} - }, - { {68, 61, 107, 155, 4}, - {241, 98, 196, 164, 15}, - {45, 157, 107, 194, 2}, - {242, 82, 52, 104, 15} - }, - { {190, 45, 203, 132, 8}, - {187, 202, 37, 35, 10}, - {18, 29, 59, 71, 13}, - {92, 74, 69, 61, 13} - }, - { {181, 31, 196, 126, 12}, - {232, 171, 235, 106, 9}, - {55, 226, 63, 138, 13}, - {149, 109, 125, 81, 7} - }, - { {111, 56, 207, 152, 8}, - {243, 225, 165, 193, 10}, - {17, 159, 49, 207, 6}, - {88, 58, 88, 124, 15} - }, - { {144, 242, 254, 147, 4}, - {97, 205, 220, 22, 7}, - {44, 151, 244, 240, 9}, - {230, 131, 187, 56, 6} - }, - { {7, 253, 84, 10, 11}, - {229, 55, 17, 113, 9}, - {213, 2, 171, 254, 0}, - {152, 232, 142, 202, 7} - }, - { {238, 83, 195, 208, 8}, - {242, 198, 169, 139, 2}, - {16, 188, 60, 167, 7}, - {77, 25, 86, 52, 15} - }, - { {247, 9, 142, 188, 10}, - {136, 251, 167, 195, 10}, - {83, 215, 25, 14, 15}, - {92, 62, 93, 241, 1} - }, - { {101, 70, 124, 48, 9}, - {164, 5, 175, 240, 4}, - {144, 195, 230, 42, 6}, - {32, 255, 90, 2, 5} - }, - { {174, 111, 215, 151, 14}, - {187, 215, 233, 55, 11}, - {126, 158, 191, 103, 5}, - {222, 201, 126, 189, 13} - }, - { {139, 196, 115, 158, 9}, - {62, 100, 145, 115, 7}, - {151, 156, 226, 61, 1}, - {236, 232, 146, 103, 12} - }, - { {151, 25, 48, 254, 4}, - {200, 42, 194, 91, 15}, - {39, 240, 201, 142, 9}, - {253, 164, 53, 65, 3} - }, - { {154, 117, 45, 245, 7}, - {95, 31, 198, 47, 6}, - {234, 251, 74, 229, 9}, - {111, 70, 63, 143, 10} - }, - { {189, 71, 254, 83, 14}, - {162, 223, 237, 126, 5}, - {124, 167, 254, 43, 13}, - {167, 235, 127, 180, 5} - }, - { {111, 21, 48, 146, 8}, - {194, 2, 161, 241, 7}, - {20, 144, 202, 143, 6}, - {232, 248, 84, 4, 3} - }, - { {93, 225, 204, 72, 9}, - {167, 175, 21, 200, 0}, - {145, 35, 56, 123, 10}, - {1, 58, 143, 94, 5} - }, - { {17, 28, 227, 220, 13}, - {124, 232, 193, 104, 14}, - {179, 188, 115, 136, 8}, - {113, 104, 49, 115, 14} - }, - { {164, 199, 225, 87, 12}, - {184, 134, 249, 46, 5}, - {62, 168, 126, 50, 5}, - {167, 73, 246, 17, 13} - }, - { {85, 38, 145, 17, 2}, - {145, 152, 136, 244, 0}, - {72, 136, 150, 74, 10}, - {2, 241, 17, 152, 9} - }, - { {157, 101, 17, 128, 5}, - {151, 14, 64, 114, 2}, - {160, 24, 138, 107, 9}, - {68, 224, 39, 14, 9} - }, - { {102, 99, 193, 203, 3}, - {181, 182, 40, 141, 3}, - {205, 56, 60, 102, 6}, - {203, 17, 70, 218, 13} - }, - { {191, 197, 148, 0, 2}, - {130, 159, 48, 115, 0}, - {64, 2, 154, 63, 13}, - {12, 224, 207, 148, 1} - }, - { {58, 53, 0, 56, 7}, - {71, 58, 226, 33, 0}, - {225, 192, 10, 197, 12}, - {8, 68, 117, 206, 2} - }, - { {118, 212, 246, 121, 12}, - {224, 237, 243, 189, 4}, - {57, 230, 242, 182, 14}, - {43, 220, 251, 112, 7} - }, - { {68, 111, 38, 103, 11}, - {141, 87, 11, 172, 13}, - {222, 102, 79, 98, 2}, - {179, 93, 14, 171, 1} - }, - { {23, 174, 89, 233, 7}, - {181, 56, 94, 125, 10}, - {233, 121, 167, 94, 8}, - {91, 231, 161, 202, 13} - }, - { {54, 19, 45, 99, 14}, - {208, 27, 111, 13, 5}, - {124, 107, 76, 134, 12}, - {171, 15, 109, 128, 11} - }, - { {142, 7, 73, 43, 15}, - {182, 50, 79, 39, 1}, - {253, 73, 46, 7, 1}, - {142, 79, 36, 198, 13} - }, - { {24, 138, 205, 223, 1}, - {62, 169, 156, 12, 11}, - {143, 187, 53, 17, 8}, - {211, 3, 153, 87, 12} - }, - { {237, 144, 82, 208, 2}, - {226, 80, 176, 218, 2}, - {64, 180, 160, 155, 7}, - {69, 176, 208, 164, 7} - }, - { {91, 107, 208, 31, 5}, - {47, 174, 200, 213, 9}, - {175, 128, 189, 109, 10}, - {154, 177, 55, 95, 4} - }, - { {132, 164, 208, 112, 14}, - {161, 144, 211, 58, 0}, - {112, 224, 178, 82, 1}, - {5, 204, 176, 152, 5} - }, - { {201, 178, 163, 175, 4}, - {91, 224, 90, 198, 7}, - {47, 92, 84, 217, 3}, - {230, 53, 160, 125, 10} - }, - { {171, 24, 133, 47, 12}, - {90, 161, 99, 71, 9}, - {63, 74, 17, 141, 5}, - {158, 44, 104, 85, 10} - }, - { {157, 233, 24, 57, 0}, - {131, 46, 150, 86, 8}, - {9, 193, 137, 123, 9}, - {22, 166, 151, 76, 1} - }, - { {53, 118, 238, 158, 0}, - {233, 237, 172, 96, 7}, - {7, 151, 118, 234, 12}, - {224, 99, 91, 121, 7} - }, - { {232, 208, 13, 33, 0}, - {82, 5, 54, 134, 0}, - {8, 75, 0, 177, 7}, - {6, 22, 202, 4, 10} - }, - { {112, 155, 90, 149, 12}, - {104, 74, 253, 148, 10}, - {58, 149, 173, 144, 14}, - {82, 155, 245, 33, 6} - }, - { {169, 2, 42, 49, 4}, - {2, 64, 238, 70, 4}, - {40, 197, 68, 9, 5}, - {38, 39, 112, 36, 0} - }, - { {160, 62, 162, 185, 12}, - {65, 224, 235, 38, 14}, - {57, 212, 87, 192, 5}, - {118, 77, 112, 120, 2} - }, - { {30, 88, 158, 197, 14}, - {202, 221, 69, 29, 10}, - {122, 55, 145, 167, 8}, - {91, 138, 43, 181, 3} - }, - { {116, 71, 14, 237, 13}, - {140, 111, 111, 172, 2}, - {187, 119, 14, 34, 14}, - {67, 95, 111, 99, 1} - }, - { {54, 189, 68, 21, 9}, - {237, 11, 177, 37, 8}, - {154, 130, 43, 214, 12}, - {26, 72, 221, 11, 7} - }, - { {183, 248, 210, 81, 6}, - {225, 220, 240, 95, 8}, - {104, 164, 177, 254, 13}, - {31, 160, 243, 184, 7} - }, - { {220, 222, 75, 250, 5}, - {246, 108, 222, 170, 11}, - {165, 253, 39, 179, 11}, - {213, 87, 179, 102, 15} - }, - { {158, 218, 153, 195, 0}, - {210, 140, 28, 31, 11}, - {12, 57, 149, 183, 9}, - {223, 131, 131, 20, 11} - }, - { {143, 251, 152, 168, 11}, - {199, 182, 31, 83, 10}, - {209, 81, 157, 255, 1}, - {92, 175, 134, 222, 3} - }, - { {154, 199, 119, 231, 1}, - {62, 79, 26, 63, 7}, - {142, 126, 238, 53, 9}, - {239, 197, 143, 39, 12} - }, - { {141, 10, 83, 203, 3}, - {182, 112, 8, 94, 11}, - {205, 60, 165, 11, 1}, - {215, 161, 0, 230, 13} - }, - { {201, 237, 29, 206, 13}, - {31, 39, 85, 250, 11}, - {183, 59, 139, 121, 3}, - {213, 250, 174, 79, 8} - }, - { {194, 56, 83, 94, 10}, - {121, 112, 129, 155, 9}, - {87, 172, 161, 196, 3}, - {157, 152, 16, 233, 14} - }, - { {238, 74, 166, 67, 7}, - {134, 213, 104, 143, 13}, - {236, 38, 85, 39, 7}, - {191, 17, 106, 182, 1} - }, - { {205, 131, 69, 161, 14}, - {178, 19, 91, 198, 2}, - {120, 90, 44, 27, 3}, - {70, 61, 172, 132, 13} - }, - { {90, 25, 224, 90, 14}, - {98, 186, 193, 137, 13}, - {117, 160, 121, 133, 10}, - {185, 24, 53, 212, 6} - }, - { {150, 215, 128, 210, 5}, - {196, 142, 216, 43, 3}, - {164, 176, 30, 182, 9}, - {205, 65, 183, 18, 3} - }, - { {226, 200, 133, 24, 5}, - {20, 165, 240, 131, 8}, - {161, 138, 17, 52, 7}, - {28, 16, 250, 82, 8} - }, - { {197, 49, 79, 141, 11}, - {253, 115, 5, 198, 2}, - {219, 31, 40, 202, 3}, - {70, 58, 12, 235, 15} - }, - { {164, 124, 55, 238, 1}, - {221, 101, 34, 58, 15}, - {135, 126, 195, 226, 5}, - {245, 196, 74, 107, 11} - }, - { {29, 130, 198, 248, 12}, - {162, 233, 219, 72, 2}, - {49, 246, 52, 27, 8}, - {65, 45, 185, 116, 5} - }, - { {50, 92, 92, 108, 8}, - {104, 45, 39, 57, 8}, - {19, 99, 163, 164, 12}, - {25, 206, 75, 65, 6} - }, - { {93, 157, 27, 174, 0}, - {218, 106, 22, 240, 11}, - {7, 93, 139, 155, 10}, - {208, 246, 133, 101, 11} - }, - { {149, 73, 224, 117, 14}, - {168, 158, 195, 78, 12}, - {122, 224, 121, 42, 9}, - {55, 44, 55, 145, 5} - }, - { {138, 104, 69, 193, 5}, - {55, 5, 64, 15, 10}, - {168, 58, 33, 101, 1}, - {95, 0, 42, 14, 12} - }, - { {173, 249, 241, 15, 14}, - {251, 182, 113, 86, 13}, - {127, 8, 249, 251, 5}, - {182, 168, 230, 221, 15} - }, - { {149, 206, 243, 11, 0}, - {176, 236, 24, 118, 13}, - {13, 12, 247, 58, 9}, - {182, 225, 131, 112, 13} - }, - { {113, 147, 59, 32, 13}, - {84, 74, 127, 208, 4}, - {176, 77, 204, 152, 14}, - {32, 191, 229, 34, 10} - }, - { {31, 13, 227, 166, 15}, - {190, 218, 67, 97, 15}, - {246, 92, 123, 15, 8}, - {248, 108, 37, 183, 13} - }, - { {89, 111, 91, 186, 6}, - {51, 126, 206, 240, 11}, - {101, 221, 175, 105, 10}, - {208, 247, 55, 236, 12} - }, - { {205, 97, 217, 243, 4}, - {179, 134, 198, 222, 3}, - {44, 249, 184, 107, 3}, - {199, 182, 54, 28, 13} - }, - { {164, 120, 173, 50, 8}, - {209, 133, 167, 2, 13}, - {20, 203, 81, 226, 5}, - {180, 14, 90, 24, 11} - }, - { {218, 222, 64, 18, 15}, - {102, 28, 217, 163, 9}, - {244, 128, 39, 181, 11}, - {156, 89, 179, 134, 6} - }, - { {209, 87, 100, 55, 0}, - {104, 15, 138, 230, 5}, - {14, 194, 110, 168, 11}, - {166, 117, 31, 1, 6} - }, - { {58, 43, 245, 210, 2}, - {51, 155, 168, 25, 15}, - {68, 186, 253, 69, 12}, - {249, 129, 93, 156, 12} - }, - { {35, 3, 227, 72, 13}, - {52, 226, 105, 73, 4}, - {177, 44, 124, 12, 4}, - {41, 41, 100, 114, 12} - }, - { {65, 185, 191, 154, 9}, - {85, 227, 149, 208, 15}, - {149, 159, 217, 216, 2}, - {240, 186, 156, 122, 10} - }, - { {90, 78, 147, 238, 3}, - {30, 252, 10, 185, 11}, - {199, 124, 151, 37, 10}, - {217, 213, 3, 247, 8} - }, - { {70, 43, 68, 81, 2}, - {161, 19, 136, 141, 8}, - {72, 162, 45, 70, 2}, - {27, 17, 28, 136, 5} - }, - { {78, 1, 113, 156, 1}, - {190, 34, 128, 145, 6}, - {131, 152, 232, 7, 2}, - {104, 144, 20, 71, 13} - }, - { {115, 160, 217, 19, 7}, - {53, 152, 244, 213, 1}, - {236, 137, 176, 92, 14}, - {138, 178, 241, 154, 12} - }, - { {147, 43, 65, 109, 13}, - {61, 42, 75, 79, 8}, - {187, 104, 45, 76, 9}, - {31, 45, 37, 75, 12} - }, - { {255, 75, 97, 90, 7}, - {182, 62, 232, 203, 13}, - {229, 168, 109, 47, 15}, - {189, 49, 119, 198, 13} - }, - { {44, 136, 15, 26, 12}, - {146, 97, 245, 0, 9}, - {53, 143, 1, 19, 4}, - {144, 10, 248, 100, 9} - }, - { {111, 59, 182, 170, 1}, - {199, 227, 42, 209, 15}, - {133, 86, 221, 207, 6}, - {248, 181, 76, 126, 3} - }, - { {71, 109, 33, 174, 6}, - {153, 54, 66, 225, 15}, - {103, 88, 75, 110, 2}, - {248, 116, 38, 201, 9} - }, - { {249, 194, 144, 104, 8}, - {2, 172, 59, 218, 0}, - {17, 96, 148, 57, 15}, - {5, 189, 195, 84, 0} - }, - { {118, 129, 17, 33, 0}, - {144, 10, 50, 149, 0}, - {8, 72, 136, 22, 14}, - {10, 148, 197, 0, 9} - }, - { {98, 52, 88, 245, 5}, - {109, 0, 230, 189, 2}, - {170, 241, 162, 196, 6}, - {75, 214, 112, 11, 6} - }, - { {243, 25, 25, 225, 15}, - {84, 26, 103, 223, 10}, - {248, 121, 137, 140, 15}, - {95, 190, 101, 130, 10} - }, - { {239, 71, 167, 221, 1}, - {158, 231, 168, 239, 6}, - {139, 190, 94, 47, 7}, - {111, 113, 94, 119, 9} - }, - { {198, 199, 119, 128, 8}, - {176, 71, 25, 179, 6}, - {16, 30, 238, 54, 3}, - {108, 217, 142, 32, 13} - }, - { {238, 201, 111, 9, 6}, - {178, 119, 116, 135, 12}, - {105, 15, 105, 55, 7}, - {62, 18, 238, 228, 13} - }, - { {155, 60, 155, 218, 15}, - {87, 248, 197, 123, 11}, - {245, 189, 147, 205, 9}, - {221, 234, 49, 254, 10} - }, - { {111, 212, 50, 87, 3}, - {206, 84, 176, 253, 5}, - {206, 164, 194, 191, 6}, - {171, 240, 210, 167, 3} - }, - { {96, 231, 208, 170, 9}, - {37, 166, 59, 176, 3}, - {149, 80, 190, 112, 6}, - {192, 221, 198, 90, 4} - }, - { {6, 81, 205, 163, 4}, - {240, 135, 70, 5, 3}, - {44, 91, 56, 166, 0}, - {202, 6, 46, 16, 15} - }, - { {226, 172, 192, 78, 7}, - {45, 176, 112, 171, 9}, - {231, 32, 51, 84, 7}, - {157, 80, 224, 219, 4} - }, - { {148, 96, 209, 162, 2}, - {177, 156, 2, 18, 3}, - {68, 88, 176, 98, 9}, - {196, 132, 3, 152, 13} - }, - { {130, 183, 155, 23, 7}, - {93, 210, 220, 55, 1}, - {238, 141, 158, 212, 1}, - {142, 195, 180, 187, 10} - }, - { {78, 177, 137, 244, 11}, - {223, 146, 151, 137, 2}, - {210, 249, 24, 215, 2}, - {73, 30, 148, 159, 11} - }, - { {203, 249, 195, 177, 14}, - {115, 214, 211, 199, 10}, - {120, 220, 57, 253, 3}, - {94, 60, 182, 188, 14} - }, - { {111, 108, 100, 132, 1}, - {175, 5, 32, 225, 14}, - {130, 18, 99, 111, 6}, - {120, 112, 74, 15, 5} - }, - { {229, 78, 80, 162, 14}, - {160, 20, 107, 242, 11}, - {116, 80, 167, 42, 7}, - {212, 253, 98, 128, 5} - }, - { {99, 47, 48, 242, 3}, - {5, 18, 170, 249, 15}, - {196, 240, 207, 76, 6}, - {249, 245, 84, 138, 0} - }, - { {179, 187, 61, 230, 12}, - {89, 11, 127, 91, 15}, - {54, 123, 205, 220, 13}, - {253, 175, 237, 9, 10} - }, - { {99, 49, 245, 56, 3}, - {117, 179, 162, 209, 4}, - {193, 202, 248, 204, 6}, - {40, 180, 92, 218, 14} - }, - { {188, 161, 218, 254, 5}, - {175, 234, 246, 26, 3}, - {167, 245, 184, 83, 13}, - {197, 134, 245, 127, 5} - }, - { {209, 129, 108, 171, 9}, - {36, 43, 23, 198, 7}, - {157, 83, 104, 24, 11}, - {230, 62, 141, 66, 4} - }, - { {13, 132, 7, 172, 10}, - {154, 113, 19, 96, 2}, - {83, 94, 2, 27, 0}, - {64, 108, 136, 229, 9} - }, - { {121, 78, 29, 126, 5}, - {30, 45, 238, 248, 9}, - {167, 235, 135, 41, 14}, - {145, 247, 123, 71, 8} - }, - { {146, 222, 101, 68, 2}, - {120, 29, 24, 43, 12}, - {66, 42, 103, 180, 9}, - {61, 65, 139, 129, 14} - }, - { {101, 20, 151, 148, 9}, - {220, 193, 161, 240, 2}, - {146, 158, 146, 138, 6}, - {64, 248, 88, 51, 11} - }, - { {51, 247, 81, 113, 2}, - {113, 30, 186, 125, 0}, - {72, 232, 174, 252, 12}, - {11, 229, 215, 136, 14} - }, - { {60, 156, 233, 25, 5}, - {246, 168, 244, 36, 12}, - {169, 137, 115, 147, 12}, - {50, 66, 241, 86, 15} - }, - { {126, 203, 129, 215, 10}, - {154, 158, 185, 141, 11}, - {94, 184, 29, 55, 14}, - {219, 25, 215, 149, 9} - }, - { {237, 225, 64, 23, 8}, - {171, 6, 177, 198, 1}, - {30, 128, 40, 123, 7}, - {134, 56, 214, 13, 5} - }, - { {192, 155, 14, 226, 12}, - {64, 67, 95, 138, 11}, - {52, 119, 13, 144, 3}, - {213, 31, 172, 32, 2} - }, - { {20, 230, 31, 119, 4}, - {153, 77, 222, 60, 1}, - {46, 239, 134, 114, 8}, - {131, 199, 187, 41, 9} - }, - { {109, 51, 102, 73, 10}, - {227, 115, 41, 204, 4}, - {89, 38, 108, 203, 6}, - {35, 57, 76, 236, 7} - }, - { {134, 93, 208, 136, 0}, - {224, 166, 0, 51, 10}, - {1, 16, 187, 166, 1}, - {92, 192, 6, 80, 7} - }, - { {247, 33, 75, 251, 8}, - {177, 106, 167, 207, 3}, - {29, 253, 40, 78, 15}, - {207, 62, 85, 104, 13} - }, - { {216, 185, 194, 226, 2}, - {99, 218, 18, 138, 11}, - {68, 116, 57, 209, 11}, - {213, 20, 133, 188, 6} - }, - { {153, 167, 7, 55, 15}, - {31, 91, 219, 102, 1}, - {254, 206, 14, 89, 9}, - {134, 109, 189, 175, 8} - }, - { {192, 236, 249, 12, 9}, - {61, 164, 21, 178, 12}, - {147, 9, 243, 112, 3}, - {52, 218, 130, 91, 12} - }, - { {187, 139, 38, 224, 1}, - {6, 75, 58, 75, 14}, - {128, 118, 77, 29, 13}, - {125, 37, 205, 38, 0} - }, - { {5, 103, 143, 202, 12}, - {145, 231, 77, 104, 3}, - {53, 63, 30, 106, 0}, - {193, 107, 46, 120, 9} - }, - { {51, 180, 131, 131, 4}, - {81, 200, 112, 101, 3}, - {44, 28, 18, 220, 12}, - {202, 96, 225, 56, 10} - }, - { {235, 157, 132, 184, 3}, - {70, 179, 178, 227, 10}, - {193, 210, 27, 157, 7}, - {92, 116, 220, 214, 2} - }, - { {124, 162, 119, 234, 2}, - {179, 121, 58, 152, 7}, - {69, 126, 228, 83, 14}, - {225, 149, 201, 236, 13} - }, - { {2, 175, 95, 102, 11}, - {61, 83, 31, 57, 9}, - {214, 111, 175, 84, 0}, - {153, 207, 140, 171, 12} - }, - { {74, 103, 140, 18, 2}, - {3, 151, 140, 161, 1}, - {68, 131, 30, 101, 2}, - {136, 83, 30, 156, 0} - }, - { {18, 21, 146, 191, 3}, - {76, 250, 130, 53, 3}, - {207, 212, 154, 132, 8}, - {202, 196, 21, 243, 2} - }, - { {204, 130, 180, 140, 9}, - {142, 161, 25, 146, 6}, - {147, 18, 212, 19, 3}, - {100, 153, 136, 87, 1} - }, - { {119, 203, 233, 15, 12}, - {184, 174, 125, 197, 13}, - {63, 9, 125, 62, 14}, - {186, 59, 231, 81, 13} - }, - { {94, 247, 215, 147, 1}, - {247, 207, 152, 181, 3}, - {140, 158, 190, 247, 10}, - {202, 209, 159, 62, 15} - }, - { {17, 121, 71, 153, 5}, - {117, 111, 192, 68, 10}, - {169, 158, 41, 232, 8}, - {82, 32, 63, 106, 14} - }, - { {52, 77, 95, 151, 0}, - {184, 79, 164, 52, 11}, - {14, 159, 171, 34, 12}, - {210, 194, 95, 33, 13} - }, - { {223, 47, 18, 231, 2}, - {139, 90, 10, 255, 11}, - {78, 116, 143, 79, 11}, - {223, 245, 5, 173, 1} - }, - { {17, 66, 119, 179, 7}, - {52, 93, 202, 84, 7}, - {236, 222, 228, 40, 8}, - {226, 165, 59, 162, 12} - }, - { {11, 143, 167, 61, 5}, - {30, 227, 218, 101, 12}, - {171, 206, 95, 29, 0}, - {58, 101, 188, 119, 8} - }, - { {54, 225, 111, 66, 3}, - {181, 95, 52, 9, 5}, - {196, 47, 104, 118, 12}, - {169, 2, 207, 170, 13} - }, - { {5, 116, 2, 183, 14}, - {201, 84, 195, 100, 3}, - {126, 212, 2, 234, 0}, - {194, 108, 50, 169, 3} - }, - { {195, 66, 66, 58, 7}, - {36, 116, 202, 195, 1}, - {229, 196, 36, 44, 3}, - {140, 53, 50, 226, 4} - }, - { {117, 140, 52, 218, 11}, - {132, 57, 177, 248, 15}, - {213, 178, 195, 26, 14}, - {241, 248, 217, 194, 1} - }, - { {103, 220, 97, 97, 4}, - {240, 4, 114, 237, 12}, - {40, 104, 99, 190, 6}, - {59, 116, 226, 0, 15} - }, - { {41, 178, 34, 6, 8}, - {75, 64, 57, 64, 5}, - {22, 4, 68, 217, 4}, - {160, 41, 192, 45, 2} - }, - { {113, 1, 175, 15, 10}, - {24, 251, 37, 196, 5}, - {95, 15, 88, 8, 14}, - {162, 58, 77, 241, 8} - }, - { {84, 210, 43, 199, 5}, - {220, 76, 92, 140, 7}, - {174, 61, 68, 178, 10}, - {227, 19, 163, 35, 11} - }, - { {144, 139, 163, 175, 9}, - {28, 234, 27, 6, 15}, - {159, 92, 93, 16, 9}, - {246, 13, 133, 115, 8} - }, - { {235, 104, 201, 181, 1}, - {63, 132, 166, 199, 10}, - {138, 217, 49, 109, 7}, - {94, 54, 82, 31, 12} - }, - { {38, 5, 212, 66, 12}, - {160, 131, 97, 57, 1}, - {52, 34, 186, 6, 4}, - {137, 200, 108, 16, 5} - }, - { {106, 184, 139, 127, 4}, - {91, 224, 246, 141, 9}, - {47, 237, 17, 213, 6}, - {155, 22, 240, 125, 10} - }, - { {32, 124, 118, 87, 13}, - {109, 69, 225, 60, 13}, - {190, 166, 227, 224, 4}, - {179, 200, 122, 43, 6} - }, - { {55, 121, 64, 210, 0}, - {225, 14, 160, 73, 11}, - {4, 176, 41, 238, 12}, - {217, 32, 87, 8, 7} - }, - { {172, 46, 183, 123, 6}, - {147, 241, 234, 62, 13}, - {109, 238, 215, 67, 5}, - {183, 197, 120, 252, 9} - }, - { {62, 135, 49, 142, 12}, - {154, 42, 121, 49, 7}, - {55, 24, 206, 23, 12}, - {232, 201, 229, 69, 9} - }, - { {71, 175, 234, 26, 10}, - {161, 242, 157, 225, 13}, - {85, 133, 127, 94, 2}, - {184, 123, 148, 248, 5} - }, - { {70, 222, 53, 183, 4}, - {216, 5, 218, 181, 15}, - {46, 218, 199, 182, 2}, - {250, 213, 186, 1, 11} - }, - { {72, 163, 107, 161, 0}, - {51, 66, 30, 132, 6}, - {8, 93, 108, 81, 2}, - {98, 23, 132, 44, 12} - }, - { {240, 113, 21, 54, 11}, - {93, 31, 163, 146, 1}, - {214, 202, 136, 224, 15}, - {132, 156, 95, 139, 10} - }, - { {150, 63, 135, 237, 4}, - {217, 235, 74, 47, 10}, - {43, 126, 31, 198, 9}, - {95, 69, 45, 121, 11} - }, - { {216, 58, 185, 150, 5}, - {95, 136, 204, 146, 15}, - {166, 153, 213, 193, 11}, - {244, 147, 49, 31, 10} - }, - { {123, 46, 181, 160, 15}, - {23, 153, 107, 241, 14}, - {240, 90, 215, 77, 14}, - {120, 253, 105, 158, 8} - }, - { {182, 173, 250, 243, 2}, - {161, 218, 182, 63, 15}, - {76, 245, 251, 86, 13}, - {255, 198, 213, 184, 5} - }, - { {135, 224, 122, 78, 10}, - {169, 116, 21, 91, 5}, - {87, 37, 224, 126, 1}, - {173, 170, 130, 233, 5} - }, - { {123, 69, 24, 244, 14}, - {10, 30, 231, 249, 2}, - {114, 241, 138, 45, 14}, - {73, 254, 119, 133, 0} - }, - { {144, 100, 163, 115, 8}, - {17, 204, 131, 46, 5}, - {28, 236, 82, 96, 9}, - {167, 76, 19, 56, 8} - }, - { {60, 36, 44, 161, 2}, - {131, 25, 38, 36, 6}, - {72, 83, 66, 67, 12}, - {98, 70, 73, 140, 1} - }, - { {150, 178, 92, 74, 4}, - {225, 41, 92, 27, 1}, - {37, 35, 164, 214, 9}, - {141, 131, 169, 72, 7} - }, - { {139, 1, 216, 58, 12}, - {34, 162, 199, 83, 1}, - {53, 193, 184, 13, 1}, - {140, 174, 52, 84, 4} - }, - { {229, 28, 206, 178, 5}, - {228, 193, 230, 226, 11}, - {164, 215, 51, 138, 7}, - {212, 118, 120, 50, 7} - }, - { {72, 15, 88, 219, 0}, - {34, 34, 140, 188, 11}, - {13, 177, 175, 1, 2}, - {211, 211, 20, 68, 4} - }, - { {186, 226, 149, 134, 7}, - {31, 157, 120, 19, 3}, - {230, 26, 148, 117, 13}, - {204, 129, 235, 159, 8} - }, - { {137, 252, 15, 186, 0}, - {83, 101, 150, 98, 11}, - {5, 223, 3, 249, 1}, - {212, 102, 154, 108, 10} - }, - { {221, 6, 71, 94, 9}, - {190, 105, 137, 234, 1}, - {151, 174, 38, 11, 11}, - {133, 121, 25, 103, 13} - }, - { {247, 254, 70, 180, 12}, - {233, 77, 251, 227, 10}, - {50, 214, 39, 254, 15}, - {92, 125, 251, 41, 7} - }, - { {67, 206, 240, 38, 13}, - {44, 132, 91, 241, 13}, - {182, 64, 247, 60, 2}, - {184, 253, 162, 19, 4} - }, - { {99, 147, 88, 90, 2}, - {96, 50, 188, 217, 1}, - {69, 161, 172, 156, 6}, - {137, 179, 212, 192, 6} - }, - { {135, 180, 4, 69, 6}, - {201, 17, 80, 111, 0}, - {106, 34, 2, 222, 1}, - {15, 96, 168, 137, 3} - }, - { {79, 164, 124, 190, 10}, - {171, 49, 151, 241, 7}, - {87, 211, 226, 95, 2}, - {232, 254, 152, 205, 5} - }, - { {118, 25, 249, 85, 10}, - {248, 154, 165, 157, 12}, - {90, 169, 249, 134, 14}, - {59, 154, 85, 145, 15} - }, - { {81, 185, 161, 51, 0}, - {81, 138, 146, 196, 13}, - {12, 200, 89, 216, 10}, - {178, 52, 149, 24, 10} - }, - { {21, 51, 99, 124, 7}, - {253, 122, 202, 72, 4}, - {227, 236, 108, 202, 8}, - {33, 37, 53, 235, 15} - }, - { {117, 135, 169, 114, 10}, - {144, 154, 191, 232, 5}, - {84, 233, 94, 26, 14}, - {161, 127, 213, 144, 9} - }, - { {96, 222, 17, 98, 2}, - {80, 20, 58, 184, 9}, - {68, 104, 135, 176, 6}, - {145, 213, 194, 128, 10} - }, - { {125, 76, 230, 138, 14}, - {162, 253, 97, 224, 15}, - {117, 22, 115, 43, 14}, - {240, 120, 107, 244, 5} - }, - { {175, 19, 94, 129, 12}, - {226, 67, 109, 87, 2}, - {56, 23, 172, 143, 5}, - {78, 171, 108, 36, 7} - }, - { {49, 201, 182, 71, 12}, - {8, 207, 113, 92, 13}, - {62, 38, 217, 56, 12}, - {179, 168, 239, 49, 0} - }, - { {172, 67, 216, 21, 5}, - {174, 134, 236, 22, 0}, - {170, 129, 188, 35, 5}, - {6, 131, 118, 23, 5} - }, - { {216, 83, 225, 207, 11}, - {126, 190, 9, 142, 7}, - {223, 56, 124, 161, 11}, - {231, 25, 7, 215, 14} - }, - { {79, 205, 197, 207, 0}, - {186, 167, 16, 237, 11}, - {15, 58, 59, 63, 2}, - {219, 112, 142, 85, 13} - }, - { {238, 105, 111, 210, 13}, - {183, 71, 229, 139, 15}, - {180, 191, 105, 103, 7}, - {253, 26, 126, 46, 13} - }, - { {183, 22, 24, 71, 5}, - {204, 8, 108, 127, 1}, - {174, 33, 134, 142, 13}, - {143, 227, 97, 3, 3} - }, - { {185, 180, 184, 120, 1}, - {71, 168, 182, 122, 4}, - {129, 225, 210, 217, 13}, - {37, 230, 209, 94, 2} - }, - { {50, 133, 121, 80, 5}, - {52, 10, 244, 57, 4}, - {160, 169, 234, 20, 12}, - {41, 194, 245, 2, 12} - }, - { {149, 233, 71, 119, 1}, - {189, 79, 146, 78, 9}, - {142, 238, 41, 122, 9}, - {151, 36, 159, 43, 13} - }, - { {154, 160, 48, 46, 2}, - {11, 56, 18, 19, 5}, - {71, 64, 192, 85, 9}, - {172, 132, 129, 205, 0} - }, - { {236, 24, 40, 209, 7}, - {198, 16, 228, 142, 14}, - {232, 177, 65, 131, 7}, - {119, 18, 112, 134, 3} - }, - { {188, 80, 121, 99, 5}, - {246, 12, 102, 30, 5}, - {172, 105, 224, 163, 13}, - {167, 134, 99, 6, 15} - }, - { {135, 133, 193, 183, 9}, - {188, 130, 147, 103, 3}, - {158, 216, 58, 30, 1}, - {206, 108, 148, 19, 13} - }, - { {211, 122, 173, 183, 10}, - {89, 157, 143, 199, 15}, - {94, 219, 85, 236, 11}, - {254, 63, 27, 153, 10} - }, - { {129, 127, 2, 206, 0}, - {73, 102, 8, 106, 11}, - {7, 52, 15, 232, 1}, - {213, 97, 6, 105, 2} - }, - { {253, 99, 223, 176, 10}, - {179, 223, 175, 210, 2}, - {80, 223, 188, 107, 15}, - {68, 191, 95, 188, 13} - }, - { {81, 10, 193, 175, 2}, - {56, 184, 10, 196, 11}, - {79, 88, 53, 8, 10}, - {210, 53, 1, 209, 12} - }, - { {61, 30, 223, 141, 3}, - {254, 249, 44, 116, 10}, - {203, 31, 183, 139, 12}, - {82, 227, 73, 247, 15} - }, - { {244, 183, 58, 64, 2}, - {193, 90, 60, 186, 4}, - {64, 37, 206, 210, 15}, - {37, 211, 197, 168, 3} - }, - { {55, 215, 137, 233, 12}, - {208, 174, 127, 109, 2}, - {57, 121, 30, 190, 12}, - {75, 111, 231, 80, 11} - }, - { {143, 17, 199, 124, 10}, - {250, 243, 131, 75, 0}, - {83, 238, 56, 143, 1}, - {13, 44, 28, 245, 15} - }, - { {196, 189, 3, 107, 14}, - {209, 114, 83, 174, 9}, - {125, 108, 11, 210, 3}, - {151, 92, 164, 232, 11} - }, - { {125, 250, 153, 92, 9}, - {223, 172, 189, 216, 8}, - {147, 169, 149, 251, 14}, - {17, 187, 211, 95, 11} - }, - { {123, 20, 232, 37, 1}, - {110, 136, 38, 229, 4}, - {138, 65, 114, 141, 14}, - {42, 118, 65, 23, 6} - }, - { {202, 7, 14, 127, 4}, - {10, 99, 206, 175, 1}, - {47, 231, 14, 5, 3}, - {143, 87, 60, 101, 0} - }, - { {92, 34, 219, 205, 5}, - {191, 232, 76, 156, 2}, - {171, 61, 180, 67, 10}, - {67, 147, 33, 127, 13} - }, - { {160, 219, 114, 108, 8}, - {104, 102, 59, 26, 12}, - {19, 100, 237, 176, 5}, - {53, 141, 198, 97, 6} - }, - { {107, 119, 118, 222, 2}, - {107, 119, 168, 249, 7}, - {71, 182, 238, 237, 6}, - {233, 241, 94, 237, 6} - }, - { {128, 245, 142, 116, 8}, - {73, 199, 151, 42, 0}, - {18, 231, 26, 240, 1}, - {5, 78, 158, 57, 2} - }, - { {225, 172, 31, 244, 10}, - {25, 81, 183, 250, 10}, - {82, 255, 131, 88, 7}, - {85, 254, 216, 169, 8} - }, - { {27, 186, 234, 133, 12}, - {107, 200, 93, 69, 14}, - {58, 21, 117, 221, 8}, - {122, 43, 161, 61, 6} - }, - { {38, 172, 85, 127, 12}, - {185, 33, 243, 61, 9}, - {63, 234, 163, 86, 4}, - {155, 204, 248, 73, 13} - }, - { {229, 209, 239, 53, 7}, - {252, 215, 246, 198, 4}, - {234, 207, 120, 186, 7}, - {38, 54, 254, 179, 15} - }, - { {31, 41, 212, 225, 0}, - {163, 139, 2, 93, 10}, - {8, 114, 185, 79, 8}, - {91, 164, 13, 28, 5} - }, - { {69, 159, 187, 21, 11}, - {220, 210, 157, 244, 12}, - {218, 141, 223, 154, 2}, - {50, 251, 148, 179, 11} - }, - { {212, 176, 73, 85, 9}, - {253, 8, 149, 142, 0}, - {154, 169, 32, 210, 11}, - {7, 26, 145, 11, 15} - }, - { {108, 183, 10, 207, 8}, - {203, 98, 61, 172, 3}, - {31, 53, 14, 211, 6}, - {195, 91, 196, 109, 3} - }, - { {248, 97, 122, 81, 8}, - {35, 78, 165, 158, 4}, - {24, 165, 232, 97, 15}, - {39, 154, 87, 44, 4} - }, - { {195, 145, 218, 195, 4}, - {96, 194, 84, 223, 3}, - {44, 53, 184, 156, 3}, - {207, 178, 164, 48, 6} - }, - { {81, 35, 94, 44, 12}, - {41, 107, 79, 208, 0}, - {51, 71, 172, 72, 10}, - {0, 191, 45, 105, 4} - }, - { {116, 64, 124, 79, 14}, - {168, 61, 101, 156, 5}, - {127, 35, 224, 34, 14}, - {163, 154, 107, 193, 5} - }, - { {223, 22, 9, 162, 4}, - {210, 8, 78, 227, 3}, - {36, 89, 6, 143, 11}, - {204, 119, 33, 4, 11} - }, - { {206, 210, 125, 193, 7}, - {246, 21, 92, 159, 6}, - {232, 59, 228, 183, 3}, - {111, 147, 170, 134, 15} - } -}; - -}//namespace diff --git a/modules/aruco/src/apriltag/unionfind.hpp b/modules/aruco/src/apriltag/unionfind.hpp deleted file mode 100644 index 1affa6b93c..0000000000 --- a/modules/aruco/src/apriltag/unionfind.hpp +++ /dev/null @@ -1,133 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copyright (C) 2013-2016, The Regents of The University of Michigan. -// -// This software was developed in the APRIL Robotics Lab under the -// direction of Edwin Olson, ebolson@umich.edu. This software may be -// available under alternative licensing terms; contact the address above. -// -// The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, -// either expressed or implied, of the Regents of The University of Michigan. -#ifndef _OPENCV_UNIONFIND_HPP_ -#define _OPENCV_UNIONFIND_HPP_ - -#include -#include -namespace cv { -namespace aruco { - -typedef struct unionfind unionfind_t; -struct unionfind{ - uint32_t maxid; - struct ufrec *data; -}; - -struct ufrec{ - // the parent of this node. If a node's parent is its own index, - // then it is a root. - uint32_t parent; - - // for the root of a connected component, the number of components - // connected to it. For intermediate values, it's not meaningful. - uint32_t size; -}; - -static inline unionfind_t *unionfind_create(uint32_t maxid){ - unionfind_t *uf = (unionfind_t*) calloc(1, sizeof(unionfind_t)); - uf->maxid = maxid; - uf->data = (struct ufrec*) malloc((maxid+1) * sizeof(struct ufrec)); - for (unsigned int i = 0; i <= maxid; i++) { - uf->data[i].size = 1; - uf->data[i].parent = i; - } - return uf; -} - -static inline void unionfind_destroy(unionfind_t *uf){ - free(uf->data); - free(uf); -} - -/* -static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id) -{ - // base case: a node is its own parent - if (uf->data[id].parent == id) - return id; - - // otherwise, recurse - uint32_t root = unionfind_get_representative(uf, uf->data[id].parent); - - // short circuit the path. [XXX This write prevents tail recursion] - uf->data[id].parent = root; - - return root; -} - */ - -// this one seems to be every-so-slightly faster than the recursive -// version above. -static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id){ - uint32_t root = id; - - // chase down the root - while (uf->data[root].parent != root) { - root = uf->data[root].parent; - } - - // go back and collapse the tree. - // - // XXX: on some of our workloads that have very shallow trees - // (e.g. image segmentation), we are actually faster not doing - // this... - while (uf->data[id].parent != root) { - uint32_t tmp = uf->data[id].parent; - uf->data[id].parent = root; - id = tmp; - } - - return root; -} - -static inline uint32_t unionfind_get_set_size(unionfind_t *uf, uint32_t id){ - uint32_t repid = unionfind_get_representative(uf, id); - return uf->data[repid].size; -} - -static inline uint32_t unionfind_connect(unionfind_t *uf, uint32_t aid, uint32_t bid){ - uint32_t aroot = unionfind_get_representative(uf, aid); - uint32_t broot = unionfind_get_representative(uf, bid); - - if (aroot == broot) - return aroot; - - // we don't perform "union by rank", but we perform a similar - // operation (but probably without the same asymptotic guarantee): - // We join trees based on the number of *elements* (as opposed to - // rank) contained within each tree. I.e., we use size as a proxy - // for rank. In my testing, it's often *faster* to use size than - // rank, perhaps because the rank of the tree isn't that critical - // if there are very few nodes in it. - uint32_t asize = uf->data[aroot].size; - uint32_t bsize = uf->data[broot].size; - - // optimization idea: We could shortcut some or all of the tree - // that is grafted onto the other tree. Pro: those nodes were just - // read and so are probably in cache. Con: it might end up being - // wasted effort -- the tree might be grafted onto another tree in - // a moment! - if (asize > bsize) { - uf->data[broot].parent = aroot; - uf->data[aroot].size += bsize; - return aroot; - } else { - uf->data[aroot].parent = broot; - uf->data[broot].size += asize; - return broot; - } -} -}} -#endif diff --git a/modules/aruco/src/apriltag/zarray.hpp b/modules/aruco/src/apriltag/zarray.hpp deleted file mode 100644 index 91933c5f5f..0000000000 --- a/modules/aruco/src/apriltag/zarray.hpp +++ /dev/null @@ -1,150 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copyright (C) 2013-2016, The Regents of The University of Michigan. -// -// This software was developed in the APRIL Robotics Lab under the -// direction of Edwin Olson, ebolson@umich.edu. This software may be -// available under alternative licensing terms; contact the address above. -// -// The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, -// either expressed or implied, of the Regents of The University of Michigan. -#ifndef _OPENCV_ZARRAY_HPP_ -#define _OPENCV_ZARRAY_HPP_ - -#include -#include - -namespace cv { -namespace aruco { - - -struct sQuad{ - float p[4][2]; // corners -}; - -/** - * Defines a structure which acts as a resize-able array ala Java's ArrayList. - */ -typedef struct zarray zarray_t; -struct zarray{ - size_t el_sz; // size of each element - - int size; // how many elements? - int alloc; // we've allocated storage for how many elements? - char *data; -}; - -/** - * Creates and returns a variable array structure capable of holding elements of - * the specified size. It is the caller's responsibility to call zarray_destroy() - * on the returned array when it is no longer needed. - */ -inline static zarray_t *_zarray_create(size_t el_sz){ - zarray_t *za = (zarray_t*) calloc(1, sizeof(zarray_t)); - za->el_sz = el_sz; - return za; -} - -/** - * Frees all resources associated with the variable array structure which was - * created by zarray_create(). After calling, 'za' will no longer be valid for storage. - */ -inline static void _zarray_destroy(zarray_t *za){ - if (za == NULL) - return; - - if (za->data != NULL) - free(za->data); - memset(za, 0, sizeof(zarray_t)); - free(za); -} - -/** - * Retrieves the number of elements currently being contained by the passed - * array, which may be different from its capacity. The index of the last element - * in the array will be one less than the returned value. - */ -inline static int _zarray_size(const zarray_t *za){ - return za->size; -} - -/** - * Allocates enough internal storage in the supplied variable array structure to - * guarantee that the supplied number of elements (capacity) can be safely stored. - */ -inline static void _zarray_ensure_capacity(zarray_t *za, int capacity){ - if (capacity <= za->alloc) - return; - - while (za->alloc < capacity) { - za->alloc *= 2; - if (za->alloc < 8) - za->alloc = 8; - } - - za->data = (char*) realloc(za->data, za->alloc * za->el_sz); -} - -/** - * Adds a new element to the end of the supplied array, and sets its value - * (by copying) from the data pointed to by the supplied pointer 'p'. - * Automatically ensures that enough storage space is available for the new element. - */ -inline static void _zarray_add(zarray_t *za, const void *p){ - _zarray_ensure_capacity(za, za->size + 1); - - memcpy(&za->data[za->size*za->el_sz], p, za->el_sz); - za->size++; -} - -/** - * Retrieves the element from the supplied array located at the zero-based - * index of 'idx' and copies its value into the variable pointed to by the pointer - * 'p'. - */ -inline static void _zarray_get(const zarray_t *za, int idx, void *p){ - CV_DbgAssert(idx >= 0); - CV_DbgAssert(idx < za->size); - - memcpy(p, &za->data[idx*za->el_sz], za->el_sz); -} - -/** - * Similar to zarray_get(), but returns a "live" pointer to the internal - * storage, avoiding a memcpy. This pointer is not valid across - * operations which might move memory around (i.e. zarray_remove_value(), - * zarray_remove_index(), zarray_insert(), zarray_sort(), zarray_clear()). - * 'p' should be a pointer to the pointer which will be set to the internal address. - */ -inline static void _zarray_get_volatile(const zarray_t *za, int idx, void *p){ - CV_DbgAssert(idx >= 0); - CV_DbgAssert(idx < za->size); - - *((void**) p) = &za->data[idx*za->el_sz]; -} - -inline static void _zarray_truncate(zarray_t *za, int sz){ - za->size = sz; -} - -/** - * Sets the value of the current element at index 'idx' by copying its value from - * the data pointed to by 'p'. The previous value of the changed element will be - * copied into the data pointed to by 'outp' if it is not null. - */ -static inline void _zarray_set(zarray_t *za, int idx, const void *p, void *outp){ - CV_DbgAssert(idx >= 0); - CV_DbgAssert(idx < za->size); - - if (outp != NULL) - memcpy(outp, &za->data[idx*za->el_sz], za->el_sz); - - memcpy(&za->data[idx*za->el_sz], p, za->el_sz); -} - -} -} -#endif diff --git a/modules/aruco/src/apriltag/zmaxheap.cpp b/modules/aruco/src/apriltag/zmaxheap.cpp deleted file mode 100644 index f3b315e731..0000000000 --- a/modules/aruco/src/apriltag/zmaxheap.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copyright (C) 2013-2016, The Regents of The University of Michigan. -// -// This software was developed in the APRIL Robotics Lab under the -// direction of Edwin Olson, ebolson@umich.edu. This software may be -// available under alternative licensing terms; contact the address above. -// -// The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, -// either expressed or implied, of the Regents of The University of Michigan. - -#include "../precomp.hpp" -#include "zmaxheap.hpp" - - -// 0 -// 1 2 -// 3 4 5 6 -// 7 8 9 10 11 12 13 14 -// -// Children of node i: 2*i+1, 2*i+2 -// Parent of node i: (i-1) / 2 -// -// Heap property: a parent is greater than (or equal to) its children. - -#define MIN_CAPACITY 16 -namespace cv { -namespace aruco { -struct zmaxheap -{ - size_t el_sz; - - int size; - int alloc; - - float *values; - char *data; - - void (*swap)(zmaxheap_t *heap, int a, int b); -}; - -static inline void _swap_default(zmaxheap_t *heap, int a, int b) -{ - float t = heap->values[a]; - heap->values[a] = heap->values[b]; - heap->values[b] = t; - - cv::AutoBuffer tmp(heap->el_sz); - memcpy(tmp.data(), &heap->data[a*heap->el_sz], heap->el_sz); - memcpy(&heap->data[a*heap->el_sz], &heap->data[b*heap->el_sz], heap->el_sz); - memcpy(&heap->data[b*heap->el_sz], tmp.data(), heap->el_sz); -} - -static inline void _swap_pointer(zmaxheap_t *heap, int a, int b) -{ - float t = heap->values[a]; - heap->values[a] = heap->values[b]; - heap->values[b] = t; - - void **pp = (void**) heap->data; - void *tmp = pp[a]; - pp[a] = pp[b]; - pp[b] = tmp; -} - - -zmaxheap_t *zmaxheap_create(size_t el_sz) -{ - zmaxheap_t *heap = (zmaxheap_t*)calloc(1, sizeof(zmaxheap_t)); - heap->el_sz = el_sz; - - heap->swap = _swap_default; - - if (el_sz == sizeof(void*)) - heap->swap = _swap_pointer; - - return heap; -} - -void zmaxheap_destroy(zmaxheap_t *heap) -{ - free(heap->values); - free(heap->data); - memset(heap, 0, sizeof(zmaxheap_t)); - free(heap); -} - -static void _zmaxheap_ensure_capacity(zmaxheap_t *heap, int capacity) -{ - if (heap->alloc >= capacity) - return; - - int newcap = heap->alloc; - - while (newcap < capacity) { - if (newcap < MIN_CAPACITY) { - newcap = MIN_CAPACITY; - continue; - } - - newcap *= 2; - } - - heap->values = (float*)realloc(heap->values, newcap * sizeof(float)); - heap->data = (char*)realloc(heap->data, newcap * heap->el_sz); - heap->alloc = newcap; -} - -void zmaxheap_add(zmaxheap_t *heap, void *p, float v) -{ - _zmaxheap_ensure_capacity(heap, heap->size + 1); - - int idx = heap->size; - - heap->values[idx] = v; - memcpy(&heap->data[idx*heap->el_sz], p, heap->el_sz); - - heap->size++; - - while (idx > 0) { - - int parent = (idx - 1) / 2; - - // we're done! - if (heap->values[parent] >= v) - break; - - // else, swap and recurse upwards. - heap->swap(heap, idx, parent); - idx = parent; - } -} - -// Removes the item in the heap at the given index. Returns 1 if the -// item existed. 0 Indicates an invalid idx (heap is smaller than -// idx). This is mostly intended to be used by zmaxheap_remove_max. -static int zmaxheap_remove_index(zmaxheap_t *heap, int idx, void *p, float *v) -{ - if (idx >= heap->size) - return 0; - - // copy out the requested element from the heap. - if (v != NULL) - *v = heap->values[idx]; - if (p != NULL) - memcpy(p, &heap->data[idx*heap->el_sz], heap->el_sz); - - heap->size--; - - // If this element is already the last one, then there's nothing - // for us to do. - if (idx == heap->size) - return 1; - - // copy last element to first element. (which probably upsets - // the heap property). - heap->values[idx] = heap->values[heap->size]; - memcpy(&heap->data[idx*heap->el_sz], &heap->data[heap->el_sz * heap->size], heap->el_sz); - - // now fix the heap. Note, as we descend, we're "pushing down" - // the same node the entire time. Thus, while the index of the - // parent might change, the parent_score doesn't. - int parent = idx; - float parent_score = heap->values[idx]; - - // descend, fixing the heap. - while (parent < heap->size) { - - int left = 2*parent + 1; - int right = left + 1; - -// assert(parent_score == heap->values[parent]); - - float left_score = (left < heap->size) ? heap->values[left] : -INFINITY; - float right_score = (right < heap->size) ? heap->values[right] : -INFINITY; - - // put the biggest of (parent, left, right) as the parent. - - // already okay? - if (parent_score >= left_score && parent_score >= right_score) - break; - - // if we got here, then one of the children is bigger than the parent. - if (left_score >= right_score) { - CV_Assert(left < heap->size); - heap->swap(heap, parent, left); - parent = left; - } else { - // right_score can't be less than left_score if right_score is -INFINITY. - CV_Assert(right < heap->size); - heap->swap(heap, parent, right); - parent = right; - } - } - - return 1; -} - -int zmaxheap_remove_max(zmaxheap_t *heap, void *p, float *v) -{ - return zmaxheap_remove_index(heap, 0, p, v); -} - -}} diff --git a/modules/aruco/src/apriltag/zmaxheap.hpp b/modules/aruco/src/apriltag/zmaxheap.hpp deleted file mode 100644 index 7055e512f6..0000000000 --- a/modules/aruco/src/apriltag/zmaxheap.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copyright (C) 2013-2016, The Regents of The University of Michigan. -// -// This software was developed in the APRIL Robotics Lab under the -// direction of Edwin Olson, ebolson@umich.edu. This software may be -// available under alternative licensing terms; contact the address above. -// -// The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, -// either expressed or implied, of the Regents of The University of Michigan. -#ifndef _OPENCV_ZMAXHEAP_HPP_ -#define _OPENCV_ZMAXHEAP_HPP_ - -#include -#include -#include - -namespace cv { -namespace aruco { -typedef struct zmaxheap zmaxheap_t; - -typedef struct zmaxheap_iterator zmaxheap_iterator_t; -struct zmaxheap_iterator { - zmaxheap_t *heap; - int in, out; -}; - -zmaxheap_t *zmaxheap_create(size_t el_sz); - -void zmaxheap_destroy(zmaxheap_t *heap); - -void zmaxheap_add(zmaxheap_t *heap, void *p, float v); - -// returns 0 if the heap is empty, so you can do -// while (zmaxheap_remove_max(...)) { } -int zmaxheap_remove_max(zmaxheap_t *heap, void *p, float *v); - -}} -#endif diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index 92abb40ae8..c13710b212 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -2,7 +2,9 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html +#include "precomp.hpp" #include "opencv2/aruco.hpp" +#include namespace cv { namespace aruco { @@ -28,5 +30,152 @@ void refineDetectedMarkers(InputArray _image, const Ptr &_board, _distCoeffs, _recoveredIdxs); } +void drawPlanarBoard(const Ptr &board, Size outSize, const _OutputArray &img, int marginSize, int borderBits) { + board->draw(outSize, img, marginSize, borderBits); +} + +void getBoardObjectAndImagePoints(const Ptr &board, InputArrayOfArrays detectedCorners, InputArray detectedIds, + OutputArray objPoints, OutputArray imgPoints) { + board->matchImagePoints(detectedCorners, detectedIds, objPoints, imgPoints); +} + +int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr &board, + InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess) { + CV_Assert(corners.total() == ids.total()); + + // get object and image points for the solvePnP function + Mat objPoints, imgPoints; + board->matchImagePoints(corners, ids, objPoints, imgPoints); + + CV_Assert(imgPoints.total() == objPoints.total()); + + if(objPoints.total() == 0) // 0 of the detected markers in board + return 0; + + solvePnP(objPoints, imgPoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess); + + // divide by four since all the four corners are concatenated in the array for each marker + return (int)objPoints.total() / 4; +} + + +/** + * Check if a set of 3d points are enough for calibration. Z coordinate is ignored. + * Only axis parallel lines are considered + */ +static bool _arePointsEnoughForPoseEstimation(const vector &points) { + if(points.size() < 4) return false; + + vector sameXValue; // different x values in points + vector sameXCounter; // number of points with the x value in sameXValue + for(unsigned int i = 0; i < points.size(); i++) { + bool found = false; + for(unsigned int j = 0; j < sameXValue.size(); j++) { + if(sameXValue[j] == points[i].x) { + found = true; + sameXCounter[j]++; + } + } + if(!found) { + sameXValue.push_back(points[i].x); + sameXCounter.push_back(1); + } + } + + // count how many x values has more than 2 points + int moreThan2 = 0; + for(unsigned int i = 0; i < sameXCounter.size(); i++) { + if(sameXCounter[i] >= 2) moreThan2++; + } + + // if we have more than 1 two xvalues with more than 2 points, calibration is ok + if(moreThan2 > 1) + return true; + return false; +} + +bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, + const Ptr &board, InputArray cameraMatrix, + InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess) { + CV_Assert((charucoCorners.getMat().total() == charucoIds.getMat().total())); + + // need, at least, 4 corners + if(charucoIds.getMat().total() < 4) return false; + + vector objPoints; + objPoints.reserve(charucoIds.getMat().total()); + for(unsigned int i = 0; i < charucoIds.getMat().total(); i++) { + int currId = charucoIds.getMat().at< int >(i); + CV_Assert(currId >= 0 && currId < (int)board->getChessboardCorners().size()); + objPoints.push_back(board->getChessboardCorners()[currId]); + } + + // points need to be in different lines, check if detected points are enough + if(!_arePointsEnoughForPoseEstimation(objPoints)) return false; + + solvePnP(objPoints, charucoCorners, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess); + return true; +} + +bool testCharucoCornersCollinear(const Ptr &board, InputArray charucoIds) { + return board->testCharucoCornersCollinear(charucoIds); +} + +/** + * @brief Return object points for the system centered in a middle (by default) or in a top left corner of single + * marker, given the marker length + */ +static Mat _getSingleMarkerObjectPoints(float markerLength, const EstimateParameters& estimateParameters) { + CV_Assert(markerLength > 0); + Mat objPoints(4, 1, CV_32FC3); + // set coordinate system in the top-left corner of the marker, with Z pointing out + if (estimateParameters.pattern == ARUCO_CW_TOP_LEFT_CORNER) { + objPoints.ptr(0)[0] = Vec3f(0.f, 0.f, 0); + objPoints.ptr(0)[1] = Vec3f(markerLength, 0.f, 0); + objPoints.ptr(0)[2] = Vec3f(markerLength, markerLength, 0); + objPoints.ptr(0)[3] = Vec3f(0.f, markerLength, 0); + } + else if (estimateParameters.pattern == ARUCO_CCW_CENTER) { + objPoints.ptr(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0); + objPoints.ptr(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0); + objPoints.ptr(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0); + objPoints.ptr(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0); + } + else + CV_Error(Error::StsBadArg, "Unknown estimateParameters pattern"); + return objPoints; +} + +void estimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength, + InputArray _cameraMatrix, InputArray _distCoeffs, + OutputArray _rvecs, OutputArray _tvecs, OutputArray _objPoints, + const Ptr& estimateParameters) { + CV_Assert(markerLength > 0); + + Mat markerObjPoints = _getSingleMarkerObjectPoints(markerLength, *estimateParameters); + int nMarkers = (int)_corners.total(); + _rvecs.create(nMarkers, 1, CV_64FC3); + _tvecs.create(nMarkers, 1, CV_64FC3); + + Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat(); + + //// for each marker, calculate its pose + parallel_for_(Range(0, nMarkers), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + + for (int i = begin; i < end; i++) { + solvePnP(markerObjPoints, _corners.getMat(i), _cameraMatrix, _distCoeffs, rvecs.at(i), + tvecs.at(i), estimateParameters->useExtrinsicGuess, estimateParameters->solvePnPMethod); + } + }); + + if(_objPoints.needed()){ + markerObjPoints.convertTo(_objPoints, -1); + } +} + } } diff --git a/modules/aruco/src/aruco_calib.cpp b/modules/aruco/src/aruco_calib.cpp new file mode 100644 index 0000000000..3e619e5b45 --- /dev/null +++ b/modules/aruco/src/aruco_calib.cpp @@ -0,0 +1,95 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include +#include + +namespace cv { +namespace aruco { +using namespace std; + +double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, + const Ptr &board, Size imageSize, InputOutputArray _cameraMatrix, + InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, + OutputArrayOfArrays _tvecs, + OutputArray _stdDeviationsIntrinsics, + OutputArray _stdDeviationsExtrinsics, + OutputArray _perViewErrors, + int flags, const TermCriteria& criteria) { + // for each frame, get properly processed imagePoints and objectPoints for the calibrateCamera + // function + vector processedObjectPoints, processedImagePoints; + size_t nFrames = _counter.total(); + int markerCounter = 0; + for(size_t frame = 0; frame < nFrames; frame++) { + int nMarkersInThisFrame = _counter.getMat().ptr< int >()[frame]; + vector thisFrameCorners; + vector thisFrameIds; + + CV_Assert(nMarkersInThisFrame > 0); + + thisFrameCorners.reserve((size_t) nMarkersInThisFrame); + thisFrameIds.reserve((size_t) nMarkersInThisFrame); + for(int j = markerCounter; j < markerCounter + nMarkersInThisFrame; j++) { + thisFrameCorners.push_back(_corners.getMat(j)); + thisFrameIds.push_back(_ids.getMat().ptr< int >()[j]); + } + markerCounter += nMarkersInThisFrame; + Mat currentImgPoints, currentObjPoints; + board->matchImagePoints(thisFrameCorners, thisFrameIds, currentObjPoints, + currentImgPoints); + if(currentImgPoints.total() > 0 && currentObjPoints.total() > 0) { + processedImagePoints.push_back(currentImgPoints); + processedObjectPoints.push_back(currentObjPoints); + } + } + return calibrateCamera(processedObjectPoints, processedImagePoints, imageSize, _cameraMatrix, _distCoeffs, _rvecs, + _tvecs, _stdDeviationsIntrinsics, _stdDeviationsExtrinsics, _perViewErrors, flags, criteria); +} + +double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, const Ptr &board, + Size imageSize, InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, + OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, int flags, const TermCriteria& criteria) { + return calibrateCameraAruco(_corners, _ids, _counter, board, imageSize, _cameraMatrix, _distCoeffs, + _rvecs, _tvecs, noArray(), noArray(), noArray(), flags, criteria); +} + +double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds, + const Ptr &_board, Size imageSize, + InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, + OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, + OutputArray _stdDeviationsIntrinsics, + OutputArray _stdDeviationsExtrinsics, + OutputArray _perViewErrors, + int flags, const TermCriteria& criteria) { + CV_Assert(_charucoIds.total() > 0 && (_charucoIds.total() == _charucoCorners.total())); + + // Join object points of charuco corners in a single vector for calibrateCamera() function + vector > allObjPoints; + allObjPoints.resize(_charucoIds.total()); + for(unsigned int i = 0; i < _charucoIds.total(); i++) { + unsigned int nCorners = (unsigned int)_charucoIds.getMat(i).total(); + CV_Assert(nCorners > 0 && nCorners == _charucoCorners.getMat(i).total()); + allObjPoints[i].reserve(nCorners); + + for(unsigned int j = 0; j < nCorners; j++) { + int pointId = _charucoIds.getMat(i).at< int >(j); + CV_Assert(pointId >= 0 && pointId < (int)_board->getChessboardCorners().size()); + allObjPoints[i].push_back(_board->getChessboardCorners()[pointId]); + } + } + return calibrateCamera(allObjPoints, _charucoCorners, imageSize, _cameraMatrix, _distCoeffs, _rvecs, _tvecs, + _stdDeviationsIntrinsics, _stdDeviationsExtrinsics, _perViewErrors, flags, criteria); +} + +double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds, + const Ptr &_board, Size imageSize, InputOutputArray _cameraMatrix, + InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, + int flags, const TermCriteria& criteria) { +return calibrateCameraCharuco(_charucoCorners, _charucoIds, _board, imageSize, _cameraMatrix, _distCoeffs, _rvecs, + _tvecs, noArray(), noArray(), noArray(), flags, criteria); +} + +} +} diff --git a/modules/aruco/src/aruco_calib_pose.cpp b/modules/aruco/src/aruco_calib_pose.cpp deleted file mode 100644 index 1290126d5c..0000000000 --- a/modules/aruco/src/aruco_calib_pose.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html - -#include - -namespace cv { -namespace aruco { -using namespace std; - -void getBoardObjectAndImagePoints(const Ptr &board, InputArrayOfArrays detectedCorners, InputArray detectedIds, - OutputArray objPoints, OutputArray imgPoints) { - CV_Assert(board->getIds().size() == board->getObjPoints().size()); - CV_Assert(detectedIds.total() == detectedCorners.total()); - - size_t nDetectedMarkers = detectedIds.total(); - - vector objPnts; - objPnts.reserve(nDetectedMarkers); - - vector imgPnts; - imgPnts.reserve(nDetectedMarkers); - - // look for detected markers that belong to the board and get their information - for(unsigned int i = 0; i < nDetectedMarkers; i++) { - int currentId = detectedIds.getMat().ptr< int >(0)[i]; - for(unsigned int j = 0; j < board->getIds().size(); j++) { - if(currentId == board->getIds()[j]) { - for(int p = 0; p < 4; p++) { - objPnts.push_back(board->getObjPoints()[j][p]); - imgPnts.push_back(detectedCorners.getMat(i).ptr< Point2f >(0)[p]); - } - } - } - } - - // create output - Mat(objPnts).copyTo(objPoints); - Mat(imgPnts).copyTo(imgPoints); -} - -/** - * @brief Return object points for the system centered in a middle (by default) or in a top left corner of single - * marker, given the marker length - */ -static Mat _getSingleMarkerObjectPoints(float markerLength, const EstimateParameters& estimateParameters) { - CV_Assert(markerLength > 0); - Mat objPoints(4, 1, CV_32FC3); - // set coordinate system in the top-left corner of the marker, with Z pointing out - if (estimateParameters.pattern == ARUCO_CW_TOP_LEFT_CORNER) { - objPoints.ptr(0)[0] = Vec3f(0.f, 0.f, 0); - objPoints.ptr(0)[1] = Vec3f(markerLength, 0.f, 0); - objPoints.ptr(0)[2] = Vec3f(markerLength, markerLength, 0); - objPoints.ptr(0)[3] = Vec3f(0.f, markerLength, 0); - } - else if (estimateParameters.pattern == ARUCO_CCW_CENTER) { - objPoints.ptr(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0); - objPoints.ptr(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0); - objPoints.ptr(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0); - objPoints.ptr(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0); - } - else - CV_Error(Error::StsBadArg, "Unknown estimateParameters pattern"); - return objPoints; -} - -void estimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength, - InputArray _cameraMatrix, InputArray _distCoeffs, - OutputArray _rvecs, OutputArray _tvecs, OutputArray _objPoints, - const Ptr& estimateParameters) { - CV_Assert(markerLength > 0); - - Mat markerObjPoints = _getSingleMarkerObjectPoints(markerLength, *estimateParameters); - int nMarkers = (int)_corners.total(); - _rvecs.create(nMarkers, 1, CV_64FC3); - _tvecs.create(nMarkers, 1, CV_64FC3); - - Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat(); - - //// for each marker, calculate its pose - parallel_for_(Range(0, nMarkers), [&](const Range& range) { - const int begin = range.start; - const int end = range.end; - - for (int i = begin; i < end; i++) { - solvePnP(markerObjPoints, _corners.getMat(i), _cameraMatrix, _distCoeffs, rvecs.at(i), - tvecs.at(i), estimateParameters->useExtrinsicGuess, estimateParameters->solvePnPMethod); - } - }); - - if(_objPoints.needed()){ - markerObjPoints.convertTo(_objPoints, -1); - } -} - -int estimatePoseBoard(InputArrayOfArrays _corners, InputArray _ids, const Ptr &board, - InputArray _cameraMatrix, InputArray _distCoeffs, InputOutputArray _rvec, - InputOutputArray _tvec, bool useExtrinsicGuess) { - CV_Assert(_corners.total() == _ids.total()); - - // get object and image points for the solvePnP function - Mat objPoints, imgPoints; - getBoardObjectAndImagePoints(board, _corners, _ids, objPoints, imgPoints); - - CV_Assert(imgPoints.total() == objPoints.total()); - - if(objPoints.total() == 0) // 0 of the detected markers in board - return 0; - - solvePnP(objPoints, imgPoints, _cameraMatrix, _distCoeffs, _rvec, _tvec, useExtrinsicGuess); - - // divide by four since all the four corners are concatenated in the array for each marker - return (int)objPoints.total() / 4; -} - -/** - * Check if a set of 3d points are enough for calibration. Z coordinate is ignored. - * Only axis parallel lines are considered - */ -static bool _arePointsEnoughForPoseEstimation(const vector &points) { - if(points.size() < 4) return false; - - vector sameXValue; // different x values in points - vector sameXCounter; // number of points with the x value in sameXValue - for(unsigned int i = 0; i < points.size(); i++) { - bool found = false; - for(unsigned int j = 0; j < sameXValue.size(); j++) { - if(sameXValue[j] == points[i].x) { - found = true; - sameXCounter[j]++; - } - } - if(!found) { - sameXValue.push_back(points[i].x); - sameXCounter.push_back(1); - } - } - - // count how many x values has more than 2 points - int moreThan2 = 0; - for(unsigned int i = 0; i < sameXCounter.size(); i++) { - if(sameXCounter[i] >= 2) moreThan2++; - } - - // if we have more than 1 two xvalues with more than 2 points, calibration is ok - if(moreThan2 > 1) - return true; - return false; -} - -bool estimatePoseCharucoBoard(InputArray _charucoCorners, InputArray _charucoIds, - const Ptr &_board, InputArray _cameraMatrix, InputArray _distCoeffs, - InputOutputArray _rvec, InputOutputArray _tvec, bool useExtrinsicGuess) { - CV_Assert((_charucoCorners.getMat().total() == _charucoIds.getMat().total())); - - // need, at least, 4 corners - if(_charucoIds.getMat().total() < 4) return false; - - vector objPoints; - objPoints.reserve(_charucoIds.getMat().total()); - for(unsigned int i = 0; i < _charucoIds.getMat().total(); i++) { - int currId = _charucoIds.getMat().at< int >(i); - CV_Assert(currId >= 0 && currId < (int)_board->chessboardCorners.size()); - objPoints.push_back(_board->chessboardCorners[currId]); - } - - // points need to be in different lines, check if detected points are enough - if(!_arePointsEnoughForPoseEstimation(objPoints)) return false; - - solvePnP(objPoints, _charucoCorners, _cameraMatrix, _distCoeffs, _rvec, _tvec, useExtrinsicGuess); - return true; -} - -double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, - const Ptr &board, Size imageSize, InputOutputArray _cameraMatrix, - InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, - OutputArrayOfArrays _tvecs, - OutputArray _stdDeviationsIntrinsics, - OutputArray _stdDeviationsExtrinsics, - OutputArray _perViewErrors, - int flags, const TermCriteria& criteria) { - // for each frame, get properly processed imagePoints and objectPoints for the calibrateCamera - // function - vector processedObjectPoints, processedImagePoints; - size_t nFrames = _counter.total(); - int markerCounter = 0; - for(size_t frame = 0; frame < nFrames; frame++) { - int nMarkersInThisFrame = _counter.getMat().ptr< int >()[frame]; - vector thisFrameCorners; - vector thisFrameIds; - - CV_Assert(nMarkersInThisFrame > 0); - - thisFrameCorners.reserve((size_t) nMarkersInThisFrame); - thisFrameIds.reserve((size_t) nMarkersInThisFrame); - for(int j = markerCounter; j < markerCounter + nMarkersInThisFrame; j++) { - thisFrameCorners.push_back(_corners.getMat(j)); - thisFrameIds.push_back(_ids.getMat().ptr< int >()[j]); - } - markerCounter += nMarkersInThisFrame; - Mat currentImgPoints, currentObjPoints; - getBoardObjectAndImagePoints(board, thisFrameCorners, thisFrameIds, currentObjPoints, - currentImgPoints); - if(currentImgPoints.total() > 0 && currentObjPoints.total() > 0) { - processedImagePoints.push_back(currentImgPoints); - processedObjectPoints.push_back(currentObjPoints); - } - } - return calibrateCamera(processedObjectPoints, processedImagePoints, imageSize, _cameraMatrix, _distCoeffs, _rvecs, - _tvecs, _stdDeviationsIntrinsics, _stdDeviationsExtrinsics, _perViewErrors, flags, criteria); -} - -double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, const Ptr &board, - Size imageSize, InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, - OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, int flags, const TermCriteria& criteria) { - return calibrateCameraAruco(_corners, _ids, _counter, board, imageSize, _cameraMatrix, _distCoeffs, - _rvecs, _tvecs, noArray(), noArray(), noArray(), flags, criteria); -} - -double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds, - const Ptr &_board, Size imageSize, - InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, - OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, - OutputArray _stdDeviationsIntrinsics, - OutputArray _stdDeviationsExtrinsics, - OutputArray _perViewErrors, - int flags, const TermCriteria& criteria) { - CV_Assert(_charucoIds.total() > 0 && (_charucoIds.total() == _charucoCorners.total())); - - // Join object points of charuco corners in a single vector for calibrateCamera() function - vector > allObjPoints; - allObjPoints.resize(_charucoIds.total()); - for(unsigned int i = 0; i < _charucoIds.total(); i++) { - unsigned int nCorners = (unsigned int)_charucoIds.getMat(i).total(); - CV_Assert(nCorners > 0 && nCorners == _charucoCorners.getMat(i).total()); - allObjPoints[i].reserve(nCorners); - - for(unsigned int j = 0; j < nCorners; j++) { - int pointId = _charucoIds.getMat(i).at< int >(j); - CV_Assert(pointId >= 0 && pointId < (int)_board->chessboardCorners.size()); - allObjPoints[i].push_back(_board->chessboardCorners[pointId]); - } - } - return calibrateCamera(allObjPoints, _charucoCorners, imageSize, _cameraMatrix, _distCoeffs, _rvecs, _tvecs, - _stdDeviationsIntrinsics, _stdDeviationsExtrinsics, _perViewErrors, flags, criteria); -} - -double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds, - const Ptr &_board, Size imageSize, InputOutputArray _cameraMatrix, - InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, - int flags, const TermCriteria& criteria) { -return calibrateCameraCharuco(_charucoCorners, _charucoIds, _board, imageSize, _cameraMatrix, _distCoeffs, _rvecs, - _tvecs, noArray(), noArray(), noArray(), flags, criteria); -} - -} -} diff --git a/modules/aruco/src/aruco_detector.cpp b/modules/aruco/src/aruco_detector.cpp deleted file mode 100644 index 684e403fd4..0000000000 --- a/modules/aruco/src/aruco_detector.cpp +++ /dev/null @@ -1,1261 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html - -#include "precomp.hpp" -#include -#include "opencv2/aruco_detector.hpp" -#include "opencv2/aruco/aruco_calib_pose.hpp" -#include "aruco_utils.hpp" -#include "apriltag/apriltag_quad_thresh.hpp" -#include - -namespace cv { -namespace aruco { - -using namespace std; - -static inline bool readWrite(DetectorParameters ¶ms, const Ptr& readNode = nullptr, - const Ptr& writeStorage = nullptr) { - CV_Assert(!readNode.empty() || !writeStorage.empty()); - bool check = false; - - check |= readWriteParameter("adaptiveThreshWinSizeMin", params.adaptiveThreshWinSizeMin, readNode, writeStorage); - check |= readWriteParameter("adaptiveThreshWinSizeMax", params.adaptiveThreshWinSizeMax, readNode, writeStorage); - check |= readWriteParameter("adaptiveThreshWinSizeStep", params.adaptiveThreshWinSizeStep, readNode, writeStorage); - check |= readWriteParameter("adaptiveThreshConstant", params.adaptiveThreshConstant, readNode, writeStorage); - check |= readWriteParameter("minMarkerPerimeterRate", params.minMarkerPerimeterRate, readNode, writeStorage); - check |= readWriteParameter("maxMarkerPerimeterRate", params.maxMarkerPerimeterRate, readNode, writeStorage); - check |= readWriteParameter("polygonalApproxAccuracyRate", params.polygonalApproxAccuracyRate, - readNode, writeStorage); - check |= readWriteParameter("minCornerDistanceRate", params.minCornerDistanceRate, readNode, writeStorage); - check |= readWriteParameter("minDistanceToBorder", params.minDistanceToBorder, readNode, writeStorage); - check |= readWriteParameter("minMarkerDistanceRate", params.minMarkerDistanceRate, readNode, writeStorage); - check |= readWriteParameter("cornerRefinementMethod", params.cornerRefinementMethod, readNode, writeStorage); - check |= readWriteParameter("cornerRefinementWinSize", params.cornerRefinementWinSize, readNode, writeStorage); - check |= readWriteParameter("cornerRefinementMaxIterations", params.cornerRefinementMaxIterations, - readNode, writeStorage); - check |= readWriteParameter("cornerRefinementMinAccuracy", params.cornerRefinementMinAccuracy, - readNode, writeStorage); - check |= readWriteParameter("markerBorderBits", params.markerBorderBits, readNode, writeStorage); - check |= readWriteParameter("perspectiveRemovePixelPerCell", params.perspectiveRemovePixelPerCell, - readNode, writeStorage); - check |= readWriteParameter("perspectiveRemoveIgnoredMarginPerCell", params.perspectiveRemoveIgnoredMarginPerCell, - readNode, writeStorage); - check |= readWriteParameter("maxErroneousBitsInBorderRate", params.maxErroneousBitsInBorderRate, - readNode, writeStorage); - check |= readWriteParameter("minOtsuStdDev", params.minOtsuStdDev, readNode, writeStorage); - check |= readWriteParameter("errorCorrectionRate", params.errorCorrectionRate, readNode, writeStorage); - // new aruco 3 functionality - check |= readWriteParameter("useAruco3Detection", params.useAruco3Detection, readNode, writeStorage); - check |= readWriteParameter("minSideLengthCanonicalImg", params.minSideLengthCanonicalImg, readNode, writeStorage); - check |= readWriteParameter("minMarkerLengthRatioOriginalImg", params.minMarkerLengthRatioOriginalImg, - readNode, writeStorage); - return check; -} - -bool DetectorParameters::readDetectorParameters(const FileNode& fn) { - if(fn.empty()) - return false; - Ptr pfn = makePtr(fn); - return readWrite(*this, pfn); -} - -bool DetectorParameters::writeDetectorParameters(const Ptr& fs) -{ - if (fs.empty() || !fs->isOpened()) - return false; - return readWrite(*this, nullptr, fs); -} - -static inline bool readWrite(RefineParameters& refineParameters, const Ptr& readNode, - const Ptr& writeStorage = nullptr) { - CV_Assert(!readNode.empty() || !writeStorage.empty()); - bool check = false; - - check |= readWriteParameter("minRepDistance", refineParameters.minRepDistance, readNode, writeStorage); - check |= readWriteParameter("errorCorrectionRate", refineParameters.errorCorrectionRate, readNode, writeStorage); - check |= readWriteParameter("checkAllOrders", refineParameters.checkAllOrders, readNode, writeStorage); - return check; -} - -bool RefineParameters::readRefineParameters(const FileNode &fn) { - if(fn.empty()) - return false; - Ptr pfn = makePtr(fn); - return readWrite(*this, pfn); -} - -bool RefineParameters::writeRefineParameters(const Ptr &fs) { - if(fs.empty()) - return false; - return readWrite(*this, nullptr, fs); -} - -/** - * @brief Threshold input image using adaptive thresholding - */ -static void _threshold(InputArray _in, OutputArray _out, int winSize, double constant) { - - CV_Assert(winSize >= 3); - if(winSize % 2 == 0) winSize++; // win size must be odd - adaptiveThreshold(_in, _out, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, winSize, constant); -} - - -/** - * @brief Given a tresholded image, find the contours, calculate their polygonal approximation - * and take those that accomplish some conditions - */ -static void _findMarkerContours(const Mat &in, vector< vector< Point2f > > &candidates, - vector< vector< Point > > &contoursOut, double minPerimeterRate, - double maxPerimeterRate, double accuracyRate, - double minCornerDistanceRate, int minDistanceToBorder, int minSize) { - - CV_Assert(minPerimeterRate > 0 && maxPerimeterRate > 0 && accuracyRate > 0 && - minCornerDistanceRate >= 0 && minDistanceToBorder >= 0); - - // calculate maximum and minimum sizes in pixels - unsigned int minPerimeterPixels = - (unsigned int)(minPerimeterRate * max(in.cols, in.rows)); - unsigned int maxPerimeterPixels = - (unsigned int)(maxPerimeterRate * max(in.cols, in.rows)); - - // for aruco3 functionality - if (minSize != 0) { - minPerimeterPixels = 4*minSize; - } - - Mat contoursImg; - in.copyTo(contoursImg); - vector< vector< Point > > contours; - findContours(contoursImg, contours, RETR_LIST, CHAIN_APPROX_NONE); - // now filter list of contours - for(unsigned int i = 0; i < contours.size(); i++) { - // check perimeter - if(contours[i].size() < minPerimeterPixels || contours[i].size() > maxPerimeterPixels) - continue; - - // check is square and is convex - vector< Point > approxCurve; - approxPolyDP(contours[i], approxCurve, double(contours[i].size()) * accuracyRate, true); - if(approxCurve.size() != 4 || !isContourConvex(approxCurve)) continue; - - // check min distance between corners - double minDistSq = - max(contoursImg.cols, contoursImg.rows) * max(contoursImg.cols, contoursImg.rows); - for(int j = 0; j < 4; j++) { - double d = (double)(approxCurve[j].x - approxCurve[(j + 1) % 4].x) * - (double)(approxCurve[j].x - approxCurve[(j + 1) % 4].x) + - (double)(approxCurve[j].y - approxCurve[(j + 1) % 4].y) * - (double)(approxCurve[j].y - approxCurve[(j + 1) % 4].y); - minDistSq = min(minDistSq, d); - } - double minCornerDistancePixels = double(contours[i].size()) * minCornerDistanceRate; - if(minDistSq < minCornerDistancePixels * minCornerDistancePixels) continue; - - // check if it is too near to the image border - bool tooNearBorder = false; - for(int j = 0; j < 4; j++) { - if(approxCurve[j].x < minDistanceToBorder || approxCurve[j].y < minDistanceToBorder || - approxCurve[j].x > contoursImg.cols - 1 - minDistanceToBorder || - approxCurve[j].y > contoursImg.rows - 1 - minDistanceToBorder) - tooNearBorder = true; - } - if(tooNearBorder) continue; - - // if it passes all the test, add to candidates vector - vector< Point2f > currentCandidate; - currentCandidate.resize(4); - for(int j = 0; j < 4; j++) { - currentCandidate[j] = Point2f((float)approxCurve[j].x, (float)approxCurve[j].y); - } - candidates.push_back(currentCandidate); - contoursOut.push_back(contours[i]); - } -} - - -/** - * @brief Assure order of candidate corners is clockwise direction - */ -static void _reorderCandidatesCorners(vector< vector< Point2f > > &candidates) { - - for(unsigned int i = 0; i < candidates.size(); i++) { - double dx1 = candidates[i][1].x - candidates[i][0].x; - double dy1 = candidates[i][1].y - candidates[i][0].y; - double dx2 = candidates[i][2].x - candidates[i][0].x; - double dy2 = candidates[i][2].y - candidates[i][0].y; - double crossProduct = (dx1 * dy2) - (dy1 * dx2); - - if(crossProduct < 0.0) { // not clockwise direction - swap(candidates[i][1], candidates[i][3]); - } - } -} - -/** - * @brief to make sure that the corner's order of both candidates (default/white) is the same - */ -static vector alignContourOrder(Point2f corner, vector< Point2f > candidate) { - uint8_t r=0; - double min = cv::norm( Vec2f( corner - candidate[0] ), NORM_L2SQR); - for(uint8_t pos=1; pos < 4; pos++) { - double nDiff = cv::norm( Vec2f( corner - candidate[pos] ), NORM_L2SQR); - if(nDiff < min){ - r = pos; - min =nDiff; - } - } - std::rotate(candidate.begin(), candidate.begin() + r, candidate.end()); - return candidate; -} - -/** - * @brief Check candidates that are too close to each other, save the potential candidates - * (i.e. biggest/smallest contour) and remove the rest - */ -static void _filterTooCloseCandidates(const vector< vector< Point2f > > &candidatesIn, - vector< vector< vector< Point2f > > > &candidatesSetOut, - const vector< vector< Point > > &contoursIn, - vector< vector< vector< Point > > > &contoursSetOut, - double minMarkerDistanceRate, bool detectInvertedMarker) { - - CV_Assert(minMarkerDistanceRate >= 0); - vector candGroup; - candGroup.resize(candidatesIn.size(), -1); - vector< vector > groupedCandidates; - for(unsigned int i = 0; i < candidatesIn.size(); i++) { - bool isSingleContour = true; - for(unsigned int j = i + 1; j < candidatesIn.size(); j++) { - - int minimumPerimeter = min((int)contoursIn[i].size(), (int)contoursIn[j].size() ); - - // fc is the first corner considered on one of the markers, 4 combinations are possible - for(int fc = 0; fc < 4; fc++) { - double distSq = 0; - for(int c = 0; c < 4; c++) { - // modC is the corner considering first corner is fc - int modC = (c + fc) % 4; - distSq += (candidatesIn[i][modC].x - candidatesIn[j][c].x) * - (candidatesIn[i][modC].x - candidatesIn[j][c].x) + - (candidatesIn[i][modC].y - candidatesIn[j][c].y) * - (candidatesIn[i][modC].y - candidatesIn[j][c].y); - } - distSq /= 4.; - - // if mean square distance is too low, remove the smaller one of the two markers - double minMarkerDistancePixels = double(minimumPerimeter) * minMarkerDistanceRate; - if(distSq < minMarkerDistancePixels * minMarkerDistancePixels) { - isSingleContour = false; - // i and j are not related to a group - if(candGroup[i]<0 && candGroup[j]<0){ - // mark candidates with their corresponding group number - candGroup[i] = candGroup[j] = (int)groupedCandidates.size(); - - // create group - vector grouped; - grouped.push_back(i); - grouped.push_back(j); - groupedCandidates.push_back( grouped ); - } - // i is related to a group - else if(candGroup[i] > -1 && candGroup[j] == -1){ - int group = candGroup[i]; - candGroup[j] = group; - - // add to group - groupedCandidates[group].push_back( j ); - } - // j is related to a group - else if(candGroup[j] > -1 && candGroup[i] == -1){ - int group = candGroup[j]; - candGroup[i] = group; - - // add to group - groupedCandidates[group].push_back( i ); - } - } - } - } - if (isSingleContour && candGroup[i] < 0) - { - candGroup[i] = (int)groupedCandidates.size(); - vector grouped; - grouped.push_back(i); - grouped.push_back(i); // step "save possible candidates" require minimum 2 elements - groupedCandidates.push_back(grouped); - } - } - - // save possible candidates - candidatesSetOut.clear(); - contoursSetOut.clear(); - - vector< vector< Point2f > > biggerCandidates; - vector< vector< Point > > biggerContours; - vector< vector< Point2f > > smallerCandidates; - vector< vector< Point > > smallerContours; - - // save possible candidates - for(unsigned int i = 0; i < groupedCandidates.size(); i++) { - unsigned int smallerIdx = groupedCandidates[i][0]; - unsigned int biggerIdx = smallerIdx; - double smallerArea = contourArea(candidatesIn[smallerIdx]); - double biggerArea = smallerArea; - - // evaluate group elements - for(unsigned int j = 1; j < groupedCandidates[i].size(); j++) { - unsigned int currIdx = groupedCandidates[i][j]; - double currArea = contourArea(candidatesIn[currIdx]); - - // check if current contour is bigger - if(currArea >= biggerArea) { - biggerIdx = currIdx; - biggerArea = currArea; - } - - // check if current contour is smaller - if(currArea < smallerArea && detectInvertedMarker) { - smallerIdx = currIdx; - smallerArea = currArea; - } - } - - // add contours and candidates - biggerCandidates.push_back(candidatesIn[biggerIdx]); - biggerContours.push_back(contoursIn[biggerIdx]); - if(detectInvertedMarker) { - smallerCandidates.push_back(alignContourOrder(candidatesIn[biggerIdx][0], candidatesIn[smallerIdx])); - smallerContours.push_back(contoursIn[smallerIdx]); - } - } - // to preserve the structure :: candidateSet< defaultCandidates, whiteCandidates > - // default candidates - candidatesSetOut.push_back(biggerCandidates); - contoursSetOut.push_back(biggerContours); - // white candidates - candidatesSetOut.push_back(smallerCandidates); - contoursSetOut.push_back(smallerContours); -} - -/** - * @brief Initial steps on finding square candidates - */ -static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > > &candidates, - vector< vector< Point > > &contours, - const Ptr ¶ms) { - - CV_Assert(params->adaptiveThreshWinSizeMin >= 3 && params->adaptiveThreshWinSizeMax >= 3); - CV_Assert(params->adaptiveThreshWinSizeMax >= params->adaptiveThreshWinSizeMin); - CV_Assert(params->adaptiveThreshWinSizeStep > 0); - - // number of window sizes (scales) to apply adaptive thresholding - int nScales = (params->adaptiveThreshWinSizeMax - params->adaptiveThreshWinSizeMin) / - params->adaptiveThreshWinSizeStep + 1; - - vector< vector< vector< Point2f > > > candidatesArrays((size_t) nScales); - vector< vector< vector< Point > > > contoursArrays((size_t) nScales); - - ////for each value in the interval of thresholding window sizes - parallel_for_(Range(0, nScales), [&](const Range& range) { - const int begin = range.start; - const int end = range.end; - - for (int i = begin; i < end; i++) { - int currScale = params->adaptiveThreshWinSizeMin + i * params->adaptiveThreshWinSizeStep; - // threshold - Mat thresh; - _threshold(grey, thresh, currScale, params->adaptiveThreshConstant); - - // detect rectangles - _findMarkerContours(thresh, candidatesArrays[i], contoursArrays[i], - params->minMarkerPerimeterRate, params->maxMarkerPerimeterRate, - params->polygonalApproxAccuracyRate, params->minCornerDistanceRate, - params->minDistanceToBorder, params->minSideLengthCanonicalImg); - } - }); - // join candidates - for(int i = 0; i < nScales; i++) { - for(unsigned int j = 0; j < candidatesArrays[i].size(); j++) { - candidates.push_back(candidatesArrays[i][j]); - contours.push_back(contoursArrays[i][j]); - } - } -} - - -/** - * @brief Detect square candidates in the input image - */ -static void _detectCandidates(InputArray _grayImage, vector< vector< vector< Point2f > > >& candidatesSetOut, - vector< vector< vector< Point > > >& contoursSetOut, const Ptr &_params) { - Mat grey = _grayImage.getMat(); - CV_DbgAssert(grey.total() != 0); - CV_DbgAssert(grey.type() == CV_8UC1); - - /// 1. DETECT FIRST SET OF CANDIDATES - vector< vector< Point2f > > candidates; - vector< vector< Point > > contours; - _detectInitialCandidates(grey, candidates, contours, _params); - /// 2. SORT CORNERS - _reorderCandidatesCorners(candidates); - - /// 3. FILTER OUT NEAR CANDIDATE PAIRS - // save the outter/inner border (i.e. potential candidates) - _filterTooCloseCandidates(candidates, candidatesSetOut, contours, contoursSetOut, - _params->minMarkerDistanceRate, _params->detectInvertedMarker); -} - - -/** - * @brief Given an input image and candidate corners, extract the bits of the candidate, including - * the border bits - */ -static Mat _extractBits(InputArray _image, const vector& corners, int markerSize, - int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu) { - CV_Assert(_image.getMat().channels() == 1); - CV_Assert(corners.size() == 4ull); - CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 1); - CV_Assert(minStdDevOtsu >= 0); - - // number of bits in the marker - int markerSizeWithBorders = markerSize + 2 * markerBorderBits; - int cellMarginPixels = int(cellMarginRate * cellSize); - - Mat resultImg; // marker image after removing perspective - int resultImgSize = markerSizeWithBorders * cellSize; - Mat resultImgCorners(4, 1, CV_32FC2); - resultImgCorners.ptr< Point2f >(0)[0] = Point2f(0, 0); - resultImgCorners.ptr< Point2f >(0)[1] = Point2f((float)resultImgSize - 1, 0); - resultImgCorners.ptr< Point2f >(0)[2] = - Point2f((float)resultImgSize - 1, (float)resultImgSize - 1); - resultImgCorners.ptr< Point2f >(0)[3] = Point2f(0, (float)resultImgSize - 1); - - // remove perspective - Mat transformation = getPerspectiveTransform(corners, resultImgCorners); - warpPerspective(_image, resultImg, transformation, Size(resultImgSize, resultImgSize), - INTER_NEAREST); - - // output image containing the bits - Mat bits(markerSizeWithBorders, markerSizeWithBorders, CV_8UC1, Scalar::all(0)); - - // check if standard deviation is enough to apply Otsu - // if not enough, it probably means all bits are the same color (black or white) - Mat mean, stddev; - // Remove some border just to avoid border noise from perspective transformation - Mat innerRegion = resultImg.colRange(cellSize / 2, resultImg.cols - cellSize / 2) - .rowRange(cellSize / 2, resultImg.rows - cellSize / 2); - meanStdDev(innerRegion, mean, stddev); - if(stddev.ptr< double >(0)[0] < minStdDevOtsu) { - // all black or all white, depending on mean value - if(mean.ptr< double >(0)[0] > 127) - bits.setTo(1); - else - bits.setTo(0); - return bits; - } - - // now extract code, first threshold using Otsu - threshold(resultImg, resultImg, 125, 255, THRESH_BINARY | THRESH_OTSU); - - // for each cell - for(int y = 0; y < markerSizeWithBorders; y++) { - for(int x = 0; x < markerSizeWithBorders; x++) { - int Xstart = x * (cellSize) + cellMarginPixels; - int Ystart = y * (cellSize) + cellMarginPixels; - Mat square = resultImg(Rect(Xstart, Ystart, cellSize - 2 * cellMarginPixels, - cellSize - 2 * cellMarginPixels)); - // count white pixels on each cell to assign its value - size_t nZ = (size_t) countNonZero(square); - if(nZ > square.total() / 2) bits.at< unsigned char >(y, x) = 1; - } - } - - return bits; -} - - - -/** - * @brief Return number of erroneous bits in border, i.e. number of white bits in border. - */ -static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { - - int sizeWithBorders = markerSize + 2 * borderSize; - - CV_Assert(markerSize > 0 && bits.cols == sizeWithBorders && bits.rows == sizeWithBorders); - - int totalErrors = 0; - for(int y = 0; y < sizeWithBorders; y++) { - for(int k = 0; k < borderSize; k++) { - if(bits.ptr< unsigned char >(y)[k] != 0) totalErrors++; - if(bits.ptr< unsigned char >(y)[sizeWithBorders - 1 - k] != 0) totalErrors++; - } - } - for(int x = borderSize; x < sizeWithBorders - borderSize; x++) { - for(int k = 0; k < borderSize; k++) { - if(bits.ptr< unsigned char >(k)[x] != 0) totalErrors++; - if(bits.ptr< unsigned char >(sizeWithBorders - 1 - k)[x] != 0) totalErrors++; - } - } - return totalErrors; -} - - -/** - * @brief Tries to identify one candidate given the dictionary - * @return candidate typ. zero if the candidate is not valid, - * 1 if the candidate is a black candidate (default candidate) - * 2 if the candidate is a white candidate - */ -static uint8_t _identifyOneCandidate(const Ptr& dictionary, InputArray _image, - const vector& _corners, int& idx, - const Ptr& params, int& rotation, - const float scale = 1.f) { - CV_DbgAssert(_corners.size() == 4); - CV_DbgAssert(_image.getMat().total() != 0); - CV_DbgAssert(params->markerBorderBits > 0); - uint8_t typ=1; - // get bits - // scale corners to the correct size to search on the corresponding image pyramid - vector scaled_corners(4); - for (int i = 0; i < 4; ++i) { - scaled_corners[i].x = _corners[i].x * scale; - scaled_corners[i].y = _corners[i].y * scale; - } - - Mat candidateBits = - _extractBits(_image, scaled_corners, dictionary->markerSize, params->markerBorderBits, - params->perspectiveRemovePixelPerCell, - params->perspectiveRemoveIgnoredMarginPerCell, params->minOtsuStdDev); - - // analyze border bits - int maximumErrorsInBorder = - int(dictionary->markerSize * dictionary->markerSize * params->maxErroneousBitsInBorderRate); - int borderErrors = - _getBorderErrors(candidateBits, dictionary->markerSize, params->markerBorderBits); - - // check if it is a white marker - if(params->detectInvertedMarker){ - // to get from 255 to 1 - Mat invertedImg = ~candidateBits-254; - int invBError = _getBorderErrors(invertedImg, dictionary->markerSize, params->markerBorderBits); - // white marker - if(invBError maximumErrorsInBorder) return 0; // border is wrong - - // take only inner bits - Mat onlyBits = - candidateBits.rowRange(params->markerBorderBits, - candidateBits.rows - params->markerBorderBits) - .colRange(params->markerBorderBits, candidateBits.cols - params->markerBorderBits); - - // try to indentify the marker - if(!dictionary->identify(onlyBits, idx, rotation, params->errorCorrectionRate)) - return 0; - - return typ; -} - -/** - * @brief rotate the initial corner to get to the right position - */ -static void correctCornerPosition( vector< Point2f >& _candidate, int rotate){ - std::rotate(_candidate.begin(), _candidate.begin() + 4 - rotate, _candidate.end()); -} - -static size_t _findOptPyrImageForCanonicalImg( - const std::vector& img_pyr, - const int scaled_width, - const int cur_perimeter, - const int min_perimeter) { - CV_Assert(scaled_width > 0); - size_t optLevel = 0; - float dist = std::numeric_limits::max(); - for (size_t i = 0; i < img_pyr.size(); ++i) { - const float scale = img_pyr[i].cols / static_cast(scaled_width); - const float perimeter_scaled = cur_perimeter * scale; - // instead of std::abs() favor the larger pyramid level by checking if the distance is postive - // will slow down the algorithm but find more corners in the end - const float new_dist = perimeter_scaled - min_perimeter; - if (new_dist < dist && new_dist > 0.f) { - dist = new_dist; - optLevel = i; - } - } - return optLevel; -} - -/** - * @brief Identify square candidates according to a marker dictionary - */ - -static void _identifyCandidates(InputArray grey, - const std::vector& image_pyr, - vector< vector< vector< Point2f > > >& _candidatesSet, - vector< vector< vector > >& _contoursSet, const Ptr &_dictionary, - vector< vector< Point2f > >& _accepted, vector< vector >& _contours, vector< int >& ids, - const Ptr ¶ms, - OutputArrayOfArrays _rejected = noArray()) { - CV_DbgAssert(grey.getMat().total() != 0); - CV_DbgAssert(grey.getMat().type() == CV_8UC1); - int ncandidates = (int)_candidatesSet[0].size(); - vector< vector< Point2f > > accepted; - vector< vector< Point2f > > rejected; - vector< vector< Point > > contours; - - vector< int > idsTmp(ncandidates, -1); - vector< int > rotated(ncandidates, 0); - vector< uint8_t > validCandidates(ncandidates, 0); - - //// Analyze each of the candidates - parallel_for_(Range(0, ncandidates), [&](const Range &range) { - const int begin = range.start; - const int end = range.end; - - vector< vector< Point2f > >& candidates = params->detectInvertedMarker ? _candidatesSet[1] : _candidatesSet[0]; - vector< vector< Point > >& contourS = params->detectInvertedMarker ? _contoursSet[1] : _contoursSet[0]; - - for(int i = begin; i < end; i++) { - int currId = -1; - // implements equation (4) - if (params->useAruco3Detection) { - const int perimeterOfContour = static_cast(contourS[i].size()); - const int min_perimeter = params->minSideLengthCanonicalImg * 4; - const size_t nearestImgId = _findOptPyrImageForCanonicalImg(image_pyr, grey.cols(), perimeterOfContour, min_perimeter); - const float scale = image_pyr[nearestImgId].cols / static_cast(grey.cols()); - - validCandidates[i] = _identifyOneCandidate(_dictionary, image_pyr[nearestImgId], candidates[i], currId, params, rotated[i], scale); - } - else { - validCandidates[i] = _identifyOneCandidate(_dictionary, grey, candidates[i], currId, params, rotated[i]); - } - - if(validCandidates[i] > 0) - idsTmp[i] = currId; - } - }); - - for(int i = 0; i < ncandidates; i++) { - if(validCandidates[i] > 0) { - // to choose the right set of candidates :: 0 for default, 1 for white markers - uint8_t set = validCandidates[i]-1; - - // shift corner positions to the correct rotation - correctCornerPosition(_candidatesSet[set][i], rotated[i]); - - if( !params->detectInvertedMarker && validCandidates[i] == 2 ) - continue; - - // add valid candidate - accepted.push_back(_candidatesSet[set][i]); - ids.push_back(idsTmp[i]); - - contours.push_back(_contoursSet[set][i]); - - } else { - rejected.push_back(_candidatesSet[0][i]); - } - } - - // parse output - _accepted = accepted; - - _contours= contours; - - if(_rejected.needed()) { - _copyVector2Output(rejected, _rejected); - } -} - -/** - * Line fitting A * B = C :: Called from function refineCandidateLines - * @param nContours, contour-container - */ -static Point3f _interpolate2Dline(const std::vector& nContours){ - CV_Assert(nContours.size() >= 2); - float minX, minY, maxX, maxY; - minX = maxX = nContours[0].x; - minY = maxY = nContours[0].y; - - for(unsigned int i = 0; i< nContours.size(); i++){ - minX = nContours[i].x < minX ? nContours[i].x : minX; - minY = nContours[i].y < minY ? nContours[i].y : minY; - maxX = nContours[i].x > maxX ? nContours[i].x : maxX; - maxY = nContours[i].y > maxY ? nContours[i].y : maxY; - } - - Mat A = Mat::ones((int)nContours.size(), 2, CV_32F); // Coefficient Matrix (N x 2) - Mat B((int)nContours.size(), 1, CV_32F); // Variables Matrix (N x 1) - Mat C; // Constant - - if(maxX - minX > maxY - minY){ - for(unsigned int i =0; i < nContours.size(); i++){ - A.at(i,0)= nContours[i].x; - B.at(i,0)= nContours[i].y; - } - - solve(A, B, C, DECOMP_NORMAL); - - return Point3f(C.at(0, 0), -1., C.at(1, 0)); - } - else{ - for(unsigned int i =0; i < nContours.size(); i++){ - A.at(i,0)= nContours[i].y; - B.at(i,0)= nContours[i].x; - } - - solve(A, B, C, DECOMP_NORMAL); - - return Point3f(-1., C.at(0, 0), C.at(1, 0)); - } - -} - -/** - * Find the Point where the lines crosses :: Called from function refineCandidateLines - * @param nLine1 - * @param nLine2 - * @return Crossed Point - */ -static Point2f _getCrossPoint(Point3f nLine1, Point3f nLine2){ - Matx22f A(nLine1.x, nLine1.y, nLine2.x, nLine2.y); - Vec2f B(-nLine1.z, -nLine2.z); - return Vec2f(A.solve(B).val); -} - -/** - * Refine Corners using the contour vector :: Called from function detectMarkers - * @param nContours, contour-container - * @param nCorners, candidate Corners - * @param camMatrix, cameraMatrix input 3x3 floating-point camera matrix - * @param distCoeff, distCoeffs vector of distortion coefficient - */ -static void _refineCandidateLines(std::vector& nContours, std::vector& nCorners){ - vector contour2f(nContours.begin(), nContours.end()); - /* 5 groups :: to group the edges - * 4 - classified by its corner - * extra group - (temporary) if contours do not begin with a corner - */ - vector cntPts[5]; - int cornerIndex[4]={-1}; - int group=4; - - for ( unsigned int i =0; i < nContours.size(); i++ ) { - for(unsigned int j=0; j<4; j++){ - if ( nCorners[j] == contour2f[i] ){ - cornerIndex[j] = i; - group=j; - } - } - cntPts[group].push_back(contour2f[i]); - } - for (int i = 0; i < 4; i++) - { - CV_Assert(cornerIndex[i] != -1); - } - // saves extra group into corresponding - if( !cntPts[4].empty() ){ - for( unsigned int i=0; i < cntPts[4].size() ; i++ ) - cntPts[group].push_back(cntPts[4].at(i)); - cntPts[4].clear(); - } - - //Evaluate contour direction :: using the position of the detected corners - int inc=1; - - inc = ( (cornerIndex[0] > cornerIndex[1]) && (cornerIndex[3] > cornerIndex[0]) ) ? -1:inc; - inc = ( (cornerIndex[2] > cornerIndex[3]) && (cornerIndex[1] > cornerIndex[2]) ) ? -1:inc; - - // calculate the line :: who passes through the grouped points - Point3f lines[4]; - for(int i=0; i<4; i++){ - lines[i]=_interpolate2Dline(cntPts[i]); - } - - /* - * calculate the corner :: where the lines crosses to each other - * clockwise direction no clockwise direction - * 0 1 - * .---. 1 .---. 2 - * | | | | - * 3 .___. 0 .___. - * 2 3 - */ - for(int i=0; i < 4; i++){ - if(inc<0) - nCorners[i] = _getCrossPoint(lines[ i ], lines[ (i+1)%4 ]); // 01 12 23 30 - else - nCorners[i] = _getCrossPoint(lines[ i ], lines[ (i+3)%4 ]); // 30 01 12 23 - } -} - -static inline void findCornerInPyrImage(const float scale_init, const int closest_pyr_image_idx, - const std::vector& grey_pyramid, Mat corners, - const Ptr& params) { - // scale them to the closest pyramid level - if (scale_init != 1.f) - corners *= scale_init; // scale_init * scale_pyr - for (int idx = closest_pyr_image_idx - 1; idx >= 0; --idx) { - // scale them to new pyramid level - corners *= 2.f; // *= scale_pyr; - // use larger win size for larger images - const int subpix_win_size = std::max(grey_pyramid[idx].cols, grey_pyramid[idx].rows) > 1080 ? 5 : 3; - cornerSubPix(grey_pyramid[idx], corners, - Size(subpix_win_size, subpix_win_size), - Size(-1, -1), - TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - params->cornerRefinementMaxIterations, - params->cornerRefinementMinAccuracy)); - } -} - -void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, - OutputArrayOfArrays _rejectedImgPoints) { - CV_Assert(!_image.empty()); - CV_Assert(params->markerBorderBits > 0); - // check that the parameters are set correctly if Aruco3 is used - CV_Assert(!(params->useAruco3Detection == true && - params->minSideLengthCanonicalImg == 0 && - params->minMarkerLengthRatioOriginalImg == 0.0)); - - Mat grey; - _convertToGrey(_image.getMat(), grey); - - // Aruco3 functionality is the extension of Aruco. - // The description can be found in: - // [1] Speeded up detection of squared fiducial markers, 2018, FJ Romera-Ramirez et al. - // if Aruco3 functionality if not wanted - // change some parameters to be sure to turn it off - if (!params->useAruco3Detection) { - params->minMarkerLengthRatioOriginalImg = 0.0; - params->minSideLengthCanonicalImg = 0; - } - else { - // always turn on corner refinement in case of Aruco3, due to upsampling - params->cornerRefinementMethod = CORNER_REFINE_SUBPIX; - // only CORNER_REFINE_SUBPIX implement correctly for useAruco3Detection - // Todo: update other CORNER_REFINE methods - } - - /// Step 0: equation (2) from paper [1] - const float fxfy = (!params->useAruco3Detection ? 1.f : params->minSideLengthCanonicalImg / - (params->minSideLengthCanonicalImg + std::max(grey.cols, grey.rows)*params->minMarkerLengthRatioOriginalImg)); - - /// Step 1: create image pyramid. Section 3.4. in [1] - std::vector grey_pyramid; - int closest_pyr_image_idx = 0, num_levels = 0; - //// Step 1.1: resize image with equation (1) from paper [1] - if (params->useAruco3Detection) { - const float scale_pyr = 2.f; - const float img_area = static_cast(grey.rows*grey.cols); - const float min_area_marker = static_cast(params->minSideLengthCanonicalImg*params->minSideLengthCanonicalImg); - // find max level - num_levels = static_cast(log2(img_area / min_area_marker)/scale_pyr); - // the closest pyramid image to the downsampled segmentation image - // will later be used as start index for corner upsampling - const float scale_img_area = img_area * fxfy * fxfy; - closest_pyr_image_idx = cvRound(log2(img_area / scale_img_area)/scale_pyr); - } - cv::buildPyramid(grey, grey_pyramid, num_levels); - - // resize to segmentation image - // in this reduces size the contours will be detected - if (fxfy != 1.f) - cv::resize(grey, grey, cv::Size(cvRound(fxfy * grey.cols), cvRound(fxfy * grey.rows))); - - /// STEP 2: Detect marker candidates - vector< vector< Point2f > > candidates; - vector< vector< Point > > contours; - vector< int > ids; - - vector< vector< vector< Point2f > > > candidatesSet; - vector< vector< vector< Point > > > contoursSet; - - /// STEP 2.a Detect marker candidates :: using AprilTag - if(params->cornerRefinementMethod == CORNER_REFINE_APRILTAG){ - _apriltag(grey, params, candidates, contours); - - candidatesSet.push_back(candidates); - contoursSet.push_back(contours); - } - /// STEP 2.b Detect marker candidates :: traditional way - else - _detectCandidates(grey, candidatesSet, contoursSet, params); - - /// STEP 2: Check candidate codification (identify markers) - _identifyCandidates(grey, grey_pyramid, candidatesSet, contoursSet, dictionary, - candidates, contours, ids, params, _rejectedImgPoints); - - /// STEP 3: Corner refinement :: use corner subpix - if( params->cornerRefinementMethod == CORNER_REFINE_SUBPIX ) { - CV_Assert(params->cornerRefinementWinSize > 0 && params->cornerRefinementMaxIterations > 0 && - params->cornerRefinementMinAccuracy > 0); - // Do subpixel estimation. In Aruco3 start on the lowest pyramid level and upscale the corners - parallel_for_(Range(0, (int)candidates.size()), [&](const Range& range) { - const int begin = range.start; - const int end = range.end; - - for (int i = begin; i < end; i++) { - if (params->useAruco3Detection) { - const float scale_init = (float) grey_pyramid[closest_pyr_image_idx].cols / grey.cols; - findCornerInPyrImage(scale_init, closest_pyr_image_idx, grey_pyramid, Mat(candidates[i]), params); - } - else - cornerSubPix(grey, Mat(candidates[i]), - Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize), - Size(-1, -1), - TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - params->cornerRefinementMaxIterations, - params->cornerRefinementMinAccuracy)); - } - }); - } - - /// STEP 3, Optional : Corner refinement :: use contour container - if( params->cornerRefinementMethod == CORNER_REFINE_CONTOUR){ - - if(! _ids.empty()){ - - // do corner refinement using the contours for each detected markers - parallel_for_(Range(0, (int)candidates.size()), [&](const Range& range) { - for (int i = range.start; i < range.end; i++) { - _refineCandidateLines(contours[i], candidates[i]); - } - }); - } - } - - if (params->cornerRefinementMethod != CORNER_REFINE_SUBPIX && fxfy != 1.f) { - // only CORNER_REFINE_SUBPIX implement correctly for useAruco3Detection - // Todo: update other CORNER_REFINE methods - - // scale to orignal size, this however will lead to inaccurate detections! - for (auto &vecPoints : candidates) - for (auto &point : vecPoints) - point *= 1.f/fxfy; - } - - // copy to output arrays - _copyVector2Output(candidates, _corners); - Mat(ids).copyTo(_ids); -} - -/** - * Project board markers that are not included in the list of detected markers - */ -static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArrayOfArrays _detectedCorners, - InputOutputArray _detectedIds, InputArray _cameraMatrix, InputArray _distCoeffs, - vector >& _undetectedMarkersProjectedCorners, - OutputArray _undetectedMarkersIds) { - // first estimate board pose with the current avaible markers - Mat rvec, tvec; - int boardDetectedMarkers = aruco::estimatePoseBoard(_detectedCorners, _detectedIds, _board, - _cameraMatrix, _distCoeffs, rvec, tvec); - - // at least one marker from board so rvec and tvec are valid - if(boardDetectedMarkers == 0) return; - - // search undetected markers and project them using the previous pose - vector > undetectedCorners; - vector undetectedIds; - for(unsigned int i = 0; i < _board->getIds().size(); i++) { - int foundIdx = -1; - for(unsigned int j = 0; j < _detectedIds.total(); j++) { - if(_board->getIds()[i] == _detectedIds.getMat().ptr< int >()[j]) { - foundIdx = j; - break; - } - } - - // not detected - if(foundIdx == -1) { - undetectedCorners.push_back(vector()); - undetectedIds.push_back(_board->getIds()[i]); - projectPoints(_board->getObjPoints()[i], rvec, tvec, _cameraMatrix, _distCoeffs, - undetectedCorners.back()); - } - } - // parse output - Mat(undetectedIds).copyTo(_undetectedMarkersIds); - _undetectedMarkersProjectedCorners = undetectedCorners; -} - -/** - * Interpolate board markers that are not included in the list of detected markers using - * global homography - */ -static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArrayOfArrays _detectedCorners, - InputOutputArray _detectedIds, - vector >& _undetectedMarkersProjectedCorners, - OutputArray _undetectedMarkersIds) { - // check board points are in the same plane, if not, global homography cannot be applied - CV_Assert(_board->getObjPoints().size() > 0); - CV_Assert(_board->getObjPoints()[0].size() > 0); - float boardZ = _board->getObjPoints()[0][0].z; - for(unsigned int i = 0; i < _board->getObjPoints().size(); i++) { - for(unsigned int j = 0; j < _board->getObjPoints()[i].size(); j++) - CV_Assert(boardZ == _board->getObjPoints()[i][j].z); - } - - vector detectedMarkersObj2DAll; // Object coordinates (without Z) of all the detected - // marker corners in a single vector - vector imageCornersAll; // Image corners of all detected markers in a single vector - vector > undetectedMarkersObj2D; // Object coordinates (without Z) of all - // missing markers in different vectors - vector undetectedMarkersIds; // ids of missing markers - // find markers included in board, and missing markers from board. Fill the previous vectors - for(unsigned int j = 0; j < _board->getIds().size(); j++) { - bool found = false; - for(unsigned int i = 0; i < _detectedIds.total(); i++) { - if(_detectedIds.getMat().ptr< int >()[i] == _board->getIds()[j]) { - for(int c = 0; c < 4; c++) { - imageCornersAll.push_back(_detectedCorners.getMat(i).ptr< Point2f >()[c]); - detectedMarkersObj2DAll.push_back( - Point2f(_board->getObjPoints()[j][c].x, _board->getObjPoints()[j][c].y)); - } - found = true; - break; - } - } - if(!found) { - undetectedMarkersObj2D.push_back(vector()); - for(int c = 0; c < 4; c++) { - undetectedMarkersObj2D.back().push_back( - Point2f(_board->getObjPoints()[j][c].x, _board->getObjPoints()[j][c].y)); - } - undetectedMarkersIds.push_back(_board->getIds()[j]); - } - } - if(imageCornersAll.size() == 0) return; - - // get homography from detected markers - Mat transformation = findHomography(detectedMarkersObj2DAll, imageCornersAll); - - _undetectedMarkersProjectedCorners.resize(undetectedMarkersIds.size()); - - // for each undetected marker, apply transformation - for(unsigned int i = 0; i < undetectedMarkersObj2D.size(); i++) { - perspectiveTransform(undetectedMarkersObj2D[i], _undetectedMarkersProjectedCorners[i], transformation); - } - Mat(undetectedMarkersIds).copyTo(_undetectedMarkersIds); -} - - -void ArucoDetector::refineDetectedMarkers(InputArray _image, const Ptr &_board, - InputOutputArrayOfArrays _detectedCorners, InputOutputArray _detectedIds, - InputOutputArrayOfArrays _rejectedCorners, InputArray _cameraMatrix, - InputArray _distCoeffs, OutputArray _recoveredIdxs) { - CV_Assert(refineParams->minRepDistance > 0); - - if(_detectedIds.total() == 0 || _rejectedCorners.total() == 0) return; - - // get projections of missing markers in the board - vector< vector< Point2f > > undetectedMarkersCorners; - vector< int > undetectedMarkersIds; - if(_cameraMatrix.total() != 0) { - // reproject based on camera projection model - _projectUndetectedMarkers(_board, _detectedCorners, _detectedIds, _cameraMatrix, _distCoeffs, - undetectedMarkersCorners, undetectedMarkersIds); - - } else { - // reproject based on global homography - _projectUndetectedMarkers(_board, _detectedCorners, _detectedIds, undetectedMarkersCorners, - undetectedMarkersIds); - } - - // list of missing markers indicating if they have been assigned to a candidate - vector< bool > alreadyIdentified(_rejectedCorners.total(), false); - - // maximum bits that can be corrected - int maxCorrectionRecalculated = - int(double(dictionary->maxCorrectionBits) * refineParams->errorCorrectionRate); - - Mat grey; - _convertToGrey(_image, grey); - - // vector of final detected marker corners and ids - vector > finalAcceptedCorners; - vector< int > finalAcceptedIds; - // fill with the current markers - finalAcceptedCorners.resize(_detectedCorners.total()); - finalAcceptedIds.resize(_detectedIds.total()); - for(unsigned int i = 0; i < _detectedIds.total(); i++) { - finalAcceptedCorners[i] = _detectedCorners.getMat(i).clone(); - finalAcceptedIds[i] = _detectedIds.getMat().ptr< int >()[i]; - } - vector< int > recoveredIdxs; // original indexes of accepted markers in _rejectedCorners - - // for each missing marker, try to find a correspondence - for(unsigned int i = 0; i < undetectedMarkersIds.size(); i++) { - - // best match at the moment - int closestCandidateIdx = -1; - double closestCandidateDistance = refineParams->minRepDistance * refineParams->minRepDistance + 1; - Mat closestRotatedMarker; - - for(unsigned int j = 0; j < _rejectedCorners.total(); j++) { - if(alreadyIdentified[j]) continue; - - // check distance - double minDistance = closestCandidateDistance + 1; - bool valid = false; - int validRot = 0; - for(int c = 0; c < 4; c++) { // first corner in rejected candidate - double currentMaxDistance = 0; - for(int k = 0; k < 4; k++) { - Point2f rejCorner = _rejectedCorners.getMat(j).ptr< Point2f >()[(c + k) % 4]; - Point2f distVector = undetectedMarkersCorners[i][k] - rejCorner; - double cornerDist = distVector.x * distVector.x + distVector.y * distVector.y; - currentMaxDistance = max(currentMaxDistance, cornerDist); - } - // if distance is better than current best distance - if(currentMaxDistance < closestCandidateDistance) { - valid = true; - validRot = c; - minDistance = currentMaxDistance; - } - if(!refineParams->checkAllOrders) break; - } - - if(!valid) continue; - - // apply rotation - Mat rotatedMarker; - if(refineParams->checkAllOrders) { - rotatedMarker = Mat(4, 1, CV_32FC2); - for(int c = 0; c < 4; c++) - rotatedMarker.ptr< Point2f >()[c] = - _rejectedCorners.getMat(j).ptr< Point2f >()[(c + 4 + validRot) % 4]; - } - else rotatedMarker = _rejectedCorners.getMat(j); - - // last filter, check if inner code is close enough to the assigned marker code - int codeDistance = 0; - // if errorCorrectionRate, dont check code - if(refineParams->errorCorrectionRate >= 0) { - - // extract bits - Mat bits = _extractBits( - grey, rotatedMarker, dictionary->markerSize, params->markerBorderBits, - params->perspectiveRemovePixelPerCell, - params->perspectiveRemoveIgnoredMarginPerCell, params->minOtsuStdDev); - - Mat onlyBits = - bits.rowRange(params->markerBorderBits, bits.rows - params->markerBorderBits) - .colRange(params->markerBorderBits, bits.rows - params->markerBorderBits); - - codeDistance = - dictionary->getDistanceToId(onlyBits, undetectedMarkersIds[i], false); - } - - // if everythin is ok, assign values to current best match - if(refineParams->errorCorrectionRate < 0 || codeDistance < maxCorrectionRecalculated) { - closestCandidateIdx = j; - closestCandidateDistance = minDistance; - closestRotatedMarker = rotatedMarker; - } - } - - // if at least one good match, we have rescue the missing marker - if(closestCandidateIdx >= 0) { - - // subpixel refinement - if(params->cornerRefinementMethod == CORNER_REFINE_SUBPIX) { - CV_Assert(params->cornerRefinementWinSize > 0 && - params->cornerRefinementMaxIterations > 0 && - params->cornerRefinementMinAccuracy > 0); - cornerSubPix(grey, closestRotatedMarker, - Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize), - Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - params->cornerRefinementMaxIterations, - params->cornerRefinementMinAccuracy)); - } - - // remove from rejected - alreadyIdentified[closestCandidateIdx] = true; - - // add to detected - finalAcceptedCorners.push_back(closestRotatedMarker); - finalAcceptedIds.push_back(undetectedMarkersIds[i]); - - // add the original index of the candidate - recoveredIdxs.push_back(closestCandidateIdx); - } - } - - // parse output - if(finalAcceptedIds.size() != _detectedIds.total()) { - // parse output - Mat(finalAcceptedIds).copyTo(_detectedIds); - _copyVector2Output(finalAcceptedCorners, _detectedCorners); - - // recalculate _rejectedCorners based on alreadyIdentified - vector > finalRejected; - for(unsigned int i = 0; i < alreadyIdentified.size(); i++) { - if(!alreadyIdentified[i]) { - finalRejected.push_back(_rejectedCorners.getMat(i).clone()); - } - } - _copyVector2Output(finalRejected, _rejectedCorners); - - if(_recoveredIdxs.needed()) { - Mat(recoveredIdxs).copyTo(_recoveredIdxs); - } - } -} - - -void drawDetectedMarkers(InputOutputArray _image, InputArrayOfArrays _corners, - InputArray _ids, Scalar borderColor) { - CV_Assert(_image.getMat().total() != 0 && - (_image.getMat().channels() == 1 || _image.getMat().channels() == 3)); - CV_Assert((_corners.total() == _ids.total()) || _ids.total() == 0); - - // calculate colors - Scalar textColor, cornerColor; - textColor = cornerColor = borderColor; - swap(textColor.val[0], textColor.val[1]); // text color just sawp G and R - swap(cornerColor.val[1], cornerColor.val[2]); // corner color just sawp G and B - - int nMarkers = (int)_corners.total(); - for(int i = 0; i < nMarkers; i++) { - Mat currentMarker = _corners.getMat(i); - CV_Assert(currentMarker.total() == 4 && currentMarker.type() == CV_32FC2); - - // draw marker sides - for(int j = 0; j < 4; j++) { - Point2f p0, p1; - p0 = currentMarker.ptr< Point2f >(0)[j]; - p1 = currentMarker.ptr< Point2f >(0)[(j + 1) % 4]; - line(_image, p0, p1, borderColor, 1); - } - // draw first corner mark - rectangle(_image, currentMarker.ptr< Point2f >(0)[0] - Point2f(3, 3), - currentMarker.ptr< Point2f >(0)[0] + Point2f(3, 3), cornerColor, 1, LINE_AA); - - // draw ID - if(_ids.total() != 0) { - Point2f cent(0, 0); - for(int p = 0; p < 4; p++) - cent += currentMarker.ptr< Point2f >(0)[p]; - cent = cent / 4.; - stringstream s; - s << "id=" << _ids.getMat().ptr< int >(0)[i]; - putText(_image, s.str(), cent, FONT_HERSHEY_SIMPLEX, 0.5, textColor, 2); - } - } -} - -void drawMarker(const Ptr &dictionary, int id, int sidePixels, OutputArray _img, int borderBits) { - dictionary->drawMarker(id, sidePixels, _img, borderBits); -} - -} -} diff --git a/modules/aruco/src/aruco_utils.cpp b/modules/aruco/src/aruco_utils.cpp deleted file mode 100644 index 8d680848ca..0000000000 --- a/modules/aruco/src/aruco_utils.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html - -#include "aruco_utils.hpp" -#include - -namespace cv { -namespace aruco { -using namespace std; - -void _copyVector2Output(std::vector > &vec, OutputArrayOfArrays out, const float scale) { - out.create((int)vec.size(), 1, CV_32FC2); - if(out.isMatVector()) { - for (unsigned int i = 0; i < vec.size(); i++) { - out.create(4, 1, CV_32FC2, i); - Mat &m = out.getMatRef(i); - Mat(Mat(vec[i]).t()*scale).copyTo(m); - } - } - else if(out.isUMatVector()) { - for (unsigned int i = 0; i < vec.size(); i++) { - out.create(4, 1, CV_32FC2, i); - UMat &m = out.getUMatRef(i); - Mat(Mat(vec[i]).t()*scale).copyTo(m); - } - } - else if(out.kind() == _OutputArray::STD_VECTOR_VECTOR){ - for (unsigned int i = 0; i < vec.size(); i++) { - out.create(4, 1, CV_32FC2, i); - Mat m = out.getMat(i); - Mat(Mat(vec[i]).t()*scale).copyTo(m); - } - } - else { - CV_Error(cv::Error::StsNotImplemented, - "Only Mat vector, UMat vector, and vector OutputArrays are currently supported."); - } -} - -void _convertToGrey(InputArray _in, OutputArray _out) { - CV_Assert(_in.type() == CV_8UC1 || _in.type() == CV_8UC3); - if(_in.type() == CV_8UC3) - cvtColor(_in, _out, COLOR_BGR2GRAY); - else - _in.copyTo(_out); -} - -} -} diff --git a/modules/aruco/src/aruco_utils.hpp b/modules/aruco/src/aruco_utils.hpp deleted file mode 100644 index 029cd3fa81..0000000000 --- a/modules/aruco/src/aruco_utils.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html -#ifndef __OPENCV_ARUCO_UTILS_HPP__ -#define __OPENCV_ARUCO_UTILS_HPP__ - -#include -#include - -namespace cv { -namespace aruco { - -/** - * @brief Copy the contents of a corners vector to an OutputArray, settings its size. - */ -void _copyVector2Output(std::vector > &vec, OutputArrayOfArrays out, const float scale = 1.f); - -/** - * @brief Convert input image to gray if it is a 3-channels image - */ -void _convertToGrey(InputArray _in, OutputArray _out); - -template -inline bool readParameter(const std::string& name, T& parameter, const FileNode& node) -{ - if (!node.empty() && !node[name].empty()) { - node[name] >> parameter; - return true; - } - return false; -} - -template -inline bool readWriteParameter(const std::string& name, T& parameter, const Ptr readNode = nullptr, - const Ptr writeStorage = nullptr) { - if (!readNode.empty()) - return readParameter(name, parameter, *readNode); - *writeStorage << name << parameter; - return true; -} - -} -} -#endif diff --git a/modules/aruco/src/board.cpp b/modules/aruco/src/board.cpp deleted file mode 100644 index 53352f35f5..0000000000 --- a/modules/aruco/src/board.cpp +++ /dev/null @@ -1,467 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html - -#include -#include -#include - -namespace cv { -namespace aruco { -using namespace std; - -/** @brief Implementation of drawPlanarBoard that accepts a raw Board pointer. - */ -static void _drawPlanarBoardImpl(Board *_board, Size outSize, OutputArray _img, int marginSize, int borderBits) { - CV_Assert(!outSize.empty()); - CV_Assert(marginSize >= 0); - - _img.create(outSize, CV_8UC1); - Mat out = _img.getMat(); - out.setTo(Scalar::all(255)); - out.adjustROI(-marginSize, -marginSize, -marginSize, -marginSize); - - // calculate max and min values in XY plane - CV_Assert(_board->getObjPoints().size() > 0); - float minX, maxX, minY, maxY; - minX = maxX = _board->getObjPoints()[0][0].x; - minY = maxY = _board->getObjPoints()[0][0].y; - - for(unsigned int i = 0; i < _board->getObjPoints().size(); i++) { - for(int j = 0; j < 4; j++) { - minX = min(minX, _board->getObjPoints()[i][j].x); - maxX = max(maxX, _board->getObjPoints()[i][j].x); - minY = min(minY, _board->getObjPoints()[i][j].y); - maxY = max(maxY, _board->getObjPoints()[i][j].y); - } - } - - float sizeX = maxX - minX; - float sizeY = maxY - minY; - - // proportion transformations - float xReduction = sizeX / float(out.cols); - float yReduction = sizeY / float(out.rows); - - // determine the zone where the markers are placed - if(xReduction > yReduction) { - int nRows = int(sizeY / xReduction); - int rowsMargins = (out.rows - nRows) / 2; - out.adjustROI(-rowsMargins, -rowsMargins, 0, 0); - } else { - int nCols = int(sizeX / yReduction); - int colsMargins = (out.cols - nCols) / 2; - out.adjustROI(0, 0, -colsMargins, -colsMargins); - } - - // now paint each marker - Dictionary &dictionary = *(_board->getDictionary()); - Mat marker; - Point2f outCorners[3]; - Point2f inCorners[3]; - for(unsigned int m = 0; m < _board->getObjPoints().size(); m++) { - // transform corners to markerZone coordinates - for(int j = 0; j < 3; j++) { - Point2f pf = Point2f(_board->getObjPoints()[m][j].x, _board->getObjPoints()[m][j].y); - // move top left to 0, 0 - pf -= Point2f(minX, minY); - pf.x = pf.x / sizeX * float(out.cols); - pf.y = pf.y / sizeY * float(out.rows); - outCorners[j] = pf; - } - - // get marker - Size dst_sz(outCorners[2] - outCorners[0]); // assuming CCW order - dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square - dictionary.drawMarker(_board->getIds()[m], dst_sz.width, marker, borderBits); - - if((outCorners[0].y == outCorners[1].y) && (outCorners[1].x == outCorners[2].x)) { - // marker is aligned to image axes - marker.copyTo(out(Rect(outCorners[0], dst_sz))); - continue; - } - - // interpolate tiny marker to marker position in markerZone - inCorners[0] = Point2f(-0.5f, -0.5f); - inCorners[1] = Point2f(marker.cols - 0.5f, -0.5f); - inCorners[2] = Point2f(marker.cols - 0.5f, marker.rows - 0.5f); - - // remove perspective - Mat transformation = getAffineTransform(inCorners, outCorners); - warpAffine(marker, out, transformation, out.size(), INTER_LINEAR, - BORDER_TRANSPARENT); - } -} - -void drawPlanarBoard(const Ptr &_board, Size outSize, OutputArray _img, int marginSize, - int borderBits) { - _drawPlanarBoardImpl(_board, outSize, _img, marginSize, borderBits); -} - -struct GridBoard::GridImpl { - GridImpl(){}; - // number of markers in X and Y directions - int sizeX = 3, sizeY = 3; - - // marker side length (normally in meters) - float markerLength = 1.f; - - // separation between markers in the grid - float markerSeparation = .5f; -}; - -GridBoard::GridBoard(): gridImpl(makePtr()) {} - -Board::Board(): dictionary(makePtr(getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME::DICT_4X4_50))) {} - -Ptr Board::create(InputArrayOfArrays objPoints, const Ptr &dictionary, InputArray ids) { - CV_Assert(objPoints.total() == ids.total()); - CV_Assert(objPoints.type() == CV_32FC3 || objPoints.type() == CV_32FC1); - - std::vector > obj_points_vector; - Point3f rightBottomBorder = Point3f(0.f, 0.f, 0.f); - for (unsigned int i = 0; i < objPoints.total(); i++) { - std::vector corners; - Mat corners_mat = objPoints.getMat(i); - - if (corners_mat.type() == CV_32FC1) - corners_mat = corners_mat.reshape(3); - CV_Assert(corners_mat.total() == 4); - - for (int j = 0; j < 4; j++) { - const Point3f &corner = corners_mat.at(j); - corners.push_back(corner); - rightBottomBorder.x = std::max(rightBottomBorder.x, corner.x); - rightBottomBorder.y = std::max(rightBottomBorder.y, corner.y); - rightBottomBorder.z = std::max(rightBottomBorder.z, corner.z); - } - obj_points_vector.push_back(corners); - } - Ptr res = makePtr(); - ids.copyTo(res->ids); - res->objPoints = obj_points_vector; - res->dictionary = cv::makePtr(dictionary); - res->rightBottomBorder = rightBottomBorder; - return res; -} - -void Board::setIds(InputArray ids_) { - CV_Assert(objPoints.size() == ids_.total()); - ids_.copyTo(this->ids); -} - -Ptr Board::getDictionary() const { - return this->dictionary; -} - -void Board::setDictionary(const Ptr &_dictionary) { - this->dictionary = _dictionary; -} - -const std::vector >& Board::getObjPoints() const { - return this->objPoints; -} - -void Board::setObjPoints(const vector> &_objPoints) { - CV_Assert(!_objPoints.empty()); - this->objPoints = _objPoints; - rightBottomBorder = _objPoints.front().front(); - for (size_t i = 0; i < this->objPoints.size(); i++) { - for (int j = 0; j < 4; j++) { - const Point3f &corner = this->objPoints[i][j]; - rightBottomBorder.x = std::max(rightBottomBorder.x, corner.x); - rightBottomBorder.y = std::max(rightBottomBorder.y, corner.y); - rightBottomBorder.z = std::max(rightBottomBorder.z, corner.z); - } - } -} - -const Point3f& Board::getRightBottomBorder() const { - return this->rightBottomBorder; -} - -const std::vector& Board::getIds() const { - return this->ids; -} - -void Board::changeId(int index, int newId) { - CV_Assert(index >= 0 && index < (int)ids.size()); - CV_Assert(newId >= 0 && newId < dictionary->bytesList.rows); - this->ids[index] = newId; -} - -Ptr GridBoard::create(int markersX, int markersY, float markerLength, float markerSeparation, - const Ptr &dictionary, int firstMarker) { - CV_Assert(markersX > 0 && markersY > 0 && markerLength > 0 && markerSeparation > 0); - Ptr res = makePtr(); - res->gridImpl->sizeX = markersX; - res->gridImpl->sizeY = markersY; - res->gridImpl->markerLength = markerLength; - res->gridImpl->markerSeparation = markerSeparation; - res->setDictionary(dictionary); - - size_t totalMarkers = (size_t) markersX * markersY; - res->ids.resize(totalMarkers); - std::vector > objPoints; - objPoints.reserve(totalMarkers); - - // fill ids with first identifiers - for (unsigned int i = 0; i < totalMarkers; i++) { - res->ids[i] = i + firstMarker; - } - - // calculate Board objPoints - for (int y = 0; y < markersY; y++) { - for (int x = 0; x < markersX; x++) { - vector corners(4); - corners[0] = Point3f(x * (markerLength + markerSeparation), - y * (markerLength + markerSeparation), 0); - corners[1] = corners[0] + Point3f(markerLength, 0, 0); - corners[2] = corners[0] + Point3f(markerLength, markerLength, 0); - corners[3] = corners[0] + Point3f(0, markerLength, 0); - objPoints.push_back(corners); - } - } - res->setObjPoints(objPoints); - res->rightBottomBorder = Point3f(markersX * markerLength + markerSeparation * (markersX - 1), - markersY * markerLength + markerSeparation * (markersY - 1), 0.f); - return res; -} - -void GridBoard::draw(Size outSize, OutputArray _img, int marginSize, int borderBits) { - _drawPlanarBoardImpl((Board*)this, outSize, _img, marginSize, borderBits); -} - -Size GridBoard::getGridSize() const { - return Size(gridImpl->sizeX, gridImpl->sizeY); -} - -float GridBoard::getMarkerLength() const { - return gridImpl->markerLength; -} - -float GridBoard::getMarkerSeparation() const { - return gridImpl->markerSeparation; -} - -struct CharucoBoard::CharucoImpl : GridBoard::GridImpl { - // size of chessboard squares side (normally in meters) - float squareLength; - - // marker side length (normally in meters) - float markerLength; -}; - -CharucoBoard::CharucoBoard(): charucoImpl(makePtr()) {} - -void CharucoBoard::draw(Size outSize, OutputArray _img, int marginSize, int borderBits) { - CV_Assert(!outSize.empty()); - CV_Assert(marginSize >= 0); - - _img.create(outSize, CV_8UC1); - _img.setTo(255); - Mat out = _img.getMat(); - Mat noMarginsImg = - out.colRange(marginSize, out.cols - marginSize).rowRange(marginSize, out.rows - marginSize); - - double totalLengthX, totalLengthY; - totalLengthX = charucoImpl->squareLength * charucoImpl->sizeX; - totalLengthY = charucoImpl->squareLength * charucoImpl->sizeY; - - // proportional transformation - double xReduction = totalLengthX / double(noMarginsImg.cols); - double yReduction = totalLengthY / double(noMarginsImg.rows); - - // determine the zone where the chessboard is placed - Mat chessboardZoneImg; - if(xReduction > yReduction) { - int nRows = int(totalLengthY / xReduction); - int rowsMargins = (noMarginsImg.rows - nRows) / 2; - chessboardZoneImg = noMarginsImg.rowRange(rowsMargins, noMarginsImg.rows - rowsMargins); - } else { - int nCols = int(totalLengthX / yReduction); - int colsMargins = (noMarginsImg.cols - nCols) / 2; - chessboardZoneImg = noMarginsImg.colRange(colsMargins, noMarginsImg.cols - colsMargins); - } - - // determine the margins to draw only the markers - // take the minimum just to be sure - double squareSizePixels = min(double(chessboardZoneImg.cols) / double(charucoImpl->sizeX), - double(chessboardZoneImg.rows) / double(charucoImpl->sizeY)); - - double diffSquareMarkerLength = (charucoImpl->squareLength - charucoImpl->markerLength) / 2; - int diffSquareMarkerLengthPixels = - int(diffSquareMarkerLength * squareSizePixels / charucoImpl->squareLength); - - // draw markers - Mat markersImg; - _drawPlanarBoardImpl(this, chessboardZoneImg.size(), markersImg, diffSquareMarkerLengthPixels, borderBits); - markersImg.copyTo(chessboardZoneImg); - - // now draw black squares - for(int y = 0; y < charucoImpl->sizeY; y++) { - for(int x = 0; x < charucoImpl->sizeX; x++) { - - if(y % 2 != x % 2) continue; // white corner, dont do anything - - double startX, startY; - startX = squareSizePixels * double(x); - startY = squareSizePixels * double(y); - - Mat squareZone = chessboardZoneImg.rowRange(int(startY), int(startY + squareSizePixels)) - .colRange(int(startX), int(startX + squareSizePixels)); - - squareZone.setTo(0); - } - } -} - -/** - * Fill nearestMarkerIdx and nearestMarkerCorners arrays - */ -static inline void _getNearestMarkerCorners(CharucoBoard &board, float squareLength) { - board.nearestMarkerIdx.resize(board.chessboardCorners.size()); - board.nearestMarkerCorners.resize(board.chessboardCorners.size()); - - unsigned int nMarkers = (unsigned int)board.getIds().size(); - unsigned int nCharucoCorners = (unsigned int)board.chessboardCorners.size(); - for(unsigned int i = 0; i < nCharucoCorners; i++) { - double minDist = -1; // distance of closest markers - Point3f charucoCorner = board.chessboardCorners[i]; - for(unsigned int j = 0; j < nMarkers; j++) { - // calculate distance from marker center to charuco corner - Point3f center = Point3f(0, 0, 0); - for(unsigned int k = 0; k < 4; k++) - center += board.getObjPoints()[j][k]; - center /= 4.; - double sqDistance; - Point3f distVector = charucoCorner - center; - sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; - if(j == 0 || fabs(sqDistance - minDist) < cv::pow(0.01 * squareLength, 2)) { - // if same minimum distance (or first iteration), add to nearestMarkerIdx vector - board.nearestMarkerIdx[i].push_back(j); - minDist = sqDistance; - } else if(sqDistance < minDist) { - // if finding a closest marker to the charuco corner - board.nearestMarkerIdx[i].clear(); // remove any previous added marker - board.nearestMarkerIdx[i].push_back(j); // add the new closest marker index - minDist = sqDistance; - } - } - // for each of the closest markers, search the marker corner index closer - // to the charuco corner - for(unsigned int j = 0; j < board.nearestMarkerIdx[i].size(); j++) { - board.nearestMarkerCorners[i].resize(board.nearestMarkerIdx[i].size()); - double minDistCorner = -1; - for(unsigned int k = 0; k < 4; k++) { - double sqDistance; - Point3f distVector = charucoCorner - board.getObjPoints()[board.nearestMarkerIdx[i][j]][k]; - sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; - if(k == 0 || sqDistance < minDistCorner) { - // if this corner is closer to the charuco corner, assing its index - // to nearestMarkerCorners - minDistCorner = sqDistance; - board.nearestMarkerCorners[i][j] = k; - } - } - } - } -} - -Ptr CharucoBoard::create(int squaresX, int squaresY, float squareLength, - float markerLength, const Ptr &dictionary) { - CV_Assert(squaresX > 1 && squaresY > 1 && markerLength > 0 && squareLength > markerLength); - Ptr res = makePtr(); - - res->charucoImpl->sizeX = squaresX; - res->charucoImpl->sizeY = squaresY; - res->charucoImpl->squareLength = squareLength; - res->charucoImpl->markerLength = markerLength; - res->setDictionary(dictionary); - std::vector > objPoints; - - float diffSquareMarkerLength = (squareLength - markerLength) / 2; - // calculate Board objPoints - for(int y = 0; y < squaresY; y++) { - for(int x = 0; x < squaresX; x++) { - - if(y % 2 == x % 2) continue; // black corner, no marker here - - vector corners(4); - corners[0] = Point3f(x * squareLength + diffSquareMarkerLength, - y * squareLength + diffSquareMarkerLength, 0); - corners[1] = corners[0] + Point3f(markerLength, 0, 0); - corners[2] = corners[0] + Point3f(markerLength, markerLength, 0); - corners[3] = corners[0] + Point3f(0, markerLength, 0); - objPoints.push_back(corners); - // first ids in dictionary - int nextId = (int)res->ids.size(); - res->ids.push_back(nextId); - } - } - res->setObjPoints(objPoints); - - // now fill chessboardCorners - for(int y = 0; y < squaresY - 1; y++) { - for(int x = 0; x < squaresX - 1; x++) { - Point3f corner; - corner.x = (x + 1) * squareLength; - corner.y = (y + 1) * squareLength; - corner.z = 0; - res->chessboardCorners.push_back(corner); - } - } - res->rightBottomBorder = Point3f(squaresX * squareLength, - squaresY * squareLength, 0.f); - _getNearestMarkerCorners(*res, res->charucoImpl->squareLength); - return res; -} - -Size CharucoBoard::getChessboardSize() const { return Size(charucoImpl->sizeX, charucoImpl->sizeY); } - -float CharucoBoard::getSquareLength() const { return charucoImpl->squareLength; } - -float CharucoBoard::getMarkerLength() const { return charucoImpl->markerLength; } - -bool testCharucoCornersCollinear(const Ptr &_board, InputArray _charucoIds) { - unsigned int nCharucoCorners = (unsigned int)_charucoIds.getMat().total(); - if (nCharucoCorners <= 2) - return true; - - // only test if there are 3 or more corners - CV_Assert( _board->chessboardCorners.size() >= _charucoIds.getMat().total()); - - Vec point0( _board->chessboardCorners[_charucoIds.getMat().at< int >(0)].x, - _board->chessboardCorners[_charucoIds.getMat().at< int >(0)].y, 1); - - Vec point1( _board->chessboardCorners[_charucoIds.getMat().at< int >(1)].x, - _board->chessboardCorners[_charucoIds.getMat().at< int >(1)].y, 1); - - // create a line from the first two points. - Vec testLine = point0.cross(point1); - Vec testPoint(0, 0, 1); - - double divisor = sqrt(testLine[0]*testLine[0] + testLine[1]*testLine[1]); - CV_Assert(divisor != 0.0); - - // normalize the line with normal - testLine /= divisor; - - double dotProduct; - for (unsigned int i = 2; i < nCharucoCorners; i++){ - testPoint(0) = _board->chessboardCorners[_charucoIds.getMat().at< int >(i)].x; - testPoint(1) = _board->chessboardCorners[_charucoIds.getMat().at< int >(i)].y; - - // if testPoint is on testLine, dotProduct will be zero (or very, very close) - dotProduct = testPoint.dot(testLine); - - if (std::abs(dotProduct) > 1e-6){ - return false; - } - } - // no points found that were off of testLine, return true that all points collinear. - return true; -} - -} -} diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index ba10a7eae4..730b51dd83 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -3,8 +3,8 @@ // of this distribution and at http://opencv.org/license.html #include "precomp.hpp" +#include #include "opencv2/aruco/charuco.hpp" -#include #include namespace cv { @@ -30,8 +30,8 @@ static int _filterCornersWithoutMinMarkers(const Ptr &_board, int currentCharucoId = _allCharucoIds.getMat().at< int >(i); int totalMarkers = 0; // nomber of closest marker detected // look for closest markers - for(unsigned int m = 0; m < _board->nearestMarkerIdx[currentCharucoId].size(); m++) { - int markerId = _board->getIds()[_board->nearestMarkerIdx[currentCharucoId][m]]; + for(unsigned int m = 0; m < _board->getNearestMarkerIdx()[currentCharucoId].size(); m++) { + int markerId = _board->getIds()[_board->getNearestMarkerIdx()[currentCharucoId][m]]; bool found = false; for(unsigned int k = 0; k < _allArucoIds.getMat().total(); k++) { if(_allArucoIds.getMat().at< int >(k) == markerId) { @@ -133,15 +133,15 @@ static void _getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, Input for(unsigned int i = 0; i < nCharucoCorners; i++) { if(charucoCorners.getMat().at< Point2f >(i) == Point2f(-1, -1)) continue; - if(board->nearestMarkerIdx[i].size() == 0) continue; + if(board->getNearestMarkerIdx()[i].size() == 0) continue; double minDist = -1; int counter = 0; // calculate the distance to each of the closest corner of each closest marker - for(unsigned int j = 0; j < board->nearestMarkerIdx[i].size(); j++) { + for(unsigned int j = 0; j < board->getNearestMarkerIdx()[i].size(); j++) { // find marker - int markerId = board->getIds()[board->nearestMarkerIdx[i][j]]; + int markerId = board->getIds()[board->getNearestMarkerIdx()[i][j]]; int markerIdx = -1; for(unsigned int k = 0; k < markerIds.getMat().total(); k++) { if(markerIds.getMat().at< int >(k) == markerId) { @@ -151,7 +151,7 @@ static void _getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, Input } if(markerIdx == -1) continue; Point2f markerCorner = - markerCorners.getMat(markerIdx).at< Point2f >(board->nearestMarkerCorners[i][j]); + markerCorners.getMat(markerIdx).at< Point2f >(board->getNearestMarkerCorners()[i][j]); Point2f charucoCorner = charucoCorners.getMat().at< Point2f >(i); double dist = norm(markerCorner - charucoCorner); if(minDist == -1) minDist = dist; // if first distance, just assign it @@ -200,7 +200,7 @@ static int _interpolateCornersCharucoApproxCalib(InputArrayOfArrays _markerCorne // project chessboard corners vector< Point2f > allChessboardImgPoints; - projectPoints(_board->chessboardCorners, approximatedRvec, approximatedTvec, _cameraMatrix, + projectPoints(_board->getChessboardCorners(), approximatedRvec, approximatedTvec, _cameraMatrix, _distCoeffs, allChessboardImgPoints); @@ -257,17 +257,17 @@ static int _interpolateCornersCharucoLocalHom(InputArrayOfArrays _markerCorners, validTransform[i] = std::abs(det) > 1e-6; } - unsigned int nCharucoCorners = (unsigned int)_board->chessboardCorners.size(); + unsigned int nCharucoCorners = (unsigned int)_board->getChessboardCorners().size(); vector< Point2f > allChessboardImgPoints(nCharucoCorners, Point2f(-1, -1)); // for each charuco corner, calculate its interpolation position based on the closest markers // homographies for(unsigned int i = 0; i < nCharucoCorners; i++) { - Point2f objPoint2D = Point2f(_board->chessboardCorners[i].x, _board->chessboardCorners[i].y); + Point2f objPoint2D = Point2f(_board->getChessboardCorners()[i].x, _board->getChessboardCorners()[i].y); vector< Point2f > interpolatedPositions; - for(unsigned int j = 0; j < _board->nearestMarkerIdx[i].size(); j++) { - int markerId = _board->getIds()[_board->nearestMarkerIdx[i][j]]; + for(unsigned int j = 0; j < _board->getNearestMarkerIdx()[i].size(); j++) { + int markerId = _board->getIds()[_board->getNearestMarkerIdx()[i][j]]; int markerIdx = -1; for(unsigned int k = 0; k < _markerIds.getMat().total(); k++) { if(_markerIds.getMat().at< int >(k) == markerId) { diff --git a/modules/aruco/src/dictionary.cpp b/modules/aruco/src/dictionary.cpp deleted file mode 100644 index 5672538f29..0000000000 --- a/modules/aruco/src/dictionary.cpp +++ /dev/null @@ -1,465 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html - -#include -#include "opencv2/core/hal/hal.hpp" - -#include "precomp.hpp" -#include "aruco_utils.hpp" -#include "predefined_dictionaries.hpp" -#include "apriltag/predefined_dictionaries_apriltag.hpp" -#include - -namespace cv { -namespace aruco { - -using namespace std; - - -Dictionary::Dictionary(const Ptr &_dictionary) { - markerSize = _dictionary->markerSize; - maxCorrectionBits = _dictionary->maxCorrectionBits; - bytesList = _dictionary->bytesList.clone(); -} - - -Dictionary::Dictionary(const Mat &_bytesList, int _markerSize, int _maxcorr) { - markerSize = _markerSize; - maxCorrectionBits = _maxcorr; - bytesList = _bytesList; -} - - -Ptr Dictionary::create(int nMarkers, int markerSize, int randomSeed) { - const Ptr baseDictionary = makePtr(); - return create(nMarkers, markerSize, baseDictionary, randomSeed); -} - - -Ptr Dictionary::create(int nMarkers, int markerSize, - const Ptr &baseDictionary, int randomSeed) { - return generateCustomDictionary(nMarkers, markerSize, baseDictionary, randomSeed); -} - - -bool Dictionary::readDictionary(const cv::FileNode& fn) { - int nMarkers = 0, _markerSize = 0; - if (fn.empty() || !readParameter("nmarkers", nMarkers, fn) || !readParameter("markersize", _markerSize, fn)) - return false; - Mat bytes(0, 0, CV_8UC1), marker(_markerSize, _markerSize, CV_8UC1); - std::string markerString; - for (int i = 0; i < nMarkers; i++) { - std::ostringstream ostr; - ostr << i; - if (!readParameter("marker_" + ostr.str(), markerString, fn)) - return false; - for (int j = 0; j < (int) markerString.size(); j++) - marker.at(j) = (markerString[j] == '0') ? 0 : 1; - bytes.push_back(Dictionary::getByteListFromBits(marker)); - } - int _maxCorrectionBits = 0; - readParameter("maxCorrectionBits", _maxCorrectionBits, fn); - *this = Dictionary(bytes, _markerSize, _maxCorrectionBits); - return true; -} - - -void Dictionary::writeDictionary(Ptr& fs) { - *fs << "nmarkers" << bytesList.rows; - *fs << "markersize" << markerSize; - *fs << "maxCorrectionBits" << maxCorrectionBits; - for (int i = 0; i < bytesList.rows; i++) { - Mat row = bytesList.row(i);; - Mat bitMarker = getBitsFromByteList(row, markerSize); - std::ostringstream ostr; - ostr << i; - string markerName = "marker_" + ostr.str(); - string marker; - for (int j = 0; j < markerSize * markerSize; j++) - marker.push_back(bitMarker.at(j) + '0'); - *fs << markerName << marker; - } -} - - -Ptr Dictionary::get(int dict) { - return getPredefinedDictionary(dict); -} - - -bool Dictionary::identify(const Mat &onlyBits, int &idx, int &rotation, double maxCorrectionRate) const { - CV_Assert(onlyBits.rows == markerSize && onlyBits.cols == markerSize); - - int maxCorrectionRecalculed = int(double(maxCorrectionBits) * maxCorrectionRate); - - // get as a byte list - Mat candidateBytes = getByteListFromBits(onlyBits); - - idx = -1; // by default, not found - - // search closest marker in dict - for(int m = 0; m < bytesList.rows; m++) { - int currentMinDistance = markerSize * markerSize + 1; - int currentRotation = -1; - for(unsigned int r = 0; r < 4; r++) { - int currentHamming = cv::hal::normHamming( - bytesList.ptr(m)+r*candidateBytes.cols, - candidateBytes.ptr(), - candidateBytes.cols); - - if(currentHamming < currentMinDistance) { - currentMinDistance = currentHamming; - currentRotation = r; - } - } - - // if maxCorrection is fulfilled, return this one - if(currentMinDistance <= maxCorrectionRecalculed) { - idx = m; - rotation = currentRotation; - break; - } - } - - return idx != -1; -} - - -int Dictionary::getDistanceToId(InputArray bits, int id, bool allRotations) const { - - CV_Assert(id >= 0 && id < bytesList.rows); - - unsigned int nRotations = 4; - if(!allRotations) nRotations = 1; - - Mat candidateBytes = getByteListFromBits(bits.getMat()); - int currentMinDistance = int(bits.total() * bits.total()); - for(unsigned int r = 0; r < nRotations; r++) { - int currentHamming = cv::hal::normHamming( - bytesList.ptr(id) + r*candidateBytes.cols, - candidateBytes.ptr(), - candidateBytes.cols); - - if(currentHamming < currentMinDistance) { - currentMinDistance = currentHamming; - } - } - return currentMinDistance; -} - - -void Dictionary::drawMarker(int id, int sidePixels, OutputArray _img, int borderBits) const { - CV_Assert(sidePixels >= (markerSize + 2*borderBits)); - CV_Assert(id < bytesList.rows); - CV_Assert(borderBits > 0); - - _img.create(sidePixels, sidePixels, CV_8UC1); - - // create small marker with 1 pixel per bin - Mat tinyMarker(markerSize + 2 * borderBits, markerSize + 2 * borderBits, CV_8UC1, - Scalar::all(0)); - Mat innerRegion = tinyMarker.rowRange(borderBits, tinyMarker.rows - borderBits) - .colRange(borderBits, tinyMarker.cols - borderBits); - // put inner bits - Mat bits = 255 * getBitsFromByteList(bytesList.rowRange(id, id + 1), markerSize); - CV_Assert(innerRegion.total() == bits.total()); - bits.copyTo(innerRegion); - - // resize tiny marker to output size - cv::resize(tinyMarker, _img.getMat(), _img.getMat().size(), 0, 0, INTER_NEAREST); -} - - -Mat Dictionary::getByteListFromBits(const Mat &bits) { - // integer ceil - int nbytes = (bits.cols * bits.rows + 8 - 1) / 8; - - Mat candidateByteList(1, nbytes, CV_8UC4, Scalar::all(0)); - unsigned char currentBit = 0; - int currentByte = 0; - - // the 4 rotations - uchar* rot0 = candidateByteList.ptr(); - uchar* rot1 = candidateByteList.ptr() + 1*nbytes; - uchar* rot2 = candidateByteList.ptr() + 2*nbytes; - uchar* rot3 = candidateByteList.ptr() + 3*nbytes; - - for(int row = 0; row < bits.rows; row++) { - for(int col = 0; col < bits.cols; col++) { - // circular shift - rot0[currentByte] <<= 1; - rot1[currentByte] <<= 1; - rot2[currentByte] <<= 1; - rot3[currentByte] <<= 1; - // set bit - rot0[currentByte] |= bits.at(row, col); - rot1[currentByte] |= bits.at(col, bits.cols - 1 - row); - rot2[currentByte] |= bits.at(bits.rows - 1 - row, bits.cols - 1 - col); - rot3[currentByte] |= bits.at(bits.rows - 1 - col, row); - currentBit++; - if(currentBit == 8) { - // next byte - currentBit = 0; - currentByte++; - } - } - } - return candidateByteList; -} - - -Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) { - CV_Assert(byteList.total() > 0 && - byteList.total() >= (unsigned int)markerSize * markerSize / 8 && - byteList.total() <= (unsigned int)markerSize * markerSize / 8 + 1); - Mat bits(markerSize, markerSize, CV_8UC1, Scalar::all(0)); - - unsigned char base2List[] = { 128, 64, 32, 16, 8, 4, 2, 1 }; - int currentByteIdx = 0; - // we only need the bytes in normal rotation - unsigned char currentByte = byteList.ptr()[0]; - int currentBit = 0; - for(int row = 0; row < bits.rows; row++) { - for(int col = 0; col < bits.cols; col++) { - if(currentByte >= base2List[currentBit]) { - bits.at< unsigned char >(row, col) = 1; - currentByte -= base2List[currentBit]; - } - currentBit++; - if(currentBit == 8) { - currentByteIdx++; - currentByte = byteList.ptr()[currentByteIdx]; - // if not enough bits for one more byte, we are in the end - // update bit position accordingly - if(8 * (currentByteIdx + 1) > (int)bits.total()) - currentBit = 8 * (currentByteIdx + 1) - (int)bits.total(); - else - currentBit = 0; // ok, bits enough for next byte - } - } - } - return bits; -} - - -Ptr getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME name) { - // DictionaryData constructors calls - // moved out of globals so construted on first use, which allows lazy-loading of opencv dll - static const Dictionary DICT_ARUCO_DATA = Dictionary(Mat(1024, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_ARUCO_BYTES), 5, 0); - - static const Dictionary DICT_4X4_50_DATA = Dictionary(Mat(50, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1); - static const Dictionary DICT_4X4_100_DATA = Dictionary(Mat(100, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1); - static const Dictionary DICT_4X4_250_DATA = Dictionary(Mat(250, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1); - static const Dictionary DICT_4X4_1000_DATA = Dictionary(Mat(1000, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 0); - - static const Dictionary DICT_5X5_50_DATA = Dictionary(Mat(50, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 3); - static const Dictionary DICT_5X5_100_DATA = Dictionary(Mat(100, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 3); - static const Dictionary DICT_5X5_250_DATA = Dictionary(Mat(250, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 2); - static const Dictionary DICT_5X5_1000_DATA = Dictionary(Mat(1000, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 2); - - static const Dictionary DICT_6X6_50_DATA = Dictionary(Mat(50, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 6); - static const Dictionary DICT_6X6_100_DATA = Dictionary(Mat(100, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 5); - static const Dictionary DICT_6X6_250_DATA = Dictionary(Mat(250, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 5); - static const Dictionary DICT_6X6_1000_DATA = Dictionary(Mat(1000, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 4); - - static const Dictionary DICT_7X7_50_DATA = Dictionary(Mat(50, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 9); - static const Dictionary DICT_7X7_100_DATA = Dictionary(Mat(100, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 8); - static const Dictionary DICT_7X7_250_DATA = Dictionary(Mat(250, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 8); - static const Dictionary DICT_7X7_1000_DATA = Dictionary(Mat(1000, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 6); - - static const Dictionary DICT_APRILTAG_16h5_DATA = Dictionary(Mat(30, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_16h5_BYTES), 4, 0); - static const Dictionary DICT_APRILTAG_25h9_DATA = Dictionary(Mat(35, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_25h9_BYTES), 5, 0); - static const Dictionary DICT_APRILTAG_36h10_DATA = Dictionary(Mat(2320, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h10_BYTES), 6, 0); - static const Dictionary DICT_APRILTAG_36h11_DATA = Dictionary(Mat(587, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h11_BYTES), 6, 0); - - switch(name) { - - case DICT_ARUCO_ORIGINAL: - return makePtr(DICT_ARUCO_DATA); - - case DICT_4X4_50: - return makePtr(DICT_4X4_50_DATA); - case DICT_4X4_100: - return makePtr(DICT_4X4_100_DATA); - case DICT_4X4_250: - return makePtr(DICT_4X4_250_DATA); - case DICT_4X4_1000: - return makePtr(DICT_4X4_1000_DATA); - - case DICT_5X5_50: - return makePtr(DICT_5X5_50_DATA); - case DICT_5X5_100: - return makePtr(DICT_5X5_100_DATA); - case DICT_5X5_250: - return makePtr(DICT_5X5_250_DATA); - case DICT_5X5_1000: - return makePtr(DICT_5X5_1000_DATA); - - case DICT_6X6_50: - return makePtr(DICT_6X6_50_DATA); - case DICT_6X6_100: - return makePtr(DICT_6X6_100_DATA); - case DICT_6X6_250: - return makePtr(DICT_6X6_250_DATA); - case DICT_6X6_1000: - return makePtr(DICT_6X6_1000_DATA); - - case DICT_7X7_50: - return makePtr(DICT_7X7_50_DATA); - case DICT_7X7_100: - return makePtr(DICT_7X7_100_DATA); - case DICT_7X7_250: - return makePtr(DICT_7X7_250_DATA); - case DICT_7X7_1000: - return makePtr(DICT_7X7_1000_DATA); - - case DICT_APRILTAG_16h5: - return makePtr(DICT_APRILTAG_16h5_DATA); - case DICT_APRILTAG_25h9: - return makePtr(DICT_APRILTAG_25h9_DATA); - case DICT_APRILTAG_36h10: - return makePtr(DICT_APRILTAG_36h10_DATA); - case DICT_APRILTAG_36h11: - return makePtr(DICT_APRILTAG_36h11_DATA); - - } - return makePtr(DICT_4X4_50_DATA); -} - - -Ptr getPredefinedDictionary(int dict) { - return getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME(dict)); -} - - -/** - * @brief Generates a random marker Mat of size markerSize x markerSize - */ -static Mat _generateRandomMarker(int markerSize, RNG &rng) { - Mat marker(markerSize, markerSize, CV_8UC1, Scalar::all(0)); - for(int i = 0; i < markerSize; i++) { - for(int j = 0; j < markerSize; j++) { - unsigned char bit = (unsigned char) (rng.uniform(0,2)); - marker.at< unsigned char >(i, j) = bit; - } - } - return marker; -} - -/** - * @brief Calculate selfDistance of the codification of a marker Mat. Self distance is the Hamming - * distance of the marker to itself in the other rotations. - * See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014. - * "Automatic generation and detection of highly reliable fiducial markers under occlusion". - * Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005 - */ -static int _getSelfDistance(const Mat &marker) { - Mat bytes = Dictionary::getByteListFromBits(marker); - int minHamming = (int)marker.total() + 1; - for(int r = 1; r < 4; r++) { - int currentHamming = cv::hal::normHamming(bytes.ptr(), bytes.ptr() + bytes.cols*r, bytes.cols); - if(currentHamming < minHamming) minHamming = currentHamming; - } - return minHamming; -} - - -Ptr generateCustomDictionary(int nMarkers, int markerSize, - const Ptr &baseDictionary, int randomSeed) { - RNG rng((uint64)(randomSeed)); - - Ptr out = makePtr(); - out->markerSize = markerSize; - - // theoretical maximum intermarker distance - // See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014. - // "Automatic generation and detection of highly reliable fiducial markers under occlusion". - // Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005 - int C = (int)std::floor(float(markerSize * markerSize) / 4.f); - int tau = 2 * (int)std::floor(float(C) * 4.f / 3.f); - - // if baseDictionary is provided, calculate its intermarker distance - if(baseDictionary->bytesList.rows > 0) { - CV_Assert(baseDictionary->markerSize == markerSize); - out->bytesList = baseDictionary->bytesList.clone(); - - int minDistance = markerSize * markerSize + 1; - for(int i = 0; i < out->bytesList.rows; i++) { - Mat markerBytes = out->bytesList.rowRange(i, i + 1); - Mat markerBits = Dictionary::getBitsFromByteList(markerBytes, markerSize); - minDistance = min(minDistance, _getSelfDistance(markerBits)); - for(int j = i + 1; j < out->bytesList.rows; j++) { - minDistance = min(minDistance, out->getDistanceToId(markerBits, j)); - } - } - tau = minDistance; - } - - // current best option - int bestTau = 0; - Mat bestMarker; - - // after these number of unproductive iterations, the best option is accepted - const int maxUnproductiveIterations = 5000; - int unproductiveIterations = 0; - - while(out->bytesList.rows < nMarkers) { - Mat currentMarker = _generateRandomMarker(markerSize, rng); - - int selfDistance = _getSelfDistance(currentMarker); - int minDistance = selfDistance; - - // if self distance is better or equal than current best option, calculate distance - // to previous accepted markers - if(selfDistance >= bestTau) { - for(int i = 0; i < out->bytesList.rows; i++) { - int currentDistance = out->getDistanceToId(currentMarker, i); - minDistance = min(currentDistance, minDistance); - if(minDistance <= bestTau) { - break; - } - } - } - - // if distance is high enough, accept the marker - if(minDistance >= tau) { - unproductiveIterations = 0; - bestTau = 0; - Mat bytes = Dictionary::getByteListFromBits(currentMarker); - out->bytesList.push_back(bytes); - } else { - unproductiveIterations++; - - // if distance is not enough, but is better than the current best option - if(minDistance > bestTau) { - bestTau = minDistance; - bestMarker = currentMarker; - } - - // if number of unproductive iterarions has been reached, accept the current best option - if(unproductiveIterations == maxUnproductiveIterations) { - unproductiveIterations = 0; - tau = bestTau; - bestTau = 0; - Mat bytes = Dictionary::getByteListFromBits(bestMarker); - out->bytesList.push_back(bytes); - } - } - } - - // update the maximum number of correction bits for the generated dictionary - out->maxCorrectionBits = (tau - 1) / 2; - - return out; -} - - -Ptr generateCustomDictionary(int nMarkers, int markerSize, int randomSeed) { - Ptr baseDictionary = makePtr(); - return generateCustomDictionary(nMarkers, markerSize, baseDictionary, randomSeed); -} - - -} -} diff --git a/modules/aruco/src/precomp.hpp b/modules/aruco/src/precomp.hpp index 955d9f3ed8..e49b075065 100644 --- a/modules/aruco/src/precomp.hpp +++ b/modules/aruco/src/precomp.hpp @@ -6,7 +6,6 @@ #define __OPENCV_CCALIB_PRECOMP__ #include -#include #include #endif diff --git a/modules/aruco/src/predefined_dictionaries.hpp b/modules/aruco/src/predefined_dictionaries.hpp deleted file mode 100644 index f343183059..0000000000 --- a/modules/aruco/src/predefined_dictionaries.hpp +++ /dev/null @@ -1,20127 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html - -namespace { - - - -/** - * Dictionaries are stored as a list of bytes in its four rotations - * On each rotation, the marker is divided in bytes assuming a row-major order - * This format allows a faster marker identification. - * For a dictionary composed by M markers of NxN bits, the structure dimensions should be: - * const char name[nMarkers][4rotations][nBytes], or more specifically: - * const char name[M][4][ceil(NxN/8)] - * The element [i][j][k] represents the k-th byte of the i-th marker in the dictionary - * in its j-th rotation. - * Each rotation implies a 90 degree rotation of the marker in anticlockwise direction. - */ - -static unsigned char DICT_ARUCO_BYTES[][4][4] = { - { { 132, 33, 8, 0 }, - { 0, 0, 15, 1 }, - { 8, 66, 16, 1 }, - { 248, 0, 0, 0 }, }, - { { 132, 33, 11, 1 }, - { 8, 66, 15, 1 }, - { 232, 66, 16, 1 }, - { 248, 33, 8, 0 }, }, - { { 132, 33, 4, 1 }, - { 8, 0, 31, 0 }, - { 144, 66, 16, 1 }, - { 124, 0, 8, 0 }, }, - { { 132, 33, 7, 0 }, - { 0, 66, 31, 0 }, - { 112, 66, 16, 1 }, - { 124, 33, 0, 0 }, }, - { { 132, 33, 120, 0 }, - { 16, 132, 15, 1 }, - { 15, 66, 16, 1 }, - { 248, 16, 132, 0 }, }, - { { 132, 33, 123, 1 }, - { 24, 198, 15, 1 }, - { 239, 66, 16, 1 }, - { 248, 49, 140, 0 }, }, - { { 132, 33, 116, 1 }, - { 24, 132, 31, 0 }, - { 151, 66, 16, 1 }, - { 124, 16, 140, 0 }, }, - { { 132, 33, 119, 0 }, - { 16, 198, 31, 0 }, - { 119, 66, 16, 1 }, - { 124, 49, 132, 0 }, }, - { { 132, 32, 152, 0 }, - { 16, 0, 46, 1 }, - { 12, 130, 16, 1 }, - { 186, 0, 4, 0 }, }, - { { 132, 32, 155, 1 }, - { 24, 66, 46, 1 }, - { 236, 130, 16, 1 }, - { 186, 33, 12, 0 }, }, - { { 132, 32, 148, 1 }, - { 24, 0, 62, 0 }, - { 148, 130, 16, 1 }, - { 62, 0, 12, 0 }, }, - { { 132, 32, 151, 0 }, - { 16, 66, 62, 0 }, - { 116, 130, 16, 1 }, - { 62, 33, 4, 0 }, }, - { { 132, 32, 232, 0 }, - { 0, 132, 46, 1 }, - { 11, 130, 16, 1 }, - { 186, 16, 128, 0 }, }, - { { 132, 32, 235, 1 }, - { 8, 198, 46, 1 }, - { 235, 130, 16, 1 }, - { 186, 49, 136, 0 }, }, - { { 132, 32, 228, 1 }, - { 8, 132, 62, 0 }, - { 147, 130, 16, 1 }, - { 62, 16, 136, 0 }, }, - { { 132, 32, 231, 0 }, - { 0, 198, 62, 0 }, - { 115, 130, 16, 1 }, - { 62, 49, 128, 0 }, }, - { { 132, 47, 8, 0 }, - { 33, 8, 15, 1 }, - { 8, 122, 16, 1 }, - { 248, 8, 66, 0 }, }, - { { 132, 47, 11, 1 }, - { 41, 74, 15, 1 }, - { 232, 122, 16, 1 }, - { 248, 41, 74, 0 }, }, - { { 132, 47, 4, 1 }, - { 41, 8, 31, 0 }, - { 144, 122, 16, 1 }, - { 124, 8, 74, 0 }, }, - { { 132, 47, 7, 0 }, - { 33, 74, 31, 0 }, - { 112, 122, 16, 1 }, - { 124, 41, 66, 0 }, }, - { { 132, 47, 120, 0 }, - { 49, 140, 15, 1 }, - { 15, 122, 16, 1 }, - { 248, 24, 198, 0 }, }, - { { 132, 47, 123, 1 }, - { 57, 206, 15, 1 }, - { 239, 122, 16, 1 }, - { 248, 57, 206, 0 }, }, - { { 132, 47, 116, 1 }, - { 57, 140, 31, 0 }, - { 151, 122, 16, 1 }, - { 124, 24, 206, 0 }, }, - { { 132, 47, 119, 0 }, - { 49, 206, 31, 0 }, - { 119, 122, 16, 1 }, - { 124, 57, 198, 0 }, }, - { { 132, 46, 152, 0 }, - { 49, 8, 46, 1 }, - { 12, 186, 16, 1 }, - { 186, 8, 70, 0 }, }, - { { 132, 46, 155, 1 }, - { 57, 74, 46, 1 }, - { 236, 186, 16, 1 }, - { 186, 41, 78, 0 }, }, - { { 132, 46, 148, 1 }, - { 57, 8, 62, 0 }, - { 148, 186, 16, 1 }, - { 62, 8, 78, 0 }, }, - { { 132, 46, 151, 0 }, - { 49, 74, 62, 0 }, - { 116, 186, 16, 1 }, - { 62, 41, 70, 0 }, }, - { { 132, 46, 232, 0 }, - { 33, 140, 46, 1 }, - { 11, 186, 16, 1 }, - { 186, 24, 194, 0 }, }, - { { 132, 46, 235, 1 }, - { 41, 206, 46, 1 }, - { 235, 186, 16, 1 }, - { 186, 57, 202, 0 }, }, - { { 132, 46, 228, 1 }, - { 41, 140, 62, 0 }, - { 147, 186, 16, 1 }, - { 62, 24, 202, 0 }, }, - { { 132, 46, 231, 0 }, - { 33, 206, 62, 0 }, - { 115, 186, 16, 1 }, - { 62, 57, 194, 0 }, }, - { { 132, 19, 8, 0 }, - { 32, 0, 77, 1 }, - { 8, 100, 16, 1 }, - { 217, 0, 2, 0 }, }, - { { 132, 19, 11, 1 }, - { 40, 66, 77, 1 }, - { 232, 100, 16, 1 }, - { 217, 33, 10, 0 }, }, - { { 132, 19, 4, 1 }, - { 40, 0, 93, 0 }, - { 144, 100, 16, 1 }, - { 93, 0, 10, 0 }, }, - { { 132, 19, 7, 0 }, - { 32, 66, 93, 0 }, - { 112, 100, 16, 1 }, - { 93, 33, 2, 0 }, }, - { { 132, 19, 120, 0 }, - { 48, 132, 77, 1 }, - { 15, 100, 16, 1 }, - { 217, 16, 134, 0 }, }, - { { 132, 19, 123, 1 }, - { 56, 198, 77, 1 }, - { 239, 100, 16, 1 }, - { 217, 49, 142, 0 }, }, - { { 132, 19, 116, 1 }, - { 56, 132, 93, 0 }, - { 151, 100, 16, 1 }, - { 93, 16, 142, 0 }, }, - { { 132, 19, 119, 0 }, - { 48, 198, 93, 0 }, - { 119, 100, 16, 1 }, - { 93, 49, 134, 0 }, }, - { { 132, 18, 152, 0 }, - { 48, 0, 108, 1 }, - { 12, 164, 16, 1 }, - { 155, 0, 6, 0 }, }, - { { 132, 18, 155, 1 }, - { 56, 66, 108, 1 }, - { 236, 164, 16, 1 }, - { 155, 33, 14, 0 }, }, - { { 132, 18, 148, 1 }, - { 56, 0, 124, 0 }, - { 148, 164, 16, 1 }, - { 31, 0, 14, 0 }, }, - { { 132, 18, 151, 0 }, - { 48, 66, 124, 0 }, - { 116, 164, 16, 1 }, - { 31, 33, 6, 0 }, }, - { { 132, 18, 232, 0 }, - { 32, 132, 108, 1 }, - { 11, 164, 16, 1 }, - { 155, 16, 130, 0 }, }, - { { 132, 18, 235, 1 }, - { 40, 198, 108, 1 }, - { 235, 164, 16, 1 }, - { 155, 49, 138, 0 }, }, - { { 132, 18, 228, 1 }, - { 40, 132, 124, 0 }, - { 147, 164, 16, 1 }, - { 31, 16, 138, 0 }, }, - { { 132, 18, 231, 0 }, - { 32, 198, 124, 0 }, - { 115, 164, 16, 1 }, - { 31, 49, 130, 0 }, }, - { { 132, 29, 8, 0 }, - { 1, 8, 77, 1 }, - { 8, 92, 16, 1 }, - { 217, 8, 64, 0 }, }, - { { 132, 29, 11, 1 }, - { 9, 74, 77, 1 }, - { 232, 92, 16, 1 }, - { 217, 41, 72, 0 }, }, - { { 132, 29, 4, 1 }, - { 9, 8, 93, 0 }, - { 144, 92, 16, 1 }, - { 93, 8, 72, 0 }, }, - { { 132, 29, 7, 0 }, - { 1, 74, 93, 0 }, - { 112, 92, 16, 1 }, - { 93, 41, 64, 0 }, }, - { { 132, 29, 120, 0 }, - { 17, 140, 77, 1 }, - { 15, 92, 16, 1 }, - { 217, 24, 196, 0 }, }, - { { 132, 29, 123, 1 }, - { 25, 206, 77, 1 }, - { 239, 92, 16, 1 }, - { 217, 57, 204, 0 }, }, - { { 132, 29, 116, 1 }, - { 25, 140, 93, 0 }, - { 151, 92, 16, 1 }, - { 93, 24, 204, 0 }, }, - { { 132, 29, 119, 0 }, - { 17, 206, 93, 0 }, - { 119, 92, 16, 1 }, - { 93, 57, 196, 0 }, }, - { { 132, 28, 152, 0 }, - { 17, 8, 108, 1 }, - { 12, 156, 16, 1 }, - { 155, 8, 68, 0 }, }, - { { 132, 28, 155, 1 }, - { 25, 74, 108, 1 }, - { 236, 156, 16, 1 }, - { 155, 41, 76, 0 }, }, - { { 132, 28, 148, 1 }, - { 25, 8, 124, 0 }, - { 148, 156, 16, 1 }, - { 31, 8, 76, 0 }, }, - { { 132, 28, 151, 0 }, - { 17, 74, 124, 0 }, - { 116, 156, 16, 1 }, - { 31, 41, 68, 0 }, }, - { { 132, 28, 232, 0 }, - { 1, 140, 108, 1 }, - { 11, 156, 16, 1 }, - { 155, 24, 192, 0 }, }, - { { 132, 28, 235, 1 }, - { 9, 206, 108, 1 }, - { 235, 156, 16, 1 }, - { 155, 57, 200, 0 }, }, - { { 132, 28, 228, 1 }, - { 9, 140, 124, 0 }, - { 147, 156, 16, 1 }, - { 31, 24, 200, 0 }, }, - { { 132, 28, 231, 0 }, - { 1, 206, 124, 0 }, - { 115, 156, 16, 1 }, - { 31, 57, 192, 0 }, }, - { { 133, 225, 8, 0 }, - { 66, 16, 15, 1 }, - { 8, 67, 208, 1 }, - { 248, 4, 33, 0 }, }, - { { 133, 225, 11, 1 }, - { 74, 82, 15, 1 }, - { 232, 67, 208, 1 }, - { 248, 37, 41, 0 }, }, - { { 133, 225, 4, 1 }, - { 74, 16, 31, 0 }, - { 144, 67, 208, 1 }, - { 124, 4, 41, 0 }, }, - { { 133, 225, 7, 0 }, - { 66, 82, 31, 0 }, - { 112, 67, 208, 1 }, - { 124, 37, 33, 0 }, }, - { { 133, 225, 120, 0 }, - { 82, 148, 15, 1 }, - { 15, 67, 208, 1 }, - { 248, 20, 165, 0 }, }, - { { 133, 225, 123, 1 }, - { 90, 214, 15, 1 }, - { 239, 67, 208, 1 }, - { 248, 53, 173, 0 }, }, - { { 133, 225, 116, 1 }, - { 90, 148, 31, 0 }, - { 151, 67, 208, 1 }, - { 124, 20, 173, 0 }, }, - { { 133, 225, 119, 0 }, - { 82, 214, 31, 0 }, - { 119, 67, 208, 1 }, - { 124, 53, 165, 0 }, }, - { { 133, 224, 152, 0 }, - { 82, 16, 46, 1 }, - { 12, 131, 208, 1 }, - { 186, 4, 37, 0 }, }, - { { 133, 224, 155, 1 }, - { 90, 82, 46, 1 }, - { 236, 131, 208, 1 }, - { 186, 37, 45, 0 }, }, - { { 133, 224, 148, 1 }, - { 90, 16, 62, 0 }, - { 148, 131, 208, 1 }, - { 62, 4, 45, 0 }, }, - { { 133, 224, 151, 0 }, - { 82, 82, 62, 0 }, - { 116, 131, 208, 1 }, - { 62, 37, 37, 0 }, }, - { { 133, 224, 232, 0 }, - { 66, 148, 46, 1 }, - { 11, 131, 208, 1 }, - { 186, 20, 161, 0 }, }, - { { 133, 224, 235, 1 }, - { 74, 214, 46, 1 }, - { 235, 131, 208, 1 }, - { 186, 53, 169, 0 }, }, - { { 133, 224, 228, 1 }, - { 74, 148, 62, 0 }, - { 147, 131, 208, 1 }, - { 62, 20, 169, 0 }, }, - { { 133, 224, 231, 0 }, - { 66, 214, 62, 0 }, - { 115, 131, 208, 1 }, - { 62, 53, 161, 0 }, }, - { { 133, 239, 8, 0 }, - { 99, 24, 15, 1 }, - { 8, 123, 208, 1 }, - { 248, 12, 99, 0 }, }, - { { 133, 239, 11, 1 }, - { 107, 90, 15, 1 }, - { 232, 123, 208, 1 }, - { 248, 45, 107, 0 }, }, - { { 133, 239, 4, 1 }, - { 107, 24, 31, 0 }, - { 144, 123, 208, 1 }, - { 124, 12, 107, 0 }, }, - { { 133, 239, 7, 0 }, - { 99, 90, 31, 0 }, - { 112, 123, 208, 1 }, - { 124, 45, 99, 0 }, }, - { { 133, 239, 120, 0 }, - { 115, 156, 15, 1 }, - { 15, 123, 208, 1 }, - { 248, 28, 231, 0 }, }, - { { 133, 239, 123, 1 }, - { 123, 222, 15, 1 }, - { 239, 123, 208, 1 }, - { 248, 61, 239, 0 }, }, - { { 133, 239, 116, 1 }, - { 123, 156, 31, 0 }, - { 151, 123, 208, 1 }, - { 124, 28, 239, 0 }, }, - { { 133, 239, 119, 0 }, - { 115, 222, 31, 0 }, - { 119, 123, 208, 1 }, - { 124, 61, 231, 0 }, }, - { { 133, 238, 152, 0 }, - { 115, 24, 46, 1 }, - { 12, 187, 208, 1 }, - { 186, 12, 103, 0 }, }, - { { 133, 238, 155, 1 }, - { 123, 90, 46, 1 }, - { 236, 187, 208, 1 }, - { 186, 45, 111, 0 }, }, - { { 133, 238, 148, 1 }, - { 123, 24, 62, 0 }, - { 148, 187, 208, 1 }, - { 62, 12, 111, 0 }, }, - { { 133, 238, 151, 0 }, - { 115, 90, 62, 0 }, - { 116, 187, 208, 1 }, - { 62, 45, 103, 0 }, }, - { { 133, 238, 232, 0 }, - { 99, 156, 46, 1 }, - { 11, 187, 208, 1 }, - { 186, 28, 227, 0 }, }, - { { 133, 238, 235, 1 }, - { 107, 222, 46, 1 }, - { 235, 187, 208, 1 }, - { 186, 61, 235, 0 }, }, - { { 133, 238, 228, 1 }, - { 107, 156, 62, 0 }, - { 147, 187, 208, 1 }, - { 62, 28, 235, 0 }, }, - { { 133, 238, 231, 0 }, - { 99, 222, 62, 0 }, - { 115, 187, 208, 1 }, - { 62, 61, 227, 0 }, }, - { { 133, 211, 8, 0 }, - { 98, 16, 77, 1 }, - { 8, 101, 208, 1 }, - { 217, 4, 35, 0 }, }, - { { 133, 211, 11, 1 }, - { 106, 82, 77, 1 }, - { 232, 101, 208, 1 }, - { 217, 37, 43, 0 }, }, - { { 133, 211, 4, 1 }, - { 106, 16, 93, 0 }, - { 144, 101, 208, 1 }, - { 93, 4, 43, 0 }, }, - { { 133, 211, 7, 0 }, - { 98, 82, 93, 0 }, - { 112, 101, 208, 1 }, - { 93, 37, 35, 0 }, }, - { { 133, 211, 120, 0 }, - { 114, 148, 77, 1 }, - { 15, 101, 208, 1 }, - { 217, 20, 167, 0 }, }, - { { 133, 211, 123, 1 }, - { 122, 214, 77, 1 }, - { 239, 101, 208, 1 }, - { 217, 53, 175, 0 }, }, - { { 133, 211, 116, 1 }, - { 122, 148, 93, 0 }, - { 151, 101, 208, 1 }, - { 93, 20, 175, 0 }, }, - { { 133, 211, 119, 0 }, - { 114, 214, 93, 0 }, - { 119, 101, 208, 1 }, - { 93, 53, 167, 0 }, }, - { { 133, 210, 152, 0 }, - { 114, 16, 108, 1 }, - { 12, 165, 208, 1 }, - { 155, 4, 39, 0 }, }, - { { 133, 210, 155, 1 }, - { 122, 82, 108, 1 }, - { 236, 165, 208, 1 }, - { 155, 37, 47, 0 }, }, - { { 133, 210, 148, 1 }, - { 122, 16, 124, 0 }, - { 148, 165, 208, 1 }, - { 31, 4, 47, 0 }, }, - { { 133, 210, 151, 0 }, - { 114, 82, 124, 0 }, - { 116, 165, 208, 1 }, - { 31, 37, 39, 0 }, }, - { { 133, 210, 232, 0 }, - { 98, 148, 108, 1 }, - { 11, 165, 208, 1 }, - { 155, 20, 163, 0 }, }, - { { 133, 210, 235, 1 }, - { 106, 214, 108, 1 }, - { 235, 165, 208, 1 }, - { 155, 53, 171, 0 }, }, - { { 133, 210, 228, 1 }, - { 106, 148, 124, 0 }, - { 147, 165, 208, 1 }, - { 31, 20, 171, 0 }, }, - { { 133, 210, 231, 0 }, - { 98, 214, 124, 0 }, - { 115, 165, 208, 1 }, - { 31, 53, 163, 0 }, }, - { { 133, 221, 8, 0 }, - { 67, 24, 77, 1 }, - { 8, 93, 208, 1 }, - { 217, 12, 97, 0 }, }, - { { 133, 221, 11, 1 }, - { 75, 90, 77, 1 }, - { 232, 93, 208, 1 }, - { 217, 45, 105, 0 }, }, - { { 133, 221, 4, 1 }, - { 75, 24, 93, 0 }, - { 144, 93, 208, 1 }, - { 93, 12, 105, 0 }, }, - { { 133, 221, 7, 0 }, - { 67, 90, 93, 0 }, - { 112, 93, 208, 1 }, - { 93, 45, 97, 0 }, }, - { { 133, 221, 120, 0 }, - { 83, 156, 77, 1 }, - { 15, 93, 208, 1 }, - { 217, 28, 229, 0 }, }, - { { 133, 221, 123, 1 }, - { 91, 222, 77, 1 }, - { 239, 93, 208, 1 }, - { 217, 61, 237, 0 }, }, - { { 133, 221, 116, 1 }, - { 91, 156, 93, 0 }, - { 151, 93, 208, 1 }, - { 93, 28, 237, 0 }, }, - { { 133, 221, 119, 0 }, - { 83, 222, 93, 0 }, - { 119, 93, 208, 1 }, - { 93, 61, 229, 0 }, }, - { { 133, 220, 152, 0 }, - { 83, 24, 108, 1 }, - { 12, 157, 208, 1 }, - { 155, 12, 101, 0 }, }, - { { 133, 220, 155, 1 }, - { 91, 90, 108, 1 }, - { 236, 157, 208, 1 }, - { 155, 45, 109, 0 }, }, - { { 133, 220, 148, 1 }, - { 91, 24, 124, 0 }, - { 148, 157, 208, 1 }, - { 31, 12, 109, 0 }, }, - { { 133, 220, 151, 0 }, - { 83, 90, 124, 0 }, - { 116, 157, 208, 1 }, - { 31, 45, 101, 0 }, }, - { { 133, 220, 232, 0 }, - { 67, 156, 108, 1 }, - { 11, 157, 208, 1 }, - { 155, 28, 225, 0 }, }, - { { 133, 220, 235, 1 }, - { 75, 222, 108, 1 }, - { 235, 157, 208, 1 }, - { 155, 61, 233, 0 }, }, - { { 133, 220, 228, 1 }, - { 75, 156, 124, 0 }, - { 147, 157, 208, 1 }, - { 31, 28, 233, 0 }, }, - { { 133, 220, 231, 0 }, - { 67, 222, 124, 0 }, - { 115, 157, 208, 1 }, - { 31, 61, 225, 0 }, }, - { { 130, 97, 8, 0 }, - { 64, 0, 139, 1 }, - { 8, 67, 32, 1 }, - { 232, 128, 1, 0 }, }, - { { 130, 97, 11, 1 }, - { 72, 66, 139, 1 }, - { 232, 67, 32, 1 }, - { 232, 161, 9, 0 }, }, - { { 130, 97, 4, 1 }, - { 72, 0, 155, 0 }, - { 144, 67, 32, 1 }, - { 108, 128, 9, 0 }, }, - { { 130, 97, 7, 0 }, - { 64, 66, 155, 0 }, - { 112, 67, 32, 1 }, - { 108, 161, 1, 0 }, }, - { { 130, 97, 120, 0 }, - { 80, 132, 139, 1 }, - { 15, 67, 32, 1 }, - { 232, 144, 133, 0 }, }, - { { 130, 97, 123, 1 }, - { 88, 198, 139, 1 }, - { 239, 67, 32, 1 }, - { 232, 177, 141, 0 }, }, - { { 130, 97, 116, 1 }, - { 88, 132, 155, 0 }, - { 151, 67, 32, 1 }, - { 108, 144, 141, 0 }, }, - { { 130, 97, 119, 0 }, - { 80, 198, 155, 0 }, - { 119, 67, 32, 1 }, - { 108, 177, 133, 0 }, }, - { { 130, 96, 152, 0 }, - { 80, 0, 170, 1 }, - { 12, 131, 32, 1 }, - { 170, 128, 5, 0 }, }, - { { 130, 96, 155, 1 }, - { 88, 66, 170, 1 }, - { 236, 131, 32, 1 }, - { 170, 161, 13, 0 }, }, - { { 130, 96, 148, 1 }, - { 88, 0, 186, 0 }, - { 148, 131, 32, 1 }, - { 46, 128, 13, 0 }, }, - { { 130, 96, 151, 0 }, - { 80, 66, 186, 0 }, - { 116, 131, 32, 1 }, - { 46, 161, 5, 0 }, }, - { { 130, 96, 232, 0 }, - { 64, 132, 170, 1 }, - { 11, 131, 32, 1 }, - { 170, 144, 129, 0 }, }, - { { 130, 96, 235, 1 }, - { 72, 198, 170, 1 }, - { 235, 131, 32, 1 }, - { 170, 177, 137, 0 }, }, - { { 130, 96, 228, 1 }, - { 72, 132, 186, 0 }, - { 147, 131, 32, 1 }, - { 46, 144, 137, 0 }, }, - { { 130, 96, 231, 0 }, - { 64, 198, 186, 0 }, - { 115, 131, 32, 1 }, - { 46, 177, 129, 0 }, }, - { { 130, 111, 8, 0 }, - { 97, 8, 139, 1 }, - { 8, 123, 32, 1 }, - { 232, 136, 67, 0 }, }, - { { 130, 111, 11, 1 }, - { 105, 74, 139, 1 }, - { 232, 123, 32, 1 }, - { 232, 169, 75, 0 }, }, - { { 130, 111, 4, 1 }, - { 105, 8, 155, 0 }, - { 144, 123, 32, 1 }, - { 108, 136, 75, 0 }, }, - { { 130, 111, 7, 0 }, - { 97, 74, 155, 0 }, - { 112, 123, 32, 1 }, - { 108, 169, 67, 0 }, }, - { { 130, 111, 120, 0 }, - { 113, 140, 139, 1 }, - { 15, 123, 32, 1 }, - { 232, 152, 199, 0 }, }, - { { 130, 111, 123, 1 }, - { 121, 206, 139, 1 }, - { 239, 123, 32, 1 }, - { 232, 185, 207, 0 }, }, - { { 130, 111, 116, 1 }, - { 121, 140, 155, 0 }, - { 151, 123, 32, 1 }, - { 108, 152, 207, 0 }, }, - { { 130, 111, 119, 0 }, - { 113, 206, 155, 0 }, - { 119, 123, 32, 1 }, - { 108, 185, 199, 0 }, }, - { { 130, 110, 152, 0 }, - { 113, 8, 170, 1 }, - { 12, 187, 32, 1 }, - { 170, 136, 71, 0 }, }, - { { 130, 110, 155, 1 }, - { 121, 74, 170, 1 }, - { 236, 187, 32, 1 }, - { 170, 169, 79, 0 }, }, - { { 130, 110, 148, 1 }, - { 121, 8, 186, 0 }, - { 148, 187, 32, 1 }, - { 46, 136, 79, 0 }, }, - { { 130, 110, 151, 0 }, - { 113, 74, 186, 0 }, - { 116, 187, 32, 1 }, - { 46, 169, 71, 0 }, }, - { { 130, 110, 232, 0 }, - { 97, 140, 170, 1 }, - { 11, 187, 32, 1 }, - { 170, 152, 195, 0 }, }, - { { 130, 110, 235, 1 }, - { 105, 206, 170, 1 }, - { 235, 187, 32, 1 }, - { 170, 185, 203, 0 }, }, - { { 130, 110, 228, 1 }, - { 105, 140, 186, 0 }, - { 147, 187, 32, 1 }, - { 46, 152, 203, 0 }, }, - { { 130, 110, 231, 0 }, - { 97, 206, 186, 0 }, - { 115, 187, 32, 1 }, - { 46, 185, 195, 0 }, }, - { { 130, 83, 8, 0 }, - { 96, 0, 201, 1 }, - { 8, 101, 32, 1 }, - { 201, 128, 3, 0 }, }, - { { 130, 83, 11, 1 }, - { 104, 66, 201, 1 }, - { 232, 101, 32, 1 }, - { 201, 161, 11, 0 }, }, - { { 130, 83, 4, 1 }, - { 104, 0, 217, 0 }, - { 144, 101, 32, 1 }, - { 77, 128, 11, 0 }, }, - { { 130, 83, 7, 0 }, - { 96, 66, 217, 0 }, - { 112, 101, 32, 1 }, - { 77, 161, 3, 0 }, }, - { { 130, 83, 120, 0 }, - { 112, 132, 201, 1 }, - { 15, 101, 32, 1 }, - { 201, 144, 135, 0 }, }, - { { 130, 83, 123, 1 }, - { 120, 198, 201, 1 }, - { 239, 101, 32, 1 }, - { 201, 177, 143, 0 }, }, - { { 130, 83, 116, 1 }, - { 120, 132, 217, 0 }, - { 151, 101, 32, 1 }, - { 77, 144, 143, 0 }, }, - { { 130, 83, 119, 0 }, - { 112, 198, 217, 0 }, - { 119, 101, 32, 1 }, - { 77, 177, 135, 0 }, }, - { { 130, 82, 152, 0 }, - { 112, 0, 232, 1 }, - { 12, 165, 32, 1 }, - { 139, 128, 7, 0 }, }, - { { 130, 82, 155, 1 }, - { 120, 66, 232, 1 }, - { 236, 165, 32, 1 }, - { 139, 161, 15, 0 }, }, - { { 130, 82, 148, 1 }, - { 120, 0, 248, 0 }, - { 148, 165, 32, 1 }, - { 15, 128, 15, 0 }, }, - { { 130, 82, 151, 0 }, - { 112, 66, 248, 0 }, - { 116, 165, 32, 1 }, - { 15, 161, 7, 0 }, }, - { { 130, 82, 232, 0 }, - { 96, 132, 232, 1 }, - { 11, 165, 32, 1 }, - { 139, 144, 131, 0 }, }, - { { 130, 82, 235, 1 }, - { 104, 198, 232, 1 }, - { 235, 165, 32, 1 }, - { 139, 177, 139, 0 }, }, - { { 130, 82, 228, 1 }, - { 104, 132, 248, 0 }, - { 147, 165, 32, 1 }, - { 15, 144, 139, 0 }, }, - { { 130, 82, 231, 0 }, - { 96, 198, 248, 0 }, - { 115, 165, 32, 1 }, - { 15, 177, 131, 0 }, }, - { { 130, 93, 8, 0 }, - { 65, 8, 201, 1 }, - { 8, 93, 32, 1 }, - { 201, 136, 65, 0 }, }, - { { 130, 93, 11, 1 }, - { 73, 74, 201, 1 }, - { 232, 93, 32, 1 }, - { 201, 169, 73, 0 }, }, - { { 130, 93, 4, 1 }, - { 73, 8, 217, 0 }, - { 144, 93, 32, 1 }, - { 77, 136, 73, 0 }, }, - { { 130, 93, 7, 0 }, - { 65, 74, 217, 0 }, - { 112, 93, 32, 1 }, - { 77, 169, 65, 0 }, }, - { { 130, 93, 120, 0 }, - { 81, 140, 201, 1 }, - { 15, 93, 32, 1 }, - { 201, 152, 197, 0 }, }, - { { 130, 93, 123, 1 }, - { 89, 206, 201, 1 }, - { 239, 93, 32, 1 }, - { 201, 185, 205, 0 }, }, - { { 130, 93, 116, 1 }, - { 89, 140, 217, 0 }, - { 151, 93, 32, 1 }, - { 77, 152, 205, 0 }, }, - { { 130, 93, 119, 0 }, - { 81, 206, 217, 0 }, - { 119, 93, 32, 1 }, - { 77, 185, 197, 0 }, }, - { { 130, 92, 152, 0 }, - { 81, 8, 232, 1 }, - { 12, 157, 32, 1 }, - { 139, 136, 69, 0 }, }, - { { 130, 92, 155, 1 }, - { 89, 74, 232, 1 }, - { 236, 157, 32, 1 }, - { 139, 169, 77, 0 }, }, - { { 130, 92, 148, 1 }, - { 89, 8, 248, 0 }, - { 148, 157, 32, 1 }, - { 15, 136, 77, 0 }, }, - { { 130, 92, 151, 0 }, - { 81, 74, 248, 0 }, - { 116, 157, 32, 1 }, - { 15, 169, 69, 0 }, }, - { { 130, 92, 232, 0 }, - { 65, 140, 232, 1 }, - { 11, 157, 32, 1 }, - { 139, 152, 193, 0 }, }, - { { 130, 92, 235, 1 }, - { 73, 206, 232, 1 }, - { 235, 157, 32, 1 }, - { 139, 185, 201, 0 }, }, - { { 130, 92, 228, 1 }, - { 73, 140, 248, 0 }, - { 147, 157, 32, 1 }, - { 15, 152, 201, 0 }, }, - { { 130, 92, 231, 0 }, - { 65, 206, 248, 0 }, - { 115, 157, 32, 1 }, - { 15, 185, 193, 0 }, }, - { { 131, 161, 8, 0 }, - { 2, 16, 139, 1 }, - { 8, 66, 224, 1 }, - { 232, 132, 32, 0 }, }, - { { 131, 161, 11, 1 }, - { 10, 82, 139, 1 }, - { 232, 66, 224, 1 }, - { 232, 165, 40, 0 }, }, - { { 131, 161, 4, 1 }, - { 10, 16, 155, 0 }, - { 144, 66, 224, 1 }, - { 108, 132, 40, 0 }, }, - { { 131, 161, 7, 0 }, - { 2, 82, 155, 0 }, - { 112, 66, 224, 1 }, - { 108, 165, 32, 0 }, }, - { { 131, 161, 120, 0 }, - { 18, 148, 139, 1 }, - { 15, 66, 224, 1 }, - { 232, 148, 164, 0 }, }, - { { 131, 161, 123, 1 }, - { 26, 214, 139, 1 }, - { 239, 66, 224, 1 }, - { 232, 181, 172, 0 }, }, - { { 131, 161, 116, 1 }, - { 26, 148, 155, 0 }, - { 151, 66, 224, 1 }, - { 108, 148, 172, 0 }, }, - { { 131, 161, 119, 0 }, - { 18, 214, 155, 0 }, - { 119, 66, 224, 1 }, - { 108, 181, 164, 0 }, }, - { { 131, 160, 152, 0 }, - { 18, 16, 170, 1 }, - { 12, 130, 224, 1 }, - { 170, 132, 36, 0 }, }, - { { 131, 160, 155, 1 }, - { 26, 82, 170, 1 }, - { 236, 130, 224, 1 }, - { 170, 165, 44, 0 }, }, - { { 131, 160, 148, 1 }, - { 26, 16, 186, 0 }, - { 148, 130, 224, 1 }, - { 46, 132, 44, 0 }, }, - { { 131, 160, 151, 0 }, - { 18, 82, 186, 0 }, - { 116, 130, 224, 1 }, - { 46, 165, 36, 0 }, }, - { { 131, 160, 232, 0 }, - { 2, 148, 170, 1 }, - { 11, 130, 224, 1 }, - { 170, 148, 160, 0 }, }, - { { 131, 160, 235, 1 }, - { 10, 214, 170, 1 }, - { 235, 130, 224, 1 }, - { 170, 181, 168, 0 }, }, - { { 131, 160, 228, 1 }, - { 10, 148, 186, 0 }, - { 147, 130, 224, 1 }, - { 46, 148, 168, 0 }, }, - { { 131, 160, 231, 0 }, - { 2, 214, 186, 0 }, - { 115, 130, 224, 1 }, - { 46, 181, 160, 0 }, }, - { { 131, 175, 8, 0 }, - { 35, 24, 139, 1 }, - { 8, 122, 224, 1 }, - { 232, 140, 98, 0 }, }, - { { 131, 175, 11, 1 }, - { 43, 90, 139, 1 }, - { 232, 122, 224, 1 }, - { 232, 173, 106, 0 }, }, - { { 131, 175, 4, 1 }, - { 43, 24, 155, 0 }, - { 144, 122, 224, 1 }, - { 108, 140, 106, 0 }, }, - { { 131, 175, 7, 0 }, - { 35, 90, 155, 0 }, - { 112, 122, 224, 1 }, - { 108, 173, 98, 0 }, }, - { { 131, 175, 120, 0 }, - { 51, 156, 139, 1 }, - { 15, 122, 224, 1 }, - { 232, 156, 230, 0 }, }, - { { 131, 175, 123, 1 }, - { 59, 222, 139, 1 }, - { 239, 122, 224, 1 }, - { 232, 189, 238, 0 }, }, - { { 131, 175, 116, 1 }, - { 59, 156, 155, 0 }, - { 151, 122, 224, 1 }, - { 108, 156, 238, 0 }, }, - { { 131, 175, 119, 0 }, - { 51, 222, 155, 0 }, - { 119, 122, 224, 1 }, - { 108, 189, 230, 0 }, }, - { { 131, 174, 152, 0 }, - { 51, 24, 170, 1 }, - { 12, 186, 224, 1 }, - { 170, 140, 102, 0 }, }, - { { 131, 174, 155, 1 }, - { 59, 90, 170, 1 }, - { 236, 186, 224, 1 }, - { 170, 173, 110, 0 }, }, - { { 131, 174, 148, 1 }, - { 59, 24, 186, 0 }, - { 148, 186, 224, 1 }, - { 46, 140, 110, 0 }, }, - { { 131, 174, 151, 0 }, - { 51, 90, 186, 0 }, - { 116, 186, 224, 1 }, - { 46, 173, 102, 0 }, }, - { { 131, 174, 232, 0 }, - { 35, 156, 170, 1 }, - { 11, 186, 224, 1 }, - { 170, 156, 226, 0 }, }, - { { 131, 174, 235, 1 }, - { 43, 222, 170, 1 }, - { 235, 186, 224, 1 }, - { 170, 189, 234, 0 }, }, - { { 131, 174, 228, 1 }, - { 43, 156, 186, 0 }, - { 147, 186, 224, 1 }, - { 46, 156, 234, 0 }, }, - { { 131, 174, 231, 0 }, - { 35, 222, 186, 0 }, - { 115, 186, 224, 1 }, - { 46, 189, 226, 0 }, }, - { { 131, 147, 8, 0 }, - { 34, 16, 201, 1 }, - { 8, 100, 224, 1 }, - { 201, 132, 34, 0 }, }, - { { 131, 147, 11, 1 }, - { 42, 82, 201, 1 }, - { 232, 100, 224, 1 }, - { 201, 165, 42, 0 }, }, - { { 131, 147, 4, 1 }, - { 42, 16, 217, 0 }, - { 144, 100, 224, 1 }, - { 77, 132, 42, 0 }, }, - { { 131, 147, 7, 0 }, - { 34, 82, 217, 0 }, - { 112, 100, 224, 1 }, - { 77, 165, 34, 0 }, }, - { { 131, 147, 120, 0 }, - { 50, 148, 201, 1 }, - { 15, 100, 224, 1 }, - { 201, 148, 166, 0 }, }, - { { 131, 147, 123, 1 }, - { 58, 214, 201, 1 }, - { 239, 100, 224, 1 }, - { 201, 181, 174, 0 }, }, - { { 131, 147, 116, 1 }, - { 58, 148, 217, 0 }, - { 151, 100, 224, 1 }, - { 77, 148, 174, 0 }, }, - { { 131, 147, 119, 0 }, - { 50, 214, 217, 0 }, - { 119, 100, 224, 1 }, - { 77, 181, 166, 0 }, }, - { { 131, 146, 152, 0 }, - { 50, 16, 232, 1 }, - { 12, 164, 224, 1 }, - { 139, 132, 38, 0 }, }, - { { 131, 146, 155, 1 }, - { 58, 82, 232, 1 }, - { 236, 164, 224, 1 }, - { 139, 165, 46, 0 }, }, - { { 131, 146, 148, 1 }, - { 58, 16, 248, 0 }, - { 148, 164, 224, 1 }, - { 15, 132, 46, 0 }, }, - { { 131, 146, 151, 0 }, - { 50, 82, 248, 0 }, - { 116, 164, 224, 1 }, - { 15, 165, 38, 0 }, }, - { { 131, 146, 232, 0 }, - { 34, 148, 232, 1 }, - { 11, 164, 224, 1 }, - { 139, 148, 162, 0 }, }, - { { 131, 146, 235, 1 }, - { 42, 214, 232, 1 }, - { 235, 164, 224, 1 }, - { 139, 181, 170, 0 }, }, - { { 131, 146, 228, 1 }, - { 42, 148, 248, 0 }, - { 147, 164, 224, 1 }, - { 15, 148, 170, 0 }, }, - { { 131, 146, 231, 0 }, - { 34, 214, 248, 0 }, - { 115, 164, 224, 1 }, - { 15, 181, 162, 0 }, }, - { { 131, 157, 8, 0 }, - { 3, 24, 201, 1 }, - { 8, 92, 224, 1 }, - { 201, 140, 96, 0 }, }, - { { 131, 157, 11, 1 }, - { 11, 90, 201, 1 }, - { 232, 92, 224, 1 }, - { 201, 173, 104, 0 }, }, - { { 131, 157, 4, 1 }, - { 11, 24, 217, 0 }, - { 144, 92, 224, 1 }, - { 77, 140, 104, 0 }, }, - { { 131, 157, 7, 0 }, - { 3, 90, 217, 0 }, - { 112, 92, 224, 1 }, - { 77, 173, 96, 0 }, }, - { { 131, 157, 120, 0 }, - { 19, 156, 201, 1 }, - { 15, 92, 224, 1 }, - { 201, 156, 228, 0 }, }, - { { 131, 157, 123, 1 }, - { 27, 222, 201, 1 }, - { 239, 92, 224, 1 }, - { 201, 189, 236, 0 }, }, - { { 131, 157, 116, 1 }, - { 27, 156, 217, 0 }, - { 151, 92, 224, 1 }, - { 77, 156, 236, 0 }, }, - { { 131, 157, 119, 0 }, - { 19, 222, 217, 0 }, - { 119, 92, 224, 1 }, - { 77, 189, 228, 0 }, }, - { { 131, 156, 152, 0 }, - { 19, 24, 232, 1 }, - { 12, 156, 224, 1 }, - { 139, 140, 100, 0 }, }, - { { 131, 156, 155, 1 }, - { 27, 90, 232, 1 }, - { 236, 156, 224, 1 }, - { 139, 173, 108, 0 }, }, - { { 131, 156, 148, 1 }, - { 27, 24, 248, 0 }, - { 148, 156, 224, 1 }, - { 15, 140, 108, 0 }, }, - { { 131, 156, 151, 0 }, - { 19, 90, 248, 0 }, - { 116, 156, 224, 1 }, - { 15, 173, 100, 0 }, }, - { { 131, 156, 232, 0 }, - { 3, 156, 232, 1 }, - { 11, 156, 224, 1 }, - { 139, 156, 224, 0 }, }, - { { 131, 156, 235, 1 }, - { 11, 222, 232, 1 }, - { 235, 156, 224, 1 }, - { 139, 189, 232, 0 }, }, - { { 131, 156, 228, 1 }, - { 11, 156, 248, 0 }, - { 147, 156, 224, 1 }, - { 15, 156, 232, 0 }, }, - { { 131, 156, 231, 0 }, - { 3, 222, 248, 0 }, - { 115, 156, 224, 1 }, - { 15, 189, 224, 0 }, }, - { { 188, 33, 8, 0 }, - { 132, 32, 15, 1 }, - { 8, 66, 30, 1 }, - { 248, 2, 16, 1 }, }, - { { 188, 33, 11, 1 }, - { 140, 98, 15, 1 }, - { 232, 66, 30, 1 }, - { 248, 35, 24, 1 }, }, - { { 188, 33, 4, 1 }, - { 140, 32, 31, 0 }, - { 144, 66, 30, 1 }, - { 124, 2, 24, 1 }, }, - { { 188, 33, 7, 0 }, - { 132, 98, 31, 0 }, - { 112, 66, 30, 1 }, - { 124, 35, 16, 1 }, }, - { { 188, 33, 120, 0 }, - { 148, 164, 15, 1 }, - { 15, 66, 30, 1 }, - { 248, 18, 148, 1 }, }, - { { 188, 33, 123, 1 }, - { 156, 230, 15, 1 }, - { 239, 66, 30, 1 }, - { 248, 51, 156, 1 }, }, - { { 188, 33, 116, 1 }, - { 156, 164, 31, 0 }, - { 151, 66, 30, 1 }, - { 124, 18, 156, 1 }, }, - { { 188, 33, 119, 0 }, - { 148, 230, 31, 0 }, - { 119, 66, 30, 1 }, - { 124, 51, 148, 1 }, }, - { { 188, 32, 152, 0 }, - { 148, 32, 46, 1 }, - { 12, 130, 30, 1 }, - { 186, 2, 20, 1 }, }, - { { 188, 32, 155, 1 }, - { 156, 98, 46, 1 }, - { 236, 130, 30, 1 }, - { 186, 35, 28, 1 }, }, - { { 188, 32, 148, 1 }, - { 156, 32, 62, 0 }, - { 148, 130, 30, 1 }, - { 62, 2, 28, 1 }, }, - { { 188, 32, 151, 0 }, - { 148, 98, 62, 0 }, - { 116, 130, 30, 1 }, - { 62, 35, 20, 1 }, }, - { { 188, 32, 232, 0 }, - { 132, 164, 46, 1 }, - { 11, 130, 30, 1 }, - { 186, 18, 144, 1 }, }, - { { 188, 32, 235, 1 }, - { 140, 230, 46, 1 }, - { 235, 130, 30, 1 }, - { 186, 51, 152, 1 }, }, - { { 188, 32, 228, 1 }, - { 140, 164, 62, 0 }, - { 147, 130, 30, 1 }, - { 62, 18, 152, 1 }, }, - { { 188, 32, 231, 0 }, - { 132, 230, 62, 0 }, - { 115, 130, 30, 1 }, - { 62, 51, 144, 1 }, }, - { { 188, 47, 8, 0 }, - { 165, 40, 15, 1 }, - { 8, 122, 30, 1 }, - { 248, 10, 82, 1 }, }, - { { 188, 47, 11, 1 }, - { 173, 106, 15, 1 }, - { 232, 122, 30, 1 }, - { 248, 43, 90, 1 }, }, - { { 188, 47, 4, 1 }, - { 173, 40, 31, 0 }, - { 144, 122, 30, 1 }, - { 124, 10, 90, 1 }, }, - { { 188, 47, 7, 0 }, - { 165, 106, 31, 0 }, - { 112, 122, 30, 1 }, - { 124, 43, 82, 1 }, }, - { { 188, 47, 120, 0 }, - { 181, 172, 15, 1 }, - { 15, 122, 30, 1 }, - { 248, 26, 214, 1 }, }, - { { 188, 47, 123, 1 }, - { 189, 238, 15, 1 }, - { 239, 122, 30, 1 }, - { 248, 59, 222, 1 }, }, - { { 188, 47, 116, 1 }, - { 189, 172, 31, 0 }, - { 151, 122, 30, 1 }, - { 124, 26, 222, 1 }, }, - { { 188, 47, 119, 0 }, - { 181, 238, 31, 0 }, - { 119, 122, 30, 1 }, - { 124, 59, 214, 1 }, }, - { { 188, 46, 152, 0 }, - { 181, 40, 46, 1 }, - { 12, 186, 30, 1 }, - { 186, 10, 86, 1 }, }, - { { 188, 46, 155, 1 }, - { 189, 106, 46, 1 }, - { 236, 186, 30, 1 }, - { 186, 43, 94, 1 }, }, - { { 188, 46, 148, 1 }, - { 189, 40, 62, 0 }, - { 148, 186, 30, 1 }, - { 62, 10, 94, 1 }, }, - { { 188, 46, 151, 0 }, - { 181, 106, 62, 0 }, - { 116, 186, 30, 1 }, - { 62, 43, 86, 1 }, }, - { { 188, 46, 232, 0 }, - { 165, 172, 46, 1 }, - { 11, 186, 30, 1 }, - { 186, 26, 210, 1 }, }, - { { 188, 46, 235, 1 }, - { 173, 238, 46, 1 }, - { 235, 186, 30, 1 }, - { 186, 59, 218, 1 }, }, - { { 188, 46, 228, 1 }, - { 173, 172, 62, 0 }, - { 147, 186, 30, 1 }, - { 62, 26, 218, 1 }, }, - { { 188, 46, 231, 0 }, - { 165, 238, 62, 0 }, - { 115, 186, 30, 1 }, - { 62, 59, 210, 1 }, }, - { { 188, 19, 8, 0 }, - { 164, 32, 77, 1 }, - { 8, 100, 30, 1 }, - { 217, 2, 18, 1 }, }, - { { 188, 19, 11, 1 }, - { 172, 98, 77, 1 }, - { 232, 100, 30, 1 }, - { 217, 35, 26, 1 }, }, - { { 188, 19, 4, 1 }, - { 172, 32, 93, 0 }, - { 144, 100, 30, 1 }, - { 93, 2, 26, 1 }, }, - { { 188, 19, 7, 0 }, - { 164, 98, 93, 0 }, - { 112, 100, 30, 1 }, - { 93, 35, 18, 1 }, }, - { { 188, 19, 120, 0 }, - { 180, 164, 77, 1 }, - { 15, 100, 30, 1 }, - { 217, 18, 150, 1 }, }, - { { 188, 19, 123, 1 }, - { 188, 230, 77, 1 }, - { 239, 100, 30, 1 }, - { 217, 51, 158, 1 }, }, - { { 188, 19, 116, 1 }, - { 188, 164, 93, 0 }, - { 151, 100, 30, 1 }, - { 93, 18, 158, 1 }, }, - { { 188, 19, 119, 0 }, - { 180, 230, 93, 0 }, - { 119, 100, 30, 1 }, - { 93, 51, 150, 1 }, }, - { { 188, 18, 152, 0 }, - { 180, 32, 108, 1 }, - { 12, 164, 30, 1 }, - { 155, 2, 22, 1 }, }, - { { 188, 18, 155, 1 }, - { 188, 98, 108, 1 }, - { 236, 164, 30, 1 }, - { 155, 35, 30, 1 }, }, - { { 188, 18, 148, 1 }, - { 188, 32, 124, 0 }, - { 148, 164, 30, 1 }, - { 31, 2, 30, 1 }, }, - { { 188, 18, 151, 0 }, - { 180, 98, 124, 0 }, - { 116, 164, 30, 1 }, - { 31, 35, 22, 1 }, }, - { { 188, 18, 232, 0 }, - { 164, 164, 108, 1 }, - { 11, 164, 30, 1 }, - { 155, 18, 146, 1 }, }, - { { 188, 18, 235, 1 }, - { 172, 230, 108, 1 }, - { 235, 164, 30, 1 }, - { 155, 51, 154, 1 }, }, - { { 188, 18, 228, 1 }, - { 172, 164, 124, 0 }, - { 147, 164, 30, 1 }, - { 31, 18, 154, 1 }, }, - { { 188, 18, 231, 0 }, - { 164, 230, 124, 0 }, - { 115, 164, 30, 1 }, - { 31, 51, 146, 1 }, }, - { { 188, 29, 8, 0 }, - { 133, 40, 77, 1 }, - { 8, 92, 30, 1 }, - { 217, 10, 80, 1 }, }, - { { 188, 29, 11, 1 }, - { 141, 106, 77, 1 }, - { 232, 92, 30, 1 }, - { 217, 43, 88, 1 }, }, - { { 188, 29, 4, 1 }, - { 141, 40, 93, 0 }, - { 144, 92, 30, 1 }, - { 93, 10, 88, 1 }, }, - { { 188, 29, 7, 0 }, - { 133, 106, 93, 0 }, - { 112, 92, 30, 1 }, - { 93, 43, 80, 1 }, }, - { { 188, 29, 120, 0 }, - { 149, 172, 77, 1 }, - { 15, 92, 30, 1 }, - { 217, 26, 212, 1 }, }, - { { 188, 29, 123, 1 }, - { 157, 238, 77, 1 }, - { 239, 92, 30, 1 }, - { 217, 59, 220, 1 }, }, - { { 188, 29, 116, 1 }, - { 157, 172, 93, 0 }, - { 151, 92, 30, 1 }, - { 93, 26, 220, 1 }, }, - { { 188, 29, 119, 0 }, - { 149, 238, 93, 0 }, - { 119, 92, 30, 1 }, - { 93, 59, 212, 1 }, }, - { { 188, 28, 152, 0 }, - { 149, 40, 108, 1 }, - { 12, 156, 30, 1 }, - { 155, 10, 84, 1 }, }, - { { 188, 28, 155, 1 }, - { 157, 106, 108, 1 }, - { 236, 156, 30, 1 }, - { 155, 43, 92, 1 }, }, - { { 188, 28, 148, 1 }, - { 157, 40, 124, 0 }, - { 148, 156, 30, 1 }, - { 31, 10, 92, 1 }, }, - { { 188, 28, 151, 0 }, - { 149, 106, 124, 0 }, - { 116, 156, 30, 1 }, - { 31, 43, 84, 1 }, }, - { { 188, 28, 232, 0 }, - { 133, 172, 108, 1 }, - { 11, 156, 30, 1 }, - { 155, 26, 208, 1 }, }, - { { 188, 28, 235, 1 }, - { 141, 238, 108, 1 }, - { 235, 156, 30, 1 }, - { 155, 59, 216, 1 }, }, - { { 188, 28, 228, 1 }, - { 141, 172, 124, 0 }, - { 147, 156, 30, 1 }, - { 31, 26, 216, 1 }, }, - { { 188, 28, 231, 0 }, - { 133, 238, 124, 0 }, - { 115, 156, 30, 1 }, - { 31, 59, 208, 1 }, }, - { { 189, 225, 8, 0 }, - { 198, 48, 15, 1 }, - { 8, 67, 222, 1 }, - { 248, 6, 49, 1 }, }, - { { 189, 225, 11, 1 }, - { 206, 114, 15, 1 }, - { 232, 67, 222, 1 }, - { 248, 39, 57, 1 }, }, - { { 189, 225, 4, 1 }, - { 206, 48, 31, 0 }, - { 144, 67, 222, 1 }, - { 124, 6, 57, 1 }, }, - { { 189, 225, 7, 0 }, - { 198, 114, 31, 0 }, - { 112, 67, 222, 1 }, - { 124, 39, 49, 1 }, }, - { { 189, 225, 120, 0 }, - { 214, 180, 15, 1 }, - { 15, 67, 222, 1 }, - { 248, 22, 181, 1 }, }, - { { 189, 225, 123, 1 }, - { 222, 246, 15, 1 }, - { 239, 67, 222, 1 }, - { 248, 55, 189, 1 }, }, - { { 189, 225, 116, 1 }, - { 222, 180, 31, 0 }, - { 151, 67, 222, 1 }, - { 124, 22, 189, 1 }, }, - { { 189, 225, 119, 0 }, - { 214, 246, 31, 0 }, - { 119, 67, 222, 1 }, - { 124, 55, 181, 1 }, }, - { { 189, 224, 152, 0 }, - { 214, 48, 46, 1 }, - { 12, 131, 222, 1 }, - { 186, 6, 53, 1 }, }, - { { 189, 224, 155, 1 }, - { 222, 114, 46, 1 }, - { 236, 131, 222, 1 }, - { 186, 39, 61, 1 }, }, - { { 189, 224, 148, 1 }, - { 222, 48, 62, 0 }, - { 148, 131, 222, 1 }, - { 62, 6, 61, 1 }, }, - { { 189, 224, 151, 0 }, - { 214, 114, 62, 0 }, - { 116, 131, 222, 1 }, - { 62, 39, 53, 1 }, }, - { { 189, 224, 232, 0 }, - { 198, 180, 46, 1 }, - { 11, 131, 222, 1 }, - { 186, 22, 177, 1 }, }, - { { 189, 224, 235, 1 }, - { 206, 246, 46, 1 }, - { 235, 131, 222, 1 }, - { 186, 55, 185, 1 }, }, - { { 189, 224, 228, 1 }, - { 206, 180, 62, 0 }, - { 147, 131, 222, 1 }, - { 62, 22, 185, 1 }, }, - { { 189, 224, 231, 0 }, - { 198, 246, 62, 0 }, - { 115, 131, 222, 1 }, - { 62, 55, 177, 1 }, }, - { { 189, 239, 8, 0 }, - { 231, 56, 15, 1 }, - { 8, 123, 222, 1 }, - { 248, 14, 115, 1 }, }, - { { 189, 239, 11, 1 }, - { 239, 122, 15, 1 }, - { 232, 123, 222, 1 }, - { 248, 47, 123, 1 }, }, - { { 189, 239, 4, 1 }, - { 239, 56, 31, 0 }, - { 144, 123, 222, 1 }, - { 124, 14, 123, 1 }, }, - { { 189, 239, 7, 0 }, - { 231, 122, 31, 0 }, - { 112, 123, 222, 1 }, - { 124, 47, 115, 1 }, }, - { { 189, 239, 120, 0 }, - { 247, 188, 15, 1 }, - { 15, 123, 222, 1 }, - { 248, 30, 247, 1 }, }, - { { 189, 239, 123, 1 }, - { 255, 254, 15, 1 }, - { 239, 123, 222, 1 }, - { 248, 63, 255, 1 }, }, - { { 189, 239, 116, 1 }, - { 255, 188, 31, 0 }, - { 151, 123, 222, 1 }, - { 124, 30, 255, 1 }, }, - { { 189, 239, 119, 0 }, - { 247, 254, 31, 0 }, - { 119, 123, 222, 1 }, - { 124, 63, 247, 1 }, }, - { { 189, 238, 152, 0 }, - { 247, 56, 46, 1 }, - { 12, 187, 222, 1 }, - { 186, 14, 119, 1 }, }, - { { 189, 238, 155, 1 }, - { 255, 122, 46, 1 }, - { 236, 187, 222, 1 }, - { 186, 47, 127, 1 }, }, - { { 189, 238, 148, 1 }, - { 255, 56, 62, 0 }, - { 148, 187, 222, 1 }, - { 62, 14, 127, 1 }, }, - { { 189, 238, 151, 0 }, - { 247, 122, 62, 0 }, - { 116, 187, 222, 1 }, - { 62, 47, 119, 1 }, }, - { { 189, 238, 232, 0 }, - { 231, 188, 46, 1 }, - { 11, 187, 222, 1 }, - { 186, 30, 243, 1 }, }, - { { 189, 238, 235, 1 }, - { 239, 254, 46, 1 }, - { 235, 187, 222, 1 }, - { 186, 63, 251, 1 }, }, - { { 189, 238, 228, 1 }, - { 239, 188, 62, 0 }, - { 147, 187, 222, 1 }, - { 62, 30, 251, 1 }, }, - { { 189, 238, 231, 0 }, - { 231, 254, 62, 0 }, - { 115, 187, 222, 1 }, - { 62, 63, 243, 1 }, }, - { { 189, 211, 8, 0 }, - { 230, 48, 77, 1 }, - { 8, 101, 222, 1 }, - { 217, 6, 51, 1 }, }, - { { 189, 211, 11, 1 }, - { 238, 114, 77, 1 }, - { 232, 101, 222, 1 }, - { 217, 39, 59, 1 }, }, - { { 189, 211, 4, 1 }, - { 238, 48, 93, 0 }, - { 144, 101, 222, 1 }, - { 93, 6, 59, 1 }, }, - { { 189, 211, 7, 0 }, - { 230, 114, 93, 0 }, - { 112, 101, 222, 1 }, - { 93, 39, 51, 1 }, }, - { { 189, 211, 120, 0 }, - { 246, 180, 77, 1 }, - { 15, 101, 222, 1 }, - { 217, 22, 183, 1 }, }, - { { 189, 211, 123, 1 }, - { 254, 246, 77, 1 }, - { 239, 101, 222, 1 }, - { 217, 55, 191, 1 }, }, - { { 189, 211, 116, 1 }, - { 254, 180, 93, 0 }, - { 151, 101, 222, 1 }, - { 93, 22, 191, 1 }, }, - { { 189, 211, 119, 0 }, - { 246, 246, 93, 0 }, - { 119, 101, 222, 1 }, - { 93, 55, 183, 1 }, }, - { { 189, 210, 152, 0 }, - { 246, 48, 108, 1 }, - { 12, 165, 222, 1 }, - { 155, 6, 55, 1 }, }, - { { 189, 210, 155, 1 }, - { 254, 114, 108, 1 }, - { 236, 165, 222, 1 }, - { 155, 39, 63, 1 }, }, - { { 189, 210, 148, 1 }, - { 254, 48, 124, 0 }, - { 148, 165, 222, 1 }, - { 31, 6, 63, 1 }, }, - { { 189, 210, 151, 0 }, - { 246, 114, 124, 0 }, - { 116, 165, 222, 1 }, - { 31, 39, 55, 1 }, }, - { { 189, 210, 232, 0 }, - { 230, 180, 108, 1 }, - { 11, 165, 222, 1 }, - { 155, 22, 179, 1 }, }, - { { 189, 210, 235, 1 }, - { 238, 246, 108, 1 }, - { 235, 165, 222, 1 }, - { 155, 55, 187, 1 }, }, - { { 189, 210, 228, 1 }, - { 238, 180, 124, 0 }, - { 147, 165, 222, 1 }, - { 31, 22, 187, 1 }, }, - { { 189, 210, 231, 0 }, - { 230, 246, 124, 0 }, - { 115, 165, 222, 1 }, - { 31, 55, 179, 1 }, }, - { { 189, 221, 8, 0 }, - { 199, 56, 77, 1 }, - { 8, 93, 222, 1 }, - { 217, 14, 113, 1 }, }, - { { 189, 221, 11, 1 }, - { 207, 122, 77, 1 }, - { 232, 93, 222, 1 }, - { 217, 47, 121, 1 }, }, - { { 189, 221, 4, 1 }, - { 207, 56, 93, 0 }, - { 144, 93, 222, 1 }, - { 93, 14, 121, 1 }, }, - { { 189, 221, 7, 0 }, - { 199, 122, 93, 0 }, - { 112, 93, 222, 1 }, - { 93, 47, 113, 1 }, }, - { { 189, 221, 120, 0 }, - { 215, 188, 77, 1 }, - { 15, 93, 222, 1 }, - { 217, 30, 245, 1 }, }, - { { 189, 221, 123, 1 }, - { 223, 254, 77, 1 }, - { 239, 93, 222, 1 }, - { 217, 63, 253, 1 }, }, - { { 189, 221, 116, 1 }, - { 223, 188, 93, 0 }, - { 151, 93, 222, 1 }, - { 93, 30, 253, 1 }, }, - { { 189, 221, 119, 0 }, - { 215, 254, 93, 0 }, - { 119, 93, 222, 1 }, - { 93, 63, 245, 1 }, }, - { { 189, 220, 152, 0 }, - { 215, 56, 108, 1 }, - { 12, 157, 222, 1 }, - { 155, 14, 117, 1 }, }, - { { 189, 220, 155, 1 }, - { 223, 122, 108, 1 }, - { 236, 157, 222, 1 }, - { 155, 47, 125, 1 }, }, - { { 189, 220, 148, 1 }, - { 223, 56, 124, 0 }, - { 148, 157, 222, 1 }, - { 31, 14, 125, 1 }, }, - { { 189, 220, 151, 0 }, - { 215, 122, 124, 0 }, - { 116, 157, 222, 1 }, - { 31, 47, 117, 1 }, }, - { { 189, 220, 232, 0 }, - { 199, 188, 108, 1 }, - { 11, 157, 222, 1 }, - { 155, 30, 241, 1 }, }, - { { 189, 220, 235, 1 }, - { 207, 254, 108, 1 }, - { 235, 157, 222, 1 }, - { 155, 63, 249, 1 }, }, - { { 189, 220, 228, 1 }, - { 207, 188, 124, 0 }, - { 147, 157, 222, 1 }, - { 31, 30, 249, 1 }, }, - { { 189, 220, 231, 0 }, - { 199, 254, 124, 0 }, - { 115, 157, 222, 1 }, - { 31, 63, 241, 1 }, }, - { { 186, 97, 8, 0 }, - { 196, 32, 139, 1 }, - { 8, 67, 46, 1 }, - { 232, 130, 17, 1 }, }, - { { 186, 97, 11, 1 }, - { 204, 98, 139, 1 }, - { 232, 67, 46, 1 }, - { 232, 163, 25, 1 }, }, - { { 186, 97, 4, 1 }, - { 204, 32, 155, 0 }, - { 144, 67, 46, 1 }, - { 108, 130, 25, 1 }, }, - { { 186, 97, 7, 0 }, - { 196, 98, 155, 0 }, - { 112, 67, 46, 1 }, - { 108, 163, 17, 1 }, }, - { { 186, 97, 120, 0 }, - { 212, 164, 139, 1 }, - { 15, 67, 46, 1 }, - { 232, 146, 149, 1 }, }, - { { 186, 97, 123, 1 }, - { 220, 230, 139, 1 }, - { 239, 67, 46, 1 }, - { 232, 179, 157, 1 }, }, - { { 186, 97, 116, 1 }, - { 220, 164, 155, 0 }, - { 151, 67, 46, 1 }, - { 108, 146, 157, 1 }, }, - { { 186, 97, 119, 0 }, - { 212, 230, 155, 0 }, - { 119, 67, 46, 1 }, - { 108, 179, 149, 1 }, }, - { { 186, 96, 152, 0 }, - { 212, 32, 170, 1 }, - { 12, 131, 46, 1 }, - { 170, 130, 21, 1 }, }, - { { 186, 96, 155, 1 }, - { 220, 98, 170, 1 }, - { 236, 131, 46, 1 }, - { 170, 163, 29, 1 }, }, - { { 186, 96, 148, 1 }, - { 220, 32, 186, 0 }, - { 148, 131, 46, 1 }, - { 46, 130, 29, 1 }, }, - { { 186, 96, 151, 0 }, - { 212, 98, 186, 0 }, - { 116, 131, 46, 1 }, - { 46, 163, 21, 1 }, }, - { { 186, 96, 232, 0 }, - { 196, 164, 170, 1 }, - { 11, 131, 46, 1 }, - { 170, 146, 145, 1 }, }, - { { 186, 96, 235, 1 }, - { 204, 230, 170, 1 }, - { 235, 131, 46, 1 }, - { 170, 179, 153, 1 }, }, - { { 186, 96, 228, 1 }, - { 204, 164, 186, 0 }, - { 147, 131, 46, 1 }, - { 46, 146, 153, 1 }, }, - { { 186, 96, 231, 0 }, - { 196, 230, 186, 0 }, - { 115, 131, 46, 1 }, - { 46, 179, 145, 1 }, }, - { { 186, 111, 8, 0 }, - { 229, 40, 139, 1 }, - { 8, 123, 46, 1 }, - { 232, 138, 83, 1 }, }, - { { 186, 111, 11, 1 }, - { 237, 106, 139, 1 }, - { 232, 123, 46, 1 }, - { 232, 171, 91, 1 }, }, - { { 186, 111, 4, 1 }, - { 237, 40, 155, 0 }, - { 144, 123, 46, 1 }, - { 108, 138, 91, 1 }, }, - { { 186, 111, 7, 0 }, - { 229, 106, 155, 0 }, - { 112, 123, 46, 1 }, - { 108, 171, 83, 1 }, }, - { { 186, 111, 120, 0 }, - { 245, 172, 139, 1 }, - { 15, 123, 46, 1 }, - { 232, 154, 215, 1 }, }, - { { 186, 111, 123, 1 }, - { 253, 238, 139, 1 }, - { 239, 123, 46, 1 }, - { 232, 187, 223, 1 }, }, - { { 186, 111, 116, 1 }, - { 253, 172, 155, 0 }, - { 151, 123, 46, 1 }, - { 108, 154, 223, 1 }, }, - { { 186, 111, 119, 0 }, - { 245, 238, 155, 0 }, - { 119, 123, 46, 1 }, - { 108, 187, 215, 1 }, }, - { { 186, 110, 152, 0 }, - { 245, 40, 170, 1 }, - { 12, 187, 46, 1 }, - { 170, 138, 87, 1 }, }, - { { 186, 110, 155, 1 }, - { 253, 106, 170, 1 }, - { 236, 187, 46, 1 }, - { 170, 171, 95, 1 }, }, - { { 186, 110, 148, 1 }, - { 253, 40, 186, 0 }, - { 148, 187, 46, 1 }, - { 46, 138, 95, 1 }, }, - { { 186, 110, 151, 0 }, - { 245, 106, 186, 0 }, - { 116, 187, 46, 1 }, - { 46, 171, 87, 1 }, }, - { { 186, 110, 232, 0 }, - { 229, 172, 170, 1 }, - { 11, 187, 46, 1 }, - { 170, 154, 211, 1 }, }, - { { 186, 110, 235, 1 }, - { 237, 238, 170, 1 }, - { 235, 187, 46, 1 }, - { 170, 187, 219, 1 }, }, - { { 186, 110, 228, 1 }, - { 237, 172, 186, 0 }, - { 147, 187, 46, 1 }, - { 46, 154, 219, 1 }, }, - { { 186, 110, 231, 0 }, - { 229, 238, 186, 0 }, - { 115, 187, 46, 1 }, - { 46, 187, 211, 1 }, }, - { { 186, 83, 8, 0 }, - { 228, 32, 201, 1 }, - { 8, 101, 46, 1 }, - { 201, 130, 19, 1 }, }, - { { 186, 83, 11, 1 }, - { 236, 98, 201, 1 }, - { 232, 101, 46, 1 }, - { 201, 163, 27, 1 }, }, - { { 186, 83, 4, 1 }, - { 236, 32, 217, 0 }, - { 144, 101, 46, 1 }, - { 77, 130, 27, 1 }, }, - { { 186, 83, 7, 0 }, - { 228, 98, 217, 0 }, - { 112, 101, 46, 1 }, - { 77, 163, 19, 1 }, }, - { { 186, 83, 120, 0 }, - { 244, 164, 201, 1 }, - { 15, 101, 46, 1 }, - { 201, 146, 151, 1 }, }, - { { 186, 83, 123, 1 }, - { 252, 230, 201, 1 }, - { 239, 101, 46, 1 }, - { 201, 179, 159, 1 }, }, - { { 186, 83, 116, 1 }, - { 252, 164, 217, 0 }, - { 151, 101, 46, 1 }, - { 77, 146, 159, 1 }, }, - { { 186, 83, 119, 0 }, - { 244, 230, 217, 0 }, - { 119, 101, 46, 1 }, - { 77, 179, 151, 1 }, }, - { { 186, 82, 152, 0 }, - { 244, 32, 232, 1 }, - { 12, 165, 46, 1 }, - { 139, 130, 23, 1 }, }, - { { 186, 82, 155, 1 }, - { 252, 98, 232, 1 }, - { 236, 165, 46, 1 }, - { 139, 163, 31, 1 }, }, - { { 186, 82, 148, 1 }, - { 252, 32, 248, 0 }, - { 148, 165, 46, 1 }, - { 15, 130, 31, 1 }, }, - { { 186, 82, 151, 0 }, - { 244, 98, 248, 0 }, - { 116, 165, 46, 1 }, - { 15, 163, 23, 1 }, }, - { { 186, 82, 232, 0 }, - { 228, 164, 232, 1 }, - { 11, 165, 46, 1 }, - { 139, 146, 147, 1 }, }, - { { 186, 82, 235, 1 }, - { 236, 230, 232, 1 }, - { 235, 165, 46, 1 }, - { 139, 179, 155, 1 }, }, - { { 186, 82, 228, 1 }, - { 236, 164, 248, 0 }, - { 147, 165, 46, 1 }, - { 15, 146, 155, 1 }, }, - { { 186, 82, 231, 0 }, - { 228, 230, 248, 0 }, - { 115, 165, 46, 1 }, - { 15, 179, 147, 1 }, }, - { { 186, 93, 8, 0 }, - { 197, 40, 201, 1 }, - { 8, 93, 46, 1 }, - { 201, 138, 81, 1 }, }, - { { 186, 93, 11, 1 }, - { 205, 106, 201, 1 }, - { 232, 93, 46, 1 }, - { 201, 171, 89, 1 }, }, - { { 186, 93, 4, 1 }, - { 205, 40, 217, 0 }, - { 144, 93, 46, 1 }, - { 77, 138, 89, 1 }, }, - { { 186, 93, 7, 0 }, - { 197, 106, 217, 0 }, - { 112, 93, 46, 1 }, - { 77, 171, 81, 1 }, }, - { { 186, 93, 120, 0 }, - { 213, 172, 201, 1 }, - { 15, 93, 46, 1 }, - { 201, 154, 213, 1 }, }, - { { 186, 93, 123, 1 }, - { 221, 238, 201, 1 }, - { 239, 93, 46, 1 }, - { 201, 187, 221, 1 }, }, - { { 186, 93, 116, 1 }, - { 221, 172, 217, 0 }, - { 151, 93, 46, 1 }, - { 77, 154, 221, 1 }, }, - { { 186, 93, 119, 0 }, - { 213, 238, 217, 0 }, - { 119, 93, 46, 1 }, - { 77, 187, 213, 1 }, }, - { { 186, 92, 152, 0 }, - { 213, 40, 232, 1 }, - { 12, 157, 46, 1 }, - { 139, 138, 85, 1 }, }, - { { 186, 92, 155, 1 }, - { 221, 106, 232, 1 }, - { 236, 157, 46, 1 }, - { 139, 171, 93, 1 }, }, - { { 186, 92, 148, 1 }, - { 221, 40, 248, 0 }, - { 148, 157, 46, 1 }, - { 15, 138, 93, 1 }, }, - { { 186, 92, 151, 0 }, - { 213, 106, 248, 0 }, - { 116, 157, 46, 1 }, - { 15, 171, 85, 1 }, }, - { { 186, 92, 232, 0 }, - { 197, 172, 232, 1 }, - { 11, 157, 46, 1 }, - { 139, 154, 209, 1 }, }, - { { 186, 92, 235, 1 }, - { 205, 238, 232, 1 }, - { 235, 157, 46, 1 }, - { 139, 187, 217, 1 }, }, - { { 186, 92, 228, 1 }, - { 205, 172, 248, 0 }, - { 147, 157, 46, 1 }, - { 15, 154, 217, 1 }, }, - { { 186, 92, 231, 0 }, - { 197, 238, 248, 0 }, - { 115, 157, 46, 1 }, - { 15, 187, 209, 1 }, }, - { { 187, 161, 8, 0 }, - { 134, 48, 139, 1 }, - { 8, 66, 238, 1 }, - { 232, 134, 48, 1 }, }, - { { 187, 161, 11, 1 }, - { 142, 114, 139, 1 }, - { 232, 66, 238, 1 }, - { 232, 167, 56, 1 }, }, - { { 187, 161, 4, 1 }, - { 142, 48, 155, 0 }, - { 144, 66, 238, 1 }, - { 108, 134, 56, 1 }, }, - { { 187, 161, 7, 0 }, - { 134, 114, 155, 0 }, - { 112, 66, 238, 1 }, - { 108, 167, 48, 1 }, }, - { { 187, 161, 120, 0 }, - { 150, 180, 139, 1 }, - { 15, 66, 238, 1 }, - { 232, 150, 180, 1 }, }, - { { 187, 161, 123, 1 }, - { 158, 246, 139, 1 }, - { 239, 66, 238, 1 }, - { 232, 183, 188, 1 }, }, - { { 187, 161, 116, 1 }, - { 158, 180, 155, 0 }, - { 151, 66, 238, 1 }, - { 108, 150, 188, 1 }, }, - { { 187, 161, 119, 0 }, - { 150, 246, 155, 0 }, - { 119, 66, 238, 1 }, - { 108, 183, 180, 1 }, }, - { { 187, 160, 152, 0 }, - { 150, 48, 170, 1 }, - { 12, 130, 238, 1 }, - { 170, 134, 52, 1 }, }, - { { 187, 160, 155, 1 }, - { 158, 114, 170, 1 }, - { 236, 130, 238, 1 }, - { 170, 167, 60, 1 }, }, - { { 187, 160, 148, 1 }, - { 158, 48, 186, 0 }, - { 148, 130, 238, 1 }, - { 46, 134, 60, 1 }, }, - { { 187, 160, 151, 0 }, - { 150, 114, 186, 0 }, - { 116, 130, 238, 1 }, - { 46, 167, 52, 1 }, }, - { { 187, 160, 232, 0 }, - { 134, 180, 170, 1 }, - { 11, 130, 238, 1 }, - { 170, 150, 176, 1 }, }, - { { 187, 160, 235, 1 }, - { 142, 246, 170, 1 }, - { 235, 130, 238, 1 }, - { 170, 183, 184, 1 }, }, - { { 187, 160, 228, 1 }, - { 142, 180, 186, 0 }, - { 147, 130, 238, 1 }, - { 46, 150, 184, 1 }, }, - { { 187, 160, 231, 0 }, - { 134, 246, 186, 0 }, - { 115, 130, 238, 1 }, - { 46, 183, 176, 1 }, }, - { { 187, 175, 8, 0 }, - { 167, 56, 139, 1 }, - { 8, 122, 238, 1 }, - { 232, 142, 114, 1 }, }, - { { 187, 175, 11, 1 }, - { 175, 122, 139, 1 }, - { 232, 122, 238, 1 }, - { 232, 175, 122, 1 }, }, - { { 187, 175, 4, 1 }, - { 175, 56, 155, 0 }, - { 144, 122, 238, 1 }, - { 108, 142, 122, 1 }, }, - { { 187, 175, 7, 0 }, - { 167, 122, 155, 0 }, - { 112, 122, 238, 1 }, - { 108, 175, 114, 1 }, }, - { { 187, 175, 120, 0 }, - { 183, 188, 139, 1 }, - { 15, 122, 238, 1 }, - { 232, 158, 246, 1 }, }, - { { 187, 175, 123, 1 }, - { 191, 254, 139, 1 }, - { 239, 122, 238, 1 }, - { 232, 191, 254, 1 }, }, - { { 187, 175, 116, 1 }, - { 191, 188, 155, 0 }, - { 151, 122, 238, 1 }, - { 108, 158, 254, 1 }, }, - { { 187, 175, 119, 0 }, - { 183, 254, 155, 0 }, - { 119, 122, 238, 1 }, - { 108, 191, 246, 1 }, }, - { { 187, 174, 152, 0 }, - { 183, 56, 170, 1 }, - { 12, 186, 238, 1 }, - { 170, 142, 118, 1 }, }, - { { 187, 174, 155, 1 }, - { 191, 122, 170, 1 }, - { 236, 186, 238, 1 }, - { 170, 175, 126, 1 }, }, - { { 187, 174, 148, 1 }, - { 191, 56, 186, 0 }, - { 148, 186, 238, 1 }, - { 46, 142, 126, 1 }, }, - { { 187, 174, 151, 0 }, - { 183, 122, 186, 0 }, - { 116, 186, 238, 1 }, - { 46, 175, 118, 1 }, }, - { { 187, 174, 232, 0 }, - { 167, 188, 170, 1 }, - { 11, 186, 238, 1 }, - { 170, 158, 242, 1 }, }, - { { 187, 174, 235, 1 }, - { 175, 254, 170, 1 }, - { 235, 186, 238, 1 }, - { 170, 191, 250, 1 }, }, - { { 187, 174, 228, 1 }, - { 175, 188, 186, 0 }, - { 147, 186, 238, 1 }, - { 46, 158, 250, 1 }, }, - { { 187, 174, 231, 0 }, - { 167, 254, 186, 0 }, - { 115, 186, 238, 1 }, - { 46, 191, 242, 1 }, }, - { { 187, 147, 8, 0 }, - { 166, 48, 201, 1 }, - { 8, 100, 238, 1 }, - { 201, 134, 50, 1 }, }, - { { 187, 147, 11, 1 }, - { 174, 114, 201, 1 }, - { 232, 100, 238, 1 }, - { 201, 167, 58, 1 }, }, - { { 187, 147, 4, 1 }, - { 174, 48, 217, 0 }, - { 144, 100, 238, 1 }, - { 77, 134, 58, 1 }, }, - { { 187, 147, 7, 0 }, - { 166, 114, 217, 0 }, - { 112, 100, 238, 1 }, - { 77, 167, 50, 1 }, }, - { { 187, 147, 120, 0 }, - { 182, 180, 201, 1 }, - { 15, 100, 238, 1 }, - { 201, 150, 182, 1 }, }, - { { 187, 147, 123, 1 }, - { 190, 246, 201, 1 }, - { 239, 100, 238, 1 }, - { 201, 183, 190, 1 }, }, - { { 187, 147, 116, 1 }, - { 190, 180, 217, 0 }, - { 151, 100, 238, 1 }, - { 77, 150, 190, 1 }, }, - { { 187, 147, 119, 0 }, - { 182, 246, 217, 0 }, - { 119, 100, 238, 1 }, - { 77, 183, 182, 1 }, }, - { { 187, 146, 152, 0 }, - { 182, 48, 232, 1 }, - { 12, 164, 238, 1 }, - { 139, 134, 54, 1 }, }, - { { 187, 146, 155, 1 }, - { 190, 114, 232, 1 }, - { 236, 164, 238, 1 }, - { 139, 167, 62, 1 }, }, - { { 187, 146, 148, 1 }, - { 190, 48, 248, 0 }, - { 148, 164, 238, 1 }, - { 15, 134, 62, 1 }, }, - { { 187, 146, 151, 0 }, - { 182, 114, 248, 0 }, - { 116, 164, 238, 1 }, - { 15, 167, 54, 1 }, }, - { { 187, 146, 232, 0 }, - { 166, 180, 232, 1 }, - { 11, 164, 238, 1 }, - { 139, 150, 178, 1 }, }, - { { 187, 146, 235, 1 }, - { 174, 246, 232, 1 }, - { 235, 164, 238, 1 }, - { 139, 183, 186, 1 }, }, - { { 187, 146, 228, 1 }, - { 174, 180, 248, 0 }, - { 147, 164, 238, 1 }, - { 15, 150, 186, 1 }, }, - { { 187, 146, 231, 0 }, - { 166, 246, 248, 0 }, - { 115, 164, 238, 1 }, - { 15, 183, 178, 1 }, }, - { { 187, 157, 8, 0 }, - { 135, 56, 201, 1 }, - { 8, 92, 238, 1 }, - { 201, 142, 112, 1 }, }, - { { 187, 157, 11, 1 }, - { 143, 122, 201, 1 }, - { 232, 92, 238, 1 }, - { 201, 175, 120, 1 }, }, - { { 187, 157, 4, 1 }, - { 143, 56, 217, 0 }, - { 144, 92, 238, 1 }, - { 77, 142, 120, 1 }, }, - { { 187, 157, 7, 0 }, - { 135, 122, 217, 0 }, - { 112, 92, 238, 1 }, - { 77, 175, 112, 1 }, }, - { { 187, 157, 120, 0 }, - { 151, 188, 201, 1 }, - { 15, 92, 238, 1 }, - { 201, 158, 244, 1 }, }, - { { 187, 157, 123, 1 }, - { 159, 254, 201, 1 }, - { 239, 92, 238, 1 }, - { 201, 191, 252, 1 }, }, - { { 187, 157, 116, 1 }, - { 159, 188, 217, 0 }, - { 151, 92, 238, 1 }, - { 77, 158, 252, 1 }, }, - { { 187, 157, 119, 0 }, - { 151, 254, 217, 0 }, - { 119, 92, 238, 1 }, - { 77, 191, 244, 1 }, }, - { { 187, 156, 152, 0 }, - { 151, 56, 232, 1 }, - { 12, 156, 238, 1 }, - { 139, 142, 116, 1 }, }, - { { 187, 156, 155, 1 }, - { 159, 122, 232, 1 }, - { 236, 156, 238, 1 }, - { 139, 175, 124, 1 }, }, - { { 187, 156, 148, 1 }, - { 159, 56, 248, 0 }, - { 148, 156, 238, 1 }, - { 15, 142, 124, 1 }, }, - { { 187, 156, 151, 0 }, - { 151, 122, 248, 0 }, - { 116, 156, 238, 1 }, - { 15, 175, 116, 1 }, }, - { { 187, 156, 232, 0 }, - { 135, 188, 232, 1 }, - { 11, 156, 238, 1 }, - { 139, 158, 240, 1 }, }, - { { 187, 156, 235, 1 }, - { 143, 254, 232, 1 }, - { 235, 156, 238, 1 }, - { 139, 191, 248, 1 }, }, - { { 187, 156, 228, 1 }, - { 143, 188, 248, 0 }, - { 147, 156, 238, 1 }, - { 15, 158, 248, 1 }, }, - { { 187, 156, 231, 0 }, - { 135, 254, 248, 0 }, - { 115, 156, 238, 1 }, - { 15, 191, 240, 1 }, }, - { { 76, 33, 8, 0 }, - { 128, 1, 7, 1 }, - { 8, 66, 25, 0 }, - { 240, 64, 0, 1 }, }, - { { 76, 33, 11, 1 }, - { 136, 67, 7, 1 }, - { 232, 66, 25, 0 }, - { 240, 97, 8, 1 }, }, - { { 76, 33, 4, 1 }, - { 136, 1, 23, 0 }, - { 144, 66, 25, 0 }, - { 116, 64, 8, 1 }, }, - { { 76, 33, 7, 0 }, - { 128, 67, 23, 0 }, - { 112, 66, 25, 0 }, - { 116, 97, 0, 1 }, }, - { { 76, 33, 120, 0 }, - { 144, 133, 7, 1 }, - { 15, 66, 25, 0 }, - { 240, 80, 132, 1 }, }, - { { 76, 33, 123, 1 }, - { 152, 199, 7, 1 }, - { 239, 66, 25, 0 }, - { 240, 113, 140, 1 }, }, - { { 76, 33, 116, 1 }, - { 152, 133, 23, 0 }, - { 151, 66, 25, 0 }, - { 116, 80, 140, 1 }, }, - { { 76, 33, 119, 0 }, - { 144, 199, 23, 0 }, - { 119, 66, 25, 0 }, - { 116, 113, 132, 1 }, }, - { { 76, 32, 152, 0 }, - { 144, 1, 38, 1 }, - { 12, 130, 25, 0 }, - { 178, 64, 4, 1 }, }, - { { 76, 32, 155, 1 }, - { 152, 67, 38, 1 }, - { 236, 130, 25, 0 }, - { 178, 97, 12, 1 }, }, - { { 76, 32, 148, 1 }, - { 152, 1, 54, 0 }, - { 148, 130, 25, 0 }, - { 54, 64, 12, 1 }, }, - { { 76, 32, 151, 0 }, - { 144, 67, 54, 0 }, - { 116, 130, 25, 0 }, - { 54, 97, 4, 1 }, }, - { { 76, 32, 232, 0 }, - { 128, 133, 38, 1 }, - { 11, 130, 25, 0 }, - { 178, 80, 128, 1 }, }, - { { 76, 32, 235, 1 }, - { 136, 199, 38, 1 }, - { 235, 130, 25, 0 }, - { 178, 113, 136, 1 }, }, - { { 76, 32, 228, 1 }, - { 136, 133, 54, 0 }, - { 147, 130, 25, 0 }, - { 54, 80, 136, 1 }, }, - { { 76, 32, 231, 0 }, - { 128, 199, 54, 0 }, - { 115, 130, 25, 0 }, - { 54, 113, 128, 1 }, }, - { { 76, 47, 8, 0 }, - { 161, 9, 7, 1 }, - { 8, 122, 25, 0 }, - { 240, 72, 66, 1 }, }, - { { 76, 47, 11, 1 }, - { 169, 75, 7, 1 }, - { 232, 122, 25, 0 }, - { 240, 105, 74, 1 }, }, - { { 76, 47, 4, 1 }, - { 169, 9, 23, 0 }, - { 144, 122, 25, 0 }, - { 116, 72, 74, 1 }, }, - { { 76, 47, 7, 0 }, - { 161, 75, 23, 0 }, - { 112, 122, 25, 0 }, - { 116, 105, 66, 1 }, }, - { { 76, 47, 120, 0 }, - { 177, 141, 7, 1 }, - { 15, 122, 25, 0 }, - { 240, 88, 198, 1 }, }, - { { 76, 47, 123, 1 }, - { 185, 207, 7, 1 }, - { 239, 122, 25, 0 }, - { 240, 121, 206, 1 }, }, - { { 76, 47, 116, 1 }, - { 185, 141, 23, 0 }, - { 151, 122, 25, 0 }, - { 116, 88, 206, 1 }, }, - { { 76, 47, 119, 0 }, - { 177, 207, 23, 0 }, - { 119, 122, 25, 0 }, - { 116, 121, 198, 1 }, }, - { { 76, 46, 152, 0 }, - { 177, 9, 38, 1 }, - { 12, 186, 25, 0 }, - { 178, 72, 70, 1 }, }, - { { 76, 46, 155, 1 }, - { 185, 75, 38, 1 }, - { 236, 186, 25, 0 }, - { 178, 105, 78, 1 }, }, - { { 76, 46, 148, 1 }, - { 185, 9, 54, 0 }, - { 148, 186, 25, 0 }, - { 54, 72, 78, 1 }, }, - { { 76, 46, 151, 0 }, - { 177, 75, 54, 0 }, - { 116, 186, 25, 0 }, - { 54, 105, 70, 1 }, }, - { { 76, 46, 232, 0 }, - { 161, 141, 38, 1 }, - { 11, 186, 25, 0 }, - { 178, 88, 194, 1 }, }, - { { 76, 46, 235, 1 }, - { 169, 207, 38, 1 }, - { 235, 186, 25, 0 }, - { 178, 121, 202, 1 }, }, - { { 76, 46, 228, 1 }, - { 169, 141, 54, 0 }, - { 147, 186, 25, 0 }, - { 54, 88, 202, 1 }, }, - { { 76, 46, 231, 0 }, - { 161, 207, 54, 0 }, - { 115, 186, 25, 0 }, - { 54, 121, 194, 1 }, }, - { { 76, 19, 8, 0 }, - { 160, 1, 69, 1 }, - { 8, 100, 25, 0 }, - { 209, 64, 2, 1 }, }, - { { 76, 19, 11, 1 }, - { 168, 67, 69, 1 }, - { 232, 100, 25, 0 }, - { 209, 97, 10, 1 }, }, - { { 76, 19, 4, 1 }, - { 168, 1, 85, 0 }, - { 144, 100, 25, 0 }, - { 85, 64, 10, 1 }, }, - { { 76, 19, 7, 0 }, - { 160, 67, 85, 0 }, - { 112, 100, 25, 0 }, - { 85, 97, 2, 1 }, }, - { { 76, 19, 120, 0 }, - { 176, 133, 69, 1 }, - { 15, 100, 25, 0 }, - { 209, 80, 134, 1 }, }, - { { 76, 19, 123, 1 }, - { 184, 199, 69, 1 }, - { 239, 100, 25, 0 }, - { 209, 113, 142, 1 }, }, - { { 76, 19, 116, 1 }, - { 184, 133, 85, 0 }, - { 151, 100, 25, 0 }, - { 85, 80, 142, 1 }, }, - { { 76, 19, 119, 0 }, - { 176, 199, 85, 0 }, - { 119, 100, 25, 0 }, - { 85, 113, 134, 1 }, }, - { { 76, 18, 152, 0 }, - { 176, 1, 100, 1 }, - { 12, 164, 25, 0 }, - { 147, 64, 6, 1 }, }, - { { 76, 18, 155, 1 }, - { 184, 67, 100, 1 }, - { 236, 164, 25, 0 }, - { 147, 97, 14, 1 }, }, - { { 76, 18, 148, 1 }, - { 184, 1, 116, 0 }, - { 148, 164, 25, 0 }, - { 23, 64, 14, 1 }, }, - { { 76, 18, 151, 0 }, - { 176, 67, 116, 0 }, - { 116, 164, 25, 0 }, - { 23, 97, 6, 1 }, }, - { { 76, 18, 232, 0 }, - { 160, 133, 100, 1 }, - { 11, 164, 25, 0 }, - { 147, 80, 130, 1 }, }, - { { 76, 18, 235, 1 }, - { 168, 199, 100, 1 }, - { 235, 164, 25, 0 }, - { 147, 113, 138, 1 }, }, - { { 76, 18, 228, 1 }, - { 168, 133, 116, 0 }, - { 147, 164, 25, 0 }, - { 23, 80, 138, 1 }, }, - { { 76, 18, 231, 0 }, - { 160, 199, 116, 0 }, - { 115, 164, 25, 0 }, - { 23, 113, 130, 1 }, }, - { { 76, 29, 8, 0 }, - { 129, 9, 69, 1 }, - { 8, 92, 25, 0 }, - { 209, 72, 64, 1 }, }, - { { 76, 29, 11, 1 }, - { 137, 75, 69, 1 }, - { 232, 92, 25, 0 }, - { 209, 105, 72, 1 }, }, - { { 76, 29, 4, 1 }, - { 137, 9, 85, 0 }, - { 144, 92, 25, 0 }, - { 85, 72, 72, 1 }, }, - { { 76, 29, 7, 0 }, - { 129, 75, 85, 0 }, - { 112, 92, 25, 0 }, - { 85, 105, 64, 1 }, }, - { { 76, 29, 120, 0 }, - { 145, 141, 69, 1 }, - { 15, 92, 25, 0 }, - { 209, 88, 196, 1 }, }, - { { 76, 29, 123, 1 }, - { 153, 207, 69, 1 }, - { 239, 92, 25, 0 }, - { 209, 121, 204, 1 }, }, - { { 76, 29, 116, 1 }, - { 153, 141, 85, 0 }, - { 151, 92, 25, 0 }, - { 85, 88, 204, 1 }, }, - { { 76, 29, 119, 0 }, - { 145, 207, 85, 0 }, - { 119, 92, 25, 0 }, - { 85, 121, 196, 1 }, }, - { { 76, 28, 152, 0 }, - { 145, 9, 100, 1 }, - { 12, 156, 25, 0 }, - { 147, 72, 68, 1 }, }, - { { 76, 28, 155, 1 }, - { 153, 75, 100, 1 }, - { 236, 156, 25, 0 }, - { 147, 105, 76, 1 }, }, - { { 76, 28, 148, 1 }, - { 153, 9, 116, 0 }, - { 148, 156, 25, 0 }, - { 23, 72, 76, 1 }, }, - { { 76, 28, 151, 0 }, - { 145, 75, 116, 0 }, - { 116, 156, 25, 0 }, - { 23, 105, 68, 1 }, }, - { { 76, 28, 232, 0 }, - { 129, 141, 100, 1 }, - { 11, 156, 25, 0 }, - { 147, 88, 192, 1 }, }, - { { 76, 28, 235, 1 }, - { 137, 207, 100, 1 }, - { 235, 156, 25, 0 }, - { 147, 121, 200, 1 }, }, - { { 76, 28, 228, 1 }, - { 137, 141, 116, 0 }, - { 147, 156, 25, 0 }, - { 23, 88, 200, 1 }, }, - { { 76, 28, 231, 0 }, - { 129, 207, 116, 0 }, - { 115, 156, 25, 0 }, - { 23, 121, 192, 1 }, }, - { { 77, 225, 8, 0 }, - { 194, 17, 7, 1 }, - { 8, 67, 217, 0 }, - { 240, 68, 33, 1 }, }, - { { 77, 225, 11, 1 }, - { 202, 83, 7, 1 }, - { 232, 67, 217, 0 }, - { 240, 101, 41, 1 }, }, - { { 77, 225, 4, 1 }, - { 202, 17, 23, 0 }, - { 144, 67, 217, 0 }, - { 116, 68, 41, 1 }, }, - { { 77, 225, 7, 0 }, - { 194, 83, 23, 0 }, - { 112, 67, 217, 0 }, - { 116, 101, 33, 1 }, }, - { { 77, 225, 120, 0 }, - { 210, 149, 7, 1 }, - { 15, 67, 217, 0 }, - { 240, 84, 165, 1 }, }, - { { 77, 225, 123, 1 }, - { 218, 215, 7, 1 }, - { 239, 67, 217, 0 }, - { 240, 117, 173, 1 }, }, - { { 77, 225, 116, 1 }, - { 218, 149, 23, 0 }, - { 151, 67, 217, 0 }, - { 116, 84, 173, 1 }, }, - { { 77, 225, 119, 0 }, - { 210, 215, 23, 0 }, - { 119, 67, 217, 0 }, - { 116, 117, 165, 1 }, }, - { { 77, 224, 152, 0 }, - { 210, 17, 38, 1 }, - { 12, 131, 217, 0 }, - { 178, 68, 37, 1 }, }, - { { 77, 224, 155, 1 }, - { 218, 83, 38, 1 }, - { 236, 131, 217, 0 }, - { 178, 101, 45, 1 }, }, - { { 77, 224, 148, 1 }, - { 218, 17, 54, 0 }, - { 148, 131, 217, 0 }, - { 54, 68, 45, 1 }, }, - { { 77, 224, 151, 0 }, - { 210, 83, 54, 0 }, - { 116, 131, 217, 0 }, - { 54, 101, 37, 1 }, }, - { { 77, 224, 232, 0 }, - { 194, 149, 38, 1 }, - { 11, 131, 217, 0 }, - { 178, 84, 161, 1 }, }, - { { 77, 224, 235, 1 }, - { 202, 215, 38, 1 }, - { 235, 131, 217, 0 }, - { 178, 117, 169, 1 }, }, - { { 77, 224, 228, 1 }, - { 202, 149, 54, 0 }, - { 147, 131, 217, 0 }, - { 54, 84, 169, 1 }, }, - { { 77, 224, 231, 0 }, - { 194, 215, 54, 0 }, - { 115, 131, 217, 0 }, - { 54, 117, 161, 1 }, }, - { { 77, 239, 8, 0 }, - { 227, 25, 7, 1 }, - { 8, 123, 217, 0 }, - { 240, 76, 99, 1 }, }, - { { 77, 239, 11, 1 }, - { 235, 91, 7, 1 }, - { 232, 123, 217, 0 }, - { 240, 109, 107, 1 }, }, - { { 77, 239, 4, 1 }, - { 235, 25, 23, 0 }, - { 144, 123, 217, 0 }, - { 116, 76, 107, 1 }, }, - { { 77, 239, 7, 0 }, - { 227, 91, 23, 0 }, - { 112, 123, 217, 0 }, - { 116, 109, 99, 1 }, }, - { { 77, 239, 120, 0 }, - { 243, 157, 7, 1 }, - { 15, 123, 217, 0 }, - { 240, 92, 231, 1 }, }, - { { 77, 239, 123, 1 }, - { 251, 223, 7, 1 }, - { 239, 123, 217, 0 }, - { 240, 125, 239, 1 }, }, - { { 77, 239, 116, 1 }, - { 251, 157, 23, 0 }, - { 151, 123, 217, 0 }, - { 116, 92, 239, 1 }, }, - { { 77, 239, 119, 0 }, - { 243, 223, 23, 0 }, - { 119, 123, 217, 0 }, - { 116, 125, 231, 1 }, }, - { { 77, 238, 152, 0 }, - { 243, 25, 38, 1 }, - { 12, 187, 217, 0 }, - { 178, 76, 103, 1 }, }, - { { 77, 238, 155, 1 }, - { 251, 91, 38, 1 }, - { 236, 187, 217, 0 }, - { 178, 109, 111, 1 }, }, - { { 77, 238, 148, 1 }, - { 251, 25, 54, 0 }, - { 148, 187, 217, 0 }, - { 54, 76, 111, 1 }, }, - { { 77, 238, 151, 0 }, - { 243, 91, 54, 0 }, - { 116, 187, 217, 0 }, - { 54, 109, 103, 1 }, }, - { { 77, 238, 232, 0 }, - { 227, 157, 38, 1 }, - { 11, 187, 217, 0 }, - { 178, 92, 227, 1 }, }, - { { 77, 238, 235, 1 }, - { 235, 223, 38, 1 }, - { 235, 187, 217, 0 }, - { 178, 125, 235, 1 }, }, - { { 77, 238, 228, 1 }, - { 235, 157, 54, 0 }, - { 147, 187, 217, 0 }, - { 54, 92, 235, 1 }, }, - { { 77, 238, 231, 0 }, - { 227, 223, 54, 0 }, - { 115, 187, 217, 0 }, - { 54, 125, 227, 1 }, }, - { { 77, 211, 8, 0 }, - { 226, 17, 69, 1 }, - { 8, 101, 217, 0 }, - { 209, 68, 35, 1 }, }, - { { 77, 211, 11, 1 }, - { 234, 83, 69, 1 }, - { 232, 101, 217, 0 }, - { 209, 101, 43, 1 }, }, - { { 77, 211, 4, 1 }, - { 234, 17, 85, 0 }, - { 144, 101, 217, 0 }, - { 85, 68, 43, 1 }, }, - { { 77, 211, 7, 0 }, - { 226, 83, 85, 0 }, - { 112, 101, 217, 0 }, - { 85, 101, 35, 1 }, }, - { { 77, 211, 120, 0 }, - { 242, 149, 69, 1 }, - { 15, 101, 217, 0 }, - { 209, 84, 167, 1 }, }, - { { 77, 211, 123, 1 }, - { 250, 215, 69, 1 }, - { 239, 101, 217, 0 }, - { 209, 117, 175, 1 }, }, - { { 77, 211, 116, 1 }, - { 250, 149, 85, 0 }, - { 151, 101, 217, 0 }, - { 85, 84, 175, 1 }, }, - { { 77, 211, 119, 0 }, - { 242, 215, 85, 0 }, - { 119, 101, 217, 0 }, - { 85, 117, 167, 1 }, }, - { { 77, 210, 152, 0 }, - { 242, 17, 100, 1 }, - { 12, 165, 217, 0 }, - { 147, 68, 39, 1 }, }, - { { 77, 210, 155, 1 }, - { 250, 83, 100, 1 }, - { 236, 165, 217, 0 }, - { 147, 101, 47, 1 }, }, - { { 77, 210, 148, 1 }, - { 250, 17, 116, 0 }, - { 148, 165, 217, 0 }, - { 23, 68, 47, 1 }, }, - { { 77, 210, 151, 0 }, - { 242, 83, 116, 0 }, - { 116, 165, 217, 0 }, - { 23, 101, 39, 1 }, }, - { { 77, 210, 232, 0 }, - { 226, 149, 100, 1 }, - { 11, 165, 217, 0 }, - { 147, 84, 163, 1 }, }, - { { 77, 210, 235, 1 }, - { 234, 215, 100, 1 }, - { 235, 165, 217, 0 }, - { 147, 117, 171, 1 }, }, - { { 77, 210, 228, 1 }, - { 234, 149, 116, 0 }, - { 147, 165, 217, 0 }, - { 23, 84, 171, 1 }, }, - { { 77, 210, 231, 0 }, - { 226, 215, 116, 0 }, - { 115, 165, 217, 0 }, - { 23, 117, 163, 1 }, }, - { { 77, 221, 8, 0 }, - { 195, 25, 69, 1 }, - { 8, 93, 217, 0 }, - { 209, 76, 97, 1 }, }, - { { 77, 221, 11, 1 }, - { 203, 91, 69, 1 }, - { 232, 93, 217, 0 }, - { 209, 109, 105, 1 }, }, - { { 77, 221, 4, 1 }, - { 203, 25, 85, 0 }, - { 144, 93, 217, 0 }, - { 85, 76, 105, 1 }, }, - { { 77, 221, 7, 0 }, - { 195, 91, 85, 0 }, - { 112, 93, 217, 0 }, - { 85, 109, 97, 1 }, }, - { { 77, 221, 120, 0 }, - { 211, 157, 69, 1 }, - { 15, 93, 217, 0 }, - { 209, 92, 229, 1 }, }, - { { 77, 221, 123, 1 }, - { 219, 223, 69, 1 }, - { 239, 93, 217, 0 }, - { 209, 125, 237, 1 }, }, - { { 77, 221, 116, 1 }, - { 219, 157, 85, 0 }, - { 151, 93, 217, 0 }, - { 85, 92, 237, 1 }, }, - { { 77, 221, 119, 0 }, - { 211, 223, 85, 0 }, - { 119, 93, 217, 0 }, - { 85, 125, 229, 1 }, }, - { { 77, 220, 152, 0 }, - { 211, 25, 100, 1 }, - { 12, 157, 217, 0 }, - { 147, 76, 101, 1 }, }, - { { 77, 220, 155, 1 }, - { 219, 91, 100, 1 }, - { 236, 157, 217, 0 }, - { 147, 109, 109, 1 }, }, - { { 77, 220, 148, 1 }, - { 219, 25, 116, 0 }, - { 148, 157, 217, 0 }, - { 23, 76, 109, 1 }, }, - { { 77, 220, 151, 0 }, - { 211, 91, 116, 0 }, - { 116, 157, 217, 0 }, - { 23, 109, 101, 1 }, }, - { { 77, 220, 232, 0 }, - { 195, 157, 100, 1 }, - { 11, 157, 217, 0 }, - { 147, 92, 225, 1 }, }, - { { 77, 220, 235, 1 }, - { 203, 223, 100, 1 }, - { 235, 157, 217, 0 }, - { 147, 125, 233, 1 }, }, - { { 77, 220, 228, 1 }, - { 203, 157, 116, 0 }, - { 147, 157, 217, 0 }, - { 23, 92, 233, 1 }, }, - { { 77, 220, 231, 0 }, - { 195, 223, 116, 0 }, - { 115, 157, 217, 0 }, - { 23, 125, 225, 1 }, }, - { { 74, 97, 8, 0 }, - { 192, 1, 131, 1 }, - { 8, 67, 41, 0 }, - { 224, 192, 1, 1 }, }, - { { 74, 97, 11, 1 }, - { 200, 67, 131, 1 }, - { 232, 67, 41, 0 }, - { 224, 225, 9, 1 }, }, - { { 74, 97, 4, 1 }, - { 200, 1, 147, 0 }, - { 144, 67, 41, 0 }, - { 100, 192, 9, 1 }, }, - { { 74, 97, 7, 0 }, - { 192, 67, 147, 0 }, - { 112, 67, 41, 0 }, - { 100, 225, 1, 1 }, }, - { { 74, 97, 120, 0 }, - { 208, 133, 131, 1 }, - { 15, 67, 41, 0 }, - { 224, 208, 133, 1 }, }, - { { 74, 97, 123, 1 }, - { 216, 199, 131, 1 }, - { 239, 67, 41, 0 }, - { 224, 241, 141, 1 }, }, - { { 74, 97, 116, 1 }, - { 216, 133, 147, 0 }, - { 151, 67, 41, 0 }, - { 100, 208, 141, 1 }, }, - { { 74, 97, 119, 0 }, - { 208, 199, 147, 0 }, - { 119, 67, 41, 0 }, - { 100, 241, 133, 1 }, }, - { { 74, 96, 152, 0 }, - { 208, 1, 162, 1 }, - { 12, 131, 41, 0 }, - { 162, 192, 5, 1 }, }, - { { 74, 96, 155, 1 }, - { 216, 67, 162, 1 }, - { 236, 131, 41, 0 }, - { 162, 225, 13, 1 }, }, - { { 74, 96, 148, 1 }, - { 216, 1, 178, 0 }, - { 148, 131, 41, 0 }, - { 38, 192, 13, 1 }, }, - { { 74, 96, 151, 0 }, - { 208, 67, 178, 0 }, - { 116, 131, 41, 0 }, - { 38, 225, 5, 1 }, }, - { { 74, 96, 232, 0 }, - { 192, 133, 162, 1 }, - { 11, 131, 41, 0 }, - { 162, 208, 129, 1 }, }, - { { 74, 96, 235, 1 }, - { 200, 199, 162, 1 }, - { 235, 131, 41, 0 }, - { 162, 241, 137, 1 }, }, - { { 74, 96, 228, 1 }, - { 200, 133, 178, 0 }, - { 147, 131, 41, 0 }, - { 38, 208, 137, 1 }, }, - { { 74, 96, 231, 0 }, - { 192, 199, 178, 0 }, - { 115, 131, 41, 0 }, - { 38, 241, 129, 1 }, }, - { { 74, 111, 8, 0 }, - { 225, 9, 131, 1 }, - { 8, 123, 41, 0 }, - { 224, 200, 67, 1 }, }, - { { 74, 111, 11, 1 }, - { 233, 75, 131, 1 }, - { 232, 123, 41, 0 }, - { 224, 233, 75, 1 }, }, - { { 74, 111, 4, 1 }, - { 233, 9, 147, 0 }, - { 144, 123, 41, 0 }, - { 100, 200, 75, 1 }, }, - { { 74, 111, 7, 0 }, - { 225, 75, 147, 0 }, - { 112, 123, 41, 0 }, - { 100, 233, 67, 1 }, }, - { { 74, 111, 120, 0 }, - { 241, 141, 131, 1 }, - { 15, 123, 41, 0 }, - { 224, 216, 199, 1 }, }, - { { 74, 111, 123, 1 }, - { 249, 207, 131, 1 }, - { 239, 123, 41, 0 }, - { 224, 249, 207, 1 }, }, - { { 74, 111, 116, 1 }, - { 249, 141, 147, 0 }, - { 151, 123, 41, 0 }, - { 100, 216, 207, 1 }, }, - { { 74, 111, 119, 0 }, - { 241, 207, 147, 0 }, - { 119, 123, 41, 0 }, - { 100, 249, 199, 1 }, }, - { { 74, 110, 152, 0 }, - { 241, 9, 162, 1 }, - { 12, 187, 41, 0 }, - { 162, 200, 71, 1 }, }, - { { 74, 110, 155, 1 }, - { 249, 75, 162, 1 }, - { 236, 187, 41, 0 }, - { 162, 233, 79, 1 }, }, - { { 74, 110, 148, 1 }, - { 249, 9, 178, 0 }, - { 148, 187, 41, 0 }, - { 38, 200, 79, 1 }, }, - { { 74, 110, 151, 0 }, - { 241, 75, 178, 0 }, - { 116, 187, 41, 0 }, - { 38, 233, 71, 1 }, }, - { { 74, 110, 232, 0 }, - { 225, 141, 162, 1 }, - { 11, 187, 41, 0 }, - { 162, 216, 195, 1 }, }, - { { 74, 110, 235, 1 }, - { 233, 207, 162, 1 }, - { 235, 187, 41, 0 }, - { 162, 249, 203, 1 }, }, - { { 74, 110, 228, 1 }, - { 233, 141, 178, 0 }, - { 147, 187, 41, 0 }, - { 38, 216, 203, 1 }, }, - { { 74, 110, 231, 0 }, - { 225, 207, 178, 0 }, - { 115, 187, 41, 0 }, - { 38, 249, 195, 1 }, }, - { { 74, 83, 8, 0 }, - { 224, 1, 193, 1 }, - { 8, 101, 41, 0 }, - { 193, 192, 3, 1 }, }, - { { 74, 83, 11, 1 }, - { 232, 67, 193, 1 }, - { 232, 101, 41, 0 }, - { 193, 225, 11, 1 }, }, - { { 74, 83, 4, 1 }, - { 232, 1, 209, 0 }, - { 144, 101, 41, 0 }, - { 69, 192, 11, 1 }, }, - { { 74, 83, 7, 0 }, - { 224, 67, 209, 0 }, - { 112, 101, 41, 0 }, - { 69, 225, 3, 1 }, }, - { { 74, 83, 120, 0 }, - { 240, 133, 193, 1 }, - { 15, 101, 41, 0 }, - { 193, 208, 135, 1 }, }, - { { 74, 83, 123, 1 }, - { 248, 199, 193, 1 }, - { 239, 101, 41, 0 }, - { 193, 241, 143, 1 }, }, - { { 74, 83, 116, 1 }, - { 248, 133, 209, 0 }, - { 151, 101, 41, 0 }, - { 69, 208, 143, 1 }, }, - { { 74, 83, 119, 0 }, - { 240, 199, 209, 0 }, - { 119, 101, 41, 0 }, - { 69, 241, 135, 1 }, }, - { { 74, 82, 152, 0 }, - { 240, 1, 224, 1 }, - { 12, 165, 41, 0 }, - { 131, 192, 7, 1 }, }, - { { 74, 82, 155, 1 }, - { 248, 67, 224, 1 }, - { 236, 165, 41, 0 }, - { 131, 225, 15, 1 }, }, - { { 74, 82, 148, 1 }, - { 248, 1, 240, 0 }, - { 148, 165, 41, 0 }, - { 7, 192, 15, 1 }, }, - { { 74, 82, 151, 0 }, - { 240, 67, 240, 0 }, - { 116, 165, 41, 0 }, - { 7, 225, 7, 1 }, }, - { { 74, 82, 232, 0 }, - { 224, 133, 224, 1 }, - { 11, 165, 41, 0 }, - { 131, 208, 131, 1 }, }, - { { 74, 82, 235, 1 }, - { 232, 199, 224, 1 }, - { 235, 165, 41, 0 }, - { 131, 241, 139, 1 }, }, - { { 74, 82, 228, 1 }, - { 232, 133, 240, 0 }, - { 147, 165, 41, 0 }, - { 7, 208, 139, 1 }, }, - { { 74, 82, 231, 0 }, - { 224, 199, 240, 0 }, - { 115, 165, 41, 0 }, - { 7, 241, 131, 1 }, }, - { { 74, 93, 8, 0 }, - { 193, 9, 193, 1 }, - { 8, 93, 41, 0 }, - { 193, 200, 65, 1 }, }, - { { 74, 93, 11, 1 }, - { 201, 75, 193, 1 }, - { 232, 93, 41, 0 }, - { 193, 233, 73, 1 }, }, - { { 74, 93, 4, 1 }, - { 201, 9, 209, 0 }, - { 144, 93, 41, 0 }, - { 69, 200, 73, 1 }, }, - { { 74, 93, 7, 0 }, - { 193, 75, 209, 0 }, - { 112, 93, 41, 0 }, - { 69, 233, 65, 1 }, }, - { { 74, 93, 120, 0 }, - { 209, 141, 193, 1 }, - { 15, 93, 41, 0 }, - { 193, 216, 197, 1 }, }, - { { 74, 93, 123, 1 }, - { 217, 207, 193, 1 }, - { 239, 93, 41, 0 }, - { 193, 249, 205, 1 }, }, - { { 74, 93, 116, 1 }, - { 217, 141, 209, 0 }, - { 151, 93, 41, 0 }, - { 69, 216, 205, 1 }, }, - { { 74, 93, 119, 0 }, - { 209, 207, 209, 0 }, - { 119, 93, 41, 0 }, - { 69, 249, 197, 1 }, }, - { { 74, 92, 152, 0 }, - { 209, 9, 224, 1 }, - { 12, 157, 41, 0 }, - { 131, 200, 69, 1 }, }, - { { 74, 92, 155, 1 }, - { 217, 75, 224, 1 }, - { 236, 157, 41, 0 }, - { 131, 233, 77, 1 }, }, - { { 74, 92, 148, 1 }, - { 217, 9, 240, 0 }, - { 148, 157, 41, 0 }, - { 7, 200, 77, 1 }, }, - { { 74, 92, 151, 0 }, - { 209, 75, 240, 0 }, - { 116, 157, 41, 0 }, - { 7, 233, 69, 1 }, }, - { { 74, 92, 232, 0 }, - { 193, 141, 224, 1 }, - { 11, 157, 41, 0 }, - { 131, 216, 193, 1 }, }, - { { 74, 92, 235, 1 }, - { 201, 207, 224, 1 }, - { 235, 157, 41, 0 }, - { 131, 249, 201, 1 }, }, - { { 74, 92, 228, 1 }, - { 201, 141, 240, 0 }, - { 147, 157, 41, 0 }, - { 7, 216, 201, 1 }, }, - { { 74, 92, 231, 0 }, - { 193, 207, 240, 0 }, - { 115, 157, 41, 0 }, - { 7, 249, 193, 1 }, }, - { { 75, 161, 8, 0 }, - { 130, 17, 131, 1 }, - { 8, 66, 233, 0 }, - { 224, 196, 32, 1 }, }, - { { 75, 161, 11, 1 }, - { 138, 83, 131, 1 }, - { 232, 66, 233, 0 }, - { 224, 229, 40, 1 }, }, - { { 75, 161, 4, 1 }, - { 138, 17, 147, 0 }, - { 144, 66, 233, 0 }, - { 100, 196, 40, 1 }, }, - { { 75, 161, 7, 0 }, - { 130, 83, 147, 0 }, - { 112, 66, 233, 0 }, - { 100, 229, 32, 1 }, }, - { { 75, 161, 120, 0 }, - { 146, 149, 131, 1 }, - { 15, 66, 233, 0 }, - { 224, 212, 164, 1 }, }, - { { 75, 161, 123, 1 }, - { 154, 215, 131, 1 }, - { 239, 66, 233, 0 }, - { 224, 245, 172, 1 }, }, - { { 75, 161, 116, 1 }, - { 154, 149, 147, 0 }, - { 151, 66, 233, 0 }, - { 100, 212, 172, 1 }, }, - { { 75, 161, 119, 0 }, - { 146, 215, 147, 0 }, - { 119, 66, 233, 0 }, - { 100, 245, 164, 1 }, }, - { { 75, 160, 152, 0 }, - { 146, 17, 162, 1 }, - { 12, 130, 233, 0 }, - { 162, 196, 36, 1 }, }, - { { 75, 160, 155, 1 }, - { 154, 83, 162, 1 }, - { 236, 130, 233, 0 }, - { 162, 229, 44, 1 }, }, - { { 75, 160, 148, 1 }, - { 154, 17, 178, 0 }, - { 148, 130, 233, 0 }, - { 38, 196, 44, 1 }, }, - { { 75, 160, 151, 0 }, - { 146, 83, 178, 0 }, - { 116, 130, 233, 0 }, - { 38, 229, 36, 1 }, }, - { { 75, 160, 232, 0 }, - { 130, 149, 162, 1 }, - { 11, 130, 233, 0 }, - { 162, 212, 160, 1 }, }, - { { 75, 160, 235, 1 }, - { 138, 215, 162, 1 }, - { 235, 130, 233, 0 }, - { 162, 245, 168, 1 }, }, - { { 75, 160, 228, 1 }, - { 138, 149, 178, 0 }, - { 147, 130, 233, 0 }, - { 38, 212, 168, 1 }, }, - { { 75, 160, 231, 0 }, - { 130, 215, 178, 0 }, - { 115, 130, 233, 0 }, - { 38, 245, 160, 1 }, }, - { { 75, 175, 8, 0 }, - { 163, 25, 131, 1 }, - { 8, 122, 233, 0 }, - { 224, 204, 98, 1 }, }, - { { 75, 175, 11, 1 }, - { 171, 91, 131, 1 }, - { 232, 122, 233, 0 }, - { 224, 237, 106, 1 }, }, - { { 75, 175, 4, 1 }, - { 171, 25, 147, 0 }, - { 144, 122, 233, 0 }, - { 100, 204, 106, 1 }, }, - { { 75, 175, 7, 0 }, - { 163, 91, 147, 0 }, - { 112, 122, 233, 0 }, - { 100, 237, 98, 1 }, }, - { { 75, 175, 120, 0 }, - { 179, 157, 131, 1 }, - { 15, 122, 233, 0 }, - { 224, 220, 230, 1 }, }, - { { 75, 175, 123, 1 }, - { 187, 223, 131, 1 }, - { 239, 122, 233, 0 }, - { 224, 253, 238, 1 }, }, - { { 75, 175, 116, 1 }, - { 187, 157, 147, 0 }, - { 151, 122, 233, 0 }, - { 100, 220, 238, 1 }, }, - { { 75, 175, 119, 0 }, - { 179, 223, 147, 0 }, - { 119, 122, 233, 0 }, - { 100, 253, 230, 1 }, }, - { { 75, 174, 152, 0 }, - { 179, 25, 162, 1 }, - { 12, 186, 233, 0 }, - { 162, 204, 102, 1 }, }, - { { 75, 174, 155, 1 }, - { 187, 91, 162, 1 }, - { 236, 186, 233, 0 }, - { 162, 237, 110, 1 }, }, - { { 75, 174, 148, 1 }, - { 187, 25, 178, 0 }, - { 148, 186, 233, 0 }, - { 38, 204, 110, 1 }, }, - { { 75, 174, 151, 0 }, - { 179, 91, 178, 0 }, - { 116, 186, 233, 0 }, - { 38, 237, 102, 1 }, }, - { { 75, 174, 232, 0 }, - { 163, 157, 162, 1 }, - { 11, 186, 233, 0 }, - { 162, 220, 226, 1 }, }, - { { 75, 174, 235, 1 }, - { 171, 223, 162, 1 }, - { 235, 186, 233, 0 }, - { 162, 253, 234, 1 }, }, - { { 75, 174, 228, 1 }, - { 171, 157, 178, 0 }, - { 147, 186, 233, 0 }, - { 38, 220, 234, 1 }, }, - { { 75, 174, 231, 0 }, - { 163, 223, 178, 0 }, - { 115, 186, 233, 0 }, - { 38, 253, 226, 1 }, }, - { { 75, 147, 8, 0 }, - { 162, 17, 193, 1 }, - { 8, 100, 233, 0 }, - { 193, 196, 34, 1 }, }, - { { 75, 147, 11, 1 }, - { 170, 83, 193, 1 }, - { 232, 100, 233, 0 }, - { 193, 229, 42, 1 }, }, - { { 75, 147, 4, 1 }, - { 170, 17, 209, 0 }, - { 144, 100, 233, 0 }, - { 69, 196, 42, 1 }, }, - { { 75, 147, 7, 0 }, - { 162, 83, 209, 0 }, - { 112, 100, 233, 0 }, - { 69, 229, 34, 1 }, }, - { { 75, 147, 120, 0 }, - { 178, 149, 193, 1 }, - { 15, 100, 233, 0 }, - { 193, 212, 166, 1 }, }, - { { 75, 147, 123, 1 }, - { 186, 215, 193, 1 }, - { 239, 100, 233, 0 }, - { 193, 245, 174, 1 }, }, - { { 75, 147, 116, 1 }, - { 186, 149, 209, 0 }, - { 151, 100, 233, 0 }, - { 69, 212, 174, 1 }, }, - { { 75, 147, 119, 0 }, - { 178, 215, 209, 0 }, - { 119, 100, 233, 0 }, - { 69, 245, 166, 1 }, }, - { { 75, 146, 152, 0 }, - { 178, 17, 224, 1 }, - { 12, 164, 233, 0 }, - { 131, 196, 38, 1 }, }, - { { 75, 146, 155, 1 }, - { 186, 83, 224, 1 }, - { 236, 164, 233, 0 }, - { 131, 229, 46, 1 }, }, - { { 75, 146, 148, 1 }, - { 186, 17, 240, 0 }, - { 148, 164, 233, 0 }, - { 7, 196, 46, 1 }, }, - { { 75, 146, 151, 0 }, - { 178, 83, 240, 0 }, - { 116, 164, 233, 0 }, - { 7, 229, 38, 1 }, }, - { { 75, 146, 232, 0 }, - { 162, 149, 224, 1 }, - { 11, 164, 233, 0 }, - { 131, 212, 162, 1 }, }, - { { 75, 146, 235, 1 }, - { 170, 215, 224, 1 }, - { 235, 164, 233, 0 }, - { 131, 245, 170, 1 }, }, - { { 75, 146, 228, 1 }, - { 170, 149, 240, 0 }, - { 147, 164, 233, 0 }, - { 7, 212, 170, 1 }, }, - { { 75, 146, 231, 0 }, - { 162, 215, 240, 0 }, - { 115, 164, 233, 0 }, - { 7, 245, 162, 1 }, }, - { { 75, 157, 8, 0 }, - { 131, 25, 193, 1 }, - { 8, 92, 233, 0 }, - { 193, 204, 96, 1 }, }, - { { 75, 157, 11, 1 }, - { 139, 91, 193, 1 }, - { 232, 92, 233, 0 }, - { 193, 237, 104, 1 }, }, - { { 75, 157, 4, 1 }, - { 139, 25, 209, 0 }, - { 144, 92, 233, 0 }, - { 69, 204, 104, 1 }, }, - { { 75, 157, 7, 0 }, - { 131, 91, 209, 0 }, - { 112, 92, 233, 0 }, - { 69, 237, 96, 1 }, }, - { { 75, 157, 120, 0 }, - { 147, 157, 193, 1 }, - { 15, 92, 233, 0 }, - { 193, 220, 228, 1 }, }, - { { 75, 157, 123, 1 }, - { 155, 223, 193, 1 }, - { 239, 92, 233, 0 }, - { 193, 253, 236, 1 }, }, - { { 75, 157, 116, 1 }, - { 155, 157, 209, 0 }, - { 151, 92, 233, 0 }, - { 69, 220, 236, 1 }, }, - { { 75, 157, 119, 0 }, - { 147, 223, 209, 0 }, - { 119, 92, 233, 0 }, - { 69, 253, 228, 1 }, }, - { { 75, 156, 152, 0 }, - { 147, 25, 224, 1 }, - { 12, 156, 233, 0 }, - { 131, 204, 100, 1 }, }, - { { 75, 156, 155, 1 }, - { 155, 91, 224, 1 }, - { 236, 156, 233, 0 }, - { 131, 237, 108, 1 }, }, - { { 75, 156, 148, 1 }, - { 155, 25, 240, 0 }, - { 148, 156, 233, 0 }, - { 7, 204, 108, 1 }, }, - { { 75, 156, 151, 0 }, - { 147, 91, 240, 0 }, - { 116, 156, 233, 0 }, - { 7, 237, 100, 1 }, }, - { { 75, 156, 232, 0 }, - { 131, 157, 224, 1 }, - { 11, 156, 233, 0 }, - { 131, 220, 224, 1 }, }, - { { 75, 156, 235, 1 }, - { 139, 223, 224, 1 }, - { 235, 156, 233, 0 }, - { 131, 253, 232, 1 }, }, - { { 75, 156, 228, 1 }, - { 139, 157, 240, 0 }, - { 147, 156, 233, 0 }, - { 7, 220, 232, 1 }, }, - { { 75, 156, 231, 0 }, - { 131, 223, 240, 0 }, - { 115, 156, 233, 0 }, - { 7, 253, 224, 1 }, }, - { { 116, 33, 8, 0 }, - { 4, 33, 7, 1 }, - { 8, 66, 23, 0 }, - { 240, 66, 16, 0 }, }, - { { 116, 33, 11, 1 }, - { 12, 99, 7, 1 }, - { 232, 66, 23, 0 }, - { 240, 99, 24, 0 }, }, - { { 116, 33, 4, 1 }, - { 12, 33, 23, 0 }, - { 144, 66, 23, 0 }, - { 116, 66, 24, 0 }, }, - { { 116, 33, 7, 0 }, - { 4, 99, 23, 0 }, - { 112, 66, 23, 0 }, - { 116, 99, 16, 0 }, }, - { { 116, 33, 120, 0 }, - { 20, 165, 7, 1 }, - { 15, 66, 23, 0 }, - { 240, 82, 148, 0 }, }, - { { 116, 33, 123, 1 }, - { 28, 231, 7, 1 }, - { 239, 66, 23, 0 }, - { 240, 115, 156, 0 }, }, - { { 116, 33, 116, 1 }, - { 28, 165, 23, 0 }, - { 151, 66, 23, 0 }, - { 116, 82, 156, 0 }, }, - { { 116, 33, 119, 0 }, - { 20, 231, 23, 0 }, - { 119, 66, 23, 0 }, - { 116, 115, 148, 0 }, }, - { { 116, 32, 152, 0 }, - { 20, 33, 38, 1 }, - { 12, 130, 23, 0 }, - { 178, 66, 20, 0 }, }, - { { 116, 32, 155, 1 }, - { 28, 99, 38, 1 }, - { 236, 130, 23, 0 }, - { 178, 99, 28, 0 }, }, - { { 116, 32, 148, 1 }, - { 28, 33, 54, 0 }, - { 148, 130, 23, 0 }, - { 54, 66, 28, 0 }, }, - { { 116, 32, 151, 0 }, - { 20, 99, 54, 0 }, - { 116, 130, 23, 0 }, - { 54, 99, 20, 0 }, }, - { { 116, 32, 232, 0 }, - { 4, 165, 38, 1 }, - { 11, 130, 23, 0 }, - { 178, 82, 144, 0 }, }, - { { 116, 32, 235, 1 }, - { 12, 231, 38, 1 }, - { 235, 130, 23, 0 }, - { 178, 115, 152, 0 }, }, - { { 116, 32, 228, 1 }, - { 12, 165, 54, 0 }, - { 147, 130, 23, 0 }, - { 54, 82, 152, 0 }, }, - { { 116, 32, 231, 0 }, - { 4, 231, 54, 0 }, - { 115, 130, 23, 0 }, - { 54, 115, 144, 0 }, }, - { { 116, 47, 8, 0 }, - { 37, 41, 7, 1 }, - { 8, 122, 23, 0 }, - { 240, 74, 82, 0 }, }, - { { 116, 47, 11, 1 }, - { 45, 107, 7, 1 }, - { 232, 122, 23, 0 }, - { 240, 107, 90, 0 }, }, - { { 116, 47, 4, 1 }, - { 45, 41, 23, 0 }, - { 144, 122, 23, 0 }, - { 116, 74, 90, 0 }, }, - { { 116, 47, 7, 0 }, - { 37, 107, 23, 0 }, - { 112, 122, 23, 0 }, - { 116, 107, 82, 0 }, }, - { { 116, 47, 120, 0 }, - { 53, 173, 7, 1 }, - { 15, 122, 23, 0 }, - { 240, 90, 214, 0 }, }, - { { 116, 47, 123, 1 }, - { 61, 239, 7, 1 }, - { 239, 122, 23, 0 }, - { 240, 123, 222, 0 }, }, - { { 116, 47, 116, 1 }, - { 61, 173, 23, 0 }, - { 151, 122, 23, 0 }, - { 116, 90, 222, 0 }, }, - { { 116, 47, 119, 0 }, - { 53, 239, 23, 0 }, - { 119, 122, 23, 0 }, - { 116, 123, 214, 0 }, }, - { { 116, 46, 152, 0 }, - { 53, 41, 38, 1 }, - { 12, 186, 23, 0 }, - { 178, 74, 86, 0 }, }, - { { 116, 46, 155, 1 }, - { 61, 107, 38, 1 }, - { 236, 186, 23, 0 }, - { 178, 107, 94, 0 }, }, - { { 116, 46, 148, 1 }, - { 61, 41, 54, 0 }, - { 148, 186, 23, 0 }, - { 54, 74, 94, 0 }, }, - { { 116, 46, 151, 0 }, - { 53, 107, 54, 0 }, - { 116, 186, 23, 0 }, - { 54, 107, 86, 0 }, }, - { { 116, 46, 232, 0 }, - { 37, 173, 38, 1 }, - { 11, 186, 23, 0 }, - { 178, 90, 210, 0 }, }, - { { 116, 46, 235, 1 }, - { 45, 239, 38, 1 }, - { 235, 186, 23, 0 }, - { 178, 123, 218, 0 }, }, - { { 116, 46, 228, 1 }, - { 45, 173, 54, 0 }, - { 147, 186, 23, 0 }, - { 54, 90, 218, 0 }, }, - { { 116, 46, 231, 0 }, - { 37, 239, 54, 0 }, - { 115, 186, 23, 0 }, - { 54, 123, 210, 0 }, }, - { { 116, 19, 8, 0 }, - { 36, 33, 69, 1 }, - { 8, 100, 23, 0 }, - { 209, 66, 18, 0 }, }, - { { 116, 19, 11, 1 }, - { 44, 99, 69, 1 }, - { 232, 100, 23, 0 }, - { 209, 99, 26, 0 }, }, - { { 116, 19, 4, 1 }, - { 44, 33, 85, 0 }, - { 144, 100, 23, 0 }, - { 85, 66, 26, 0 }, }, - { { 116, 19, 7, 0 }, - { 36, 99, 85, 0 }, - { 112, 100, 23, 0 }, - { 85, 99, 18, 0 }, }, - { { 116, 19, 120, 0 }, - { 52, 165, 69, 1 }, - { 15, 100, 23, 0 }, - { 209, 82, 150, 0 }, }, - { { 116, 19, 123, 1 }, - { 60, 231, 69, 1 }, - { 239, 100, 23, 0 }, - { 209, 115, 158, 0 }, }, - { { 116, 19, 116, 1 }, - { 60, 165, 85, 0 }, - { 151, 100, 23, 0 }, - { 85, 82, 158, 0 }, }, - { { 116, 19, 119, 0 }, - { 52, 231, 85, 0 }, - { 119, 100, 23, 0 }, - { 85, 115, 150, 0 }, }, - { { 116, 18, 152, 0 }, - { 52, 33, 100, 1 }, - { 12, 164, 23, 0 }, - { 147, 66, 22, 0 }, }, - { { 116, 18, 155, 1 }, - { 60, 99, 100, 1 }, - { 236, 164, 23, 0 }, - { 147, 99, 30, 0 }, }, - { { 116, 18, 148, 1 }, - { 60, 33, 116, 0 }, - { 148, 164, 23, 0 }, - { 23, 66, 30, 0 }, }, - { { 116, 18, 151, 0 }, - { 52, 99, 116, 0 }, - { 116, 164, 23, 0 }, - { 23, 99, 22, 0 }, }, - { { 116, 18, 232, 0 }, - { 36, 165, 100, 1 }, - { 11, 164, 23, 0 }, - { 147, 82, 146, 0 }, }, - { { 116, 18, 235, 1 }, - { 44, 231, 100, 1 }, - { 235, 164, 23, 0 }, - { 147, 115, 154, 0 }, }, - { { 116, 18, 228, 1 }, - { 44, 165, 116, 0 }, - { 147, 164, 23, 0 }, - { 23, 82, 154, 0 }, }, - { { 116, 18, 231, 0 }, - { 36, 231, 116, 0 }, - { 115, 164, 23, 0 }, - { 23, 115, 146, 0 }, }, - { { 116, 29, 8, 0 }, - { 5, 41, 69, 1 }, - { 8, 92, 23, 0 }, - { 209, 74, 80, 0 }, }, - { { 116, 29, 11, 1 }, - { 13, 107, 69, 1 }, - { 232, 92, 23, 0 }, - { 209, 107, 88, 0 }, }, - { { 116, 29, 4, 1 }, - { 13, 41, 85, 0 }, - { 144, 92, 23, 0 }, - { 85, 74, 88, 0 }, }, - { { 116, 29, 7, 0 }, - { 5, 107, 85, 0 }, - { 112, 92, 23, 0 }, - { 85, 107, 80, 0 }, }, - { { 116, 29, 120, 0 }, - { 21, 173, 69, 1 }, - { 15, 92, 23, 0 }, - { 209, 90, 212, 0 }, }, - { { 116, 29, 123, 1 }, - { 29, 239, 69, 1 }, - { 239, 92, 23, 0 }, - { 209, 123, 220, 0 }, }, - { { 116, 29, 116, 1 }, - { 29, 173, 85, 0 }, - { 151, 92, 23, 0 }, - { 85, 90, 220, 0 }, }, - { { 116, 29, 119, 0 }, - { 21, 239, 85, 0 }, - { 119, 92, 23, 0 }, - { 85, 123, 212, 0 }, }, - { { 116, 28, 152, 0 }, - { 21, 41, 100, 1 }, - { 12, 156, 23, 0 }, - { 147, 74, 84, 0 }, }, - { { 116, 28, 155, 1 }, - { 29, 107, 100, 1 }, - { 236, 156, 23, 0 }, - { 147, 107, 92, 0 }, }, - { { 116, 28, 148, 1 }, - { 29, 41, 116, 0 }, - { 148, 156, 23, 0 }, - { 23, 74, 92, 0 }, }, - { { 116, 28, 151, 0 }, - { 21, 107, 116, 0 }, - { 116, 156, 23, 0 }, - { 23, 107, 84, 0 }, }, - { { 116, 28, 232, 0 }, - { 5, 173, 100, 1 }, - { 11, 156, 23, 0 }, - { 147, 90, 208, 0 }, }, - { { 116, 28, 235, 1 }, - { 13, 239, 100, 1 }, - { 235, 156, 23, 0 }, - { 147, 123, 216, 0 }, }, - { { 116, 28, 228, 1 }, - { 13, 173, 116, 0 }, - { 147, 156, 23, 0 }, - { 23, 90, 216, 0 }, }, - { { 116, 28, 231, 0 }, - { 5, 239, 116, 0 }, - { 115, 156, 23, 0 }, - { 23, 123, 208, 0 }, }, - { { 117, 225, 8, 0 }, - { 70, 49, 7, 1 }, - { 8, 67, 215, 0 }, - { 240, 70, 49, 0 }, }, - { { 117, 225, 11, 1 }, - { 78, 115, 7, 1 }, - { 232, 67, 215, 0 }, - { 240, 103, 57, 0 }, }, - { { 117, 225, 4, 1 }, - { 78, 49, 23, 0 }, - { 144, 67, 215, 0 }, - { 116, 70, 57, 0 }, }, - { { 117, 225, 7, 0 }, - { 70, 115, 23, 0 }, - { 112, 67, 215, 0 }, - { 116, 103, 49, 0 }, }, - { { 117, 225, 120, 0 }, - { 86, 181, 7, 1 }, - { 15, 67, 215, 0 }, - { 240, 86, 181, 0 }, }, - { { 117, 225, 123, 1 }, - { 94, 247, 7, 1 }, - { 239, 67, 215, 0 }, - { 240, 119, 189, 0 }, }, - { { 117, 225, 116, 1 }, - { 94, 181, 23, 0 }, - { 151, 67, 215, 0 }, - { 116, 86, 189, 0 }, }, - { { 117, 225, 119, 0 }, - { 86, 247, 23, 0 }, - { 119, 67, 215, 0 }, - { 116, 119, 181, 0 }, }, - { { 117, 224, 152, 0 }, - { 86, 49, 38, 1 }, - { 12, 131, 215, 0 }, - { 178, 70, 53, 0 }, }, - { { 117, 224, 155, 1 }, - { 94, 115, 38, 1 }, - { 236, 131, 215, 0 }, - { 178, 103, 61, 0 }, }, - { { 117, 224, 148, 1 }, - { 94, 49, 54, 0 }, - { 148, 131, 215, 0 }, - { 54, 70, 61, 0 }, }, - { { 117, 224, 151, 0 }, - { 86, 115, 54, 0 }, - { 116, 131, 215, 0 }, - { 54, 103, 53, 0 }, }, - { { 117, 224, 232, 0 }, - { 70, 181, 38, 1 }, - { 11, 131, 215, 0 }, - { 178, 86, 177, 0 }, }, - { { 117, 224, 235, 1 }, - { 78, 247, 38, 1 }, - { 235, 131, 215, 0 }, - { 178, 119, 185, 0 }, }, - { { 117, 224, 228, 1 }, - { 78, 181, 54, 0 }, - { 147, 131, 215, 0 }, - { 54, 86, 185, 0 }, }, - { { 117, 224, 231, 0 }, - { 70, 247, 54, 0 }, - { 115, 131, 215, 0 }, - { 54, 119, 177, 0 }, }, - { { 117, 239, 8, 0 }, - { 103, 57, 7, 1 }, - { 8, 123, 215, 0 }, - { 240, 78, 115, 0 }, }, - { { 117, 239, 11, 1 }, - { 111, 123, 7, 1 }, - { 232, 123, 215, 0 }, - { 240, 111, 123, 0 }, }, - { { 117, 239, 4, 1 }, - { 111, 57, 23, 0 }, - { 144, 123, 215, 0 }, - { 116, 78, 123, 0 }, }, - { { 117, 239, 7, 0 }, - { 103, 123, 23, 0 }, - { 112, 123, 215, 0 }, - { 116, 111, 115, 0 }, }, - { { 117, 239, 120, 0 }, - { 119, 189, 7, 1 }, - { 15, 123, 215, 0 }, - { 240, 94, 247, 0 }, }, - { { 117, 239, 123, 1 }, - { 127, 255, 7, 1 }, - { 239, 123, 215, 0 }, - { 240, 127, 255, 0 }, }, - { { 117, 239, 116, 1 }, - { 127, 189, 23, 0 }, - { 151, 123, 215, 0 }, - { 116, 94, 255, 0 }, }, - { { 117, 239, 119, 0 }, - { 119, 255, 23, 0 }, - { 119, 123, 215, 0 }, - { 116, 127, 247, 0 }, }, - { { 117, 238, 152, 0 }, - { 119, 57, 38, 1 }, - { 12, 187, 215, 0 }, - { 178, 78, 119, 0 }, }, - { { 117, 238, 155, 1 }, - { 127, 123, 38, 1 }, - { 236, 187, 215, 0 }, - { 178, 111, 127, 0 }, }, - { { 117, 238, 148, 1 }, - { 127, 57, 54, 0 }, - { 148, 187, 215, 0 }, - { 54, 78, 127, 0 }, }, - { { 117, 238, 151, 0 }, - { 119, 123, 54, 0 }, - { 116, 187, 215, 0 }, - { 54, 111, 119, 0 }, }, - { { 117, 238, 232, 0 }, - { 103, 189, 38, 1 }, - { 11, 187, 215, 0 }, - { 178, 94, 243, 0 }, }, - { { 117, 238, 235, 1 }, - { 111, 255, 38, 1 }, - { 235, 187, 215, 0 }, - { 178, 127, 251, 0 }, }, - { { 117, 238, 228, 1 }, - { 111, 189, 54, 0 }, - { 147, 187, 215, 0 }, - { 54, 94, 251, 0 }, }, - { { 117, 238, 231, 0 }, - { 103, 255, 54, 0 }, - { 115, 187, 215, 0 }, - { 54, 127, 243, 0 }, }, - { { 117, 211, 8, 0 }, - { 102, 49, 69, 1 }, - { 8, 101, 215, 0 }, - { 209, 70, 51, 0 }, }, - { { 117, 211, 11, 1 }, - { 110, 115, 69, 1 }, - { 232, 101, 215, 0 }, - { 209, 103, 59, 0 }, }, - { { 117, 211, 4, 1 }, - { 110, 49, 85, 0 }, - { 144, 101, 215, 0 }, - { 85, 70, 59, 0 }, }, - { { 117, 211, 7, 0 }, - { 102, 115, 85, 0 }, - { 112, 101, 215, 0 }, - { 85, 103, 51, 0 }, }, - { { 117, 211, 120, 0 }, - { 118, 181, 69, 1 }, - { 15, 101, 215, 0 }, - { 209, 86, 183, 0 }, }, - { { 117, 211, 123, 1 }, - { 126, 247, 69, 1 }, - { 239, 101, 215, 0 }, - { 209, 119, 191, 0 }, }, - { { 117, 211, 116, 1 }, - { 126, 181, 85, 0 }, - { 151, 101, 215, 0 }, - { 85, 86, 191, 0 }, }, - { { 117, 211, 119, 0 }, - { 118, 247, 85, 0 }, - { 119, 101, 215, 0 }, - { 85, 119, 183, 0 }, }, - { { 117, 210, 152, 0 }, - { 118, 49, 100, 1 }, - { 12, 165, 215, 0 }, - { 147, 70, 55, 0 }, }, - { { 117, 210, 155, 1 }, - { 126, 115, 100, 1 }, - { 236, 165, 215, 0 }, - { 147, 103, 63, 0 }, }, - { { 117, 210, 148, 1 }, - { 126, 49, 116, 0 }, - { 148, 165, 215, 0 }, - { 23, 70, 63, 0 }, }, - { { 117, 210, 151, 0 }, - { 118, 115, 116, 0 }, - { 116, 165, 215, 0 }, - { 23, 103, 55, 0 }, }, - { { 117, 210, 232, 0 }, - { 102, 181, 100, 1 }, - { 11, 165, 215, 0 }, - { 147, 86, 179, 0 }, }, - { { 117, 210, 235, 1 }, - { 110, 247, 100, 1 }, - { 235, 165, 215, 0 }, - { 147, 119, 187, 0 }, }, - { { 117, 210, 228, 1 }, - { 110, 181, 116, 0 }, - { 147, 165, 215, 0 }, - { 23, 86, 187, 0 }, }, - { { 117, 210, 231, 0 }, - { 102, 247, 116, 0 }, - { 115, 165, 215, 0 }, - { 23, 119, 179, 0 }, }, - { { 117, 221, 8, 0 }, - { 71, 57, 69, 1 }, - { 8, 93, 215, 0 }, - { 209, 78, 113, 0 }, }, - { { 117, 221, 11, 1 }, - { 79, 123, 69, 1 }, - { 232, 93, 215, 0 }, - { 209, 111, 121, 0 }, }, - { { 117, 221, 4, 1 }, - { 79, 57, 85, 0 }, - { 144, 93, 215, 0 }, - { 85, 78, 121, 0 }, }, - { { 117, 221, 7, 0 }, - { 71, 123, 85, 0 }, - { 112, 93, 215, 0 }, - { 85, 111, 113, 0 }, }, - { { 117, 221, 120, 0 }, - { 87, 189, 69, 1 }, - { 15, 93, 215, 0 }, - { 209, 94, 245, 0 }, }, - { { 117, 221, 123, 1 }, - { 95, 255, 69, 1 }, - { 239, 93, 215, 0 }, - { 209, 127, 253, 0 }, }, - { { 117, 221, 116, 1 }, - { 95, 189, 85, 0 }, - { 151, 93, 215, 0 }, - { 85, 94, 253, 0 }, }, - { { 117, 221, 119, 0 }, - { 87, 255, 85, 0 }, - { 119, 93, 215, 0 }, - { 85, 127, 245, 0 }, }, - { { 117, 220, 152, 0 }, - { 87, 57, 100, 1 }, - { 12, 157, 215, 0 }, - { 147, 78, 117, 0 }, }, - { { 117, 220, 155, 1 }, - { 95, 123, 100, 1 }, - { 236, 157, 215, 0 }, - { 147, 111, 125, 0 }, }, - { { 117, 220, 148, 1 }, - { 95, 57, 116, 0 }, - { 148, 157, 215, 0 }, - { 23, 78, 125, 0 }, }, - { { 117, 220, 151, 0 }, - { 87, 123, 116, 0 }, - { 116, 157, 215, 0 }, - { 23, 111, 117, 0 }, }, - { { 117, 220, 232, 0 }, - { 71, 189, 100, 1 }, - { 11, 157, 215, 0 }, - { 147, 94, 241, 0 }, }, - { { 117, 220, 235, 1 }, - { 79, 255, 100, 1 }, - { 235, 157, 215, 0 }, - { 147, 127, 249, 0 }, }, - { { 117, 220, 228, 1 }, - { 79, 189, 116, 0 }, - { 147, 157, 215, 0 }, - { 23, 94, 249, 0 }, }, - { { 117, 220, 231, 0 }, - { 71, 255, 116, 0 }, - { 115, 157, 215, 0 }, - { 23, 127, 241, 0 }, }, - { { 114, 97, 8, 0 }, - { 68, 33, 131, 1 }, - { 8, 67, 39, 0 }, - { 224, 194, 17, 0 }, }, - { { 114, 97, 11, 1 }, - { 76, 99, 131, 1 }, - { 232, 67, 39, 0 }, - { 224, 227, 25, 0 }, }, - { { 114, 97, 4, 1 }, - { 76, 33, 147, 0 }, - { 144, 67, 39, 0 }, - { 100, 194, 25, 0 }, }, - { { 114, 97, 7, 0 }, - { 68, 99, 147, 0 }, - { 112, 67, 39, 0 }, - { 100, 227, 17, 0 }, }, - { { 114, 97, 120, 0 }, - { 84, 165, 131, 1 }, - { 15, 67, 39, 0 }, - { 224, 210, 149, 0 }, }, - { { 114, 97, 123, 1 }, - { 92, 231, 131, 1 }, - { 239, 67, 39, 0 }, - { 224, 243, 157, 0 }, }, - { { 114, 97, 116, 1 }, - { 92, 165, 147, 0 }, - { 151, 67, 39, 0 }, - { 100, 210, 157, 0 }, }, - { { 114, 97, 119, 0 }, - { 84, 231, 147, 0 }, - { 119, 67, 39, 0 }, - { 100, 243, 149, 0 }, }, - { { 114, 96, 152, 0 }, - { 84, 33, 162, 1 }, - { 12, 131, 39, 0 }, - { 162, 194, 21, 0 }, }, - { { 114, 96, 155, 1 }, - { 92, 99, 162, 1 }, - { 236, 131, 39, 0 }, - { 162, 227, 29, 0 }, }, - { { 114, 96, 148, 1 }, - { 92, 33, 178, 0 }, - { 148, 131, 39, 0 }, - { 38, 194, 29, 0 }, }, - { { 114, 96, 151, 0 }, - { 84, 99, 178, 0 }, - { 116, 131, 39, 0 }, - { 38, 227, 21, 0 }, }, - { { 114, 96, 232, 0 }, - { 68, 165, 162, 1 }, - { 11, 131, 39, 0 }, - { 162, 210, 145, 0 }, }, - { { 114, 96, 235, 1 }, - { 76, 231, 162, 1 }, - { 235, 131, 39, 0 }, - { 162, 243, 153, 0 }, }, - { { 114, 96, 228, 1 }, - { 76, 165, 178, 0 }, - { 147, 131, 39, 0 }, - { 38, 210, 153, 0 }, }, - { { 114, 96, 231, 0 }, - { 68, 231, 178, 0 }, - { 115, 131, 39, 0 }, - { 38, 243, 145, 0 }, }, - { { 114, 111, 8, 0 }, - { 101, 41, 131, 1 }, - { 8, 123, 39, 0 }, - { 224, 202, 83, 0 }, }, - { { 114, 111, 11, 1 }, - { 109, 107, 131, 1 }, - { 232, 123, 39, 0 }, - { 224, 235, 91, 0 }, }, - { { 114, 111, 4, 1 }, - { 109, 41, 147, 0 }, - { 144, 123, 39, 0 }, - { 100, 202, 91, 0 }, }, - { { 114, 111, 7, 0 }, - { 101, 107, 147, 0 }, - { 112, 123, 39, 0 }, - { 100, 235, 83, 0 }, }, - { { 114, 111, 120, 0 }, - { 117, 173, 131, 1 }, - { 15, 123, 39, 0 }, - { 224, 218, 215, 0 }, }, - { { 114, 111, 123, 1 }, - { 125, 239, 131, 1 }, - { 239, 123, 39, 0 }, - { 224, 251, 223, 0 }, }, - { { 114, 111, 116, 1 }, - { 125, 173, 147, 0 }, - { 151, 123, 39, 0 }, - { 100, 218, 223, 0 }, }, - { { 114, 111, 119, 0 }, - { 117, 239, 147, 0 }, - { 119, 123, 39, 0 }, - { 100, 251, 215, 0 }, }, - { { 114, 110, 152, 0 }, - { 117, 41, 162, 1 }, - { 12, 187, 39, 0 }, - { 162, 202, 87, 0 }, }, - { { 114, 110, 155, 1 }, - { 125, 107, 162, 1 }, - { 236, 187, 39, 0 }, - { 162, 235, 95, 0 }, }, - { { 114, 110, 148, 1 }, - { 125, 41, 178, 0 }, - { 148, 187, 39, 0 }, - { 38, 202, 95, 0 }, }, - { { 114, 110, 151, 0 }, - { 117, 107, 178, 0 }, - { 116, 187, 39, 0 }, - { 38, 235, 87, 0 }, }, - { { 114, 110, 232, 0 }, - { 101, 173, 162, 1 }, - { 11, 187, 39, 0 }, - { 162, 218, 211, 0 }, }, - { { 114, 110, 235, 1 }, - { 109, 239, 162, 1 }, - { 235, 187, 39, 0 }, - { 162, 251, 219, 0 }, }, - { { 114, 110, 228, 1 }, - { 109, 173, 178, 0 }, - { 147, 187, 39, 0 }, - { 38, 218, 219, 0 }, }, - { { 114, 110, 231, 0 }, - { 101, 239, 178, 0 }, - { 115, 187, 39, 0 }, - { 38, 251, 211, 0 }, }, - { { 114, 83, 8, 0 }, - { 100, 33, 193, 1 }, - { 8, 101, 39, 0 }, - { 193, 194, 19, 0 }, }, - { { 114, 83, 11, 1 }, - { 108, 99, 193, 1 }, - { 232, 101, 39, 0 }, - { 193, 227, 27, 0 }, }, - { { 114, 83, 4, 1 }, - { 108, 33, 209, 0 }, - { 144, 101, 39, 0 }, - { 69, 194, 27, 0 }, }, - { { 114, 83, 7, 0 }, - { 100, 99, 209, 0 }, - { 112, 101, 39, 0 }, - { 69, 227, 19, 0 }, }, - { { 114, 83, 120, 0 }, - { 116, 165, 193, 1 }, - { 15, 101, 39, 0 }, - { 193, 210, 151, 0 }, }, - { { 114, 83, 123, 1 }, - { 124, 231, 193, 1 }, - { 239, 101, 39, 0 }, - { 193, 243, 159, 0 }, }, - { { 114, 83, 116, 1 }, - { 124, 165, 209, 0 }, - { 151, 101, 39, 0 }, - { 69, 210, 159, 0 }, }, - { { 114, 83, 119, 0 }, - { 116, 231, 209, 0 }, - { 119, 101, 39, 0 }, - { 69, 243, 151, 0 }, }, - { { 114, 82, 152, 0 }, - { 116, 33, 224, 1 }, - { 12, 165, 39, 0 }, - { 131, 194, 23, 0 }, }, - { { 114, 82, 155, 1 }, - { 124, 99, 224, 1 }, - { 236, 165, 39, 0 }, - { 131, 227, 31, 0 }, }, - { { 114, 82, 148, 1 }, - { 124, 33, 240, 0 }, - { 148, 165, 39, 0 }, - { 7, 194, 31, 0 }, }, - { { 114, 82, 151, 0 }, - { 116, 99, 240, 0 }, - { 116, 165, 39, 0 }, - { 7, 227, 23, 0 }, }, - { { 114, 82, 232, 0 }, - { 100, 165, 224, 1 }, - { 11, 165, 39, 0 }, - { 131, 210, 147, 0 }, }, - { { 114, 82, 235, 1 }, - { 108, 231, 224, 1 }, - { 235, 165, 39, 0 }, - { 131, 243, 155, 0 }, }, - { { 114, 82, 228, 1 }, - { 108, 165, 240, 0 }, - { 147, 165, 39, 0 }, - { 7, 210, 155, 0 }, }, - { { 114, 82, 231, 0 }, - { 100, 231, 240, 0 }, - { 115, 165, 39, 0 }, - { 7, 243, 147, 0 }, }, - { { 114, 93, 8, 0 }, - { 69, 41, 193, 1 }, - { 8, 93, 39, 0 }, - { 193, 202, 81, 0 }, }, - { { 114, 93, 11, 1 }, - { 77, 107, 193, 1 }, - { 232, 93, 39, 0 }, - { 193, 235, 89, 0 }, }, - { { 114, 93, 4, 1 }, - { 77, 41, 209, 0 }, - { 144, 93, 39, 0 }, - { 69, 202, 89, 0 }, }, - { { 114, 93, 7, 0 }, - { 69, 107, 209, 0 }, - { 112, 93, 39, 0 }, - { 69, 235, 81, 0 }, }, - { { 114, 93, 120, 0 }, - { 85, 173, 193, 1 }, - { 15, 93, 39, 0 }, - { 193, 218, 213, 0 }, }, - { { 114, 93, 123, 1 }, - { 93, 239, 193, 1 }, - { 239, 93, 39, 0 }, - { 193, 251, 221, 0 }, }, - { { 114, 93, 116, 1 }, - { 93, 173, 209, 0 }, - { 151, 93, 39, 0 }, - { 69, 218, 221, 0 }, }, - { { 114, 93, 119, 0 }, - { 85, 239, 209, 0 }, - { 119, 93, 39, 0 }, - { 69, 251, 213, 0 }, }, - { { 114, 92, 152, 0 }, - { 85, 41, 224, 1 }, - { 12, 157, 39, 0 }, - { 131, 202, 85, 0 }, }, - { { 114, 92, 155, 1 }, - { 93, 107, 224, 1 }, - { 236, 157, 39, 0 }, - { 131, 235, 93, 0 }, }, - { { 114, 92, 148, 1 }, - { 93, 41, 240, 0 }, - { 148, 157, 39, 0 }, - { 7, 202, 93, 0 }, }, - { { 114, 92, 151, 0 }, - { 85, 107, 240, 0 }, - { 116, 157, 39, 0 }, - { 7, 235, 85, 0 }, }, - { { 114, 92, 232, 0 }, - { 69, 173, 224, 1 }, - { 11, 157, 39, 0 }, - { 131, 218, 209, 0 }, }, - { { 114, 92, 235, 1 }, - { 77, 239, 224, 1 }, - { 235, 157, 39, 0 }, - { 131, 251, 217, 0 }, }, - { { 114, 92, 228, 1 }, - { 77, 173, 240, 0 }, - { 147, 157, 39, 0 }, - { 7, 218, 217, 0 }, }, - { { 114, 92, 231, 0 }, - { 69, 239, 240, 0 }, - { 115, 157, 39, 0 }, - { 7, 251, 209, 0 }, }, - { { 115, 161, 8, 0 }, - { 6, 49, 131, 1 }, - { 8, 66, 231, 0 }, - { 224, 198, 48, 0 }, }, - { { 115, 161, 11, 1 }, - { 14, 115, 131, 1 }, - { 232, 66, 231, 0 }, - { 224, 231, 56, 0 }, }, - { { 115, 161, 4, 1 }, - { 14, 49, 147, 0 }, - { 144, 66, 231, 0 }, - { 100, 198, 56, 0 }, }, - { { 115, 161, 7, 0 }, - { 6, 115, 147, 0 }, - { 112, 66, 231, 0 }, - { 100, 231, 48, 0 }, }, - { { 115, 161, 120, 0 }, - { 22, 181, 131, 1 }, - { 15, 66, 231, 0 }, - { 224, 214, 180, 0 }, }, - { { 115, 161, 123, 1 }, - { 30, 247, 131, 1 }, - { 239, 66, 231, 0 }, - { 224, 247, 188, 0 }, }, - { { 115, 161, 116, 1 }, - { 30, 181, 147, 0 }, - { 151, 66, 231, 0 }, - { 100, 214, 188, 0 }, }, - { { 115, 161, 119, 0 }, - { 22, 247, 147, 0 }, - { 119, 66, 231, 0 }, - { 100, 247, 180, 0 }, }, - { { 115, 160, 152, 0 }, - { 22, 49, 162, 1 }, - { 12, 130, 231, 0 }, - { 162, 198, 52, 0 }, }, - { { 115, 160, 155, 1 }, - { 30, 115, 162, 1 }, - { 236, 130, 231, 0 }, - { 162, 231, 60, 0 }, }, - { { 115, 160, 148, 1 }, - { 30, 49, 178, 0 }, - { 148, 130, 231, 0 }, - { 38, 198, 60, 0 }, }, - { { 115, 160, 151, 0 }, - { 22, 115, 178, 0 }, - { 116, 130, 231, 0 }, - { 38, 231, 52, 0 }, }, - { { 115, 160, 232, 0 }, - { 6, 181, 162, 1 }, - { 11, 130, 231, 0 }, - { 162, 214, 176, 0 }, }, - { { 115, 160, 235, 1 }, - { 14, 247, 162, 1 }, - { 235, 130, 231, 0 }, - { 162, 247, 184, 0 }, }, - { { 115, 160, 228, 1 }, - { 14, 181, 178, 0 }, - { 147, 130, 231, 0 }, - { 38, 214, 184, 0 }, }, - { { 115, 160, 231, 0 }, - { 6, 247, 178, 0 }, - { 115, 130, 231, 0 }, - { 38, 247, 176, 0 }, }, - { { 115, 175, 8, 0 }, - { 39, 57, 131, 1 }, - { 8, 122, 231, 0 }, - { 224, 206, 114, 0 }, }, - { { 115, 175, 11, 1 }, - { 47, 123, 131, 1 }, - { 232, 122, 231, 0 }, - { 224, 239, 122, 0 }, }, - { { 115, 175, 4, 1 }, - { 47, 57, 147, 0 }, - { 144, 122, 231, 0 }, - { 100, 206, 122, 0 }, }, - { { 115, 175, 7, 0 }, - { 39, 123, 147, 0 }, - { 112, 122, 231, 0 }, - { 100, 239, 114, 0 }, }, - { { 115, 175, 120, 0 }, - { 55, 189, 131, 1 }, - { 15, 122, 231, 0 }, - { 224, 222, 246, 0 }, }, - { { 115, 175, 123, 1 }, - { 63, 255, 131, 1 }, - { 239, 122, 231, 0 }, - { 224, 255, 254, 0 }, }, - { { 115, 175, 116, 1 }, - { 63, 189, 147, 0 }, - { 151, 122, 231, 0 }, - { 100, 222, 254, 0 }, }, - { { 115, 175, 119, 0 }, - { 55, 255, 147, 0 }, - { 119, 122, 231, 0 }, - { 100, 255, 246, 0 }, }, - { { 115, 174, 152, 0 }, - { 55, 57, 162, 1 }, - { 12, 186, 231, 0 }, - { 162, 206, 118, 0 }, }, - { { 115, 174, 155, 1 }, - { 63, 123, 162, 1 }, - { 236, 186, 231, 0 }, - { 162, 239, 126, 0 }, }, - { { 115, 174, 148, 1 }, - { 63, 57, 178, 0 }, - { 148, 186, 231, 0 }, - { 38, 206, 126, 0 }, }, - { { 115, 174, 151, 0 }, - { 55, 123, 178, 0 }, - { 116, 186, 231, 0 }, - { 38, 239, 118, 0 }, }, - { { 115, 174, 232, 0 }, - { 39, 189, 162, 1 }, - { 11, 186, 231, 0 }, - { 162, 222, 242, 0 }, }, - { { 115, 174, 235, 1 }, - { 47, 255, 162, 1 }, - { 235, 186, 231, 0 }, - { 162, 255, 250, 0 }, }, - { { 115, 174, 228, 1 }, - { 47, 189, 178, 0 }, - { 147, 186, 231, 0 }, - { 38, 222, 250, 0 }, }, - { { 115, 174, 231, 0 }, - { 39, 255, 178, 0 }, - { 115, 186, 231, 0 }, - { 38, 255, 242, 0 }, }, - { { 115, 147, 8, 0 }, - { 38, 49, 193, 1 }, - { 8, 100, 231, 0 }, - { 193, 198, 50, 0 }, }, - { { 115, 147, 11, 1 }, - { 46, 115, 193, 1 }, - { 232, 100, 231, 0 }, - { 193, 231, 58, 0 }, }, - { { 115, 147, 4, 1 }, - { 46, 49, 209, 0 }, - { 144, 100, 231, 0 }, - { 69, 198, 58, 0 }, }, - { { 115, 147, 7, 0 }, - { 38, 115, 209, 0 }, - { 112, 100, 231, 0 }, - { 69, 231, 50, 0 }, }, - { { 115, 147, 120, 0 }, - { 54, 181, 193, 1 }, - { 15, 100, 231, 0 }, - { 193, 214, 182, 0 }, }, - { { 115, 147, 123, 1 }, - { 62, 247, 193, 1 }, - { 239, 100, 231, 0 }, - { 193, 247, 190, 0 }, }, - { { 115, 147, 116, 1 }, - { 62, 181, 209, 0 }, - { 151, 100, 231, 0 }, - { 69, 214, 190, 0 }, }, - { { 115, 147, 119, 0 }, - { 54, 247, 209, 0 }, - { 119, 100, 231, 0 }, - { 69, 247, 182, 0 }, }, - { { 115, 146, 152, 0 }, - { 54, 49, 224, 1 }, - { 12, 164, 231, 0 }, - { 131, 198, 54, 0 }, }, - { { 115, 146, 155, 1 }, - { 62, 115, 224, 1 }, - { 236, 164, 231, 0 }, - { 131, 231, 62, 0 }, }, - { { 115, 146, 148, 1 }, - { 62, 49, 240, 0 }, - { 148, 164, 231, 0 }, - { 7, 198, 62, 0 }, }, - { { 115, 146, 151, 0 }, - { 54, 115, 240, 0 }, - { 116, 164, 231, 0 }, - { 7, 231, 54, 0 }, }, - { { 115, 146, 232, 0 }, - { 38, 181, 224, 1 }, - { 11, 164, 231, 0 }, - { 131, 214, 178, 0 }, }, - { { 115, 146, 235, 1 }, - { 46, 247, 224, 1 }, - { 235, 164, 231, 0 }, - { 131, 247, 186, 0 }, }, - { { 115, 146, 228, 1 }, - { 46, 181, 240, 0 }, - { 147, 164, 231, 0 }, - { 7, 214, 186, 0 }, }, - { { 115, 146, 231, 0 }, - { 38, 247, 240, 0 }, - { 115, 164, 231, 0 }, - { 7, 247, 178, 0 }, }, - { { 115, 157, 8, 0 }, - { 7, 57, 193, 1 }, - { 8, 92, 231, 0 }, - { 193, 206, 112, 0 }, }, - { { 115, 157, 11, 1 }, - { 15, 123, 193, 1 }, - { 232, 92, 231, 0 }, - { 193, 239, 120, 0 }, }, - { { 115, 157, 4, 1 }, - { 15, 57, 209, 0 }, - { 144, 92, 231, 0 }, - { 69, 206, 120, 0 }, }, - { { 115, 157, 7, 0 }, - { 7, 123, 209, 0 }, - { 112, 92, 231, 0 }, - { 69, 239, 112, 0 }, }, - { { 115, 157, 120, 0 }, - { 23, 189, 193, 1 }, - { 15, 92, 231, 0 }, - { 193, 222, 244, 0 }, }, - { { 115, 157, 123, 1 }, - { 31, 255, 193, 1 }, - { 239, 92, 231, 0 }, - { 193, 255, 252, 0 }, }, - { { 115, 157, 116, 1 }, - { 31, 189, 209, 0 }, - { 151, 92, 231, 0 }, - { 69, 222, 252, 0 }, }, - { { 115, 157, 119, 0 }, - { 23, 255, 209, 0 }, - { 119, 92, 231, 0 }, - { 69, 255, 244, 0 }, }, - { { 115, 156, 152, 0 }, - { 23, 57, 224, 1 }, - { 12, 156, 231, 0 }, - { 131, 206, 116, 0 }, }, - { { 115, 156, 155, 1 }, - { 31, 123, 224, 1 }, - { 236, 156, 231, 0 }, - { 131, 239, 124, 0 }, }, - { { 115, 156, 148, 1 }, - { 31, 57, 240, 0 }, - { 148, 156, 231, 0 }, - { 7, 206, 124, 0 }, }, - { { 115, 156, 151, 0 }, - { 23, 123, 240, 0 }, - { 116, 156, 231, 0 }, - { 7, 239, 116, 0 }, }, - { { 115, 156, 232, 0 }, - { 7, 189, 224, 1 }, - { 11, 156, 231, 0 }, - { 131, 222, 240, 0 }, }, - { { 115, 156, 235, 1 }, - { 15, 255, 224, 1 }, - { 235, 156, 231, 0 }, - { 131, 255, 248, 0 }, }, - { { 115, 156, 228, 1 }, - { 15, 189, 240, 0 }, - { 147, 156, 231, 0 }, - { 7, 222, 248, 0 }, }, - { { 115, 156, 231, 0 }, - { 7, 255, 240, 0 }, - { 115, 156, 231, 0 }, - { 7, 255, 240, 0 }, }, -}; - -static unsigned char DICT_4X4_1000_BYTES[][4][2] = - { { { 181, 50 }, - { 235, 72 }, - { 76, 173 }, - { 18, 215 }, }, - { { 15, 154 }, - { 101, 71 }, - { 89, 240 }, - { 226, 166 }, }, - { { 51, 45 }, - { 222, 17 }, - { 180, 204 }, - { 136, 123 }, }, - { { 153, 70 }, - { 193, 60 }, - { 98, 153 }, - { 60, 131 }, }, - { { 84, 158 }, - { 161, 211 }, - { 121, 42 }, - { 203, 133 }, }, - { { 121, 205 }, - { 216, 183 }, - { 179, 158 }, - { 237, 27 }, }, - { { 158, 46 }, - { 135, 93 }, - { 116, 121 }, - { 186, 225 }, }, - { { 196, 242 }, - { 35, 234 }, - { 79, 35 }, - { 87, 196 }, }, - { { 254, 218 }, - { 173, 239 }, - { 91, 127 }, - { 247, 181 }, }, - { { 207, 86 }, - { 101, 252 }, - { 106, 243 }, - { 63, 166 }, }, - { { 249, 145 }, - { 248, 142 }, - { 137, 159 }, - { 113, 31 }, }, - { { 17, 167 }, - { 211, 18 }, - { 229, 136 }, - { 72, 203 }, }, - { { 14, 183 }, - { 55, 86 }, - { 237, 112 }, - { 106, 236 }, }, - { { 42, 15 }, - { 29, 21 }, - { 240, 84 }, - { 168, 184 }, }, - { { 36, 177 }, - { 58, 66 }, - { 141, 36 }, - { 66, 92 }, }, - { { 38, 62 }, - { 47, 81 }, - { 124, 100 }, - { 138, 244 }, }, - { { 70, 101 }, - { 22, 240 }, - { 166, 98 }, - { 15, 104 }, }, - { { 102, 0 }, - { 12, 192 }, - { 0, 102 }, - { 3, 48 }, }, - { { 108, 94 }, - { 41, 245 }, - { 122, 54 }, - { 175, 148 }, }, - { { 118, 175 }, - { 159, 211 }, - { 245, 110 }, - { 203, 249 }, }, - { { 134, 139 }, - { 21, 75 }, - { 209, 97 }, - { 210, 168 }, }, - { { 176, 43 }, - { 155, 9 }, - { 212, 13 }, - { 144, 217 }, }, - { { 204, 213 }, - { 48, 254 }, - { 171, 51 }, - { 127, 12 }, }, - { { 221, 130 }, - { 193, 206 }, - { 65, 187 }, - { 115, 131 }, }, - { { 254, 71 }, - { 157, 252 }, - { 226, 127 }, - { 63, 185 }, }, - { { 148, 113 }, - { 178, 104 }, - { 142, 41 }, - { 22, 77 }, }, - { { 172, 228 }, - { 10, 126 }, - { 39, 53 }, - { 126, 80 }, }, - { { 165, 84 }, - { 104, 120 }, - { 42, 165 }, - { 30, 22 }, }, - { { 33, 35 }, - { 91, 0 }, - { 196, 132 }, - { 0, 218 }, }, - { { 52, 111 }, - { 155, 113 }, - { 246, 44 }, - { 142, 217 }, }, - { { 68, 21 }, - { 48, 208 }, - { 168, 34 }, - { 11, 12 }, }, - { { 87, 178 }, - { 231, 194 }, - { 77, 234 }, - { 67, 231 }, }, - { { 158, 207 }, - { 149, 127 }, - { 243, 121 }, - { 254, 169 }, }, - { { 240, 203 }, - { 153, 171 }, - { 211, 15 }, - { 213, 153 }, }, - { { 8, 174 }, - { 3, 23 }, - { 117, 16 }, - { 232, 192 }, }, - { { 9, 41 }, - { 82, 5 }, - { 148, 144 }, - { 160, 74 }, }, - { { 24, 117 }, - { 178, 52 }, - { 174, 24 }, - { 44, 77 }, }, - { { 4, 255 }, - { 51, 115 }, - { 255, 32 }, - { 206, 204 }, }, - { { 13, 246 }, - { 99, 118 }, - { 111, 176 }, - { 110, 198 }, }, - { { 28, 90 }, - { 161, 101 }, - { 90, 56 }, - { 166, 133 }, }, - { { 23, 24 }, - { 228, 65 }, - { 24, 232 }, - { 130, 39 }, }, - { { 42, 40 }, - { 14, 5 }, - { 20, 84 }, - { 160, 112 }, }, - { { 50, 140 }, - { 140, 19 }, - { 49, 76 }, - { 200, 49 }, }, - { { 56, 178 }, - { 171, 6 }, - { 77, 28 }, - { 96, 213 }, }, - { { 36, 232 }, - { 10, 99 }, - { 23, 36 }, - { 198, 80 }, }, - { { 46, 235 }, - { 31, 103 }, - { 215, 116 }, - { 230, 248 }, }, - { { 45, 63 }, - { 123, 85 }, - { 252, 180 }, - { 170, 222 }, }, - { { 75, 100 }, - { 70, 180 }, - { 38, 210 }, - { 45, 98 }, }, - { { 80, 46 }, - { 131, 145 }, - { 116, 10 }, - { 137, 193 }, }, - { { 80, 19 }, - { 177, 128 }, - { 200, 10 }, - { 1, 141 }, }, - { { 81, 148 }, - { 224, 146 }, - { 41, 138 }, - { 73, 7 }, }, - { { 85, 104 }, - { 194, 225 }, - { 22, 170 }, - { 135, 67 }, }, - { { 93, 65 }, - { 208, 228 }, - { 130, 186 }, - { 39, 11 }, }, - { { 95, 151 }, - { 245, 214 }, - { 233, 250 }, - { 107, 175 }, }, - { { 104, 1 }, - { 24, 132 }, - { 128, 22 }, - { 33, 24 }, }, - { { 104, 103 }, - { 27, 180 }, - { 230, 22 }, - { 45, 216 }, }, - { { 97, 36 }, - { 74, 144 }, - { 36, 134 }, - { 9, 82 }, }, - { { 97, 233 }, - { 90, 163 }, - { 151, 134 }, - { 197, 90 }, }, - { { 107, 18 }, - { 109, 132 }, - { 72, 214 }, - { 33, 182 }, }, - { { 111, 229 }, - { 94, 246 }, - { 167, 246 }, - { 111, 122 }, }, - { { 103, 223 }, - { 125, 243 }, - { 251, 230 }, - { 207, 190 }, }, - { { 126, 27 }, - { 189, 197 }, - { 216, 126 }, - { 163, 189 }, }, - { { 128, 160 }, - { 2, 10 }, - { 5, 1 }, - { 80, 64 }, }, - { { 131, 68 }, - { 68, 56 }, - { 34, 193 }, - { 28, 34 }, }, - { { 139, 162 }, - { 71, 14 }, - { 69, 209 }, - { 112, 226 }, }, - { { 147, 122 }, - { 231, 41 }, - { 94, 201 }, - { 148, 231 }, }, - { { 132, 108 }, - { 2, 121 }, - { 54, 33 }, - { 158, 64 }, }, - { { 133, 42 }, - { 67, 73 }, - { 84, 161 }, - { 146, 194 }, }, - { { 133, 156 }, - { 96, 91 }, - { 57, 161 }, - { 218, 6 }, }, - { { 156, 137 }, - { 144, 79 }, - { 145, 57 }, - { 242, 9 }, }, - { { 159, 161 }, - { 214, 78 }, - { 133, 249 }, - { 114, 107 }, }, - { { 187, 124 }, - { 238, 61 }, - { 62, 221 }, - { 188, 119 }, }, - { { 188, 4 }, - { 136, 92 }, - { 32, 61 }, - { 58, 17 }, }, - { { 182, 91 }, - { 189, 105 }, - { 218, 109 }, - { 150, 189 }, }, - { { 191, 200 }, - { 204, 111 }, - { 19, 253 }, - { 246, 51 }, }, - { { 183, 171 }, - { 223, 75 }, - { 213, 237 }, - { 210, 251 }, }, - { { 202, 31 }, - { 53, 157 }, - { 248, 83 }, - { 185, 172 }, }, - { { 201, 98 }, - { 67, 172 }, - { 70, 147 }, - { 53, 194 }, }, - { { 217, 88 }, - { 224, 173 }, - { 26, 155 }, - { 181, 7 }, }, - { { 211, 213 }, - { 244, 186 }, - { 171, 203 }, - { 93, 47 }, }, - { { 204, 152 }, - { 32, 207 }, - { 25, 51 }, - { 243, 4 }, }, - { { 199, 160 }, - { 70, 202 }, - { 5, 227 }, - { 83, 98 }, }, - { { 197, 55 }, - { 115, 216 }, - { 236, 163 }, - { 27, 206 }, }, - { { 233, 93 }, - { 120, 189 }, - { 186, 151 }, - { 189, 30 }, }, - { { 249, 37 }, - { 218, 156 }, - { 164, 159 }, - { 57, 91 }, }, - { { 251, 187 }, - { 255, 143 }, - { 221, 223 }, - { 241, 255 }, }, - { { 238, 42 }, - { 15, 205 }, - { 84, 119 }, - { 179, 240 }, }, - { { 247, 77 }, - { 220, 249 }, - { 178, 239 }, - { 159, 59 }, }, - { { 53, 117 }, - { 250, 112 }, - { 174, 172 }, - { 14, 95 }, }, - { { 138, 173 }, - { 22, 31 }, - { 181, 81 }, - { 248, 104 }, }, - { { 118, 23 }, - { 189, 208 }, - { 232, 110 }, - { 11, 189 }, }, - { { 10, 207 }, - { 21, 55 }, - { 243, 80 }, - { 236, 168 }, }, - { { 6, 75 }, - { 21, 97 }, - { 210, 96 }, - { 134, 168 }, }, - { { 45, 193 }, - { 88, 102 }, - { 131, 180 }, - { 102, 26 }, }, - { { 73, 216 }, - { 96, 167 }, - { 27, 146 }, - { 229, 6 }, }, - { { 67, 244 }, - { 102, 178 }, - { 47, 194 }, - { 77, 102 }, }, - { { 79, 54 }, - { 103, 212 }, - { 108, 242 }, - { 43, 230 }, }, - { { 79, 211 }, - { 117, 230 }, - { 203, 242 }, - { 103, 174 }, }, - { { 105, 228 }, - { 74, 182 }, - { 39, 150 }, - { 109, 82 }, }, - { { 112, 199 }, - { 153, 178 }, - { 227, 14 }, - { 77, 153 }, }, - { { 122, 110 }, - { 143, 181 }, - { 118, 94 }, - { 173, 241 }, }, - { { 180, 234 }, - { 139, 107 }, - { 87, 45 }, - { 214, 209 }, }, - { { 237, 79 }, - { 89, 253 }, - { 242, 183 }, - { 191, 154 }, }, - { { 252, 231 }, - { 155, 254 }, - { 231, 63 }, - { 127, 217 }, }, - { { 254, 166 }, - { 143, 222 }, - { 101, 127 }, - { 123, 241 }, }, - { { 0, 37 }, - { 18, 16 }, - { 164, 0 }, - { 8, 72 }, }, - { { 0, 67 }, - { 17, 32 }, - { 194, 0 }, - { 4, 136 }, }, - { { 10, 136 }, - { 4, 7 }, - { 17, 80 }, - { 224, 32 }, }, - { { 10, 134 }, - { 5, 22 }, - { 97, 80 }, - { 104, 160 }, }, - { { 2, 111 }, - { 23, 49 }, - { 246, 64 }, - { 140, 232 }, }, - { { 0, 28 }, - { 32, 17 }, - { 56, 0 }, - { 136, 4 }, }, - { { 0, 151 }, - { 49, 18 }, - { 233, 0 }, - { 72, 140 }, }, - { { 8, 55 }, - { 51, 20 }, - { 236, 16 }, - { 40, 204 }, }, - { { 10, 49 }, - { 54, 4 }, - { 140, 80 }, - { 32, 108 }, }, - { { 9, 198 }, - { 65, 54 }, - { 99, 144 }, - { 108, 130 }, }, - { { 11, 1 }, - { 84, 4 }, - { 128, 208 }, - { 32, 42 }, }, - { { 9, 251 }, - { 115, 39 }, - { 223, 144 }, - { 228, 206 }, }, - { { 11, 88 }, - { 100, 37 }, - { 26, 208 }, - { 164, 38 }, }, - { { 16, 130 }, - { 129, 2 }, - { 65, 8 }, - { 64, 129 }, }, - { { 24, 45 }, - { 146, 21 }, - { 180, 24 }, - { 168, 73 }, }, - { { 16, 120 }, - { 162, 33 }, - { 30, 8 }, - { 132, 69 }, }, - { { 16, 115 }, - { 179, 32 }, - { 206, 8 }, - { 4, 205 }, }, - { { 18, 116 }, - { 166, 48 }, - { 46, 72 }, - { 12, 101 }, }, - { { 18, 177 }, - { 182, 2 }, - { 141, 72 }, - { 64, 109 }, }, - { { 26, 249 }, - { 182, 39 }, - { 159, 88 }, - { 228, 109 }, }, - { { 19, 6 }, - { 197, 16 }, - { 96, 200 }, - { 8, 163 }, }, - { { 12, 14 }, - { 1, 85 }, - { 112, 48 }, - { 170, 128 }, }, - { { 12, 241 }, - { 50, 102 }, - { 143, 48 }, - { 102, 76 }, }, - { { 4, 51 }, - { 51, 64 }, - { 204, 32 }, - { 2, 204 }, }, - { { 12, 159 }, - { 49, 87 }, - { 249, 48 }, - { 234, 140 }, }, - { { 14, 242 }, - { 39, 102 }, - { 79, 112 }, - { 102, 228 }, }, - { { 14, 253 }, - { 54, 119 }, - { 191, 112 }, - { 238, 108 }, }, - { { 7, 76 }, - { 68, 113 }, - { 50, 224 }, - { 142, 34 }, }, - { { 15, 164 }, - { 70, 86 }, - { 37, 240 }, - { 106, 98 }, }, - { { 7, 47 }, - { 87, 81 }, - { 244, 224 }, - { 138, 234 }, }, - { { 5, 181 }, - { 114, 82 }, - { 173, 160 }, - { 74, 78 }, }, - { { 15, 145 }, - { 116, 70 }, - { 137, 240 }, - { 98, 46 }, }, - { { 7, 219 }, - { 117, 99 }, - { 219, 224 }, - { 198, 174 }, }, - { { 30, 228 }, - { 134, 118 }, - { 39, 120 }, - { 110, 97 }, }, - { { 20, 57 }, - { 178, 65 }, - { 156, 40 }, - { 130, 77 }, }, - { { 29, 128 }, - { 192, 70 }, - { 1, 184 }, - { 98, 3 }, }, - { { 21, 200 }, - { 192, 99 }, - { 19, 168 }, - { 198, 3 }, }, - { { 31, 139 }, - { 213, 71 }, - { 209, 248 }, - { 226, 171 }, }, - { { 21, 186 }, - { 227, 67 }, - { 93, 168 }, - { 194, 199 }, }, - { { 29, 177 }, - { 242, 70 }, - { 141, 184 }, - { 98, 79 }, }, - { { 32, 128 }, - { 8, 2 }, - { 1, 4 }, - { 64, 16 }, }, - { { 40, 233 }, - { 26, 39 }, - { 151, 20 }, - { 228, 88 }, }, - { { 34, 162 }, - { 15, 2 }, - { 69, 68 }, - { 64, 240 }, }, - { { 40, 83 }, - { 57, 36 }, - { 202, 20 }, - { 36, 156 }, }, - { { 42, 240 }, - { 46, 38 }, - { 15, 84 }, - { 100, 116 }, }, - { { 34, 247 }, - { 63, 50 }, - { 239, 68 }, - { 76, 252 }, }, - { { 41, 64 }, - { 72, 36 }, - { 2, 148 }, - { 36, 18 }, }, - { { 33, 70 }, - { 73, 48 }, - { 98, 132 }, - { 12, 146 }, }, - { { 41, 185 }, - { 122, 7 }, - { 157, 148 }, - { 224, 94 }, }, - { { 43, 156 }, - { 108, 23 }, - { 57, 212 }, - { 232, 54 }, }, - { { 43, 178 }, - { 111, 6 }, - { 77, 212 }, - { 96, 246 }, }, - { { 56, 202 }, - { 137, 39 }, - { 83, 28 }, - { 228, 145 }, }, - { { 56, 46 }, - { 139, 21 }, - { 116, 28 }, - { 168, 209 }, }, - { { 48, 7 }, - { 153, 16 }, - { 224, 12 }, - { 8, 153 }, }, - { { 56, 231 }, - { 155, 54 }, - { 231, 28 }, - { 108, 217 }, }, - { { 58, 73 }, - { 156, 37 }, - { 146, 92 }, - { 164, 57 }, }, - { { 58, 101 }, - { 158, 52 }, - { 166, 92 }, - { 44, 121 }, }, - { { 50, 93 }, - { 188, 49 }, - { 186, 76 }, - { 140, 61 }, }, - { { 59, 136 }, - { 204, 7 }, - { 17, 220 }, - { 224, 51 }, }, - { { 57, 29 }, - { 248, 21 }, - { 184, 156 }, - { 168, 31 }, }, - { { 59, 211 }, - { 253, 38 }, - { 203, 220 }, - { 100, 191 }, }, - { { 38, 71 }, - { 29, 112 }, - { 226, 100 }, - { 14, 184 }, }, - { { 39, 128 }, - { 76, 66 }, - { 1, 228 }, - { 66, 50 }, }, - { { 47, 170 }, - { 79, 71 }, - { 85, 244 }, - { 226, 242 }, }, - { { 45, 20 }, - { 104, 84 }, - { 40, 180 }, - { 42, 22 }, }, - { { 37, 222 }, - { 105, 115 }, - { 123, 164 }, - { 206, 150 }, }, - { { 37, 83 }, - { 121, 96 }, - { 202, 164 }, - { 6, 158 }, }, - { { 47, 119 }, - { 127, 116 }, - { 238, 244 }, - { 46, 254 }, }, - { { 52, 72 }, - { 136, 97 }, - { 18, 44 }, - { 134, 17 }, }, - { { 60, 168 }, - { 138, 71 }, - { 21, 60 }, - { 226, 81 }, }, - { { 60, 65 }, - { 152, 100 }, - { 130, 60 }, - { 38, 25 }, }, - { { 52, 13 }, - { 152, 81 }, - { 176, 44 }, - { 138, 25 }, }, - { { 52, 251 }, - { 187, 99 }, - { 223, 44 }, - { 198, 221 }, }, - { { 54, 154 }, - { 173, 67 }, - { 89, 108 }, - { 194, 181 }, }, - { { 61, 224 }, - { 202, 102 }, - { 7, 188 }, - { 102, 83 }, }, - { { 53, 106 }, - { 203, 97 }, - { 86, 172 }, - { 134, 211 }, }, - { { 61, 9 }, - { 216, 69 }, - { 144, 188 }, - { 162, 27 }, }, - { { 61, 237 }, - { 218, 119 }, - { 183, 188 }, - { 238, 91 }, }, - { { 63, 196 }, - { 204, 118 }, - { 35, 252 }, - { 110, 51 }, }, - { { 63, 108 }, - { 206, 117 }, - { 54, 252 }, - { 174, 115 }, }, - { { 55, 206 }, - { 205, 115 }, - { 115, 236 }, - { 206, 179 }, }, - { { 61, 92 }, - { 232, 117 }, - { 58, 188 }, - { 174, 23 }, }, - { { 61, 118 }, - { 235, 116 }, - { 110, 188 }, - { 46, 215 }, }, - { { 55, 176 }, - { 238, 66 }, - { 13, 236 }, - { 66, 119 }, }, - { { 63, 23 }, - { 253, 84 }, - { 232, 252 }, - { 42, 191 }, }, - { { 63, 255 }, - { 255, 119 }, - { 255, 252 }, - { 238, 255 }, }, - { { 72, 229 }, - { 18, 182 }, - { 167, 18 }, - { 109, 72 }, }, - { { 66, 104 }, - { 6, 161 }, - { 22, 66 }, - { 133, 96 }, }, - { { 74, 45 }, - { 22, 149 }, - { 180, 82 }, - { 169, 104 }, }, - { { 65, 96 }, - { 66, 160 }, - { 6, 130 }, - { 5, 66 }, }, - { { 73, 81 }, - { 112, 164 }, - { 138, 146 }, - { 37, 14 }, }, - { { 65, 221 }, - { 112, 179 }, - { 187, 130 }, - { 205, 14 }, }, - { { 75, 223 }, - { 117, 183 }, - { 251, 210 }, - { 237, 174 }, }, - { { 88, 79 }, - { 145, 181 }, - { 242, 26 }, - { 173, 137 }, }, - { { 90, 72 }, - { 132, 165 }, - { 18, 90 }, - { 165, 33 }, }, - { { 88, 22 }, - { 161, 148 }, - { 104, 26 }, - { 41, 133 }, }, - { { 80, 93 }, - { 176, 177 }, - { 186, 10 }, - { 141, 13 }, }, - { { 90, 250 }, - { 167, 167 }, - { 95, 90 }, - { 229, 229 }, }, - { { 90, 181 }, - { 182, 150 }, - { 173, 90 }, - { 105, 109 }, }, - { { 81, 35 }, - { 211, 128 }, - { 196, 138 }, - { 1, 203 }, }, - { { 91, 138 }, - { 197, 135 }, - { 81, 218 }, - { 225, 163 }, }, - { { 89, 25 }, - { 240, 133 }, - { 152, 154 }, - { 161, 15 }, }, - { { 81, 53 }, - { 242, 144 }, - { 172, 138 }, - { 9, 79 }, }, - { { 76, 105 }, - { 18, 229 }, - { 150, 50 }, - { 167, 72 }, }, - { { 70, 193 }, - { 20, 226 }, - { 131, 98 }, - { 71, 40 }, }, - { { 78, 11 }, - { 21, 197 }, - { 208, 114 }, - { 163, 168 }, }, - { { 68, 95 }, - { 49, 241 }, - { 250, 34 }, - { 143, 140 }, }, - { { 78, 89 }, - { 52, 229 }, - { 154, 114 }, - { 167, 44 }, }, - { { 77, 131 }, - { 81, 198 }, - { 193, 178 }, - { 99, 138 }, }, - { { 77, 125 }, - { 114, 245 }, - { 190, 178 }, - { 175, 78 }, }, - { { 71, 216 }, - { 100, 227 }, - { 27, 226 }, - { 199, 38 }, }, - { { 71, 115 }, - { 119, 224 }, - { 206, 226 }, - { 7, 238 }, }, - { { 92, 133 }, - { 144, 214 }, - { 161, 58 }, - { 107, 9 }, }, - { { 94, 68 }, - { 132, 244 }, - { 34, 122 }, - { 47, 33 }, }, - { { 86, 43 }, - { 151, 193 }, - { 212, 106 }, - { 131, 233 }, }, - { { 92, 187 }, - { 179, 199 }, - { 221, 58 }, - { 227, 205 }, }, - { { 85, 195 }, - { 209, 226 }, - { 195, 170 }, - { 71, 139 }, }, - { { 95, 110 }, - { 199, 245 }, - { 118, 250 }, - { 175, 227 }, }, - { { 95, 235 }, - { 215, 231 }, - { 215, 250 }, - { 231, 235 }, }, - { { 93, 18 }, - { 225, 196 }, - { 72, 186 }, - { 35, 135 }, }, - { { 85, 94 }, - { 225, 241 }, - { 122, 170 }, - { 143, 135 }, }, - { { 98, 112 }, - { 46, 160 }, - { 14, 70 }, - { 5, 116 }, }, - { { 98, 21 }, - { 60, 144 }, - { 168, 70 }, - { 9, 60 }, }, - { { 97, 194 }, - { 73, 162 }, - { 67, 134 }, - { 69, 146 }, }, - { { 107, 32 }, - { 78, 132 }, - { 4, 214 }, - { 33, 114 }, }, - { { 99, 69 }, - { 92, 176 }, - { 162, 198 }, - { 13, 58 }, }, - { { 107, 92 }, - { 108, 181 }, - { 58, 214 }, - { 173, 54 }, }, - { { 107, 91 }, - { 125, 165 }, - { 218, 214 }, - { 165, 190 }, }, - { { 120, 12 }, - { 136, 149 }, - { 48, 30 }, - { 169, 17 }, }, - { { 122, 207 }, - { 157, 183 }, - { 243, 94 }, - { 237, 185 }, }, - { { 120, 127 }, - { 187, 181 }, - { 254, 30 }, - { 173, 221 }, }, - { { 121, 128 }, - { 200, 134 }, - { 1, 158 }, - { 97, 19 }, }, - { { 113, 229 }, - { 218, 178 }, - { 167, 142 }, - { 77, 91 }, }, - { { 113, 116 }, - { 234, 176 }, - { 46, 142 }, - { 13, 87 }, }, - { { 121, 182 }, - { 235, 150 }, - { 109, 158 }, - { 105, 215 }, }, - { { 113, 211 }, - { 249, 162 }, - { 203, 142 }, - { 69, 159 }, }, - { { 123, 51 }, - { 255, 132 }, - { 204, 222 }, - { 33, 255 }, }, - { { 100, 106 }, - { 11, 225 }, - { 86, 38 }, - { 135, 208 }, }, - { { 102, 168 }, - { 14, 195 }, - { 21, 102 }, - { 195, 112 }, }, - { { 110, 167 }, - { 31, 214 }, - { 229, 118 }, - { 107, 248 }, }, - { { 110, 145 }, - { 60, 198 }, - { 137, 118 }, - { 99, 60 }, }, - { { 101, 34 }, - { 75, 192 }, - { 68, 166 }, - { 3, 210 }, }, - { { 109, 203 }, - { 89, 231 }, - { 211, 182 }, - { 231, 154 }, }, - { { 103, 141 }, - { 92, 211 }, - { 177, 230 }, - { 203, 58 }, }, - { { 109, 49 }, - { 122, 196 }, - { 140, 182 }, - { 35, 94 }, }, - { { 126, 128 }, - { 140, 198 }, - { 1, 126 }, - { 99, 49 }, }, - { { 126, 226 }, - { 143, 230 }, - { 71, 126 }, - { 103, 241 }, }, - { { 126, 141 }, - { 156, 215 }, - { 177, 126 }, - { 235, 57 }, }, - { { 116, 210 }, - { 169, 226 }, - { 75, 46 }, - { 71, 149 }, }, - { { 124, 50 }, - { 171, 196 }, - { 76, 62 }, - { 35, 213 }, }, - { { 126, 53 }, - { 190, 212 }, - { 172, 126 }, - { 43, 125 }, }, - { { 117, 171 }, - { 219, 195 }, - { 213, 174 }, - { 195, 219 }, }, - { { 119, 5 }, - { 220, 208 }, - { 160, 238 }, - { 11, 59 }, }, - { { 127, 43 }, - { 223, 197 }, - { 212, 254 }, - { 163, 251 }, }, - { { 125, 218 }, - { 233, 231 }, - { 91, 190 }, - { 231, 151 }, }, - { { 127, 146 }, - { 237, 198 }, - { 73, 254 }, - { 99, 183 }, }, - { { 128, 117 }, - { 50, 56 }, - { 174, 1 }, - { 28, 76 }, }, - { { 128, 243 }, - { 51, 42 }, - { 207, 1 }, - { 84, 204 }, }, - { { 129, 166 }, - { 67, 26 }, - { 101, 129 }, - { 88, 194 }, }, - { { 137, 237 }, - { 82, 63 }, - { 183, 145 }, - { 252, 74 }, }, - { { 129, 252 }, - { 98, 59 }, - { 63, 129 }, - { 220, 70 }, }, - { { 152, 166 }, - { 131, 30 }, - { 101, 25 }, - { 120, 193 }, }, - { { 154, 32 }, - { 134, 12 }, - { 4, 89 }, - { 48, 97 }, }, - { { 145, 67 }, - { 209, 40 }, - { 194, 137 }, - { 20, 139 }, }, - { { 153, 249 }, - { 242, 47 }, - { 159, 153 }, - { 244, 79 }, }, - { { 145, 147 }, - { 241, 10 }, - { 201, 137 }, - { 80, 143 }, }, - { { 155, 212 }, - { 228, 62 }, - { 43, 217 }, - { 124, 39 }, }, - { { 132, 9 }, - { 16, 73 }, - { 144, 33 }, - { 146, 8 }, }, - { { 132, 107 }, - { 19, 105 }, - { 214, 33 }, - { 150, 200 }, }, - { { 134, 196 }, - { 4, 122 }, - { 35, 97 }, - { 94, 32 }, }, - { { 142, 100 }, - { 6, 124 }, - { 38, 113 }, - { 62, 96 }, }, - { { 134, 26 }, - { 37, 73 }, - { 88, 97 }, - { 146, 164 }, }, - { { 133, 78 }, - { 65, 121 }, - { 114, 161 }, - { 158, 130 }, }, - { { 141, 203 }, - { 81, 111 }, - { 211, 177 }, - { 246, 138 }, }, - { { 133, 103 }, - { 83, 120 }, - { 230, 161 }, - { 30, 202 }, }, - { { 133, 175 }, - { 83, 91 }, - { 245, 161 }, - { 218, 202 }, }, - { { 133, 215 }, - { 113, 122 }, - { 235, 161 }, - { 94, 142 }, }, - { { 135, 179 }, - { 119, 74 }, - { 205, 225 }, - { 82, 238 }, }, - { { 156, 225 }, - { 146, 110 }, - { 135, 57 }, - { 118, 73 }, }, - { { 156, 242 }, - { 163, 110 }, - { 79, 57 }, - { 118, 197 }, }, - { { 148, 23 }, - { 177, 88 }, - { 232, 41 }, - { 26, 141 }, }, - { { 149, 0 }, - { 192, 72 }, - { 0, 169 }, - { 18, 3 }, }, - { { 149, 162 }, - { 195, 74 }, - { 69, 169 }, - { 82, 195 }, }, - { { 157, 35 }, - { 211, 76 }, - { 196, 185 }, - { 50, 203 }, }, - { { 159, 98 }, - { 199, 108 }, - { 70, 249 }, - { 54, 227 }, }, - { { 157, 82 }, - { 225, 108 }, - { 74, 185 }, - { 54, 135 }, }, - { { 149, 218 }, - { 225, 107 }, - { 91, 169 }, - { 214, 135 }, }, - { { 160, 197 }, - { 24, 58 }, - { 163, 5 }, - { 92, 24 }, }, - { { 170, 205 }, - { 28, 63 }, - { 179, 85 }, - { 252, 56 }, }, - { { 162, 216 }, - { 44, 43 }, - { 27, 69 }, - { 212, 52 }, }, - { { 162, 87 }, - { 61, 56 }, - { 234, 69 }, - { 28, 188 }, }, - { { 169, 61 }, - { 122, 29 }, - { 188, 149 }, - { 184, 94 }, }, - { { 169, 87 }, - { 121, 60 }, - { 234, 149 }, - { 60, 158 }, }, - { { 171, 82 }, - { 109, 44 }, - { 74, 213 }, - { 52, 182 }, }, - { { 163, 54 }, - { 111, 24 }, - { 108, 197 }, - { 24, 246 }, }, - { { 163, 89 }, - { 124, 41 }, - { 154, 197 }, - { 148, 62 }, }, - { { 176, 244 }, - { 170, 58 }, - { 47, 13 }, - { 92, 85 }, }, - { { 184, 18 }, - { 169, 12 }, - { 72, 29 }, - { 48, 149 }, }, - { { 176, 191 }, - { 187, 27 }, - { 253, 13 }, - { 216, 221 }, }, - { { 178, 157 }, - { 188, 27 }, - { 185, 77 }, - { 216, 61 }, }, - { { 187, 237 }, - { 222, 63 }, - { 183, 221 }, - { 252, 123 }, }, - { { 185, 114 }, - { 235, 44 }, - { 78, 157 }, - { 52, 215 }, }, - { { 185, 150 }, - { 233, 30 }, - { 105, 157 }, - { 120, 151 }, }, - { { 164, 195 }, - { 25, 106 }, - { 195, 37 }, - { 86, 152 }, }, - { { 172, 210 }, - { 41, 110 }, - { 75, 53 }, - { 118, 148 }, }, - { { 174, 177 }, - { 62, 78 }, - { 141, 117 }, - { 114, 124 }, }, - { { 165, 130 }, - { 73, 74 }, - { 65, 165 }, - { 82, 146 }, }, - { { 175, 101 }, - { 94, 124 }, - { 166, 245 }, - { 62, 122 }, }, - { { 165, 123 }, - { 123, 105 }, - { 222, 165 }, - { 150, 222 }, }, - { { 175, 250 }, - { 111, 111 }, - { 95, 245 }, - { 246, 246 }, }, - { { 180, 100 }, - { 138, 120 }, - { 38, 45 }, - { 30, 81 }, }, - { { 188, 98 }, - { 139, 108 }, - { 70, 61 }, - { 54, 209 }, }, - { { 180, 129 }, - { 152, 74 }, - { 129, 45 }, - { 82, 25 }, }, - { { 182, 160 }, - { 142, 74 }, - { 5, 109 }, - { 82, 113 }, }, - { { 190, 238 }, - { 143, 127 }, - { 119, 125 }, - { 254, 241 }, }, - { { 190, 13 }, - { 156, 93 }, - { 176, 125 }, - { 186, 57 }, }, - { { 188, 217 }, - { 184, 111 }, - { 155, 61 }, - { 246, 29 }, }, - { { 190, 248 }, - { 174, 111 }, - { 31, 125 }, - { 246, 117 }, }, - { { 181, 40 }, - { 202, 73 }, - { 20, 173 }, - { 146, 83 }, }, - { { 183, 9 }, - { 220, 73 }, - { 144, 237 }, - { 146, 59 }, }, - { { 183, 210 }, - { 237, 106 }, - { 75, 237 }, - { 86, 183 }, }, - { { 192, 234 }, - { 3, 171 }, - { 87, 3 }, - { 213, 192 }, }, - { { 192, 25 }, - { 48, 137 }, - { 152, 3 }, - { 145, 12 }, }, - { { 192, 253 }, - { 50, 187 }, - { 191, 3 }, - { 221, 76 }, }, - { { 200, 211 }, - { 49, 174 }, - { 203, 19 }, - { 117, 140 }, }, - { { 202, 90 }, - { 37, 173 }, - { 90, 83 }, - { 181, 164 }, }, - { { 193, 77 }, - { 80, 185 }, - { 178, 131 }, - { 157, 10 }, }, - { { 201, 180 }, - { 98, 158 }, - { 45, 147 }, - { 121, 70 }, }, - { { 193, 87 }, - { 113, 184 }, - { 234, 131 }, - { 29, 142 }, }, - { { 195, 152 }, - { 100, 139 }, - { 25, 195 }, - { 209, 38 }, }, - { { 195, 29 }, - { 116, 153 }, - { 184, 195 }, - { 153, 46 }, }, - { { 216, 128 }, - { 128, 142 }, - { 1, 27 }, - { 113, 1 }, }, - { { 216, 239 }, - { 147, 191 }, - { 247, 27 }, - { 253, 201 }, }, - { { 218, 43 }, - { 151, 141 }, - { 212, 91 }, - { 177, 233 }, }, - { { 208, 30 }, - { 161, 153 }, - { 120, 11 }, - { 153, 133 }, }, - { { 209, 5 }, - { 208, 152 }, - { 160, 139 }, - { 25, 11 }, }, - { { 211, 173 }, - { 214, 155 }, - { 181, 203 }, - { 217, 107 }, }, - { { 219, 167 }, - { 215, 158 }, - { 229, 219 }, - { 121, 235 }, }, - { { 196, 201 }, - { 16, 235 }, - { 147, 35 }, - { 215, 8 }, }, - { { 204, 120 }, - { 34, 237 }, - { 30, 51 }, - { 183, 68 }, }, - { { 205, 69 }, - { 80, 252 }, - { 162, 179 }, - { 63, 10 }, }, - { { 197, 11 }, - { 81, 201 }, - { 208, 163 }, - { 147, 138 }, }, - { { 207, 207 }, - { 85, 255 }, - { 243, 243 }, - { 255, 170 }, }, - { { 220, 172 }, - { 130, 223 }, - { 53, 59 }, - { 251, 65 }, }, - { { 212, 2 }, - { 129, 200 }, - { 64, 43 }, - { 19, 129 }, }, - { { 220, 99 }, - { 147, 236 }, - { 198, 59 }, - { 55, 201 }, }, - { { 212, 39 }, - { 147, 216 }, - { 228, 43 }, - { 27, 201 }, }, - { { 212, 245 }, - { 178, 250 }, - { 175, 43 }, - { 95, 77 }, }, - { { 214, 120 }, - { 166, 233 }, - { 30, 107 }, - { 151, 101 }, }, - { { 222, 184 }, - { 166, 207 }, - { 29, 123 }, - { 243, 101 }, }, - { { 221, 230 }, - { 195, 254 }, - { 103, 187 }, - { 127, 195 }, }, - { { 213, 93 }, - { 240, 249 }, - { 186, 171 }, - { 159, 15 }, }, - { { 221, 189 }, - { 242, 223 }, - { 189, 187 }, - { 251, 79 }, }, - { { 223, 29 }, - { 244, 221 }, - { 184, 251 }, - { 187, 47 }, }, - { { 226, 202 }, - { 13, 171 }, - { 83, 71 }, - { 213, 176 }, }, - { { 234, 107 }, - { 31, 173 }, - { 214, 87 }, - { 181, 248 }, }, - { { 224, 180 }, - { 42, 154 }, - { 45, 7 }, - { 89, 84 }, }, - { { 226, 56 }, - { 46, 137 }, - { 28, 71 }, - { 145, 116 }, }, - { { 226, 212 }, - { 44, 186 }, - { 43, 71 }, - { 93, 52 }, }, - { { 227, 34 }, - { 79, 136 }, - { 68, 199 }, - { 17, 242 }, }, - { { 225, 216 }, - { 104, 171 }, - { 27, 135 }, - { 213, 22 }, }, - { { 240, 3 }, - { 153, 136 }, - { 192, 15 }, - { 17, 153 }, }, - { { 242, 204 }, - { 140, 187 }, - { 51, 79 }, - { 221, 49 }, }, - { { 248, 246 }, - { 171, 190 }, - { 111, 31 }, - { 125, 213 }, }, - { { 241, 73 }, - { 216, 169 }, - { 146, 143 }, - { 149, 27 }, }, - { { 243, 234 }, - { 207, 171 }, - { 87, 207 }, - { 213, 243 }, }, - { { 241, 156 }, - { 232, 155 }, - { 57, 143 }, - { 217, 23 }, }, - { { 249, 245 }, - { 250, 190 }, - { 175, 159 }, - { 125, 95 }, }, - { { 241, 59 }, - { 251, 137 }, - { 220, 143 }, - { 145, 223 }, }, - { { 236, 141 }, - { 24, 223 }, - { 177, 55 }, - { 251, 24 }, }, - { { 238, 201 }, - { 28, 239 }, - { 147, 119 }, - { 247, 56 }, }, - { { 230, 15 }, - { 29, 217 }, - { 240, 103 }, - { 155, 184 }, }, - { { 228, 247 }, - { 59, 250 }, - { 239, 39 }, - { 95, 220 }, }, - { { 231, 96 }, - { 78, 232 }, - { 6, 231 }, - { 23, 114 }, }, - { { 239, 232 }, - { 78, 239 }, - { 23, 247 }, - { 247, 114 }, }, - { { 237, 178 }, - { 107, 206 }, - { 77, 183 }, - { 115, 214 }, }, - { { 229, 21 }, - { 120, 216 }, - { 168, 167 }, - { 27, 30 }, }, - { { 239, 209 }, - { 124, 238 }, - { 139, 247 }, - { 119, 62 }, }, - { { 244, 134 }, - { 137, 218 }, - { 97, 47 }, - { 91, 145 }, }, - { { 252, 1 }, - { 152, 204 }, - { 128, 63 }, - { 51, 25 }, }, - { { 246, 195 }, - { 157, 234 }, - { 195, 111 }, - { 87, 185 }, }, - { { 244, 124 }, - { 170, 249 }, - { 62, 47 }, - { 159, 85 }, }, - { { 252, 147 }, - { 185, 206 }, - { 201, 63 }, - { 115, 157 }, }, - { { 245, 66 }, - { 201, 232 }, - { 66, 175 }, - { 23, 147 }, }, - { { 253, 152 }, - { 232, 207 }, - { 25, 191 }, - { 243, 23 }, }, - { { 245, 61 }, - { 250, 217 }, - { 188, 175 }, - { 155, 95 }, }, - { { 2, 189 }, - { 54, 19 }, - { 189, 64 }, - { 200, 108 }, }, - { { 0, 225 }, - { 18, 34 }, - { 135, 0 }, - { 68, 72 }, }, - { { 2, 226 }, - { 7, 34 }, - { 71, 64 }, - { 68, 224 }, }, - { { 2, 174 }, - { 7, 19 }, - { 117, 64 }, - { 200, 224 }, }, - { { 8, 120 }, - { 34, 37 }, - { 30, 16 }, - { 164, 68 }, }, - { { 0, 116 }, - { 34, 48 }, - { 46, 0 }, - { 12, 68 }, }, - { { 8, 158 }, - { 33, 23 }, - { 121, 16 }, - { 232, 132 }, }, - { { 8, 209 }, - { 48, 38 }, - { 139, 16 }, - { 100, 12 }, }, - { { 8, 125 }, - { 50, 53 }, - { 190, 16 }, - { 172, 76 }, }, - { { 10, 50 }, - { 39, 4 }, - { 76, 80 }, - { 32, 228 }, }, - { { 10, 222 }, - { 37, 55 }, - { 123, 80 }, - { 236, 164 }, }, - { { 2, 81 }, - { 52, 32 }, - { 138, 64 }, - { 4, 44 }, }, - { { 1, 162 }, - { 67, 2 }, - { 69, 128 }, - { 64, 194 }, }, - { { 3, 128 }, - { 68, 2 }, - { 1, 192 }, - { 64, 34 }, }, - { { 11, 131 }, - { 85, 6 }, - { 193, 208 }, - { 96, 170 }, }, - { { 11, 75 }, - { 85, 37 }, - { 210, 208 }, - { 164, 170 }, }, - { { 11, 39 }, - { 87, 20 }, - { 228, 208 }, - { 40, 234 }, }, - { { 11, 239 }, - { 87, 55 }, - { 247, 208 }, - { 236, 234 }, }, - { { 9, 182 }, - { 99, 22 }, - { 109, 144 }, - { 104, 198 }, }, - { { 9, 89 }, - { 112, 37 }, - { 154, 144 }, - { 164, 14 }, }, - { { 9, 147 }, - { 113, 6 }, - { 201, 144 }, - { 96, 142 }, }, - { { 11, 248 }, - { 102, 39 }, - { 31, 208 }, - { 228, 102 }, }, - { { 3, 217 }, - { 116, 35 }, - { 155, 192 }, - { 196, 46 }, }, - { { 3, 241 }, - { 118, 34 }, - { 143, 192 }, - { 68, 110 }, }, - { { 16, 196 }, - { 128, 50 }, - { 35, 8 }, - { 76, 1 }, }, - { { 24, 171 }, - { 147, 7 }, - { 213, 24 }, - { 224, 201 }, }, - { { 26, 160 }, - { 134, 6 }, - { 5, 88 }, - { 96, 97 }, }, - { { 26, 4 }, - { 132, 20 }, - { 32, 88 }, - { 40, 33 }, }, - { { 26, 108 }, - { 134, 53 }, - { 54, 88 }, - { 172, 97 }, }, - { { 26, 174 }, - { 135, 23 }, - { 117, 88 }, - { 232, 225 }, }, - { { 18, 137 }, - { 148, 3 }, - { 145, 72 }, - { 192, 41 }, }, - { { 16, 23 }, - { 177, 16 }, - { 232, 8 }, - { 8, 141 }, }, - { { 26, 243 }, - { 183, 38 }, - { 207, 88 }, - { 100, 237 }, }, - { { 25, 64 }, - { 192, 36 }, - { 2, 152 }, - { 36, 3 }, }, - { { 17, 2 }, - { 193, 0 }, - { 64, 136 }, - { 0, 131 }, }, - { { 17, 43 }, - { 211, 1 }, - { 212, 136 }, - { 128, 203 }, }, - { { 17, 207 }, - { 209, 51 }, - { 243, 136 }, - { 204, 139 }, }, - { { 27, 34 }, - { 199, 4 }, - { 68, 216 }, - { 32, 227 }, }, - { { 19, 46 }, - { 199, 17 }, - { 116, 200 }, - { 136, 227 }, }, - { { 17, 21 }, - { 240, 16 }, - { 168, 136 }, - { 8, 15 }, }, - { { 19, 187 }, - { 247, 3 }, - { 221, 200 }, - { 192, 239 }, }, - { { 12, 32 }, - { 2, 68 }, - { 4, 48 }, - { 34, 64 }, }, - { { 12, 201 }, - { 16, 103 }, - { 147, 48 }, - { 230, 8 }, }, - { { 12, 220 }, - { 32, 119 }, - { 59, 48 }, - { 238, 4 }, }, - { { 12, 54 }, - { 35, 84 }, - { 108, 48 }, - { 42, 196 }, }, - { { 6, 20 }, - { 36, 80 }, - { 40, 96 }, - { 10, 36 }, }, - { { 6, 114 }, - { 39, 96 }, - { 78, 96 }, - { 6, 228 }, }, - { { 13, 97 }, - { 82, 100 }, - { 134, 176 }, - { 38, 74 }, }, - { { 5, 13 }, - { 80, 81 }, - { 176, 160 }, - { 138, 10 }, }, - { { 13, 143 }, - { 81, 87 }, - { 241, 176 }, - { 234, 138 }, }, - { { 15, 224 }, - { 70, 102 }, - { 7, 240 }, - { 102, 98 }, }, - { { 15, 73 }, - { 84, 101 }, - { 146, 240 }, - { 166, 42 }, }, - { { 7, 133 }, - { 84, 82 }, - { 161, 224 }, - { 74, 42 }, }, - { { 5, 144 }, - { 96, 66 }, - { 9, 160 }, - { 66, 6 }, }, - { { 13, 51 }, - { 115, 68 }, - { 204, 176 }, - { 34, 206 }, }, - { { 15, 150 }, - { 101, 86 }, - { 105, 240 }, - { 106, 166 }, }, - { { 15, 118 }, - { 103, 116 }, - { 110, 240 }, - { 46, 230 }, }, - { { 20, 96 }, - { 130, 96 }, - { 6, 40 }, - { 6, 65 }, }, - { { 28, 141 }, - { 144, 87 }, - { 177, 56 }, - { 234, 9 }, }, - { { 20, 218 }, - { 161, 99 }, - { 91, 40 }, - { 198, 133 }, }, - { { 28, 115 }, - { 179, 100 }, - { 206, 56 }, - { 38, 205 }, }, - { { 30, 148 }, - { 164, 86 }, - { 41, 120 }, - { 106, 37 }, }, - { { 30, 186 }, - { 167, 71 }, - { 93, 120 }, - { 226, 229 }, }, - { { 22, 217 }, - { 180, 99 }, - { 155, 104 }, - { 198, 45 }, }, - { { 30, 61 }, - { 182, 85 }, - { 188, 120 }, - { 170, 109 }, }, - { { 22, 251 }, - { 183, 99 }, - { 223, 104 }, - { 198, 237 }, }, - { { 29, 233 }, - { 210, 103 }, - { 151, 184 }, - { 230, 75 }, }, - { { 29, 254 }, - { 227, 119 }, - { 127, 184 }, - { 238, 199 }, }, - { { 31, 159 }, - { 245, 87 }, - { 249, 248 }, - { 234, 175 }, }, - { { 40, 139 }, - { 25, 7 }, - { 209, 20 }, - { 224, 152 }, }, - { { 32, 175 }, - { 27, 19 }, - { 245, 4 }, - { 200, 216 }, }, - { { 34, 14 }, - { 13, 17 }, - { 112, 68 }, - { 136, 176 }, }, - { { 34, 169 }, - { 30, 3 }, - { 149, 68 }, - { 192, 120 }, }, - { { 42, 141 }, - { 28, 23 }, - { 177, 84 }, - { 232, 56 }, }, - { { 42, 163 }, - { 31, 6 }, - { 197, 84 }, - { 96, 248 }, }, - { { 42, 239 }, - { 31, 55 }, - { 247, 84 }, - { 236, 248 }, }, - { { 40, 144 }, - { 40, 6 }, - { 9, 20 }, - { 96, 20 }, }, - { { 40, 59 }, - { 59, 5 }, - { 220, 20 }, - { 160, 220 }, }, - { { 42, 88 }, - { 44, 37 }, - { 26, 84 }, - { 164, 52 }, }, - { { 34, 51 }, - { 63, 0 }, - { 204, 68 }, - { 0, 252 }, }, - { { 33, 160 }, - { 74, 2 }, - { 5, 132 }, - { 64, 82 }, }, - { { 33, 2 }, - { 73, 0 }, - { 64, 132 }, - { 0, 146 }, }, - { { 33, 165 }, - { 90, 18 }, - { 165, 132 }, - { 72, 90 }, }, - { { 33, 199 }, - { 89, 50 }, - { 227, 132 }, - { 76, 154 }, }, - { { 43, 3 }, - { 93, 4 }, - { 192, 212 }, - { 32, 186 }, }, - { { 35, 103 }, - { 95, 48 }, - { 230, 196 }, - { 12, 250 }, }, - { { 41, 48 }, - { 106, 4 }, - { 12, 148 }, - { 32, 86 }, }, - { { 41, 210 }, - { 105, 38 }, - { 75, 148 }, - { 100, 150 }, }, - { { 43, 25 }, - { 124, 5 }, - { 152, 212 }, - { 160, 62 }, }, - { { 43, 155 }, - { 125, 7 }, - { 217, 212 }, - { 224, 190 }, }, - { { 43, 151 }, - { 125, 22 }, - { 233, 212 }, - { 104, 190 }, }, - { { 56, 40 }, - { 138, 5 }, - { 20, 28 }, - { 160, 81 }, }, - { { 56, 165 }, - { 154, 22 }, - { 165, 28 }, - { 104, 89 }, }, - { { 58, 134 }, - { 141, 22 }, - { 97, 92 }, - { 104, 177 }, }, - { { 50, 1 }, - { 156, 0 }, - { 128, 76 }, - { 0, 57 }, }, - { { 56, 159 }, - { 185, 23 }, - { 249, 28 }, - { 232, 157 }, }, - { { 50, 210 }, - { 173, 34 }, - { 75, 76 }, - { 68, 181 }, }, - { { 58, 153 }, - { 188, 7 }, - { 153, 92 }, - { 224, 61 }, }, - { { 58, 213 }, - { 188, 54 }, - { 171, 92 }, - { 108, 61 }, }, - { { 57, 232 }, - { 202, 39 }, - { 23, 156 }, - { 228, 83 }, }, - { { 59, 193 }, - { 220, 38 }, - { 131, 220 }, - { 100, 59 }, }, - { { 51, 67 }, - { 221, 32 }, - { 194, 204 }, - { 4, 187 }, }, - { { 59, 231 }, - { 223, 54 }, - { 231, 220 }, - { 108, 251 }, }, - { { 49, 154 }, - { 233, 3 }, - { 89, 140 }, - { 192, 151 }, }, - { { 51, 144 }, - { 236, 2 }, - { 9, 204 }, - { 64, 55 }, }, - { { 59, 158 }, - { 237, 23 }, - { 121, 220 }, - { 232, 183 }, }, - { { 36, 196 }, - { 8, 114 }, - { 35, 36 }, - { 78, 16 }, }, - { { 44, 74 }, - { 9, 101 }, - { 82, 52 }, - { 166, 144 }, }, - { { 44, 173 }, - { 26, 87 }, - { 181, 52 }, - { 234, 88 }, }, - { { 44, 207 }, - { 25, 119 }, - { 243, 52 }, - { 238, 152 }, }, - { { 44, 103 }, - { 27, 116 }, - { 230, 52 }, - { 46, 216 }, }, - { { 38, 234 }, - { 15, 99 }, - { 87, 100 }, - { 198, 240 }, }, - { { 46, 229 }, - { 30, 118 }, - { 167, 116 }, - { 110, 120 }, }, - { { 44, 112 }, - { 42, 100 }, - { 14, 52 }, - { 38, 84 }, }, - { { 46, 18 }, - { 45, 68 }, - { 72, 116 }, - { 34, 180 }, }, - { { 46, 209 }, - { 60, 102 }, - { 139, 116 }, - { 102, 60 }, }, - { { 46, 57 }, - { 62, 69 }, - { 156, 116 }, - { 162, 124 }, }, - { { 37, 100 }, - { 74, 112 }, - { 38, 164 }, - { 14, 82 }, }, - { { 37, 231 }, - { 91, 114 }, - { 231, 164 }, - { 78, 218 }, }, - { { 47, 204 }, - { 76, 119 }, - { 51, 244 }, - { 238, 50 }, }, - { { 45, 188 }, - { 106, 87 }, - { 61, 180 }, - { 234, 86 }, }, - { { 45, 113 }, - { 122, 100 }, - { 142, 180 }, - { 38, 94 }, }, - { { 37, 213 }, - { 120, 114 }, - { 171, 164 }, - { 78, 30 }, }, - { { 37, 155 }, - { 121, 67 }, - { 217, 164 }, - { 194, 158 }, }, - { { 39, 16 }, - { 108, 64 }, - { 8, 228 }, - { 2, 54 }, }, - { { 47, 124 }, - { 110, 117 }, - { 62, 244 }, - { 174, 118 }, }, - { { 39, 242 }, - { 111, 98 }, - { 79, 228 }, - { 70, 246 }, }, - { { 39, 58 }, - { 111, 65 }, - { 92, 228 }, - { 130, 246 }, }, - { { 47, 182 }, - { 111, 86 }, - { 109, 244 }, - { 106, 246 }, }, - { { 39, 211 }, - { 125, 98 }, - { 203, 228 }, - { 70, 190 }, }, - { { 47, 179 }, - { 127, 70 }, - { 205, 244 }, - { 98, 254 }, }, - { { 39, 31 }, - { 125, 81 }, - { 248, 228 }, - { 138, 190 }, }, - { { 60, 75 }, - { 153, 101 }, - { 210, 60 }, - { 166, 153 }, }, - { { 54, 192 }, - { 140, 98 }, - { 3, 108 }, - { 70, 49 }, }, - { { 54, 238 }, - { 143, 115 }, - { 119, 108 }, - { 206, 241 }, }, - { { 62, 233 }, - { 158, 103 }, - { 151, 124 }, - { 230, 121 }, }, - { { 52, 184 }, - { 170, 67 }, - { 29, 44 }, - { 194, 85 }, }, - { { 60, 20 }, - { 168, 84 }, - { 40, 60 }, - { 42, 21 }, }, - { { 60, 82 }, - { 169, 100 }, - { 74, 60 }, - { 38, 149 }, }, - { { 52, 114 }, - { 171, 96 }, - { 78, 44 }, - { 6, 213 }, }, - { { 52, 126 }, - { 171, 113 }, - { 126, 44 }, - { 142, 213 }, }, - { { 52, 191 }, - { 187, 83 }, - { 253, 44 }, - { 202, 221 }, }, - { { 62, 113 }, - { 190, 100 }, - { 142, 124 }, - { 38, 125 }, }, - { { 62, 83 }, - { 189, 100 }, - { 202, 124 }, - { 38, 189 }, }, - { { 61, 140 }, - { 200, 87 }, - { 49, 188 }, - { 234, 19 }, }, - { { 53, 162 }, - { 203, 66 }, - { 69, 172 }, - { 66, 211 }, }, - { { 53, 46 }, - { 203, 81 }, - { 116, 172 }, - { 138, 211 }, }, - { { 53, 45 }, - { 218, 81 }, - { 180, 172 }, - { 138, 91 }, }, - { { 55, 172 }, - { 206, 83 }, - { 53, 236 }, - { 202, 115 }, }, - { { 53, 112 }, - { 234, 96 }, - { 14, 172 }, - { 6, 87 }, }, - { { 55, 250 }, - { 239, 99 }, - { 95, 236 }, - { 198, 247 }, }, - { { 63, 241 }, - { 254, 102 }, - { 143, 252 }, - { 102, 127 }, }, - { { 63, 219 }, - { 253, 103 }, - { 219, 252 }, - { 230, 191 }, }, - { { 72, 196 }, - { 0, 182 }, - { 35, 18 }, - { 109, 0 }, }, - { { 72, 233 }, - { 18, 167 }, - { 151, 18 }, - { 229, 72 }, }, - { { 74, 194 }, - { 5, 166 }, - { 67, 82 }, - { 101, 160 }, }, - { { 74, 65 }, - { 20, 164 }, - { 130, 82 }, - { 37, 40 }, }, - { { 66, 235 }, - { 23, 163 }, - { 215, 66 }, - { 197, 232 }, }, - { { 72, 19 }, - { 49, 132 }, - { 200, 18 }, - { 33, 140 }, }, - { { 74, 216 }, - { 36, 167 }, - { 27, 82 }, - { 229, 36 }, }, - { { 66, 253 }, - { 54, 179 }, - { 191, 66 }, - { 205, 108 }, }, - { { 74, 23 }, - { 53, 148 }, - { 232, 82 }, - { 41, 172 }, }, - { { 73, 99 }, - { 83, 164 }, - { 198, 146 }, - { 37, 202 }, }, - { { 67, 110 }, - { 71, 177 }, - { 118, 194 }, - { 141, 226 }, }, - { { 65, 58 }, - { 99, 129 }, - { 92, 130 }, - { 129, 198 }, }, - { { 73, 177 }, - { 114, 134 }, - { 141, 146 }, - { 97, 78 }, }, - { { 65, 61 }, - { 114, 145 }, - { 188, 130 }, - { 137, 78 }, }, - { { 75, 146 }, - { 101, 134 }, - { 73, 210 }, - { 97, 166 }, }, - { { 75, 155 }, - { 117, 135 }, - { 217, 210 }, - { 225, 174 }, }, - { { 67, 63 }, - { 119, 145 }, - { 252, 194 }, - { 137, 238 }, }, - { { 88, 34 }, - { 131, 132 }, - { 68, 26 }, - { 33, 193 }, }, - { { 80, 170 }, - { 131, 131 }, - { 85, 10 }, - { 193, 193 }, }, - { { 88, 39 }, - { 147, 148 }, - { 228, 26 }, - { 41, 201 }, }, - { { 82, 200 }, - { 132, 163 }, - { 19, 74 }, - { 197, 33 }, }, - { { 82, 132 }, - { 132, 146 }, - { 33, 74 }, - { 73, 33 }, }, - { { 82, 10 }, - { 133, 129 }, - { 80, 74 }, - { 129, 161 }, }, - { { 90, 15 }, - { 149, 149 }, - { 240, 90 }, - { 169, 169 }, }, - { { 88, 152 }, - { 160, 135 }, - { 25, 26 }, - { 225, 5 }, }, - { { 88, 92 }, - { 160, 181 }, - { 58, 26 }, - { 173, 5 }, }, - { { 80, 219 }, - { 177, 163 }, - { 219, 10 }, - { 197, 141 }, }, - { { 80, 247 }, - { 179, 178 }, - { 239, 10 }, - { 77, 205 }, }, - { { 90, 244 }, - { 166, 182 }, - { 47, 90 }, - { 109, 101 }, }, - { { 81, 236 }, - { 194, 179 }, - { 55, 138 }, - { 205, 67 }, }, - { { 81, 66 }, - { 193, 160 }, - { 66, 138 }, - { 5, 131 }, }, - { { 81, 13 }, - { 208, 145 }, - { 176, 138 }, - { 137, 11 }, }, - { { 91, 3 }, - { 213, 132 }, - { 192, 218 }, - { 33, 171 }, }, - { { 83, 235 }, - { 215, 163 }, - { 215, 202 }, - { 197, 235 }, }, - { { 81, 118 }, - { 227, 176 }, - { 110, 138 }, - { 13, 199 }, }, - { { 89, 113 }, - { 242, 164 }, - { 142, 154 }, - { 37, 79 }, }, - { { 81, 147 }, - { 241, 130 }, - { 201, 138 }, - { 65, 143 }, }, - { { 83, 249 }, - { 246, 163 }, - { 159, 202 }, - { 197, 111 }, }, - { { 91, 179 }, - { 247, 134 }, - { 205, 218 }, - { 97, 239 }, }, - { { 83, 151 }, - { 245, 146 }, - { 233, 202 }, - { 73, 175 }, }, - { { 76, 76 }, - { 0, 245 }, - { 50, 50 }, - { 175, 0 }, }, - { { 68, 75 }, - { 17, 225 }, - { 210, 34 }, - { 135, 136 }, }, - { { 76, 35 }, - { 19, 196 }, - { 196, 50 }, - { 35, 200 }, }, - { { 70, 140 }, - { 4, 211 }, - { 49, 98 }, - { 203, 32 }, }, - { { 78, 39 }, - { 23, 212 }, - { 228, 114 }, - { 43, 232 }, }, - { { 70, 144 }, - { 36, 194 }, - { 9, 98 }, - { 67, 36 }, }, - { { 78, 212 }, - { 36, 246 }, - { 43, 114 }, - { 111, 36 }, }, - { { 69, 206 }, - { 65, 243 }, - { 115, 162 }, - { 207, 130 }, }, - { { 69, 229 }, - { 82, 242 }, - { 167, 162 }, - { 79, 74 }, }, - { { 69, 39 }, - { 83, 208 }, - { 228, 162 }, - { 11, 202 }, }, - { { 79, 193 }, - { 84, 230 }, - { 131, 242 }, - { 103, 42 }, }, - { { 71, 5 }, - { 84, 208 }, - { 160, 226 }, - { 11, 42 }, }, - { { 69, 52 }, - { 98, 208 }, - { 44, 162 }, - { 11, 70 }, }, - { { 69, 114 }, - { 99, 224 }, - { 78, 162 }, - { 7, 198 }, }, - { { 92, 200 }, - { 128, 231 }, - { 19, 58 }, - { 231, 1 }, }, - { { 92, 14 }, - { 129, 213 }, - { 112, 58 }, - { 171, 129 }, }, - { { 84, 235 }, - { 147, 227 }, - { 215, 42 }, - { 199, 201 }, }, - { { 86, 137 }, - { 148, 195 }, - { 145, 106 }, - { 195, 41 }, }, - { { 86, 67 }, - { 149, 224 }, - { 194, 106 }, - { 7, 169 }, }, - { { 94, 231 }, - { 151, 246 }, - { 231, 122 }, - { 111, 233 }, }, - { { 92, 112 }, - { 162, 228 }, - { 14, 58 }, - { 39, 69 }, }, - { { 84, 178 }, - { 163, 194 }, - { 77, 42 }, - { 67, 197 }, }, - { { 94, 121 }, - { 182, 229 }, - { 158, 122 }, - { 167, 109 }, }, - { { 86, 243 }, - { 183, 226 }, - { 207, 106 }, - { 71, 237 }, }, - { { 93, 163 }, - { 211, 198 }, - { 197, 186 }, - { 99, 203 }, }, - { { 93, 242 }, - { 227, 230 }, - { 79, 186 }, - { 103, 199 }, }, - { { 85, 29 }, - { 240, 209 }, - { 184, 170 }, - { 139, 15 }, }, - { { 93, 157 }, - { 240, 215 }, - { 185, 186 }, - { 235, 15 }, }, - { { 87, 252 }, - { 230, 243 }, - { 63, 234 }, - { 207, 103 }, }, - { { 87, 210 }, - { 229, 226 }, - { 75, 234 }, - { 71, 167 }, }, - { { 95, 115 }, - { 247, 228 }, - { 206, 250 }, - { 39, 239 }, }, - { { 104, 45 }, - { 26, 149 }, - { 180, 22 }, - { 169, 88 }, }, - { { 104, 195 }, - { 25, 166 }, - { 195, 22 }, - { 101, 152 }, }, - { { 104, 135 }, - { 25, 150 }, - { 225, 22 }, - { 105, 152 }, }, - { { 106, 74 }, - { 13, 165 }, - { 82, 86 }, - { 165, 176 }, }, - { { 98, 105 }, - { 30, 161 }, - { 150, 70 }, - { 133, 120 }, }, - { { 96, 185 }, - { 58, 131 }, - { 157, 6 }, - { 193, 92 }, }, - { { 104, 255 }, - { 59, 183 }, - { 255, 22 }, - { 237, 220 }, }, - { { 106, 220 }, - { 44, 183 }, - { 59, 86 }, - { 237, 52 }, }, - { { 106, 218 }, - { 45, 167 }, - { 91, 86 }, - { 229, 180 }, }, - { { 106, 62 }, - { 47, 149 }, - { 124, 86 }, - { 169, 244 }, }, - { { 106, 81 }, - { 60, 164 }, - { 138, 86 }, - { 37, 60 }, }, - { { 106, 49 }, - { 62, 132 }, - { 140, 86 }, - { 33, 124 }, }, - { { 98, 215 }, - { 61, 178 }, - { 235, 70 }, - { 77, 188 }, }, - { { 97, 204 }, - { 72, 179 }, - { 51, 134 }, - { 205, 18 }, }, - { { 107, 130 }, - { 77, 134 }, - { 65, 214 }, - { 97, 178 }, }, - { { 107, 227 }, - { 95, 166 }, - { 199, 214 }, - { 101, 250 }, }, - { { 105, 58 }, - { 107, 133 }, - { 92, 150 }, - { 161, 214 }, }, - { { 97, 158 }, - { 105, 147 }, - { 121, 134 }, - { 201, 150 }, }, - { { 97, 149 }, - { 120, 146 }, - { 169, 134 }, - { 73, 30 }, }, - { { 97, 117 }, - { 122, 176 }, - { 174, 134 }, - { 13, 94 }, }, - { { 105, 95 }, - { 121, 181 }, - { 250, 150 }, - { 173, 158 }, }, - { { 105, 55 }, - { 123, 148 }, - { 236, 150 }, - { 41, 222 }, }, - { { 99, 218 }, - { 109, 163 }, - { 91, 198 }, - { 197, 182 }, }, - { { 112, 2 }, - { 137, 128 }, - { 64, 14 }, - { 1, 145 }, }, - { { 120, 99 }, - { 155, 164 }, - { 198, 30 }, - { 37, 217 }, }, - { { 112, 79 }, - { 153, 177 }, - { 242, 14 }, - { 141, 153 }, }, - { { 114, 202 }, - { 141, 163 }, - { 83, 78 }, - { 197, 177 }, }, - { { 122, 173 }, - { 158, 151 }, - { 181, 94 }, - { 233, 121 }, }, - { { 112, 123 }, - { 187, 161 }, - { 222, 14 }, - { 133, 221 }, }, - { { 122, 20 }, - { 172, 148 }, - { 40, 94 }, - { 41, 53 }, }, - { { 122, 249 }, - { 190, 167 }, - { 159, 94 }, - { 229, 125 }, }, - { { 122, 211 }, - { 189, 166 }, - { 203, 94 }, - { 101, 189 }, }, - { { 122, 187 }, - { 191, 135 }, - { 221, 94 }, - { 225, 253 }, }, - { { 121, 226 }, - { 203, 166 }, - { 71, 158 }, - { 101, 211 }, }, - { { 113, 41 }, - { 218, 129 }, - { 148, 142 }, - { 129, 91 }, }, - { { 123, 103 }, - { 223, 180 }, - { 230, 222 }, - { 45, 251 }, }, - { { 113, 208 }, - { 232, 162 }, - { 11, 142 }, - { 69, 23 }, }, - { { 121, 57 }, - { 250, 133 }, - { 156, 158 }, - { 161, 95 }, }, - { { 115, 48 }, - { 238, 128 }, - { 12, 206 }, - { 1, 119 }, }, - { { 115, 185 }, - { 254, 131 }, - { 157, 206 }, - { 193, 127 }, }, - { { 115, 83 }, - { 253, 160 }, - { 202, 206 }, - { 5, 191 }, }, - { { 115, 255 }, - { 255, 179 }, - { 255, 206 }, - { 205, 255 }, }, - { { 108, 136 }, - { 8, 199 }, - { 17, 54 }, - { 227, 16 }, }, - { { 100, 9 }, - { 24, 193 }, - { 144, 38 }, - { 131, 24 }, }, - { { 108, 67 }, - { 25, 228 }, - { 194, 54 }, - { 39, 152 }, }, - { { 102, 6 }, - { 13, 208 }, - { 96, 102 }, - { 11, 176 }, }, - { { 102, 131 }, - { 29, 194 }, - { 193, 102 }, - { 67, 184 }, }, - { { 100, 176 }, - { 42, 194 }, - { 13, 38 }, - { 67, 84 }, }, - { { 100, 218 }, - { 41, 227 }, - { 91, 38 }, - { 199, 148 }, }, - { { 110, 159 }, - { 61, 215 }, - { 249, 118 }, - { 235, 188 }, }, - { { 103, 200 }, - { 76, 227 }, - { 19, 230 }, - { 199, 50 }, }, - { { 111, 238 }, - { 79, 247 }, - { 119, 246 }, - { 239, 242 }, }, - { { 109, 59 }, - { 123, 197 }, - { 220, 182 }, - { 163, 222 }, }, - { { 111, 210 }, - { 109, 230 }, - { 75, 246 }, - { 103, 182 }, }, - { { 116, 128 }, - { 136, 194 }, - { 1, 46 }, - { 67, 17 }, }, - { { 124, 171 }, - { 155, 199 }, - { 213, 62 }, - { 227, 217 }, }, - { { 126, 104 }, - { 142, 229 }, - { 22, 126 }, - { 167, 113 }, }, - { { 126, 2 }, - { 141, 196 }, - { 64, 126 }, - { 35, 177 }, }, - { { 124, 156 }, - { 168, 215 }, - { 57, 62 }, - { 235, 21 }, }, - { { 116, 54 }, - { 171, 208 }, - { 108, 46 }, - { 11, 213 }, }, - { { 124, 17 }, - { 184, 196 }, - { 136, 62 }, - { 35, 29 }, }, - { { 126, 222 }, - { 173, 247 }, - { 123, 126 }, - { 239, 181 }, }, - { { 126, 182 }, - { 175, 214 }, - { 109, 126 }, - { 107, 245 }, }, - { { 118, 219 }, - { 189, 227 }, - { 219, 110 }, - { 199, 189 }, }, - { { 125, 196 }, - { 200, 246 }, - { 35, 190 }, - { 111, 19 }, }, - { { 125, 138 }, - { 201, 199 }, - { 81, 190 }, - { 227, 147 }, }, - { { 117, 109 }, - { 218, 241 }, - { 182, 174 }, - { 143, 91 }, }, - { { 119, 136 }, - { 204, 195 }, - { 17, 238 }, - { 195, 51 }, }, - { { 119, 32 }, - { 206, 192 }, - { 4, 238 }, - { 3, 115 }, }, - { { 119, 65 }, - { 220, 224 }, - { 130, 238 }, - { 7, 59 }, }, - { { 117, 56 }, - { 234, 193 }, - { 28, 174 }, - { 131, 87 }, }, - { { 117, 190 }, - { 235, 211 }, - { 125, 174 }, - { 203, 215 }, }, - { { 125, 155 }, - { 249, 199 }, - { 217, 190 }, - { 227, 159 }, }, - { { 119, 87 }, - { 253, 240 }, - { 234, 238 }, - { 15, 191 }, }, - { { 136, 40 }, - { 2, 13 }, - { 20, 17 }, - { 176, 64 }, }, - { { 128, 172 }, - { 2, 27 }, - { 53, 1 }, - { 216, 64 }, }, - { { 136, 13 }, - { 16, 29 }, - { 176, 17 }, - { 184, 8 }, }, - { { 136, 103 }, - { 19, 60 }, - { 230, 17 }, - { 60, 200 }, }, - { { 130, 78 }, - { 5, 57 }, - { 114, 65 }, - { 156, 160 }, }, - { { 138, 161 }, - { 22, 14 }, - { 133, 81 }, - { 112, 104 }, }, - { { 130, 43 }, - { 23, 9 }, - { 212, 65 }, - { 144, 232 }, }, - { { 128, 24 }, - { 32, 9 }, - { 24, 1 }, - { 144, 4 }, }, - { { 136, 249 }, - { 50, 47 }, - { 159, 17 }, - { 244, 76 }, }, - { { 128, 157 }, - { 48, 27 }, - { 185, 1 }, - { 216, 12 }, }, - { { 138, 156 }, - { 36, 31 }, - { 57, 81 }, - { 248, 36 }, }, - { { 130, 49 }, - { 54, 8 }, - { 140, 65 }, - { 16, 108 }, }, - { { 138, 117 }, - { 54, 60 }, - { 174, 81 }, - { 60, 108 }, }, - { { 130, 151 }, - { 53, 26 }, - { 233, 65 }, - { 88, 172 }, }, - { { 129, 9 }, - { 80, 9 }, - { 144, 129 }, - { 144, 10 }, }, - { { 129, 235 }, - { 83, 43 }, - { 215, 129 }, - { 212, 202 }, }, - { { 129, 7 }, - { 81, 24 }, - { 224, 129 }, - { 24, 138 }, }, - { { 139, 40 }, - { 70, 13 }, - { 20, 209 }, - { 176, 98 }, }, - { { 139, 172 }, - { 70, 31 }, - { 53, 209 }, - { 248, 98 }, }, - { { 131, 46 }, - { 71, 25 }, - { 116, 193 }, - { 152, 226 }, }, - { { 131, 229 }, - { 86, 58 }, - { 167, 193 }, - { 92, 106 }, }, - { { 129, 80 }, - { 96, 40 }, - { 10, 129 }, - { 20, 6 }, }, - { { 137, 50 }, - { 99, 12 }, - { 76, 145 }, - { 48, 198 }, }, - { { 139, 122 }, - { 103, 45 }, - { 94, 209 }, - { 180, 230 }, }, - { { 139, 150 }, - { 101, 30 }, - { 105, 209 }, - { 120, 166 }, }, - { { 131, 125 }, - { 118, 57 }, - { 190, 193 }, - { 156, 110 }, }, - { { 144, 135 }, - { 145, 26 }, - { 225, 9 }, - { 88, 137 }, }, - { { 154, 252 }, - { 166, 63 }, - { 63, 89 }, - { 252, 101 }, }, - { { 146, 245 }, - { 182, 58 }, - { 175, 73 }, - { 92, 109 }, }, - { { 145, 170 }, - { 195, 11 }, - { 85, 137 }, - { 208, 195 }, }, - { { 147, 65 }, - { 212, 40 }, - { 130, 201 }, - { 20, 43 }, }, - { { 147, 37 }, - { 214, 24 }, - { 164, 201 }, - { 24, 107 }, }, - { { 155, 235 }, - { 215, 47 }, - { 215, 217 }, - { 244, 235 }, }, - { { 153, 52 }, - { 226, 28 }, - { 44, 153 }, - { 56, 71 }, }, - { { 145, 247 }, - { 243, 58 }, - { 239, 137 }, - { 92, 207 }, }, - { { 155, 218 }, - { 229, 47 }, - { 91, 217 }, - { 244, 167 }, }, - { { 147, 86 }, - { 229, 56 }, - { 106, 201 }, - { 28, 167 }, }, - { { 132, 66 }, - { 1, 104 }, - { 66, 33 }, - { 22, 128 }, }, - { { 140, 129 }, - { 16, 78 }, - { 129, 49 }, - { 114, 8 }, }, - { { 140, 79 }, - { 17, 125 }, - { 242, 49 }, - { 190, 136 }, }, - { { 134, 72 }, - { 4, 105 }, - { 18, 97 }, - { 150, 32 }, }, - { { 134, 166 }, - { 7, 90 }, - { 101, 97 }, - { 90, 224 }, }, - { { 142, 3 }, - { 21, 76 }, - { 192, 113 }, - { 50, 168 }, }, - { { 134, 227 }, - { 23, 106 }, - { 199, 97 }, - { 86, 232 }, }, - { { 134, 111 }, - { 23, 121 }, - { 246, 97 }, - { 158, 232 }, }, - { { 142, 175 }, - { 23, 95 }, - { 245, 113 }, - { 250, 232 }, }, - { { 132, 94 }, - { 33, 121 }, - { 122, 33 }, - { 158, 132 }, }, - { { 132, 119 }, - { 51, 120 }, - { 238, 33 }, - { 30, 204 }, }, - { { 134, 250 }, - { 39, 107 }, - { 95, 97 }, - { 214, 228 }, }, - { { 142, 30 }, - { 37, 93 }, - { 120, 113 }, - { 186, 164 }, }, - { { 142, 55 }, - { 55, 92 }, - { 236, 113 }, - { 58, 236 }, }, - { { 135, 10 }, - { 69, 73 }, - { 80, 225 }, - { 146, 162 }, }, - { { 143, 138 }, - { 69, 79 }, - { 81, 241 }, - { 242, 162 }, }, - { { 143, 38 }, - { 71, 92 }, - { 100, 241 }, - { 58, 226 }, }, - { { 135, 33 }, - { 86, 72 }, - { 132, 225 }, - { 18, 106 }, }, - { { 135, 13 }, - { 84, 89 }, - { 176, 225 }, - { 154, 42 }, }, - { { 133, 114 }, - { 99, 104 }, - { 78, 161 }, - { 22, 198 }, }, - { { 135, 62 }, - { 103, 89 }, - { 124, 225 }, - { 154, 230 }, }, - { { 156, 67 }, - { 145, 108 }, - { 194, 57 }, - { 54, 137 }, }, - { { 158, 97 }, - { 150, 108 }, - { 134, 121 }, - { 54, 105 }, }, - { { 148, 88 }, - { 160, 105 }, - { 26, 41 }, - { 150, 5 }, }, - { { 148, 248 }, - { 162, 107 }, - { 31, 41 }, - { 214, 69 }, }, - { { 156, 50 }, - { 163, 76 }, - { 76, 57 }, - { 50, 197 }, }, - { { 148, 118 }, - { 163, 120 }, - { 110, 41 }, - { 30, 197 }, }, - { { 148, 177 }, - { 178, 74 }, - { 141, 41 }, - { 82, 77 }, }, - { { 148, 221 }, - { 176, 123 }, - { 187, 41 }, - { 222, 13 }, }, - { { 148, 155 }, - { 177, 75 }, - { 217, 41 }, - { 210, 141 }, }, - { { 156, 219 }, - { 177, 111 }, - { 219, 57 }, - { 246, 141 }, }, - { { 158, 156 }, - { 164, 95 }, - { 57, 121 }, - { 250, 37 }, }, - { { 158, 210 }, - { 165, 110 }, - { 75, 121 }, - { 118, 165 }, }, - { { 150, 25 }, - { 180, 73 }, - { 152, 105 }, - { 146, 45 }, }, - { { 158, 177 }, - { 182, 78 }, - { 141, 121 }, - { 114, 109 }, }, - { { 149, 105 }, - { 210, 105 }, - { 150, 169 }, - { 150, 75 }, }, - { { 159, 109 }, - { 214, 125 }, - { 182, 249 }, - { 190, 107 }, }, - { { 151, 43 }, - { 215, 73 }, - { 212, 233 }, - { 146, 235 }, }, - { { 149, 182 }, - { 227, 90 }, - { 109, 169 }, - { 90, 199 }, }, - { { 149, 185 }, - { 242, 75 }, - { 157, 169 }, - { 210, 79 }, }, - { { 157, 61 }, - { 242, 93 }, - { 188, 185 }, - { 186, 79 }, }, - { { 157, 87 }, - { 241, 124 }, - { 234, 185 }, - { 62, 143 }, }, - { { 168, 236 }, - { 10, 63 }, - { 55, 21 }, - { 252, 80 }, }, - { { 168, 37 }, - { 26, 28 }, - { 164, 21 }, - { 56, 88 }, }, - { { 162, 172 }, - { 14, 27 }, - { 53, 69 }, - { 216, 112 }, }, - { { 162, 2 }, - { 13, 8 }, - { 64, 69 }, - { 16, 176 }, }, - { { 170, 102 }, - { 15, 60 }, - { 102, 85 }, - { 60, 240 }, }, - { { 170, 143 }, - { 29, 31 }, - { 241, 85 }, - { 248, 184 }, }, - { { 170, 231 }, - { 31, 62 }, - { 231, 85 }, - { 124, 248 }, }, - { { 168, 48 }, - { 42, 12 }, - { 12, 21 }, - { 48, 84 }, }, - { { 168, 122 }, - { 43, 45 }, - { 94, 21 }, - { 180, 212 }, }, - { { 168, 246 }, - { 43, 62 }, - { 111, 21 }, - { 124, 212 }, }, - { { 168, 147 }, - { 57, 14 }, - { 201, 21 }, - { 112, 156 }, }, - { { 162, 20 }, - { 44, 24 }, - { 40, 69 }, - { 24, 52 }, }, - { { 170, 52 }, - { 46, 28 }, - { 44, 85 }, - { 56, 116 }, }, - { { 162, 114 }, - { 47, 40 }, - { 78, 69 }, - { 20, 244 }, }, - { { 170, 242 }, - { 47, 46 }, - { 79, 85 }, - { 116, 244 }, }, - { { 162, 241 }, - { 62, 42 }, - { 143, 69 }, - { 84, 124 }, }, - { { 161, 64 }, - { 72, 40 }, - { 2, 133 }, - { 20, 18 }, }, - { { 169, 10 }, - { 73, 13 }, - { 80, 149 }, - { 176, 146 }, }, - { { 161, 38 }, - { 75, 24 }, - { 100, 133 }, - { 24, 210 }, }, - { { 169, 197 }, - { 88, 62 }, - { 163, 149 }, - { 124, 26 }, }, - { { 169, 207 }, - { 89, 63 }, - { 243, 149 }, - { 252, 154 }, }, - { { 161, 52 }, - { 106, 24 }, - { 44, 133 }, - { 24, 86 }, }, - { { 169, 18 }, - { 105, 12 }, - { 72, 149 }, - { 48, 150 }, }, - { { 161, 250 }, - { 107, 43 }, - { 95, 133 }, - { 212, 214 }, }, - { { 171, 152 }, - { 108, 15 }, - { 25, 213 }, - { 240, 54 }, }, - { { 163, 247 }, - { 127, 58 }, - { 239, 197 }, - { 92, 254 }, }, - { { 176, 6 }, - { 137, 24 }, - { 96, 13 }, - { 24, 145 }, }, - { { 176, 69 }, - { 152, 56 }, - { 162, 13 }, - { 28, 25 }, }, - { { 184, 141 }, - { 152, 31 }, - { 177, 29 }, - { 248, 25 }, }, - { { 178, 132 }, - { 140, 26 }, - { 33, 77 }, - { 88, 49 }, }, - { { 184, 240 }, - { 170, 46 }, - { 15, 29 }, - { 116, 85 }, }, - { { 184, 85 }, - { 184, 60 }, - { 170, 29 }, - { 60, 29 }, }, - { { 178, 118 }, - { 175, 56 }, - { 110, 77 }, - { 28, 245 }, }, - { { 186, 145 }, - { 188, 14 }, - { 137, 93 }, - { 112, 61 }, }, - { { 178, 113 }, - { 190, 40 }, - { 142, 77 }, - { 20, 125 }, }, - { { 185, 192 }, - { 200, 46 }, - { 3, 157 }, - { 116, 19 }, }, - { { 185, 66 }, - { 201, 44 }, - { 66, 157 }, - { 52, 147 }, }, - { { 185, 42 }, - { 203, 13 }, - { 84, 157 }, - { 176, 211 }, }, - { { 179, 140 }, - { 204, 27 }, - { 49, 205 }, - { 216, 51 }, }, - { { 179, 202 }, - { 205, 43 }, - { 83, 205 }, - { 212, 179 }, }, - { { 187, 102 }, - { 207, 60 }, - { 102, 221 }, - { 60, 243 }, }, - { { 179, 15 }, - { 221, 25 }, - { 240, 205 }, - { 152, 187 }, }, - { { 177, 218 }, - { 233, 43 }, - { 91, 141 }, - { 212, 151 }, }, - { { 187, 20 }, - { 236, 28 }, - { 40, 221 }, - { 56, 55 }, }, - { { 187, 246 }, - { 239, 62 }, - { 111, 221 }, - { 124, 247 }, }, - { { 179, 19 }, - { 253, 8 }, - { 200, 205 }, - { 16, 191 }, }, - { { 164, 104 }, - { 10, 105 }, - { 22, 37 }, - { 150, 80 }, }, - { { 172, 44 }, - { 10, 93 }, - { 52, 53 }, - { 186, 80 }, }, - { { 172, 161 }, - { 26, 78 }, - { 133, 53 }, - { 114, 88 }, }, - { { 172, 235 }, - { 27, 111 }, - { 215, 53 }, - { 246, 216 }, }, - { { 172, 199 }, - { 25, 126 }, - { 227, 53 }, - { 126, 152 }, }, - { { 164, 103 }, - { 27, 120 }, - { 230, 37 }, - { 30, 216 }, }, - { { 166, 192 }, - { 12, 106 }, - { 3, 101 }, - { 86, 48 }, }, - { { 174, 224 }, - { 14, 110 }, - { 7, 117 }, - { 118, 112 }, }, - { { 166, 35 }, - { 31, 72 }, - { 196, 101 }, - { 18, 248 }, }, - { { 173, 232 }, - { 74, 111 }, - { 23, 181 }, - { 246, 82 }, }, - { { 165, 204 }, - { 72, 123 }, - { 51, 165 }, - { 222, 18 }, }, - { { 167, 236 }, - { 78, 123 }, - { 55, 229 }, - { 222, 114 }, }, - { { 173, 124 }, - { 106, 125 }, - { 62, 181 }, - { 190, 86 }, }, - { { 165, 26 }, - { 105, 73 }, - { 88, 165 }, - { 146, 150 }, }, - { { 165, 145 }, - { 120, 74 }, - { 137, 165 }, - { 82, 30 }, }, - { { 173, 25 }, - { 120, 77 }, - { 152, 181 }, - { 178, 30 }, }, - { { 165, 151 }, - { 121, 90 }, - { 233, 165 }, - { 90, 158 }, }, - { { 180, 109 }, - { 154, 121 }, - { 182, 45 }, - { 158, 89 }, }, - { { 190, 203 }, - { 157, 111 }, - { 211, 125 }, - { 246, 185 }, }, - { { 188, 58 }, - { 171, 77 }, - { 92, 61 }, - { 178, 213 }, }, - { { 188, 245 }, - { 186, 126 }, - { 175, 61 }, - { 126, 93 }, }, - { { 190, 189 }, - { 190, 95 }, - { 189, 125 }, - { 250, 125 }, }, - { { 190, 243 }, - { 191, 110 }, - { 207, 125 }, - { 118, 253 }, }, - { { 181, 37 }, - { 218, 88 }, - { 164, 173 }, - { 26, 91 }, }, - { { 181, 143 }, - { 217, 91 }, - { 241, 173 }, - { 218, 155 }, }, - { { 183, 104 }, - { 206, 105 }, - { 22, 237 }, - { 150, 115 }, }, - { { 191, 228 }, - { 206, 126 }, - { 39, 253 }, - { 126, 115 }, }, - { { 189, 254 }, - { 235, 127 }, - { 127, 189 }, - { 254, 215 }, }, - { { 189, 157 }, - { 248, 95 }, - { 185, 189 }, - { 250, 31 }, }, - { { 181, 245 }, - { 250, 122 }, - { 175, 173 }, - { 94, 95 }, }, - { { 181, 243 }, - { 251, 106 }, - { 207, 173 }, - { 86, 223 }, }, - { { 191, 176 }, - { 238, 78 }, - { 13, 253 }, - { 114, 119 }, }, - { { 183, 90 }, - { 237, 105 }, - { 90, 237 }, - { 150, 183 }, }, - { { 191, 62 }, - { 239, 93 }, - { 124, 253 }, - { 186, 247 }, }, - { { 183, 57 }, - { 254, 73 }, - { 156, 237 }, - { 146, 127 }, }, - { { 191, 213 }, - { 252, 126 }, - { 171, 253 }, - { 126, 63 }, }, - { { 183, 29 }, - { 252, 89 }, - { 184, 237 }, - { 154, 63 }, }, - { { 191, 53 }, - { 254, 92 }, - { 172, 253 }, - { 58, 127 }, }, - { { 183, 127 }, - { 255, 121 }, - { 254, 237 }, - { 158, 255 }, }, - { { 200, 1 }, - { 16, 140 }, - { 128, 19 }, - { 49, 8 }, }, - { { 192, 165 }, - { 18, 154 }, - { 165, 3 }, - { 89, 72 }, }, - { { 194, 130 }, - { 5, 138 }, - { 65, 67 }, - { 81, 160 }, }, - { { 200, 189 }, - { 50, 159 }, - { 189, 19 }, - { 249, 76 }, }, - { { 194, 252 }, - { 38, 187 }, - { 63, 67 }, - { 221, 100 }, }, - { { 202, 145 }, - { 52, 142 }, - { 137, 83 }, - { 113, 44 }, }, - { { 194, 91 }, - { 53, 169 }, - { 218, 67 }, - { 149, 172 }, }, - { { 201, 68 }, - { 64, 188 }, - { 34, 147 }, - { 61, 2 }, }, - { { 193, 42 }, - { 67, 137 }, - { 84, 131 }, - { 145, 194 }, }, - { { 195, 192 }, - { 68, 170 }, - { 3, 195 }, - { 85, 34 }, }, - { { 201, 122 }, - { 99, 173 }, - { 94, 147 }, - { 181, 198 }, }, - { { 193, 185 }, - { 114, 139 }, - { 157, 131 }, - { 209, 78 }, }, - { { 201, 117 }, - { 114, 188 }, - { 174, 147 }, - { 61, 78 }, }, - { { 193, 247 }, - { 115, 186 }, - { 239, 131 }, - { 93, 206 }, }, - { { 203, 177 }, - { 118, 142 }, - { 141, 211 }, - { 113, 110 }, }, - { { 208, 108 }, - { 130, 185 }, - { 54, 11 }, - { 157, 65 }, }, - { { 216, 135 }, - { 145, 158 }, - { 225, 27 }, - { 121, 137 }, }, - { { 208, 175 }, - { 147, 155 }, - { 245, 11 }, - { 217, 201 }, }, - { { 218, 196 }, - { 132, 190 }, - { 35, 91 }, - { 125, 33 }, }, - { { 210, 12 }, - { 132, 153 }, - { 48, 75 }, - { 153, 33 }, }, - { { 218, 9 }, - { 148, 141 }, - { 144, 91 }, - { 177, 41 }, }, - { { 208, 48 }, - { 162, 136 }, - { 12, 11 }, - { 17, 69 }, }, - { { 216, 148 }, - { 160, 158 }, - { 41, 27 }, - { 121, 5 }, }, - { { 208, 58 }, - { 163, 137 }, - { 92, 11 }, - { 145, 197 }, }, - { { 208, 182 }, - { 163, 154 }, - { 109, 11 }, - { 89, 197 }, }, - { { 208, 117 }, - { 178, 184 }, - { 174, 11 }, - { 29, 77 }, }, - { { 210, 118 }, - { 167, 184 }, - { 110, 75 }, - { 29, 229 }, }, - { { 218, 93 }, - { 180, 189 }, - { 186, 91 }, - { 189, 45 }, }, - { { 218, 53 }, - { 182, 156 }, - { 172, 91 }, - { 57, 109 }, }, - { { 210, 23 }, - { 181, 152 }, - { 232, 75 }, - { 25, 173 }, }, - { { 217, 2 }, - { 193, 140 }, - { 64, 155 }, - { 49, 131 }, }, - { { 211, 232 }, - { 198, 171 }, - { 23, 203 }, - { 213, 99 }, }, - { { 211, 229 }, - { 214, 186 }, - { 167, 203 }, - { 93, 107 }, }, - { { 209, 154 }, - { 225, 139 }, - { 89, 139 }, - { 209, 135 }, }, - { { 209, 246 }, - { 227, 186 }, - { 111, 139 }, - { 93, 199 }, }, - { { 209, 81 }, - { 240, 168 }, - { 138, 139 }, - { 21, 15 }, }, - { { 219, 20 }, - { 228, 156 }, - { 40, 219 }, - { 57, 39 }, }, - { { 211, 62 }, - { 231, 153 }, - { 124, 203 }, - { 153, 231 }, }, - { { 211, 211 }, - { 245, 170 }, - { 203, 203 }, - { 85, 175 }, }, - { { 196, 96 }, - { 2, 232 }, - { 6, 35 }, - { 23, 64 }, }, - { { 204, 167 }, - { 19, 222 }, - { 229, 51 }, - { 123, 200 }, }, - { { 198, 66 }, - { 5, 232 }, - { 66, 99 }, - { 23, 160 }, }, - { { 198, 71 }, - { 21, 248 }, - { 226, 99 }, - { 31, 168 }, }, - { { 206, 231 }, - { 23, 254 }, - { 231, 115 }, - { 127, 232 }, }, - { { 196, 92 }, - { 32, 249 }, - { 58, 35 }, - { 159, 4 }, }, - { { 204, 29 }, - { 48, 221 }, - { 184, 51 }, - { 187, 12 }, }, - { { 204, 53 }, - { 50, 220 }, - { 172, 51 }, - { 59, 76 }, }, - { { 198, 188 }, - { 38, 219 }, - { 61, 99 }, - { 219, 100 }, }, - { { 205, 168 }, - { 66, 207 }, - { 21, 179 }, - { 243, 66 }, }, - { { 197, 12 }, - { 64, 217 }, - { 48, 163 }, - { 155, 2 }, }, - { { 197, 228 }, - { 66, 250 }, - { 39, 163 }, - { 95, 66 }, }, - { { 197, 194 }, - { 65, 234 }, - { 67, 163 }, - { 87, 130 }, }, - { { 205, 45 }, - { 82, 221 }, - { 180, 179 }, - { 187, 74 }, }, - { { 205, 89 }, - { 112, 237 }, - { 154, 179 }, - { 183, 14 }, }, - { { 205, 149 }, - { 112, 222 }, - { 169, 179 }, - { 123, 14 }, }, - { { 197, 147 }, - { 113, 202 }, - { 201, 163 }, - { 83, 142 }, }, - { { 199, 95 }, - { 117, 249 }, - { 250, 227 }, - { 159, 174 }, }, - { { 212, 197 }, - { 144, 250 }, - { 163, 43 }, - { 95, 9 }, }, - { { 222, 136 }, - { 132, 207 }, - { 17, 123 }, - { 243, 33 }, }, - { { 214, 36 }, - { 134, 216 }, - { 36, 107 }, - { 27, 97 }, }, - { { 222, 236 }, - { 134, 255 }, - { 55, 123 }, - { 255, 97 }, }, - { { 214, 226 }, - { 135, 234 }, - { 71, 107 }, - { 87, 225 }, }, - { { 222, 198 }, - { 133, 254 }, - { 99, 123 }, - { 127, 161 }, }, - { { 222, 35 }, - { 151, 204 }, - { 196, 123 }, - { 51, 233 }, }, - { { 220, 220 }, - { 160, 255 }, - { 59, 59 }, - { 255, 5 }, }, - { { 220, 26 }, - { 161, 205 }, - { 88, 59 }, - { 179, 133 }, }, - { { 212, 17 }, - { 176, 200 }, - { 136, 43 }, - { 19, 13 }, }, - { { 222, 84 }, - { 164, 252 }, - { 42, 123 }, - { 63, 37 }, }, - { { 214, 148 }, - { 164, 218 }, - { 41, 107 }, - { 91, 37 }, }, - { { 222, 157 }, - { 180, 223 }, - { 185, 123 }, - { 251, 45 }, }, - { { 221, 129 }, - { 208, 206 }, - { 129, 187 }, - { 115, 11 }, }, - { { 213, 165 }, - { 210, 218 }, - { 165, 171 }, - { 91, 75 }, }, - { { 215, 172 }, - { 198, 219 }, - { 53, 235 }, - { 219, 99 }, }, - { { 215, 102 }, - { 199, 248 }, - { 102, 235 }, - { 31, 227 }, }, - { { 223, 169 }, - { 214, 207 }, - { 149, 251 }, - { 243, 107 }, }, - { { 213, 220 }, - { 224, 251 }, - { 59, 171 }, - { 223, 7 }, }, - { { 221, 31 }, - { 241, 221 }, - { 248, 187 }, - { 187, 143 }, }, - { { 223, 240 }, - { 230, 238 }, - { 15, 251 }, - { 119, 103 }, }, - { { 226, 72 }, - { 12, 169 }, - { 18, 71 }, - { 149, 48 }, }, - { { 226, 232 }, - { 14, 171 }, - { 23, 71 }, - { 213, 112 }, }, - { { 226, 7 }, - { 29, 152 }, - { 224, 71 }, - { 25, 184 }, }, - { { 224, 93 }, - { 56, 185 }, - { 186, 7 }, - { 157, 28 }, }, - { { 234, 245 }, - { 62, 190 }, - { 175, 87 }, - { 125, 124 }, }, - { { 235, 38 }, - { 79, 156 }, - { 100, 215 }, - { 57, 242 }, }, - { { 235, 237 }, - { 94, 191 }, - { 183, 215 }, - { 253, 122 }, }, - { { 225, 82 }, - { 105, 168 }, - { 74, 135 }, - { 21, 150 }, }, - { { 225, 126 }, - { 107, 185 }, - { 126, 135 }, - { 157, 214 }, }, - { { 233, 219 }, - { 121, 175 }, - { 219, 151 }, - { 245, 158 }, }, - { { 248, 6 }, - { 137, 156 }, - { 96, 31 }, - { 57, 145 }, }, - { { 240, 238 }, - { 139, 187 }, - { 119, 15 }, - { 221, 209 }, }, - { { 248, 161 }, - { 154, 142 }, - { 133, 31 }, - { 113, 89 }, }, - { { 250, 0 }, - { 140, 140 }, - { 0, 95 }, - { 49, 49 }, }, - { { 250, 194 }, - { 141, 174 }, - { 67, 95 }, - { 117, 177 }, }, - { { 240, 155 }, - { 185, 139 }, - { 217, 15 }, - { 209, 157 }, }, - { { 250, 244 }, - { 174, 190 }, - { 47, 95 }, - { 125, 117 }, }, - { { 250, 60 }, - { 174, 157 }, - { 60, 95 }, - { 185, 117 }, }, - { { 242, 252 }, - { 174, 187 }, - { 63, 79 }, - { 221, 117 }, }, - { { 242, 189 }, - { 190, 155 }, - { 189, 79 }, - { 217, 125 }, }, - { { 242, 147 }, - { 189, 138 }, - { 201, 79 }, - { 81, 189 }, }, - { { 241, 96 }, - { 202, 168 }, - { 6, 143 }, - { 21, 83 }, }, - { { 249, 236 }, - { 202, 191 }, - { 55, 159 }, - { 253, 83 }, }, - { { 241, 70 }, - { 201, 184 }, - { 98, 143 }, - { 29, 147 }, }, - { { 249, 225 }, - { 218, 174 }, - { 135, 159 }, - { 117, 91 }, }, - { { 243, 72 }, - { 204, 169 }, - { 18, 207 }, - { 149, 51 }, }, - { { 243, 174 }, - { 207, 155 }, - { 117, 207 }, - { 217, 243 }, }, - { { 243, 193 }, - { 220, 170 }, - { 131, 207 }, - { 85, 59 }, }, - { { 243, 139 }, - { 221, 139 }, - { 209, 207 }, - { 209, 187 }, }, - { { 243, 167 }, - { 223, 154 }, - { 229, 207 }, - { 89, 251 }, }, - { { 241, 115 }, - { 251, 168 }, - { 206, 143 }, - { 21, 223 }, }, - { { 241, 151 }, - { 249, 154 }, - { 233, 143 }, - { 89, 159 }, }, - { { 243, 244 }, - { 238, 186 }, - { 47, 207 }, - { 93, 119 }, }, - { { 251, 50 }, - { 239, 140 }, - { 76, 223 }, - { 49, 247 }, }, - { { 228, 7 }, - { 25, 216 }, - { 224, 39 }, - { 27, 152 }, }, - { { 230, 77 }, - { 28, 249 }, - { 178, 103 }, - { 159, 56 }, }, - { { 236, 85 }, - { 56, 252 }, - { 170, 55 }, - { 63, 28 }, }, - { { 237, 192 }, - { 72, 238 }, - { 3, 183 }, - { 119, 18 }, }, - { { 237, 133 }, - { 88, 222 }, - { 161, 183 }, - { 123, 26 }, }, - { { 239, 162 }, - { 79, 206 }, - { 69, 247 }, - { 115, 242 }, }, - { { 231, 78 }, - { 77, 249 }, - { 114, 231 }, - { 159, 178 }, }, - { { 229, 213 }, - { 120, 250 }, - { 171, 167 }, - { 95, 30 }, }, - { { 239, 80 }, - { 108, 236 }, - { 10, 247 }, - { 55, 54 }, }, - { { 244, 34 }, - { 139, 200 }, - { 68, 47 }, - { 19, 209 }, }, - { { 244, 137 }, - { 152, 203 }, - { 145, 47 }, - { 211, 25 }, }, - { { 244, 41 }, - { 154, 201 }, - { 148, 47 }, - { 147, 89 }, }, - { { 246, 106 }, - { 143, 233 }, - { 86, 111 }, - { 151, 241 }, }, - { { 254, 11 }, - { 157, 205 }, - { 208, 127 }, - { 179, 185 }, }, - { { 254, 111 }, - { 159, 253 }, - { 246, 127 }, - { 191, 249 }, }, - { { 244, 149 }, - { 184, 218 }, - { 169, 47 }, - { 91, 29 }, }, - { { 244, 53 }, - { 186, 216 }, - { 172, 47 }, - { 27, 93 }, }, - { { 244, 31 }, - { 185, 217 }, - { 248, 47 }, - { 155, 157 }, }, - { { 246, 176 }, - { 174, 202 }, - { 13, 111 }, - { 83, 117 }, }, - { { 245, 232 }, - { 202, 235 }, - { 23, 175 }, - { 215, 83 }, }, - { { 245, 197 }, - { 216, 250 }, - { 163, 175 }, - { 95, 27 }, }, - { { 253, 35 }, - { 219, 204 }, - { 196, 191 }, - { 51, 219 }, }, - { { 255, 192 }, - { 204, 238 }, - { 3, 255 }, - { 119, 51 }, }, - { { 247, 204 }, - { 204, 251 }, - { 51, 239 }, - { 223, 51 }, }, - { { 247, 233 }, - { 222, 235 }, - { 151, 239 }, - { 215, 123 }, }, - { { 245, 188 }, - { 234, 219 }, - { 61, 175 }, - { 219, 87 }, }, - { { 253, 246 }, - { 235, 254 }, - { 111, 191 }, - { 127, 215 }, }, - { { 245, 217 }, - { 248, 235 }, - { 155, 175 }, - { 215, 31 }, }, - { { 253, 151 }, - { 249, 222 }, - { 233, 191 }, - { 123, 159 }, }, - { { 253, 63 }, - { 251, 221 }, - { 252, 191 }, - { 187, 223 }, }, - { { 255, 156 }, - { 236, 223 }, - { 57, 255 }, - { 251, 55 }, }, - { { 255, 90 }, - { 237, 237 }, - { 90, 255 }, - { 183, 183 }, }, - { { 247, 254 }, - { 239, 251 }, - { 127, 239 }, - { 223, 247 }, }, - { { 255, 17 }, - { 252, 204 }, - { 136, 255 }, - { 51, 63 }, }, - { { 247, 191 }, - { 255, 219 }, - { 253, 239 }, - { 219, 255 }, }, }; - -static unsigned char DICT_5X5_1000_BYTES[][4][4] = - { { { 162, 217, 94, 0 }, - { 82, 46, 217, 1 }, - { 61, 77, 162, 1 }, - { 205, 186, 37, 0 }, }, - { { 14, 3, 115, 0 }, - { 176, 198, 133, 0 }, - { 103, 96, 56, 0 }, - { 80, 177, 134, 1 }, }, - { { 215, 135, 110, 1 }, - { 47, 151, 157, 1 }, - { 187, 112, 245, 1 }, - { 220, 244, 250, 0 }, }, - { { 129, 202, 251, 1 }, - { 122, 222, 40, 1 }, - { 239, 169, 192, 1 }, - { 138, 61, 175, 0 }, }, - { { 215, 90, 146, 0 }, - { 116, 27, 236, 0 }, - { 36, 173, 117, 1 }, - { 27, 236, 23, 0 }, }, - { { 234, 4, 22, 1 }, - { 153, 35, 152, 0 }, - { 180, 16, 43, 1 }, - { 12, 226, 76, 1 }, }, - { { 105, 235, 246, 0 }, - { 242, 191, 51, 0 }, - { 55, 235, 203, 0 }, - { 102, 126, 167, 1 }, }, - { { 113, 10, 53, 1 }, - { 60, 249, 16, 0 }, - { 214, 40, 71, 0 }, - { 4, 79, 158, 0 }, }, - { { 134, 176, 153, 0 }, - { 18, 64, 238, 1 }, - { 76, 134, 176, 1 }, - { 187, 129, 36, 0 }, }, - { { 152, 159, 210, 1 }, - { 191, 14, 105, 0 }, - { 165, 252, 140, 1 }, - { 75, 56, 126, 1 }, }, - { { 158, 119, 1, 1 }, - { 237, 64, 207, 0 }, - { 192, 119, 60, 1 }, - { 121, 129, 91, 1 }, }, - { { 209, 109, 96, 0 }, - { 69, 157, 11, 0 }, - { 3, 91, 69, 1 }, - { 104, 92, 209, 0 }, }, - { { 243, 21, 136, 1 }, - { 13, 49, 233, 1 }, - { 136, 212, 103, 1 }, - { 203, 198, 88, 0 }, }, - { { 47, 56, 179, 0 }, - { 144, 250, 230, 0 }, - { 102, 142, 122, 0 }, - { 51, 175, 132, 1 }, }, - { { 254, 126, 84, 0 }, - { 245, 45, 222, 0 }, - { 21, 63, 63, 1 }, - { 61, 218, 87, 1 }, }, - { { 40, 241, 191, 1 }, - { 218, 226, 115, 1 }, - { 254, 199, 138, 0 }, - { 231, 35, 173, 1 }, }, - { { 75, 211, 172, 0 }, - { 226, 145, 241, 1 }, - { 26, 229, 233, 0 }, - { 199, 196, 163, 1 }, }, - { { 95, 81, 55, 1 }, - { 220, 211, 213, 0 }, - { 246, 69, 125, 0 }, - { 85, 229, 157, 1 }, }, - { { 123, 38, 226, 0 }, - { 165, 183, 162, 0 }, - { 35, 178, 111, 0 }, - { 34, 246, 210, 1 }, }, - { { 131, 14, 244, 0 }, - { 49, 156, 184, 0 }, - { 23, 184, 96, 1 }, - { 14, 156, 198, 0 }, }, - { { 150, 237, 58, 1 }, - { 95, 138, 143, 1 }, - { 174, 91, 180, 1 }, - { 248, 168, 253, 0 }, }, - { { 168, 114, 32, 0 }, - { 224, 160, 74, 0 }, - { 2, 39, 10, 1 }, - { 41, 2, 131, 1 }, }, - { { 181, 134, 80, 1 }, - { 63, 52, 12, 0 }, - { 133, 48, 214, 1 }, - { 24, 22, 126, 0 }, }, - { { 93, 9, 111, 0 }, - { 132, 223, 21, 1 }, - { 123, 72, 93, 0 }, - { 212, 125, 144, 1 }, }, - { { 206, 104, 17, 1 }, - { 216, 73, 142, 0 }, - { 196, 11, 57, 1 }, - { 56, 201, 13, 1 }, }, - { { 210, 204, 185, 0 }, - { 87, 201, 168, 1 }, - { 78, 153, 165, 1 }, - { 138, 201, 245, 0 }, }, - { { 225, 231, 69, 1 }, - { 107, 117, 27, 0 }, - { 209, 115, 195, 1 }, - { 108, 87, 107, 0 }, }, - { { 17, 33, 35, 0 }, - { 4, 210, 3, 0 }, - { 98, 66, 68, 0 }, - { 96, 37, 144, 0 }, }, - { { 29, 203, 57, 0 }, - { 246, 216, 5, 1 }, - { 78, 105, 220, 0 }, - { 208, 13, 183, 1 }, }, - { { 18, 17, 29, 1 }, - { 28, 64, 209, 1 }, - { 220, 68, 36, 0 }, - { 197, 129, 28, 0 }, }, - { { 19, 155, 183, 0 }, - { 54, 218, 241, 0 }, - { 118, 236, 228, 0 }, - { 71, 173, 182, 0 }, }, - { { 27, 68, 57, 1 }, - { 221, 208, 128, 1 }, - { 206, 17, 108, 0 }, - { 128, 133, 221, 1 }, }, - { { 32, 104, 103, 0 }, - { 64, 238, 18, 0 }, - { 115, 11, 2, 0 }, - { 36, 59, 129, 0 }, }, - { { 37, 85, 100, 0 }, - { 65, 180, 85, 0 }, - { 19, 85, 82, 0 }, - { 85, 22, 193, 0 }, }, - { { 35, 33, 221, 0 }, - { 16, 116, 179, 1 }, - { 93, 194, 98, 0 }, - { 230, 151, 4, 0 }, }, - { { 61, 55, 245, 0 }, - { 181, 244, 119, 0 }, - { 87, 246, 94, 0 }, - { 119, 23, 214, 1 }, }, - { { 76, 197, 86, 0 }, - { 211, 7, 21, 0 }, - { 53, 81, 153, 0 }, - { 84, 112, 101, 1 }, }, - { { 65, 104, 128, 1 }, - { 72, 25, 34, 0 }, - { 128, 139, 65, 0 }, - { 34, 76, 9, 0 }, }, - { { 77, 86, 142, 1 }, - { 233, 19, 116, 1 }, - { 184, 181, 89, 0 }, - { 151, 100, 75, 1 }, }, - { { 67, 30, 57, 0 }, - { 49, 217, 192, 1 }, - { 78, 60, 97, 0 }, - { 129, 205, 198, 0 }, }, - { { 86, 148, 18, 1 }, - { 31, 3, 196, 0 }, - { 164, 20, 181, 0 }, - { 17, 224, 124, 0 }, }, - { { 82, 151, 207, 0 }, - { 39, 71, 241, 1 }, - { 121, 244, 165, 0 }, - { 199, 241, 114, 0 }, }, - { { 108, 36, 251, 1 }, - { 153, 231, 38, 1 }, - { 239, 146, 27, 0 }, - { 178, 115, 204, 1 }, }, - { { 97, 132, 236, 1 }, - { 11, 181, 48, 1 }, - { 155, 144, 195, 0 }, - { 134, 86, 232, 0 }, }, - { { 109, 63, 24, 1 }, - { 185, 57, 71, 1 }, - { 140, 126, 91, 0 }, - { 241, 78, 78, 1 }, }, - { { 116, 177, 61, 0 }, - { 22, 225, 87, 1 }, - { 94, 70, 151, 0 }, - { 245, 67, 180, 0 }, }, - { { 116, 220, 203, 1 }, - { 79, 111, 100, 1 }, - { 233, 157, 151, 0 }, - { 147, 123, 121, 0 }, }, - { { 124, 164, 3, 0 }, - { 135, 99, 6, 0 }, - { 96, 18, 159, 0 }, - { 48, 99, 112, 1 }, }, - { { 122, 200, 146, 1 }, - { 222, 43, 160, 0 }, - { 164, 137, 175, 0 }, - { 2, 234, 61, 1 }, }, - { { 123, 91, 235, 1 }, - { 236, 255, 225, 1 }, - { 235, 237, 111, 0 }, - { 195, 255, 155, 1 }, }, - { { 141, 172, 114, 0 }, - { 147, 158, 14, 0 }, - { 39, 26, 216, 1 }, - { 56, 60, 228, 1 }, }, - { { 141, 105, 60, 1 }, - { 216, 152, 31, 1 }, - { 158, 75, 88, 1 }, - { 252, 12, 141, 1 }, }, - { { 143, 28, 5, 1 }, - { 137, 88, 220, 0 }, - { 208, 28, 120, 1 }, - { 29, 141, 72, 1 }, }, - { { 139, 74, 34, 1 }, - { 232, 154, 136, 0 }, - { 162, 41, 104, 1 }, - { 8, 172, 139, 1 }, }, - { { 151, 253, 165, 0 }, - { 71, 216, 255, 0 }, - { 82, 223, 244, 1 }, - { 127, 141, 241, 0 }, }, - { { 172, 101, 198, 1 }, - { 201, 38, 63, 0 }, - { 177, 211, 26, 1 }, - { 126, 50, 73, 1 }, }, - { { 172, 195, 248, 0 }, - { 242, 164, 45, 1 }, - { 15, 225, 154, 1 }, - { 218, 18, 167, 1 }, }, - { { 161, 23, 239, 1 }, - { 41, 246, 121, 1 }, - { 251, 244, 66, 1 }, - { 207, 55, 202, 0 }, }, - { { 167, 9, 19, 1 }, - { 24, 122, 141, 0 }, - { 228, 72, 114, 1 }, - { 88, 175, 12, 0 }, }, - { { 171, 111, 145, 0 }, - { 241, 120, 171, 0 }, - { 68, 251, 106, 1 }, - { 106, 143, 71, 1 }, }, - { { 185, 237, 248, 1 }, - { 223, 188, 43, 1 }, - { 143, 219, 206, 1 }, - { 234, 30, 253, 1 }, }, - { { 178, 100, 158, 0 }, - { 85, 34, 186, 1 }, - { 60, 147, 38, 1 }, - { 174, 162, 85, 0 }, }, - { { 190, 93, 195, 0 }, - { 197, 110, 237, 0 }, - { 97, 221, 62, 1 }, - { 91, 187, 81, 1 }, }, - { { 196, 5, 67, 1 }, - { 9, 71, 13, 0 }, - { 225, 80, 17, 1 }, - { 88, 113, 72, 0 }, }, - { { 200, 163, 238, 1 }, - { 170, 135, 59, 1 }, - { 187, 226, 137, 1 }, - { 238, 112, 170, 1 }, }, - { { 194, 117, 197, 0 }, - { 65, 69, 251, 0 }, - { 81, 215, 33, 1 }, - { 111, 209, 65, 0 }, }, - { { 198, 194, 214, 1 }, - { 122, 7, 188, 0 }, - { 181, 161, 177, 1 }, - { 30, 240, 47, 0 }, }, - { { 217, 102, 212, 1 }, - { 253, 21, 58, 0 }, - { 149, 179, 77, 1 }, - { 46, 84, 95, 1 }, }, - { { 221, 94, 185, 1 }, - { 253, 217, 108, 1 }, - { 206, 189, 93, 1 }, - { 155, 77, 223, 1 }, }, - { { 244, 234, 25, 0 }, - { 118, 105, 14, 1 }, - { 76, 43, 151, 1 }, - { 184, 75, 55, 0 }, }, - { { 243, 178, 148, 0 }, - { 54, 49, 250, 0 }, - { 20, 166, 231, 1 }, - { 47, 198, 54, 0 }, }, - { { 122, 186, 5, 0 }, - { 166, 105, 210, 0 }, - { 80, 46, 175, 0 }, - { 37, 203, 50, 1 }, }, - { { 216, 141, 41, 1 }, - { 143, 201, 9, 1 }, - { 202, 88, 141, 1 }, - { 200, 73, 248, 1 }, }, - { { 12, 103, 50, 1 }, - { 249, 130, 7, 0 }, - { 166, 115, 24, 0 }, - { 112, 32, 207, 1 }, }, - { { 21, 89, 12, 1 }, - { 76, 24, 85, 1 }, - { 152, 77, 84, 0 }, - { 213, 12, 25, 0 }, }, - { { 76, 116, 192, 1 }, - { 201, 5, 102, 0 }, - { 129, 151, 25, 0 }, - { 51, 80, 73, 1 }, }, - { { 84, 3, 14, 0 }, - { 36, 3, 21, 1 }, - { 56, 96, 21, 0 }, - { 212, 96, 18, 0 }, }, - { { 160, 208, 172, 0 }, - { 66, 160, 120, 1 }, - { 26, 133, 130, 1 }, - { 143, 2, 161, 0 }, }, - { { 194, 152, 166, 0 }, - { 2, 139, 248, 0 }, - { 50, 140, 161, 1 }, - { 15, 232, 160, 0 }, }, - { { 203, 104, 150, 0 }, - { 208, 27, 186, 0 }, - { 52, 139, 105, 1 }, - { 46, 236, 5, 1 }, }, - { { 253, 105, 209, 0 }, - { 212, 125, 47, 0 }, - { 69, 203, 95, 1 }, - { 122, 95, 21, 1 }, }, - { { 4, 145, 90, 1 }, - { 26, 6, 69, 1 }, - { 173, 68, 144, 0 }, - { 209, 48, 44, 0 }, }, - { { 12, 222, 112, 0 }, - { 243, 140, 68, 0 }, - { 7, 61, 152, 0 }, - { 17, 24, 231, 1 }, }, - { { 5, 170, 62, 0 }, - { 50, 154, 22, 1 }, - { 62, 42, 208, 0 }, - { 180, 44, 166, 0 }, }, - { { 1, 99, 183, 1 }, - { 120, 210, 51, 0 }, - { 246, 227, 64, 0 }, - { 102, 37, 143, 0 }, }, - { { 9, 145, 68, 1 }, - { 138, 20, 81, 0 }, - { 145, 68, 200, 0 }, - { 69, 20, 40, 1 }, }, - { { 9, 105, 83, 1 }, - { 216, 94, 3, 0 }, - { 229, 75, 72, 0 }, - { 96, 61, 13, 1 }, }, - { { 6, 37, 161, 1 }, - { 9, 192, 167, 0 }, - { 194, 210, 48, 0 }, - { 114, 129, 200, 0 }, }, - { { 3, 61, 226, 1 }, - { 9, 158, 227, 0 }, - { 163, 222, 96, 0 }, - { 99, 188, 200, 0 }, }, - { { 7, 213, 6, 1 }, - { 75, 18, 213, 0 }, - { 176, 85, 240, 0 }, - { 85, 164, 105, 0 }, }, - { { 15, 143, 170, 0 }, - { 163, 154, 165, 1 }, - { 42, 248, 248, 0 }, - { 210, 172, 226, 1 }, }, - { { 28, 116, 60, 1 }, - { 221, 128, 86, 1 }, - { 158, 23, 28, 0 }, - { 181, 0, 221, 1 }, }, - { { 25, 29, 145, 1 }, - { 157, 88, 97, 0 }, - { 196, 220, 76, 0 }, - { 67, 13, 92, 1 }, }, - { { 22, 27, 35, 1 }, - { 44, 202, 197, 0 }, - { 226, 108, 52, 0 }, - { 81, 169, 154, 0 }, }, - { { 22, 210, 141, 1 }, - { 110, 64, 244, 1 }, - { 216, 165, 180, 0 }, - { 151, 129, 59, 0 }, }, - { { 23, 78, 198, 1 }, - { 109, 30, 180, 0 }, - { 177, 185, 116, 0 }, - { 22, 188, 91, 0 }, }, - { { 19, 226, 177, 0 }, - { 118, 208, 162, 0 }, - { 70, 163, 228, 0 }, - { 34, 133, 183, 0 }, }, - { { 31, 126, 250, 1 }, - { 253, 158, 230, 1 }, - { 175, 191, 124, 0 }, - { 179, 188, 223, 1 }, }, - { { 41, 15, 112, 0 }, - { 177, 188, 1, 0 }, - { 7, 120, 74, 0 }, - { 64, 30, 198, 1 }, }, - { { 34, 71, 126, 1 }, - { 121, 166, 145, 1 }, - { 191, 113, 34, 0 }, - { 196, 178, 207, 0 }, }, - { { 42, 23, 192, 1 }, - { 169, 36, 225, 0 }, - { 129, 244, 42, 0 }, - { 67, 146, 74, 1 }, }, - { { 42, 251, 100, 1 }, - { 234, 172, 211, 0 }, - { 147, 111, 170, 0 }, - { 101, 154, 171, 1 }, }, - { { 35, 240, 4, 0 }, - { 66, 48, 210, 0 }, - { 16, 7, 226, 0 }, - { 37, 134, 33, 0 }, }, - { { 39, 205, 252, 0 }, - { 83, 188, 181, 1 }, - { 31, 217, 242, 0 }, - { 214, 158, 229, 0 }, }, - { { 35, 106, 172, 0 }, - { 96, 184, 178, 1 }, - { 26, 171, 98, 0 }, - { 166, 142, 131, 0 }, }, - { { 47, 123, 47, 0 }, - { 224, 250, 215, 1 }, - { 122, 111, 122, 0 }, - { 245, 175, 131, 1 }, }, - { { 52, 144, 196, 0 }, - { 6, 36, 116, 0 }, - { 17, 132, 150, 0 }, - { 23, 18, 48, 0 }, }, - { { 48, 9, 214, 1 }, - { 28, 46, 49, 0 }, - { 181, 200, 6, 0 }, - { 70, 58, 28, 0 }, }, - { { 52, 94, 16, 1 }, - { 125, 40, 68, 0 }, - { 132, 61, 22, 0 }, - { 17, 10, 95, 0 }, }, - { { 57, 82, 232, 0 }, - { 228, 180, 96, 1 }, - { 11, 165, 78, 0 }, - { 131, 22, 147, 1 }, }, - { { 50, 0, 113, 0 }, - { 20, 228, 128, 0 }, - { 71, 0, 38, 0 }, - { 0, 147, 148, 0 }, }, - { { 54, 231, 29, 0 }, - { 119, 96, 151, 1 }, - { 92, 115, 182, 0 }, - { 244, 131, 119, 0 }, }, - { { 68, 138, 4, 1 }, - { 42, 9, 20, 0 }, - { 144, 40, 145, 0 }, - { 20, 72, 42, 0 }, }, - { { 68, 236, 254, 1 }, - { 91, 143, 54, 1 }, - { 191, 155, 145, 0 }, - { 182, 120, 237, 0 }, }, - { { 69, 31, 114, 1 }, - { 57, 159, 69, 0 }, - { 167, 124, 81, 0 }, - { 81, 124, 206, 0 }, }, - { { 69, 74, 149, 0 }, - { 112, 89, 52, 0 }, - { 84, 169, 81, 0 }, - { 22, 77, 7, 0 }, }, - { { 70, 19, 190, 1 }, - { 56, 131, 245, 1 }, - { 190, 228, 49, 0 }, - { 215, 224, 142, 0 }, }, - { { 78, 131, 9, 0 }, - { 162, 65, 133, 1 }, - { 72, 96, 185, 0 }, - { 208, 193, 34, 1 }, }, - { { 71, 114, 153, 1 }, - { 120, 81, 230, 1 }, - { 204, 167, 113, 0 }, - { 179, 197, 15, 0 }, }, - { { 79, 130, 125, 1 }, - { 186, 213, 148, 1 }, - { 223, 32, 249, 0 }, - { 148, 213, 174, 1 }, }, - { { 92, 55, 141, 1 }, - { 173, 65, 119, 1 }, - { 216, 246, 29, 0 }, - { 247, 65, 90, 1 }, }, - { { 88, 253, 119, 0 }, - { 215, 207, 83, 0 }, - { 119, 95, 141, 0 }, - { 101, 121, 245, 1 }, }, - { { 81, 183, 248, 0 }, - { 55, 149, 99, 1 }, - { 15, 246, 197, 0 }, - { 227, 84, 246, 0 }, }, - { { 89, 58, 251, 0 }, - { 180, 223, 98, 1 }, - { 111, 174, 77, 0 }, - { 163, 125, 150, 1 }, }, - { { 90, 153, 153, 0 }, - { 150, 73, 225, 1 }, - { 76, 204, 173, 0 }, - { 195, 201, 52, 1 }, }, - { { 83, 1, 240, 1 }, - { 28, 149, 161, 0 }, - { 135, 192, 101, 0 }, - { 66, 212, 156, 0 }, }, - { { 83, 204, 103, 1 }, - { 79, 223, 144, 0 }, - { 243, 25, 229, 0 }, - { 4, 253, 249, 0 }, }, - { { 83, 110, 1, 0 }, - { 101, 89, 130, 0 }, - { 64, 59, 101, 0 }, - { 32, 205, 83, 0 }, }, - { { 91, 37, 175, 1 }, - { 141, 211, 179, 1 }, - { 250, 210, 109, 0 }, - { 230, 229, 216, 1 }, }, - { { 95, 213, 204, 1 }, - { 207, 21, 245, 1 }, - { 153, 213, 253, 0 }, - { 215, 212, 121, 1 }, }, - { { 101, 102, 66, 1 }, - { 105, 55, 6, 0 }, - { 161, 51, 83, 0 }, - { 48, 118, 75, 0 }, }, - { { 101, 199, 175, 0 }, - { 99, 243, 53, 1 }, - { 122, 241, 211, 0 }, - { 214, 103, 227, 0 }, }, - { { 102, 55, 81, 0 }, - { 49, 101, 199, 0 }, - { 69, 118, 51, 0 }, - { 113, 211, 70, 0 }, }, - { { 103, 12, 197, 1 }, - { 9, 125, 180, 0 }, - { 209, 152, 115, 0 }, - { 22, 223, 72, 0 }, }, - { { 112, 150, 93, 1 }, - { 63, 101, 80, 1 }, - { 221, 52, 135, 0 }, - { 133, 83, 126, 0 }, }, - { { 124, 99, 128, 1 }, - { 236, 33, 39, 0 }, - { 128, 227, 31, 0 }, - { 114, 66, 27, 1 }, }, - { { 121, 171, 169, 0 }, - { 166, 249, 35, 1 }, - { 74, 234, 207, 0 }, - { 226, 79, 178, 1 }, }, - { { 114, 61, 70, 0 }, - { 5, 47, 211, 0 }, - { 49, 94, 39, 0 }, - { 101, 250, 80, 0 }, }, - { { 115, 229, 178, 1 }, - { 95, 179, 163, 0 }, - { 166, 211, 231, 0 }, - { 98, 230, 253, 0 }, }, - { { 132, 38, 140, 0 }, - { 33, 0, 62, 1 }, - { 24, 178, 16, 1 }, - { 190, 0, 66, 0 }, }, - { { 137, 241, 1, 0 }, - { 194, 80, 75, 0 }, - { 64, 71, 200, 1 }, - { 105, 5, 33, 1 }, }, - { { 134, 174, 233, 1 }, - { 43, 204, 174, 1 }, - { 203, 186, 176, 1 }, - { 186, 153, 234, 0 }, }, - { { 138, 35, 249, 1 }, - { 184, 196, 171, 1 }, - { 207, 226, 40, 1 }, - { 234, 145, 142, 1 }, }, - { { 148, 53, 113, 1 }, - { 29, 196, 79, 0 }, - { 199, 86, 20, 1 }, - { 121, 17, 220, 0 }, }, - { { 156, 69, 27, 0 }, - { 213, 66, 13, 1 }, - { 108, 81, 28, 1 }, - { 216, 33, 85, 1 }, }, - { { 149, 31, 164, 1 }, - { 45, 152, 125, 0 }, - { 146, 252, 84, 1 }, - { 95, 12, 218, 0 }, }, - { { 150, 141, 144, 0 }, - { 23, 8, 173, 0 }, - { 4, 216, 180, 1 }, - { 90, 136, 116, 0 }, }, - { { 151, 38, 183, 0 }, - { 53, 210, 190, 0 }, - { 118, 178, 116, 1 }, - { 62, 165, 214, 0 }, }, - { { 155, 55, 103, 0 }, - { 165, 214, 219, 0 }, - { 115, 118, 108, 1 }, - { 109, 181, 210, 1 }, }, - { { 155, 198, 224, 1 }, - { 239, 148, 168, 0 }, - { 131, 177, 236, 1 }, - { 10, 148, 251, 1 }, }, - { { 160, 154, 110, 1 }, - { 42, 174, 88, 1 }, - { 187, 44, 130, 1 }, - { 141, 58, 170, 0 }, }, - { { 164, 82, 134, 1 }, - { 104, 34, 124, 0 }, - { 176, 165, 18, 1 }, - { 31, 34, 11, 0 }, }, - { { 168, 176, 133, 1 }, - { 138, 96, 122, 0 }, - { 208, 134, 138, 1 }, - { 47, 3, 40, 1 }, }, - { { 173, 155, 66, 0 }, - { 162, 62, 77, 0 }, - { 33, 108, 218, 1 }, - { 89, 62, 34, 1 }, }, - { { 175, 240, 88, 1 }, - { 218, 52, 206, 1 }, - { 141, 7, 250, 1 }, - { 185, 150, 45, 1 }, }, - { { 176, 46, 99, 1 }, - { 45, 238, 10, 0 }, - { 227, 58, 6, 1 }, - { 40, 59, 218, 0 }, }, - { { 176, 15, 24, 0 }, - { 53, 40, 9, 1 }, - { 12, 120, 6, 1 }, - { 200, 10, 86, 0 }, }, - { { 188, 234, 178, 1 }, - { 254, 170, 46, 0 }, - { 166, 171, 158, 1 }, - { 58, 42, 191, 1 }, }, - { { 190, 196, 76, 1 }, - { 207, 36, 156, 1 }, - { 153, 17, 190, 1 }, - { 156, 146, 121, 1 }, }, - { { 179, 130, 250, 0 }, - { 54, 182, 168, 1 }, - { 47, 160, 230, 1 }, - { 138, 182, 182, 0 }, }, - { { 179, 88, 60, 1 }, - { 92, 184, 216, 1 }, - { 158, 13, 102, 1 }, - { 141, 142, 157, 0 }, }, - { { 191, 69, 238, 0 }, - { 197, 182, 189, 1 }, - { 59, 209, 126, 1 }, - { 222, 182, 209, 1 }, }, - { { 191, 86, 51, 1 }, - { 253, 242, 204, 0 }, - { 230, 53, 126, 1 }, - { 25, 167, 223, 1 }, }, - { { 196, 58, 240, 0 }, - { 48, 141, 110, 0 }, - { 7, 174, 17, 1 }, - { 59, 88, 134, 0 }, }, - { { 204, 233, 131, 0 }, - { 194, 75, 47, 0 }, - { 96, 203, 153, 1 }, - { 122, 105, 33, 1 }, }, - { { 197, 51, 0, 1 }, - { 40, 17, 79, 0 }, - { 128, 102, 81, 1 }, - { 121, 68, 10, 0 }, }, - { { 205, 34, 99, 1 }, - { 168, 215, 14, 0 }, - { 227, 34, 89, 1 }, - { 56, 117, 138, 1 }, }, - { { 198, 8, 138, 1 }, - { 8, 11, 172, 1 }, - { 168, 136, 49, 1 }, - { 154, 232, 8, 0 }, }, - { { 198, 148, 63, 0 }, - { 19, 195, 220, 1 }, - { 126, 20, 177, 1 }, - { 157, 225, 228, 0 }, }, - { { 202, 238, 132, 1 }, - { 235, 9, 186, 0 }, - { 144, 187, 169, 1 }, - { 46, 200, 107, 1 }, }, - { { 207, 167, 18, 1 }, - { 187, 19, 143, 0 }, - { 164, 114, 249, 1 }, - { 120, 228, 110, 1 }, }, - { { 203, 97, 226, 1 }, - { 200, 151, 171, 0 }, - { 163, 195, 105, 1 }, - { 106, 244, 137, 1 }, }, - { { 208, 2, 233, 0 }, - { 36, 197, 40, 1 }, - { 75, 160, 5, 1 }, - { 138, 81, 146, 0 }, }, - { { 220, 183, 70, 0 }, - { 167, 7, 95, 0 }, - { 49, 118, 157, 1 }, - { 125, 112, 114, 1 }, }, - { { 217, 139, 132, 1 }, - { 174, 25, 57, 0 }, - { 144, 232, 205, 1 }, - { 78, 76, 58, 1 }, }, - { { 217, 206, 178, 0 }, - { 247, 155, 40, 0 }, - { 38, 185, 205, 1 }, - { 10, 108, 247, 1 }, }, - { { 210, 153, 64, 1 }, - { 14, 13, 201, 0 }, - { 129, 76, 165, 1 }, - { 73, 216, 56, 0 }, }, - { { 210, 209, 23, 0 }, - { 86, 67, 217, 0 }, - { 116, 69, 165, 1 }, - { 77, 225, 53, 0 }, }, - { { 222, 90, 110, 1 }, - { 236, 143, 220, 1 }, - { 187, 45, 61, 1 }, - { 157, 248, 155, 1 }, }, - { { 218, 71, 66, 1 }, - { 237, 7, 137, 0 }, - { 161, 113, 45, 1 }, - { 72, 240, 91, 1 }, }, - { { 219, 60, 16, 0 }, - { 149, 25, 202, 0 }, - { 4, 30, 109, 1 }, - { 41, 204, 84, 1 }, }, - { { 219, 143, 201, 0 }, - { 167, 93, 169, 1 }, - { 73, 248, 237, 1 }, - { 202, 221, 114, 1 }, }, - { { 232, 24, 227, 1 }, - { 136, 239, 104, 0 }, - { 227, 140, 11, 1 }, - { 11, 123, 136, 1 }, }, - { { 229, 28, 111, 0 }, - { 1, 255, 92, 1 }, - { 123, 28, 83, 1 }, - { 157, 127, 192, 0 }, }, - { { 225, 174, 56, 1 }, - { 59, 185, 10, 1 }, - { 142, 58, 195, 1 }, - { 168, 78, 238, 0 }, }, - { { 237, 185, 123, 1 }, - { 154, 255, 79, 1 }, - { 239, 78, 219, 1 }, - { 249, 127, 172, 1 }, }, - { { 233, 224, 50, 0 }, - { 210, 179, 10, 0 }, - { 38, 3, 203, 1 }, - { 40, 102, 165, 1 }, }, - { { 235, 93, 12, 0 }, - { 193, 57, 217, 1 }, - { 24, 93, 107, 1 }, - { 205, 206, 65, 1 }, }, - { { 239, 249, 157, 0 }, - { 210, 121, 255, 1 }, - { 92, 207, 251, 1 }, - { 255, 207, 37, 1 }, }, - { { 248, 17, 1, 0 }, - { 132, 97, 73, 0 }, - { 64, 68, 15, 1 }, - { 73, 67, 16, 1 }, }, - { { 248, 7, 211, 0 }, - { 181, 103, 41, 0 }, - { 101, 240, 15, 1 }, - { 74, 115, 86, 1 }, }, - { { 246, 2, 32, 1 }, - { 44, 161, 140, 0 }, - { 130, 32, 55, 1 }, - { 24, 194, 154, 0 }, }, - { { 246, 27, 95, 1 }, - { 60, 111, 221, 1 }, - { 253, 108, 55, 1 }, - { 221, 251, 30, 0 }, }, - { { 254, 181, 237, 0 }, - { 135, 229, 255, 1 }, - { 91, 214, 191, 1 }, - { 255, 211, 240, 1 }, }, - { { 250, 51, 56, 1 }, - { 188, 161, 203, 1 }, - { 142, 102, 47, 1 }, - { 233, 194, 158, 1 }, }, - { { 250, 74, 193, 1 }, - { 236, 109, 168, 0 }, - { 193, 169, 47, 1 }, - { 10, 219, 27, 1 }, }, - { { 247, 47, 112, 1 }, - { 61, 189, 143, 0 }, - { 135, 122, 119, 1 }, - { 120, 222, 222, 0 }, }, - { { 247, 234, 252, 1 }, - { 126, 189, 190, 1 }, - { 159, 171, 247, 1 }, - { 190, 222, 191, 0 }, }, - { { 255, 24, 148, 1 }, - { 156, 57, 252, 0 }, - { 148, 140, 127, 1 }, - { 31, 206, 28, 1 }, }, - { { 251, 163, 94, 0 }, - { 182, 55, 155, 1 }, - { 61, 98, 239, 1 }, - { 236, 246, 54, 1 }, }, - { { 104, 184, 47, 0 }, - { 130, 235, 82, 1 }, - { 122, 14, 139, 0 }, - { 165, 107, 160, 1 }, }, - { { 153, 15, 11, 1 }, - { 173, 90, 9, 1 }, - { 232, 120, 76, 1 }, - { 200, 45, 90, 1 }, }, - { { 153, 216, 38, 1 }, - { 206, 154, 88, 0 }, - { 178, 13, 204, 1 }, - { 13, 44, 185, 1 }, }, - { { 228, 95, 14, 1 }, - { 105, 43, 93, 1 }, - { 184, 125, 19, 1 }, - { 221, 106, 75, 0 }, }, - { { 29, 16, 110, 1 }, - { 140, 150, 84, 1 }, - { 187, 4, 92, 0 }, - { 149, 52, 152, 1 }, }, - { { 42, 193, 48, 1 }, - { 218, 160, 129, 0 }, - { 134, 65, 170, 0 }, - { 64, 130, 173, 1 }, }, - { { 52, 65, 99, 1 }, - { 76, 230, 5, 0 }, - { 227, 65, 22, 0 }, - { 80, 51, 153, 0 }, }, - { { 55, 192, 116, 1 }, - { 94, 180, 148, 0 }, - { 151, 1, 246, 0 }, - { 20, 150, 189, 0 }, }, - { { 63, 53, 203, 1 }, - { 141, 118, 231, 1 }, - { 233, 214, 126, 0 }, - { 243, 183, 88, 1 }, }, - { { 86, 160, 76, 0 }, - { 6, 5, 150, 1 }, - { 25, 2, 181, 0 }, - { 180, 208, 48, 0 }, }, - { { 87, 56, 57, 1 }, - { 28, 217, 198, 1 }, - { 206, 14, 117, 0 }, - { 177, 205, 156, 0 }, }, - { { 102, 152, 184, 0 }, - { 18, 169, 228, 1 }, - { 14, 140, 179, 0 }, - { 147, 202, 164, 0 }, }, - { { 115, 165, 23, 0 }, - { 23, 115, 147, 0 }, - { 116, 82, 231, 0 }, - { 100, 231, 116, 0 }, }, - { { 127, 44, 253, 1 }, - { 157, 253, 182, 1 }, - { 223, 154, 127, 0 }, - { 182, 223, 220, 1 }, }, - { { 139, 71, 233, 0 }, - { 225, 212, 169, 1 }, - { 75, 241, 104, 1 }, - { 202, 149, 195, 1 }, }, - { { 165, 195, 151, 0 }, - { 114, 114, 61, 0 }, - { 116, 225, 210, 1 }, - { 94, 39, 39, 0 }, }, - { { 169, 169, 235, 0 }, - { 130, 254, 43, 1 }, - { 107, 202, 202, 1 }, - { 234, 63, 160, 1 }, }, - { { 181, 25, 183, 0 }, - { 20, 250, 125, 0 }, - { 118, 204, 86, 1 }, - { 95, 47, 148, 0 }, }, - { { 178, 218, 153, 1 }, - { 126, 104, 232, 1 }, - { 204, 173, 166, 1 }, - { 139, 139, 63, 0 }, }, - { { 196, 193, 244, 0 }, - { 82, 133, 61, 0 }, - { 23, 193, 145, 1 }, - { 94, 80, 165, 0 }, }, - { { 202, 185, 30, 0 }, - { 146, 11, 219, 1 }, - { 60, 78, 169, 1 }, - { 237, 232, 36, 1 }, }, - { { 216, 111, 163, 1 }, - { 237, 203, 43, 0 }, - { 226, 251, 13, 1 }, - { 106, 105, 219, 1 }, }, - { { 223, 141, 142, 0 }, - { 135, 27, 189, 1 }, - { 56, 216, 253, 1 }, - { 222, 236, 112, 1 }, }, - { { 229, 102, 143, 1 }, - { 105, 115, 62, 1 }, - { 248, 179, 83, 1 }, - { 190, 103, 75, 0 }, }, - { { 237, 71, 26, 1 }, - { 249, 51, 13, 1 }, - { 172, 113, 91, 1 }, - { 216, 102, 79, 1 }, }, - { { 240, 103, 134, 1 }, - { 109, 35, 59, 0 }, - { 176, 243, 7, 1 }, - { 110, 98, 91, 0 }, }, - { { 4, 41, 238, 1 }, - { 8, 142, 55, 1 }, - { 187, 202, 16, 0 }, - { 246, 56, 136, 0 }, }, - { { 0, 35, 71, 1 }, - { 40, 70, 19, 0 }, - { 241, 98, 0, 0 }, - { 100, 49, 10, 0 }, }, - { { 0, 162, 251, 0 }, - { 50, 198, 34, 1 }, - { 111, 162, 128, 0 }, - { 162, 49, 166, 0 }, }, - { { 4, 200, 206, 0 }, - { 66, 14, 52, 1 }, - { 57, 137, 144, 0 }, - { 150, 56, 33, 0 }, }, - { { 0, 210, 225, 1 }, - { 106, 196, 96, 0 }, - { 195, 165, 128, 0 }, - { 3, 17, 171, 0 }, }, - { { 12, 10, 15, 0 }, - { 160, 74, 20, 1 }, - { 120, 40, 24, 0 }, - { 148, 41, 2, 1 }, }, - { { 8, 139, 198, 0 }, - { 162, 14, 49, 0 }, - { 49, 232, 136, 0 }, - { 70, 56, 34, 1 }, }, - { { 12, 134, 91, 0 }, - { 179, 70, 4, 1 }, - { 109, 48, 152, 0 }, - { 144, 49, 102, 1 }, }, - { { 5, 129, 253, 0 }, - { 18, 212, 53, 1 }, - { 95, 192, 208, 0 }, - { 214, 21, 164, 0 }, }, - { { 1, 113, 30, 0 }, - { 80, 18, 83, 1 }, - { 60, 71, 64, 0 }, - { 229, 36, 5, 0 }, }, - { { 1, 194, 21, 1 }, - { 122, 80, 16, 0 }, - { 212, 33, 192, 0 }, - { 4, 5, 47, 0 }, }, - { { 1, 231, 113, 0 }, - { 115, 212, 3, 0 }, - { 71, 115, 192, 0 }, - { 96, 21, 231, 0 }, }, - { { 9, 41, 191, 0 }, - { 144, 218, 51, 1 }, - { 126, 202, 72, 0 }, - { 230, 45, 132, 1 }, }, - { { 13, 16, 27, 1 }, - { 152, 82, 68, 1 }, - { 236, 4, 88, 0 }, - { 145, 37, 12, 1 }, }, - { { 13, 5, 24, 0 }, - { 145, 16, 5, 1 }, - { 12, 80, 88, 0 }, - { 208, 4, 68, 1 }, }, - { { 13, 237, 47, 1 }, - { 203, 218, 23, 1 }, - { 250, 91, 216, 0 }, - { 244, 45, 233, 1 }, }, - { { 13, 102, 222, 0 }, - { 241, 22, 54, 1 }, - { 61, 179, 88, 0 }, - { 182, 52, 71, 1 }, }, - { { 9, 242, 118, 0 }, - { 242, 150, 82, 0 }, - { 55, 39, 200, 0 }, - { 37, 52, 167, 1 }, }, - { { 2, 208, 130, 1 }, - { 74, 2, 224, 0 }, - { 160, 133, 160, 0 }, - { 3, 160, 41, 0 }, }, - { { 2, 98, 201, 0 }, - { 96, 68, 162, 1 }, - { 73, 163, 32, 0 }, - { 162, 145, 3, 0 }, }, - { { 6, 243, 169, 0 }, - { 98, 192, 231, 1 }, - { 74, 231, 176, 0 }, - { 243, 129, 163, 0 }, }, - { { 6, 239, 35, 0 }, - { 99, 202, 135, 0 }, - { 98, 123, 176, 0 }, - { 112, 169, 227, 0 }, }, - { { 14, 62, 18, 0 }, - { 177, 10, 198, 0 }, - { 36, 62, 56, 0 }, - { 49, 168, 70, 1 }, }, - { { 10, 154, 221, 1 }, - { 186, 76, 240, 1 }, - { 221, 172, 168, 0 }, - { 135, 153, 46, 1 }, }, - { { 10, 97, 109, 1 }, - { 200, 196, 147, 1 }, - { 219, 67, 40, 0 }, - { 228, 145, 137, 1 }, }, - { { 10, 255, 81, 0 }, - { 243, 76, 195, 0 }, - { 69, 127, 168, 0 }, - { 97, 153, 103, 1 }, }, - { { 7, 230, 114, 1 }, - { 123, 150, 134, 0 }, - { 167, 51, 240, 0 }, - { 48, 180, 239, 0 }, }, - { { 11, 154, 1, 0 }, - { 162, 88, 192, 0 }, - { 64, 44, 232, 0 }, - { 1, 141, 34, 1 }, }, - { { 15, 208, 142, 0 }, - { 194, 18, 244, 1 }, - { 56, 133, 248, 0 }, - { 151, 164, 33, 1 }, }, - { { 15, 73, 147, 0 }, - { 208, 90, 165, 0 }, - { 100, 201, 120, 0 }, - { 82, 173, 5, 1 }, }, - { { 15, 247, 123, 0 }, - { 243, 214, 199, 1 }, - { 111, 119, 248, 0 }, - { 241, 181, 231, 1 }, }, - { { 16, 158, 96, 1 }, - { 47, 140, 64, 0 }, - { 131, 60, 132, 0 }, - { 1, 24, 250, 0 }, }, - { { 16, 81, 237, 0 }, - { 68, 196, 113, 1 }, - { 91, 197, 4, 0 }, - { 199, 17, 145, 0 }, }, - { { 20, 122, 222, 1 }, - { 124, 14, 118, 1 }, - { 189, 175, 20, 0 }, - { 183, 56, 31, 0 }, }, - { { 28, 133, 62, 1 }, - { 159, 130, 21, 1 }, - { 190, 80, 156, 0 }, - { 212, 32, 252, 1 }, }, - { { 28, 35, 104, 0 }, - { 164, 132, 7, 1 }, - { 11, 98, 28, 0 }, - { 240, 16, 146, 1 }, }, - { { 24, 199, 52, 0 }, - { 247, 128, 17, 0 }, - { 22, 113, 140, 0 }, - { 68, 0, 247, 1 }, }, - { { 21, 251, 130, 1 }, - { 110, 26, 103, 0 }, - { 160, 239, 212, 0 }, - { 115, 44, 59, 0 }, }, - { { 17, 78, 147, 1 }, - { 125, 90, 32, 0 }, - { 228, 185, 68, 0 }, - { 2, 45, 95, 0 }, }, - { { 25, 160, 234, 1 }, - { 142, 150, 34, 1 }, - { 171, 130, 204, 0 }, - { 162, 52, 184, 1 }, }, - { { 18, 24, 76, 0 }, - { 4, 12, 208, 1 }, - { 25, 12, 36, 0 }, - { 133, 152, 16, 0 }, }, - { { 18, 163, 174, 0 }, - { 38, 130, 179, 1 }, - { 58, 226, 164, 0 }, - { 230, 160, 178, 0 }, }, - { { 30, 172, 60, 0 }, - { 151, 136, 150, 1 }, - { 30, 26, 188, 0 }, - { 180, 136, 244, 1 }, }, - { { 30, 201, 108, 0 }, - { 198, 140, 149, 1 }, - { 27, 73, 188, 0 }, - { 212, 152, 177, 1 }, }, - { { 30, 233, 163, 1 }, - { 206, 202, 167, 0 }, - { 226, 203, 188, 0 }, - { 114, 169, 185, 1 }, }, - { { 26, 118, 216, 0 }, - { 245, 4, 226, 1 }, - { 13, 183, 44, 0 }, - { 163, 144, 87, 1 }, }, - { { 19, 182, 7, 1 }, - { 47, 82, 210, 0 }, - { 240, 54, 228, 0 }, - { 37, 165, 122, 0 }, }, - { { 23, 70, 188, 0 }, - { 117, 144, 180, 1 }, - { 30, 177, 116, 0 }, - { 150, 132, 215, 0 }, }, - { { 31, 21, 121, 0 }, - { 149, 212, 197, 1 }, - { 79, 84, 124, 0 }, - { 209, 149, 212, 1 }, }, - { { 27, 35, 125, 0 }, - { 180, 212, 147, 1 }, - { 95, 98, 108, 0 }, - { 228, 149, 150, 1 }, }, - { { 27, 191, 146, 0 }, - { 183, 26, 227, 0 }, - { 36, 254, 236, 0 }, - { 99, 172, 118, 1 }, }, - { { 31, 150, 154, 1 }, - { 191, 18, 228, 1 }, - { 172, 180, 252, 0 }, - { 147, 164, 126, 1 }, }, - { { 27, 72, 235, 0 }, - { 196, 222, 160, 1 }, - { 107, 137, 108, 0 }, - { 130, 189, 145, 1 }, }, - { { 32, 12, 253, 0 }, - { 17, 236, 48, 1 }, - { 95, 152, 2, 0 }, - { 134, 27, 196, 0 }, }, - { { 32, 23, 86, 0 }, - { 49, 38, 81, 0 }, - { 53, 116, 2, 0 }, - { 69, 50, 70, 0 }, }, - { { 40, 1, 153, 1 }, - { 152, 96, 33, 1 }, - { 204, 192, 10, 0 }, - { 194, 3, 12, 1 }, }, - { { 44, 50, 55, 1 }, - { 184, 226, 86, 0 }, - { 246, 38, 26, 0 }, - { 53, 35, 142, 1 }, }, - { { 37, 140, 230, 0 }, - { 3, 190, 52, 0 }, - { 51, 152, 210, 0 }, - { 22, 62, 224, 0 }, }, - { { 33, 30, 140, 0 }, - { 33, 56, 112, 1 }, - { 24, 188, 66, 0 }, - { 135, 14, 66, 0 }, }, - { { 45, 125, 105, 0 }, - { 193, 252, 71, 1 }, - { 75, 95, 90, 0 }, - { 241, 31, 193, 1 }, }, - { { 41, 192, 73, 0 }, - { 194, 116, 0, 1 }, - { 73, 1, 202, 0 }, - { 128, 23, 33, 1 }, }, - { { 38, 177, 32, 0 }, - { 2, 160, 199, 0 }, - { 2, 70, 178, 0 }, - { 113, 130, 160, 0 }, }, - { { 38, 128, 28, 1 }, - { 26, 32, 148, 1 }, - { 156, 0, 178, 0 }, - { 148, 130, 44, 0 }, }, - { { 34, 180, 26, 0 }, - { 19, 34, 194, 1 }, - { 44, 22, 162, 0 }, - { 161, 162, 100, 0 }, }, - { { 38, 42, 68, 1 }, - { 40, 44, 150, 0 }, - { 145, 42, 50, 0 }, - { 52, 154, 10, 0 }, }, - { { 38, 171, 89, 0 }, - { 50, 108, 135, 1 }, - { 77, 106, 178, 0 }, - { 240, 155, 38, 0 }, }, - { { 38, 201, 132, 0 }, - { 66, 40, 181, 0 }, - { 16, 201, 178, 0 }, - { 86, 138, 33, 0 }, }, - { { 42, 48, 98, 1 }, - { 136, 166, 194, 0 }, - { 163, 6, 42, 0 }, - { 33, 178, 136, 1 }, }, - { { 46, 50, 173, 0 }, - { 160, 224, 246, 1 }, - { 90, 166, 58, 0 }, - { 183, 131, 130, 1 }, }, - { { 35, 63, 1, 1 }, - { 41, 120, 195, 0 }, - { 192, 126, 98, 0 }, - { 97, 143, 74, 0 }, }, - { { 35, 131, 80, 0 }, - { 50, 52, 129, 0 }, - { 5, 96, 226, 0 }, - { 64, 150, 38, 0 }, }, - { { 39, 100, 46, 1 }, - { 73, 178, 150, 1 }, - { 186, 19, 114, 0 }, - { 180, 166, 201, 0 }, }, - { { 39, 212, 176, 1 }, - { 91, 176, 228, 0 }, - { 134, 149, 242, 0 }, - { 19, 134, 237, 0 }, }, - { { 47, 186, 44, 1 }, - { 170, 184, 214, 1 }, - { 154, 46, 250, 0 }, - { 181, 142, 170, 1 }, }, - { { 43, 38, 186, 1 }, - { 185, 178, 162, 1 }, - { 174, 178, 106, 0 }, - { 162, 166, 206, 1 }, }, - { { 48, 11, 141, 0 }, - { 36, 104, 49, 1 }, - { 88, 232, 6, 0 }, - { 198, 11, 18, 0 }, }, - { { 52, 38, 146, 1 }, - { 61, 34, 38, 0 }, - { 164, 178, 22, 0 }, - { 50, 34, 94, 0 }, }, - { { 48, 130, 25, 0 }, - { 54, 96, 0, 1 }, - { 76, 32, 134, 0 }, - { 128, 3, 54, 0 }, }, - { { 48, 123, 44, 1 }, - { 108, 168, 83, 1 }, - { 154, 111, 6, 0 }, - { 229, 10, 155, 0 }, }, - { { 60, 184, 59, 1 }, - { 158, 234, 70, 1 }, - { 238, 14, 158, 0 }, - { 177, 43, 188, 1 }, }, - { { 60, 38, 15, 1 }, - { 173, 98, 22, 1 }, - { 248, 50, 30, 0 }, - { 180, 35, 90, 1 }, }, - { { 56, 233, 220, 0 }, - { 214, 44, 51, 1 }, - { 29, 203, 142, 0 }, - { 230, 26, 53, 1 }, }, - { { 60, 192, 247, 0 }, - { 214, 230, 52, 0 }, - { 119, 129, 158, 0 }, - { 22, 51, 181, 1 }, }, - { { 56, 247, 84, 1 }, - { 255, 36, 83, 0 }, - { 149, 119, 142, 0 }, - { 101, 18, 127, 1 }, }, - { { 49, 5, 180, 1 }, - { 29, 176, 49, 0 }, - { 150, 208, 70, 0 }, - { 70, 6, 220, 0 }, }, - { { 53, 184, 254, 1 }, - { 30, 190, 118, 1 }, - { 191, 142, 214, 0 }, - { 183, 62, 188, 0 }, }, - { { 53, 178, 211, 1 }, - { 62, 118, 102, 0 }, - { 229, 166, 214, 0 }, - { 51, 55, 62, 0 }, }, - { { 57, 155, 206, 1 }, - { 174, 62, 113, 1 }, - { 185, 236, 206, 0 }, - { 199, 62, 58, 1 }, }, - { { 57, 109, 46, 0 }, - { 197, 186, 19, 1 }, - { 58, 91, 78, 0 }, - { 228, 46, 209, 1 }, }, - { { 61, 224, 131, 1 }, - { 206, 114, 38, 0 }, - { 224, 131, 222, 0 }, - { 50, 39, 57, 1 }, }, - { { 57, 244, 208, 0 }, - { 215, 52, 98, 0 }, - { 5, 151, 206, 0 }, - { 35, 22, 117, 1 }, }, - { { 57, 255, 32, 0 }, - { 231, 184, 67, 0 }, - { 2, 127, 206, 0 }, - { 97, 14, 243, 1 }, }, - { { 57, 199, 136, 1 }, - { 239, 48, 33, 1 }, - { 136, 241, 206, 0 }, - { 194, 6, 123, 1 }, }, - { { 54, 15, 36, 0 }, - { 37, 168, 149, 0 }, - { 18, 120, 54, 0 }, - { 84, 138, 210, 0 }, }, - { { 50, 30, 213, 0 }, - { 53, 108, 240, 0 }, - { 85, 188, 38, 0 }, - { 7, 155, 86, 0 }, }, - { { 54, 191, 209, 1 }, - { 63, 108, 231, 0 }, - { 197, 254, 182, 0 }, - { 115, 155, 126, 0 }, }, - { { 54, 70, 235, 0 }, - { 101, 230, 164, 1 }, - { 107, 177, 54, 0 }, - { 146, 179, 211, 0 }, }, - { { 58, 45, 168, 0 }, - { 133, 168, 163, 1 }, - { 10, 218, 46, 0 }, - { 226, 138, 208, 1 }, }, - { { 58, 147, 187, 0 }, - { 182, 226, 225, 1 }, - { 110, 228, 174, 0 }, - { 195, 163, 182, 1 }, }, - { { 55, 149, 173, 0 }, - { 7, 240, 245, 1 }, - { 90, 212, 246, 0 }, - { 215, 135, 240, 0 }, }, - { { 55, 4, 147, 0 }, - { 21, 114, 164, 0 }, - { 100, 144, 118, 0 }, - { 18, 167, 84, 0 }, }, - { { 51, 160, 216, 1 }, - { 30, 52, 162, 1 }, - { 141, 130, 230, 0 }, - { 162, 150, 60, 0 }, }, - { { 55, 67, 167, 0 }, - { 100, 242, 181, 0 }, - { 114, 225, 118, 0 }, - { 86, 167, 147, 0 }, }, - { { 51, 250, 92, 0 }, - { 118, 60, 210, 1 }, - { 29, 47, 230, 0 }, - { 165, 158, 55, 0 }, }, - { { 63, 131, 110, 0 }, - { 166, 182, 149, 1 }, - { 59, 96, 254, 0 }, - { 212, 182, 178, 1 }, }, - { { 63, 107, 96, 0 }, - { 228, 188, 135, 0 }, - { 3, 107, 126, 0 }, - { 112, 158, 147, 1 }, }, - { { 64, 225, 205, 0 }, - { 66, 69, 51, 1 }, - { 89, 195, 129, 0 }, - { 230, 81, 33, 0 }, }, - { { 76, 144, 234, 0 }, - { 130, 135, 100, 1 }, - { 43, 132, 153, 0 }, - { 147, 112, 160, 1 }, }, - { { 72, 154, 105, 1 }, - { 170, 205, 64, 1 }, - { 203, 44, 137, 0 }, - { 129, 89, 170, 1 }, }, - { { 76, 190, 247, 0 }, - { 179, 207, 118, 0 }, - { 119, 190, 153, 0 }, - { 55, 121, 230, 1 }, }, - { { 72, 235, 138, 1 }, - { 234, 11, 35, 1 }, - { 168, 235, 137, 0 }, - { 226, 104, 43, 1 }, }, - { { 72, 211, 7, 1 }, - { 234, 67, 81, 0 }, - { 240, 101, 137, 0 }, - { 69, 97, 43, 1 }, }, - { { 76, 90, 152, 1 }, - { 248, 9, 100, 1 }, - { 140, 173, 25, 0 }, - { 147, 72, 15, 1 }, }, - { { 65, 36, 19, 1 }, - { 25, 83, 2, 0 }, - { 228, 18, 65, 0 }, - { 32, 101, 76, 0 }, }, - { { 77, 11, 248, 0 }, - { 176, 157, 37, 1 }, - { 15, 232, 89, 0 }, - { 210, 92, 134, 1 }, }, - { { 73, 91, 189, 1 }, - { 248, 217, 113, 1 }, - { 222, 237, 73, 0 }, - { 199, 77, 143, 1 }, }, - { { 70, 141, 101, 0 }, - { 3, 205, 149, 0 }, - { 83, 88, 177, 0 }, - { 84, 217, 224, 0 }, }, - { { 66, 181, 107, 1 }, - { 11, 199, 195, 1 }, - { 235, 86, 161, 0 }, - { 225, 241, 232, 0 }, }, - { { 70, 92, 131, 0 }, - { 65, 75, 228, 0 }, - { 96, 157, 49, 0 }, - { 19, 233, 65, 0 }, }, - { { 70, 244, 103, 0 }, - { 67, 199, 214, 0 }, - { 115, 23, 177, 0 }, - { 53, 241, 225, 0 }, }, - { { 70, 121, 187, 0 }, - { 80, 203, 231, 1 }, - { 110, 207, 49, 0 }, - { 243, 233, 133, 0 }, }, - { { 74, 163, 157, 1 }, - { 186, 65, 179, 1 }, - { 220, 226, 169, 0 }, - { 230, 193, 46, 1 }, }, - { { 78, 65, 78, 1 }, - { 200, 7, 149, 1 }, - { 185, 65, 57, 0 }, - { 212, 240, 9, 1 }, }, - { { 78, 75, 194, 0 }, - { 224, 15, 165, 0 }, - { 33, 233, 57, 0 }, - { 82, 248, 3, 1 }, }, - { { 67, 137, 211, 1 }, - { 26, 95, 161, 0 }, - { 229, 200, 225, 0 }, - { 66, 253, 44, 0 }, }, - { { 71, 170, 200, 1 }, - { 42, 29, 166, 1 }, - { 137, 170, 241, 0 }, - { 178, 220, 42, 0 }, }, - { { 67, 59, 19, 0 }, - { 48, 91, 195, 0 }, - { 100, 110, 97, 0 }, - { 97, 237, 6, 0 }, }, - { { 71, 74, 105, 1 }, - { 104, 221, 132, 1 }, - { 203, 41, 113, 0 }, - { 144, 221, 139, 0 }, }, - { { 79, 22, 235, 1 }, - { 169, 215, 228, 1 }, - { 235, 180, 121, 0 }, - { 147, 245, 202, 1 }, }, - { { 79, 69, 160, 0 }, - { 193, 145, 165, 0 }, - { 2, 209, 121, 0 }, - { 82, 196, 193, 1 }, }, - { { 80, 145, 108, 1 }, - { 14, 133, 81, 1 }, - { 155, 68, 133, 0 }, - { 197, 80, 184, 0 }, }, - { { 84, 16, 187, 0 }, - { 20, 195, 100, 1 }, - { 110, 132, 21, 0 }, - { 147, 97, 148, 0 }, }, - { { 80, 90, 77, 1 }, - { 108, 77, 80, 1 }, - { 217, 45, 5, 0 }, - { 133, 89, 27, 0 }, }, - { { 80, 114, 212, 0 }, - { 116, 5, 114, 0 }, - { 21, 167, 5, 0 }, - { 39, 80, 23, 0 }, }, - { { 84, 234, 241, 1 }, - { 126, 205, 38, 0 }, - { 199, 171, 149, 0 }, - { 50, 89, 191, 0 }, }, - { { 88, 9, 195, 1 }, - { 140, 79, 33, 0 }, - { 225, 200, 13, 0 }, - { 66, 121, 24, 1 }, }, - { { 92, 89, 121, 0 }, - { 212, 205, 69, 1 }, - { 79, 77, 29, 0 }, - { 209, 89, 149, 1 }, }, - { { 88, 107, 27, 0 }, - { 244, 75, 3, 1 }, - { 108, 107, 13, 0 }, - { 224, 105, 23, 1 }, }, - { { 81, 52, 246, 1 }, - { 29, 151, 114, 0 }, - { 183, 150, 69, 0 }, - { 39, 116, 220, 0 }, }, - { { 85, 169, 118, 1 }, - { 30, 159, 23, 0 }, - { 183, 74, 213, 0 }, - { 116, 124, 188, 0 }, }, - { { 85, 151, 183, 1 }, - { 63, 211, 117, 0 }, - { 246, 244, 213, 0 }, - { 87, 101, 254, 0 }, }, - { { 85, 72, 67, 1 }, - { 76, 95, 4, 0 }, - { 225, 9, 85, 0 }, - { 16, 125, 25, 0 }, }, - { { 81, 203, 28, 0 }, - { 118, 25, 17, 1 }, - { 28, 105, 197, 0 }, - { 196, 76, 55, 0 }, }, - { { 89, 103, 105, 1 }, - { 237, 213, 3, 1 }, - { 203, 115, 77, 0 }, - { 224, 85, 219, 1 }, }, - { { 89, 87, 155, 0 }, - { 245, 83, 97, 1 }, - { 108, 245, 77, 0 }, - { 195, 101, 87, 1 }, }, - { { 86, 160, 171, 1 }, - { 14, 195, 166, 1 }, - { 234, 130, 181, 0 }, - { 178, 225, 184, 0 }, }, - { { 82, 76, 88, 0 }, - { 85, 13, 128, 1 }, - { 13, 25, 37, 0 }, - { 128, 216, 85, 0 }, }, - { { 86, 87, 68, 1 }, - { 109, 5, 213, 0 }, - { 145, 117, 53, 0 }, - { 85, 208, 91, 0 }, }, - { { 94, 1, 56, 0 }, - { 148, 129, 133, 1 }, - { 14, 64, 61, 0 }, - { 208, 192, 148, 1 }, }, - { { 90, 1, 255, 0 }, - { 148, 199, 177, 1 }, - { 127, 192, 45, 0 }, - { 198, 241, 148, 1 }, }, - { { 90, 53, 19, 1 }, - { 157, 67, 195, 0 }, - { 228, 86, 45, 0 }, - { 97, 225, 92, 1 }, }, - { { 94, 29, 215, 1 }, - { 157, 79, 245, 0 }, - { 245, 220, 61, 0 }, - { 87, 249, 92, 1 }, }, - { { 94, 146, 70, 1 }, - { 174, 7, 212, 0 }, - { 177, 36, 189, 0 }, - { 21, 240, 58, 1 }, }, - { { 90, 108, 162, 0 }, - { 197, 139, 162, 0 }, - { 34, 155, 45, 0 }, - { 34, 232, 209, 1 }, }, - { { 87, 57, 5, 0 }, - { 4, 89, 215, 0 }, - { 80, 78, 117, 0 }, - { 117, 205, 16, 0 }, }, - { { 87, 185, 238, 0 }, - { 6, 159, 247, 1 }, - { 59, 206, 245, 0 }, - { 247, 252, 176, 0 }, }, - { { 83, 27, 44, 1 }, - { 44, 153, 209, 1 }, - { 154, 108, 101, 0 }, - { 197, 204, 154, 0 }, }, - { { 87, 134, 8, 0 }, - { 39, 17, 132, 1 }, - { 8, 48, 245, 0 }, - { 144, 196, 114, 0 }, }, - { { 87, 179, 31, 1 }, - { 62, 83, 215, 1 }, - { 252, 102, 245, 0 }, - { 245, 229, 62, 0 }, }, - { { 87, 175, 125, 0 }, - { 55, 221, 151, 1 }, - { 95, 122, 245, 0 }, - { 244, 221, 246, 0 }, }, - { { 83, 240, 235, 0 }, - { 70, 215, 226, 1 }, - { 107, 135, 229, 0 }, - { 163, 245, 177, 0 }, }, - { { 91, 30, 236, 0 }, - { 165, 157, 240, 1 }, - { 27, 188, 109, 0 }, - { 135, 220, 210, 1 }, }, - { { 95, 6, 166, 1 }, - { 173, 147, 180, 0 }, - { 178, 176, 125, 0 }, - { 22, 228, 218, 1 }, }, - { { 95, 46, 87, 1 }, - { 189, 95, 150, 0 }, - { 245, 58, 125, 0 }, - { 52, 253, 94, 1 }, }, - { { 91, 89, 192, 0 }, - { 196, 29, 225, 0 }, - { 1, 205, 109, 0 }, - { 67, 220, 17, 1 }, }, - { { 91, 221, 125, 1 }, - { 223, 221, 209, 1 }, - { 223, 93, 237, 0 }, - { 197, 221, 253, 1 }, }, - { { 91, 78, 47, 0 }, - { 229, 219, 144, 1 }, - { 122, 57, 109, 0 }, - { 132, 237, 211, 1 }, }, - { { 95, 111, 137, 1 }, - { 237, 89, 167, 1 }, - { 200, 251, 125, 0 }, - { 242, 205, 91, 1 }, }, - { { 91, 99, 218, 0 }, - { 244, 23, 163, 1 }, - { 45, 227, 109, 0 }, - { 226, 244, 23, 1 }, }, - { { 96, 32, 36, 1 }, - { 8, 161, 18, 0 }, - { 146, 2, 3, 0 }, - { 36, 66, 136, 0 }, }, - { { 100, 167, 30, 1 }, - { 59, 35, 23, 1 }, - { 188, 114, 147, 0 }, - { 244, 98, 110, 0 }, }, - { { 96, 84, 84, 1 }, - { 89, 37, 80, 0 }, - { 149, 21, 3, 0 }, - { 5, 82, 77, 0 }, }, - { { 104, 166, 103, 0 }, - { 163, 231, 18, 0 }, - { 115, 50, 139, 0 }, - { 36, 115, 226, 1 }, }, - { { 108, 235, 188, 1 }, - { 250, 169, 55, 1 }, - { 158, 235, 155, 0 }, - { 246, 74, 175, 1 }, }, - { { 105, 70, 205, 1 }, - { 233, 117, 48, 1 }, - { 217, 177, 75, 0 }, - { 134, 87, 75, 1 }, }, - { { 109, 243, 169, 1 }, - { 234, 241, 103, 1 }, - { 202, 231, 219, 0 }, - { 243, 71, 171, 1 }, }, - { { 109, 94, 31, 0 }, - { 241, 123, 84, 1 }, - { 124, 61, 91, 0 }, - { 149, 111, 71, 1 }, }, - { { 102, 32, 99, 0 }, - { 0, 231, 134, 0 }, - { 99, 2, 51, 0 }, - { 48, 243, 128, 0 }, }, - { { 102, 219, 83, 1 }, - { 122, 111, 197, 0 }, - { 229, 109, 179, 0 }, - { 81, 251, 47, 0 }, }, - { { 98, 215, 16, 1 }, - { 123, 33, 193, 0 }, - { 132, 117, 163, 0 }, - { 65, 194, 111, 0 }, }, - { { 106, 165, 84, 1 }, - { 155, 37, 147, 0 }, - { 149, 82, 171, 0 }, - { 100, 210, 108, 1 }, }, - { { 110, 46, 41, 1 }, - { 169, 233, 134, 1 }, - { 202, 58, 59, 0 }, - { 176, 203, 202, 1 }, }, - { { 103, 54, 146, 0 }, - { 49, 51, 230, 0 }, - { 36, 182, 115, 0 }, - { 51, 230, 70, 0 }, }, - { { 103, 196, 202, 0 }, - { 67, 55, 164, 1 }, - { 41, 145, 243, 0 }, - { 146, 246, 97, 0 }, }, - { { 99, 83, 34, 1 }, - { 104, 179, 193, 0 }, - { 162, 101, 99, 0 }, - { 65, 230, 139, 0 }, }, - { { 107, 59, 196, 1 }, - { 168, 61, 243, 0 }, - { 145, 238, 107, 0 }, - { 103, 222, 10, 1 }, }, - { { 111, 22, 80, 1 }, - { 185, 53, 196, 0 }, - { 133, 52, 123, 0 }, - { 17, 214, 78, 1 }, }, - { { 107, 88, 37, 1 }, - { 200, 249, 208, 0 }, - { 210, 13, 107, 0 }, - { 5, 207, 137, 1 }, }, - { { 111, 99, 213, 1 }, - { 248, 117, 183, 0 }, - { 213, 227, 123, 0 }, - { 118, 215, 15, 1 }, }, - { { 112, 192, 248, 1 }, - { 94, 165, 32, 1 }, - { 143, 129, 135, 0 }, - { 130, 82, 189, 0 }, }, - { { 116, 215, 208, 0 }, - { 119, 37, 101, 0 }, - { 5, 245, 151, 0 }, - { 83, 82, 119, 0 }, }, - { { 113, 43, 110, 1 }, - { 44, 191, 19, 1 }, - { 187, 106, 71, 0 }, - { 228, 126, 154, 0 }, }, - { { 113, 121, 27, 1 }, - { 92, 123, 67, 1 }, - { 236, 79, 71, 0 }, - { 225, 111, 29, 0 }, }, - { { 113, 66, 210, 0 }, - { 116, 55, 32, 0 }, - { 37, 161, 71, 0 }, - { 2, 118, 23, 0 }, }, - { { 113, 99, 253, 0 }, - { 116, 245, 51, 1 }, - { 95, 227, 71, 0 }, - { 230, 87, 151, 0 }, }, - { { 117, 254, 122, 1 }, - { 127, 191, 70, 1 }, - { 175, 63, 215, 0 }, - { 177, 126, 255, 0 }, }, - { { 125, 152, 140, 0 }, - { 134, 57, 116, 1 }, - { 24, 140, 223, 0 }, - { 151, 78, 48, 1 }, }, - { { 125, 185, 247, 0 }, - { 150, 255, 119, 0 }, - { 119, 206, 223, 0 }, - { 119, 127, 180, 1 }, }, - { { 125, 19, 147, 1 }, - { 188, 115, 101, 0 }, - { 228, 228, 95, 0 }, - { 83, 103, 30, 1 }, }, - { { 121, 71, 103, 0 }, - { 229, 247, 17, 0 }, - { 115, 113, 79, 0 }, - { 68, 119, 211, 1 }, }, - { { 125, 223, 195, 0 }, - { 231, 127, 101, 0 }, - { 97, 253, 223, 0 }, - { 83, 127, 115, 1 }, }, - { { 121, 239, 82, 1 }, - { 255, 63, 3, 0 }, - { 165, 123, 207, 0 }, - { 96, 126, 127, 1 }, }, - { { 114, 0, 182, 0 }, - { 20, 163, 176, 0 }, - { 54, 128, 39, 0 }, - { 6, 226, 148, 0 }, }, - { { 118, 68, 152, 1 }, - { 93, 33, 164, 1 }, - { 140, 145, 55, 0 }, - { 146, 194, 93, 0 }, }, - { { 118, 122, 66, 0 }, - { 100, 47, 198, 0 }, - { 33, 47, 55, 0 }, - { 49, 250, 19, 0 }, }, - { { 126, 11, 181, 1 }, - { 188, 233, 181, 0 }, - { 214, 232, 63, 0 }, - { 86, 203, 158, 1 }, }, - { { 115, 58, 225, 1 }, - { 44, 253, 226, 0 }, - { 195, 174, 103, 0 }, - { 35, 223, 154, 0 }, }, - { { 119, 139, 59, 1 }, - { 62, 251, 133, 1 }, - { 238, 104, 247, 0 }, - { 208, 239, 190, 0 }, }, - { { 119, 93, 234, 0 }, - { 69, 191, 229, 1 }, - { 43, 221, 119, 0 }, - { 211, 254, 209, 0 }, }, - { { 127, 76, 0, 1 }, - { 205, 57, 132, 0 }, - { 128, 25, 127, 0 }, - { 16, 206, 89, 1 }, }, - { { 123, 117, 224, 1 }, - { 205, 181, 227, 0 }, - { 131, 215, 111, 0 }, - { 99, 214, 217, 1 }, }, - { { 127, 226, 186, 0 }, - { 246, 179, 166, 1 }, - { 46, 163, 255, 0 }, - { 178, 230, 183, 1 }, }, - { { 128, 176, 67, 1 }, - { 10, 70, 74, 0 }, - { 225, 6, 128, 1 }, - { 41, 49, 40, 0 }, }, - { { 128, 188, 61, 0 }, - { 19, 200, 90, 1 }, - { 94, 30, 128, 1 }, - { 173, 9, 228, 0 }, }, - { { 128, 50, 88, 0 }, - { 48, 4, 74, 1 }, - { 13, 38, 0, 1 }, - { 169, 16, 6, 0 }, }, - { { 128, 92, 26, 1 }, - { 89, 10, 72, 1 }, - { 172, 29, 0, 1 }, - { 137, 40, 77, 0 }, }, - { { 132, 251, 36, 0 }, - { 98, 136, 95, 0 }, - { 18, 111, 144, 1 }, - { 125, 8, 163, 0 }, }, - { { 128, 127, 245, 1 }, - { 121, 204, 123, 0 }, - { 215, 255, 0, 1 }, - { 111, 25, 207, 0 }, }, - { { 136, 45, 197, 1 }, - { 137, 76, 59, 0 }, - { 209, 218, 8, 1 }, - { 110, 25, 72, 1 }, }, - { { 136, 153, 224, 1 }, - { 138, 140, 105, 0 }, - { 131, 204, 136, 1 }, - { 75, 24, 168, 1 }, }, - { { 136, 25, 149, 0 }, - { 144, 72, 121, 0 }, - { 84, 204, 8, 1 }, - { 79, 9, 4, 1 }, }, - { { 133, 29, 199, 1 }, - { 9, 94, 125, 0 }, - { 241, 220, 80, 1 }, - { 95, 61, 72, 0 }, }, - { { 129, 187, 135, 0 }, - { 34, 90, 123, 0 }, - { 112, 238, 192, 1 }, - { 111, 45, 34, 0 }, }, - { { 129, 225, 96, 1 }, - { 74, 148, 11, 0 }, - { 131, 67, 192, 1 }, - { 104, 20, 169, 0 }, }, - { { 133, 126, 33, 1 }, - { 105, 216, 78, 0 }, - { 194, 63, 80, 1 }, - { 57, 13, 203, 0 }, }, - { { 129, 242, 146, 0 }, - { 114, 18, 106, 0 }, - { 36, 167, 192, 1 }, - { 43, 36, 39, 0 }, }, - { { 137, 61, 108, 1 }, - { 137, 156, 91, 1 }, - { 155, 94, 72, 1 }, - { 237, 28, 200, 1 }, }, - { { 137, 100, 178, 1 }, - { 217, 146, 42, 0 }, - { 166, 147, 72, 1 }, - { 42, 36, 205, 1 }, }, - { { 141, 212, 30, 1 }, - { 219, 18, 92, 1 }, - { 188, 21, 216, 1 }, - { 157, 36, 109, 1 }, }, - { { 137, 94, 209, 1 }, - { 249, 92, 104, 0 }, - { 197, 189, 72, 1 }, - { 11, 29, 79, 1 }, }, - { { 138, 4, 172, 0 }, - { 129, 128, 184, 1 }, - { 26, 144, 40, 1 }, - { 142, 128, 192, 1 }, }, - { { 142, 200, 22, 0 }, - { 210, 10, 156, 0 }, - { 52, 9, 184, 1 }, - { 28, 168, 37, 1 }, }, - { { 142, 242, 252, 1 }, - { 250, 132, 254, 1 }, - { 159, 167, 184, 1 }, - { 191, 144, 175, 1 }, }, - { { 131, 11, 137, 0 }, - { 32, 88, 169, 1 }, - { 72, 232, 96, 1 }, - { 202, 141, 2, 0 }, }, - { { 135, 43, 109, 0 }, - { 32, 220, 159, 1 }, - { 91, 106, 112, 1 }, - { 252, 157, 130, 0 }, }, - { { 135, 69, 49, 0 }, - { 81, 208, 141, 0 }, - { 70, 81, 112, 1 }, - { 88, 133, 197, 0 }, }, - { { 131, 108, 120, 1 }, - { 89, 156, 138, 1 }, - { 143, 27, 96, 1 }, - { 168, 156, 205, 0 }, }, - { { 135, 238, 13, 0 }, - { 99, 88, 158, 1 }, - { 88, 59, 240, 1 }, - { 188, 141, 99, 0 }, }, - { { 139, 177, 43, 1 }, - { 138, 210, 203, 1 }, - { 234, 70, 232, 1 }, - { 233, 165, 168, 1 }, }, - { { 139, 145, 216, 0 }, - { 146, 20, 233, 1 }, - { 13, 196, 232, 1 }, - { 203, 148, 36, 1 }, }, - { { 139, 89, 102, 0 }, - { 192, 158, 217, 0 }, - { 51, 77, 104, 1 }, - { 77, 188, 129, 1 }, }, - { { 143, 229, 85, 0 }, - { 211, 84, 159, 0 }, - { 85, 83, 248, 1 }, - { 124, 149, 101, 1 }, }, - { { 143, 243, 101, 1 }, - { 234, 212, 223, 0 }, - { 211, 103, 248, 1 }, - { 125, 149, 171, 1 }, }, - { { 148, 26, 21, 0 }, - { 52, 72, 92, 0 }, - { 84, 44, 20, 1 }, - { 29, 9, 22, 0 }, }, - { { 144, 225, 167, 0 }, - { 70, 194, 59, 0 }, - { 114, 195, 132, 1 }, - { 110, 33, 177, 0 }, }, - { { 144, 71, 255, 0 }, - { 117, 198, 57, 1 }, - { 127, 241, 4, 1 }, - { 206, 49, 215, 0 }, }, - { { 156, 188, 213, 0 }, - { 151, 76, 126, 0 }, - { 85, 158, 156, 1 }, - { 63, 25, 116, 1 }, }, - { { 152, 146, 244, 0 }, - { 182, 132, 120, 0 }, - { 23, 164, 140, 1 }, - { 15, 16, 182, 1 }, }, - { { 156, 83, 168, 1 }, - { 236, 128, 109, 1 }, - { 138, 229, 28, 1 }, - { 219, 0, 155, 1 }, }, - { { 152, 195, 107, 0 }, - { 230, 198, 9, 1 }, - { 107, 97, 140, 1 }, - { 200, 49, 179, 1 }, }, - { { 152, 114, 63, 1 }, - { 252, 194, 90, 1 }, - { 254, 39, 12, 1 }, - { 173, 33, 159, 1 }, }, - { { 145, 19, 213, 0 }, - { 52, 84, 121, 0 }, - { 85, 228, 68, 1 }, - { 79, 21, 22, 0 }, }, - { { 149, 191, 251, 0 }, - { 55, 222, 111, 1 }, - { 111, 254, 212, 1 }, - { 251, 61, 246, 0 }, }, - { { 149, 69, 22, 1 }, - { 93, 18, 29, 0 }, - { 180, 81, 84, 1 }, - { 92, 36, 93, 0 }, }, - { { 149, 238, 216, 0 }, - { 119, 28, 46, 1 }, - { 13, 187, 212, 1 }, - { 186, 28, 119, 0 }, }, - { { 157, 214, 4, 0 }, - { 231, 16, 92, 0 }, - { 16, 53, 220, 1 }, - { 29, 4, 115, 1 }, }, - { { 157, 98, 112, 0 }, - { 244, 148, 14, 0 }, - { 7, 35, 92, 1 }, - { 56, 20, 151, 1 }, }, - { { 150, 175, 206, 0 }, - { 39, 14, 191, 1 }, - { 57, 250, 180, 1 }, - { 254, 184, 114, 0 }, }, - { { 150, 186, 24, 1 }, - { 62, 8, 206, 1 }, - { 140, 46, 180, 1 }, - { 185, 136, 62, 0 }, }, - { { 150, 217, 61, 1 }, - { 94, 200, 221, 1 }, - { 222, 77, 180, 1 }, - { 221, 137, 189, 0 }, }, - { { 158, 33, 46, 0 }, - { 132, 130, 159, 1 }, - { 58, 66, 60, 1 }, - { 252, 160, 144, 1 }, }, - { { 154, 185, 132, 0 }, - { 134, 8, 251, 0 }, - { 16, 206, 172, 1 }, - { 111, 136, 48, 1 }, }, - { { 154, 129, 79, 1 }, - { 142, 70, 153, 1 }, - { 249, 64, 172, 1 }, - { 204, 177, 56, 1 }, }, - { { 154, 59, 54, 1 }, - { 188, 138, 219, 0 }, - { 182, 110, 44, 1 }, - { 109, 168, 158, 1 }, }, - { { 158, 195, 37, 0 }, - { 230, 192, 157, 0 }, - { 82, 97, 188, 1 }, - { 92, 129, 179, 1 }, }, - { { 147, 149, 40, 0 }, - { 7, 144, 201, 1 }, - { 10, 84, 228, 1 }, - { 201, 132, 240, 0 }, }, - { { 151, 50, 46, 1 }, - { 44, 146, 222, 1 }, - { 186, 38, 116, 1 }, - { 189, 164, 154, 0 }, }, - { { 151, 211, 152, 0 }, - { 118, 16, 237, 1 }, - { 12, 229, 244, 1 }, - { 219, 132, 55, 0 }, }, - { { 155, 157, 163, 1 }, - { 143, 218, 233, 0 }, - { 226, 220, 236, 1 }, - { 75, 173, 248, 1 }, }, - { { 159, 28, 240, 0 }, - { 149, 156, 236, 0 }, - { 7, 156, 124, 1 }, - { 27, 156, 212, 1 }, }, - { { 159, 51, 82, 1 }, - { 188, 22, 207, 0 }, - { 165, 102, 124, 1 }, - { 121, 180, 30, 1 }, }, - { { 155, 250, 202, 0 }, - { 230, 30, 234, 1 }, - { 41, 175, 236, 1 }, - { 171, 188, 51, 1 }, }, - { { 159, 250, 148, 0 }, - { 246, 24, 254, 0 }, - { 20, 175, 252, 1 }, - { 63, 140, 55, 1 }, }, - { { 160, 60, 203, 1 }, - { 9, 110, 106, 1 }, - { 233, 158, 2, 1 }, - { 171, 59, 72, 0 }, }, - { { 164, 168, 105, 0 }, - { 2, 236, 14, 1 }, - { 75, 10, 146, 1 }, - { 184, 27, 160, 0 }, }, - { { 160, 179, 245, 0 }, - { 50, 228, 123, 0 }, - { 87, 230, 130, 1 }, - { 111, 19, 166, 0 }, }, - { { 164, 106, 191, 0 }, - { 112, 234, 62, 1 }, - { 126, 171, 18, 1 }, - { 190, 43, 135, 0 }, }, - { { 160, 91, 120, 1 }, - { 120, 172, 73, 1 }, - { 143, 109, 2, 1 }, - { 201, 26, 143, 0 }, }, - { { 168, 104, 152, 1 }, - { 216, 40, 42, 1 }, - { 140, 139, 10, 1 }, - { 170, 10, 13, 1 }, }, - { { 172, 205, 54, 1 }, - { 219, 170, 29, 0 }, - { 182, 89, 154, 1 }, - { 92, 42, 237, 1 }, }, - { { 172, 220, 121, 1 }, - { 219, 236, 76, 1 }, - { 207, 29, 154, 1 }, - { 153, 27, 237, 1 }, }, - { { 161, 73, 35, 0 }, - { 64, 250, 9, 0 }, - { 98, 73, 66, 1 }, - { 72, 47, 129, 0 }, }, - { { 161, 67, 217, 0 }, - { 112, 116, 41, 1 }, - { 77, 225, 66, 1 }, - { 202, 23, 7, 0 }, }, - { { 173, 176, 209, 0 }, - { 146, 116, 110, 0 }, - { 69, 134, 218, 1 }, - { 59, 23, 36, 1 }, }, - { { 169, 155, 28, 1 }, - { 186, 56, 89, 1 }, - { 156, 108, 202, 1 }, - { 205, 14, 46, 1 }, }, - { { 169, 138, 215, 1 }, - { 186, 126, 56, 0 }, - { 245, 168, 202, 1 }, - { 14, 63, 46, 1 }, }, - { { 173, 84, 106, 1 }, - { 201, 182, 76, 1 }, - { 171, 21, 90, 1 }, - { 153, 54, 201, 1 }, }, - { { 173, 235, 81, 1 }, - { 250, 124, 15, 0 }, - { 197, 107, 218, 1 }, - { 120, 31, 47, 1 }, }, - { { 166, 29, 142, 0 }, - { 1, 42, 253, 1 }, - { 56, 220, 50, 1 }, - { 223, 170, 64, 0 }, }, - { { 166, 92, 102, 1 }, - { 73, 174, 220, 0 }, - { 179, 29, 50, 1 }, - { 29, 186, 201, 0 }, }, - { { 174, 188, 36, 0 }, - { 131, 168, 222, 0 }, - { 18, 30, 186, 1 }, - { 61, 138, 224, 1 }, }, - { { 170, 171, 48, 0 }, - { 178, 168, 139, 0 }, - { 6, 106, 170, 1 }, - { 104, 138, 166, 1 }, }, - { { 174, 204, 216, 0 }, - { 211, 44, 172, 1 }, - { 13, 153, 186, 1 }, - { 154, 154, 101, 1 }, }, - { { 170, 237, 187, 0 }, - { 211, 234, 171, 1 }, - { 110, 219, 170, 1 }, - { 234, 171, 229, 1 }, }, - { { 174, 227, 205, 1 }, - { 234, 100, 191, 1 }, - { 217, 227, 186, 1 }, - { 254, 147, 43, 1 }, }, - { { 167, 54, 8, 1 }, - { 41, 48, 206, 1 }, - { 136, 54, 114, 1 }, - { 185, 134, 74, 0 }, }, - { { 163, 236, 229, 0 }, - { 67, 252, 186, 0 }, - { 83, 155, 226, 1 }, - { 46, 159, 225, 0 }, }, - { { 163, 193, 155, 1 }, - { 90, 114, 169, 1 }, - { 236, 193, 226, 1 }, - { 202, 167, 45, 0 }, }, - { { 167, 250, 58, 0 }, - { 114, 186, 206, 1 }, - { 46, 47, 242, 1 }, - { 185, 174, 167, 0 }, }, - { { 175, 165, 105, 1 }, - { 139, 244, 143, 1 }, - { 203, 82, 250, 1 }, - { 248, 151, 232, 1 }, }, - { { 175, 200, 189, 1 }, - { 218, 248, 188, 1 }, - { 222, 137, 250, 1 }, - { 158, 143, 173, 1 }, }, - { { 175, 239, 164, 0 }, - { 227, 184, 191, 0 }, - { 18, 251, 250, 1 }, - { 126, 142, 227, 1 }, }, - { { 171, 103, 246, 0 }, - { 241, 182, 187, 0 }, - { 55, 243, 106, 1 }, - { 110, 182, 199, 1 }, }, - { { 180, 28, 82, 0 }, - { 21, 46, 76, 0 }, - { 37, 28, 22, 1 }, - { 25, 58, 84, 0 }, }, - { { 180, 63, 68, 1 }, - { 45, 44, 95, 0 }, - { 145, 126, 22, 1 }, - { 125, 26, 90, 0 }, }, - { { 180, 240, 202, 0 }, - { 70, 38, 110, 1 }, - { 41, 135, 150, 1 }, - { 187, 50, 49, 0 }, }, - { { 184, 24, 45, 0 }, - { 132, 232, 88, 1 }, - { 90, 12, 14, 1 }, - { 141, 11, 144, 1 }, }, - { { 188, 191, 136, 1 }, - { 175, 40, 111, 1 }, - { 136, 254, 158, 1 }, - { 251, 10, 122, 1 }, }, - { { 184, 142, 4, 1 }, - { 175, 40, 24, 0 }, - { 144, 56, 142, 1 }, - { 12, 10, 122, 1 }, }, - { { 188, 120, 40, 1 }, - { 204, 168, 78, 1 }, - { 138, 15, 30, 1 }, - { 185, 10, 153, 1 }, }, - { { 184, 200, 67, 1 }, - { 206, 110, 8, 0 }, - { 225, 9, 142, 1 }, - { 8, 59, 57, 1 }, }, - { { 188, 243, 60, 1 }, - { 254, 160, 95, 1 }, - { 158, 103, 158, 1 }, - { 253, 2, 191, 1 }, }, - { { 181, 19, 12, 0 }, - { 36, 48, 93, 1 }, - { 24, 100, 86, 1 }, - { 221, 6, 18, 0 }, }, - { { 177, 63, 106, 0 }, - { 37, 190, 75, 1 }, - { 43, 126, 70, 1 }, - { 233, 62, 210, 0 }, }, - { { 177, 101, 123, 1 }, - { 93, 246, 11, 1 }, - { 239, 83, 70, 1 }, - { 232, 55, 221, 0 }, }, - { { 177, 203, 237, 0 }, - { 102, 252, 57, 1 }, - { 91, 233, 198, 1 }, - { 206, 31, 179, 0 }, }, - { { 177, 194, 103, 1 }, - { 110, 246, 24, 0 }, - { 243, 33, 198, 1 }, - { 12, 55, 187, 0 }, }, - { { 177, 90, 242, 1 }, - { 124, 190, 104, 0 }, - { 167, 173, 70, 1 }, - { 11, 62, 159, 0 }, }, - { { 181, 238, 31, 1 }, - { 127, 122, 30, 1 }, - { 252, 59, 214, 1 }, - { 188, 47, 127, 0 }, }, - { { 185, 144, 112, 1 }, - { 158, 180, 72, 0 }, - { 135, 4, 206, 1 }, - { 9, 22, 188, 1 }, }, - { { 185, 163, 64, 1 }, - { 174, 52, 11, 0 }, - { 129, 98, 206, 1 }, - { 104, 22, 58, 1 }, }, - { { 185, 120, 90, 0 }, - { 212, 62, 74, 1 }, - { 45, 15, 78, 1 }, - { 169, 62, 21, 1 }, }, - { { 185, 247, 203, 0 }, - { 231, 118, 107, 1 }, - { 105, 247, 206, 1 }, - { 235, 55, 115, 1 }, }, - { { 182, 5, 1, 1 }, - { 13, 96, 141, 0 }, - { 192, 80, 54, 1 }, - { 88, 131, 88, 0 }, }, - { { 178, 255, 194, 0 }, - { 103, 46, 235, 0 }, - { 33, 255, 166, 1 }, - { 107, 186, 115, 0 }, }, - { { 178, 123, 21, 0 }, - { 116, 104, 219, 0 }, - { 84, 111, 38, 1 }, - { 109, 139, 23, 0 }, }, - { { 178, 110, 90, 1 }, - { 125, 46, 138, 1 }, - { 173, 59, 38, 1 }, - { 168, 186, 95, 0 }, }, - { { 190, 181, 243, 1 }, - { 159, 230, 239, 0 }, - { 231, 214, 190, 1 }, - { 123, 179, 252, 1 }, }, - { { 186, 167, 161, 0 }, - { 167, 224, 171, 0 }, - { 66, 242, 174, 1 }, - { 106, 131, 242, 1 }, }, - { { 186, 114, 10, 1 }, - { 236, 34, 202, 1 }, - { 168, 39, 46, 1 }, - { 169, 162, 27, 1 }, }, - { { 179, 48, 64, 0 }, - { 4, 52, 202, 0 }, - { 1, 6, 102, 1 }, - { 41, 150, 16, 0 }, }, - { { 183, 181, 52, 1 }, - { 31, 176, 223, 0 }, - { 150, 86, 246, 1 }, - { 125, 134, 252, 0 }, }, - { { 183, 105, 76, 0 }, - { 68, 60, 159, 1 }, - { 25, 75, 118, 1 }, - { 252, 158, 17, 0 }, }, - { { 183, 197, 193, 0 }, - { 71, 116, 173, 0 }, - { 65, 209, 246, 1 }, - { 90, 151, 113, 0 }, }, - { { 179, 87, 222, 0 }, - { 117, 54, 249, 1 }, - { 61, 245, 102, 1 }, - { 207, 182, 87, 0 }, }, - { { 187, 84, 251, 0 }, - { 213, 246, 232, 1 }, - { 111, 149, 110, 1 }, - { 139, 183, 213, 1 }, }, - { { 187, 202, 108, 1 }, - { 238, 188, 152, 1 }, - { 155, 41, 238, 1 }, - { 140, 158, 187, 1 }, }, - { { 187, 251, 225, 1 }, - { 238, 252, 235, 0 }, - { 195, 239, 238, 1 }, - { 107, 159, 187, 1 }, }, - { { 191, 246, 134, 1 }, - { 239, 50, 254, 0 }, - { 176, 183, 254, 1 }, - { 63, 166, 123, 1 }, }, - { { 196, 45, 124, 0 }, - { 17, 141, 31, 1 }, - { 31, 90, 17, 1 }, - { 252, 88, 196, 0 }, }, - { { 200, 28, 52, 0 }, - { 145, 137, 88, 0 }, - { 22, 28, 9, 1 }, - { 13, 72, 196, 1 }, }, - { { 204, 67, 229, 1 }, - { 232, 197, 61, 0 }, - { 211, 225, 25, 1 }, - { 94, 81, 139, 1 }, }, - { { 197, 53, 174, 1 }, - { 9, 147, 127, 1 }, - { 186, 214, 81, 1 }, - { 255, 100, 200, 0 }, }, - { { 193, 146, 192, 0 }, - { 34, 21, 104, 0 }, - { 1, 164, 193, 1 }, - { 11, 84, 34, 0 }, }, - { { 197, 35, 247, 0 }, - { 48, 215, 63, 0 }, - { 119, 226, 81, 1 }, - { 126, 117, 134, 0 }, }, - { { 193, 159, 254, 0 }, - { 51, 159, 121, 1 }, - { 63, 252, 193, 1 }, - { 207, 124, 230, 0 }, }, - { { 193, 84, 97, 0 }, - { 65, 213, 72, 0 }, - { 67, 21, 65, 1 }, - { 9, 85, 193, 0 }, }, - { { 193, 87, 130, 0 }, - { 97, 19, 105, 0 }, - { 32, 245, 65, 1 }, - { 75, 100, 67, 0 }, }, - { { 193, 199, 55, 1 }, - { 123, 211, 25, 0 }, - { 246, 113, 193, 1 }, - { 76, 101, 239, 0 }, }, - { { 201, 140, 239, 1 }, - { 139, 223, 56, 1 }, - { 251, 152, 201, 1 }, - { 142, 125, 232, 1 }, }, - { { 205, 240, 111, 0 }, - { 194, 215, 94, 1 }, - { 123, 7, 217, 1 }, - { 189, 117, 161, 1 }, }, - { { 201, 95, 103, 1 }, - { 233, 223, 89, 0 }, - { 243, 125, 73, 1 }, - { 77, 125, 203, 1 }, }, - { { 201, 119, 241, 0 }, - { 241, 213, 107, 0 }, - { 71, 247, 73, 1 }, - { 107, 85, 199, 1 }, }, - { { 205, 226, 149, 1 }, - { 250, 81, 62, 0 }, - { 212, 163, 217, 1 }, - { 62, 69, 47, 1 }, }, - { { 198, 191, 7, 1 }, - { 43, 75, 223, 0 }, - { 240, 126, 177, 1 }, - { 125, 233, 106, 0 }, }, - { { 198, 191, 184, 0 }, - { 51, 137, 239, 1 }, - { 14, 254, 177, 1 }, - { 251, 200, 230, 0 }, }, - { { 198, 219, 201, 1 }, - { 106, 77, 237, 1 }, - { 201, 237, 177, 1 }, - { 219, 217, 43, 0 }, }, - { { 202, 49, 136, 0 }, - { 128, 1, 235, 1 }, - { 8, 198, 41, 1 }, - { 235, 192, 0, 1 }, }, - { { 206, 222, 45, 1 }, - { 235, 201, 220, 1 }, - { 218, 61, 185, 1 }, - { 157, 201, 235, 1 }, }, - { { 199, 120, 44, 1 }, - { 72, 153, 222, 1 }, - { 154, 15, 113, 1 }, - { 189, 204, 137, 0 }, }, - { { 203, 171, 75, 1 }, - { 170, 95, 139, 1 }, - { 233, 106, 233, 1 }, - { 232, 253, 42, 1 }, }, - { { 207, 67, 191, 0 }, - { 240, 211, 189, 1 }, - { 126, 225, 121, 1 }, - { 222, 229, 135, 1 }, }, - { { 208, 174, 86, 1 }, - { 63, 15, 26, 0 }, - { 181, 58, 133, 1 }, - { 44, 120, 126, 0 }, }, - { { 208, 115, 51, 0 }, - { 116, 195, 75, 0 }, - { 102, 103, 5, 1 }, - { 105, 97, 151, 0 }, }, - { { 216, 20, 152, 0 }, - { 149, 1, 104, 1 }, - { 12, 148, 13, 1 }, - { 139, 64, 84, 1 }, }, - { { 220, 96, 159, 1 }, - { 220, 67, 62, 1 }, - { 252, 131, 29, 1 }, - { 190, 97, 29, 1 }, }, - { { 213, 117, 88, 0 }, - { 85, 21, 79, 1 }, - { 13, 87, 85, 1 }, - { 249, 84, 85, 0 }, }, - { { 209, 224, 215, 0 }, - { 86, 87, 58, 0 }, - { 117, 131, 197, 1 }, - { 46, 117, 53, 0 }, }, - { { 213, 251, 177, 0 }, - { 118, 217, 111, 0 }, - { 70, 239, 213, 1 }, - { 123, 77, 183, 0 }, }, - { { 213, 218, 116, 1 }, - { 126, 157, 92, 0 }, - { 151, 45, 213, 1 }, - { 29, 92, 191, 0 }, }, - { { 221, 60, 116, 1 }, - { 157, 157, 94, 0 }, - { 151, 30, 93, 1 }, - { 61, 92, 220, 1 }, }, - { { 221, 151, 109, 0 }, - { 167, 213, 93, 1 }, - { 91, 116, 221, 1 }, - { 221, 85, 242, 1 }, }, - { { 217, 27, 49, 0 }, - { 180, 217, 73, 0 }, - { 70, 108, 77, 1 }, - { 73, 77, 150, 1 }, }, - { { 217, 98, 166, 0 }, - { 228, 147, 58, 0 }, - { 50, 163, 77, 1 }, - { 46, 100, 147, 1 }, }, - { { 210, 171, 124, 1 }, - { 62, 141, 155, 1 }, - { 159, 106, 165, 1 }, - { 236, 216, 190, 0 }, }, - { { 210, 197, 236, 0 }, - { 71, 133, 185, 1 }, - { 27, 209, 165, 1 }, - { 206, 208, 241, 0 }, }, - { { 210, 202, 106, 0 }, - { 102, 143, 136, 1 }, - { 43, 41, 165, 1 }, - { 136, 248, 179, 0 }, }, - { { 210, 107, 214, 0 }, - { 116, 15, 187, 0 }, - { 53, 235, 37, 1 }, - { 110, 248, 23, 0 }, }, - { { 222, 155, 52, 0 }, - { 182, 137, 221, 0 }, - { 22, 108, 189, 1 }, - { 93, 200, 182, 1 }, }, - { { 222, 146, 158, 0 }, - { 182, 3, 252, 1 }, - { 60, 164, 189, 1 }, - { 159, 224, 54, 1 }, }, - { { 218, 96, 216, 1 }, - { 220, 5, 170, 1 }, - { 141, 131, 45, 1 }, - { 170, 208, 29, 1 }, }, - { { 215, 57, 178, 1 }, - { 28, 155, 239, 0 }, - { 166, 206, 117, 1 }, - { 123, 236, 156, 0 }, }, - { { 215, 95, 38, 0 }, - { 101, 155, 221, 0 }, - { 50, 125, 117, 1 }, - { 93, 236, 211, 0 }, }, - { { 223, 1, 65, 1 }, - { 140, 85, 141, 0 }, - { 193, 64, 125, 1 }, - { 88, 213, 24, 1 }, }, - { { 223, 121, 15, 1 }, - { 204, 91, 223, 1 }, - { 248, 79, 125, 1 }, - { 253, 237, 25, 1 }, }, - { { 219, 247, 116, 0 }, - { 247, 149, 219, 0 }, - { 23, 119, 237, 1 }, - { 109, 212, 247, 1 }, }, - { { 224, 67, 79, 1 }, - { 104, 103, 25, 1 }, - { 249, 97, 3, 1 }, - { 204, 115, 11, 0 }, }, - { { 224, 198, 227, 0 }, - { 99, 231, 40, 0 }, - { 99, 177, 131, 1 }, - { 10, 115, 227, 0 }, }, - { { 236, 56, 189, 1 }, - { 152, 233, 126, 1 }, - { 222, 142, 27, 1 }, - { 191, 75, 140, 1 }, }, - { { 236, 81, 253, 0 }, - { 208, 229, 125, 1 }, - { 95, 197, 27, 1 }, - { 223, 83, 133, 1 }, }, - { { 229, 141, 36, 0 }, - { 3, 185, 29, 0 }, - { 18, 88, 211, 1 }, - { 92, 78, 224, 0 }, }, - { { 229, 5, 121, 1 }, - { 25, 245, 13, 1 }, - { 207, 80, 83, 1 }, - { 216, 87, 204, 0 }, }, - { { 225, 78, 248, 0 }, - { 113, 189, 40, 1 }, - { 15, 185, 67, 1 }, - { 138, 94, 199, 0 }, }, - { { 229, 87, 220, 1 }, - { 121, 53, 125, 1 }, - { 157, 245, 83, 1 }, - { 223, 86, 79, 0 }, }, - { { 225, 247, 242, 1 }, - { 123, 183, 107, 0 }, - { 167, 247, 195, 1 }, - { 107, 118, 239, 0 }, }, - { { 226, 133, 77, 0 }, - { 3, 101, 153, 1 }, - { 89, 80, 163, 1 }, - { 204, 211, 96, 0 }, }, - { { 226, 24, 26, 0 }, - { 16, 43, 200, 1 }, - { 44, 12, 35, 1 }, - { 137, 234, 4, 0 }, }, - { { 226, 43, 203, 0 }, - { 32, 111, 171, 1 }, - { 105, 234, 35, 1 }, - { 234, 251, 2, 0 }, }, - { { 230, 236, 2, 0 }, - { 67, 43, 142, 0 }, - { 32, 27, 179, 1 }, - { 56, 234, 97, 0 }, }, - { { 230, 98, 129, 0 }, - { 96, 97, 174, 0 }, - { 64, 163, 51, 1 }, - { 58, 195, 3, 0 }, }, - { { 234, 38, 64, 0 }, - { 161, 37, 138, 0 }, - { 1, 50, 43, 1 }, - { 40, 210, 66, 1 }, }, - { { 234, 224, 196, 0 }, - { 194, 37, 186, 0 }, - { 17, 131, 171, 1 }, - { 46, 210, 33, 1 }, }, - { { 234, 114, 148, 1 }, - { 248, 33, 250, 0 }, - { 148, 167, 43, 1 }, - { 47, 194, 15, 1 }, }, - { { 227, 17, 63, 1 }, - { 24, 243, 217, 1 }, - { 254, 68, 99, 1 }, - { 205, 231, 140, 0 }, }, - { { 231, 11, 230, 1 }, - { 40, 191, 189, 0 }, - { 179, 232, 115, 1 }, - { 94, 254, 138, 0 }, }, - { { 227, 100, 241, 1 }, - { 89, 245, 170, 0 }, - { 199, 147, 99, 1 }, - { 42, 215, 205, 0 }, }, - { { 231, 250, 226, 1 }, - { 106, 191, 238, 0 }, - { 163, 175, 243, 1 }, - { 59, 254, 171, 0 }, }, - { { 227, 219, 243, 0 }, - { 114, 255, 233, 0 }, - { 103, 237, 227, 1 }, - { 75, 255, 167, 0 }, }, - { { 239, 135, 98, 0 }, - { 163, 183, 141, 0 }, - { 35, 112, 251, 1 }, - { 88, 246, 226, 1 }, }, - { { 239, 93, 112, 0 }, - { 209, 189, 205, 0 }, - { 7, 93, 123, 1 }, - { 89, 222, 197, 1 }, }, - { { 235, 102, 14, 0 }, - { 225, 51, 154, 1 }, - { 56, 51, 107, 1 }, - { 172, 230, 67, 1 }, }, - { { 239, 74, 60, 0 }, - { 240, 185, 156, 1 }, - { 30, 41, 123, 1 }, - { 156, 206, 135, 1 }, }, - { { 240, 178, 33, 1 }, - { 46, 225, 74, 0 }, - { 194, 38, 135, 1 }, - { 41, 67, 186, 0 }, }, - { { 240, 175, 79, 0 }, - { 39, 111, 27, 1 }, - { 121, 122, 135, 1 }, - { 236, 123, 114, 0 }, }, - { { 244, 159, 60, 1 }, - { 63, 169, 93, 1 }, - { 158, 124, 151, 1 }, - { 221, 74, 254, 0 }, }, - { { 240, 245, 4, 0 }, - { 71, 33, 91, 0 }, - { 16, 87, 135, 1 }, - { 109, 66, 113, 0 }, }, - { { 240, 242, 120, 0 }, - { 118, 165, 74, 1 }, - { 15, 39, 135, 1 }, - { 169, 82, 183, 0 }, }, - { { 248, 52, 99, 0 }, - { 133, 231, 74, 0 }, - { 99, 22, 15, 1 }, - { 41, 115, 208, 1 }, }, - { { 252, 219, 143, 0 }, - { 230, 107, 125, 1 }, - { 120, 237, 159, 1 }, - { 223, 107, 51, 1 }, }, - { { 252, 254, 77, 1 }, - { 239, 109, 94, 1 }, - { 217, 63, 159, 1 }, - { 189, 91, 123, 1 }, }, - { { 253, 176, 69, 1 }, - { 142, 117, 94, 0 }, - { 209, 6, 223, 1 }, - { 61, 87, 56, 1 }, }, - { { 253, 51, 235, 0 }, - { 164, 247, 111, 1 }, - { 107, 230, 95, 1 }, - { 251, 119, 146, 1 }, }, - { { 249, 196, 217, 1 }, - { 223, 117, 40, 1 }, - { 205, 145, 207, 1 }, - { 138, 87, 125, 1 }, }, - { { 242, 35, 145, 1 }, - { 60, 97, 171, 0 }, - { 196, 226, 39, 1 }, - { 106, 195, 30, 0 }, }, - { { 246, 210, 251, 1 }, - { 126, 231, 236, 1 }, - { 239, 165, 183, 1 }, - { 155, 243, 191, 0 }, }, - { { 254, 239, 211, 1 }, - { 255, 111, 175, 0 }, - { 229, 251, 191, 1 }, - { 122, 251, 127, 1 }, }, - { { 243, 176, 58, 1 }, - { 30, 179, 202, 1 }, - { 174, 6, 231, 1 }, - { 169, 230, 188, 0 }, }, - { { 247, 138, 77, 1 }, - { 46, 125, 156, 1 }, - { 217, 40, 247, 1 }, - { 156, 223, 58, 0 }, }, - { { 243, 143, 157, 1 }, - { 63, 121, 185, 1 }, - { 220, 248, 231, 1 }, - { 206, 207, 126, 0 }, }, - { { 247, 242, 39, 0 }, - { 102, 243, 222, 0 }, - { 114, 39, 247, 1 }, - { 61, 231, 179, 0 }, }, - { { 255, 189, 133, 1 }, - { 143, 121, 255, 0 }, - { 208, 222, 255, 1 }, - { 127, 207, 120, 1 }, }, - { { 255, 38, 197, 1 }, - { 173, 117, 190, 0 }, - { 209, 178, 127, 1 }, - { 62, 215, 90, 1 }, }, - { { 251, 224, 97, 0 }, - { 198, 245, 138, 0 }, - { 67, 3, 239, 1 }, - { 40, 215, 177, 1 }, }, - { { 255, 220, 26, 0 }, - { 215, 59, 204, 1 }, - { 44, 29, 255, 1 }, - { 153, 238, 117, 1 }, }, - { { 25, 248, 99, 1 }, - { 206, 222, 66, 0 }, - { 227, 15, 204, 0 }, - { 33, 61, 185, 1 }, }, - { { 169, 93, 31, 1 }, - { 217, 122, 89, 1 }, - { 252, 93, 74, 1 }, - { 205, 47, 77, 1 }, }, - { { 0, 184, 72, 1 }, - { 10, 12, 66, 1 }, - { 137, 14, 128, 0 }, - { 161, 24, 40, 0 }, }, - { { 0, 236, 225, 0 }, - { 67, 204, 34, 0 }, - { 67, 155, 128, 0 }, - { 34, 25, 225, 0 }, }, - { { 8, 97, 102, 0 }, - { 192, 134, 19, 0 }, - { 51, 67, 8, 0 }, - { 100, 48, 129, 1 }, }, - { { 8, 244, 131, 0 }, - { 195, 66, 98, 0 }, - { 96, 151, 136, 0 }, - { 35, 33, 97, 1 }, }, - { { 12, 248, 157, 0 }, - { 210, 72, 118, 1 }, - { 92, 143, 152, 0 }, - { 183, 9, 37, 1 }, }, - { { 8, 192, 62, 1 }, - { 218, 130, 16, 1 }, - { 190, 1, 136, 0 }, - { 132, 32, 173, 1 }, }, - { { 1, 57, 192, 0 }, - { 0, 28, 99, 0 }, - { 1, 206, 64, 0 }, - { 99, 28, 0, 0 }, }, - { { 1, 40, 10, 0 }, - { 0, 26, 2, 1 }, - { 40, 10, 64, 0 }, - { 160, 44, 0, 0 }, }, - { { 1, 190, 179, 1 }, - { 59, 218, 98, 0 }, - { 230, 190, 192, 0 }, - { 35, 45, 238, 0 }, }, - { { 13, 128, 85, 0 }, - { 146, 84, 20, 0 }, - { 85, 0, 216, 0 }, - { 20, 21, 36, 1 }, }, - { { 13, 175, 99, 1 }, - { 171, 222, 7, 0 }, - { 227, 122, 216, 0 }, - { 112, 61, 234, 1 }, }, - { { 6, 181, 236, 0 }, - { 3, 132, 247, 1 }, - { 27, 214, 176, 0 }, - { 247, 144, 224, 0 }, }, - { { 2, 153, 54, 1 }, - { 26, 138, 209, 0 }, - { 182, 76, 160, 0 }, - { 69, 168, 172, 0 }, }, - { { 6, 182, 143, 0 }, - { 35, 66, 246, 1 }, - { 120, 182, 176, 0 }, - { 183, 161, 98, 0 }, }, - { { 6, 125, 64, 1 }, - { 73, 12, 199, 0 }, - { 129, 95, 48, 0 }, - { 113, 152, 73, 0 }, }, - { { 2, 114, 104, 1 }, - { 104, 132, 194, 1 }, - { 139, 39, 32, 0 }, - { 161, 144, 139, 0 }, }, - { { 6, 203, 237, 1 }, - { 106, 204, 181, 1 }, - { 219, 233, 176, 0 }, - { 214, 153, 171, 0 }, }, - { { 14, 15, 61, 0 }, - { 177, 200, 149, 1 }, - { 94, 120, 56, 0 }, - { 212, 137, 198, 1 }, }, - { { 14, 213, 203, 0 }, - { 195, 70, 229, 1 }, - { 105, 213, 184, 0 }, - { 211, 177, 97, 1 }, }, - { { 3, 125, 39, 1 }, - { 73, 218, 211, 0 }, - { 242, 95, 96, 0 }, - { 101, 173, 201, 0 }, }, - { { 3, 233, 143, 0 }, - { 66, 90, 179, 1 }, - { 120, 203, 224, 0 }, - { 230, 173, 33, 0 }, }, - { { 7, 250, 159, 1 }, - { 122, 90, 246, 1 }, - { 252, 175, 240, 0 }, - { 183, 173, 47, 0 }, }, - { { 11, 76, 16, 0 }, - { 209, 24, 128, 0 }, - { 4, 25, 104, 0 }, - { 0, 140, 69, 1 }, }, - { { 16, 185, 87, 1 }, - { 30, 78, 83, 0 }, - { 245, 78, 132, 0 }, - { 101, 57, 60, 0 }, }, - { { 16, 22, 41, 0 }, - { 37, 192, 64, 1 }, - { 74, 52, 4, 0 }, - { 129, 1, 210, 0 }, }, - { { 16, 98, 139, 1 }, - { 108, 66, 34, 1 }, - { 232, 163, 4, 0 }, - { 162, 33, 27, 0 }, }, - { { 24, 157, 182, 0 }, - { 151, 138, 113, 0 }, - { 54, 220, 140, 0 }, - { 71, 40, 244, 1 }, }, - { { 24, 109, 0, 1 }, - { 205, 8, 3, 0 }, - { 128, 91, 12, 0 }, - { 96, 8, 89, 1 }, }, - { { 28, 118, 42, 0 }, - { 229, 130, 70, 1 }, - { 42, 55, 28, 0 }, - { 177, 32, 211, 1 }, }, - { { 24, 95, 228, 1 }, - { 237, 140, 113, 0 }, - { 147, 253, 12, 0 }, - { 71, 24, 219, 1 }, }, - { { 28, 66, 22, 0 }, - { 244, 2, 20, 0 }, - { 52, 33, 28, 0 }, - { 20, 32, 23, 1 }, }, - { { 17, 60, 169, 0 }, - { 5, 216, 98, 1 }, - { 74, 158, 68, 0 }, - { 163, 13, 208, 0 }, }, - { { 17, 152, 57, 1 }, - { 30, 216, 64, 1 }, - { 206, 12, 196, 0 }, - { 129, 13, 188, 0 }, }, - { { 21, 58, 99, 0 }, - { 36, 222, 70, 0 }, - { 99, 46, 84, 0 }, - { 49, 61, 146, 0 }, }, - { { 21, 18, 117, 1 }, - { 60, 212, 84, 0 }, - { 215, 36, 84, 0 }, - { 21, 21, 158, 0 }, }, - { { 21, 222, 162, 0 }, - { 103, 154, 100, 0 }, - { 34, 189, 212, 0 }, - { 19, 44, 243, 0 }, }, - { { 21, 106, 24, 1 }, - { 124, 24, 6, 1 }, - { 140, 43, 84, 0 }, - { 176, 12, 31, 0 }, }, - { { 29, 209, 40, 0 }, - { 198, 144, 69, 1 }, - { 10, 69, 220, 0 }, - { 209, 4, 177, 1 }, }, - { { 29, 113, 148, 1 }, - { 220, 16, 119, 0 }, - { 148, 199, 92, 0 }, - { 119, 4, 29, 1 }, }, - { { 18, 57, 139, 1 }, - { 12, 74, 227, 1 }, - { 232, 206, 36, 0 }, - { 227, 169, 24, 0 }, }, - { { 18, 5, 152, 0 }, - { 21, 0, 161, 1 }, - { 12, 208, 36, 0 }, - { 194, 128, 84, 0 }, }, - { { 22, 2, 152, 1 }, - { 60, 0, 164, 1 }, - { 140, 160, 52, 0 }, - { 146, 128, 30, 0 }, }, - { { 26, 52, 127, 1 }, - { 157, 198, 210, 1 }, - { 255, 22, 44, 0 }, - { 165, 177, 220, 1 }, }, - { { 30, 220, 239, 1 }, - { 207, 206, 244, 1 }, - { 251, 157, 188, 0 }, - { 151, 185, 249, 1 }, }, - { { 30, 112, 115, 0 }, - { 212, 198, 198, 0 }, - { 103, 7, 60, 0 }, - { 49, 177, 149, 1 }, }, - { { 26, 67, 149, 0 }, - { 244, 64, 177, 0 }, - { 84, 225, 44, 0 }, - { 70, 129, 23, 1 }, }, - { { 26, 126, 27, 1 }, - { 253, 74, 194, 1 }, - { 236, 63, 44, 0 }, - { 161, 169, 95, 1 }, }, - { { 23, 173, 194, 0 }, - { 7, 30, 167, 0 }, - { 33, 218, 244, 0 }, - { 114, 188, 112, 0 }, }, - { { 19, 25, 222, 1 }, - { 28, 30, 241, 1 }, - { 189, 204, 100, 0 }, - { 199, 188, 28, 0 }, }, - { { 27, 168, 147, 1 }, - { 158, 90, 162, 0 }, - { 228, 138, 236, 0 }, - { 34, 173, 60, 1 }, }, - { { 27, 147, 73, 1 }, - { 174, 84, 193, 1 }, - { 201, 100, 236, 0 }, - { 193, 149, 58, 1 }, }, - { { 27, 208, 27, 0 }, - { 214, 82, 192, 1 }, - { 108, 5, 236, 0 }, - { 129, 165, 53, 1 }, }, - { { 31, 66, 219, 1 }, - { 252, 86, 164, 1 }, - { 237, 161, 124, 0 }, - { 146, 181, 31, 1 }, }, - { { 32, 185, 170, 0 }, - { 2, 170, 99, 1 }, - { 42, 206, 130, 0 }, - { 227, 42, 160, 0 }, }, - { { 36, 196, 186, 0 }, - { 83, 162, 36, 1 }, - { 46, 145, 146, 0 }, - { 146, 34, 229, 0 }, }, - { { 40, 173, 5, 0 }, - { 131, 104, 19, 0 }, - { 80, 90, 138, 0 }, - { 100, 11, 96, 1 }, }, - { { 44, 35, 100, 1 }, - { 168, 164, 23, 0 }, - { 147, 98, 26, 0 }, - { 116, 18, 138, 1 }, }, - { { 44, 14, 154, 1 }, - { 185, 42, 36, 1 }, - { 172, 184, 26, 0 }, - { 146, 42, 78, 1 }, }, - { { 44, 81, 3, 1 }, - { 200, 98, 69, 0 }, - { 224, 69, 26, 0 }, - { 81, 35, 9, 1 }, }, - { { 37, 197, 207, 1 }, - { 75, 118, 53, 1 }, - { 249, 209, 210, 0 }, - { 214, 55, 105, 0 }, }, - { { 37, 200, 211, 1 }, - { 90, 126, 36, 0 }, - { 229, 137, 210, 0 }, - { 18, 63, 45, 0 }, }, - { { 33, 119, 232, 1 }, - { 105, 180, 99, 1 }, - { 139, 247, 66, 0 }, - { 227, 22, 203, 0 }, }, - { { 34, 141, 218, 1 }, - { 27, 46, 161, 1 }, - { 173, 216, 162, 0 }, - { 194, 186, 108, 0 }, }, - { { 34, 10, 238, 0 }, - { 32, 174, 176, 1 }, - { 59, 168, 34, 0 }, - { 134, 186, 130, 0 }, }, - { { 46, 134, 44, 0 }, - { 163, 160, 148, 1 }, - { 26, 48, 186, 0 }, - { 148, 130, 226, 1 }, }, - { { 42, 182, 47, 1 }, - { 171, 226, 210, 1 }, - { 250, 54, 170, 0 }, - { 165, 163, 234, 1 }, }, - { { 39, 60, 45, 0 }, - { 1, 248, 214, 1 }, - { 90, 30, 114, 0 }, - { 181, 143, 192, 0 }, }, - { { 39, 157, 74, 0 }, - { 3, 62, 197, 1 }, - { 41, 92, 242, 0 }, - { 209, 190, 96, 0 }, }, - { { 39, 155, 7, 1 }, - { 42, 122, 213, 0 }, - { 240, 108, 242, 0 }, - { 85, 175, 42, 0 }, }, - { { 35, 87, 132, 0 }, - { 97, 48, 241, 0 }, - { 16, 245, 98, 0 }, - { 71, 134, 67, 0 }, }, - { { 35, 78, 67, 1 }, - { 105, 126, 128, 0 }, - { 225, 57, 98, 0 }, - { 0, 191, 75, 0 }, }, - { { 47, 171, 185, 1 }, - { 186, 248, 167, 1 }, - { 206, 234, 250, 0 }, - { 242, 143, 174, 1 }, }, - { { 48, 176, 46, 1 }, - { 14, 162, 82, 1 }, - { 186, 6, 134, 0 }, - { 165, 34, 184, 0 }, }, - { { 48, 40, 122, 0 }, - { 20, 174, 2, 1 }, - { 47, 10, 6, 0 }, - { 160, 58, 148, 0 }, }, - { { 52, 253, 160, 1 }, - { 79, 168, 103, 0 }, - { 130, 223, 150, 0 }, - { 115, 10, 249, 0 }, }, - { { 60, 214, 137, 0 }, - { 231, 96, 100, 1 }, - { 72, 181, 158, 0 }, - { 147, 3, 115, 1 }, }, - { { 56, 126, 182, 0 }, - { 245, 170, 114, 0 }, - { 54, 191, 14, 0 }, - { 39, 42, 215, 1 }, }, - { { 49, 0, 109, 0 }, - { 4, 244, 16, 1 }, - { 91, 0, 70, 0 }, - { 132, 23, 144, 0 }, }, - { { 49, 23, 219, 1 }, - { 61, 118, 97, 1 }, - { 237, 244, 70, 0 }, - { 195, 55, 94, 0 }, }, - { { 49, 167, 119, 1 }, - { 63, 246, 19, 0 }, - { 247, 114, 198, 0 }, - { 100, 55, 254, 0 }, }, - { { 61, 128, 10, 1 }, - { 142, 50, 4, 1 }, - { 168, 0, 222, 0 }, - { 144, 38, 56, 1 }, }, - { { 61, 226, 157, 0 }, - { 246, 112, 54, 1 }, - { 92, 163, 222, 0 }, - { 182, 7, 55, 1 }, }, - { { 54, 106, 119, 1 }, - { 124, 238, 150, 0 }, - { 247, 43, 54, 0 }, - { 52, 187, 159, 0 }, }, - { { 54, 199, 190, 1 }, - { 127, 162, 181, 1 }, - { 190, 241, 182, 0 }, - { 214, 162, 255, 0 }, }, - { { 62, 187, 41, 0 }, - { 166, 232, 199, 1 }, - { 74, 110, 190, 0 }, - { 241, 139, 178, 1 }, }, - { { 62, 104, 154, 0 }, - { 212, 42, 166, 1 }, - { 44, 139, 62, 0 }, - { 178, 170, 21, 1 }, }, - { { 62, 95, 2, 1 }, - { 237, 42, 197, 0 }, - { 160, 125, 62, 0 }, - { 81, 170, 91, 1 }, }, - { { 58, 86, 206, 1 }, - { 237, 38, 240, 1 }, - { 185, 181, 46, 0 }, - { 135, 178, 91, 1 }, }, - { { 62, 95, 252, 0 }, - { 245, 172, 245, 1 }, - { 31, 253, 62, 0 }, - { 215, 154, 215, 1 }, }, - { { 51, 185, 172, 0 }, - { 6, 184, 243, 1 }, - { 26, 206, 230, 0 }, - { 231, 142, 176, 0 }, }, - { { 51, 84, 174, 0 }, - { 69, 178, 240, 1 }, - { 58, 149, 102, 0 }, - { 135, 166, 209, 0 }, }, - { { 63, 25, 160, 0 }, - { 132, 184, 229, 0 }, - { 2, 204, 126, 0 }, - { 83, 142, 144, 1 }, }, - { { 59, 173, 97, 1 }, - { 143, 252, 131, 0 }, - { 195, 90, 238, 0 }, - { 96, 159, 248, 1 }, }, - { { 59, 112, 47, 1 }, - { 204, 242, 210, 1 }, - { 250, 7, 110, 0 }, - { 165, 167, 153, 1 }, }, - { { 59, 235, 58, 1 }, - { 254, 186, 131, 1 }, - { 174, 107, 238, 0 }, - { 224, 174, 191, 1 }, }, - { { 64, 83, 90, 0 }, - { 112, 7, 65, 1 }, - { 45, 101, 1, 0 }, - { 193, 112, 7, 0 }, }, - { { 65, 152, 22, 0 }, - { 18, 27, 80, 0 }, - { 52, 12, 193, 0 }, - { 5, 108, 36, 0 }, }, - { { 65, 143, 177, 0 }, - { 51, 217, 33, 0 }, - { 70, 248, 193, 0 }, - { 66, 77, 230, 0 }, }, - { { 69, 217, 33, 1 }, - { 74, 217, 69, 0 }, - { 194, 77, 209, 0 }, - { 81, 77, 169, 0 }, }, - { { 69, 235, 1, 0 }, - { 98, 89, 7, 0 }, - { 64, 107, 209, 0 }, - { 112, 77, 35, 0 }, }, - { { 73, 157, 82, 0 }, - { 147, 31, 65, 0 }, - { 37, 92, 201, 0 }, - { 65, 124, 100, 1 }, }, - { { 66, 56, 79, 1 }, - { 8, 79, 210, 1 }, - { 249, 14, 33, 0 }, - { 165, 249, 8, 0 }, }, - { { 74, 166, 163, 1 }, - { 171, 195, 162, 0 }, - { 226, 178, 169, 0 }, - { 34, 225, 234, 1 }, }, - { { 74, 197, 42, 1 }, - { 203, 131, 129, 1 }, - { 170, 81, 169, 0 }, - { 192, 224, 233, 1 }, }, - { { 74, 200, 115, 0 }, - { 210, 207, 128, 0 }, - { 103, 9, 169, 0 }, - { 0, 249, 165, 1 }, }, - { { 71, 180, 198, 1 }, - { 11, 23, 246, 0 }, - { 177, 150, 241, 0 }, - { 55, 244, 104, 0 }, }, - { { 67, 147, 93, 0 }, - { 50, 85, 209, 1 }, - { 93, 100, 225, 0 }, - { 197, 213, 38, 0 }, }, - { { 67, 120, 117, 0 }, - { 80, 221, 210, 0 }, - { 87, 15, 97, 0 }, - { 37, 221, 133, 0 }, }, - { { 79, 58, 175, 1 }, - { 168, 219, 246, 1 }, - { 250, 174, 121, 0 }, - { 183, 237, 138, 1 }, }, - { { 79, 135, 68, 1 }, - { 171, 21, 149, 0 }, - { 145, 112, 249, 0 }, - { 84, 212, 106, 1 }, }, - { { 79, 135, 223, 0 }, - { 179, 87, 181, 1 }, - { 125, 240, 249, 0 }, - { 214, 245, 102, 1 }, }, - { { 79, 196, 222, 1 }, - { 219, 23, 180, 1 }, - { 189, 145, 249, 0 }, - { 150, 244, 109, 1 }, }, - { { 84, 31, 71, 0 }, - { 37, 79, 85, 0 }, - { 113, 124, 21, 0 }, - { 85, 121, 82, 0 }, }, - { { 80, 31, 148, 0 }, - { 53, 9, 113, 0 }, - { 20, 252, 5, 0 }, - { 71, 72, 86, 0 }, }, - { { 92, 145, 67, 0 }, - { 134, 71, 69, 0 }, - { 97, 68, 157, 0 }, - { 81, 113, 48, 1 }, }, - { { 88, 189, 239, 1 }, - { 143, 207, 115, 1 }, - { 251, 222, 141, 0 }, - { 231, 121, 248, 1 }, }, - { { 92, 221, 7, 1 }, - { 207, 75, 85, 0 }, - { 240, 93, 157, 0 }, - { 85, 105, 121, 1 }, }, - { { 92, 102, 88, 1 }, - { 253, 5, 6, 1 }, - { 141, 51, 29, 0 }, - { 176, 80, 95, 1 }, }, - { { 92, 219, 50, 1 }, - { 254, 139, 69, 0 }, - { 166, 109, 157, 0 }, - { 81, 104, 191, 1 }, }, - { { 85, 69, 212, 0 }, - { 85, 21, 53, 0 }, - { 21, 209, 85, 0 }, - { 86, 84, 85, 0 }, }, - { { 81, 68, 62, 0 }, - { 85, 147, 16, 1 }, - { 62, 17, 69, 0 }, - { 132, 100, 213, 0 }, }, - { { 81, 231, 230, 0 }, - { 103, 151, 51, 0 }, - { 51, 243, 197, 0 }, - { 102, 116, 243, 0 }, }, - { { 89, 11, 167, 0 }, - { 164, 219, 49, 0 }, - { 114, 232, 77, 0 }, - { 70, 109, 146, 1 }, }, - { { 93, 186, 56, 1 }, - { 190, 153, 70, 1 }, - { 142, 46, 221, 0 }, - { 177, 76, 190, 1 }, }, - { { 93, 166, 55, 1 }, - { 191, 211, 22, 0 }, - { 246, 50, 221, 0 }, - { 52, 101, 254, 1 }, }, - { { 89, 249, 149, 0 }, - { 214, 89, 115, 0 }, - { 84, 207, 205, 0 }, - { 103, 77, 53, 1 }, }, - { { 93, 255, 236, 0 }, - { 231, 157, 119, 1 }, - { 27, 255, 221, 0 }, - { 247, 92, 243, 1 }, }, - { { 82, 157, 58, 0 }, - { 23, 139, 193, 1 }, - { 46, 92, 165, 0 }, - { 193, 232, 244, 0 }, }, - { { 86, 58, 151, 0 }, - { 52, 75, 246, 0 }, - { 116, 174, 53, 0 }, - { 55, 233, 22, 0 }, }, - { { 82, 99, 34, 0 }, - { 100, 131, 131, 0 }, - { 34, 99, 37, 0 }, - { 96, 224, 147, 0 }, }, - { { 82, 203, 49, 1 }, - { 126, 201, 129, 0 }, - { 198, 105, 165, 0 }, - { 64, 201, 191, 0 }, }, - { { 90, 139, 204, 1 }, - { 174, 13, 177, 1 }, - { 153, 232, 173, 0 }, - { 198, 216, 58, 1 }, }, - { { 83, 33, 106, 0 }, - { 4, 151, 131, 1 }, - { 43, 66, 101, 0 }, - { 224, 244, 144, 0 }, }, - { { 87, 245, 17, 0 }, - { 87, 81, 199, 0 }, - { 68, 87, 245, 0 }, - { 113, 197, 117, 0 }, }, - { { 95, 220, 33, 0 }, - { 199, 217, 196, 0 }, - { 66, 29, 253, 0 }, - { 17, 205, 241, 1 }, }, - { { 96, 140, 28, 0 }, - { 19, 41, 16, 1 }, - { 28, 24, 131, 0 }, - { 132, 74, 100, 0 }, }, - { { 96, 162, 213, 1 }, - { 58, 101, 50, 0 }, - { 213, 162, 131, 0 }, - { 38, 83, 46, 0 }, }, - { { 96, 222, 5, 1 }, - { 107, 105, 80, 0 }, - { 208, 61, 131, 0 }, - { 5, 75, 107, 0 }, }, - { { 108, 76, 110, 0 }, - { 193, 175, 20, 1 }, - { 59, 25, 27, 0 }, - { 148, 122, 193, 1 }, }, - { { 104, 82, 12, 0 }, - { 224, 33, 80, 1 }, - { 24, 37, 11, 0 }, - { 133, 66, 3, 1 }, }, - { { 101, 59, 167, 0 }, - { 32, 251, 119, 0 }, - { 114, 238, 83, 0 }, - { 119, 111, 130, 0 }, }, - { { 97, 170, 234, 0 }, - { 34, 191, 34, 1 }, - { 43, 170, 195, 0 }, - { 162, 126, 162, 0 }, }, - { { 101, 113, 110, 1 }, - { 72, 183, 87, 1 }, - { 187, 71, 83, 0 }, - { 245, 118, 137, 0 }, }, - { { 101, 124, 89, 1 }, - { 89, 125, 70, 1 }, - { 205, 31, 83, 0 }, - { 177, 95, 77, 0 }, }, - { { 105, 131, 46, 1 }, - { 170, 179, 17, 1 }, - { 186, 96, 203, 0 }, - { 196, 102, 170, 1 }, }, - { { 109, 196, 12, 0 }, - { 195, 49, 20, 1 }, - { 24, 17, 219, 0 }, - { 148, 70, 97, 1 }, }, - { { 109, 210, 20, 1 }, - { 250, 49, 84, 0 }, - { 148, 37, 219, 0 }, - { 21, 70, 47, 1 }, }, - { { 98, 150, 165, 1 }, - { 43, 225, 240, 0 }, - { 210, 180, 163, 0 }, - { 7, 195, 234, 0 }, }, - { { 98, 117, 38, 1 }, - { 73, 163, 211, 0 }, - { 178, 87, 35, 0 }, - { 101, 226, 201, 0 }, }, - { { 98, 100, 214, 1 }, - { 89, 39, 178, 0 }, - { 181, 147, 35, 0 }, - { 38, 242, 77, 0 }, }, - { { 98, 91, 220, 1 }, - { 120, 45, 241, 1 }, - { 157, 237, 35, 0 }, - { 199, 218, 15, 0 }, }, - { { 110, 51, 2, 1 }, - { 168, 35, 199, 0 }, - { 160, 102, 59, 0 }, - { 113, 226, 10, 1 }, }, - { { 110, 90, 137, 0 }, - { 224, 105, 228, 1 }, - { 72, 173, 59, 0 }, - { 147, 203, 3, 1 }, }, - { { 99, 209, 212, 0 }, - { 82, 53, 241, 0 }, - { 21, 197, 227, 0 }, - { 71, 214, 37, 0 }, }, - { { 99, 75, 129, 1 }, - { 104, 121, 161, 0 }, - { 192, 233, 99, 0 }, - { 66, 207, 11, 0 }, }, - { { 107, 56, 145, 1 }, - { 152, 121, 226, 0 }, - { 196, 142, 107, 0 }, - { 35, 207, 12, 1 }, }, - { { 107, 122, 42, 0 }, - { 224, 187, 194, 1 }, - { 42, 47, 107, 0 }, - { 161, 238, 131, 1 }, }, - { { 116, 56, 51, 0 }, - { 20, 235, 70, 0 }, - { 102, 14, 23, 0 }, - { 49, 107, 148, 0 }, }, - { { 112, 6, 44, 0 }, - { 37, 161, 16, 1 }, - { 26, 48, 7, 0 }, - { 132, 66, 210, 0 }, }, - { { 116, 170, 141, 1 }, - { 46, 105, 54, 1 }, - { 216, 170, 151, 0 }, - { 182, 75, 58, 0 }, }, - { { 116, 76, 143, 0 }, - { 69, 107, 52, 1 }, - { 120, 153, 23, 0 }, - { 150, 107, 81, 0 }, }, - { { 124, 183, 167, 0 }, - { 167, 227, 119, 0 }, - { 114, 246, 159, 0 }, - { 119, 99, 242, 1 }, }, - { { 120, 3, 22, 1 }, - { 188, 35, 17, 0 }, - { 180, 96, 15, 0 }, - { 68, 98, 30, 1 }, }, - { { 124, 199, 198, 1 }, - { 239, 39, 53, 0 }, - { 177, 241, 159, 0 }, - { 86, 114, 123, 1 }, }, - { { 117, 182, 4, 1 }, - { 47, 49, 86, 0 }, - { 144, 54, 215, 0 }, - { 53, 70, 122, 0 }, }, - { { 113, 254, 101, 0 }, - { 103, 253, 82, 0 }, - { 83, 63, 199, 0 }, - { 37, 95, 243, 0 }, }, - { { 113, 79, 154, 1 }, - { 125, 59, 33, 1 }, - { 172, 249, 71, 0 }, - { 194, 110, 95, 0 }, }, - { { 121, 32, 119, 0 }, - { 148, 247, 18, 0 }, - { 119, 2, 79, 0 }, - { 36, 119, 148, 1 }, }, - { { 125, 121, 205, 0 }, - { 196, 125, 119, 1 }, - { 89, 207, 95, 0 }, - { 247, 95, 17, 1 }, }, - { { 125, 67, 92, 0 }, - { 244, 53, 21, 1 }, - { 29, 97, 95, 0 }, - { 212, 86, 23, 1 }, }, - { { 125, 127, 59, 0 }, - { 245, 251, 71, 1 }, - { 110, 127, 95, 0 }, - { 241, 111, 215, 1 }, }, - { { 118, 85, 8, 0 }, - { 69, 33, 197, 1 }, - { 8, 85, 55, 0 }, - { 209, 194, 81, 0 }, }, - { { 122, 152, 199, 0 }, - { 134, 111, 240, 0 }, - { 113, 140, 175, 0 }, - { 7, 251, 48, 1 }, }, - { { 126, 19, 192, 0 }, - { 164, 37, 229, 0 }, - { 1, 228, 63, 0 }, - { 83, 210, 18, 1 }, }, - { { 126, 94, 89, 1 }, - { 253, 109, 196, 1 }, - { 205, 61, 63, 0 }, - { 145, 219, 95, 1 }, }, - { { 115, 36, 132, 0 }, - { 5, 49, 178, 0 }, - { 16, 146, 103, 0 }, - { 38, 198, 80, 0 }, }, - { { 115, 46, 39, 1 }, - { 45, 251, 146, 0 }, - { 242, 58, 103, 0 }, - { 36, 239, 218, 0 }, }, - { { 115, 139, 84, 1 }, - { 62, 61, 145, 0 }, - { 149, 104, 231, 0 }, - { 68, 222, 62, 0 }, }, - { { 132, 4, 34, 0 }, - { 1, 130, 12, 0 }, - { 34, 16, 16, 1 }, - { 24, 32, 192, 0 }, }, - { { 128, 232, 46, 0 }, - { 66, 138, 26, 1 }, - { 58, 11, 128, 1 }, - { 172, 40, 161, 0 }, }, - { { 128, 237, 48, 0 }, - { 83, 136, 11, 0 }, - { 6, 91, 128, 1 }, - { 104, 8, 229, 0 }, }, - { { 140, 53, 96, 0 }, - { 129, 132, 79, 0 }, - { 3, 86, 24, 1 }, - { 121, 16, 192, 1 }, }, - { { 140, 120, 240, 1 }, - { 216, 140, 110, 0 }, - { 135, 143, 24, 1 }, - { 59, 24, 141, 1 }, }, - { { 136, 194, 128, 1 }, - { 234, 0, 40, 0 }, - { 128, 161, 136, 1 }, - { 10, 0, 43, 1 }, }, - { { 129, 26, 100, 1 }, - { 40, 156, 88, 0 }, - { 147, 44, 64, 1 }, - { 13, 28, 138, 0 }, }, - { { 129, 38, 163, 0 }, - { 33, 210, 42, 0 }, - { 98, 178, 64, 1 }, - { 42, 37, 194, 0 }, }, - { { 133, 154, 140, 1 }, - { 42, 24, 124, 1 }, - { 152, 172, 208, 1 }, - { 159, 12, 42, 0 }, }, - { { 137, 19, 23, 1 }, - { 184, 82, 89, 0 }, - { 244, 100, 72, 1 }, - { 77, 37, 14, 1 }, }, - { { 130, 9, 37, 0 }, - { 0, 200, 153, 0 }, - { 82, 72, 32, 1 }, - { 76, 137, 128, 0 }, }, - { { 130, 183, 201, 1 }, - { 43, 68, 235, 1 }, - { 201, 246, 160, 1 }, - { 235, 145, 106, 0 }, }, - { { 142, 185, 191, 1 }, - { 154, 202, 255, 1 }, - { 254, 206, 184, 1 }, - { 255, 169, 172, 1 }, }, - { { 138, 159, 236, 0 }, - { 163, 140, 249, 1 }, - { 27, 252, 168, 1 }, - { 207, 152, 226, 1 }, }, - { { 142, 228, 226, 0 }, - { 195, 134, 174, 0 }, - { 35, 147, 184, 1 }, - { 58, 176, 225, 1 }, }, - { { 135, 32, 232, 0 }, - { 0, 148, 174, 1 }, - { 11, 130, 112, 1 }, - { 186, 148, 128, 0 }, }, - { { 135, 205, 96, 1 }, - { 75, 156, 141, 0 }, - { 131, 89, 240, 1 }, - { 88, 156, 233, 0 }, }, - { { 135, 71, 172, 1 }, - { 105, 144, 189, 1 }, - { 154, 241, 112, 1 }, - { 222, 132, 203, 0 }, }, - { { 139, 10, 91, 0 }, - { 176, 94, 136, 1 }, - { 109, 40, 104, 1 }, - { 136, 189, 6, 1 }, }, - { { 139, 92, 190, 1 }, - { 217, 154, 248, 1 }, - { 190, 157, 104, 1 }, - { 143, 172, 205, 1 }, }, - { { 143, 213, 180, 0 }, - { 211, 144, 253, 0 }, - { 22, 213, 248, 1 }, - { 95, 132, 229, 1 }, }, - { { 139, 118, 41, 0 }, - { 225, 208, 202, 1 }, - { 74, 55, 104, 1 }, - { 169, 133, 195, 1 }, }, - { { 148, 51, 175, 0 }, - { 36, 194, 127, 1 }, - { 122, 230, 20, 1 }, - { 255, 33, 146, 0 }, }, - { { 144, 85, 72, 1 }, - { 77, 4, 73, 1 }, - { 137, 85, 4, 1 }, - { 201, 16, 89, 0 }, }, - { { 156, 62, 39, 0 }, - { 165, 202, 94, 0 }, - { 114, 62, 28, 1 }, - { 61, 41, 210, 1 }, }, - { { 152, 151, 159, 0 }, - { 183, 66, 121, 1 }, - { 124, 244, 140, 1 }, - { 207, 33, 118, 1 }, }, - { { 152, 88, 144, 1 }, - { 220, 8, 104, 0 }, - { 132, 141, 12, 1 }, - { 11, 8, 29, 1 }, }, - { { 149, 182, 31, 0 }, - { 55, 82, 94, 1 }, - { 124, 54, 212, 1 }, - { 189, 37, 118, 0 }, }, - { { 145, 64, 55, 0 }, - { 84, 210, 24, 0 }, - { 118, 1, 68, 1 }, - { 12, 37, 149, 0 }, }, - { { 157, 179, 128, 0 }, - { 166, 16, 111, 0 }, - { 0, 230, 220, 1 }, - { 123, 4, 50, 1 }, }, - { { 153, 65, 137, 1 }, - { 204, 80, 41, 1 }, - { 200, 193, 76, 1 }, - { 202, 5, 25, 1 }, }, - { { 146, 173, 38, 0 }, - { 7, 138, 155, 0 }, - { 50, 90, 164, 1 }, - { 108, 168, 240, 0 }, }, - { { 146, 101, 228, 1 }, - { 77, 132, 187, 0 }, - { 147, 211, 36, 1 }, - { 110, 144, 217, 0 }, }, - { { 146, 111, 141, 1 }, - { 109, 72, 187, 1 }, - { 216, 251, 36, 1 }, - { 238, 137, 91, 0 }, }, - { { 150, 94, 153, 0 }, - { 117, 72, 236, 1 }, - { 76, 189, 52, 1 }, - { 155, 137, 87, 0 }, }, - { { 154, 11, 77, 0 }, - { 164, 76, 153, 1 }, - { 89, 104, 44, 1 }, - { 204, 153, 18, 1 }, }, - { { 154, 221, 17, 0 }, - { 215, 72, 201, 0 }, - { 68, 93, 172, 1 }, - { 73, 137, 117, 1 }, }, - { { 147, 246, 156, 1 }, - { 127, 16, 250, 1 }, - { 156, 183, 228, 1 }, - { 175, 132, 127, 0 }, }, - { { 159, 52, 189, 1 }, - { 157, 208, 254, 1 }, - { 222, 150, 124, 1 }, - { 191, 133, 220, 1 }, }, - { { 159, 80, 9, 0 }, - { 196, 80, 204, 1 }, - { 72, 5, 124, 1 }, - { 153, 133, 17, 1 }, }, - { { 160, 153, 27, 1 }, - { 26, 106, 73, 1 }, - { 236, 76, 130, 1 }, - { 201, 43, 44, 0 }, }, - { { 164, 15, 234, 0 }, - { 33, 174, 45, 1 }, - { 43, 248, 18, 1 }, - { 218, 58, 194, 0 }, }, - { { 160, 15, 83, 1 }, - { 57, 110, 9, 0 }, - { 229, 120, 2, 1 }, - { 72, 59, 78, 0 }, }, - { { 168, 2, 105, 1 }, - { 168, 228, 8, 1 }, - { 203, 32, 10, 1 }, - { 136, 19, 138, 1 }, }, - { { 172, 27, 205, 1 }, - { 168, 108, 125, 1 }, - { 217, 236, 26, 1 }, - { 223, 27, 10, 1 }, }, - { { 172, 135, 77, 0 }, - { 163, 100, 29, 1 }, - { 89, 112, 154, 1 }, - { 220, 19, 98, 1 }, }, - { { 172, 118, 200, 0 }, - { 225, 36, 110, 1 }, - { 9, 183, 26, 1 }, - { 187, 18, 67, 1 }, }, - { { 165, 68, 167, 0 }, - { 65, 242, 60, 0 }, - { 114, 145, 82, 1 }, - { 30, 39, 193, 0 }, }, - { { 161, 244, 92, 0 }, - { 83, 52, 90, 1 }, - { 29, 23, 194, 1 }, - { 173, 22, 101, 0 }, }, - { { 169, 157, 169, 0 }, - { 131, 248, 105, 1 }, - { 74, 220, 202, 1 }, - { 203, 15, 224, 1 }, }, - { { 169, 135, 255, 0 }, - { 179, 246, 57, 1 }, - { 127, 240, 202, 1 }, - { 206, 55, 230, 1 }, }, - { { 169, 71, 98, 1 }, - { 233, 182, 9, 0 }, - { 163, 113, 74, 1 }, - { 72, 54, 203, 1 }, }, - { { 166, 129, 114, 1 }, - { 26, 166, 141, 0 }, - { 167, 64, 178, 1 }, - { 88, 178, 172, 0 }, }, - { { 166, 249, 172, 1 }, - { 74, 168, 255, 1 }, - { 154, 207, 178, 1 }, - { 255, 138, 169, 0 }, }, - { { 162, 196, 38, 0 }, - { 67, 162, 152, 0 }, - { 50, 17, 162, 1 }, - { 12, 162, 225, 0 }, }, - { { 170, 53, 101, 1 }, - { 137, 228, 219, 0 }, - { 211, 86, 42, 1 }, - { 109, 147, 200, 1 }, }, - { { 170, 168, 102, 0 }, - { 130, 174, 154, 0 }, - { 51, 10, 170, 1 }, - { 44, 186, 160, 1 }, }, - { { 170, 200, 143, 0 }, - { 194, 106, 184, 1 }, - { 120, 137, 170, 1 }, - { 142, 171, 33, 1 }, }, - { { 167, 110, 148, 1 }, - { 121, 56, 190, 0 }, - { 148, 187, 114, 1 }, - { 62, 142, 79, 0 }, }, - { { 175, 4, 36, 1 }, - { 137, 176, 156, 0 }, - { 146, 16, 122, 1 }, - { 28, 134, 200, 1 }, }, - { { 176, 65, 44, 0 }, - { 68, 160, 25, 1 }, - { 26, 65, 6, 1 }, - { 204, 2, 145, 0 }, }, - { { 176, 208, 227, 0 }, - { 70, 230, 104, 0 }, - { 99, 133, 134, 1 }, - { 11, 51, 177, 0 }, }, - { { 176, 237, 129, 1 }, - { 79, 104, 43, 0 }, - { 192, 219, 134, 1 }, - { 106, 11, 121, 0 }, }, - { { 180, 231, 236, 0 }, - { 103, 164, 63, 1 }, - { 27, 243, 150, 1 }, - { 254, 18, 243, 0 }, }, - { { 184, 160, 236, 1 }, - { 142, 164, 58, 1 }, - { 155, 130, 142, 1 }, - { 174, 18, 184, 1 }, }, - { { 184, 188, 60, 1 }, - { 159, 168, 90, 1 }, - { 158, 30, 142, 1 }, - { 173, 10, 252, 1 }, }, - { { 177, 102, 224, 1 }, - { 109, 180, 42, 0 }, - { 131, 179, 70, 1 }, - { 42, 22, 219, 0 }, }, - { { 189, 49, 10, 1 }, - { 140, 50, 79, 1 }, - { 168, 70, 94, 1 }, - { 249, 38, 24, 1 }, }, - { { 189, 63, 186, 1 }, - { 189, 186, 111, 1 }, - { 174, 254, 94, 1 }, - { 251, 46, 222, 1 }, }, - { { 185, 190, 220, 0 }, - { 183, 60, 122, 1 }, - { 29, 190, 206, 1 }, - { 175, 30, 118, 1 }, }, - { { 178, 58, 240, 1 }, - { 60, 172, 234, 0 }, - { 135, 174, 38, 1 }, - { 43, 154, 158, 0 }, }, - { { 182, 207, 169, 0 }, - { 103, 232, 173, 1 }, - { 74, 249, 182, 1 }, - { 218, 139, 243, 0 }, }, - { { 186, 35, 226, 1 }, - { 172, 166, 171, 0 }, - { 163, 226, 46, 1 }, - { 106, 178, 154, 1 }, }, - { { 183, 36, 171, 1 }, - { 13, 242, 174, 1 }, - { 234, 146, 118, 1 }, - { 186, 167, 216, 0 }, }, - { { 183, 41, 251, 0 }, - { 20, 254, 175, 1 }, - { 111, 202, 118, 1 }, - { 250, 191, 148, 0 }, }, - { { 179, 174, 8, 0 }, - { 39, 56, 138, 1 }, - { 8, 58, 230, 1 }, - { 168, 142, 114, 0 }, }, - { { 183, 86, 202, 1 }, - { 109, 54, 236, 1 }, - { 169, 181, 118, 1 }, - { 155, 182, 91, 0 }, }, - { { 191, 168, 244, 0 }, - { 150, 188, 190, 0 }, - { 23, 138, 254, 1 }, - { 62, 158, 180, 1 }, }, - { { 191, 148, 18, 1 }, - { 159, 50, 204, 0 }, - { 164, 20, 254, 1 }, - { 25, 166, 124, 1 }, }, - { { 191, 122, 64, 1 }, - { 236, 60, 206, 0 }, - { 129, 47, 126, 1 }, - { 57, 158, 27, 1 }, }, - { { 196, 133, 200, 0 }, - { 3, 5, 45, 1 }, - { 9, 208, 145, 1 }, - { 218, 80, 96, 0 }, }, - { { 192, 14, 9, 1 }, - { 41, 73, 8, 1 }, - { 200, 56, 1, 1 }, - { 136, 73, 74, 0 }, }, - { { 192, 112, 254, 0 }, - { 80, 135, 122, 1 }, - { 63, 135, 1, 1 }, - { 175, 112, 133, 0 }, }, - { { 204, 116, 17, 0 }, - { 209, 65, 78, 0 }, - { 68, 23, 25, 1 }, - { 57, 65, 69, 1 }, }, - { { 193, 8, 90, 1 }, - { 24, 31, 8, 1 }, - { 173, 8, 65, 1 }, - { 136, 124, 12, 0 }, }, - { { 193, 47, 130, 1 }, - { 41, 27, 43, 0 }, - { 160, 250, 65, 1 }, - { 106, 108, 74, 0 }, }, - { { 193, 35, 120, 0 }, - { 48, 149, 11, 1 }, - { 15, 98, 65, 1 }, - { 232, 84, 134, 0 }, }, - { { 198, 52, 219, 1 }, - { 25, 71, 238, 1 }, - { 237, 150, 49, 1 }, - { 187, 241, 76, 0 }, }, - { { 198, 164, 244, 1 }, - { 27, 133, 190, 0 }, - { 151, 146, 177, 1 }, - { 62, 208, 236, 0 }, }, - { { 194, 228, 25, 0 }, - { 83, 65, 138, 1 }, - { 76, 19, 161, 1 }, - { 168, 193, 101, 0 }, }, - { { 195, 29, 35, 0 }, - { 1, 219, 201, 0 }, - { 98, 92, 97, 1 }, - { 73, 237, 192, 0 }, }, - { { 195, 242, 196, 1 }, - { 106, 21, 250, 0 }, - { 145, 167, 225, 1 }, - { 47, 212, 43, 0 }, }, - { { 199, 95, 223, 0 }, - { 113, 95, 253, 1 }, - { 125, 253, 113, 1 }, - { 223, 253, 71, 0 }, }, - { { 203, 101, 189, 0 }, - { 209, 209, 187, 1 }, - { 94, 211, 105, 1 }, - { 238, 197, 197, 1 }, }, - { { 203, 199, 15, 1 }, - { 235, 83, 153, 1 }, - { 248, 113, 233, 1 }, - { 204, 229, 107, 1 }, }, - { { 207, 246, 185, 0 }, - { 243, 209, 238, 1 }, - { 78, 183, 249, 1 }, - { 187, 197, 231, 1 }, }, - { { 212, 84, 239, 0 }, - { 69, 199, 124, 1 }, - { 123, 149, 21, 1 }, - { 159, 113, 209, 0 }, }, - { { 212, 251, 220, 0 }, - { 118, 13, 127, 1 }, - { 29, 239, 149, 1 }, - { 255, 88, 55, 0 }, }, - { { 220, 232, 84, 0 }, - { 214, 13, 30, 0 }, - { 21, 11, 157, 1 }, - { 60, 88, 53, 1 }, }, - { { 213, 177, 150, 0 }, - { 22, 19, 127, 0 }, - { 52, 198, 213, 1 }, - { 127, 100, 52, 0 }, }, - { { 209, 159, 7, 1 }, - { 47, 91, 89, 0 }, - { 240, 124, 197, 1 }, - { 77, 109, 122, 0 }, }, - { { 209, 93, 126, 1 }, - { 93, 159, 89, 1 }, - { 191, 93, 69, 1 }, - { 205, 124, 221, 0 }, }, - { { 217, 164, 134, 1 }, - { 143, 19, 58, 0 }, - { 176, 146, 205, 1 }, - { 46, 100, 120, 1 }, }, - { { 221, 9, 48, 1 }, - { 156, 153, 13, 0 }, - { 134, 72, 93, 1 }, - { 88, 76, 156, 1 }, }, - { { 217, 180, 57, 1 }, - { 159, 209, 74, 1 }, - { 206, 22, 205, 1 }, - { 169, 69, 252, 1 }, }, - { { 221, 171, 105, 1 }, - { 174, 221, 15, 1 }, - { 203, 106, 221, 1 }, - { 248, 93, 186, 1 }, }, - { { 217, 203, 66, 0 }, - { 230, 31, 9, 0 }, - { 33, 105, 205, 1 }, - { 72, 124, 51, 1 }, }, - { { 210, 33, 13, 0 }, - { 4, 65, 155, 1 }, - { 88, 66, 37, 1 }, - { 236, 193, 16, 0 }, }, - { { 214, 73, 4, 0 }, - { 68, 9, 157, 0 }, - { 16, 73, 53, 1 }, - { 92, 200, 17, 0 }, }, - { { 210, 216, 219, 0 }, - { 86, 79, 232, 1 }, - { 109, 141, 165, 1 }, - { 139, 249, 53, 0 }, }, - { { 218, 63, 69, 1 }, - { 173, 77, 219, 0 }, - { 209, 126, 45, 1 }, - { 109, 217, 90, 1 }, }, - { { 218, 125, 218, 0 }, - { 213, 15, 235, 1 }, - { 45, 223, 45, 1 }, - { 235, 248, 85, 1 }, }, - { { 218, 211, 230, 0 }, - { 230, 135, 249, 0 }, - { 51, 229, 173, 1 }, - { 79, 240, 179, 1 }, }, - { { 211, 51, 189, 0 }, - { 52, 209, 251, 1 }, - { 94, 230, 101, 1 }, - { 239, 197, 150, 0 }, }, - { { 215, 225, 237, 1 }, - { 78, 213, 191, 1 }, - { 219, 195, 245, 1 }, - { 254, 213, 185, 0 }, }, - { { 219, 197, 122, 0 }, - { 215, 151, 137, 1 }, - { 47, 81, 237, 1 }, - { 200, 244, 245, 1 }, }, - { { 224, 128, 59, 1 }, - { 26, 227, 8, 1 }, - { 238, 0, 131, 1 }, - { 136, 99, 172, 0 }, }, - { { 224, 207, 105, 1 }, - { 107, 237, 9, 1 }, - { 203, 121, 131, 1 }, - { 200, 91, 235, 0 }, }, - { { 228, 71, 48, 1 }, - { 121, 161, 13, 0 }, - { 134, 113, 19, 1 }, - { 88, 66, 207, 0 }, }, - { { 232, 29, 86, 1 }, - { 153, 47, 89, 0 }, - { 181, 92, 11, 1 }, - { 77, 122, 76, 1 }, }, - { { 232, 243, 72, 1 }, - { 234, 37, 75, 1 }, - { 137, 103, 139, 1 }, - { 233, 82, 43, 1 }, }, - { { 232, 254, 175, 0 }, - { 227, 235, 122, 1 }, - { 122, 191, 139, 1 }, - { 175, 107, 227, 1 }, }, - { { 236, 231, 159, 0 }, - { 243, 99, 63, 1 }, - { 124, 243, 155, 1 }, - { 254, 99, 103, 1 }, }, - { { 225, 60, 212, 1 }, - { 25, 61, 122, 0 }, - { 149, 158, 67, 1 }, - { 47, 94, 76, 0 }, }, - { { 229, 204, 106, 1 }, - { 75, 191, 12, 1 }, - { 171, 25, 211, 1 }, - { 152, 126, 233, 0 }, }, - { { 233, 175, 154, 0 }, - { 179, 59, 43, 1 }, - { 44, 250, 203, 1 }, - { 234, 110, 102, 1 }, }, - { { 227, 0, 237, 0 }, - { 0, 245, 184, 1 }, - { 91, 128, 99, 1 }, - { 142, 215, 128, 0 }, }, - { { 231, 177, 14, 0 }, - { 2, 51, 223, 1 }, - { 56, 70, 243, 1 }, - { 253, 230, 32, 0 }, }, - { { 227, 192, 126, 0 }, - { 82, 183, 152, 1 }, - { 63, 1, 227, 1 }, - { 140, 246, 165, 0 }, }, - { { 235, 140, 163, 0 }, - { 131, 251, 168, 0 }, - { 98, 152, 235, 1 }, - { 10, 239, 224, 1 }, }, - { { 235, 159, 109, 1 }, - { 171, 253, 217, 1 }, - { 219, 124, 235, 1 }, - { 205, 223, 234, 1 }, }, - { { 239, 65, 11, 0 }, - { 192, 115, 141, 1 }, - { 104, 65, 123, 1 }, - { 216, 231, 1, 1 }, }, - { { 239, 194, 195, 0 }, - { 226, 119, 172, 0 }, - { 97, 161, 251, 1 }, - { 26, 247, 35, 1 }, }, - { { 240, 168, 32, 0 }, - { 6, 169, 10, 0 }, - { 2, 10, 135, 1 }, - { 40, 74, 176, 0 }, }, - { { 244, 25, 211, 0 }, - { 20, 111, 109, 0 }, - { 101, 204, 23, 1 }, - { 91, 123, 20, 0 }, }, - { { 244, 133, 191, 1 }, - { 31, 227, 61, 1 }, - { 254, 208, 151, 1 }, - { 222, 99, 252, 0 }, }, - { { 240, 106, 204, 0 }, - { 100, 45, 58, 1 }, - { 25, 171, 7, 1 }, - { 174, 90, 19, 0 }, }, - { { 240, 123, 107, 1 }, - { 108, 239, 75, 1 }, - { 235, 111, 7, 1 }, - { 233, 123, 155, 0 }, }, - { { 244, 251, 170, 0 }, - { 102, 171, 111, 1 }, - { 42, 239, 151, 1 }, - { 251, 106, 179, 0 }, }, - { { 252, 198, 47, 0 }, - { 231, 227, 28, 1 }, - { 122, 49, 159, 1 }, - { 156, 99, 243, 1 }, }, - { { 248, 111, 16, 0 }, - { 245, 41, 11, 0 }, - { 4, 123, 15, 1 }, - { 104, 74, 87, 1 }, }, - { { 252, 127, 120, 1 }, - { 253, 173, 79, 1 }, - { 143, 127, 31, 1 }, - { 249, 90, 223, 1 }, }, - { { 241, 171, 217, 0 }, - { 54, 125, 43, 1 }, - { 77, 234, 199, 1 }, - { 234, 95, 54, 0 }, }, - { { 245, 81, 143, 1 }, - { 76, 115, 125, 1 }, - { 248, 197, 87, 1 }, - { 223, 103, 25, 0 }, }, - { { 241, 248, 166, 1 }, - { 78, 187, 122, 0 }, - { 178, 143, 199, 1 }, - { 47, 110, 185, 0 }, }, - { { 241, 86, 24, 0 }, - { 117, 49, 72, 1 }, - { 12, 53, 71, 1 }, - { 137, 70, 87, 0 }, }, - { { 253, 132, 196, 0 }, - { 135, 53, 60, 0 }, - { 17, 144, 223, 1 }, - { 30, 86, 112, 1 }, }, - { { 249, 141, 114, 1 }, - { 159, 191, 9, 0 }, - { 167, 88, 207, 1 }, - { 72, 126, 252, 1 }, }, - { { 253, 15, 85, 1 }, - { 189, 125, 29, 0 }, - { 213, 120, 95, 1 }, - { 92, 95, 94, 1 }, }, - { { 242, 156, 111, 1 }, - { 15, 239, 216, 1 }, - { 251, 28, 167, 1 }, - { 141, 251, 248, 0 }, }, - { { 246, 136, 24, 1 }, - { 30, 41, 140, 1 }, - { 140, 8, 183, 1 }, - { 152, 202, 60, 0 }, }, - { { 246, 7, 197, 0 }, - { 37, 101, 189, 0 }, - { 81, 240, 55, 1 }, - { 94, 211, 82, 0 }, }, - { { 242, 65, 215, 0 }, - { 84, 103, 185, 0 }, - { 117, 193, 39, 1 }, - { 78, 243, 21, 0 }, }, - { { 246, 241, 27, 0 }, - { 86, 99, 207, 1 }, - { 108, 71, 183, 1 }, - { 249, 227, 53, 0 }, }, - { { 242, 230, 203, 1 }, - { 111, 103, 170, 1 }, - { 233, 179, 167, 1 }, - { 170, 243, 123, 0 }, }, - { { 250, 213, 32, 1 }, - { 207, 161, 201, 0 }, - { 130, 85, 175, 1 }, - { 73, 194, 249, 1 }, }, - { { 254, 67, 233, 0 }, - { 228, 229, 173, 1 }, - { 75, 225, 63, 1 }, - { 218, 211, 147, 1 }, }, - { { 243, 251, 142, 0 }, - { 102, 59, 251, 1 }, - { 56, 239, 231, 1 }, - { 239, 238, 51, 0 }, }, - { { 255, 187, 218, 0 }, - { 182, 63, 239, 1 }, - { 45, 238, 255, 1 }, - { 251, 254, 54, 1 }, }, - { { 251, 201, 14, 1 }, - { 206, 59, 153, 1 }, - { 184, 73, 239, 1 }, - { 204, 238, 57, 1 }, }, }; - -static unsigned char DICT_6X6_1000_BYTES[][4][5] = - { { { 30, 61, 216, 42, 6 }, - { 227, 186, 70, 49, 9 }, - { 101, 65, 187, 199, 8 }, - { 152, 198, 37, 220, 7 }, }, - { { 14, 251, 163, 137, 1 }, - { 215, 230, 24, 5, 14 }, - { 137, 28, 93, 247, 0 }, - { 122, 1, 134, 126, 11 }, }, - { { 21, 144, 126, 172, 13 }, - { 236, 105, 87, 80, 6 }, - { 179, 87, 224, 154, 8 }, - { 96, 174, 169, 99, 7 }, }, - { { 201, 27, 48, 105, 14 }, - { 66, 50, 75, 222, 12 }, - { 121, 96, 205, 137, 3 }, - { 55, 189, 36, 196, 2 }, }, - { { 214, 7, 214, 225, 5 }, - { 164, 203, 74, 191, 2 }, - { 168, 118, 190, 6, 11 }, - { 79, 213, 45, 50, 5 }, }, - { { 216, 232, 224, 230, 8 }, - { 43, 140, 19, 138, 15 }, - { 22, 112, 113, 113, 11 }, - { 245, 28, 131, 29, 4 }, }, - { { 66, 104, 180, 31, 5 }, - { 13, 165, 192, 149, 13 }, - { 175, 130, 209, 100, 2 }, - { 186, 144, 58, 91, 0 }, }, - { { 136, 165, 15, 41, 10 }, - { 19, 115, 23, 38, 0 }, - { 89, 79, 10, 81, 1 }, - { 6, 78, 140, 236, 8 }, }, - { { 48, 125, 82, 79, 13 }, - { 109, 110, 97, 60, 9 }, - { 191, 36, 171, 224, 12 }, - { 147, 200, 103, 107, 6 }, }, - { { 60, 47, 52, 179, 12 }, - { 131, 11, 235, 52, 15 }, - { 60, 210, 207, 67, 12 }, - { 242, 205, 125, 12, 1 }, }, - { { 69, 223, 199, 78, 3 }, - { 252, 247, 24, 232, 9 }, - { 199, 46, 63, 186, 2 }, - { 145, 113, 142, 243, 15 }, }, - { { 72, 216, 91, 37, 7 }, - { 126, 84, 86, 148, 8 }, - { 234, 77, 161, 177, 2 }, - { 18, 150, 162, 167, 14 }, }, - { { 113, 5, 88, 252, 6 }, - { 40, 58, 230, 248, 2 }, - { 99, 241, 170, 8, 14 }, - { 65, 246, 117, 193, 4 }, }, - { { 134, 220, 250, 208, 7 }, - { 228, 212, 212, 59, 14 }, - { 224, 181, 243, 182, 1 }, - { 125, 194, 178, 178, 7 }, }, - { { 141, 114, 169, 63, 6 }, - { 219, 180, 206, 70, 5 }, - { 111, 201, 84, 235, 1 }, - { 166, 39, 50, 221, 11 }, }, - { { 162, 184, 157, 205, 14 }, - { 89, 177, 117, 31, 10 }, - { 123, 59, 145, 212, 5 }, - { 95, 138, 232, 217, 10 }, }, - { { 9, 253, 30, 156, 4 }, - { 75, 103, 212, 112, 10 }, - { 35, 151, 139, 249, 0 }, - { 80, 226, 190, 109, 2 }, }, - { { 21, 77, 189, 24, 15 }, - { 148, 191, 197, 112, 12 }, - { 241, 139, 219, 42, 8 }, - { 48, 234, 63, 210, 9 }, }, - { { 48, 10, 49, 14, 2 }, - { 24, 56, 40, 16, 13 }, - { 71, 8, 197, 0, 12 }, - { 176, 129, 65, 193, 8 }, }, - { { 72, 7, 239, 175, 13 }, - { 62, 227, 79, 164, 7 }, - { 191, 95, 126, 1, 2 }, - { 226, 95, 44, 119, 12 }, }, - { { 86, 223, 17, 219, 6 }, - { 208, 62, 216, 189, 11 }, - { 109, 184, 143, 182, 10 }, - { 219, 209, 183, 192, 11 }, }, - { { 102, 136, 50, 116, 12 }, - { 136, 64, 243, 153, 12 }, - { 50, 228, 193, 22, 6 }, - { 57, 156, 240, 33, 1 }, }, - { { 118, 232, 203, 120, 1 }, - { 181, 236, 182, 137, 8 }, - { 129, 237, 49, 118, 14 }, - { 25, 22, 211, 122, 13 }, }, - { { 154, 83, 217, 207, 3 }, - { 126, 190, 12, 31, 3 }, - { 207, 57, 188, 165, 9 }, - { 207, 131, 7, 215, 14 }, }, - { { 169, 203, 132, 2, 4 }, - { 2, 135, 120, 66, 9 }, - { 36, 2, 29, 57, 5 }, - { 148, 33, 238, 20, 0 }, }, - { { 198, 117, 73, 73, 0 }, - { 241, 38, 4, 175, 0 }, - { 9, 41, 42, 230, 3 }, - { 15, 82, 6, 72, 15 }, }, - { { 193, 210, 136, 148, 1 }, - { 76, 132, 156, 194, 2 }, - { 130, 145, 20, 184, 3 }, - { 68, 51, 146, 19, 2 }, }, - { { 231, 72, 8, 82, 11 }, - { 132, 20, 165, 203, 9 }, - { 212, 161, 1, 46, 7 }, - { 157, 58, 82, 130, 1 }, }, - { { 234, 47, 202, 132, 8 }, - { 43, 194, 45, 163, 10 }, - { 18, 21, 63, 69, 7 }, - { 92, 91, 68, 61, 4 }, }, - { { 233, 99, 183, 123, 1 }, - { 23, 231, 170, 222, 5 }, - { 141, 238, 220, 105, 7 }, - { 167, 181, 94, 126, 8 }, }, - { { 250, 54, 101, 42, 15 }, - { 119, 57, 107, 163, 5 }, - { 245, 74, 102, 197, 15 }, - { 172, 93, 105, 206, 14 }, }, - { { 6, 91, 255, 123, 13 }, - { 244, 231, 207, 29, 13 }, - { 189, 239, 253, 166, 0 }, - { 187, 143, 62, 114, 15 }, }, - { { 5, 65, 215, 45, 6 }, - { 184, 247, 66, 84, 0 }, - { 107, 78, 184, 42, 0 }, - { 2, 164, 46, 241, 13 }, }, - { { 12, 247, 36, 106, 2 }, - { 195, 55, 26, 40, 5 }, - { 69, 98, 78, 243, 0 }, - { 161, 69, 142, 204, 3 }, }, - { { 19, 56, 163, 158, 11 }, - { 93, 248, 129, 65, 15 }, - { 215, 156, 81, 204, 8 }, - { 248, 40, 17, 251, 10 }, }, - { { 21, 168, 147, 231, 4 }, - { 153, 200, 82, 92, 11 }, - { 46, 124, 145, 90, 8 }, - { 211, 164, 161, 57, 9 }, }, - { { 58, 65, 126, 233, 14 }, - { 34, 127, 103, 29, 6 }, - { 121, 119, 232, 37, 12 }, - { 107, 142, 111, 228, 4 }, }, - { { 79, 17, 226, 108, 0 }, - { 234, 226, 2, 201, 4 }, - { 3, 100, 120, 143, 2 }, - { 41, 52, 4, 117, 7 }, }, - { { 83, 13, 182, 210, 0 }, - { 0, 203, 128, 249, 15 }, - { 4, 182, 219, 12, 10 }, - { 249, 240, 29, 48, 0 }, }, - { { 88, 155, 250, 227, 4 }, - { 98, 202, 94, 156, 15 }, - { 44, 117, 253, 145, 10 }, - { 243, 151, 165, 52, 6 }, }, - { { 100, 9, 232, 160, 11 }, - { 164, 146, 39, 128, 14 }, - { 208, 81, 121, 2, 6 }, - { 112, 30, 68, 146, 5 }, }, - { { 96, 83, 122, 137, 1 }, - { 100, 102, 44, 148, 6 }, - { 137, 21, 236, 160, 6 }, - { 98, 147, 70, 98, 6 }, }, - { { 97, 89, 6, 155, 10 }, - { 64, 119, 161, 196, 11 }, - { 93, 150, 9, 168, 6 }, - { 210, 56, 94, 224, 2 }, }, - { { 107, 255, 120, 215, 11 }, - { 111, 22, 189, 253, 15 }, - { 222, 177, 239, 253, 6 }, - { 251, 251, 214, 143, 6 }, }, - { { 112, 173, 150, 164, 15 }, - { 13, 219, 115, 176, 10 }, - { 242, 86, 155, 80, 14 }, - { 80, 220, 237, 187, 0 }, }, - { { 117, 132, 111, 113, 10 }, - { 176, 89, 183, 236, 4 }, - { 88, 239, 98, 26, 14 }, - { 35, 126, 217, 160, 13 }, }, - { { 122, 149, 25, 47, 12 }, - { 90, 42, 119, 181, 1 }, - { 63, 73, 138, 149, 14 }, - { 138, 222, 229, 69, 10 }, }, - { { 134, 9, 118, 10, 10 }, - { 160, 115, 1, 19, 13 }, - { 85, 6, 233, 6, 1 }, - { 188, 136, 12, 224, 5 }, }, - { { 138, 45, 68, 195, 15 }, - { 39, 19, 65, 47, 11 }, - { 252, 50, 43, 69, 1 }, - { 223, 72, 44, 142, 4 }, }, - { { 147, 235, 120, 177, 4 }, - { 33, 14, 222, 87, 14 }, - { 40, 209, 237, 124, 9 }, - { 126, 167, 183, 8, 4 }, }, - { { 152, 141, 168, 77, 4 }, - { 10, 170, 84, 46, 12 }, - { 43, 33, 91, 17, 9 }, - { 55, 66, 165, 85, 0 }, }, - { { 158, 222, 43, 60, 8 }, - { 218, 108, 159, 35, 12 }, - { 19, 205, 71, 183, 9 }, - { 60, 79, 147, 101, 11 }, }, - { { 165, 41, 224, 123, 8 }, - { 161, 162, 163, 78, 13 }, - { 29, 224, 121, 74, 5 }, - { 183, 44, 84, 88, 5 }, }, - { { 181, 147, 184, 85, 15 }, - { 204, 154, 253, 94, 4 }, - { 250, 161, 220, 154, 13 }, - { 39, 171, 245, 147, 3 }, }, - { { 183, 248, 228, 38, 15 }, - { 237, 157, 115, 67, 13 }, - { 246, 66, 113, 254, 13 }, - { 188, 44, 235, 155, 7 }, }, - { { 188, 32, 82, 37, 14 }, - { 171, 88, 99, 22, 0 }, - { 122, 68, 160, 67, 13 }, - { 6, 140, 97, 173, 5 }, }, - { { 192, 68, 135, 118, 5 }, - { 28, 197, 194, 170, 1 }, - { 166, 238, 18, 32, 3 }, - { 133, 84, 58, 51, 8 }, }, - { { 196, 195, 36, 37, 9 }, - { 140, 7, 27, 134, 4 }, - { 154, 66, 76, 50, 3 }, - { 38, 29, 142, 3, 1 }, }, - { { 197, 169, 27, 216, 13 }, - { 149, 98, 213, 218, 10 }, - { 177, 189, 137, 90, 3 }, - { 85, 186, 180, 106, 9 }, }, - { { 206, 115, 230, 178, 12 }, - { 227, 199, 203, 131, 7 }, - { 52, 214, 124, 231, 3 }, - { 236, 29, 62, 60, 7 }, }, - { { 205, 12, 166, 39, 2 }, - { 138, 209, 2, 230, 13 }, - { 78, 70, 83, 11, 3 }, - { 182, 116, 8, 181, 1 }, }, - { { 201, 67, 93, 68, 13 }, - { 62, 7, 77, 218, 0 }, - { 178, 43, 172, 41, 3 }, - { 5, 187, 46, 7, 12 }, }, - { { 207, 190, 128, 243, 4 }, - { 195, 128, 218, 239, 11 }, - { 44, 240, 23, 223, 3 }, - { 223, 117, 176, 28, 3 }, }, - { { 229, 125, 21, 135, 7 }, - { 221, 23, 96, 246, 11 }, - { 238, 26, 139, 234, 7 }, - { 214, 240, 110, 139, 11 }, }, - { { 239, 198, 133, 142, 9 }, - { 158, 165, 57, 227, 3 }, - { 151, 26, 22, 63, 7 }, - { 204, 121, 202, 87, 9 }, }, - { { 247, 126, 243, 119, 2 }, - { 249, 220, 170, 255, 13 }, - { 78, 236, 247, 238, 15 }, - { 191, 245, 83, 185, 15 }, }, - { { 44, 228, 63, 37, 4 }, - { 155, 69, 118, 52, 4 }, - { 42, 79, 194, 115, 4 }, - { 34, 198, 234, 45, 9 }, }, - { { 43, 220, 255, 75, 3 }, - { 118, 245, 52, 125, 13 }, - { 205, 47, 243, 189, 4 }, - { 187, 226, 202, 246, 14 }, }, - { { 55, 199, 221, 189, 10 }, - { 184, 191, 191, 117, 2 }, - { 91, 219, 190, 62, 12 }, - { 74, 239, 223, 209, 13 }, }, - { { 161, 162, 84, 224, 15 }, - { 37, 17, 123, 90, 2 }, - { 240, 114, 164, 88, 5 }, - { 69, 173, 232, 138, 4 }, }, - { { 169, 130, 193, 187, 5 }, - { 54, 160, 250, 70, 3 }, - { 173, 216, 52, 25, 5 }, - { 198, 37, 240, 86, 12 }, }, - { { 216, 27, 73, 176, 8 }, - { 114, 10, 143, 130, 10 }, - { 16, 217, 45, 129, 11 }, - { 84, 31, 21, 4, 14 }, }, - { { 3, 88, 41, 248, 6 }, - { 80, 52, 198, 73, 14 }, - { 97, 249, 65, 172, 0 }, - { 121, 38, 50, 192, 10 }, }, - { { 7, 196, 9, 95, 12 }, - { 152, 36, 213, 109, 1 }, - { 63, 169, 2, 62, 0 }, - { 139, 106, 178, 65, 9 }, }, - { { 15, 226, 102, 23, 11 }, - { 175, 85, 153, 69, 5 }, - { 222, 134, 100, 127, 0 }, - { 170, 41, 154, 175, 5 }, }, - { { 20, 72, 54, 68, 1 }, - { 140, 77, 0, 24, 12 }, - { 130, 38, 193, 34, 8 }, - { 49, 128, 11, 35, 1 }, }, - { { 16, 173, 95, 251, 7 }, - { 53, 123, 214, 60, 11 }, - { 237, 255, 171, 80, 8 }, - { 211, 198, 189, 234, 12 }, }, - { { 18, 130, 149, 83, 15 }, - { 20, 153, 217, 29, 1 }, - { 252, 170, 148, 20, 8 }, - { 139, 137, 185, 146, 8 }, }, - { { 22, 225, 49, 132, 12 }, - { 153, 14, 81, 17, 6 }, - { 50, 24, 200, 118, 8 }, - { 104, 136, 167, 9, 9 }, }, - { { 24, 122, 73, 107, 0 }, - { 115, 44, 14, 12, 9 }, - { 13, 105, 37, 225, 8 }, - { 147, 7, 3, 76, 14 }, }, - { { 26, 232, 134, 17, 2 }, - { 3, 221, 144, 5, 8 }, - { 72, 134, 17, 117, 8 }, - { 26, 0, 155, 188, 0 }, }, - { { 25, 19, 174, 10, 1 }, - { 70, 235, 12, 64, 5 }, - { 133, 7, 92, 137, 8 }, - { 160, 35, 13, 118, 2 }, }, - { { 27, 103, 181, 161, 7 }, - { 23, 159, 74, 117, 6 }, - { 232, 90, 222, 109, 8 }, - { 106, 229, 47, 158, 8 }, }, - { { 37, 220, 149, 240, 11 }, - { 212, 149, 179, 120, 10 }, - { 208, 250, 147, 186, 4 }, - { 81, 236, 218, 146, 11 }, }, - { { 40, 137, 97, 247, 6 }, - { 58, 18, 242, 12, 15 }, - { 110, 248, 105, 17, 4 }, - { 243, 4, 244, 133, 12 }, }, - { { 51, 84, 20, 106, 10 }, - { 64, 61, 35, 121, 1 }, - { 85, 98, 130, 172, 12 }, - { 137, 236, 75, 192, 2 }, }, - { { 49, 193, 108, 31, 7 }, - { 44, 63, 244, 68, 5 }, - { 239, 131, 104, 56, 12 }, - { 162, 34, 255, 195, 4 }, }, - { { 51, 203, 24, 198, 6 }, - { 8, 30, 124, 89, 11 }, - { 102, 49, 141, 60, 12 }, - { 217, 163, 231, 129, 0 }, }, - { { 62, 207, 228, 144, 15 }, - { 166, 159, 249, 33, 14 }, - { 240, 146, 127, 55, 12 }, - { 120, 73, 255, 150, 5 }, }, - { { 70, 69, 24, 163, 15 }, - { 132, 22, 71, 181, 3 }, - { 252, 81, 138, 38, 2 }, - { 202, 222, 38, 130, 1 }, }, - { { 68, 186, 112, 182, 7 }, - { 237, 16, 218, 144, 15 }, - { 230, 208, 229, 210, 2 }, - { 240, 149, 176, 139, 7 }, }, - { { 65, 156, 98, 62, 8 }, - { 104, 96, 147, 224, 13 }, - { 23, 196, 99, 152, 2 }, - { 176, 124, 144, 97, 6 }, }, - { { 72, 209, 145, 74, 1 }, - { 86, 166, 16, 152, 1 }, - { 133, 40, 152, 177, 2 }, - { 129, 144, 134, 86, 10 }, }, - { { 84, 244, 153, 246, 13 }, - { 221, 140, 215, 184, 3 }, - { 182, 249, 146, 242, 10 }, - { 193, 222, 179, 27, 11 }, }, - { { 87, 90, 156, 129, 3 }, - { 196, 157, 12, 213, 10 }, - { 200, 19, 149, 174, 10 }, - { 90, 179, 11, 146, 3 }, }, - { { 85, 131, 85, 178, 12 }, - { 176, 11, 219, 208, 3 }, - { 52, 218, 172, 26, 10 }, - { 192, 189, 189, 0, 13 }, }, - { { 87, 183, 118, 16, 15 }, - { 229, 91, 217, 241, 4 }, - { 240, 134, 238, 222, 10 }, - { 40, 249, 189, 170, 7 }, }, - { { 92, 52, 54, 254, 4 }, - { 203, 105, 194, 184, 7 }, - { 39, 246, 194, 195, 10 }, - { 225, 212, 57, 109, 3 }, }, - { { 92, 72, 252, 119, 14 }, - { 170, 157, 199, 156, 13 }, - { 126, 227, 241, 35, 10 }, - { 179, 158, 59, 149, 5 }, }, - { { 94, 110, 239, 64, 2 }, - { 179, 221, 12, 169, 12 }, - { 64, 47, 119, 103, 10 }, - { 57, 83, 11, 188, 13 }, }, - { { 95, 35, 59, 111, 15 }, - { 159, 122, 79, 221, 5 }, - { 255, 109, 204, 79, 10 }, - { 171, 191, 37, 239, 9 }, }, - { { 91, 116, 42, 99, 2 }, - { 67, 92, 6, 237, 5 }, - { 76, 101, 66, 237, 10 }, - { 171, 118, 3, 172, 2 }, }, - { { 101, 15, 163, 58, 14 }, - { 144, 242, 235, 224, 13 }, - { 117, 204, 95, 10, 6 }, - { 176, 125, 116, 240, 9 }, }, - { { 101, 211, 23, 92, 12 }, - { 216, 103, 249, 216, 0 }, - { 51, 174, 140, 186, 6 }, - { 1, 185, 254, 97, 11 }, }, - { { 106, 156, 36, 90, 14 }, - { 66, 49, 241, 169, 13 }, - { 117, 162, 67, 149, 6 }, - { 185, 88, 248, 196, 2 }, }, - { { 105, 197, 243, 4, 2 }, - { 58, 214, 48, 240, 4 }, - { 66, 12, 250, 57, 6 }, - { 32, 240, 198, 181, 12 }, }, - { { 105, 210, 72, 78, 10 }, - { 106, 52, 61, 200, 1 }, - { 87, 33, 36, 185, 6 }, - { 129, 59, 194, 197, 6 }, }, - { { 116, 121, 226, 222, 6 }, - { 233, 254, 224, 136, 15 }, - { 103, 180, 121, 226, 14 }, - { 241, 16, 119, 249, 7 }, }, - { { 114, 207, 35, 234, 11 }, - { 20, 126, 59, 169, 15 }, - { 213, 124, 79, 52, 14 }, - { 249, 93, 199, 226, 8 }, }, - { { 119, 177, 220, 65, 4 }, - { 225, 139, 116, 221, 0 }, - { 40, 35, 184, 222, 14 }, - { 11, 178, 237, 24, 7 }, }, - { { 126, 12, 7, 33, 7 }, - { 150, 89, 98, 165, 8 }, - { 232, 78, 3, 7, 14 }, - { 26, 84, 105, 166, 9 }, }, - { { 122, 105, 112, 100, 7 }, - { 47, 30, 98, 153, 12 }, - { 226, 96, 233, 101, 14 }, - { 57, 148, 103, 143, 4 }, }, - { { 120, 178, 216, 112, 7 }, - { 103, 152, 254, 152, 0 }, - { 224, 225, 180, 209, 14 }, - { 1, 151, 241, 158, 6 }, }, - { { 121, 197, 133, 121, 4 }, - { 18, 175, 242, 236, 0 }, - { 41, 234, 26, 57, 14 }, - { 3, 116, 255, 84, 8 }, }, - { { 134, 111, 89, 252, 6 }, - { 185, 54, 206, 59, 10 }, - { 99, 249, 175, 102, 1 }, - { 93, 199, 54, 201, 13 }, }, - { { 130, 246, 114, 127, 5 }, - { 109, 100, 218, 63, 5 }, - { 175, 228, 230, 244, 1 }, - { 175, 197, 178, 107, 6 }, }, - { { 133, 78, 47, 65, 4 }, - { 144, 69, 76, 110, 12 }, - { 40, 47, 71, 42, 1 }, - { 55, 99, 42, 32, 9 }, }, - { { 154, 17, 133, 147, 4 }, - { 82, 139, 192, 7, 3 }, - { 44, 154, 24, 133, 9 }, - { 206, 0, 61, 20, 10 }, }, - { { 156, 113, 96, 201, 7 }, - { 231, 62, 64, 14, 6 }, - { 233, 48, 104, 227, 9 }, - { 103, 0, 39, 206, 7 }, }, - { { 157, 209, 148, 253, 8 }, - { 202, 175, 147, 94, 2 }, - { 27, 242, 152, 187, 9 }, - { 71, 172, 159, 85, 3 }, }, - { { 162, 30, 18, 227, 8 }, - { 64, 64, 43, 63, 11 }, - { 28, 116, 135, 132, 5 }, - { 223, 205, 64, 32, 2 }, }, - { { 174, 112, 28, 130, 12 }, - { 195, 5, 101, 19, 3 }, - { 52, 19, 128, 231, 5 }, - { 204, 138, 106, 12, 3 }, }, - { { 173, 1, 33, 156, 1 }, - { 158, 34, 160, 66, 6 }, - { 131, 152, 72, 11, 5 }, - { 100, 32, 84, 71, 9 }, }, - { { 176, 53, 31, 158, 14 }, - { 89, 123, 229, 50, 3 }, - { 119, 159, 138, 192, 13 }, - { 196, 202, 125, 233, 10 }, }, - { { 182, 74, 216, 13, 4 }, - { 168, 172, 108, 23, 8 }, - { 43, 1, 181, 38, 13 }, - { 30, 131, 99, 81, 5 }, }, - { { 181, 55, 49, 75, 4 }, - { 209, 42, 104, 126, 5 }, - { 45, 40, 206, 202, 13 }, - { 167, 225, 101, 72, 11 }, }, - { { 190, 170, 199, 227, 11 }, - { 183, 217, 59, 15, 11 }, - { 220, 126, 53, 87, 13 }, - { 223, 13, 201, 190, 13 }, }, - { { 187, 104, 61, 188, 15 }, - { 31, 61, 231, 83, 14 }, - { 243, 219, 193, 109, 13 }, - { 124, 174, 123, 207, 8 }, }, - { { 198, 114, 247, 44, 1 }, - { 253, 229, 10, 147, 4 }, - { 131, 78, 244, 230, 3 }, - { 44, 149, 10, 123, 15 }, }, - { { 193, 231, 77, 186, 11 }, - { 53, 55, 159, 226, 3 }, - { 213, 219, 46, 120, 3 }, - { 196, 127, 158, 202, 12 }, }, - { { 203, 85, 238, 89, 13 }, - { 102, 231, 197, 239, 4 }, - { 185, 167, 122, 173, 3 }, - { 47, 122, 62, 118, 6 }, }, - { { 203, 160, 83, 114, 4 }, - { 51, 64, 210, 219, 1 }, - { 36, 236, 160, 93, 3 }, - { 141, 180, 176, 44, 12 }, }, - { { 208, 9, 15, 207, 1 }, - { 28, 107, 4, 142, 11 }, - { 143, 63, 9, 0, 11 }, - { 215, 18, 13, 99, 8 }, }, - { { 208, 108, 58, 213, 4 }, - { 9, 76, 196, 190, 14 }, - { 42, 181, 195, 96, 11 }, - { 119, 210, 51, 41, 0 }, }, - { { 211, 241, 32, 87, 4 }, - { 73, 14, 208, 207, 5 }, - { 46, 160, 72, 252, 11 }, - { 175, 48, 183, 9, 2 }, }, - { { 230, 227, 59, 26, 7 }, - { 149, 118, 252, 147, 5 }, - { 229, 141, 204, 118, 7 }, - { 172, 147, 246, 234, 9 }, }, - { { 227, 83, 62, 164, 10 }, - { 72, 87, 47, 211, 6 }, - { 82, 87, 204, 172, 7 }, - { 108, 191, 78, 161, 2 }, }, - { { 232, 6, 142, 177, 4 }, - { 2, 193, 238, 166, 2 }, - { 40, 215, 22, 1, 7 }, - { 70, 87, 120, 52, 0 }, }, - { { 236, 7, 192, 89, 7 }, - { 166, 178, 232, 174, 0 }, - { 233, 160, 62, 3, 7 }, - { 7, 81, 116, 214, 5 }, }, - { { 234, 243, 128, 61, 10 }, - { 75, 182, 187, 135, 0 }, - { 91, 192, 28, 245, 7 }, - { 14, 29, 214, 221, 2 }, }, - { { 246, 59, 39, 216, 8 }, - { 209, 107, 169, 139, 14 }, - { 17, 190, 77, 198, 15 }, - { 125, 25, 93, 104, 11 }, }, - { { 243, 7, 152, 55, 9 }, - { 12, 138, 175, 247, 1 }, - { 158, 193, 158, 12, 15 }, - { 142, 255, 85, 19, 0 }, }, - { { 254, 75, 186, 155, 9 }, - { 134, 238, 173, 151, 15 }, - { 157, 149, 221, 39, 15 }, - { 254, 155, 87, 118, 1 }, }, - { { 171, 165, 125, 134, 11 }, - { 63, 19, 53, 115, 7 }, - { 214, 27, 234, 93, 5 }, - { 236, 234, 204, 143, 12 }, }, - { { 192, 209, 98, 90, 11 }, - { 100, 118, 145, 138, 5 }, - { 213, 164, 104, 176, 3 }, - { 165, 24, 150, 226, 6 }, }, - { { 19, 206, 123, 174, 7 }, - { 60, 124, 94, 113, 15 }, - { 231, 93, 231, 60, 8 }, - { 248, 231, 163, 227, 12 }, }, - { { 78, 129, 253, 97, 7 }, - { 182, 147, 86, 157, 4 }, - { 232, 107, 248, 23, 2 }, - { 43, 150, 172, 150, 13 }, }, - { { 86, 224, 118, 50, 0 }, - { 161, 77, 146, 145, 5 }, - { 4, 198, 224, 118, 10 }, - { 168, 148, 155, 40, 5 }, }, - { { 106, 112, 138, 84, 0 }, - { 75, 196, 164, 137, 0 }, - { 2, 165, 16, 229, 6 }, - { 9, 18, 82, 61, 2 }, }, - { { 114, 168, 152, 161, 8 }, - { 1, 136, 55, 149, 10 }, - { 24, 81, 145, 84, 14 }, - { 90, 158, 193, 24, 0 }, }, - { { 129, 93, 66, 248, 0 }, - { 96, 102, 130, 106, 10 }, - { 1, 244, 43, 168, 1 }, - { 85, 100, 22, 96, 6 }, }, - { { 207, 76, 195, 213, 15 }, - { 190, 212, 193, 239, 10 }, - { 250, 188, 51, 47, 3 }, - { 95, 120, 50, 183, 13 }, }, - { { 214, 187, 101, 134, 4 }, - { 249, 11, 88, 131, 15 }, - { 38, 26, 109, 214, 11 }, - { 252, 17, 173, 9, 15 }, }, - { { 236, 211, 19, 163, 1 }, - { 214, 70, 58, 150, 3 }, - { 140, 92, 140, 179, 7 }, - { 198, 149, 198, 38, 11 }, }, - { { 245, 33, 245, 32, 7 }, - { 181, 155, 98, 210, 4 }, - { 224, 74, 248, 74, 15 }, - { 36, 180, 109, 154, 13 }, }, - { { 249, 31, 165, 223, 7 }, - { 94, 187, 232, 238, 15 }, - { 239, 186, 95, 137, 15 }, - { 247, 113, 125, 215, 10 }, }, - { { 0, 36, 244, 122, 7 }, - { 37, 177, 194, 56, 5 }, - { 229, 226, 242, 64, 0 }, - { 161, 196, 56, 218, 4 }, }, - { { 0, 8, 77, 136, 2 }, - { 48, 49, 4, 0, 10 }, - { 65, 27, 33, 0, 0 }, - { 80, 2, 8, 192, 12 }, }, - { { 4, 60, 194, 242, 9 }, - { 229, 192, 131, 40, 11 }, - { 148, 244, 51, 194, 0 }, - { 209, 76, 16, 58, 7 }, }, - { { 4, 123, 80, 33, 1 }, - { 229, 6, 10, 20, 8 }, - { 136, 64, 173, 226, 0 }, - { 18, 133, 6, 10, 7 }, }, - { { 6, 122, 228, 193, 13 }, - { 229, 133, 73, 13, 14 }, - { 184, 50, 117, 230, 0 }, - { 123, 9, 42, 26, 7 }, }, - { { 0, 170, 150, 138, 3 }, - { 5, 241, 24, 16, 11 }, - { 197, 22, 149, 80, 0 }, - { 208, 129, 136, 250, 0 }, }, - { { 4, 209, 56, 233, 4 }, - { 192, 38, 86, 28, 6 }, - { 41, 113, 200, 178, 0 }, - { 99, 134, 166, 64, 3 }, }, - { { 5, 16, 168, 13, 10 }, - { 200, 176, 5, 68, 4 }, - { 91, 1, 80, 138, 0 }, - { 34, 42, 0, 209, 3 }, }, - { { 1, 64, 176, 0, 7 }, - { 4, 148, 64, 80, 4 }, - { 224, 0, 208, 40, 0 }, - { 32, 160, 34, 146, 0 }, }, - { { 1, 157, 156, 238, 1 }, - { 76, 163, 22, 120, 11 }, - { 135, 115, 155, 152, 0 }, - { 209, 230, 140, 83, 2 }, }, - { { 8, 16, 87, 227, 11 }, - { 118, 81, 3, 28, 3 }, - { 220, 126, 160, 129, 0 }, - { 195, 140, 8, 166, 14 }, }, - { { 8, 107, 151, 182, 6 }, - { 27, 215, 202, 16, 11 }, - { 102, 222, 157, 97, 0 }, - { 208, 133, 62, 189, 8 }, }, - { { 14, 232, 184, 96, 10 }, - { 131, 148, 23, 25, 12 }, - { 80, 97, 209, 119, 0 }, - { 57, 142, 130, 156, 1 }, }, - { { 11, 108, 118, 185, 11 }, - { 39, 117, 131, 117, 14 }, - { 217, 214, 227, 109, 0 }, - { 122, 236, 26, 238, 4 }, }, - { { 15, 220, 185, 140, 11 }, - { 222, 180, 21, 113, 14 }, - { 211, 25, 211, 191, 0 }, - { 120, 234, 130, 215, 11 }, }, - { { 15, 202, 207, 58, 0 }, - { 178, 229, 158, 65, 9 }, - { 5, 207, 53, 63, 0 }, - { 152, 39, 154, 116, 13 }, }, - { { 20, 36, 159, 217, 8 }, - { 145, 233, 133, 60, 2 }, - { 25, 191, 146, 66, 8 }, - { 67, 202, 25, 120, 9 }, }, - { { 20, 7, 32, 31, 13 }, - { 140, 42, 201, 36, 5 }, - { 191, 128, 78, 2, 8 }, - { 162, 73, 53, 67, 1 }, }, - { { 21, 9, 16, 213, 7 }, - { 140, 26, 192, 92, 10 }, - { 234, 176, 137, 10, 8 }, - { 83, 160, 53, 131, 1 }, }, - { { 19, 92, 215, 48, 7 }, - { 116, 221, 194, 113, 8 }, - { 224, 206, 179, 172, 8 }, - { 24, 228, 59, 178, 14 }, }, - { { 17, 71, 154, 187, 6 }, - { 0, 254, 206, 116, 3 }, - { 109, 213, 158, 40, 8 }, - { 194, 231, 55, 240, 0 }, }, - { { 28, 185, 169, 35, 8 }, - { 211, 138, 23, 4, 13 }, - { 28, 73, 89, 211, 8 }, - { 178, 14, 133, 28, 11 }, }, - { { 28, 221, 7, 118, 6 }, - { 218, 95, 210, 40, 9 }, - { 102, 238, 11, 179, 8 }, - { 145, 68, 191, 165, 11 }, }, - { { 31, 46, 124, 36, 11 }, - { 175, 25, 15, 113, 12 }, - { 210, 67, 231, 79, 8 }, - { 56, 239, 9, 143, 5 }, }, - { { 25, 102, 66, 71, 7 }, - { 47, 92, 72, 108, 1 }, - { 238, 36, 38, 105, 8 }, - { 131, 97, 35, 175, 4 }, }, - { { 25, 87, 212, 200, 4 }, - { 98, 175, 72, 120, 2 }, - { 33, 50, 190, 169, 8 }, - { 65, 225, 47, 84, 6 }, }, - { { 31, 168, 244, 240, 4 }, - { 163, 137, 210, 89, 14 }, - { 32, 242, 241, 95, 8 }, - { 121, 164, 185, 28, 5 }, }, - { { 27, 130, 70, 237, 8 }, - { 42, 105, 27, 77, 2 }, - { 27, 118, 36, 29, 8 }, - { 75, 45, 137, 101, 4 }, }, - { { 27, 174, 225, 15, 14 }, - { 59, 184, 89, 101, 13 }, - { 127, 8, 119, 93, 8 }, - { 186, 105, 161, 221, 12 }, }, - { { 34, 164, 182, 60, 10 }, - { 9, 241, 179, 49, 4 }, - { 83, 198, 210, 84, 4 }, - { 40, 204, 216, 249, 0 }, }, - { { 34, 191, 144, 18, 15 }, - { 69, 146, 249, 49, 9 }, - { 244, 128, 159, 212, 4 }, - { 152, 201, 244, 154, 2 }, }, - { { 35, 44, 21, 180, 0 }, - { 25, 1, 162, 113, 10 }, - { 2, 218, 131, 76, 4 }, - { 88, 228, 88, 9, 8 }, }, - { { 37, 90, 169, 102, 12 }, - { 216, 132, 111, 72, 13 }, - { 54, 105, 85, 170, 4 }, - { 177, 47, 98, 17, 11 }, }, - { { 39, 165, 175, 169, 7 }, - { 149, 243, 118, 101, 6 }, - { 233, 95, 90, 94, 4 }, - { 106, 102, 236, 250, 9 }, }, - { { 37, 244, 14, 66, 5 }, - { 197, 69, 116, 104, 1 }, - { 164, 39, 2, 250, 4 }, - { 129, 98, 234, 42, 3 }, }, - { { 40, 102, 85, 205, 14 }, - { 59, 53, 105, 60, 2 }, - { 123, 58, 166, 97, 4 }, - { 67, 201, 106, 205, 12 }, }, - { { 44, 66, 126, 14, 0 }, - { 170, 101, 44, 16, 5 }, - { 7, 7, 228, 35, 4 }, - { 160, 131, 74, 101, 5 }, }, - { { 42, 185, 124, 189, 0 }, - { 107, 35, 182, 21, 14 }, - { 11, 211, 233, 213, 4 }, - { 122, 134, 220, 77, 6 }, }, - { { 41, 70, 225, 210, 3 }, - { 54, 148, 168, 104, 7 }, - { 196, 184, 118, 41, 4 }, - { 225, 97, 82, 150, 12 }, }, - { { 45, 166, 40, 65, 0 }, - { 131, 0, 60, 108, 4 }, - { 8, 33, 70, 91, 4 }, - { 35, 99, 192, 12, 1 }, }, - { { 43, 251, 32, 154, 6 }, - { 67, 54, 248, 65, 15 }, - { 101, 144, 77, 253, 4 }, - { 248, 33, 246, 204, 2 }, }, - { { 54, 140, 214, 107, 12 }, - { 160, 233, 115, 61, 9 }, - { 61, 102, 179, 22, 12 }, - { 155, 204, 233, 112, 5 }, }, - { { 52, 135, 119, 124, 7 }, - { 188, 123, 250, 56, 4 }, - { 227, 238, 238, 18, 12 }, - { 33, 197, 253, 227, 13 }, }, - { { 52, 221, 235, 132, 0 }, - { 248, 206, 52, 32, 14 }, - { 2, 29, 123, 178, 12 }, - { 112, 66, 199, 49, 15 }, }, - { { 55, 145, 247, 111, 1 }, - { 252, 235, 50, 93, 5 }, - { 143, 110, 248, 158, 12 }, - { 171, 164, 205, 115, 15 }, }, - { { 58, 34, 142, 23, 5 }, - { 15, 201, 236, 5, 1 }, - { 174, 135, 20, 69, 12 }, - { 138, 3, 121, 63, 0 }, }, - { { 62, 19, 189, 64, 8 }, - { 210, 139, 45, 25, 4 }, - { 16, 43, 220, 135, 12 }, - { 41, 139, 77, 20, 11 }, }, - { { 60, 152, 67, 202, 2 }, - { 242, 120, 48, 8, 11 }, - { 69, 60, 33, 147, 12 }, - { 209, 0, 193, 228, 15 }, }, - { { 57, 88, 157, 23, 9 }, - { 94, 141, 165, 84, 9 }, - { 158, 139, 145, 169, 12 }, - { 146, 170, 91, 23, 10 }, }, - { { 57, 116, 218, 238, 11 }, - { 111, 252, 39, 120, 3 }, - { 215, 117, 178, 233, 12 }, - { 193, 238, 67, 255, 6 }, }, - { { 63, 109, 188, 115, 1 }, - { 135, 143, 166, 125, 13 }, - { 140, 227, 219, 111, 12 }, - { 187, 230, 95, 30, 1 }, }, - { { 61, 107, 192, 80, 12 }, - { 163, 142, 233, 72, 8 }, - { 48, 160, 61, 107, 12 }, - { 17, 41, 119, 28, 5 }, }, - { { 57, 171, 39, 73, 7 }, - { 23, 123, 120, 76, 12 }, - { 233, 46, 77, 89, 12 }, - { 51, 33, 237, 238, 8 }, }, - { { 70, 2, 78, 37, 14 }, - { 168, 81, 79, 133, 0 }, - { 122, 71, 36, 6, 2 }, - { 10, 31, 40, 161, 5 }, }, - { { 70, 130, 186, 11, 12 }, - { 128, 224, 93, 149, 5 }, - { 61, 5, 212, 22, 2 }, - { 170, 155, 160, 112, 1 }, }, - { { 66, 233, 205, 90, 14 }, - { 49, 183, 213, 137, 9 }, - { 117, 171, 57, 116, 2 }, - { 153, 26, 190, 216, 12 }, }, - { { 68, 201, 183, 179, 15 }, - { 148, 215, 211, 148, 15 }, - { 252, 222, 217, 50, 2 }, - { 242, 156, 190, 178, 9 }, }, - { { 64, 199, 212, 30, 9 }, - { 44, 167, 153, 176, 1 }, - { 151, 130, 190, 48, 2 }, - { 128, 217, 158, 83, 4 }, }, - { { 70, 210, 180, 204, 14 }, - { 200, 181, 89, 153, 6 }, - { 115, 50, 212, 182, 2 }, - { 105, 153, 170, 209, 3 }, }, - { { 67, 25, 83, 86, 11 }, - { 124, 82, 129, 217, 9 }, - { 214, 172, 169, 140, 2 }, - { 153, 184, 20, 163, 14 }, }, - { { 65, 34, 230, 221, 9 }, - { 45, 225, 137, 204, 6 }, - { 155, 182, 116, 72, 2 }, - { 99, 57, 24, 123, 4 }, }, - { { 71, 83, 165, 154, 11 }, - { 212, 183, 137, 193, 7 }, - { 213, 154, 92, 174, 2 }, - { 232, 57, 30, 210, 11 }, }, - { { 78, 30, 241, 224, 8 }, - { 242, 128, 11, 185, 14 }, - { 16, 120, 247, 135, 2 }, - { 121, 221, 0, 20, 15 }, }, - { { 78, 74, 192, 150, 0 }, - { 170, 132, 136, 129, 11 }, - { 6, 144, 53, 39, 2 }, - { 216, 17, 18, 21, 5 }, }, - { { 78, 95, 170, 6, 15 }, - { 206, 214, 77, 161, 13 }, - { 246, 5, 95, 167, 2 }, - { 184, 91, 38, 183, 3 }, }, - { { 74, 141, 50, 148, 3 }, - { 14, 82, 144, 177, 14 }, - { 194, 148, 203, 21, 2 }, - { 120, 208, 148, 167, 0 }, }, - { { 73, 21, 148, 179, 9 }, - { 70, 131, 131, 244, 3 }, - { 156, 210, 154, 137, 2 }, - { 194, 252, 28, 22, 2 }, }, - { { 77, 77, 219, 98, 1 }, - { 182, 198, 6, 248, 9 }, - { 132, 109, 187, 43, 2 }, - { 145, 246, 6, 54, 13 }, }, - { { 75, 167, 97, 232, 1 }, - { 55, 34, 26, 233, 6 }, - { 129, 120, 110, 93, 2 }, - { 105, 117, 132, 78, 12 }, }, - { { 73, 212, 131, 216, 14 }, - { 82, 244, 209, 232, 2 }, - { 113, 188, 18, 185, 2 }, - { 65, 120, 178, 244, 10 }, }, - { { 86, 41, 14, 246, 12 }, - { 137, 75, 199, 137, 11 }, - { 54, 247, 9, 70, 10 }, - { 217, 30, 61, 41, 1 }, }, - { { 83, 126, 213, 255, 12 }, - { 121, 173, 203, 253, 11 }, - { 63, 250, 183, 236, 10 }, - { 219, 253, 59, 89, 14 }, }, - { { 85, 245, 167, 175, 10 }, - { 217, 255, 19, 228, 7 }, - { 95, 94, 90, 250, 10 }, - { 226, 124, 143, 249, 11 }, }, - { { 85, 213, 234, 100, 15 }, - { 236, 222, 87, 232, 4 }, - { 242, 101, 122, 186, 10 }, - { 33, 126, 167, 179, 7 }, }, - { { 88, 27, 171, 29, 10 }, - { 90, 250, 141, 132, 12 }, - { 91, 141, 93, 129, 10 }, - { 50, 27, 21, 245, 10 }, }, - { { 94, 190, 146, 109, 13 }, - { 207, 232, 91, 189, 8 }, - { 187, 100, 151, 215, 10 }, - { 27, 221, 161, 127, 3 }, }, - { { 95, 16, 249, 155, 5 }, - { 246, 168, 196, 213, 7 }, - { 173, 153, 240, 143, 10 }, - { 234, 178, 49, 86, 15 }, }, - { { 93, 30, 223, 165, 12 }, - { 250, 201, 79, 244, 10 }, - { 58, 95, 183, 139, 10 }, - { 82, 255, 41, 53, 15 }, }, - { { 95, 113, 141, 240, 2 }, - { 211, 159, 134, 201, 2 }, - { 64, 251, 24, 239, 10 }, - { 73, 54, 31, 156, 11 }, }, - { { 93, 225, 30, 70, 8 }, - { 139, 79, 21, 216, 1 }, - { 22, 39, 136, 123, 10 }, - { 129, 186, 143, 45, 1 }, }, - { { 96, 51, 187, 36, 7 }, - { 93, 210, 110, 144, 4 }, - { 226, 77, 220, 192, 6 }, - { 32, 151, 100, 187, 10 }, }, - { { 100, 88, 26, 254, 1 }, - { 204, 100, 166, 152, 11 }, - { 135, 245, 129, 162, 6 }, - { 209, 150, 82, 99, 3 }, }, - { { 99, 200, 221, 167, 6 }, - { 56, 149, 118, 213, 11 }, - { 110, 91, 177, 60, 6 }, - { 218, 182, 234, 145, 12 }, }, - { { 97, 218, 61, 143, 13 }, - { 92, 37, 125, 212, 15 }, - { 191, 27, 197, 184, 6 }, - { 242, 187, 234, 67, 10 }, }, - { { 110, 58, 34, 175, 10 }, - { 203, 112, 43, 133, 15 }, - { 95, 84, 69, 199, 6 }, - { 250, 29, 64, 237, 3 }, }, - { { 110, 97, 5, 183, 1 }, - { 159, 7, 162, 133, 3 }, - { 142, 218, 8, 103, 6 }, - { 202, 20, 94, 15, 9 }, }, - { { 106, 137, 169, 232, 12 }, - { 18, 162, 119, 137, 14 }, - { 49, 121, 89, 21, 6 }, - { 121, 30, 228, 84, 8 }, }, - { { 106, 151, 34, 79, 5 }, - { 78, 98, 120, 173, 5 }, - { 175, 36, 78, 149, 6 }, - { 171, 81, 228, 103, 2 }, }, - { { 107, 18, 195, 128, 1 }, - { 118, 192, 40, 193, 2 }, - { 128, 28, 52, 141, 6 }, - { 72, 49, 64, 54, 14 }, }, - { { 107, 104, 75, 34, 10 }, - { 51, 84, 39, 193, 9 }, - { 84, 77, 33, 109, 6 }, - { 152, 62, 66, 172, 12 }, }, - { { 111, 148, 193, 87, 9 }, - { 254, 128, 177, 237, 1 }, - { 158, 168, 50, 159, 6 }, - { 139, 120, 208, 23, 15 }, }, - { { 109, 166, 254, 160, 13 }, - { 167, 193, 127, 240, 6 }, - { 176, 87, 246, 91, 6 }, - { 96, 255, 232, 62, 5 }, }, - { { 111, 234, 202, 69, 7 }, - { 175, 212, 124, 205, 8 }, - { 234, 37, 53, 127, 6 }, - { 27, 51, 226, 191, 5 }, }, - { { 112, 61, 56, 166, 0 }, - { 73, 10, 38, 176, 15 }, - { 6, 81, 203, 192, 14 }, - { 240, 214, 69, 9, 2 }, }, - { { 118, 108, 53, 231, 8 }, - { 153, 13, 35, 189, 15 }, - { 30, 122, 195, 102, 14 }, - { 251, 220, 75, 9, 9 }, }, - { { 112, 74, 13, 255, 6 }, - { 24, 61, 238, 140, 11 }, - { 111, 251, 5, 32, 14 }, - { 211, 23, 123, 193, 8 }, }, - { { 117, 120, 169, 200, 0 }, - { 209, 172, 36, 200, 14 }, - { 1, 57, 81, 234, 14 }, - { 113, 50, 67, 88, 11 }, }, - { { 113, 74, 112, 19, 8 }, - { 32, 12, 169, 212, 13 }, - { 28, 128, 229, 40, 14 }, - { 178, 185, 83, 0, 4 }, }, - { { 117, 127, 140, 187, 9 }, - { 197, 175, 175, 228, 11 }, - { 157, 211, 31, 234, 14 }, - { 210, 127, 95, 90, 3 }, }, - { { 124, 35, 104, 51, 1 }, - { 167, 10, 174, 132, 5 }, - { 140, 193, 108, 67, 14 }, - { 162, 23, 85, 14, 5 }, }, - { { 124, 181, 167, 211, 1 }, - { 215, 203, 176, 172, 7 }, - { 140, 190, 90, 211, 14 }, - { 227, 80, 221, 62, 11 }, }, - { { 124, 248, 44, 237, 14 }, - { 203, 61, 119, 140, 14 }, - { 123, 115, 65, 243, 14 }, - { 115, 30, 235, 205, 3 }, }, - { { 127, 36, 226, 52, 15 }, - { 175, 216, 227, 225, 4 }, - { 242, 196, 114, 79, 14 }, - { 40, 124, 113, 191, 5 }, }, - { { 127, 71, 41, 141, 8 }, - { 154, 46, 45, 229, 6 }, - { 27, 25, 78, 47, 14 }, - { 106, 123, 71, 69, 9 }, }, - { { 134, 216, 3, 209, 9 }, - { 212, 68, 145, 15, 10 }, - { 152, 188, 1, 182, 1 }, - { 95, 8, 146, 34, 11 }, }, - { { 131, 139, 27, 161, 3 }, - { 20, 82, 30, 87, 10 }, - { 200, 93, 141, 28, 1 }, - { 94, 167, 132, 162, 8 }, }, - { { 135, 162, 121, 197, 9 }, - { 189, 0, 29, 95, 6 }, - { 154, 57, 228, 94, 1 }, - { 111, 171, 128, 11, 13 }, }, - { { 138, 67, 100, 140, 14 }, - { 42, 55, 73, 3, 6 }, - { 115, 18, 108, 37, 1 }, - { 108, 9, 46, 197, 4 }, }, - { { 136, 147, 59, 76, 8 }, - { 90, 98, 29, 26, 4 }, - { 19, 45, 204, 145, 1 }, - { 37, 139, 132, 101, 10 }, }, - { { 143, 33, 223, 78, 3 }, - { 191, 243, 4, 91, 1 }, - { 199, 47, 184, 79, 1 }, - { 141, 162, 12, 255, 13 }, }, - { { 141, 132, 53, 114, 9 }, - { 150, 1, 147, 122, 5 }, - { 148, 234, 194, 27, 1 }, - { 165, 236, 152, 6, 9 }, }, - { { 141, 136, 215, 31, 13 }, - { 190, 225, 209, 86, 9 }, - { 191, 142, 177, 27, 1 }, - { 150, 168, 184, 119, 13 }, }, - { { 137, 159, 120, 252, 13 }, - { 110, 34, 223, 122, 14 }, - { 179, 241, 239, 153, 1 }, - { 117, 239, 180, 71, 6 }, }, - { { 146, 107, 22, 121, 12 }, - { 1, 111, 203, 31, 8 }, - { 57, 230, 141, 100, 9 }, - { 31, 141, 63, 104, 0 }, }, - { { 148, 142, 34, 241, 2 }, - { 128, 88, 154, 46, 14 }, - { 72, 244, 71, 18, 9 }, - { 119, 69, 145, 160, 1 }, }, - { { 144, 229, 230, 49, 7 }, - { 37, 223, 210, 38, 4 }, - { 232, 198, 122, 112, 9 }, - { 38, 68, 191, 186, 4 }, }, - { { 150, 216, 133, 42, 1 }, - { 212, 173, 18, 3, 9 }, - { 133, 74, 17, 182, 9 }, - { 156, 4, 139, 82, 11 }, }, - { { 149, 57, 59, 164, 6 }, - { 217, 90, 70, 82, 14 }, - { 98, 93, 201, 202, 9 }, - { 116, 166, 37, 169, 11 }, }, - { { 149, 60, 251, 77, 13 }, - { 253, 232, 69, 126, 12 }, - { 187, 45, 243, 202, 9 }, - { 55, 234, 33, 123, 15 }, }, - { { 145, 62, 170, 18, 6 }, - { 65, 216, 204, 98, 13 }, - { 100, 133, 87, 200, 9 }, - { 180, 99, 49, 184, 2 }, }, - { { 151, 111, 90, 175, 9 }, - { 173, 110, 15, 119, 11 }, - { 159, 85, 175, 110, 9 }, - { 222, 239, 7, 107, 5 }, }, - { { 145, 178, 41, 253, 10 }, - { 89, 56, 159, 78, 6 }, - { 91, 249, 68, 216, 9 }, - { 103, 47, 145, 201, 10 }, }, - { { 145, 211, 250, 118, 1 }, - { 108, 206, 158, 90, 5 }, - { 134, 229, 252, 184, 9 }, - { 165, 167, 151, 51, 6 }, }, - { { 154, 112, 134, 200, 8 }, - { 67, 237, 1, 11, 2 }, - { 17, 54, 16, 229, 9 }, - { 77, 8, 11, 124, 2 }, }, - { { 152, 142, 205, 3, 1 }, - { 54, 137, 28, 38, 9 }, - { 140, 11, 55, 17, 9 }, - { 150, 67, 137, 22, 12 }, }, - { { 152, 199, 16, 151, 10 }, - { 10, 30, 153, 54, 3 }, - { 94, 144, 142, 49, 9 }, - { 198, 201, 151, 133, 0 }, }, - { { 157, 203, 235, 70, 6 }, - { 186, 222, 92, 74, 13 }, - { 102, 45, 125, 59, 9 }, - { 181, 35, 167, 181, 13 }, }, - { { 164, 40, 245, 182, 14 }, - { 185, 145, 227, 18, 15 }, - { 118, 218, 241, 66, 5 }, - { 244, 140, 120, 153, 13 }, }, - { { 163, 55, 241, 121, 3 }, - { 117, 178, 170, 127, 4 }, - { 201, 232, 254, 204, 5 }, - { 47, 229, 84, 218, 14 }, }, - { { 163, 68, 64, 245, 10 }, - { 40, 20, 163, 111, 2 }, - { 90, 240, 34, 44, 5 }, - { 79, 108, 82, 129, 4 }, }, - { { 161, 127, 173, 133, 8 }, - { 89, 135, 45, 102, 14 }, - { 26, 27, 95, 232, 5 }, - { 118, 107, 78, 25, 10 }, }, - { { 167, 210, 150, 35, 13 }, - { 196, 197, 123, 87, 1 }, - { 188, 70, 148, 190, 5 }, - { 142, 173, 234, 50, 3 }, }, - { { 168, 69, 112, 43, 11 }, - { 38, 54, 35, 54, 5 }, - { 221, 64, 234, 33, 5 }, - { 166, 204, 70, 198, 4 }, }, - { { 174, 72, 127, 160, 9 }, - { 182, 69, 39, 19, 14 }, - { 144, 95, 225, 39, 5 }, - { 124, 142, 74, 38, 13 }, }, - { { 172, 79, 182, 214, 8 }, - { 138, 199, 169, 58, 15 }, - { 22, 182, 223, 35, 5 }, - { 245, 201, 94, 53, 1 }, }, - { { 168, 168, 211, 133, 3 }, - { 63, 208, 48, 22, 10 }, - { 202, 28, 177, 81, 5 }, - { 86, 128, 192, 191, 12 }, }, - { { 169, 139, 10, 203, 8 }, - { 2, 98, 61, 78, 11 }, - { 29, 53, 13, 25, 5 }, - { 215, 43, 196, 100, 0 }, }, - { { 173, 254, 140, 222, 2 }, - { 203, 181, 188, 106, 11 }, - { 71, 179, 23, 251, 5 }, - { 213, 99, 218, 221, 3 }, }, - { { 180, 239, 46, 46, 14 }, - { 137, 127, 127, 34, 13 }, - { 119, 71, 79, 114, 13 }, - { 180, 79, 239, 233, 1 }, }, - { { 183, 153, 137, 199, 0 }, - { 216, 138, 52, 79, 11 }, - { 14, 57, 25, 158, 13 }, - { 223, 34, 197, 17, 11 }, }, - { { 190, 12, 162, 14, 12 }, - { 138, 232, 97, 35, 13 }, - { 55, 4, 83, 7, 13 }, - { 188, 72, 97, 117, 1 }, }, - { { 188, 112, 34, 122, 9 }, - { 199, 108, 163, 10, 5 }, - { 149, 228, 64, 227, 13 }, - { 165, 12, 83, 110, 3 }, }, - { { 190, 188, 47, 145, 10 }, - { 211, 89, 181, 39, 14 }, - { 88, 159, 67, 215, 13 }, - { 126, 74, 217, 172, 11 }, }, - { { 184, 233, 10, 152, 3 }, - { 7, 126, 180, 2, 10 }, - { 193, 149, 9, 113, 13 }, - { 84, 2, 215, 238, 0 }, }, - { { 189, 10, 48, 236, 8 }, - { 138, 40, 43, 90, 14 }, - { 19, 112, 197, 11, 13 }, - { 117, 173, 65, 69, 1 }, }, - { { 194, 2, 224, 243, 1 }, - { 36, 128, 138, 143, 7 }, - { 140, 240, 116, 4, 3 }, - { 239, 21, 16, 18, 4 }, }, - { { 194, 107, 50, 227, 7 }, - { 5, 86, 74, 159, 15 }, - { 236, 116, 205, 100, 3 }, - { 255, 149, 38, 170, 0 }, }, - { { 198, 202, 66, 106, 8 }, - { 160, 100, 27, 139, 9 }, - { 21, 100, 37, 54, 3 }, - { 157, 29, 130, 96, 5 }, }, - { { 199, 30, 238, 104, 14 }, - { 224, 241, 79, 235, 12 }, - { 113, 103, 119, 142, 3 }, - { 61, 127, 40, 240, 7 }, }, - { { 199, 125, 46, 145, 3 }, - { 197, 87, 132, 231, 14 }, - { 200, 151, 75, 238, 3 }, - { 126, 114, 30, 170, 3 }, }, - { { 206, 60, 32, 116, 2 }, - { 203, 16, 130, 171, 12 }, - { 66, 224, 67, 199, 3 }, - { 61, 84, 16, 141, 3 }, }, - { { 204, 74, 185, 197, 7 }, - { 158, 148, 76, 158, 14 }, - { 234, 57, 213, 35, 3 }, - { 119, 147, 34, 151, 9 }, }, - { { 206, 247, 99, 220, 13 }, - { 255, 102, 217, 171, 6 }, - { 179, 188, 110, 247, 3 }, - { 109, 89, 182, 111, 15 }, }, - { { 205, 67, 34, 202, 2 }, - { 130, 118, 8, 202, 7 }, - { 69, 52, 76, 43, 3 }, - { 229, 49, 6, 228, 1 }, }, - { { 207, 183, 204, 29, 0 }, - { 235, 163, 156, 231, 0 }, - { 11, 131, 62, 223, 3 }, - { 14, 115, 156, 93, 7 }, }, - { { 201, 206, 200, 53, 10 }, - { 42, 148, 159, 230, 8 }, - { 90, 193, 55, 57, 3 }, - { 22, 127, 146, 149, 4 }, }, - { { 207, 243, 75, 113, 3 }, - { 247, 86, 158, 207, 0 }, - { 200, 237, 44, 255, 3 }, - { 15, 55, 150, 174, 15 }, }, - { { 214, 46, 123, 112, 13 }, - { 181, 72, 207, 187, 12 }, - { 176, 237, 231, 70, 11 }, - { 61, 223, 49, 42, 13 }, }, - { { 212, 23, 75, 59, 4 }, - { 240, 106, 206, 166, 1 }, - { 45, 205, 46, 130, 11 }, - { 134, 87, 53, 96, 15 }, }, - { { 215, 141, 250, 151, 14 }, - { 168, 218, 213, 247, 15 }, - { 126, 149, 251, 30, 11 }, - { 254, 250, 181, 177, 5 }, }, - { { 209, 216, 245, 85, 1 }, - { 124, 141, 144, 222, 12 }, - { 138, 170, 241, 184, 11 }, - { 55, 176, 155, 19, 14 }, }, - { { 213, 207, 225, 211, 9 }, - { 180, 142, 153, 238, 15 }, - { 156, 184, 127, 58, 11 }, - { 247, 121, 151, 18, 13 }, }, - { { 218, 22, 168, 204, 9 }, - { 78, 168, 13, 171, 6 }, - { 147, 49, 86, 133, 11 }, - { 109, 91, 1, 87, 2 }, }, - { { 216, 76, 68, 133, 9 }, - { 46, 13, 1, 166, 10 }, - { 154, 18, 35, 33, 11 }, - { 86, 88, 11, 7, 4 }, }, - { { 220, 217, 114, 142, 13 }, - { 238, 110, 81, 146, 15 }, - { 183, 20, 233, 179, 11 }, - { 244, 152, 167, 103, 7 }, }, - { { 223, 103, 17, 126, 8 }, - { 155, 46, 139, 251, 1 }, - { 23, 232, 142, 111, 11 }, - { 141, 253, 23, 77, 9 }, }, - { { 219, 153, 125, 230, 7 }, - { 126, 27, 86, 219, 15 }, - { 230, 123, 233, 157, 11 }, - { 253, 182, 173, 135, 14 }, }, - { { 221, 171, 142, 49, 14 }, - { 131, 219, 223, 198, 8 }, - { 120, 199, 29, 91, 11 }, - { 22, 63, 189, 188, 1 }, }, - { { 224, 25, 8, 76, 13 }, - { 76, 34, 101, 138, 8 }, - { 179, 33, 9, 128, 7 }, - { 21, 26, 100, 67, 2 }, }, - { { 230, 54, 218, 82, 1 }, - { 229, 192, 172, 187, 1 }, - { 132, 165, 182, 198, 7 }, - { 141, 211, 80, 58, 7 }, }, - { { 226, 172, 199, 155, 0 }, - { 49, 225, 176, 167, 11 }, - { 13, 158, 51, 84, 7 }, - { 222, 80, 216, 120, 12 }, }, - { { 228, 141, 33, 98, 0 }, - { 144, 2, 50, 170, 13 }, - { 4, 104, 75, 18, 7 }, - { 181, 84, 196, 0, 9 }, }, - { { 226, 254, 208, 197, 1 }, - { 109, 132, 56, 191, 10 }, - { 138, 48, 183, 244, 7 }, - { 95, 209, 194, 27, 6 }, }, - { { 225, 58, 125, 2, 10 }, - { 113, 17, 45, 210, 13 }, - { 84, 11, 229, 200, 7 }, - { 180, 187, 72, 136, 14 }, }, - { { 231, 208, 91, 142, 5 }, - { 252, 100, 116, 211, 3 }, - { 167, 29, 160, 190, 7 }, - { 204, 178, 226, 99, 15 }, }, - { { 236, 48, 156, 107, 5 }, - { 199, 161, 102, 158, 1 }, - { 173, 99, 144, 195, 7 }, - { 135, 150, 104, 94, 3 }, }, - { { 236, 170, 73, 210, 0 }, - { 179, 0, 188, 138, 11 }, - { 4, 185, 37, 83, 7 }, - { 213, 19, 208, 12, 13 }, }, - { { 238, 179, 122, 196, 6 }, - { 235, 82, 124, 155, 6 }, - { 98, 53, 236, 215, 7 }, - { 109, 147, 228, 173, 7 }, }, - { { 232, 224, 103, 46, 2 }, - { 59, 117, 50, 130, 5 }, - { 71, 78, 96, 113, 7 }, - { 164, 20, 202, 237, 12 }, }, - { { 234, 229, 213, 36, 12 }, - { 59, 135, 115, 179, 0 }, - { 50, 74, 186, 117, 7 }, - { 12, 220, 238, 29, 12 }, }, - { { 237, 107, 28, 44, 2 }, - { 139, 55, 46, 210, 8 }, - { 67, 67, 141, 107, 7 }, - { 20, 183, 78, 205, 1 }, }, - { { 235, 200, 175, 29, 6 }, - { 26, 245, 244, 199, 12 }, - { 107, 143, 81, 61, 7 }, - { 62, 50, 250, 245, 8 }, }, - { { 242, 5, 98, 212, 5 }, - { 44, 74, 224, 171, 6 }, - { 162, 180, 106, 4, 15 }, - { 109, 80, 117, 35, 4 }, }, - { { 246, 25, 188, 251, 2 }, - { 192, 187, 166, 159, 15 }, - { 77, 243, 217, 134, 15 }, - { 255, 150, 93, 208, 3 }, }, - { { 246, 163, 92, 109, 11 }, - { 173, 59, 63, 159, 0 }, - { 219, 99, 172, 86, 15 }, - { 15, 159, 205, 203, 5 }, }, - { { 244, 241, 189, 15, 0 }, - { 217, 175, 52, 150, 5 }, - { 15, 11, 216, 242, 15 }, - { 166, 146, 207, 89, 11 }, }, - { { 241, 106, 155, 67, 5 }, - { 21, 204, 108, 222, 9 }, - { 172, 45, 149, 104, 15 }, - { 151, 179, 99, 58, 8 }, }, - { { 241, 178, 145, 41, 14 }, - { 81, 184, 123, 214, 0 }, - { 121, 72, 148, 216, 15 }, - { 6, 189, 225, 216, 10 }, }, - { { 250, 84, 91, 243, 5 }, - { 118, 76, 230, 191, 3 }, - { 172, 253, 162, 165, 15 }, - { 207, 214, 115, 38, 14 }, }, - { { 254, 110, 134, 124, 6 }, - { 139, 253, 234, 171, 8 }, - { 99, 230, 23, 103, 15 }, - { 29, 85, 123, 253, 1 }, }, - { { 249, 13, 185, 67, 2 }, - { 18, 154, 36, 254, 13 }, - { 76, 41, 219, 9, 15 }, - { 183, 242, 69, 148, 8 }, }, - { { 249, 105, 102, 43, 13 }, - { 39, 111, 99, 198, 13 }, - { 189, 70, 105, 105, 15 }, - { 182, 60, 111, 110, 4 }, }, - { { 251, 65, 203, 72, 4 }, - { 50, 238, 100, 203, 0 }, - { 33, 45, 56, 45, 15 }, - { 13, 50, 103, 116, 12 }, }, - { { 253, 87, 191, 152, 5 }, - { 214, 239, 236, 242, 6 }, - { 161, 159, 222, 171, 15 }, - { 100, 243, 127, 118, 11 }, }, - { { 251, 152, 144, 126, 6 }, - { 74, 184, 242, 219, 9 }, - { 103, 224, 145, 157, 15 }, - { 157, 180, 241, 213, 2 }, }, - { { 255, 234, 33, 198, 3 }, - { 159, 28, 56, 203, 15 }, - { 198, 56, 69, 127, 15 }, - { 253, 49, 195, 143, 9 }, }, - { { 163, 165, 111, 69, 0 }, - { 57, 67, 52, 111, 4 }, - { 10, 47, 106, 92, 5 }, - { 47, 98, 204, 41, 12 }, }, - { { 161, 152, 104, 48, 2 }, - { 96, 16, 182, 66, 12 }, - { 64, 193, 97, 152, 5 }, - { 52, 38, 208, 128, 6 }, }, - { { 15, 55, 131, 43, 0 }, - { 211, 226, 10, 101, 1 }, - { 13, 76, 30, 207, 0 }, - { 138, 101, 4, 124, 11 }, }, - { { 38, 236, 72, 39, 2 }, - { 169, 20, 54, 37, 9 }, - { 78, 65, 35, 118, 4 }, - { 154, 70, 194, 137, 5 }, }, - { { 65, 152, 184, 168, 15 }, - { 68, 176, 87, 208, 14 }, - { 241, 81, 209, 152, 2 }, - { 112, 190, 160, 210, 2 }, }, - { { 78, 181, 67, 138, 4 }, - { 243, 98, 80, 161, 3 }, - { 37, 28, 42, 215, 2 }, - { 200, 80, 164, 108, 15 }, }, - { { 99, 197, 227, 123, 10 }, - { 48, 246, 179, 237, 5 }, - { 93, 236, 122, 60, 6 }, - { 171, 124, 214, 240, 12 }, }, - { { 110, 89, 221, 230, 12 }, - { 250, 135, 103, 153, 11 }, - { 54, 123, 185, 167, 6 }, - { 217, 158, 110, 21, 15 }, }, - { { 128, 212, 89, 240, 8 }, - { 112, 4, 151, 58, 2 }, - { 16, 249, 162, 176, 1 }, - { 69, 206, 146, 0, 14 }, }, - { { 152, 8, 136, 159, 13 }, - { 14, 168, 197, 6, 11 }, - { 191, 145, 17, 1, 9 }, - { 214, 10, 49, 87, 0 }, }, - { { 163, 6, 103, 166, 15 }, - { 60, 81, 107, 99, 7 }, - { 246, 94, 102, 12, 5 }, - { 236, 109, 104, 163, 12 }, }, - { { 2, 25, 166, 20, 7 }, - { 76, 211, 192, 1, 12 }, - { 226, 134, 89, 132, 0 }, - { 56, 0, 60, 179, 2 }, }, - { { 2, 21, 202, 78, 2 }, - { 104, 242, 4, 41, 1 }, - { 71, 37, 58, 132, 0 }, - { 137, 66, 4, 241, 6 }, }, - { { 0, 104, 204, 57, 9 }, - { 37, 165, 135, 4, 8 }, - { 153, 195, 49, 96, 0 }, - { 18, 14, 26, 90, 4 }, }, - { { 0, 161, 19, 254, 8 }, - { 25, 98, 147, 24, 3 }, - { 23, 252, 136, 80, 0 }, - { 193, 140, 148, 105, 8 }, }, - { { 2, 185, 86, 117, 5 }, - { 109, 67, 210, 29, 8 }, - { 170, 230, 169, 212, 0 }, - { 27, 132, 188, 43, 6 }, }, - { { 2, 198, 187, 83, 2 }, - { 16, 212, 156, 61, 5 }, - { 76, 173, 214, 52, 0 }, - { 171, 195, 146, 176, 8 }, }, - { { 2, 243, 31, 29, 9 }, - { 93, 103, 157, 21, 0 }, - { 155, 143, 140, 244, 0 }, - { 10, 139, 158, 107, 10 }, }, - { { 7, 42, 193, 126, 3 }, - { 189, 176, 138, 73, 9 }, - { 199, 232, 53, 78, 0 }, - { 153, 37, 16, 219, 13 }, }, - { { 7, 55, 141, 151, 2 }, - { 217, 147, 140, 101, 3 }, - { 78, 155, 30, 206, 0 }, - { 202, 99, 28, 153, 11 }, }, - { { 1, 111, 31, 231, 5 }, - { 29, 71, 78, 124, 11 }, - { 174, 127, 143, 104, 0 }, - { 211, 231, 46, 43, 8 }, }, - { { 1, 119, 48, 21, 5 }, - { 77, 6, 200, 116, 4 }, - { 170, 128, 206, 232, 0 }, - { 34, 225, 54, 11, 2 }, }, - { { 7, 149, 114, 65, 10 }, - { 224, 82, 17, 125, 4 }, - { 88, 36, 234, 158, 0 }, - { 43, 232, 132, 160, 7 }, }, - { { 7, 200, 163, 134, 14 }, - { 152, 212, 81, 65, 15 }, - { 118, 28, 81, 62, 0 }, - { 248, 40, 162, 177, 9 }, }, - { { 5, 254, 251, 247, 9 }, - { 253, 196, 159, 124, 15 }, - { 158, 253, 247, 250, 0 }, - { 243, 239, 146, 59, 15 }, }, - { { 12, 21, 243, 16, 1 }, - { 246, 194, 128, 48, 4 }, - { 128, 140, 250, 131, 0 }, - { 32, 192, 20, 54, 15 }, }, - { { 10, 68, 98, 226, 14 }, - { 34, 84, 67, 41, 7 }, - { 116, 116, 98, 37, 0 }, - { 233, 76, 34, 164, 4 }, }, - { { 8, 94, 55, 238, 9 }, - { 94, 101, 11, 56, 15 }, - { 151, 126, 199, 161, 0 }, - { 241, 205, 10, 103, 10 }, }, - { { 8, 82, 222, 18, 13 }, - { 102, 197, 205, 16, 1 }, - { 180, 135, 180, 161, 0 }, - { 128, 139, 58, 54, 6 }, }, - { { 12, 102, 136, 3, 9 }, - { 135, 132, 13, 36, 1 }, - { 156, 1, 22, 99, 0 }, - { 130, 75, 2, 30, 1 }, }, - { { 8, 140, 186, 71, 15 }, - { 14, 208, 85, 60, 13 }, - { 254, 37, 211, 17, 0 }, - { 179, 202, 160, 183, 0 }, }, - { { 12, 172, 39, 30, 5 }, - { 159, 97, 208, 32, 13 }, - { 167, 142, 67, 83, 0 }, - { 176, 64, 184, 111, 9 }, }, - { { 8, 179, 56, 11, 15 }, - { 71, 50, 93, 20, 5 }, - { 253, 1, 204, 209, 0 }, - { 162, 139, 164, 206, 2 }, }, - { { 12, 162, 165, 217, 4 }, - { 147, 161, 216, 12, 6 }, - { 41, 186, 84, 83, 0 }, - { 99, 1, 184, 92, 9 }, }, - { { 8, 227, 82, 192, 1 }, - { 39, 70, 24, 24, 2 }, - { 128, 52, 172, 113, 0 }, - { 65, 129, 134, 46, 4 }, }, - { { 14, 255, 68, 245, 14 }, - { 235, 23, 219, 45, 10 }, - { 122, 242, 47, 247, 0 }, - { 91, 77, 190, 141, 7 }, }, - { { 14, 255, 93, 234, 11 }, - { 247, 55, 31, 57, 11 }, - { 213, 123, 175, 247, 0 }, - { 217, 207, 142, 206, 15 }, }, - { { 11, 52, 109, 201, 14 }, - { 115, 49, 69, 109, 6 }, - { 121, 59, 98, 205, 0 }, - { 107, 106, 40, 204, 14 }, }, - { { 13, 9, 254, 187, 3 }, - { 166, 243, 134, 84, 15 }, - { 205, 215, 249, 11, 0 }, - { 242, 166, 28, 246, 5 }, }, - { { 15, 16, 170, 146, 6 }, - { 194, 208, 196, 65, 7 }, - { 100, 149, 80, 143, 0 }, - { 232, 34, 48, 180, 3 }, }, - { { 13, 31, 101, 167, 14 }, - { 250, 19, 75, 100, 15 }, - { 126, 90, 111, 139, 0 }, - { 242, 109, 44, 133, 15 }, }, - { { 15, 177, 95, 160, 0 }, - { 243, 67, 22, 81, 2 }, - { 0, 95, 168, 223, 0 }, - { 72, 166, 140, 44, 15 }, }, - { { 13, 162, 5, 35, 3 }, - { 151, 17, 26, 68, 1 }, - { 204, 74, 4, 91, 0 }, - { 130, 37, 136, 142, 9 }, }, - { { 13, 175, 35, 219, 11 }, - { 151, 114, 153, 108, 15 }, - { 221, 188, 79, 91, 0 }, - { 243, 105, 148, 238, 9 }, }, - { { 11, 240, 165, 238, 4 }, - { 91, 165, 82, 73, 7 }, - { 39, 122, 80, 253, 0 }, - { 233, 36, 170, 93, 10 }, }, - { { 13, 200, 153, 251, 14 }, - { 146, 180, 215, 92, 11 }, - { 125, 249, 145, 59, 0 }, - { 211, 174, 178, 212, 9 }, }, - { { 15, 253, 48, 39, 7 }, - { 207, 22, 82, 117, 13 }, - { 238, 64, 203, 255, 0 }, - { 186, 228, 166, 143, 3 }, }, - { { 11, 199, 93, 86, 2 }, - { 58, 23, 156, 121, 1 }, - { 70, 171, 174, 61, 0 }, - { 137, 227, 158, 133, 12 }, }, - { { 18, 47, 163, 1, 13 }, - { 21, 202, 73, 37, 12 }, - { 184, 12, 95, 68, 8 }, - { 58, 73, 37, 58, 8 }, }, - { { 18, 117, 72, 114, 5 }, - { 101, 14, 198, 41, 1 }, - { 164, 225, 42, 228, 8 }, - { 137, 70, 55, 10, 6 }, }, - { { 16, 79, 174, 98, 7 }, - { 4, 223, 78, 40, 13 }, - { 228, 103, 95, 32, 8 }, - { 177, 71, 47, 178, 0 }, }, - { { 16, 140, 138, 232, 11 }, - { 4, 248, 23, 40, 10 }, - { 209, 117, 19, 16, 8 }, - { 81, 78, 129, 242, 0 }, }, - { { 16, 243, 244, 46, 14 }, - { 105, 191, 91, 16, 5 }, - { 119, 66, 252, 240, 8 }, - { 160, 141, 175, 217, 6 }, }, - { { 19, 55, 238, 112, 2 }, - { 97, 219, 142, 105, 4 }, - { 64, 231, 126, 204, 8 }, - { 41, 103, 29, 184, 6 }, }, - { { 23, 11, 35, 235, 0 }, - { 144, 106, 10, 77, 15 }, - { 13, 124, 77, 14, 8 }, - { 251, 37, 5, 96, 9 }, }, - { { 21, 2, 111, 27, 11 }, - { 180, 121, 141, 68, 5 }, - { 221, 143, 100, 10, 8 }, - { 162, 43, 25, 226, 13 }, }, - { { 17, 161, 171, 203, 2 }, - { 17, 250, 20, 76, 7 }, - { 77, 61, 88, 88, 8 }, - { 227, 34, 133, 248, 8 }, }, - { { 19, 166, 74, 200, 13 }, - { 37, 104, 93, 105, 2 }, - { 177, 53, 38, 92, 8 }, - { 73, 107, 161, 106, 4 }, }, - { { 23, 142, 53, 205, 3 }, - { 156, 57, 24, 125, 14 }, - { 203, 58, 199, 30, 8 }, - { 123, 225, 137, 195, 9 }, }, - { { 21, 139, 229, 157, 12 }, - { 184, 171, 217, 68, 14 }, - { 59, 154, 125, 26, 8 }, - { 114, 41, 189, 81, 13 }, }, - { { 23, 166, 249, 125, 4 }, - { 185, 168, 222, 125, 4 }, - { 43, 233, 246, 94, 8 }, - { 43, 231, 177, 89, 13 }, }, - { { 21, 220, 164, 180, 4 }, - { 200, 141, 210, 96, 14 }, - { 34, 210, 83, 186, 8 }, - { 112, 100, 187, 17, 3 }, }, - { { 17, 223, 5, 67, 12 }, - { 80, 15, 89, 108, 9 }, - { 60, 42, 15, 184, 8 }, - { 147, 105, 175, 0, 10 }, }, - { { 21, 210, 1, 147, 5 }, - { 212, 12, 216, 68, 3 }, - { 172, 152, 4, 186, 8 }, - { 194, 33, 179, 2, 11 }, }, - { { 24, 31, 173, 250, 10 }, - { 82, 187, 143, 40, 15 }, - { 85, 251, 95, 129, 8 }, - { 241, 79, 29, 212, 10 }, }, - { { 28, 64, 14, 171, 13 }, - { 134, 109, 71, 4, 3 }, - { 189, 87, 0, 35, 8 }, - { 194, 14, 43, 102, 1 }, }, - { { 30, 76, 93, 61, 2 }, - { 186, 61, 134, 53, 8 }, - { 75, 203, 163, 39, 8 }, - { 26, 198, 27, 197, 13 }, }, - { { 24, 111, 246, 127, 9 }, - { 47, 239, 139, 60, 13 }, - { 159, 230, 255, 97, 8 }, - { 179, 205, 31, 127, 4 }, }, - { { 24, 87, 52, 184, 15 }, - { 70, 63, 203, 48, 6 }, - { 241, 210, 206, 161, 8 }, - { 96, 205, 63, 198, 2 }, }, - { { 30, 86, 137, 227, 4 }, - { 210, 140, 78, 45, 3 }, - { 44, 121, 22, 167, 8 }, - { 203, 71, 35, 20, 11 }, }, - { { 26, 149, 209, 132, 5 }, - { 126, 138, 80, 49, 2 }, - { 162, 24, 186, 149, 8 }, - { 72, 192, 165, 23, 14 }, }, - { { 24, 167, 255, 2, 10 }, - { 51, 219, 29, 48, 5 }, - { 84, 15, 254, 81, 8 }, - { 160, 203, 141, 188, 12 }, }, - { { 24, 158, 177, 201, 14 }, - { 82, 184, 89, 60, 14 }, - { 121, 56, 215, 145, 8 }, - { 115, 201, 161, 212, 10 }, }, - { { 28, 178, 10, 96, 10 }, - { 195, 88, 31, 8, 0 }, - { 80, 101, 4, 211, 8 }, - { 1, 15, 129, 172, 3 }, }, - { { 28, 146, 53, 136, 1 }, - { 214, 41, 24, 16, 6 }, - { 129, 26, 196, 147, 8 }, - { 96, 129, 137, 70, 11 }, }, - { { 28, 147, 183, 214, 13 }, - { 222, 203, 217, 24, 7 }, - { 182, 190, 220, 147, 8 }, - { 225, 137, 189, 55, 11 }, }, - { { 26, 202, 188, 88, 1 }, - { 6, 173, 156, 25, 12 }, - { 129, 163, 213, 53, 8 }, - { 57, 131, 155, 86, 0 }, }, - { { 28, 226, 172, 181, 8 }, - { 139, 141, 159, 4, 6 }, - { 26, 211, 84, 115, 8 }, - { 98, 15, 155, 29, 1 }, }, - { { 27, 121, 237, 6, 4 }, - { 123, 143, 68, 65, 13 }, - { 38, 11, 121, 237, 8 }, - { 184, 34, 47, 29, 14 }, }, - { { 31, 106, 57, 19, 2 }, - { 147, 28, 140, 85, 13 }, - { 76, 137, 197, 111, 8 }, - { 186, 163, 19, 140, 9 }, }, - { { 29, 152, 56, 117, 0 }, - { 202, 8, 150, 92, 12 }, - { 10, 225, 193, 155, 8 }, - { 51, 166, 145, 5, 3 }, }, - { { 32, 13, 193, 3, 15 }, - { 52, 146, 97, 36, 9 }, - { 252, 8, 59, 0, 4 }, - { 146, 72, 100, 146, 12 }, }, - { { 38, 53, 212, 232, 0 }, - { 225, 163, 34, 57, 2 }, - { 1, 114, 186, 198, 4 }, - { 73, 196, 76, 88, 7 }, }, - { { 38, 117, 44, 47, 8 }, - { 201, 39, 39, 37, 5 }, - { 31, 67, 74, 230, 4 }, - { 170, 78, 78, 73, 3 }, }, - { { 32, 83, 133, 143, 6 }, - { 88, 183, 104, 4, 3 }, - { 111, 26, 28, 160, 4 }, - { 194, 1, 110, 209, 10 }, }, - { { 34, 136, 31, 122, 3 }, - { 20, 113, 182, 25, 9 }, - { 197, 239, 129, 20, 4 }, - { 153, 134, 216, 226, 8 }, }, - { { 39, 44, 250, 229, 5 }, - { 173, 192, 102, 125, 14 }, - { 170, 117, 243, 78, 4 }, - { 123, 230, 96, 59, 5 }, }, - { { 33, 107, 76, 67, 11 }, - { 37, 23, 45, 76, 9 }, - { 220, 35, 45, 104, 4 }, - { 147, 43, 78, 138, 4 }, }, - { { 37, 188, 103, 11, 10 }, - { 241, 113, 49, 100, 13 }, - { 93, 14, 99, 218, 4 }, - { 178, 104, 200, 232, 15 }, }, - { { 33, 163, 233, 176, 14 }, - { 49, 146, 255, 64, 6 }, - { 112, 217, 124, 88, 4 }, - { 96, 47, 244, 152, 12 }, }, - { { 33, 150, 242, 145, 15 }, - { 100, 208, 249, 116, 6 }, - { 248, 148, 246, 152, 4 }, - { 98, 233, 240, 178, 6 }, }, - { { 39, 162, 43, 140, 10 }, - { 153, 112, 61, 65, 6 }, - { 83, 29, 68, 94, 4 }, - { 104, 43, 192, 233, 9 }, }, - { { 39, 159, 72, 50, 8 }, - { 224, 2, 191, 97, 9 }, - { 20, 193, 47, 158, 4 }, - { 152, 111, 212, 0, 7 }, }, - { { 35, 226, 188, 201, 7 }, - { 5, 181, 124, 93, 6 }, - { 233, 51, 212, 124, 4 }, - { 107, 163, 234, 218, 0 }, }, - { { 33, 194, 87, 244, 1 }, - { 60, 69, 186, 88, 2 }, - { 130, 254, 164, 56, 4 }, - { 65, 165, 218, 35, 12 }, }, - { { 39, 207, 186, 248, 11 }, - { 132, 246, 191, 121, 14 }, - { 209, 245, 223, 62, 4 }, - { 121, 239, 214, 242, 1 }, }, - { { 42, 47, 189, 228, 11 }, - { 31, 147, 47, 57, 14 }, - { 210, 123, 223, 69, 4 }, - { 121, 207, 76, 159, 8 }, }, - { { 42, 84, 58, 140, 12 }, - { 74, 100, 101, 49, 6 }, - { 51, 21, 194, 165, 4 }, - { 104, 202, 98, 101, 2 }, }, - { { 42, 209, 187, 151, 15 }, - { 94, 214, 245, 21, 7 }, - { 254, 157, 216, 181, 4 }, - { 234, 138, 246, 183, 10 }, }, - { { 40, 255, 58, 99, 1 }, - { 71, 70, 62, 60, 13 }, - { 140, 101, 207, 241, 4 }, - { 179, 199, 198, 46, 2 }, }, - { { 43, 84, 197, 185, 8 }, - { 114, 165, 163, 101, 2 }, - { 25, 218, 50, 173, 4 }, - { 74, 108, 90, 84, 14 }, }, - { { 45, 97, 175, 26, 10 }, - { 147, 247, 165, 64, 5 }, - { 85, 143, 88, 107, 4 }, - { 160, 42, 94, 252, 9 }, }, - { { 43, 188, 219, 62, 6 }, - { 123, 240, 246, 113, 9 }, - { 103, 205, 179, 221, 4 }, - { 152, 230, 240, 253, 14 }, }, - { { 45, 159, 154, 13, 2 }, - { 202, 242, 60, 116, 8 }, - { 75, 5, 159, 155, 4 }, - { 18, 227, 196, 245, 3 }, }, - { { 47, 187, 114, 106, 3 }, - { 231, 114, 58, 89, 13 }, - { 197, 100, 237, 223, 4 }, - { 185, 165, 196, 238, 7 }, }, - { { 43, 217, 204, 255, 7 }, - { 110, 183, 246, 77, 11 }, - { 239, 243, 57, 189, 4 }, - { 219, 38, 254, 215, 6 }, }, - { { 41, 198, 223, 142, 12 }, - { 58, 229, 125, 112, 3 }, - { 55, 31, 182, 57, 4 }, - { 192, 235, 234, 117, 12 }, }, - { { 52, 12, 63, 195, 5 }, - { 148, 73, 100, 60, 15 }, - { 172, 63, 195, 2, 12 }, - { 243, 194, 105, 34, 9 }, }, - { { 48, 3, 27, 40, 14 }, - { 16, 122, 111, 16, 0 }, - { 113, 77, 140, 0, 12 }, - { 0, 143, 101, 224, 8 }, }, - { { 48, 113, 29, 236, 3 }, - { 93, 63, 38, 24, 2 }, - { 195, 123, 136, 224, 12 }, - { 65, 134, 79, 203, 10 }, }, - { { 52, 76, 222, 162, 10 }, - { 160, 221, 39, 48, 11 }, - { 84, 87, 179, 34, 12 }, - { 208, 206, 75, 176, 5 }, }, - { { 50, 122, 138, 139, 12 }, - { 65, 236, 109, 5, 11 }, - { 61, 21, 21, 228, 12 }, - { 218, 11, 99, 120, 2 }, }, - { { 54, 87, 99, 215, 2 }, - { 248, 94, 168, 45, 7 }, - { 78, 188, 110, 166, 12 }, - { 235, 65, 87, 161, 15 }, }, - { { 52, 141, 50, 170, 6 }, - { 128, 122, 114, 48, 15 }, - { 101, 84, 203, 18, 12 }, - { 240, 196, 229, 224, 1 }, }, - { { 54, 129, 250, 177, 1 }, - { 164, 202, 182, 21, 6 }, - { 136, 213, 248, 22, 12 }, - { 106, 134, 213, 50, 5 }, }, - { { 54, 153, 111, 21, 13 }, - { 252, 75, 245, 5, 12 }, - { 186, 143, 105, 150, 12 }, - { 58, 10, 253, 35, 15 }, }, - { { 50, 204, 108, 49, 14 }, - { 32, 29, 247, 37, 12 }, - { 120, 195, 99, 52, 12 }, - { 58, 78, 251, 128, 4 }, }, - { { 54, 224, 7, 77, 4 }, - { 153, 109, 112, 13, 0 }, - { 43, 46, 0, 118, 12 }, - { 11, 0, 235, 105, 9 }, }, - { { 51, 64, 141, 156, 5 }, - { 28, 173, 228, 65, 2 }, - { 163, 155, 16, 44, 12 }, - { 72, 34, 123, 83, 8 }, }, - { { 51, 125, 174, 182, 10 }, - { 73, 223, 167, 97, 15 }, - { 86, 215, 91, 236, 12 }, - { 248, 110, 95, 185, 2 }, }, - { { 49, 94, 255, 61, 1 }, - { 124, 237, 174, 116, 12 }, - { 139, 207, 247, 168, 12 }, - { 50, 231, 91, 115, 14 }, }, - { { 53, 164, 249, 40, 11 }, - { 181, 184, 55, 112, 4 }, - { 209, 73, 242, 90, 12 }, - { 32, 238, 193, 218, 13 }, }, - { { 51, 134, 161, 198, 4 }, - { 24, 136, 120, 105, 7 }, - { 38, 56, 86, 28, 12 }, - { 233, 97, 225, 17, 8 }, }, - { { 53, 232, 26, 158, 12 }, - { 137, 108, 245, 80, 11 }, - { 55, 149, 129, 122, 12 }, - { 208, 170, 243, 105, 1 }, }, - { { 55, 216, 88, 186, 3 }, - { 228, 60, 182, 81, 11 }, - { 197, 209, 161, 190, 12 }, - { 216, 166, 211, 194, 7 }, }, - { { 56, 21, 51, 89, 12 }, - { 82, 106, 225, 60, 4 }, - { 57, 172, 202, 129, 12 }, - { 35, 200, 117, 100, 10 }, }, - { { 56, 27, 98, 106, 12 }, - { 98, 106, 107, 8, 13 }, - { 53, 100, 109, 129, 12 }, - { 177, 13, 101, 100, 6 }, }, - { { 58, 54, 80, 219, 10 }, - { 99, 56, 169, 61, 3 }, - { 93, 176, 166, 197, 12 }, - { 203, 201, 81, 204, 6 }, }, - { { 56, 136, 209, 242, 9 }, - { 54, 136, 179, 24, 11 }, - { 148, 248, 177, 17, 12 }, - { 209, 140, 209, 22, 12 }, }, - { { 58, 204, 141, 200, 2 }, - { 18, 189, 52, 41, 10 }, - { 65, 59, 19, 53, 12 }, - { 89, 66, 203, 212, 8 }, }, - { { 60, 252, 249, 7, 11 }, - { 255, 156, 53, 52, 13 }, - { 222, 9, 243, 243, 12 }, - { 178, 202, 195, 159, 15 }, }, - { { 60, 246, 137, 57, 12 }, - { 211, 172, 255, 36, 0 }, - { 57, 201, 22, 243, 12 }, - { 2, 79, 243, 92, 11 }, }, - { { 59, 15, 174, 199, 9 }, - { 14, 203, 45, 109, 15 }, - { 158, 55, 95, 13, 12 }, - { 251, 107, 77, 55, 0 }, }, - { { 61, 11, 95, 245, 6 }, - { 186, 91, 238, 92, 10 }, - { 106, 255, 173, 11, 12 }, - { 83, 167, 125, 165, 13 }, }, - { { 57, 73, 131, 170, 6 }, - { 18, 254, 98, 64, 11 }, - { 101, 92, 25, 41, 12 }, - { 208, 36, 103, 244, 8 }, }, - { { 61, 92, 175, 229, 6 }, - { 218, 221, 102, 108, 14 }, - { 106, 127, 83, 171, 12 }, - { 115, 102, 107, 181, 11 }, }, - { { 59, 111, 209, 254, 2 }, - { 59, 190, 170, 121, 11 }, - { 71, 248, 191, 109, 12 }, - { 217, 229, 87, 221, 12 }, }, - { { 61, 114, 229, 206, 7 }, - { 255, 189, 104, 72, 7 }, - { 231, 58, 116, 235, 12 }, - { 225, 33, 107, 223, 15 }, }, - { { 59, 165, 24, 48, 4 }, - { 3, 10, 246, 113, 0 }, - { 32, 193, 138, 93, 12 }, - { 8, 230, 245, 12, 0 }, }, - { { 59, 215, 215, 116, 14 }, - { 122, 223, 251, 121, 0 }, - { 114, 238, 190, 189, 12 }, - { 9, 237, 255, 181, 14 }, }, - { { 66, 21, 7, 134, 1 }, - { 92, 67, 0, 161, 3 }, - { 134, 30, 10, 132, 2 }, - { 200, 80, 12, 35, 10 }, }, - { { 68, 42, 51, 204, 5 }, - { 157, 96, 72, 152, 14 }, - { 163, 60, 197, 66, 2 }, - { 113, 145, 32, 107, 9 }, }, - { { 64, 105, 62, 32, 12 }, - { 1, 71, 71, 144, 12 }, - { 48, 71, 201, 96, 2 }, - { 48, 158, 46, 40, 0 }, }, - { { 68, 76, 84, 241, 12 }, - { 160, 5, 195, 188, 10 }, - { 56, 242, 163, 34, 2 }, - { 83, 220, 58, 0, 5 }, }, - { { 64, 78, 207, 5, 5 }, - { 60, 197, 76, 164, 8 }, - { 170, 15, 55, 32, 2 }, - { 18, 83, 42, 51, 12 }, }, - { { 64, 223, 74, 196, 6 }, - { 104, 86, 92, 168, 10 }, - { 98, 53, 47, 176, 2 }, - { 81, 83, 166, 161, 6 }, }, - { { 70, 218, 103, 26, 14 }, - { 240, 117, 217, 129, 13 }, - { 117, 142, 101, 182, 2 }, - { 184, 25, 186, 224, 15 }, }, - { { 67, 32, 239, 178, 13 }, - { 53, 193, 199, 193, 7 }, - { 180, 223, 112, 76, 2 }, - { 232, 62, 56, 58, 12 }, }, - { { 65, 99, 121, 242, 7 }, - { 53, 22, 206, 216, 7 }, - { 228, 249, 236, 104, 2 }, - { 225, 183, 54, 138, 12 }, }, - { { 71, 127, 169, 44, 10 }, - { 217, 182, 15, 225, 12 }, - { 83, 73, 95, 238, 2 }, - { 56, 127, 6, 217, 11 }, }, - { { 67, 168, 36, 115, 10 }, - { 1, 17, 147, 205, 13 }, - { 92, 226, 65, 92, 2 }, - { 187, 60, 152, 136, 0 }, }, - { { 69, 174, 2, 166, 1 }, - { 141, 64, 26, 224, 11 }, - { 134, 84, 7, 90, 2 }, - { 208, 117, 128, 43, 1 }, }, - { { 72, 29, 125, 107, 6 }, - { 114, 51, 70, 188, 13 }, - { 109, 107, 235, 129, 2 }, - { 179, 214, 44, 196, 14 }, }, - { { 72, 68, 41, 14, 4 }, - { 26, 36, 68, 160, 5 }, - { 39, 9, 66, 33, 2 }, - { 160, 82, 34, 69, 8 }, }, - { { 76, 93, 176, 242, 6 }, - { 194, 150, 194, 184, 15 }, - { 100, 240, 219, 163, 2 }, - { 241, 212, 54, 148, 3 }, }, - { { 72, 79, 166, 76, 0 }, - { 10, 231, 8, 168, 12 }, - { 3, 38, 95, 33, 2 }, - { 49, 81, 14, 117, 0 }, }, - { { 72, 129, 242, 45, 11 }, - { 46, 242, 19, 148, 4 }, - { 219, 68, 248, 17, 2 }, - { 34, 156, 132, 247, 4 }, }, - { { 72, 140, 105, 154, 11 }, - { 54, 48, 149, 160, 15 }, - { 213, 153, 99, 17, 2 }, - { 240, 90, 144, 198, 12 }, }, - { { 76, 230, 39, 197, 2 }, - { 155, 85, 24, 172, 6 }, - { 74, 62, 70, 115, 2 }, - { 99, 81, 138, 173, 9 }, }, - { { 79, 61, 163, 205, 7 }, - { 223, 242, 64, 237, 14 }, - { 235, 60, 91, 207, 2 }, - { 123, 112, 36, 255, 11 }, }, - { { 73, 73, 253, 184, 8 }, - { 50, 167, 135, 208, 14 }, - { 17, 219, 249, 41, 2 }, - { 112, 190, 30, 84, 12 }, }, - { { 77, 89, 111, 25, 7 }, - { 246, 119, 196, 196, 12 }, - { 233, 143, 105, 171, 2 }, - { 50, 50, 62, 230, 15 }, }, - { { 79, 79, 213, 39, 11 }, - { 190, 151, 11, 245, 9 }, - { 222, 74, 191, 47, 2 }, - { 154, 253, 14, 151, 13 }, }, - { { 75, 148, 100, 228, 11 }, - { 110, 17, 19, 233, 6 }, - { 210, 114, 98, 157, 2 }, - { 105, 124, 136, 135, 6 }, }, - { { 77, 160, 106, 169, 14 }, - { 163, 112, 87, 196, 6 }, - { 121, 85, 96, 91, 2 }, - { 98, 62, 160, 236, 5 }, }, - { { 75, 146, 209, 251, 2 }, - { 114, 176, 154, 221, 3 }, - { 77, 248, 180, 157, 2 }, - { 203, 181, 144, 212, 14 }, }, - { { 79, 237, 128, 190, 11 }, - { 143, 182, 147, 225, 11 }, - { 215, 208, 27, 127, 2 }, - { 216, 124, 150, 223, 1 }, }, - { { 73, 250, 110, 175, 3 }, - { 111, 117, 30, 196, 15 }, - { 207, 87, 101, 249, 2 }, - { 242, 55, 138, 239, 6 }, }, - { { 84, 32, 172, 167, 7 }, - { 141, 153, 70, 132, 7 }, - { 238, 83, 80, 66, 10 }, - { 226, 22, 41, 155, 1 }, }, - { { 84, 25, 164, 142, 8 }, - { 200, 171, 1, 128, 15 }, - { 23, 18, 89, 130, 10 }, - { 240, 24, 13, 81, 3 }, }, - { { 86, 116, 218, 31, 8 }, - { 233, 236, 133, 181, 1 }, - { 31, 133, 178, 230, 10 }, - { 138, 218, 19, 121, 7 }, }, - { { 82, 70, 69, 212, 11 }, - { 60, 29, 137, 169, 2 }, - { 210, 186, 38, 36, 10 }, - { 73, 89, 27, 131, 12 }, }, - { { 80, 153, 112, 192, 3 }, - { 100, 26, 16, 152, 14 }, - { 192, 48, 233, 144, 10 }, - { 113, 144, 133, 130, 6 }, }, - { { 82, 162, 196, 106, 5 }, - { 37, 169, 90, 137, 1 }, - { 165, 98, 52, 84, 10 }, - { 137, 21, 169, 90, 4 }, }, - { { 80, 232, 170, 66, 4 }, - { 1, 204, 84, 136, 13 }, - { 36, 37, 81, 112, 10 }, - { 177, 18, 163, 56, 0 }, }, - { { 82, 242, 173, 89, 8 }, - { 81, 173, 157, 141, 4 }, - { 25, 171, 84, 244, 10 }, - { 43, 27, 155, 88, 10 }, }, - { { 82, 218, 235, 246, 10 }, - { 120, 220, 159, 137, 15 }, - { 86, 253, 117, 180, 10 }, - { 249, 31, 147, 177, 14 }, }, - { { 85, 46, 248, 226, 2 }, - { 161, 152, 14, 248, 15 }, - { 68, 113, 247, 74, 10 }, - { 241, 247, 1, 152, 5 }, }, - { { 81, 65, 96, 182, 2 }, - { 40, 30, 130, 192, 7 }, - { 70, 208, 104, 40, 10 }, - { 224, 52, 23, 129, 4 }, }, - { { 85, 104, 6, 21, 8 }, - { 137, 77, 129, 196, 8 }, - { 26, 134, 1, 106, 10 }, - { 18, 56, 27, 41, 1 }, }, - { { 83, 74, 126, 75, 13 }, - { 36, 109, 77, 221, 13 }, - { 189, 39, 229, 44, 10 }, - { 187, 187, 43, 98, 4 }, }, - { { 83, 107, 211, 224, 11 }, - { 53, 222, 11, 217, 10 }, - { 208, 124, 189, 108, 10 }, - { 89, 189, 7, 186, 12 }, }, - { { 87, 114, 130, 210, 7 }, - { 197, 220, 200, 201, 3 }, - { 228, 180, 20, 238, 10 }, - { 201, 49, 51, 186, 3 }, }, - { { 87, 129, 213, 88, 2 }, - { 176, 187, 144, 217, 0 }, - { 65, 170, 184, 30, 10 }, - { 9, 176, 157, 208, 13 }, }, - { { 87, 164, 195, 74, 8 }, - { 177, 232, 17, 233, 1 }, - { 21, 44, 50, 94, 10 }, - { 137, 120, 129, 120, 13 }, }, - { { 81, 151, 175, 148, 8 }, - { 88, 203, 157, 224, 6 }, - { 18, 159, 94, 152, 10 }, - { 96, 123, 157, 49, 10 }, }, - { { 87, 143, 23, 115, 11 }, - { 148, 91, 155, 253, 9 }, - { 220, 238, 143, 30, 10 }, - { 155, 253, 157, 162, 9 }, }, - { { 85, 194, 224, 207, 0 }, - { 168, 172, 24, 204, 7 }, - { 15, 48, 116, 58, 10 }, - { 227, 49, 131, 81, 5 }, }, - { { 87, 246, 164, 229, 1 }, - { 205, 141, 26, 237, 6 }, - { 138, 114, 86, 254, 10 }, - { 107, 117, 139, 27, 3 }, }, - { { 92, 5, 94, 2, 5 }, - { 166, 75, 68, 176, 1 }, - { 164, 7, 170, 3, 10 }, - { 128, 210, 45, 38, 5 }, }, - { { 94, 56, 204, 77, 0 }, - { 235, 169, 4, 141, 8 }, - { 11, 35, 49, 199, 10 }, - { 27, 18, 9, 93, 7 }, }, - { { 94, 101, 102, 31, 5 }, - { 175, 111, 192, 165, 5 }, - { 175, 134, 106, 103, 10 }, - { 170, 80, 63, 111, 5 }, }, - { { 90, 165, 235, 123, 5 }, - { 55, 234, 214, 173, 5 }, - { 173, 237, 122, 85, 10 }, - { 171, 86, 181, 126, 12 }, }, - { { 94, 173, 81, 224, 13 }, - { 183, 10, 83, 185, 10 }, - { 176, 120, 171, 87, 10 }, - { 89, 220, 165, 14, 13 }, }, - { { 88, 179, 133, 252, 1 }, - { 95, 171, 154, 136, 2 }, - { 131, 250, 28, 209, 10 }, - { 65, 21, 157, 95, 10 }, }, - { { 90, 197, 248, 110, 0 }, - { 42, 174, 22, 185, 5 }, - { 7, 97, 250, 53, 10 }, - { 169, 214, 135, 85, 4 }, }, - { { 88, 241, 107, 96, 0 }, - { 115, 78, 22, 136, 4 }, - { 0, 109, 104, 241, 10 }, - { 33, 22, 135, 44, 14 }, }, - { { 91, 48, 177, 32, 8 }, - { 83, 136, 3, 209, 4 }, - { 16, 72, 208, 205, 10 }, - { 40, 188, 1, 28, 10 }, }, - { { 95, 52, 239, 231, 11 }, - { 255, 217, 7, 237, 7 }, - { 222, 127, 114, 207, 10 }, - { 235, 126, 9, 191, 15 }, }, - { { 89, 64, 115, 102, 9 }, - { 62, 76, 3, 216, 5 }, - { 150, 108, 224, 41, 10 }, - { 161, 188, 3, 39, 12 }, }, - { { 93, 105, 19, 173, 1 }, - { 159, 110, 2, 212, 10 }, - { 139, 92, 137, 107, 10 }, - { 82, 180, 7, 111, 9 }, }, - { { 89, 82, 185, 227, 11 }, - { 86, 156, 15, 220, 7 }, - { 220, 121, 212, 169, 10 }, - { 227, 191, 3, 150, 10 }, }, - { { 93, 70, 244, 172, 2 }, - { 170, 189, 10, 240, 6 }, - { 67, 82, 246, 43, 10 }, - { 96, 245, 11, 213, 5 }, }, - { { 95, 128, 157, 46, 8 }, - { 154, 169, 23, 209, 1 }, - { 23, 75, 144, 31, 10 }, - { 136, 190, 137, 85, 9 }, }, - { { 95, 224, 251, 80, 14 }, - { 179, 220, 213, 217, 4 }, - { 112, 173, 240, 127, 10 }, - { 41, 186, 179, 188, 13 }, }, - { { 95, 235, 211, 215, 0 }, - { 187, 206, 152, 221, 11 }, - { 14, 188, 189, 127, 10 }, - { 219, 177, 151, 61, 13 }, }, - { { 100, 105, 134, 115, 5 }, - { 133, 199, 226, 140, 9 }, - { 172, 230, 25, 98, 6 }, - { 147, 20, 126, 58, 1 }, }, - { { 96, 188, 232, 205, 8 }, - { 105, 160, 53, 172, 14 }, - { 27, 49, 115, 208, 6 }, - { 115, 90, 192, 89, 6 }, }, - { { 100, 158, 227, 5, 6 }, - { 248, 208, 120, 164, 12 }, - { 106, 12, 119, 146, 6 }, - { 50, 81, 224, 177, 15 }, }, - { { 98, 212, 165, 37, 13 }, - { 92, 133, 115, 165, 4 }, - { 186, 74, 82, 180, 6 }, - { 42, 92, 234, 19, 10 }, }, - { { 98, 246, 143, 142, 3 }, - { 93, 245, 60, 161, 3 }, - { 199, 31, 22, 244, 6 }, - { 200, 83, 202, 251, 10 }, }, - { { 101, 96, 72, 36, 7 }, - { 173, 20, 102, 192, 0 }, - { 226, 65, 32, 106, 6 }, - { 0, 54, 98, 139, 5 }, }, - { { 99, 180, 12, 145, 8 }, - { 65, 1, 181, 229, 2 }, - { 24, 147, 2, 220, 6 }, - { 74, 122, 216, 8, 2 }, }, - { { 99, 159, 233, 153, 13 }, - { 116, 162, 253, 229, 14 }, - { 185, 153, 127, 156, 6 }, - { 122, 123, 244, 82, 14 }, }, - { { 101, 237, 229, 156, 7 }, - { 189, 183, 240, 224, 14 }, - { 227, 154, 123, 122, 6 }, - { 112, 112, 254, 219, 13 }, }, - { { 103, 235, 231, 112, 4 }, - { 177, 199, 250, 201, 12 }, - { 32, 238, 125, 126, 6 }, - { 57, 53, 254, 56, 13 }, }, - { { 106, 1, 200, 157, 4 }, - { 42, 162, 228, 133, 2 }, - { 43, 145, 56, 5, 6 }, - { 74, 18, 116, 85, 4 }, }, - { { 104, 61, 242, 11, 0 }, - { 99, 226, 32, 180, 13 }, - { 13, 4, 251, 193, 6 }, - { 178, 208, 68, 124, 6 }, }, - { { 104, 24, 102, 125, 13 }, - { 110, 97, 227, 140, 12 }, - { 187, 230, 97, 129, 6 }, - { 51, 28, 120, 103, 6 }, }, - { { 108, 84, 97, 80, 15 }, - { 246, 20, 225, 168, 4 }, - { 240, 168, 98, 163, 6 }, - { 33, 88, 114, 134, 15 }, }, - { { 104, 75, 24, 245, 13 }, - { 14, 6, 239, 156, 10 }, - { 186, 241, 141, 33, 6 }, - { 83, 159, 118, 7, 0 }, }, - { { 104, 79, 21, 129, 0 }, - { 18, 7, 40, 180, 10 }, - { 8, 26, 143, 33, 6 }, - { 82, 209, 78, 4, 8 }, }, - { { 110, 110, 244, 89, 0 }, - { 163, 165, 168, 189, 12 }, - { 9, 162, 247, 103, 6 }, - { 59, 209, 90, 92, 5 }, }, - { { 110, 152, 128, 38, 5 }, - { 206, 128, 114, 129, 9 }, - { 166, 64, 17, 151, 6 }, - { 152, 20, 224, 23, 3 }, }, - { { 110, 144, 99, 51, 3 }, - { 246, 80, 178, 133, 5 }, - { 204, 204, 96, 151, 6 }, - { 170, 20, 208, 166, 15 }, }, - { { 110, 241, 228, 10, 7 }, - { 231, 183, 112, 129, 5 }, - { 229, 2, 120, 247, 6 }, - { 168, 16, 238, 222, 7 }, }, - { { 108, 195, 36, 252, 0 }, - { 138, 39, 186, 136, 6 }, - { 3, 242, 76, 51, 6 }, - { 97, 21, 222, 69, 1 }, }, - { { 105, 0, 81, 91, 15 }, - { 54, 48, 225, 220, 1 }, - { 253, 168, 160, 9, 6 }, - { 131, 184, 112, 198, 12 }, }, - { { 107, 64, 14, 250, 12 }, - { 2, 101, 231, 201, 3 }, - { 53, 247, 0, 45, 6 }, - { 201, 62, 122, 100, 0 }, }, - { { 107, 124, 181, 68, 7 }, - { 95, 149, 96, 249, 12 }, - { 226, 42, 211, 237, 6 }, - { 57, 240, 106, 159, 10 }, }, - { { 105, 172, 245, 202, 2 }, - { 51, 177, 48, 248, 15 }, - { 69, 58, 243, 89, 6 }, - { 241, 240, 200, 220, 12 }, }, - { { 105, 167, 102, 247, 12 }, - { 43, 67, 251, 236, 7 }, - { 62, 246, 110, 89, 6 }, - { 227, 125, 252, 45, 4 }, }, - { { 109, 235, 50, 140, 11 }, - { 143, 118, 57, 208, 14 }, - { 211, 20, 205, 123, 6 }, - { 112, 185, 198, 239, 1 }, }, - { { 116, 23, 252, 110, 12 }, - { 232, 171, 111, 184, 5 }, - { 55, 99, 254, 130, 14 }, - { 161, 223, 109, 81, 7 }, }, - { { 112, 97, 170, 185, 4 }, - { 1, 238, 230, 132, 6 }, - { 41, 213, 88, 96, 14 }, - { 98, 22, 119, 120, 0 }, }, - { { 116, 67, 17, 82, 1 }, - { 148, 14, 168, 152, 1 }, - { 132, 168, 140, 34, 14 }, - { 129, 145, 87, 2, 9 }, }, - { { 112, 185, 187, 138, 9 }, - { 85, 234, 53, 144, 15 }, - { 149, 29, 217, 208, 14 }, - { 240, 154, 197, 122, 10 }, }, - { { 118, 148, 62, 229, 4 }, - { 200, 73, 118, 189, 6 }, - { 42, 119, 194, 150, 14 }, - { 107, 214, 233, 33, 3 }, }, - { { 118, 188, 96, 18, 9 }, - { 229, 8, 177, 161, 13 }, - { 148, 128, 99, 214, 14 }, - { 184, 88, 209, 10, 7 }, }, - { { 112, 155, 7, 165, 4 }, - { 88, 75, 122, 132, 10 }, - { 42, 94, 13, 144, 14 }, - { 82, 21, 237, 33, 10 }, }, - { { 116, 139, 191, 1, 3 }, - { 148, 219, 60, 148, 12 }, - { 200, 15, 221, 18, 14 }, - { 50, 147, 205, 178, 9 }, }, - { { 114, 248, 93, 79, 11 }, - { 125, 61, 53, 157, 9 }, - { 223, 43, 161, 244, 14 }, - { 155, 154, 203, 203, 14 }, }, - { { 118, 211, 218, 167, 15 }, - { 236, 222, 127, 149, 3 }, - { 254, 85, 188, 182, 14 }, - { 202, 159, 231, 179, 7 }, }, - { { 117, 36, 208, 249, 9 }, - { 165, 168, 163, 252, 2 }, - { 153, 240, 178, 74, 14 }, - { 67, 252, 81, 90, 5 }, }, - { { 117, 60, 188, 48, 6 }, - { 193, 153, 230, 240, 12 }, - { 96, 195, 211, 202, 14 }, - { 48, 246, 121, 152, 3 }, }, - { { 119, 3, 201, 213, 15 }, - { 188, 154, 237, 205, 2 }, - { 250, 185, 60, 14, 14 }, - { 75, 59, 117, 147, 13 }, }, - { { 113, 69, 59, 42, 5 }, - { 20, 110, 102, 240, 5 }, - { 165, 77, 202, 40, 14 }, - { 160, 246, 103, 98, 8 }, }, - { { 115, 108, 25, 223, 1 }, - { 29, 44, 164, 253, 11 }, - { 143, 185, 131, 108, 14 }, - { 219, 242, 83, 75, 8 }, }, - { { 113, 72, 238, 44, 10 }, - { 40, 253, 39, 192, 12 }, - { 83, 71, 113, 40, 14 }, - { 48, 62, 75, 241, 4 }, }, - { { 115, 111, 34, 124, 0 }, - { 9, 110, 170, 233, 12 }, - { 3, 228, 79, 108, 14 }, - { 57, 117, 87, 105, 0 }, }, - { { 115, 145, 99, 174, 14 }, - { 120, 122, 115, 193, 7 }, - { 119, 92, 104, 156, 14 }, - { 232, 60, 229, 225, 14 }, }, - { { 117, 131, 158, 142, 9 }, - { 140, 235, 61, 208, 3 }, - { 151, 23, 156, 26, 14 }, - { 192, 187, 205, 115, 1 }, }, - { { 119, 191, 100, 123, 6 }, - { 225, 59, 250, 237, 13 }, - { 109, 226, 111, 222, 14 }, - { 187, 117, 253, 200, 7 }, }, - { { 115, 229, 189, 18, 10 }, - { 17, 159, 181, 241, 5 }, - { 84, 139, 218, 124, 14 }, - { 168, 250, 223, 152, 8 }, }, - { { 115, 207, 104, 128, 3 }, - { 36, 30, 60, 225, 14 }, - { 192, 17, 111, 60, 14 }, - { 120, 115, 199, 130, 4 }, }, - { { 113, 247, 74, 10, 0 }, - { 97, 110, 60, 224, 1 }, - { 5, 5, 46, 248, 14 }, - { 128, 115, 199, 104, 6 }, }, - { { 120, 45, 207, 39, 6 }, - { 59, 219, 102, 164, 9 }, - { 110, 79, 59, 65, 14 }, - { 146, 86, 109, 189, 12 }, }, - { { 124, 2, 58, 157, 7 }, - { 142, 120, 236, 148, 6 }, - { 235, 149, 196, 3, 14 }, - { 98, 147, 113, 231, 1 }, }, - { { 122, 92, 206, 58, 4 }, - { 98, 237, 230, 161, 9 }, - { 37, 199, 51, 165, 14 }, - { 152, 86, 123, 116, 6 }, }, - { { 122, 114, 16, 7, 7 }, - { 79, 28, 104, 149, 1 }, - { 238, 0, 132, 229, 14 }, - { 138, 145, 99, 143, 2 }, }, - { { 126, 178, 36, 208, 11 }, - { 199, 25, 185, 137, 6 }, - { 208, 178, 68, 215, 14 }, - { 105, 25, 217, 142, 3 }, }, - { { 120, 253, 197, 213, 2 }, - { 123, 159, 176, 172, 10 }, - { 74, 186, 59, 241, 14 }, - { 83, 80, 223, 157, 14 }, }, - { { 126, 232, 117, 158, 9 }, - { 191, 45, 177, 145, 15 }, - { 151, 154, 225, 119, 14 }, - { 248, 152, 219, 79, 13 }, }, - { { 123, 28, 43, 68, 14 }, - { 90, 88, 101, 233, 12 }, - { 114, 45, 67, 141, 14 }, - { 57, 122, 97, 165, 10 }, }, - { { 127, 4, 160, 250, 2 }, - { 130, 184, 162, 233, 7 }, - { 69, 240, 82, 15, 14 }, - { 233, 116, 81, 212, 1 }, }, - { { 123, 39, 78, 235, 3 }, - { 39, 123, 46, 237, 3 }, - { 205, 119, 46, 77, 14 }, - { 203, 119, 77, 238, 4 }, }, - { { 125, 38, 127, 122, 1 }, - { 183, 105, 174, 248, 5 }, - { 133, 239, 230, 75, 14 }, - { 161, 247, 89, 110, 13 }, }, - { { 127, 50, 169, 94, 10 }, - { 219, 184, 173, 201, 5 }, - { 87, 169, 84, 207, 14 }, - { 169, 59, 81, 221, 11 }, }, - { { 121, 180, 170, 170, 7 }, - { 71, 248, 118, 224, 7 }, - { 229, 85, 82, 217, 14 }, - { 224, 118, 225, 254, 2 }, }, - { { 125, 176, 215, 37, 3 }, - { 255, 217, 50, 212, 0 }, - { 202, 78, 176, 219, 14 }, - { 2, 180, 201, 191, 15 }, }, - { { 127, 147, 79, 12, 2 }, - { 250, 123, 60, 193, 0 }, - { 67, 15, 44, 159, 14 }, - { 8, 51, 205, 229, 15 }, }, - { { 125, 252, 16, 66, 2 }, - { 195, 28, 48, 248, 9 }, - { 68, 32, 131, 251, 14 }, - { 145, 240, 195, 140, 3 }, }, - { { 125, 213, 216, 211, 15 }, - { 230, 158, 245, 252, 3 }, - { 252, 177, 186, 187, 14 }, - { 195, 250, 247, 150, 7 }, }, - { { 128, 56, 32, 103, 11 }, - { 77, 16, 3, 14, 13 }, - { 222, 96, 65, 192, 1 }, - { 183, 12, 0, 139, 2 }, }, - { { 132, 17, 185, 214, 3 }, - { 220, 146, 132, 26, 7 }, - { 198, 185, 216, 130, 1 }, - { 229, 130, 20, 147, 11 }, }, - { { 128, 3, 252, 100, 10 }, - { 40, 147, 15, 26, 4 }, - { 82, 99, 252, 0, 1 }, - { 37, 143, 12, 145, 4 }, }, - { { 130, 59, 15, 187, 10 }, - { 81, 115, 143, 7, 11 }, - { 93, 223, 13, 196, 1 }, - { 222, 15, 28, 232, 10 }, }, - { { 134, 42, 167, 117, 10 }, - { 153, 209, 139, 15, 12 }, - { 90, 238, 85, 70, 1 }, - { 63, 13, 24, 185, 9 }, }, - { { 132, 27, 89, 75, 7 }, - { 244, 50, 76, 30, 9 }, - { 237, 41, 173, 130, 1 }, - { 151, 131, 36, 194, 15 }, }, - { { 130, 68, 49, 200, 10 }, - { 16, 52, 1, 59, 6 }, - { 81, 56, 194, 36, 1 }, - { 109, 200, 2, 192, 8 }, }, - { { 130, 89, 216, 117, 3 }, - { 108, 150, 134, 31, 8 }, - { 202, 225, 185, 164, 1 }, - { 31, 134, 22, 147, 6 }, }, - { { 132, 72, 31, 220, 14 }, - { 152, 117, 197, 26, 10 }, - { 115, 191, 129, 34, 1 }, - { 85, 138, 58, 225, 9 }, }, - { { 130, 181, 229, 107, 4 }, - { 113, 163, 82, 47, 5 }, - { 45, 106, 122, 212, 1 }, - { 175, 68, 172, 88, 14 }, }, - { { 128, 183, 10, 183, 0 }, - { 73, 66, 158, 38, 3 }, - { 14, 213, 14, 208, 1 }, - { 198, 71, 148, 41, 2 }, }, - { { 134, 178, 80, 25, 10 }, - { 225, 48, 153, 23, 0 }, - { 89, 128, 164, 214, 1 }, - { 14, 137, 144, 200, 7 }, }, - { { 128, 204, 47, 123, 8 }, - { 16, 101, 151, 46, 13 }, - { 29, 239, 67, 48, 1 }, - { 183, 78, 154, 96, 8 }, }, - { { 134, 253, 182, 100, 8 }, - { 201, 199, 19, 59, 12 }, - { 18, 102, 219, 246, 1 }, - { 61, 204, 142, 57, 3 }, }, - { { 129, 121, 60, 62, 9 }, - { 77, 39, 135, 82, 13 }, - { 151, 195, 201, 232, 1 }, - { 180, 174, 30, 75, 2 }, }, - { { 133, 96, 132, 133, 11 }, - { 141, 149, 1, 70, 2 }, - { 218, 18, 16, 106, 1 }, - { 70, 40, 10, 155, 1 }, }, - { { 131, 98, 22, 146, 5 }, - { 5, 69, 200, 83, 3 }, - { 164, 150, 132, 108, 1 }, - { 204, 161, 58, 42, 0 }, }, - { { 131, 128, 229, 221, 15 }, - { 60, 177, 209, 79, 6 }, - { 251, 186, 112, 28, 1 }, - { 111, 40, 184, 211, 12 }, }, - { { 135, 160, 76, 203, 2 }, - { 161, 49, 20, 79, 3 }, - { 77, 51, 32, 94, 1 }, - { 207, 34, 136, 200, 5 }, }, - { { 135, 184, 138, 180, 13 }, - { 205, 192, 215, 67, 10 }, - { 178, 213, 17, 222, 1 }, - { 92, 46, 176, 59, 3 }, }, - { { 142, 2, 6, 56, 4 }, - { 130, 97, 202, 3, 0 }, - { 33, 198, 4, 7, 1 }, - { 12, 5, 56, 100, 1 }, }, - { { 140, 54, 125, 172, 7 }, - { 255, 49, 78, 50, 6 }, - { 227, 91, 230, 195, 1 }, - { 100, 199, 40, 207, 15 }, }, - { { 140, 65, 233, 44, 2 }, - { 186, 182, 6, 2, 4 }, - { 67, 73, 120, 35, 1 }, - { 36, 6, 6, 213, 13 }, }, - { { 140, 152, 59, 175, 12 }, - { 218, 96, 87, 22, 15 }, - { 63, 93, 193, 147, 1 }, - { 246, 142, 160, 101, 11 }, }, - { { 136, 244, 107, 79, 11 }, - { 127, 116, 21, 46, 5 }, - { 223, 45, 98, 241, 1 }, - { 167, 74, 130, 239, 14 }, }, - { { 143, 44, 197, 204, 4 }, - { 187, 161, 64, 107, 10 }, - { 35, 58, 51, 79, 1 }, - { 93, 96, 40, 93, 13 }, }, - { { 141, 55, 126, 115, 3 }, - { 231, 83, 142, 126, 5 }, - { 204, 231, 238, 203, 1 }, - { 167, 231, 28, 174, 7 }, }, - { { 139, 69, 79, 63, 7 }, - { 62, 119, 198, 103, 1 }, - { 239, 207, 42, 45, 1 }, - { 142, 102, 62, 231, 12 }, }, - { { 141, 86, 37, 150, 0 }, - { 218, 5, 136, 98, 7 }, - { 6, 154, 70, 171, 1 }, - { 228, 97, 26, 5, 11 }, }, - { { 139, 191, 24, 57, 2 }, - { 67, 50, 158, 119, 8 }, - { 73, 193, 143, 221, 1 }, - { 30, 231, 148, 204, 2 }, }, - { { 141, 166, 67, 9, 4 }, - { 179, 96, 88, 102, 0 }, - { 41, 12, 38, 91, 1 }, - { 6, 97, 160, 108, 13 }, }, - { { 141, 208, 202, 166, 8 }, - { 234, 196, 23, 66, 3 }, - { 22, 85, 48, 187, 1 }, - { 196, 46, 130, 53, 7 }, }, - { { 139, 246, 152, 79, 4 }, - { 75, 164, 92, 127, 1 }, - { 47, 33, 150, 253, 1 }, - { 143, 227, 162, 93, 2 }, }, - { { 144, 18, 153, 165, 4 }, - { 88, 136, 78, 22, 2 }, - { 42, 89, 148, 128, 9 }, - { 70, 135, 33, 17, 10 }, }, - { { 150, 46, 4, 135, 5 }, - { 141, 9, 72, 39, 11 }, - { 174, 18, 7, 70, 9 }, - { 222, 65, 41, 11, 1 }, }, - { { 146, 92, 163, 212, 0 }, - { 88, 204, 128, 43, 14 }, - { 2, 188, 83, 164, 9 }, - { 125, 64, 19, 49, 10 }, }, - { { 146, 133, 143, 119, 10 }, - { 24, 219, 151, 47, 1 }, - { 94, 239, 26, 20, 9 }, - { 143, 78, 157, 177, 8 }, }, - { { 148, 169, 61, 69, 10 }, - { 153, 27, 21, 30, 12 }, - { 90, 43, 201, 82, 9 }, - { 55, 138, 141, 137, 9 }, }, - { { 145, 32, 243, 51, 2 }, - { 49, 216, 130, 86, 5 }, - { 76, 204, 240, 72, 9 }, - { 166, 164, 17, 184, 12 }, }, - { { 145, 39, 109, 79, 3 }, - { 61, 59, 12, 110, 5 }, - { 207, 43, 110, 72, 9 }, - { 167, 99, 13, 203, 12 }, }, - { { 151, 51, 253, 233, 10 }, - { 241, 187, 15, 95, 6 }, - { 89, 123, 252, 206, 9 }, - { 111, 175, 13, 216, 15 }, }, - { { 151, 72, 243, 129, 3 }, - { 180, 220, 0, 87, 14 }, - { 200, 28, 241, 46, 9 }, - { 126, 160, 3, 178, 13 }, }, - { { 151, 159, 160, 6, 9 }, - { 204, 138, 25, 99, 13 }, - { 150, 0, 95, 158, 9 }, - { 188, 105, 133, 19, 3 }, }, - { { 147, 208, 63, 215, 12 }, - { 88, 77, 213, 95, 7 }, - { 62, 191, 192, 188, 9 }, - { 239, 170, 187, 33, 10 }, }, - { { 149, 252, 208, 110, 0 }, - { 233, 172, 18, 122, 9 }, - { 7, 96, 179, 250, 9 }, - { 149, 228, 131, 89, 7 }, }, - { { 147, 195, 178, 11, 1 }, - { 4, 238, 24, 87, 5 }, - { 141, 4, 220, 60, 9 }, - { 174, 161, 135, 114, 0 }, }, - { { 145, 195, 66, 1, 14 }, - { 32, 94, 89, 70, 0 }, - { 120, 4, 44, 56, 9 }, - { 6, 41, 167, 160, 4 }, }, - { { 158, 10, 212, 208, 9 }, - { 166, 137, 137, 27, 10 }, - { 144, 178, 181, 7, 9 }, - { 93, 137, 25, 22, 5 }, }, - { { 158, 62, 24, 70, 8 }, - { 203, 8, 13, 59, 9 }, - { 22, 33, 135, 199, 9 }, - { 157, 203, 1, 13, 3 }, }, - { { 154, 97, 122, 220, 9 }, - { 47, 110, 133, 27, 6 }, - { 147, 181, 232, 101, 9 }, - { 109, 138, 23, 111, 4 }, }, - { { 152, 200, 135, 101, 13 }, - { 30, 205, 83, 14, 8 }, - { 186, 110, 17, 49, 9 }, - { 23, 12, 171, 55, 8 }, }, - { { 152, 193, 223, 48, 9 }, - { 54, 207, 151, 18, 0 }, - { 144, 207, 184, 49, 9 }, - { 4, 142, 159, 54, 12 }, }, - { { 158, 249, 40, 250, 12 }, - { 195, 46, 215, 11, 15 }, - { 53, 241, 73, 247, 9 }, - { 253, 14, 183, 76, 3 }, }, - { { 158, 240, 171, 22, 1 }, - { 223, 204, 148, 3, 5 }, - { 134, 141, 80, 247, 9 }, - { 172, 2, 147, 63, 11 }, }, - { { 152, 251, 117, 9, 13 }, - { 119, 47, 89, 22, 12 }, - { 185, 10, 237, 241, 9 }, - { 54, 137, 175, 78, 14 }, }, - { { 159, 32, 1, 53, 4 }, - { 155, 8, 194, 71, 0 }, - { 42, 200, 0, 79, 9 }, - { 14, 36, 49, 13, 9 }, }, - { { 153, 18, 120, 7, 12 }, - { 106, 8, 77, 86, 5 }, - { 62, 1, 228, 137, 9 }, - { 166, 171, 33, 5, 6 }, }, - { { 155, 131, 245, 126, 11 }, - { 62, 187, 155, 91, 5 }, - { 215, 234, 252, 29, 9 }, - { 173, 173, 157, 215, 12 }, }, - { { 153, 249, 200, 173, 5 }, - { 111, 174, 86, 70, 10 }, - { 171, 81, 57, 249, 9 }, - { 86, 38, 167, 95, 6 }, }, - { { 157, 228, 46, 236, 2 }, - { 139, 125, 22, 106, 6 }, - { 67, 119, 66, 123, 9 }, - { 101, 102, 139, 237, 1 }, }, - { { 155, 219, 144, 210, 3 }, - { 70, 158, 152, 91, 11 }, - { 196, 176, 157, 189, 9 }, - { 221, 161, 151, 150, 2 }, }, - { { 159, 254, 132, 153, 15 }, - { 199, 189, 217, 103, 10 }, - { 249, 146, 23, 255, 9 }, - { 94, 105, 187, 222, 3 }, }, - { { 162, 66, 140, 215, 9 }, - { 12, 133, 173, 15, 3 }, - { 158, 179, 20, 36, 5 }, - { 207, 11, 90, 19, 0 }, }, - { { 164, 205, 25, 53, 7 }, - { 156, 22, 246, 54, 8 }, - { 234, 201, 139, 50, 5 }, - { 22, 198, 246, 131, 9 }, }, - { { 166, 213, 162, 25, 0 }, - { 192, 230, 176, 39, 4 }, - { 9, 132, 90, 182, 5 }, - { 46, 64, 214, 112, 3 }, }, - { { 160, 223, 192, 176, 6 }, - { 96, 150, 250, 34, 10 }, - { 96, 208, 63, 176, 5 }, - { 84, 69, 246, 144, 6 }, }, - { { 163, 49, 180, 144, 4 }, - { 65, 131, 224, 83, 6 }, - { 32, 146, 216, 204, 5 }, - { 108, 160, 124, 24, 2 }, }, - { { 167, 32, 134, 58, 15 }, - { 133, 241, 227, 67, 1 }, - { 245, 198, 16, 78, 5 }, - { 140, 44, 120, 250, 1 }, }, - { { 167, 56, 45, 40, 13 }, - { 213, 33, 103, 67, 12 }, - { 177, 75, 65, 206, 5 }, - { 60, 46, 104, 74, 11 }, }, - { { 163, 63, 204, 220, 14 }, - { 105, 179, 237, 107, 10 }, - { 115, 179, 63, 204, 5 }, - { 93, 107, 124, 217, 6 }, }, - { { 165, 62, 179, 132, 11 }, - { 221, 208, 41, 114, 14 }, - { 210, 28, 215, 202, 5 }, - { 116, 233, 64, 187, 11 }, }, - { { 161, 112, 26, 189, 7 }, - { 77, 116, 230, 86, 2 }, - { 235, 213, 128, 232, 5 }, - { 70, 166, 114, 235, 2 }, }, - { { 167, 103, 101, 55, 9 }, - { 189, 7, 171, 103, 5 }, - { 158, 202, 110, 110, 5 }, - { 174, 109, 94, 11, 13 }, }, - { { 165, 123, 102, 174, 4 }, - { 233, 103, 106, 66, 15 }, - { 39, 86, 109, 234, 5 }, - { 244, 37, 110, 105, 7 }, }, - { { 161, 213, 29, 147, 2 }, - { 80, 23, 180, 118, 3 }, - { 76, 155, 138, 184, 5 }, - { 198, 226, 222, 128, 10 }, }, - { { 163, 248, 233, 155, 14 }, - { 113, 180, 245, 71, 15 }, - { 125, 153, 113, 252, 5 }, - { 254, 42, 242, 216, 14 }, }, - { { 165, 211, 131, 218, 3 }, - { 212, 246, 184, 74, 3 }, - { 197, 188, 28, 186, 5 }, - { 197, 33, 214, 242, 11 }, }, - { { 165, 214, 235, 188, 6 }, - { 248, 244, 254, 98, 6 }, - { 99, 221, 118, 186, 5 }, - { 100, 103, 242, 241, 15 }, }, - { { 170, 38, 227, 151, 9 }, - { 63, 192, 169, 39, 7 }, - { 158, 156, 118, 69, 5 }, - { 238, 73, 80, 63, 12 }, }, - { { 174, 30, 201, 63, 12 }, - { 250, 160, 239, 39, 9 }, - { 63, 201, 55, 135, 5 }, - { 158, 79, 112, 85, 15 }, }, - { { 172, 119, 184, 237, 2 }, - { 203, 182, 46, 62, 6 }, - { 75, 113, 222, 227, 5 }, - { 103, 199, 70, 221, 3 }, }, - { { 170, 169, 238, 77, 15 }, - { 47, 243, 117, 15, 12 }, - { 251, 39, 121, 85, 5 }, - { 63, 10, 236, 255, 4 }, }, - { { 170, 128, 121, 170, 6 }, - { 50, 48, 118, 19, 7 }, - { 101, 89, 224, 21, 5 }, - { 236, 134, 224, 196, 12 }, }, - { { 174, 194, 96, 202, 1 }, - { 166, 36, 56, 11, 7 }, - { 133, 48, 100, 55, 5 }, - { 237, 1, 194, 70, 5 }, }, - { { 169, 58, 152, 98, 11 }, - { 71, 144, 47, 90, 9 }, - { 212, 97, 149, 201, 5 }, - { 149, 175, 64, 158, 2 }, }, - { { 171, 18, 81, 200, 12 }, - { 114, 32, 105, 91, 2 }, - { 49, 56, 164, 141, 5 }, - { 77, 169, 96, 68, 14 }, }, - { { 173, 102, 219, 216, 13 }, - { 183, 228, 237, 122, 2 }, - { 177, 189, 182, 107, 5 }, - { 69, 235, 114, 126, 13 }, }, - { { 173, 82, 221, 74, 1 }, - { 246, 165, 44, 90, 1 }, - { 133, 43, 180, 171, 5 }, - { 133, 163, 74, 86, 15 }, }, - { { 169, 182, 113, 16, 8 }, - { 115, 0, 185, 114, 4 }, - { 16, 136, 230, 217, 5 }, - { 36, 233, 208, 12, 14 }, }, - { { 173, 186, 226, 53, 0 }, - { 235, 192, 186, 70, 12 }, - { 10, 196, 117, 219, 5 }, - { 54, 37, 208, 61, 7 }, }, - { { 173, 151, 65, 223, 12 }, - { 250, 34, 249, 110, 3 }, - { 63, 184, 46, 155, 5 }, - { 199, 105, 244, 69, 15 }, }, - { { 171, 243, 182, 44, 7 }, - { 79, 247, 122, 83, 4 }, - { 227, 70, 220, 253, 5 }, - { 44, 165, 238, 255, 2 }, }, - { { 182, 34, 108, 113, 12 }, - { 161, 9, 239, 15, 4 }, - { 56, 227, 100, 70, 13 }, - { 47, 15, 121, 8, 5 }, }, - { { 180, 30, 37, 122, 7 }, - { 212, 57, 234, 42, 13 }, - { 229, 234, 71, 130, 13 }, - { 181, 69, 121, 194, 11 }, }, - { { 182, 55, 195, 138, 3 }, - { 245, 250, 40, 35, 3 }, - { 197, 28, 62, 198, 13 }, - { 204, 65, 69, 250, 15 }, }, - { { 176, 76, 212, 55, 0 }, - { 40, 141, 162, 54, 9 }, - { 14, 194, 179, 32, 13 }, - { 150, 196, 91, 17, 4 }, }, - { { 178, 127, 139, 5, 3 }, - { 93, 222, 44, 39, 8 }, - { 202, 13, 31, 228, 13 }, - { 30, 67, 71, 187, 10 }, }, - { { 176, 173, 216, 34, 5 }, - { 37, 138, 118, 50, 9 }, - { 164, 65, 187, 80, 13 }, - { 148, 198, 229, 26, 4 }, }, - { { 180, 155, 130, 43, 11 }, - { 196, 250, 59, 6, 9 }, - { 221, 68, 29, 146, 13 }, - { 150, 13, 197, 242, 3 }, }, - { { 176, 211, 78, 194, 3 }, - { 100, 95, 60, 10, 3 }, - { 196, 55, 44, 176, 13 }, - { 197, 3, 207, 162, 6 }, }, - { { 177, 61, 126, 195, 14 }, - { 97, 91, 101, 126, 15 }, - { 124, 55, 235, 200, 13 }, - { 247, 234, 109, 168, 6 }, }, - { { 177, 6, 164, 99, 14 }, - { 0, 153, 107, 110, 5 }, - { 124, 98, 86, 8, 13 }, - { 167, 109, 105, 144, 0 }, }, - { { 179, 43, 116, 63, 14 }, - { 41, 59, 235, 87, 13 }, - { 127, 194, 237, 76, 13 }, - { 190, 173, 125, 201, 4 }, }, - { { 183, 31, 214, 70, 15 }, - { 236, 219, 105, 123, 9 }, - { 246, 38, 191, 142, 13 }, - { 157, 233, 109, 179, 7 }, }, - { { 177, 110, 241, 244, 5 }, - { 61, 140, 234, 122, 14 }, - { 162, 248, 247, 104, 13 }, - { 117, 229, 115, 27, 12 }, }, - { { 177, 126, 83, 138, 14 }, - { 113, 124, 105, 114, 11 }, - { 117, 28, 167, 232, 13 }, - { 212, 233, 99, 232, 14 }, }, - { { 183, 98, 223, 55, 7 }, - { 189, 221, 238, 87, 1 }, - { 238, 207, 180, 110, 13 }, - { 142, 167, 123, 187, 13 }, }, - { { 177, 169, 248, 148, 9 }, - { 45, 138, 181, 82, 14 }, - { 146, 145, 249, 88, 13 }, - { 116, 170, 213, 27, 4 }, }, - { { 179, 183, 217, 210, 4 }, - { 113, 138, 252, 123, 3 }, - { 36, 185, 190, 220, 13 }, - { 205, 227, 245, 24, 14 }, }, - { { 179, 238, 187, 76, 6 }, - { 25, 252, 124, 123, 12 }, - { 99, 45, 215, 124, 13 }, - { 61, 227, 227, 249, 8 }, }, - { { 184, 44, 165, 82, 4 }, - { 19, 137, 224, 42, 13 }, - { 36, 170, 83, 65, 13 }, - { 181, 64, 121, 28, 8 }, }, - { { 184, 20, 13, 235, 15 }, - { 86, 57, 103, 46, 3 }, - { 253, 123, 2, 129, 13 }, - { 199, 78, 105, 198, 10 }, }, - { { 188, 25, 220, 199, 7 }, - { 238, 155, 100, 30, 11 }, - { 238, 51, 185, 131, 13 }, - { 215, 130, 109, 151, 7 }, }, - { { 188, 84, 38, 185, 6 }, - { 194, 125, 226, 38, 6 }, - { 105, 214, 66, 163, 13 }, - { 102, 68, 123, 228, 3 }, }, - { { 190, 103, 55, 196, 5 }, - { 159, 79, 104, 59, 6 }, - { 162, 62, 206, 103, 13 }, - { 109, 193, 111, 47, 9 }, }, - { { 184, 250, 232, 211, 5 }, - { 103, 140, 252, 14, 15 }, - { 172, 177, 117, 241, 13 }, - { 247, 3, 243, 30, 6 }, }, - { { 190, 194, 26, 55, 8 }, - { 138, 76, 191, 23, 1 }, - { 30, 197, 132, 55, 13 }, - { 142, 143, 211, 37, 1 }, }, - { { 187, 24, 207, 164, 11 }, - { 126, 217, 39, 67, 10 }, - { 210, 95, 49, 141, 13 }, - { 92, 46, 73, 183, 14 }, }, - { { 189, 1, 126, 246, 13 }, - { 174, 75, 231, 90, 7 }, - { 182, 247, 232, 11, 13 }, - { 229, 174, 125, 39, 5 }, }, - { { 189, 34, 47, 210, 7 }, - { 151, 89, 236, 74, 7 }, - { 228, 191, 68, 75, 13 }, - { 229, 35, 121, 174, 9 }, }, - { { 189, 2, 85, 133, 5 }, - { 190, 9, 104, 86, 2 }, - { 170, 26, 164, 11, 13 }, - { 70, 161, 105, 7, 13 }, }, - { { 185, 114, 78, 96, 5 }, - { 103, 77, 110, 74, 0 }, - { 160, 103, 36, 233, 13 }, - { 5, 39, 107, 46, 6 }, }, - { { 189, 119, 92, 22, 15 }, - { 239, 31, 237, 114, 1 }, - { 246, 131, 174, 235, 13 }, - { 132, 235, 127, 143, 7 }, }, - { { 185, 136, 172, 46, 1 }, - { 14, 169, 54, 66, 13 }, - { 135, 67, 81, 25, 13 }, - { 180, 38, 201, 87, 0 }, }, - { { 191, 160, 38, 103, 5 }, - { 143, 73, 114, 79, 5 }, - { 174, 102, 64, 95, 13 }, - { 175, 36, 233, 47, 1 }, }, - { { 185, 130, 150, 168, 6 }, - { 2, 249, 122, 82, 2 }, - { 97, 86, 148, 25, 13 }, - { 68, 165, 233, 244, 0 }, }, - { { 189, 135, 23, 24, 10 }, - { 146, 123, 185, 114, 0 }, - { 81, 142, 142, 27, 13 }, - { 4, 233, 221, 228, 9 }, }, - { { 191, 190, 239, 45, 7 }, - { 255, 249, 126, 103, 12 }, - { 235, 79, 119, 223, 13 }, - { 62, 103, 233, 255, 15 }, }, - { { 189, 250, 159, 0, 7 }, - { 215, 221, 124, 82, 8 }, - { 224, 15, 149, 251, 13 }, - { 20, 163, 235, 190, 11 }, }, - { { 194, 112, 53, 187, 8 }, - { 81, 37, 131, 151, 7 }, - { 29, 218, 192, 228, 3 }, - { 238, 156, 26, 72, 10 }, }, - { { 196, 108, 5, 172, 13 }, - { 157, 37, 67, 162, 10 }, - { 179, 90, 3, 98, 3 }, - { 84, 92, 42, 75, 9 }, }, - { { 196, 121, 84, 220, 3 }, - { 237, 55, 128, 154, 10 }, - { 195, 178, 169, 226, 3 }, - { 85, 144, 30, 203, 7 }, }, - { { 194, 106, 81, 13, 8 }, - { 57, 36, 9, 151, 8 }, - { 27, 8, 165, 100, 3 }, - { 30, 153, 2, 73, 12 }, }, - { { 198, 164, 159, 104, 4 }, - { 145, 225, 86, 187, 0 }, - { 33, 111, 146, 86, 3 }, - { 13, 214, 168, 120, 9 }, }, - { { 192, 158, 44, 235, 4 }, - { 64, 33, 94, 174, 15 }, - { 45, 115, 71, 144, 3 }, - { 247, 87, 168, 64, 2 }, }, - { { 198, 146, 33, 73, 13 }, - { 212, 32, 89, 143, 4 }, - { 185, 40, 68, 150, 3 }, - { 47, 25, 160, 66, 11 }, }, - { { 196, 186, 131, 207, 8 }, - { 217, 224, 25, 142, 11 }, - { 31, 60, 21, 210, 3 }, - { 215, 25, 128, 121, 11 }, }, - { { 198, 231, 133, 67, 11 }, - { 149, 151, 25, 175, 1 }, - { 220, 42, 30, 118, 3 }, - { 143, 89, 142, 154, 9 }, }, - { { 198, 215, 175, 70, 4 }, - { 216, 199, 92, 171, 5 }, - { 38, 47, 94, 182, 3 }, - { 173, 83, 174, 49, 11 }, }, - { { 197, 54, 85, 77, 11 }, - { 253, 49, 9, 254, 0 }, - { 219, 42, 166, 202, 3 }, - { 7, 249, 8, 203, 15 }, }, - { { 195, 147, 255, 13, 7 }, - { 124, 243, 92, 215, 4 }, - { 235, 15, 252, 156, 3 }, - { 46, 179, 172, 243, 14 }, }, - { { 193, 245, 112, 165, 10 }, - { 105, 22, 19, 246, 6 }, - { 90, 80, 234, 248, 3 }, - { 102, 252, 134, 137, 6 }, }, - { { 204, 57, 113, 197, 0 }, - { 251, 2, 0, 158, 14 }, - { 10, 56, 233, 195, 3 }, - { 119, 144, 4, 13, 15 }, }, - { { 200, 2, 137, 73, 2 }, - { 18, 176, 12, 142, 0 }, - { 73, 41, 20, 1, 3 }, - { 7, 19, 0, 212, 8 }, }, - { { 200, 124, 100, 74, 5 }, - { 103, 37, 64, 170, 13 }, - { 165, 34, 99, 225, 3 }, - { 181, 80, 42, 78, 6 }, }, - { { 202, 103, 225, 13, 7 }, - { 63, 182, 72, 167, 4 }, - { 235, 8, 126, 101, 3 }, - { 46, 81, 38, 223, 12 }, }, - { { 200, 153, 231, 66, 2 }, - { 114, 211, 16, 138, 13 }, - { 68, 46, 121, 145, 3 }, - { 181, 16, 140, 180, 14 }, }, - { { 200, 170, 178, 16, 5 }, - { 7, 192, 216, 146, 12 }, - { 160, 132, 213, 81, 3 }, - { 52, 145, 176, 62, 0 }, }, - { { 204, 166, 213, 159, 3 }, - { 191, 177, 152, 182, 3 }, - { 207, 154, 182, 83, 3 }, - { 198, 209, 152, 223, 13 }, }, - { { 202, 229, 134, 251, 14 }, - { 3, 247, 211, 175, 3 }, - { 125, 246, 26, 117, 3 }, - { 207, 92, 190, 252, 0 }, }, - { { 202, 217, 124, 49, 15 }, - { 102, 23, 215, 151, 12 }, - { 248, 195, 233, 181, 3 }, - { 62, 158, 190, 134, 6 }, }, - { { 206, 228, 202, 14, 6 }, - { 171, 244, 84, 163, 1 }, - { 103, 5, 50, 119, 3 }, - { 140, 82, 162, 253, 5 }, }, - { { 203, 45, 164, 50, 12 }, - { 3, 131, 195, 227, 13 }, - { 52, 194, 91, 77, 3 }, - { 188, 124, 60, 28, 0 }, }, - { { 203, 87, 214, 130, 2 }, - { 98, 215, 8, 243, 3 }, - { 68, 22, 190, 173, 3 }, - { 204, 241, 14, 180, 6 }, }, - { { 201, 144, 33, 177, 0 }, - { 82, 0, 146, 198, 6 }, - { 8, 216, 64, 153, 3 }, - { 102, 52, 144, 4, 10 }, }, - { { 201, 130, 95, 176, 10 }, - { 50, 81, 159, 210, 2 }, - { 80, 223, 164, 25, 3 }, - { 68, 191, 152, 164, 12 }, }, - { { 203, 158, 212, 36, 7 }, - { 110, 145, 90, 243, 8 }, - { 226, 66, 183, 157, 3 }, - { 28, 245, 168, 151, 6 }, }, - { { 205, 134, 171, 157, 13 }, - { 158, 224, 221, 230, 6 }, - { 187, 157, 86, 27, 3 }, - { 102, 123, 176, 119, 9 }, }, - { { 201, 201, 176, 119, 5 }, - { 14, 134, 210, 222, 13 }, - { 174, 224, 217, 57, 3 }, - { 183, 180, 182, 23, 0 }, }, - { { 201, 250, 31, 99, 2 }, - { 83, 85, 30, 222, 9 }, - { 76, 111, 133, 249, 3 }, - { 151, 183, 138, 172, 10 }, }, - { { 203, 247, 64, 6, 9 }, - { 111, 6, 25, 227, 1 }, - { 150, 0, 46, 253, 3 }, - { 140, 121, 134, 15, 6 }, }, - { { 205, 218, 13, 28, 15 }, - { 222, 53, 221, 194, 8 }, - { 243, 139, 5, 187, 3 }, - { 20, 59, 186, 199, 11 }, }, - { { 208, 11, 130, 83, 15 }, - { 4, 218, 201, 142, 9 }, - { 252, 164, 29, 0, 11 }, - { 151, 25, 53, 178, 0 }, }, - { { 212, 59, 207, 214, 15 }, - { 253, 219, 205, 138, 11 }, - { 246, 191, 61, 194, 11 }, - { 213, 27, 61, 187, 15 }, }, - { { 214, 98, 171, 209, 11 }, - { 149, 220, 141, 143, 6 }, - { 216, 189, 84, 102, 11 }, - { 111, 27, 19, 186, 9 }, }, - { { 212, 102, 200, 58, 15 }, - { 165, 188, 207, 162, 1 }, - { 245, 193, 54, 98, 11 }, - { 132, 95, 51, 218, 5 }, }, - { { 209, 32, 28, 120, 10 }, - { 1, 57, 135, 218, 0 }, - { 81, 227, 128, 72, 11 }, - { 5, 190, 25, 200, 0 }, }, - { { 211, 57, 65, 195, 9 }, - { 117, 10, 1, 207, 11 }, - { 156, 56, 41, 204, 11 }, - { 223, 56, 5, 10, 14 }, }, - { { 209, 7, 160, 253, 4 }, - { 8, 170, 202, 238, 6 }, - { 43, 240, 94, 8, 11 }, - { 103, 117, 53, 81, 0 }, }, - { { 209, 149, 58, 14, 2 }, - { 72, 122, 20, 242, 5 }, - { 71, 5, 202, 152, 11 }, - { 164, 242, 133, 225, 2 }, }, - { { 215, 189, 66, 29, 13 }, - { 237, 106, 209, 231, 8 }, - { 187, 132, 43, 222, 11 }, - { 30, 120, 181, 107, 7 }, }, - { { 215, 192, 166, 80, 13 }, - { 132, 205, 209, 203, 4 }, - { 176, 166, 80, 62, 11 }, - { 45, 56, 187, 50, 1 }, }, - { { 215, 229, 141, 245, 12 }, - { 153, 143, 215, 239, 2 }, - { 58, 251, 26, 126, 11 }, - { 79, 126, 191, 25, 9 }, }, - { { 215, 197, 83, 246, 7 }, - { 188, 94, 210, 251, 3 }, - { 230, 252, 170, 62, 11 }, - { 205, 244, 183, 163, 13 }, }, - { { 213, 254, 112, 184, 8 }, - { 225, 44, 155, 242, 14 }, - { 17, 208, 231, 250, 11 }, - { 116, 253, 147, 72, 7 }, }, - { { 216, 12, 185, 190, 6 }, - { 26, 184, 198, 178, 15 }, - { 103, 217, 211, 1, 11 }, - { 244, 214, 49, 213, 8 }, }, - { { 222, 52, 186, 37, 6 }, - { 203, 216, 70, 183, 4 }, - { 106, 69, 210, 199, 11 }, - { 46, 214, 33, 189, 3 }, }, - { { 220, 14, 198, 139, 14 }, - { 162, 249, 73, 166, 11 }, - { 125, 22, 55, 3, 11 }, - { 214, 89, 41, 244, 5 }, }, - { { 222, 3, 247, 164, 11 }, - { 190, 219, 11, 147, 6 }, - { 210, 94, 252, 7, 11 }, - { 108, 157, 13, 183, 13 }, }, - { { 218, 93, 11, 138, 2 }, - { 82, 126, 4, 163, 11 }, - { 69, 29, 11, 165, 11 }, - { 220, 82, 7, 228, 10 }, }, - { { 216, 113, 249, 127, 15 }, - { 127, 190, 199, 158, 5 }, - { 255, 233, 248, 225, 11 }, - { 167, 158, 55, 223, 14 }, }, - { { 220, 189, 130, 231, 2 }, - { 203, 218, 18, 174, 11 }, - { 78, 116, 27, 211, 11 }, - { 215, 84, 133, 189, 3 }, }, - { { 220, 176, 90, 229, 5 }, - { 239, 72, 86, 158, 2 }, - { 170, 117, 160, 211, 11 }, - { 71, 150, 161, 47, 7 }, }, - { { 218, 147, 222, 80, 0 }, - { 98, 203, 156, 155, 0 }, - { 0, 167, 188, 149, 11 }, - { 13, 147, 157, 52, 6 }, }, - { { 218, 183, 241, 191, 8 }, - { 123, 170, 155, 183, 7 }, - { 31, 216, 254, 213, 11 }, - { 238, 221, 149, 93, 14 }, }, - { { 222, 159, 2, 50, 14 }, - { 194, 90, 219, 163, 9 }, - { 116, 196, 15, 151, 11 }, - { 156, 93, 181, 164, 3 }, }, - { { 218, 196, 13, 85, 0 }, - { 26, 13, 148, 175, 0 }, - { 10, 171, 2, 53, 11 }, - { 15, 82, 155, 5, 8 }, }, - { { 220, 197, 171, 128, 14 }, - { 146, 222, 85, 162, 6 }, - { 112, 29, 90, 51, 11 }, - { 100, 90, 167, 180, 9 }, }, - { { 218, 194, 178, 58, 10 }, - { 2, 252, 155, 147, 5 }, - { 85, 196, 212, 53, 11 }, - { 172, 157, 147, 244, 0 }, }, - { { 218, 218, 88, 253, 0 }, - { 106, 44, 158, 159, 10 }, - { 11, 241, 165, 181, 11 }, - { 95, 151, 147, 69, 6 }, }, - { { 217, 37, 142, 168, 1 }, - { 7, 235, 6, 226, 2 }, - { 129, 87, 26, 73, 11 }, - { 68, 118, 13, 126, 0 }, }, - { { 221, 5, 118, 59, 14 }, - { 162, 123, 195, 246, 5 }, - { 125, 198, 234, 11, 11 }, - { 166, 252, 61, 228, 5 }, }, - { { 221, 46, 153, 8, 12 }, - { 147, 168, 77, 242, 8 }, - { 49, 9, 151, 75, 11 }, - { 20, 251, 33, 92, 9 }, }, - { { 217, 85, 104, 199, 5 }, - { 110, 14, 68, 238, 7 }, - { 174, 49, 106, 169, 11 }, - { 231, 114, 39, 7, 6 }, }, - { { 219, 80, 109, 76, 3 }, - { 126, 61, 4, 203, 4 }, - { 195, 43, 96, 173, 11 }, - { 45, 50, 11, 199, 14 }, }, - { { 223, 104, 170, 56, 8 }, - { 131, 236, 135, 195, 12 }, - { 17, 197, 81, 111, 11 }, - { 60, 62, 19, 124, 1 }, }, - { { 219, 66, 135, 167, 5 }, - { 30, 205, 74, 199, 3 }, - { 174, 94, 20, 45, 11 }, - { 206, 53, 43, 55, 8 }, }, - { { 219, 129, 40, 179, 13 }, - { 6, 10, 215, 199, 7 }, - { 188, 209, 72, 29, 11 }, - { 238, 62, 181, 6, 0 }, }, - { { 217, 163, 194, 250, 13 }, - { 39, 234, 219, 202, 3 }, - { 181, 244, 60, 89, 11 }, - { 197, 61, 181, 126, 4 }, }, - { { 221, 179, 56, 211, 8 }, - { 195, 10, 157, 222, 7 }, - { 28, 177, 204, 219, 11 }, - { 231, 187, 149, 12, 3 }, }, - { { 217, 202, 123, 155, 1 }, - { 54, 108, 156, 214, 15 }, - { 141, 157, 229, 57, 11 }, - { 246, 179, 147, 102, 12 }, }, - { { 219, 246, 158, 176, 7 }, - { 71, 221, 222, 243, 2 }, - { 224, 215, 150, 253, 11 }, - { 76, 247, 187, 190, 2 }, }, - { { 226, 8, 108, 175, 8 }, - { 40, 33, 39, 135, 15 }, - { 31, 83, 97, 4, 7 }, - { 254, 30, 72, 65, 4 }, }, - { { 224, 27, 171, 150, 4 }, - { 88, 194, 236, 130, 15 }, - { 38, 157, 93, 128, 7 }, - { 244, 19, 116, 49, 10 }, }, - { { 224, 72, 115, 243, 0 }, - { 48, 68, 162, 158, 15 }, - { 12, 252, 225, 32, 7 }, - { 247, 148, 82, 32, 12 }, }, - { { 228, 117, 169, 91, 14 }, - { 209, 182, 229, 174, 5 }, - { 125, 169, 90, 226, 7 }, - { 167, 90, 118, 216, 11 }, }, - { { 230, 128, 236, 116, 9 }, - { 172, 129, 183, 139, 4 }, - { 146, 227, 112, 22, 7 }, - { 45, 30, 216, 19, 5 }, }, - { { 226, 175, 118, 72, 7 }, - { 37, 115, 120, 187, 12 }, - { 225, 38, 239, 84, 7 }, - { 61, 209, 236, 234, 4 }, }, - { { 224, 162, 200, 17, 13 }, - { 37, 128, 253, 134, 0 }, - { 184, 129, 52, 80, 7 }, - { 6, 27, 240, 26, 4 }, }, - { { 226, 203, 113, 80, 12 }, - { 48, 6, 249, 155, 12 }, - { 48, 168, 237, 52, 7 }, - { 61, 153, 246, 0, 12 }, }, - { { 224, 210, 73, 197, 15 }, - { 124, 20, 125, 142, 2 }, - { 250, 57, 36, 176, 7 }, - { 71, 27, 226, 131, 14 }, }, - { { 225, 12, 194, 130, 9 }, - { 36, 192, 33, 226, 11 }, - { 148, 20, 51, 8, 7 }, - { 212, 120, 64, 50, 4 }, }, - { { 225, 127, 68, 52, 3 }, - { 109, 23, 170, 226, 8 }, - { 194, 194, 47, 232, 7 }, - { 20, 117, 94, 139, 6 }, }, - { { 231, 173, 69, 177, 14 }, - { 177, 19, 243, 231, 10 }, - { 120, 218, 43, 94, 7 }, - { 94, 124, 252, 136, 13 }, }, - { { 225, 182, 90, 159, 9 }, - { 109, 96, 189, 246, 3 }, - { 159, 149, 166, 216, 7 }, - { 198, 251, 208, 107, 6 }, }, - { { 231, 252, 32, 65, 12 }, - { 193, 4, 113, 239, 12 }, - { 56, 32, 67, 254, 7 }, - { 63, 120, 226, 8, 3 }, }, - { { 238, 51, 65, 56, 1 }, - { 247, 34, 170, 131, 0 }, - { 129, 200, 44, 199, 7 }, - { 12, 21, 84, 78, 15 }, }, - { { 238, 124, 54, 51, 4 }, - { 195, 69, 226, 183, 13 }, - { 44, 198, 195, 231, 7 }, - { 190, 212, 122, 44, 3 }, }, - { { 236, 112, 123, 248, 10 }, - { 243, 116, 167, 154, 6 }, - { 81, 253, 224, 227, 7 }, - { 101, 158, 82, 236, 15 }, }, - { { 232, 123, 59, 230, 14 }, - { 91, 86, 111, 154, 15 }, - { 118, 125, 205, 225, 7 }, - { 245, 159, 102, 173, 10 }, }, - { { 232, 149, 232, 57, 1 }, - { 102, 162, 182, 166, 4 }, - { 137, 193, 122, 145, 7 }, - { 38, 86, 212, 86, 6 }, }, - { { 238, 172, 9, 125, 5 }, - { 159, 32, 246, 175, 8 }, - { 171, 233, 3, 87, 7 }, - { 31, 86, 240, 79, 9 }, }, - { { 236, 161, 201, 55, 4 }, - { 187, 130, 246, 134, 1 }, - { 46, 201, 56, 83, 7 }, - { 134, 22, 244, 29, 13 }, }, - { { 232, 151, 189, 197, 5 }, - { 94, 131, 124, 190, 6 }, - { 170, 59, 222, 145, 7 }, - { 103, 211, 236, 23, 10 }, }, - { { 232, 204, 18, 29, 0 }, - { 10, 100, 176, 182, 8 }, - { 11, 132, 131, 49, 7 }, - { 22, 208, 210, 101, 0 }, }, - { { 238, 193, 29, 105, 8 }, - { 146, 39, 55, 159, 0 }, - { 25, 107, 136, 55, 7 }, - { 15, 158, 206, 68, 9 }, }, - { { 237, 7, 255, 219, 10 }, - { 178, 243, 173, 254, 7 }, - { 93, 191, 254, 11, 7 }, - { 231, 251, 92, 244, 13 }, }, - { { 237, 105, 243, 54, 11 }, - { 191, 214, 163, 210, 13 }, - { 214, 204, 249, 107, 7 }, - { 180, 188, 86, 191, 13 }, }, - { { 237, 118, 20, 181, 12 }, - { 203, 5, 235, 246, 2 }, - { 58, 210, 134, 235, 7 }, - { 70, 253, 122, 13, 3 }, }, - { { 239, 132, 33, 209, 7 }, - { 150, 16, 240, 239, 6 }, - { 232, 184, 66, 31, 7 }, - { 111, 112, 240, 134, 9 }, }, - { { 235, 245, 218, 120, 2 }, - { 99, 246, 182, 251, 0 }, - { 65, 229, 186, 253, 7 }, - { 13, 246, 214, 252, 6 }, }, - { { 233, 249, 229, 214, 13 }, - { 127, 135, 241, 202, 15 }, - { 182, 186, 121, 249, 7 }, - { 245, 56, 254, 31, 14 }, }, - { { 235, 211, 15, 145, 9 }, - { 86, 71, 189, 199, 2 }, - { 152, 159, 12, 189, 7 }, - { 78, 59, 222, 38, 10 }, }, - { { 233, 219, 241, 32, 9 }, - { 118, 134, 59, 210, 12 }, - { 144, 72, 253, 185, 7 }, - { 52, 189, 198, 22, 14 }, }, - { { 244, 41, 139, 109, 8 }, - { 153, 234, 39, 142, 8 }, - { 27, 109, 25, 66, 15 }, - { 23, 30, 69, 121, 9 }, }, - { { 246, 10, 52, 34, 5 }, - { 132, 9, 106, 147, 13 }, - { 164, 66, 197, 6, 15 }, - { 188, 149, 105, 2, 1 }, }, - { { 244, 88, 85, 155, 1 }, - { 244, 45, 160, 150, 11 }, - { 141, 154, 161, 162, 15 }, - { 214, 144, 91, 66, 15 }, }, - { { 242, 118, 55, 97, 3 }, - { 85, 93, 42, 191, 4 }, - { 200, 110, 198, 228, 15 }, - { 47, 213, 75, 170, 10 }, }, - { { 244, 75, 10, 224, 6 }, - { 128, 94, 110, 138, 10 }, - { 96, 117, 13, 34, 15 }, - { 85, 23, 103, 160, 1 }, }, - { { 246, 110, 129, 75, 12 }, - { 145, 172, 105, 175, 9 }, - { 61, 40, 23, 102, 15 }, - { 159, 89, 99, 88, 9 }, }, - { { 246, 67, 29, 76, 6 }, - { 152, 63, 108, 155, 0 }, - { 99, 43, 140, 38, 15 }, - { 13, 147, 111, 193, 9 }, }, - { { 244, 87, 34, 238, 1 }, - { 204, 110, 42, 170, 7 }, - { 135, 116, 78, 162, 15 }, - { 229, 85, 71, 99, 3 }, }, - { { 244, 153, 83, 229, 11 }, - { 252, 90, 51, 158, 10 }, - { 218, 124, 169, 146, 15 }, - { 87, 156, 197, 163, 15 }, }, - { { 240, 252, 47, 27, 5 }, - { 85, 109, 244, 166, 13 }, - { 173, 143, 67, 240, 15 }, - { 182, 82, 251, 106, 10 }, }, - { { 247, 9, 30, 9, 12 }, - { 128, 107, 101, 215, 8 }, - { 57, 7, 137, 14, 15 }, - { 30, 186, 109, 96, 1 }, }, - { { 247, 38, 87, 194, 0 }, - { 177, 73, 40, 251, 3 }, - { 4, 62, 166, 78, 15 }, - { 205, 241, 73, 40, 13 }, }, - { { 241, 133, 27, 198, 13 }, - { 28, 74, 117, 250, 3 }, - { 182, 61, 138, 24, 15 }, - { 197, 250, 229, 35, 8 }, }, - { { 243, 136, 98, 232, 1 }, - { 36, 104, 50, 203, 14 }, - { 129, 116, 97, 28, 15 }, - { 125, 52, 193, 98, 4 }, }, - { { 247, 169, 183, 234, 6 }, - { 145, 251, 114, 219, 15 }, - { 101, 126, 217, 94, 15 }, - { 253, 180, 237, 248, 9 }, }, - { { 247, 138, 138, 92, 2 }, - { 136, 248, 188, 203, 8 }, - { 67, 165, 21, 30, 15 }, - { 29, 51, 209, 241, 1 }, }, - { { 243, 237, 194, 21, 2 }, - { 41, 222, 176, 231, 8 }, - { 74, 132, 59, 124, 15 }, - { 30, 112, 215, 185, 4 }, }, - { { 245, 245, 148, 242, 4 }, - { 193, 143, 242, 250, 3 }, - { 36, 242, 154, 250, 15 }, - { 197, 244, 255, 24, 3 }, }, - { { 248, 15, 62, 15, 4 }, - { 10, 107, 108, 182, 13 }, - { 47, 7, 207, 1, 15 }, - { 182, 211, 109, 101, 0 }, }, - { { 252, 190, 99, 125, 14 }, - { 251, 120, 251, 174, 12 }, - { 123, 236, 103, 211, 15 }, - { 55, 93, 241, 237, 15 }, }, - { { 248, 237, 33, 73, 11 }, - { 23, 62, 49, 174, 12 }, - { 217, 40, 75, 113, 15 }, - { 55, 88, 199, 206, 8 }, }, - { { 250, 216, 194, 128, 14 }, - { 98, 220, 113, 131, 10 }, - { 112, 20, 49, 181, 15 }, - { 92, 24, 227, 180, 6 }, }, - { { 254, 229, 5, 156, 6 }, - { 155, 63, 240, 163, 2 }, - { 99, 154, 10, 119, 15 }, - { 76, 80, 255, 205, 9 }, }, - { { 254, 196, 253, 27, 8 }, - { 178, 173, 181, 183, 5 }, - { 29, 139, 242, 55, 15 }, - { 174, 218, 219, 84, 13 }, }, - { { 250, 210, 225, 31, 4 }, - { 122, 172, 248, 135, 5 }, - { 47, 136, 116, 181, 15 }, - { 174, 17, 243, 85, 14 }, }, - { { 252, 195, 230, 115, 2 }, - { 162, 223, 186, 142, 5 }, - { 76, 230, 124, 51, 15 }, - { 167, 21, 223, 180, 5 }, }, - { { 249, 63, 162, 110, 11 }, - { 79, 250, 43, 234, 13 }, - { 215, 100, 95, 201, 15 }, - { 181, 125, 69, 255, 2 }, }, - { { 251, 86, 194, 94, 3 }, - { 110, 252, 168, 235, 1 }, - { 199, 164, 54, 173, 15 }, - { 141, 113, 83, 247, 6 }, }, - { { 255, 78, 140, 162, 15 }, - { 134, 157, 111, 227, 11 }, - { 244, 83, 23, 47, 15 }, - { 220, 127, 107, 150, 1 }, }, - { { 255, 79, 112, 226, 4 }, - { 162, 14, 106, 251, 15 }, - { 36, 112, 239, 47, 15 }, - { 253, 245, 103, 4, 5 }, }, - { { 249, 177, 42, 52, 9 }, - { 79, 74, 183, 194, 4 }, - { 146, 197, 72, 217, 15 }, - { 36, 62, 213, 47, 2 }, }, - { { 255, 135, 146, 120, 1 }, - { 134, 234, 186, 251, 0 }, - { 129, 228, 158, 31, 15 }, - { 13, 245, 213, 118, 1 }, }, - { { 249, 195, 188, 9, 10 }, - { 2, 191, 61, 214, 4 }, - { 89, 3, 220, 57, 15 }, - { 38, 187, 207, 212, 0 }, }, - { { 255, 202, 25, 209, 12 }, - { 146, 12, 253, 223, 10 }, - { 56, 185, 133, 63, 15 }, - { 95, 187, 243, 4, 9 }, }, - { { 255, 239, 23, 165, 10 }, - { 155, 95, 59, 247, 10 }, - { 90, 94, 143, 127, 15 }, - { 94, 253, 207, 173, 9 }, }, - { { 253, 254, 218, 140, 1 }, - { 239, 236, 60, 242, 10 }, - { 131, 21, 183, 251, 15 }, - { 84, 243, 195, 127, 7 }, }, - { { 6, 66, 233, 9, 7 }, - { 180, 180, 76, 5, 4 }, - { 233, 9, 116, 38, 0 }, - { 42, 3, 34, 210, 13 }, }, - { { 3, 36, 36, 70, 13 }, - { 13, 1, 65, 105, 5 }, - { 182, 34, 66, 76, 0 }, - { 169, 104, 40, 11, 0 }, }, - { { 7, 94, 92, 135, 12 }, - { 232, 5, 77, 117, 11 }, - { 62, 19, 167, 174, 0 }, - { 218, 235, 42, 1, 7 }, }, - { { 7, 136, 66, 250, 6 }, - { 160, 112, 210, 73, 11 }, - { 101, 244, 33, 30, 0 }, - { 217, 36, 176, 224, 5 }, }, - { { 10, 7, 178, 27, 11 }, - { 6, 242, 137, 53, 5 }, - { 221, 132, 222, 5, 0 }, - { 170, 201, 20, 246, 0 }, }, - { { 16, 148, 220, 241, 14 }, - { 96, 153, 215, 60, 2 }, - { 120, 243, 178, 144, 8 }, - { 67, 206, 185, 144, 6 }, }, - { { 22, 162, 90, 176, 15 }, - { 165, 88, 223, 17, 2 }, - { 240, 213, 164, 86, 8 }, - { 72, 143, 177, 170, 5 }, }, - { { 16, 253, 203, 109, 14 }, - { 121, 254, 87, 44, 8 }, - { 123, 109, 59, 240, 8 }, - { 19, 78, 167, 249, 14 }, }, - { { 17, 52, 172, 162, 12 }, - { 65, 137, 71, 96, 7 }, - { 52, 83, 82, 200, 8 }, - { 224, 110, 41, 24, 2 }, }, - { { 17, 166, 62, 16, 1 }, - { 5, 73, 156, 112, 4 }, - { 128, 135, 198, 88, 8 }, - { 32, 227, 153, 42, 0 }, }, - { { 30, 88, 44, 226, 2 }, - { 194, 29, 6, 9, 15 }, - { 68, 115, 65, 167, 8 }, - { 249, 6, 11, 132, 3 }, }, - { { 24, 99, 58, 139, 0 }, - { 3, 110, 12, 20, 7 }, - { 13, 21, 204, 97, 8 }, - { 226, 131, 7, 108, 0 }, }, - { { 26, 99, 1, 47, 13 }, - { 31, 46, 75, 5, 1 }, - { 191, 72, 12, 101, 8 }, - { 138, 13, 39, 79, 8 }, }, - { { 26, 241, 71, 206, 15 }, - { 127, 127, 81, 9, 3 }, - { 247, 62, 40, 245, 8 }, - { 201, 8, 175, 239, 14 }, }, - { { 25, 108, 177, 139, 1 }, - { 23, 172, 0, 116, 15 }, - { 141, 24, 211, 105, 8 }, - { 242, 224, 3, 94, 8 }, }, - { { 29, 149, 207, 92, 12 }, - { 250, 235, 213, 104, 0 }, - { 51, 175, 58, 155, 8 }, - { 1, 106, 189, 117, 15 }, }, - { { 25, 158, 56, 146, 2 }, - { 66, 24, 156, 112, 15 }, - { 68, 145, 199, 153, 8 }, - { 240, 227, 145, 132, 2 }, }, - { { 31, 252, 86, 35, 10 }, - { 227, 93, 19, 117, 9 }, - { 92, 70, 163, 255, 8 }, - { 154, 236, 139, 172, 7 }, }, - { { 36, 27, 41, 179, 3 }, - { 212, 18, 174, 4, 15 }, - { 204, 217, 77, 130, 4 }, - { 242, 7, 84, 130, 11 }, }, - { { 38, 231, 136, 133, 0 }, - { 137, 134, 60, 37, 2 }, - { 10, 17, 30, 118, 4 }, - { 74, 67, 198, 25, 1 }, }, - { { 33, 7, 54, 188, 1 }, - { 12, 99, 170, 112, 6 }, - { 131, 214, 206, 8, 4 }, - { 96, 229, 92, 99, 0 }, }, - { { 40, 77, 70, 86, 10 }, - { 42, 87, 161, 40, 9 }, - { 86, 166, 43, 33, 4 }, - { 145, 72, 94, 165, 4 }, }, - { { 46, 236, 227, 69, 8 }, - { 187, 196, 49, 45, 12 }, - { 26, 44, 115, 119, 4 }, - { 59, 72, 194, 61, 13 }, }, - { { 45, 79, 225, 77, 4 }, - { 186, 166, 104, 108, 12 }, - { 43, 40, 127, 43, 4 }, - { 51, 97, 102, 85, 13 }, }, - { { 45, 138, 40, 60, 7 }, - { 142, 48, 254, 64, 12 }, - { 227, 193, 69, 27, 4 }, - { 48, 39, 240, 199, 1 }, }, - { { 52, 248, 62, 55, 1 }, - { 205, 77, 182, 20, 13 }, - { 142, 199, 193, 242, 12 }, - { 178, 134, 219, 43, 3 }, }, - { { 56, 99, 93, 125, 8 }, - { 59, 47, 175, 28, 0 }, - { 27, 235, 172, 97, 12 }, - { 3, 143, 95, 77, 12 }, }, - { { 63, 162, 28, 196, 12 }, - { 139, 9, 125, 89, 2 }, - { 50, 51, 132, 95, 12 }, - { 73, 171, 233, 13, 1 }, }, - { { 66, 221, 151, 174, 14 }, - { 88, 247, 83, 177, 11 }, - { 119, 94, 155, 180, 2 }, - { 216, 220, 174, 241, 10 }, }, - { { 68, 223, 18, 214, 11 }, - { 204, 86, 153, 184, 11 }, - { 214, 180, 143, 178, 2 }, - { 209, 217, 150, 163, 3 }, }, - { { 65, 58, 205, 236, 8 }, - { 121, 161, 15, 200, 10 }, - { 19, 123, 53, 200, 2 }, - { 81, 63, 8, 89, 14 }, }, - { { 65, 192, 219, 73, 4 }, - { 48, 228, 84, 220, 0 }, - { 41, 45, 176, 56, 2 }, - { 3, 178, 162, 112, 12 }, }, - { { 76, 180, 0, 200, 13 }, - { 199, 32, 81, 168, 2 }, - { 177, 48, 2, 211, 2 }, - { 65, 88, 160, 78, 3 }, }, - { { 73, 35, 72, 149, 2 }, - { 43, 18, 140, 196, 2 }, - { 74, 145, 44, 73, 2 }, - { 66, 51, 20, 141, 4 }, }, - { { 82, 55, 185, 92, 6 }, - { 89, 186, 204, 185, 4 }, - { 99, 169, 222, 196, 10 }, - { 41, 211, 53, 217, 10 }, }, - { { 82, 81, 56, 221, 10 }, - { 72, 62, 133, 157, 6 }, - { 91, 177, 200, 164, 10 }, - { 107, 154, 23, 193, 2 }, }, - { { 94, 113, 146, 65, 3 }, - { 199, 222, 0, 157, 0 }, - { 200, 36, 152, 231, 10 }, - { 11, 144, 7, 190, 3 }, }, - { { 92, 178, 113, 235, 13 }, - { 247, 40, 91, 156, 7 }, - { 189, 120, 228, 211, 10 }, - { 227, 157, 161, 78, 15 }, }, - { { 91, 238, 55, 54, 6 }, - { 27, 93, 218, 241, 13 }, - { 102, 206, 199, 125, 10 }, - { 184, 245, 187, 173, 8 }, }, - { { 96, 5, 132, 21, 2 }, - { 8, 147, 160, 164, 0 }, - { 74, 130, 26, 0, 6 }, - { 2, 80, 92, 145, 0 }, }, - { { 100, 8, 128, 51, 8 }, - { 128, 128, 163, 132, 9 }, - { 28, 192, 17, 2, 6 }, - { 146, 28, 80, 16, 1 }, }, - { { 110, 40, 135, 140, 3 }, - { 159, 241, 32, 129, 10 }, - { 195, 30, 17, 71, 6 }, - { 88, 16, 72, 255, 9 }, }, - { { 108, 85, 84, 181, 7 }, - { 238, 23, 226, 180, 2 }, - { 234, 210, 170, 163, 6 }, - { 66, 212, 126, 135, 7 }, }, - { { 104, 186, 254, 70, 1 }, - { 111, 193, 60, 152, 13 }, - { 134, 39, 245, 209, 6 }, - { 177, 147, 200, 63, 6 }, }, - { { 104, 179, 85, 202, 7 }, - { 119, 51, 120, 152, 3 }, - { 229, 58, 172, 209, 6 }, - { 193, 145, 236, 206, 14 }, }, - { { 117, 156, 155, 102, 9 }, - { 220, 200, 55, 248, 9 }, - { 150, 109, 147, 154, 14 }, - { 145, 254, 193, 51, 11 }, }, - { { 124, 11, 200, 10, 0 }, - { 162, 170, 44, 128, 9 }, - { 5, 1, 61, 3, 14 }, - { 144, 19, 69, 84, 5 }, }, - { { 126, 89, 104, 196, 9 }, - { 238, 14, 37, 137, 14 }, - { 146, 49, 105, 167, 14 }, - { 121, 26, 71, 7, 7 }, }, - { { 120, 111, 61, 52, 5 }, - { 31, 15, 238, 176, 12 }, - { 162, 203, 207, 97, 14 }, - { 48, 215, 127, 15, 8 }, }, - { { 120, 193, 191, 255, 10 }, - { 26, 255, 183, 156, 7 }, - { 95, 255, 216, 49, 14 }, - { 227, 158, 223, 245, 8 }, }, - { { 122, 218, 185, 69, 10 }, - { 90, 156, 61, 157, 12 }, - { 90, 41, 213, 181, 14 }, - { 59, 155, 195, 149, 10 }, }, - { { 128, 60, 123, 156, 0 }, - { 121, 96, 132, 50, 14 }, - { 3, 157, 227, 192, 1 }, - { 116, 194, 16, 105, 14 }, }, - { { 135, 20, 88, 249, 7 }, - { 228, 48, 198, 127, 2 }, - { 233, 241, 162, 142, 1 }, - { 79, 230, 48, 194, 7 }, }, - { { 131, 27, 5, 165, 13 }, - { 92, 3, 75, 71, 10 }, - { 186, 90, 13, 140, 1 }, - { 94, 45, 44, 3, 10 }, }, - { { 136, 59, 180, 103, 4 }, - { 75, 131, 74, 30, 13 }, - { 46, 98, 221, 193, 1 }, - { 183, 133, 44, 29, 2 }, }, - { { 140, 169, 136, 156, 0 }, - { 139, 162, 148, 2, 10 }, - { 3, 145, 25, 83, 1 }, - { 84, 2, 148, 93, 1 }, }, - { { 142, 177, 3, 26, 11 }, - { 215, 114, 145, 3, 1 }, - { 213, 140, 8, 215, 1 }, - { 140, 8, 148, 238, 11 }, }, - { { 143, 39, 191, 54, 4 }, - { 155, 195, 206, 115, 5 }, - { 38, 207, 222, 79, 1 }, - { 172, 231, 60, 61, 9 }, }, - { { 143, 22, 179, 202, 0 }, - { 210, 224, 8, 123, 7 }, - { 5, 60, 214, 143, 1 }, - { 237, 225, 0, 116, 11 }, }, - { { 148, 24, 222, 74, 0 }, - { 224, 233, 4, 26, 9 }, - { 5, 39, 177, 130, 9 }, - { 149, 130, 9, 112, 7 }, }, - { { 147, 69, 36, 197, 0 }, - { 8, 15, 0, 111, 6 }, - { 10, 50, 74, 44, 9 }, - { 111, 96, 15, 1, 0 }, }, - { { 156, 253, 156, 216, 5 }, - { 199, 175, 212, 58, 10 }, - { 161, 179, 155, 243, 9 }, - { 85, 194, 191, 94, 3 }, }, - { { 155, 34, 55, 120, 13 }, - { 23, 105, 203, 91, 4 }, - { 177, 238, 196, 77, 9 }, - { 45, 173, 57, 110, 8 }, }, - { { 153, 250, 163, 209, 6 }, - { 83, 220, 216, 78, 14 }, - { 104, 188, 85, 249, 9 }, - { 119, 33, 179, 188, 10 }, }, - { { 162, 159, 28, 84, 3 }, - { 76, 19, 188, 59, 8 }, - { 194, 163, 143, 148, 5 }, - { 29, 195, 220, 131, 2 }, }, - { { 172, 79, 91, 26, 11 }, - { 182, 118, 173, 50, 9 }, - { 213, 141, 175, 35, 5 }, - { 148, 203, 86, 230, 13 }, }, - { { 172, 191, 109, 54, 6 }, - { 251, 19, 254, 34, 13 }, - { 102, 203, 111, 211, 5 }, - { 180, 71, 252, 141, 15 }, }, - { { 169, 185, 76, 64, 8 }, - { 99, 3, 53, 74, 8 }, - { 16, 35, 41, 217, 5 }, - { 21, 42, 204, 12, 6 }, }, - { { 182, 40, 233, 2, 12 }, - { 177, 136, 101, 3, 13 }, - { 52, 9, 113, 70, 13 }, - { 188, 10, 97, 24, 13 }, }, - { { 180, 109, 218, 222, 0 }, - { 169, 238, 164, 58, 11 }, - { 7, 181, 187, 98, 13 }, - { 213, 194, 87, 121, 5 }, }, - { { 178, 110, 246, 10, 1 }, - { 37, 237, 40, 51, 13 }, - { 133, 6, 247, 100, 13 }, - { 188, 193, 75, 122, 4 }, }, - { { 179, 37, 153, 105, 14 }, - { 17, 186, 103, 127, 0 }, - { 121, 105, 154, 76, 13 }, - { 15, 238, 101, 216, 8 }, }, - { { 179, 80, 40, 17, 5 }, - { 68, 12, 228, 71, 4 }, - { 168, 129, 64, 172, 13 }, - { 46, 34, 115, 2, 2 }, }, - { { 190, 28, 27, 59, 6 }, - { 210, 120, 230, 55, 9 }, - { 109, 205, 131, 135, 13 }, - { 158, 198, 113, 228, 11 }, }, - { { 188, 127, 85, 99, 14 }, - { 243, 31, 107, 62, 9 }, - { 124, 106, 175, 227, 13 }, - { 151, 205, 111, 140, 15 }, }, - { { 188, 175, 230, 141, 5 }, - { 175, 235, 120, 38, 14 }, - { 171, 22, 127, 83, 13 }, - { 118, 65, 237, 127, 5 }, }, - { { 189, 37, 146, 140, 4 }, - { 139, 234, 96, 114, 2 }, - { 35, 20, 154, 75, 13 }, - { 68, 224, 101, 125, 1 }, }, - { { 196, 27, 214, 183, 1 }, - { 236, 195, 138, 150, 11 }, - { 142, 214, 189, 130, 3 }, - { 214, 149, 28, 51, 7 }, }, - { { 198, 99, 190, 252, 14 }, - { 137, 247, 207, 155, 6 }, - { 115, 247, 220, 102, 3 }, - { 109, 159, 62, 249, 1 }, }, - { { 198, 86, 130, 19, 13 }, - { 196, 196, 201, 167, 1 }, - { 188, 132, 22, 166, 3 }, - { 142, 89, 50, 50, 3 }, }, - { { 198, 139, 73, 36, 10 }, - { 184, 18, 31, 131, 8 }, - { 82, 73, 45, 22, 3 }, - { 28, 31, 132, 129, 13 }, }, - { { 193, 61, 206, 190, 2 }, - { 105, 243, 134, 226, 11 }, - { 71, 215, 59, 200, 3 }, - { 212, 118, 28, 249, 6 }, }, - { { 197, 25, 109, 109, 1 }, - { 252, 35, 6, 206, 12 }, - { 139, 107, 105, 138, 3 }, - { 55, 54, 12, 67, 15 }, }, - { { 199, 77, 61, 239, 11 }, - { 156, 55, 7, 255, 15 }, - { 223, 123, 203, 46, 3 }, - { 255, 254, 14, 195, 9 }, }, - { { 193, 90, 43, 220, 9 }, - { 92, 100, 141, 202, 14 }, - { 147, 189, 69, 168, 3 }, - { 117, 59, 18, 99, 10 }, }, - { { 202, 35, 114, 33, 12 }, - { 35, 66, 75, 151, 4 }, - { 56, 68, 236, 69, 3 }, - { 46, 157, 36, 44, 4 }, }, - { { 207, 199, 244, 213, 9 }, - { 174, 135, 153, 255, 6 }, - { 154, 178, 254, 63, 3 }, - { 111, 249, 158, 23, 5 }, }, - { { 208, 49, 39, 226, 6 }, - { 81, 91, 66, 138, 7 }, - { 100, 126, 72, 192, 11 }, - { 229, 20, 45, 168, 10 }, }, - { { 212, 119, 84, 14, 0 }, - { 233, 47, 8, 178, 1 }, - { 7, 2, 174, 226, 11 }, - { 132, 209, 15, 73, 7 }, }, - { { 209, 134, 49, 90, 2 }, - { 16, 56, 152, 250, 5 }, - { 69, 168, 198, 24, 11 }, - { 165, 241, 145, 192, 8 }, }, - { { 219, 76, 100, 122, 11 }, - { 38, 61, 131, 235, 13 }, - { 213, 226, 99, 45, 11 }, - { 189, 124, 27, 198, 4 }, }, - { { 219, 132, 135, 144, 8 }, - { 18, 201, 145, 227, 2 }, - { 16, 158, 18, 29, 11 }, - { 76, 120, 153, 52, 8 }, }, - { { 223, 222, 6, 112, 0 }, - { 194, 77, 154, 235, 8 }, - { 0, 230, 7, 191, 11 }, - { 29, 117, 155, 36, 3 }, }, - { { 226, 41, 186, 96, 0 }, - { 1, 194, 38, 155, 12 }, - { 0, 101, 217, 68, 7 }, - { 61, 150, 68, 56, 0 }, }, - { { 225, 64, 224, 141, 6 }, - { 40, 180, 96, 198, 6 }, - { 107, 16, 112, 40, 7 }, - { 102, 48, 98, 209, 4 }, }, - { { 225, 154, 144, 165, 2 }, - { 72, 144, 58, 214, 10 }, - { 74, 80, 149, 152, 7 }, - { 86, 181, 192, 145, 2 }, }, - { { 231, 242, 192, 250, 9 }, - { 229, 164, 187, 203, 3 }, - { 149, 240, 52, 254, 7 }, - { 205, 61, 210, 90, 7 }, }, - { { 238, 173, 190, 131, 8 }, - { 131, 195, 53, 183, 15 }, - { 28, 23, 219, 87, 7 }, - { 254, 218, 204, 60, 1 }, }, - { { 240, 28, 242, 124, 1 }, - { 108, 232, 162, 186, 12 }, - { 131, 228, 243, 128, 15 }, - { 53, 212, 81, 115, 6 }, }, - { { 247, 101, 168, 38, 4 }, - { 137, 142, 102, 227, 5 }, - { 38, 65, 90, 110, 15 }, - { 172, 118, 103, 25, 1 }, }, - { { 247, 236, 195, 164, 13 }, - { 189, 204, 115, 227, 10 }, - { 178, 92, 51, 126, 15 }, - { 92, 124, 227, 59, 13 }, }, - { { 248, 45, 84, 113, 4 }, - { 35, 11, 226, 190, 8 }, - { 40, 226, 171, 65, 15 }, - { 23, 212, 125, 12, 4 }, }, - { { 254, 133, 143, 205, 11 }, - { 158, 251, 53, 175, 2 }, - { 219, 63, 26, 23, 15 }, - { 79, 90, 205, 247, 9 }, }, - { { 248, 227, 91, 11, 6 }, - { 51, 126, 124, 150, 1 }, - { 109, 13, 172, 113, 15 }, - { 134, 147, 231, 236, 12 }, }, - { { 254, 214, 62, 31, 15 }, - { 206, 125, 253, 183, 5 }, - { 255, 135, 198, 183, 15 }, - { 174, 219, 251, 231, 3 }, }, }; - -static unsigned char DICT_7X7_1000_BYTES[][4][7] = - { { { 221, 92, 108, 165, 202, 10, 1 }, - { 99, 179, 173, 228, 49, 180, 0 }, - { 168, 41, 210, 155, 29, 93, 1 }, - { 22, 198, 19, 218, 230, 227, 0 }, }, - { { 228, 27, 241, 62, 64, 171, 0 }, - { 17, 253, 137, 11, 181, 42, 1 }, - { 106, 129, 62, 71, 236, 19, 1 }, - { 170, 86, 232, 72, 223, 196, 0 }, }, - { { 158, 170, 43, 172, 93, 39, 1 }, - { 163, 182, 158, 145, 75, 171, 1 }, - { 242, 93, 26, 234, 42, 188, 1 }, - { 234, 233, 68, 188, 182, 226, 1 }, }, - { { 166, 103, 5, 183, 233, 76, 0 }, - { 221, 48, 50, 221, 165, 172, 0 }, - { 25, 75, 246, 208, 115, 50, 1 }, - { 26, 210, 221, 166, 6, 93, 1 }, }, - { { 198, 188, 123, 19, 50, 86, 0 }, - { 253, 193, 139, 113, 154, 97, 0 }, - { 53, 38, 100, 111, 30, 177, 1 }, - { 67, 44, 199, 104, 193, 223, 1 }, }, - { { 88, 128, 20, 35, 89, 238, 0 }, - { 4, 122, 28, 80, 184, 133, 1 }, - { 59, 205, 98, 20, 0, 141, 0 }, - { 208, 142, 133, 28, 47, 16, 0 }, }, - { { 211, 107, 190, 111, 84, 72, 0 }, - { 164, 240, 198, 70, 247, 63, 0 }, - { 9, 21, 123, 62, 235, 101, 1 }, - { 126, 119, 177, 49, 135, 146, 1 }, }, - { { 60, 161, 109, 136, 139, 219, 0 }, - { 37, 14, 191, 169, 44, 198, 0 }, - { 109, 232, 136, 219, 66, 158, 0 }, - { 49, 154, 74, 254, 184, 82, 0 }, }, - { { 137, 7, 31, 86, 150, 158, 0 }, - { 112, 74, 104, 49, 231, 125, 0 }, - { 60, 180, 181, 124, 112, 72, 1 }, - { 95, 115, 198, 11, 41, 7, 0 }, }, - { { 187, 101, 177, 141, 110, 63, 0 }, - { 200, 86, 142, 255, 101, 242, 1 }, - { 126, 59, 88, 198, 211, 110, 1 }, - { 167, 211, 127, 184, 181, 9, 1 }, }, - { { 245, 0, 209, 130, 244, 144, 1 }, - { 11, 88, 37, 139, 208, 113, 0 }, - { 132, 151, 160, 197, 128, 87, 1 }, - { 71, 5, 232, 210, 13, 104, 0 }, }, - { { 2, 38, 112, 184, 84, 26, 0 }, - { 208, 112, 139, 128, 96, 75, 0 }, - { 44, 21, 14, 135, 50, 32, 0 }, - { 105, 3, 0, 232, 135, 5, 1 }, }, - { { 84, 243, 107, 151, 72, 240, 1 }, - { 55, 25, 135, 197, 159, 200, 1 }, - { 135, 137, 116, 235, 103, 149, 0 }, - { 137, 252, 209, 240, 204, 118, 0 }, }, - { { 158, 36, 136, 70, 35, 153, 1 }, - { 235, 14, 86, 34, 161, 96, 0 }, - { 204, 226, 49, 8, 146, 60, 1 }, - { 3, 66, 162, 53, 56, 107, 1 }, }, - { { 189, 255, 50, 107, 201, 203, 1 }, - { 71, 255, 254, 76, 174, 186, 0 }, - { 233, 201, 235, 38, 127, 222, 1 }, - { 46, 186, 153, 63, 255, 241, 0 }, }, - { { 206, 17, 5, 176, 90, 151, 1 }, - { 147, 63, 8, 177, 20, 229, 0 }, - { 244, 173, 6, 208, 68, 57, 1 }, - { 83, 148, 70, 136, 126, 100, 1 }, }, - { { 211, 240, 97, 114, 31, 139, 0 }, - { 144, 45, 223, 37, 248, 177, 0 }, - { 104, 252, 39, 67, 7, 229, 1 }, - { 70, 143, 210, 125, 218, 4, 1 }, }, - { { 1, 225, 158, 206, 200, 237, 1 }, - { 38, 92, 98, 150, 175, 150, 1 }, - { 219, 137, 185, 188, 195, 192, 0 }, - { 180, 250, 180, 163, 29, 50, 0 }, }, - { { 38, 203, 204, 42, 112, 120, 1 }, - { 175, 176, 1, 14, 172, 79, 1 }, - { 143, 7, 42, 25, 233, 178, 0 }, - { 249, 26, 184, 64, 6, 250, 1 }, }, - { { 56, 125, 77, 118, 65, 19, 0 }, - { 112, 183, 95, 13, 133, 68, 0 }, - { 100, 65, 55, 89, 95, 14, 0 }, - { 17, 80, 216, 125, 118, 135, 0 }, }, - { { 58, 144, 74, 233, 225, 233, 1 }, - { 174, 63, 117, 200, 42, 2, 1 }, - { 203, 195, 203, 169, 4, 174, 0 }, - { 160, 42, 9, 215, 126, 58, 1 }, }, - { { 70, 212, 132, 123, 60, 221, 0 }, - { 221, 45, 64, 86, 248, 199, 0 }, - { 93, 158, 111, 16, 149, 177, 0 }, - { 113, 143, 181, 1, 90, 93, 1 }, }, - { { 72, 146, 167, 222, 158, 43, 1 }, - { 18, 7, 232, 163, 251, 143, 1 }, - { 234, 60, 189, 242, 164, 137, 0 }, - { 248, 239, 226, 139, 240, 36, 0 }, }, - { { 94, 118, 195, 228, 152, 168, 0 }, - { 193, 43, 103, 135, 51, 137, 1 }, - { 10, 140, 147, 225, 183, 61, 0 }, - { 200, 230, 112, 243, 106, 65, 1 }, }, - { { 116, 162, 222, 250, 47, 136, 1 }, - { 59, 104, 87, 170, 250, 142, 0 }, - { 136, 250, 47, 189, 162, 151, 0 }, - { 56, 175, 170, 245, 11, 110, 0 }, }, - { { 148, 93, 226, 40, 19, 89, 0 }, - { 69, 165, 149, 38, 38, 99, 0 }, - { 77, 100, 10, 35, 221, 20, 1 }, - { 99, 50, 50, 84, 210, 209, 0 }, }, - { { 155, 18, 149, 100, 237, 163, 1 }, - { 138, 127, 124, 3, 65, 188, 1 }, - { 226, 219, 147, 84, 164, 108, 1 }, - { 158, 193, 96, 31, 127, 40, 1 }, }, - { { 161, 212, 148, 45, 130, 248, 0 }, - { 68, 105, 32, 110, 41, 118, 1 }, - { 15, 160, 218, 20, 149, 194, 1 }, - { 183, 74, 59, 2, 75, 17, 0 }, }, - { { 187, 120, 230, 49, 108, 208, 1 }, - { 158, 187, 135, 78, 66, 244, 0 }, - { 133, 155, 70, 51, 143, 110, 1 }, - { 23, 161, 57, 112, 238, 188, 1 }, }, - { { 203, 181, 221, 203, 219, 24, 0 }, - { 224, 83, 115, 227, 188, 247, 0 }, - { 12, 109, 233, 221, 214, 233, 1 }, - { 119, 158, 227, 231, 101, 3, 1 }, }, - { { 211, 229, 238, 183, 131, 51, 0 }, - { 240, 36, 191, 230, 159, 116, 1 }, - { 102, 96, 246, 187, 211, 229, 1 }, - { 151, 124, 179, 254, 146, 7, 1 }, }, - { { 242, 148, 35, 63, 209, 156, 1 }, - { 210, 57, 180, 89, 187, 99, 0 }, - { 156, 197, 254, 98, 20, 167, 1 }, - { 99, 110, 205, 22, 206, 37, 1 }, }, - { { 253, 214, 209, 159, 59, 230, 1 }, - { 95, 75, 29, 255, 153, 187, 1 }, - { 179, 238, 124, 197, 181, 223, 1 }, - { 238, 204, 255, 220, 105, 125, 0 }, }, - { { 58, 70, 36, 238, 132, 238, 1 }, - { 198, 42, 236, 156, 225, 14, 1 }, - { 187, 144, 187, 146, 49, 46, 0 }, - { 184, 67, 156, 155, 170, 49, 1 }, }, - { { 98, 24, 126, 36, 59, 15, 0 }, - { 168, 229, 153, 56, 51, 133, 0 }, - { 120, 110, 18, 63, 12, 35, 0 }, - { 80, 230, 14, 76, 211, 138, 1 }, }, - { { 10, 205, 67, 111, 157, 195, 0 }, - { 196, 174, 121, 69, 207, 131, 0 }, - { 97, 220, 251, 97, 89, 168, 0 }, - { 96, 249, 209, 79, 58, 145, 1 }, }, - { { 14, 64, 212, 195, 142, 2, 0 }, - { 129, 66, 105, 230, 192, 132, 0 }, - { 32, 56, 225, 149, 129, 56, 0 }, - { 16, 129, 179, 203, 33, 64, 1 }, }, - { { 20, 159, 217, 160, 98, 36, 0 }, - { 105, 241, 5, 179, 12, 8, 1 }, - { 18, 35, 2, 205, 252, 148, 0 }, - { 136, 24, 102, 208, 71, 203, 0 }, }, - { { 25, 162, 225, 26, 101, 237, 0 }, - { 28, 30, 151, 19, 232, 26, 1 }, - { 91, 211, 44, 67, 162, 204, 0 }, - { 172, 11, 228, 116, 188, 28, 0 }, }, - { { 43, 80, 130, 126, 27, 52, 0 }, - { 144, 35, 80, 62, 131, 211, 1 }, - { 22, 108, 63, 32, 133, 106, 0 }, - { 229, 224, 190, 5, 98, 4, 1 }, }, - { { 41, 105, 56, 146, 159, 163, 0 }, - { 48, 206, 186, 172, 196, 145, 1 }, - { 98, 252, 164, 142, 75, 74, 0 }, - { 196, 145, 154, 174, 185, 134, 0 }, }, - { { 44, 247, 84, 219, 244, 86, 1 }, - { 95, 83, 107, 220, 204, 79, 0 }, - { 181, 23, 237, 149, 119, 154, 0 }, - { 121, 25, 157, 235, 101, 125, 0 }, }, - { { 53, 233, 63, 158, 54, 124, 0 }, - { 61, 192, 134, 189, 239, 87, 1 }, - { 31, 54, 60, 254, 75, 214, 0 }, - { 245, 123, 222, 176, 129, 222, 0 }, }, - { { 63, 116, 171, 216, 61, 243, 1 }, - { 255, 15, 222, 143, 66, 211, 1 }, - { 231, 222, 13, 234, 151, 126, 0 }, - { 229, 161, 120, 189, 248, 127, 1 }, }, - { { 68, 208, 41, 244, 255, 64, 0 }, - { 61, 49, 240, 165, 89, 129, 0 }, - { 1, 127, 151, 202, 5, 145, 0 }, - { 64, 205, 82, 135, 198, 94, 0 }, }, - { { 72, 64, 228, 132, 42, 245, 0 }, - { 12, 14, 129, 182, 17, 196, 1 }, - { 87, 170, 16, 147, 129, 9, 0 }, - { 145, 196, 54, 192, 184, 24, 0 }, }, - { { 95, 25, 19, 104, 52, 2, 0 }, - { 137, 227, 76, 1, 86, 19, 0 }, - { 32, 22, 11, 100, 76, 125, 0 }, - { 100, 53, 64, 25, 99, 200, 1 }, }, - { { 94, 178, 114, 64, 61, 93, 1 }, - { 143, 71, 215, 16, 122, 201, 0 }, - { 221, 94, 1, 39, 38, 189, 0 }, - { 73, 175, 4, 117, 241, 120, 1 }, }, - { { 111, 155, 36, 31, 21, 194, 0 }, - { 149, 139, 152, 72, 221, 31, 0 }, - { 33, 212, 124, 18, 108, 251, 0 }, - { 124, 93, 137, 12, 232, 212, 1 }, }, - { { 114, 170, 249, 32, 168, 227, 1 }, - { 174, 236, 175, 11, 24, 136, 1 }, - { 227, 138, 130, 79, 170, 167, 0 }, - { 136, 140, 104, 122, 155, 186, 1 }, }, - { { 121, 136, 117, 59, 134, 54, 1 }, - { 18, 226, 173, 121, 216, 86, 1 }, - { 182, 48, 238, 87, 8, 207, 0 }, - { 181, 13, 207, 90, 163, 164, 0 }, }, - { { 123, 231, 19, 174, 121, 81, 1 }, - { 206, 118, 22, 141, 159, 219, 0 }, - { 197, 79, 58, 228, 115, 239, 0 }, - { 109, 252, 216, 180, 55, 57, 1 }, }, - { { 133, 106, 70, 201, 89, 224, 0 }, - { 5, 152, 83, 196, 2, 191, 1 }, - { 3, 205, 73, 177, 43, 80, 1 }, - { 254, 160, 17, 229, 12, 208, 0 }, }, - { { 142, 63, 238, 242, 217, 81, 1 }, - { 247, 183, 243, 130, 134, 237, 0 }, - { 197, 77, 167, 187, 254, 56, 1 }, - { 91, 176, 160, 231, 246, 247, 1 }, }, - { { 173, 56, 205, 33, 115, 103, 1 }, - { 47, 183, 27, 123, 0, 53, 1 }, - { 243, 103, 66, 89, 142, 90, 1 }, - { 214, 0, 111, 108, 118, 250, 0 }, }, - { { 177, 55, 37, 48, 185, 63, 1 }, - { 90, 37, 190, 25, 36, 253, 1 }, - { 254, 78, 134, 82, 118, 70, 1 }, - { 223, 146, 76, 62, 210, 45, 0 }, }, - { { 186, 195, 128, 210, 81, 219, 0 }, - { 148, 30, 92, 142, 172, 105, 0 }, - { 109, 197, 37, 128, 225, 174, 1 }, - { 75, 26, 184, 157, 60, 20, 1 }, }, - { { 193, 117, 109, 93, 24, 166, 1 }, - { 114, 9, 203, 85, 21, 183, 1 }, - { 178, 140, 93, 91, 87, 65, 1 }, - { 246, 212, 85, 105, 200, 39, 0 }, }, - { { 206, 97, 211, 126, 229, 17, 0 }, - { 153, 118, 115, 7, 215, 98, 0 }, - { 68, 83, 191, 101, 195, 57, 1 }, - { 35, 117, 240, 103, 55, 76, 1 }, }, - { { 210, 92, 152, 54, 221, 91, 1 }, - { 246, 245, 60, 6, 241, 225, 0 }, - { 237, 93, 182, 12, 157, 37, 1 }, - { 67, 199, 176, 30, 87, 183, 1 }, }, - { { 208, 106, 145, 233, 239, 206, 1 }, - { 14, 248, 126, 247, 112, 170, 0 }, - { 185, 251, 203, 196, 171, 5, 1 }, - { 42, 135, 119, 191, 15, 184, 0 }, }, - { { 73, 228, 227, 141, 169, 90, 1 }, - { 78, 2, 187, 199, 59, 210, 0 }, - { 173, 74, 216, 227, 147, 201, 0 }, - { 37, 238, 113, 238, 160, 57, 0 }, }, - { { 82, 209, 159, 40, 31, 179, 0 }, - { 160, 109, 28, 39, 94, 199, 1 }, - { 102, 252, 10, 124, 197, 165, 0 }, - { 241, 189, 114, 28, 91, 2, 1 }, }, - { { 2, 24, 175, 24, 115, 160, 1 }, - { 186, 153, 144, 35, 2, 7, 1 }, - { 130, 231, 12, 122, 140, 32, 0 }, - { 240, 32, 98, 4, 204, 174, 1 }, }, - { { 5, 192, 75, 56, 81, 119, 0 }, - { 53, 52, 25, 21, 10, 83, 1 }, - { 119, 69, 14, 105, 1, 208, 0 }, - { 229, 40, 84, 76, 22, 86, 0 }, }, - { { 15, 88, 169, 33, 137, 159, 0 }, - { 161, 175, 184, 87, 32, 208, 0 }, - { 124, 200, 194, 74, 141, 120, 0 }, - { 5, 130, 117, 14, 250, 194, 1 }, }, - { { 15, 206, 165, 68, 68, 26, 0 }, - { 193, 146, 200, 7, 105, 92, 0 }, - { 44, 17, 17, 82, 185, 248, 0 }, - { 29, 75, 112, 9, 164, 193, 1 }, }, - { { 21, 238, 211, 115, 20, 216, 1 }, - { 87, 232, 71, 71, 234, 89, 0 }, - { 141, 148, 103, 101, 187, 212, 0 }, - { 77, 43, 241, 113, 11, 245, 0 }, }, - { { 22, 241, 226, 252, 69, 158, 1 }, - { 147, 57, 223, 150, 111, 66, 0 }, - { 188, 209, 31, 163, 199, 180, 0 }, - { 33, 123, 52, 253, 206, 100, 1 }, }, - { { 30, 94, 76, 65, 59, 115, 0 }, - { 237, 135, 93, 100, 0, 205, 1 }, - { 103, 110, 65, 25, 61, 60, 0 }, - { 217, 128, 19, 93, 112, 219, 1 }, }, - { { 31, 194, 82, 102, 200, 118, 1 }, - { 135, 114, 109, 20, 139, 216, 1 }, - { 183, 9, 179, 37, 33, 252, 0 }, - { 141, 232, 148, 91, 39, 112, 1 }, }, - { { 39, 33, 22, 246, 49, 159, 1 }, - { 155, 108, 90, 152, 167, 85, 0 }, - { 252, 198, 55, 180, 66, 114, 0 }, - { 85, 114, 140, 173, 27, 108, 1 }, }, - { { 39, 155, 122, 137, 27, 220, 0 }, - { 165, 201, 145, 248, 46, 219, 0 }, - { 29, 236, 72, 175, 108, 242, 0 }, - { 109, 186, 15, 196, 201, 210, 1 }, }, - { { 46, 12, 48, 75, 106, 108, 0 }, - { 205, 210, 192, 120, 160, 130, 1 }, - { 27, 43, 105, 6, 24, 58, 0 }, - { 160, 130, 143, 1, 165, 217, 1 }, }, - { { 47, 169, 226, 121, 230, 43, 0 }, - { 153, 182, 235, 106, 110, 18, 1 }, - { 106, 51, 207, 35, 202, 250, 0 }, - { 164, 59, 43, 107, 182, 204, 1 }, }, - { { 44, 207, 211, 2, 147, 19, 1 }, - { 67, 198, 57, 47, 142, 73, 0 }, - { 228, 100, 160, 101, 249, 154, 0 }, - { 73, 56, 250, 78, 49, 225, 0 }, }, - { { 49, 83, 3, 2, 18, 230, 1 }, - { 6, 9, 12, 61, 134, 25, 1 }, - { 179, 164, 32, 96, 101, 70, 0 }, - { 204, 48, 222, 24, 72, 48, 0 }, }, - { { 48, 120, 115, 90, 253, 41, 1 }, - { 26, 213, 247, 13, 226, 131, 1 }, - { 202, 95, 173, 103, 15, 6, 0 }, - { 224, 163, 216, 119, 213, 172, 0 }, }, - { { 55, 226, 67, 44, 167, 160, 1 }, - { 139, 40, 55, 45, 75, 26, 1 }, - { 130, 242, 154, 97, 35, 246, 0 }, - { 172, 105, 90, 118, 10, 104, 1 }, }, - { { 76, 11, 79, 138, 255, 84, 0 }, - { 45, 146, 49, 177, 214, 207, 0 }, - { 21, 127, 168, 249, 104, 25, 0 }, - { 121, 181, 198, 198, 36, 218, 0 }, }, - { { 78, 218, 184, 202, 73, 33, 1 }, - { 163, 215, 208, 134, 152, 138, 1 }, - { 194, 73, 41, 142, 173, 185, 0 }, - { 168, 140, 176, 133, 245, 226, 1 }, }, - { { 76, 218, 221, 21, 152, 110, 0 }, - { 53, 195, 41, 87, 57, 141, 1 }, - { 59, 12, 212, 93, 173, 153, 0 }, - { 216, 206, 117, 74, 97, 214, 0 }, }, - { { 86, 123, 85, 187, 37, 96, 1 }, - { 159, 225, 23, 197, 212, 14, 1 }, - { 131, 82, 110, 213, 111, 53, 0 }, - { 184, 21, 209, 244, 67, 252, 1 }, }, - { { 84, 151, 117, 241, 41, 203, 0 }, - { 93, 109, 221, 193, 60, 140, 0 }, - { 105, 202, 71, 215, 116, 149, 0 }, - { 24, 158, 65, 221, 219, 93, 0 }, }, - { { 84, 152, 140, 205, 4, 162, 0 }, - { 33, 137, 76, 194, 89, 6, 1 }, - { 34, 144, 89, 152, 140, 149, 0 }, - { 176, 77, 33, 153, 72, 194, 0 }, }, - { { 94, 110, 164, 168, 119, 169, 0 }, - { 201, 190, 150, 166, 112, 15, 1 }, - { 74, 247, 10, 146, 187, 61, 0 }, - { 248, 7, 50, 180, 190, 201, 1 }, }, - { { 99, 4, 81, 119, 99, 178, 0 }, - { 216, 120, 89, 105, 145, 80, 1 }, - { 38, 227, 119, 69, 16, 99, 0 }, - { 133, 68, 203, 77, 15, 13, 1 }, }, - { { 105, 226, 58, 120, 116, 228, 1 }, - { 62, 122, 194, 28, 90, 27, 1 }, - { 147, 151, 15, 46, 35, 203, 0 }, - { 236, 45, 28, 33, 175, 62, 0 }, }, - { { 109, 23, 226, 74, 80, 28, 1 }, - { 67, 19, 193, 26, 182, 91, 0 }, - { 156, 5, 41, 35, 244, 91, 0 }, - { 109, 54, 172, 65, 228, 97, 0 }, }, - { { 112, 62, 128, 226, 203, 177, 0 }, - { 64, 189, 118, 170, 144, 200, 1 }, - { 70, 233, 163, 128, 190, 7, 0 }, - { 137, 132, 170, 183, 94, 129, 0 }, }, - { { 119, 80, 90, 139, 20, 43, 1 }, - { 163, 69, 13, 204, 242, 19, 1 }, - { 234, 20, 104, 173, 5, 119, 0 }, - { 228, 39, 153, 216, 81, 98, 1 }, }, - { { 130, 100, 225, 43, 36, 151, 1 }, - { 202, 44, 139, 87, 192, 98, 0 }, - { 244, 146, 106, 67, 147, 32, 1 }, - { 35, 1, 245, 104, 154, 41, 1 }, }, - { { 131, 253, 167, 209, 241, 197, 0 }, - { 220, 157, 242, 215, 14, 53, 0 }, - { 81, 199, 197, 242, 223, 224, 1 }, - { 86, 56, 117, 167, 220, 157, 1 }, }, - { { 147, 189, 107, 77, 96, 125, 1 }, - { 238, 149, 199, 81, 47, 114, 1 }, - { 223, 3, 89, 107, 94, 228, 1 }, - { 167, 122, 69, 113, 212, 187, 1 }, }, - { { 148, 216, 165, 194, 122, 237, 1 }, - { 15, 157, 196, 183, 168, 165, 1 }, - { 219, 175, 33, 210, 141, 148, 1 }, - { 210, 138, 246, 145, 220, 248, 0 }, }, - { { 148, 247, 189, 26, 15, 79, 1 }, - { 119, 69, 158, 55, 236, 174, 0 }, - { 249, 120, 44, 94, 247, 148, 1 }, - { 58, 155, 246, 60, 209, 119, 0 }, }, - { { 152, 241, 170, 62, 148, 248, 0 }, - { 52, 43, 166, 6, 239, 99, 1 }, - { 15, 148, 190, 42, 199, 140, 1 }, - { 227, 123, 176, 50, 234, 22, 0 }, }, - { { 169, 138, 223, 140, 87, 40, 0 }, - { 32, 210, 17, 171, 107, 63, 1 }, - { 10, 117, 24, 253, 168, 202, 1 }, - { 254, 107, 106, 196, 37, 130, 0 }, }, - { { 177, 14, 54, 223, 65, 63, 0 }, - { 80, 212, 220, 216, 163, 126, 1 }, - { 126, 65, 125, 182, 56, 70, 1 }, - { 191, 98, 141, 157, 149, 133, 0 }, }, - { { 185, 234, 28, 100, 226, 107, 0 }, - { 44, 246, 110, 44, 41, 60, 1 }, - { 107, 35, 147, 28, 43, 206, 1 }, - { 158, 74, 26, 59, 55, 154, 0 }, }, - { { 188, 38, 74, 240, 242, 183, 1 }, - { 123, 62, 111, 184, 2, 105, 1 }, - { 246, 167, 135, 169, 50, 30, 1 }, - { 203, 32, 14, 251, 62, 111, 0 }, }, - { { 189, 63, 146, 218, 126, 170, 0 }, - { 89, 219, 78, 170, 230, 187, 1 }, - { 42, 191, 45, 164, 254, 94, 1 }, - { 238, 179, 170, 185, 109, 205, 0 }, }, - { { 192, 53, 46, 74, 202, 87, 0 }, - { 100, 21, 234, 48, 150, 230, 0 }, - { 117, 41, 169, 58, 86, 1, 1 }, - { 51, 180, 134, 43, 212, 19, 0 }, }, - { { 194, 54, 220, 36, 22, 128, 1 }, - { 226, 105, 3, 34, 81, 45, 0 }, - { 128, 180, 18, 29, 182, 33, 1 }, - { 90, 69, 34, 96, 75, 35, 1 }, }, - { { 211, 79, 245, 172, 136, 118, 1 }, - { 198, 224, 173, 151, 21, 254, 1 }, - { 183, 8, 154, 215, 249, 101, 1 }, - { 191, 212, 116, 218, 131, 177, 1 }, }, - { { 215, 6, 210, 240, 31, 150, 0 }, - { 209, 104, 93, 178, 82, 249, 0 }, - { 52, 252, 7, 165, 176, 117, 1 }, - { 79, 165, 38, 221, 11, 69, 1 }, }, - { { 216, 50, 245, 201, 86, 5, 0 }, - { 0, 87, 199, 243, 80, 47, 0 }, - { 80, 53, 73, 215, 166, 13, 1 }, - { 122, 5, 103, 241, 245, 0, 0 }, }, - { { 216, 217, 65, 4, 131, 170, 0 }, - { 0, 139, 61, 37, 61, 32, 1 }, - { 42, 224, 144, 65, 77, 141, 1 }, - { 130, 94, 82, 94, 104, 128, 0 }, }, - { { 224, 211, 100, 108, 77, 209, 1 }, - { 6, 61, 209, 12, 93, 238, 0 }, - { 197, 217, 27, 19, 101, 131, 1 }, - { 59, 221, 24, 69, 222, 48, 0 }, }, - { { 225, 232, 37, 60, 152, 205, 0 }, - { 20, 172, 162, 29, 57, 183, 0 }, - { 89, 140, 158, 82, 11, 195, 1 }, - { 118, 206, 92, 34, 154, 148, 0 }, }, - { { 229, 118, 79, 139, 232, 157, 1 }, - { 107, 29, 35, 221, 178, 254, 0 }, - { 220, 139, 232, 249, 55, 83, 1 }, - { 63, 166, 221, 226, 92, 107, 0 }, }, - { { 232, 80, 250, 159, 250, 74, 0 }, - { 60, 83, 169, 238, 179, 163, 0 }, - { 41, 47, 252, 175, 133, 11, 1 }, - { 98, 230, 187, 202, 229, 30, 0 }, }, - { { 235, 128, 234, 240, 130, 92, 1 }, - { 182, 34, 225, 186, 58, 112, 0 }, - { 157, 32, 135, 171, 128, 235, 1 }, - { 7, 46, 46, 195, 162, 54, 1 }, }, - { { 239, 108, 113, 174, 69, 200, 1 }, - { 199, 250, 147, 141, 241, 50, 0 }, - { 137, 209, 58, 199, 27, 123, 1 }, - { 38, 71, 216, 228, 175, 241, 1 }, }, - { { 247, 217, 64, 237, 177, 12, 0 }, - { 137, 161, 117, 220, 61, 51, 0 }, - { 24, 70, 219, 129, 77, 247, 1 }, - { 102, 94, 29, 215, 66, 200, 1 }, }, - { { 254, 214, 40, 241, 92, 60, 1 }, - { 243, 51, 196, 220, 120, 233, 1 }, - { 158, 29, 71, 138, 53, 191, 1 }, - { 203, 143, 29, 145, 230, 103, 1 }, }, - { { 20, 199, 225, 220, 124, 3, 1 }, - { 91, 20, 205, 135, 77, 139, 0 }, - { 224, 31, 29, 195, 241, 148, 0 }, - { 104, 217, 112, 217, 148, 109, 0 }, }, - { { 34, 236, 79, 160, 255, 225, 0 }, - { 236, 188, 51, 173, 74, 133, 1 }, - { 67, 255, 130, 249, 27, 162, 0 }, - { 208, 169, 90, 230, 30, 155, 1 }, }, - { { 164, 134, 72, 34, 231, 234, 0 }, - { 109, 56, 57, 40, 232, 40, 1 }, - { 43, 243, 162, 9, 48, 146, 1 }, - { 138, 11, 138, 78, 14, 91, 0 }, }, - { { 179, 172, 156, 128, 134, 12, 1 }, - { 226, 192, 38, 186, 104, 52, 0 }, - { 152, 48, 128, 156, 154, 230, 1 }, - { 22, 11, 46, 178, 1, 163, 1 }, }, - { { 235, 197, 247, 106, 12, 68, 0 }, - { 196, 98, 193, 31, 222, 182, 0 }, - { 17, 24, 43, 119, 209, 235, 1 }, - { 54, 189, 252, 65, 163, 17, 1 }, }, - { { 0, 36, 28, 209, 199, 244, 0 }, - { 116, 88, 114, 240, 64, 68, 1 }, - { 23, 241, 197, 156, 18, 0, 0 }, - { 145, 1, 7, 167, 13, 23, 0 }, }, - { { 0, 65, 85, 60, 228, 236, 0 }, - { 28, 120, 33, 21, 101, 6, 1 }, - { 27, 147, 158, 85, 65, 0, 0 }, - { 176, 83, 84, 66, 15, 28, 0 }, }, - { { 2, 119, 133, 135, 91, 48, 1 }, - { 194, 17, 18, 231, 133, 205, 1 }, - { 134, 109, 112, 208, 247, 32, 0 }, - { 217, 208, 243, 164, 68, 33, 1 }, }, - { { 1, 218, 232, 155, 2, 227, 0 }, - { 52, 141, 137, 230, 136, 26, 1 }, - { 99, 160, 108, 139, 173, 192, 0 }, - { 172, 8, 179, 200, 216, 150, 0 }, }, - { { 4, 40, 5, 26, 111, 159, 0 }, - { 25, 156, 26, 49, 224, 198, 0 }, - { 124, 251, 44, 80, 10, 16, 0 }, - { 49, 131, 198, 44, 28, 204, 0 }, }, - { { 7, 145, 0, 75, 83, 171, 0 }, - { 129, 29, 88, 96, 172, 19, 1 }, - { 106, 229, 105, 0, 68, 240, 0 }, - { 228, 26, 131, 13, 92, 64, 1 }, }, - { { 10, 89, 153, 210, 227, 215, 1 }, - { 190, 223, 120, 183, 132, 64, 0 }, - { 245, 227, 165, 204, 205, 40, 0 }, - { 1, 16, 246, 143, 125, 190, 1 }, }, - { { 9, 178, 243, 249, 248, 177, 1 }, - { 26, 127, 227, 195, 10, 219, 1 }, - { 198, 143, 207, 231, 166, 200, 0 }, - { 237, 168, 97, 227, 255, 44, 0 }, }, - { { 13, 49, 205, 127, 92, 218, 1 }, - { 55, 59, 75, 67, 229, 215, 0 }, - { 173, 157, 127, 89, 198, 88, 0 }, - { 117, 211, 225, 105, 110, 118, 0 }, }, - { { 14, 110, 159, 79, 116, 37, 1 }, - { 235, 214, 66, 87, 195, 15, 1 }, - { 210, 23, 121, 124, 187, 56, 0 }, - { 248, 97, 245, 33, 53, 235, 1 }, }, - { { 23, 114, 128, 242, 245, 110, 0 }, - { 157, 49, 126, 150, 224, 25, 1 }, - { 59, 87, 167, 128, 167, 116, 0 }, - { 204, 3, 180, 191, 70, 92, 1 }, }, - { { 22, 255, 97, 63, 186, 238, 0 }, - { 221, 169, 175, 117, 173, 139, 1 }, - { 59, 174, 254, 67, 127, 180, 0 }, - { 232, 218, 215, 122, 202, 221, 1 }, }, - { { 25, 50, 221, 83, 147, 173, 1 }, - { 50, 79, 119, 115, 160, 29, 1 }, - { 218, 228, 229, 93, 166, 76, 0 }, - { 220, 2, 231, 119, 121, 38, 0 }, }, - { { 30, 55, 90, 161, 138, 28, 1 }, - { 227, 99, 39, 240, 38, 200, 0 }, - { 156, 40, 194, 173, 118, 60, 0 }, - { 9, 178, 7, 242, 99, 99, 1 }, }, - { { 30, 243, 155, 67, 46, 40, 0 }, - { 169, 67, 70, 103, 238, 136, 1 }, - { 10, 58, 97, 108, 231, 188, 0 }, - { 136, 187, 243, 49, 97, 74, 1 }, }, - { { 35, 152, 218, 66, 102, 77, 0 }, - { 172, 213, 65, 58, 234, 16, 0 }, - { 89, 51, 33, 45, 140, 226, 0 }, - { 4, 43, 174, 65, 85, 154, 1 }, }, - { { 35, 183, 98, 39, 73, 166, 1 }, - { 194, 57, 155, 88, 143, 152, 1 }, - { 178, 201, 114, 35, 118, 226, 0 }, - { 140, 248, 141, 108, 206, 33, 1 }, }, - { { 39, 232, 89, 247, 30, 7, 0 }, - { 177, 228, 75, 253, 201, 145, 0 }, - { 112, 60, 119, 205, 11, 242, 0 }, - { 68, 201, 223, 233, 19, 198, 1 }, }, - { { 41, 207, 36, 233, 56, 245, 0 }, - { 76, 174, 192, 220, 12, 223, 1 }, - { 87, 142, 75, 146, 121, 202, 0 }, - { 253, 152, 29, 129, 186, 153, 0 }, }, - { { 43, 202, 225, 232, 241, 71, 1 }, - { 142, 182, 249, 159, 8, 27, 0 }, - { 241, 71, 139, 195, 169, 234, 0 }, - { 108, 8, 124, 207, 182, 184, 1 }, }, - { { 46, 52, 167, 60, 136, 145, 0 }, - { 209, 47, 162, 11, 3, 198, 0 }, - { 68, 136, 158, 114, 150, 58, 0 }, - { 49, 224, 104, 34, 250, 69, 1 }, }, - { { 44, 130, 54, 130, 20, 241, 1 }, - { 7, 78, 128, 136, 202, 77, 1 }, - { 199, 148, 32, 182, 32, 154, 0 }, - { 217, 41, 136, 128, 185, 112, 0 }, }, - { { 52, 119, 39, 34, 124, 128, 0 }, - { 73, 57, 134, 13, 198, 141, 0 }, - { 0, 159, 34, 114, 119, 22, 0 }, - { 88, 177, 216, 48, 206, 73, 0 }, }, - { { 62, 106, 78, 239, 207, 36, 0 }, - { 161, 178, 119, 252, 195, 142, 1 }, - { 18, 121, 251, 185, 43, 62, 0 }, - { 184, 225, 159, 247, 38, 194, 1 }, }, - { { 61, 189, 0, 186, 135, 236, 0 }, - { 85, 171, 54, 184, 236, 18, 1 }, - { 27, 240, 174, 128, 94, 222, 0 }, - { 164, 27, 142, 182, 106, 213, 0 }, }, - { { 65, 89, 70, 17, 248, 154, 0 }, - { 24, 153, 41, 68, 54, 213, 0 }, - { 44, 143, 196, 49, 77, 65, 0 }, - { 85, 182, 17, 74, 76, 140, 0 }, }, - { { 66, 160, 192, 80, 251, 19, 1 }, - { 154, 20, 123, 34, 24, 193, 0 }, - { 228, 111, 133, 1, 130, 161, 0 }, - { 65, 140, 34, 111, 20, 44, 1 }, }, - { { 66, 215, 59, 80, 17, 218, 1 }, - { 246, 73, 216, 5, 62, 73, 0 }, - { 173, 196, 5, 110, 117, 161, 0 }, - { 73, 62, 80, 13, 201, 55, 1 }, }, - { { 71, 21, 159, 236, 40, 89, 1 }, - { 239, 101, 64, 131, 55, 214, 0 }, - { 205, 10, 27, 252, 212, 113, 0 }, - { 53, 246, 96, 129, 83, 123, 1 }, }, - { { 72, 72, 74, 156, 121, 141, 1 }, - { 58, 158, 17, 148, 51, 131, 0 }, - { 216, 207, 28, 169, 9, 9, 0 }, - { 96, 230, 20, 196, 60, 174, 0 }, }, - { { 75, 141, 239, 56, 120, 19, 0 }, - { 248, 182, 137, 3, 30, 215, 0 }, - { 100, 15, 14, 123, 216, 233, 0 }, - { 117, 188, 96, 72, 182, 143, 1 }, }, - { { 72, 197, 79, 119, 42, 255, 1 }, - { 126, 46, 73, 117, 191, 196, 1 }, - { 255, 170, 119, 121, 81, 137, 0 }, - { 145, 254, 215, 73, 58, 63, 0 }, }, - { { 73, 205, 149, 222, 250, 38, 0 }, - { 88, 210, 104, 183, 157, 151, 1 }, - { 50, 47, 189, 212, 217, 201, 0 }, - { 244, 220, 246, 139, 37, 141, 0 }, }, - { { 74, 248, 19, 154, 40, 250, 0 }, - { 156, 203, 10, 133, 186, 194, 1 }, - { 47, 138, 44, 228, 15, 169, 0 }, - { 161, 174, 208, 168, 105, 156, 1 }, }, - { { 76, 39, 45, 47, 65, 165, 0 }, - { 97, 62, 146, 81, 149, 14, 1 }, - { 82, 193, 122, 90, 114, 25, 0 }, - { 184, 84, 197, 36, 190, 67, 0 }, }, - { { 76, 63, 202, 163, 60, 106, 1 }, - { 111, 163, 11, 194, 246, 137, 1 }, - { 171, 30, 98, 169, 254, 25, 0 }, - { 200, 183, 161, 232, 98, 251, 0 }, }, - { { 76, 105, 150, 233, 240, 91, 0 }, - { 13, 246, 106, 198, 54, 71, 0 }, - { 109, 7, 203, 180, 203, 25, 0 }, - { 113, 54, 49, 171, 55, 216, 0 }, }, - { { 83, 59, 100, 210, 198, 162, 0 }, - { 144, 153, 239, 160, 212, 28, 1 }, - { 34, 177, 165, 147, 110, 101, 0 }, - { 156, 21, 130, 251, 204, 132, 1 }, }, - { { 81, 70, 77, 104, 180, 53, 1 }, - { 106, 36, 101, 21, 80, 95, 1 }, - { 214, 22, 139, 89, 49, 69, 0 }, - { 253, 5, 84, 83, 18, 43, 0 }, }, - { { 81, 86, 86, 98, 113, 201, 1 }, - { 78, 125, 85, 4, 178, 29, 0 }, - { 201, 199, 35, 53, 53, 69, 0 }, - { 92, 38, 144, 85, 95, 57, 0 }, }, - { { 80, 115, 53, 140, 113, 223, 1 }, - { 14, 93, 158, 149, 53, 79, 0 }, - { 253, 199, 24, 214, 103, 5, 0 }, - { 121, 86, 84, 188, 221, 56, 0 }, }, - { { 84, 74, 231, 46, 2, 158, 1 }, - { 3, 168, 141, 55, 179, 78, 0 }, - { 188, 160, 58, 115, 169, 21, 0 }, - { 57, 102, 246, 88, 138, 224, 0 }, }, - { { 84, 219, 22, 238, 94, 71, 0 }, - { 5, 245, 76, 180, 223, 143, 0 }, - { 113, 61, 59, 180, 109, 149, 0 }, - { 120, 253, 150, 153, 87, 208, 0 }, }, - { { 86, 240, 240, 30, 140, 100, 1 }, - { 151, 65, 167, 22, 217, 130, 1 }, - { 147, 24, 188, 7, 135, 181, 0 }, - { 160, 205, 180, 114, 193, 116, 1 }, }, - { { 91, 106, 3, 196, 23, 123, 1 }, - { 134, 134, 94, 165, 115, 89, 1 }, - { 239, 116, 17, 224, 43, 109, 0 }, - { 205, 103, 82, 189, 48, 176, 1 }, }, - { { 91, 249, 239, 82, 25, 108, 1 }, - { 182, 131, 215, 23, 190, 149, 1 }, - { 155, 76, 37, 123, 207, 237, 0 }, - { 212, 190, 244, 117, 224, 182, 1 }, }, - { { 92, 116, 249, 2, 251, 190, 0 }, - { 105, 91, 191, 55, 176, 193, 1 }, - { 62, 239, 160, 79, 151, 29, 0 }, - { 193, 134, 246, 126, 237, 75, 0 }, }, - { { 94, 157, 11, 47, 79, 122, 1 }, - { 231, 179, 28, 97, 255, 194, 1 }, - { 175, 121, 122, 104, 92, 189, 0 }, - { 161, 255, 195, 28, 102, 243, 1 }, }, - { { 99, 67, 213, 185, 201, 130, 0 }, - { 144, 120, 57, 207, 20, 158, 0 }, - { 32, 201, 206, 213, 225, 99, 0 }, - { 60, 148, 121, 206, 15, 4, 1 }, }, - { { 96, 93, 164, 245, 22, 155, 1 }, - { 82, 173, 200, 238, 117, 69, 0 }, - { 236, 180, 87, 146, 221, 3, 0 }, - { 81, 87, 59, 137, 218, 165, 0 }, }, - { { 100, 136, 47, 135, 72, 47, 0 }, - { 33, 148, 136, 217, 187, 132, 1 }, - { 122, 9, 112, 250, 8, 147, 0 }, - { 144, 238, 205, 136, 148, 194, 0 }, }, - { { 104, 106, 145, 148, 102, 64, 0 }, - { 28, 210, 2, 175, 81, 8, 0 }, - { 1, 51, 20, 196, 171, 11, 0 }, - { 8, 69, 122, 160, 37, 156, 0 }, }, - { { 108, 55, 209, 238, 204, 101, 1 }, - { 71, 119, 99, 155, 213, 138, 1 }, - { 211, 25, 187, 197, 246, 27, 0 }, - { 168, 213, 236, 227, 119, 113, 0 }, }, - { { 109, 89, 245, 135, 43, 42, 0 }, - { 9, 195, 153, 239, 181, 148, 1 }, - { 42, 106, 112, 215, 205, 91, 0 }, - { 148, 214, 251, 204, 225, 200, 0 }, }, - { { 115, 31, 7, 158, 40, 12, 0 }, - { 216, 129, 4, 153, 183, 158, 0 }, - { 24, 10, 60, 240, 124, 103, 0 }, - { 60, 246, 204, 144, 64, 141, 1 }, }, - { { 114, 38, 22, 146, 200, 199, 1 }, - { 214, 92, 46, 152, 146, 140, 0 }, - { 241, 137, 164, 180, 50, 39, 0 }, - { 24, 164, 140, 186, 29, 53, 1 }, }, - { { 115, 131, 239, 207, 199, 93, 1 }, - { 166, 20, 245, 251, 255, 94, 0 }, - { 221, 113, 249, 251, 224, 231, 0 }, - { 61, 127, 239, 215, 148, 50, 1 }, }, - { { 113, 246, 244, 181, 170, 13, 0 }, - { 88, 101, 167, 254, 57, 156, 0 }, - { 88, 42, 214, 151, 183, 199, 0 }, - { 28, 206, 63, 242, 211, 13, 0 }, }, - { { 118, 16, 2, 241, 170, 59, 0 }, - { 153, 37, 108, 232, 50, 192, 1 }, - { 110, 42, 199, 160, 4, 55, 0 }, - { 129, 166, 11, 155, 82, 76, 1 }, }, - { { 118, 84, 255, 84, 38, 33, 0 }, - { 249, 69, 197, 47, 83, 4, 1 }, - { 66, 50, 21, 127, 149, 55, 0 }, - { 144, 101, 122, 81, 209, 79, 1 }, }, - { { 122, 227, 200, 30, 38, 54, 0 }, - { 184, 2, 15, 62, 221, 74, 1 }, - { 54, 50, 60, 9, 227, 175, 0 }, - { 169, 93, 190, 120, 32, 14, 1 }, }, - { { 126, 32, 91, 27, 109, 102, 0 }, - { 189, 82, 31, 89, 210, 130, 1 }, - { 51, 91, 108, 109, 2, 63, 0 }, - { 160, 165, 205, 124, 37, 94, 1 }, }, - { { 124, 113, 102, 127, 179, 75, 1 }, - { 31, 39, 255, 108, 183, 7, 0 }, - { 233, 102, 255, 51, 71, 31, 0 }, - { 112, 118, 155, 127, 242, 124, 0 }, }, - { { 126, 136, 249, 129, 71, 26, 0 }, - { 161, 210, 157, 235, 120, 64, 0 }, - { 44, 113, 64, 207, 136, 191, 0 }, - { 1, 15, 107, 220, 165, 194, 1 }, }, - { { 127, 189, 182, 243, 152, 70, 0 }, - { 213, 227, 238, 218, 158, 149, 0 }, - { 49, 12, 231, 182, 222, 255, 0 }, - { 84, 188, 173, 187, 227, 213, 1 }, }, - { { 131, 186, 63, 66, 156, 103, 0 }, - { 164, 197, 234, 17, 202, 189, 1 }, - { 115, 28, 161, 126, 46, 224, 1 }, - { 222, 169, 196, 43, 209, 146, 1 }, }, - { { 131, 179, 163, 188, 159, 85, 1 }, - { 150, 37, 178, 179, 79, 251, 0 }, - { 213, 124, 158, 226, 230, 224, 1 }, - { 111, 249, 102, 166, 210, 52, 1 }, }, - { { 134, 184, 56, 238, 195, 91, 1 }, - { 167, 245, 250, 160, 169, 98, 0 }, - { 237, 97, 187, 142, 14, 176, 1 }, - { 35, 74, 130, 175, 215, 242, 1 }, }, - { { 138, 251, 116, 46, 23, 43, 0 }, - { 128, 231, 155, 36, 237, 47, 1 }, - { 106, 116, 58, 23, 111, 168, 1 }, - { 250, 91, 146, 108, 243, 128, 1 }, }, - { { 142, 5, 134, 252, 131, 42, 0 }, - { 209, 34, 120, 162, 39, 38, 1 }, - { 42, 96, 159, 176, 208, 56, 1 }, - { 178, 114, 34, 143, 34, 69, 1 }, }, - { { 143, 72, 60, 30, 32, 231, 0 }, - { 189, 206, 136, 20, 129, 54, 1 }, - { 115, 130, 60, 30, 9, 120, 1 }, - { 182, 64, 148, 8, 185, 222, 1 }, }, - { { 142, 83, 181, 252, 28, 224, 0 }, - { 149, 107, 192, 135, 69, 175, 1 }, - { 3, 156, 31, 214, 229, 56, 1 }, - { 250, 209, 112, 129, 235, 84, 1 }, }, - { { 141, 157, 35, 130, 73, 96, 0 }, - { 69, 147, 144, 129, 142, 176, 1 }, - { 3, 73, 32, 226, 92, 216, 1 }, - { 134, 184, 192, 132, 228, 209, 0 }, }, - { { 141, 185, 144, 117, 146, 156, 1 }, - { 19, 235, 98, 114, 45, 113, 0 }, - { 156, 164, 215, 4, 206, 216, 1 }, - { 71, 90, 39, 35, 107, 228, 0 }, }, - { { 144, 19, 230, 225, 162, 176, 0 }, - { 8, 41, 229, 226, 6, 108, 1 }, - { 6, 162, 195, 179, 228, 4, 1 }, - { 155, 48, 35, 211, 202, 8, 0 }, }, - { { 147, 155, 55, 123, 26, 144, 1 }, - { 146, 233, 196, 97, 142, 255, 0 }, - { 132, 172, 111, 118, 108, 228, 1 }, - { 127, 184, 195, 17, 203, 164, 1 }, }, - { { 151, 14, 49, 237, 242, 168, 1 }, - { 203, 248, 228, 225, 33, 59, 1 }, - { 138, 167, 219, 198, 56, 116, 1 }, - { 238, 66, 67, 147, 143, 233, 1 }, }, - { { 151, 52, 94, 59, 153, 170, 0 }, - { 241, 105, 63, 64, 162, 183, 1 }, - { 42, 204, 238, 61, 22, 116, 1 }, - { 246, 162, 129, 126, 75, 71, 1 }, }, - { { 149, 122, 253, 44, 125, 85, 0 }, - { 45, 245, 151, 23, 65, 255, 0 }, - { 85, 95, 26, 95, 175, 84, 1 }, - { 127, 193, 116, 116, 215, 218, 0 }, }, - { { 151, 221, 14, 6, 20, 80, 1 }, - { 231, 129, 4, 4, 207, 117, 0 }, - { 133, 20, 48, 56, 93, 244, 1 }, - { 87, 121, 144, 16, 64, 243, 1 }, }, - { { 154, 154, 4, 227, 189, 68, 1 }, - { 142, 163, 116, 208, 200, 173, 0 }, - { 145, 94, 227, 144, 44, 172, 1 }, - { 90, 137, 133, 151, 98, 184, 1 }, }, - { { 155, 190, 173, 173, 13, 248, 1 }, - { 230, 171, 150, 195, 105, 254, 1 }, - { 143, 216, 90, 218, 190, 236, 1 }, - { 191, 203, 97, 180, 234, 179, 1 }, }, - { { 157, 103, 144, 38, 24, 93, 0 }, - { 69, 102, 6, 22, 165, 249, 0 }, - { 93, 12, 50, 4, 243, 92, 1 }, - { 79, 210, 180, 48, 51, 81, 0 }, }, - { { 159, 129, 201, 246, 175, 127, 0 }, - { 189, 38, 125, 179, 237, 240, 1 }, - { 127, 122, 183, 201, 192, 252, 1 }, - { 135, 219, 230, 223, 50, 94, 1 }, }, - { { 159, 247, 196, 109, 149, 97, 1 }, - { 199, 39, 119, 70, 77, 63, 1 }, - { 195, 84, 219, 17, 247, 252, 1 }, - { 254, 89, 49, 119, 114, 113, 1 }, }, - { { 167, 3, 62, 38, 186, 122, 0 }, - { 173, 96, 168, 40, 167, 253, 1 }, - { 47, 46, 178, 62, 96, 114, 1 }, - { 223, 242, 138, 10, 131, 90, 1 }, }, - { { 166, 79, 109, 144, 30, 111, 0 }, - { 245, 132, 137, 189, 100, 173, 1 }, - { 123, 60, 4, 219, 121, 50, 1 }, - { 218, 147, 94, 200, 144, 215, 1 }, }, - { { 166, 84, 136, 155, 103, 3, 0 }, - { 249, 21, 24, 238, 192, 34, 0 }, - { 96, 115, 108, 136, 149, 50, 1 }, - { 34, 1, 187, 140, 84, 79, 1 }, }, - { { 164, 250, 171, 52, 23, 138, 0 }, - { 49, 169, 154, 47, 107, 41, 0 }, - { 40, 244, 22, 106, 175, 146, 1 }, - { 74, 107, 122, 44, 202, 198, 0 }, }, - { { 169, 35, 165, 79, 219, 255, 1 }, - { 6, 30, 250, 123, 165, 255, 1 }, - { 255, 237, 249, 82, 226, 74, 1 }, - { 255, 210, 239, 47, 188, 48, 0 }, }, - { { 175, 22, 89, 55, 116, 14, 1 }, - { 251, 115, 9, 89, 225, 57, 0 }, - { 184, 23, 118, 77, 52, 122, 1 }, - { 78, 67, 205, 72, 103, 111, 1 }, }, - { { 174, 60, 2, 96, 17, 226, 1 }, - { 199, 171, 90, 8, 2, 33, 1 }, - { 163, 196, 3, 32, 30, 58, 1 }, - { 194, 32, 8, 45, 106, 241, 1 }, }, - { { 174, 166, 70, 29, 70, 134, 0 }, - { 209, 26, 11, 120, 75, 46, 0 }, - { 48, 177, 92, 49, 50, 186, 1 }, - { 58, 105, 15, 104, 44, 69, 1 }, }, - { { 174, 169, 188, 206, 254, 64, 0 }, - { 173, 210, 226, 170, 205, 167, 0 }, - { 1, 63, 185, 158, 202, 186, 1 }, - { 114, 217, 170, 163, 165, 218, 1 }, }, - { { 177, 170, 233, 41, 210, 190, 0 }, - { 32, 184, 175, 123, 40, 123, 1 }, - { 62, 165, 202, 75, 170, 198, 1 }, - { 239, 10, 111, 122, 142, 130, 0 }, }, - { { 179, 236, 199, 247, 61, 40, 0 }, - { 216, 160, 87, 207, 235, 181, 1 }, - { 10, 94, 119, 241, 155, 230, 1 }, - { 214, 235, 249, 245, 2, 141, 1 }, }, - { { 179, 242, 249, 209, 5, 96, 1 }, - { 182, 65, 215, 207, 72, 56, 1 }, - { 131, 80, 69, 207, 167, 230, 1 }, - { 142, 9, 121, 245, 193, 54, 1 }, }, - { { 183, 83, 178, 95, 132, 147, 0 }, - { 145, 77, 236, 78, 199, 122, 0 }, - { 100, 144, 253, 38, 229, 118, 1 }, - { 47, 113, 185, 27, 217, 68, 1 }, }, - { { 180, 99, 115, 84, 106, 222, 0 }, - { 29, 88, 207, 61, 39, 232, 0 }, - { 61, 171, 21, 103, 99, 22, 1 }, - { 11, 242, 94, 121, 141, 92, 0 }, }, - { { 182, 97, 89, 33, 192, 136, 0 }, - { 161, 120, 39, 77, 36, 32, 0 }, - { 8, 129, 194, 77, 67, 54, 1 }, - { 2, 18, 89, 114, 15, 66, 1 }, }, - { { 186, 184, 208, 95, 68, 186, 1 }, - { 146, 219, 79, 74, 233, 98, 1 }, - { 174, 145, 125, 5, 142, 174, 1 }, - { 163, 75, 169, 121, 109, 164, 1 }, }, - { { 186, 192, 40, 124, 16, 3, 1 }, - { 178, 38, 204, 12, 9, 35, 0 }, - { 224, 4, 31, 10, 1, 174, 1 }, - { 98, 72, 24, 25, 178, 38, 1 }, }, - { { 188, 9, 19, 77, 135, 103, 0 }, - { 5, 198, 124, 121, 71, 34, 1 }, - { 115, 112, 217, 100, 72, 30, 1 }, - { 162, 113, 79, 31, 49, 208, 0 }, }, - { { 190, 60, 15, 68, 249, 12, 0 }, - { 233, 147, 118, 25, 35, 165, 0 }, - { 24, 79, 145, 120, 30, 62, 1 }, - { 82, 226, 76, 55, 100, 203, 1 }, }, - { { 193, 39, 249, 24, 172, 4, 0 }, - { 120, 64, 163, 19, 84, 186, 0 }, - { 16, 26, 140, 79, 242, 65, 1 }, - { 46, 149, 100, 98, 129, 15, 0 }, }, - { { 194, 54, 103, 175, 248, 99, 1 }, - { 206, 53, 171, 193, 147, 175, 1 }, - { 227, 15, 250, 243, 54, 33, 1 }, - { 250, 228, 193, 234, 214, 57, 1 }, }, - { { 194, 202, 232, 228, 90, 50, 0 }, - { 160, 176, 201, 166, 25, 233, 1 }, - { 38, 45, 19, 139, 169, 161, 1 }, - { 203, 204, 50, 201, 134, 130, 1 }, }, - { { 198, 18, 22, 32, 228, 28, 0 }, - { 137, 113, 32, 16, 114, 108, 0 }, - { 28, 19, 130, 52, 36, 49, 1 }, - { 27, 39, 4, 2, 71, 72, 1 }, }, - { { 199, 229, 93, 208, 65, 150, 0 }, - { 241, 88, 91, 149, 28, 116, 0 }, - { 52, 193, 5, 221, 83, 241, 1 }, - { 23, 28, 84, 237, 13, 71, 1 }, }, - { { 202, 89, 204, 102, 248, 236, 0 }, - { 172, 187, 97, 22, 181, 165, 1 }, - { 27, 143, 179, 25, 205, 41, 1 }, - { 210, 214, 180, 67, 110, 154, 1 }, }, - { { 200, 251, 13, 58, 124, 119, 1 }, - { 62, 183, 10, 21, 220, 239, 1 }, - { 247, 31, 46, 88, 111, 137, 1 }, - { 251, 157, 212, 40, 118, 190, 0 }, }, - { { 204, 35, 191, 4, 244, 230, 0 }, - { 45, 90, 170, 19, 87, 45, 1 }, - { 51, 151, 144, 126, 226, 25, 1 }, - { 218, 117, 100, 42, 173, 90, 0 }, }, - { { 204, 185, 29, 222, 17, 77, 0 }, - { 53, 199, 82, 145, 189, 39, 0 }, - { 89, 68, 61, 220, 78, 153, 1 }, - { 114, 94, 196, 165, 113, 214, 0 }, }, - { { 214, 71, 53, 203, 221, 81, 1 }, - { 199, 84, 244, 197, 212, 239, 0 }, - { 197, 93, 233, 214, 113, 53, 1 }, - { 123, 149, 209, 151, 149, 113, 1 }, }, - { { 212, 80, 13, 82, 140, 243, 1 }, - { 55, 13, 108, 5, 208, 228, 1 }, - { 231, 152, 165, 88, 5, 21, 1 }, - { 147, 133, 208, 27, 88, 118, 0 }, }, - { { 214, 126, 50, 1, 136, 117, 0 }, - { 197, 197, 166, 84, 18, 232, 1 }, - { 87, 8, 192, 38, 63, 53, 1 }, - { 139, 164, 21, 50, 209, 209, 1 }, }, - { { 214, 239, 103, 48, 199, 69, 1 }, - { 215, 180, 183, 53, 94, 44, 0 }, - { 209, 113, 134, 115, 123, 181, 1 }, - { 26, 61, 86, 118, 150, 245, 1 }, }, - { { 217, 10, 193, 255, 8, 72, 0 }, - { 20, 162, 69, 195, 177, 186, 0 }, - { 9, 8, 127, 193, 168, 77, 1 }, - { 46, 198, 225, 209, 34, 148, 0 }, }, - { { 218, 68, 144, 181, 192, 149, 0 }, - { 208, 126, 36, 214, 17, 96, 0 }, - { 84, 129, 214, 132, 145, 45, 1 }, - { 3, 68, 53, 146, 63, 5, 1 }, }, - { { 218, 142, 240, 138, 31, 78, 1 }, - { 198, 194, 157, 178, 248, 171, 0 }, - { 185, 124, 40, 135, 184, 173, 1 }, - { 106, 143, 166, 220, 161, 177, 1 }, }, - { { 226, 186, 72, 154, 71, 244, 1 }, - { 182, 153, 19, 184, 216, 106, 1 }, - { 151, 241, 44, 137, 46, 163, 1 }, - { 171, 13, 142, 228, 76, 182, 1 }, }, - { { 226, 251, 142, 197, 152, 25, 0 }, - { 160, 133, 98, 206, 63, 237, 0 }, - { 76, 12, 209, 184, 239, 163, 1 }, - { 91, 254, 57, 163, 80, 130, 1 }, }, - { { 229, 62, 6, 191, 131, 16, 1 }, - { 83, 161, 50, 232, 147, 126, 0 }, - { 132, 96, 254, 176, 62, 83, 1 }, - { 63, 100, 139, 166, 66, 229, 0 }, }, - { { 230, 135, 176, 105, 180, 112, 0 }, - { 205, 96, 224, 74, 92, 107, 1 }, - { 7, 22, 203, 6, 240, 179, 1 }, - { 235, 29, 41, 3, 131, 89, 1 }, }, - { { 229, 161, 35, 223, 231, 210, 0 }, - { 29, 24, 250, 233, 223, 114, 0 }, - { 37, 243, 253, 226, 66, 211, 1 }, - { 39, 125, 203, 175, 140, 92, 0 }, }, - { { 228, 231, 74, 54, 176, 3, 0 }, - { 121, 36, 43, 12, 159, 41, 0 }, - { 96, 6, 182, 41, 115, 147, 1 }, - { 74, 124, 152, 106, 18, 79, 0 }, }, - { { 235, 25, 74, 181, 219, 227, 0 }, - { 180, 191, 57, 232, 23, 177, 1 }, - { 99, 237, 214, 169, 76, 107, 1 }, - { 198, 244, 11, 206, 126, 150, 1 }, }, - { { 235, 104, 214, 214, 135, 188, 0 }, - { 144, 202, 115, 190, 243, 116, 1 }, - { 30, 240, 181, 181, 139, 107, 1 }, - { 151, 103, 190, 231, 41, 132, 1 }, }, - { { 239, 76, 157, 25, 92, 242, 0 }, - { 245, 218, 8, 79, 80, 247, 1 }, - { 39, 157, 76, 92, 153, 123, 1 }, - { 247, 133, 121, 8, 45, 215, 1 }, }, - { { 240, 140, 65, 178, 161, 79, 1 }, - { 94, 164, 61, 153, 184, 32, 0 }, - { 249, 66, 166, 193, 24, 135, 1 }, - { 2, 14, 204, 222, 18, 189, 0 }, }, - { { 245, 42, 227, 102, 145, 91, 0 }, - { 5, 164, 255, 11, 179, 121, 0 }, - { 109, 68, 179, 99, 170, 87, 1 }, - { 79, 102, 232, 127, 146, 208, 0 }, }, - { { 247, 127, 207, 238, 89, 167, 0 }, - { 225, 189, 95, 159, 151, 191, 1 }, - { 114, 205, 59, 249, 255, 119, 1 }, - { 254, 244, 252, 253, 94, 195, 1 }, }, - { { 248, 218, 47, 9, 127, 235, 0 }, - { 44, 159, 156, 109, 122, 175, 1 }, - { 107, 255, 72, 122, 45, 143, 1 }, - { 250, 175, 91, 28, 252, 154, 0 }, }, - { { 252, 25, 236, 171, 9, 228, 1 }, - { 39, 171, 149, 218, 148, 166, 1 }, - { 147, 200, 106, 155, 204, 31, 1 }, - { 178, 148, 173, 212, 234, 242, 0 }, }, - { { 147, 75, 97, 136, 48, 21, 0 }, - { 136, 132, 133, 149, 4, 123, 0 }, - { 84, 6, 8, 195, 105, 100, 1 }, - { 111, 16, 84, 208, 144, 136, 1 }, }, - { { 200, 173, 102, 37, 128, 21, 1 }, - { 66, 166, 163, 80, 31, 100, 0 }, - { 212, 0, 210, 51, 90, 137, 1 }, - { 19, 124, 5, 98, 178, 161, 0 }, }, - { { 28, 218, 21, 111, 160, 160, 1 }, - { 11, 235, 100, 69, 137, 14, 1 }, - { 130, 130, 251, 84, 45, 156, 0 }, - { 184, 72, 209, 19, 107, 232, 0 }, }, - { { 35, 147, 244, 147, 165, 30, 0 }, - { 152, 65, 185, 218, 236, 92, 0 }, - { 60, 82, 228, 151, 228, 226, 0 }, - { 29, 27, 173, 206, 193, 12, 1 }, }, - { { 47, 88, 146, 189, 228, 140, 1 }, - { 155, 251, 32, 222, 99, 18, 0 }, - { 152, 147, 222, 164, 141, 122, 0 }, - { 36, 99, 61, 130, 111, 236, 1 }, }, - { { 67, 161, 247, 150, 104, 223, 0 }, - { 156, 92, 139, 147, 191, 212, 0 }, - { 125, 139, 52, 247, 194, 225, 0 }, - { 21, 254, 228, 232, 157, 28, 1 }, }, - { { 73, 80, 111, 179, 116, 206, 1 }, - { 62, 59, 137, 213, 242, 21, 0 }, - { 185, 151, 102, 251, 5, 73, 0 }, - { 84, 39, 213, 200, 238, 62, 0 }, }, - { { 82, 96, 94, 14, 152, 127, 0 }, - { 164, 68, 47, 20, 179, 199, 1 }, - { 127, 12, 184, 61, 3, 37, 0 }, - { 241, 230, 148, 122, 17, 18, 1 }, }, - { { 82, 145, 24, 227, 252, 161, 0 }, - { 168, 125, 100, 192, 220, 129, 1 }, - { 66, 159, 227, 140, 68, 165, 0 }, - { 192, 157, 129, 147, 95, 10, 1 }, }, - { { 101, 223, 133, 82, 42, 63, 1 }, - { 91, 133, 72, 63, 188, 220, 1 }, - { 254, 42, 37, 80, 253, 211, 0 }, - { 157, 158, 254, 9, 80, 237, 0 }, }, - { { 113, 72, 12, 215, 43, 160, 0 }, - { 56, 136, 84, 236, 145, 148, 1 }, - { 2, 234, 117, 152, 9, 71, 0 }, - { 148, 196, 155, 149, 8, 142, 0 }, }, - { { 142, 6, 96, 125, 160, 37, 0 }, - { 217, 38, 225, 80, 1, 42, 1 }, - { 82, 2, 223, 3, 48, 56, 1 }, - { 170, 64, 5, 67, 178, 77, 1 }, }, - { { 150, 170, 33, 3, 84, 112, 1 }, - { 135, 144, 134, 65, 200, 105, 1 }, - { 135, 21, 96, 66, 42, 180, 1 }, - { 203, 9, 193, 48, 132, 240, 1 }, }, - { { 161, 37, 174, 137, 93, 100, 1 }, - { 102, 16, 146, 218, 70, 183, 1 }, - { 147, 93, 72, 186, 210, 66, 1 }, - { 246, 177, 45, 164, 132, 51, 0 }, }, - { { 188, 234, 86, 10, 46, 231, 1 }, - { 15, 206, 15, 60, 202, 174, 1 }, - { 243, 186, 40, 53, 43, 158, 1 }, - { 186, 169, 158, 120, 57, 248, 0 }, }, - { { 194, 210, 70, 164, 139, 68, 0 }, - { 132, 33, 49, 180, 27, 172, 0 }, - { 17, 104, 146, 177, 37, 161, 1 }, - { 26, 236, 22, 198, 66, 16, 1 }, }, - { { 218, 226, 149, 139, 69, 176, 0 }, - { 128, 90, 22, 199, 216, 110, 1 }, - { 6, 209, 104, 212, 163, 173, 1 }, - { 187, 13, 241, 180, 45, 0, 1 }, }, - { { 236, 63, 99, 213, 29, 216, 0 }, - { 85, 139, 211, 201, 119, 233, 0 }, - { 13, 220, 85, 227, 126, 27, 1 }, - { 75, 247, 73, 229, 232, 213, 0 }, }, - { { 255, 105, 45, 60, 107, 15, 0 }, - { 185, 182, 158, 61, 53, 182, 0 }, - { 120, 107, 30, 90, 75, 127, 1 }, - { 54, 214, 94, 60, 182, 206, 1 }, }, - { { 0, 28, 79, 167, 70, 146, 1 }, - { 98, 185, 9, 225, 195, 68, 0 }, - { 164, 177, 114, 249, 28, 0, 0 }, - { 17, 97, 195, 200, 78, 163, 0 }, }, - { { 2, 24, 244, 93, 202, 150, 0 }, - { 144, 217, 233, 114, 1, 198, 0 }, - { 52, 169, 221, 23, 140, 32, 0 }, - { 49, 192, 39, 75, 205, 132, 1 }, }, - { { 3, 209, 163, 72, 235, 88, 0 }, - { 140, 17, 240, 39, 46, 210, 0 }, - { 13, 107, 137, 98, 197, 224, 0 }, - { 37, 186, 114, 7, 196, 24, 1 }, }, - { { 4, 27, 131, 253, 41, 154, 1 }, - { 27, 169, 88, 195, 39, 202, 0 }, - { 172, 202, 95, 224, 236, 16, 0 }, - { 41, 242, 97, 141, 74, 236, 0 }, }, - { { 9, 141, 142, 139, 148, 215, 1 }, - { 102, 142, 40, 210, 206, 87, 0 }, - { 245, 148, 232, 184, 216, 200, 0 }, - { 117, 57, 165, 138, 56, 179, 0 }, }, - { { 8, 163, 222, 26, 89, 139, 0 }, - { 48, 94, 27, 2, 174, 143, 0 }, - { 104, 205, 44, 61, 226, 136, 0 }, - { 120, 186, 160, 108, 61, 6, 0 }, }, - { { 11, 188, 12, 8, 32, 24, 0 }, - { 232, 131, 2, 0, 40, 86, 0 }, - { 12, 2, 8, 24, 30, 232, 0 }, - { 53, 10, 0, 32, 96, 139, 1 }, }, - { { 8, 180, 118, 44, 124, 199, 0 }, - { 76, 127, 139, 16, 75, 135, 0 }, - { 113, 159, 26, 55, 22, 136, 0 }, - { 112, 233, 4, 104, 255, 25, 0 }, }, - { { 8, 252, 181, 200, 85, 47, 1 }, - { 66, 215, 218, 151, 104, 7, 1 }, - { 250, 85, 9, 214, 159, 136, 0 }, - { 240, 11, 116, 173, 245, 161, 0 }, }, - { { 14, 22, 54, 84, 254, 34, 0 }, - { 217, 83, 232, 32, 67, 141, 1 }, - { 34, 63, 149, 54, 52, 56, 0 }, - { 216, 225, 2, 11, 229, 77, 1 }, }, - { { 18, 44, 177, 159, 7, 34, 1 }, - { 210, 192, 158, 227, 193, 2, 1 }, - { 162, 112, 124, 198, 154, 36, 0 }, - { 160, 65, 227, 188, 129, 165, 1 }, }, - { { 18, 69, 156, 171, 22, 232, 0 }, - { 228, 104, 4, 230, 228, 7, 1 }, - { 11, 180, 106, 156, 209, 36, 0 }, - { 240, 19, 179, 144, 11, 19, 1 }, }, - { { 19, 147, 126, 245, 174, 67, 1 }, - { 190, 101, 237, 224, 79, 156, 0 }, - { 225, 58, 215, 191, 100, 228, 0 }, - { 28, 249, 3, 219, 211, 62, 1 }, }, - { { 22, 41, 244, 18, 48, 140, 0 }, - { 153, 200, 135, 18, 164, 5, 0 }, - { 24, 134, 36, 23, 202, 52, 0 }, - { 80, 18, 164, 112, 137, 204, 1 }, }, - { { 21, 124, 103, 88, 122, 66, 0 }, - { 93, 145, 207, 37, 2, 151, 0 }, - { 33, 47, 13, 115, 31, 84, 0 }, - { 116, 160, 82, 121, 196, 221, 0 }, }, - { { 21, 204, 177, 89, 131, 214, 1 }, - { 87, 200, 252, 119, 8, 82, 0 }, - { 181, 224, 205, 70, 153, 212, 0 }, - { 37, 8, 119, 31, 137, 245, 0 }, }, - { { 23, 227, 47, 161, 221, 185, 0 }, - { 161, 60, 182, 197, 110, 221, 1 }, - { 78, 221, 194, 250, 99, 244, 0 }, - { 221, 187, 81, 182, 158, 66, 1 }, }, - { { 27, 70, 90, 142, 239, 10, 1 }, - { 234, 82, 61, 164, 227, 154, 0 }, - { 168, 123, 184, 173, 49, 108, 0 }, - { 44, 227, 146, 222, 37, 43, 1 }, }, - { { 27, 105, 78, 73, 179, 188, 1 }, - { 170, 138, 119, 116, 38, 87, 1 }, - { 158, 230, 201, 57, 75, 108, 0 }, - { 245, 50, 23, 119, 40, 170, 1 }, }, - { { 27, 196, 203, 219, 144, 138, 1 }, - { 242, 10, 109, 199, 170, 19, 0 }, - { 168, 132, 237, 233, 145, 236, 0 }, - { 100, 42, 241, 219, 40, 39, 1 }, }, - { { 31, 44, 109, 141, 119, 17, 1 }, - { 235, 150, 151, 225, 65, 87, 0 }, - { 196, 119, 88, 219, 26, 124, 0 }, - { 117, 65, 67, 244, 180, 235, 1 }, }, - { { 31, 87, 63, 15, 8, 234, 1 }, - { 231, 75, 140, 69, 167, 158, 1 }, - { 171, 136, 120, 126, 117, 124, 0 }, - { 188, 242, 209, 24, 233, 115, 1 }, }, - { { 28, 109, 100, 176, 216, 126, 1 }, - { 87, 178, 175, 148, 36, 197, 1 }, - { 191, 13, 134, 147, 91, 28, 0 }, - { 209, 146, 20, 250, 166, 245, 0 }, }, - { { 31, 214, 51, 233, 39, 78, 1 }, - { 207, 99, 220, 245, 106, 26, 0 }, - { 185, 114, 75, 230, 53, 252, 0 }, - { 44, 43, 87, 157, 227, 121, 1 }, }, - { { 31, 237, 94, 0, 105, 171, 1 }, - { 235, 222, 31, 4, 46, 148, 1 }, - { 234, 203, 0, 61, 91, 252, 0 }, - { 148, 186, 16, 124, 61, 235, 1 }, }, - { { 32, 25, 133, 43, 191, 166, 0 }, - { 8, 169, 56, 123, 196, 135, 1 }, - { 50, 254, 234, 80, 204, 2, 0 }, - { 240, 145, 239, 14, 74, 136, 0 }, }, - { { 34, 61, 51, 120, 7, 207, 0 }, - { 212, 237, 218, 57, 102, 2, 0 }, - { 121, 240, 15, 102, 94, 34, 0 }, - { 32, 51, 78, 45, 219, 149, 1 }, }, - { { 32, 99, 33, 28, 107, 49, 0 }, - { 24, 20, 146, 45, 5, 202, 1 }, - { 70, 107, 28, 66, 99, 2, 0 }, - { 169, 208, 90, 36, 148, 12, 0 }, }, - { { 34, 110, 87, 72, 118, 216, 1 }, - { 206, 216, 67, 45, 98, 79, 0 }, - { 141, 183, 9, 117, 59, 34, 0 }, - { 121, 35, 90, 97, 13, 185, 1 }, }, - { { 32, 116, 241, 178, 248, 194, 1 }, - { 94, 121, 171, 143, 128, 129, 0 }, - { 161, 143, 166, 199, 151, 2, 0 }, - { 64, 128, 248, 234, 207, 61, 0 }, }, - { { 33, 172, 61, 93, 237, 110, 0 }, - { 124, 208, 250, 89, 105, 150, 1 }, - { 59, 91, 221, 94, 26, 194, 0 }, - { 180, 203, 77, 47, 133, 159, 0 }, }, - { { 33, 195, 23, 29, 144, 62, 1 }, - { 18, 64, 40, 93, 47, 95, 1 }, - { 190, 4, 220, 116, 97, 194, 0 }, - { 253, 122, 93, 10, 1, 36, 0 }, }, - { { 39, 47, 226, 196, 67, 84, 0 }, - { 197, 144, 211, 186, 7, 88, 0 }, - { 21, 97, 17, 163, 250, 114, 0 }, - { 13, 112, 46, 229, 132, 209, 1 }, }, - { { 39, 159, 50, 224, 232, 133, 0 }, - { 201, 253, 224, 152, 14, 152, 0 }, - { 80, 139, 131, 166, 124, 242, 0 }, - { 12, 184, 12, 131, 223, 201, 1 }, }, - { { 38, 190, 33, 114, 154, 9, 1 }, - { 211, 165, 226, 41, 168, 137, 0 }, - { 200, 44, 167, 66, 62, 178, 0 }, - { 72, 138, 202, 35, 210, 229, 1 }, }, - { { 42, 3, 57, 247, 158, 125, 1 }, - { 182, 102, 224, 249, 229, 201, 1 }, - { 223, 60, 247, 206, 96, 42, 0 }, - { 201, 211, 207, 131, 179, 54, 1 }, }, - { { 43, 24, 147, 239, 72, 235, 0 }, - { 132, 255, 72, 203, 163, 146, 1 }, - { 107, 137, 123, 228, 140, 106, 0 }, - { 164, 226, 233, 137, 127, 144, 1 }, }, - { { 41, 20, 232, 100, 42, 8, 0 }, - { 104, 35, 193, 42, 33, 144, 0 }, - { 8, 42, 19, 11, 148, 74, 0 }, - { 4, 194, 42, 65, 226, 11, 0 }, }, - { { 41, 104, 166, 84, 120, 185, 0 }, - { 24, 158, 194, 14, 35, 213, 1 }, - { 78, 143, 21, 50, 139, 74, 0 }, - { 213, 226, 56, 33, 188, 140, 0 }, }, - { { 42, 193, 129, 37, 248, 55, 0 }, - { 136, 54, 40, 95, 13, 193, 1 }, - { 118, 15, 210, 64, 193, 170, 0 }, - { 193, 216, 125, 10, 54, 8, 1 }, }, - { { 40, 212, 32, 190, 102, 221, 1 }, - { 94, 63, 128, 188, 233, 66, 0 }, - { 221, 179, 62, 130, 21, 138, 0 }, - { 33, 75, 158, 128, 254, 61, 0 }, }, - { { 41, 251, 7, 40, 147, 64, 0 }, - { 4, 163, 50, 45, 14, 31, 0 }, - { 1, 100, 138, 112, 111, 202, 0 }, - { 124, 56, 90, 38, 98, 144, 0 }, }, - { { 45, 79, 27, 209, 68, 171, 0 }, - { 113, 222, 72, 205, 102, 24, 1 }, - { 106, 145, 69, 236, 121, 90, 0 }, - { 140, 51, 89, 137, 61, 199, 0 }, }, - { { 44, 77, 58, 93, 125, 209, 0 }, - { 125, 222, 208, 76, 71, 195, 0 }, - { 69, 223, 93, 46, 89, 26, 0 }, - { 97, 241, 25, 5, 189, 223, 0 }, }, - { { 46, 201, 163, 115, 80, 78, 1 }, - { 151, 178, 200, 95, 174, 1, 0 }, - { 185, 5, 103, 98, 201, 186, 0 }, - { 64, 58, 253, 9, 166, 244, 1 }, }, - { { 47, 242, 157, 56, 166, 39, 0 }, - { 185, 103, 42, 63, 72, 30, 1 }, - { 114, 50, 142, 92, 167, 250, 0 }, - { 188, 9, 126, 42, 115, 78, 1 }, }, - { { 51, 14, 242, 29, 118, 78, 0 }, - { 220, 208, 141, 122, 99, 27, 0 }, - { 57, 55, 92, 39, 184, 102, 0 }, - { 108, 99, 47, 88, 133, 157, 1 }, }, - { { 51, 46, 161, 171, 153, 4, 0 }, - { 192, 160, 182, 219, 128, 155, 0 }, - { 16, 76, 234, 194, 186, 102, 0 }, - { 108, 128, 237, 182, 130, 129, 1 }, }, - { { 49, 41, 200, 243, 224, 220, 1 }, - { 62, 184, 103, 218, 164, 80, 0 }, - { 157, 131, 231, 137, 202, 70, 0 }, - { 5, 18, 173, 243, 14, 190, 0 }, }, - { { 53, 0, 254, 245, 196, 8, 0 }, - { 49, 112, 229, 202, 99, 20, 0 }, - { 8, 17, 215, 191, 128, 86, 0 }, - { 20, 99, 41, 211, 135, 70, 0 }, }, - { { 55, 36, 110, 62, 36, 63, 0 }, - { 249, 36, 143, 24, 227, 86, 1 }, - { 126, 18, 62, 59, 18, 118, 0 }, - { 181, 99, 140, 120, 146, 79, 1 }, }, - { { 53, 81, 121, 197, 146, 92, 1 }, - { 39, 65, 229, 253, 37, 81, 0 }, - { 157, 36, 209, 207, 69, 86, 0 }, - { 69, 82, 95, 211, 193, 114, 0 }, }, - { { 54, 184, 189, 91, 81, 60, 0 }, - { 177, 209, 214, 91, 168, 71, 1 }, - { 30, 69, 109, 94, 142, 182, 0 }, - { 241, 10, 237, 53, 197, 198, 1 }, }, - { { 54, 221, 188, 202, 44, 119, 0 }, - { 237, 197, 204, 158, 204, 198, 1 }, - { 119, 26, 41, 158, 221, 182, 0 }, - { 177, 153, 188, 153, 209, 219, 1 }, }, - { { 55, 221, 249, 250, 116, 140, 0 }, - { 249, 249, 197, 159, 236, 19, 0 }, - { 24, 151, 47, 207, 221, 246, 0 }, - { 100, 27, 252, 209, 207, 207, 1 }, }, - { { 57, 8, 64, 158, 68, 97, 1 }, - { 22, 150, 5, 136, 193, 18, 1 }, - { 195, 17, 60, 129, 8, 78, 0 }, - { 164, 65, 136, 208, 52, 180, 0 }, }, - { { 59, 1, 93, 14, 177, 201, 1 }, - { 174, 78, 53, 9, 165, 23, 0 }, - { 201, 198, 184, 93, 64, 110, 0 }, - { 116, 82, 200, 86, 57, 58, 1 }, }, - { { 58, 14, 216, 225, 88, 0, 1 }, - { 226, 242, 69, 202, 0, 137, 0 }, - { 128, 13, 67, 141, 184, 46, 0 }, - { 72, 128, 41, 209, 39, 163, 1 }, }, - { { 56, 110, 40, 10, 241, 243, 1 }, - { 110, 158, 190, 12, 128, 75, 1 }, - { 231, 199, 168, 10, 59, 14, 0 }, - { 233, 0, 152, 62, 188, 187, 0 }, }, - { { 57, 97, 243, 168, 199, 88, 1 }, - { 6, 114, 183, 175, 102, 82, 0 }, - { 141, 113, 138, 231, 195, 78, 0 }, - { 37, 51, 122, 246, 167, 48, 0 }, }, - { { 57, 175, 5, 221, 138, 171, 1 }, - { 82, 142, 110, 233, 45, 158, 1 }, - { 234, 168, 221, 208, 122, 206, 0 }, - { 188, 218, 75, 187, 56, 165, 0 }, }, - { { 57, 172, 253, 140, 32, 166, 1 }, - { 106, 202, 143, 155, 9, 22, 1 }, - { 178, 130, 24, 223, 154, 206, 0 }, - { 180, 72, 108, 248, 169, 171, 0 }, }, - { { 56, 215, 227, 25, 189, 206, 1 }, - { 94, 11, 189, 95, 110, 139, 0 }, - { 185, 222, 204, 99, 245, 142, 0 }, - { 104, 187, 125, 94, 232, 61, 0 }, }, - { { 62, 29, 177, 174, 155, 223, 1 }, - { 199, 239, 188, 187, 165, 195, 0 }, - { 253, 236, 186, 198, 220, 62, 0 }, - { 97, 210, 238, 158, 251, 241, 1 }, }, - { { 63, 57, 54, 16, 131, 58, 1 }, - { 147, 195, 190, 40, 38, 84, 1 }, - { 174, 96, 132, 54, 78, 126, 0 }, - { 149, 50, 10, 62, 225, 228, 1 }, }, - { { 61, 68, 172, 103, 73, 121, 1 }, - { 103, 54, 212, 78, 161, 212, 1 }, - { 207, 73, 115, 26, 145, 94, 0 }, - { 149, 194, 185, 21, 182, 115, 0 }, }, - { { 62, 216, 120, 160, 212, 247, 1 }, - { 167, 255, 173, 156, 72, 65, 1 }, - { 247, 149, 130, 143, 13, 190, 0 }, - { 193, 9, 28, 218, 255, 242, 1 }, }, - { { 67, 45, 92, 111, 10, 140, 0 }, - { 224, 232, 67, 112, 181, 150, 0 }, - { 24, 168, 123, 29, 90, 97, 0 }, - { 52, 214, 135, 97, 11, 131, 1 }, }, - { { 66, 52, 3, 145, 156, 82, 1 }, - { 214, 1, 42, 193, 82, 193, 0 }, - { 165, 28, 196, 224, 22, 33, 0 }, - { 65, 165, 65, 170, 64, 53, 1 }, }, - { { 64, 59, 107, 130, 137, 239, 0 }, - { 36, 141, 187, 145, 182, 136, 1 }, - { 123, 200, 160, 235, 110, 1, 0 }, - { 136, 182, 196, 238, 216, 146, 0 }, }, - { { 67, 109, 145, 73, 70, 116, 0 }, - { 196, 208, 66, 119, 84, 82, 1 }, - { 23, 49, 73, 68, 219, 97, 0 }, - { 165, 21, 119, 33, 5, 145, 1 }, }, - { { 67, 151, 133, 107, 233, 44, 1 }, - { 202, 49, 112, 83, 188, 158, 1 }, - { 154, 75, 235, 80, 244, 225, 0 }, - { 188, 158, 229, 7, 70, 41, 1 }, }, - { { 71, 29, 159, 177, 209, 0, 1 }, - { 243, 241, 48, 195, 22, 21, 0 }, - { 128, 69, 198, 252, 220, 113, 0 }, - { 84, 52, 97, 134, 71, 231, 1 }, }, - { { 70, 43, 251, 166, 87, 121, 0 }, - { 165, 244, 147, 163, 247, 73, 1 }, - { 79, 117, 50, 239, 234, 49, 0 }, - { 201, 119, 226, 228, 151, 210, 1 }, }, - { { 70, 92, 73, 248, 0, 58, 1 }, - { 243, 161, 73, 133, 48, 66, 1 }, - { 174, 0, 15, 201, 29, 49, 0 }, - { 161, 6, 80, 201, 66, 231, 1 }, }, - { { 69, 118, 247, 32, 48, 38, 1 }, - { 75, 97, 139, 23, 18, 29, 1 }, - { 178, 6, 2, 119, 183, 81, 0 }, - { 220, 36, 116, 104, 195, 105, 0 }, }, - { { 70, 168, 40, 69, 154, 252, 0 }, - { 165, 136, 226, 112, 57, 193, 1 }, - { 31, 172, 209, 10, 10, 177, 0 }, - { 193, 206, 7, 35, 136, 210, 1 }, }, - { { 68, 160, 107, 94, 1, 10, 0 }, - { 49, 0, 219, 1, 187, 2, 0 }, - { 40, 64, 61, 107, 2, 145, 0 }, - { 32, 110, 192, 109, 128, 70, 0 }, }, - { { 69, 185, 78, 113, 53, 37, 0 }, - { 57, 165, 83, 80, 94, 21, 1 }, - { 82, 86, 71, 57, 78, 209, 0 }, - { 212, 61, 5, 101, 82, 206, 0 }, }, - { { 75, 113, 215, 160, 253, 197, 1 }, - { 142, 127, 51, 151, 86, 149, 0 }, - { 209, 223, 130, 245, 199, 105, 0 }, - { 84, 181, 116, 230, 127, 56, 1 }, }, - { { 79, 60, 121, 82, 108, 209, 1 }, - { 255, 223, 195, 1, 208, 208, 0 }, - { 197, 155, 37, 79, 30, 121, 0 }, - { 5, 133, 192, 97, 253, 255, 1 }, }, - { { 79, 178, 80, 33, 70, 98, 0 }, - { 133, 115, 11, 96, 88, 24, 1 }, - { 35, 49, 66, 5, 38, 249, 0 }, - { 140, 13, 3, 104, 103, 80, 1 }, }, - { { 79, 239, 177, 247, 108, 238, 1 }, - { 223, 250, 202, 215, 253, 152, 1 }, - { 187, 155, 119, 198, 251, 249, 0 }, - { 140, 223, 245, 169, 175, 253, 1 }, }, - { { 82, 75, 82, 250, 189, 204, 0 }, - { 156, 232, 117, 148, 246, 139, 0 }, - { 25, 222, 175, 165, 105, 37, 0 }, - { 104, 183, 148, 215, 11, 156, 1 }, }, - { { 83, 200, 244, 221, 123, 200, 1 }, - { 158, 216, 213, 230, 57, 151, 0 }, - { 137, 239, 93, 151, 137, 229, 0 }, - { 116, 206, 51, 213, 141, 188, 1 }, }, - { { 87, 5, 99, 141, 134, 48, 0 }, - { 193, 0, 165, 225, 87, 82, 1 }, - { 6, 48, 216, 227, 80, 117, 0 }, - { 165, 117, 67, 210, 128, 65, 1 }, }, - { { 85, 75, 225, 85, 227, 0, 0 }, - { 25, 144, 245, 103, 21, 24, 0 }, - { 0, 99, 213, 67, 233, 85, 0 }, - { 12, 84, 115, 87, 132, 204, 0 }, }, - { { 85, 138, 47, 175, 183, 98, 0 }, - { 45, 160, 188, 225, 219, 31, 1 }, - { 35, 118, 250, 250, 40, 213, 0 }, - { 252, 109, 195, 158, 130, 218, 0 }, }, - { { 84, 153, 49, 31, 160, 83, 0 }, - { 29, 197, 172, 65, 157, 66, 0 }, - { 101, 2, 252, 70, 76, 149, 0 }, - { 33, 92, 193, 26, 209, 220, 0 }, }, - { { 88, 13, 255, 122, 69, 176, 1 }, - { 114, 250, 213, 3, 214, 70, 1 }, - { 134, 209, 47, 127, 216, 13, 0 }, - { 177, 53, 224, 85, 175, 167, 0 }, }, - { { 90, 18, 17, 208, 247, 52, 1 }, - { 154, 83, 116, 177, 80, 73, 1 }, - { 150, 119, 133, 196, 36, 45, 0 }, - { 201, 5, 70, 151, 101, 44, 1 }, }, - { { 95, 17, 24, 22, 24, 164, 0 }, - { 177, 75, 4, 16, 149, 145, 1 }, - { 18, 140, 52, 12, 68, 125, 0 }, - { 196, 212, 132, 16, 105, 70, 1 }, }, - { { 93, 61, 87, 179, 227, 47, 0 }, - { 89, 247, 63, 241, 182, 20, 1 }, - { 122, 99, 230, 245, 94, 93, 0 }, - { 148, 54, 199, 254, 119, 205, 0 }, }, - { { 93, 74, 155, 244, 162, 166, 0 }, - { 57, 234, 108, 183, 19, 24, 1 }, - { 50, 162, 151, 236, 169, 93, 0 }, - { 140, 100, 118, 155, 43, 206, 0 }, }, - { { 94, 137, 78, 179, 16, 89, 0 }, - { 181, 166, 5, 192, 190, 69, 0 }, - { 77, 4, 102, 185, 72, 189, 0 }, - { 81, 62, 129, 208, 50, 214, 1 }, }, - { { 95, 182, 4, 116, 142, 30, 1 }, - { 211, 35, 110, 48, 121, 220, 0 }, - { 188, 56, 151, 16, 54, 253, 0 }, - { 29, 207, 6, 59, 98, 101, 1 }, }, - { { 95, 252, 162, 7, 10, 47, 1 }, - { 195, 135, 142, 118, 187, 144, 1 }, - { 250, 40, 112, 34, 159, 253, 0 }, - { 132, 238, 183, 56, 240, 225, 1 }, }, - { { 98, 20, 52, 163, 198, 43, 1 }, - { 194, 117, 168, 232, 240, 4, 1 }, - { 234, 49, 226, 150, 20, 35, 0 }, - { 144, 7, 139, 138, 215, 33, 1 }, }, - { { 96, 69, 249, 159, 75, 213, 0 }, - { 116, 92, 145, 255, 149, 194, 0 }, - { 85, 233, 124, 207, 209, 3, 0 }, - { 33, 212, 255, 196, 157, 23, 0 }, }, - { { 96, 94, 235, 202, 23, 75, 1 }, - { 102, 133, 217, 175, 242, 11, 0 }, - { 233, 116, 41, 235, 189, 3, 0 }, - { 104, 39, 250, 205, 208, 179, 0 }, }, - { { 97, 111, 101, 43, 244, 79, 0 }, - { 76, 180, 171, 93, 244, 31, 0 }, - { 121, 23, 234, 83, 123, 67, 0 }, - { 124, 23, 221, 106, 150, 153, 0 }, }, - { { 96, 111, 231, 237, 175, 53, 0 }, - { 72, 164, 243, 255, 87, 206, 1 }, - { 86, 122, 219, 243, 251, 3, 0 }, - { 185, 245, 127, 231, 146, 137, 0 }, }, - { { 98, 227, 54, 39, 167, 213, 0 }, - { 140, 108, 178, 124, 223, 76, 0 }, - { 85, 242, 242, 54, 99, 163, 0 }, - { 25, 125, 159, 38, 155, 24, 1 }, }, - { { 101, 50, 13, 146, 242, 6, 0 }, - { 57, 17, 42, 185, 144, 29, 0 }, - { 48, 39, 164, 216, 38, 83, 0 }, - { 92, 4, 206, 170, 68, 78, 0 }, }, - { { 101, 174, 116, 178, 81, 246, 0 }, - { 85, 248, 155, 152, 152, 93, 1 }, - { 55, 197, 38, 151, 58, 211, 0 }, - { 221, 12, 140, 236, 143, 213, 0 }, }, - { { 106, 37, 233, 73, 31, 2, 1 }, - { 226, 2, 219, 107, 84, 131, 0 }, - { 160, 124, 73, 75, 210, 43, 0 }, - { 96, 149, 107, 109, 160, 35, 1 }, }, - { { 105, 154, 22, 139, 171, 78, 0 }, - { 12, 195, 56, 248, 186, 158, 0 }, - { 57, 106, 232, 180, 44, 203, 0 }, - { 60, 174, 143, 142, 97, 152, 0 }, }, - { { 104, 159, 185, 186, 26, 80, 0 }, - { 116, 227, 128, 171, 156, 203, 0 }, - { 5, 44, 46, 206, 252, 139, 0 }, - { 105, 156, 234, 128, 227, 151, 0 }, }, - { { 104, 172, 27, 248, 226, 27, 1 }, - { 122, 246, 106, 169, 58, 66, 0 }, - { 236, 35, 143, 236, 26, 139, 0 }, - { 33, 46, 74, 171, 55, 175, 0 }, }, - { { 107, 204, 220, 27, 224, 85, 0 }, - { 252, 214, 33, 94, 152, 86, 0 }, - { 85, 3, 236, 29, 153, 235, 0 }, - { 53, 12, 189, 66, 53, 159, 1 }, }, - { { 110, 70, 68, 174, 42, 27, 1 }, - { 203, 38, 9, 172, 177, 206, 0 }, - { 236, 42, 58, 145, 49, 59, 0 }, - { 57, 198, 154, 200, 50, 105, 1 }, }, - { { 110, 91, 221, 156, 85, 145, 1 }, - { 179, 223, 17, 143, 85, 79, 0 }, - { 196, 213, 28, 221, 237, 59, 0 }, - { 121, 85, 120, 196, 125, 230, 1 }, }, - { { 110, 114, 103, 14, 62, 109, 0 }, - { 141, 7, 131, 61, 243, 143, 1 }, - { 91, 62, 56, 115, 39, 59, 0 }, - { 248, 231, 222, 96, 240, 88, 1 }, }, - { { 111, 134, 147, 35, 45, 171, 0 }, - { 201, 110, 24, 75, 250, 152, 1 }, - { 106, 218, 98, 100, 176, 251, 0 }, - { 140, 175, 233, 12, 59, 73, 1 }, }, - { { 108, 137, 244, 64, 218, 114, 1 }, - { 7, 210, 233, 42, 28, 197, 1 }, - { 167, 45, 129, 23, 200, 155, 0 }, - { 209, 156, 42, 75, 165, 240, 0 }, }, - { { 111, 156, 82, 187, 178, 211, 1 }, - { 223, 239, 41, 232, 154, 83, 0 }, - { 229, 166, 238, 165, 28, 251, 0 }, - { 101, 44, 139, 202, 123, 253, 1 }, }, - { { 111, 148, 127, 90, 90, 175, 1 }, - { 243, 95, 201, 57, 186, 151, 1 }, - { 250, 173, 45, 127, 20, 251, 0 }, - { 244, 174, 206, 73, 253, 103, 1 }, }, - { { 110, 168, 22, 55, 188, 108, 1 }, - { 159, 226, 34, 88, 251, 133, 1 }, - { 155, 30, 246, 52, 10, 187, 0 }, - { 208, 239, 141, 34, 35, 252, 1 }, }, - { { 110, 177, 138, 108, 23, 81, 0 }, - { 165, 39, 82, 42, 95, 67, 0 }, - { 69, 116, 27, 40, 198, 187, 0 }, - { 97, 125, 42, 37, 114, 82, 1 }, }, - { { 111, 200, 142, 200, 247, 162, 0 }, - { 169, 154, 120, 174, 90, 23, 1 }, - { 34, 247, 137, 184, 137, 251, 0 }, - { 244, 45, 58, 143, 44, 202, 1 }, }, - { { 115, 81, 80, 190, 35, 239, 0 }, - { 156, 109, 29, 188, 181, 18, 1 }, - { 123, 226, 62, 133, 69, 103, 0 }, - { 164, 86, 158, 220, 91, 28, 1 }, }, - { { 112, 139, 71, 240, 27, 189, 0 }, - { 16, 172, 85, 185, 62, 205, 1 }, - { 94, 236, 7, 241, 104, 135, 0 }, - { 217, 190, 78, 213, 26, 132, 0 }, }, - { { 112, 149, 37, 251, 26, 110, 1 }, - { 86, 33, 204, 249, 188, 135, 1 }, - { 187, 44, 111, 210, 84, 135, 0 }, - { 240, 158, 207, 153, 194, 53, 0 }, }, - { { 114, 158, 255, 114, 99, 90, 0 }, - { 252, 241, 221, 43, 186, 76, 0 }, - { 45, 99, 39, 127, 188, 167, 0 }, - { 25, 46, 234, 93, 199, 159, 1 }, }, - { { 112, 171, 227, 124, 150, 208, 1 }, - { 22, 168, 231, 43, 95, 75, 0 }, - { 133, 180, 159, 99, 234, 135, 0 }, - { 105, 125, 106, 115, 138, 180, 0 }, }, - { { 114, 245, 151, 82, 179, 142, 0 }, - { 216, 73, 126, 63, 190, 5, 0 }, - { 56, 230, 165, 116, 215, 167, 0 }, - { 80, 62, 254, 63, 73, 13, 1 }, }, - { { 118, 5, 164, 230, 226, 221, 0 }, - { 205, 60, 228, 186, 181, 68, 0 }, - { 93, 163, 179, 146, 208, 55, 0 }, - { 17, 86, 174, 147, 158, 89, 1 }, }, - { { 116, 17, 6, 76, 105, 165, 0 }, - { 9, 29, 84, 24, 23, 134, 1 }, - { 82, 203, 25, 48, 68, 23, 0 }, - { 176, 244, 12, 21, 92, 72, 0 }, }, - { { 118, 36, 17, 60, 244, 246, 1 }, - { 223, 120, 46, 25, 81, 67, 1 }, - { 183, 151, 158, 68, 18, 55, 0 }, - { 225, 69, 76, 58, 15, 125, 1 }, }, - { { 116, 101, 193, 88, 187, 101, 1 }, - { 95, 4, 119, 63, 20, 131, 1 }, - { 211, 110, 141, 65, 211, 23, 0 }, - { 224, 148, 126, 119, 16, 125, 0 }, }, - { { 119, 125, 91, 58, 188, 225, 0 }, - { 253, 237, 39, 13, 214, 147, 1 }, - { 67, 158, 174, 109, 95, 119, 0 }, - { 228, 181, 216, 114, 91, 223, 1 }, }, - { { 119, 182, 15, 188, 123, 59, 0 }, - { 249, 53, 30, 169, 59, 223, 1 }, - { 110, 111, 30, 248, 54, 247, 0 }, - { 253, 238, 74, 188, 86, 79, 1 }, }, - { { 118, 180, 133, 0, 61, 168, 1 }, - { 203, 9, 22, 11, 120, 133, 1 }, - { 138, 222, 0, 80, 150, 183, 0 }, - { 208, 143, 104, 52, 72, 105, 1 }, }, - { { 117, 238, 187, 75, 14, 98, 1 }, - { 103, 192, 206, 111, 218, 154, 1 }, - { 163, 56, 105, 110, 187, 215, 0 }, - { 172, 173, 251, 57, 129, 243, 0 }, }, - { { 118, 250, 208, 216, 178, 184, 0 }, - { 153, 201, 103, 174, 56, 75, 1 }, - { 14, 166, 141, 133, 175, 183, 0 }, - { 233, 14, 58, 243, 73, 204, 1 }, }, - { { 120, 75, 97, 130, 218, 8, 1 }, - { 2, 146, 165, 173, 180, 137, 0 }, - { 136, 45, 160, 195, 105, 15, 0 }, - { 72, 150, 218, 210, 164, 160, 0 }, }, - { { 122, 187, 251, 47, 52, 31, 1 }, - { 170, 231, 143, 91, 255, 75, 0 }, - { 252, 22, 122, 111, 238, 175, 0 }, - { 105, 127, 237, 120, 243, 170, 1 }, }, - { { 127, 39, 5, 215, 84, 100, 0 }, - { 213, 18, 70, 217, 213, 29, 1 }, - { 19, 21, 117, 208, 114, 127, 0 }, - { 220, 85, 205, 177, 36, 85, 1 }, }, - { { 125, 227, 180, 31, 248, 152, 0 }, - { 25, 90, 166, 78, 189, 223, 0 }, - { 12, 143, 252, 22, 227, 223, 0 }, - { 125, 222, 185, 50, 173, 76, 0 }, }, - { { 127, 237, 141, 135, 251, 207, 1 }, - { 239, 158, 62, 255, 189, 149, 0 }, - { 249, 239, 240, 216, 219, 255, 0 }, - { 84, 222, 255, 190, 60, 251, 1 }, }, - { { 126, 246, 82, 55, 17, 197, 0 }, - { 213, 111, 23, 92, 155, 9, 0 }, - { 81, 196, 118, 37, 55, 191, 0 }, - { 72, 108, 157, 116, 123, 85, 1 }, }, - { { 129, 31, 240, 203, 185, 104, 1 }, - { 78, 193, 241, 194, 164, 187, 1 }, - { 139, 78, 233, 135, 252, 64, 1 }, - { 238, 146, 161, 199, 193, 185, 0 }, }, - { { 128, 46, 234, 6, 149, 52, 0 }, - { 96, 128, 179, 18, 195, 105, 1 }, - { 22, 84, 176, 43, 186, 0, 1 }, - { 203, 97, 164, 102, 128, 131, 0 }, }, - { { 130, 73, 255, 59, 138, 203, 1 }, - { 182, 236, 169, 103, 166, 166, 0 }, - { 233, 168, 238, 127, 201, 32, 1 }, - { 50, 178, 243, 74, 155, 182, 1 }, }, - { { 129, 200, 212, 49, 219, 37, 0 }, - { 16, 244, 49, 118, 8, 181, 1 }, - { 82, 109, 198, 21, 137, 192, 1 }, - { 214, 136, 55, 70, 23, 132, 0 }, }, - { { 132, 43, 15, 88, 160, 122, 0 }, - { 61, 128, 106, 1, 38, 110, 1 }, - { 47, 2, 141, 120, 106, 16, 1 }, - { 187, 50, 64, 43, 0, 222, 0 }, }, - { { 133, 43, 127, 179, 216, 14, 0 }, - { 49, 240, 171, 209, 166, 189, 0 }, - { 56, 13, 230, 255, 106, 80, 1 }, - { 94, 178, 197, 234, 135, 198, 0 }, }, - { { 134, 32, 167, 236, 235, 192, 1 }, - { 143, 56, 242, 163, 3, 166, 0 }, - { 129, 235, 155, 242, 130, 48, 1 }, - { 50, 224, 98, 167, 142, 120, 1 }, }, - { { 134, 89, 195, 75, 94, 119, 1 }, - { 135, 149, 73, 119, 198, 227, 1 }, - { 247, 61, 105, 97, 205, 48, 1 }, - { 227, 177, 247, 73, 84, 240, 1 }, }, - { { 135, 96, 6, 117, 87, 9, 0 }, - { 145, 52, 82, 100, 99, 53, 0 }, - { 72, 117, 87, 48, 3, 112, 1 }, - { 86, 99, 19, 37, 22, 68, 1 }, }, - { { 132, 219, 148, 26, 247, 200, 0 }, - { 29, 217, 48, 38, 236, 47, 0 }, - { 9, 247, 172, 20, 237, 144, 1 }, - { 122, 27, 178, 6, 77, 220, 0 }, }, - { { 138, 22, 181, 10, 57, 125, 0 }, - { 204, 71, 144, 19, 160, 239, 1 }, - { 95, 78, 40, 86, 180, 40, 1 }, - { 251, 130, 228, 4, 241, 25, 1 }, }, - { { 138, 63, 78, 17, 183, 8, 0 }, - { 248, 131, 51, 96, 102, 45, 0 }, - { 8, 118, 196, 57, 126, 40, 1 }, - { 90, 51, 3, 102, 96, 143, 1 }, }, - { { 137, 70, 134, 138, 98, 190, 0 }, - { 72, 26, 8, 182, 162, 126, 1 }, - { 62, 163, 40, 176, 177, 72, 1 }, - { 191, 34, 182, 136, 44, 9, 0 }, }, - { { 139, 122, 129, 163, 127, 219, 0 }, - { 140, 191, 26, 231, 224, 249, 0 }, - { 109, 255, 98, 192, 175, 104, 1 }, - { 79, 131, 243, 172, 126, 152, 1 }, }, - { { 137, 134, 220, 73, 174, 214, 0 }, - { 108, 74, 105, 114, 72, 254, 0 }, - { 53, 186, 201, 29, 176, 200, 1 }, - { 63, 137, 39, 75, 41, 27, 0 }, }, - { { 137, 153, 52, 70, 112, 178, 1 }, - { 10, 219, 200, 0, 141, 117, 1 }, - { 166, 135, 49, 22, 76, 200, 1 }, - { 215, 88, 128, 9, 237, 168, 0 }, }, - { { 139, 205, 202, 224, 85, 9, 1 }, - { 226, 182, 81, 134, 110, 49, 0 }, - { 200, 85, 3, 169, 217, 232, 1 }, - { 70, 59, 48, 197, 54, 163, 1 }, }, - { { 139, 222, 241, 118, 82, 104, 1 }, - { 214, 243, 193, 39, 169, 57, 1 }, - { 139, 37, 55, 71, 189, 232, 1 }, - { 206, 74, 242, 65, 231, 181, 1 }, }, - { { 142, 18, 98, 26, 198, 81, 1 }, - { 151, 23, 161, 32, 194, 106, 0 }, - { 197, 49, 172, 35, 36, 56, 1 }, - { 43, 33, 130, 66, 244, 116, 1 }, }, - { { 141, 46, 148, 181, 204, 168, 0 }, - { 81, 250, 34, 194, 97, 188, 1 }, - { 10, 153, 214, 148, 186, 88, 1 }, - { 158, 195, 33, 162, 47, 197, 0 }, }, - { { 140, 75, 185, 11, 82, 34, 0 }, - { 33, 210, 136, 103, 132, 43, 1 }, - { 34, 37, 104, 78, 233, 24, 1 }, - { 234, 16, 243, 8, 165, 194, 0 }, }, - { { 141, 153, 201, 208, 216, 171, 1 }, - { 51, 159, 105, 131, 44, 177, 1 }, - { 234, 141, 133, 201, 204, 216, 1 }, - { 198, 154, 96, 203, 124, 230, 0 }, }, - { { 144, 17, 129, 73, 60, 185, 1 }, - { 10, 13, 68, 67, 100, 227, 1 }, - { 206, 158, 73, 64, 196, 4, 1 }, - { 227, 147, 97, 17, 88, 40, 0 }, }, - { { 146, 45, 168, 84, 94, 84, 1 }, - { 246, 144, 198, 50, 69, 225, 0 }, - { 149, 61, 21, 10, 218, 36, 1 }, - { 67, 209, 38, 49, 132, 183, 1 }, }, - { { 144, 78, 120, 161, 56, 250, 1 }, - { 110, 232, 141, 196, 32, 233, 1 }, - { 175, 142, 66, 143, 57, 4, 1 }, - { 203, 130, 17, 216, 139, 187, 0 }, }, - { { 144, 151, 90, 16, 127, 241, 0 }, - { 124, 93, 21, 32, 78, 233, 1 }, - { 71, 255, 4, 45, 116, 132, 1 }, - { 203, 185, 2, 84, 93, 31, 0 }, }, - { { 144, 179, 61, 187, 100, 42, 0 }, - { 56, 113, 142, 193, 236, 46, 1 }, - { 42, 19, 110, 222, 102, 132, 1 }, - { 186, 27, 193, 184, 199, 14, 0 }, }, - { { 147, 192, 227, 79, 19, 241, 0 }, - { 132, 12, 213, 103, 139, 115, 1 }, - { 71, 228, 121, 99, 129, 228, 1 }, - { 231, 104, 243, 85, 152, 16, 1 }, }, - { { 151, 73, 34, 81, 12, 38, 0 }, - { 145, 128, 204, 84, 70, 176, 1 }, - { 50, 24, 69, 34, 73, 116, 1 }, - { 134, 177, 21, 25, 128, 196, 1 }, }, - { { 151, 74, 87, 164, 6, 195, 0 }, - { 133, 236, 13, 165, 67, 60, 0 }, - { 97, 176, 18, 245, 41, 116, 1 }, - { 30, 97, 82, 216, 27, 208, 1 }, }, - { { 150, 115, 104, 38, 246, 150, 1 }, - { 171, 57, 175, 52, 197, 105, 0 }, - { 180, 183, 178, 11, 103, 52, 1 }, - { 75, 81, 150, 122, 206, 106, 1 }, }, - { { 149, 138, 223, 218, 96, 3, 1 }, - { 59, 212, 77, 131, 138, 62, 0 }, - { 224, 3, 45, 253, 168, 212, 1 }, - { 62, 40, 224, 217, 21, 238, 0 }, }, - { { 150, 175, 46, 35, 58, 45, 0 }, - { 233, 164, 134, 112, 174, 173, 1 }, - { 90, 46, 98, 58, 122, 180, 1 }, - { 218, 186, 135, 48, 146, 203, 1 }, }, - { { 155, 7, 68, 229, 222, 47, 0 }, - { 192, 54, 109, 240, 101, 189, 1 }, - { 122, 61, 211, 145, 112, 108, 1 }, - { 222, 211, 7, 219, 54, 1, 1 }, }, - { { 152, 75, 88, 148, 206, 180, 0 }, - { 48, 218, 37, 180, 69, 232, 1 }, - { 22, 185, 148, 141, 105, 12, 1 }, - { 139, 209, 22, 210, 45, 134, 0 }, }, - { { 153, 122, 202, 55, 11, 119, 1 }, - { 54, 167, 31, 118, 131, 248, 1 }, - { 247, 104, 118, 41, 175, 76, 1 }, - { 143, 224, 183, 124, 114, 182, 0 }, }, - { { 156, 0, 144, 208, 133, 65, 1 }, - { 23, 70, 116, 130, 64, 32, 0 }, - { 193, 80, 133, 132, 128, 28, 1 }, - { 2, 1, 32, 151, 49, 116, 0 }, }, - { { 157, 117, 4, 69, 227, 242, 1 }, - { 79, 27, 126, 100, 5, 116, 1 }, - { 167, 227, 209, 16, 87, 92, 1 }, - { 151, 80, 19, 63, 108, 121, 0 }, }, - { { 160, 66, 244, 31, 20, 137, 1 }, - { 18, 76, 129, 78, 225, 47, 0 }, - { 200, 148, 124, 23, 161, 2, 1 }, - { 122, 67, 185, 64, 153, 36, 0 }, }, - { { 160, 154, 32, 41, 137, 97, 0 }, - { 4, 165, 176, 72, 8, 170, 1 }, - { 67, 72, 202, 2, 44, 130, 1 }, - { 170, 136, 9, 6, 210, 144, 0 }, }, - { { 161, 151, 65, 181, 6, 20, 0 }, - { 80, 33, 1, 249, 77, 120, 0 }, - { 20, 48, 86, 193, 116, 194, 1 }, - { 15, 89, 79, 192, 66, 5, 0 }, }, - { { 162, 199, 179, 237, 218, 219, 0 }, - { 196, 124, 232, 239, 47, 235, 0 }, - { 109, 173, 219, 230, 241, 162, 1 }, - { 107, 250, 123, 139, 159, 17, 1 }, }, - { { 163, 237, 115, 3, 171, 156, 0 }, - { 200, 200, 179, 125, 174, 240, 0 }, - { 28, 234, 224, 103, 91, 226, 1 }, - { 7, 186, 223, 102, 137, 137, 1 }, }, - { { 164, 14, 153, 123, 197, 24, 0 }, - { 113, 240, 112, 75, 224, 106, 0 }, - { 12, 81, 239, 76, 184, 18, 1 }, - { 43, 3, 233, 7, 7, 199, 0 }, }, - { { 166, 46, 211, 181, 170, 239, 0 }, - { 221, 236, 43, 251, 35, 168, 1 }, - { 123, 170, 214, 229, 186, 50, 1 }, - { 138, 226, 111, 234, 27, 221, 1 }, }, - { { 166, 72, 83, 81, 97, 91, 1 }, - { 159, 212, 89, 77, 34, 96, 0 }, - { 237, 67, 69, 101, 9, 50, 1 }, - { 3, 34, 89, 77, 21, 252, 1 }, }, - { { 166, 88, 46, 241, 183, 120, 1 }, - { 191, 161, 240, 236, 98, 101, 1 }, - { 143, 118, 199, 186, 13, 50, 1 }, - { 211, 35, 27, 135, 194, 254, 1 }, }, - { { 167, 125, 62, 203, 3, 105, 0 }, - { 229, 197, 210, 236, 166, 54, 1 }, - { 75, 96, 105, 190, 95, 114, 1 }, - { 182, 50, 155, 165, 209, 211, 1 }, }, - { { 165, 183, 40, 12, 100, 157, 0 }, - { 105, 29, 130, 24, 109, 122, 0 }, - { 92, 147, 24, 10, 118, 210, 1 }, - { 47, 91, 12, 32, 220, 75, 0 }, }, - { { 164, 207, 91, 78, 212, 244, 0 }, - { 101, 216, 97, 29, 207, 107, 1 }, - { 23, 149, 185, 109, 121, 146, 1 }, - { 235, 121, 220, 67, 13, 211, 0 }, }, - { { 171, 21, 211, 205, 48, 133, 0 }, - { 200, 79, 65, 219, 7, 51, 0 }, - { 80, 134, 89, 229, 212, 106, 1 }, - { 102, 112, 109, 193, 121, 9, 1 }, }, - { { 171, 47, 103, 30, 208, 8, 1 }, - { 210, 146, 163, 9, 167, 63, 0 }, - { 136, 5, 188, 115, 122, 106, 1 }, - { 126, 114, 200, 98, 164, 165, 1 }, }, - { { 168, 43, 177, 100, 80, 17, 1 }, - { 2, 246, 194, 11, 5, 105, 0 }, - { 196, 5, 19, 70, 234, 10, 1 }, - { 75, 80, 104, 33, 183, 160, 0 }, }, - { { 168, 67, 58, 121, 199, 250, 1 }, - { 54, 122, 248, 108, 102, 106, 1 }, - { 175, 241, 207, 46, 97, 10, 1 }, - { 171, 51, 27, 15, 175, 54, 0 }, }, - { { 168, 95, 145, 245, 169, 81, 0 }, - { 92, 231, 112, 207, 5, 232, 0 }, - { 69, 74, 215, 196, 253, 10, 1 }, - { 11, 208, 121, 135, 115, 157, 0 }, }, - { { 168, 126, 123, 132, 128, 94, 1 }, - { 102, 195, 171, 157, 35, 104, 0 }, - { 189, 0, 144, 239, 63, 10, 1 }, - { 11, 98, 92, 234, 225, 179, 0 }, }, - { { 168, 132, 121, 195, 96, 56, 1 }, - { 106, 82, 193, 201, 168, 96, 1 }, - { 142, 3, 97, 207, 16, 138, 1 }, - { 131, 10, 201, 193, 165, 43, 0 }, }, - { { 171, 163, 68, 207, 130, 112, 0 }, - { 132, 2, 99, 232, 141, 126, 1 }, - { 7, 32, 249, 145, 98, 234, 1 }, - { 191, 88, 139, 227, 32, 16, 1 }, }, - { { 171, 230, 49, 247, 179, 118, 0 }, - { 220, 98, 250, 253, 137, 121, 1 }, - { 55, 102, 247, 198, 51, 234, 1 }, - { 207, 72, 223, 175, 163, 29, 1 }, }, - { { 172, 22, 16, 22, 73, 151, 0 }, - { 81, 95, 24, 24, 129, 232, 0 }, - { 116, 201, 52, 4, 52, 26, 1 }, - { 11, 192, 140, 12, 125, 69, 0 }, }, - { { 174, 39, 216, 18, 235, 36, 1 }, - { 251, 82, 51, 58, 132, 168, 1 }, - { 146, 107, 164, 13, 242, 58, 1 }, - { 138, 144, 174, 102, 37, 111, 1 }, }, - { { 175, 59, 230, 2, 186, 159, 1 }, - { 139, 143, 171, 58, 166, 253, 0 }, - { 252, 174, 160, 51, 238, 122, 1 }, - { 95, 178, 174, 106, 248, 232, 1 }, }, - { { 174, 70, 26, 234, 137, 71, 1 }, - { 231, 102, 120, 156, 130, 170, 0 }, - { 241, 72, 171, 172, 49, 58, 1 }, - { 42, 160, 156, 143, 51, 115, 1 }, }, - { { 173, 66, 113, 218, 119, 159, 1 }, - { 27, 94, 217, 189, 224, 123, 0 }, - { 252, 247, 45, 199, 33, 90, 1 }, - { 111, 3, 222, 205, 189, 108, 0 }, }, - { { 173, 88, 23, 206, 43, 193, 1 }, - { 15, 207, 80, 173, 131, 182, 0 }, - { 193, 234, 57, 244, 13, 90, 1 }, - { 54, 224, 218, 133, 121, 248, 0 }, }, - { { 172, 151, 210, 208, 179, 77, 0 }, - { 93, 71, 113, 186, 46, 41, 0 }, - { 89, 102, 133, 165, 244, 154, 1 }, - { 74, 58, 46, 199, 113, 93, 0 }, }, - { { 173, 190, 197, 66, 11, 202, 0 }, - { 69, 139, 91, 43, 168, 188, 0 }, - { 41, 232, 33, 81, 190, 218, 1 }, - { 30, 138, 234, 109, 104, 209, 0 }, }, - { { 175, 250, 80, 85, 120, 101, 0 }, - { 157, 215, 67, 92, 9, 185, 1 }, - { 83, 15, 85, 5, 47, 250, 1 }, - { 206, 200, 29, 97, 117, 220, 1 }, }, - { { 176, 31, 206, 26, 12, 146, 1 }, - { 114, 137, 13, 10, 198, 238, 0 }, - { 164, 152, 44, 57, 252, 6, 1 }, - { 59, 177, 168, 88, 72, 167, 0 }, }, - { { 177, 112, 134, 190, 143, 137, 0 }, - { 16, 45, 54, 174, 227, 182, 0 }, - { 72, 248, 190, 176, 135, 70, 1 }, - { 54, 227, 186, 182, 90, 4, 0 }, }, - { { 183, 222, 54, 61, 41, 2, 0 }, - { 217, 225, 156, 76, 11, 190, 0 }, - { 32, 74, 94, 54, 61, 246, 1 }, - { 62, 232, 25, 28, 195, 205, 1 }, }, - { { 180, 229, 14, 234, 89, 126, 0 }, - { 101, 48, 94, 156, 174, 231, 1 }, - { 63, 77, 43, 184, 83, 150, 1 }, - { 243, 186, 156, 189, 6, 83, 0 }, }, - { { 186, 59, 44, 235, 91, 138, 0 }, - { 160, 187, 222, 232, 164, 175, 0 }, - { 40, 237, 107, 154, 110, 46, 1 }, - { 122, 146, 139, 189, 238, 130, 1 }, }, - { { 187, 123, 82, 243, 209, 20, 0 }, - { 144, 243, 119, 220, 134, 121, 0 }, - { 20, 69, 231, 165, 111, 110, 1 }, - { 79, 48, 157, 247, 103, 132, 1 }, }, - { { 186, 116, 182, 71, 89, 86, 1 }, - { 198, 83, 222, 94, 131, 229, 0 }, - { 181, 77, 113, 54, 151, 46, 1 }, - { 83, 224, 189, 61, 229, 49, 1 }, }, - { { 186, 139, 73, 97, 25, 111, 1 }, - { 166, 166, 93, 89, 44, 169, 1 }, - { 251, 76, 67, 73, 104, 174, 1 }, - { 202, 154, 77, 93, 50, 178, 1 }, }, - { { 187, 152, 233, 200, 28, 89, 0 }, - { 164, 135, 197, 139, 104, 243, 0 }, - { 77, 28, 9, 203, 140, 238, 1 }, - { 103, 139, 104, 209, 240, 146, 1 }, }, - { { 188, 30, 177, 81, 156, 163, 1 }, - { 83, 207, 236, 75, 64, 169, 1 }, - { 226, 156, 197, 70, 188, 30, 1 }, - { 202, 129, 105, 27, 249, 229, 0 }, }, - { { 188, 42, 52, 17, 27, 68, 1 }, - { 23, 194, 150, 120, 0, 173, 0 }, - { 145, 108, 68, 22, 42, 30, 1 }, - { 90, 128, 15, 52, 161, 244, 0 }, }, - { { 189, 81, 101, 27, 30, 213, 0 }, - { 21, 15, 133, 125, 196, 247, 0 }, - { 85, 188, 108, 83, 69, 94, 1 }, - { 119, 145, 223, 80, 248, 84, 0 }, }, - { { 190, 112, 117, 173, 51, 180, 1 }, - { 139, 107, 151, 253, 1, 103, 1 }, - { 150, 230, 90, 215, 7, 62, 1 }, - { 243, 64, 95, 244, 235, 104, 1 }, }, - { { 189, 255, 164, 161, 70, 198, 1 }, - { 71, 187, 142, 254, 76, 60, 0 }, - { 177, 177, 66, 146, 255, 222, 1 }, - { 30, 25, 63, 184, 238, 241, 0 }, }, - { { 193, 4, 151, 159, 155, 192, 0 }, - { 84, 72, 48, 227, 147, 183, 0 }, - { 1, 236, 252, 244, 144, 65, 1 }, - { 118, 228, 227, 134, 9, 21, 0 }, }, - { { 193, 30, 33, 36, 254, 38, 1 }, - { 74, 177, 168, 49, 81, 185, 1 }, - { 178, 63, 146, 66, 60, 65, 1 }, - { 206, 197, 70, 10, 198, 169, 0 }, }, - { { 192, 237, 72, 64, 106, 165, 0 }, - { 104, 156, 67, 52, 28, 160, 1 }, - { 82, 171, 1, 9, 91, 129, 1 }, - { 130, 156, 22, 97, 28, 139, 0 }, }, - { { 192, 234, 184, 166, 4, 229, 0 }, - { 36, 236, 130, 150, 217, 40, 1 }, - { 83, 144, 50, 142, 171, 129, 1 }, - { 138, 77, 180, 160, 155, 146, 0 }, }, - { { 198, 87, 250, 41, 77, 82, 1 }, - { 231, 113, 153, 70, 86, 234, 0 }, - { 165, 89, 74, 47, 245, 49, 1 }, - { 43, 181, 49, 76, 199, 115, 1 }, }, - { { 200, 111, 63, 193, 206, 104, 0 }, - { 100, 210, 226, 229, 118, 172, 1 }, - { 11, 57, 193, 254, 123, 9, 1 }, - { 154, 183, 83, 163, 165, 147, 0 }, }, - { { 200, 99, 86, 204, 12, 4, 1 }, - { 2, 66, 67, 148, 87, 174, 0 }, - { 144, 24, 25, 181, 99, 9, 1 }, - { 58, 245, 20, 225, 33, 32, 0 }, }, - { { 201, 215, 21, 97, 151, 171, 1 }, - { 66, 111, 120, 101, 124, 61, 1 }, - { 234, 244, 195, 84, 117, 201, 1 }, - { 222, 31, 83, 15, 123, 33, 0 }, }, - { { 203, 227, 118, 18, 26, 242, 1 }, - { 150, 74, 139, 36, 158, 253, 1 }, - { 167, 172, 36, 55, 99, 233, 1 }, - { 223, 188, 146, 104, 169, 52, 1 }, }, - { { 204, 23, 188, 202, 55, 164, 1 }, - { 107, 75, 208, 178, 212, 47, 1 }, - { 146, 246, 41, 158, 244, 25, 1 }, - { 250, 21, 166, 133, 233, 107, 0 }, }, - { { 204, 223, 166, 39, 145, 190, 0 }, - { 65, 171, 184, 86, 191, 109, 1 }, - { 62, 196, 242, 50, 253, 153, 1 }, - { 219, 126, 181, 14, 234, 193, 0 }, }, - { { 204, 235, 235, 67, 36, 93, 0 }, - { 45, 134, 195, 87, 254, 104, 0 }, - { 93, 18, 97, 107, 235, 153, 1 }, - { 11, 63, 245, 97, 176, 218, 0 }, }, - { { 209, 53, 155, 82, 5, 119, 0 }, - { 116, 69, 94, 19, 214, 112, 1 }, - { 119, 80, 37, 108, 214, 69, 1 }, - { 135, 53, 228, 61, 81, 23, 0 }, }, - { { 209, 91, 212, 112, 60, 106, 0 }, - { 28, 225, 77, 6, 116, 189, 1 }, - { 43, 30, 7, 21, 237, 69, 1 }, - { 222, 151, 48, 89, 67, 156, 0 }, }, - { { 215, 26, 117, 66, 163, 79, 1 }, - { 143, 197, 253, 49, 176, 60, 0 }, - { 249, 98, 161, 87, 44, 117, 1 }, - { 30, 6, 198, 95, 209, 248, 1 }, }, - { { 215, 230, 123, 63, 234, 215, 1 }, - { 255, 124, 175, 117, 155, 250, 0 }, - { 245, 171, 254, 111, 51, 245, 1 }, - { 47, 236, 215, 122, 159, 127, 1 }, }, - { { 219, 2, 186, 238, 81, 189, 1 }, - { 162, 126, 212, 146, 179, 123, 1 }, - { 222, 197, 59, 174, 160, 109, 1 }, - { 239, 102, 164, 149, 191, 34, 1 }, }, - { { 216, 126, 223, 70, 165, 187, 1 }, - { 106, 207, 127, 7, 243, 108, 1 }, - { 238, 210, 177, 125, 191, 13, 1 }, - { 155, 103, 240, 127, 121, 171, 0 }, }, - { { 216, 131, 133, 71, 132, 38, 1 }, - { 2, 2, 108, 83, 221, 44, 1 }, - { 178, 16, 241, 80, 224, 141, 1 }, - { 154, 93, 229, 27, 32, 32, 0 }, }, - { { 218, 189, 22, 192, 207, 231, 0 }, - { 196, 223, 126, 176, 94, 164, 1 }, - { 115, 249, 129, 180, 94, 173, 1 }, - { 146, 189, 6, 191, 125, 145, 1 }, }, - { { 219, 226, 77, 128, 236, 124, 1 }, - { 174, 18, 39, 149, 120, 252, 1 }, - { 159, 27, 128, 217, 35, 237, 1 }, - { 159, 143, 84, 242, 36, 58, 1 }, }, - { { 217, 244, 154, 237, 44, 69, 0 }, - { 108, 103, 70, 214, 91, 178, 0 }, - { 81, 26, 91, 172, 151, 205, 1 }, - { 38, 237, 53, 177, 115, 27, 0 }, }, - { { 222, 24, 187, 178, 78, 206, 1 }, - { 183, 251, 140, 179, 242, 160, 0 }, - { 185, 185, 38, 238, 140, 61, 1 }, - { 2, 167, 230, 152, 239, 246, 1 }, }, - { { 221, 25, 226, 31, 178, 162, 1 }, - { 27, 139, 173, 98, 151, 51, 1 }, - { 162, 166, 252, 35, 204, 93, 1 }, - { 230, 116, 163, 90, 232, 236, 0 }, }, - { { 222, 44, 65, 181, 149, 43, 0 }, - { 209, 166, 63, 193, 113, 33, 1 }, - { 106, 84, 214, 193, 26, 61, 1 }, - { 194, 71, 65, 254, 50, 197, 1 }, }, - { { 220, 75, 143, 150, 13, 97, 0 }, - { 53, 134, 20, 135, 215, 172, 1 }, - { 67, 88, 52, 248, 233, 29, 1 }, - { 154, 245, 240, 148, 48, 214, 0 }, }, - { { 221, 148, 173, 142, 18, 115, 1 }, - { 103, 7, 140, 163, 153, 119, 1 }, - { 231, 36, 56, 218, 148, 221, 1 }, - { 247, 76, 226, 152, 240, 115, 0 }, }, - { { 221, 209, 152, 178, 105, 248, 1 }, - { 63, 123, 20, 134, 188, 240, 1 }, - { 143, 203, 38, 140, 197, 221, 1 }, - { 135, 158, 176, 148, 111, 126, 0 }, }, - { { 225, 16, 129, 210, 125, 199, 0 }, - { 28, 29, 88, 155, 208, 177, 0 }, - { 113, 223, 37, 192, 132, 67, 1 }, - { 70, 133, 236, 141, 92, 28, 0 }, }, - { { 226, 29, 244, 60, 230, 69, 1 }, - { 222, 245, 161, 58, 85, 38, 0 }, - { 209, 51, 158, 23, 220, 35, 1 }, - { 50, 85, 46, 66, 215, 189, 1 }, }, - { { 227, 42, 142, 26, 117, 17, 0 }, - { 184, 148, 18, 10, 210, 127, 0 }, - { 68, 87, 44, 56, 170, 99, 1 }, - { 127, 37, 168, 36, 20, 142, 1 }, }, - { { 230, 127, 34, 189, 228, 63, 1 }, - { 219, 181, 170, 220, 119, 106, 1 }, - { 254, 19, 222, 162, 127, 51, 1 }, - { 171, 119, 29, 170, 214, 237, 1 }, }, - { { 230, 201, 244, 50, 47, 175, 1 }, - { 155, 236, 153, 62, 252, 164, 1 }, - { 250, 250, 38, 23, 201, 179, 1 }, - { 146, 159, 190, 76, 155, 236, 1 }, }, - { { 233, 65, 95, 6, 96, 115, 0 }, - { 44, 86, 9, 13, 151, 116, 1 }, - { 103, 3, 48, 125, 65, 75, 1 }, - { 151, 116, 216, 72, 53, 26, 0 }, }, - { { 232, 162, 221, 84, 147, 113, 0 }, - { 52, 70, 115, 43, 25, 109, 1 }, - { 71, 100, 149, 93, 162, 139, 1 }, - { 219, 76, 106, 103, 49, 22, 0 }, }, - { { 232, 216, 146, 243, 185, 171, 1 }, - { 26, 239, 120, 206, 186, 161, 1 }, - { 234, 206, 231, 164, 141, 139, 1 }, - { 194, 174, 185, 143, 123, 172, 0 }, }, - { { 234, 224, 242, 13, 25, 46, 0 }, - { 128, 66, 155, 94, 59, 163, 1 }, - { 58, 76, 88, 39, 131, 171, 1 }, - { 226, 238, 61, 108, 161, 0, 1 }, }, - { { 237, 183, 135, 120, 221, 244, 0 }, - { 85, 59, 114, 27, 94, 255, 1 }, - { 23, 221, 143, 112, 246, 219, 1 }, - { 255, 189, 108, 39, 110, 85, 0 }, }, - { { 237, 243, 41, 3, 202, 39, 1 }, - { 35, 23, 170, 125, 156, 184, 1 }, - { 242, 41, 224, 74, 103, 219, 1 }, - { 142, 156, 223, 42, 244, 98, 0 }, }, - { { 242, 9, 23, 170, 178, 81, 0 }, - { 140, 228, 36, 169, 150, 103, 0 }, - { 69, 38, 170, 244, 72, 39, 1 }, - { 115, 52, 202, 146, 19, 152, 1 }, }, - { { 242, 87, 191, 49, 98, 230, 1 }, - { 254, 121, 140, 127, 22, 44, 1 }, - { 179, 163, 70, 126, 245, 39, 1 }, - { 154, 52, 127, 24, 207, 63, 1 }, }, - { { 240, 161, 59, 114, 216, 194, 0 }, - { 52, 120, 238, 9, 158, 161, 0 }, - { 33, 141, 167, 110, 66, 135, 1 }, - { 66, 188, 200, 59, 143, 22, 0 }, }, - { { 242, 204, 10, 145, 3, 251, 0 }, - { 244, 140, 28, 236, 58, 96, 1 }, - { 111, 224, 68, 168, 25, 167, 1 }, - { 131, 46, 27, 156, 24, 151, 1 }, }, - { { 240, 197, 80, 127, 169, 160, 1 }, - { 90, 104, 117, 76, 157, 162, 1 }, - { 130, 202, 255, 5, 81, 135, 1 }, - { 162, 220, 153, 87, 11, 45, 0 }, }, - { { 241, 197, 159, 71, 93, 153, 1 }, - { 98, 92, 84, 79, 255, 245, 0 }, - { 204, 221, 113, 124, 209, 199, 1 }, - { 87, 255, 249, 21, 29, 35, 0 }, }, - { { 240, 244, 101, 147, 182, 176, 1 }, - { 90, 9, 167, 237, 216, 101, 1 }, - { 134, 182, 228, 211, 23, 135, 1 }, - { 211, 13, 219, 242, 200, 45, 0 }, }, - { { 244, 0, 37, 19, 37, 72, 0 }, - { 29, 0, 148, 73, 240, 36, 0 }, - { 9, 82, 100, 82, 0, 23, 1 }, - { 18, 7, 201, 20, 128, 92, 0 }, }, - { { 244, 111, 108, 190, 223, 216, 1 }, - { 119, 184, 183, 172, 245, 239, 0 }, - { 141, 253, 190, 155, 123, 23, 1 }, - { 123, 215, 154, 246, 142, 247, 0 }, }, - { { 247, 104, 114, 223, 78, 225, 0 }, - { 149, 220, 199, 236, 211, 178, 1 }, - { 67, 185, 125, 167, 11, 119, 1 }, - { 166, 229, 155, 241, 157, 212, 1 }, }, - { { 251, 140, 211, 46, 207, 23, 0 }, - { 192, 246, 61, 59, 219, 242, 0 }, - { 116, 121, 186, 101, 152, 239, 1 }, - { 39, 237, 238, 94, 55, 129, 1 }, }, - { { 249, 145, 73, 232, 47, 176, 0 }, - { 40, 43, 85, 169, 92, 242, 1 }, - { 6, 250, 11, 201, 68, 207, 1 }, - { 167, 157, 74, 213, 106, 10, 0 }, }, - { { 251, 204, 144, 194, 16, 228, 0 }, - { 196, 202, 68, 158, 152, 49, 1 }, - { 19, 132, 33, 132, 153, 239, 1 }, - { 198, 12, 188, 145, 41, 145, 1 }, }, - { { 248, 252, 106, 92, 170, 113, 1 }, - { 126, 135, 231, 44, 27, 226, 1 }, - { 199, 42, 157, 43, 31, 143, 1 }, - { 163, 236, 26, 115, 240, 191, 0 }, }, - { { 254, 15, 171, 22, 40, 191, 1 }, - { 251, 142, 140, 27, 183, 232, 1 }, - { 254, 138, 52, 106, 248, 63, 1 }, - { 139, 246, 236, 24, 184, 239, 1 }, }, - { { 252, 66, 14, 134, 208, 220, 0 }, - { 37, 26, 36, 156, 179, 109, 0 }, - { 29, 133, 176, 184, 33, 31, 1 }, - { 91, 102, 156, 146, 44, 82, 0 }, }, - { { 252, 124, 113, 120, 44, 31, 0 }, - { 89, 231, 207, 29, 112, 226, 0 }, - { 124, 26, 15, 71, 31, 31, 1 }, - { 35, 135, 92, 121, 243, 205, 0 }, }, - { { 255, 159, 183, 150, 227, 134, 1 }, - { 219, 219, 188, 187, 159, 60, 0 }, - { 176, 227, 180, 246, 252, 255, 1 }, - { 30, 124, 238, 158, 237, 237, 1 }, }, - { { 112, 127, 0, 71, 111, 110, 0 }, - { 76, 145, 94, 124, 245, 136, 1 }, - { 59, 123, 113, 0, 127, 7, 0 }, - { 136, 215, 159, 61, 68, 153, 0 }, }, - { { 131, 238, 196, 68, 55, 228, 0 }, - { 204, 136, 83, 54, 73, 61, 1 }, - { 19, 246, 17, 17, 187, 224, 1 }, - { 222, 73, 54, 101, 8, 153, 1 }, }, - { { 1, 32, 180, 5, 180, 95, 1 }, - { 14, 68, 170, 82, 97, 85, 0 }, - { 253, 22, 208, 22, 130, 64, 0 }, - { 85, 67, 37, 42, 145, 56, 0 }, }, - { { 0, 59, 124, 20, 69, 167, 0 }, - { 48, 221, 155, 16, 69, 12, 1 }, - { 114, 209, 20, 31, 110, 0, 0 }, - { 152, 81, 4, 108, 221, 134, 0 }, }, - { { 3, 79, 186, 190, 163, 12, 0 }, - { 248, 224, 176, 182, 167, 26, 0 }, - { 24, 98, 190, 174, 249, 96, 0 }, - { 44, 114, 182, 134, 131, 143, 1 }, }, - { { 1, 86, 37, 1, 21, 142, 0 }, - { 64, 9, 152, 85, 96, 29, 0 }, - { 56, 212, 64, 82, 53, 64, 0 }, - { 92, 3, 85, 12, 200, 1, 0 }, }, - { { 1, 143, 230, 168, 33, 42, 0 }, - { 72, 160, 153, 130, 46, 30, 1 }, - { 42, 66, 10, 179, 248, 192, 0 }, - { 188, 58, 32, 204, 130, 137, 0 }, }, - { { 0, 175, 103, 1, 210, 134, 0 }, - { 64, 152, 171, 113, 14, 13, 0 }, - { 48, 165, 192, 115, 122, 128, 0 }, - { 88, 56, 71, 106, 140, 129, 0 }, }, - { { 0, 217, 16, 247, 55, 249, 0 }, - { 28, 237, 80, 228, 237, 65, 1 }, - { 79, 246, 119, 132, 77, 128, 0 }, - { 193, 91, 147, 133, 91, 156, 0 }, }, - { { 3, 240, 9, 255, 186, 56, 0 }, - { 184, 33, 98, 229, 169, 211, 1 }, - { 14, 46, 255, 200, 7, 224, 0 }, - { 229, 202, 211, 163, 66, 14, 1 }, }, - { { 5, 92, 205, 107, 57, 177, 1 }, - { 107, 173, 81, 71, 128, 215, 1 }, - { 198, 206, 107, 89, 157, 80, 0 }, - { 245, 128, 241, 69, 90, 235, 0 }, }, - { { 6, 109, 146, 36, 128, 158, 1 }, - { 195, 232, 42, 22, 39, 64, 0 }, - { 188, 128, 146, 36, 219, 48, 0 }, - { 1, 114, 52, 42, 11, 225, 1 }, }, - { { 4, 166, 38, 247, 185, 66, 1 }, - { 95, 32, 250, 192, 139, 141, 0 }, - { 161, 78, 247, 178, 50, 144, 0 }, - { 88, 232, 129, 175, 130, 125, 0 }, }, - { { 4, 196, 160, 187, 5, 176, 0 }, - { 81, 40, 144, 198, 200, 66, 1 }, - { 6, 208, 110, 130, 145, 144, 0 }, - { 161, 9, 177, 132, 138, 69, 0 }, }, - { { 9, 4, 240, 102, 157, 166, 0 }, - { 64, 106, 249, 18, 193, 145, 1 }, - { 50, 220, 179, 7, 144, 72, 0 }, - { 196, 193, 164, 79, 171, 1, 0 }, }, - { { 8, 63, 162, 23, 251, 204, 1 }, - { 94, 155, 178, 114, 167, 137, 0 }, - { 153, 239, 244, 34, 254, 8, 0 }, - { 72, 242, 167, 38, 236, 189, 0 }, }, - { { 10, 107, 164, 59, 63, 71, 1 }, - { 158, 166, 154, 118, 196, 143, 0 }, - { 241, 126, 110, 18, 235, 40, 0 }, - { 120, 145, 183, 44, 178, 188, 1 }, }, - { { 9, 147, 68, 160, 208, 252, 0 }, - { 4, 59, 33, 144, 44, 93, 1 }, - { 31, 133, 130, 145, 100, 200, 0 }, - { 221, 26, 4, 194, 110, 16, 0 }, }, - { { 8, 210, 94, 4, 114, 98, 1 }, - { 46, 83, 9, 36, 11, 13, 1 }, - { 163, 39, 16, 61, 37, 136, 0 }, - { 216, 104, 18, 72, 101, 58, 0 }, }, - { { 14, 39, 43, 135, 201, 75, 1 }, - { 231, 22, 186, 193, 167, 136, 0 }, - { 233, 73, 240, 234, 114, 56, 0 }, - { 8, 242, 193, 174, 180, 115, 1 }, }, - { { 15, 42, 254, 150, 218, 227, 0 }, - { 181, 222, 171, 162, 131, 157, 1 }, - { 99, 173, 180, 191, 170, 120, 0 }, - { 220, 224, 162, 234, 189, 214, 1 }, }, - { { 13, 94, 114, 56, 152, 3, 0 }, - { 81, 231, 169, 4, 2, 155, 0 }, - { 96, 12, 142, 39, 61, 88, 0 }, - { 108, 160, 16, 74, 243, 197, 0 }, }, - { { 14, 87, 112, 167, 99, 99, 1 }, - { 207, 119, 153, 228, 133, 8, 1 }, - { 227, 99, 114, 135, 117, 56, 0 }, - { 136, 80, 147, 204, 247, 121, 1 }, }, - { { 13, 106, 169, 220, 27, 63, 0 }, - { 49, 134, 218, 183, 33, 219, 1 }, - { 126, 108, 29, 202, 171, 88, 0 }, - { 237, 194, 118, 173, 176, 198, 0 }, }, - { { 13, 118, 154, 128, 113, 208, 1 }, - { 111, 91, 18, 134, 2, 89, 0 }, - { 133, 199, 0, 172, 183, 88, 0 }, - { 77, 32, 48, 164, 109, 123, 0 }, }, - { { 14, 124, 197, 35, 226, 204, 1 }, - { 207, 187, 35, 119, 160, 4, 0 }, - { 153, 163, 226, 81, 159, 56, 0 }, - { 16, 2, 247, 98, 110, 249, 1 }, }, - { { 15, 139, 106, 20, 246, 21, 0 }, - { 185, 150, 161, 48, 79, 89, 0 }, - { 84, 55, 148, 43, 104, 248, 0 }, - { 77, 121, 6, 66, 180, 206, 1 }, }, - { { 13, 171, 134, 215, 206, 17, 1 }, - { 19, 150, 98, 226, 207, 220, 0 }, - { 196, 57, 245, 176, 234, 216, 0 }, - { 29, 249, 163, 163, 52, 228, 0 }, }, - { { 15, 203, 199, 97, 60, 150, 1 }, - { 139, 170, 73, 87, 78, 221, 0 }, - { 180, 158, 67, 113, 233, 248, 0 }, - { 93, 185, 117, 73, 42, 232, 1 }, }, - { { 14, 236, 63, 51, 174, 17, 1 }, - { 251, 230, 162, 101, 202, 196, 0 }, - { 196, 58, 230, 126, 27, 184, 0 }, - { 17, 169, 211, 34, 179, 239, 1 }, }, - { { 17, 15, 152, 165, 71, 82, 0 }, - { 100, 240, 28, 226, 69, 88, 0 }, - { 37, 113, 82, 140, 248, 68, 0 }, - { 13, 81, 35, 156, 7, 147, 0 }, }, - { { 17, 119, 210, 131, 226, 153, 0 }, - { 72, 93, 39, 230, 166, 88, 0 }, - { 76, 163, 224, 165, 247, 68, 0 }, - { 13, 50, 179, 242, 93, 9, 0 }, }, - { { 17, 151, 181, 190, 81, 161, 0 }, - { 80, 125, 148, 131, 141, 31, 1 }, - { 66, 197, 62, 214, 244, 196, 0 }, - { 252, 88, 224, 148, 223, 5, 0 }, }, - { { 18, 155, 152, 58, 105, 111, 1 }, - { 190, 245, 28, 18, 172, 138, 1 }, - { 251, 75, 46, 12, 236, 164, 0 }, - { 168, 154, 164, 28, 87, 190, 1 }, }, - { { 19, 233, 65, 189, 245, 218, 0 }, - { 156, 184, 63, 197, 109, 83, 0 }, - { 45, 215, 222, 193, 75, 228, 0 }, - { 101, 91, 81, 254, 14, 156, 1 }, }, - { { 22, 13, 83, 212, 169, 87, 0 }, - { 221, 196, 125, 145, 7, 192, 0 }, - { 117, 74, 149, 229, 88, 52, 0 }, - { 1, 240, 68, 223, 17, 221, 1 }, }, - { { 22, 61, 21, 111, 231, 73, 0 }, - { 205, 245, 118, 97, 229, 6, 0 }, - { 73, 115, 251, 84, 94, 52, 0 }, - { 48, 83, 195, 55, 87, 217, 1 }, }, - { { 20, 130, 100, 0, 95, 151, 1 }, - { 3, 28, 157, 48, 72, 205, 0 }, - { 244, 253, 0, 19, 32, 148, 0 }, - { 89, 137, 6, 92, 156, 96, 0 }, }, - { { 21, 130, 161, 143, 42, 56, 1 }, - { 11, 0, 132, 227, 169, 218, 1 }, - { 142, 42, 120, 194, 160, 212, 0 }, - { 173, 202, 227, 144, 128, 104, 0 }, }, - { { 21, 153, 34, 53, 74, 250, 0 }, - { 21, 185, 140, 96, 47, 208, 1 }, - { 47, 169, 86, 34, 76, 212, 0 }, - { 133, 250, 3, 24, 206, 212, 0 }, }, - { { 23, 156, 89, 25, 214, 46, 0 }, - { 241, 209, 45, 113, 104, 19, 1 }, - { 58, 53, 204, 77, 28, 244, 0 }, - { 228, 11, 71, 90, 69, 199, 1 }, }, - { { 21, 165, 87, 175, 25, 244, 1 }, - { 71, 104, 23, 209, 143, 215, 1 }, - { 151, 204, 122, 245, 82, 212, 0 }, - { 245, 248, 197, 244, 11, 113, 0 }, }, - { { 22, 185, 129, 147, 151, 243, 0 }, - { 149, 141, 62, 227, 204, 65, 1 }, - { 103, 244, 228, 192, 206, 180, 0 }, - { 193, 25, 227, 190, 88, 212, 1 }, }, - { { 20, 212, 174, 31, 83, 198, 0 }, - { 117, 25, 156, 118, 139, 7, 0 }, - { 49, 229, 124, 58, 149, 148, 0 }, - { 112, 104, 183, 28, 204, 87, 0 }, }, - { { 22, 222, 188, 166, 177, 46, 1 }, - { 235, 225, 188, 150, 169, 13, 1 }, - { 186, 70, 178, 158, 189, 180, 0 }, - { 216, 74, 180, 158, 195, 235, 1 }, }, - { { 21, 215, 231, 50, 136, 117, 0 }, - { 85, 37, 165, 23, 142, 220, 1 }, - { 87, 8, 166, 115, 245, 212, 0 }, - { 157, 184, 244, 82, 210, 85, 0 }, }, - { { 27, 5, 137, 184, 14, 199, 1 }, - { 246, 46, 12, 179, 68, 146, 0 }, - { 241, 184, 14, 200, 208, 108, 0 }, - { 36, 145, 102, 152, 58, 55, 1 }, }, - { { 24, 76, 13, 17, 16, 216, 1 }, - { 118, 138, 4, 69, 32, 69, 0 }, - { 141, 132, 68, 88, 25, 12, 0 }, - { 81, 2, 81, 16, 40, 183, 0 }, }, - { { 27, 80, 19, 3, 222, 147, 1 }, - { 130, 95, 44, 101, 194, 209, 0 }, - { 228, 189, 224, 100, 5, 108, 0 }, - { 69, 161, 211, 26, 125, 32, 1 }, }, - { { 26, 117, 67, 117, 230, 58, 0 }, - { 216, 51, 111, 101, 103, 64, 1 }, - { 46, 51, 215, 97, 87, 44, 0 }, - { 129, 115, 83, 123, 102, 13, 1 }, }, - { { 25, 139, 44, 109, 101, 206, 0 }, - { 44, 186, 220, 80, 109, 30, 0 }, - { 57, 211, 91, 26, 104, 204, 0 }, - { 60, 91, 5, 29, 174, 154, 0 }, }, - { { 24, 157, 237, 154, 191, 3, 0 }, - { 120, 135, 189, 163, 204, 135, 0 }, - { 96, 126, 172, 219, 220, 140, 0 }, - { 112, 153, 226, 222, 240, 143, 0 }, }, - { { 26, 163, 189, 21, 22, 74, 1 }, - { 182, 66, 142, 99, 109, 13, 0 }, - { 169, 52, 84, 94, 226, 172, 0 }, - { 88, 91, 99, 56, 161, 54, 1 }, }, - { { 24, 191, 221, 217, 136, 80, 1 }, - { 118, 195, 103, 195, 12, 206, 0 }, - { 133, 8, 205, 221, 254, 140, 0 }, - { 57, 152, 97, 243, 97, 183, 0 }, }, - { { 26, 187, 197, 128, 248, 166, 1 }, - { 138, 155, 47, 147, 12, 141, 1 }, - { 178, 143, 128, 209, 238, 172, 0 }, - { 216, 152, 100, 250, 108, 168, 1 }, }, - { { 24, 202, 200, 115, 174, 12, 1 }, - { 58, 162, 101, 118, 232, 136, 0 }, - { 152, 58, 231, 9, 169, 140, 0 }, - { 8, 139, 183, 83, 34, 174, 0 }, }, - { { 26, 251, 85, 224, 195, 153, 1 }, - { 130, 255, 119, 165, 44, 76, 0 }, - { 204, 225, 131, 213, 111, 172, 0 }, - { 25, 26, 82, 247, 127, 160, 1 }, }, - { { 25, 252, 172, 250, 123, 235, 0 }, - { 124, 191, 222, 166, 168, 151, 1 }, - { 107, 239, 47, 154, 159, 204, 0 }, - { 244, 138, 178, 189, 254, 159, 0 }, }, - { { 31, 33, 77, 51, 43, 129, 0 }, - { 185, 46, 23, 97, 132, 148, 0 }, - { 64, 234, 102, 89, 66, 124, 0 }, - { 20, 144, 195, 116, 58, 78, 1 }, }, - { { 29, 104, 97, 97, 80, 45, 1 }, - { 3, 182, 199, 85, 32, 17, 1 }, - { 218, 5, 67, 67, 11, 92, 0 }, - { 196, 2, 85, 113, 182, 224, 0 }, }, - { { 29, 113, 17, 105, 159, 205, 1 }, - { 7, 111, 118, 117, 100, 147, 0 }, - { 217, 252, 203, 68, 71, 92, 0 }, - { 100, 147, 87, 55, 123, 112, 0 }, }, - { { 28, 115, 52, 93, 58, 61, 1 }, - { 27, 71, 198, 116, 37, 207, 1 }, - { 222, 46, 93, 22, 103, 28, 0 }, - { 249, 210, 23, 49, 241, 108, 0 }, }, - { { 30, 167, 7, 22, 61, 22, 1 }, - { 219, 2, 30, 17, 207, 205, 0 }, - { 180, 94, 52, 112, 114, 188, 0 }, - { 89, 249, 196, 60, 32, 109, 1 }, }, - { { 32, 22, 241, 69, 239, 16, 1 }, - { 74, 81, 241, 107, 65, 200, 0 }, - { 132, 123, 209, 71, 180, 2, 0 }, - { 9, 193, 107, 71, 197, 41, 0 }, }, - { { 33, 37, 79, 144, 49, 77, 0 }, - { 124, 4, 19, 153, 38, 21, 0 }, - { 89, 70, 4, 249, 82, 66, 0 }, - { 84, 50, 76, 228, 16, 31, 0 }, }, - { { 35, 45, 115, 235, 220, 62, 1 }, - { 194, 240, 235, 217, 230, 211, 1 }, - { 190, 29, 235, 231, 90, 98, 0 }, - { 229, 179, 205, 235, 135, 161, 1 }, }, - { { 35, 72, 62, 43, 100, 184, 1 }, - { 170, 248, 128, 76, 226, 86, 1 }, - { 142, 147, 106, 62, 9, 98, 0 }, - { 181, 35, 153, 0, 143, 170, 1 }, }, - { { 35, 86, 58, 73, 248, 94, 1 }, - { 238, 81, 232, 92, 34, 219, 0 }, - { 189, 15, 201, 46, 53, 98, 0 }, - { 109, 162, 29, 11, 197, 59, 1 }, }, - { { 34, 138, 184, 17, 107, 194, 0 }, - { 188, 216, 152, 106, 8, 136, 0 }, - { 33, 235, 68, 14, 168, 162, 0 }, - { 8, 136, 43, 12, 141, 158, 1 }, }, - { { 32, 167, 72, 150, 66, 82, 1 }, - { 118, 16, 11, 168, 141, 72, 0 }, - { 165, 33, 52, 137, 114, 130, 0 }, - { 9, 88, 138, 232, 4, 55, 0 }, }, - { { 34, 164, 248, 245, 231, 39, 1 }, - { 250, 116, 251, 250, 73, 0, 1 }, - { 242, 115, 215, 143, 146, 162, 0 }, - { 128, 73, 47, 239, 151, 47, 1 }, }, - { { 33, 239, 227, 53, 220, 27, 1 }, - { 82, 180, 171, 79, 111, 217, 0 }, - { 236, 29, 214, 99, 251, 194, 0 }, - { 77, 251, 121, 106, 150, 165, 0 }, }, - { { 33, 255, 170, 148, 240, 254, 0 }, - { 124, 153, 170, 158, 47, 89, 1 }, - { 63, 135, 148, 170, 255, 194, 0 }, - { 205, 122, 60, 170, 204, 159, 0 }, }, - { { 37, 48, 68, 183, 73, 106, 0 }, - { 21, 49, 27, 200, 161, 148, 1 }, - { 43, 73, 118, 145, 6, 82, 0 }, - { 148, 194, 137, 236, 70, 84, 0 }, }, - { { 38, 55, 142, 202, 57, 200, 0 }, - { 237, 9, 82, 138, 166, 143, 0 }, - { 9, 206, 41, 184, 246, 50, 0 }, - { 120, 178, 168, 165, 72, 91, 1 }, }, - { { 39, 67, 65, 27, 226, 165, 0 }, - { 153, 28, 33, 125, 132, 26, 1 }, - { 82, 163, 236, 65, 97, 114, 0 }, - { 172, 16, 223, 66, 28, 76, 1 }, }, - { { 39, 119, 180, 37, 12, 229, 0 }, - { 197, 109, 130, 94, 69, 156, 1 }, - { 83, 152, 82, 22, 247, 114, 0 }, - { 156, 209, 61, 32, 219, 81, 1 }, }, - { { 36, 140, 97, 44, 211, 5, 0 }, - { 65, 180, 177, 57, 9, 3, 0 }, - { 80, 101, 154, 67, 24, 146, 0 }, - { 96, 72, 78, 70, 150, 193, 0 }, }, - { { 39, 181, 134, 58, 242, 110, 1 }, - { 223, 49, 42, 58, 174, 23, 1 }, - { 187, 39, 174, 48, 214, 242, 0 }, - { 244, 58, 174, 42, 70, 125, 1 }, }, - { { 37, 192, 47, 169, 170, 233, 1 }, - { 47, 44, 160, 237, 42, 150, 1 }, - { 203, 170, 202, 250, 1, 210, 0 }, - { 180, 170, 91, 130, 154, 122, 0 }, }, - { { 37, 193, 244, 243, 24, 172, 1 }, - { 19, 104, 193, 222, 172, 149, 1 }, - { 154, 140, 103, 151, 193, 210, 0 }, - { 212, 154, 189, 193, 139, 100, 0 }, }, - { { 38, 223, 180, 89, 168, 216, 1 }, - { 223, 201, 224, 78, 44, 206, 0 }, - { 141, 138, 205, 22, 253, 178, 0 }, - { 57, 154, 57, 3, 201, 253, 1 }, }, - { { 36, 229, 181, 254, 134, 140, 0 }, - { 81, 104, 226, 191, 237, 6, 0 }, - { 24, 176, 191, 214, 211, 146, 0 }, - { 48, 91, 254, 163, 139, 69, 0 }, }, - { { 36, 250, 147, 106, 49, 84, 1 }, - { 15, 225, 82, 31, 138, 75, 0 }, - { 149, 70, 43, 100, 175, 146, 0 }, - { 105, 40, 252, 37, 67, 248, 0 }, }, - { { 41, 33, 120, 64, 98, 223, 1 }, - { 46, 94, 203, 56, 36, 80, 0 }, - { 253, 163, 1, 15, 66, 74, 0 }, - { 5, 18, 14, 105, 189, 58, 0 }, }, - { { 42, 47, 218, 50, 70, 201, 1 }, - { 246, 254, 3, 42, 230, 8, 0 }, - { 201, 177, 38, 45, 250, 42, 0 }, - { 8, 51, 170, 96, 63, 183, 1 }, }, - { { 40, 62, 63, 42, 103, 4, 0 }, - { 104, 243, 146, 57, 194, 14, 0 }, - { 16, 115, 42, 126, 62, 10, 0 }, - { 56, 33, 206, 36, 231, 139, 0 }, }, - { { 43, 205, 185, 118, 1, 186, 0 }, - { 240, 234, 216, 15, 173, 80, 1 }, - { 46, 192, 55, 78, 217, 234, 0 }, - { 133, 90, 248, 13, 171, 135, 1 }, }, - { { 40, 234, 218, 205, 222, 126, 1 }, - { 38, 210, 107, 254, 107, 203, 1 }, - { 191, 61, 217, 173, 171, 138, 0 }, - { 233, 235, 63, 235, 37, 178, 0 }, }, - { { 44, 77, 140, 242, 231, 58, 1 }, - { 123, 178, 120, 174, 228, 68, 1 }, - { 174, 115, 167, 152, 217, 26, 0 }, - { 145, 19, 186, 143, 38, 239, 0 }, }, - { { 46, 88, 0, 228, 62, 206, 0 }, - { 141, 171, 72, 188, 97, 129, 0 }, - { 57, 190, 19, 128, 13, 58, 0 }, - { 64, 195, 30, 137, 106, 216, 1 }, }, - { { 45, 129, 187, 116, 79, 227, 1 }, - { 55, 126, 216, 43, 79, 144, 1 }, - { 227, 249, 23, 110, 192, 218, 0 }, - { 132, 249, 106, 13, 191, 118, 0 }, }, - { { 44, 145, 67, 129, 237, 29, 0 }, - { 9, 23, 49, 217, 110, 192, 0 }, - { 92, 91, 192, 225, 68, 154, 0 }, - { 1, 187, 77, 198, 116, 72, 0 }, }, - { { 45, 180, 8, 215, 116, 177, 1 }, - { 123, 31, 66, 200, 201, 81, 1 }, - { 198, 151, 117, 136, 22, 218, 0 }, - { 197, 73, 137, 161, 124, 111, 0 }, }, - { { 44, 224, 23, 61, 89, 3, 1 }, - { 19, 118, 26, 77, 11, 135, 0 }, - { 224, 77, 94, 116, 3, 154, 0 }, - { 112, 232, 89, 44, 55, 100, 0 }, }, - { { 48, 9, 23, 86, 236, 208, 1 }, - { 30, 216, 100, 9, 199, 196, 0 }, - { 133, 155, 181, 116, 72, 6, 0 }, - { 17, 241, 200, 19, 13, 188, 0 }, }, - { { 51, 70, 13, 15, 126, 150, 0 }, - { 232, 24, 12, 125, 193, 223, 0 }, - { 52, 191, 120, 88, 49, 102, 0 }, - { 125, 193, 223, 24, 12, 11, 1 }, }, - { { 50, 113, 60, 116, 164, 89, 0 }, - { 188, 101, 230, 12, 101, 68, 0 }, - { 77, 18, 151, 30, 71, 38, 0 }, - { 17, 83, 24, 51, 211, 30, 1 }, }, - { { 48, 167, 239, 179, 52, 231, 1 }, - { 126, 44, 143, 219, 206, 13, 1 }, - { 243, 150, 102, 251, 242, 134, 0 }, - { 216, 57, 237, 248, 154, 63, 0 }, }, - { { 48, 183, 244, 73, 95, 202, 0 }, - { 68, 89, 223, 106, 108, 143, 0 }, - { 41, 253, 73, 23, 246, 134, 0 }, - { 120, 155, 43, 125, 205, 17, 0 }, }, - { { 51, 236, 16, 56, 154, 82, 0 }, - { 212, 224, 46, 44, 8, 211, 0 }, - { 37, 44, 142, 4, 27, 230, 0 }, - { 101, 136, 26, 58, 3, 149, 1 }, }, - { { 53, 106, 133, 153, 65, 91, 0 }, - { 21, 148, 30, 207, 32, 94, 0 }, - { 109, 65, 76, 208, 171, 86, 0 }, - { 61, 2, 121, 188, 20, 212, 0 }, }, - { { 54, 125, 199, 249, 16, 137, 1 }, - { 211, 173, 71, 207, 38, 7, 0 }, - { 200, 132, 79, 241, 223, 54, 0 }, - { 112, 50, 121, 241, 90, 229, 1 }, }, - { { 53, 145, 236, 15, 52, 5, 1 }, - { 43, 5, 133, 90, 205, 23, 0 }, - { 208, 22, 120, 27, 196, 214, 0 }, - { 116, 89, 173, 80, 208, 106, 0 }, }, - { { 58, 31, 122, 71, 185, 141, 0 }, - { 232, 207, 245, 88, 167, 137, 0 }, - { 88, 206, 241, 47, 124, 46, 0 }, - { 72, 242, 141, 87, 249, 139, 1 }, }, - { { 58, 86, 77, 209, 166, 203, 1 }, - { 254, 15, 109, 237, 96, 12, 0 }, - { 233, 178, 197, 217, 53, 46, 0 }, - { 24, 3, 91, 219, 120, 63, 1 }, }, - { { 57, 105, 99, 159, 57, 176, 1 }, - { 26, 138, 151, 205, 135, 211, 1 }, - { 134, 206, 124, 227, 75, 78, 0 }, - { 229, 240, 217, 244, 168, 172, 0 }, }, - { { 59, 96, 103, 17, 227, 243, 0 }, - { 156, 30, 191, 109, 2, 84, 1 }, - { 103, 227, 196, 115, 3, 110, 0 }, - { 149, 32, 91, 126, 188, 28, 1 }, }, - { { 57, 204, 214, 65, 214, 205, 1 }, - { 70, 222, 101, 126, 106, 21, 0 }, - { 217, 181, 193, 53, 153, 206, 0 }, - { 84, 43, 63, 83, 61, 177, 0 }, }, - { { 60, 51, 21, 240, 128, 231, 0 }, - { 21, 111, 110, 153, 4, 12, 1 }, - { 115, 128, 135, 212, 102, 30, 0 }, - { 152, 16, 76, 187, 123, 84, 0 }, }, - { { 60, 51, 197, 75, 3, 115, 1 }, - { 7, 7, 95, 107, 132, 78, 1 }, - { 231, 96, 105, 81, 230, 30, 0 }, - { 185, 16, 235, 125, 112, 112, 0 }, }, - { { 61, 98, 186, 75, 99, 200, 0 }, - { 45, 90, 214, 110, 162, 26, 0 }, - { 9, 227, 105, 46, 163, 94, 0 }, - { 44, 34, 187, 53, 173, 90, 0 }, }, - { { 61, 117, 9, 162, 109, 95, 1 }, - { 111, 55, 30, 157, 228, 208, 0 }, - { 253, 91, 34, 200, 87, 94, 0 }, - { 5, 147, 220, 188, 118, 123, 0 }, }, - { { 63, 152, 179, 12, 62, 51, 1 }, - { 139, 199, 140, 43, 75, 211, 1 }, - { 230, 62, 24, 102, 140, 254, 0 }, - { 229, 233, 106, 24, 241, 232, 1 }, }, - { { 62, 205, 175, 189, 212, 43, 0 }, - { 241, 182, 172, 207, 111, 7, 1 }, - { 106, 21, 222, 250, 217, 190, 0 }, - { 240, 123, 121, 154, 182, 199, 1 }, }, - { { 63, 213, 180, 83, 119, 231, 1 }, - { 223, 95, 220, 126, 204, 21, 1 }, - { 243, 247, 101, 22, 213, 254, 0 }, - { 212, 25, 191, 29, 253, 125, 1 }, }, - { { 60, 233, 181, 48, 105, 180, 0 }, - { 25, 250, 150, 31, 12, 196, 1 }, - { 22, 203, 6, 86, 203, 158, 0 }, - { 145, 152, 124, 52, 175, 204, 0 }, }, - { { 64, 3, 93, 254, 66, 40, 1 }, - { 50, 112, 65, 161, 181, 14, 1 }, - { 138, 33, 63, 221, 96, 1, 0 }, - { 184, 86, 194, 193, 7, 38, 0 }, }, - { { 67, 106, 171, 8, 205, 110, 1 }, - { 166, 144, 186, 23, 114, 154, 1 }, - { 187, 89, 136, 106, 171, 97, 0 }, - { 172, 167, 116, 46, 132, 178, 1 }, }, - { { 67, 113, 229, 169, 206, 30, 1 }, - { 130, 49, 171, 247, 116, 214, 0 }, - { 188, 57, 202, 211, 199, 97, 0 }, - { 53, 151, 119, 234, 198, 32, 1 }, }, - { { 65, 168, 233, 191, 52, 145, 0 }, - { 56, 172, 131, 195, 217, 83, 0 }, - { 68, 150, 126, 203, 138, 193, 0 }, - { 101, 77, 225, 224, 154, 142, 0 }, }, - { { 65, 202, 197, 166, 227, 19, 0 }, - { 8, 180, 57, 167, 153, 92, 0 }, - { 100, 99, 178, 209, 169, 193, 0 }, - { 29, 76, 242, 206, 22, 136, 0 }, }, - { { 66, 247, 60, 189, 2, 52, 0 }, - { 240, 97, 130, 244, 29, 78, 1 }, - { 22, 32, 94, 158, 119, 161, 0 }, - { 185, 92, 23, 160, 195, 7, 1 }, }, - { { 68, 39, 29, 179, 182, 219, 1 }, - { 127, 108, 42, 225, 244, 77, 0 }, - { 237, 182, 230, 220, 114, 17, 0 }, - { 89, 23, 195, 170, 27, 127, 0 }, }, - { { 68, 76, 27, 64, 24, 97, 1 }, - { 103, 196, 64, 5, 18, 129, 1 }, - { 195, 12, 1, 108, 25, 17, 0 }, - { 192, 164, 80, 1, 17, 243, 0 }, }, - { { 70, 74, 152, 175, 201, 220, 0 }, - { 165, 248, 48, 214, 177, 202, 0 }, - { 29, 201, 250, 140, 169, 49, 0 }, - { 41, 198, 181, 134, 15, 210, 1 }, }, - { { 70, 131, 6, 249, 199, 224, 1 }, - { 151, 56, 112, 224, 94, 14, 1 }, - { 131, 241, 207, 176, 96, 177, 0 }, - { 184, 61, 3, 135, 14, 116, 1 }, }, - { { 68, 140, 202, 221, 114, 250, 1 }, - { 127, 152, 73, 226, 59, 67, 1 }, - { 175, 167, 93, 169, 152, 145, 0 }, - { 225, 110, 35, 201, 12, 255, 0 }, }, - { { 70, 158, 58, 59, 116, 65, 1 }, - { 255, 245, 128, 64, 218, 11, 0 }, - { 193, 23, 110, 46, 60, 177, 0 }, - { 104, 45, 129, 0, 215, 255, 1 }, }, - { { 73, 31, 143, 197, 190, 227, 0 }, - { 108, 143, 104, 227, 87, 157, 1 }, - { 99, 190, 209, 248, 252, 73, 0 }, - { 220, 245, 99, 139, 120, 155, 0 }, }, - { { 73, 75, 34, 203, 253, 35, 1 }, - { 10, 150, 248, 196, 214, 155, 1 }, - { 226, 95, 233, 162, 105, 73, 0 }, - { 236, 181, 145, 143, 180, 168, 0 }, }, - { { 73, 91, 8, 111, 19, 202, 1 }, - { 38, 171, 88, 100, 181, 27, 0 }, - { 169, 228, 123, 8, 109, 73, 0 }, - { 108, 86, 147, 13, 106, 178, 0 }, }, - { { 74, 177, 68, 70, 10, 33, 1 }, - { 130, 7, 67, 32, 157, 132, 1 }, - { 194, 40, 49, 17, 70, 169, 0 }, - { 144, 220, 130, 97, 112, 32, 1 }, }, - { { 75, 195, 24, 213, 28, 201, 0 }, - { 180, 78, 64, 196, 125, 153, 0 }, - { 73, 156, 85, 140, 97, 233, 0 }, - { 76, 223, 17, 129, 57, 22, 1 }, }, - { { 76, 116, 244, 77, 39, 23, 1 }, - { 75, 71, 219, 118, 81, 70, 0 }, - { 244, 114, 89, 23, 151, 25, 0 }, - { 49, 69, 55, 109, 241, 105, 0 }, }, - { { 78, 157, 120, 2, 250, 136, 1 }, - { 235, 219, 161, 32, 188, 129, 0 }, - { 136, 175, 160, 15, 92, 185, 0 }, - { 64, 158, 130, 66, 237, 235, 1 }, }, - { { 79, 168, 131, 36, 88, 72, 0 }, - { 133, 178, 2, 3, 59, 145, 0 }, - { 9, 13, 18, 96, 138, 249, 0 }, - { 68, 238, 96, 32, 38, 208, 1 }, }, - { { 77, 204, 62, 66, 47, 44, 0 }, - { 105, 194, 208, 52, 250, 148, 1 }, - { 26, 122, 33, 62, 25, 217, 0 }, - { 148, 175, 150, 5, 161, 203, 0 }, }, - { { 77, 205, 182, 47, 105, 192, 1 }, - { 79, 250, 144, 70, 159, 150, 0 }, - { 129, 203, 122, 54, 217, 217, 0 }, - { 52, 252, 177, 4, 175, 249, 0 }, }, - { { 76, 252, 81, 246, 35, 2, 0 }, - { 89, 227, 91, 165, 153, 0, 0 }, - { 32, 98, 55, 197, 31, 153, 0 }, - { 0, 76, 210, 237, 99, 205, 0 }, }, - { { 77, 248, 189, 181, 78, 179, 0 }, - { 49, 255, 138, 231, 89, 212, 1 }, - { 102, 185, 86, 222, 143, 217, 0 }, - { 149, 205, 115, 168, 255, 198, 0 }, }, - { { 78, 254, 206, 197, 81, 2, 1 }, - { 227, 147, 91, 198, 27, 13, 0 }, - { 160, 69, 81, 185, 191, 185, 0 }, - { 88, 108, 49, 237, 100, 227, 1 }, }, - { { 83, 45, 244, 148, 190, 11, 1 }, - { 218, 196, 175, 162, 117, 149, 0 }, - { 232, 62, 148, 151, 218, 101, 0 }, - { 84, 215, 34, 250, 145, 173, 1 }, }, - { { 83, 55, 48, 177, 237, 76, 1 }, - { 222, 113, 182, 208, 116, 152, 0 }, - { 153, 91, 198, 134, 118, 101, 0 }, - { 12, 151, 5, 182, 199, 61, 1 }, }, - { { 81, 138, 115, 76, 78, 26, 0 }, - { 0, 208, 205, 33, 123, 218, 0 }, - { 44, 57, 25, 103, 40, 197, 0 }, - { 45, 239, 66, 89, 133, 128, 0 }, }, - { { 81, 173, 41, 83, 250, 51, 0 }, - { 120, 148, 238, 97, 156, 209, 1 }, - { 102, 47, 229, 74, 90, 197, 0 }, - { 197, 156, 195, 59, 148, 143, 0 }, }, - { { 80, 244, 139, 181, 87, 23, 1 }, - { 114, 53, 30, 247, 91, 65, 0 }, - { 244, 117, 86, 232, 151, 133, 0 }, - { 65, 109, 119, 188, 86, 39, 0 }, }, - { { 83, 242, 216, 169, 125, 63, 0 }, - { 168, 117, 31, 214, 120, 219, 1 }, - { 126, 95, 74, 141, 167, 229, 0 }, - { 237, 143, 53, 252, 87, 10, 1 }, }, - { { 87, 46, 51, 92, 65, 229, 1 }, - { 215, 220, 214, 17, 19, 26, 1 }, - { 211, 193, 29, 102, 58, 117, 0 }, - { 172, 100, 68, 53, 157, 245, 1 }, }, - { { 85, 97, 138, 153, 243, 43, 1 }, - { 59, 20, 62, 230, 54, 19, 1 }, - { 234, 103, 204, 168, 195, 85, 0 }, - { 228, 54, 51, 190, 20, 110, 0 }, }, - { { 84, 114, 176, 221, 66, 144, 1 }, - { 19, 89, 198, 230, 17, 74, 0 }, - { 132, 161, 93, 134, 167, 21, 0 }, - { 41, 68, 51, 177, 205, 100, 0 }, }, - { { 87, 120, 188, 172, 178, 210, 1 }, - { 175, 233, 174, 166, 17, 87, 0 }, - { 165, 166, 154, 158, 143, 117, 0 }, - { 117, 68, 50, 186, 203, 250, 1 }, }, - { { 85, 178, 111, 85, 255, 238, 1 }, - { 63, 25, 255, 113, 123, 157, 1 }, - { 187, 255, 213, 123, 38, 213, 0 }, - { 220, 239, 71, 127, 204, 126, 0 }, }, - { { 87, 215, 193, 128, 15, 12, 1 }, - { 195, 1, 21, 183, 124, 152, 0 }, - { 152, 120, 0, 193, 245, 245, 0 }, - { 12, 159, 118, 212, 64, 97, 1 }, }, - { { 85, 243, 37, 159, 247, 53, 0 }, - { 25, 21, 182, 245, 221, 95, 1 }, - { 86, 119, 252, 210, 103, 213, 0 }, - { 253, 93, 215, 182, 212, 76, 0 }, }, - { { 90, 0, 169, 237, 210, 158, 0 }, - { 160, 58, 236, 243, 49, 67, 0 }, - { 60, 165, 219, 202, 128, 45, 0 }, - { 97, 70, 103, 155, 174, 2, 1 }, }, - { { 89, 39, 61, 104, 89, 78, 1 }, - { 102, 114, 222, 17, 52, 159, 0 }, - { 185, 77, 11, 94, 114, 77, 0 }, - { 124, 150, 68, 61, 167, 51, 0 }, }, - { { 89, 73, 179, 211, 30, 79, 0 }, - { 20, 198, 204, 247, 246, 145, 0 }, - { 121, 60, 101, 230, 201, 77, 0 }, - { 68, 183, 247, 153, 177, 148, 0 }, }, - { { 91, 92, 30, 37, 185, 241, 1 }, - { 238, 239, 52, 68, 19, 213, 1 }, - { 199, 206, 210, 60, 29, 109, 0 }, - { 213, 228, 17, 22, 123, 187, 1 }, }, - { { 89, 81, 133, 145, 139, 249, 0 }, - { 20, 15, 52, 231, 52, 212, 1 }, - { 79, 232, 196, 208, 197, 77, 0 }, - { 149, 150, 115, 150, 120, 20, 0 }, }, - { { 91, 156, 92, 115, 202, 248, 1 }, - { 246, 251, 101, 96, 184, 212, 1 }, - { 143, 169, 231, 29, 28, 237, 0 }, - { 149, 142, 131, 83, 111, 183, 1 }, }, - { { 90, 148, 209, 233, 34, 152, 1 }, - { 202, 107, 69, 227, 56, 66, 0 }, - { 140, 162, 75, 197, 148, 173, 0 }, - { 33, 14, 99, 209, 107, 41, 1 }, }, - { { 88, 170, 155, 107, 27, 52, 0 }, - { 32, 226, 86, 115, 154, 203, 1 }, - { 22, 108, 107, 108, 170, 141, 0 }, - { 233, 172, 231, 53, 35, 130, 0 }, }, - { { 90, 202, 163, 22, 250, 87, 1 }, - { 158, 150, 172, 55, 155, 201, 0 }, - { 245, 47, 180, 98, 169, 173, 0 }, - { 73, 236, 246, 26, 180, 188, 1 }, }, - { { 88, 216, 190, 3, 241, 57, 0 }, - { 40, 215, 180, 70, 186, 69, 1 }, - { 78, 71, 224, 62, 141, 141, 0 }, - { 209, 46, 177, 22, 245, 138, 0 }, }, - { { 90, 253, 27, 96, 216, 252, 0 }, - { 228, 251, 102, 21, 62, 193, 1 }, - { 31, 141, 131, 108, 95, 173, 0 }, - { 193, 190, 84, 51, 111, 147, 1 }, }, - { { 93, 40, 12, 244, 83, 170, 1 }, - { 51, 186, 94, 160, 49, 21, 1 }, - { 170, 229, 23, 152, 10, 93, 0 }, - { 212, 70, 2, 189, 46, 230, 0 }, }, - { { 94, 52, 215, 24, 145, 91, 0 }, - { 213, 71, 63, 3, 50, 71, 0 }, - { 109, 68, 140, 117, 150, 61, 0 }, - { 113, 38, 96, 126, 113, 85, 1 }, }, - { { 94, 140, 231, 129, 51, 103, 0 }, - { 205, 134, 157, 243, 26, 5, 1 }, - { 115, 102, 64, 243, 152, 189, 0 }, - { 208, 44, 103, 220, 176, 217, 1 }, }, - { { 92, 240, 52, 170, 32, 5, 0 }, - { 9, 103, 134, 148, 152, 6, 0 }, - { 80, 2, 42, 150, 7, 157, 0 }, - { 48, 12, 148, 176, 243, 72, 0 }, }, - { { 99, 47, 8, 198, 1, 227, 0 }, - { 228, 140, 90, 136, 149, 24, 1 }, - { 99, 192, 49, 136, 122, 99, 0 }, - { 140, 84, 136, 173, 24, 147, 1 }, }, - { { 97, 34, 240, 131, 154, 222, 0 }, - { 4, 72, 171, 250, 176, 217, 0 }, - { 61, 172, 224, 135, 162, 67, 0 }, - { 77, 134, 175, 234, 137, 16, 0 }, }, - { { 97, 64, 6, 28, 66, 91, 1 }, - { 22, 20, 8, 44, 51, 86, 0 }, - { 237, 33, 28, 48, 1, 67, 0 }, - { 53, 102, 26, 8, 20, 52, 0 }, }, - { { 99, 104, 252, 248, 31, 16, 0 }, - { 176, 224, 211, 174, 80, 215, 0 }, - { 4, 124, 15, 159, 139, 99, 0 }, - { 117, 133, 58, 229, 131, 134, 1 }, }, - { { 98, 163, 80, 160, 5, 206, 1 }, - { 134, 104, 27, 152, 124, 8, 0 }, - { 185, 208, 2, 133, 98, 163, 0 }, - { 8, 31, 12, 236, 11, 48, 1 }, }, - { { 96, 207, 8, 137, 207, 134, 1 }, - { 98, 152, 56, 252, 92, 138, 0 }, - { 176, 249, 200, 136, 121, 131, 0 }, - { 40, 157, 31, 142, 12, 163, 0 }, }, - { { 100, 31, 102, 147, 38, 250, 0 }, - { 93, 137, 137, 232, 246, 76, 1 }, - { 47, 178, 100, 179, 124, 19, 0 }, - { 153, 55, 139, 200, 200, 221, 0 }, }, - { { 103, 84, 36, 66, 155, 194, 1 }, - { 199, 9, 248, 44, 144, 149, 0 }, - { 161, 236, 161, 18, 21, 115, 0 }, - { 84, 132, 154, 15, 200, 113, 1 }, }, - { { 103, 94, 109, 245, 161, 167, 0 }, - { 249, 173, 249, 221, 17, 28, 1 }, - { 114, 194, 215, 219, 61, 115, 0 }, - { 156, 68, 93, 207, 218, 207, 1 }, }, - { { 100, 112, 128, 63, 112, 33, 0 }, - { 25, 53, 2, 78, 145, 3, 1 }, - { 66, 7, 126, 0, 135, 19, 0 }, - { 224, 68, 185, 32, 86, 76, 0 }, }, - { { 101, 113, 143, 62, 145, 182, 1 }, - { 51, 41, 58, 31, 151, 87, 1 }, - { 182, 196, 190, 120, 199, 83, 0 }, - { 245, 116, 252, 46, 74, 102, 0 }, }, - { { 103, 153, 192, 236, 194, 232, 1 }, - { 135, 185, 97, 170, 61, 18, 1 }, - { 139, 161, 155, 129, 204, 243, 0 }, - { 164, 94, 42, 195, 78, 240, 1 }, }, - { { 102, 181, 191, 1, 79, 214, 0 }, - { 229, 89, 154, 123, 94, 196, 0 }, - { 53, 249, 64, 126, 214, 179, 0 }, - { 17, 189, 111, 44, 205, 83, 1 }, }, - { { 103, 209, 57, 91, 135, 184, 0 }, - { 177, 73, 240, 109, 252, 82, 1 }, - { 14, 240, 237, 78, 69, 243, 0 }, - { 165, 31, 219, 7, 201, 70, 1 }, }, - { { 101, 214, 185, 83, 116, 18, 0 }, - { 121, 81, 200, 79, 216, 89, 0 }, - { 36, 23, 101, 78, 181, 211, 0 }, - { 77, 13, 249, 9, 197, 79, 0 }, }, - { { 107, 53, 69, 174, 190, 183, 0 }, - { 200, 47, 43, 185, 213, 215, 1 }, - { 118, 190, 186, 209, 86, 107, 0 }, - { 245, 213, 206, 234, 122, 9, 1 }, }, - { { 106, 85, 31, 116, 156, 15, 1 }, - { 242, 103, 104, 29, 119, 133, 0 }, - { 248, 28, 151, 124, 85, 43, 0 }, - { 80, 247, 92, 11, 115, 39, 1 }, }, - { { 105, 214, 240, 35, 1, 52, 0 }, - { 64, 99, 145, 94, 152, 88, 1 }, - { 22, 64, 98, 7, 181, 203, 0 }, - { 141, 12, 189, 68, 227, 1, 0 }, }, - { { 107, 214, 206, 166, 84, 232, 1 }, - { 230, 59, 1, 142, 251, 29, 1 }, - { 139, 149, 50, 185, 181, 235, 0 }, - { 220, 111, 184, 192, 110, 51, 1 }, }, - { { 108, 83, 126, 168, 65, 235, 0 }, - { 37, 127, 153, 140, 54, 14, 1 }, - { 107, 193, 10, 191, 101, 27, 0 }, - { 184, 54, 24, 204, 255, 82, 0 }, }, - { { 110, 99, 164, 20, 64, 36, 1 }, - { 147, 18, 130, 30, 21, 12, 1 }, - { 146, 1, 20, 18, 227, 59, 0 }, - { 152, 84, 60, 32, 164, 100, 1 }, }, - { { 108, 120, 204, 22, 132, 88, 1 }, - { 55, 131, 35, 14, 241, 68, 0 }, - { 141, 16, 180, 25, 143, 27, 0 }, - { 17, 71, 184, 98, 96, 246, 0 }, }, - { { 108, 206, 226, 122, 51, 47, 0 }, - { 89, 166, 217, 62, 186, 11, 1 }, - { 122, 102, 47, 35, 185, 155, 0 }, - { 232, 46, 190, 77, 178, 205, 0 }, }, - { { 111, 226, 98, 8, 71, 155, 0 }, - { 129, 30, 155, 44, 122, 90, 0 }, - { 108, 241, 8, 35, 35, 251, 0 }, - { 45, 47, 26, 108, 188, 64, 1 }, }, - { { 113, 2, 15, 225, 198, 243, 0 }, - { 36, 60, 108, 233, 82, 92, 1 }, - { 103, 177, 195, 248, 32, 71, 0 }, - { 157, 37, 75, 155, 30, 18, 0 }, }, - { { 113, 1, 66, 223, 219, 44, 0 }, - { 16, 16, 117, 248, 183, 147, 1 }, - { 26, 109, 253, 161, 64, 71, 0 }, - { 228, 246, 143, 215, 4, 4, 0 }, }, - { { 114, 10, 186, 206, 130, 96, 0 }, - { 164, 192, 228, 170, 147, 10, 1 }, - { 3, 32, 185, 174, 168, 39, 0 }, - { 168, 100, 170, 147, 129, 146, 1 }, }, - { { 114, 76, 255, 13, 215, 153, 0 }, - { 224, 220, 181, 111, 115, 71, 0 }, - { 76, 245, 216, 127, 153, 39, 0 }, - { 113, 103, 123, 86, 157, 131, 1 }, }, - { { 112, 148, 200, 167, 175, 228, 0 }, - { 108, 41, 53, 250, 217, 128, 1 }, - { 19, 250, 242, 137, 148, 135, 0 }, - { 128, 205, 175, 214, 74, 27, 0 }, }, - { { 115, 219, 92, 88, 31, 37, 1 }, - { 178, 197, 85, 60, 92, 159, 1 }, - { 210, 124, 13, 29, 109, 231, 0 }, - { 252, 157, 30, 85, 81, 166, 1 }, }, - { { 113, 220, 240, 226, 63, 213, 1 }, - { 78, 237, 213, 190, 216, 209, 0 }, - { 213, 254, 35, 135, 157, 199, 0 }, - { 69, 141, 190, 213, 219, 185, 0 }, }, - { { 115, 240, 217, 222, 113, 35, 1 }, - { 186, 85, 95, 143, 153, 19, 1 }, - { 226, 71, 61, 205, 135, 231, 0 }, - { 228, 76, 248, 253, 85, 46, 1 }, }, - { { 117, 16, 178, 106, 60, 109, 0 }, - { 13, 101, 196, 26, 242, 147, 1 }, - { 91, 30, 43, 38, 132, 87, 0 }, - { 228, 167, 172, 17, 211, 88, 0 }, }, - { { 117, 125, 232, 146, 6, 63, 0 }, - { 113, 133, 143, 190, 244, 80, 1 }, - { 126, 48, 36, 139, 223, 87, 0 }, - { 133, 23, 190, 248, 208, 199, 0 }, }, - { { 116, 159, 153, 133, 17, 253, 0 }, - { 101, 205, 20, 219, 61, 73, 1 }, - { 95, 196, 80, 204, 252, 151, 0 }, - { 201, 94, 109, 148, 89, 211, 0 }, }, - { { 122, 14, 219, 24, 154, 237, 0 }, - { 244, 206, 37, 59, 50, 139, 1 }, - { 91, 172, 140, 109, 184, 47, 0 }, - { 232, 166, 110, 82, 57, 151, 1 }, }, - { { 121, 22, 143, 58, 72, 165, 1 }, - { 114, 63, 4, 27, 146, 158, 1 }, - { 210, 137, 46, 120, 180, 79, 0 }, - { 188, 164, 236, 16, 126, 39, 0 }, }, - { { 121, 188, 129, 6, 112, 53, 0 }, - { 72, 151, 6, 27, 153, 81, 1 }, - { 86, 7, 48, 64, 158, 207, 0 }, - { 197, 76, 236, 48, 116, 137, 0 }, }, - { { 125, 77, 194, 163, 143, 129, 1 }, - { 67, 174, 53, 238, 214, 144, 0 }, - { 192, 248, 226, 161, 217, 95, 0 }, - { 4, 181, 187, 214, 58, 225, 0 }, }, - { { 124, 135, 26, 99, 181, 78, 0 }, - { 109, 98, 124, 88, 254, 9, 0 }, - { 57, 86, 227, 44, 112, 159, 0 }, - { 72, 63, 141, 31, 35, 91, 0 }, }, - { { 127, 162, 123, 214, 2, 146, 1 }, - { 179, 74, 207, 169, 155, 88, 0 }, - { 164, 160, 53, 239, 34, 255, 0 }, - { 13, 108, 202, 249, 169, 102, 1 }, }, - { { 125, 171, 223, 59, 221, 197, 1 }, - { 55, 254, 55, 91, 222, 159, 0 }, - { 209, 221, 238, 125, 234, 223, 0 }, - { 124, 189, 237, 118, 63, 246, 0 }, }, - { { 125, 187, 65, 173, 85, 124, 1 }, - { 7, 179, 23, 217, 125, 91, 1 }, - { 159, 85, 90, 193, 110, 223, 0 }, - { 237, 95, 77, 244, 102, 240, 0 }, }, - { { 131, 4, 66, 138, 233, 199, 0 }, - { 204, 28, 57, 144, 130, 178, 0 }, - { 113, 203, 168, 161, 16, 96, 1 }, - { 38, 160, 132, 206, 28, 25, 1 }, }, - { { 131, 43, 42, 216, 218, 107, 1 }, - { 182, 148, 234, 160, 38, 187, 1 }, - { 235, 45, 141, 170, 106, 96, 1 }, - { 238, 178, 2, 171, 148, 182, 1 }, }, - { { 129, 42, 82, 146, 228, 127, 0 }, - { 28, 212, 43, 144, 226, 120, 1 }, - { 127, 19, 164, 165, 42, 64, 1 }, - { 143, 35, 132, 234, 21, 156, 0 }, }, - { { 129, 56, 22, 164, 149, 242, 0 }, - { 4, 233, 58, 128, 67, 117, 1 }, - { 39, 212, 146, 180, 14, 64, 1 }, - { 215, 97, 0, 174, 75, 144, 0 }, }, - { { 129, 100, 221, 191, 156, 110, 1 }, - { 118, 96, 43, 215, 225, 183, 1 }, - { 187, 28, 254, 221, 147, 64, 1 }, - { 246, 195, 245, 234, 3, 55, 0 }, }, - { { 129, 123, 93, 252, 187, 253, 1 }, - { 62, 237, 115, 181, 37, 255, 1 }, - { 223, 238, 159, 221, 111, 64, 1 }, - { 255, 210, 86, 231, 91, 190, 0 }, }, - { { 130, 178, 225, 68, 80, 78, 0 }, - { 132, 17, 203, 19, 41, 41, 0 }, - { 57, 5, 17, 67, 166, 160, 1 }, - { 74, 74, 100, 105, 196, 16, 1 }, }, - { { 128, 247, 188, 97, 25, 21, 0 }, - { 96, 101, 210, 86, 12, 237, 0 }, - { 84, 76, 67, 30, 247, 128, 1 }, - { 91, 152, 53, 37, 211, 3, 0 }, }, - { { 135, 52, 106, 165, 125, 200, 0 }, - { 237, 57, 147, 192, 99, 177, 0 }, - { 9, 223, 82, 171, 22, 112, 1 }, - { 70, 227, 1, 228, 206, 91, 1 }, }, - { { 132, 140, 23, 210, 149, 239, 1 }, - { 87, 204, 120, 145, 234, 37, 1 }, - { 251, 212, 165, 244, 24, 144, 1 }, - { 210, 43, 196, 143, 25, 245, 0 }, }, - { { 132, 150, 182, 186, 170, 44, 0 }, - { 89, 97, 160, 178, 170, 174, 1 }, - { 26, 42, 174, 182, 180, 144, 1 }, - { 186, 170, 166, 130, 195, 77, 0 }, }, - { { 135, 156, 185, 242, 219, 180, 0 }, - { 241, 249, 240, 179, 136, 241, 1 }, - { 22, 237, 167, 206, 156, 240, 1 }, - { 199, 136, 230, 135, 207, 199, 1 }, }, - { { 132, 196, 118, 79, 240, 21, 1 }, - { 75, 84, 225, 84, 139, 103, 0 }, - { 212, 7, 249, 55, 17, 144, 1 }, - { 115, 104, 149, 67, 149, 105, 0 }, }, - { { 135, 196, 142, 129, 238, 18, 1 }, - { 235, 16, 40, 230, 74, 244, 0 }, - { 164, 59, 192, 184, 145, 240, 1 }, - { 23, 169, 51, 138, 4, 107, 1 }, }, - { { 133, 225, 245, 45, 64, 89, 1 }, - { 7, 116, 131, 71, 45, 118, 0 }, - { 205, 1, 90, 87, 195, 208, 1 }, - { 55, 90, 113, 96, 151, 112, 0 }, }, - { { 137, 14, 35, 66, 210, 193, 1 }, - { 70, 158, 224, 33, 130, 57, 0 }, - { 193, 165, 161, 98, 56, 72, 1 }, - { 78, 32, 194, 3, 188, 177, 0 }, }, - { { 138, 56, 218, 197, 237, 134, 1 }, - { 170, 219, 123, 210, 67, 160, 0 }, - { 176, 219, 209, 173, 142, 40, 1 }, - { 2, 225, 37, 239, 109, 170, 1 }, }, - { { 138, 149, 152, 168, 110, 21, 0 }, - { 232, 119, 0, 178, 76, 226, 0 }, - { 84, 59, 10, 140, 212, 168, 1 }, - { 35, 153, 38, 128, 119, 11, 1 }, }, - { { 141, 29, 47, 20, 46, 123, 1 }, - { 127, 135, 136, 33, 103, 244, 1 }, - { 239, 58, 20, 122, 92, 88, 1 }, - { 151, 243, 66, 8, 240, 255, 0 }, }, - { { 141, 111, 99, 41, 14, 170, 1 }, - { 67, 170, 139, 101, 102, 186, 1 }, - { 170, 184, 74, 99, 123, 88, 1 }, - { 174, 179, 83, 104, 170, 225, 0 }, }, - { { 142, 178, 140, 83, 176, 124, 1 }, - { 191, 3, 98, 82, 168, 109, 1 }, - { 159, 6, 229, 24, 166, 184, 1 }, - { 219, 10, 165, 35, 96, 126, 1 }, }, - { { 142, 222, 235, 43, 219, 30, 1 }, - { 227, 179, 185, 119, 170, 235, 0 }, - { 188, 109, 234, 107, 189, 184, 1 }, - { 107, 170, 247, 78, 230, 227, 1 }, }, - { { 141, 245, 6, 243, 158, 250, 0 }, - { 85, 43, 106, 228, 238, 245, 1 }, - { 47, 188, 231, 176, 87, 216, 1 }, - { 215, 187, 147, 171, 106, 85, 0 }, }, - { { 144, 23, 149, 47, 152, 143, 0 }, - { 64, 109, 44, 83, 165, 175, 0 }, - { 120, 140, 250, 84, 244, 4, 1 }, - { 122, 210, 229, 26, 91, 1, 0 }, }, - { { 145, 35, 7, 27, 254, 232, 0 }, - { 28, 24, 38, 97, 230, 191, 1 }, - { 11, 191, 236, 112, 98, 68, 1 }, - { 254, 179, 195, 50, 12, 28, 0 }, }, - { { 144, 37, 85, 177, 206, 2, 1 }, - { 82, 112, 47, 225, 68, 164, 0 }, - { 160, 57, 198, 213, 82, 4, 1 }, - { 18, 145, 67, 250, 7, 37, 0 }, }, - { { 144, 40, 188, 66, 77, 232, 0 }, - { 36, 216, 214, 2, 224, 164, 1 }, - { 11, 217, 33, 30, 138, 4, 1 }, - { 146, 131, 160, 53, 141, 146, 0 }, }, - { { 144, 57, 208, 138, 32, 54, 1 }, - { 10, 193, 15, 146, 132, 98, 1 }, - { 182, 2, 40, 133, 206, 4, 1 }, - { 163, 16, 164, 248, 65, 168, 0 }, }, - { { 147, 79, 149, 211, 130, 90, 0 }, - { 212, 192, 108, 231, 164, 124, 0 }, - { 45, 32, 229, 212, 249, 100, 1 }, - { 31, 18, 243, 155, 1, 149, 1 }, }, - { { 146, 164, 210, 234, 226, 74, 0 }, - { 204, 112, 111, 162, 170, 34, 0 }, - { 41, 35, 171, 165, 146, 164, 1 }, - { 34, 42, 162, 251, 7, 25, 1 }, }, - { { 145, 180, 112, 9, 138, 92, 1 }, - { 70, 65, 167, 112, 40, 242, 0 }, - { 157, 40, 200, 7, 22, 196, 1 }, - { 39, 138, 7, 114, 193, 49, 0 }, }, - { { 147, 222, 25, 219, 41, 219, 1 }, - { 254, 205, 92, 197, 168, 250, 0 }, - { 237, 202, 109, 204, 61, 228, 1 }, - { 47, 138, 209, 157, 89, 191, 1 }, }, - { { 145, 229, 150, 87, 118, 92, 0 }, - { 92, 80, 70, 118, 239, 117, 0 }, - { 29, 55, 117, 52, 211, 196, 1 }, - { 87, 123, 183, 49, 5, 29, 0 }, }, - { { 146, 236, 144, 218, 63, 135, 0 }, - { 216, 204, 94, 182, 200, 163, 0 }, - { 112, 254, 45, 132, 155, 164, 1 }, - { 98, 137, 182, 189, 25, 141, 1 }, }, - { { 146, 252, 112, 168, 201, 176, 0 }, - { 192, 249, 183, 132, 8, 226, 1 }, - { 6, 201, 138, 135, 31, 164, 1 }, - { 163, 136, 16, 246, 207, 129, 1 }, }, - { { 149, 25, 170, 136, 106, 158, 1 }, - { 43, 153, 140, 178, 38, 242, 0 }, - { 188, 171, 8, 170, 204, 84, 1 }, - { 39, 178, 38, 152, 204, 234, 0 }, }, - { { 148, 82, 41, 183, 65, 127, 0 }, - { 53, 53, 156, 213, 161, 104, 1 }, - { 127, 65, 118, 202, 37, 20, 1 }, - { 139, 66, 213, 156, 214, 86, 0 }, }, - { { 148, 87, 59, 92, 68, 38, 1 }, - { 115, 81, 204, 21, 71, 42, 1 }, - { 178, 17, 29, 110, 117, 20, 1 }, - { 170, 113, 84, 25, 197, 103, 0 }, }, - { { 151, 82, 5, 249, 168, 20, 0 }, - { 153, 33, 100, 213, 0, 254, 0 }, - { 20, 10, 207, 208, 37, 116, 1 }, - { 63, 128, 85, 147, 66, 76, 1 }, }, - { { 150, 156, 251, 61, 143, 160, 1 }, - { 243, 233, 181, 99, 75, 162, 1 }, - { 130, 248, 222, 111, 156, 180, 1 }, - { 162, 233, 99, 86, 203, 231, 1 }, }, - { { 151, 163, 111, 26, 69, 144, 1 }, - { 179, 24, 151, 1, 206, 126, 0 }, - { 132, 209, 44, 123, 98, 244, 1 }, - { 63, 57, 192, 116, 140, 102, 1 }, }, - { { 151, 170, 229, 229, 236, 98, 0 }, - { 141, 176, 239, 195, 73, 188, 1 }, - { 35, 27, 211, 211, 170, 244, 1 }, - { 158, 201, 97, 251, 134, 216, 1 }, }, - { { 151, 229, 170, 59, 113, 88, 1 }, - { 255, 48, 150, 70, 174, 115, 0 }, - { 141, 71, 110, 42, 211, 244, 1 }, - { 103, 58, 177, 52, 134, 127, 1 }, }, - { { 154, 32, 49, 130, 105, 146, 1 }, - { 138, 90, 158, 129, 128, 224, 0 }, - { 164, 203, 32, 198, 2, 44, 1 }, - { 3, 128, 192, 188, 173, 40, 1 }, }, - { { 153, 69, 56, 75, 61, 53, 1 }, - { 106, 70, 212, 84, 196, 243, 1 }, - { 214, 94, 105, 14, 81, 76, 1 }, - { 231, 145, 149, 21, 177, 43, 0 }, }, - { { 154, 177, 236, 143, 208, 111, 1 }, - { 166, 23, 175, 210, 173, 39, 1 }, - { 251, 5, 248, 155, 198, 172, 1 }, - { 242, 90, 165, 250, 244, 50, 1 }, }, - { { 154, 213, 151, 178, 63, 108, 1 }, - { 222, 99, 20, 183, 238, 165, 1 }, - { 155, 126, 38, 244, 213, 172, 1 }, - { 210, 187, 246, 148, 99, 61, 1 }, }, - { { 153, 249, 19, 79, 121, 230, 0 }, - { 12, 219, 94, 85, 143, 179, 1 }, - { 51, 207, 121, 100, 79, 204, 1 }, - { 230, 248, 213, 61, 109, 152, 0 }, }, - { { 158, 24, 232, 0, 191, 106, 1 }, - { 175, 131, 189, 34, 96, 161, 1 }, - { 171, 126, 128, 11, 140, 60, 1 }, - { 194, 131, 34, 94, 224, 250, 1 }, }, - { { 159, 72, 93, 163, 29, 40, 1 }, - { 163, 226, 21, 197, 224, 181, 1 }, - { 138, 92, 98, 221, 9, 124, 1 }, - { 214, 131, 209, 212, 35, 226, 1 }, }, - { { 159, 84, 21, 142, 137, 140, 0 }, - { 193, 75, 52, 149, 161, 182, 0 }, - { 24, 200, 184, 212, 21, 124, 1 }, - { 54, 194, 212, 150, 105, 65, 1 }, }, - { { 157, 106, 155, 82, 57, 78, 1 }, - { 63, 194, 94, 23, 162, 185, 0 }, - { 185, 78, 37, 108, 171, 92, 1 }, - { 78, 162, 244, 61, 33, 254, 0 }, }, - { { 158, 111, 156, 10, 159, 118, 0 }, - { 229, 194, 62, 54, 196, 239, 1 }, - { 55, 124, 168, 28, 251, 60, 1 }, - { 251, 145, 182, 62, 33, 211, 1 }, }, - { { 160, 9, 100, 5, 91, 91, 0 }, - { 4, 148, 153, 104, 37, 229, 0 }, - { 109, 109, 80, 19, 72, 2, 1 }, - { 83, 210, 11, 76, 148, 144, 0 }, }, - { { 162, 60, 219, 252, 54, 178, 0 }, - { 248, 233, 75, 171, 67, 99, 1 }, - { 38, 182, 31, 237, 158, 34, 1 }, - { 227, 97, 106, 233, 75, 143, 1 }, }, - { { 163, 91, 247, 118, 247, 143, 0 }, - { 152, 253, 249, 63, 231, 61, 0 }, - { 120, 247, 183, 119, 237, 98, 1 }, - { 94, 115, 254, 79, 223, 140, 1 }, }, - { { 162, 157, 115, 18, 132, 59, 0 }, - { 208, 197, 169, 9, 238, 96, 1 }, - { 110, 16, 164, 103, 92, 162, 1 }, - { 131, 59, 200, 74, 209, 133, 1 }, }, - { { 160, 206, 10, 169, 116, 87, 0 }, - { 108, 180, 8, 220, 74, 107, 0 }, - { 117, 23, 74, 168, 57, 130, 1 }, - { 107, 41, 29, 136, 22, 155, 0 }, }, - { { 160, 220, 172, 38, 43, 179, 1 }, - { 106, 173, 152, 46, 137, 228, 1 }, - { 230, 234, 50, 26, 157, 130, 1 }, - { 147, 200, 186, 12, 218, 171, 0 }, }, - { { 162, 245, 118, 116, 106, 43, 1 }, - { 218, 117, 203, 44, 47, 164, 1 }, - { 234, 43, 23, 55, 87, 162, 1 }, - { 146, 250, 26, 105, 215, 45, 1 }, }, - { { 166, 47, 214, 238, 159, 142, 1 }, - { 195, 232, 123, 186, 231, 175, 0 }, - { 184, 252, 187, 181, 250, 50, 1 }, - { 122, 243, 174, 239, 11, 225, 1 }, }, - { { 165, 56, 116, 248, 96, 115, 1 }, - { 31, 245, 203, 136, 0, 118, 1 }, - { 231, 3, 15, 151, 14, 82, 1 }, - { 183, 0, 8, 233, 215, 252, 0 }, }, - { { 164, 94, 71, 141, 231, 74, 0 }, - { 77, 145, 57, 237, 99, 46, 0 }, - { 41, 115, 216, 241, 61, 18, 1 }, - { 58, 99, 91, 206, 68, 217, 0 }, }, - { { 167, 93, 87, 124, 48, 116, 1 }, - { 223, 225, 65, 29, 7, 119, 1 }, - { 151, 6, 31, 117, 93, 114, 1 }, - { 247, 112, 92, 65, 67, 253, 1 }, }, - { { 166, 114, 149, 34, 69, 134, 0 }, - { 129, 121, 26, 31, 192, 44, 0 }, - { 48, 209, 34, 84, 167, 50, 1 }, - { 26, 1, 252, 44, 79, 64, 1 }, }, - { { 165, 131, 104, 82, 20, 90, 0 }, - { 53, 0, 201, 8, 236, 121, 0 }, - { 45, 20, 37, 11, 96, 210, 1 }, - { 79, 27, 136, 73, 128, 86, 0 }, }, - { { 164, 155, 84, 147, 194, 3, 0 }, - { 17, 213, 41, 232, 140, 44, 0 }, - { 96, 33, 228, 149, 108, 146, 1 }, - { 26, 24, 139, 202, 85, 196, 0 }, }, - { { 166, 189, 42, 131, 213, 14, 0 }, - { 225, 145, 186, 216, 238, 33, 0 }, - { 56, 85, 224, 170, 94, 178, 1 }, - { 66, 59, 141, 174, 196, 195, 1 }, }, - { { 169, 7, 89, 5, 251, 125, 0 }, - { 108, 86, 49, 121, 37, 249, 1 }, - { 95, 111, 208, 77, 112, 74, 1 }, - { 207, 210, 79, 70, 53, 27, 0 }, }, - { { 169, 21, 217, 154, 37, 235, 1 }, - { 126, 79, 25, 139, 228, 50, 1 }, - { 235, 210, 44, 205, 212, 74, 1 }, - { 166, 19, 232, 204, 121, 63, 0 }, }, - { { 171, 34, 146, 112, 50, 2, 0 }, - { 152, 98, 74, 42, 2, 57, 0 }, - { 32, 38, 7, 36, 162, 106, 1 }, - { 78, 32, 42, 41, 35, 12, 1 }, }, - { { 169, 55, 228, 166, 33, 245, 1 }, - { 78, 47, 147, 154, 133, 124, 1 }, - { 215, 194, 50, 147, 246, 74, 1 }, - { 159, 80, 172, 228, 250, 57, 0 }, }, - { { 170, 114, 189, 81, 74, 123, 0 }, - { 180, 87, 202, 111, 32, 236, 1 }, - { 111, 41, 69, 94, 167, 42, 1 }, - { 155, 130, 123, 41, 245, 22, 1 }, }, - { { 168, 137, 199, 107, 240, 9, 0 }, - { 8, 182, 97, 75, 174, 39, 0 }, - { 72, 7, 235, 113, 200, 138, 1 }, - { 114, 58, 233, 67, 54, 136, 0 }, }, - { { 170, 213, 171, 110, 131, 212, 1 }, - { 230, 43, 240, 63, 143, 98, 0 }, - { 149, 224, 187, 106, 213, 170, 1 }, - { 35, 120, 254, 7, 234, 51, 1 }, }, - { { 168, 211, 200, 181, 190, 175, 0 }, - { 56, 47, 41, 254, 109, 169, 1 }, - { 122, 190, 214, 137, 229, 138, 1 }, - { 202, 219, 63, 202, 122, 14, 0 }, }, - { { 174, 20, 19, 54, 167, 164, 0 }, - { 217, 107, 48, 57, 195, 32, 1 }, - { 18, 242, 182, 100, 20, 58, 1 }, - { 130, 97, 206, 6, 107, 77, 1 }, }, - { { 173, 100, 135, 249, 112, 220, 1 }, - { 95, 58, 66, 223, 34, 119, 0 }, - { 157, 135, 79, 240, 147, 90, 1 }, - { 119, 34, 125, 161, 46, 125, 0 }, }, - { { 175, 121, 76, 217, 234, 209, 1 }, - { 191, 159, 99, 236, 4, 246, 0 }, - { 197, 171, 205, 153, 79, 122, 1 }, - { 55, 144, 27, 227, 124, 254, 1 }, }, - { { 173, 154, 157, 184, 0, 254, 1 }, - { 55, 235, 8, 155, 40, 126, 1 }, - { 191, 128, 14, 220, 172, 218, 1 }, - { 191, 10, 108, 136, 107, 246, 0 }, }, - { { 175, 156, 136, 245, 213, 82, 0 }, - { 245, 179, 120, 202, 73, 113, 0 }, - { 37, 85, 215, 136, 156, 250, 1 }, - { 71, 73, 41, 143, 102, 215, 1 }, }, - { { 173, 207, 238, 79, 108, 35, 0 }, - { 105, 150, 201, 78, 207, 190, 1 }, - { 98, 27, 121, 59, 249, 218, 1 }, - { 190, 249, 185, 73, 180, 203, 0 }, }, - { { 173, 247, 163, 231, 58, 61, 1 }, - { 75, 39, 194, 255, 175, 249, 1 }, - { 222, 46, 115, 226, 247, 218, 1 }, - { 207, 250, 255, 161, 242, 105, 0 }, }, - { { 178, 65, 252, 215, 31, 187, 0 }, - { 176, 76, 221, 238, 229, 229, 1 }, - { 110, 252, 117, 159, 193, 38, 1 }, - { 211, 211, 187, 221, 153, 6, 1 }, }, - { { 179, 66, 216, 120, 217, 241, 1 }, - { 182, 124, 117, 14, 0, 251, 1 }, - { 199, 205, 143, 13, 161, 102, 1 }, - { 239, 128, 56, 87, 31, 54, 1 }, }, - { { 178, 97, 81, 226, 141, 99, 0 }, - { 132, 100, 127, 141, 196, 160, 1 }, - { 99, 88, 163, 197, 67, 38, 1 }, - { 130, 145, 216, 255, 19, 16, 1 }, }, - { { 179, 99, 173, 194, 60, 10, 1 }, - { 170, 0, 206, 143, 228, 189, 0 }, - { 168, 30, 33, 218, 227, 102, 1 }, - { 94, 147, 248, 185, 128, 42, 1 }, }, - { { 179, 122, 143, 78, 111, 189, 1 }, - { 170, 157, 86, 63, 227, 254, 1 }, - { 222, 251, 57, 120, 175, 102, 1 }, - { 191, 227, 254, 53, 92, 170, 1 }, }, - { { 176, 177, 13, 218, 203, 9, 1 }, - { 50, 21, 118, 169, 172, 166, 0 }, - { 200, 105, 173, 216, 70, 134, 1 }, - { 50, 154, 202, 183, 84, 38, 0 }, }, - { { 176, 187, 235, 133, 79, 183, 0 }, - { 32, 157, 159, 251, 79, 232, 1 }, - { 118, 249, 80, 235, 238, 134, 1 }, - { 139, 249, 111, 252, 220, 130, 0 }, }, - { { 177, 195, 46, 176, 79, 2, 0 }, - { 48, 48, 156, 172, 78, 188, 0 }, - { 32, 121, 6, 186, 97, 198, 1 }, - { 30, 185, 26, 156, 134, 6, 0 }, }, - { { 177, 202, 162, 122, 198, 206, 0 }, - { 20, 184, 236, 62, 234, 58, 0 }, - { 57, 177, 175, 34, 169, 198, 1 }, - { 46, 43, 190, 27, 142, 148, 0 }, }, - { { 180, 119, 47, 73, 43, 245, 0 }, - { 109, 13, 214, 125, 6, 238, 1 }, - { 87, 234, 73, 122, 119, 22, 1 }, - { 187, 176, 95, 53, 216, 91, 0 }, }, - { { 181, 174, 77, 39, 89, 131, 1 }, - { 99, 188, 31, 73, 137, 189, 0 }, - { 224, 205, 114, 89, 58, 214, 1 }, - { 94, 200, 201, 124, 30, 227, 0 }, }, - { { 183, 210, 205, 142, 206, 219, 0 }, - { 165, 29, 45, 175, 233, 254, 0 }, - { 109, 185, 184, 217, 165, 246, 1 }, - { 63, 203, 250, 218, 92, 82, 1 }, }, - { { 182, 235, 156, 93, 104, 151, 0 }, - { 185, 220, 78, 94, 13, 238, 0 }, - { 116, 139, 93, 28, 235, 182, 1 }, - { 59, 216, 61, 57, 29, 206, 1 }, }, - { { 180, 244, 103, 243, 78, 87, 0 }, - { 85, 53, 207, 253, 202, 228, 0 }, - { 117, 57, 103, 243, 23, 150, 1 }, - { 19, 169, 223, 249, 214, 85, 0 }, }, - { { 184, 15, 212, 222, 49, 7, 1 }, - { 90, 198, 93, 154, 133, 47, 0 }, - { 240, 70, 61, 149, 248, 14, 1 }, - { 122, 80, 172, 221, 49, 173, 0 }, }, - { { 184, 57, 126, 65, 128, 2, 0 }, - { 32, 195, 239, 72, 6, 36, 0 }, - { 32, 0, 193, 63, 78, 14, 1 }, - { 18, 48, 9, 123, 225, 130, 0 }, }, - { { 184, 56, 131, 182, 233, 190, 0 }, - { 24, 187, 62, 155, 163, 224, 1 }, - { 62, 203, 182, 224, 142, 14, 1 }, - { 131, 226, 236, 190, 110, 140, 0 }, }, - { { 184, 108, 6, 172, 40, 232, 0 }, - { 76, 170, 6, 140, 35, 166, 1 }, - { 11, 138, 26, 176, 27, 14, 1 }, - { 178, 226, 24, 176, 42, 153, 0 }, }, - { { 185, 136, 183, 154, 126, 192, 1 }, - { 30, 218, 132, 171, 202, 183, 0 }, - { 129, 191, 44, 246, 136, 206, 1 }, - { 118, 169, 234, 144, 173, 188, 0 }, }, - { { 184, 156, 165, 183, 60, 206, 0 }, - { 92, 171, 140, 219, 233, 165, 0 }, - { 57, 158, 118, 210, 156, 142, 1 }, - { 82, 203, 237, 152, 234, 157, 0 }, }, - { { 186, 207, 131, 167, 229, 28, 1 }, - { 202, 178, 52, 223, 239, 104, 0 }, - { 156, 83, 242, 224, 249, 174, 1 }, - { 11, 123, 253, 150, 38, 169, 1 }, }, - { { 184, 231, 17, 147, 88, 172, 1 }, - { 82, 90, 6, 221, 172, 169, 1 }, - { 154, 141, 100, 196, 115, 142, 1 }, - { 202, 154, 221, 176, 45, 37, 0 }, }, - { { 189, 114, 116, 100, 13, 206, 1 }, - { 7, 107, 223, 28, 97, 188, 0 }, - { 185, 216, 19, 23, 39, 94, 1 }, - { 30, 195, 28, 125, 235, 112, 0 }, }, - { { 188, 125, 134, 57, 75, 186, 1 }, - { 83, 187, 30, 110, 38, 230, 1 }, - { 174, 233, 78, 48, 223, 30, 1 }, - { 179, 178, 59, 60, 110, 229, 0 }, }, - { { 195, 30, 73, 175, 1, 169, 1 }, - { 226, 173, 17, 193, 177, 58, 1 }, - { 202, 192, 122, 201, 60, 97, 1 }, - { 174, 70, 193, 196, 90, 163, 1 }, }, - { { 192, 46, 91, 239, 41, 95, 0 }, - { 108, 228, 91, 209, 179, 234, 0 }, - { 125, 74, 123, 237, 58, 1, 1 }, - { 43, 230, 197, 237, 19, 155, 0 }, }, - { { 193, 78, 242, 15, 34, 121, 1 }, - { 78, 196, 129, 102, 179, 122, 1 }, - { 207, 34, 120, 39, 185, 65, 1 }, - { 175, 102, 179, 64, 145, 185, 0 }, }, - { { 193, 129, 72, 108, 154, 70, 1 }, - { 38, 32, 105, 48, 29, 179, 0 }, - { 177, 44, 155, 9, 64, 193, 1 }, - { 102, 220, 6, 75, 2, 50, 0 }, }, - { { 193, 153, 177, 237, 205, 191, 1 }, - { 2, 253, 248, 211, 125, 242, 1 }, - { 254, 217, 219, 198, 204, 193, 1 }, - { 167, 223, 101, 143, 223, 160, 0 }, }, - { { 193, 146, 177, 38, 19, 223, 1 }, - { 6, 109, 152, 51, 185, 121, 0 }, - { 253, 228, 50, 70, 164, 193, 1 }, - { 79, 78, 230, 12, 219, 48, 0 }, }, - { { 195, 183, 213, 56, 210, 51, 1 }, - { 210, 117, 43, 35, 28, 127, 1 }, - { 230, 37, 142, 85, 246, 225, 1 }, - { 255, 28, 98, 106, 87, 37, 1 }, }, - { { 193, 215, 25, 188, 235, 129, 1 }, - { 122, 125, 48, 165, 29, 186, 0 }, - { 192, 235, 158, 204, 117, 193, 1 }, - { 46, 220, 82, 134, 95, 47, 0 }, }, - { { 193, 246, 67, 78, 186, 232, 1 }, - { 78, 9, 99, 37, 187, 187, 1 }, - { 139, 174, 185, 97, 55, 193, 1 }, - { 238, 238, 210, 99, 72, 57, 0 }, }, - { { 196, 56, 145, 7, 35, 108, 1 }, - { 15, 193, 18, 115, 177, 32, 1 }, - { 155, 98, 112, 68, 142, 17, 1 }, - { 130, 70, 231, 36, 65, 248, 0 }, }, - { { 198, 77, 74, 227, 205, 183, 0 }, - { 225, 188, 121, 212, 214, 224, 1 }, - { 118, 217, 227, 169, 89, 49, 1 }, - { 131, 181, 149, 207, 30, 195, 1 }, }, - { { 199, 83, 172, 246, 95, 75, 1 }, - { 183, 53, 216, 166, 245, 189, 0 }, - { 233, 125, 55, 154, 229, 113, 1 }, - { 94, 215, 178, 141, 214, 118, 1 }, }, - { { 197, 126, 74, 113, 2, 75, 0 }, - { 117, 165, 75, 100, 50, 56, 0 }, - { 105, 32, 71, 41, 63, 81, 1 }, - { 14, 38, 19, 105, 82, 215, 0 }, }, - { { 198, 116, 217, 128, 138, 199, 1 }, - { 231, 77, 43, 183, 16, 160, 0 }, - { 241, 168, 128, 205, 151, 49, 1 }, - { 2, 132, 118, 234, 89, 115, 1 }, }, - { { 197, 136, 152, 47, 230, 202, 1 }, - { 47, 248, 40, 98, 249, 50, 0 }, - { 169, 179, 250, 12, 136, 209, 1 }, - { 38, 79, 163, 10, 15, 250, 0 }, }, - { { 197, 174, 39, 8, 53, 94, 1 }, - { 79, 128, 154, 17, 122, 127, 0 }, - { 189, 86, 8, 114, 58, 209, 1 }, - { 127, 47, 68, 44, 128, 249, 0 }, }, - { { 199, 188, 103, 248, 187, 33, 1 }, - { 219, 165, 243, 161, 26, 183, 1 }, - { 194, 110, 143, 243, 30, 241, 1 }, - { 246, 172, 66, 231, 210, 237, 1 }, }, - { { 196, 190, 140, 164, 243, 99, 0 }, - { 109, 181, 58, 162, 25, 45, 1 }, - { 99, 103, 146, 152, 190, 145, 1 }, - { 218, 76, 34, 174, 86, 219, 0 }, }, - { { 203, 15, 36, 114, 82, 53, 0 }, - { 208, 182, 192, 48, 148, 125, 1 }, - { 86, 37, 39, 18, 120, 105, 1 }, - { 223, 20, 134, 1, 182, 133, 1 }, }, - { { 201, 72, 89, 111, 111, 39, 1 }, - { 42, 246, 89, 117, 209, 178, 1 }, - { 242, 123, 123, 77, 9, 73, 1 }, - { 166, 197, 215, 77, 55, 170, 0 }, }, - { { 200, 85, 15, 64, 161, 129, 0 }, - { 104, 15, 112, 5, 22, 36, 0 }, - { 64, 194, 129, 120, 85, 9, 1 }, - { 18, 52, 80, 7, 120, 11, 0 }, }, - { { 203, 90, 139, 223, 86, 169, 0 }, - { 176, 159, 64, 231, 243, 59, 1 }, - { 74, 181, 125, 232, 173, 105, 1 }, - { 238, 103, 243, 129, 124, 134, 1 }, }, - { { 200, 115, 24, 175, 214, 16, 0 }, - { 32, 115, 34, 228, 213, 107, 0 }, - { 4, 53, 250, 140, 103, 9, 1 }, - { 107, 85, 147, 162, 103, 2, 0 }, }, - { { 201, 159, 158, 95, 56, 56, 0 }, - { 120, 195, 64, 66, 191, 255, 1 }, - { 14, 14, 125, 60, 252, 201, 1 }, - { 255, 254, 161, 1, 97, 143, 0 }, }, - { { 202, 191, 51, 44, 49, 240, 0 }, - { 204, 235, 146, 1, 31, 107, 1 }, - { 7, 198, 26, 102, 126, 169, 1 }, - { 235, 124, 64, 36, 235, 153, 1 }, }, - { { 201, 233, 181, 7, 221, 84, 0 }, - { 4, 210, 178, 87, 221, 245, 0 }, - { 21, 93, 240, 86, 203, 201, 1 }, - { 87, 221, 245, 38, 165, 144, 0 }, }, - { { 203, 240, 30, 59, 146, 129, 0 }, - { 176, 111, 34, 100, 154, 55, 0 }, - { 64, 164, 238, 60, 7, 233, 1 }, - { 118, 44, 147, 34, 123, 6, 1 }, }, - { { 204, 2, 72, 129, 217, 10, 0 }, - { 33, 18, 57, 192, 48, 169, 0 }, - { 40, 77, 192, 137, 32, 25, 1 }, - { 74, 134, 1, 206, 36, 66, 0 }, }, - { { 206, 44, 78, 124, 45, 3, 1 }, - { 251, 166, 91, 0, 83, 166, 0 }, - { 224, 90, 31, 57, 26, 57, 1 }, - { 50, 229, 0, 109, 50, 239, 1 }, }, - { { 205, 131, 179, 65, 153, 204, 1 }, - { 7, 74, 240, 83, 62, 185, 0 }, - { 153, 204, 193, 102, 224, 217, 1 }, - { 78, 190, 101, 7, 169, 112, 0 }, }, - { { 206, 128, 171, 157, 219, 17, 1 }, - { 179, 22, 176, 227, 27, 227, 0 }, - { 196, 109, 220, 234, 128, 185, 1 }, - { 99, 236, 99, 134, 180, 102, 1 }, }, - { { 204, 183, 70, 234, 75, 14, 1 }, - { 67, 51, 91, 176, 190, 174, 0 }, - { 184, 105, 43, 177, 118, 153, 1 }, - { 58, 190, 134, 237, 102, 97, 0 }, }, - { { 207, 228, 35, 113, 192, 123, 1 }, - { 215, 54, 234, 69, 58, 112, 1 }, - { 239, 1, 199, 98, 19, 249, 1 }, - { 135, 46, 81, 43, 182, 117, 1 }, }, - { { 205, 246, 59, 148, 188, 171, 1 }, - { 123, 79, 170, 133, 123, 185, 1 }, - { 234, 158, 148, 238, 55, 217, 1 }, - { 206, 239, 80, 170, 249, 111, 0 }, }, - { { 211, 23, 149, 186, 69, 222, 1 }, - { 214, 121, 28, 147, 244, 126, 0 }, - { 189, 209, 46, 212, 244, 101, 1 }, - { 63, 23, 228, 156, 79, 53, 1 }, }, - { { 208, 58, 34, 105, 80, 179, 0 }, - { 0, 189, 206, 64, 18, 107, 1 }, - { 102, 133, 75, 34, 46, 5, 1 }, - { 235, 36, 1, 57, 222, 128, 0 }, }, - { { 211, 54, 150, 219, 2, 36, 1 }, - { 210, 65, 70, 242, 146, 62, 1 }, - { 146, 32, 109, 180, 182, 101, 1 }, - { 190, 36, 167, 177, 65, 37, 1 }, }, - { { 210, 86, 224, 221, 145, 27, 0 }, - { 208, 5, 253, 198, 49, 107, 0 }, - { 108, 68, 221, 131, 181, 37, 1 }, - { 107, 70, 49, 223, 208, 5, 1 }, }, - { { 210, 188, 170, 228, 3, 129, 0 }, - { 224, 173, 214, 162, 27, 32, 0 }, - { 64, 224, 19, 170, 158, 165, 1 }, - { 2, 108, 34, 181, 218, 131, 1 }, }, - { { 211, 196, 238, 80, 249, 166, 0 }, - { 248, 24, 253, 22, 26, 181, 1 }, - { 50, 207, 133, 59, 145, 229, 1 }, - { 214, 172, 52, 95, 140, 15, 1 }, }, - { { 211, 228, 7, 252, 5, 226, 1 }, - { 214, 40, 94, 133, 91, 54, 1 }, - { 163, 208, 31, 240, 19, 229, 1 }, - { 182, 109, 80, 189, 10, 53, 1 }, }, - { { 211, 252, 28, 167, 191, 2, 0 }, - { 232, 225, 62, 228, 217, 181, 0 }, - { 32, 126, 242, 156, 31, 229, 1 }, - { 86, 205, 147, 190, 67, 139, 1 }, }, - { { 215, 68, 167, 168, 172, 223, 0 }, - { 205, 44, 172, 151, 114, 246, 0 }, - { 125, 154, 138, 242, 145, 117, 1 }, - { 55, 167, 116, 154, 154, 89, 1 }, }, - { { 214, 159, 244, 68, 89, 83, 0 }, - { 197, 213, 221, 2, 29, 237, 0 }, - { 101, 77, 17, 23, 252, 181, 1 }, - { 91, 220, 32, 93, 213, 209, 1 }, }, - { { 212, 175, 147, 230, 193, 68, 0 }, - { 69, 240, 118, 147, 159, 40, 0 }, - { 17, 65, 179, 228, 250, 149, 1 }, - { 10, 124, 228, 183, 7, 209, 0 }, }, - { { 217, 18, 102, 139, 15, 210, 1 }, - { 6, 11, 157, 224, 210, 254, 0 }, - { 165, 248, 104, 179, 36, 77, 1 }, - { 63, 165, 131, 220, 232, 48, 0 }, }, - { { 216, 81, 88, 45, 224, 190, 1 }, - { 42, 123, 45, 84, 53, 98, 1 }, - { 190, 131, 218, 13, 69, 13, 1 }, - { 163, 86, 21, 90, 111, 42, 0 }, }, - { { 216, 99, 102, 115, 129, 229, 0 }, - { 20, 46, 247, 84, 150, 44, 1 }, - { 83, 192, 231, 51, 99, 13, 1 }, - { 154, 52, 149, 119, 186, 20, 0 }, }, - { { 219, 108, 88, 112, 132, 206, 1 }, - { 246, 234, 111, 20, 112, 48, 0 }, - { 185, 144, 135, 13, 27, 109, 1 }, - { 6, 7, 20, 123, 43, 183, 1 }, }, - { { 217, 99, 155, 60, 255, 211, 0 }, - { 60, 126, 62, 39, 87, 251, 0 }, - { 101, 255, 158, 108, 227, 77, 1 }, - { 111, 245, 114, 62, 63, 30, 0 }, }, - { { 216, 138, 98, 229, 71, 244, 0 }, - { 4, 186, 213, 240, 91, 104, 1 }, - { 23, 241, 83, 163, 40, 141, 1 }, - { 139, 109, 7, 213, 174, 144, 0 }, }, - { { 217, 153, 210, 99, 213, 126, 1 }, - { 6, 243, 125, 82, 254, 113, 1 }, - { 191, 85, 227, 37, 204, 205, 1 }, - { 199, 63, 165, 95, 103, 176, 0 }, }, - { { 217, 162, 40, 145, 224, 209, 0 }, - { 60, 30, 166, 192, 24, 120, 0 }, - { 69, 131, 196, 138, 34, 205, 1 }, - { 15, 12, 1, 178, 188, 30, 0 }, }, - { { 219, 244, 50, 154, 65, 205, 0 }, - { 212, 95, 150, 148, 186, 50, 0 }, - { 89, 193, 44, 166, 23, 237, 1 }, - { 38, 46, 148, 180, 253, 21, 1 }, }, - { { 220, 29, 107, 96, 189, 237, 0 }, - { 109, 175, 245, 17, 118, 161, 1 }, - { 91, 222, 131, 107, 92, 29, 1 }, - { 194, 183, 68, 87, 250, 219, 0 }, }, - { { 222, 26, 217, 103, 197, 109, 0 }, - { 165, 247, 117, 83, 241, 40, 1 }, - { 91, 81, 243, 77, 172, 61, 1 }, - { 138, 71, 229, 87, 119, 210, 1 }, }, - { { 221, 114, 161, 214, 214, 204, 1 }, - { 23, 27, 230, 183, 241, 57, 0 }, - { 153, 181, 181, 194, 167, 93, 1 }, - { 78, 71, 246, 179, 236, 116, 0 }, }, - { { 221, 116, 213, 91, 237, 200, 1 }, - { 95, 91, 119, 71, 240, 182, 0 }, - { 137, 219, 237, 85, 151, 93, 1 }, - { 54, 135, 241, 119, 109, 125, 0 }, }, - { { 221, 134, 26, 161, 4, 19, 1 }, - { 99, 102, 12, 192, 90, 120, 0 }, - { 228, 16, 66, 172, 48, 221, 1 }, - { 15, 45, 1, 152, 51, 99, 0 }, }, - { { 223, 175, 27, 5, 181, 189, 1 }, - { 235, 206, 54, 81, 127, 121, 1 }, - { 222, 214, 208, 108, 122, 253, 1 }, - { 207, 127, 69, 54, 57, 235, 1 }, }, - { { 222, 176, 237, 214, 204, 22, 1 }, - { 179, 19, 239, 147, 217, 228, 0 }, - { 180, 25, 181, 219, 134, 189, 1 }, - { 19, 205, 228, 251, 228, 102, 1 }, }, - { { 223, 230, 10, 66, 93, 241, 0 }, - { 229, 30, 86, 4, 218, 249, 1 }, - { 71, 221, 33, 40, 51, 253, 1 }, - { 207, 173, 144, 53, 60, 83, 1 }, }, - { { 221, 251, 88, 81, 243, 143, 0 }, - { 57, 223, 127, 116, 60, 57, 0 }, - { 120, 231, 197, 13, 111, 221, 1 }, - { 78, 30, 23, 127, 125, 206, 0 }, }, - { { 227, 22, 68, 69, 140, 160, 0 }, - { 192, 9, 97, 72, 81, 188, 1 }, - { 2, 152, 209, 17, 52, 99, 1 }, - { 158, 197, 9, 67, 72, 1, 1 }, }, - { { 226, 26, 171, 99, 248, 21, 1 }, - { 170, 181, 224, 91, 146, 233, 0 }, - { 212, 15, 227, 106, 172, 35, 1 }, - { 75, 164, 237, 3, 214, 170, 1 }, }, - { { 226, 90, 168, 144, 16, 221, 1 }, - { 182, 141, 128, 158, 48, 105, 0 }, - { 221, 132, 4, 138, 173, 35, 1 }, - { 75, 6, 60, 128, 216, 182, 1 }, }, - { { 224, 126, 198, 224, 106, 118, 1 }, - { 78, 177, 75, 190, 18, 236, 1 }, - { 183, 43, 3, 177, 191, 3, 1 }, - { 155, 164, 62, 233, 70, 185, 0 }, }, - { { 225, 117, 204, 107, 230, 146, 0 }, - { 104, 57, 107, 110, 212, 118, 0 }, - { 36, 179, 235, 25, 215, 67, 1 }, - { 55, 21, 187, 107, 78, 11, 0 }, }, - { { 224, 221, 231, 62, 185, 106, 0 }, - { 92, 161, 185, 15, 191, 167, 1 }, - { 43, 78, 190, 115, 221, 131, 1 }, - { 242, 254, 248, 78, 194, 157, 0 }, }, - { { 231, 26, 89, 61, 43, 68, 0 }, - { 189, 225, 17, 121, 17, 186, 0 }, - { 17, 106, 94, 77, 44, 115, 1 }, - { 46, 196, 79, 68, 67, 222, 1 }, }, - { { 230, 61, 186, 78, 33, 23, 0 }, - { 233, 197, 218, 26, 151, 98, 0 }, - { 116, 66, 57, 46, 222, 51, 1 }, - { 35, 116, 172, 45, 209, 203, 1 }, }, - { { 231, 80, 110, 107, 241, 111, 0 }, - { 173, 53, 249, 92, 178, 55, 1 }, - { 123, 71, 235, 59, 5, 115, 1 }, - { 246, 38, 157, 79, 214, 90, 1 }, }, - { { 229, 83, 212, 72, 59, 179, 0 }, - { 9, 77, 89, 46, 20, 255, 1 }, - { 102, 238, 9, 21, 229, 83, 1 }, - { 255, 148, 58, 77, 89, 72, 0 }, }, - { { 228, 120, 9, 177, 15, 166, 1 }, - { 51, 169, 26, 253, 80, 160, 1 }, - { 178, 248, 70, 200, 15, 19, 1 }, - { 130, 133, 95, 172, 74, 230, 0 }, }, - { { 229, 122, 161, 185, 186, 74, 1 }, - { 31, 161, 170, 239, 48, 187, 0 }, - { 169, 46, 206, 194, 175, 83, 1 }, - { 110, 134, 123, 170, 194, 252, 0 }, }, - { { 231, 124, 180, 146, 88, 174, 1 }, - { 211, 217, 138, 158, 176, 181, 1 }, - { 186, 141, 36, 150, 159, 115, 1 }, - { 214, 134, 188, 168, 205, 229, 1 }, }, - { { 230, 164, 245, 240, 132, 66, 1 }, - { 215, 96, 235, 139, 88, 36, 0 }, - { 161, 16, 135, 215, 146, 179, 1 }, - { 18, 13, 104, 235, 131, 117, 1 }, }, - { { 229, 213, 42, 118, 142, 25, 1 }, - { 115, 37, 224, 44, 255, 240, 0 }, - { 204, 56, 183, 42, 85, 211, 1 }, - { 7, 255, 154, 3, 210, 103, 0 }, }, - { { 231, 215, 202, 210, 228, 123, 1 }, - { 255, 21, 105, 142, 254, 120, 1 }, - { 239, 19, 165, 169, 245, 243, 1 }, - { 143, 63, 184, 203, 84, 127, 1 }, }, - { { 228, 239, 89, 211, 239, 61, 1 }, - { 123, 212, 115, 253, 252, 232, 1 }, - { 222, 123, 229, 205, 123, 147, 1 }, - { 139, 159, 223, 231, 21, 239, 0 }, }, - { { 235, 27, 228, 239, 142, 205, 0 }, - { 132, 175, 225, 250, 245, 190, 0 }, - { 89, 184, 251, 147, 236, 107, 1 }, - { 62, 215, 175, 195, 250, 144, 1 }, }, - { { 233, 50, 9, 4, 85, 129, 1 }, - { 34, 31, 18, 9, 81, 57, 0 }, - { 192, 213, 16, 72, 38, 75, 1 }, - { 78, 69, 72, 36, 124, 34, 0 }, }, - { { 235, 52, 254, 217, 172, 58, 1 }, - { 250, 67, 235, 202, 114, 246, 1 }, - { 174, 26, 205, 191, 150, 107, 1 }, - { 183, 167, 41, 235, 225, 47, 1 }, }, - { { 234, 78, 19, 245, 78, 30, 0 }, - { 208, 242, 72, 253, 115, 232, 0 }, - { 60, 57, 87, 228, 57, 43, 1 }, - { 11, 231, 95, 137, 39, 133, 1 }, }, - { { 233, 176, 103, 32, 6, 254, 0 }, - { 4, 43, 139, 57, 122, 116, 1 }, - { 63, 176, 2, 115, 6, 203, 1 }, - { 151, 47, 78, 104, 234, 16, 0 }, }, - { { 234, 213, 177, 80, 109, 191, 0 }, - { 216, 95, 216, 31, 124, 224, 1 }, - { 126, 219, 5, 70, 213, 171, 1 }, - { 131, 159, 124, 13, 253, 13, 1 }, }, - { { 239, 10, 161, 134, 54, 174, 0 }, - { 137, 138, 136, 187, 241, 57, 1 }, - { 58, 182, 48, 194, 168, 123, 1 }, - { 206, 71, 238, 136, 168, 200, 1 }, }, - { { 237, 54, 239, 15, 39, 238, 1 }, - { 111, 11, 155, 123, 243, 62, 1 }, - { 187, 242, 120, 123, 182, 91, 1 }, - { 190, 103, 239, 108, 232, 123, 0 }, }, - { { 237, 74, 130, 29, 33, 213, 0 }, - { 29, 142, 16, 94, 19, 122, 0 }, - { 85, 194, 92, 32, 169, 91, 1 }, - { 47, 100, 61, 4, 56, 220, 0 }, }, - { { 236, 96, 189, 109, 140, 6, 0 }, - { 33, 98, 234, 95, 81, 166, 0 }, - { 48, 24, 219, 94, 131, 27, 1 }, - { 50, 197, 125, 43, 163, 66, 0 }, }, - { { 239, 130, 202, 100, 252, 71, 0 }, - { 173, 54, 105, 26, 91, 185, 0 }, - { 113, 31, 147, 41, 160, 251, 1 }, - { 78, 237, 44, 75, 54, 90, 1 }, }, - { { 238, 161, 127, 189, 151, 92, 0 }, - { 181, 98, 179, 249, 127, 103, 0 }, - { 29, 116, 222, 255, 66, 187, 1 }, - { 115, 127, 79, 230, 163, 86, 1 }, }, - { { 238, 209, 152, 137, 82, 229, 1 }, - { 167, 95, 0, 254, 28, 35, 1 }, - { 211, 165, 72, 140, 197, 187, 1 }, - { 226, 28, 63, 128, 125, 114, 1 }, }, - { { 241, 9, 85, 233, 4, 138, 1 }, - { 2, 232, 77, 201, 116, 54, 0 }, - { 168, 144, 75, 213, 72, 71, 1 }, - { 54, 23, 73, 217, 11, 160, 0 }, }, - { { 243, 42, 138, 178, 12, 104, 1 }, - { 182, 160, 6, 138, 242, 184, 1 }, - { 139, 24, 38, 168, 170, 103, 1 }, - { 142, 167, 168, 176, 2, 182, 1 }, }, - { { 243, 71, 148, 129, 100, 1, 1 }, - { 202, 84, 4, 206, 84, 60, 0 }, - { 192, 19, 64, 148, 241, 103, 1 }, - { 30, 21, 57, 144, 21, 41, 1 }, }, - { { 243, 136, 79, 37, 132, 41, 1 }, - { 162, 164, 37, 73, 123, 52, 1 }, - { 202, 16, 210, 121, 8, 231, 1 }, - { 150, 111, 73, 82, 18, 162, 1 }, }, - { { 240, 162, 31, 6, 126, 77, 1 }, - { 46, 84, 6, 57, 251, 173, 0 }, - { 217, 63, 48, 124, 34, 135, 1 }, - { 90, 239, 206, 48, 21, 58, 0 }, }, - { { 243, 170, 11, 255, 165, 205, 0 }, - { 188, 172, 118, 217, 251, 58, 0 }, - { 89, 210, 255, 232, 42, 231, 1 }, - { 46, 111, 205, 183, 26, 158, 1 }, }, - { { 240, 175, 124, 121, 142, 228, 0 }, - { 116, 232, 231, 120, 92, 174, 1 }, - { 19, 184, 207, 31, 122, 135, 1 }, - { 186, 157, 15, 115, 139, 151, 0 }, }, - { { 240, 217, 16, 212, 202, 31, 1 }, - { 18, 213, 108, 188, 61, 224, 0 }, - { 252, 41, 149, 132, 77, 135, 1 }, - { 3, 222, 30, 155, 85, 164, 0 }, }, - { { 245, 75, 179, 178, 85, 245, 1 }, - { 23, 252, 148, 159, 214, 121, 1 }, - { 215, 213, 38, 230, 233, 87, 1 }, - { 207, 53, 252, 148, 159, 244, 0 }, }, - { { 246, 81, 96, 41, 26, 234, 1 }, - { 135, 41, 141, 108, 52, 163, 1 }, - { 171, 172, 74, 3, 69, 55, 1 }, - { 226, 150, 27, 88, 202, 112, 1 }, }, - { { 246, 117, 82, 145, 217, 185, 1 }, - { 211, 93, 55, 204, 54, 225, 1 }, - { 206, 205, 196, 165, 87, 55, 1 }, - { 195, 182, 25, 246, 93, 101, 1 }, }, - { { 245, 114, 198, 173, 192, 65, 0 }, - { 5, 53, 39, 206, 19, 62, 0 }, - { 65, 1, 218, 177, 167, 87, 1 }, - { 62, 100, 57, 242, 86, 80, 0 }, }, - { { 247, 124, 210, 85, 227, 70, 0 }, - { 221, 209, 127, 126, 19, 48, 0 }, - { 49, 99, 213, 37, 159, 119, 1 }, - { 6, 100, 63, 127, 69, 221, 1 }, }, - { { 246, 132, 78, 31, 171, 28, 0 }, - { 249, 0, 53, 120, 187, 230, 0 }, - { 28, 106, 252, 57, 16, 183, 1 }, - { 51, 238, 143, 86, 0, 79, 1 }, }, - { { 248, 61, 136, 57, 165, 1, 1 }, - { 122, 167, 54, 74, 84, 34, 0 }, - { 192, 82, 206, 8, 222, 15, 1 }, - { 34, 21, 41, 54, 114, 175, 0 }, }, - { { 251, 54, 195, 223, 220, 215, 1 }, - { 214, 31, 111, 219, 211, 251, 0 }, - { 245, 157, 253, 225, 182, 111, 1 }, - { 111, 229, 237, 251, 124, 53, 1 }, }, - { { 248, 76, 172, 3, 76, 20, 1 }, - { 98, 146, 132, 94, 208, 228, 0 }, - { 148, 25, 96, 26, 153, 15, 1 }, - { 19, 133, 189, 16, 164, 163, 0 }, }, - { { 251, 160, 208, 185, 211, 75, 1 }, - { 150, 118, 63, 234, 56, 51, 0 }, - { 233, 101, 206, 133, 130, 239, 1 }, - { 102, 14, 43, 254, 55, 52, 1 }, }, - { { 250, 195, 132, 204, 125, 54, 1 }, - { 138, 18, 92, 158, 93, 239, 1 }, - { 182, 95, 25, 144, 225, 175, 1 }, - { 251, 221, 60, 157, 36, 40, 1 }, }, - { { 249, 231, 61, 60, 30, 97, 1 }, - { 118, 102, 134, 45, 93, 191, 1 }, - { 195, 60, 30, 94, 115, 207, 1 }, - { 254, 221, 90, 48, 179, 55, 0 }, }, - { { 248, 255, 88, 101, 6, 119, 1 }, - { 102, 231, 79, 124, 93, 104, 1 }, - { 247, 48, 83, 13, 127, 143, 1 }, - { 139, 93, 31, 121, 115, 179, 0 }, }, - { { 253, 11, 219, 85, 100, 56, 1 }, - { 59, 210, 69, 75, 119, 120, 1 }, - { 142, 19, 85, 109, 232, 95, 1 }, - { 143, 119, 105, 81, 37, 238, 0 }, }, - { { 253, 22, 174, 228, 229, 62, 1 }, - { 107, 51, 252, 154, 115, 124, 1 }, - { 190, 83, 147, 186, 180, 95, 1 }, - { 159, 103, 44, 159, 230, 107, 0 }, }, - { { 254, 73, 163, 120, 194, 52, 0 }, - { 145, 178, 228, 63, 22, 98, 1 }, - { 22, 33, 143, 98, 201, 63, 1 }, - { 163, 52, 126, 19, 166, 196, 1 }, }, - { { 253, 132, 226, 198, 107, 168, 1 }, - { 75, 26, 213, 170, 187, 176, 1 }, - { 138, 235, 49, 163, 144, 223, 1 }, - { 134, 238, 170, 213, 172, 105, 0 }, }, - { { 252, 149, 79, 148, 230, 207, 0 }, - { 125, 31, 45, 185, 127, 36, 0 }, - { 121, 179, 148, 249, 84, 159, 1 }, - { 18, 127, 78, 218, 124, 95, 0 }, }, - { { 254, 165, 35, 88, 48, 164, 1 }, - { 219, 10, 198, 25, 30, 35, 1 }, - { 146, 134, 13, 98, 82, 191, 1 }, - { 226, 60, 76, 49, 168, 109, 1 }, }, - { { 254, 168, 217, 255, 176, 118, 0 }, - { 189, 226, 111, 219, 153, 99, 1 }, - { 55, 6, 255, 205, 138, 191, 1 }, - { 227, 76, 237, 251, 35, 222, 1 }, }, - { { 255, 195, 135, 255, 162, 252, 1 }, - { 159, 42, 100, 255, 191, 126, 1 }, - { 159, 162, 255, 240, 225, 255, 1 }, - { 191, 126, 255, 147, 42, 124, 1 }, }, - { { 95, 114, 124, 241, 17, 248, 0 }, - { 181, 107, 215, 196, 48, 93, 1 }, - { 15, 196, 71, 159, 39, 125, 0 }, - { 221, 6, 17, 245, 235, 86, 1 }, }, - { { 135, 81, 63, 164, 61, 190, 1 }, - { 171, 105, 152, 149, 103, 245, 1 }, - { 190, 222, 18, 254, 69, 112, 1 }, - { 215, 243, 84, 140, 203, 106, 1 }, }, - { { 198, 147, 77, 53, 31, 189, 0 }, - { 177, 45, 17, 113, 125, 237, 1 }, - { 94, 252, 86, 89, 100, 177, 1 }, - { 219, 223, 71, 68, 90, 70, 1 }, }, - { { 231, 230, 150, 226, 6, 159, 1 }, - { 195, 108, 74, 190, 250, 124, 0 }, - { 252, 176, 35, 180, 179, 243, 1 }, - { 31, 47, 190, 169, 27, 97, 1 }, }, }; -} diff --git a/modules/aruco/test/test_aruco_tutorial.cpp b/modules/aruco/test/test_aruco_tutorial.cpp new file mode 100644 index 0000000000..b8d743d423 --- /dev/null +++ b/modules/aruco/test/test_aruco_tutorial.cpp @@ -0,0 +1,216 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" +#include "opencv2/objdetect/aruco_detector.hpp" + +namespace opencv_test { namespace { + + +TEST(CV_ArucoTutorial, can_find_singlemarkersoriginal) +{ + string img_path = cvtest::findDataFile("singlemarkersoriginal.jpg", false); + Mat image = imread(img_path); + aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); + + vector ids; + vector > corners, rejected; + const size_t N = 6ull; + // corners of ArUco markers with indices goldCornersIds + const int goldCorners[N][8] = { {359,310, 404,310, 410,350, 362,350}, {427,255, 469,256, 477,289, 434,288}, + {233,273, 190,273, 196,241, 237,241}, {298,185, 334,186, 335,212, 297,211}, + {425,163, 430,186, 394,186, 390,162}, {195,155, 230,155, 227,178, 190,178} }; + const int goldCornersIds[N] = { 40, 98, 62, 23, 124, 203}; + map mapGoldCorners; + for (size_t i = 0; i < N; i++) + mapGoldCorners[goldCornersIds[i]] = goldCorners[i]; + + detector.detectMarkers(image, corners, ids, rejected); + + ASSERT_EQ(N, ids.size()); + for (size_t i = 0; i < N; i++) + { + int arucoId = ids[i]; + ASSERT_EQ(4ull, corners[i].size()); + ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); + for (int j = 0; j < 4; j++) + { + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f); + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f); + } + } +} + +TEST(CV_ArucoTutorial, can_find_gboriginal) +{ + string imgPath = cvtest::findDataFile("gboriginal.png", false); + Mat image = imread(imgPath); + string dictPath = cvtest::findDataFile("tutorial_dict.yml", false); + Ptr dictionary = makePtr(); + + FileStorage fs(dictPath, FileStorage::READ); + dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml + Ptr detectorParams = aruco::DetectorParameters::create(); + + aruco::ArucoDetector detector(dictionary, detectorParams); + + vector ids; + vector > corners, rejected; + const size_t N = 35ull; + // corners of ArUco markers with indices 0, 1, ..., 34 + const int goldCorners[N][8] = { {252,74, 286,81, 274,102, 238,95}, {295,82, 330,89, 319,111, 282,104}, + {338,91, 375,99, 365,121, 327,113}, {383,100, 421,107, 412,130, 374,123}, + {429,109, 468,116, 461,139, 421,132}, {235,100, 270,108, 257,130, 220,122}, + {279,109, 316,117, 304,140, 266,133}, {324,119, 362,126, 352,150, 313,143}, + {371,128, 410,136, 400,161, 360,152}, {418,139, 459,145, 451,170, 410,163}, + {216,128, 253,136, 239,161, 200,152}, {262,138, 300,146, 287,172, 248,164}, + {309,148, 349,156, 337,183, 296,174}, {358,158, 398,167, 388,194, 346,185}, + {407,169, 449,176, 440,205, 397,196}, {196,158, 235,168, 218,195, 179,185}, + {243,170, 283,178, 269,206, 228,197}, {293,180, 334,190, 321,218, 279,209}, + {343,192, 385,200, 374,230, 330,220}, {395,203, 438,211, 429,241, 384,233}, + {174,192, 215,201, 197,231, 156,221}, {223,204, 265,213, 249,244, 207,234}, + {275,215, 317,225, 303,257, 259,246}, {327,227, 371,238, 359,270, 313,259}, + {381,240, 426,249, 416,282, 369,273}, {151,228, 193,238, 173,271, 130,260}, + {202,241, 245,251, 228,285, 183,274}, {255,254, 300,264, 284,299, 238,288}, + {310,267, 355,278, 342,314, 295,302}, {366,281, 413,290, 402,327, 353,317}, + {125,267, 168,278, 147,314, 102,303}, {178,281, 223,293, 204,330, 157,317}, + {233,296, 280,307, 263,346, 214,333}, {291,310, 338,322, 323,363, 274,349}, + {349,325, 399,336, 386,378, 335,366} }; + map mapGoldCorners; + for (int i = 0; i < static_cast(N); i++) + mapGoldCorners[i] = goldCorners[i]; + + detector.detectMarkers(image, corners, ids, rejected); + + ASSERT_EQ(N, ids.size()); + for (size_t i = 0; i < N; i++) + { + int arucoId = ids[i]; + ASSERT_EQ(4ull, corners[i].size()); + ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); + for (int j = 0; j < 4; j++) + { + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j*2]), corners[i][j].x, 1.f); + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j*2+1]), corners[i][j].y, 1.f); + } + } +} + +TEST(CV_ArucoTutorial, can_find_choriginal) +{ + string imgPath = cvtest::findDataFile("choriginal.jpg", false); + Mat image = imread(imgPath); + aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); + + vector< int > ids; + vector< vector< Point2f > > corners, rejected; + const size_t N = 17ull; + // corners of aruco markers with indices goldCornersIds + const int goldCorners[N][8] = { {268,77, 290,80, 286,97, 263,94}, {360,90, 382,93, 379,111, 357,108}, + {211,106, 233,109, 228,127, 205,123}, {306,120, 328,124, 325,142, 302,138}, + {402,135, 425,139, 423,157, 400,154}, {247,152, 271,155, 267,174, 242,171}, + {347,167, 371,171, 369,191, 344,187}, {185,185, 209,189, 203,210, 178,206}, + {288,201, 313,206, 309,227, 284,223}, {393,218, 418,222, 416,245, 391,241}, + {223,240, 250,244, 244,268, 217,263}, {333,258, 359,262, 356,286, 329,282}, + {152,281, 179,285, 171,312, 143,307}, {267,300, 294,305, 289,331, 261,327}, + {383,319, 410,324, 408,351, 380,347}, {194,347, 223,352, 216,382, 186,377}, + {315,368, 345,373, 341,403, 310,398} }; + map mapGoldCorners; + for (int i = 0; i < static_cast(N); i++) + mapGoldCorners[i] = goldCorners[i]; + + detector.detectMarkers(image, corners, ids, rejected); + + ASSERT_EQ(N, ids.size()); + for (size_t i = 0; i < N; i++) + { + int arucoId = ids[i]; + ASSERT_EQ(4ull, corners[i].size()); + ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); + for (int j = 0; j < 4; j++) + { + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f); + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f); + } + } +} + +TEST(CV_ArucoTutorial, can_find_chocclusion) +{ + string imgPath = cvtest::findDataFile("chocclusion_original.jpg", false); + Mat image = imread(imgPath); + aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); + + vector< int > ids; + vector< vector< Point2f > > corners, rejected; + const size_t N = 13ull; + // corners of aruco markers with indices goldCornersIds + const int goldCorners[N][8] = { {301,57, 322,62, 317,79, 295,73}, {391,80, 413,85, 408,103, 386,97}, + {242,79, 264,85, 256,102, 234,96}, {334,103, 357,109, 352,126, 329,121}, + {428,129, 451,134, 448,152, 425,146}, {274,128, 296,134, 290,153, 266,147}, + {371,154, 394,160, 390,180, 366,174}, {208,155, 232,161, 223,181, 199,175}, + {309,182, 333,188, 327,209, 302,203}, {411,210, 436,216, 432,238, 407,231}, + {241,212, 267,219, 258,242, 232,235}, {167,244, 194,252, 183,277, 156,269}, + {202,314, 230,322, 220,349, 191,341} }; + map mapGoldCorners; + const int goldCornersIds[N] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15}; + for (int i = 0; i < static_cast(N); i++) + mapGoldCorners[goldCornersIds[i]] = goldCorners[i]; + + detector.detectMarkers(image, corners, ids, rejected); + + ASSERT_EQ(N, ids.size()); + for (size_t i = 0; i < N; i++) + { + int arucoId = ids[i]; + ASSERT_EQ(4ull, corners[i].size()); + ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); + for (int j = 0; j < 4; j++) + { + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f); + EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f); + } + } +} + +TEST(CV_ArucoTutorial, can_find_diamondmarkers) +{ + string imgPath = cvtest::findDataFile("diamondmarkers.png", false); + Mat image = imread(imgPath); + + string dictPath = cvtest::findDataFile("tutorial_dict.yml", false); + Ptr dictionary = makePtr(); + FileStorage fs(dictPath, FileStorage::READ); + dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml + + string detectorPath = cvtest::findDataFile("detector_params.yml", false); + fs = FileStorage(detectorPath, FileStorage::READ); + Ptr detectorParams = aruco::DetectorParameters::create(); + detectorParams->readDetectorParameters(fs.root()); + detectorParams->cornerRefinementMethod = 3; + + aruco::ArucoDetector detector(dictionary, detectorParams); + + vector< int > ids; + vector< vector< Point2f > > corners, rejected; + const size_t N = 12ull; + // corner indices of ArUco markers + const int goldCornersIds[N] = { 4, 12, 11, 3, 12, 10, 12, 10, 10, 11, 2, 11 }; + map counterGoldCornersIds; + for (int i = 0; i < static_cast(N); i++) + counterGoldCornersIds[goldCornersIds[i]]++; + + detector.detectMarkers(image, corners, ids, rejected); + map counterRes; + for (size_t i = 0; i < N; i++) + { + int arucoId = ids[i]; + counterRes[arucoId]++; + } + + ASSERT_EQ(N, ids.size()); + EXPECT_EQ(counterGoldCornersIds, counterRes); // check the number of ArUco markers +} + +}} // namespace diff --git a/modules/aruco/test/test_arucodetection.cpp b/modules/aruco/test/test_arucodetection.cpp deleted file mode 100644 index 78ec99cc05..0000000000 --- a/modules/aruco/test/test_arucodetection.cpp +++ /dev/null @@ -1,814 +0,0 @@ -/* -By downloading, copying, installing or using the software you agree to this -license. If you do not agree to this license, do not download, install, -copy or use the software. - - License Agreement - For Open Source Computer Vision Library - (3-clause BSD License) - -Copyright (C) 2013, OpenCV Foundation, all rights reserved. -Third party copyrights are property of their respective owners. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the names of the copyright holders nor the names of the contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -This software is provided by the copyright holders and contributors "as is" and -any express or implied warranties, including, but not limited to, the implied -warranties of merchantability and fitness for a particular purpose are -disclaimed. In no event shall copyright holders or contributors be liable for -any direct, indirect, incidental, special, exemplary, or consequential damages -(including, but not limited to, procurement of substitute goods or services; -loss of use, data, or profits; or business interruption) however caused -and on any theory of liability, whether in contract, strict liability, -or tort (including negligence or otherwise) arising in any way out of -the use of this software, even if advised of the possibility of such damage. -*/ - -#include "test_precomp.hpp" -#include - -namespace opencv_test { namespace { - -/** - * @brief Draw 2D synthetic markers and detect them - */ -class CV_ArucoDetectionSimple : public cvtest::BaseTest { - public: - CV_ArucoDetectionSimple(); - - protected: - void run(int); -}; - - -CV_ArucoDetectionSimple::CV_ArucoDetectionSimple() {} - - -void CV_ArucoDetectionSimple::run(int) { - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); - - // 20 images - for(int i = 0; i < 20; i++) { - - const int markerSidePixels = 100; - int imageSize = markerSidePixels * 2 + 3 * (markerSidePixels / 2); - - // draw synthetic image and store marker corners and ids - vector< vector< Point2f > > groundTruthCorners; - vector< int > groundTruthIds; - Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255)); - for(int y = 0; y < 2; y++) { - for(int x = 0; x < 2; x++) { - Mat marker; - int id = i * 4 + y * 2 + x; - aruco::drawMarker(detector.dictionary, id, markerSidePixels, marker); - Point2f firstCorner = - Point2f(markerSidePixels / 2.f + x * (1.5f * markerSidePixels), - markerSidePixels / 2.f + y * (1.5f * markerSidePixels)); - Mat aux = img.colRange((int)firstCorner.x, (int)firstCorner.x + markerSidePixels) - .rowRange((int)firstCorner.y, (int)firstCorner.y + markerSidePixels); - marker.copyTo(aux); - groundTruthIds.push_back(id); - groundTruthCorners.push_back(vector< Point2f >()); - groundTruthCorners.back().push_back(firstCorner); - groundTruthCorners.back().push_back(firstCorner + Point2f(markerSidePixels - 1, 0)); - groundTruthCorners.back().push_back( - firstCorner + Point2f(markerSidePixels - 1, markerSidePixels - 1)); - groundTruthCorners.back().push_back(firstCorner + Point2f(0, markerSidePixels - 1)); - } - } - if(i % 2 == 1) img.convertTo(img, CV_8UC3); - - // detect markers - vector< vector< Point2f > > corners; - vector< int > ids; - - detector.detectMarkers(img, corners, ids); - - // check detection results - for(unsigned int m = 0; m < groundTruthIds.size(); m++) { - int idx = -1; - for(unsigned int k = 0; k < ids.size(); k++) { - if(groundTruthIds[m] == ids[k]) { - idx = (int)k; - break; - } - } - if(idx == -1) { - ts->printf(cvtest::TS::LOG, "Marker not detected"); - ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); - return; - } - - for(int c = 0; c < 4; c++) { - double dist = cv::norm(groundTruthCorners[m][c] - corners[idx][c]); // TODO cvtest - if(dist > 0.001) { - ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } - } - } -} - - -static double deg2rad(double deg) { return deg * CV_PI / 180.; } - -/** - * @brief Get rvec and tvec from yaw, pitch and distance - */ -static void getSyntheticRT(double yaw, double pitch, double distance, Mat &rvec, Mat &tvec) { - - rvec = Mat(3, 1, CV_64FC1); - tvec = Mat(3, 1, CV_64FC1); - - // Rvec - // first put the Z axis aiming to -X (like the camera axis system) - Mat rotZ(3, 1, CV_64FC1); - rotZ.ptr< double >(0)[0] = 0; - rotZ.ptr< double >(0)[1] = 0; - rotZ.ptr< double >(0)[2] = -0.5 * CV_PI; - - Mat rotX(3, 1, CV_64FC1); - rotX.ptr< double >(0)[0] = 0.5 * CV_PI; - rotX.ptr< double >(0)[1] = 0; - rotX.ptr< double >(0)[2] = 0; - - Mat camRvec, camTvec; - composeRT(rotZ, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotX, Mat(3, 1, CV_64FC1, Scalar::all(0)), - camRvec, camTvec); - - // now pitch and yaw angles - Mat rotPitch(3, 1, CV_64FC1); - rotPitch.ptr< double >(0)[0] = 0; - rotPitch.ptr< double >(0)[1] = pitch; - rotPitch.ptr< double >(0)[2] = 0; - - Mat rotYaw(3, 1, CV_64FC1); - rotYaw.ptr< double >(0)[0] = yaw; - rotYaw.ptr< double >(0)[1] = 0; - rotYaw.ptr< double >(0)[2] = 0; - - composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw, - Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec); - - // compose both rotations - composeRT(camRvec, Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, - Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec); - - // Tvec, just move in z (camera) direction the specific distance - tvec.ptr< double >(0)[0] = 0.; - tvec.ptr< double >(0)[1] = 0.; - tvec.ptr< double >(0)[2] = distance; -} - -/** - * @brief Create a synthetic image of a marker with perspective - */ -static Mat projectMarker(Ptr &dictionary, int id, Mat cameraMatrix, double yaw, - double pitch, double distance, Size imageSize, int markerBorder, - vector< Point2f > &corners, int encloseMarker=0) { - - // canonical image - Mat marker, markerImg; - const int markerSizePixels = 100; - - aruco::drawMarker(dictionary, id, markerSizePixels, marker, markerBorder); - marker.copyTo(markerImg); - - if(encloseMarker){ //to enclose the marker - int enclose = int(marker.rows/4); - markerImg = Mat::zeros(marker.rows+(2*enclose), marker.cols+(enclose*2), CV_8UC1); - - Mat field= markerImg.rowRange(int(enclose), int(markerImg.rows-enclose)) - .colRange(int(0), int(markerImg.cols)); - field.setTo(255); - field= markerImg.rowRange(int(0), int(markerImg.rows)) - .colRange(int(enclose), int(markerImg.cols-enclose)); - field.setTo(255); - - field = markerImg(Rect(enclose,enclose,marker.rows,marker.cols)); - marker.copyTo(field); - } - - // get rvec and tvec for the perspective - Mat rvec, tvec; - getSyntheticRT(yaw, pitch, distance, rvec, tvec); - - const float markerLength = 0.05f; - vector< Point3f > markerObjPoints; - markerObjPoints.push_back(Point3f(-markerLength / 2.f, +markerLength / 2.f, 0)); - markerObjPoints.push_back(markerObjPoints[0] + Point3f(markerLength, 0, 0)); - markerObjPoints.push_back(markerObjPoints[0] + Point3f(markerLength, -markerLength, 0)); - markerObjPoints.push_back(markerObjPoints[0] + Point3f(0, -markerLength, 0)); - - // project markers and draw them - Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); - projectPoints(markerObjPoints, rvec, tvec, cameraMatrix, distCoeffs, corners); - - vector< Point2f > originalCorners; - originalCorners.push_back(Point2f(0+float(encloseMarker*markerSizePixels/4), 0+float(encloseMarker*markerSizePixels/4))); - originalCorners.push_back(originalCorners[0]+Point2f((float)markerSizePixels, 0)); - originalCorners.push_back(originalCorners[0]+Point2f((float)markerSizePixels, (float)markerSizePixels)); - originalCorners.push_back(originalCorners[0]+Point2f(0, (float)markerSizePixels)); - - Mat transformation = getPerspectiveTransform(originalCorners, corners); - - Mat img(imageSize, CV_8UC1, Scalar::all(255)); - Mat aux; - const char borderValue = 127; - warpPerspective(markerImg, aux, transformation, imageSize, INTER_NEAREST, BORDER_CONSTANT, - Scalar::all(borderValue)); - - // copy only not-border pixels - for(int y = 0; y < aux.rows; y++) { - for(int x = 0; x < aux.cols; x++) { - if(aux.at< unsigned char >(y, x) == borderValue) continue; - img.at< unsigned char >(y, x) = aux.at< unsigned char >(y, x); - } - } - - return img; -} - -enum class ArucoAlgParams -{ - USE_DEFAULT = 0, - USE_APRILTAG=1, /// Detect marker candidates :: using AprilTag - DETECT_INVERTED_MARKER, /// Check if there is a white marker - USE_ARUCO3 /// Check if aruco3 should be used -}; - - -/** - * @brief Draws markers in perspective and detect them - */ -class CV_ArucoDetectionPerspective : public cvtest::BaseTest { - public: - CV_ArucoDetectionPerspective(ArucoAlgParams arucoAlgParam) : arucoAlgParams(arucoAlgParam) {} - - protected: - void run(int); - ArucoAlgParams arucoAlgParams; -}; - - -void CV_ArucoDetectionPerspective::run(int) { - - int iter = 0; - int szEnclosed = 0; - Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); - Size imgSize(500, 500); - cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; - cameraMatrix.at< double >(0, 2) = imgSize.width / 2; - cameraMatrix.at< double >(1, 2) = imgSize.height / 2; - Ptr params = aruco::DetectorParameters::create(); - params->minDistanceToBorder = 1; - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); - - // detect from different positions - for(double distance = 0.1; distance < 0.7; distance += 0.2) { - for(int pitch = 0; pitch < 360; pitch += (distance == 0.1? 60:180)) { - for(int yaw = 70; yaw <= 120; yaw += 40){ - int currentId = iter % 250; - int markerBorder = iter % 2 + 1; - iter++; - vector< Point2f > groundTruthCorners; - - params->markerBorderBits = markerBorder; - - /// create synthetic image - Mat img= - projectMarker(detector.dictionary, currentId, cameraMatrix, deg2rad(yaw), deg2rad(pitch), - distance, imgSize, markerBorder, groundTruthCorners, szEnclosed); - // marker :: Inverted - if(ArucoAlgParams::DETECT_INVERTED_MARKER == arucoAlgParams){ - img = ~img; - params->detectInvertedMarker = true; - } - - if(ArucoAlgParams::USE_APRILTAG == arucoAlgParams){ - params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG; - } - - if (ArucoAlgParams::USE_ARUCO3 == arucoAlgParams) { - params->useAruco3Detection = true; - params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; - } - - // detect markers - vector< vector< Point2f > > corners; - vector< int > ids; - detector.detectMarkers(img, corners, ids); - - // check results - if(ids.size() != 1 || (ids.size() == 1 && ids[0] != currentId)) { - if(ids.size() != 1) - ts->printf(cvtest::TS::LOG, "Incorrect number of detected markers"); - else - ts->printf(cvtest::TS::LOG, "Incorrect marker id"); - ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); - return; - } - for(int c = 0; c < 4; c++) { - double dist = cv::norm(groundTruthCorners[c] - corners[0][c]); // TODO cvtest - if(dist > 5) { - ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } - } - } - // change the state :: to detect an enclosed inverted marker - if(ArucoAlgParams::DETECT_INVERTED_MARKER == arucoAlgParams && distance == 0.1){ - distance -= 0.1; - szEnclosed++; - } - } -} - - -/** - * @brief Check max and min size in marker detection parameters - */ -class CV_ArucoDetectionMarkerSize : public cvtest::BaseTest { - public: - CV_ArucoDetectionMarkerSize(); - - protected: - void run(int); -}; - - -CV_ArucoDetectionMarkerSize::CV_ArucoDetectionMarkerSize() {} - - -void CV_ArucoDetectionMarkerSize::run(int) { - Ptr params = aruco::DetectorParameters::create(); - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); - int markerSide = 20; - int imageSize = 200; - - // 10 cases - for(int i = 0; i < 10; i++) { - Mat marker; - int id = 10 + i * 20; - - // create synthetic image - Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255)); - aruco::drawMarker(detector.dictionary, id, markerSide, marker); - Mat aux = img.colRange(30, 30 + markerSide).rowRange(50, 50 + markerSide); - marker.copyTo(aux); - - vector< vector< Point2f > > corners; - vector< int > ids; - - // set a invalid minMarkerPerimeterRate - params->minMarkerPerimeterRate = min(4., (4. * markerSide) / float(imageSize) + 0.1); - detector.detectMarkers(img, corners, ids); - if(corners.size() != 0) { - ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::minMarkerPerimeterRate"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - - // set an valid minMarkerPerimeterRate - params->minMarkerPerimeterRate = max(0., (4. * markerSide) / float(imageSize) - 0.1); - detector.detectMarkers(img, corners, ids); - if(corners.size() != 1 || (corners.size() == 1 && ids[0] != id)) { - ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::minMarkerPerimeterRate"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - - // set a invalid maxMarkerPerimeterRate - params->maxMarkerPerimeterRate = min(4., (4. * markerSide) / float(imageSize) - 0.1); - detector.detectMarkers(img, corners, ids); - if(corners.size() != 0) { - ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::maxMarkerPerimeterRate"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - - // set an valid maxMarkerPerimeterRate - params->maxMarkerPerimeterRate = max(0., (4. * markerSide) / float(imageSize) + 0.1); - detector.detectMarkers(img, corners, ids); - if(corners.size() != 1 || (corners.size() == 1 && ids[0] != id)) { - ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::maxMarkerPerimeterRate"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } -} - - -/** - * @brief Check error correction in marker bits - */ -class CV_ArucoBitCorrection : public cvtest::BaseTest { - public: - CV_ArucoBitCorrection(); - - protected: - void run(int); -}; - - -CV_ArucoBitCorrection::CV_ArucoBitCorrection() {} - - -void CV_ArucoBitCorrection::run(int) { - - Ptr _dictionary1 = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr _dictionary2 = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - aruco::Dictionary &dictionary1 = *_dictionary1; - aruco::Dictionary &dictionary2 = *_dictionary2; - Ptr params = aruco::DetectorParameters::create(); - aruco::ArucoDetector detector1(_dictionary1, params); - int markerSide = 50; - int imageSize = 150; - - // 10 markers - for(int l = 0; l < 10; l++) { - Mat marker; - int id = 10 + l * 20; - - Mat currentCodeBytes = dictionary1.bytesList.rowRange(id, id + 1); - - // 5 valid cases - for(int i = 0; i < 5; i++) { - // how many bit errors (the error is low enough so it can be corrected) - params->errorCorrectionRate = 0.2 + i * 0.1; - int errors = - (int)std::floor(dictionary1.maxCorrectionBits * params->errorCorrectionRate - 1.); - - // create erroneous marker in currentCodeBits - Mat currentCodeBits = - aruco::Dictionary::getBitsFromByteList(currentCodeBytes, dictionary1.markerSize); - for(int e = 0; e < errors; e++) { - currentCodeBits.ptr< unsigned char >()[2 * e] = - !currentCodeBits.ptr< unsigned char >()[2 * e]; - } - - // add erroneous marker to dictionary2 in order to create the erroneous marker image - Mat currentCodeBytesError = aruco::Dictionary::getByteListFromBits(currentCodeBits); - currentCodeBytesError.copyTo(dictionary2.bytesList.rowRange(id, id + 1)); - Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255)); - dictionary2.drawMarker(id, markerSide, marker); - Mat aux = img.colRange(30, 30 + markerSide).rowRange(50, 50 + markerSide); - marker.copyTo(aux); - - // try to detect using original dictionary - vector< vector< Point2f > > corners; - vector< int > ids; - detector1.detectMarkers(img, corners, ids); - if(corners.size() != 1 || (corners.size() == 1 && ids[0] != id)) { - ts->printf(cvtest::TS::LOG, "Error in bit correction"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } - - // 5 invalid cases - for(int i = 0; i < 5; i++) { - // how many bit errors (the error is too high to be corrected) - params->errorCorrectionRate = 0.2 + i * 0.1; - int errors = - (int)std::floor(dictionary1.maxCorrectionBits * params->errorCorrectionRate + 1.); - - // create erroneous marker in currentCodeBits - Mat currentCodeBits = - aruco::Dictionary::getBitsFromByteList(currentCodeBytes, dictionary1.markerSize); - for(int e = 0; e < errors; e++) { - currentCodeBits.ptr< unsigned char >()[2 * e] = - !currentCodeBits.ptr< unsigned char >()[2 * e]; - } - - // dictionary3 is only composed by the modified marker (in its original form) - Ptr _dictionary3 = makePtr( - dictionary2.bytesList.rowRange(id, id + 1).clone(), - dictionary1.markerSize, - dictionary1.maxCorrectionBits); - aruco::ArucoDetector detector3(_dictionary3, params); - // add erroneous marker to dictionary2 in order to create the erroneous marker image - Mat currentCodeBytesError = aruco::Dictionary::getByteListFromBits(currentCodeBits); - currentCodeBytesError.copyTo(dictionary2.bytesList.rowRange(id, id + 1)); - Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255)); - dictionary2.drawMarker(id, markerSide, marker); - Mat aux = img.colRange(30, 30 + markerSide).rowRange(50, 50 + markerSide); - marker.copyTo(aux); - - // try to detect using dictionary3, it should fail - vector< vector< Point2f > > corners; - vector< int > ids; - detector3.detectMarkers(img, corners, ids); - if(corners.size() != 0) { - ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::errorCorrectionRate"); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } - } -} - -typedef CV_ArucoDetectionPerspective CV_AprilTagDetectionPerspective; -typedef CV_ArucoDetectionPerspective CV_InvertedArucoDetectionPerspective; -typedef CV_ArucoDetectionPerspective CV_Aruco3DetectionPerspective; - -TEST(CV_InvertedArucoDetectionPerspective, algorithmic) { - CV_InvertedArucoDetectionPerspective test(ArucoAlgParams::DETECT_INVERTED_MARKER); - test.safe_run(); -} - -TEST(CV_AprilTagDetectionPerspective, algorithmic) { - CV_AprilTagDetectionPerspective test(ArucoAlgParams::USE_APRILTAG); - test.safe_run(); -} - -TEST(CV_Aruco3DetectionPerspective, algorithmic) { - CV_Aruco3DetectionPerspective test(ArucoAlgParams::USE_ARUCO3); - test.safe_run(); -} - -TEST(CV_ArucoDetectionSimple, algorithmic) { - CV_ArucoDetectionSimple test; - test.safe_run(); -} - -TEST(CV_ArucoDetectionPerspective, algorithmic) { - CV_ArucoDetectionPerspective test(ArucoAlgParams::USE_DEFAULT); - test.safe_run(); -} - -TEST(CV_ArucoDetectionMarkerSize, algorithmic) { - CV_ArucoDetectionMarkerSize test; - test.safe_run(); -} - -TEST(CV_ArucoBitCorrection, algorithmic) { - CV_ArucoBitCorrection test; - test.safe_run(); -} - -TEST(CV_ArucoTutorial, can_find_singlemarkersoriginal) -{ - string img_path = cvtest::findDataFile("singlemarkersoriginal.jpg", false); - Mat image = imread(img_path); - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); - - vector< int > ids; - vector< vector< Point2f > > corners, rejected; - const size_t N = 6ull; - // corners of ArUco markers with indices goldCornersIds - const int goldCorners[N][8] = { {359,310, 404,310, 410,350, 362,350}, {427,255, 469,256, 477,289, 434,288}, - {233,273, 190,273, 196,241, 237,241}, {298,185, 334,186, 335,212, 297,211}, - {425,163, 430,186, 394,186, 390,162}, {195,155, 230,155, 227,178, 190,178} }; - const int goldCornersIds[N] = { 40, 98, 62, 23, 124, 203}; - map mapGoldCorners; - for (size_t i = 0; i < N; i++) - mapGoldCorners[goldCornersIds[i]] = goldCorners[i]; - - detector.detectMarkers(image, corners, ids, rejected); - - ASSERT_EQ(N, ids.size()); - for (size_t i = 0; i < N; i++) - { - int arucoId = ids[i]; - ASSERT_EQ(4ull, corners[i].size()); - ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); - for (int j = 0; j < 4; j++) - { - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f); - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f); - } - } -} - -TEST(CV_ArucoTutorial, can_find_gboriginal) -{ - string imgPath = cvtest::findDataFile("gboriginal.png", false); - Mat image = imread(imgPath); - string dictPath = cvtest::findDataFile("tutorial_dict.yml", false); - Ptr dictionary = makePtr(); - - FileStorage fs(dictPath, FileStorage::READ); - dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml - Ptr detectorParams = aruco::DetectorParameters::create(); - - aruco::ArucoDetector detector(dictionary, detectorParams); - - vector< int > ids; - vector< vector< Point2f > > corners, rejected; - const size_t N = 35ull; - // corners of ArUco markers with indices 0, 1, ..., 34 - const int goldCorners[N][8] = { {252,74, 286,81, 274,102, 238,95}, {295,82, 330,89, 319,111, 282,104}, - {338,91, 375,99, 365,121, 327,113}, {383,100, 421,107, 412,130, 374,123}, - {429,109, 468,116, 461,139, 421,132}, {235,100, 270,108, 257,130, 220,122}, - {279,109, 316,117, 304,140, 266,133}, {324,119, 362,126, 352,150, 313,143}, - {371,128, 410,136, 400,161, 360,152}, {418,139, 459,145, 451,170, 410,163}, - {216,128, 253,136, 239,161, 200,152}, {262,138, 300,146, 287,172, 248,164}, - {309,148, 349,156, 337,183, 296,174}, {358,158, 398,167, 388,194, 346,185}, - {407,169, 449,176, 440,205, 397,196}, {196,158, 235,168, 218,195, 179,185}, - {243,170, 283,178, 269,206, 228,197}, {293,180, 334,190, 321,218, 279,209}, - {343,192, 385,200, 374,230, 330,220}, {395,203, 438,211, 429,241, 384,233}, - {174,192, 215,201, 197,231, 156,221}, {223,204, 265,213, 249,244, 207,234}, - {275,215, 317,225, 303,257, 259,246}, {327,227, 371,238, 359,270, 313,259}, - {381,240, 426,249, 416,282, 369,273}, {151,228, 193,238, 173,271, 130,260}, - {202,241, 245,251, 228,285, 183,274}, {255,254, 300,264, 284,299, 238,288}, - {310,267, 355,278, 342,314, 295,302}, {366,281, 413,290, 402,327, 353,317}, - {125,267, 168,278, 147,314, 102,303}, {178,281, 223,293, 204,330, 157,317}, - {233,296, 280,307, 263,346, 214,333}, {291,310, 338,322, 323,363, 274,349}, - {349,325, 399,336, 386,378, 335,366} }; - map mapGoldCorners; - for (int i = 0; i < static_cast(N); i++) - mapGoldCorners[i] = goldCorners[i]; - - detector.detectMarkers(image, corners, ids, rejected); - - - ASSERT_EQ(N, ids.size()); - for (size_t i = 0; i < N; i++) - { - int arucoId = ids[i]; - ASSERT_EQ(4ull, corners[i].size()); - ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); - for (int j = 0; j < 4; j++) - { - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j*2]), corners[i][j].x, 1.f); - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j*2+1]), corners[i][j].y, 1.f); - } - } -} - -TEST(CV_ArucoDetectMarkers, regression_3192) -{ - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_4X4_50)); - vector< int > markerIds; - vector > markerCorners; - string imgPath = cvtest::findDataFile("aruco/regression_3192.png"); - Mat image = imread(imgPath); - const size_t N = 2ull; - const int goldCorners[N][8] = { {345,120, 520,120, 520,295, 345,295}, {101,114, 270,112, 276,287, 101,287} }; - const int goldCornersIds[N] = { 6, 4 }; - map mapGoldCorners; - for (size_t i = 0; i < N; i++) - mapGoldCorners[goldCornersIds[i]] = goldCorners[i]; - - detector.detectMarkers(image, markerCorners, markerIds); - - ASSERT_EQ(N, markerIds.size()); - for (size_t i = 0; i < N; i++) - { - int arucoId = markerIds[i]; - ASSERT_EQ(4ull, markerCorners[i].size()); - ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); - for (int j = 0; j < 4; j++) - { - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), markerCorners[i][j].x, 1.f); - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), markerCorners[i][j].y, 1.f); - } - } -} - -TEST(CV_ArucoDetectMarkers, regression_2492) -{ - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_5X5_50)); - detector.params->minMarkerDistanceRate = 0.026; - vector< int > markerIds; - vector > markerCorners; - string imgPath = cvtest::findDataFile("aruco/regression_2492.png"); - Mat image = imread(imgPath); - const size_t N = 8ull; - const int goldCorners[N][8] = { {179,139, 179,95, 223,95, 223,139}, {99,139, 99,95, 143,95, 143,139}, - {19,139, 19,95, 63,95, 63,139}, {256,140, 256,93, 303,93, 303,140}, - {256,62, 259,21, 300,23, 297,64}, {99,21, 143,17, 147,60, 103,64}, - {69,61, 28,61, 14,21, 58,17}, {174,62, 182,13, 230,19, 223,68} }; - const int goldCornersIds[N] = {13, 13, 13, 13, 1, 15, 14, 4}; - map > mapGoldCorners; - for (size_t i = 0; i < N; i++) - mapGoldCorners[goldCornersIds[i]].push_back(goldCorners[i]); - - detector.detectMarkers(image, markerCorners, markerIds); - - ASSERT_EQ(N, markerIds.size()); - for (size_t i = 0; i < N; i++) - { - int arucoId = markerIds[i]; - ASSERT_EQ(4ull, markerCorners[i].size()); - ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); - float totalDist = 8.f; - for (size_t k = 0ull; k < mapGoldCorners[arucoId].size(); k++) - { - float dist = 0.f; - for (int j = 0; j < 4; j++) // total distance up to 4 points - { - dist += abs(mapGoldCorners[arucoId][k][j * 2] - markerCorners[i][j].x); - dist += abs(mapGoldCorners[arucoId][k][j * 2 + 1] - markerCorners[i][j].y); - } - totalDist = min(totalDist, dist); - } - EXPECT_LT(totalDist, 8.f); - } -} - -struct ArucoThreading: public testing::TestWithParam -{ - struct NumThreadsSetter { - NumThreadsSetter(const int num_threads) - : original_num_threads_(cv::getNumThreads()) { - cv::setNumThreads(num_threads); - } - - ~NumThreadsSetter() { - cv::setNumThreads(original_num_threads_); - } - private: - int original_num_threads_; - }; -}; - -TEST_P(ArucoThreading, number_of_threads_does_not_change_results) -{ - // We are not testing against different dictionaries - // As we are interested mostly in small images, smaller - // markers is better -> 4x4 - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_4X4_50)); - - // Height of the test image can be chosen quite freely - // We aim to test against small images as in those the - // number of threads has most effect - const int height_img = 20; - // Just to get nice white boarder - const int shift = height_img > 10 ? 5 : 1; - const int height_marker = height_img-2*shift; - - // Create a test image - cv::Mat img_marker; - cv::aruco::drawMarker(detector.dictionary, 23, height_marker, img_marker, 1); - - // Copy to bigger image to get a white border - cv::Mat img(height_img, height_img, CV_8UC1, cv::Scalar(255)); - img_marker.copyTo(img(cv::Rect(shift, shift, height_marker, height_marker))); - - detector.params->cornerRefinementMethod = GetParam(); - - std::vector > original_corners; - std::vector original_ids; - { - NumThreadsSetter thread_num_setter(1); - detector.detectMarkers(img, original_corners, original_ids); - } - - ASSERT_EQ(original_ids.size(), 1ull); - ASSERT_EQ(original_corners.size(), 1ull); - - int num_threads_to_test[] = { 2, 8, 16, 32, height_img-1, height_img, height_img+1}; - - for (size_t i_num_threads = 0; i_num_threads < sizeof(num_threads_to_test)/sizeof(int); ++i_num_threads) { - NumThreadsSetter thread_num_setter(num_threads_to_test[i_num_threads]); - - std::vector > corners; - std::vector ids; - detector.detectMarkers(img, corners, ids); - - // If we don't find any markers, the test is broken - ASSERT_EQ(ids.size(), 1ull); - - // Make sure we got the same result as the first time - ASSERT_EQ(corners.size(), original_corners.size()); - ASSERT_EQ(ids.size(), original_ids.size()); - ASSERT_EQ(ids.size(), corners.size()); - for (size_t i = 0; i < corners.size(); ++i) { - EXPECT_EQ(ids[i], original_ids[i]); - for (size_t j = 0; j < corners[i].size(); ++j) { - EXPECT_NEAR(corners[i][j].x, original_corners[i][j].x, 0.1f); - EXPECT_NEAR(corners[i][j].y, original_corners[i][j].y, 0.1f); - } - } - } -} - -INSTANTIATE_TEST_CASE_P( - CV_ArucoDetectMarkers, ArucoThreading, - ::testing::Values( - cv::aruco::CORNER_REFINE_NONE, - cv::aruco::CORNER_REFINE_SUBPIX, - cv::aruco::CORNER_REFINE_CONTOUR, - cv::aruco::CORNER_REFINE_APRILTAG - )); - -}} // namespace diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index 5a65c225a7..81512ddec2 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -107,7 +107,7 @@ void CV_ArucoBoardPose::run(int) { // estimate pose Mat rvec, tvec; - aruco::estimatePoseBoard(corners, ids, board, cameraMatrix, distCoeffs, rvec, tvec); + estimatePoseBoard(corners, ids, board, cameraMatrix, distCoeffs, rvec, tvec); // check axes vector axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, gridboard->getRightBottomBorder().x); diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 99b87264df..45e81b0b64 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -185,7 +185,7 @@ void CV_CharucoDetection::run(int) { vector< Point2f > projectedCharucoCorners; // copy chessboardCorners - vector copyChessboardCorners = board->chessboardCorners; + vector copyChessboardCorners = board->getChessboardCorners(); // move copyChessboardCorners points for (size_t i = 0; i < copyChessboardCorners.size(); i++) copyChessboardCorners[i] -= board->getRightBottomBorder() / 2.f; @@ -196,7 +196,7 @@ void CV_CharucoDetection::run(int) { int currentId = charucoIds[i]; - if(currentId >= (int)board->chessboardCorners.size()) { + if(currentId >= (int)board->getChessboardCorners().size()) { ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id"); ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); return; @@ -284,8 +284,7 @@ void CV_CharucoPoseEstimation::run(int) { if(charucoIds.size() == 0) continue; // estimate charuco pose - aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, - distCoeffs, rvec, tvec); + estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec); // check axes @@ -301,14 +300,14 @@ void CV_CharucoPoseEstimation::run(int) { // check estimate result vector< Point2f > projectedCharucoCorners; - projectPoints(board->chessboardCorners, rvec, tvec, cameraMatrix, distCoeffs, + projectPoints(board->getChessboardCorners(), rvec, tvec, cameraMatrix, distCoeffs, projectedCharucoCorners); for(unsigned int i = 0; i < charucoIds.size(); i++) { int currentId = charucoIds[i]; - if(currentId >= (int)board->chessboardCorners.size()) { + if(currentId >= (int)board->getChessboardCorners().size()) { ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id"); ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); return; @@ -415,7 +414,7 @@ void CV_CharucoDiamondDetection::run(int) { vector< Point2f > projectedDiamondCorners; // copy chessboardCorners - vector copyChessboardCorners = board->chessboardCorners; + vector copyChessboardCorners = board->getChessboardCorners(); // move copyChessboardCorners points for (size_t i = 0; i < copyChessboardCorners.size(); i++) copyChessboardCorners[i] -= board->getRightBottomBorder() / 2.f; @@ -500,10 +499,10 @@ void CV_CharucoBoardCreation::run(int) Ptr board_millimeters = aruco::CharucoBoard::create( n, n, squareSize_mm, squareSize_mm * markerSizeFactor, dictionary); - for (size_t i = 0; i < board_meters->nearestMarkerIdx.size(); i++) + for (size_t i = 0; i < board_meters->getNearestMarkerIdx().size(); i++) { - if (board_meters->nearestMarkerIdx[i].size() != board_millimeters->nearestMarkerIdx[i].size() || - board_meters->nearestMarkerIdx[i][0] != board_millimeters->nearestMarkerIdx[i][0]) + if (board_meters->getNearestMarkerIdx()[i].size() != board_millimeters->getNearestMarkerIdx()[i].size() || + board_meters->getNearestMarkerIdx()[i][0] != board_millimeters->getNearestMarkerIdx()[i][0]) { ts->printf(cvtest::TS::LOG, cv::format("Charuco board topology is sensitive to scale with squareSize=%.1f\n", @@ -671,122 +670,6 @@ TEST(Charuco, testBoardSubpixelCoords) EXPECT_NEAR(0, cvtest::norm(expected_corners, c_corners.reshape(1), NORM_INF), 1e-1); } -TEST(CV_ArucoTutorial, can_find_choriginal) -{ - string imgPath = cvtest::findDataFile("choriginal.jpg", false); - Mat image = imread(imgPath); - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); - - vector< int > ids; - vector< vector< Point2f > > corners, rejected; - const size_t N = 17ull; - // corners of aruco markers with indices goldCornersIds - const int goldCorners[N][8] = { {268,77, 290,80, 286,97, 263,94}, {360,90, 382,93, 379,111, 357,108}, - {211,106, 233,109, 228,127, 205,123}, {306,120, 328,124, 325,142, 302,138}, - {402,135, 425,139, 423,157, 400,154}, {247,152, 271,155, 267,174, 242,171}, - {347,167, 371,171, 369,191, 344,187}, {185,185, 209,189, 203,210, 178,206}, - {288,201, 313,206, 309,227, 284,223}, {393,218, 418,222, 416,245, 391,241}, - {223,240, 250,244, 244,268, 217,263}, {333,258, 359,262, 356,286, 329,282}, - {152,281, 179,285, 171,312, 143,307}, {267,300, 294,305, 289,331, 261,327}, - {383,319, 410,324, 408,351, 380,347}, {194,347, 223,352, 216,382, 186,377}, - {315,368, 345,373, 341,403, 310,398} }; - map mapGoldCorners; - for (int i = 0; i < static_cast(N); i++) - mapGoldCorners[i] = goldCorners[i]; - - detector.detectMarkers(image, corners, ids, rejected); - - ASSERT_EQ(N, ids.size()); - for (size_t i = 0; i < N; i++) - { - int arucoId = ids[i]; - ASSERT_EQ(4ull, corners[i].size()); - ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); - for (int j = 0; j < 4; j++) - { - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f); - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f); - } - } -} - -TEST(CV_ArucoTutorial, can_find_chocclusion) -{ - string imgPath = cvtest::findDataFile("chocclusion_original.jpg", false); - Mat image = imread(imgPath); - aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); - - vector< int > ids; - vector< vector< Point2f > > corners, rejected; - const size_t N = 13ull; - // corners of aruco markers with indices goldCornersIds - const int goldCorners[N][8] = { {301,57, 322,62, 317,79, 295,73}, {391,80, 413,85, 408,103, 386,97}, - {242,79, 264,85, 256,102, 234,96}, {334,103, 357,109, 352,126, 329,121}, - {428,129, 451,134, 448,152, 425,146}, {274,128, 296,134, 290,153, 266,147}, - {371,154, 394,160, 390,180, 366,174}, {208,155, 232,161, 223,181, 199,175}, - {309,182, 333,188, 327,209, 302,203}, {411,210, 436,216, 432,238, 407,231}, - {241,212, 267,219, 258,242, 232,235}, {167,244, 194,252, 183,277, 156,269}, - {202,314, 230,322, 220,349, 191,341} }; - map mapGoldCorners; - const int goldCornersIds[N] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15}; - for (int i = 0; i < static_cast(N); i++) - mapGoldCorners[goldCornersIds[i]] = goldCorners[i]; - - detector.detectMarkers(image, corners, ids, rejected); - - ASSERT_EQ(N, ids.size()); - for (size_t i = 0; i < N; i++) - { - int arucoId = ids[i]; - ASSERT_EQ(4ull, corners[i].size()); - ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end()); - for (int j = 0; j < 4; j++) - { - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f); - EXPECT_NEAR(static_cast(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f); - } - } -} - -TEST(CV_ArucoTutorial, can_find_diamondmarkers) -{ - string imgPath = cvtest::findDataFile("diamondmarkers.png", false); - Mat image = imread(imgPath); - - string dictPath = cvtest::findDataFile("tutorial_dict.yml", false); - Ptr dictionary = makePtr(); - FileStorage fs(dictPath, FileStorage::READ); - dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml - - string detectorPath = cvtest::findDataFile("detector_params.yml", false); - fs = FileStorage(detectorPath, FileStorage::READ); - Ptr detectorParams = aruco::DetectorParameters::create(); - detectorParams->readDetectorParameters(fs.root()); - detectorParams->cornerRefinementMethod = 3; - - aruco::ArucoDetector detector(dictionary, detectorParams); - - vector< int > ids; - vector< vector< Point2f > > corners, rejected; - const size_t N = 12ull; - // corner indices of ArUco markers - const int goldCornersIds[N] = { 4, 12, 11, 3, 12, 10, 12, 10, 10, 11, 2, 11 }; - map counterGoldCornersIds; - for (int i = 0; i < static_cast(N); i++) - counterGoldCornersIds[goldCornersIds[i]]++; - - detector.detectMarkers(image, corners, ids, rejected); - map counterRes; - for (size_t i = 0; i < N; i++) - { - int arucoId = ids[i]; - counterRes[arucoId]++; - } - - ASSERT_EQ(N, ids.size()); - EXPECT_EQ(counterGoldCornersIds, counterRes); // check the number of ArUco markers -} - TEST(Charuco, issue_14014) { string imgPath = cvtest::findDataFile("aruco/recover.png"); From 29c3a4aa6597d7c8589e8ed713a1d7cb9c8e402f Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 2 Nov 2022 06:11:41 +0300 Subject: [PATCH 47/99] remove static create, remove setters, move EstimateParameters, fix Python --- modules/aruco/include/opencv2/aruco.hpp | 11 ++--- .../include/opencv2/aruco/aruco_calib.hpp | 49 +++++++++++++++++++ modules/aruco/misc/java/test/ArucoTest.java | 4 +- modules/aruco/misc/python/test/test_aruco.py | 15 +++--- modules/aruco/perf/perf_aruco.cpp | 6 +-- modules/aruco/samples/calibrate_camera.cpp | 2 +- .../samples/calibrate_camera_charuco.cpp | 2 +- modules/aruco/samples/detect_board.cpp | 2 +- .../aruco/samples/detect_board_charuco.cpp | 2 +- modules/aruco/samples/detect_diamonds.cpp | 2 +- modules/aruco/samples/detect_markers.cpp | 2 +- .../tutorial_charuco_create_detect.cpp | 4 +- modules/aruco/src/aruco.cpp | 2 +- modules/aruco/src/aruco_calib.cpp | 3 ++ modules/aruco/src/charuco.cpp | 31 ++++++------ modules/aruco/test/test_aruco_tutorial.cpp | 4 +- modules/aruco/test/test_boarddetection.cpp | 37 +++++++------- modules/aruco/test/test_charucodetection.cpp | 31 ++++++------ 18 files changed, 125 insertions(+), 84 deletions(-) diff --git a/modules/aruco/include/opencv2/aruco.hpp b/modules/aruco/include/opencv2/aruco.hpp index 0968a4a754..82ac2ea90d 100644 --- a/modules/aruco/include/opencv2/aruco.hpp +++ b/modules/aruco/include/opencv2/aruco.hpp @@ -10,12 +10,11 @@ namespace cv { namespace aruco { - /** @deprecated Use class ArucoDetector::detectMarkers */ CV_EXPORTS_W void detectMarkers(InputArray image, const Ptr &dictionary, OutputArrayOfArrays corners, - OutputArray ids, const Ptr ¶meters = DetectorParameters::create(), + OutputArray ids, const Ptr ¶meters = makePtr(), OutputArrayOfArrays rejectedImgPoints = noArray()); /** @@ -27,7 +26,7 @@ CV_EXPORTS_W void refineDetectedMarkers(InputArray image,const Ptr &boar InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), float minRepDistance = 10.f, float errorCorrectionRate = 3.f, bool checkAllOrders = true, OutputArray recoveredIdxs = noArray(), - const Ptr ¶meters = DetectorParameters::create()); + const Ptr ¶meters = makePtr()); /** @deprecated Use Board::draw @@ -138,15 +137,13 @@ CV_EXPORTS_W bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray CV_EXPORTS_W void estimatePoseSingleMarkers(InputArrayOfArrays corners, float markerLength, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvecs, OutputArray tvecs, OutputArray objPoints = noArray(), - const Ptr& estimateParameters = EstimateParameters::create()); + const Ptr& estimateParameters = makePtr()); -/** -@deprecated Use CharucoBoard::testCharucoCornersCollinear +/** @deprecated Use CharucoBoard::testCharucoCornersCollinear */ CV_EXPORTS_W bool testCharucoCornersCollinear(const Ptr &board, InputArray charucoIds); - } } diff --git a/modules/aruco/include/opencv2/aruco/aruco_calib.hpp b/modules/aruco/include/opencv2/aruco/aruco_calib.hpp index ed5efa7b12..ad59a2c407 100644 --- a/modules/aruco/include/opencv2/aruco/aruco_calib.hpp +++ b/modules/aruco/include/opencv2/aruco/aruco_calib.hpp @@ -11,6 +11,55 @@ namespace aruco { //! @addtogroup aruco //! @{ +/** @brief rvec/tvec define the right handed coordinate system of the marker. + * + * PatternPositionType defines center this system and axes direction. + * Axis X (red color) - first coordinate, axis Y (green color) - second coordinate, + * axis Z (blue color) - third coordinate. + * @sa estimatePoseSingleMarkers(), check tutorial_aruco_detection in aruco contrib + */ +enum PatternPositionType { + /** @brief The marker coordinate system is centered on the middle of the marker. + * + * The coordinates of the four corners (CCW order) of the marker in its own coordinate system are: + * (-markerLength/2, markerLength/2, 0), (markerLength/2, markerLength/2, 0), + * (markerLength/2, -markerLength/2, 0), (-markerLength/2, -markerLength/2, 0). + * + * These pattern points define this coordinate system: + * ![Image with axes drawn](tutorials/images/singlemarkersaxes.jpg) + */ + ARUCO_CCW_CENTER, + /** @brief The marker coordinate system is centered on the top-left corner of the marker. + * + * The coordinates of the four corners (CW order) of the marker in its own coordinate system are: + * (0, 0, 0), (markerLength, 0, 0), + * (markerLength, markerLength, 0), (0, markerLength, 0). + * + * These pattern points define this coordinate system: + * ![Image with axes drawn](tutorials/images/singlemarkersaxes2.jpg) + * + * These pattern dots are convenient to use with a chessboard/ChArUco board. + */ + ARUCO_CW_TOP_LEFT_CORNER +}; + +/** @brief Pose estimation parameters + * + * @param pattern Defines center this system and axes direction (default PatternPositionType::ARUCO_CCW_CENTER). + * @param useExtrinsicGuess Parameter used for SOLVEPNP_ITERATIVE. If true (1), the function uses the provided + * rvec and tvec values as initial approximations of the rotation and translation vectors, respectively, and further + * optimizes them (default false). + * @param solvePnPMethod Method for solving a PnP problem: see @ref calib3d_solvePnP_flags (default SOLVEPNP_ITERATIVE). + * @sa PatternPositionType, solvePnP(), check tutorial_aruco_detection in aruco contrib + */ +struct CV_EXPORTS_W EstimateParameters { + CV_PROP_RW PatternPositionType pattern; + CV_PROP_RW bool useExtrinsicGuess; + CV_PROP_RW int solvePnPMethod; + + CV_WRAP EstimateParameters(); +}; + /** * @brief Calibrate a camera using aruco markers * diff --git a/modules/aruco/misc/java/test/ArucoTest.java b/modules/aruco/misc/java/test/ArucoTest.java index a0a575410b..2ed6806b3e 100644 --- a/modules/aruco/misc/java/test/ArucoTest.java +++ b/modules/aruco/misc/java/test/ArucoTest.java @@ -33,8 +33,8 @@ public void testArucoIssue3133() { public void testArucoDetector() { Dictionary dictionary = Dictionary.get(0); - DetectorParameters detectorParameters = DetectorParameters.create(); - ArucoDetector detector = ArucoDetector.create(dictionary, detectorParameters); + DetectorParameters detectorParameters = new DetectorParameters(); + ArucoDetector detector = new ArucoDetector(dictionary, detectorParameters); Mat markerImage = new Mat(); int id = 1, offset = 5, size = 40; diff --git a/modules/aruco/misc/python/test/test_aruco.py b/modules/aruco/misc/python/test/test_aruco.py index ebbb86622a..b22b94eca9 100644 --- a/modules/aruco/misc/python/test/test_aruco.py +++ b/modules/aruco/misc/python/test/test_aruco.py @@ -21,15 +21,12 @@ def test_idsAccessibility(self): np.testing.assert_array_equal(board.getIds().squeeze(), ids) - board.setIds(rev_ids) + board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict, rev_ids) np.testing.assert_array_equal(board.getIds().squeeze(), rev_ids) - board.setIds(ids) + board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict, ids) np.testing.assert_array_equal(board.getIds().squeeze(), ids) - with self.assertRaises(cv.error): - board.setIds(np.array([0])) - def test_drawCharucoDiamond(self): aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_50) img = cv.aruco.drawCharucoDiamond(aruco_dict, np.array([0, 1, 2, 3]), 100, 80) @@ -86,9 +83,9 @@ def test_getDistanceToId(self): self.assertEqual(dist, 0) def test_aruco_detector(self): - aruco_params = cv.aruco.DetectorParameters_create() + aruco_params = cv.aruco.DetectorParameters() aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_250) - aruco_detector = cv.aruco.ArucoDetector_create(aruco_dict, aruco_params) + aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) id = 2 marker_size = 100 offset = 10 @@ -109,9 +106,9 @@ def test_aruco_detector(self): np.testing.assert_array_equal(gold_corners, corners[i].reshape(4, 2)) def test_aruco_detector_refine(self): - aruco_params = cv.aruco.DetectorParameters_create() + aruco_params = cv.aruco.DetectorParameters() aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_250) - aruco_detector = cv.aruco.ArucoDetector_create(aruco_dict, aruco_params) + aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) board_size = (3, 4) board = cv.aruco.GridBoard_create(board_size[0], board_size[1], 5.0, 1.0, aruco_dict) board_image = board.draw((board_size[0]*50, board_size[1]*50), marginSize=10) diff --git a/modules/aruco/perf/perf_aruco.cpp b/modules/aruco/perf/perf_aruco.cpp index a24792d32a..28de7fd9be 100644 --- a/modules/aruco/perf/perf_aruco.cpp +++ b/modules/aruco/perf/perf_aruco.cpp @@ -178,7 +178,7 @@ PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) { UseArucoParams testParams = GetParam(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); detectorParams->minDistanceToBorder = 1; detectorParams->markerBorderBits = 1; detectorParams->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; @@ -212,7 +212,7 @@ PERF_TEST_P(EstimateAruco, ArucoSecond, ESTIMATE_PARAMS) { UseArucoParams testParams = GetParam(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); detectorParams->minDistanceToBorder = 1; detectorParams->markerBorderBits = 1; detectorParams->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; @@ -268,7 +268,7 @@ PERF_TEST_P(EstimateLargeAruco, ArucoFHD, ESTIMATE_FHD_PARAMS) { ArucoTestParams testParams = GetParam(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); detectorParams->minDistanceToBorder = 1; detectorParams->markerBorderBits = 1; detectorParams->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; diff --git a/modules/aruco/samples/calibrate_camera.cpp b/modules/aruco/samples/calibrate_camera.cpp index bf92a1daf5..723eed8f11 100644 --- a/modules/aruco/samples/calibrate_camera.cpp +++ b/modules/aruco/samples/calibrate_camera.cpp @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) { if(parser.get("zt")) calibrationFlags |= CALIB_ZERO_TANGENT_DIST; if(parser.get("pc")) calibrationFlags |= CALIB_FIX_PRINCIPAL_POINT; - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); bool readOk = detectorParams->readDetectorParameters(fs.root()); diff --git a/modules/aruco/samples/calibrate_camera_charuco.cpp b/modules/aruco/samples/calibrate_camera_charuco.cpp index 57b09bd96f..3b5f7f499b 100644 --- a/modules/aruco/samples/calibrate_camera_charuco.cpp +++ b/modules/aruco/samples/calibrate_camera_charuco.cpp @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) { if(parser.get("zt")) calibrationFlags |= CALIB_ZERO_TANGENT_DIST; if(parser.get("pc")) calibrationFlags |= CALIB_FIX_PRINCIPAL_POINT; - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); bool readOk = detectorParams->readDetectorParameters(fs.root()); diff --git a/modules/aruco/samples/detect_board.cpp b/modules/aruco/samples/detect_board.cpp index 1c8e5a0e69..df3dbc8693 100644 --- a/modules/aruco/samples/detect_board.cpp +++ b/modules/aruco/samples/detect_board.cpp @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) { } } - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); bool readOk = detectorParams->readDetectorParameters(fs.root()); diff --git a/modules/aruco/samples/detect_board_charuco.cpp b/modules/aruco/samples/detect_board_charuco.cpp index 68977b727f..bb22ad7fde 100644 --- a/modules/aruco/samples/detect_board_charuco.cpp +++ b/modules/aruco/samples/detect_board_charuco.cpp @@ -99,7 +99,7 @@ int main(int argc, char *argv[]) { } } - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); bool readOk = detectorParams->readDetectorParameters(fs.root()); diff --git a/modules/aruco/samples/detect_diamonds.cpp b/modules/aruco/samples/detect_diamonds.cpp index e5255a6602..cdafdecc02 100644 --- a/modules/aruco/samples/detect_diamonds.cpp +++ b/modules/aruco/samples/detect_diamonds.cpp @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) { bool autoScale = parser.has("as"); float autoScaleFactor = autoScale ? parser.get("as") : 1.f; - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); bool readOk = detectorParams->readDetectorParameters(fs.root()); diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index 6f882b82ac..976dc59d67 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) { bool estimatePose = parser.has("c"); float markerLength = parser.get("l"); - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); bool readOk = detectorParams->readDetectorParameters(fs.root()); diff --git a/modules/aruco/samples/tutorial_charuco_create_detect.cpp b/modules/aruco/samples/tutorial_charuco_create_detect.cpp index 1c47003b57..d61ab30df1 100644 --- a/modules/aruco/samples/tutorial_charuco_create_detect.cpp +++ b/modules/aruco/samples/tutorial_charuco_create_detect.cpp @@ -38,7 +38,7 @@ static inline void detectCharucoBoardWithCalibrationPose() //! [dictboard] cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); cv::Ptr board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary); - cv::Ptr params = cv::aruco::DetectorParameters::create(); + cv::Ptr params = cv::makePtr(); //! [dictboard] while (inputVideo.grab()) { //! [inputImg] @@ -92,7 +92,7 @@ static inline void detectCharucoBoardWithoutCalibration() cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); cv::Ptr board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary); - cv::Ptr params = cv::aruco::DetectorParameters::create(); + cv::Ptr params = cv::makePtr(); params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE; while (inputVideo.grab()) { cv::Mat image, imageCopy; diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index c13710b212..ab30d67f98 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -24,7 +24,7 @@ void refineDetectedMarkers(InputArray _image, const Ptr &_board, InputArray _distCoeffs, float minRepDistance, float errorCorrectionRate, bool checkAllOrders, OutputArray _recoveredIdxs, const Ptr &_params) { - Ptr refineParams = RefineParameters::create(minRepDistance, errorCorrectionRate, checkAllOrders); + Ptr refineParams = makePtr(minRepDistance, errorCorrectionRate, checkAllOrders); ArucoDetector detector(_board->getDictionary(), _params, refineParams); detector.refineDetectedMarkers(_image, _board, _detectedCorners, _detectedIds, _rejectedCorners, _cameraMatrix, _distCoeffs, _recoveredIdxs); diff --git a/modules/aruco/src/aruco_calib.cpp b/modules/aruco/src/aruco_calib.cpp index 3e619e5b45..7b6ff4ff9a 100644 --- a/modules/aruco/src/aruco_calib.cpp +++ b/modules/aruco/src/aruco_calib.cpp @@ -9,6 +9,9 @@ namespace cv { namespace aruco { using namespace std; +EstimateParameters::EstimateParameters() : pattern(ARUCO_CCW_CENTER), useExtrinsicGuess(false), + solvePnPMethod(SOLVEPNP_ITERATIVE) {} + double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, const Ptr &board, Size imageSize, InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index 730b51dd83..b6696689de 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -90,7 +90,7 @@ static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray else grey = _image.getMat(); - const Ptr params = DetectorParameters::create(); // use default params for corner refinement + const Ptr params = makePtr(); // use default params for corner refinement //// For each of the charuco corners, apply subpixel refinement using its correspondind winSize parallel_for_(Range(0, (int)filteredChessboardImgPoints.size()), [&](const Range& range) { @@ -367,10 +367,6 @@ void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, const float minRepDistanceRate = 1.302455f; - // create Charuco board layout for diamond (3x3 layout) - Ptr _charucoDiamondLayout = CharucoBoard::create(3, 3, squareMarkerLengthRate, 1., dictionary); - - vector< vector< Point2f > > diamondCorners; vector< Vec4i > diamondIds; @@ -420,18 +416,21 @@ void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, } if(candidates.size() < 3) break; // we need at least 3 free markers // modify charuco layout id to make sure all the ids are different than current id + vector tmpIds(4); for(int k = 1; k < 4; k++) - _charucoDiamondLayout->changeId(k, currentId + 1 + k); + tmpIds[k] = currentId + 1 + k; // current id is assigned to [0], so it is the marker on the top - _charucoDiamondLayout->changeId(0, currentId); + tmpIds[0] = currentId; + // create Charuco board layout for diamond (3x3 layout) + Ptr _charucoDiamondLayout = CharucoBoard::create(3, 3, squareMarkerLengthRate, 1., dictionary, + tmpIds); // try to find the rest of markers in the diamond vector< int > acceptedIdxs; - Ptr _b = _charucoDiamondLayout.staticCast(); Ptr refineParameters = makePtr(minRepDistance, -1.f, false); - ArucoDetector detector(dictionary, DetectorParameters::create(), refineParameters); - detector.refineDetectedMarkers(grey, _b, currentMarker, currentMarkerId, candidates, noArray(), noArray(), - acceptedIdxs); + ArucoDetector detector(dictionary, makePtr(), refineParameters); + detector.refineDetectedMarkers(grey, _charucoDiamondLayout, currentMarker, currentMarkerId, candidates, + noArray(), noArray(), acceptedIdxs); // if found, we have a diamond if(currentMarker.size() == 4) { @@ -491,14 +490,12 @@ void drawCharucoDiamond(const Ptr &dictionary, Vec4i ids, int square CV_Assert(squareLength > 0 && markerLength > 0 && squareLength > markerLength); CV_Assert(marginSize >= 0 && borderBits > 0); - // create a charuco board similar to a charuco marker and print it - Ptr board = - CharucoBoard::create(3, 3, (float)squareLength, (float)markerLength, dictionary); - // assign the charuco marker ids + vector tmpIds(4); for(int i = 0; i < 4; i++) - board->changeId(i, ids[i]); - + tmpIds[i] = ids[i]; + // create a charuco board similar to a charuco marker and print it + Ptr board = CharucoBoard::create(3, 3, (float)squareLength, (float)markerLength, dictionary, tmpIds); Size outSize(3 * squareLength + 2 * marginSize, 3 * squareLength + 2 * marginSize); board->draw(outSize, _img, marginSize, borderBits); } diff --git a/modules/aruco/test/test_aruco_tutorial.cpp b/modules/aruco/test/test_aruco_tutorial.cpp index b8d743d423..72ce9d6a49 100644 --- a/modules/aruco/test/test_aruco_tutorial.cpp +++ b/modules/aruco/test/test_aruco_tutorial.cpp @@ -51,7 +51,7 @@ TEST(CV_ArucoTutorial, can_find_gboriginal) FileStorage fs(dictPath, FileStorage::READ); dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); aruco::ArucoDetector detector(dictionary, detectorParams); @@ -186,7 +186,7 @@ TEST(CV_ArucoTutorial, can_find_diamondmarkers) string detectorPath = cvtest::findDataFile("detector_params.yml", false); fs = FileStorage(detectorPath, FileStorage::READ); - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); detectorParams->readDetectorParameters(fs.root()); detectorParams->cornerRefinementMethod = 3; diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index 81512ddec2..f34dcf1f8b 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -57,7 +57,7 @@ class CV_ArucoBoardPose : public cvtest::BaseTest { { Ptr params; Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - params = aruco::DetectorParameters::create(); + params = makePtr(); params->minDistanceToBorder = 3; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) { params->useAruco3Detection = true; @@ -78,21 +78,20 @@ void CV_ArucoBoardPose::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.dictionary); - Ptr board = gridboard.staticCast(); cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; cameraMatrix.at< double >(0, 2) = imgSize.width / 2; cameraMatrix.at< double >(1, 2) = imgSize.height / 2; Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + const int sizeX = 3, sizeY = 3; // for different perspectives for(double distance = 0.2; distance <= 0.4; distance += 0.15) { for(int yaw = -55; yaw <= 50; yaw += 25) { for(int pitch = -55; pitch <= 50; pitch += 25) { vector tmpIds; - for(unsigned int i = 0; i < gridboard->getIds().size(); i++) + for(int i = 0; i < sizeX*sizeY; i++) tmpIds.push_back((iter + int(i)) % 250); - gridboard->setIds(tmpIds); + Ptr gridboard = aruco::GridBoard::create(sizeX, sizeY, 0.02f, 0.005f, detector.dictionary, tmpIds); int markerBorder = iter % 2 + 1; iter++; // create synthetic image @@ -100,13 +99,14 @@ void CV_ArucoBoardPose::run(int) { imgSize, markerBorder); vector > corners; vector ids; - detector.params->markerBorderBits = markerBorder; + detector.detectorParams->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), gridboard->getIds().size()); // estimate pose Mat rvec, tvec; + Ptr board = gridboard.staticCast(); estimatePoseBoard(corners, ids, board, cameraMatrix, distCoeffs, rvec, tvec); // check axes @@ -165,7 +165,7 @@ class CV_ArucoRefine : public cvtest::BaseTest { CV_ArucoRefine(ArucoAlgParams arucoAlgParams) { Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr params = aruco::DetectorParameters::create(); + Ptr params = makePtr(); params->minDistanceToBorder = 3; params->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) @@ -186,7 +186,6 @@ void CV_ArucoRefine::run(int) { Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); Ptr gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.dictionary); - Ptr board = gridboard.staticCast(); cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; cameraMatrix.at< double >(0, 2) = imgSize.width / 2; cameraMatrix.at< double >(1, 2) = imgSize.height / 2; @@ -199,7 +198,7 @@ void CV_ArucoRefine::run(int) { vector tmpIds; for(unsigned int i = 0; i < gridboard->getIds().size(); i++) tmpIds.push_back(iter + int(i) % 250); - gridboard->setIds(tmpIds); + gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.dictionary, tmpIds); int markerBorder = iter % 2 + 1; iter++; @@ -209,7 +208,7 @@ void CV_ArucoRefine::run(int) { // detect markers vector > corners, rejected; vector ids; - detector.params->markerBorderBits = markerBorder; + detector.detectorParams->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids, rejected); // remove a marker from detection @@ -221,8 +220,9 @@ void CV_ArucoRefine::run(int) { ids.erase(ids.begin(), ids.begin() + 1); // try to refind the erased marker + Ptr board = gridboard.staticCast(); detector.refineDetectedMarkers(img, board, corners, ids, rejected, cameraMatrix, - distCoeffs, noArray()); + distCoeffs, noArray()); // check result if((int)ids.size() < markersBeforeDelete) { @@ -268,8 +268,6 @@ TEST(CV_ArucoBoardPose, CheckNegativeZ) 0., 0., 1 }; cv::Mat cameraMatrix = cv::Mat(3, 3, CV_64F, matrixData); - cv::Ptr boardPtr = makePtr(); - cv::aruco::Board& board = *boardPtr; vector pts3d1, pts3d2; pts3d1.push_back(cv::Point3f(0.326198f, -0.030621f, 0.303620f)); @@ -282,8 +280,9 @@ TEST(CV_ArucoBoardPose, CheckNegativeZ) pts3d2.push_back(cv::Point3f(-0.105289f, -0.102120f, 0.237120f)); pts3d2.push_back(cv::Point3f(-0.102926f, -0.032235f, 0.240349f)); - board.setObjPoints({pts3d1, pts3d2}); - board.setIds(vector{0, 1}); + vector tmpIds = {0, 1}; + vector > tmpObjectPoints = {pts3d1, pts3d2}; + Ptr board = aruco::Board::create(tmpObjectPoints, aruco::getPredefinedDictionary(0), tmpIds); vector > corners; vector pts2d; @@ -300,12 +299,12 @@ TEST(CV_ArucoBoardPose, CheckNegativeZ) corners.push_back(pts2d); Vec3d rvec, tvec; - int nUsed = cv::aruco::estimatePoseBoard(corners, board.getIds(), boardPtr, cameraMatrix, Mat(), rvec, tvec); + int nUsed = cv::aruco::estimatePoseBoard(corners, board->getIds(), board, cameraMatrix, Mat(), rvec, tvec); ASSERT_EQ(nUsed, 2); cv::Matx33d rotm; cv::Point3d out; cv::Rodrigues(rvec, rotm); - out = cv::Point3d(tvec) + rotm*Point3d(board.getObjPoints()[0][0]); + out = cv::Point3d(tvec) + rotm*Point3d(board->getObjPoints()[0][0]); ASSERT_GT(out.z, 0); corners.clear(); pts2d.clear(); @@ -321,11 +320,11 @@ TEST(CV_ArucoBoardPose, CheckNegativeZ) pts2d.push_back(cv::Point2f(586.3f, 188.5f)); corners.push_back(pts2d); - nUsed = cv::aruco::estimatePoseBoard(corners, board.getIds(), boardPtr, cameraMatrix, Mat(), rvec, tvec, true); + nUsed = cv::aruco::estimatePoseBoard(corners, board->getIds(), board, cameraMatrix, Mat(), rvec, tvec, true); ASSERT_EQ(nUsed, 2); cv::Rodrigues(rvec, rotm); - out = cv::Point3d(tvec) + rotm*Point3d(board.getObjPoints()[0][0]); + out = cv::Point3d(tvec) + rotm*Point3d(board->getObjPoints()[0][0]); ASSERT_GT(out.z, 0); } diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 45e81b0b64..e602882396 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -132,7 +132,7 @@ void CV_CharucoDetection::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr params = aruco::DetectorParameters::create(); + Ptr params = makePtr(); params->minDistanceToBorder = 3; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.dictionary); @@ -160,7 +160,7 @@ void CV_CharucoDetection::run(int) { vector > corners; vector ids; - detector.params->markerBorderBits = markerBorder; + detector.detectorParams->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); if(ids.size() == 0) { @@ -238,7 +238,7 @@ void CV_CharucoPoseEstimation::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr params = aruco::DetectorParameters::create(); + Ptr params = makePtr(); params->minDistanceToBorder = 3; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.dictionary); @@ -264,7 +264,7 @@ void CV_CharucoPoseEstimation::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.params->markerBorderBits = markerBorder; + detector.detectorParams->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), board->getIds().size()); @@ -348,13 +348,11 @@ void CV_CharucoDiamondDetection::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr params = aruco::DetectorParameters::create(); + Ptr params = makePtr(); params->minDistanceToBorder = 0; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); float squareLength = 0.03f; float markerLength = 0.015f; - Ptr board = - aruco::CharucoBoard::create(3, 3, squareLength, markerLength, detector.dictionary); cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; cameraMatrix.at< double >(0, 2) = imgSize.width / 2; @@ -370,7 +368,8 @@ void CV_CharucoDiamondDetection::run(int) { vector idsTmp; for(int i = 0; i < 4; i++) idsTmp.push_back(4 * iter + i); - board->setIds(idsTmp); + Ptr board = aruco::CharucoBoard::create(3, 3, squareLength, markerLength, + detector.dictionary, idsTmp); iter++; // get synthetic image @@ -381,7 +380,7 @@ void CV_CharucoDiamondDetection::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.params->markerBorderBits = markerBorder; + detector.detectorParams->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); if(ids.size() != 4) { @@ -440,7 +439,7 @@ void CV_CharucoDiamondDetection::run(int) { } } - Ptr estimateParameters = aruco::EstimateParameters::create(); + Ptr estimateParameters = makePtr(); estimateParameters->pattern = aruco::ARUCO_CW_TOP_LEFT_CORNER; // estimate diamond pose vector< Vec3d > estimatedRvec, estimatedTvec; @@ -544,7 +543,7 @@ TEST(Charuco, testCharucoCornersCollinear_true) int dictionaryId = 11; - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); @@ -586,7 +585,7 @@ TEST(Charuco, testCharucoCornersCollinear_false) int dictionaryId = 11; - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); @@ -637,14 +636,14 @@ TEST(Charuco, testBoardSubpixelCoords) cv::Mat gray; - auto dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_APRILTAG_36h11); - auto board = cv::aruco::CharucoBoard::create(4, 4, 1.f, .8f, dict); + Ptr dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_APRILTAG_36h11); + Ptr board = cv::aruco::CharucoBoard::create(4, 4, 1.f, .8f, dict); // generate ChArUco board board->draw(Size(res.width, res.height), gray, 150); cv::GaussianBlur(gray, gray, Size(5, 5), 1.0); - auto params = cv::aruco::DetectorParameters::create(); + Ptr params = makePtr(); params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG; aruco::ArucoDetector detector(dict, params); @@ -675,7 +674,7 @@ TEST(Charuco, issue_14014) string imgPath = cvtest::findDataFile("aruco/recover.png"); Mat img = imread(imgPath); - Ptr detectorParams = aruco::DetectorParameters::create(); + Ptr detectorParams = makePtr(); detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; detectorParams->cornerRefinementMinAccuracy = 0.01; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_7X7_250), detectorParams); From 0159ea3cf65cc34dd640c95382d57d5a53fcb224 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 2 Nov 2022 07:29:54 +0300 Subject: [PATCH 48/99] add pimpl for ArucoDetector --- modules/aruco/test/test_boarddetection.cpp | 10 +++++----- modules/aruco/test/test_charucodetection.cpp | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index f34dcf1f8b..e06f3254a3 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -91,7 +91,7 @@ void CV_ArucoBoardPose::run(int) { vector tmpIds; for(int i = 0; i < sizeX*sizeY; i++) tmpIds.push_back((iter + int(i)) % 250); - Ptr gridboard = aruco::GridBoard::create(sizeX, sizeY, 0.02f, 0.005f, detector.dictionary, tmpIds); + Ptr gridboard = aruco::GridBoard::create(sizeX, sizeY, 0.02f, 0.005f, detector.getDictionary(), tmpIds); int markerBorder = iter % 2 + 1; iter++; // create synthetic image @@ -99,7 +99,7 @@ void CV_ArucoBoardPose::run(int) { imgSize, markerBorder); vector > corners; vector ids; - detector.detectorParams->markerBorderBits = markerBorder; + detector.getDetectorParameters()->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), gridboard->getIds().size()); @@ -185,7 +185,7 @@ void CV_ArucoRefine::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.dictionary); + Ptr gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.getDictionary()); cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; cameraMatrix.at< double >(0, 2) = imgSize.width / 2; cameraMatrix.at< double >(1, 2) = imgSize.height / 2; @@ -198,7 +198,7 @@ void CV_ArucoRefine::run(int) { vector tmpIds; for(unsigned int i = 0; i < gridboard->getIds().size(); i++) tmpIds.push_back(iter + int(i) % 250); - gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.dictionary, tmpIds); + gridboard = aruco::GridBoard::create(3, 3, 0.02f, 0.005f, detector.getDictionary(), tmpIds); int markerBorder = iter % 2 + 1; iter++; @@ -208,7 +208,7 @@ void CV_ArucoRefine::run(int) { // detect markers vector > corners, rejected; vector ids; - detector.detectorParams->markerBorderBits = markerBorder; + detector.getDetectorParameters()->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids, rejected); // remove a marker from detection diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index e602882396..030246f6de 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -135,7 +135,7 @@ void CV_CharucoDetection::run(int) { Ptr params = makePtr(); params->minDistanceToBorder = 3; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); - Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.dictionary); + Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.getDictionary()); cameraMatrix.at(0, 0) = cameraMatrix.at(1, 1) = 600; cameraMatrix.at(0, 2) = imgSize.width / 2; @@ -160,7 +160,7 @@ void CV_CharucoDetection::run(int) { vector > corners; vector ids; - detector.detectorParams->markerBorderBits = markerBorder; + detector.getDetectorParameters()->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); if(ids.size() == 0) { @@ -241,7 +241,7 @@ void CV_CharucoPoseEstimation::run(int) { Ptr params = makePtr(); params->minDistanceToBorder = 3; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); - Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.dictionary); + Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.getDictionary()); cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; cameraMatrix.at< double >(0, 2) = imgSize.width / 2; @@ -264,7 +264,7 @@ void CV_CharucoPoseEstimation::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.detectorParams->markerBorderBits = markerBorder; + detector.getDetectorParameters()->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), board->getIds().size()); @@ -369,7 +369,7 @@ void CV_CharucoDiamondDetection::run(int) { for(int i = 0; i < 4; i++) idsTmp.push_back(4 * iter + i); Ptr board = aruco::CharucoBoard::create(3, 3, squareLength, markerLength, - detector.dictionary, idsTmp); + detector.getDictionary(), idsTmp); iter++; // get synthetic image @@ -380,7 +380,7 @@ void CV_CharucoDiamondDetection::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.detectorParams->markerBorderBits = markerBorder; + detector.getDetectorParameters()->markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); if(ids.size() != 4) { @@ -392,7 +392,7 @@ void CV_CharucoDiamondDetection::run(int) { vector< vector< Point2f > > diamondCorners; vector< Vec4i > diamondIds; aruco::detectCharucoDiamond(img, corners, ids, squareLength / markerLength, diamondCorners, diamondIds, - cameraMatrix, distCoeffs, detector.dictionary); + cameraMatrix, distCoeffs, detector.getDictionary()); // check results if(diamondIds.size() != 1) { @@ -678,7 +678,7 @@ TEST(Charuco, issue_14014) detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; detectorParams->cornerRefinementMinAccuracy = 0.01; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_7X7_250), detectorParams); - Ptr board = aruco::CharucoBoard::create(8, 5, 0.03455f, 0.02164f, detector.dictionary); + Ptr board = aruco::CharucoBoard::create(8, 5, 0.03455f, 0.02164f, detector.getDictionary()); vector corners, rejectedPoints; vector ids; From 95cf9efea9478ff3d174c850f96b57fa9b73edac Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 2 Nov 2022 16:28:54 +0300 Subject: [PATCH 49/99] fix dict --- modules/aruco/misc/java/test/ArucoTest.java | 4 ++-- modules/aruco/samples/aruco_dict_utils.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/aruco/misc/java/test/ArucoTest.java b/modules/aruco/misc/java/test/ArucoTest.java index 2ed6806b3e..716f178d89 100644 --- a/modules/aruco/misc/java/test/ArucoTest.java +++ b/modules/aruco/misc/java/test/ArucoTest.java @@ -15,7 +15,7 @@ public class ArucoTest extends OpenCVTestCase { public void testArucoIssue3133() { byte[][] marker = {{0,1,1},{1,1,1},{0,1,1}}; - Dictionary dictionary = Dictionary.create(1, 3); + Dictionary dictionary = extendDictionary(1, 3); dictionary.set_maxCorrectionBits(0); Mat markerBits = new Mat(3, 3, CvType.CV_8UC1); for (int i = 0; i < 3; i++) { @@ -32,7 +32,7 @@ public void testArucoIssue3133() { } public void testArucoDetector() { - Dictionary dictionary = Dictionary.get(0); + Dictionary dictionary = getPredefinedDictionary(0); DetectorParameters detectorParameters = new DetectorParameters(); ArucoDetector detector = new ArucoDetector(dictionary, detectorParameters); diff --git a/modules/aruco/samples/aruco_dict_utils.cpp b/modules/aruco/samples/aruco_dict_utils.cpp index beee9a2996..f2aeedbd0f 100644 --- a/modules/aruco/samples/aruco_dict_utils.cpp +++ b/modules/aruco/samples/aruco_dict_utils.cpp @@ -253,7 +253,7 @@ int main(int argc, char *argv[]) if (checkFlippedMarkers) dictionary = generateCustomAsymmetricDictionary(nMarkers, markerSize, makePtr(), 0); else - dictionary = aruco::generateCustomDictionary(nMarkers, markerSize, makePtr(), 0); + dictionary = aruco::extendDictionary(nMarkers, markerSize, makePtr(), 0); dictionary->writeDictionary(fs); } From b562b69d0cd69b82864dc760adaa61e895511c1b Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Thu, 24 Nov 2022 20:21:45 +0300 Subject: [PATCH 50/99] fix python/Java tests, add new Java test --- .../aruco/include/opencv2/aruco/charuco.hpp | 4 +-- modules/aruco/misc/java/test/ArucoTest.java | 26 +++++++++++++++++-- .../misc/pattern_generator/MarkerPrinter.py | 2 +- modules/aruco/misc/python/test/test_aruco.py | 12 ++++----- modules/ovis/samples/aruco_ar_demo.py | 2 +- modules/rapid/samples/track_marker.py | 2 +- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/modules/aruco/include/opencv2/aruco/charuco.hpp b/modules/aruco/include/opencv2/aruco/charuco.hpp index 2f1a2334e0..122228d4ab 100644 --- a/modules/aruco/include/opencv2/aruco/charuco.hpp +++ b/modules/aruco/include/opencv2/aruco/charuco.hpp @@ -134,8 +134,8 @@ CV_EXPORTS_W void drawDetectedDiamonds(InputOutputArray image, InputArrayOfArray * This function return the image of a ChArUco marker, ready to be printed. */ CV_EXPORTS_W void drawCharucoDiamond(const Ptr &dictionary, Vec4i ids, int squareLength, - int markerLength, OutputArray img, int marginSize = 0, - int borderBits = 1); + int markerLength, OutputArray img, int marginSize = 0, + int borderBits = 1); //! @} } diff --git a/modules/aruco/misc/java/test/ArucoTest.java b/modules/aruco/misc/java/test/ArucoTest.java index 716f178d89..ffdb25c5fe 100644 --- a/modules/aruco/misc/java/test/ArucoTest.java +++ b/modules/aruco/misc/java/test/ArucoTest.java @@ -6,6 +6,7 @@ import org.opencv.test.OpenCVTestCase; import org.opencv.core.Scalar; import org.opencv.core.Mat; +import org.opencv.core.Size; import org.opencv.core.CvType; import org.opencv.aruco.*; import org.opencv.objdetect.*; @@ -13,9 +14,30 @@ public class ArucoTest extends OpenCVTestCase { + public void testDrawBoards() { + Dictionary dictionary = Objdetect.getPredefinedDictionary(Objdetect.DICT_4X4_50); + + Mat point1 = new Mat(4, 3, CvType.CV_32FC1); + int row = 0, col = 0; + point1.put(row ,col, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0); + + ListobjPoints = new ArrayList(); + objPoints.add(point1); + + Mat ids = new Mat(1, 1, CvType.CV_32SC1); + ids.put(row, col, 0); + + Board board = Board.create(objPoints, dictionary, ids); + + Mat image = new Mat(); + board.draw(new Size(50, 50), image); + + assertTrue(image.total() > 0); + } + public void testArucoIssue3133() { byte[][] marker = {{0,1,1},{1,1,1},{0,1,1}}; - Dictionary dictionary = extendDictionary(1, 3); + Dictionary dictionary = Objdetect.extendDictionary(1, 3); dictionary.set_maxCorrectionBits(0); Mat markerBits = new Mat(3, 3, CvType.CV_8UC1); for (int i = 0; i < 3; i++) { @@ -32,7 +54,7 @@ public void testArucoIssue3133() { } public void testArucoDetector() { - Dictionary dictionary = getPredefinedDictionary(0); + Dictionary dictionary = Objdetect.getPredefinedDictionary(0); DetectorParameters detectorParameters = new DetectorParameters(); ArucoDetector detector = new ArucoDetector(dictionary, detectorParameters); diff --git a/modules/aruco/misc/pattern_generator/MarkerPrinter.py b/modules/aruco/misc/pattern_generator/MarkerPrinter.py index 301f4f918f..4cf9185c3f 100644 --- a/modules/aruco/misc/pattern_generator/MarkerPrinter.py +++ b/modules/aruco/misc/pattern_generator/MarkerPrinter.py @@ -39,7 +39,7 @@ def SaveArucoDictBytesList(filePath = "arucoDictBytesList.npz"): arucoDictBytesList = {} for name, flag in dictInfo: - arucoDict = aruco.Dictionary_get(flag) + arucoDict = aruco.getPredefinedDictionary(flag) arucoDictBytesList[name] = arucoDict.bytesList np.savez_compressed(filePath, **arucoDictBytesList) diff --git a/modules/aruco/misc/python/test/test_aruco.py b/modules/aruco/misc/python/test/test_aruco.py index b22b94eca9..a37ccd6183 100644 --- a/modules/aruco/misc/python/test/test_aruco.py +++ b/modules/aruco/misc/python/test/test_aruco.py @@ -16,7 +16,7 @@ def test_idsAccessibility(self): ids = np.arange(17) rev_ids = ids[::-1] - aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_5X5_250) + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_250) board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict) np.testing.assert_array_equal(board.getIds().squeeze(), ids) @@ -28,7 +28,7 @@ def test_idsAccessibility(self): np.testing.assert_array_equal(board.getIds().squeeze(), ids) def test_drawCharucoDiamond(self): - aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_50) + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) img = cv.aruco.drawCharucoDiamond(aruco_dict, np.array([0, 1, 2, 3]), 100, 80) self.assertTrue(img is not None) @@ -62,7 +62,7 @@ def test_write_read_dict(self): os.remove(filename) def test_identify(self): - aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_50) + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) expected_idx = 9 expected_rotation = 2 bit_marker = np.array([[0, 1, 1, 0], [1, 0, 1, 0], [1, 1, 1, 1], [0, 0, 1, 1]], dtype=np.uint8) @@ -74,7 +74,7 @@ def test_identify(self): self.assertEqual(rotation, expected_rotation) def test_getDistanceToId(self): - aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_50) + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) idx = 7 rotation = 3 bit_marker = np.array([[0, 1, 0, 1], [0, 1, 1, 1], [1, 1, 0, 0], [0, 1, 0, 0]], dtype=np.uint8) @@ -84,7 +84,7 @@ def test_getDistanceToId(self): def test_aruco_detector(self): aruco_params = cv.aruco.DetectorParameters() - aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_250) + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) id = 2 marker_size = 100 @@ -107,7 +107,7 @@ def test_aruco_detector(self): def test_aruco_detector_refine(self): aruco_params = cv.aruco.DetectorParameters() - aruco_dict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_250) + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) board_size = (3, 4) board = cv.aruco.GridBoard_create(board_size[0], board_size[1], 5.0, 1.0, aruco_dict) diff --git a/modules/ovis/samples/aruco_ar_demo.py b/modules/ovis/samples/aruco_ar_demo.py index 567912c5b6..72aeeaebea 100644 --- a/modules/ovis/samples/aruco_ar_demo.py +++ b/modules/ovis/samples/aruco_ar_demo.py @@ -2,7 +2,7 @@ import cv2 as cv # aruco -adict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_50) +adict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) cv.imshow("marker", cv.aruco.drawMarker(adict, 0, 400)) # random calibration data. your mileage may vary. diff --git a/modules/rapid/samples/track_marker.py b/modules/rapid/samples/track_marker.py index fc17353ffb..aa084e5ca4 100644 --- a/modules/rapid/samples/track_marker.py +++ b/modules/rapid/samples/track_marker.py @@ -2,7 +2,7 @@ import cv2 as cv # aruco config -adict = cv.aruco.Dictionary_get(cv.aruco.DICT_4X4_50) +adict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) cv.imshow("marker", cv.aruco.drawMarker(adict, 0, 400)) marker_len = 5 From 83f9947a086d0cd762ab630970659502842ad9c8 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Thu, 24 Nov 2022 22:09:17 +0300 Subject: [PATCH 51/99] fixes --- modules/aruco/misc/java/test/ArucoTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/aruco/misc/java/test/ArucoTest.java b/modules/aruco/misc/java/test/ArucoTest.java index ffdb25c5fe..9a601f1d49 100644 --- a/modules/aruco/misc/java/test/ArucoTest.java +++ b/modules/aruco/misc/java/test/ArucoTest.java @@ -19,8 +19,11 @@ public void testDrawBoards() { Mat point1 = new Mat(4, 3, CvType.CV_32FC1); int row = 0, col = 0; - point1.put(row ,col, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0); - + double squareLength = 40.; + point1.put(row, col, 0, 0, 0, + 0, squareLength, 0, + squareLength, squareLength, 0, + 0, squareLength, 0); ListobjPoints = new ArrayList(); objPoints.add(point1); @@ -30,7 +33,7 @@ public void testDrawBoards() { Board board = Board.create(objPoints, dictionary, ids); Mat image = new Mat(); - board.draw(new Size(50, 50), image); + board.draw(new Size(80, 80), image, 2); assertTrue(image.total() > 0); } From f64e2d30796ae2d7f34ecf5fa4a1b50cbfd6ac5c Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Fri, 25 Nov 2022 18:16:44 +0300 Subject: [PATCH 52/99] remove Ptr RefineParameters --- modules/aruco/src/aruco.cpp | 2 +- modules/aruco/src/charuco.cpp | 2 +- modules/aruco/test/test_boarddetection.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index ab30d67f98..2c608e4c82 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -24,7 +24,7 @@ void refineDetectedMarkers(InputArray _image, const Ptr &_board, InputArray _distCoeffs, float minRepDistance, float errorCorrectionRate, bool checkAllOrders, OutputArray _recoveredIdxs, const Ptr &_params) { - Ptr refineParams = makePtr(minRepDistance, errorCorrectionRate, checkAllOrders); + RefineParameters refineParams(minRepDistance, errorCorrectionRate, checkAllOrders); ArucoDetector detector(_board->getDictionary(), _params, refineParams); detector.refineDetectedMarkers(_image, _board, _detectedCorners, _detectedIds, _rejectedCorners, _cameraMatrix, _distCoeffs, _recoveredIdxs); diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index b6696689de..46a67bf69d 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -427,7 +427,7 @@ void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, // try to find the rest of markers in the diamond vector< int > acceptedIdxs; - Ptr refineParameters = makePtr(minRepDistance, -1.f, false); + RefineParameters refineParameters(minRepDistance, -1.f, false); ArucoDetector detector(dictionary, makePtr(), refineParameters); detector.refineDetectedMarkers(grey, _charucoDiamondLayout, currentMarker, currentMarkerId, candidates, noArray(), noArray(), acceptedIdxs); diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index e06f3254a3..5cc9af8aed 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -170,7 +170,7 @@ class CV_ArucoRefine : public cvtest::BaseTest { params->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) params->useAruco3Detection = true; - Ptr refineParams = makePtr(10.f, 3.f, true); + aruco::RefineParameters refineParams(10.f, 3.f, true); detector = aruco::ArucoDetector(dictionary, params, refineParams); } From 52c0566e8350835a80f89b34aca50fb0ebf9586f Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 28 Nov 2022 23:41:43 +0300 Subject: [PATCH 53/99] remove Ptr DetectorParameters --- modules/aruco/perf/perf_aruco.cpp | 54 ++++++++++---------- modules/aruco/samples/calibrate_camera.cpp | 4 +- modules/aruco/samples/detect_board.cpp | 6 +-- modules/aruco/samples/detect_markers.cpp | 8 +-- modules/aruco/src/aruco.cpp | 4 +- modules/aruco/src/charuco.cpp | 10 ++-- modules/aruco/test/test_aruco_tutorial.cpp | 8 +-- modules/aruco/test/test_boarddetection.cpp | 25 +++++---- modules/aruco/test/test_charucodetection.cpp | 28 +++++----- 9 files changed, 73 insertions(+), 74 deletions(-) diff --git a/modules/aruco/perf/perf_aruco.cpp b/modules/aruco/perf/perf_aruco.cpp index 28de7fd9be..34cc38a629 100644 --- a/modules/aruco/perf/perf_aruco.cpp +++ b/modules/aruco/perf/perf_aruco.cpp @@ -81,7 +81,7 @@ class MarkerPainter } std::pair > getProjectMarker(int id, double yaw, double pitch, - const Ptr& parameters, + const aruco::DetectorParameters& parameters, const Ptr& dictionary) { auto marker_corners = std::make_pair(Mat(imgMarkerSize, imgMarkerSize, CV_8UC1, Scalar::all(255)), vector()); @@ -90,7 +90,7 @@ class MarkerPainter // canonical image const int markerSizePixels = static_cast(imgMarkerSize/sqrt(2.f)); - aruco::drawMarker(dictionary, id, markerSizePixels, img, parameters->markerBorderBits); + aruco::drawMarker(dictionary, id, markerSizePixels, img, parameters.markerBorderBits); // get rvec and tvec for the perspective const double distance = 0.1; @@ -123,7 +123,7 @@ class MarkerPainter } std::pair > > getProjectMarkersTile(const int numMarkers, - const Ptr& params, + const aruco::DetectorParameters& params, const Ptr& dictionary) { Mat tileImage(imgMarkerSize*numMarkers, imgMarkerSize*numMarkers, CV_8UC1, Scalar::all(255)); @@ -178,18 +178,18 @@ PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) { UseArucoParams testParams = GetParam(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr detectorParams = makePtr(); - detectorParams->minDistanceToBorder = 1; - detectorParams->markerBorderBits = 1; - detectorParams->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + aruco::DetectorParameters detectorParams; + detectorParams.minDistanceToBorder = 1; + detectorParams.markerBorderBits = 1; + detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; const int markerSize = 100; const int numMarkersInRow = 9; //USE_ARUCO3 - detectorParams->useAruco3Detection = get<0>(testParams); - if (detectorParams->useAruco3Detection) { - detectorParams->minSideLengthCanonicalImg = 32; - detectorParams->minMarkerLengthRatioOriginalImg = 0.04f / numMarkersInRow; + detectorParams.useAruco3Detection = get<0>(testParams); + if (detectorParams.useAruco3Detection) { + detectorParams.minSideLengthCanonicalImg = 32; + detectorParams.minMarkerLengthRatioOriginalImg = 0.04f / numMarkersInRow; } aruco::ArucoDetector detector(dictionary, detectorParams); MarkerPainter painter(markerSize); @@ -212,16 +212,16 @@ PERF_TEST_P(EstimateAruco, ArucoSecond, ESTIMATE_PARAMS) { UseArucoParams testParams = GetParam(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr detectorParams = makePtr(); - detectorParams->minDistanceToBorder = 1; - detectorParams->markerBorderBits = 1; - detectorParams->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + aruco::DetectorParameters detectorParams; + detectorParams.minDistanceToBorder = 1; + detectorParams.markerBorderBits = 1; + detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; //USE_ARUCO3 - detectorParams->useAruco3Detection = get<0>(testParams); - if (detectorParams->useAruco3Detection) { - detectorParams->minSideLengthCanonicalImg = 64; - detectorParams->minMarkerLengthRatioOriginalImg = 0.f; + detectorParams.useAruco3Detection = get<0>(testParams); + if (detectorParams.useAruco3Detection) { + detectorParams.minSideLengthCanonicalImg = 64; + detectorParams.minMarkerLengthRatioOriginalImg = 0.f; } aruco::ArucoDetector detector(dictionary, detectorParams); const int markerSize = 200; @@ -268,16 +268,16 @@ PERF_TEST_P(EstimateLargeAruco, ArucoFHD, ESTIMATE_FHD_PARAMS) { ArucoTestParams testParams = GetParam(); Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr detectorParams = makePtr(); - detectorParams->minDistanceToBorder = 1; - detectorParams->markerBorderBits = 1; - detectorParams->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + aruco::DetectorParameters detectorParams; + detectorParams.minDistanceToBorder = 1; + detectorParams.markerBorderBits = 1; + detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; //USE_ARUCO3 - detectorParams->useAruco3Detection = get<0>(testParams).useAruco3Detection; - if (detectorParams->useAruco3Detection) { - detectorParams->minSideLengthCanonicalImg = get<0>(testParams).minSideLengthCanonicalImg; - detectorParams->minMarkerLengthRatioOriginalImg = get<0>(testParams).minMarkerLengthRatioOriginalImg; + detectorParams.useAruco3Detection = get<0>(testParams).useAruco3Detection; + if (detectorParams.useAruco3Detection) { + detectorParams.minSideLengthCanonicalImg = get<0>(testParams).minSideLengthCanonicalImg; + detectorParams.minMarkerLengthRatioOriginalImg = get<0>(testParams).minMarkerLengthRatioOriginalImg; } aruco::ArucoDetector detector(dictionary, detectorParams); const int markerSize = get<1>(testParams).first; // 1440 or 480 or 144 diff --git a/modules/aruco/samples/calibrate_camera.cpp b/modules/aruco/samples/calibrate_camera.cpp index 723eed8f11..10a4a83807 100644 --- a/modules/aruco/samples/calibrate_camera.cpp +++ b/modules/aruco/samples/calibrate_camera.cpp @@ -102,10 +102,10 @@ int main(int argc, char *argv[]) { if(parser.get("zt")) calibrationFlags |= CALIB_ZERO_TANGENT_DIST; if(parser.get("pc")) calibrationFlags |= CALIB_FIX_PRINCIPAL_POINT; - Ptr detectorParams = makePtr(); + aruco::DetectorParameters detectorParams; if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); - bool readOk = detectorParams->readDetectorParameters(fs.root()); + bool readOk = detectorParams.readDetectorParameters(fs.root()); if(!readOk) { cerr << "Invalid detector parameters file" << endl; return 0; diff --git a/modules/aruco/samples/detect_board.cpp b/modules/aruco/samples/detect_board.cpp index df3dbc8693..2ca829592b 100644 --- a/modules/aruco/samples/detect_board.cpp +++ b/modules/aruco/samples/detect_board.cpp @@ -97,16 +97,16 @@ int main(int argc, char *argv[]) { } } - Ptr detectorParams = makePtr(); + aruco::DetectorParameters detectorParams; if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); - bool readOk = detectorParams->readDetectorParameters(fs.root()); + bool readOk = detectorParams.readDetectorParameters(fs.root()); if(!readOk) { cerr << "Invalid detector parameters file" << endl; return 0; } } - detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; // do corner refinement in markers + detectorParams.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; // do corner refinement in markers String video; if(parser.has("v")) { diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index 976dc59d67..ff63c86105 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -80,10 +80,10 @@ int main(int argc, char *argv[]) { bool estimatePose = parser.has("c"); float markerLength = parser.get("l"); - Ptr detectorParams = makePtr(); + aruco::DetectorParameters detectorParams; if(parser.has("dp")) { FileStorage fs(parser.get("dp"), FileStorage::READ); - bool readOk = detectorParams->readDetectorParameters(fs.root()); + bool readOk = detectorParams.readDetectorParameters(fs.root()); if(!readOk) { cerr << "Invalid detector parameters file" << endl; return 0; @@ -92,9 +92,9 @@ int main(int argc, char *argv[]) { if (parser.has("refine")) { //override cornerRefinementMethod read from config file - detectorParams->cornerRefinementMethod = parser.get("refine"); + detectorParams.cornerRefinementMethod = parser.get("refine"); } - std::cout << "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag 2): " << detectorParams->cornerRefinementMethod << std::endl; + std::cout << "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag 2): " << detectorParams.cornerRefinementMethod << std::endl; int camId = parser.get("ci"); diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index 2c608e4c82..fefa014db3 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -14,7 +14,7 @@ using namespace std; void detectMarkers(InputArray _image, const Ptr &_dictionary, OutputArrayOfArrays _corners, OutputArray _ids, const Ptr &_params, OutputArrayOfArrays _rejectedImgPoints) { - ArucoDetector detector(_dictionary, _params); + ArucoDetector detector(_dictionary, *_params); detector.detectMarkers(_image, _corners, _ids, _rejectedImgPoints); } @@ -25,7 +25,7 @@ void refineDetectedMarkers(InputArray _image, const Ptr &_board, bool checkAllOrders, OutputArray _recoveredIdxs, const Ptr &_params) { RefineParameters refineParams(minRepDistance, errorCorrectionRate, checkAllOrders); - ArucoDetector detector(_board->getDictionary(), _params, refineParams); + ArucoDetector detector(_board->getDictionary(), *_params, refineParams); detector.refineDetectedMarkers(_image, _board, _detectedCorners, _detectedIds, _rejectedCorners, _cameraMatrix, _distCoeffs, _recoveredIdxs); } diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index 46a67bf69d..1a2602d5f1 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -90,7 +90,7 @@ static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray else grey = _image.getMat(); - const Ptr params = makePtr(); // use default params for corner refinement + DetectorParameters params; // use default params for corner refinement //// For each of the charuco corners, apply subpixel refinement using its correspondind winSize parallel_for_(Range(0, (int)filteredChessboardImgPoints.size()), [&](const Range& range) { @@ -102,12 +102,12 @@ static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray in.push_back(filteredChessboardImgPoints[i] - Point2f(0.5, 0.5)); // adjust sub-pixel coordinates for cornerSubPix Size winSize = filteredWinSizes[i]; if (winSize.height == -1 || winSize.width == -1) - winSize = Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize); + winSize = Size(params.cornerRefinementWinSize, params.cornerRefinementWinSize); cornerSubPix(grey, in, winSize, Size(), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - params->cornerRefinementMaxIterations, - params->cornerRefinementMinAccuracy)); + params.cornerRefinementMaxIterations, + params.cornerRefinementMinAccuracy)); filteredChessboardImgPoints[i] = in[0] + Point2f(0.5, 0.5); } @@ -428,7 +428,7 @@ void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, // try to find the rest of markers in the diamond vector< int > acceptedIdxs; RefineParameters refineParameters(minRepDistance, -1.f, false); - ArucoDetector detector(dictionary, makePtr(), refineParameters); + ArucoDetector detector(dictionary, DetectorParameters(), refineParameters); detector.refineDetectedMarkers(grey, _charucoDiamondLayout, currentMarker, currentMarkerId, candidates, noArray(), noArray(), acceptedIdxs); diff --git a/modules/aruco/test/test_aruco_tutorial.cpp b/modules/aruco/test/test_aruco_tutorial.cpp index 72ce9d6a49..9452036b41 100644 --- a/modules/aruco/test/test_aruco_tutorial.cpp +++ b/modules/aruco/test/test_aruco_tutorial.cpp @@ -51,7 +51,7 @@ TEST(CV_ArucoTutorial, can_find_gboriginal) FileStorage fs(dictPath, FileStorage::READ); dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml - Ptr detectorParams = makePtr(); + aruco::DetectorParameters detectorParams; aruco::ArucoDetector detector(dictionary, detectorParams); @@ -186,9 +186,9 @@ TEST(CV_ArucoTutorial, can_find_diamondmarkers) string detectorPath = cvtest::findDataFile("detector_params.yml", false); fs = FileStorage(detectorPath, FileStorage::READ); - Ptr detectorParams = makePtr(); - detectorParams->readDetectorParameters(fs.root()); - detectorParams->cornerRefinementMethod = 3; + aruco::DetectorParameters detectorParams; + detectorParams.readDetectorParameters(fs.root()); + detectorParams.cornerRefinementMethod = 3; aruco::ArucoDetector detector(dictionary, detectorParams); diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index 5cc9af8aed..e72febbf41 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -55,15 +55,14 @@ class CV_ArucoBoardPose : public cvtest::BaseTest { public: CV_ArucoBoardPose(ArucoAlgParams arucoAlgParams) { - Ptr params; + aruco::DetectorParameters params; Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - params = makePtr(); - params->minDistanceToBorder = 3; + params.minDistanceToBorder = 3; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) { - params->useAruco3Detection = true; - params->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; - params->minSideLengthCanonicalImg = 16; - params->errorCorrectionRate = 0.8; + params.useAruco3Detection = true; + params.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; + params.minSideLengthCanonicalImg = 16; + params.errorCorrectionRate = 0.8; } detector = aruco::ArucoDetector(dictionary, params); } @@ -99,7 +98,7 @@ void CV_ArucoBoardPose::run(int) { imgSize, markerBorder); vector > corners; vector ids; - detector.getDetectorParameters()->markerBorderBits = markerBorder; + detector.getDetectorParameters().markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), gridboard->getIds().size()); @@ -165,11 +164,11 @@ class CV_ArucoRefine : public cvtest::BaseTest { CV_ArucoRefine(ArucoAlgParams arucoAlgParams) { Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); - Ptr params = makePtr(); - params->minDistanceToBorder = 3; - params->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; + aruco::DetectorParameters params; + params.minDistanceToBorder = 3; + params.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) - params->useAruco3Detection = true; + params.useAruco3Detection = true; aruco::RefineParameters refineParams(10.f, 3.f, true); detector = aruco::ArucoDetector(dictionary, params, refineParams); } @@ -208,7 +207,7 @@ void CV_ArucoRefine::run(int) { // detect markers vector > corners, rejected; vector ids; - detector.getDetectorParameters()->markerBorderBits = markerBorder; + detector.getDetectorParameters().markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids, rejected); // remove a marker from detection diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 030246f6de..c7ece1b2b9 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -132,8 +132,8 @@ void CV_CharucoDetection::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr params = makePtr(); - params->minDistanceToBorder = 3; + aruco::DetectorParameters params; + params.minDistanceToBorder = 3; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.getDictionary()); @@ -160,7 +160,7 @@ void CV_CharucoDetection::run(int) { vector > corners; vector ids; - detector.getDetectorParameters()->markerBorderBits = markerBorder; + detector.getDetectorParameters().markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); if(ids.size() == 0) { @@ -238,8 +238,8 @@ void CV_CharucoPoseEstimation::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr params = makePtr(); - params->minDistanceToBorder = 3; + aruco::DetectorParameters params; + params.minDistanceToBorder = 3; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); Ptr board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, detector.getDictionary()); @@ -264,7 +264,7 @@ void CV_CharucoPoseEstimation::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.getDetectorParameters()->markerBorderBits = markerBorder; + detector.getDetectorParameters().markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), board->getIds().size()); @@ -348,8 +348,8 @@ void CV_CharucoDiamondDetection::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - Ptr params = makePtr(); - params->minDistanceToBorder = 0; + aruco::DetectorParameters params ; + params.minDistanceToBorder = 0; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); float squareLength = 0.03f; float markerLength = 0.015f; @@ -380,7 +380,7 @@ void CV_CharucoDiamondDetection::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.getDetectorParameters()->markerBorderBits = markerBorder; + detector.getDetectorParameters().markerBorderBits = markerBorder; detector.detectMarkers(img, corners, ids); if(ids.size() != 4) { @@ -643,8 +643,8 @@ TEST(Charuco, testBoardSubpixelCoords) board->draw(Size(res.width, res.height), gray, 150); cv::GaussianBlur(gray, gray, Size(5, 5), 1.0); - Ptr params = makePtr(); - params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG; + aruco::DetectorParameters params; + params.cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG; aruco::ArucoDetector detector(dict, params); @@ -674,9 +674,9 @@ TEST(Charuco, issue_14014) string imgPath = cvtest::findDataFile("aruco/recover.png"); Mat img = imread(imgPath); - Ptr detectorParams = makePtr(); - detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; - detectorParams->cornerRefinementMinAccuracy = 0.01; + aruco::DetectorParameters detectorParams; + detectorParams.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; + detectorParams.cornerRefinementMinAccuracy = 0.01; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_7X7_250), detectorParams); Ptr board = aruco::CharucoBoard::create(8, 5, 0.03455f, 0.02164f, detector.getDictionary()); From 85d735750f0afb55bfd7cb262675e18f6c007668 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 28 Nov 2022 23:59:59 +0300 Subject: [PATCH 54/99] fix EstimateParameters --- modules/aruco/include/opencv2/aruco/aruco_calib.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aruco/include/opencv2/aruco/aruco_calib.hpp b/modules/aruco/include/opencv2/aruco/aruco_calib.hpp index ad59a2c407..fc86dab843 100644 --- a/modules/aruco/include/opencv2/aruco/aruco_calib.hpp +++ b/modules/aruco/include/opencv2/aruco/aruco_calib.hpp @@ -52,7 +52,7 @@ enum PatternPositionType { * @param solvePnPMethod Method for solving a PnP problem: see @ref calib3d_solvePnP_flags (default SOLVEPNP_ITERATIVE). * @sa PatternPositionType, solvePnP(), check tutorial_aruco_detection in aruco contrib */ -struct CV_EXPORTS_W EstimateParameters { +struct CV_EXPORTS_W_SIMPLE EstimateParameters { CV_PROP_RW PatternPositionType pattern; CV_PROP_RW bool useExtrinsicGuess; CV_PROP_RW int solvePnPMethod; From 0af5f73079cbe319e6d13d7d7c6602c7a8fc91ec Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:53:16 +0200 Subject: [PATCH 55/99] Reset cuda runtime error code to cudasuccess on runtime failure. --- modules/cudev/include/opencv2/cudev/common.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/cudev/include/opencv2/cudev/common.hpp b/modules/cudev/include/opencv2/cudev/common.hpp index b4439f5515..e1a27b675f 100644 --- a/modules/cudev/include/opencv2/cudev/common.hpp +++ b/modules/cudev/include/opencv2/cudev/common.hpp @@ -69,8 +69,10 @@ using namespace cv::cuda; __host__ __forceinline__ void checkCudaError(cudaError_t err, const char* file, const int line, const char* func) { - if (cudaSuccess != err) + if (cudaSuccess != err) { + cudaGetLastError(); // reset the last stored error to cudaSuccess cv::error(cv::Error::GpuApiCallError, cudaGetErrorString(err), func, file, line); + } } #define CV_CUDEV_SAFE_CALL(expr) cv::cudev::checkCudaError((expr), __FILE__, __LINE__, CV_Func) From 53ab7c3efa1e91fff6dab02b98e31bef78929ce3 Mon Sep 17 00:00:00 2001 From: David Geldreich Date: Wed, 30 Nov 2022 18:32:28 +0000 Subject: [PATCH 56/99] keep cufftPlan2d across ConvolveImpl::convolve calls on some CUDA versions creating/destroying cufftPlan2d is very time consuming we now create them in ConvolveImpl::create() and destroy them in the dtor this solves issue #3385 --- modules/cudaarithm/src/arithm.cpp | 34 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/modules/cudaarithm/src/arithm.cpp b/modules/cudaarithm/src/arithm.cpp index 381580cff4..30cf225e15 100644 --- a/modules/cudaarithm/src/arithm.cpp +++ b/modules/cudaarithm/src/arithm.cpp @@ -439,7 +439,8 @@ namespace class ConvolutionImpl : public Convolution { public: - explicit ConvolutionImpl(Size user_block_size_) : user_block_size(user_block_size_) {} + explicit ConvolutionImpl(Size user_block_size_) : user_block_size(user_block_size_), planR2C(0), planC2R(0) {} + ~ConvolutionImpl(); void convolve(InputArray image, InputArray templ, OutputArray result, bool ccorr = false, Stream& stream = Stream::Null()); @@ -452,6 +453,9 @@ namespace Size user_block_size; Size dft_size; + cufftHandle planR2C, planC2R; + Size plan_size; + GpuMat image_spect, templ_spect, result_spect; GpuMat image_block, templ_block, result_data; }; @@ -491,6 +495,27 @@ namespace // Use maximum result matrix block size for the estimated DFT block size block_size.width = std::min(dft_size.width - templ_size.width + 1, result_size.width); block_size.height = std::min(dft_size.height - templ_size.height + 1, result_size.height); + + if (dft_size != plan_size) + { + if (planR2C != 0) + cufftSafeCall( cufftDestroy(planR2C) ); + if (planC2R != 0) + cufftSafeCall( cufftDestroy(planC2R) ); + + cufftSafeCall( cufftPlan2d(&planC2R, dft_size.height, dft_size.width, CUFFT_C2R) ); + cufftSafeCall( cufftPlan2d(&planR2C, dft_size.height, dft_size.width, CUFFT_R2C) ); + + plan_size = dft_size; + } + } + + ConvolutionImpl::~ConvolutionImpl() + { + if (planR2C != 0) + cufftSafeCall( cufftDestroy(planR2C) ); + if (planC2R != 0) + cufftSafeCall( cufftDestroy(planC2R) ); } Size ConvolutionImpl::estimateBlockSize(Size result_size) @@ -516,10 +541,6 @@ namespace cudaStream_t stream = StreamAccessor::getStream(_stream); - cufftHandle planR2C, planC2R; - cufftSafeCall( cufftPlan2d(&planC2R, dft_size.height, dft_size.width, CUFFT_C2R) ); - cufftSafeCall( cufftPlan2d(&planR2C, dft_size.height, dft_size.width, CUFFT_R2C) ); - cufftSafeCall( cufftSetStream(planR2C, stream) ); cufftSafeCall( cufftSetStream(planC2R, stream) ); @@ -559,9 +580,6 @@ namespace } } - cufftSafeCall( cufftDestroy(planR2C) ); - cufftSafeCall( cufftDestroy(planC2R) ); - syncOutput(result, _result, _stream); } } From 4e0187f7162f553d1622c9f35330f71f2746d7d1 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 5 Dec 2022 17:55:15 +0300 Subject: [PATCH 57/99] remove Ptr Dictionary --- .../aruco/include/opencv2/aruco/charuco.hpp | 3 +- modules/aruco/perf/perf_aruco.cpp | 10 +-- modules/aruco/samples/aruco_dict_utils.cpp | 67 ++++++++++--------- modules/aruco/samples/calibrate_camera.cpp | 4 +- .../samples/calibrate_camera_charuco.cpp | 6 +- modules/aruco/samples/create_board.cpp | 6 +- .../aruco/samples/create_board_charuco.cpp | 4 +- modules/aruco/samples/create_diamond.cpp | 5 +- modules/aruco/samples/create_marker.cpp | 4 +- modules/aruco/samples/detect_board.cpp | 4 +- .../aruco/samples/detect_board_charuco.cpp | 6 +- modules/aruco/samples/detect_diamonds.cpp | 6 +- modules/aruco/samples/detect_markers.cpp | 4 +- .../tutorial_charuco_create_detect.cpp | 10 +-- modules/aruco/src/aruco.cpp | 2 +- modules/aruco/src/charuco.cpp | 6 +- modules/aruco/test/test_aruco_tutorial.cpp | 8 +-- modules/aruco/test/test_boarddetection.cpp | 4 +- modules/aruco/test/test_charucodetection.cpp | 12 ++-- modules/aruco/test/test_misc.cpp | 2 +- 20 files changed, 86 insertions(+), 87 deletions(-) diff --git a/modules/aruco/include/opencv2/aruco/charuco.hpp b/modules/aruco/include/opencv2/aruco/charuco.hpp index 122228d4ab..47afb4e483 100644 --- a/modules/aruco/include/opencv2/aruco/charuco.hpp +++ b/modules/aruco/include/opencv2/aruco/charuco.hpp @@ -90,7 +90,8 @@ CV_EXPORTS_W void detectCharucoDiamond(InputArray image, InputArrayOfArrays mark OutputArrayOfArrays diamondCorners, OutputArray diamondIds, InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), - Ptr dictionary = getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME::DICT_4X4_50)); + Ptr dictionary = makePtr + (getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME::DICT_4X4_50))); diff --git a/modules/aruco/perf/perf_aruco.cpp b/modules/aruco/perf/perf_aruco.cpp index 34cc38a629..aaf5a81760 100644 --- a/modules/aruco/perf/perf_aruco.cpp +++ b/modules/aruco/perf/perf_aruco.cpp @@ -82,7 +82,7 @@ class MarkerPainter std::pair > getProjectMarker(int id, double yaw, double pitch, const aruco::DetectorParameters& parameters, - const Ptr& dictionary) + const aruco::Dictionary& dictionary) { auto marker_corners = std::make_pair(Mat(imgMarkerSize, imgMarkerSize, CV_8UC1, Scalar::all(255)), vector()); Mat& img = marker_corners.first; @@ -124,7 +124,7 @@ class MarkerPainter std::pair > > getProjectMarkersTile(const int numMarkers, const aruco::DetectorParameters& params, - const Ptr& dictionary) + const aruco::Dictionary& dictionary) { Mat tileImage(imgMarkerSize*numMarkers, imgMarkerSize*numMarkers, CV_8UC1, Scalar::all(255)); map > idCorners; @@ -177,7 +177,7 @@ static inline double getMaxDistance(map > &golds, const vec PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) { UseArucoParams testParams = GetParam(); - Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); aruco::DetectorParameters detectorParams; detectorParams.minDistanceToBorder = 1; detectorParams.markerBorderBits = 1; @@ -211,7 +211,7 @@ PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) PERF_TEST_P(EstimateAruco, ArucoSecond, ESTIMATE_PARAMS) { UseArucoParams testParams = GetParam(); - Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); aruco::DetectorParameters detectorParams; detectorParams.minDistanceToBorder = 1; detectorParams.markerBorderBits = 1; @@ -267,7 +267,7 @@ Values(std::make_pair(1440, 1), std::make_pair(480, 3), std::make_pair(144, 10)) PERF_TEST_P(EstimateLargeAruco, ArucoFHD, ESTIMATE_FHD_PARAMS) { ArucoTestParams testParams = GetParam(); - Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); aruco::DetectorParameters detectorParams; detectorParams.minDistanceToBorder = 1; detectorParams.markerBorderBits = 1; diff --git a/modules/aruco/samples/aruco_dict_utils.cpp b/modules/aruco/samples/aruco_dict_utils.cpp index f2aeedbd0f..3fb5b77e7f 100644 --- a/modules/aruco/samples/aruco_dict_utils.cpp +++ b/modules/aruco/samples/aruco_dict_utils.cpp @@ -28,8 +28,8 @@ static inline int _getSelfDistance(const Mat &marker) { return minHamming; } -static inline int getFlipDistanceToId(Ptr dict, InputArray bits, int id, bool allRotations = true) { - Mat bytesList = dict->bytesList; +static inline int getFlipDistanceToId(const aruco::Dictionary& dict, InputArray bits, int id, bool allRotations = true) { + Mat bytesList = dict.bytesList; CV_Assert(id >= 0 && id < bytesList.rows); unsigned int nRotations = 4; @@ -74,12 +74,13 @@ static inline int getFlipDistanceToId(Ptr dict, InputArray bi return currentMinDistance; } -static inline Ptr generateCustomAsymmetricDictionary(int nMarkers, int markerSize, - const Ptr &baseDictionary, int randomSeed) { +static inline aruco::Dictionary generateCustomAsymmetricDictionary(int nMarkers, int markerSize, + const aruco::Dictionary &baseDictionary, + int randomSeed) { RNG rng((uint64)(randomSeed)); - Ptr out = makePtr(); - out->markerSize = markerSize; + aruco::Dictionary out; + out.markerSize = markerSize; // theoretical maximum intermarker distance // See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014. @@ -89,16 +90,16 @@ static inline Ptr generateCustomAsymmetricDictionary(int nMar int tau = 2 * (int)std::floor(float(C) * 4.f / 3.f); // if baseDictionary is provided, calculate its intermarker distance - if(baseDictionary->bytesList.rows > 0) { - CV_Assert(baseDictionary->markerSize == markerSize); - out->bytesList = baseDictionary->bytesList.clone(); + if(baseDictionary.bytesList.rows > 0) { + CV_Assert(baseDictionary.markerSize == markerSize); + out.bytesList = baseDictionary.bytesList.clone(); int minDistance = markerSize * markerSize + 1; - for(int i = 0; i < out->bytesList.rows; i++) { - Mat markerBytes = out->bytesList.rowRange(i, i + 1); + for(int i = 0; i < out.bytesList.rows; i++) { + Mat markerBytes = out.bytesList.rowRange(i, i + 1); Mat markerBits = aruco::Dictionary::getBitsFromByteList(markerBytes, markerSize); minDistance = min(minDistance, _getSelfDistance(markerBits)); - for(int j = i + 1; j < out->bytesList.rows; j++) { + for(int j = i + 1; j < out.bytesList.rows; j++) { minDistance = min(minDistance, getFlipDistanceToId(out, markerBits, j)); } } @@ -113,7 +114,7 @@ static inline Ptr generateCustomAsymmetricDictionary(int nMar const int maxUnproductiveIterations = 5000; int unproductiveIterations = 0; - while(out->bytesList.rows < nMarkers) { + while(out.bytesList.rows < nMarkers) { Mat currentMarker(markerSize, markerSize, CV_8UC1, Scalar::all(0)); rng.fill(currentMarker, RNG::UNIFORM, 0, 2); @@ -123,7 +124,7 @@ static inline Ptr generateCustomAsymmetricDictionary(int nMar // if self distance is better or equal than current best option, calculate distance // to previous accepted markers if(selfDistance >= bestTau) { - for(int i = 0; i < out->bytesList.rows; i++) { + for(int i = 0; i < out.bytesList.rows; i++) { int currentDistance = getFlipDistanceToId(out, currentMarker, i); minDistance = min(currentDistance, minDistance); if(minDistance <= bestTau) { @@ -137,7 +138,7 @@ static inline Ptr generateCustomAsymmetricDictionary(int nMar unproductiveIterations = 0; bestTau = 0; Mat bytes = aruco::Dictionary::getByteListFromBits(currentMarker); - out->bytesList.push_back(bytes); + out.bytesList.push_back(bytes); } else { unproductiveIterations++; @@ -153,41 +154,41 @@ static inline Ptr generateCustomAsymmetricDictionary(int nMar tau = bestTau; bestTau = 0; Mat bytes = aruco::Dictionary::getByteListFromBits(bestMarker); - out->bytesList.push_back(bytes); + out.bytesList.push_back(bytes); } } } // update the maximum number of correction bits for the generated dictionary - out->maxCorrectionBits = (tau - 1) / 2; + out.maxCorrectionBits = (tau - 1) / 2; return out; } -static inline int getMinDistForDict(const Ptr& dict) { - const int dict_size = dict->bytesList.rows; - const int marker_size = dict->markerSize; +static inline int getMinDistForDict(const aruco::Dictionary& dict) { + const int dict_size = dict.bytesList.rows; + const int marker_size = dict.markerSize; int minDist = marker_size * marker_size; for (int i = 0; i < dict_size; i++) { - Mat row = dict->bytesList.row(i); - Mat marker = dict->getBitsFromByteList(row, marker_size); + Mat row = dict.bytesList.row(i); + Mat marker = dict.getBitsFromByteList(row, marker_size); for (int j = 0; j < dict_size; j++) { if (j != i) { - minDist = min(dict->getDistanceToId(marker, j), minDist); + minDist = min(dict.getDistanceToId(marker, j), minDist); } } } return minDist; } -static inline int getMinAsymDistForDict(const Ptr& dict) { - const int dict_size = dict->bytesList.rows; - const int marker_size = dict->markerSize; +static inline int getMinAsymDistForDict(const aruco::Dictionary& dict) { + const int dict_size = dict.bytesList.rows; + const int marker_size = dict.markerSize; int minDist = marker_size * marker_size; for (int i = 0; i < dict_size; i++) { - Mat row = dict->bytesList.row(i); - Mat marker = dict->getBitsFromByteList(row, marker_size); + Mat row = dict.bytesList.row(i); + Mat marker = dict.getBitsFromByteList(row, marker_size); for (int j = 0; j < dict_size; j++) { if (j != i) @@ -229,14 +230,14 @@ int main(int argc, char *argv[]) int markerSize = parser.get("markerSize"); bool checkFlippedMarkers = parser.get("r"); - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { cerr << "Invalid dictionary file" << endl; return 0; @@ -251,10 +252,10 @@ int main(int argc, char *argv[]) { Ptr fs = makePtr(outputFile, FileStorage::WRITE); if (checkFlippedMarkers) - dictionary = generateCustomAsymmetricDictionary(nMarkers, markerSize, makePtr(), 0); + dictionary = generateCustomAsymmetricDictionary(nMarkers, markerSize, aruco::Dictionary(), 0); else - dictionary = aruco::extendDictionary(nMarkers, markerSize, makePtr(), 0); - dictionary->writeDictionary(fs); + dictionary = aruco::extendDictionary(nMarkers, markerSize, aruco::Dictionary(), 0); + dictionary.writeDictionary(fs); } if (checkFlippedMarkers) { diff --git a/modules/aruco/samples/calibrate_camera.cpp b/modules/aruco/samples/calibrate_camera.cpp index 10a4a83807..e5e792c14b 100644 --- a/modules/aruco/samples/calibrate_camera.cpp +++ b/modules/aruco/samples/calibrate_camera.cpp @@ -135,14 +135,14 @@ int main(int argc, char *argv[]) { waitTime = 10; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { cerr << "Invalid dictionary file" << endl; return 0; diff --git a/modules/aruco/samples/calibrate_camera_charuco.cpp b/modules/aruco/samples/calibrate_camera_charuco.cpp index 3b5f7f499b..35f945742f 100644 --- a/modules/aruco/samples/calibrate_camera_charuco.cpp +++ b/modules/aruco/samples/calibrate_camera_charuco.cpp @@ -135,14 +135,14 @@ int main(int argc, char *argv[]) { waitTime = 10; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { cerr << "Invalid dictionary file" << endl; return 0; @@ -172,7 +172,7 @@ int main(int argc, char *argv[]) { vector< vector< Point2f > > corners, rejected; // detect markers - aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected); + aruco::detectMarkers(image, makePtr(dictionary), corners, ids, detectorParams, rejected); // refind strategy to detect more markers if(refindStrategy) aruco::refineDetectedMarkers(image, board, corners, ids, rejected); diff --git a/modules/aruco/samples/create_board.cpp b/modules/aruco/samples/create_board.cpp index 1b311a3cf4..016efbb28b 100644 --- a/modules/aruco/samples/create_board.cpp +++ b/modules/aruco/samples/create_board.cpp @@ -96,14 +96,14 @@ int main(int argc, char *argv[]) { imageSize.height = markersY * (markerLength + markerSeparation) - markerSeparation + 2 * margins; - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { std::cerr << "Invalid dictionary file" << std::endl; @@ -116,7 +116,7 @@ int main(int argc, char *argv[]) { } Ptr board = aruco::GridBoard::create(markersX, markersY, float(markerLength), - float(markerSeparation), dictionary); + float(markerSeparation), dictionary); // show created board Mat boardImage; diff --git a/modules/aruco/samples/create_board_charuco.cpp b/modules/aruco/samples/create_board_charuco.cpp index c9ece573b3..78f9819045 100644 --- a/modules/aruco/samples/create_board_charuco.cpp +++ b/modules/aruco/samples/create_board_charuco.cpp @@ -93,14 +93,14 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { std::cerr << "Invalid dictionary file" << std::endl; return 0; diff --git a/modules/aruco/samples/create_diamond.cpp b/modules/aruco/samples/create_diamond.cpp index 66aec22743..8cb77ba49d 100644 --- a/modules/aruco/samples/create_diamond.cpp +++ b/modules/aruco/samples/create_diamond.cpp @@ -86,8 +86,7 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = - aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); istringstream ss(idsString); vector< string > splittedIds; @@ -104,7 +103,7 @@ int main(int argc, char *argv[]) { ids[i] = atoi(splittedIds[i].c_str()); Mat markerImg; - aruco::drawCharucoDiamond(dictionary, ids, squareLength, markerLength, markerImg, margins, + aruco::drawCharucoDiamond(makePtr(dictionary), ids, squareLength, markerLength, markerImg, margins, borderBits); if(showImage) { diff --git a/modules/aruco/samples/create_marker.cpp b/modules/aruco/samples/create_marker.cpp index bd5b4ec988..0b1752a63f 100644 --- a/modules/aruco/samples/create_marker.cpp +++ b/modules/aruco/samples/create_marker.cpp @@ -84,14 +84,14 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { std::cerr << "Invalid dictionary file" << std::endl; return 0; diff --git a/modules/aruco/samples/detect_board.cpp b/modules/aruco/samples/detect_board.cpp index 2ca829592b..e3e5ccab7e 100644 --- a/modules/aruco/samples/detect_board.cpp +++ b/modules/aruco/samples/detect_board.cpp @@ -118,14 +118,14 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { cerr << "Invalid dictionary file" << endl; return 0; diff --git a/modules/aruco/samples/detect_board_charuco.cpp b/modules/aruco/samples/detect_board_charuco.cpp index bb22ad7fde..9f5c6d5e20 100644 --- a/modules/aruco/samples/detect_board_charuco.cpp +++ b/modules/aruco/samples/detect_board_charuco.cpp @@ -114,14 +114,14 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { cerr << "Invalid dictionary file" << endl; return 0; @@ -164,7 +164,7 @@ int main(int argc, char *argv[]) { Vec3d rvec, tvec; // detect markers - aruco::detectMarkers(image, dictionary, markerCorners, markerIds, detectorParams, + aruco::detectMarkers(image, makePtr(dictionary), markerCorners, markerIds, detectorParams, rejectedMarkers); // refind strategy to detect more markers diff --git a/modules/aruco/samples/detect_diamonds.cpp b/modules/aruco/samples/detect_diamonds.cpp index cdafdecc02..35ad298b86 100644 --- a/modules/aruco/samples/detect_diamonds.cpp +++ b/modules/aruco/samples/detect_diamonds.cpp @@ -114,14 +114,14 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { cerr << "Invalid dictionary file" << endl; return 0; @@ -166,7 +166,7 @@ int main(int argc, char *argv[]) { vector< Vec3d > rvecs, tvecs; // detect markers - aruco::detectMarkers(image, dictionary, markerCorners, markerIds, detectorParams, + aruco::detectMarkers(image, makePtr(dictionary), markerCorners, markerIds, detectorParams, rejectedMarkers); // detect diamonds diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index ff63c86105..2e0ff43d5a 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -108,14 +108,14 @@ int main(int argc, char *argv[]) { return 0; } - Ptr dictionary = aruco::getPredefinedDictionary(0); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); - bool readOk = dictionary->aruco::Dictionary::readDictionary(fs.root()); + bool readOk = dictionary.aruco::Dictionary::readDictionary(fs.root()); if(!readOk) { std::cerr << "Invalid dictionary file" << std::endl; return 0; diff --git a/modules/aruco/samples/tutorial_charuco_create_detect.cpp b/modules/aruco/samples/tutorial_charuco_create_detect.cpp index d61ab30df1..326efc0e95 100644 --- a/modules/aruco/samples/tutorial_charuco_create_detect.cpp +++ b/modules/aruco/samples/tutorial_charuco_create_detect.cpp @@ -13,7 +13,7 @@ const char* keys = "{c | | Put value of c=1 to create charuco board static inline void createBoard() { - cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); + cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); //! [createBoard] cv::Ptr board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary); cv::Mat boardImage; @@ -36,7 +36,7 @@ static inline void detectCharucoBoardWithCalibrationPose() std::cerr << "Invalid camera file" << std::endl; } else { //! [dictboard] - cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); + cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); cv::Ptr board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary); cv::Ptr params = cv::makePtr(); //! [dictboard] @@ -50,7 +50,7 @@ static inline void detectCharucoBoardWithCalibrationPose() //! [midcornerdet] std::vector markerIds; std::vector > markerCorners; - cv::aruco::detectMarkers(image, board->getDictionary(), markerCorners, markerIds, params); + cv::aruco::detectMarkers(image, cv::makePtr(board->getDictionary()), markerCorners, markerIds, params); //! [midcornerdet] // if at least one marker detected if (markerIds.size() > 0) { @@ -89,7 +89,7 @@ static inline void detectCharucoBoardWithoutCalibration() { cv::VideoCapture inputVideo; inputVideo.open(0); - cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); + cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250); cv::Ptr board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary); cv::Ptr params = cv::makePtr(); @@ -100,7 +100,7 @@ static inline void detectCharucoBoardWithoutCalibration() image.copyTo(imageCopy); std::vector markerIds; std::vector > markerCorners; - cv::aruco::detectMarkers(image, board->getDictionary(), markerCorners, markerIds, params); + cv::aruco::detectMarkers(image, cv::makePtr(board->getDictionary()), markerCorners, markerIds, params); //or //cv::aruco::detectMarkers(image, dictionary, markerCorners, markerIds, params); // if at least one marker detected diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index fefa014db3..a1b09163b7 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -14,7 +14,7 @@ using namespace std; void detectMarkers(InputArray _image, const Ptr &_dictionary, OutputArrayOfArrays _corners, OutputArray _ids, const Ptr &_params, OutputArrayOfArrays _rejectedImgPoints) { - ArucoDetector detector(_dictionary, *_params); + ArucoDetector detector(*_dictionary, *_params); detector.detectMarkers(_image, _corners, _ids, _rejectedImgPoints); } diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index 1a2602d5f1..f3592c5244 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -422,13 +422,13 @@ void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, // current id is assigned to [0], so it is the marker on the top tmpIds[0] = currentId; // create Charuco board layout for diamond (3x3 layout) - Ptr _charucoDiamondLayout = CharucoBoard::create(3, 3, squareMarkerLengthRate, 1., dictionary, + Ptr _charucoDiamondLayout = CharucoBoard::create(3, 3, squareMarkerLengthRate, 1., *dictionary, tmpIds); // try to find the rest of markers in the diamond vector< int > acceptedIdxs; RefineParameters refineParameters(minRepDistance, -1.f, false); - ArucoDetector detector(dictionary, DetectorParameters(), refineParameters); + ArucoDetector detector(*dictionary, DetectorParameters(), refineParameters); detector.refineDetectedMarkers(grey, _charucoDiamondLayout, currentMarker, currentMarkerId, candidates, noArray(), noArray(), acceptedIdxs); @@ -495,7 +495,7 @@ void drawCharucoDiamond(const Ptr &dictionary, Vec4i ids, int square for(int i = 0; i < 4; i++) tmpIds[i] = ids[i]; // create a charuco board similar to a charuco marker and print it - Ptr board = CharucoBoard::create(3, 3, (float)squareLength, (float)markerLength, dictionary, tmpIds); + Ptr board = CharucoBoard::create(3, 3, (float)squareLength, (float)markerLength, *dictionary, tmpIds); Size outSize(3 * squareLength + 2 * marginSize, 3 * squareLength + 2 * marginSize); board->draw(outSize, _img, marginSize, borderBits); } diff --git a/modules/aruco/test/test_aruco_tutorial.cpp b/modules/aruco/test/test_aruco_tutorial.cpp index 9452036b41..2531178e34 100644 --- a/modules/aruco/test/test_aruco_tutorial.cpp +++ b/modules/aruco/test/test_aruco_tutorial.cpp @@ -47,10 +47,10 @@ TEST(CV_ArucoTutorial, can_find_gboriginal) string imgPath = cvtest::findDataFile("gboriginal.png", false); Mat image = imread(imgPath); string dictPath = cvtest::findDataFile("tutorial_dict.yml", false); - Ptr dictionary = makePtr(); + aruco::Dictionary dictionary; FileStorage fs(dictPath, FileStorage::READ); - dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml + dictionary.aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml aruco::DetectorParameters detectorParams; aruco::ArucoDetector detector(dictionary, detectorParams); @@ -180,9 +180,9 @@ TEST(CV_ArucoTutorial, can_find_diamondmarkers) Mat image = imread(imgPath); string dictPath = cvtest::findDataFile("tutorial_dict.yml", false); - Ptr dictionary = makePtr(); + aruco::Dictionary dictionary; FileStorage fs(dictPath, FileStorage::READ); - dictionary->aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml + dictionary.aruco::Dictionary::readDictionary(fs.root()); // set marker from tutorial_dict.yml string detectorPath = cvtest::findDataFile("detector_params.yml", false); fs = FileStorage(detectorPath, FileStorage::READ); diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index e72febbf41..4339df3643 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -56,7 +56,7 @@ class CV_ArucoBoardPose : public cvtest::BaseTest { CV_ArucoBoardPose(ArucoAlgParams arucoAlgParams) { aruco::DetectorParameters params; - Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); params.minDistanceToBorder = 3; if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) { params.useAruco3Detection = true; @@ -163,7 +163,7 @@ class CV_ArucoRefine : public cvtest::BaseTest { public: CV_ArucoRefine(ArucoAlgParams arucoAlgParams) { - Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); aruco::DetectorParameters params; params.minDistanceToBorder = 3; params.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index c7ece1b2b9..176b29a394 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -392,7 +392,7 @@ void CV_CharucoDiamondDetection::run(int) { vector< vector< Point2f > > diamondCorners; vector< Vec4i > diamondIds; aruco::detectCharucoDiamond(img, corners, ids, squareLength / markerLength, diamondCorners, diamondIds, - cameraMatrix, distCoeffs, detector.getDictionary()); + cameraMatrix, distCoeffs, makePtr(detector.getDictionary())); // check results if(diamondIds.size() != 1) { @@ -485,7 +485,7 @@ CV_CharucoBoardCreation::CV_CharucoBoardCreation() {} void CV_CharucoBoardCreation::run(int) { - Ptr dictionary = aruco::getPredefinedDictionary(aruco::DICT_5X5_250); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_5X5_250); int n = 6; float markerSizeFactor = 0.5f; @@ -545,8 +545,7 @@ TEST(Charuco, testCharucoCornersCollinear_true) Ptr detectorParams = makePtr(); - Ptr dictionary = - aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); Ptr charucoBoard = aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary); @@ -587,8 +586,7 @@ TEST(Charuco, testCharucoCornersCollinear_false) Ptr detectorParams = makePtr(); - Ptr dictionary = - aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); Ptr charucoBoard = aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary); @@ -636,7 +634,7 @@ TEST(Charuco, testBoardSubpixelCoords) cv::Mat gray; - Ptr dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_APRILTAG_36h11); + aruco::Dictionary dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_APRILTAG_36h11); Ptr board = cv::aruco::CharucoBoard::create(4, 4, 1.f, .8f, dict); // generate ChArUco board diff --git a/modules/aruco/test/test_misc.cpp b/modules/aruco/test/test_misc.cpp index 2224fc574a..5c88007134 100644 --- a/modules/aruco/test/test_misc.cpp +++ b/modules/aruco/test/test_misc.cpp @@ -13,7 +13,7 @@ TEST(CV_ArucoDrawMarker, regression_1226) int bwidth = 1600; int bheight = 1200; - cv::Ptr dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50); + cv::aruco::Dictionary dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50); cv::Ptr board = cv::aruco::CharucoBoard::create(squares_x, squares_y, 1.0, 0.75, dict); cv::Size sz(bwidth, bheight); cv::Mat mat; From 38b0a774309dc797225e2c76febbde2067fe3a58 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 6 Dec 2022 14:00:38 +0300 Subject: [PATCH 58/99] move py aruco tests --- modules/aruco/misc/python/test/test_aruco.py | 87 -------------------- 1 file changed, 87 deletions(-) diff --git a/modules/aruco/misc/python/test/test_aruco.py b/modules/aruco/misc/python/test/test_aruco.py index a37ccd6183..882971b61c 100644 --- a/modules/aruco/misc/python/test/test_aruco.py +++ b/modules/aruco/misc/python/test/test_aruco.py @@ -11,22 +11,6 @@ class aruco_test(NewOpenCVTests): - def test_idsAccessibility(self): - - ids = np.arange(17) - rev_ids = ids[::-1] - - aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_250) - board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict) - - np.testing.assert_array_equal(board.getIds().squeeze(), ids) - - board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict, rev_ids) - np.testing.assert_array_equal(board.getIds().squeeze(), rev_ids) - - board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict, ids) - np.testing.assert_array_equal(board.getIds().squeeze(), ids) - def test_drawCharucoDiamond(self): aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) img = cv.aruco.drawCharucoDiamond(aruco_dict, np.array([0, 1, 2, 3]), 100, 80) @@ -61,76 +45,5 @@ def test_write_read_dict(self): if os.path.exists(filename): os.remove(filename) - def test_identify(self): - aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) - expected_idx = 9 - expected_rotation = 2 - bit_marker = np.array([[0, 1, 1, 0], [1, 0, 1, 0], [1, 1, 1, 1], [0, 0, 1, 1]], dtype=np.uint8) - - check, idx, rotation = aruco_dict.identify(bit_marker, 0) - - self.assertTrue(check, True) - self.assertEqual(idx, expected_idx) - self.assertEqual(rotation, expected_rotation) - - def test_getDistanceToId(self): - aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) - idx = 7 - rotation = 3 - bit_marker = np.array([[0, 1, 0, 1], [0, 1, 1, 1], [1, 1, 0, 0], [0, 1, 0, 0]], dtype=np.uint8) - dist = aruco_dict.getDistanceToId(bit_marker, idx) - - self.assertEqual(dist, 0) - - def test_aruco_detector(self): - aruco_params = cv.aruco.DetectorParameters() - aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) - aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) - id = 2 - marker_size = 100 - offset = 10 - img_marker = cv.aruco.drawMarker(aruco_dict, id, marker_size, aruco_params.markerBorderBits) - img_marker = np.pad(img_marker, pad_width=offset, mode='constant', constant_values=255) - gold_corners = np.array([[offset, offset],[marker_size+offset-1.0,offset], - [marker_size+offset-1.0,marker_size+offset-1.0], - [offset, marker_size+offset-1.0]], dtype=np.float32) - expected_corners, expected_ids, expected_rejected = cv.aruco.detectMarkers(img_marker, aruco_dict, - parameters=aruco_params) - - corners, ids, rejected = aruco_detector.detectMarkers(img_marker) - - self.assertEqual(1, len(ids)) - self.assertEqual(id, ids[0]) - for i in range(0, len(ids)): - np.testing.assert_array_equal(expected_corners[i], corners[i]) - np.testing.assert_array_equal(gold_corners, corners[i].reshape(4, 2)) - - def test_aruco_detector_refine(self): - aruco_params = cv.aruco.DetectorParameters() - aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) - aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) - board_size = (3, 4) - board = cv.aruco.GridBoard_create(board_size[0], board_size[1], 5.0, 1.0, aruco_dict) - board_image = board.draw((board_size[0]*50, board_size[1]*50), marginSize=10) - - corners, ids, rejected = aruco_detector.detectMarkers(board_image) - self.assertEqual(board_size[0]*board_size[1], len(ids)) - - part_corners, part_ids, part_rejected = corners[:-1], ids[:-1], list(rejected) - part_rejected.append(corners[-1]) - - refine_corners, refine_ids, refine_rejected, recovered_ids = aruco_detector.refineDetectedMarkers(board_image, board, part_corners, part_ids, part_rejected) - refine_corners_c, _, _, _ = cv.aruco.refineDetectedMarkers(board_image, board, part_corners, part_ids, part_rejected) - - self.assertEqual(board_size[0] * board_size[1], len(refine_ids)) - self.assertEqual(1, len(recovered_ids)) - - for i in range(0, len(ids)): - np.testing.assert_array_equal(refine_corners_c[i], refine_corners[i]) - #self.assertEqual(ids[-1], recovered_ids[0]) - self.assertEqual(ids[-1], refine_ids[-1]) - self.assertEqual((1, 4, 2), refine_corners[0].shape) - np.testing.assert_array_equal(corners, refine_corners) - if __name__ == '__main__': NewOpenCVTests.bootstrap() From 774dfe125817d28a9763ac3050cd47e2a37cc4cf Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 6 Dec 2022 14:51:41 +0300 Subject: [PATCH 59/99] move java aruco tests --- modules/aruco/misc/java/test/ArucoTest.java | 84 --------------------- 1 file changed, 84 deletions(-) delete mode 100644 modules/aruco/misc/java/test/ArucoTest.java diff --git a/modules/aruco/misc/java/test/ArucoTest.java b/modules/aruco/misc/java/test/ArucoTest.java deleted file mode 100644 index 9a601f1d49..0000000000 --- a/modules/aruco/misc/java/test/ArucoTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.opencv.test.aruco; - -import java.util.ArrayList; -import java.util.List; - -import org.opencv.test.OpenCVTestCase; -import org.opencv.core.Scalar; -import org.opencv.core.Mat; -import org.opencv.core.Size; -import org.opencv.core.CvType; -import org.opencv.aruco.*; -import org.opencv.objdetect.*; - - -public class ArucoTest extends OpenCVTestCase { - - public void testDrawBoards() { - Dictionary dictionary = Objdetect.getPredefinedDictionary(Objdetect.DICT_4X4_50); - - Mat point1 = new Mat(4, 3, CvType.CV_32FC1); - int row = 0, col = 0; - double squareLength = 40.; - point1.put(row, col, 0, 0, 0, - 0, squareLength, 0, - squareLength, squareLength, 0, - 0, squareLength, 0); - ListobjPoints = new ArrayList(); - objPoints.add(point1); - - Mat ids = new Mat(1, 1, CvType.CV_32SC1); - ids.put(row, col, 0); - - Board board = Board.create(objPoints, dictionary, ids); - - Mat image = new Mat(); - board.draw(new Size(80, 80), image, 2); - - assertTrue(image.total() > 0); - } - - public void testArucoIssue3133() { - byte[][] marker = {{0,1,1},{1,1,1},{0,1,1}}; - Dictionary dictionary = Objdetect.extendDictionary(1, 3); - dictionary.set_maxCorrectionBits(0); - Mat markerBits = new Mat(3, 3, CvType.CV_8UC1); - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - markerBits.put(i, j, marker[i][j]); - } - } - - Mat markerCompressed = Dictionary.getByteListFromBits(markerBits); - assertMatNotEqual(markerCompressed, dictionary.get_bytesList()); - - dictionary.set_bytesList(markerCompressed); - assertMatEqual(markerCompressed, dictionary.get_bytesList()); - } - - public void testArucoDetector() { - Dictionary dictionary = Objdetect.getPredefinedDictionary(0); - DetectorParameters detectorParameters = new DetectorParameters(); - ArucoDetector detector = new ArucoDetector(dictionary, detectorParameters); - - Mat markerImage = new Mat(); - int id = 1, offset = 5, size = 40; - Objdetect.drawMarker(dictionary, id, size, markerImage, detectorParameters.get_markerBorderBits()); - - Mat image = new Mat(markerImage.rows() + 2*offset, markerImage.cols() + 2*offset, - CvType.CV_8UC1, new Scalar(255)); - Mat m = image.submat(offset, size+offset, offset, size+offset); - markerImage.copyTo(m); - - List corners = new ArrayList(); - Mat ids = new Mat(); - detector.detectMarkers(image, corners, ids); - - assertEquals(1, corners.size()); - Mat res = corners.get(0); - assertArrayEquals(new double[]{offset, offset}, res.get(0, 0), 0.0); - assertArrayEquals(new double[]{size + offset - 1, offset}, res.get(0, 1), 0.0); - assertArrayEquals(new double[]{size + offset - 1, size + offset - 1}, res.get(0, 2), 0.0); - assertArrayEquals(new double[]{offset, size + offset - 1}, res.get(0, 3), 0.0); - } -} From e28f18c2b04f6e4144203037c66755aa4b66ed11 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Fri, 9 Dec 2022 00:10:43 +0300 Subject: [PATCH 60/99] fixes --- modules/aruco/samples/detect_diamonds.cpp | 4 ++-- modules/aruco/samples/detect_markers.cpp | 4 ++-- modules/aruco/test/test_aruco_tutorial.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/aruco/samples/detect_diamonds.cpp b/modules/aruco/samples/detect_diamonds.cpp index 35ad298b86..ac5e8ac3a9 100644 --- a/modules/aruco/samples/detect_diamonds.cpp +++ b/modules/aruco/samples/detect_diamonds.cpp @@ -98,9 +98,9 @@ int main(int argc, char *argv[]) { } if (parser.has("refine")) { //override cornerRefinementMethod read from config file - detectorParams->cornerRefinementMethod = parser.get("refine"); + detectorParams->cornerRefinementMethod = parser.get("refine"); } - std::cout << "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag 2): " << detectorParams->cornerRefinementMethod << std::endl; + std::cout << "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag 2): " << (int)detectorParams->cornerRefinementMethod << std::endl; int camId = parser.get("ci"); String video; diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index 2e0ff43d5a..0cb535cd6d 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -92,9 +92,9 @@ int main(int argc, char *argv[]) { if (parser.has("refine")) { //override cornerRefinementMethod read from config file - detectorParams.cornerRefinementMethod = parser.get("refine"); + detectorParams.cornerRefinementMethod = parser.get("refine"); } - std::cout << "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag 2): " << detectorParams.cornerRefinementMethod << std::endl; + std::cout << "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag 2): " << (int)detectorParams.cornerRefinementMethod << std::endl; int camId = parser.get("ci"); diff --git a/modules/aruco/test/test_aruco_tutorial.cpp b/modules/aruco/test/test_aruco_tutorial.cpp index 2531178e34..11d4595ff2 100644 --- a/modules/aruco/test/test_aruco_tutorial.cpp +++ b/modules/aruco/test/test_aruco_tutorial.cpp @@ -188,7 +188,7 @@ TEST(CV_ArucoTutorial, can_find_diamondmarkers) fs = FileStorage(detectorPath, FileStorage::READ); aruco::DetectorParameters detectorParams; detectorParams.readDetectorParameters(fs.root()); - detectorParams.cornerRefinementMethod = 3; + detectorParams.cornerRefinementMethod = aruco::CORNER_REFINE_APRILTAG; aruco::ArucoDetector detector(dictionary, detectorParams); From 267189d7ff5ea8f7534ec8b4f3bf5ba4fdaa5104 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Fri, 9 Dec 2022 01:15:53 +0300 Subject: [PATCH 61/99] remove ArucoDetector from contrib py test --- modules/aruco/misc/python/test/test_aruco.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/aruco/misc/python/test/test_aruco.py b/modules/aruco/misc/python/test/test_aruco.py index 882971b61c..a9814ad288 100644 --- a/modules/aruco/misc/python/test/test_aruco.py +++ b/modules/aruco/misc/python/test/test_aruco.py @@ -11,6 +11,25 @@ class aruco_test(NewOpenCVTests): + def test_aruco_detect_markers(self): + aruco_params = cv.aruco.DetectorParameters() + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) + id = 2 + marker_size = 100 + offset = 10 + img_marker = cv.aruco.drawMarker(aruco_dict, id, marker_size, aruco_params.markerBorderBits) + img_marker = np.pad(img_marker, pad_width=offset, mode='constant', constant_values=255) + gold_corners = np.array([[offset, offset],[marker_size+offset-1.0,offset], + [marker_size+offset-1.0,marker_size+offset-1.0], + [offset, marker_size+offset-1.0]], dtype=np.float32) + expected_corners, expected_ids, expected_rejected = cv.aruco.detectMarkers(img_marker, aruco_dict, + parameters=aruco_params) + + self.assertEqual(1, len(expected_ids)) + self.assertEqual(id, expected_ids[0]) + for i in range(0, len(expected_corners)): + np.testing.assert_array_equal(gold_corners, expected_corners[i].reshape(4, 2)) + def test_drawCharucoDiamond(self): aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50) img = cv.aruco.drawCharucoDiamond(aruco_dict, np.array([0, 1, 2, 3]), 100, 80) From bf4dd569eef59f1327d16b37c4bcb26fdfb2edb0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 10 Dec 2022 02:09:16 +0000 Subject: [PATCH 62/99] build: fix/eliminate MSVC warnings --- modules/ximgproc/src/find_ellipses.cpp | 16 ++++++++-------- modules/ximgproc/test/test_find_ellipses.cpp | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/ximgproc/src/find_ellipses.cpp b/modules/ximgproc/src/find_ellipses.cpp index 62bf074f7f..cde6432687 100644 --- a/modules/ximgproc/src/find_ellipses.cpp +++ b/modules/ximgproc/src/find_ellipses.cpp @@ -310,11 +310,11 @@ float EllipseDetectorImpl::getMedianSlope(std::vector &med, Point2f &ce // centers : centroid of the points in med // slopes : vector of the slopes - unsigned pointCount = med.size(); + size_t pointCount = med.size(); // CV_Assert(pointCount >= 2); - unsigned halfSize = pointCount >> 1; - unsigned quarterSize = halfSize >> 1; + size_t halfSize = pointCount >> 1; + size_t quarterSize = halfSize >> 1; std::vector xx, yy; slopes.reserve(halfSize); @@ -333,7 +333,7 @@ float EllipseDetectorImpl::getMedianSlope(std::vector &med, Point2f &ce float den = (p2.x - p1.x); float num = (p2.y - p1.y); - if (den == 0) den = 0.00001f; + den = (std::fabs(den) >= 1e-5) ? den : 0.00001f; // FIXIT: algorithm is not reliable slopes.push_back(num / den); } @@ -1341,7 +1341,7 @@ void EllipseDetectorImpl::preProcessing(Mat1b &image, Mat1b &dp, Mat1b &dn) { } const int CANNY_SHIFT = 15; - const float TAN22_5 = 0.4142135623730950488016887242097; // tan(22.5) = sqrt(2) - 1 + const float TAN22_5 = 0.4142135623730950488016887242097f; // tan(22.5) = sqrt(2) - 1 const int TG22 = (int) (TAN22_5 * (1 << CANNY_SHIFT) + 0.5); // #define CANNY_PUSH(d) *(d) = (uchar)2, *stack_top++ = (d) @@ -1723,8 +1723,8 @@ void EllipseDetectorImpl::findEllipses(Point2f ¢er, VP &edge_i, VP &edge_j, } // find peak in N and K accumulator - int iN = std::distance(accN, std::max_element(accN, accN + ACC_N_SIZE)); - int iK = std::distance(accR, std::max_element(accR, accR + ACC_R_SIZE)) + 90; + int iN = (int)std::distance(accN, std::max_element(accN, accN + ACC_N_SIZE)); + int iK = (int)std::distance(accR, std::max_element(accR, accR + ACC_R_SIZE)) + 90; // recover real values auto fK = float(iK); @@ -1767,7 +1767,7 @@ void EllipseDetectorImpl::findEllipses(Point2f ¢er, VP &edge_i, VP &edge_j, } // find peak in A accumulator - int A = std::distance(accA, std::max_element(accA, accA + ACC_A_SIZE)); + int A = (int)std::distance(accA, std::max_element(accA, accA + ACC_A_SIZE)); auto fA = float(A); // find B value. See Eq [23] in the paper diff --git a/modules/ximgproc/test/test_find_ellipses.cpp b/modules/ximgproc/test/test_find_ellipses.cpp index 9e57d89579..b1fd27bbc0 100644 --- a/modules/ximgproc/test/test_find_ellipses.cpp +++ b/modules/ximgproc/test/test_find_ellipses.cpp @@ -23,9 +23,9 @@ TEST(FindEllipsesTest, EllipsesOnly) // position check // target centers - Point2f center_1(226.9, 57.2); - Point2f center_2(393.1, 187.0); - Point2f center_3(208.5, 307.5); + Point2f center_1(226.9f, 57.2f); + Point2f center_2(393.1f, 187.0f); + Point2f center_3(208.5f, 307.5f); // matching for (auto ell: ells) { bool has_match = false; From 774a91a0e6b077527e279a0603719692d17da198 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 12 Dec 2022 22:00:28 +0300 Subject: [PATCH 63/99] add setDetectorParameters() --- modules/aruco/test/test_boarddetection.cpp | 8 ++++++-- modules/aruco/test/test_charucodetection.cpp | 11 +++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index 4339df3643..f928877559 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -82,6 +82,7 @@ void CV_ArucoBoardPose::run(int) { cameraMatrix.at< double >(1, 2) = imgSize.height / 2; Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); const int sizeX = 3, sizeY = 3; + aruco::DetectorParameters detectorParameters = detector.getDetectorParameters(); // for different perspectives for(double distance = 0.2; distance <= 0.4; distance += 0.15) { @@ -98,7 +99,8 @@ void CV_ArucoBoardPose::run(int) { imgSize, markerBorder); vector > corners; vector ids; - detector.getDetectorParameters().markerBorderBits = markerBorder; + detectorParameters.markerBorderBits = markerBorder; + detector.setDetectorParameters(detectorParameters); detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), gridboard->getIds().size()); @@ -189,6 +191,7 @@ void CV_ArucoRefine::run(int) { cameraMatrix.at< double >(0, 2) = imgSize.width / 2; cameraMatrix.at< double >(1, 2) = imgSize.height / 2; Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + aruco::DetectorParameters detectorParameters = detector.getDetectorParameters(); // for different perspectives for(double distance = 0.2; distance <= 0.4; distance += 0.2) { @@ -207,7 +210,8 @@ void CV_ArucoRefine::run(int) { // detect markers vector > corners, rejected; vector ids; - detector.getDetectorParameters().markerBorderBits = markerBorder; + detectorParameters.markerBorderBits = markerBorder; + detector.setDetectorParameters(detectorParameters); detector.detectMarkers(img, corners, ids, rejected); // remove a marker from detection diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 176b29a394..56af034774 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -160,7 +160,8 @@ void CV_CharucoDetection::run(int) { vector > corners; vector ids; - detector.getDetectorParameters().markerBorderBits = markerBorder; + params.markerBorderBits = markerBorder; + detector.setDetectorParameters(params); detector.detectMarkers(img, corners, ids); if(ids.size() == 0) { @@ -264,7 +265,8 @@ void CV_CharucoPoseEstimation::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.getDetectorParameters().markerBorderBits = markerBorder; + params.markerBorderBits = markerBorder; + detector.setDetectorParameters(params); detector.detectMarkers(img, corners, ids); ASSERT_EQ(ids.size(), board->getIds().size()); @@ -348,7 +350,7 @@ void CV_CharucoDiamondDetection::run(int) { int iter = 0; Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); Size imgSize(500, 500); - aruco::DetectorParameters params ; + aruco::DetectorParameters params; params.minDistanceToBorder = 0; aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); float squareLength = 0.03f; @@ -380,7 +382,8 @@ void CV_CharucoDiamondDetection::run(int) { // detect markers vector< vector< Point2f > > corners; vector< int > ids; - detector.getDetectorParameters().markerBorderBits = markerBorder; + params.markerBorderBits = markerBorder; + detector.setDetectorParameters(params); detector.detectMarkers(img, corners, ids); if(ids.size() != 4) { From 86fc959fe039876b4358a8ee4b46350345adcdac Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 13 Dec 2022 02:39:03 +0300 Subject: [PATCH 64/99] renaming draw --- modules/aruco/include/opencv2/aruco.hpp | 2 +- modules/aruco/misc/python/test/test_aruco.py | 2 +- modules/aruco/perf/perf_aruco.cpp | 2 +- modules/aruco/samples/create_board.cpp | 2 +- modules/aruco/samples/create_board_charuco.cpp | 2 +- modules/aruco/samples/create_marker.cpp | 2 +- modules/aruco/samples/tutorial_charuco_create_detect.cpp | 2 +- modules/aruco/src/aruco.cpp | 4 ++-- modules/aruco/src/charuco.cpp | 2 +- modules/aruco/test/test_aruco_utils.hpp | 2 +- modules/aruco/test/test_charucodetection.cpp | 2 +- modules/aruco/test/test_misc.cpp | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/aruco/include/opencv2/aruco.hpp b/modules/aruco/include/opencv2/aruco.hpp index 82ac2ea90d..01bd1e22f2 100644 --- a/modules/aruco/include/opencv2/aruco.hpp +++ b/modules/aruco/include/opencv2/aruco.hpp @@ -140,7 +140,7 @@ CV_EXPORTS_W void estimatePoseSingleMarkers(InputArrayOfArrays corners, float ma const Ptr& estimateParameters = makePtr()); -/** @deprecated Use CharucoBoard::testCharucoCornersCollinear +/** @deprecated Use CharucoBoard::checkCharucoCornersCollinear */ CV_EXPORTS_W bool testCharucoCornersCollinear(const Ptr &board, InputArray charucoIds); diff --git a/modules/aruco/misc/python/test/test_aruco.py b/modules/aruco/misc/python/test/test_aruco.py index a9814ad288..3f262a2b30 100644 --- a/modules/aruco/misc/python/test/test_aruco.py +++ b/modules/aruco/misc/python/test/test_aruco.py @@ -17,7 +17,7 @@ def test_aruco_detect_markers(self): id = 2 marker_size = 100 offset = 10 - img_marker = cv.aruco.drawMarker(aruco_dict, id, marker_size, aruco_params.markerBorderBits) + img_marker = cv.aruco.generateImageMarker(aruco_dict, id, marker_size, aruco_params.markerBorderBits) img_marker = np.pad(img_marker, pad_width=offset, mode='constant', constant_values=255) gold_corners = np.array([[offset, offset],[marker_size+offset-1.0,offset], [marker_size+offset-1.0,marker_size+offset-1.0], diff --git a/modules/aruco/perf/perf_aruco.cpp b/modules/aruco/perf/perf_aruco.cpp index aaf5a81760..8cb9224a7d 100644 --- a/modules/aruco/perf/perf_aruco.cpp +++ b/modules/aruco/perf/perf_aruco.cpp @@ -90,7 +90,7 @@ class MarkerPainter // canonical image const int markerSizePixels = static_cast(imgMarkerSize/sqrt(2.f)); - aruco::drawMarker(dictionary, id, markerSizePixels, img, parameters.markerBorderBits); + aruco::generateImageMarker(dictionary, id, markerSizePixels, img, parameters.markerBorderBits); // get rvec and tvec for the perspective const double distance = 0.1; diff --git a/modules/aruco/samples/create_board.cpp b/modules/aruco/samples/create_board.cpp index 016efbb28b..9201cba8f9 100644 --- a/modules/aruco/samples/create_board.cpp +++ b/modules/aruco/samples/create_board.cpp @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) { // show created board Mat boardImage; - board->draw(imageSize, boardImage, margins, borderBits); + board->generateImage(imageSize, boardImage, margins, borderBits); if(showImage) { imshow("board", boardImage); diff --git a/modules/aruco/samples/create_board_charuco.cpp b/modules/aruco/samples/create_board_charuco.cpp index 78f9819045..1c762cb396 100644 --- a/modules/aruco/samples/create_board_charuco.cpp +++ b/modules/aruco/samples/create_board_charuco.cpp @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) { // show created board Mat boardImage; - board->draw(imageSize, boardImage, margins, borderBits); + board->generateImage(imageSize, boardImage, margins, borderBits); if(showImage) { imshow("board", boardImage); diff --git a/modules/aruco/samples/create_marker.cpp b/modules/aruco/samples/create_marker.cpp index 0b1752a63f..2e83e9e934 100644 --- a/modules/aruco/samples/create_marker.cpp +++ b/modules/aruco/samples/create_marker.cpp @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) { } Mat markerImg; - aruco::drawMarker(dictionary, markerId, markerSize, markerImg, borderBits); + aruco::generateImageMarker(dictionary, markerId, markerSize, markerImg, borderBits); if(showImage) { imshow("marker", markerImg); diff --git a/modules/aruco/samples/tutorial_charuco_create_detect.cpp b/modules/aruco/samples/tutorial_charuco_create_detect.cpp index 326efc0e95..84ea04df48 100644 --- a/modules/aruco/samples/tutorial_charuco_create_detect.cpp +++ b/modules/aruco/samples/tutorial_charuco_create_detect.cpp @@ -17,7 +17,7 @@ static inline void createBoard() //! [createBoard] cv::Ptr board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary); cv::Mat boardImage; - board->draw(cv::Size(600, 500), boardImage, 10, 1); + board->generateImage(cv::Size(600, 500), boardImage, 10, 1); //! [createBoard] cv::imwrite("BoardImage.jpg", boardImage); } diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index a1b09163b7..52ec072c12 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -31,7 +31,7 @@ void refineDetectedMarkers(InputArray _image, const Ptr &_board, } void drawPlanarBoard(const Ptr &board, Size outSize, const _OutputArray &img, int marginSize, int borderBits) { - board->draw(outSize, img, marginSize, borderBits); + board->generateImage(outSize, img, marginSize, borderBits); } void getBoardObjectAndImagePoints(const Ptr &board, InputArrayOfArrays detectedCorners, InputArray detectedIds, @@ -120,7 +120,7 @@ bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, } bool testCharucoCornersCollinear(const Ptr &board, InputArray charucoIds) { - return board->testCharucoCornersCollinear(charucoIds); + return board->checkCharucoCornersCollinear(charucoIds); } /** diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index f3592c5244..b897ac9c07 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -497,7 +497,7 @@ void drawCharucoDiamond(const Ptr &dictionary, Vec4i ids, int square // create a charuco board similar to a charuco marker and print it Ptr board = CharucoBoard::create(3, 3, (float)squareLength, (float)markerLength, *dictionary, tmpIds); Size outSize(3 * squareLength + 2 * marginSize, 3 * squareLength + 2 * marginSize); - board->draw(outSize, _img, marginSize, borderBits); + board->generateImage(outSize, _img, marginSize, borderBits); } diff --git a/modules/aruco/test/test_aruco_utils.hpp b/modules/aruco/test/test_aruco_utils.hpp index 9ae94f789e..a8a72c1b71 100644 --- a/modules/aruco/test/test_aruco_utils.hpp +++ b/modules/aruco/test/test_aruco_utils.hpp @@ -65,7 +65,7 @@ static inline void projectMarker(Mat& img, Ptr board, int markerIn // canonical image Mat markerImg; const int markerSizePixels = 100; - aruco::drawMarker(board->getDictionary(), board->getIds()[markerIndex], markerSizePixels, markerImg, markerBorder); + aruco::generateImageMarker(board->getDictionary(), board->getIds()[markerIndex], markerSizePixels, markerImg, markerBorder); // projected corners Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 56af034774..5928b223d7 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -641,7 +641,7 @@ TEST(Charuco, testBoardSubpixelCoords) Ptr board = cv::aruco::CharucoBoard::create(4, 4, 1.f, .8f, dict); // generate ChArUco board - board->draw(Size(res.width, res.height), gray, 150); + board->generateImage(Size(res.width, res.height), gray, 150); cv::GaussianBlur(gray, gray, Size(5, 5), 1.0); aruco::DetectorParameters params; diff --git a/modules/aruco/test/test_misc.cpp b/modules/aruco/test/test_misc.cpp index 5c88007134..d560100827 100644 --- a/modules/aruco/test/test_misc.cpp +++ b/modules/aruco/test/test_misc.cpp @@ -20,7 +20,7 @@ TEST(CV_ArucoDrawMarker, regression_1226) ASSERT_NO_THROW( { - board->draw(sz, mat, 0, 1); + board->generateImage(sz, mat, 0, 1); }); } From 7b1f75121ac63438619a57ed8fb2bf9a170674f3 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 13 Dec 2022 22:46:37 +0300 Subject: [PATCH 65/99] renaming --- .../aruco/include/opencv2/aruco/charuco.hpp | 2 +- modules/aruco/samples/aruco_dict_utils.cpp | 2 +- modules/aruco/samples/calibrate_camera.cpp | 2 +- .../samples/calibrate_camera_charuco.cpp | 2 +- modules/aruco/samples/create_board.cpp | 2 +- .../aruco/samples/create_board_charuco.cpp | 2 +- modules/aruco/samples/create_diamond.cpp | 2 +- modules/aruco/samples/create_marker.cpp | 2 +- modules/aruco/samples/detect_board.cpp | 2 +- .../aruco/samples/detect_board_charuco.cpp | 2 +- modules/aruco/samples/detect_diamonds.cpp | 2 +- modules/aruco/samples/detect_markers.cpp | 2 +- modules/aruco/src/charuco.cpp | 47 +++++++------------ modules/aruco/test/test_aruco_utils.hpp | 2 +- modules/aruco/test/test_boarddetection.cpp | 2 +- modules/aruco/test/test_charucodetection.cpp | 8 ++-- .../aruco_board_detection.markdown | 4 +- .../aruco_detection/aruco_detection.markdown | 6 +-- .../charuco_detection.markdown | 4 +- 19 files changed, 42 insertions(+), 55 deletions(-) diff --git a/modules/aruco/include/opencv2/aruco/charuco.hpp b/modules/aruco/include/opencv2/aruco/charuco.hpp index 47afb4e483..bfa0c5a90f 100644 --- a/modules/aruco/include/opencv2/aruco/charuco.hpp +++ b/modules/aruco/include/opencv2/aruco/charuco.hpp @@ -91,7 +91,7 @@ CV_EXPORTS_W void detectCharucoDiamond(InputArray image, InputArrayOfArrays mark InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), Ptr dictionary = makePtr - (getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME::DICT_4X4_50))); + (getPredefinedDictionary(PREDEFINED_DICTIONARY::DICT_4X4_50))); diff --git a/modules/aruco/samples/aruco_dict_utils.cpp b/modules/aruco/samples/aruco_dict_utils.cpp index 3fb5b77e7f..a8346a0852 100644 --- a/modules/aruco/samples/aruco_dict_utils.cpp +++ b/modules/aruco/samples/aruco_dict_utils.cpp @@ -233,7 +233,7 @@ int main(int argc, char *argv[]) aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/calibrate_camera.cpp b/modules/aruco/samples/calibrate_camera.cpp index e5e792c14b..eb708b57a7 100644 --- a/modules/aruco/samples/calibrate_camera.cpp +++ b/modules/aruco/samples/calibrate_camera.cpp @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/calibrate_camera_charuco.cpp b/modules/aruco/samples/calibrate_camera_charuco.cpp index 35f945742f..073c75be2e 100644 --- a/modules/aruco/samples/calibrate_camera_charuco.cpp +++ b/modules/aruco/samples/calibrate_camera_charuco.cpp @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/create_board.cpp b/modules/aruco/samples/create_board.cpp index 9201cba8f9..1d98c4d1da 100644 --- a/modules/aruco/samples/create_board.cpp +++ b/modules/aruco/samples/create_board.cpp @@ -99,7 +99,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/create_board_charuco.cpp b/modules/aruco/samples/create_board_charuco.cpp index 1c762cb396..b3af61fbfc 100644 --- a/modules/aruco/samples/create_board_charuco.cpp +++ b/modules/aruco/samples/create_board_charuco.cpp @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/create_diamond.cpp b/modules/aruco/samples/create_diamond.cpp index 8cb77ba49d..ce4ccb0436 100644 --- a/modules/aruco/samples/create_diamond.cpp +++ b/modules/aruco/samples/create_diamond.cpp @@ -86,7 +86,7 @@ int main(int argc, char *argv[]) { return 0; } - aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); istringstream ss(idsString); vector< string > splittedIds; diff --git a/modules/aruco/samples/create_marker.cpp b/modules/aruco/samples/create_marker.cpp index 2e83e9e934..71796738ea 100644 --- a/modules/aruco/samples/create_marker.cpp +++ b/modules/aruco/samples/create_marker.cpp @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_board.cpp b/modules/aruco/samples/detect_board.cpp index e3e5ccab7e..7bdecd5858 100644 --- a/modules/aruco/samples/detect_board.cpp +++ b/modules/aruco/samples/detect_board.cpp @@ -121,7 +121,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_board_charuco.cpp b/modules/aruco/samples/detect_board_charuco.cpp index 9f5c6d5e20..b5b3b86ec2 100644 --- a/modules/aruco/samples/detect_board_charuco.cpp +++ b/modules/aruco/samples/detect_board_charuco.cpp @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_diamonds.cpp b/modules/aruco/samples/detect_diamonds.cpp index ac5e8ac3a9..08999086ec 100644 --- a/modules/aruco/samples/detect_diamonds.cpp +++ b/modules/aruco/samples/detect_diamonds.cpp @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index 0cb535cd6d..f7bcfc45d3 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index b897ac9c07..c1d773adf3 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -14,12 +14,9 @@ using namespace std; /** * Remove charuco corners if any of their minMarkers closest markers has not been detected */ -static int _filterCornersWithoutMinMarkers(const Ptr &_board, - InputArray _allCharucoCorners, - InputArray _allCharucoIds, - InputArray _allArucoIds, int minMarkers, - OutputArray _filteredCharucoCorners, - OutputArray _filteredCharucoIds) { +static int _filterCornersWithoutMinMarkers(const Ptr &_board, InputArray _allCharucoCorners, + InputArray _allCharucoIds, InputArray _allArucoIds, int minMarkers, + OutputArray _filteredCharucoCorners, OutputArray _filteredCharucoIds) { CV_Assert(minMarkers >= 0 && minMarkers <= 2); @@ -58,10 +55,8 @@ static int _filterCornersWithoutMinMarkers(const Ptr &_board, * @brief From all projected chessboard corners, select those inside the image and apply subpixel * refinement. Returns number of valid corners. */ -static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray _image, - OutputArray _selectedCorners, - OutputArray _selectedIds, - const vector< Size > &winSizes) { +static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray _image, OutputArray _selectedCorners, + OutputArray _selectedIds, const vector< Size > &winSizes) { const int minDistToBorder = 2; // minimum distance of the corner to the image border // remaining corners, ids and window refinement sizes after removing corners outside the image @@ -176,12 +171,10 @@ static void _getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, Input /** * Interpolate charuco corners using approximated pose estimation */ -static int _interpolateCornersCharucoApproxCalib(InputArrayOfArrays _markerCorners, - InputArray _markerIds, InputArray _image, - const Ptr &_board, +static int _interpolateCornersCharucoApproxCalib(InputArrayOfArrays _markerCorners, InputArray _markerIds, + InputArray _image, const Ptr &_board, InputArray _cameraMatrix, InputArray _distCoeffs, - OutputArray _charucoCorners, - OutputArray _charucoIds) { + OutputArray _charucoCorners, OutputArray _charucoIds) { CV_Assert(_image.getMat().channels() == 1 || _image.getMat().channels() == 3); CV_Assert(_markerCorners.total() == _markerIds.getMat().total() && @@ -219,11 +212,9 @@ static int _interpolateCornersCharucoApproxCalib(InputArrayOfArrays _markerCorne /** * Interpolate charuco corners using local homography */ -static int _interpolateCornersCharucoLocalHom(InputArrayOfArrays _markerCorners, - InputArray _markerIds, InputArray _image, - const Ptr &_board, - OutputArray _charucoCorners, - OutputArray _charucoIds) { +static int _interpolateCornersCharucoLocalHom(InputArrayOfArrays _markerCorners, InputArray _markerIds, + InputArray _image, const Ptr &_board, + OutputArray _charucoCorners, OutputArray _charucoIds) { CV_Assert(_image.getMat().channels() == 1 || _image.getMat().channels() == 3); CV_Assert(_markerCorners.total() == _markerIds.getMat().total() && @@ -316,14 +307,12 @@ int interpolateCornersCharuco(InputArrayOfArrays _markerCorners, InputArray _mar // if camera parameters are avaible, use approximated calibration if(_cameraMatrix.total() != 0) { - _interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, _image, _board, - _cameraMatrix, _distCoeffs, _charucoCorners, - _charucoIds); + _interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, _image, _board, _cameraMatrix, _distCoeffs, + _charucoCorners, _charucoIds); } // else use local homography else { - _interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, _image, _board, - _charucoCorners, _charucoIds); + _interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, _image, _board, _charucoCorners, _charucoIds); } // to return a charuco corner, its closest aruco markers should have been detected @@ -359,9 +348,8 @@ void drawDetectedCornersCharuco(InputOutputArray _image, InputArray _charucoCorn } -void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, - InputArray _markerIds, float squareMarkerLengthRate, - OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds, +void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners, InputArray _markerIds, + float squareMarkerLengthRate, OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds, InputArray _cameraMatrix, InputArray _distCoeffs, Ptr dictionary) { CV_Assert(_markerIds.total() > 0 && _markerIds.total() == _markerCorners.total()); @@ -501,8 +489,7 @@ void drawCharucoDiamond(const Ptr &dictionary, Vec4i ids, int square } -void drawDetectedDiamonds(InputOutputArray _image, InputArrayOfArrays _corners, - InputArray _ids, Scalar borderColor) { +void drawDetectedDiamonds(InputOutputArray _image, InputArrayOfArrays _corners, InputArray _ids, Scalar borderColor) { CV_Assert(_image.getMat().total() != 0 && (_image.getMat().channels() == 1 || _image.getMat().channels() == 3)); CV_Assert((_corners.total() == _ids.total()) || _ids.total() == 0); diff --git a/modules/aruco/test/test_aruco_utils.hpp b/modules/aruco/test/test_aruco_utils.hpp index a8a72c1b71..24fdc4ec0c 100644 --- a/modules/aruco/test/test_aruco_utils.hpp +++ b/modules/aruco/test/test_aruco_utils.hpp @@ -72,7 +72,7 @@ static inline void projectMarker(Mat& img, Ptr board, int markerIn vector corners; // get max coordinate of board - Point3f maxCoord = board->getRightBottomBorder(); + Point3f maxCoord = board->getRightBottomCorner(); // copy objPoints vector objPoints = board->getObjPoints()[markerIndex]; // move the marker to the origin diff --git a/modules/aruco/test/test_boarddetection.cpp b/modules/aruco/test/test_boarddetection.cpp index f928877559..9d35379bae 100644 --- a/modules/aruco/test/test_boarddetection.cpp +++ b/modules/aruco/test/test_boarddetection.cpp @@ -111,7 +111,7 @@ void CV_ArucoBoardPose::run(int) { estimatePoseBoard(corners, ids, board, cameraMatrix, distCoeffs, rvec, tvec); // check axes - vector axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, gridboard->getRightBottomBorder().x); + vector axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, gridboard->getRightBottomCorner().x); vector topLeft = getMarkerById(gridboard->getIds()[0], corners, ids); ASSERT_NEAR(topLeft[0].x, axes[0].x, 2.f); ASSERT_NEAR(topLeft[0].y, axes[0].y, 2.f); diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 5928b223d7..8aea923eec 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -189,7 +189,7 @@ void CV_CharucoDetection::run(int) { vector copyChessboardCorners = board->getChessboardCorners(); // move copyChessboardCorners points for (size_t i = 0; i < copyChessboardCorners.size(); i++) - copyChessboardCorners[i] -= board->getRightBottomBorder() / 2.f; + copyChessboardCorners[i] -= board->getRightBottomCorner() / 2.f; projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs, projectedCharucoCorners); @@ -419,7 +419,7 @@ void CV_CharucoDiamondDetection::run(int) { vector copyChessboardCorners = board->getChessboardCorners(); // move copyChessboardCorners points for (size_t i = 0; i < copyChessboardCorners.size(); i++) - copyChessboardCorners[i] -= board->getRightBottomBorder() / 2.f; + copyChessboardCorners[i] -= board->getRightBottomCorner() / 2.f; projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs, projectedDiamondCorners); @@ -548,7 +548,7 @@ TEST(Charuco, testCharucoCornersCollinear_true) Ptr detectorParams = makePtr(); - aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); Ptr charucoBoard = aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary); @@ -589,7 +589,7 @@ TEST(Charuco, testCharucoCornersCollinear_false) Ptr detectorParams = makePtr(); - aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); Ptr charucoBoard = aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary); diff --git a/modules/aruco/tutorials/aruco_board_detection/aruco_board_detection.markdown b/modules/aruco/tutorials/aruco_board_detection/aruco_board_detection.markdown index 6ce8603ecf..75453def9e 100644 --- a/modules/aruco/tutorials/aruco_board_detection/aruco_board_detection.markdown +++ b/modules/aruco/tutorials/aruco_board_detection/aruco_board_detection.markdown @@ -139,7 +139,7 @@ order starting on 0, so they will be 0, 1, 2, ..., 34. This can be easily custom through ```board.ids```, like in the ```Board``` parent class. After creating a Grid Board, we probably want to print it and use it. A function to generate the image -of a ```GridBoard``` is provided in ```cv::aruco::GridBoard::draw()```. For example: +of a ```GridBoard``` is provided in ```cv::aruco::GridBoard::generateImage()```. For example: @code{.cpp} cv::Ptr board = cv::aruco::GridBoard::create(5, 7, 0.04, 0.01, dictionary); @@ -152,7 +152,7 @@ to the board dimensions, it will be centered on the image. - ```boardImage```: the output image with the board. - The third parameter is the (optional) margin in pixels, so none of the markers are touching the image border. In this case the margin is 10. -- Finally, the size of the marker border, similarly to ```drawMarker()``` function. The default value is 1. +- Finally, the size of the marker border, similarly to ```generateImageMarker()``` function. The default value is 1. The output image will be something like this: diff --git a/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown b/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown index 2e81d09b1f..69d5f1d11f 100644 --- a/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown +++ b/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown @@ -67,7 +67,7 @@ Marker Creation ------ Before their detection, markers need to be printed in order to be placed in the environment. -Marker images can be generated using the `drawMarker()` function. +Marker images can be generated using the `generateImageMarker()` function. For example, lets analyze the following call: @@ -404,7 +404,7 @@ From all the provided dictionaries, it is recommended to choose the smallest one For instance, if you need 200 markers of 6x6 bits, it is better to use `DICT_6X6_250` than `DICT_6X6_1000`. The smaller the dictionary, the higher the inter-marker distance. -The list of available predefined dictionaries can be found in the documentation for the `PREDEFINED_DICTIONARY_NAME` enum. +The list of available predefined dictionaries can be found in the documentation for the `PREDEFINED_DICTIONARY` enum. ### Automatic dictionary generation @@ -644,7 +644,7 @@ This parameter indicates the width of the marker border. It is relative to the s value of 2 indicates the border has the width of two internal bits. This parameter needs to coincide with the border size of the markers you are using. The border size -can be configured in the marker drawing functions such as `drawMarker()`. +can be configured in the marker drawing functions such as `generateImageMarker()`. Default value: diff --git a/modules/aruco/tutorials/charuco_detection/charuco_detection.markdown b/modules/aruco/tutorials/charuco_detection/charuco_detection.markdown index ddcd5e91be..5d47ce5926 100644 --- a/modules/aruco/tutorials/charuco_detection/charuco_detection.markdown +++ b/modules/aruco/tutorials/charuco_detection/charuco_detection.markdown @@ -75,7 +75,7 @@ The ids of each of the markers are assigned by default in ascending order and st This can be easily customized by accessing to the ids vector through ```board.ids```, like in the ```Board``` parent class. Once we have our ```CharucoBoard``` object, we can create an image to print it. This can be done with the -CharucoBoard::draw() method: +CharucoBoard::generateImage() method: @snippet samples/tutorial_charuco_create_detect.cpp createBoard @@ -84,7 +84,7 @@ to the board dimensions, it will be centered on the image. - ```boardImage```: the output image with the board. - The third parameter is the (optional) margin in pixels, so none of the markers are touching the image border. In this case the margin is 10. -- Finally, the size of the marker border, similarly to ```drawMarker()``` function. The default value is 1. +- Finally, the size of the marker border, similarly to ```generateImageMarker()``` function. The default value is 1. The output image will be something like this: From ef510ade2e5706a68940e7ece7be991c568397c9 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 14 Dec 2022 18:46:19 +0300 Subject: [PATCH 66/99] fixing --- modules/aruco/include/opencv2/aruco/charuco.hpp | 2 +- modules/aruco/samples/aruco_dict_utils.cpp | 2 +- modules/aruco/samples/calibrate_camera.cpp | 2 +- modules/aruco/samples/calibrate_camera_charuco.cpp | 2 +- modules/aruco/samples/create_board.cpp | 2 +- modules/aruco/samples/create_board_charuco.cpp | 2 +- modules/aruco/samples/create_diamond.cpp | 2 +- modules/aruco/samples/create_marker.cpp | 2 +- modules/aruco/samples/detect_board.cpp | 2 +- modules/aruco/samples/detect_board_charuco.cpp | 2 +- modules/aruco/samples/detect_diamonds.cpp | 2 +- modules/aruco/samples/detect_markers.cpp | 2 +- modules/aruco/test/test_charucodetection.cpp | 4 ++-- .../aruco/tutorials/aruco_detection/aruco_detection.markdown | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/aruco/include/opencv2/aruco/charuco.hpp b/modules/aruco/include/opencv2/aruco/charuco.hpp index bfa0c5a90f..bdbb46a0ab 100644 --- a/modules/aruco/include/opencv2/aruco/charuco.hpp +++ b/modules/aruco/include/opencv2/aruco/charuco.hpp @@ -91,7 +91,7 @@ CV_EXPORTS_W void detectCharucoDiamond(InputArray image, InputArrayOfArrays mark InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), Ptr dictionary = makePtr - (getPredefinedDictionary(PREDEFINED_DICTIONARY::DICT_4X4_50))); + (getPredefinedDictionary(PredefinedDictionaryType::DICT_4X4_50))); diff --git a/modules/aruco/samples/aruco_dict_utils.cpp b/modules/aruco/samples/aruco_dict_utils.cpp index a8346a0852..8ddc454968 100644 --- a/modules/aruco/samples/aruco_dict_utils.cpp +++ b/modules/aruco/samples/aruco_dict_utils.cpp @@ -233,7 +233,7 @@ int main(int argc, char *argv[]) aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/calibrate_camera.cpp b/modules/aruco/samples/calibrate_camera.cpp index eb708b57a7..7f07f8df8d 100644 --- a/modules/aruco/samples/calibrate_camera.cpp +++ b/modules/aruco/samples/calibrate_camera.cpp @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/calibrate_camera_charuco.cpp b/modules/aruco/samples/calibrate_camera_charuco.cpp index 073c75be2e..fb1430b045 100644 --- a/modules/aruco/samples/calibrate_camera_charuco.cpp +++ b/modules/aruco/samples/calibrate_camera_charuco.cpp @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/create_board.cpp b/modules/aruco/samples/create_board.cpp index 1d98c4d1da..2e0f47c4a4 100644 --- a/modules/aruco/samples/create_board.cpp +++ b/modules/aruco/samples/create_board.cpp @@ -99,7 +99,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/create_board_charuco.cpp b/modules/aruco/samples/create_board_charuco.cpp index b3af61fbfc..5a1c5c8350 100644 --- a/modules/aruco/samples/create_board_charuco.cpp +++ b/modules/aruco/samples/create_board_charuco.cpp @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/create_diamond.cpp b/modules/aruco/samples/create_diamond.cpp index ce4ccb0436..1ccc0cd351 100644 --- a/modules/aruco/samples/create_diamond.cpp +++ b/modules/aruco/samples/create_diamond.cpp @@ -86,7 +86,7 @@ int main(int argc, char *argv[]) { return 0; } - aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); istringstream ss(idsString); vector< string > splittedIds; diff --git a/modules/aruco/samples/create_marker.cpp b/modules/aruco/samples/create_marker.cpp index 71796738ea..199817ca18 100644 --- a/modules/aruco/samples/create_marker.cpp +++ b/modules/aruco/samples/create_marker.cpp @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_board.cpp b/modules/aruco/samples/detect_board.cpp index 7bdecd5858..8a2e201e67 100644 --- a/modules/aruco/samples/detect_board.cpp +++ b/modules/aruco/samples/detect_board.cpp @@ -121,7 +121,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_board_charuco.cpp b/modules/aruco/samples/detect_board_charuco.cpp index b5b3b86ec2..0e0a1d3322 100644 --- a/modules/aruco/samples/detect_board_charuco.cpp +++ b/modules/aruco/samples/detect_board_charuco.cpp @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_diamonds.cpp b/modules/aruco/samples/detect_diamonds.cpp index 08999086ec..8baa267858 100644 --- a/modules/aruco/samples/detect_diamonds.cpp +++ b/modules/aruco/samples/detect_diamonds.cpp @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/samples/detect_markers.cpp b/modules/aruco/samples/detect_markers.cpp index f7bcfc45d3..7c2e8e077b 100644 --- a/modules/aruco/samples/detect_markers.cpp +++ b/modules/aruco/samples/detect_markers.cpp @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) { aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); if (parser.has("d")) { int dictionaryId = parser.get("d"); - dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); } else if (parser.has("cd")) { FileStorage fs(parser.get("cd"), FileStorage::READ); diff --git a/modules/aruco/test/test_charucodetection.cpp b/modules/aruco/test/test_charucodetection.cpp index 8aea923eec..b3ddbe8e7d 100644 --- a/modules/aruco/test/test_charucodetection.cpp +++ b/modules/aruco/test/test_charucodetection.cpp @@ -548,7 +548,7 @@ TEST(Charuco, testCharucoCornersCollinear_true) Ptr detectorParams = makePtr(); - aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); Ptr charucoBoard = aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary); @@ -589,7 +589,7 @@ TEST(Charuco, testCharucoCornersCollinear_false) Ptr detectorParams = makePtr(); - aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY(dictionaryId)); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); Ptr charucoBoard = aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary); diff --git a/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown b/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown index 69d5f1d11f..9ecd781b5a 100644 --- a/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown +++ b/modules/aruco/tutorials/aruco_detection/aruco_detection.markdown @@ -404,7 +404,7 @@ From all the provided dictionaries, it is recommended to choose the smallest one For instance, if you need 200 markers of 6x6 bits, it is better to use `DICT_6X6_250` than `DICT_6X6_1000`. The smaller the dictionary, the higher the inter-marker distance. -The list of available predefined dictionaries can be found in the documentation for the `PREDEFINED_DICTIONARY` enum. +The list of available predefined dictionaries can be found in the documentation for the `PredefinedDictionaryType` enum. ### Automatic dictionary generation From 874ca26e88e139e728d7410cbd2b2aa20f07ce56 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 15 Dec 2022 10:02:11 +0300 Subject: [PATCH 67/99] 4.x: Always run CUDA branch on CI. --- .github/workflows/PR-4.x.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index 9d2a733c55..91e5b9b08b 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -13,7 +13,6 @@ jobs: uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-U20.yaml@main Ubuntu2004-x64-CUDA: - if: "${{ contains(github.event.pull_request.labels.*.name, 'category: cuda') }}" uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-U20-Cuda.yaml@main Windows10-x64: From 8a6ea82ed0b42a7105cedb23fe9afdd901c6d360 Mon Sep 17 00:00:00 2001 From: James Bowley <12133430+cudawarped@users.noreply.github.com> Date: Sat, 15 Oct 2022 10:03:00 +0300 Subject: [PATCH 68/99] Replace all instances of texture references/objects with texture objects using the existing updated cv::cudev::Texture class. Fixes bugs in cv::cuda::demosaicing, cv::cuda::resize and cv::cuda::HoughSegmentDetector. --- modules/cudaarithm/src/cuda/lut.cu | 84 +--- modules/cudaarithm/src/lut.hpp | 6 +- modules/cudaimgproc/src/cuda/canny.cu | 217 +-------- modules/cudaimgproc/src/cuda/corners.cu | 55 +-- modules/cudaimgproc/src/cuda/debayer.cu | 48 +- modules/cudaimgproc/src/cuda/gftt.cu | 52 +-- .../cudaimgproc/src/cuda/hough_segments.cu | 25 +- modules/cudaimgproc/src/cuda/mean_shift.cu | 39 +- modules/cudaimgproc/src/gftt.cpp | 24 +- modules/cudaimgproc/test/test_color.cpp | 18 +- modules/cudaimgproc/test/test_hough.cpp | 64 ++- modules/cudaimgproc/test/test_precomp.hpp | 2 + .../include/opencv2/cudalegacy/NCV.hpp | 4 +- .../opencv2/cudalegacy/NPP_staging.hpp | 10 +- .../cudalegacy/src/cuda/NCVBroxOpticalFlow.cu | 363 ++++----------- .../src/cuda/NCVHaarObjectDetection.cu | 352 ++++---------- modules/cudalegacy/src/cuda/NPP_staging.cu | 428 +++++------------ modules/cudalegacy/src/cuda/bm.cu | 24 +- .../cudalegacy/test/TestHypothesesGrow.cpp | 3 +- modules/cudaobjdetect/src/cuda/hog.cu | 52 +-- modules/cudaobjdetect/test/test_objdetect.cpp | 13 +- modules/cudaoptflow/src/cuda/pyrlk.cu | 306 +++---------- modules/cudaoptflow/src/cuda/tvl1flow.cu | 113 +---- modules/cudastereo/src/cuda/stereobm.cu | 39 +- modules/cudawarping/src/cuda/remap.cu | 189 ++++---- modules/cudawarping/src/cuda/resize.cu | 112 ++--- modules/cudawarping/src/cuda/warp.cu | 161 ++----- modules/cudawarping/test/test_precomp.hpp | 2 + modules/cudawarping/test/test_resize.cpp | 54 +++ .../include/opencv2/cudev/ptr2d/texture.hpp | 429 +++++++++--------- .../include/opencv2/cudev/warp/shuffle.hpp | 2 +- modules/xfeatures2d/src/cuda/surf.cu | 140 +++--- modules/xfeatures2d/src/surf.cuda.cpp | 34 +- 33 files changed, 1135 insertions(+), 2329 deletions(-) diff --git a/modules/cudaarithm/src/cuda/lut.cu b/modules/cudaarithm/src/cuda/lut.cu index 336f9b2885..26e69db9a9 100644 --- a/modules/cudaarithm/src/cuda/lut.cu +++ b/modules/cudaarithm/src/cuda/lut.cu @@ -53,6 +53,7 @@ #include "opencv2/cudaarithm.hpp" #include "opencv2/cudev.hpp" #include "opencv2/core/private.cuda.hpp" +#include using namespace cv; using namespace cv::cuda; @@ -60,8 +61,6 @@ using namespace cv::cudev; namespace cv { namespace cuda { - texture texLutTable; - LookUpTableImpl::LookUpTableImpl(InputArray _lut) { if (_lut.kind() == _InputArray::CUDA_GPU_MAT) @@ -73,83 +72,28 @@ namespace cv { namespace cuda { Mat h_lut = _lut.getMat(); d_lut.upload(Mat(1, 256, h_lut.type(), h_lut.data)); } - CV_Assert( d_lut.depth() == CV_8U ); CV_Assert( d_lut.rows == 1 && d_lut.cols == 256 ); - - cc30 = deviceSupports(FEATURE_SET_COMPUTE_30); - - if (cc30) - { - // Use the texture object - cudaResourceDesc texRes; - std::memset(&texRes, 0, sizeof(texRes)); - texRes.resType = cudaResourceTypeLinear; - texRes.res.linear.devPtr = d_lut.data; - texRes.res.linear.desc = cudaCreateChannelDesc(); - texRes.res.linear.sizeInBytes = 256 * d_lut.channels() * sizeof(uchar); - - cudaTextureDesc texDescr; - std::memset(&texDescr, 0, sizeof(texDescr)); - - CV_CUDEV_SAFE_CALL( cudaCreateTextureObject(&texLutTableObj, &texRes, &texDescr, 0) ); - } - else - { - // Use the texture reference - cudaChannelFormatDesc desc = cudaCreateChannelDesc(); - CV_CUDEV_SAFE_CALL( cudaBindTexture(0, &texLutTable, d_lut.data, &desc) ); - } - } - - LookUpTableImpl::~LookUpTableImpl() - { - if (cc30) - { - // Use the texture object - cudaDestroyTextureObject(texLutTableObj); - } - else - { - // Use the texture reference - cudaUnbindTexture(texLutTable); - } + szInBytes = 256 * d_lut.channels() * sizeof(uchar); } struct LutTablePtrC1 { typedef uchar value_type; typedef uchar index_type; - - cudaTextureObject_t texLutTableObj; - - __device__ __forceinline__ uchar operator ()(uchar, uchar x) const - { - #if CV_CUDEV_ARCH < 300 - // Use the texture reference - return tex1Dfetch(texLutTable, x); - #else - // Use the texture object - return tex1Dfetch(texLutTableObj, x); - #endif + cv::cudev::TexturePtr tex; + __device__ __forceinline__ uchar operator ()(uchar, uchar x) const { + return tex(x); } }; + struct LutTablePtrC3 { typedef uchar3 value_type; typedef uchar3 index_type; - - cudaTextureObject_t texLutTableObj; - - __device__ __forceinline__ uchar3 operator ()(const uchar3&, const uchar3& x) const - { - #if CV_CUDEV_ARCH < 300 - // Use the texture reference - return make_uchar3(tex1Dfetch(texLutTable, x.x * 3), tex1Dfetch(texLutTable, x.y * 3 + 1), tex1Dfetch(texLutTable, x.z * 3 + 2)); - #else - // Use the texture object - return make_uchar3(tex1Dfetch(texLutTableObj, x.x * 3), tex1Dfetch(texLutTableObj, x.y * 3 + 1), tex1Dfetch(texLutTableObj, x.z * 3 + 2)); - #endif + cv::cudev::TexturePtr tex; + __device__ __forceinline__ uchar3 operator ()(const uchar3&, const uchar3& x) const { + return make_uchar3(tex(x.x * 3), tex(x.y * 3 + 1), tex(x.z * 3 + 2)); } }; @@ -169,20 +113,18 @@ namespace cv { namespace cuda { { GpuMat_ src1(src.reshape(1)); GpuMat_ dst1(dst.reshape(1)); - + cv::cudev::Texture tex(szInBytes, reinterpret_cast(d_lut.data)); LutTablePtrC1 tbl; - tbl.texLutTableObj = texLutTableObj; - + tbl.tex = TexturePtr(tex); dst1.assign(lut_(src1, tbl), stream); } else if (lut_cn == 3) { GpuMat_& src3 = (GpuMat_&) src; GpuMat_& dst3 = (GpuMat_&) dst; - + cv::cudev::Texture tex(szInBytes, reinterpret_cast(d_lut.data)); LutTablePtrC3 tbl; - tbl.texLutTableObj = texLutTableObj; - + tbl.tex = TexturePtr(tex); dst3.assign(lut_(src3, tbl), stream); } diff --git a/modules/cudaarithm/src/lut.hpp b/modules/cudaarithm/src/lut.hpp index 2c63e9acdf..d28b1a9e9a 100644 --- a/modules/cudaarithm/src/lut.hpp +++ b/modules/cudaarithm/src/lut.hpp @@ -15,14 +15,10 @@ class LookUpTableImpl : public LookUpTable { public: LookUpTableImpl(InputArray lut); - ~LookUpTableImpl(); - void transform(InputArray src, OutputArray dst, Stream& stream = Stream::Null()) CV_OVERRIDE; - private: GpuMat d_lut; - cudaTextureObject_t texLutTableObj; - bool cc30; + size_t szInBytes = 0; }; } } diff --git a/modules/cudaimgproc/src/cuda/canny.cu b/modules/cudaimgproc/src/cuda/canny.cu index 99a4f72a8f..46b9624fb6 100644 --- a/modules/cudaimgproc/src/cuda/canny.cu +++ b/modules/cudaimgproc/src/cuda/canny.cu @@ -48,6 +48,7 @@ #include "opencv2/core/cuda/functional.hpp" #include "opencv2/core/cuda/utility.hpp" #include "opencv2/core/cuda.hpp" +#include using namespace cv::cuda; using namespace cv::cuda::device; @@ -90,47 +91,8 @@ namespace cv { namespace cuda { namespace device namespace canny { - struct SrcTex - { - virtual ~SrcTex() {} - - __host__ SrcTex(int _xoff, int _yoff) : xoff(_xoff), yoff(_yoff) {} - - __device__ __forceinline__ virtual int operator ()(int y, int x) const = 0; - - int xoff; - int yoff; - }; - - texture tex_src(false, cudaFilterModePoint, cudaAddressModeClamp); - struct SrcTexRef : SrcTex - { - __host__ SrcTexRef(int _xoff, int _yoff) : SrcTex(_xoff, _yoff) {} - - __device__ __forceinline__ int operator ()(int y, int x) const override - { - return tex2D(tex_src, x + xoff, y + yoff); - } - }; - - struct SrcTexObj : SrcTex - { - __host__ SrcTexObj(int _xoff, int _yoff, cudaTextureObject_t _tex_src_object) : SrcTex(_xoff, _yoff), tex_src_object(_tex_src_object) { } - - __device__ __forceinline__ int operator ()(int y, int x) const override - { - return tex2D(tex_src_object, x + xoff, y + yoff); - } - - cudaTextureObject_t tex_src_object; - }; - - template < - class T, - class Norm, - typename = typename std::enable_if::value>::type - > - __global__ void calcMagnitudeKernel(const T src, PtrStepi dx, PtrStepi dy, PtrStepSzf mag, const Norm norm) + template + __global__ void calcMagnitudeKernel(cv::cudev::TextureOffPtr texSrc, PtrStepi dx, PtrStepi dy, PtrStepSzf mag, const Norm norm) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -138,8 +100,8 @@ namespace canny if (y >= mag.rows || x >= mag.cols) return; - int dxVal = (src(y - 1, x + 1) + 2 * src(y, x + 1) + src(y + 1, x + 1)) - (src(y - 1, x - 1) + 2 * src(y, x - 1) + src(y + 1, x - 1)); - int dyVal = (src(y + 1, x - 1) + 2 * src(y + 1, x) + src(y + 1, x + 1)) - (src(y - 1, x - 1) + 2 * src(y - 1, x) + src(y - 1, x + 1)); + int dxVal = (texSrc(y - 1, x + 1) + 2 * texSrc(y, x + 1) + texSrc(y + 1, x + 1)) - (texSrc(y - 1, x - 1) + 2 * texSrc(y, x - 1) + texSrc(y + 1, x - 1)); + int dyVal = (texSrc(y + 1, x - 1) + 2 * texSrc(y + 1, x) + texSrc(y + 1, x + 1)) - (texSrc(y - 1, x - 1) + 2 * texSrc(y - 1, x) + texSrc(y - 1, x + 1)); dx(y, x) = dxVal; dy(y, x) = dyVal; @@ -151,63 +113,20 @@ namespace canny { const dim3 block(16, 16); const dim3 grid(divUp(mag.cols, block.x), divUp(mag.rows, block.y)); - - bool cc30 = deviceSupports(FEATURE_SET_COMPUTE_30); - - if (cc30) + cv::cudev::TextureOff texSrc(srcWhole, yoff, xoff); + if (L2Grad) { - cudaTextureDesc texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.addressMode[0] = cudaAddressModeClamp; - texDesc.addressMode[1] = cudaAddressModeClamp; - texDesc.addressMode[2] = cudaAddressModeClamp; - - cudaTextureObject_t tex = 0; - createTextureObjectPitch2D(&tex, srcWhole, texDesc); - - SrcTexObj src(xoff, yoff, tex); - - if (L2Grad) - { - L2 norm; - calcMagnitudeKernel<<>>(src, dx, dy, mag, norm); - } - else - { - L1 norm; - calcMagnitudeKernel<<>>(src, dx, dy, mag, norm); - } - - cudaSafeCall( cudaGetLastError() ); - - if (stream == NULL) - cudaSafeCall( cudaDeviceSynchronize() ); - else - cudaSafeCall( cudaStreamSynchronize(stream) ); - - cudaSafeCall( cudaDestroyTextureObject(tex) ); + L2 norm; + calcMagnitudeKernel<<>>(texSrc, dx, dy, mag, norm); } else { - bindTexture(&tex_src, srcWhole); - SrcTexRef src(xoff, yoff); - - if (L2Grad) - { - L2 norm; - calcMagnitudeKernel<<>>(src, dx, dy, mag, norm); - } - else - { - L1 norm; - calcMagnitudeKernel<<>>(src, dx, dy, mag, norm); - } - - cudaSafeCall( cudaGetLastError() ); - - if (stream == NULL) - cudaSafeCall( cudaDeviceSynchronize() ); + L1 norm; + calcMagnitudeKernel<<>>(texSrc, dx, dy, mag, norm); } + + if (stream == NULL) + cudaSafeCall(cudaDeviceSynchronize()); } void calcMagnitude(PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, bool L2Grad, cudaStream_t stream) @@ -229,8 +148,7 @@ namespace canny namespace canny { - texture tex_mag(false, cudaFilterModePoint, cudaAddressModeClamp); - __global__ void calcMapKernel(const PtrStepSzi dx, const PtrStepi dy, PtrStepi map, const float low_thresh, const float high_thresh) + __global__ void calcMapKernel(cv::cudev::TexturePtr texMag, const PtrStepSzi dx, const PtrStepi dy, PtrStepi map, const float low_thresh, const float high_thresh) { const int CANNY_SHIFT = 15; const int TG22 = (int)(0.4142135623730950488016887242097*(1< tex2D(tex_mag, x - 1, y) && m >= tex2D(tex_mag, x + 1, y)) + if (m > texMag(y, x - 1) && m >= texMag(y, x + 1)) edge_type = 1 + (int)(m > high_thresh); } else if(dyVal > tg67x) { - if (m > tex2D(tex_mag, x, y - 1) && m >= tex2D(tex_mag, x, y + 1)) + if (m > texMag(y - 1, x) && m >= texMag(y + 1, x)) edge_type = 1 + (int)(m > high_thresh); } else { - if (m > tex2D(tex_mag, x - s, y - 1) && m >= tex2D(tex_mag, x + s, y + 1)) - edge_type = 1 + (int)(m > high_thresh); - } - } - - map(y, x) = edge_type; - } - - __global__ void calcMapKernel(const PtrStepSzi dx, const PtrStepi dy, PtrStepi map, const float low_thresh, const float high_thresh, cudaTextureObject_t tex_mag) - { - const int CANNY_SHIFT = 15; - const int TG22 = (int)(0.4142135623730950488016887242097*(1<= dx.cols - 1 || y == 0 || y >= dx.rows - 1) - return; - - int dxVal = dx(y, x); - int dyVal = dy(y, x); - - const int s = (dxVal ^ dyVal) < 0 ? -1 : 1; - const float m = tex2D(tex_mag, x, y); - - dxVal = ::abs(dxVal); - dyVal = ::abs(dyVal); - - // 0 - the pixel can not belong to an edge - // 1 - the pixel might belong to an edge - // 2 - the pixel does belong to an edge - int edge_type = 0; - - if (m > low_thresh) - { - const int tg22x = dxVal * TG22; - const int tg67x = tg22x + ((dxVal + dxVal) << CANNY_SHIFT); - - dyVal <<= CANNY_SHIFT; - - if (dyVal < tg22x) - { - if (m > tex2D(tex_mag, x - 1, y) && m >= tex2D(tex_mag, x + 1, y)) - edge_type = 1 + (int)(m > high_thresh); - } - else if(dyVal > tg67x) - { - if (m > tex2D(tex_mag, x, y - 1) && m >= tex2D(tex_mag, x, y + 1)) - edge_type = 1 + (int)(m > high_thresh); - } - else - { - if (m > tex2D(tex_mag, x - s, y - 1) && m >= tex2D(tex_mag, x + s, y + 1)) + if (m > texMag(y - 1, x - s) && m >= texMag(y + 1, x + s)) edge_type = 1 + (int)(m > high_thresh); } } @@ -338,47 +204,10 @@ namespace canny { const dim3 block(16, 16); const dim3 grid(divUp(dx.cols, block.x), divUp(dx.rows, block.y)); - - if (deviceSupports(FEATURE_SET_COMPUTE_30)) - { - // Use the texture object - cudaResourceDesc resDesc; - memset(&resDesc, 0, sizeof(resDesc)); - resDesc.resType = cudaResourceTypePitch2D; - resDesc.res.pitch2D.devPtr = mag.ptr(); - resDesc.res.pitch2D.height = mag.rows; - resDesc.res.pitch2D.width = mag.cols; - resDesc.res.pitch2D.pitchInBytes = mag.step; - resDesc.res.pitch2D.desc = cudaCreateChannelDesc(); - - cudaTextureDesc texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.addressMode[0] = cudaAddressModeClamp; - texDesc.addressMode[1] = cudaAddressModeClamp; - texDesc.addressMode[2] = cudaAddressModeClamp; - - cudaTextureObject_t tex=0; - cudaCreateTextureObject(&tex, &resDesc, &texDesc, NULL); - calcMapKernel<<>>(dx, dy, map, low_thresh, high_thresh, tex); - cudaSafeCall( cudaGetLastError() ); - - if (stream == NULL) - cudaSafeCall( cudaDeviceSynchronize() ); - else - cudaSafeCall( cudaStreamSynchronize(stream) ); - - cudaSafeCall( cudaDestroyTextureObject(tex) ); - } - else - { - // Use the texture reference - bindTexture(&tex_mag, mag); - calcMapKernel<<>>(dx, dy, map, low_thresh, high_thresh); - cudaSafeCall( cudaGetLastError() ); - - if (stream == NULL) - cudaSafeCall( cudaDeviceSynchronize() ); - } + cv::cudev::Texture texMag(mag); + calcMapKernel<<>>(texMag, dx, dy, map, low_thresh, high_thresh); + if (stream == NULL) + cudaSafeCall( cudaDeviceSynchronize() ); } } diff --git a/modules/cudaimgproc/src/cuda/corners.cu b/modules/cudaimgproc/src/cuda/corners.cu index 92a37e6fde..2f3452648c 100644 --- a/modules/cudaimgproc/src/cuda/corners.cu +++ b/modules/cudaimgproc/src/cuda/corners.cu @@ -47,6 +47,7 @@ #include "opencv2/core/cuda/vec_math.hpp" #include "opencv2/core/cuda/saturate_cast.hpp" #include "opencv2/core/cuda/border_interpolate.hpp" +#include #include "opencv2/opencv_modules.hpp" @@ -58,10 +59,7 @@ namespace cv { namespace cuda { namespace device { /////////////////////////////////////////// Corner Harris ///////////////////////////////////////////////// - texture harrisDxTex(0, cudaFilterModePoint, cudaAddressModeClamp); - texture harrisDyTex(0, cudaFilterModePoint, cudaAddressModeClamp); - - __global__ void cornerHarris_kernel(const int block_size, const float k, PtrStepSzf dst) + __global__ void cornerHarris_kernel(cv::cudev::TexturePtr texDx, cv::cudev::TexturePtr texDy, const int block_size, const float k, PtrStepSzf dst) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -81,8 +79,8 @@ namespace cv { namespace cuda { namespace device { for (int j = jbegin; j < jend; ++j) { - float dx = tex2D(harrisDxTex, j, i); - float dy = tex2D(harrisDyTex, j, i); + float dx = texDx(i, j); + float dy = texDy(i, j); a += dx * dx; b += dx * dy; @@ -95,7 +93,7 @@ namespace cv { namespace cuda { namespace device } template - __global__ void cornerHarris_kernel(const int block_size, const float k, PtrStepSzf dst, const BR border_row, const BC border_col) + __global__ void cornerHarris_kernel(cv::cudev::TexturePtr texDx, cv::cudev::TexturePtr texDy, const int block_size, const float k, PtrStepSzf dst, const BR border_row, const BC border_col) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -119,8 +117,8 @@ namespace cv { namespace cuda { namespace device { const int x = border_row.idx_col(j); - float dx = tex2D(harrisDxTex, x, y); - float dy = tex2D(harrisDyTex, x, y); + float dx = texDx(y, x); + float dy = texDy(y, x); a += dx * dx; b += dx * dy; @@ -136,22 +134,20 @@ namespace cv { namespace cuda { namespace device { dim3 block(32, 8); dim3 grid(divUp(Dx.cols, block.x), divUp(Dx.rows, block.y)); - - bindTexture(&harrisDxTex, Dx); - bindTexture(&harrisDyTex, Dy); - + cv::cudev::Texture texDx(Dx); + cv::cudev::Texture texDy(Dy); switch (border_type) { case BORDER_REFLECT101: - cornerHarris_kernel<<>>(block_size, k, dst, BrdRowReflect101(Dx.cols), BrdColReflect101(Dx.rows)); + cornerHarris_kernel<<>>(texDx, texDy, block_size, k, dst, BrdRowReflect101(Dx.cols), BrdColReflect101(Dx.rows)); break; case BORDER_REFLECT: - cornerHarris_kernel<<>>(block_size, k, dst, BrdRowReflect(Dx.cols), BrdColReflect(Dx.rows)); + cornerHarris_kernel<<>>(texDx, texDy, block_size, k, dst, BrdRowReflect(Dx.cols), BrdColReflect(Dx.rows)); break; case BORDER_REPLICATE: - cornerHarris_kernel<<>>(block_size, k, dst); + cornerHarris_kernel<<>>(texDx, texDy, block_size, k, dst); break; } @@ -163,10 +159,7 @@ namespace cv { namespace cuda { namespace device /////////////////////////////////////////// Corner Min Eigen Val ///////////////////////////////////////////////// - texture minEigenValDxTex(0, cudaFilterModePoint, cudaAddressModeClamp); - texture minEigenValDyTex(0, cudaFilterModePoint, cudaAddressModeClamp); - - __global__ void cornerMinEigenVal_kernel(const int block_size, PtrStepSzf dst) + __global__ void cornerMinEigenVal_kernel(cv::cudev::TexturePtr texMinEigenValDx, cv::cudev::TexturePtr texMinEigenValDy, const int block_size, PtrStepSzf dst) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -186,8 +179,8 @@ namespace cv { namespace cuda { namespace device { for (int j = jbegin; j < jend; ++j) { - float dx = tex2D(minEigenValDxTex, j, i); - float dy = tex2D(minEigenValDyTex, j, i); + float dx = texMinEigenValDx(i, j); + float dy = texMinEigenValDy(i, j); a += dx * dx; b += dx * dy; @@ -204,7 +197,7 @@ namespace cv { namespace cuda { namespace device template - __global__ void cornerMinEigenVal_kernel(const int block_size, PtrStepSzf dst, const BR border_row, const BC border_col) + __global__ void cornerMinEigenVal_kernel(cv::cudev::TexturePtr texMinEigenValDx, cv::cudev::TexturePtr texMinEigenValDy, const int block_size, PtrStepSzf dst, const BR border_row, const BC border_col) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -228,8 +221,8 @@ namespace cv { namespace cuda { namespace device { int x = border_row.idx_col(j); - float dx = tex2D(minEigenValDxTex, x, y); - float dy = tex2D(minEigenValDyTex, x, y); + float dx = texMinEigenValDx(y, x); + float dy = texMinEigenValDy(y, x); a += dx * dx; b += dx * dy; @@ -248,22 +241,20 @@ namespace cv { namespace cuda { namespace device { dim3 block(32, 8); dim3 grid(divUp(Dx.cols, block.x), divUp(Dx.rows, block.y)); - - bindTexture(&minEigenValDxTex, Dx); - bindTexture(&minEigenValDyTex, Dy); - + cv::cudev::Texture texMinEigenValDx(Dx); + cv::cudev::Texture texMinEigenValDy(Dy); switch (border_type) { case BORDER_REFLECT101: - cornerMinEigenVal_kernel<<>>(block_size, dst, BrdRowReflect101(Dx.cols), BrdColReflect101(Dx.rows)); + cornerMinEigenVal_kernel<<>>(texMinEigenValDx, texMinEigenValDy, block_size, dst, BrdRowReflect101(Dx.cols), BrdColReflect101(Dx.rows)); break; case BORDER_REFLECT: - cornerMinEigenVal_kernel<<>>(block_size, dst, BrdRowReflect(Dx.cols), BrdColReflect(Dx.rows)); + cornerMinEigenVal_kernel<<>>(texMinEigenValDx, texMinEigenValDy, block_size, dst, BrdRowReflect(Dx.cols), BrdColReflect(Dx.rows)); break; case BORDER_REPLICATE: - cornerMinEigenVal_kernel<<>>(block_size, dst); + cornerMinEigenVal_kernel<<>>(texMinEigenValDx, texMinEigenValDy, block_size, dst); break; } diff --git a/modules/cudaimgproc/src/cuda/debayer.cu b/modules/cudaimgproc/src/cuda/debayer.cu index 0da7813980..bfe4b6f5ea 100644 --- a/modules/cudaimgproc/src/cuda/debayer.cu +++ b/modules/cudaimgproc/src/cuda/debayer.cu @@ -48,6 +48,7 @@ #include "opencv2/core/cuda/limits.hpp" #include "opencv2/core/cuda/color.hpp" #include "opencv2/core/cuda/saturate_cast.hpp" +#include "opencv2/cudev/ptr2d/texture.hpp" namespace cv { namespace cuda { namespace device { @@ -389,10 +390,8 @@ namespace cv { namespace cuda { namespace device // // ported to CUDA - texture sourceTex(false, cudaFilterModePoint, cudaAddressModeClamp); - - template - __global__ void MHCdemosaic(PtrStepSz dst, const int2 sourceOffset, const int2 firstRed) + template + __global__ void MHCdemosaic(PtrStepSz dst, Ptr2D src, const int2 firstRed) { const float kAx = -1.0f / 8.0f, kAy = -1.5f / 8.0f, kAz = 0.5f / 8.0f /*kAw = -1.0f / 8.0f*/; const float kBx = 2.0f / 8.0f, /*kBy = 0.0f / 8.0f,*/ /*kBz = 0.0f / 8.0f,*/ kBw = 4.0f / 8.0f ; @@ -408,8 +407,8 @@ namespace cv { namespace cuda { namespace device return; int2 center; - center.x = x + sourceOffset.x; - center.y = y + sourceOffset.y; + center.x = x; + center.y = y; int4 xCoord; xCoord.x = center.x - 2; @@ -423,25 +422,26 @@ namespace cv { namespace cuda { namespace device yCoord.z = center.y + 1; yCoord.w = center.y + 2; - float C = tex2D(sourceTex, center.x, center.y); // ( 0, 0) + float C = src(center.y, center.x); // ( 0, 0) float4 Dvec; - Dvec.x = tex2D(sourceTex, xCoord.y, yCoord.y); // (-1,-1) - Dvec.y = tex2D(sourceTex, xCoord.y, yCoord.z); // (-1, 1) - Dvec.z = tex2D(sourceTex, xCoord.z, yCoord.y); // ( 1,-1) - Dvec.w = tex2D(sourceTex, xCoord.z, yCoord.z); // ( 1, 1) + Dvec.x = src(yCoord.y, xCoord.y); // (-1,-1) + Dvec.y = src(yCoord.z, xCoord.y); // (-1, 1) + Dvec.z = src(yCoord.y, xCoord.z); // ( 1,-1) + Dvec.w = src(yCoord.z, xCoord.z); // ( 1, 1) + float4 value; - value.x = tex2D(sourceTex, center.x, yCoord.x); // ( 0,-2) A0 - value.y = tex2D(sourceTex, center.x, yCoord.y); // ( 0,-1) B0 - value.z = tex2D(sourceTex, xCoord.x, center.y); // (-2, 0) E0 - value.w = tex2D(sourceTex, xCoord.y, center.y); // (-1, 0) F0 + value.x = src(yCoord.x, center.x); // ( 0,-2) A0 + value.y = src(yCoord.y, center.x); // ( 0,-1) B0 + value.z = src(center.y, xCoord.x); // (-2, 0) E0 + value.w = src(center.y, xCoord.y); // (-1, 0) F0 // (A0 + A1), (B0 + B1), (E0 + E1), (F0 + F1) - value.x += tex2D(sourceTex, center.x, yCoord.w); // ( 0, 2) A1 - value.y += tex2D(sourceTex, center.x, yCoord.z); // ( 0, 1) B1 - value.z += tex2D(sourceTex, xCoord.w, center.y); // ( 2, 0) E1 - value.w += tex2D(sourceTex, xCoord.z, center.y); // ( 1, 0) F1 + value.x += src(yCoord.w, center.x); // ( 0, 2) A1 + value.y += src(yCoord.z, center.x); // ( 0, 1) B1 + value.z += src(center.y, xCoord.w); // ( 2, 0) E1 + value.w += src(center.y, xCoord.z); // ( 1, 0) F1 float4 PATTERN; PATTERN.x = kCx * C; @@ -527,9 +527,15 @@ namespace cv { namespace cuda { namespace device const dim3 block(32, 8); const dim3 grid(divUp(src.cols, block.x), divUp(src.rows, block.y)); - bindTexture(&sourceTex, src); + if (sourceOffset.x || sourceOffset.y) { + cv::cudev::TextureOff texSrc(src, sourceOffset.y, sourceOffset.x); + MHCdemosaic><<>>((PtrStepSz)dst, texSrc, firstRed); + } + else { + cv::cudev::Texture texSrc(src); + MHCdemosaic><<>>((PtrStepSz)dst, texSrc, firstRed); + } - MHCdemosaic<<>>((PtrStepSz)dst, sourceOffset, firstRed); cudaSafeCall( cudaGetLastError() ); if (stream == 0) diff --git a/modules/cudaimgproc/src/cuda/gftt.cu b/modules/cudaimgproc/src/cuda/gftt.cu index b565561100..8178531990 100644 --- a/modules/cudaimgproc/src/cuda/gftt.cu +++ b/modules/cudaimgproc/src/cuda/gftt.cu @@ -45,36 +45,36 @@ #include #include -#include "opencv2/core/cuda/common.hpp" #include "opencv2/core/cuda/utility.hpp" +#include #include namespace cv { namespace cuda { namespace device { namespace gfft { - template __global__ void findCorners(float threshold, const Mask mask, float2* corners, int max_count, int rows, int cols, cudaTextureObject_t eigTex, int *g_counter) + template __global__ void findCorners(cv::cudev::TexturePtr tex, float threshold, const Mask mask, float2* corners, int max_count, int rows, int cols, int *g_counter) { const int j = blockIdx.x * blockDim.x + threadIdx.x; const int i = blockIdx.y * blockDim.y + threadIdx.y; if (i > 0 && i < rows - 1 && j > 0 && j < cols - 1 && mask(i, j)) { - float val = tex2D(eigTex, j, i); + float val = tex(i, j); if (val > threshold) { float maxVal = val; - maxVal = ::fmax(tex2D(eigTex, j - 1, i - 1), maxVal); - maxVal = ::fmax(tex2D(eigTex, j , i - 1), maxVal); - maxVal = ::fmax(tex2D(eigTex, j + 1, i - 1), maxVal); + maxVal = ::fmax(tex(i - 1, j - 1), maxVal); + maxVal = ::fmax(tex(i - 1, j), maxVal); + maxVal = ::fmax(tex(i - 1, j + 1), maxVal); - maxVal = ::fmax(tex2D(eigTex, j - 1, i), maxVal); - maxVal = ::fmax(tex2D(eigTex, j + 1, i), maxVal); + maxVal = ::fmax(tex(i, j - 1), maxVal); + maxVal = ::fmax(tex(i, j + 1), maxVal); - maxVal = ::fmax(tex2D(eigTex, j - 1, i + 1), maxVal); - maxVal = ::fmax(tex2D(eigTex, j , i + 1), maxVal); - maxVal = ::fmax(tex2D(eigTex, j + 1, i + 1), maxVal); + maxVal = ::fmax(tex(i + 1, j - 1), maxVal); + maxVal = ::fmax(tex(i + 1, j), maxVal); + maxVal = ::fmax(tex(i + 1, j + 1), maxVal); if (val == maxVal) { @@ -87,17 +87,18 @@ namespace cv { namespace cuda { namespace device } } - int findCorners_gpu(const cudaTextureObject_t &eigTex, const int &rows, const int &cols, float threshold, PtrStepSzb mask, float2* corners, int max_count, int* counterPtr, cudaStream_t stream) + int findCorners_gpu(const PtrStepSzf eig, float threshold, PtrStepSzb mask, float2* corners, int max_count, int* counterPtr, cudaStream_t stream) { cudaSafeCall( cudaMemsetAsync(counterPtr, 0, sizeof(int), stream) ); + cv::cudev::Texture tex(eig); dim3 block(16, 16); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y)); + dim3 grid(divUp(eig.cols, block.x), divUp(eig.rows, block.y)); if (mask.data) - findCorners<<>>(threshold, SingleMask(mask), corners, max_count, rows, cols, eigTex, counterPtr); + findCorners<<>>(tex, threshold, SingleMask(mask), corners, max_count, eig.rows, eig.cols, counterPtr); else - findCorners<<>>(threshold, WithOutMask(), corners, max_count, rows, cols, eigTex, counterPtr); + findCorners<<>>(tex, threshold, WithOutMask(), corners, max_count, eig.rows, eig.cols, counterPtr); cudaSafeCall( cudaGetLastError() ); @@ -113,27 +114,24 @@ namespace cv { namespace cuda { namespace device class EigGreater { public: - EigGreater(const cudaTextureObject_t &eigTex_) : eigTex(eigTex_) - { - } - __device__ __forceinline__ bool operator()(float2 a, float2 b) const - { - return tex2D(eigTex, a.x, a.y) > tex2D(eigTex, b.x, b.y); + EigGreater(cv::cudev::TexturePtr tex_) : tex(tex_) {} + __device__ __forceinline__ bool operator()(float2 a, float2 b) const{ + return tex(a.y, a.x) > tex(b.y, b.x); } - - cudaTextureObject_t eigTex; + cv::cudev::TexturePtr tex; }; - void sortCorners_gpu(const cudaTextureObject_t &eigTex, float2* corners, int count, cudaStream_t stream) + void sortCorners_gpu(const PtrStepSzf eig, float2* corners, int count, cudaStream_t stream) { + cv::cudev::Texture tex(eig); thrust::device_ptr ptr(corners); #if THRUST_VERSION >= 100802 if (stream) - thrust::sort(thrust::cuda::par(ThrustAllocator::getAllocator()).on(stream), ptr, ptr + count, EigGreater(eigTex)); + thrust::sort(thrust::cuda::par(ThrustAllocator::getAllocator()).on(stream), ptr, ptr + count, EigGreater(tex)); else - thrust::sort(thrust::cuda::par(ThrustAllocator::getAllocator()), ptr, ptr + count, EigGreater(eigTex)); + thrust::sort(thrust::cuda::par(ThrustAllocator::getAllocator()), ptr, ptr + count, EigGreater(tex)); #else - thrust::sort(ptr, ptr + count, EigGreater(eigTex)); + thrust::sort(ptr, ptr + count, EigGreater(tex)); #endif } } // namespace optical_flow diff --git a/modules/cudaimgproc/src/cuda/hough_segments.cu b/modules/cudaimgproc/src/cuda/hough_segments.cu index 59eb78f699..3f4c3aff87 100644 --- a/modules/cudaimgproc/src/cuda/hough_segments.cu +++ b/modules/cudaimgproc/src/cuda/hough_segments.cu @@ -50,7 +50,8 @@ namespace cv { namespace cuda { namespace device { namespace hough_segments { - __global__ void houghLinesProbabilistic(cv::cudev::Texture src, const PtrStepSzi accum, + template + __global__ void houghLinesProbabilistic(Ptr2D src, const PtrStepSzi accum, int4* out, const int maxSize, const float rho, const float theta, const int lineGap, const int lineLength, @@ -219,15 +220,18 @@ namespace cv { namespace cuda { namespace device const dim3 block(32, 8); const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); - cv::cudev::GpuMat_ src_(mask); - cv::cudev::Texture tex(src_, false, cudaFilterModePoint, cudaAddressModeClamp); - - houghLinesProbabilistic<<>>(tex, accum, - out, maxSize, - rho, theta, - lineGap, lineLength, - mask.rows, mask.cols, - counterPtr); + Size wholeSize; + Point ofs; + mask.locateROI(wholeSize, ofs); + if (ofs.x || ofs.y) { + cv::cudev::TextureOff texMask(wholeSize.height, wholeSize.width, mask.datastart, mask.step, ofs.y, ofs.x); + houghLinesProbabilistic><<>>(texMask, accum, out, maxSize, rho, theta, lineGap, lineLength, mask.rows, mask.cols, counterPtr); + } + else { + cv::cudev::Texture texMask(mask); + houghLinesProbabilistic><<>>(texMask, accum, out, maxSize, rho, theta, lineGap, lineLength, mask.rows, mask.cols, counterPtr); + } + cudaSafeCall( cudaGetLastError() ); int totalCount; @@ -236,7 +240,6 @@ namespace cv { namespace cuda { namespace device cudaSafeCall( cudaStreamSynchronize(stream) ); totalCount = ::min(totalCount, maxSize); - return totalCount; } } diff --git a/modules/cudaimgproc/src/cuda/mean_shift.cu b/modules/cudaimgproc/src/cuda/mean_shift.cu index 3b3b93f94e..ef7497be5c 100644 --- a/modules/cudaimgproc/src/cuda/mean_shift.cu +++ b/modules/cudaimgproc/src/cuda/mean_shift.cu @@ -47,19 +47,16 @@ #include "opencv2/core/cuda/vec_math.hpp" #include "opencv2/core/cuda/saturate_cast.hpp" #include "opencv2/core/cuda/border_interpolate.hpp" +#include namespace cv { namespace cuda { namespace device { namespace imgproc { - texture tex_meanshift; - - __device__ short2 do_mean_shift(int x0, int y0, unsigned char* out, - size_t out_step, int cols, int rows, - int sp, int sr, int maxIter, float eps) + __device__ short2 do_mean_shift(cv::cudev::TexturePtr tex, int x0, int y0, unsigned char* out,size_t out_step, int cols, int rows, int sp, int sr, int maxIter, float eps) { int isr2 = sr*sr; - uchar4 c = tex2D(tex_meanshift, x0, y0 ); + uchar4 c = tex(y0, x0); // iterate meanshift procedure for( int iter = 0; iter < maxIter; iter++ ) @@ -79,7 +76,7 @@ namespace cv { namespace cuda { namespace device int rowCount = 0; for( int x = minx; x <= maxx; x++ ) { - uchar4 t = tex2D( tex_meanshift, x, y ); + uchar4 t = tex(y, x); int norm2 = (t.x - c.x) * (t.x - c.x) + (t.y - c.y) * (t.y - c.y) + (t.z - c.z) * (t.z - c.z); if( norm2 <= isr2 ) @@ -119,13 +116,13 @@ namespace cv { namespace cuda { namespace device return make_short2((short)x0, (short)y0); } - __global__ void meanshift_kernel(unsigned char* out, size_t out_step, int cols, int rows, int sp, int sr, int maxIter, float eps ) + __global__ void meanshift_kernel(cv::cudev::TexturePtr tex, unsigned char* out, size_t out_step, int cols, int rows, int sp, int sr, int maxIter, float eps ) { int x0 = blockIdx.x * blockDim.x + threadIdx.x; int y0 = blockIdx.y * blockDim.y + threadIdx.y; if( x0 < cols && y0 < rows ) - do_mean_shift(x0, y0, out, out_step, cols, rows, sp, sr, maxIter, eps); + do_mean_shift(tex, x0, y0, out, out_step, cols, rows, sp, sr, maxIter, eps); } void meanShiftFiltering_gpu(const PtrStepSzb& src, PtrStepSzb dst, int sp, int sr, int maxIter, float eps, cudaStream_t stream) @@ -134,21 +131,15 @@ namespace cv { namespace cuda { namespace device dim3 threads(32, 8, 1); grid.x = divUp(src.cols, threads.x); grid.y = divUp(src.rows, threads.y); - - cudaChannelFormatDesc desc = cudaCreateChannelDesc(); - cudaSafeCall( cudaBindTexture2D( 0, tex_meanshift, src.data, desc, src.cols, src.rows, src.step ) ); - - meanshift_kernel<<< grid, threads, 0, stream >>>( dst.data, dst.step, dst.cols, dst.rows, sp, sr, maxIter, eps ); + cv::cudev::Texture tex(src.rows, src.cols, (uchar4*)src.data, src.step); + meanshift_kernel<<< grid, threads, 0, stream >>>( tex, dst.data, dst.step, dst.cols, dst.rows, sp, sr, maxIter, eps ); cudaSafeCall( cudaGetLastError() ); - if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); } - __global__ void meanshiftproc_kernel(unsigned char* outr, size_t outrstep, - unsigned char* outsp, size_t outspstep, - int cols, int rows, - int sp, int sr, int maxIter, float eps) + __global__ void meanshiftproc_kernel(cv::cudev::TexturePtr tex, unsigned char* outr, size_t outrstep, unsigned char* outsp, size_t outspstep, + int cols, int rows,int sp, int sr, int maxIter, float eps) { int x0 = blockIdx.x * blockDim.x + threadIdx.x; int y0 = blockIdx.y * blockDim.y + threadIdx.y; @@ -156,7 +147,7 @@ namespace cv { namespace cuda { namespace device if( x0 < cols && y0 < rows ) { int basesp = (blockIdx.y * blockDim.y + threadIdx.y) * outspstep + (blockIdx.x * blockDim.x + threadIdx.x) * 2 * sizeof(short); - *(short2*)(outsp + basesp) = do_mean_shift(x0, y0, outr, outrstep, cols, rows, sp, sr, maxIter, eps); + *(short2*)(outsp + basesp) = do_mean_shift(tex, x0, y0, outr, outrstep, cols, rows, sp, sr, maxIter, eps); } } @@ -166,13 +157,9 @@ namespace cv { namespace cuda { namespace device dim3 threads(32, 8, 1); grid.x = divUp(src.cols, threads.x); grid.y = divUp(src.rows, threads.y); - - cudaChannelFormatDesc desc = cudaCreateChannelDesc(); - cudaSafeCall( cudaBindTexture2D( 0, tex_meanshift, src.data, desc, src.cols, src.rows, src.step ) ); - - meanshiftproc_kernel<<< grid, threads, 0, stream >>>( dstr.data, dstr.step, dstsp.data, dstsp.step, dstr.cols, dstr.rows, sp, sr, maxIter, eps ); + cv::cudev::Texture tex(src.rows, src.cols, (uchar4*)src.data, src.step); + meanshiftproc_kernel<<< grid, threads, 0, stream >>>( tex, dstr.data, dstr.step, dstsp.data, dstsp.step, dstr.cols, dstr.rows, sp, sr, maxIter, eps ); cudaSafeCall( cudaGetLastError() ); - if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); } diff --git a/modules/cudaimgproc/src/gftt.cpp b/modules/cudaimgproc/src/gftt.cpp index ae19087aac..540534a87a 100644 --- a/modules/cudaimgproc/src/gftt.cpp +++ b/modules/cudaimgproc/src/gftt.cpp @@ -55,8 +55,8 @@ namespace cv { namespace cuda { namespace device { namespace gfft { - int findCorners_gpu(const cudaTextureObject_t &eigTex_, const int &rows, const int &cols, float threshold, PtrStepSzb mask, float2* corners, int max_count, int* counterPtr, cudaStream_t stream); - void sortCorners_gpu(const cudaTextureObject_t &eigTex_, float2* corners, int count, cudaStream_t stream); + int findCorners_gpu(const PtrStepSzf eig, float threshold, PtrStepSzb mask, float2* corners, int max_count, int* counterPtr, cudaStream_t stream); + void sortCorners_gpu(const PtrStepSzf eig, float2* corners, int count, cudaStream_t stream); } }}} @@ -120,31 +120,15 @@ namespace cudaStream_t stream_ = StreamAccessor::getStream(stream); ensureSizeIsEnough(1, std::max(1000, static_cast(image.size().area() * 0.05)), CV_32FC2, tmpCorners_); - //create texture object for findCorners_gpu and sortCorners_gpu - cudaTextureDesc texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.readMode = cudaReadModeElementType; - texDesc.filterMode = cudaFilterModePoint; - texDesc.addressMode[0] = cudaAddressModeClamp; - texDesc.addressMode[1] = cudaAddressModeClamp; - texDesc.addressMode[2] = cudaAddressModeClamp; - - cudaTextureObject_t eigTex_; - PtrStepSzf eig = eig_; - cv::cuda::device::createTextureObjectPitch2D(&eigTex_, eig, texDesc); - - int total = findCorners_gpu(eigTex_, eig_.rows, eig_.cols, static_cast(maxVal * qualityLevel_), mask, tmpCorners_.ptr(), tmpCorners_.cols, counterPtr_, stream_); + int total = findCorners_gpu(eig_, static_cast(maxVal * qualityLevel_), mask, tmpCorners_.ptr(), tmpCorners_.cols, counterPtr_, stream_); if (total == 0) { _corners.release(); - cudaSafeCall( cudaDestroyTextureObject(eigTex_) ); return; } - sortCorners_gpu(eigTex_, tmpCorners_.ptr(), total, stream_); - - cudaSafeCall( cudaDestroyTextureObject(eigTex_) ); + sortCorners_gpu(eig_, tmpCorners_.ptr(), total, stream_); if (minDistance_ < 1) { diff --git a/modules/cudaimgproc/test/test_color.cpp b/modules/cudaimgproc/test/test_color.cpp index 97be36a121..e88bc8a1db 100644 --- a/modules/cudaimgproc/test/test_color.cpp +++ b/modules/cudaimgproc/test/test_color.cpp @@ -2294,14 +2294,15 @@ INSTANTIATE_TEST_CASE_P(CUDA_ImgProc, CvtColor, testing::Combine( /////////////////////////////////////////////////////////////////////////////////////////////////////// // Demosaicing -struct Demosaicing : testing::TestWithParam +struct Demosaicing : testing::TestWithParam> { cv::cuda::DeviceInfo devInfo; + bool useRoi; virtual void SetUp() { - devInfo = GetParam(); - + devInfo = GET_PARAM(0); + useRoi = GET_PARAM(1); cv::cuda::setDevice(devInfo.deviceID()); } @@ -2419,7 +2420,7 @@ CUDA_TEST_P(Demosaicing, BayerBG2BGR_MHT) mosaic(img, src, cv::Point(1, 1)); cv::cuda::GpuMat dst; - cv::cuda::demosaicing(loadMat(src), dst, cv::cuda::COLOR_BayerBG2BGR_MHT); + cv::cuda::demosaicing(loadMat(src, useRoi), dst, cv::cuda::COLOR_BayerBG2BGR_MHT); EXPECT_MAT_SIMILAR(img, dst, 5e-3); } @@ -2433,7 +2434,7 @@ CUDA_TEST_P(Demosaicing, BayerGB2BGR_MHT) mosaic(img, src, cv::Point(0, 1)); cv::cuda::GpuMat dst; - cv::cuda::demosaicing(loadMat(src), dst, cv::cuda::COLOR_BayerGB2BGR_MHT); + cv::cuda::demosaicing(loadMat(src, useRoi), dst, cv::cuda::COLOR_BayerGB2BGR_MHT); EXPECT_MAT_SIMILAR(img, dst, 5e-3); } @@ -2447,7 +2448,7 @@ CUDA_TEST_P(Demosaicing, BayerRG2BGR_MHT) mosaic(img, src, cv::Point(0, 0)); cv::cuda::GpuMat dst; - cv::cuda::demosaicing(loadMat(src), dst, cv::cuda::COLOR_BayerRG2BGR_MHT); + cv::cuda::demosaicing(loadMat(src, useRoi), dst, cv::cuda::COLOR_BayerRG2BGR_MHT); EXPECT_MAT_SIMILAR(img, dst, 5e-3); } @@ -2461,12 +2462,11 @@ CUDA_TEST_P(Demosaicing, BayerGR2BGR_MHT) mosaic(img, src, cv::Point(1, 0)); cv::cuda::GpuMat dst; - cv::cuda::demosaicing(loadMat(src), dst, cv::cuda::COLOR_BayerGR2BGR_MHT); - + cv::cuda::demosaicing(loadMat(src, useRoi), dst, cv::cuda::COLOR_BayerGR2BGR_MHT); EXPECT_MAT_SIMILAR(img, dst, 5e-3); } -INSTANTIATE_TEST_CASE_P(CUDA_ImgProc, Demosaicing, ALL_DEVICES); +INSTANTIATE_TEST_CASE_P(CUDA_ImgProc, Demosaicing, testing::Combine(ALL_DEVICES, WHOLE_SUBMAT)); /////////////////////////////////////////////////////////////////////////////////////////////////////// // swapChannels diff --git a/modules/cudaimgproc/test/test_hough.cpp b/modules/cudaimgproc/test/test_hough.cpp index e6a05f578f..023e1c50c7 100644 --- a/modules/cudaimgproc/test/test_hough.cpp +++ b/modules/cudaimgproc/test/test_hough.cpp @@ -115,8 +115,20 @@ INSTANTIATE_TEST_CASE_P(CUDA_ImgProc, HoughLines, testing::Combine( /////////////////////////////////////////////////////////////////////////////////////////////////////// // HoughLines Probabilistic -PARAM_TEST_CASE(HoughLinesProbabilistic, cv::cuda::DeviceInfo, cv::Size, UseRoi) +PARAM_TEST_CASE(HoughLinesProbabilistic, DeviceInfo, Size, UseRoi) { + cv::cuda::DeviceInfo devInfo; + bool useRoi; + Size size; + + virtual void SetUp() + { + devInfo = GET_PARAM(0); + size = GET_PARAM(1); + useRoi = GET_PARAM(2); + cv::cuda::setDevice(devInfo.deviceID()); + } + static void generateLines(cv::Mat& img) { img.setTo(cv::Scalar::all(0)); @@ -140,11 +152,6 @@ PARAM_TEST_CASE(HoughLinesProbabilistic, cv::cuda::DeviceInfo, cv::Size, UseRoi) CUDA_TEST_P(HoughLinesProbabilistic, Accuracy) { - const cv::cuda::DeviceInfo devInfo = GET_PARAM(0); - cv::cuda::setDevice(devInfo.deviceID()); - const cv::Size size = GET_PARAM(1); - const bool useRoi = GET_PARAM(2); - const float rho = 1.0f; const float theta = (float) (1.0 * CV_PI / 180.0); const int minLineLength = 15; @@ -169,12 +176,55 @@ CUDA_TEST_P(HoughLinesProbabilistic, Accuracy) } +void HoughLinesProbabilisticThread(const Ptr detector, const GpuMat& imgIn, const std::vector& linesOut, Stream& stream) { + for (auto& lines : linesOut) + detector->detect(imgIn, lines, stream); + stream.waitForCompletion(); +} + +CUDA_TEST_P(HoughLinesProbabilistic, Async) +{ + constexpr int nThreads = 5; + constexpr int nIters = 5; + vector streams(nThreads); // async test only + vector imgsIn; + vector> detectors; + vector> linesOut(nThreads); + const float rho = 1.0f; + const float theta = (float)(1.0 * CV_PI / 180.0); + const int minLineLength = 15; + const int maxLineGap = 8; + + cv::Mat src(size, CV_8UC1); + generateLines(src); + + for (int i = 0; i < nThreads; i++) { + imgsIn.push_back(loadMat(src, useRoi)); + detectors.push_back(createHoughSegmentDetector(rho, theta, minLineLength, maxLineGap)); + linesOut.push_back(vector(nIters)); + } + + vector thread(nThreads); + for (int i = 0; i < nThreads; i++) thread.at(i) = std::thread(HoughLinesProbabilisticThread, detectors.at(i), std::ref(imgsIn.at(i)), std::ref(linesOut.at(i)), std::ref(streams.at(i))); + for (int i = 0; i < nThreads; i++) thread.at(i).join(); + + for (int i = 0; i < nThreads; i++) { + std::vector linesSegment; + std::vector lines; + for (const auto& line : linesOut.at(i)) { + line.download(linesSegment); + cv::Mat dst(size, CV_8UC1); + drawLines(dst, linesSegment); + ASSERT_MAT_NEAR(src, dst, 0.0); + } + } +} + INSTANTIATE_TEST_CASE_P(CUDA_ImgProc, HoughLinesProbabilistic, testing::Combine( ALL_DEVICES, DIFFERENT_SIZES, WHOLE_SUBMAT)); - /////////////////////////////////////////////////////////////////////////////////////////////////////// // HoughCircles diff --git a/modules/cudaimgproc/test/test_precomp.hpp b/modules/cudaimgproc/test/test_precomp.hpp index dd94f6f285..e388fbdaa8 100644 --- a/modules/cudaimgproc/test/test_precomp.hpp +++ b/modules/cudaimgproc/test/test_precomp.hpp @@ -49,4 +49,6 @@ #include "cvconfig.h" +#include + #endif diff --git a/modules/cudalegacy/include/opencv2/cudalegacy/NCV.hpp b/modules/cudalegacy/include/opencv2/cudalegacy/NCV.hpp index d0ec6a42d6..f03410dfbd 100644 --- a/modules/cudalegacy/include/opencv2/cudalegacy/NCV.hpp +++ b/modules/cudalegacy/include/opencv2/cudalegacy/NCV.hpp @@ -119,9 +119,9 @@ typedef bool NcvBool; typedef long long Ncv64s; #if defined(__APPLE__) && !defined(__CUDACC__) - typedef uint64_t Ncv64u; + typedef uint64 Ncv64u; #else - typedef unsigned long long Ncv64u; + typedef uint64 Ncv64u; #endif typedef int Ncv32s; diff --git a/modules/cudalegacy/include/opencv2/cudalegacy/NPP_staging.hpp b/modules/cudalegacy/include/opencv2/cudalegacy/NPP_staging.hpp index 89e7f7cdea..d9189eb20b 100644 --- a/modules/cudalegacy/include/opencv2/cudalegacy/NPP_staging.hpp +++ b/modules/cudalegacy/include/opencv2/cudalegacy/NPP_staging.hpp @@ -174,7 +174,7 @@ NCVStatus nppiStInterpolateFrames(const NppStInterpolationState *pState); * \return NCV status code */ CV_EXPORTS -NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, +NCVStatus nppiStFilterRowBorder_32f_C1R(Ncv32f *pSrc, NcvSize32u srcSize, Ncv32u nSrcStep, Ncv32f *pDst, @@ -182,7 +182,7 @@ NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, Ncv32u nDstStep, NcvRect32u oROI, NppStBorderType borderType, - const Ncv32f *pKernel, + Ncv32f *pKernel, Ncv32s nKernelSize, Ncv32s nAnchor, Ncv32f multiplier); @@ -208,7 +208,7 @@ NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, * \return NCV status code */ CV_EXPORTS -NCVStatus nppiStFilterColumnBorder_32f_C1R(const Ncv32f *pSrc, +NCVStatus nppiStFilterColumnBorder_32f_C1R(Ncv32f *pSrc, NcvSize32u srcSize, Ncv32u nSrcStep, Ncv32f *pDst, @@ -216,7 +216,7 @@ NCVStatus nppiStFilterColumnBorder_32f_C1R(const Ncv32f *pSrc, Ncv32u nDstStep, NcvRect32u oROI, NppStBorderType borderType, - const Ncv32f *pKernel, + Ncv32f *pKernel, Ncv32s nKernelSize, Ncv32s nAnchor, Ncv32f multiplier); @@ -319,7 +319,7 @@ NCVStatus nppiStVectorWarp_PSF2x2_32f_C1(const Ncv32f *pSrc, * \return NCV status code */ CV_EXPORTS -NCVStatus nppiStResize_32f_C1R(const Ncv32f *pSrc, +NCVStatus nppiStResize_32f_C1R(Ncv32f *pSrc, NcvSize32u srcSize, Ncv32u nSrcStep, NcvRect32u srcROI, diff --git a/modules/cudalegacy/src/cuda/NCVBroxOpticalFlow.cu b/modules/cudalegacy/src/cuda/NCVBroxOpticalFlow.cu index 0191488024..a7f83c715d 100644 --- a/modules/cudalegacy/src/cuda/NCVBroxOpticalFlow.cu +++ b/modules/cudalegacy/src/cuda/NCVBroxOpticalFlow.cu @@ -65,9 +65,12 @@ #include "opencv2/cudalegacy/NPP_staging.hpp" #include "opencv2/cudalegacy/NCVBroxOpticalFlow.hpp" +#include typedef NCVVectorAlloc FloatVector; +typedef cv::cudev::TexturePtr Ptr2D; +typedef cv::cudev::Texture Texture; ///////////////////////////////////////////////////////////////////////////////////////// // Implementation specific constants @@ -84,39 +87,6 @@ inline int iDivUp(int a, int b) return (a + b - 1)/b; } -///////////////////////////////////////////////////////////////////////////////////////// -// Texture references -///////////////////////////////////////////////////////////////////////////////////////// - -texture tex_coarse; -texture tex_fine; - -texture tex_I1; -texture tex_I0; - -texture tex_Ix; -texture tex_Ixx; -texture tex_Ix0; - -texture tex_Iy; -texture tex_Iyy; -texture tex_Iy0; - -texture tex_Ixy; - -texture tex_u; -texture tex_v; -texture tex_du; -texture tex_dv; -texture tex_numerator_dudv; -texture tex_numerator_u; -texture tex_numerator_v; -texture tex_inv_denominator_u; -texture tex_inv_denominator_v; -texture tex_diffusivity_x; -texture tex_diffusivity_y; - - ///////////////////////////////////////////////////////////////////////////////////////// // SUPPLEMENTARY FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////// @@ -265,8 +235,7 @@ __forceinline__ __device__ void diffusivity_along_y(float *s, int pos, const flo ///\param h number of rows in global memory array ///\param p global memory array pitch in floats /////////////////////////////////////////////////////////////////////////////// -template -__forceinline__ __device__ void load_array_element(float *smem, int is, int js, int i, int j, int w, int h, int p) +__forceinline__ __device__ void load_array_element(Ptr2D texSrc, float *smem, int is, int js, int i, int j, int w, int h, int p) { //position within shared memory array const int ijs = js * PSOR_PITCH + is; @@ -276,20 +245,7 @@ __forceinline__ __device__ void load_array_element(float *smem, int is, int js, j = max(j, -j-1); j = min(j, h-j+h-1); const int pos = j * p + i; - switch(tex_id){ - case 0: - smem[ijs] = tex1Dfetch(tex_u, pos); - break; - case 1: - smem[ijs] = tex1Dfetch(tex_v, pos); - break; - case 2: - smem[ijs] = tex1Dfetch(tex_du, pos); - break; - case 3: - smem[ijs] = tex1Dfetch(tex_dv, pos); - break; - } + smem[ijs] = texSrc(pos); } /////////////////////////////////////////////////////////////////////////////// @@ -301,49 +257,48 @@ __forceinline__ __device__ void load_array_element(float *smem, int is, int js, ///\param h number of rows in global memory array ///\param p global memory array pitch in floats /////////////////////////////////////////////////////////////////////////////// -template -__forceinline__ __device__ void load_array(float *smem, int ig, int jg, int w, int h, int p) +__forceinline__ __device__ void load_array(Ptr2D texSrc, float *smem, int ig, int jg, int w, int h, int p) { const int i = threadIdx.x + 2; const int j = threadIdx.y + 2; - load_array_element(smem, i, j, ig, jg, w, h, p);//load current pixel + load_array_element(texSrc, smem, i, j, ig, jg, w, h, p);//load current pixel __syncthreads(); if(threadIdx.y < 2) { //load bottom shadow elements - load_array_element(smem, i, j-2, ig, jg-2, w, h, p); + load_array_element(texSrc, smem, i, j-2, ig, jg-2, w, h, p); if(threadIdx.x < 2) { //load bottom right shadow elements - load_array_element(smem, i+PSOR_TILE_WIDTH, j-2, ig+PSOR_TILE_WIDTH, jg-2, w, h, p); + load_array_element(texSrc, smem, i+PSOR_TILE_WIDTH, j-2, ig+PSOR_TILE_WIDTH, jg-2, w, h, p); //load middle right shadow elements - load_array_element(smem, i+PSOR_TILE_WIDTH, j, ig+PSOR_TILE_WIDTH, jg, w, h, p); + load_array_element(texSrc, smem, i+PSOR_TILE_WIDTH, j, ig+PSOR_TILE_WIDTH, jg, w, h, p); } else if(threadIdx.x >= PSOR_TILE_WIDTH-2) { //load bottom left shadow elements - load_array_element(smem, i-PSOR_TILE_WIDTH, j-2, ig-PSOR_TILE_WIDTH, jg-2, w, h, p); + load_array_element(texSrc, smem, i-PSOR_TILE_WIDTH, j-2, ig-PSOR_TILE_WIDTH, jg-2, w, h, p); //load middle left shadow elements - load_array_element(smem, i-PSOR_TILE_WIDTH, j, ig-PSOR_TILE_WIDTH, jg, w, h, p); + load_array_element(texSrc, smem, i-PSOR_TILE_WIDTH, j, ig-PSOR_TILE_WIDTH, jg, w, h, p); } } else if(threadIdx.y >= PSOR_TILE_HEIGHT-2) { //load upper shadow elements - load_array_element(smem, i, j+2, ig, jg+2, w, h, p); + load_array_element(texSrc, smem, i, j+2, ig, jg+2, w, h, p); if(threadIdx.x < 2) { //load upper right shadow elements - load_array_element(smem, i+PSOR_TILE_WIDTH, j+2, ig+PSOR_TILE_WIDTH, jg+2, w, h, p); + load_array_element(texSrc, smem, i+PSOR_TILE_WIDTH, j+2, ig+PSOR_TILE_WIDTH, jg+2, w, h, p); //load middle right shadow elements - load_array_element(smem, i+PSOR_TILE_WIDTH, j, ig+PSOR_TILE_WIDTH, jg, w, h, p); + load_array_element(texSrc, smem, i+PSOR_TILE_WIDTH, j, ig+PSOR_TILE_WIDTH, jg, w, h, p); } else if(threadIdx.x >= PSOR_TILE_WIDTH-2) { //load upper left shadow elements - load_array_element(smem, i-PSOR_TILE_WIDTH, j+2, ig-PSOR_TILE_WIDTH, jg+2, w, h, p); + load_array_element(texSrc, smem, i-PSOR_TILE_WIDTH, j+2, ig-PSOR_TILE_WIDTH, jg+2, w, h, p); //load middle left shadow elements - load_array_element(smem, i-PSOR_TILE_WIDTH, j, ig-PSOR_TILE_WIDTH, jg, w, h, p); + load_array_element(texSrc, smem, i-PSOR_TILE_WIDTH, j, ig-PSOR_TILE_WIDTH, jg, w, h, p); } } else @@ -352,12 +307,12 @@ __forceinline__ __device__ void load_array(float *smem, int ig, int jg, int w, i if(threadIdx.x < 2) { //load middle right shadow elements - load_array_element(smem, i+PSOR_TILE_WIDTH, j, ig+PSOR_TILE_WIDTH, jg, w, h, p); + load_array_element(texSrc, smem, i+PSOR_TILE_WIDTH, j, ig+PSOR_TILE_WIDTH, jg, w, h, p); } else if(threadIdx.x >= PSOR_TILE_WIDTH-2) { //load middle left shadow elements - load_array_element(smem, i-PSOR_TILE_WIDTH, j, ig-PSOR_TILE_WIDTH, jg, w, h, p); + load_array_element(texSrc, smem, i-PSOR_TILE_WIDTH, j, ig-PSOR_TILE_WIDTH, jg, w, h, p); } } __syncthreads(); @@ -382,13 +337,9 @@ __forceinline__ __device__ void load_array(float *smem, int ig, int jg, int w, i /// \param alpha (in) alpha in Brox model (flow smoothness) /// \param gamma (in) gamma in Brox model (edge importance) /////////////////////////////////////////////////////////////////////////////// - -__global__ void prepare_sor_stage_1_tex(float *diffusivity_x, float *diffusivity_y, - float *denominator_u, float *denominator_v, - float *numerator_dudv, - float *numerator_u, float *numerator_v, - int w, int h, int s, - float alpha, float gamma) +__global__ void prepare_sor_stage_1_tex(Ptr2D texU, Ptr2D texV, Ptr2D texDu, Ptr2D texDv, Ptr2D texI0, Ptr2D texI1, Ptr2D texIx, Ptr2D texIxx, Ptr2D texIx0, Ptr2D texIy, Ptr2D texIyy, + Ptr2D texIy0, Ptr2D texIxy, float *diffusivity_x, float *diffusivity_y, float *denominator_u, float *denominator_v, float *numerator_dudv, float *numerator_u, float *numerator_v, + int w, int h, int s, float alpha, float gamma) { __shared__ float u[PSOR_PITCH * PSOR_HEIGHT]; __shared__ float v[PSOR_PITCH * PSOR_HEIGHT]; @@ -408,24 +359,24 @@ __global__ void prepare_sor_stage_1_tex(float *diffusivity_x, float *diffusivity float x = (float)ig + 0.5f; float y = (float)jg + 0.5f; //load u and v to smem - load_array<0>(u, ig, jg, w, h, s); - load_array<1>(v, ig, jg, w, h, s); - load_array<2>(du, ig, jg, w, h, s); - load_array<3>(dv, ig, jg, w, h, s); + load_array(texU, u, ig, jg, w, h, s); + load_array(texV, v, ig, jg, w, h, s); + load_array(texDu, du, ig, jg, w, h, s); + load_array(texDv, dv, ig, jg, w, h, s); //warped position float wx = (x + u[ijs])/(float)w; float wy = (y + v[ijs])/(float)h; x /= (float)w; y /= (float)h; //compute image derivatives - const float Iz = tex2D(tex_I1, wx, wy) - tex2D(tex_I0, x, y); - const float Ix = tex2D(tex_Ix, wx, wy); - const float Ixz = Ix - tex2D(tex_Ix0, x, y); - const float Ixy = tex2D(tex_Ixy, wx, wy); - const float Ixx = tex2D(tex_Ixx, wx, wy); - const float Iy = tex2D(tex_Iy, wx, wy); - const float Iyz = Iy - tex2D(tex_Iy0, x, y); - const float Iyy = tex2D(tex_Iyy, wx, wy); + const float Iz = texI1(wy, wx) - texI0(y,x); + const float Ix = texIx(wy, wx); + const float Ixz = Ix - texIx0(y, x); + const float Ixy = texIxy(wy, wx); + const float Ixx = texIxx(wy, wx); + const float Iy = texIy(wy, wx); + const float Iyz = Iy - texIy0(y, x); + const float Iyy = texIyy(wy, wx); //compute data term float q0, q1, q2; q0 = Iz + Ix * du[ijs] + Iy * dv[ijs]; @@ -462,8 +413,7 @@ __global__ void prepare_sor_stage_1_tex(float *diffusivity_x, float *diffusivity ///\param h ///\param s /////////////////////////////////////////////////////////////////////////////// -__global__ void prepare_sor_stage_2(float *inv_denominator_u, float *inv_denominator_v, - int w, int h, int s) +__global__ void prepare_sor_stage_2(Ptr2D texDiffX, Ptr2D texDiffY, float *inv_denominator_u, float *inv_denominator_v, int w, int h, int s) { __shared__ float sx[(PSOR_TILE_WIDTH+1) * (PSOR_TILE_HEIGHT+1)]; __shared__ float sy[(PSOR_TILE_WIDTH+1) * (PSOR_TILE_HEIGHT+1)]; @@ -486,8 +436,8 @@ __global__ void prepare_sor_stage_2(float *inv_denominator_u, float *inv_denomin } if(inside) { - sx[ijs] = tex1Dfetch(tex_diffusivity_x, ijg); - sy[ijs] = tex1Dfetch(tex_diffusivity_y, ijg); + sx[ijs] = texDiffX(ijg); + sy[ijs] = texDiffY(ijg); } else { @@ -498,25 +448,17 @@ __global__ void prepare_sor_stage_2(float *inv_denominator_u, float *inv_denomin if(j == PSOR_TILE_HEIGHT-1) { if(jg < h-1 && inside) - { - sy[up] = tex1Dfetch(tex_diffusivity_y, ijg + s); - } + sy[up] = texDiffY(ijg + s); else - { sy[up] = 0.0f; - } } int right = ijs + 1; if(threadIdx.x == PSOR_TILE_WIDTH-1) { if(ig < w-1 && inside) - { - sx[right] = tex1Dfetch(tex_diffusivity_x, ijg + 1); - } + sx[right] = texDiffX(ijg + 1); else - { sx[right] = 0.0f; - } } __syncthreads(); float diffusivity_sum; @@ -534,17 +476,8 @@ __global__ void prepare_sor_stage_2(float *inv_denominator_u, float *inv_denomin // Red-Black SOR ///////////////////////////////////////////////////////////////////////////////////////// -template __global__ void sor_pass(float *new_du, - float *new_dv, - const float *g_inv_denominator_u, - const float *g_inv_denominator_v, - const float *g_numerator_u, - const float *g_numerator_v, - const float *g_numerator_dudv, - float omega, - int width, - int height, - int stride) +template __global__ void sor_pass(Ptr2D texU, Ptr2D texV, Ptr2D texDu, Ptr2D texDv, Ptr2D texDiffX, Ptr2D texDiffY, float *new_du, float *new_dv, const float *g_inv_denominator_u, + const float *g_inv_denominator_v, const float *g_numerator_u, const float *g_numerator_v, const float *g_numerator_dudv, float omega, int width, int height, int stride) { int i = blockIdx.x * blockDim.x + threadIdx.x; int j = blockIdx.y * blockDim.y + threadIdx.y; @@ -560,14 +493,14 @@ template __global__ void sor_pass(float *new_du, //load smooth term float s_up, s_left, s_right, s_down; - s_left = tex1Dfetch(tex_diffusivity_x, pos); - s_down = tex1Dfetch(tex_diffusivity_y, pos); + s_left = texDiffX(pos); + s_down = texDiffY(pos); if(i < width-1) - s_right = tex1Dfetch(tex_diffusivity_x, pos_r); + s_right = texDiffX(pos_r); else s_right = 0.0f; //Neumann BC if(j < height-1) - s_up = tex1Dfetch(tex_diffusivity_y, pos_u); + s_up = texDiffY(pos_u); else s_up = 0.0f; //Neumann BC @@ -577,30 +510,29 @@ template __global__ void sor_pass(float *new_du, float du_up, du_left, du_right, du_down, du; float dv_up, dv_left, dv_right, dv_down, dv; - u_left = tex1Dfetch(tex_u, pos_l); - u_right = tex1Dfetch(tex_u, pos_r); - u_down = tex1Dfetch(tex_u, pos_d); - u_up = tex1Dfetch(tex_u, pos_u); - u = tex1Dfetch(tex_u, pos); - - v_left = tex1Dfetch(tex_v, pos_l); - v_right = tex1Dfetch(tex_v, pos_r); - v_down = tex1Dfetch(tex_v, pos_d); - v = tex1Dfetch(tex_v, pos); - v_up = tex1Dfetch(tex_v, pos_u); - - du = tex1Dfetch(tex_du, pos); - du_left = tex1Dfetch(tex_du, pos_l); - du_right = tex1Dfetch(tex_du, pos_r); - du_down = tex1Dfetch(tex_du, pos_d); - du_up = tex1Dfetch(tex_du, pos_u); - - dv = tex1Dfetch(tex_dv, pos); - dv_left = tex1Dfetch(tex_dv, pos_l); - dv_right = tex1Dfetch(tex_dv, pos_r); - dv_down = tex1Dfetch(tex_dv, pos_d); - dv_up = tex1Dfetch(tex_dv, pos_u); - + u_left = texU(pos_l); + u_right = texU(pos_r); + u_down = texU(pos_d); + u_up = texU(pos_u); + u = texU(pos); + + v_left = texV(pos_l); + v_right = texV(pos_r); + v_down = texV(pos_d); + v = texV(pos); + v_up = texV(pos_u); + + du = texDu(pos); + du_left = texDu(pos_l); + du_right = texDu(pos_r); + du_down = texDu(pos_d); + du_up = texDu(pos_u); + + dv = texDv(pos); + dv_left = texDv(pos_l); + dv_right = texDv(pos_r); + dv_down = texDv(pos_d); + dv_up = texDv(pos_u); float numerator_dudv = g_numerator_dudv[pos]; if((i+j)%2 == isBlack) @@ -624,52 +556,6 @@ template __global__ void sor_pass(float *new_du, /////////////////////////////////////////////////////////////////////////////// // utility functions /////////////////////////////////////////////////////////////////////////////// - -void initTexture1D(texture &tex) -{ - tex.addressMode[0] = cudaAddressModeClamp; - tex.filterMode = cudaFilterModePoint; - tex.normalized = false; -} - -void initTexture2D(texture &tex) -{ - tex.addressMode[0] = cudaAddressModeMirror; - tex.addressMode[1] = cudaAddressModeMirror; - tex.filterMode = cudaFilterModeLinear; - tex.normalized = true; -} - -void InitTextures() -{ - initTexture2D(tex_I0); - initTexture2D(tex_I1); - initTexture2D(tex_fine); // for downsampling - initTexture2D(tex_coarse); // for prolongation - - initTexture2D(tex_Ix); - initTexture2D(tex_Ixx); - initTexture2D(tex_Ix0); - - initTexture2D(tex_Iy); - initTexture2D(tex_Iyy); - initTexture2D(tex_Iy0); - - initTexture2D(tex_Ixy); - - initTexture1D(tex_u); - initTexture1D(tex_v); - initTexture1D(tex_du); - initTexture1D(tex_dv); - initTexture1D(tex_diffusivity_x); - initTexture1D(tex_diffusivity_y); - initTexture1D(tex_inv_denominator_u); - initTexture1D(tex_inv_denominator_v); - initTexture1D(tex_numerator_dudv); - initTexture1D(tex_numerator_u); - initTexture1D(tex_numerator_v); -} - namespace { struct ImagePyramid @@ -804,8 +690,6 @@ NCVStatus NCVBroxOpticalFlow(const NCVBroxOpticalFlowDescriptor desc, ncvAssertCUDAReturn(cudaMemcpy(derivativeFilter.ptr(), derivativeFilterHost, sizeof(float) * kDFilterSize, cudaMemcpyHostToDevice), NCV_CUDA_ERROR); - - InitTextures(); } //prepare image pyramid @@ -909,9 +793,6 @@ NCVStatus NCVBroxOpticalFlow(const NCVBroxOpticalFlowDescriptor desc, ncvAssertCUDAReturn(cudaMemsetAsync(v.ptr(), 0, kSizeInPixelsAligned * sizeof(float), stream), NCV_CUDA_ERROR); //select images with lowest resolution - size_t pitch = alignUp(pyr.w.back(), kStrideAlignmentFloat) * sizeof(float); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_I0, pyr.img0.back()->ptr(), channel_desc, pyr.w.back(), pyr.h.back(), pitch), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_I1, pyr.img1.back()->ptr(), channel_desc, pyr.w.back(), pyr.h.back(), pitch), NCV_CUDA_ERROR); ncvAssertCUDAReturn(cudaStreamSynchronize(stream), NCV_CUDA_ERROR); FloatVector* ptrU = &u; @@ -941,17 +822,14 @@ NCVStatus NCVBroxOpticalFlow(const NCVBroxOpticalFlowDescriptor desc, ncvAssertCUDAReturn(cudaMemsetAsync(du.ptr(), 0, kLevelSizeInBytes, stream), NCV_CUDA_ERROR); ncvAssertCUDAReturn(cudaMemsetAsync(dv.ptr(), 0, kLevelSizeInBytes, stream), NCV_CUDA_ERROR); - //texture format descriptor - cudaChannelFormatDesc ch_desc = cudaCreateChannelDesc(); - I0 = *img0Iter; I1 = *img1Iter; ++img0Iter; ++img1Iter; - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_I0, I0->ptr(), ch_desc, kLevelWidth, kLevelHeight, kLevelStride*sizeof(float)), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_I1, I1->ptr(), ch_desc, kLevelWidth, kLevelHeight, kLevelStride*sizeof(float)), NCV_CUDA_ERROR); + Texture texI0(kLevelHeight, kLevelWidth, I0->ptr(), kLevelStride * sizeof(float), true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texI1(kLevelHeight, kLevelWidth, I1->ptr(), kLevelStride * sizeof(float), true, cudaFilterModeLinear, cudaAddressModeMirror); //compute derivatives dim3 dBlocks(iDivUp(kLevelWidth, 32), iDivUp(kLevelHeight, 6)); @@ -991,20 +869,24 @@ NCVStatus NCVBroxOpticalFlow(const NCVBroxOpticalFlowDescriptor desc, ncvAssertReturnNcvStat( nppiStFilterRowBorder_32f_C1R (Iy.ptr(), srcSize, nSrcStep, Ixy.ptr(), srcSize, nSrcStep, oROI, nppStBorderMirror, derivativeFilter.ptr(), kDFilterSize, kDFilterSize/2, 1.0f/12.0f) ); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Ix, Ix.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Ixx, Ixx.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Ix0, Ix0.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Iy, Iy.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Iyy, Iyy.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Iy0, Iy0.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture2D(0, tex_Ixy, Ixy.ptr(), ch_desc, kLevelWidth, kLevelHeight, kPitchTex), NCV_CUDA_ERROR); + Texture texIx(kLevelHeight, kLevelWidth, Ix.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texIxx(kLevelHeight, kLevelWidth, Ixx.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texIx0(kLevelHeight, kLevelWidth, Ix0.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texIy(kLevelHeight, kLevelWidth, Iy.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texIyy(kLevelHeight, kLevelWidth, Iyy.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texIy0(kLevelHeight, kLevelWidth, Iy0.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texIxy(kLevelHeight, kLevelWidth, Ixy.ptr(), kPitchTex, true, cudaFilterModeLinear, cudaAddressModeMirror); + Texture texDiffX(1, kLevelSizeInBytes / sizeof(float), diffusivity_x.ptr(), kLevelSizeInBytes); + Texture texDiffY(1, kLevelSizeInBytes / sizeof(float), diffusivity_y.ptr(), kLevelSizeInBytes); // flow - ncvAssertCUDAReturn(cudaBindTexture(0, tex_u, ptrU->ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_v, ptrV->ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); + Texture texU(1, kLevelSizeInBytes / sizeof(float), ptrU->ptr(), kLevelSizeInBytes); + Texture texV(1, kLevelSizeInBytes / sizeof(float), ptrV->ptr(), kLevelSizeInBytes); // flow increments - ncvAssertCUDAReturn(cudaBindTexture(0, tex_du, du.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_dv, dv.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); + Texture texDu(1, kLevelSizeInBytes / sizeof(float), du.ptr(), kLevelSizeInBytes); + Texture texDv(1, kLevelSizeInBytes / sizeof(float), dv.ptr(), kLevelSizeInBytes); + Texture texDuNew(1, kLevelSizeInBytes / sizeof(float), du_new.ptr(), kLevelSizeInBytes); + Texture texDvNew(1, kLevelSizeInBytes / sizeof(float), dv_new.ptr(), kLevelSizeInBytes); dim3 psor_blocks(iDivUp(kLevelWidth, PSOR_TILE_WIDTH), iDivUp(kLevelHeight, PSOR_TILE_HEIGHT)); dim3 psor_threads(PSOR_TILE_WIDTH, PSOR_TILE_HEIGHT); @@ -1018,89 +900,30 @@ NCVStatus NCVBroxOpticalFlow(const NCVBroxOpticalFlowDescriptor desc, for (Ncv32u current_inner_iteration = 0; current_inner_iteration < desc.number_of_inner_iterations; ++current_inner_iteration) { //compute coefficients - prepare_sor_stage_1_tex<<>> - (diffusivity_x.ptr(), - diffusivity_y.ptr(), - denom_u.ptr(), - denom_v.ptr(), - num_dudv.ptr(), - num_u.ptr(), - num_v.ptr(), - kLevelWidth, - kLevelHeight, - kLevelStride, - alpha, - gamma); + prepare_sor_stage_1_tex<<>> (texU, texV, texDu, texDv, texI0, texI1, texIx, texIxx, texIx0, texIy, texIyy, texIy0, texIxy, + diffusivity_x.ptr(), diffusivity_y.ptr(), denom_u.ptr(), denom_v.ptr(), num_dudv.ptr(), num_u.ptr(), num_v.ptr(), kLevelWidth, kLevelHeight, kLevelStride, alpha, gamma); ncvAssertCUDALastErrorReturn(NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_diffusivity_x, diffusivity_x.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_diffusivity_y, diffusivity_y.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - ncvAssertCUDAReturn(cudaBindTexture(0, tex_numerator_dudv, num_dudv.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - ncvAssertCUDAReturn(cudaBindTexture(0, tex_numerator_u, num_u.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_numerator_v, num_v.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - prepare_sor_stage_2<<>>(denom_u.ptr(), denom_v.ptr(), kLevelWidth, kLevelHeight, kLevelStride); + prepare_sor_stage_2<<>>(texDiffX, texDiffY, denom_u.ptr(), denom_v.ptr(), kLevelWidth, kLevelHeight, kLevelStride); ncvAssertCUDALastErrorReturn(NCV_CUDA_ERROR); - // linear system coefficients - ncvAssertCUDAReturn(cudaBindTexture(0, tex_diffusivity_x, diffusivity_x.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_diffusivity_y, diffusivity_y.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - ncvAssertCUDAReturn(cudaBindTexture(0, tex_numerator_dudv, num_dudv.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - ncvAssertCUDAReturn(cudaBindTexture(0, tex_numerator_u, num_u.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_numerator_v, num_v.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - ncvAssertCUDAReturn(cudaBindTexture(0, tex_inv_denominator_u, denom_u.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_inv_denominator_v, denom_v.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); //solve linear system for (Ncv32u solver_iteration = 0; solver_iteration < desc.number_of_solver_iterations; ++solver_iteration) { float omega = 1.99f; - - ncvAssertCUDAReturn(cudaBindTexture(0, tex_du, du.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_dv, dv.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - sor_pass<0><<>> - (du_new.ptr(), - dv_new.ptr(), - denom_u.ptr(), - denom_v.ptr(), - num_u.ptr(), - num_v.ptr(), - num_dudv.ptr(), - omega, - kLevelWidth, - kLevelHeight, - kLevelStride); + sor_pass<0><<>>(texU, texV, texDu, texDv, texDiffX, texDiffY, du_new.ptr(), dv_new.ptr(), denom_u.ptr(), denom_v.ptr(), + num_u.ptr(), num_v.ptr(), num_dudv.ptr(), omega, kLevelWidth, kLevelHeight, kLevelStride); ncvAssertCUDALastErrorReturn(NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_du, du_new.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_dv, dv_new.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - - sor_pass<1><<>> - (du.ptr(), - dv.ptr(), - denom_u.ptr(), - denom_v.ptr(), - num_u.ptr(), - num_v.ptr(), - num_dudv.ptr(), - omega, - kLevelWidth, - kLevelHeight, - kLevelStride); - ncvAssertCUDALastErrorReturn(NCV_CUDA_ERROR); + sor_pass<1><<>>(texU, texV, texDuNew, texDvNew, texDiffX, texDiffY, du.ptr(), dv.ptr(), denom_u.ptr(), denom_v.ptr(), num_u.ptr(), + num_v.ptr(),num_dudv.ptr(), omega, kLevelWidth, kLevelHeight, kLevelStride); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_du, du.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(0, tex_dv, dv.ptr(), ch_desc, kLevelSizeInBytes), NCV_CUDA_ERROR); + ncvAssertCUDALastErrorReturn(NCV_CUDA_ERROR); }//end of solver loop }// end of inner loop diff --git a/modules/cudalegacy/src/cuda/NCVHaarObjectDetection.cu b/modules/cudalegacy/src/cuda/NCVHaarObjectDetection.cu index 57506173f5..9760bcee52 100644 --- a/modules/cudalegacy/src/cuda/NCVHaarObjectDetection.cu +++ b/modules/cudalegacy/src/cuda/NCVHaarObjectDetection.cu @@ -72,6 +72,7 @@ #include "opencv2/cudalegacy/NCV.hpp" #include "opencv2/cudalegacy/NPP_staging.hpp" #include "opencv2/cudalegacy/NCVHaarObjectDetection.hpp" +#include #include "NCVRuntimeTemplates.hpp" #include "NCVAlg.hpp" @@ -94,24 +95,6 @@ const Ncv32u NUM_THREADS_ANCHORSPARALLEL = 64; #define NUM_THREADS_CLASSIFIERPARALLEL (1 << NUM_THREADS_CLASSIFIERPARALLEL_LOG2) -/** \internal -* Haar features solid array. -*/ -texture texHaarFeatures; - - -/** \internal -* Haar classifiers flattened trees container. -* Two parts: first contains root nodes, second - nodes that are referred by root nodes. -* Drawback: breaks tree locality (might cause more cache misses -* Advantage: No need to introduce additional 32-bit field to index root nodes offsets -*/ -texture texHaarClassifierNodes; - - -texture texIImage; - - __device__ HaarStage64 getStage(Ncv32u iStage, HaarStage64 *d_Stages) { return d_Stages[iStage]; @@ -119,51 +102,37 @@ __device__ HaarStage64 getStage(Ncv32u iStage, HaarStage64 *d_Stages) template -__device__ HaarClassifierNode128 getClassifierNode(Ncv32u iNode, HaarClassifierNode128 *d_ClassifierNodes) +__device__ HaarClassifierNode128 getClassifierNode(cv::cudev::TexturePtr texHaarClassifierNodes, Ncv32u iNode, HaarClassifierNode128 *d_ClassifierNodes) { HaarClassifierNode128 tmpNode; if (tbCacheTextureCascade) - { - tmpNode._ui4 = tex1Dfetch(texHaarClassifierNodes, iNode); - } + tmpNode._ui4 = texHaarClassifierNodes(iNode); else - { tmpNode = d_ClassifierNodes[iNode]; - } return tmpNode; } template -__device__ void getFeature(Ncv32u iFeature, HaarFeature64 *d_Features, - Ncv32f *weight, - Ncv32u *rectX, Ncv32u *rectY, Ncv32u *rectWidth, Ncv32u *rectHeight) +__device__ void getFeature(cv::cudev::TexturePtr texHaarFeatures, Ncv32u iFeature, HaarFeature64* d_Features, Ncv32f* weight, Ncv32u* rectX, Ncv32u* rectY, Ncv32u* rectWidth, Ncv32u* rectHeight) { HaarFeature64 feature; if (tbCacheTextureCascade) - { - feature._ui2 = tex1Dfetch(texHaarFeatures, iFeature); - } + feature._ui2 = texHaarFeatures(iFeature); else - { feature = d_Features[iFeature]; - } feature.getRect(rectX, rectY, rectWidth, rectHeight); *weight = feature.getWeight(); } template -__device__ Ncv32u getElemIImg(Ncv32u x, Ncv32u *d_IImg) +__device__ Ncv32u getElemIImg(cv::cudev::TexturePtr texImg, Ncv32u x, Ncv32u *d_IImg) { if (tbCacheTextureIImg) - { - return tex1Dfetch(texIImage, x); - } + return texImg(x); else - { return d_IImg[x]; - } } @@ -203,17 +172,10 @@ __device__ void compactBlockWriteOutAnchorParallel(Ncv32u threadPassFlag, Ncv32u } -template -__global__ void applyHaarClassifierAnchorParallel(Ncv32u *d_IImg, Ncv32u IImgStride, - Ncv32f *d_weights, Ncv32u weightsStride, - HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, - Ncv32u *d_inMask, Ncv32u *d_outMask, - Ncv32u mask1Dlen, Ncv32u mask2Dstride, - NcvSize32u anchorsRoi, Ncv32u startStageInc, Ncv32u endStageExc, Ncv32f scaleArea) +template +__global__ void applyHaarClassifierAnchorParallel(cv::cudev::TexturePtr texImg, cv::cudev::TexturePtr texHaarFeatures, cv::cudev::TexturePtr texHaarClassifierNodes, + Ncv32u *d_IImg, Ncv32u IImgStride, Ncv32f *d_weights, Ncv32u weightsStride, HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, Ncv32u *d_inMask, + Ncv32u *d_outMask, Ncv32u mask1Dlen, Ncv32u mask2Dstride, NcvSize32u anchorsRoi, Ncv32u startStageInc, Ncv32u endStageExc, Ncv32f scaleArea) { Ncv32u y_offs; Ncv32u x_offs; @@ -299,7 +261,7 @@ __global__ void applyHaarClassifierAnchorParallel(Ncv32u *d_IImg, Ncv32u IImgStr { while (bMoreNodesToTraverse) { - HaarClassifierNode128 curNode = getClassifierNode(iNode, d_ClassifierNodes); + HaarClassifierNode128 curNode = getClassifierNode(texHaarClassifierNodes, iNode, d_ClassifierNodes); HaarFeatureDescriptor32 featuresDesc = curNode.getFeatureDesc(); Ncv32u curNodeFeaturesNum = featuresDesc.getNumFeatures(); Ncv32u iFeature = featuresDesc.getFeaturesOffset(); @@ -310,19 +272,17 @@ __global__ void applyHaarClassifierAnchorParallel(Ncv32u *d_IImg, Ncv32u IImgStr { Ncv32f rectWeight; Ncv32u rectX, rectY, rectWidth, rectHeight; - getFeature - (iFeature + iRect, d_Features, - &rectWeight, &rectX, &rectY, &rectWidth, &rectHeight); + getFeature (texHaarFeatures, iFeature + iRect, d_Features, &rectWeight, &rectX, &rectY, &rectWidth, &rectHeight); Ncv32u iioffsTL = (y_offs + rectY) * IImgStride + (x_offs + rectX); Ncv32u iioffsTR = iioffsTL + rectWidth; Ncv32u iioffsBL = iioffsTL + rectHeight * IImgStride; Ncv32u iioffsBR = iioffsBL + rectWidth; - Ncv32u rectSum = getElemIImg(iioffsBR, d_IImg) - - getElemIImg(iioffsBL, d_IImg) + - getElemIImg(iioffsTL, d_IImg) - - getElemIImg(iioffsTR, d_IImg); + Ncv32u rectSum = getElemIImg(texImg, iioffsBR, d_IImg) - + getElemIImg(texImg, iioffsBL, d_IImg) + + getElemIImg(texImg, iioffsTL, d_IImg) - + getElemIImg(texImg, iioffsTR, d_IImg); #if defined CPU_FP_COMPLIANCE || defined DISABLE_MAD_SELECTIVELY curNodeVal += __fmul_rn((Ncv32f)rectSum, rectWeight); @@ -393,15 +353,10 @@ __global__ void applyHaarClassifierAnchorParallel(Ncv32u *d_IImg, Ncv32u IImgStr } -template -__global__ void applyHaarClassifierClassifierParallel(Ncv32u *d_IImg, Ncv32u IImgStride, - Ncv32f *d_weights, Ncv32u weightsStride, - HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, - Ncv32u *d_inMask, Ncv32u *d_outMask, - Ncv32u mask1Dlen, Ncv32u mask2Dstride, - NcvSize32u anchorsRoi, Ncv32u startStageInc, Ncv32u endStageExc, Ncv32f scaleArea) +template +__global__ void applyHaarClassifierClassifierParallel(cv::cudev::TexturePtr texImg, cv::cudev::TexturePtr texHaarFeatures, cv::cudev::TexturePtr texHaarClassifierNodes, Ncv32u *d_IImg, + Ncv32u IImgStride, Ncv32f *d_weights, Ncv32u weightsStride, HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, Ncv32u *d_inMask, Ncv32u *d_outMask, + Ncv32u mask1Dlen, Ncv32u mask2Dstride, NcvSize32u anchorsRoi, Ncv32u startStageInc, Ncv32u endStageExc, Ncv32f scaleArea) { Ncv32u maskOffset = MAX_GRID_DIM * blockIdx.y + blockIdx.x; @@ -439,7 +394,7 @@ __global__ void applyHaarClassifierClassifierParallel(Ncv32u *d_IImg, Ncv32u IIm while (bMoreNodesToTraverse) { - HaarClassifierNode128 curNode = getClassifierNode(iNode, d_ClassifierNodes); + HaarClassifierNode128 curNode = getClassifierNode(texHaarClassifierNodes, iNode, d_ClassifierNodes); HaarFeatureDescriptor32 featuresDesc = curNode.getFeatureDesc(); Ncv32u curNodeFeaturesNum = featuresDesc.getNumFeatures(); Ncv32u iFeature = featuresDesc.getFeaturesOffset(); @@ -450,19 +405,17 @@ __global__ void applyHaarClassifierClassifierParallel(Ncv32u *d_IImg, Ncv32u IIm { Ncv32f rectWeight; Ncv32u rectX, rectY, rectWidth, rectHeight; - getFeature - (iFeature + iRect, d_Features, - &rectWeight, &rectX, &rectY, &rectWidth, &rectHeight); + getFeature (texHaarFeatures, iFeature + iRect, d_Features, &rectWeight, &rectX, &rectY, &rectWidth, &rectHeight); Ncv32u iioffsTL = (y_offs + rectY) * IImgStride + (x_offs + rectX); Ncv32u iioffsTR = iioffsTL + rectWidth; Ncv32u iioffsBL = iioffsTL + rectHeight * IImgStride; Ncv32u iioffsBR = iioffsBL + rectWidth; - Ncv32u rectSum = getElemIImg(iioffsBR, d_IImg) - - getElemIImg(iioffsBL, d_IImg) + - getElemIImg(iioffsTL, d_IImg) - - getElemIImg(iioffsTR, d_IImg); + Ncv32u rectSum = getElemIImg(texImg, iioffsBR, d_IImg) - + getElemIImg(texImg, iioffsBL, d_IImg) + + getElemIImg(texImg, iioffsTL, d_IImg) - + getElemIImg(texImg, iioffsTR, d_IImg); #if defined CPU_FP_COMPLIANCE || defined DISABLE_MAD_SELECTIVELY curNodeVal += __fmul_rn((Ncv32f)rectSum, rectWeight); @@ -578,8 +531,9 @@ struct applyHaarClassifierAnchorParallelFunctor { dim3 gridConf, blockConf; cudaStream_t cuStream; - - //Kernel arguments are stored as members; + cv::cudev::TexturePtr texImg; + cv::cudev::TexturePtr texHaarFeatures; + cv::cudev::TexturePtr texHaarClassifierNodes; Ncv32u *d_IImg; Ncv32u IImgStride; Ncv32f *d_weights; @@ -597,32 +551,12 @@ struct applyHaarClassifierAnchorParallelFunctor Ncv32f scaleArea; //Arguments are passed through the constructor - applyHaarClassifierAnchorParallelFunctor(dim3 _gridConf, dim3 _blockConf, cudaStream_t _cuStream, - Ncv32u *_d_IImg, Ncv32u _IImgStride, - Ncv32f *_d_weights, Ncv32u _weightsStride, - HaarFeature64 *_d_Features, HaarClassifierNode128 *_d_ClassifierNodes, HaarStage64 *_d_Stages, - Ncv32u *_d_inMask, Ncv32u *_d_outMask, - Ncv32u _mask1Dlen, Ncv32u _mask2Dstride, - NcvSize32u _anchorsRoi, Ncv32u _startStageInc, - Ncv32u _endStageExc, Ncv32f _scaleArea) : - gridConf(_gridConf), - blockConf(_blockConf), - cuStream(_cuStream), - d_IImg(_d_IImg), - IImgStride(_IImgStride), - d_weights(_d_weights), - weightsStride(_weightsStride), - d_Features(_d_Features), - d_ClassifierNodes(_d_ClassifierNodes), - d_Stages(_d_Stages), - d_inMask(_d_inMask), - d_outMask(_d_outMask), - mask1Dlen(_mask1Dlen), - mask2Dstride(_mask2Dstride), - anchorsRoi(_anchorsRoi), - startStageInc(_startStageInc), - endStageExc(_endStageExc), - scaleArea(_scaleArea) + applyHaarClassifierAnchorParallelFunctor(cv::cudev::TexturePtr texImg_, cv::cudev::TexturePtr texHaarFeatures_, cv::cudev::TexturePtr texHaarClassifierNodes_, dim3 _gridConf, + dim3 _blockConf, cudaStream_t _cuStream, Ncv32u *_d_IImg, Ncv32u _IImgStride, Ncv32f *_d_weights, Ncv32u _weightsStride, HaarFeature64 *_d_Features, HaarClassifierNode128 *_d_ClassifierNodes, + HaarStage64 *_d_Stages, Ncv32u *_d_inMask, Ncv32u *_d_outMask, Ncv32u _mask1Dlen, Ncv32u _mask2Dstride, NcvSize32u _anchorsRoi, Ncv32u _startStageInc, Ncv32u _endStageExc, Ncv32f _scaleArea) : + gridConf(_gridConf), blockConf(_blockConf), cuStream(_cuStream), texImg(texImg_), texHaarFeatures(texHaarFeatures_), texHaarClassifierNodes(texHaarClassifierNodes_), d_IImg(_d_IImg), IImgStride(_IImgStride), + d_weights(_d_weights), weightsStride(_weightsStride), d_Features(_d_Features), d_ClassifierNodes(_d_ClassifierNodes), d_Stages(_d_Stages), d_inMask(_d_inMask), d_outMask(_d_outMask), mask1Dlen(_mask1Dlen), + mask2Dstride(_mask2Dstride), anchorsRoi(_anchorsRoi), startStageInc(_startStageInc), endStageExc(_endStageExc), scaleArea(_scaleArea) {} template @@ -635,43 +569,19 @@ struct applyHaarClassifierAnchorParallelFunctor Loki::TL::TypeAt::Result::value, Loki::TL::TypeAt::Result::value, Loki::TL::TypeAt::Result::value > - <<>> - (d_IImg, IImgStride, - d_weights, weightsStride, - d_Features, d_ClassifierNodes, d_Stages, - d_inMask, d_outMask, - mask1Dlen, mask2Dstride, - anchorsRoi, startStageInc, - endStageExc, scaleArea); + <<>> (texImg, texHaarFeatures, texHaarClassifierNodes, d_IImg, IImgStride, d_weights, weightsStride, d_Features, d_ClassifierNodes, d_Stages, d_inMask, + d_outMask, mask1Dlen, mask2Dstride, anchorsRoi, startStageInc, endStageExc, scaleArea); } }; -void applyHaarClassifierAnchorParallelDynTemplate(NcvBool tbInitMaskPositively, - NcvBool tbCacheTextureIImg, - NcvBool tbCacheTextureCascade, - NcvBool tbReadPixelIndexFromVector, - NcvBool tbDoAtomicCompaction, - - dim3 gridConf, dim3 blockConf, cudaStream_t cuStream, - - Ncv32u *d_IImg, Ncv32u IImgStride, - Ncv32f *d_weights, Ncv32u weightsStride, - HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, - Ncv32u *d_inMask, Ncv32u *d_outMask, - Ncv32u mask1Dlen, Ncv32u mask2Dstride, - NcvSize32u anchorsRoi, Ncv32u startStageInc, - Ncv32u endStageExc, Ncv32f scaleArea) +void applyHaarClassifierAnchorParallelDynTemplate(NcvBool tbInitMaskPositively, NcvBool tbCacheTextureIImg, NcvBool tbCacheTextureCascade, NcvBool tbReadPixelIndexFromVector, NcvBool tbDoAtomicCompaction, + dim3 gridConf, dim3 blockConf, cudaStream_t cuStream, cv::cudev::TexturePtr texImg, cv::cudev::TexturePtr texHaarFeatures, cv::cudev::TexturePtr texHaarClassifierNodes, Ncv32u *d_IImg, + Ncv32u IImgStride, Ncv32f *d_weights, Ncv32u weightsStride, HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, Ncv32u *d_inMask, Ncv32u *d_outMask, + Ncv32u mask1Dlen, Ncv32u mask2Dstride, NcvSize32u anchorsRoi, Ncv32u startStageInc, Ncv32u endStageExc, Ncv32f scaleArea) { - - applyHaarClassifierAnchorParallelFunctor functor(gridConf, blockConf, cuStream, - d_IImg, IImgStride, - d_weights, weightsStride, - d_Features, d_ClassifierNodes, d_Stages, - d_inMask, d_outMask, - mask1Dlen, mask2Dstride, - anchorsRoi, startStageInc, - endStageExc, scaleArea); + applyHaarClassifierAnchorParallelFunctor functor(texImg, texHaarFeatures, texHaarClassifierNodes, gridConf, blockConf, cuStream, d_IImg, IImgStride, d_weights, weightsStride, d_Features, d_ClassifierNodes, d_Stages, + d_inMask, d_outMask, mask1Dlen, mask2Dstride, anchorsRoi, startStageInc, endStageExc, scaleArea); //Second parameter is the number of "dynamic" template parameters NCVRuntimeTemplateBool::KernelCaller @@ -688,8 +598,9 @@ struct applyHaarClassifierClassifierParallelFunctor { dim3 gridConf, blockConf; cudaStream_t cuStream; - - //Kernel arguments are stored as members; + cv::cudev::TexturePtr texImg; + cv::cudev::TexturePtr texHaarFeatures; + cv::cudev::TexturePtr texHaarClassifierNodes; Ncv32u *d_IImg; Ncv32u IImgStride; Ncv32f *d_weights; @@ -707,32 +618,13 @@ struct applyHaarClassifierClassifierParallelFunctor Ncv32f scaleArea; //Arguments are passed through the constructor - applyHaarClassifierClassifierParallelFunctor(dim3 _gridConf, dim3 _blockConf, cudaStream_t _cuStream, - Ncv32u *_d_IImg, Ncv32u _IImgStride, - Ncv32f *_d_weights, Ncv32u _weightsStride, - HaarFeature64 *_d_Features, HaarClassifierNode128 *_d_ClassifierNodes, HaarStage64 *_d_Stages, - Ncv32u *_d_inMask, Ncv32u *_d_outMask, - Ncv32u _mask1Dlen, Ncv32u _mask2Dstride, - NcvSize32u _anchorsRoi, Ncv32u _startStageInc, - Ncv32u _endStageExc, Ncv32f _scaleArea) : - gridConf(_gridConf), - blockConf(_blockConf), - cuStream(_cuStream), - d_IImg(_d_IImg), - IImgStride(_IImgStride), - d_weights(_d_weights), - weightsStride(_weightsStride), - d_Features(_d_Features), - d_ClassifierNodes(_d_ClassifierNodes), - d_Stages(_d_Stages), - d_inMask(_d_inMask), - d_outMask(_d_outMask), - mask1Dlen(_mask1Dlen), - mask2Dstride(_mask2Dstride), - anchorsRoi(_anchorsRoi), - startStageInc(_startStageInc), - endStageExc(_endStageExc), - scaleArea(_scaleArea) + applyHaarClassifierClassifierParallelFunctor(dim3 _gridConf, dim3 _blockConf, cudaStream_t _cuStream, cv::cudev::TexturePtr texImg_, cv::cudev::TexturePtr texHaarFeatures_, + cv::cudev::TexturePtr texHaarClassifierNodes_, Ncv32u *_d_IImg, Ncv32u _IImgStride, Ncv32f *_d_weights, Ncv32u _weightsStride, HaarFeature64 *_d_Features, + HaarClassifierNode128 *_d_ClassifierNodes, HaarStage64 *_d_Stages, Ncv32u *_d_inMask, Ncv32u *_d_outMask, Ncv32u _mask1Dlen, Ncv32u _mask2Dstride, NcvSize32u _anchorsRoi, + Ncv32u _startStageInc, Ncv32u _endStageExc, Ncv32f _scaleArea) : gridConf(_gridConf), blockConf(_blockConf), cuStream(_cuStream), texImg(texImg_), texHaarFeatures(texHaarFeatures_), + texHaarClassifierNodes(texHaarClassifierNodes_), d_IImg(_d_IImg), IImgStride(_IImgStride), d_weights(_d_weights), weightsStride(_weightsStride), d_Features(_d_Features), + d_ClassifierNodes(_d_ClassifierNodes), d_Stages(_d_Stages), d_inMask(_d_inMask), d_outMask(_d_outMask), mask1Dlen(_mask1Dlen), mask2Dstride(_mask2Dstride), anchorsRoi(_anchorsRoi), + startStageInc(_startStageInc), endStageExc(_endStageExc), scaleArea(_scaleArea) {} template @@ -743,40 +635,19 @@ struct applyHaarClassifierClassifierParallelFunctor Loki::TL::TypeAt::Result::value, Loki::TL::TypeAt::Result::value, Loki::TL::TypeAt::Result::value > - <<>> - (d_IImg, IImgStride, - d_weights, weightsStride, - d_Features, d_ClassifierNodes, d_Stages, - d_inMask, d_outMask, - mask1Dlen, mask2Dstride, - anchorsRoi, startStageInc, - endStageExc, scaleArea); + <<>> (texImg, texHaarFeatures, texHaarClassifierNodes, d_IImg, IImgStride, d_weights, weightsStride, d_Features, d_ClassifierNodes, d_Stages, d_inMask, + d_outMask, mask1Dlen, mask2Dstride, anchorsRoi, startStageInc, endStageExc, scaleArea); } }; -void applyHaarClassifierClassifierParallelDynTemplate(NcvBool tbCacheTextureIImg, - NcvBool tbCacheTextureCascade, - NcvBool tbDoAtomicCompaction, - - dim3 gridConf, dim3 blockConf, cudaStream_t cuStream, - - Ncv32u *d_IImg, Ncv32u IImgStride, - Ncv32f *d_weights, Ncv32u weightsStride, - HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, - Ncv32u *d_inMask, Ncv32u *d_outMask, - Ncv32u mask1Dlen, Ncv32u mask2Dstride, - NcvSize32u anchorsRoi, Ncv32u startStageInc, - Ncv32u endStageExc, Ncv32f scaleArea) +void applyHaarClassifierClassifierParallelDynTemplate(NcvBool tbCacheTextureIImg, NcvBool tbCacheTextureCascade, NcvBool tbDoAtomicCompaction, dim3 gridConf, dim3 blockConf, cudaStream_t cuStream, + cv::cudev::TexturePtr texImg, cv::cudev::TexturePtr texHaarFeatures, cv::cudev::TexturePtr texHaarClassifierNodes, Ncv32u *d_IImg, Ncv32u IImgStride, Ncv32f *d_weights, + Ncv32u weightsStride, HaarFeature64 *d_Features, HaarClassifierNode128 *d_ClassifierNodes, HaarStage64 *d_Stages, Ncv32u *d_inMask, Ncv32u *d_outMask, Ncv32u mask1Dlen, Ncv32u mask2Dstride, + NcvSize32u anchorsRoi, Ncv32u startStageInc, Ncv32u endStageExc, Ncv32f scaleArea) { - applyHaarClassifierClassifierParallelFunctor functor(gridConf, blockConf, cuStream, - d_IImg, IImgStride, - d_weights, weightsStride, - d_Features, d_ClassifierNodes, d_Stages, - d_inMask, d_outMask, - mask1Dlen, mask2Dstride, - anchorsRoi, startStageInc, - endStageExc, scaleArea); + applyHaarClassifierClassifierParallelFunctor functor(gridConf, blockConf, cuStream, texImg, texHaarFeatures, texHaarClassifierNodes, d_IImg, IImgStride, d_weights, weightsStride, d_Features, + d_ClassifierNodes, d_Stages, d_inMask, d_outMask, mask1Dlen, mask2Dstride, anchorsRoi, startStageInc, endStageExc, scaleArea); //Second parameter is the number of "dynamic" template parameters NCVRuntimeTemplateBool::KernelCaller @@ -1015,31 +886,15 @@ NCVStatus ncvApplyHaarClassifierCascade_device(NCVMatrix &integral, NCV_SKIP_COND_BEGIN + cv::cudev::Texture texImg; if (bTexCacheIImg) - { - cudaChannelFormatDesc cfdTexIImage; - cfdTexIImage = cudaCreateChannelDesc(); + texImg = cv::cudev::Texture((anchorsRoi.height + haar.ClassifierSize.height) * integral.pitch(), integral.ptr()); - size_t alignmentOffset; - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, texIImage, integral.ptr(), cfdTexIImage, - (anchorsRoi.height + haar.ClassifierSize.height) * integral.pitch()), NCV_CUDA_ERROR); - ncvAssertReturn(alignmentOffset==0, NCV_TEXTURE_BIND_ERROR); - } - - if (bTexCacheCascade) - { - cudaChannelFormatDesc cfdTexHaarFeatures; - cudaChannelFormatDesc cfdTexHaarClassifierNodes; - cfdTexHaarFeatures = cudaCreateChannelDesc(); - cfdTexHaarClassifierNodes = cudaCreateChannelDesc(); - - size_t alignmentOffset; - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, texHaarFeatures, - d_HaarFeatures.ptr(), cfdTexHaarFeatures,sizeof(HaarFeature64) * haar.NumFeatures), NCV_CUDA_ERROR); - ncvAssertReturn(alignmentOffset==0, NCV_TEXTURE_BIND_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, texHaarClassifierNodes, - d_HaarNodes.ptr(), cfdTexHaarClassifierNodes, sizeof(HaarClassifierNode128) * haar.NumClassifierTotalNodes), NCV_CUDA_ERROR); - ncvAssertReturn(alignmentOffset==0, NCV_TEXTURE_BIND_ERROR); + cv::cudev::Texture texHaarFeatures; + cv::cudev::Texture texHaarClassifierNodes; + if (bTexCacheCascade) { + texHaarFeatures = cv::cudev::Texture(sizeof(HaarFeature64) * haar.NumFeatures, reinterpret_cast(d_HaarFeatures.ptr())); + texHaarClassifierNodes = cv::cudev::Texture(sizeof(HaarClassifierNode128) * haar.NumClassifierTotalNodes, reinterpret_cast(d_HaarNodes.ptr())); } Ncv32u stageStartAnchorParallel = 0; @@ -1130,26 +985,10 @@ NCVStatus ncvApplyHaarClassifierCascade_device(NCVMatrix &integral, dim3 grid1(((d_pixelMask.stride() + NUM_THREADS_ANCHORSPARALLEL - 1) / NUM_THREADS_ANCHORSPARALLEL), anchorsRoi.height); dim3 block1(NUM_THREADS_ANCHORSPARALLEL); - applyHaarClassifierAnchorParallelDynTemplate( - true, //tbInitMaskPositively - bTexCacheIImg, //tbCacheTextureIImg - bTexCacheCascade, //tbCacheTextureCascade - pixParallelStageStops[pixParallelStageStopsIndex] != 0,//tbReadPixelIndexFromVector - bDoAtomicCompaction, //tbDoAtomicCompaction - grid1, - block1, - cuStream, - integral.ptr(), integral.stride(), - d_weights.ptr(), d_weights.stride(), - d_HaarFeatures.ptr(), d_HaarNodes.ptr(), d_HaarStages.ptr(), - d_ptrNowData->ptr(), - bDoAtomicCompaction ? d_ptrNowTmp->ptr() : d_ptrNowData->ptr(), - 0, - d_pixelMask.stride(), - anchorsRoi, - pixParallelStageStops[pixParallelStageStopsIndex], - pixParallelStageStops[pixParallelStageStopsIndex+1], - scaleAreaPixels); + applyHaarClassifierAnchorParallelDynTemplate( true, bTexCacheIImg, bTexCacheCascade, pixParallelStageStops[pixParallelStageStopsIndex] != 0, bDoAtomicCompaction, grid1, block1, cuStream, + texImg, texHaarFeatures, texHaarClassifierNodes, integral.ptr(), integral.stride(), d_weights.ptr(), d_weights.stride(), d_HaarFeatures.ptr(), d_HaarNodes.ptr(), d_HaarStages.ptr(), + d_ptrNowData->ptr(), bDoAtomicCompaction ? d_ptrNowTmp->ptr() : d_ptrNowData->ptr(), 0, d_pixelMask.stride(), anchorsRoi, pixParallelStageStops[pixParallelStageStopsIndex], + pixParallelStageStops[pixParallelStageStopsIndex+1], scaleAreaPixels); ncvAssertCUDAReturn(cudaGetLastError(), NCV_CUDA_ERROR); if (bDoAtomicCompaction) @@ -1200,26 +1039,10 @@ NCVStatus ncvApplyHaarClassifierCascade_device(NCVMatrix &integral, } dim3 block2(NUM_THREADS_ANCHORSPARALLEL); - applyHaarClassifierAnchorParallelDynTemplate( - false, //tbInitMaskPositively - bTexCacheIImg, //tbCacheTextureIImg - bTexCacheCascade, //tbCacheTextureCascade - pixParallelStageStops[pixParallelStageStopsIndex] != 0 || pixelStep != 1 || bMaskElements,//tbReadPixelIndexFromVector - bDoAtomicCompaction, //tbDoAtomicCompaction - grid2, - block2, - cuStream, - integral.ptr(), integral.stride(), - d_weights.ptr(), d_weights.stride(), - d_HaarFeatures.ptr(), d_HaarNodes.ptr(), d_HaarStages.ptr(), - d_ptrNowData->ptr(), - bDoAtomicCompaction ? d_ptrNowTmp->ptr() : d_ptrNowData->ptr(), - numDetections, - d_pixelMask.stride(), - anchorsRoi, - pixParallelStageStops[pixParallelStageStopsIndex], - pixParallelStageStops[pixParallelStageStopsIndex+1], - scaleAreaPixels); + applyHaarClassifierAnchorParallelDynTemplate( false, bTexCacheIImg, bTexCacheCascade, pixParallelStageStops[pixParallelStageStopsIndex] != 0 || pixelStep != 1 || bMaskElements, bDoAtomicCompaction, + grid2, block2, cuStream, texImg, texHaarFeatures, texHaarClassifierNodes, integral.ptr(), integral.stride(), d_weights.ptr(), d_weights.stride(), d_HaarFeatures.ptr(), d_HaarNodes.ptr(), + d_HaarStages.ptr(), d_ptrNowData->ptr(), bDoAtomicCompaction ? d_ptrNowTmp->ptr() : d_ptrNowData->ptr(), numDetections, d_pixelMask.stride(), anchorsRoi, + pixParallelStageStops[pixParallelStageStopsIndex], pixParallelStageStops[pixParallelStageStopsIndex+1], scaleAreaPixels); ncvAssertCUDAReturn(cudaGetLastError(), NCV_CUDA_ERROR); if (bDoAtomicCompaction) @@ -1263,24 +1086,9 @@ NCVStatus ncvApplyHaarClassifierCascade_device(NCVMatrix &integral, } dim3 block3(NUM_THREADS_CLASSIFIERPARALLEL); - applyHaarClassifierClassifierParallelDynTemplate( - bTexCacheIImg, //tbCacheTextureIImg - bTexCacheCascade, //tbCacheTextureCascade - bDoAtomicCompaction, //tbDoAtomicCompaction - grid3, - block3, - cuStream, - integral.ptr(), integral.stride(), - d_weights.ptr(), d_weights.stride(), - d_HaarFeatures.ptr(), d_HaarNodes.ptr(), d_HaarStages.ptr(), - d_ptrNowData->ptr(), - bDoAtomicCompaction ? d_ptrNowTmp->ptr() : d_ptrNowData->ptr(), - numDetections, - d_pixelMask.stride(), - anchorsRoi, - stageMiddleSwitch, - stageEndClassifierParallel, - scaleAreaPixels); + applyHaarClassifierClassifierParallelDynTemplate(bTexCacheIImg, bTexCacheCascade, bDoAtomicCompaction, grid3, block3, cuStream, texImg, texHaarFeatures, texHaarClassifierNodes, integral.ptr(), integral.stride(), + d_weights.ptr(), d_weights.stride(), d_HaarFeatures.ptr(), d_HaarNodes.ptr(), d_HaarStages.ptr(), d_ptrNowData->ptr(), bDoAtomicCompaction ? d_ptrNowTmp->ptr() : d_ptrNowData->ptr(), numDetections, + d_pixelMask.stride(), anchorsRoi, stageMiddleSwitch, stageEndClassifierParallel, scaleAreaPixels); ncvAssertCUDAReturn(cudaGetLastError(), NCV_CUDA_ERROR); if (bDoAtomicCompaction) diff --git a/modules/cudalegacy/src/cuda/NPP_staging.cu b/modules/cudalegacy/src/cuda/NPP_staging.cu index 90880d56cc..b7a24ee036 100644 --- a/modules/cudalegacy/src/cuda/NPP_staging.cu +++ b/modules/cudalegacy/src/cuda/NPP_staging.cu @@ -48,12 +48,7 @@ #include "opencv2/cudev.hpp" #include "opencv2/cudalegacy/NPP_staging.hpp" - - -texture tex8u; -texture tex32u; -texture tex64u; - +#include //============================================================================== // @@ -71,7 +66,6 @@ cudaStream_t nppStGetActiveCUDAstream(void) } - cudaStream_t nppStSetActiveCUDAstream(cudaStream_t cudaStream) { cudaStream_t tmp = nppStream; @@ -117,25 +111,25 @@ private: template -inline __device__ T readElem(T *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs); +inline __device__ T readElem(cv::cudev::TexturePtr tex8u, T *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs); template<> -inline __device__ Ncv8u readElem(Ncv8u *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs) +inline __device__ Ncv8u readElem(cv::cudev::TexturePtr tex8u, Ncv8u* d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs) { - return tex1Dfetch(tex8u, texOffs + srcStride * blockIdx.x + curElemOffs); + return tex8u(texOffs + srcStride * blockIdx.x + curElemOffs); } template<> -inline __device__ Ncv32u readElem(Ncv32u *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs) +inline __device__ Ncv32u readElem(cv::cudev::TexturePtr tex8u, Ncv32u *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs) { return d_src[curElemOffs]; } template<> -inline __device__ Ncv32f readElem(Ncv32f *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs) +inline __device__ Ncv32f readElem(cv::cudev::TexturePtr tex8u, Ncv32f *d_src, Ncv32u texOffs, Ncv32u srcStride, Ncv32u curElemOffs) { return d_src[curElemOffs]; } @@ -160,8 +154,7 @@ inline __device__ Ncv32f readElem(Ncv32f *d_src, Ncv32u texOffs, Ncv32u * \return None */ template -__global__ void scanRows(T_in *d_src, Ncv32u texOffs, Ncv32u srcWidth, Ncv32u srcStride, - T_out *d_II, Ncv32u IIstride) +__global__ void scanRows(cv::cudev::TexturePtr tex8u, T_in *d_src, Ncv32u texOffs, Ncv32u srcWidth, Ncv32u srcStride, T_out *d_II, Ncv32u IIstride) { //advance pointers to the current line if (sizeof(T_in) != 1) @@ -190,7 +183,7 @@ __global__ void scanRows(T_in *d_src, Ncv32u texOffs, Ncv32u srcWidth, Ncv32u sr if (curElemOffs < srcWidth) { //load elements - curElem = readElem(d_src, texOffs, srcStride, curElemOffs); + curElem = readElem(tex8u, d_src, texOffs, srcStride, curElemOffs); } curElemMod = _scanElemOp::scanElemOp(curElem); @@ -224,25 +217,9 @@ template NCVStatus scanRowsWrapperDevice(T_in *d_src, Ncv32u srcStride, T_out *d_dst, Ncv32u dstStride, NcvSize32u roi) { - cudaChannelFormatDesc cfdTex; - size_t alignmentOffset = 0; - if (sizeof(T_in) == 1) - { - cfdTex = cudaCreateChannelDesc(); - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, tex8u, d_src, cfdTex, roi.height * srcStride), NPPST_TEXTURE_BIND_ERROR); - if (alignmentOffset > 0) - { - ncvAssertCUDAReturn(cudaUnbindTexture(tex8u), NCV_CUDA_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, tex8u, d_src, cfdTex, alignmentOffset + roi.height * srcStride), NPPST_TEXTURE_BIND_ERROR); - } - } - scanRows - - <<>> - (d_src, (Ncv32u)alignmentOffset, roi.width, srcStride, d_dst, dstStride); - + cv::cudev::Texture tex8u(static_cast(roi.height * srcStride), (Ncv8u*)d_src); + scanRows <<>> (tex8u, d_src, 0, roi.width, srcStride, d_dst, dstStride); ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); - return NPPST_SUCCESS; } @@ -585,59 +562,25 @@ NCVStatus nppiStSqrIntegral_8u64u_C1R_host(Ncv8u *h_src, Ncv32u srcStep, const Ncv32u NUM_DOWNSAMPLE_NEAREST_THREADS_X = 32; const Ncv32u NUM_DOWNSAMPLE_NEAREST_THREADS_Y = 8; - -template -__device__ T getElem_Decimate(Ncv32u x, T *d_src); - - -template<> -__device__ Ncv32u getElem_Decimate(Ncv32u x, Ncv32u *d_src) -{ - return tex1Dfetch(tex32u, x); -} - - -template<> -__device__ Ncv32u getElem_Decimate(Ncv32u x, Ncv32u *d_src) -{ - return d_src[x]; -} - - -template<> -__device__ Ncv64u getElem_Decimate(Ncv32u x, Ncv64u *d_src) -{ - uint2 tmp = tex1Dfetch(tex64u, x); - Ncv64u res = (Ncv64u)tmp.y; - res <<= 32; - res |= tmp.x; - return res; -} - - -template<> -__device__ Ncv64u getElem_Decimate(Ncv32u x, Ncv64u *d_src) +template +__global__ void decimate_C1R(T* d_src, Ncv32u srcStep, T* d_dst, Ncv32u dstStep, NcvSize32u dstRoi, Ncv32u scale) { - return d_src[x]; + int curX = blockIdx.x * blockDim.x + threadIdx.x; + int curY = blockIdx.y * blockDim.y + threadIdx.y; + if (curX >= dstRoi.width || curY >= dstRoi.height) return; + d_dst[curY * dstStep + curX] = d_src[(curY * srcStep + curX) * scale]; } - -template -__global__ void decimate_C1R(T *d_src, Ncv32u srcStep, T *d_dst, Ncv32u dstStep, - NcvSize32u dstRoi, Ncv32u scale) +template +__global__ void decimate_C1R(cv::cudev::TexturePtr texSrc, Ncv32u srcStep, T* d_dst, Ncv32u dstStep, + NcvSize32u dstRoi, Ncv32u scale) { int curX = blockIdx.x * blockDim.x + threadIdx.x; int curY = blockIdx.y * blockDim.y + threadIdx.y; - - if (curX >= dstRoi.width || curY >= dstRoi.height) - { - return; - } - - d_dst[curY * dstStep + curX] = getElem_Decimate((curY * srcStep + curX) * scale, d_src); + if (curX >= dstRoi.width || curY >= dstRoi.height) return; + d_dst[curY * dstStep + curX] = texSrc((curY * srcStep + curX) * scale); } - template static NCVStatus decimateWrapperDevice(T *d_src, Ncv32u srcStep, T *d_dst, Ncv32u dstStep, @@ -659,39 +602,12 @@ static NCVStatus decimateWrapperDevice(T *d_src, Ncv32u srcStep, dim3 grid((dstRoi.width + NUM_DOWNSAMPLE_NEAREST_THREADS_X - 1) / NUM_DOWNSAMPLE_NEAREST_THREADS_X, (dstRoi.height + NUM_DOWNSAMPLE_NEAREST_THREADS_Y - 1) / NUM_DOWNSAMPLE_NEAREST_THREADS_Y); dim3 block(NUM_DOWNSAMPLE_NEAREST_THREADS_X, NUM_DOWNSAMPLE_NEAREST_THREADS_Y); - - if (!readThruTexture) - { - decimate_C1R - - <<>> - (d_src, srcStep, d_dst, dstStep, dstRoi, scale); + if (!readThruTexture) { + decimate_C1R<<>>(d_src, srcStep, d_dst, dstStep, dstRoi, scale); } - else - { - cudaChannelFormatDesc cfdTexSrc; - - if (sizeof(T) == sizeof(Ncv32u)) - { - cfdTexSrc = cudaCreateChannelDesc(); - - size_t alignmentOffset; - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, tex32u, d_src, cfdTexSrc, srcRoi.height * srcStep * sizeof(T)), NPPST_TEXTURE_BIND_ERROR); - ncvAssertReturn(alignmentOffset==0, NPPST_TEXTURE_BIND_ERROR); - } - else - { - cfdTexSrc = cudaCreateChannelDesc(); - - size_t alignmentOffset; - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, tex64u, d_src, cfdTexSrc, srcRoi.height * srcStep * sizeof(T)), NPPST_TEXTURE_BIND_ERROR); - ncvAssertReturn(alignmentOffset==0, NPPST_TEXTURE_BIND_ERROR); - } - - decimate_C1R - - <<>> - (d_src, srcStep, d_dst, dstStep, dstRoi, scale); + else { + cv::cudev::Texture texSrc(srcRoi.height * srcStep * sizeof(T), d_src); + decimate_C1R<<>>(texSrc, srcStep, d_dst, dstStep, dstRoi, scale); } ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); @@ -753,11 +669,7 @@ static NCVStatus decimateWrapperHost(T *h_src, Ncv32u srcStep, implementNppDecimate(32, u) -implementNppDecimate(32, s) -implementNppDecimate(32, f) implementNppDecimate(64, u) -implementNppDecimate(64, s) -implementNppDecimate(64, f) implementNppDecimateHost(32, u) implementNppDecimateHost(32, s) implementNppDecimateHost(32, f) @@ -776,43 +688,29 @@ implementNppDecimateHost(64, f) const Ncv32u NUM_RECTSTDDEV_THREADS = 128; -template -__device__ Ncv32u getElemSum(Ncv32u x, Ncv32u *d_sum) +template +__device__ Ncv32u getElemSum(Ptr2D tex, Ncv32u x, Ncv32u *d_sum) { if (tbCacheTexture) - { - return tex1Dfetch(tex32u, x); - } + return tex(x); else - { return d_sum[x]; - } } -template -__device__ Ncv64u getElemSqSum(Ncv32u x, Ncv64u *d_sqsum) +template +__device__ Ncv64u getElemSqSum(Ptr2D tex, Ncv32u x, Ncv64u *d_sqsum) { if (tbCacheTexture) - { - uint2 tmp = tex1Dfetch(tex64u, x); - Ncv64u res = (Ncv64u)tmp.y; - res <<= 32; - res |= tmp.x; - return res; - } + return tex(x); else - { return d_sqsum[x]; - } } template -__global__ void rectStdDev_32f_C1R(Ncv32u *d_sum, Ncv32u sumStep, - Ncv64u *d_sqsum, Ncv32u sqsumStep, - Ncv32f *d_norm, Ncv32u normStep, - NcvSize32u roi, NcvRect32u rect, Ncv32f invRectArea) +__global__ void rectStdDev_32f_C1R(cv::cudev::TexturePtr texSum, cv::cudev::TexturePtr texSumSq, Ncv32u *d_sum, Ncv32u sumStep, Ncv64u *d_sqsum, Ncv32u sqsumStep, + Ncv32f *d_norm, Ncv32u normStep, NcvSize32u roi, NcvRect32u rect, Ncv32f invRectArea) { Ncv32u x_offs = blockIdx.x * NUM_RECTSTDDEV_THREADS + threadIdx.x; if (x_offs >= roi.width) @@ -824,17 +722,17 @@ __global__ void rectStdDev_32f_C1R(Ncv32u *d_sum, Ncv32u sumStep, Ncv32u sqsum_offset = blockIdx.y * sqsumStep + x_offs; //OPT: try swapping order (could change cache hit/miss ratio) - Ncv32u sum_tl = getElemSum(sum_offset + rect.y * sumStep + rect.x, d_sum); - Ncv32u sum_bl = getElemSum(sum_offset + (rect.y + rect.height) * sumStep + rect.x, d_sum); - Ncv32u sum_tr = getElemSum(sum_offset + rect.y * sumStep + rect.x + rect.width, d_sum); - Ncv32u sum_br = getElemSum(sum_offset + (rect.y + rect.height) * sumStep + rect.x + rect.width, d_sum); + Ncv32u sum_tl = getElemSum(texSum, sum_offset + rect.y * sumStep + rect.x, d_sum); + Ncv32u sum_bl = getElemSum(texSum, sum_offset + (rect.y + rect.height) * sumStep + rect.x, d_sum); + Ncv32u sum_tr = getElemSum(texSum, sum_offset + rect.y * sumStep + rect.x + rect.width, d_sum); + Ncv32u sum_br = getElemSum(texSum, sum_offset + (rect.y + rect.height) * sumStep + rect.x + rect.width, d_sum); Ncv32u sum_val = sum_br + sum_tl - sum_tr - sum_bl; Ncv64u sqsum_tl, sqsum_bl, sqsum_tr, sqsum_br; - sqsum_tl = getElemSqSum(sqsum_offset + rect.y * sqsumStep + rect.x, d_sqsum); - sqsum_bl = getElemSqSum(sqsum_offset + (rect.y + rect.height) * sqsumStep + rect.x, d_sqsum); - sqsum_tr = getElemSqSum(sqsum_offset + rect.y * sqsumStep + rect.x + rect.width, d_sqsum); - sqsum_br = getElemSqSum(sqsum_offset + (rect.y + rect.height) * sqsumStep + rect.x + rect.width, d_sqsum); + sqsum_tl = getElemSqSum(texSumSq, sqsum_offset + rect.y * sqsumStep + rect.x, d_sqsum); + sqsum_bl = getElemSqSum(texSumSq, sqsum_offset + (rect.y + rect.height) * sqsumStep + rect.x, d_sqsum); + sqsum_tr = getElemSqSum(texSumSq, sqsum_offset + rect.y * sqsumStep + rect.x + rect.width, d_sqsum); + sqsum_br = getElemSqSum(texSumSq, sqsum_offset + (rect.y + rect.height) * sqsumStep + rect.x + rect.width, d_sqsum); Ncv64u sqsum_val = sqsum_br + sqsum_tl - sqsum_tr - sqsum_bl; Ncv32f mean = sum_val * invRectArea; @@ -897,31 +795,12 @@ NCVStatus nppiStRectStdDev_32f_C1R(Ncv32u *d_sum, Ncv32u sumStep, dim3 grid(((roi.width + NUM_RECTSTDDEV_THREADS - 1) / NUM_RECTSTDDEV_THREADS), roi.height); dim3 block(NUM_RECTSTDDEV_THREADS); + cv::cudev::Texture texSum((roi.height + rect.y + rect.height) * sumStep * sizeof(Ncv32u), d_sum); + cv::cudev::Texture texSumSq((roi.height + rect.y + rect.height) * sqsumStep * sizeof(Ncv64u), d_sqsum); if (!readThruTexture) - { - rectStdDev_32f_C1R - - <<>> - (d_sum, sumStep, d_sqsum, sqsumStep, d_norm, normStep, roi, rect, invRectArea); - } + rectStdDev_32f_C1R<<>>(texSum, texSumSq, d_sum, sumStep, d_sqsum, sqsumStep, d_norm, normStep, roi, rect, invRectArea); else - { - cudaChannelFormatDesc cfdTexSrc; - cudaChannelFormatDesc cfdTexSqr; - cfdTexSrc = cudaCreateChannelDesc(); - cfdTexSqr = cudaCreateChannelDesc(); - - size_t alignmentOffset; - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, tex32u, d_sum, cfdTexSrc, (roi.height + rect.y + rect.height) * sumStep * sizeof(Ncv32u)), NPPST_TEXTURE_BIND_ERROR); - ncvAssertReturn(alignmentOffset==0, NPPST_TEXTURE_BIND_ERROR); - ncvAssertCUDAReturn(cudaBindTexture(&alignmentOffset, tex64u, d_sqsum, cfdTexSqr, (roi.height + rect.y + rect.height) * sqsumStep * sizeof(Ncv64u)), NPPST_TEXTURE_BIND_ERROR); - ncvAssertReturn(alignmentOffset==0, NPPST_TEXTURE_BIND_ERROR); - - rectStdDev_32f_C1R - - <<>> - (NULL, sumStep, NULL, sqsumStep, d_norm, normStep, roi, rect, invRectArea); - } + rectStdDev_32f_C1R<<>>(texSum, texSumSq, NULL, sumStep, NULL, sqsumStep, d_norm, normStep, roi, rect, invRectArea); ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); @@ -1553,40 +1432,24 @@ NCVStatus nppsStCompact_32f_host(Ncv32f *h_src, Ncv32u srcLen, // //============================================================================== - -texture texSrc; -texture texKernel; - - -__forceinline__ __device__ float getValueMirrorRow(const int rowOffset, - int i, - int w) +__forceinline__ __device__ float getValueMirrorRow(cv::cudev::TexturePtr< Ncv32f> tex, const int rowOffset, int i, int w) { if (i < 0) i = 1 - i; if (i >= w) i = w + w - i - 1; - return tex1Dfetch (texSrc, rowOffset + i); + return tex(rowOffset + i); } -__forceinline__ __device__ float getValueMirrorColumn(const int offset, - const int rowStep, - int j, - int h) +__forceinline__ __device__ float getValueMirrorColumn(cv::cudev::TexturePtr< Ncv32f> tex, const int offset, const int rowStep, int j, int h) { if (j < 0) j = 1 - j; if (j >= h) j = h + h - j - 1; - return tex1Dfetch (texSrc, offset + j * rowStep); + return tex(offset + j * rowStep); } -__global__ void FilterRowBorderMirror_32f_C1R(Ncv32u srcStep, - Ncv32f *pDst, - NcvSize32u dstSize, - Ncv32u dstStep, - NcvRect32u roi, - Ncv32s nKernelSize, - Ncv32s nAnchor, - Ncv32f multiplier) +__global__ void FilterRowBorderMirror_32f_C1R(cv::cudev::TexturePtr texSrc, cv::cudev::TexturePtr texKernel1, Ncv32u srcStep, Ncv32f *pDst, NcvSize32u dstSize, Ncv32u dstStep, + NcvRect32u roi, Ncv32s nKernelSize, Ncv32s nAnchor, Ncv32f multiplier) { // position within ROI const int ix = blockDim.x * blockIdx.x + threadIdx.x; @@ -1606,22 +1469,16 @@ __global__ void FilterRowBorderMirror_32f_C1R(Ncv32u srcStep, float sum = 0.0f; for (int m = 0; m < nKernelSize; ++m) { - sum += getValueMirrorRow (rowOffset, ix + m - p, roi.width) - * tex1Dfetch (texKernel, m); + sum += getValueMirrorRow(texSrc, rowOffset, ix + m - p, roi.width) + * texKernel1(m); } pDst[iy * dstStep + ix] = sum * multiplier; } -__global__ void FilterColumnBorderMirror_32f_C1R(Ncv32u srcStep, - Ncv32f *pDst, - NcvSize32u dstSize, - Ncv32u dstStep, - NcvRect32u roi, - Ncv32s nKernelSize, - Ncv32s nAnchor, - Ncv32f multiplier) +__global__ void FilterColumnBorderMirror_32f_C1R(cv::cudev::TexturePtr texSrc, cv::cudev::TexturePtr texKernel, Ncv32u srcStep, Ncv32f *pDst, NcvSize32u dstSize, Ncv32u dstStep, + NcvRect32u roi, Ncv32s nKernelSize, Ncv32s nAnchor, Ncv32f multiplier) { const int ix = blockDim.x * blockIdx.x + threadIdx.x; const int iy = blockDim.y * blockIdx.y + threadIdx.y; @@ -1638,15 +1495,15 @@ __global__ void FilterColumnBorderMirror_32f_C1R(Ncv32u srcStep, float sum = 0.0f; for (int m = 0; m < nKernelSize; ++m) { - sum += getValueMirrorColumn (offset, srcStep, iy + m - p, roi.height) - * tex1Dfetch (texKernel, m); + sum += getValueMirrorColumn(texSrc, offset, srcStep, iy + m - p, roi.height) + * texKernel(m); } pDst[ix + iy * dstStep] = sum * multiplier; } -NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, +NCVStatus nppiStFilterRowBorder_32f_C1R(Ncv32f *pSrc, NcvSize32u srcSize, Ncv32u nSrcStep, Ncv32f *pDst, @@ -1654,7 +1511,7 @@ NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, Ncv32u nDstStep, NcvRect32u oROI, NppStBorderType borderType, - const Ncv32f *pKernel, + Ncv32f *pKernel, Ncv32s nKernelSize, Ncv32s nAnchor, Ncv32f multiplier) @@ -1686,12 +1543,8 @@ NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, oROI.height = srcSize.height - oROI.y; } - cudaChannelFormatDesc floatChannel = cudaCreateChannelDesc (); - texSrc.normalized = false; - texKernel.normalized = false; - - cudaBindTexture (0, texSrc, pSrc, floatChannel, srcSize.height * nSrcStep); - cudaBindTexture (0, texKernel, pKernel, floatChannel, nKernelSize * sizeof (Ncv32f)); + cv::cudev::Texture texSrc(srcSize.height * nSrcStep, pSrc); + cv::cudev::Texture texKernel(nKernelSize * sizeof(Ncv32f), pKernel); dim3 ctaSize (32, 6); dim3 gridSize ((oROI.width + ctaSize.x - 1) / ctaSize.x, @@ -1706,8 +1559,7 @@ NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, case nppStBorderWrap: return NPPST_ERROR; case nppStBorderMirror: - FilterRowBorderMirror_32f_C1R <<>> - (srcStep, pDst, dstSize, dstStep, oROI, nKernelSize, nAnchor, multiplier); + FilterRowBorderMirror_32f_C1R <<>>(texSrc, texKernel, srcStep, pDst, dstSize, dstStep, oROI, nKernelSize, nAnchor, multiplier); ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); break; default: @@ -1718,7 +1570,7 @@ NCVStatus nppiStFilterRowBorder_32f_C1R(const Ncv32f *pSrc, } -NCVStatus nppiStFilterColumnBorder_32f_C1R(const Ncv32f *pSrc, +NCVStatus nppiStFilterColumnBorder_32f_C1R(Ncv32f *pSrc, NcvSize32u srcSize, Ncv32u nSrcStep, Ncv32f *pDst, @@ -1726,7 +1578,7 @@ NCVStatus nppiStFilterColumnBorder_32f_C1R(const Ncv32f *pSrc, Ncv32u nDstStep, NcvRect32u oROI, NppStBorderType borderType, - const Ncv32f *pKernel, + Ncv32f *pKernel, Ncv32s nKernelSize, Ncv32s nAnchor, Ncv32f multiplier) @@ -1758,12 +1610,8 @@ NCVStatus nppiStFilterColumnBorder_32f_C1R(const Ncv32f *pSrc, oROI.height = srcSize.height - oROI.y; } - cudaChannelFormatDesc floatChannel = cudaCreateChannelDesc (); - texSrc.normalized = false; - texKernel.normalized = false; - - cudaBindTexture (0, texSrc, pSrc, floatChannel, srcSize.height * nSrcStep); - cudaBindTexture (0, texKernel, pKernel, floatChannel, nKernelSize * sizeof (Ncv32f)); + cv::cudev::Texture texSrc(srcSize.height * nSrcStep, pSrc); + cv::cudev::Texture texKernel(nKernelSize * sizeof(Ncv32f), pKernel); dim3 ctaSize (32, 6); dim3 gridSize ((oROI.width + ctaSize.x - 1) / ctaSize.x, @@ -1776,8 +1624,7 @@ NCVStatus nppiStFilterColumnBorder_32f_C1R(const Ncv32f *pSrc, case nppStBorderWrap: return NPPST_ERROR; case nppStBorderMirror: - FilterColumnBorderMirror_32f_C1R <<>> - (srcStep, pDst, dstSize, dstStep, oROI, nKernelSize, nAnchor, multiplier); + FilterColumnBorderMirror_32f_C1R <<>>(texSrc, texKernel, srcStep, pDst, dstSize, dstStep, oROI, nKernelSize, nAnchor, multiplier); ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); break; default: @@ -1800,16 +1647,11 @@ inline Ncv32u iDivUp(Ncv32u num, Ncv32u denom) return (num + denom - 1)/denom; } - -texture tex_src1; -texture tex_src0; - - -__global__ void BlendFramesKernel(const float *u, const float *v, // forward flow - const float *ur, const float *vr, // backward flow - const float *o0, const float *o1, // coverage masks - int w, int h, int s, - float theta, float *out) +__global__ void BlendFramesKernel(cv::cudev::TexturePtr texSrc0, cv::cudev::TexturePtr texSrc1, + const float *u, const float *v, // forward flow + const float *ur, const float *vr, // backward flow + const float *o0, const float *o1, // coverage masks + int w, int h, int s, float theta, float *out) { const int ix = threadIdx.x + blockDim.x * blockIdx.x; const int iy = threadIdx.y + blockDim.y * blockIdx.y; @@ -1829,27 +1671,17 @@ __global__ void BlendFramesKernel(const float *u, const float *v, // forward f bool b0 = o0[pos] > 1e-4f; bool b1 = o1[pos] > 1e-4f; - if (b0 && b1) - { - // pixel is visible on both frames - out[pos] = tex2D(tex_src0, x - _u * theta, y - _v * theta) * (1.0f - theta) + - tex2D(tex_src1, x + _u * (1.0f - theta), y + _v * (1.0f - theta)) * theta; - } - else if (b0) - { - // visible on the first frame only - out[pos] = tex2D(tex_src0, x - _u * theta, y - _v * theta); - } - else - { - // visible on the second frame only - out[pos] = tex2D(tex_src1, x - _ur * (1.0f - theta), y - _vr * (1.0f - theta)); - } + if (b0 && b1) // pixel is visible on both frames + out[pos] = texSrc0(y - _v * theta, x - _u * theta)* (1.0f - theta) + texSrc0(y + _v * (1.0f - theta), x + _u * (1.0f - theta)) * theta; + else if (b0) // visible on the first frame only + out[pos] = texSrc0(y - _v * theta, x - _u * theta); + else // visible on the second frame only + out[pos] = texSrc1(y - _vr * (1.0f - theta), x - _ur * (1.0f - theta)); } -NCVStatus BlendFrames(const Ncv32f *src0, - const Ncv32f *src1, +NCVStatus BlendFrames(Ncv32f *src0, + Ncv32f *src1, const Ncv32f *ufi, const Ncv32f *vfi, const Ncv32f *ubi, @@ -1862,29 +1694,13 @@ NCVStatus BlendFrames(const Ncv32f *src0, Ncv32f theta, Ncv32f *out) { - tex_src1.addressMode[0] = cudaAddressModeClamp; - tex_src1.addressMode[1] = cudaAddressModeClamp; - tex_src1.filterMode = cudaFilterModeLinear; - tex_src1.normalized = false; - - tex_src0.addressMode[0] = cudaAddressModeClamp; - tex_src0.addressMode[1] = cudaAddressModeClamp; - tex_src0.filterMode = cudaFilterModeLinear; - tex_src0.normalized = false; - - cudaChannelFormatDesc desc = cudaCreateChannelDesc (); const Ncv32u pitch = stride * sizeof (float); - ncvAssertCUDAReturn (cudaBindTexture2D (0, tex_src1, src1, desc, width, height, pitch), NPPST_TEXTURE_BIND_ERROR); - ncvAssertCUDAReturn (cudaBindTexture2D (0, tex_src0, src0, desc, width, height, pitch), NPPST_TEXTURE_BIND_ERROR); - + cv::cudev::Texture texSrc0(height, width, src0, pitch, false, cudaFilterModeLinear); + cv::cudev::Texture texSrc1(height, width, src1, pitch, false, cudaFilterModeLinear); dim3 threads (32, 4); dim3 blocks (iDivUp (width, threads.x), iDivUp (height, threads.y)); - - BlendFramesKernel<<>> - (ufi, vfi, ubi, vbi, o1, o2, width, height, stride, theta, out); - + BlendFramesKernel<<>>(texSrc0, texSrc1, ufi, vfi, ubi, vbi, o1, o2, width, height, stride, theta, out); ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); - return NPPST_SUCCESS; } @@ -2255,44 +2071,27 @@ NCVStatus nppiStVectorWarp_PSF2x2_32f_C1(const Ncv32f *pSrc, // //============================================================================== - -texture texSrc2D; - - __forceinline__ -__device__ float processLine(int spos, - float xmin, - float xmax, - int ixmin, - int ixmax, - float fxmin, - float cxmax) +__device__ float processLine(cv::cudev::TexturePtr tex, int spos, float xmin, float xmax, int ixmin, int ixmax, float fxmin, float cxmax) { // first element float wsum = 1.0f - xmin + fxmin; - float sum = tex1Dfetch(texSrc, spos) * (1.0f - xmin + fxmin); + float sum = tex( spos) * (1.0f - xmin + fxmin); spos++; for (int ix = ixmin + 1; ix < ixmax; ++ix) { - sum += tex1Dfetch(texSrc, spos); + sum += tex(spos); spos++; wsum += 1.0f; } - sum += tex1Dfetch(texSrc, spos) * (cxmax - xmax); + sum += tex(spos) * (cxmax - xmax); wsum += cxmax - xmax; return sum / wsum; } -__global__ void resizeSuperSample_32f(NcvSize32u srcSize, - Ncv32u srcStep, - NcvRect32u srcROI, - Ncv32f *dst, - NcvSize32u dstSize, - Ncv32u dstStep, - NcvRect32u dstROI, - Ncv32f scaleX, - Ncv32f scaleY) +__global__ void resizeSuperSample_32f(cv::cudev::TexturePtr texSrc, NcvSize32u srcSize, Ncv32u srcStep, NcvRect32u srcROI, Ncv32f *dst, NcvSize32u dstSize, Ncv32u dstStep, + NcvRect32u dstROI, Ncv32f scaleX, Ncv32f scaleY) { // position within dst ROI const int ix = blockIdx.x * blockDim.x + threadIdx.x; @@ -2332,18 +2131,18 @@ __global__ void resizeSuperSample_32f(NcvSize32u srcSize, float wsum = 1.0f - yBegin + floorYBegin; - float sum = processLine (pos, xBegin, xEnd, iXBegin, iXEnd, floorXBegin, + float sum = processLine (texSrc, pos, xBegin, xEnd, iXBegin, iXEnd, floorXBegin, ceilXEnd) * (1.0f - yBegin + floorYBegin); pos += srcStep; for (int iy = iYBegin + 1; iy < iYEnd; ++iy) { - sum += processLine (pos, xBegin, xEnd, iXBegin, iXEnd, floorXBegin, + sum += processLine (texSrc, pos, xBegin, xEnd, iXBegin, iXEnd, floorXBegin, ceilXEnd); pos += srcStep; wsum += 1.0f; } - sum += processLine (pos, xBegin, xEnd, iXBegin, iXEnd, floorXBegin, + sum += processLine (texSrc, pos, xBegin, xEnd, iXBegin, iXEnd, floorXBegin, ceilXEnd) * (ceilYEnd - yEnd); wsum += ceilYEnd - yEnd; sum /= wsum; @@ -2372,14 +2171,7 @@ __device__ float bicubicCoeff(float x_) } -__global__ void resizeBicubic(NcvSize32u srcSize, - NcvRect32u srcROI, - NcvSize32u dstSize, - Ncv32u dstStep, - Ncv32f *dst, - NcvRect32u dstROI, - Ncv32f scaleX, - Ncv32f scaleY) +__global__ void resizeBicubic(cv::cudev::TexturePtr texSrc, NcvSize32u srcSize, NcvRect32u srcROI, NcvSize32u dstSize, Ncv32u dstStep, Ncv32f *dst, NcvRect32u dstROI, Ncv32f scaleX, Ncv32f scaleY) { const int ix = blockIdx.x * blockDim.x + threadIdx.x; const int iy = blockIdx.y * blockDim.y + threadIdx.y; @@ -2433,7 +2225,7 @@ __global__ void resizeBicubic(NcvSize32u srcSize, float wx = bicubicCoeff (xDist); float wy = bicubicCoeff (yDist); wx *= wy; - sum += wx * tex2D (texSrc2D, cx * dx, cy * dy); + sum += wx * texSrc(cy * dy, cx * dx); wsum += wx; } } @@ -2441,7 +2233,7 @@ __global__ void resizeBicubic(NcvSize32u srcSize, } -NCVStatus nppiStResize_32f_C1R(const Ncv32f *pSrc, +NCVStatus nppiStResize_32f_C1R(Ncv32f *pSrc, NcvSize32u srcSize, Ncv32u nSrcStep, NcvRect32u srcROI, @@ -2469,33 +2261,17 @@ NCVStatus nppiStResize_32f_C1R(const Ncv32f *pSrc, if (interpolation == nppStSupersample) { - // bind texture - cudaBindTexture (0, texSrc, pSrc, srcSize.height * nSrcStep); - // invoke kernel + cv::cudev::Texture texSrc(srcSize.height * nSrcStep, pSrc); dim3 ctaSize (32, 6); - dim3 gridSize ((dstROI.width + ctaSize.x - 1) / ctaSize.x, - (dstROI.height + ctaSize.y - 1) / ctaSize.y); - - resizeSuperSample_32f <<>> - (srcSize, srcStep, srcROI, pDst, dstSize, dstStep, dstROI, 1.0f / xFactor, 1.0f / yFactor); + dim3 gridSize ((dstROI.width + ctaSize.x - 1) / ctaSize.x,(dstROI.height + ctaSize.y - 1) / ctaSize.y); + resizeSuperSample_32f <<>> (texSrc, srcSize, srcStep, srcROI, pDst, dstSize, dstStep, dstROI, 1.0f / xFactor, 1.0f / yFactor); } else if (interpolation == nppStBicubic) { - texSrc2D.addressMode[0] = cudaAddressModeMirror; - texSrc2D.addressMode[1] = cudaAddressModeMirror; - texSrc2D.normalized = true; - - cudaChannelFormatDesc desc = cudaCreateChannelDesc (); - - cudaBindTexture2D (0, texSrc2D, pSrc, desc, srcSize.width, srcSize.height, - nSrcStep); - + cv::cudev::Texture texSrc(srcSize.height, srcSize.width, pSrc, nSrcStep, true, cudaFilterModePoint, cudaAddressModeMirror); dim3 ctaSize (32, 6); - dim3 gridSize ((dstSize.width + ctaSize.x - 1) / ctaSize.x, - (dstSize.height + ctaSize.y - 1) / ctaSize.y); - - resizeBicubic <<>> - (srcSize, srcROI, dstSize, dstStep, pDst, dstROI, 1.0f / xFactor, 1.0f / yFactor); + dim3 gridSize ((dstSize.width + ctaSize.x - 1) / ctaSize.x, (dstSize.height + ctaSize.y - 1) / ctaSize.y); + resizeBicubic <<>> (texSrc, srcSize, srcROI, dstSize, dstStep, pDst, dstROI, 1.0f / xFactor, 1.0f / yFactor); } else { diff --git a/modules/cudalegacy/src/cuda/bm.cu b/modules/cudalegacy/src/cuda/bm.cu index 1307a8e327..546f0903b0 100644 --- a/modules/cudalegacy/src/cuda/bm.cu +++ b/modules/cudalegacy/src/cuda/bm.cu @@ -46,29 +46,27 @@ #include "opencv2/core/cuda/limits.hpp" #include "opencv2/core/cuda/functional.hpp" #include "opencv2/core/cuda/reduce.hpp" +#include using namespace cv::cuda; using namespace cv::cuda::device; namespace optflowbm { - texture tex_prev(false, cudaFilterModePoint, cudaAddressModeClamp); - texture tex_curr(false, cudaFilterModePoint, cudaAddressModeClamp); - - __device__ int cmpBlocks(int X1, int Y1, int X2, int Y2, int2 blockSize) + __device__ int cmpBlocks(cv::cudev::TexturePtr texCurr, cv::cudev::TexturePtr texPrev, int X1, int Y1, int X2, int Y2, int2 blockSize) { int s = 0; for (int y = 0; y < blockSize.y; ++y) { for (int x = 0; x < blockSize.x; ++x) - s += ::abs(tex2D(tex_prev, X1 + x, Y1 + y) - tex2D(tex_curr, X2 + x, Y2 + y)); + s += ::abs(texPrev(Y1 + y, X1 + x) -texCurr(Y2 + y, X2 + x)); } return s; } - __global__ void calcOptFlowBM(PtrStepSzf velx, PtrStepf vely, const int2 blockSize, const int2 shiftSize, const bool usePrevious, + __global__ void calcOptFlowBM(cv::cudev::TexturePtr texPrev, cv::cudev::TexturePtr texCurr, PtrStepSzf velx, PtrStepf vely, const int2 blockSize, const int2 shiftSize, const bool usePrevious, const int maxX, const int maxY, const int acceptLevel, const int escapeLevel, const short2* ss, const int ssCount) { @@ -90,7 +88,7 @@ namespace optflowbm int dist = numeric_limits::max(); if (0 <= X2 && X2 <= maxX && 0 <= Y2 && Y2 <= maxY) - dist = cmpBlocks(X1, Y1, X2, Y2, blockSize); + dist = cmpBlocks(texPrev, texCurr, X1, Y1, X2, Y2, blockSize); int countMin = 1; int sumx = offX; @@ -111,7 +109,7 @@ namespace optflowbm if (0 <= X2 && X2 <= maxX && 0 <= Y2 && Y2 <= maxY) { - const int tmpDist = cmpBlocks(X1, Y1, X2, Y2, blockSize); + const int tmpDist = cmpBlocks(texPrev, texCurr, X1, Y1, X2, Y2, blockSize); if (tmpDist < acceptLevel) { sumx = dx; @@ -151,16 +149,12 @@ namespace optflowbm void calc(PtrStepSzb prev, PtrStepSzb curr, PtrStepSzf velx, PtrStepSzf vely, int2 blockSize, int2 shiftSize, bool usePrevious, int maxX, int maxY, int acceptLevel, int escapeLevel, const short2* ss, int ssCount, cudaStream_t stream) { - bindTexture(&tex_prev, prev); - bindTexture(&tex_curr, curr); - + cv::cudev::Texture texPrev(prev); + cv::cudev::Texture texCurr(curr); const dim3 block(32, 8); const dim3 grid(divUp(velx.cols, block.x), divUp(vely.rows, block.y)); - - calcOptFlowBM<<>>(velx, vely, blockSize, shiftSize, usePrevious, - maxX, maxY, acceptLevel, escapeLevel, ss, ssCount); + calcOptFlowBM<<>>(texPrev, texCurr, velx, vely, blockSize, shiftSize, usePrevious, maxX, maxY, acceptLevel, escapeLevel, ss, ssCount); cudaSafeCall( cudaGetLastError() ); - if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); } diff --git a/modules/cudalegacy/test/TestHypothesesGrow.cpp b/modules/cudalegacy/test/TestHypothesesGrow.cpp index e7fe4d939d..ad4c3c9df3 100644 --- a/modules/cudalegacy/test/TestHypothesesGrow.cpp +++ b/modules/cudalegacy/test/TestHypothesesGrow.cpp @@ -100,7 +100,8 @@ bool TestHypothesesGrow::process() NCV_SKIP_COND_BEGIN ncvAssertReturn(this->src.fill(h_vecSrc), false); - memset(h_vecDst.ptr(), 0, h_vecDst.length() * sizeof(NcvRect32u)); + + *h_vecDst.ptr() = {}; NCVVectorReuse h_vecDst_as32u(h_vecDst.getSegment(), lenDst * sizeof(NcvRect32u) / sizeof(Ncv32u)); ncvAssertReturn(h_vecDst_as32u.isMemReused(), false); ncvAssertReturn(this->src.fill(h_vecDst_as32u), false); diff --git a/modules/cudaobjdetect/src/cuda/hog.cu b/modules/cudaobjdetect/src/cuda/hog.cu index 5c12860620..2e0b880365 100644 --- a/modules/cudaobjdetect/src/cuda/hog.cu +++ b/modules/cudaobjdetect/src/cuda/hog.cu @@ -46,6 +46,7 @@ #include "opencv2/core/cuda/reduce.hpp" #include "opencv2/core/cuda/functional.hpp" #include "opencv2/core/cuda/warp_shuffle.hpp" +#include namespace cv { namespace cuda { namespace device { @@ -825,64 +826,57 @@ namespace cv { namespace cuda { namespace device //------------------------------------------------------------------- // Resize - texture resize8UC4_tex; - texture resize8UC1_tex; - - __global__ void resize_for_hog_kernel(float sx, float sy, PtrStepSz dst, int colOfs) + __global__ void resize_for_hog_kernel(cv::cudev::TexturePtr src, float sx, float sy, PtrStepSz dst) { unsigned int x = blockIdx.x * blockDim.x + threadIdx.x; unsigned int y = blockIdx.y * blockDim.y + threadIdx.y; if (x < dst.cols && y < dst.rows) - dst.ptr(y)[x] = tex2D(resize8UC1_tex, x * sx + colOfs, y * sy) * 255; + dst.ptr(y)[x] = src(y * sy, x * sx) * 255; } - __global__ void resize_for_hog_kernel(float sx, float sy, PtrStepSz dst, int colOfs) + __global__ void resize_for_hog_kernel(cv::cudev::TexturePtr src, float sx, float sy, PtrStepSz dst) { unsigned int x = blockIdx.x * blockDim.x + threadIdx.x; unsigned int y = blockIdx.y * blockDim.y + threadIdx.y; if (x < dst.cols && y < dst.rows) { - float4 val = tex2D(resize8UC4_tex, x * sx + colOfs, y * sy); + float4 val = src(y * sy, x * sx); dst.ptr(y)[x] = make_uchar4(val.x * 255, val.y * 255, val.z * 255, val.w * 255); } } - template - static void resize_for_hog(const PtrStepSzb& src, PtrStepSzb dst, TEX& tex) + static void resize_for_hog_8UC1(const PtrStepSzb& src, PtrStepSzb dst) { - tex.filterMode = cudaFilterModeLinear; - - size_t texOfs = 0; - int colOfs = 0; - - cudaChannelFormatDesc desc = cudaCreateChannelDesc(); - cudaSafeCall( cudaBindTexture2D(&texOfs, tex, src.data, desc, src.cols, src.rows, src.step) ); - - if (texOfs != 0) - { - colOfs = static_cast( texOfs/sizeof(T) ); - cudaSafeCall( cudaUnbindTexture(tex) ); - cudaSafeCall( cudaBindTexture2D(&texOfs, tex, src.data, desc, src.cols, src.rows, src.step) ); - } - + cv::cudev::Texture tex(src.rows, src.cols, src.data, src.step, false, cudaFilterModeLinear, cudaAddressModeClamp, cudaReadModeNormalizedFloat); dim3 threads(32, 8); dim3 grid(divUp(dst.cols, threads.x), divUp(dst.rows, threads.y)); float sx = static_cast(src.cols) / dst.cols; float sy = static_cast(src.rows) / dst.rows; - resize_for_hog_kernel<<>>(sx, sy, (PtrStepSz)dst, colOfs); + resize_for_hog_kernel<<>>(tex, sx, sy, (PtrStepSz)dst); cudaSafeCall( cudaGetLastError() ); - cudaSafeCall( cudaDeviceSynchronize() ); + } + + static void resize_for_hog_8UC4(const PtrStepSzb& src, PtrStepSzb dst) + { + cv::cudev::Texture tex(src.rows, src.cols, reinterpret_cast(src.data), src.step, false, cudaFilterModeLinear, cudaAddressModeClamp, cudaReadModeNormalizedFloat); + dim3 threads(32, 8); + dim3 grid(divUp(dst.cols, threads.x), divUp(dst.rows, threads.y)); + + float sx = static_cast(src.cols) / dst.cols; + float sy = static_cast(src.rows) / dst.rows; - cudaSafeCall( cudaUnbindTexture(tex) ); + resize_for_hog_kernel<<>>(tex, sx, sy, (PtrStepSz)dst); + cudaSafeCall(cudaGetLastError()); + cudaSafeCall(cudaDeviceSynchronize()); } - void resize_8UC1(const PtrStepSzb& src, PtrStepSzb dst) { resize_for_hog (src, dst, resize8UC1_tex); } - void resize_8UC4(const PtrStepSzb& src, PtrStepSzb dst) { resize_for_hog(src, dst, resize8UC4_tex); } + void resize_8UC1(const PtrStepSzb& src, PtrStepSzb dst) { resize_for_hog_8UC1(src, dst); } + void resize_8UC4(const PtrStepSzb& src, PtrStepSzb dst) { resize_for_hog_8UC4(src, dst); } } // namespace hog }}} // namespace cv { namespace cuda { namespace cudev diff --git a/modules/cudaobjdetect/test/test_objdetect.cpp b/modules/cudaobjdetect/test/test_objdetect.cpp index 4843cc483e..5fff18041e 100644 --- a/modules/cudaobjdetect/test/test_objdetect.cpp +++ b/modules/cudaobjdetect/test/test_objdetect.cpp @@ -222,7 +222,7 @@ INSTANTIATE_TEST_CASE_P(CUDA_ObjDetect, HOG, ALL_DEVICES); */ //============== caltech hog tests =====================// -struct CalTech : public ::testing::TestWithParam > +struct CalTech : public ::testing::TestWithParam> { cv::cuda::DeviceInfo devInfo; cv::Mat img; @@ -232,7 +232,13 @@ struct CalTech : public ::testing::TestWithParam("caltech/image_00000009_0.png", "caltech/image_00000032_0.png", "caltech/image_00000165_0.png", "caltech/image_00000261_0.png", "caltech/image_00000469_0.png", - "caltech/image_00000527_0.png", "caltech/image_00000574_0.png"))); + "caltech/image_00000527_0.png", "caltech/image_00000574_0.png"), testing::Values(GREYSCALE))); //------------------------variable GPU HOG Tests------------------------// diff --git a/modules/cudaoptflow/src/cuda/pyrlk.cu b/modules/cudaoptflow/src/cuda/pyrlk.cu index ca9759c2e5..76a9f3f797 100644 --- a/modules/cudaoptflow/src/cuda/pyrlk.cu +++ b/modules/cudaoptflow/src/cuda/pyrlk.cu @@ -50,8 +50,7 @@ #include "opencv2/core/cuda/reduce.hpp" #include "opencv2/core/cuda/filters.hpp" #include "opencv2/core/cuda/border_interpolate.hpp" - -#include +#include using namespace cv::cuda; using namespace cv::cuda::device; @@ -64,224 +63,6 @@ namespace pyrlk __constant__ int c_halfWin_y; __constant__ int c_iters; - texture tex_I8U(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_I8UC4(false, cudaFilterModeLinear, cudaAddressModeClamp); - - texture tex_I16UC4(false, cudaFilterModeLinear, cudaAddressModeClamp); - - - texture tex_If(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_If4(false, cudaFilterModeLinear, cudaAddressModeClamp); - - texture tex_Ib(false, cudaFilterModePoint, cudaAddressModeClamp); - - texture tex_J8U(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_J8UC4(false, cudaFilterModeLinear, cudaAddressModeClamp); - - texture tex_J16UC4(false, cudaFilterModeLinear, cudaAddressModeClamp); - - - texture tex_Jf(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_Jf4(false, cudaFilterModeLinear, cudaAddressModeClamp); - - - template struct Tex_I - { - static __host__ __forceinline__ void bindTexture_(PtrStepSz::vec_type> I) - { - CV_UNUSED(I); - } - }; - - template <> struct Tex_I<1, uchar> - { - static __device__ __forceinline__ float read(float x, float y) - { - return tex2D(tex_I8U, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - bindTexture(&tex_I8U, I); - } - }; - template <> struct Tex_I<1, ushort> - { - static __device__ __forceinline__ float read(float x, float y) - { - return 0.0; - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - CV_UNUSED(I); - } - }; - template <> struct Tex_I<1, int> - { - static __device__ __forceinline__ float read(float x, float y) - { - return 0.0; - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - CV_UNUSED(I); - } - }; - template <> struct Tex_I<1, float> - { - static __device__ __forceinline__ float read(float x, float y) - { - return tex2D(tex_If, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - bindTexture(&tex_If, I); - } - }; - // ****************** 3 channel specializations ************************ - template <> struct Tex_I<3, uchar> - { - static __device__ __forceinline__ float3 read(float x, float y) - { - return make_float3(0,0,0); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz I) - { - CV_UNUSED(I); - } - }; - template <> struct Tex_I<3, ushort> - { - static __device__ __forceinline__ float3 read(float x, float y) - { - return make_float3(0, 0, 0); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz I) - { - CV_UNUSED(I); - } - }; - template <> struct Tex_I<3, int> - { - static __device__ __forceinline__ float3 read(float x, float y) - { - return make_float3(0, 0, 0); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz I) - { - CV_UNUSED(I); - } - }; - template <> struct Tex_I<3, float> - { - static __device__ __forceinline__ float3 read(float x, float y) - { - return make_float3(0, 0, 0); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz I) - { - CV_UNUSED(I); - } - }; - // ****************** 4 channel specializations ************************ - - template <> struct Tex_I<4, uchar> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_I8UC4, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - bindTexture(&tex_I8UC4, I); - } - }; - template <> struct Tex_I<4, ushort> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_I16UC4, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - bindTexture(&tex_I16UC4, I); - } - }; - template <> struct Tex_I<4, float> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_If4, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& I) - { - bindTexture(&tex_If4, I); - } - }; - // ************* J *************** - template struct Tex_J - { - static __host__ __forceinline__ void bindTexture_(PtrStepSz::vec_type>& J) - { - CV_UNUSED(J); - } - }; - template <> struct Tex_J<1, uchar> - { - static __device__ __forceinline__ float read(float x, float y) - { - return tex2D(tex_J8U, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& J) - { - bindTexture(&tex_J8U, J); - } - }; - template <> struct Tex_J<1, float> - { - static __device__ __forceinline__ float read(float x, float y) - { - return tex2D(tex_Jf, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& J) - { - bindTexture(&tex_Jf, J); - } - }; - // ************* 4 channel specializations *************** - template <> struct Tex_J<4, uchar> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_J8UC4, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& J) - { - bindTexture(&tex_J8UC4, J); - } - }; - template <> struct Tex_J<4, ushort> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_J16UC4, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& J) - { - bindTexture(&tex_J16UC4, J); - } - }; - template <> struct Tex_J<4, float> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_Jf4, x, y); - } - static __host__ __forceinline__ void bindTexture_(PtrStepSz& J) - { - bindTexture(&tex_Jf4, J); - } - }; - __device__ __forceinline__ void accum(float& dst, const float& val) { dst += val; @@ -364,8 +145,8 @@ namespace pyrlk } }; - template - __global__ void sparseKernel(const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols) + template + __global__ void sparseKernel(const Ptr2D texI, const Ptr2D texJ, const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols) { #if __CUDA_ARCH__ <= 110 const int BLOCK_SIZE = 128; @@ -413,15 +194,14 @@ namespace pyrlk float x = prevPt.x + xBase + 0.5f; float y = prevPt.y + yBase + 0.5f; - I_patch[i][j] = Tex_I::read(x, y); + I_patch[i][j] = texI(y, x); // Scharr Deriv + work_type dIdx = 3.0f * texI(y - 1, x + 1) + 10.0f * texI(y, x + 1) + 3.0f * texI(y + 1, x + 1) - + (3.0f * texI(y - 1, x - 1) + 10.0f * texI(y, x - 1) + 3.0f * texI(y + 1, x - 1)); - work_type dIdx = 3.0f * Tex_I::read(x+1, y-1) + 10.0f * Tex_I::read(x+1, y) + 3.0f * Tex_I::read(x+1, y+1) - - (3.0f * Tex_I::read(x-1, y-1) + 10.0f * Tex_I::read(x-1, y) + 3.0f * Tex_I::read(x-1, y+1)); - - work_type dIdy = 3.0f * Tex_I::read(x-1, y+1) + 10.0f * Tex_I::read(x, y+1) + 3.0f * Tex_I::read(x+1, y+1) - - (3.0f * Tex_I::read(x-1, y-1) + 10.0f * Tex_I::read(x, y-1) + 3.0f * Tex_I::read(x+1, y-1)); + work_type dIdy = 3.0f * texI(y + 1, x - 1) + 10.0f * texI(y + 1, x) + 3.0f * texI(y + 1, x + 1) - + (3.0f * texI(y - 1, x - 1) + 10.0f * texI(y - 1, x) + 3.0f * texI(y - 1, x + 1)); dIdx_patch[i][j] = dIdx; dIdy_patch[i][j] = dIdy; @@ -490,7 +270,8 @@ namespace pyrlk for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j) { work_type I_val = I_patch[i][j]; - work_type J_val = Tex_J::read(nextPt.x + x + 0.5f, nextPt.y + y + 0.5f); + + work_type J_val = texJ(nextPt.y + y + 0.5f, nextPt.x + x + 0.5f); work_type diff = (J_val - I_val) * 32.0f; @@ -533,7 +314,8 @@ namespace pyrlk for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j) { work_type I_val = I_patch[i][j]; - work_type J_val = Tex_J::read(nextPt.x + x + 0.5f, nextPt.y + y + 0.5f); + + work_type J_val = texJ(nextPt.y + y + 0.5f, nextPt.x + x + 0.5f); work_type diff = J_val - I_val; @@ -749,6 +531,27 @@ namespace pyrlk } } // __global__ void sparseKernel_ + // Specialization for non float data, cudaFilterModeLinear only compatible with cudaReadModeNormalizedFloat. + template class TextureLinear : public cv::cudev::Texture::vec_type, typename TypeVec::vec_type> { + public: + typedef typename TypeVec::vec_type elem_type; + typedef typename TypeVec::vec_type ret_type; + __host__ TextureLinear(PtrStepSz src, const bool normalizedCoords = false, const cudaTextureAddressMode addressMode = cudaAddressModeClamp) : + cv::cudev::Texture(src, normalizedCoords, cudaFilterModeLinear, addressMode, cudaReadModeNormalizedFloat) + { + } + }; + + // Specialization for float data, cudaReadModeNormalizedFloat only compatible with cudaReadModeElementType. + template class TextureLinear : public cv::cudev::Texture::vec_type, typename TypeVec::vec_type> + { + public: + typedef typename TypeVec::vec_type float_type; + __host__ TextureLinear(PtrStepSz src, const bool normalizedCoords = false, const cudaTextureAddressMode addressMode = cudaAddressModeClamp) : + cv::cudev::Texture (src, normalizedCoords, cudaFilterModeLinear, addressMode, cudaReadModeElementType) + { + } + }; template class sparse_caller { @@ -756,16 +559,16 @@ namespace pyrlk static void call(PtrStepSz::vec_type> I, PtrStepSz::vec_type> J, int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, int level, dim3 block, cudaStream_t stream) { + typedef typename TypeVec::vec_type dType; + typedef typename TypeVec::vec_type rType; + TextureLinear texI(I); + TextureLinear texJ(J); dim3 grid(ptcount); - CV_UNUSED(I); - CV_UNUSED(J); if (level == 0 && err) - sparseKernel <<>>(prevPts, nextPts, status, err, level, rows, cols); + sparseKernel><<>>(texI, texJ, prevPts, nextPts, status, err, level, rows, cols); else - sparseKernel <<>>(prevPts, nextPts, status, err, level, rows, cols); - + sparseKernel><<>>(texI, texJ, prevPts, nextPts, status, err, level, rows, cols); cudaSafeCall(cudaGetLastError()); - if (stream == 0) cudaSafeCall(cudaDeviceSynchronize()); } @@ -903,8 +706,8 @@ namespace pyrlk }; - template - __global__ void denseKernel(PtrStepf u, PtrStepf v, const PtrStepf prevU, const PtrStepf prevV, PtrStepf err, const int rows, const int cols) + template + __global__ void denseKernel(const Ptr2D texI, const Ptr2D texJ, PtrStepf u, PtrStepf v, const PtrStepf prevU, const PtrStepf prevV, PtrStepf err, const int rows, const int cols) { extern __shared__ int smem[]; @@ -925,15 +728,15 @@ namespace pyrlk float x = xBase - c_halfWin_x + j + 0.5f; float y = yBase - c_halfWin_y + i + 0.5f; - I_patch[i * patchWidth + j] = tex2D(tex_If, x, y); + I_patch[i * patchWidth + j] = texI(y, x); // Scharr Deriv - dIdx_patch[i * patchWidth + j] = 3 * tex2D(tex_If, x+1, y-1) + 10 * tex2D(tex_If, x+1, y) + 3 * tex2D(tex_If, x+1, y+1) - - (3 * tex2D(tex_If, x-1, y-1) + 10 * tex2D(tex_If, x-1, y) + 3 * tex2D(tex_If, x-1, y+1)); + dIdx_patch[i * patchWidth + j] = 3 * texI(y - 1, x + 1) + 10 * texI(y, x + 1) + 3 * texI(y + 1, x + 1) - + (3 * texI(y - 1, x - 1) + 10 * texI(y, x - 1) + 3 * texI(y + 1, x - 1)); - dIdy_patch[i * patchWidth + j] = 3 * tex2D(tex_If, x-1, y+1) + 10 * tex2D(tex_If, x, y+1) + 3 * tex2D(tex_If, x+1, y+1) - - (3 * tex2D(tex_If, x-1, y-1) + 10 * tex2D(tex_If, x, y-1) + 3 * tex2D(tex_If, x+1, y-1)); + dIdy_patch[i * patchWidth + j] = 3 * texI(y + 1, x - 1) + 10 * texI(y + 1,x) + 3 * texI(y+ 1, x + 1) - + (3 * texI(y - 1, x - 1) + 10 * texI(y - 1,x) + 3 * texI(y - 1, x + 1)); } } @@ -1004,7 +807,7 @@ namespace pyrlk for (int j = 0; j < c_winSize_x; ++j) { int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j]; - int J = tex2D(tex_Jf, nextPt.x - c_halfWin_x + j + 0.5f, nextPt.y - c_halfWin_y + i + 0.5f); + int J = texJ(nextPt.y - c_halfWin_y + i + 0.5f, nextPt.x - c_halfWin_x + j + 0.5f); int diff = (J - I) * 32; @@ -1040,7 +843,8 @@ namespace pyrlk for (int j = 0; j < c_winSize_x; ++j) { int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j]; - int J = tex2D(tex_Jf, nextPt.x - c_halfWin_x + j + 0.5f, nextPt.y - c_halfWin_y + i + 0.5f); + + int J = texJ(nextPt.y - c_halfWin_y + i + 0.5f, nextPt.x - c_halfWin_x + j + 0.5f); errval += ::abs(J - I); } @@ -1109,9 +913,6 @@ namespace pyrlk { sparse_caller::call, sparse_caller::call, sparse_caller::call, sparse_caller::call, sparse_caller::call } }; - Tex_I::bindTexture_(I); - Tex_J::bindTexture_(J); - funcs[patch.y - 1][patch.x - 1](I, J, I.rows, I.cols, prevPts, nextPts, status, err, ptcount, level, block, stream); } @@ -1119,9 +920,8 @@ namespace pyrlk { dim3 block(16, 16); dim3 grid(divUp(I.cols, block.x), divUp(I.rows, block.y)); - Tex_I<1, T>::bindTexture_(I); - Tex_J<1, T>::bindTexture_(J); - + TextureLinear<1, T> texI(I); + TextureLinear<1, T> texJ(J); int2 halfWin = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2); const int patchWidth = block.x + 2 * halfWin.x; const int patchHeight = block.y + 2 * halfWin.y; @@ -1129,12 +929,12 @@ namespace pyrlk if (err.data) { - denseKernel << > >(u, v, prevU, prevV, err, I.rows, I.cols); + denseKernel><<>>(texI, texJ, u, v, prevU, prevV, err, I.rows, I.cols); cudaSafeCall(cudaGetLastError()); } else { - denseKernel << > >(u, v, prevU, prevV, PtrStepf(), I.rows, I.cols); + denseKernel><<>>(texI, texJ, u, v, prevU, prevV, PtrStepf(), I.rows, I.cols); cudaSafeCall(cudaGetLastError()); } diff --git a/modules/cudaoptflow/src/cuda/tvl1flow.cu b/modules/cudaoptflow/src/cuda/tvl1flow.cu index 7ee7b36e09..4044ec2a08 100644 --- a/modules/cudaoptflow/src/cuda/tvl1flow.cu +++ b/modules/cudaoptflow/src/cuda/tvl1flow.cu @@ -46,6 +46,7 @@ #include "opencv2/core/cuda/border_interpolate.hpp" #include "opencv2/core/cuda/limits.hpp" #include "opencv2/core/cuda.hpp" +#include using namespace cv::cuda; using namespace cv::cuda::device; @@ -102,63 +103,8 @@ namespace tvl1flow } } - struct SrcTex - { - virtual ~SrcTex() {} - - __device__ __forceinline__ virtual float I1(float x, float y) const = 0; - __device__ __forceinline__ virtual float I1x(float x, float y) const = 0; - __device__ __forceinline__ virtual float I1y(float x, float y) const = 0; - }; - - texture tex_I1 (false, cudaFilterModePoint, cudaAddressModeClamp); - texture tex_I1x(false, cudaFilterModePoint, cudaAddressModeClamp); - texture tex_I1y(false, cudaFilterModePoint, cudaAddressModeClamp); - struct SrcTexRef : SrcTex - { - __device__ __forceinline__ float I1(float x, float y) const CV_OVERRIDE - { - return tex2D(tex_I1, x, y); - } - __device__ __forceinline__ float I1x(float x, float y) const CV_OVERRIDE - { - return tex2D(tex_I1x, x, y); - } - __device__ __forceinline__ float I1y(float x, float y) const CV_OVERRIDE - { - return tex2D(tex_I1y, x, y); - } - }; - - struct SrcTexObj : SrcTex - { - __host__ SrcTexObj(cudaTextureObject_t tex_obj_I1_, cudaTextureObject_t tex_obj_I1x_, cudaTextureObject_t tex_obj_I1y_) - : tex_obj_I1(tex_obj_I1_), tex_obj_I1x(tex_obj_I1x_), tex_obj_I1y(tex_obj_I1y_) {} - - __device__ __forceinline__ float I1(float x, float y) const CV_OVERRIDE - { - return tex2D(tex_obj_I1, x, y); - } - __device__ __forceinline__ float I1x(float x, float y) const CV_OVERRIDE - { - return tex2D(tex_obj_I1x, x, y); - } - __device__ __forceinline__ float I1y(float x, float y) const CV_OVERRIDE - { - return tex2D(tex_obj_I1y, x, y); - } - - cudaTextureObject_t tex_obj_I1; - cudaTextureObject_t tex_obj_I1x; - cudaTextureObject_t tex_obj_I1y; - }; - - template < - typename T, - typename = typename std::enable_if::value>::type - > __global__ void warpBackwardKernel( - const PtrStepSzf I0, const T src, const PtrStepf u1, const PtrStepf u2, + const PtrStepSzf I0, const cv::cudev::TexturePtr I1, const cv::cudev::TexturePtr I1x, const cv::cudev::TexturePtr I1y, const PtrStepf u1, const PtrStepf u2, PtrStepf I1w, PtrStepf I1wx, PtrStepf I1wy, PtrStepf grad, PtrStepf rho) { const int x = blockIdx.x * blockDim.x + threadIdx.x; @@ -189,11 +135,9 @@ namespace tvl1flow for (int cx = xmin; cx <= xmax; ++cx) { const float w = bicubicCoeff(wx - cx) * bicubicCoeff(wy - cy); - - sum += w * src.I1(cx, cy); - sumx += w * src.I1x(cx, cy); - sumy += w * src.I1y(cx, cy); - + sum += w * I1(cy, cx); + sumx += w * I1x(cy, cx); + sumy += w * I1y(cy, cx); wsum += w; } } @@ -224,49 +168,14 @@ namespace tvl1flow PtrStepSzf I1wy, PtrStepSzf grad, PtrStepSzf rho, cudaStream_t stream) { + cv::cudev::Texture texI1(I1); + cv::cudev::Texture texI1x(I1x); + cv::cudev::Texture texI1y(I1y); const dim3 block(32, 8); const dim3 grid(divUp(I0.cols, block.x), divUp(I0.rows, block.y)); - - bool cc30 = deviceSupports(FEATURE_SET_COMPUTE_30); - - if (cc30) - { - cudaTextureDesc texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.addressMode[0] = cudaAddressModeClamp; - texDesc.addressMode[1] = cudaAddressModeClamp; - texDesc.addressMode[2] = cudaAddressModeClamp; - - cudaTextureObject_t texObj_I1 = 0, texObj_I1x = 0, texObj_I1y = 0; - - createTextureObjectPitch2D(&texObj_I1, I1, texDesc); - createTextureObjectPitch2D(&texObj_I1x, I1x, texDesc); - createTextureObjectPitch2D(&texObj_I1y, I1y, texDesc); - - warpBackwardKernel << > > (I0, SrcTexObj(texObj_I1, texObj_I1x, texObj_I1y), u1, u2, I1w, I1wx, I1wy, grad, rho); - cudaSafeCall(cudaGetLastError()); - - if (!stream) - cudaSafeCall(cudaDeviceSynchronize()); - else - cudaSafeCall(cudaStreamSynchronize(stream)); - - cudaSafeCall(cudaDestroyTextureObject(texObj_I1)); - cudaSafeCall(cudaDestroyTextureObject(texObj_I1x)); - cudaSafeCall(cudaDestroyTextureObject(texObj_I1y)); - } - else - { - bindTexture(&tex_I1, I1); - bindTexture(&tex_I1x, I1x); - bindTexture(&tex_I1y, I1y); - - warpBackwardKernel << > > (I0, SrcTexRef(), u1, u2, I1w, I1wx, I1wy, grad, rho); - cudaSafeCall(cudaGetLastError()); - - if (!stream) - cudaSafeCall(cudaDeviceSynchronize()); - } + warpBackwardKernel<<>>(I0, texI1, texI1x, texI1y , u1, u2, I1w, I1wx, I1wy, grad, rho); + if (!stream) + cudaSafeCall(cudaDeviceSynchronize()); } } diff --git a/modules/cudastereo/src/cuda/stereobm.cu b/modules/cudastereo/src/cuda/stereobm.cu index 348556060d..73df35ff63 100644 --- a/modules/cudastereo/src/cuda/stereobm.cu +++ b/modules/cudastereo/src/cuda/stereobm.cu @@ -43,8 +43,10 @@ #if !defined CUDA_DISABLER #include "opencv2/core/cuda/common.hpp" +#include #include + namespace cv { namespace cuda { namespace device { namespace stereobm @@ -601,13 +603,12 @@ namespace cv { namespace cuda { namespace device /////////////////////////////////// Textureness filtering //////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////// - texture texForTF; - - __device__ __forceinline__ float sobel(int x, int y) + __device__ __forceinline__ float sobel(cv::cudev::TexturePtr texSrc, int x, int y) { - float conv = tex2D(texForTF, x - 1, y - 1) * (-1) + tex2D(texForTF, x + 1, y - 1) * (1) + - tex2D(texForTF, x - 1, y ) * (-2) + tex2D(texForTF, x + 1, y ) * (2) + - tex2D(texForTF, x - 1, y + 1) * (-1) + tex2D(texForTF, x + 1, y + 1) * (1); + float conv = texSrc(y - 1, x - 1) * (-1) + texSrc(y - 1, x + 1) * (1) + + texSrc(y, x - 1) * (-2) + texSrc(y, x + 1) * (2) + + texSrc(y + 1, x - 1) * (-1) + texSrc(y + 1, x + 1) * (1); + return fabs(conv); } @@ -635,7 +636,7 @@ namespace cv { namespace cuda { namespace device #define RpT (2 * ROWSperTHREAD) // got experimentally - __global__ void textureness_kernel(PtrStepSzb disp, int winsz, float threshold) + __global__ void textureness_kernel(cv::cudev::TexturePtr texSrc, PtrStepSzb disp, int winsz, float threshold) { int winsz2 = winsz/2; int n_dirty_pixels = (winsz2) * 2; @@ -657,9 +658,9 @@ namespace cv { namespace cuda { namespace device for(int i = y - winsz2; i <= y + winsz2; ++i) { - sum += sobel(x - winsz2, i); + sum += sobel(texSrc, x - winsz2, i); if (cols_extra) - sum_extra += sobel(x + blockDim.x - winsz2, i); + sum_extra += sobel(texSrc, x + blockDim.x - winsz2, i); } *cols = sum; if (cols_extra) @@ -675,12 +676,12 @@ namespace cv { namespace cuda { namespace device for(int y = beg_row + 1; y < end_row; ++y) { - sum = sum - sobel(x - winsz2, y - winsz2 - 1) + sobel(x - winsz2, y + winsz2); + sum = sum - sobel(texSrc, x - winsz2, y - winsz2 - 1) + sobel(texSrc, x - winsz2, y + winsz2); *cols = sum; if (cols_extra) { - sum_extra = sum_extra - sobel(x + blockDim.x - winsz2, y - winsz2 - 1) + sobel(x + blockDim.x - winsz2, y + winsz2); + sum_extra = sum_extra - sobel(texSrc, x + blockDim.x - winsz2, y - winsz2 - 1) + sobel(texSrc, x + blockDim.x - winsz2, y + winsz2); *cols_extra = sum_extra; } @@ -697,28 +698,16 @@ namespace cv { namespace cuda { namespace device void postfilter_textureness(const PtrStepSzb& input, int winsz, float avgTexturenessThreshold, const PtrStepSzb& disp, cudaStream_t & stream) { avgTexturenessThreshold *= winsz * winsz; - - texForTF.filterMode = cudaFilterModeLinear; - texForTF.addressMode[0] = cudaAddressModeWrap; - texForTF.addressMode[1] = cudaAddressModeWrap; - - cudaChannelFormatDesc desc = cudaCreateChannelDesc(); - cudaSafeCall( cudaBindTexture2D( 0, texForTF, input.data, desc, input.cols, input.rows, input.step ) ); - + cv::cudev::Texture tex(input, false, cudaFilterModeLinear, cudaAddressModeWrap, cudaReadModeNormalizedFloat); dim3 threads(128, 1, 1); dim3 grid(1, 1, 1); - grid.x = divUp(input.cols, threads.x); grid.y = divUp(input.rows, RpT); - size_t smem_size = (threads.x + threads.x + (winsz/2) * 2 ) * sizeof(float); - textureness_kernel<<>>(disp, winsz, avgTexturenessThreshold); + textureness_kernel<<>>(tex, disp, winsz, avgTexturenessThreshold); cudaSafeCall( cudaGetLastError() ); - if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); - - cudaSafeCall( cudaUnbindTexture (texForTF) ); } } // namespace stereobm }}} // namespace cv { namespace cuda { namespace cudev diff --git a/modules/cudawarping/src/cuda/remap.cu b/modules/cudawarping/src/cuda/remap.cu index 79f155ddfb..38edf19ae2 100644 --- a/modules/cudawarping/src/cuda/remap.cu +++ b/modules/cudawarping/src/cuda/remap.cu @@ -48,6 +48,7 @@ #include "opencv2/core/cuda/vec_math.hpp" #include "opencv2/core/cuda/saturate_cast.hpp" #include "opencv2/core/cuda/filters.hpp" +#include namespace cv { namespace cuda { namespace device { @@ -77,8 +78,8 @@ namespace cv { namespace cuda { namespace device dim3 grid(divUp(dst.cols, block.x), divUp(dst.rows, block.y)); B brd(src.rows, src.cols, VecTraits::make(borderValue)); - BorderReader< PtrStep, B > brdSrc(src, brd); - Filter< BorderReader< PtrStep, B > > filter_src(brdSrc); + BorderReader, B> brdSrc(src, brd); + Filter, B>> filter_src(brdSrc); remap<<>>(filter_src, mapx, mapy, dst); cudaSafeCall( cudaGetLastError() ); @@ -98,8 +99,8 @@ namespace cv { namespace cuda { namespace device dim3 grid(divUp(dst.cols, block.x), divUp(dst.rows, block.y)); B brd(src.rows, src.cols, VecTraits::make(borderValue)); - BorderReader< PtrStep, B > brdSrc(src, brd); - Filter< BorderReader< PtrStep, B > > filter_src(brdSrc); + BorderReader, B> brdSrc(src, brd); + Filter, B>> filter_src(brdSrc); remap<<>>(filter_src, mapx, mapy, dst); cudaSafeCall( cudaGetLastError() ); @@ -108,88 +109,96 @@ namespace cv { namespace cuda { namespace device } }; - #define OPENCV_CUDA_IMPLEMENT_REMAP_TEX(type) \ - texture< type , cudaTextureType2D> tex_remap_ ## type (0, cudaFilterModePoint, cudaAddressModeClamp); \ - struct tex_remap_ ## type ## _reader \ - { \ - typedef type elem_type; \ - typedef int index_type; \ - int xoff, yoff; \ - tex_remap_ ## type ## _reader (int xoff_, int yoff_) : xoff(xoff_), yoff(yoff_) {} \ - __device__ __forceinline__ elem_type operator ()(index_type y, index_type x) const \ - { \ - return tex2D(tex_remap_ ## type , x + xoff, y + yoff); \ - } \ - }; \ - template