diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..aadc2816a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "drivers/OpenNI2-Freenect2Driver/extern/OpenNI2"] + path = drivers/OpenNI2-Freenect2Driver/extern/OpenNI2 + url = https://github.com/occipital/OpenNI2.git diff --git a/drivers/OpenNI2-Freenect2Driver/CMakeLists.txt b/drivers/OpenNI2-Freenect2Driver/CMakeLists.txt new file mode 100644 index 000000000..04dfdffe8 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/CMakeLists.txt @@ -0,0 +1,36 @@ +############################################################################## +# OpenNI2-Freenect2Driver +############################################################################## + +set(LIBFREENECT2_DIR ../..) +set(PROTONECT_DIR ${LIBFREENECT2_DIR}/examples/protonect) +set(MY_DIR ${LIBFREENECT2_DIR}/drivers/OpenNI2-Freenect2Driver) +LIST(APPEND CMAKE_MODULE_PATH ${PROTONECT_DIR}/cmake_modules) +# setup threading +INCLUDE(SetupLibfreenect2Threading) + +file(GLOB HEADERS src/*.hpp src/*.h) +file(GLOB SOURCES src/*.cpp) +add_library(Freenect2Driver SHARED ${HEADERS} ${SOURCES}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-function") + +set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib/OpenNI2-Freenect2Driver) +set_target_properties(Freenect2Driver PROPERTIES + VERSION ${PROJECT_VER} + SOVERSION ${PROJECT_APIVER} + OUTPUT_NAME Freenect2Driver) + +add_definitions(-DPROJECT_VER="${PROJECT_VER}") + +include_directories(${LIBFREENECT2_DIR}/include) +include_directories(${PROTONECT_DIR}/include) +include_directories(${MY_DIR}/extern/OpenNI2/Include) +include_directories(${MY_DIR}/src) +include_directories(${PROTONECT_DIR}/${LIBFREENECT2_THREADING_INCLUDE_DIR}) + +FIND_LIBRARY(FREENECT2_LIB freenect2 ${PROTONECT_DIR}/lib) +target_link_libraries(Freenect2Driver ${FREENECT2_LIB} ${MATH_LIB}) + +install (TARGETS Freenect2Driver + DESTINATION "${PROJECT_LIBRARY_INSTALL_DIR}/OpenNI2-Freenect2Driver") diff --git a/drivers/OpenNI2-Freenect2Driver/README.md b/drivers/OpenNI2-Freenect2Driver/README.md new file mode 100644 index 000000000..394f3558c --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/README.md @@ -0,0 +1,26 @@ +OpenNI2-Freenect2Driver +====================== + +OpenNI2-Freenect2Driver is a bridge to libfreenect2 implemented as an OpenNI2 driver. +It allows OpenNI2 to use Kinect for Windows v2 (K4W2) devices on OSX. (and on Linux?) +OpenNI2-Freenect2Driver is derived from OpenNI2-FreenectDriver (https://github.com/OpenKinect/libfreenect/tree/master/OpenNI2-FreenectDriver). + +Install +------- +1. Download and unpack [OpenNI](http://structure.io/openni) 2.2.0.33 or higher. +2. Go to the top OpenNI2-Freenect2Driver directory and build it. + + cd /some/where/libfreenect2/OpenNI2-Freenect2Driver + cmake . + make + +3. Copy the driver to your OpenNI2 driver repository. You must first change `Repository` to match your project layout. + + Repository="/example/path/to/Samples/Bin/OpenNI2/Drivers/" + cp -L lib/OpenNI2-Freenect2Driver/libFreenect2Driver.{so,dylib} ${Repository} + + # you could instead make a symlink to avoid copying after every build + # ln -s lib/OpenNI2-Freenect2Driver/libFreenect2Driver.{so,dylib} ${Repository} + +OpenNI2-Freenect2Driver is currently not built with a static libfreenect2, so you might need to include libfreenect2 when deploying. +You will need to make sure target systems have libusb and all other dependencies also. diff --git a/drivers/OpenNI2-Freenect2Driver/extern/OpenNI2 b/drivers/OpenNI2-Freenect2Driver/extern/OpenNI2 new file mode 160000 index 000000000..6857677be --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/extern/OpenNI2 @@ -0,0 +1 @@ +Subproject commit 6857677beee08e264fc5aeecb1adf647a7d616ab diff --git a/drivers/OpenNI2-Freenect2Driver/src/ColorStream.cpp b/drivers/OpenNI2-Freenect2Driver/src/ColorStream.cpp new file mode 100644 index 000000000..e209b77d6 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/ColorStream.cpp @@ -0,0 +1,130 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#include +#include "ColorStream.hpp" + +using namespace Freenect2Driver; + +// from NUI library & converted to radians +const float ColorStream::DIAGONAL_FOV = 73.9 * (M_PI / 180); +const float ColorStream::HORIZONTAL_FOV = 62 * (M_PI / 180); +const float ColorStream::VERTICAL_FOV = 48.6 * (M_PI / 180); + +ColorStream::ColorStream(libfreenect2::Freenect2Device* pDevice, Freenect2Driver::Registration *reg) : VideoStream(pDevice, reg) +{ + video_mode = makeOniVideoMode(ONI_PIXEL_FORMAT_RGB888, 1920, 1080, 30); + setVideoMode(video_mode); + pDevice->start(); +} + +// Add video modes here as you implement them +ColorStream::FreenectVideoModeMap ColorStream::getSupportedVideoModes() +{ + FreenectVideoModeMap modes; + // pixelFormat, resolutionX, resolutionY, fps freenect_video_format, freenect_resolution + modes[makeOniVideoMode(ONI_PIXEL_FORMAT_RGB888, 512, 424, 30)] = std::pair(FREENECT2_VIDEO_RGB, FREENECT2_RESOLUTION_1920x1080); + modes[makeOniVideoMode(ONI_PIXEL_FORMAT_RGB888, 1920, 1080, 30)] = std::pair(FREENECT2_VIDEO_RGB, FREENECT2_RESOLUTION_1920x1080); + + return modes; +} + +OniStatus ColorStream::setVideoMode(OniVideoMode requested_mode) +{ + FreenectVideoModeMap supported_video_modes = getSupportedVideoModes(); + FreenectVideoModeMap::const_iterator matched_mode_iter = supported_video_modes.find(requested_mode); + if (matched_mode_iter == supported_video_modes.end()) + return ONI_STATUS_NOT_SUPPORTED; + + freenect2_video_format format = matched_mode_iter->second.first; + freenect2_resolution resolution = matched_mode_iter->second.second; + + video_mode = requested_mode; + return ONI_STATUS_OK; +} + +void ColorStream::populateFrame(libfreenect2::Frame* srcFrame, int srcX, int srcY, OniFrame* dstFrame, int dstX, int dstY, int width, int height) const +{ + dstFrame->sensorType = sensor_type; + dstFrame->stride = dstFrame->width * 3; + + // copy stream buffer from freenect + switch (video_mode.pixelFormat) + { + default: + LogError("Pixel format " + to_string(video_mode.pixelFormat) + " not supported by populateFrame()"); + return; + + case ONI_PIXEL_FORMAT_RGB888: + if (reg->isEnabled()) { + libfreenect2::Frame registered(512, 424, 4); + + reg->colorFrameRGB888(srcFrame, ®istered); + + copyFrame(static_cast(registered.data), srcX, srcY, registered.width * registered.bytes_per_pixel, + static_cast(dstFrame->data), dstX, dstY, dstFrame->stride, + width, height, mirroring); + } else { + copyFrame(static_cast(srcFrame->data), srcX, srcY, srcFrame->width * srcFrame->bytes_per_pixel, + static_cast(dstFrame->data), dstX, dstY, dstFrame->stride, + width, height, mirroring); + } + return; + } +} + +void ColorStream::copyFrame(uint8_t* srcPix, int srcX, int srcY, int srcStride, uint8_t* dstPix, int dstX, int dstY, int dstStride, int width, int height, bool mirroring) +{ + srcPix += srcX + srcY * srcStride; + dstPix += dstX + dstY * dstStride; + + for (int y = 0; y < height; y++) { + uint8_t* dst = dstPix + y * dstStride; + uint8_t* src = srcPix + y * srcStride; + if (mirroring) { + dst += dstStride - 1; + for (int x = 0; x < srcStride; ++x) + { + if (x % 4 != 3) + { + *dst-- = *src++; + } + else + { + ++src; + } + } + } else { + for (int x = 0; x < dstStride-2; x += 3) + { + *dst++ = src[2]; + *dst++ = src[1]; + *dst++ = src[0]; + src += 4; + } + } + } +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/ColorStream.hpp b/drivers/OpenNI2-Freenect2Driver/src/ColorStream.hpp new file mode 100644 index 000000000..13c80a79d --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/ColorStream.hpp @@ -0,0 +1,161 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include // for transform() +#include // for M_PI +#include +#include "libfreenect2.h" +#include "libfreenect2/libfreenect2.hpp" +#include "Driver/OniDriverAPI.h" +#include "VideoStream.hpp" + + +namespace Freenect2Driver +{ + class ColorStream : public VideoStream + { + public: + // from NUI library & converted to radians + static const float DIAGONAL_FOV; + static const float HORIZONTAL_FOV; + static const float VERTICAL_FOV; + + private: + typedef std::map< OniVideoMode, std::pair > FreenectVideoModeMap; + static const OniSensorType sensor_type = ONI_SENSOR_COLOR; + + static FreenectVideoModeMap getSupportedVideoModes(); + OniStatus setVideoMode(OniVideoMode requested_mode); + void populateFrame(libfreenect2::Frame* srcFrame, int srcX, int srcY, OniFrame* dstFrame, int dstX, int dstY, int width, int height) const; + + static void copyFrame(uint8_t* srcPix, int srcX, int srcY, int srcStride, uint8_t* dstPix, int dstX, int dstY, int dstStride, int width, int height, bool mirroring); + + bool auto_white_balance; + bool auto_exposure; + + public: + ColorStream(libfreenect2::Freenect2Device* pDevice, Freenect2Driver::Registration *reg); + //~ColorStream() { } + + static OniSensorInfo getSensorInfo() + { + FreenectVideoModeMap supported_modes = getSupportedVideoModes(); + OniVideoMode* modes = new OniVideoMode[supported_modes.size()]; + std::transform(supported_modes.begin(), supported_modes.end(), modes, ExtractKey()); + OniSensorInfo sensors = { sensor_type, static_cast(supported_modes.size()), modes }; + return sensors; + } + + OniStatus setImageRegistrationMode(OniImageRegistrationMode mode) + { + if (mode == ONI_IMAGE_REGISTRATION_DEPTH_TO_COLOR) { + // XXX, switch color resolution to 512x424 for registrarion here + OniVideoMode video_mode = makeOniVideoMode(ONI_PIXEL_FORMAT_RGB888, 512, 424, 30); + setProperty(ONI_STREAM_PROPERTY_VIDEO_MODE, &video_mode, sizeof(video_mode)); + } + return ONI_STATUS_OK; + } + + // from StreamBase + OniBool isPropertySupported(int propertyId) + { + switch(propertyId) + { + default: + return VideoStream::isPropertySupported(propertyId); + + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: + case ONI_STREAM_PROPERTY_VERTICAL_FOV: + case ONI_STREAM_PROPERTY_AUTO_WHITE_BALANCE: + case ONI_STREAM_PROPERTY_AUTO_EXPOSURE: + return true; + } + } + + OniStatus getProperty(int propertyId, void* data, int* pDataSize) + { + switch (propertyId) + { + default: + return VideoStream::getProperty(propertyId, data, pDataSize); + + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: // float (radians) + { + if (*pDataSize != sizeof(float)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_HORIZONTAL_FOV"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = HORIZONTAL_FOV; + return ONI_STATUS_OK; + } + case ONI_STREAM_PROPERTY_VERTICAL_FOV: // float (radians) + { + if (*pDataSize != sizeof(float)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_VERTICAL_FOV"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = VERTICAL_FOV; + return ONI_STATUS_OK; + } + + // camera + case ONI_STREAM_PROPERTY_AUTO_WHITE_BALANCE: // OniBool + { + if (*pDataSize != sizeof(OniBool)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_AUTO_WHITE_BALANCE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = auto_white_balance; + return ONI_STATUS_OK; + } + case ONI_STREAM_PROPERTY_AUTO_EXPOSURE: // OniBool + { + if (*pDataSize != sizeof(OniBool)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_AUTO_EXPOSURE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = auto_exposure; + return ONI_STATUS_OK; + } + } + } + + OniStatus setProperty(int propertyId, const void* data, int dataSize) + { + switch (propertyId) + { + default: + return VideoStream::setProperty(propertyId, data, dataSize); + } + } + }; +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/D2S.h b/drivers/OpenNI2-Freenect2Driver/src/D2S.h new file mode 100644 index 000000000..8ca91cc8f --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/D2S.h @@ -0,0 +1,1029 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +const unsigned short D2S[] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 3, 7, 10, 13, 17, +20, 23, 27, 30, 33, 36, 40, 43, 46, 49, +52, 55, 59, 62, 65, 68, 71, 74, 77, 80, +83, 86, 88, 91, 94, 97, 100, 103, 106, 108, +111, 114, 117, 119, 122, 125, 128, 130, 133, 136, +138, 141, 143, 146, 149, 151, 154, 156, 159, 161, +164, 166, 169, 171, 174, 176, 178, 181, 183, 186, +188, 190, 193, 195, 197, 200, 202, 204, 206, 209, +211, 213, 215, 218, 220, 222, 224, 226, 229, 231, +233, 235, 237, 239, 241, 243, 245, 247, 249, 252, +254, 256, 258, 260, 262, 264, 266, 267, 269, 271, +273, 275, 277, 279, 281, 283, 285, 287, 288, 290, +292, 294, 296, 298, 299, 301, 303, 305, 307, 308, +310, 312, 314, 315, 317, 319, 321, 322, 324, 326, +327, 329, 331, 332, 334, 336, 337, 339, 341, 342, +344, 345, 347, 349, 350, 352, 353, 355, 357, 358, +360, 361, 363, 364, 366, 367, 369, 370, 372, 373, +375, 376, 378, 379, 381, 382, 383, 385, 386, 388, +389, 391, 392, 393, 395, 396, 398, 399, 400, 402, +403, 404, 406, 407, 409, 410, 411, 413, 414, 415, +416, 418, 419, 420, 422, 423, 424, 426, 427, 428, +429, 431, 432, 433, 434, 436, 437, 438, 439, 441, +442, 443, 444, 445, 447, 448, 449, 450, 451, 452, +454, 455, 456, 457, 458, 459, 461, 462, 463, 464, +465, 466, 467, 468, 470, 471, 472, 473, 474, 475, +476, 477, 478, 479, 480, 482, 483, 484, 485, 486, +487, 488, 489, 490, 491, 492, 493, 494, 495, 496, +497, 498, 499, 500, 501, 502, 503, 504, 505, 506, +507, 508, 509, 510, 511, 512, 513, 514, 515, 516, +517, 518, 519, 520, 521, 521, 522, 523, 524, 525, +526, 527, 528, 529, 530, 531, 532, 532, 533, 534, +535, 536, 537, 538, 539, 540, 540, 541, 542, 543, +544, 545, 546, 546, 547, 548, 549, 550, 551, 551, +552, 553, 554, 555, 556, 556, 557, 558, 559, 560, +561, 561, 562, 563, 564, 565, 565, 566, 567, 568, +568, 569, 570, 571, 572, 572, 573, 574, 575, 575, +576, 577, 578, 578, 579, 580, 581, 581, 582, 583, +584, 584, 585, 586, 587, 587, 588, 589, 590, 590, +591, 592, 592, 593, 594, 594, 595, 596, 597, 597, +598, 599, 599, 600, 601, 601, 602, 603, 604, 604, +605, 606, 606, 607, 608, 608, 609, 610, 610, 611, +612, 612, 613, 614, 614, 615, 615, 616, 617, 617, +618, 619, 619, 620, 621, 621, 622, 622, 623, 624, +624, 625, 626, 626, 627, 627, 628, 629, 629, 630, +631, 631, 632, 632, 633, 634, 634, 635, 635, 636, +636, 637, 638, 638, 639, 639, 640, 641, 641, 642, +642, 643, 643, 644, 645, 645, 646, 646, 647, 647, +648, 649, 649, 650, 650, 651, 651, 652, 652, 653, +654, 654, 655, 655, 656, 656, 657, 657, 658, 658, +659, 659, 660, 661, 661, 662, 662, 663, 663, 664, +664, 665, 665, 666, 666, 667, 667, 668, 668, 669, +669, 670, 670, 671, 671, 672, 672, 673, 673, 674, +674, 675, 675, 676, 676, 677, 677, 678, 678, 679, +679, 680, 680, 681, 681, 682, 682, 683, 683, 684, +684, 685, 685, 685, 686, 686, 687, 687, 688, 688, +689, 689, 690, 690, 691, 691, 691, 692, 692, 693, +693, 694, 694, 695, 695, 696, 696, 696, 697, 697, +698, 698, 699, 699, 699, 700, 700, 701, 701, 702, +702, 703, 703, 703, 704, 704, 705, 705, 706, 706, +706, 707, 707, 708, 708, 708, 709, 709, 710, 710, +711, 711, 711, 712, 712, 713, 713, 713, 714, 714, +715, 715, 715, 716, 716, 717, 717, 717, 718, 718, +719, 719, 719, 720, 720, 721, 721, 721, 722, 722, +723, 723, 723, 724, 724, 724, 725, 725, 726, 726, +726, 727, 727, 727, 728, 728, 729, 729, 729, 730, +730, 730, 731, 731, 732, 732, 732, 733, 733, 733, +734, 734, 734, 735, 735, 736, 736, 736, 737, 737, +737, 738, 738, 738, 739, 739, 739, 740, 740, 741, +741, 741, 742, 742, 742, 743, 743, 743, 744, 744, +744, 745, 745, 745, 746, 746, 746, 747, 747, 747, +748, 748, 748, 749, 749, 749, 750, 750, 750, 751, +751, 751, 752, 752, 752, 753, 753, 753, 754, 754, +754, 755, 755, 755, 756, 756, 756, 756, 757, 757, +757, 758, 758, 758, 759, 759, 759, 760, 760, 760, +761, 761, 761, 761, 762, 762, 762, 763, 763, 763, +764, 764, 764, 765, 765, 765, 765, 766, 766, 766, +767, 767, 767, 768, 768, 768, 768, 769, 769, 769, +770, 770, 770, 770, 771, 771, 771, 772, 772, 772, +773, 773, 773, 773, 774, 774, 774, 775, 775, 775, +775, 776, 776, 776, 776, 777, 777, 777, 778, 778, +778, 778, 779, 779, 779, 780, 780, 780, 780, 781, +781, 781, 781, 782, 782, 782, 783, 783, 783, 783, +784, 784, 784, 784, 785, 785, 785, 785, 786, 786, +786, 787, 787, 787, 787, 788, 788, 788, 788, 789, +789, 789, 789, 790, 790, 790, 790, 791, 791, 791, +791, 792, 792, 792, 792, 793, 793, 793, 793, 794, +794, 794, 794, 795, 795, 795, 795, 796, 796, 796, +796, 797, 797, 797, 797, 798, 798, 798, 798, 799, +799, 799, 799, 800, 800, 800, 800, 801, 801, 801, +801, 801, 802, 802, 802, 802, 803, 803, 803, 803, +804, 804, 804, 804, 805, 805, 805, 805, 805, 806, +806, 806, 806, 807, 807, 807, 807, 808, 808, 808, +808, 808, 809, 809, 809, 809, 810, 810, 810, 810, +810, 811, 811, 811, 811, 812, 812, 812, 812, 812, +813, 813, 813, 813, 813, 814, 814, 814, 814, 815, +815, 815, 815, 815, 816, 816, 816, 816, 817, 817, +817, 817, 817, 818, 818, 818, 818, 818, 819, 819, +819, 819, 819, 820, 820, 820, 820, 820, 821, 821, +821, 821, 822, 822, 822, 822, 822, 823, 823, 823, +823, 823, 824, 824, 824, 824, 824, 825, 825, 825, +825, 825, 826, 826, 826, 826, 826, 827, 827, 827, +827, 827, 828, 828, 828, 828, 828, 828, 829, 829, +829, 829, 829, 830, 830, 830, 830, 830, 831, 831, +831, 831, 831, 832, 832, 832, 832, 832, 832, 833, +833, 833, 833, 833, 834, 834, 834, 834, 834, 835, +835, 835, 835, 835, 835, 836, 836, 836, 836, 836, +837, 837, 837, 837, 837, 837, 838, 838, 838, 838, +838, 839, 839, 839, 839, 839, 839, 840, 840, 840, +840, 840, 841, 841, 841, 841, 841, 841, 842, 842, +842, 842, 842, 842, 843, 843, 843, 843, 843, 843, +844, 844, 844, 844, 844, 845, 845, 845, 845, 845, +845, 846, 846, 846, 846, 846, 846, 847, 847, 847, +847, 847, 847, 848, 848, 848, 848, 848, 848, 849, +849, 849, 849, 849, 849, 850, 850, 850, 850, 850, +850, 850, 851, 851, 851, 851, 851, 851, 852, 852, +852, 852, 852, 852, 853, 853, 853, 853, 853, 853, +854, 854, 854, 854, 854, 854, 854, 855, 855, 855, +855, 855, 855, 856, 856, 856, 856, 856, 856, 857, +857, 857, 857, 857, 857, 857, 858, 858, 858, 858, +858, 858, 858, 859, 859, 859, 859, 859, 859, 860, +860, 860, 860, 860, 860, 860, 861, 861, 861, 861, +861, 861, 861, 862, 862, 862, 862, 862, 862, 863, +863, 863, 863, 863, 863, 863, 864, 864, 864, 864, +864, 864, 864, 865, 865, 865, 865, 865, 865, 865, +866, 866, 866, 866, 866, 866, 866, 867, 867, 867, +867, 867, 867, 867, 868, 868, 868, 868, 868, 868, +868, 868, 869, 869, 869, 869, 869, 869, 869, 870, +870, 870, 870, 870, 870, 870, 871, 871, 871, 871, +871, 871, 871, 871, 872, 872, 872, 872, 872, 872, +872, 873, 873, 873, 873, 873, 873, 873, 873, 874, +874, 874, 874, 874, 874, 874, 875, 875, 875, 875, +875, 875, 875, 875, 876, 876, 876, 876, 876, 876, +876, 876, 877, 877, 877, 877, 877, 877, 877, 878, +878, 878, 878, 878, 878, 878, 878, 879, 879, 879, +879, 879, 879, 879, 879, 880, 880, 880, 880, 880, +880, 880, 880, 881, 881, 881, 881, 881, 881, 881, +881, 882, 882, 882, 882, 882, 882, 882, 882, 882, +883, 883, 883, 883, 883, 883, 883, 883, 884, 884, +884, 884, 884, 884, 884, 884, 885, 885, 885, 885, +885, 885, 885, 885, 885, 886, 886, 886, 886, 886, +886, 886, 886, 887, 887, 887, 887, 887, 887, 887, +887, 887, 888, 888, 888, 888, 888, 888, 888, 888, +888, 889, 889, 889, 889, 889, 889, 889, 889, 890, +890, 890, 890, 890, 890, 890, 890, 890, 891, 891, +891, 891, 891, 891, 891, 891, 891, 892, 892, 892, +892, 892, 892, 892, 892, 892, 893, 893, 893, 893, +893, 893, 893, 893, 893, 893, 894, 894, 894, 894, +894, 894, 894, 894, 894, 895, 895, 895, 895, 895, +895, 895, 895, 895, 896, 896, 896, 896, 896, 896, +896, 896, 896, 896, 897, 897, 897, 897, 897, 897, +897, 897, 897, 898, 898, 898, 898, 898, 898, 898, +898, 898, 898, 899, 899, 899, 899, 899, 899, 899, +899, 899, 899, 900, 900, 900, 900, 900, 900, 900, +900, 900, 900, 901, 901, 901, 901, 901, 901, 901, +901, 901, 901, 902, 902, 902, 902, 902, 902, 902, +902, 902, 902, 903, 903, 903, 903, 903, 903, 903, +903, 903, 903, 904, 904, 904, 904, 904, 904, 904, +904, 904, 904, 905, 905, 905, 905, 905, 905, 905, +905, 905, 905, 905, 906, 906, 906, 906, 906, 906, +906, 906, 906, 906, 907, 907, 907, 907, 907, 907, +907, 907, 907, 907, 907, 908, 908, 908, 908, 908, +908, 908, 908, 908, 908, 908, 909, 909, 909, 909, +909, 909, 909, 909, 909, 909, 910, 910, 910, 910, +910, 910, 910, 910, 910, 910, 910, 911, 911, 911, +911, 911, 911, 911, 911, 911, 911, 911, 911, 912, +912, 912, 912, 912, 912, 912, 912, 912, 912, 912, +913, 913, 913, 913, 913, 913, 913, 913, 913, 913, +913, 914, 914, 914, 914, 914, 914, 914, 914, 914, +914, 914, 914, 915, 915, 915, 915, 915, 915, 915, +915, 915, 915, 915, 915, 916, 916, 916, 916, 916, +916, 916, 916, 916, 916, 916, 917, 917, 917, 917, +917, 917, 917, 917, 917, 917, 917, 917, 918, 918, +918, 918, 918, 918, 918, 918, 918, 918, 918, 918, +919, 919, 919, 919, 919, 919, 919, 919, 919, 919, +919, 919, 919, 920, 920, 920, 920, 920, 920, 920, +920, 920, 920, 920, 920, 921, 921, 921, 921, 921, +921, 921, 921, 921, 921, 921, 921, 921, 922, 922, +922, 922, 922, 922, 922, 922, 922, 922, 922, 922, +923, 923, 923, 923, 923, 923, 923, 923, 923, 923, +923, 923, 923, 924, 924, 924, 924, 924, 924, 924, +924, 924, 924, 924, 924, 924, 925, 925, 925, 925, +925, 925, 925, 925, 925, 925, 925, 925, 925, 926, +926, 926, 926, 926, 926, 926, 926, 926, 926, 926, +926, 926, 926, 927, 927, 927, 927, 927, 927, 927, +927, 927, 927, 927, 927, 927, 928, 928, 928, 928, +928, 928, 928, 928, 928, 928, 928, 928, 928, 928, +929, 929, 929, 929, 929, 929, 929, 929, 929, 929, +929, 929, 929, 929, 930, 930, 930, 930, 930, 930, +930, 930, 930, 930, 930, 930, 930, 930, 931, 931, +931, 931, 931, 931, 931, 931, 931, 931, 931, 931, +931, 931, 932, 932, 932, 932, 932, 932, 932, 932, +932, 932, 932, 932, 932, 932, 933, 933, 933, 933, +933, 933, 933, 933, 933, 933, 933, 933, 933, 933, +933, 934, 934, 934, 934, 934, 934, 934, 934, 934, +934, 934, 934, 934, 934, 934, 935, 935, 935, 935, +935, 935, 935, 935, 935, 935, 935, 935, 935, 935, +935, 936, 936, 936, 936, 936, 936, 936, 936, 936, +936, 936, 936, 936, 936, 936, 937, 937, 937, 937, +937, 937, 937, 937, 937, 937, 937, 937, 937, 937, +937, 938, 938, 938, 938, 938, 938, 938, 938, 938, +938, 938, 938, 938, 938, 938, 938, 939, 939, 939, +939, 939, 939, 939, 939, 939, 939, 939, 939, 939, +939, 939, 939, 940, 940, 940, 940, 940, 940, 940, +940, 940, 940, 940, 940, 940, 940, 940, 940, 941, +941, 941, 941, 941, 941, 941, 941, 941, 941, 941, +941, 941, 941, 941, 941, 942, 942, 942, 942, 942, +942, 942, 942, 942, 942, 942, 942, 942, 942, 942, +942, 943, 943, 943, 943, 943, 943, 943, 943, 943, +943, 943, 943, 943, 943, 943, 943, 943, 944, 944, +944, 944, 944, 944, 944, 944, 944, 944, 944, 944, +944, 944, 944, 944, 944, 945, 945, 945, 945, 945, +945, 945, 945, 945, 945, 945, 945, 945, 945, 945, +945, 945, 946, 946, 946, 946, 946, 946, 946, 946, +946, 946, 946, 946, 946, 946, 946, 946, 946, 946, +947, 947, 947, 947, 947, 947, 947, 947, 947, 947, +947, 947, 947, 947, 947, 947, 947, 948, 948, 948, +948, 948, 948, 948, 948, 948, 948, 948, 948, 948, +948, 948, 948, 948, 948, 949, 949, 949, 949, 949, +949, 949, 949, 949, 949, 949, 949, 949, 949, 949, +949, 949, 949, 950, 950, 950, 950, 950, 950, 950, +950, 950, 950, 950, 950, 950, 950, 950, 950, 950, +950, 950, 951, 951, 951, 951, 951, 951, 951, 951, +951, 951, 951, 951, 951, 951, 951, 951, 951, 951, +951, 952, 952, 952, 952, 952, 952, 952, 952, 952, +952, 952, 952, 952, 952, 952, 952, 952, 952, 952, +953, 953, 953, 953, 953, 953, 953, 953, 953, 953, +953, 953, 953, 953, 953, 953, 953, 953, 953, 954, +954, 954, 954, 954, 954, 954, 954, 954, 954, 954, +954, 954, 954, 954, 954, 954, 954, 954, 955, 955, +955, 955, 955, 955, 955, 955, 955, 955, 955, 955, +955, 955, 955, 955, 955, 955, 955, 955, 956, 956, +956, 956, 956, 956, 956, 956, 956, 956, 956, 956, +956, 956, 956, 956, 956, 956, 956, 956, 956, 957, +957, 957, 957, 957, 957, 957, 957, 957, 957, 957, +957, 957, 957, 957, 957, 957, 957, 957, 957, 958, +958, 958, 958, 958, 958, 958, 958, 958, 958, 958, +958, 958, 958, 958, 958, 958, 958, 958, 958, 958, +959, 959, 959, 959, 959, 959, 959, 959, 959, 959, +959, 959, 959, 959, 959, 959, 959, 959, 959, 959, +959, 960, 960, 960, 960, 960, 960, 960, 960, 960, +960, 960, 960, 960, 960, 960, 960, 960, 960, 960, +960, 960, 960, 961, 961, 961, 961, 961, 961, 961, +961, 961, 961, 961, 961, 961, 961, 961, 961, 961, +961, 961, 961, 961, 962, 962, 962, 962, 962, 962, +962, 962, 962, 962, 962, 962, 962, 962, 962, 962, +962, 962, 962, 962, 962, 962, 962, 963, 963, 963, +963, 963, 963, 963, 963, 963, 963, 963, 963, 963, +963, 963, 963, 963, 963, 963, 963, 963, 963, 964, +964, 964, 964, 964, 964, 964, 964, 964, 964, 964, +964, 964, 964, 964, 964, 964, 964, 964, 964, 964, +964, 964, 965, 965, 965, 965, 965, 965, 965, 965, +965, 965, 965, 965, 965, 965, 965, 965, 965, 965, +965, 965, 965, 965, 965, 966, 966, 966, 966, 966, +966, 966, 966, 966, 966, 966, 966, 966, 966, 966, +966, 966, 966, 966, 966, 966, 966, 966, 966, 967, +967, 967, 967, 967, 967, 967, 967, 967, 967, 967, +967, 967, 967, 967, 967, 967, 967, 967, 967, 967, +967, 967, 967, 968, 968, 968, 968, 968, 968, 968, +968, 968, 968, 968, 968, 968, 968, 968, 968, 968, +968, 968, 968, 968, 968, 968, 968, 968, 969, 969, +969, 969, 969, 969, 969, 969, 969, 969, 969, 969, +969, 969, 969, 969, 969, 969, 969, 969, 969, 969, +969, 969, 969, 970, 970, 970, 970, 970, 970, 970, +970, 970, 970, 970, 970, 970, 970, 970, 970, 970, +970, 970, 970, 970, 970, 970, 970, 970, 971, 971, +971, 971, 971, 971, 971, 971, 971, 971, 971, 971, +971, 971, 971, 971, 971, 971, 971, 971, 971, 971, +971, 971, 971, 971, 972, 972, 972, 972, 972, 972, +972, 972, 972, 972, 972, 972, 972, 972, 972, 972, +972, 972, 972, 972, 972, 972, 972, 972, 972, 972, +973, 973, 973, 973, 973, 973, 973, 973, 973, 973, +973, 973, 973, 973, 973, 973, 973, 973, 973, 973, +973, 973, 973, 973, 973, 973, 973, 974, 974, 974, +974, 974, 974, 974, 974, 974, 974, 974, 974, 974, +974, 974, 974, 974, 974, 974, 974, 974, 974, 974, +974, 974, 974, 974, 975, 975, 975, 975, 975, 975, +975, 975, 975, 975, 975, 975, 975, 975, 975, 975, +975, 975, 975, 975, 975, 975, 975, 975, 975, 975, +975, 975, 976, 976, 976, 976, 976, 976, 976, 976, +976, 976, 976, 976, 976, 976, 976, 976, 976, 976, +976, 976, 976, 976, 976, 976, 976, 976, 976, 976, +977, 977, 977, 977, 977, 977, 977, 977, 977, 977, +977, 977, 977, 977, 977, 977, 977, 977, 977, 977, +977, 977, 977, 977, 977, 977, 977, 977, 977, 978, +978, 978, 978, 978, 978, 978, 978, 978, 978, 978, +978, 978, 978, 978, 978, 978, 978, 978, 978, 978, +978, 978, 978, 978, 978, 978, 978, 978, 979, 979, +979, 979, 979, 979, 979, 979, 979, 979, 979, 979, +979, 979, 979, 979, 979, 979, 979, 979, 979, 979, +979, 979, 979, 979, 979, 979, 979, 979, 980, 980, +980, 980, 980, 980, 980, 980, 980, 980, 980, 980, +980, 980, 980, 980, 980, 980, 980, 980, 980, 980, +980, 980, 980, 980, 980, 980, 980, 980, 981, 981, +981, 981, 981, 981, 981, 981, 981, 981, 981, 981, +981, 981, 981, 981, 981, 981, 981, 981, 981, 981, +981, 981, 981, 981, 981, 981, 981, 981, 981, 982, +982, 982, 982, 982, 982, 982, 982, 982, 982, 982, +982, 982, 982, 982, 982, 982, 982, 982, 982, 982, +982, 982, 982, 982, 982, 982, 982, 982, 982, 982, +983, 983, 983, 983, 983, 983, 983, 983, 983, 983, +983, 983, 983, 983, 983, 983, 983, 983, 983, 983, +983, 983, 983, 983, 983, 983, 983, 983, 983, 983, +983, 983, 983, 984, 984, 984, 984, 984, 984, 984, +984, 984, 984, 984, 984, 984, 984, 984, 984, 984, +984, 984, 984, 984, 984, 984, 984, 984, 984, 984, +984, 984, 984, 984, 984, 985, 985, 985, 985, 985, +985, 985, 985, 985, 985, 985, 985, 985, 985, 985, +985, 985, 985, 985, 985, 985, 985, 985, 985, 985, +985, 985, 985, 985, 985, 985, 985, 985, 985, 986, +986, 986, 986, 986, 986, 986, 986, 986, 986, 986, +986, 986, 986, 986, 986, 986, 986, 986, 986, 986, +986, 986, 986, 986, 986, 986, 986, 986, 986, 986, +986, 986, 986, 987, 987, 987, 987, 987, 987, 987, +987, 987, 987, 987, 987, 987, 987, 987, 987, 987, +987, 987, 987, 987, 987, 987, 987, 987, 987, 987, +987, 987, 987, 987, 987, 987, 987, 987, 988, 988, +988, 988, 988, 988, 988, 988, 988, 988, 988, 988, +988, 988, 988, 988, 988, 988, 988, 988, 988, 988, +988, 988, 988, 988, 988, 988, 988, 988, 988, 988, +988, 988, 988, 989, 989, 989, 989, 989, 989, 989, +989, 989, 989, 989, 989, 989, 989, 989, 989, 989, +989, 989, 989, 989, 989, 989, 989, 989, 989, 989, +989, 989, 989, 989, 989, 989, 989, 989, 989, 990, +990, 990, 990, 990, 990, 990, 990, 990, 990, 990, +990, 990, 990, 990, 990, 990, 990, 990, 990, 990, +990, 990, 990, 990, 990, 990, 990, 990, 990, 990, +990, 990, 990, 990, 990, 990, 991, 991, 991, 991, +991, 991, 991, 991, 991, 991, 991, 991, 991, 991, +991, 991, 991, 991, 991, 991, 991, 991, 991, 991, +991, 991, 991, 991, 991, 991, 991, 991, 991, 991, +991, 991, 991, 991, 992, 992, 992, 992, 992, 992, +992, 992, 992, 992, 992, 992, 992, 992, 992, 992, +992, 992, 992, 992, 992, 992, 992, 992, 992, 992, +992, 992, 992, 992, 992, 992, 992, 992, 992, 992, +992, 992, 992, 993, 993, 993, 993, 993, 993, 993, +993, 993, 993, 993, 993, 993, 993, 993, 993, 993, +993, 993, 993, 993, 993, 993, 993, 993, 993, 993, +993, 993, 993, 993, 993, 993, 993, 993, 993, 993, +993, 993, 994, 994, 994, 994, 994, 994, 994, 994, +994, 994, 994, 994, 994, 994, 994, 994, 994, 994, +994, 994, 994, 994, 994, 994, 994, 994, 994, 994, +994, 994, 994, 994, 994, 994, 994, 994, 994, 994, +994, 994, 995, 995, 995, 995, 995, 995, 995, 995, +995, 995, 995, 995, 995, 995, 995, 995, 995, 995, +995, 995, 995, 995, 995, 995, 995, 995, 995, 995, +995, 995, 995, 995, 995, 995, 995, 995, 995, 995, +995, 995, 995, 995, 996, 996, 996, 996, 996, 996, +996, 996, 996, 996, 996, 996, 996, 996, 996, 996, +996, 996, 996, 996, 996, 996, 996, 996, 996, 996, +996, 996, 996, 996, 996, 996, 996, 996, 996, 996, +996, 996, 996, 996, 996, 996, 997, 997, 997, 997, +997, 997, 997, 997, 997, 997, 997, 997, 997, 997, +997, 997, 997, 997, 997, 997, 997, 997, 997, 997, +997, 997, 997, 997, 997, 997, 997, 997, 997, 997, +997, 997, 997, 997, 997, 997, 997, 997, 997, 998, +998, 998, 998, 998, 998, 998, 998, 998, 998, 998, +998, 998, 998, 998, 998, 998, 998, 998, 998, 998, +998, 998, 998, 998, 998, 998, 998, 998, 998, 998, +998, 998, 998, 998, 998, 998, 998, 998, 998, 998, +998, 998, 998, 999, 999, 999, 999, 999, 999, 999, +999, 999, 999, 999, 999, 999, 999, 999, 999, 999, +999, 999, 999, 999, 999, 999, 999, 999, 999, 999, +999, 999, 999, 999, 999, 999, 999, 999, 999, 999, +999, 999, 999, 999, 999, 999, 999, 999, 1000, 1000, +1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, +1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, +1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, +1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, +1000, 1000, 1000, 1000, 1001, 1001, 1001, 1001, 1001, 1001, +1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, +1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, +1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, +1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, +1001, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, +1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, +1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, +1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, +1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1003, +1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, +1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, +1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, +1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, +1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1004, +1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, +1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, +1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, +1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, +1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1004, +1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, +1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, +1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, +1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, +1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, +1005, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, +1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, +1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, +1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, +1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, +1006, 1006, 1006, 1006, 1006, 1007, 1007, 1007, 1007, 1007, +1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, +1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, +1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, +1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, +1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1008, +1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, +1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, +1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, +1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, +1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, +1008, 1008, 1008, 1008, 1008, 1009, 1009, 1009, 1009, 1009, +1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, +1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, +1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, +1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, +1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, +1009, 1009, 1009, 1010, 1010, 1010, 1010, 1010, 1010, 1010, +1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, +1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, +1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, +1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, +1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, +1010, 1010, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, +1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, +1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, +1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, +1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, +1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, +1011, 1011, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, +1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, +1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, +1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, +1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, +1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, +1012, 1012, 1012, 1012, 1012, 1013, 1013, 1013, 1013, 1013, +1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, +1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, +1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, +1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, +1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, +1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1014, 1014, +1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, +1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, +1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, +1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, +1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, +1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, +1014, 1014, 1014, 1014, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, +1015, 1015, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, +1016, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, +1017, 1017, 1017, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, +1018, 1018, 1018, 1018, 1018, 1018, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, +1019, 1019, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, +1021, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, +1022, 1022, 1022, 1022, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, +1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, +1026, 1026, 1026, 1026, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, +1027, 1027, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, +1028, 1028, 1028, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, +1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, +1030, 1030, 1030, 1030, 1030, 1030, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, +1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, +1032, 1032, 1032, 1032, 1032, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, +1033, 1033, 1033, 1033, 1033, 1033, 1033, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, +1034, 1034, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, +1035, 1035, 1035, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, +1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, +1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, +1039, 1039, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, +1040, 1040, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, +1042, 1042, 1042, 1042, 1042, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, +1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, +1044, 1044, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, +1045, 1045, 1045, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, +1046, 1046, 1046, 1046, 1046, 1046, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, +1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, +1048, 1048, 1048, 1048, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, +1049, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, +1050, 1050, 1050, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, +1052}; + diff --git a/drivers/OpenNI2-Freenect2Driver/src/DepthStream.cpp b/drivers/OpenNI2-Freenect2Driver/src/DepthStream.cpp new file mode 100644 index 000000000..deb2a237c --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/DepthStream.cpp @@ -0,0 +1,96 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#include +#include "DepthStream.hpp" + +using namespace Freenect2Driver; + +// from NUI library and converted to radians +const float DepthStream::DIAGONAL_FOV = 70 * (M_PI / 180); +const float DepthStream::HORIZONTAL_FOV = 58.5 * (M_PI / 180); +const float DepthStream::VERTICAL_FOV = 45.6 * (M_PI / 180); +// from DepthKinectStream.cpp +const int DepthStream::MAX_VALUE; +const unsigned long long DepthStream::GAIN_VAL; +const unsigned long long DepthStream::CONST_SHIFT_VAL; +const unsigned long long DepthStream::MAX_SHIFT_VAL; +const unsigned long long DepthStream::PARAM_COEFF_VAL; +const unsigned long long DepthStream::SHIFT_SCALE_VAL; +const unsigned long long DepthStream::ZERO_PLANE_DISTANCE_VAL; +const double DepthStream::ZERO_PLANE_PIXEL_SIZE_VAL = 0.10520000010728836; +const double DepthStream::EMITTER_DCMOS_DISTANCE_VAL = 7.5; + +DepthStream::DepthStream(libfreenect2::Freenect2Device* pDevice, Freenect2Driver::Registration *reg) : VideoStream(pDevice, reg) +{ + //video_mode = makeOniVideoMode(ONI_PIXEL_FORMAT_DEPTH_1_MM, 512, 424, 30); + video_mode = makeOniVideoMode(ONI_PIXEL_FORMAT_DEPTH_1_MM, 640, 480, 30); + setVideoMode(video_mode); + setImageRegistrationMode(ONI_IMAGE_REGISTRATION_OFF); + pDevice->start(); +} + +// Add video modes here as you implement them +// Note: if image_registration_mode == ONI_IMAGE_REGISTRATION_DEPTH_TO_COLOR, +// setVideoFormat() will try FREENECT_DEPTH_REGISTERED first then fall back on what is set here. +DepthStream::FreenectDepthModeMap DepthStream::getSupportedVideoModes() +{ + FreenectDepthModeMap modes; + // pixelFormat, resolutionX, resolutionY, fps + modes[makeOniVideoMode(ONI_PIXEL_FORMAT_DEPTH_1_MM, 640, 480, 30)] = std::pair(FREENECT2_DEPTH_MM, FREENECT2_RESOLUTION_512x424); + modes[makeOniVideoMode(ONI_PIXEL_FORMAT_DEPTH_1_MM, 512, 424, 30)] = std::pair(FREENECT2_DEPTH_MM, FREENECT2_RESOLUTION_512x424); + + return modes; +} + +OniStatus DepthStream::setVideoMode(OniVideoMode requested_mode) +{ + FreenectDepthModeMap supported_video_modes = getSupportedVideoModes(); + FreenectDepthModeMap::const_iterator matched_mode_iter = supported_video_modes.find(requested_mode); + if (matched_mode_iter == supported_video_modes.end()) + return ONI_STATUS_NOT_SUPPORTED; + + video_mode = requested_mode; + return ONI_STATUS_OK; +} + +void DepthStream::populateFrame(libfreenect2::Frame* srcFrame, int srcX, int srcY, OniFrame* dstFrame, int dstX, int dstY, int width, int height) const +{ + dstFrame->sensorType = sensor_type; + dstFrame->stride = dstFrame->width * sizeof(uint16_t); + + // XXX, save depth map for registration + if (reg->isEnabled()) + reg->depthFrame(srcFrame); + + if (srcFrame->width < dstFrame->width || srcFrame->height < dstFrame->height) + memset(dstFrame->data, 0x00, dstFrame->width * dstFrame->height * 2); + + // copy stream buffer from freenect + copyFrame(static_cast((void*)srcFrame->data), srcX, srcY, srcFrame->width, + static_cast(dstFrame->data), dstX, dstY, dstFrame->width, + width, height, mirroring); +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/DepthStream.hpp b/drivers/OpenNI2-Freenect2Driver/src/DepthStream.hpp new file mode 100644 index 000000000..fd68a761b --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/DepthStream.hpp @@ -0,0 +1,237 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include // for transform() +#include // for M_PI +#include // for memcpy +#include "libfreenect2/libfreenect2.hpp" +#include "Driver/OniDriverAPI.h" +#include "PS1080.h" +#include "VideoStream.hpp" +#include "S2D.h" +#include "D2S.h" + + +namespace Freenect2Driver +{ + class DepthStream : public VideoStream + { + public: + // from NUI library and converted to radians + static const float DIAGONAL_FOV; + static const float HORIZONTAL_FOV; + static const float VERTICAL_FOV; + // from DepthKinectStream.cpp + static const int MAX_VALUE = 10000; + static const unsigned long long GAIN_VAL = 42; + static const unsigned long long CONST_SHIFT_VAL = 200; + static const unsigned long long MAX_SHIFT_VAL = 2047; + static const unsigned long long PARAM_COEFF_VAL = 4; + static const unsigned long long SHIFT_SCALE_VAL = 10; + static const unsigned long long ZERO_PLANE_DISTANCE_VAL = 120; + static const double ZERO_PLANE_PIXEL_SIZE_VAL; + static const double EMITTER_DCMOS_DISTANCE_VAL; + + private: + typedef std::map< OniVideoMode, std::pair > FreenectDepthModeMap; + static const OniSensorType sensor_type = ONI_SENSOR_DEPTH; + OniImageRegistrationMode image_registration_mode; + + static FreenectDepthModeMap getSupportedVideoModes(); + OniStatus setVideoMode(OniVideoMode requested_mode); + void populateFrame(libfreenect2::Frame* srcFrame, int srcX, int srcY, OniFrame* dstFrame, int dstX, int dstY, int width, int height) const; + + public: + DepthStream(libfreenect2::Freenect2Device* pDevice, Freenect2Driver::Registration *reg); + //~DepthStream() { } + + static OniSensorInfo getSensorInfo() + { + FreenectDepthModeMap supported_modes = getSupportedVideoModes(); + OniVideoMode* modes = new OniVideoMode[supported_modes.size()]; + std::transform(supported_modes.begin(), supported_modes.end(), modes, ExtractKey()); + OniSensorInfo sensors = { sensor_type, static_cast(supported_modes.size()), modes }; + return sensors; + } + + OniImageRegistrationMode getImageRegistrationMode() const { return image_registration_mode; } + OniStatus setImageRegistrationMode(OniImageRegistrationMode mode) + { + if (!isImageRegistrationModeSupported(mode)) + return ONI_STATUS_NOT_SUPPORTED; + image_registration_mode = mode; + reg->setEnable(image_registration_mode == ONI_IMAGE_REGISTRATION_DEPTH_TO_COLOR); + return setVideoMode(video_mode); + } + + // from StreamBase + OniBool isImageRegistrationModeSupported(OniImageRegistrationMode mode) { return (mode == ONI_IMAGE_REGISTRATION_OFF || mode == ONI_IMAGE_REGISTRATION_DEPTH_TO_COLOR); } + + OniBool isPropertySupported(int propertyId) + { + switch(propertyId) + { + default: + return VideoStream::isPropertySupported(propertyId); + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: + case ONI_STREAM_PROPERTY_VERTICAL_FOV: + case ONI_STREAM_PROPERTY_MAX_VALUE: + case XN_STREAM_PROPERTY_GAIN: + case XN_STREAM_PROPERTY_CONST_SHIFT: + case XN_STREAM_PROPERTY_MAX_SHIFT: + case XN_STREAM_PROPERTY_PARAM_COEFF: + case XN_STREAM_PROPERTY_SHIFT_SCALE: + case XN_STREAM_PROPERTY_ZERO_PLANE_DISTANCE: + case XN_STREAM_PROPERTY_ZERO_PLANE_PIXEL_SIZE: + case XN_STREAM_PROPERTY_EMITTER_DCMOS_DISTANCE: + case XN_STREAM_PROPERTY_S2D_TABLE: + case XN_STREAM_PROPERTY_D2S_TABLE: + return true; + } + } + + OniStatus getProperty(int propertyId, void* data, int* pDataSize) + { + switch (propertyId) + { + default: + return VideoStream::getProperty(propertyId, data, pDataSize); + + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: // float (radians) + if (*pDataSize != sizeof(float)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_HORIZONTAL_FOV"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = HORIZONTAL_FOV; + return ONI_STATUS_OK; + case ONI_STREAM_PROPERTY_VERTICAL_FOV: // float (radians) + if (*pDataSize != sizeof(float)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_VERTICAL_FOV"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = VERTICAL_FOV; + return ONI_STATUS_OK; + case ONI_STREAM_PROPERTY_MAX_VALUE: // int + if (*pDataSize != sizeof(int)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_MAX_VALUE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = MAX_VALUE; + return ONI_STATUS_OK; + + case XN_STREAM_PROPERTY_PIXEL_REGISTRATION: // XnPixelRegistration (get only) + case XN_STREAM_PROPERTY_WHITE_BALANCE_ENABLED: // unsigned long long + case XN_STREAM_PROPERTY_HOLE_FILTER: // unsigned long long + case XN_STREAM_PROPERTY_REGISTRATION_TYPE: // XnProcessingType + case XN_STREAM_PROPERTY_AGC_BIN: // XnDepthAGCBin* + case XN_STREAM_PROPERTY_PIXEL_SIZE_FACTOR: // unsigned long long + case XN_STREAM_PROPERTY_DCMOS_RCMOS_DISTANCE: // double + case XN_STREAM_PROPERTY_CLOSE_RANGE: // unsigned long long + return ONI_STATUS_NOT_SUPPORTED; + + case XN_STREAM_PROPERTY_GAIN: // unsigned long long + if (*pDataSize != sizeof(unsigned long long)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_GAIN"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = GAIN_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_CONST_SHIFT: // unsigned long long + if (*pDataSize != sizeof(unsigned long long)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_CONST_SHIFT"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = CONST_SHIFT_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_MAX_SHIFT: // unsigned long long + if (*pDataSize != sizeof(unsigned long long)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_MAX_SHIFT"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = MAX_SHIFT_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_PARAM_COEFF: // unsigned long long + if (*pDataSize != sizeof(unsigned long long)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_PARAM_COEFF"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = PARAM_COEFF_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_SHIFT_SCALE: // unsigned long long + if (*pDataSize != sizeof(unsigned long long)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_SHIFT_SCALE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = SHIFT_SCALE_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_ZERO_PLANE_DISTANCE: // unsigned long long + if (*pDataSize != sizeof(unsigned long long)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_ZERO_PLANE_DISTANCE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = ZERO_PLANE_DISTANCE_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_ZERO_PLANE_PIXEL_SIZE: // double + if (*pDataSize != sizeof(double)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_ZERO_PLANE_PIXEL_SIZE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = ZERO_PLANE_PIXEL_SIZE_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_EMITTER_DCMOS_DISTANCE: // double + if (*pDataSize != sizeof(double)) + { + LogError("Unexpected size for XN_STREAM_PROPERTY_EMITTER_DCMOS_DISTANCE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = EMITTER_DCMOS_DISTANCE_VAL; + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_S2D_TABLE: // OniDepthPixel[] + *pDataSize = sizeof(S2D); + //std::copy(S2D, S2D+1, static_cast(data)); + memcpy(data, S2D, *pDataSize); + return ONI_STATUS_OK; + case XN_STREAM_PROPERTY_D2S_TABLE: // unsigned short[] + *pDataSize = sizeof(D2S); + //std::copy(D2S, D2S+1, static_cast(data)); + memcpy(data, D2S, *pDataSize); + return ONI_STATUS_OK; + } + } + }; +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/DeviceDriver.cpp b/drivers/OpenNI2-Freenect2Driver/src/DeviceDriver.cpp new file mode 100644 index 000000000..b4119c327 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/DeviceDriver.cpp @@ -0,0 +1,538 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ +/** +* FreenectDriver +* Copyright 2013 Benn Snyder +*/ + +#include +#include +#include +#include "Driver/OniDriverAPI.h" +#include "libfreenect2/libfreenect2.hpp" +#include +#include +#include +#include "DepthStream.hpp" +#include "ColorStream.hpp" +#include "IrStream.hpp" + + +namespace Freenect2Driver +{ + typedef std::map ConfigStrings; + + class Device : public oni::driver::DeviceBase + { + private: + libfreenect2::Freenect2Device *dev; + ColorStream* color; + DepthStream* depth; + IrStream* ir; + Registration *reg; + ConfigStrings config; + bool device_stop; + libfreenect2::SyncMultiFrameListener listener; + libfreenect2::thread* thread; + + static void static_run(void* cookie) + { + static_cast(cookie)->run(); + } + + VideoStream* getStream(libfreenect2::Frame::Type type) + { + if (type == libfreenect2::Frame::Depth) + return depth; + if (type == libfreenect2::Frame::Ir) + return ir; + if (type == libfreenect2::Frame::Color) + return color; + return NULL; + } + + void run() + { + libfreenect2::FrameMap frames; + uint32_t seqNum = 0; + libfreenect2::Frame::Type seqType; + + struct streams { + const char* name; + libfreenect2::Frame::Type type; + } streams[] = { + { "Ir", libfreenect2::Frame::Ir }, + { "Depth", libfreenect2::Frame::Depth }, + { "Color", libfreenect2::Frame::Color } + }; + while(!device_stop) + { + listener.waitForNewFrame(frames); + + for (int i = 0; i < sizeof(streams)/sizeof(*streams); i++) { + struct streams& s = streams[i]; + VideoStream* stream = getStream(s.type); + libfreenect2::Frame *frame = frames[s.type]; + if (stream) { + if (seqNum == 0) + seqType = s.type; + if (s.type == seqType) + seqNum++; + frame->timestamp = seqNum * 33369; + stream->buildFrame(frame); + } + } + + listener.release(frames); + } + } + + OniStatus setStreamProperties(VideoStream* stream, std::string pfx) + { + pfx += '-'; + OniStatus res = ONI_STATUS_OK, tmp_res; + if (config.find(pfx + "size") != config.end()) { + WriteMessage("setStreamProperty: " + pfx + "size: " + config[pfx + "size"]); + std::string size(config[pfx + "size"]); + int i = size.find("x"); + OniVideoMode video_mode = makeOniVideoMode(ONI_PIXEL_FORMAT_DEPTH_1_MM, std::stoi(size.substr(0, i)), std::stoi(size.substr(i + 1)), 30); + tmp_res = stream->setProperty(ONI_STREAM_PROPERTY_VIDEO_MODE, (void*)&video_mode, sizeof(video_mode)); + if (tmp_res != ONI_STATUS_OK) + res = tmp_res; + } + + return res; + } + + public: + Device(freenect2_context* fn_ctx, int index) : //libfreenect2::Freenect2Device(fn_ctx, index), + dev(NULL), + reg(NULL), + color(NULL), + ir(NULL), + depth(NULL), + device_stop(false), + listener(libfreenect2::Frame::Depth | libfreenect2::Frame::Ir | libfreenect2::Frame::Color), + thread(NULL) + { + thread = new libfreenect2::thread(&Device::static_run, this); + } + ~Device() + { + close(); + destroyStream(color); + destroyStream(ir); + destroyStream(depth); + if (reg) { + delete reg; + reg = NULL; + } + } + + // for Freenect2Device + void setFreenect2Device(libfreenect2::Freenect2Device *dev) { + this->dev = dev; + dev->setColorFrameListener(&listener); + dev->setIrAndDepthFrameListener(&listener); + reg = new Registration(dev); + } + void setConfigStrings(ConfigStrings& config) { + this->config = config; + } + void start() { + //TODO: start thread executing the run() method + //device_stop = false; + //thread = new libfreenect2::thread(&Device::static_run, this); + dev->start(); + } + void stop() { + device_stop = true; + thread->join(); + + dev->stop(); + } + void close() { + stop(); + dev->close(); + } + + // for DeviceBase + + OniBool isImageRegistrationModeSupported(OniImageRegistrationMode mode) { return depth->isImageRegistrationModeSupported(mode); } + + OniStatus getSensorInfoList(OniSensorInfo** pSensors, int* numSensors) + { + *numSensors = 3; + OniSensorInfo * sensors = new OniSensorInfo[*numSensors]; + sensors[0] = depth->getSensorInfo(); + sensors[1] = color->getSensorInfo(); + sensors[2] = ir->getSensorInfo(); + *pSensors = sensors; + return ONI_STATUS_OK; + } + + oni::driver::StreamBase* createStream(OniSensorType sensorType) + { + switch (sensorType) + { + default: + LogError("Cannot create a stream of type " + to_string(sensorType)); + return NULL; + case ONI_SENSOR_COLOR: + if (! color) { + color = new ColorStream(dev, reg); + setStreamProperties(color, "color"); + } + return color; + case ONI_SENSOR_DEPTH: + if (! depth) { + depth = new DepthStream(dev, reg); + setStreamProperties(depth, "depth"); + } + return depth; + case ONI_SENSOR_IR: + if (! ir) { + ir = new IrStream(dev, reg); + setStreamProperties(ir, "ir"); + } + return ir; + } + } + + void destroyStream(oni::driver::StreamBase* pStream) + { + if (pStream == NULL) + return; + + // stop them all + dev->stop(); + if (pStream == color) + { + delete color; + color = NULL; + } + if (pStream == depth) + { + delete depth; + depth = NULL; + } + if (pStream == ir) + { + delete ir; + ir = NULL; + } + } + + // todo: fill out properties + OniBool isPropertySupported(int propertyId) + { + if (propertyId == ONI_DEVICE_PROPERTY_IMAGE_REGISTRATION) + return true; + return false; + } + + OniStatus getProperty(int propertyId, void* data, int* pDataSize) + { + switch (propertyId) + { + default: + case ONI_DEVICE_PROPERTY_FIRMWARE_VERSION: // string + case ONI_DEVICE_PROPERTY_DRIVER_VERSION: // OniVersion + case ONI_DEVICE_PROPERTY_HARDWARE_VERSION: // int + case ONI_DEVICE_PROPERTY_SERIAL_NUMBER: // string + case ONI_DEVICE_PROPERTY_ERROR_STATE: // ? + // files + case ONI_DEVICE_PROPERTY_PLAYBACK_SPEED: // float + case ONI_DEVICE_PROPERTY_PLAYBACK_REPEAT_ENABLED: // OniBool + // xn + case XN_MODULE_PROPERTY_USB_INTERFACE: // XnSensorUsbInterface + case XN_MODULE_PROPERTY_MIRROR: // bool + case XN_MODULE_PROPERTY_RESET_SENSOR_ON_STARTUP: // unsigned long long + case XN_MODULE_PROPERTY_LEAN_INIT: // unsigned long long + case XN_MODULE_PROPERTY_SERIAL_NUMBER: // unsigned long long + case XN_MODULE_PROPERTY_VERSION: // XnVersions + return ONI_STATUS_NOT_SUPPORTED; + + case ONI_DEVICE_PROPERTY_IMAGE_REGISTRATION: // OniImageRegistrationMode + if (*pDataSize != sizeof(OniImageRegistrationMode)) + { + LogError("Unexpected size for ONI_DEVICE_PROPERTY_IMAGE_REGISTRATION"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = depth->getImageRegistrationMode(); + return ONI_STATUS_OK; + } + } + + OniStatus setProperty(int propertyId, const void* data, int dataSize) + { + switch (propertyId) + { + default: + case ONI_DEVICE_PROPERTY_FIRMWARE_VERSION: // By implementation + case ONI_DEVICE_PROPERTY_DRIVER_VERSION: // OniVersion + case ONI_DEVICE_PROPERTY_HARDWARE_VERSION: // int + case ONI_DEVICE_PROPERTY_SERIAL_NUMBER: // string + case ONI_DEVICE_PROPERTY_ERROR_STATE: // ? + // files + case ONI_DEVICE_PROPERTY_PLAYBACK_SPEED: // float + case ONI_DEVICE_PROPERTY_PLAYBACK_REPEAT_ENABLED: // OniBool + // xn + case XN_MODULE_PROPERTY_USB_INTERFACE: // XnSensorUsbInterface + case XN_MODULE_PROPERTY_MIRROR: // bool + case XN_MODULE_PROPERTY_RESET_SENSOR_ON_STARTUP: // unsigned long long + case XN_MODULE_PROPERTY_LEAN_INIT: // unsigned long long + case XN_MODULE_PROPERTY_SERIAL_NUMBER: // unsigned long long + case XN_MODULE_PROPERTY_VERSION: // XnVersions + // xn commands + case XN_MODULE_PROPERTY_FIRMWARE_PARAM: // XnInnerParam + case XN_MODULE_PROPERTY_RESET: // unsigned long long + case XN_MODULE_PROPERTY_IMAGE_CONTROL: // XnControlProcessingData + case XN_MODULE_PROPERTY_DEPTH_CONTROL: // XnControlProcessingData + case XN_MODULE_PROPERTY_AHB: // XnAHBData + case XN_MODULE_PROPERTY_LED_STATE: // XnLedState + return ONI_STATUS_NOT_SUPPORTED; + + case ONI_DEVICE_PROPERTY_IMAGE_REGISTRATION: // OniImageRegistrationMode + if (dataSize != sizeof(OniImageRegistrationMode)) + { + LogError("Unexpected size for ONI_DEVICE_PROPERTY_IMAGE_REGISTRATION"); + return ONI_STATUS_ERROR; + } + OniImageRegistrationMode mode = *(static_cast(data)); + color->setImageRegistrationMode(mode); + return depth->setImageRegistrationMode(mode); + } + } + + OniBool isCommandSupported(int commandId) + { + switch (commandId) + { + default: + case ONI_DEVICE_COMMAND_SEEK: + return false; + } + } + + OniStatus invoke(int commandId, void* data, int dataSize) + { + switch (commandId) + { + default: + case ONI_DEVICE_COMMAND_SEEK: // OniSeek + return ONI_STATUS_NOT_SUPPORTED; + } + } + + + /* todo: for DeviceBase + virtual OniStatus tryManualTrigger() {return ONI_STATUS_OK;} + */ + }; + + + class Driver : public oni::driver::DriverBase + { + private: + typedef std::map OniDeviceMap; + OniDeviceMap devices; + std::string uriScheme; + ConfigStrings config; + libfreenect2::Freenect2 freenect2; + + std::string devid_to_uri(int id) { + return uriScheme + "://" + to_string(id); + } + + int uri_to_devid(const std::string uri) { + int id; + std::istringstream is(uri); + is.seekg((uriScheme + "://").length()); + is >> id; + return id; + } + + void register_uri(std::string uri) { + OniDeviceInfo info; + strncpy(info.uri, uri.c_str(), ONI_MAX_STR); + strncpy(info.vendor, "Microsoft", ONI_MAX_STR); + //strncpy(info.name, "Kinect 2", ONI_MAX_STR); // XXX, NiTE does not accept new name + strncpy(info.name, "Kinect", ONI_MAX_STR); + if (devices.find(info) == devices.end()) { + WriteMessage("Driver: register new uri: " + uri); + devices[info] = NULL; + deviceConnected(&info); + deviceStateChanged(&info, 0); + } + } + + public: + Driver(OniDriverServices* pDriverServices) : DriverBase(pDriverServices), + uriScheme("freenect2") + { + //WriteMessage("Using libfreenect v" + to_string(PROJECT_VER)); + WriteMessage("Using libfreenect2"); + + DriverServices = &getServices(); + } + ~Driver() { shutdown(); } + + // for DriverBase + + OniStatus initialize(oni::driver::DeviceConnectedCallback connectedCallback, oni::driver::DeviceDisconnectedCallback disconnectedCallback, oni::driver::DeviceStateChangedCallback deviceStateChangedCallback, void* pCookie) + { + DriverBase::initialize(connectedCallback, disconnectedCallback, deviceStateChangedCallback, pCookie); + for (int i = 0; i < freenect2.enumerateDevices(); i++) + { + std::string uri = devid_to_uri(i); + std::array modes = { + "", + "?depth-size=640x480", + "?depth-size=512x424", + }; + + WriteMessage("Found device " + uri); + + for (int i = 0; i < modes.size(); i++) { + register_uri(uri + modes[i]); + } + } + return ONI_STATUS_OK; + } + + oni::driver::DeviceBase* deviceOpen(const char* c_uri, const char* c_mode = NULL) + { + std::string uri(c_uri); + std::string mode(c_mode ? c_mode : ""); + if (uri.find("?") != -1) { + mode += "&"; + mode += uri.substr(uri.find("?") + 1); + uri = uri.substr(0, uri.find("?")); + } + std::stringstream ss(mode); + std::string buf; + while(std::getline(ss, buf, '&')) { + if (buf.find("=") != -1) { + config[buf.substr(0, buf.find("="))] = buf.substr(buf.find("=")+1); + } else { + if (0 < buf.length()) + config[buf] = ""; + } + } + WriteMessage("deiveOpen: " + uri); + for (std::map::iterator it = config.begin(); it != config.end(); it++) { + WriteMessage(" " + it->first + " = " + it->second); + } + + for (OniDeviceMap::iterator iter = devices.begin(); iter != devices.end(); iter++) + { + std::string iter_uri(iter->first.uri); + if (iter_uri.substr(0, iter_uri.find("?")) == uri) // found + { + if (iter->second) // already open + { + return iter->second; + } + else + { + WriteMessage("Opening device " + std::string(uri)); + int id = uri_to_devid(iter->first.uri); + Device* device = new Device(NULL, id); + device->setFreenect2Device(freenect2.openDevice(id)); // XXX, detault pipeline // const PacketPipeline *factory); + device->setConfigStrings(config); + iter->second = device; + return device; + } + } + } + + LogError("Could not find device " + std::string(uri)); + return NULL; + } + + void deviceClose(oni::driver::DeviceBase* pDevice) + { + for (OniDeviceMap::iterator iter = devices.begin(); iter != devices.end(); iter++) + { + if (iter->second == pDevice) + { + WriteMessage("Closing device " + std::string(iter->first.uri)); + int id = uri_to_devid(iter->first.uri); + + Device* device = (Device*)iter->second; + device->stop(); + device->close(); + + devices.erase(iter); + return; + } + } + + LogError("Could not close unrecognized device"); + } + + OniStatus tryDevice(const char* uri) + { + oni::driver::DeviceBase* device = deviceOpen(uri); + if (! device) + return ONI_STATUS_ERROR; + deviceClose(device); + register_uri(std::string(uri)); // XXX, register new uri here. + return ONI_STATUS_OK; + } + + void shutdown() + { + for (OniDeviceMap::iterator iter = devices.begin(); iter != devices.end(); iter++) + { + WriteMessage("Closing device " + std::string(iter->first.uri)); + int id = uri_to_devid(iter->first.uri); + Device* device = (Device*)iter->second; + if (device) { + device->stop(); + device->close(); + } + } + + devices.clear(); + } + + + /* todo: for DriverBase + virtual void* enableFrameSync(oni::driver::StreamBase** pStreams, int streamCount); + virtual void disableFrameSync(void* frameSyncGroup); + */ + }; +} + + +// macros defined in XnLib (not included) - workaround +#define XN_NEW(type, arg...) new type(arg) +#define XN_DELETE(p) delete(p) +ONI_EXPORT_DRIVER(Freenect2Driver::Driver); +#undef XN_NEW +#undef XN_DELETE diff --git a/drivers/OpenNI2-Freenect2Driver/src/IrStream.cpp b/drivers/OpenNI2-Freenect2Driver/src/IrStream.cpp new file mode 100644 index 000000000..0ca40ade5 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/IrStream.cpp @@ -0,0 +1,74 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#include +#include "IrStream.hpp" + +using namespace Freenect2Driver; + +// from NUI library and converted to radians +const float IrStream::HORIZONTAL_FOV = 58.5 * (M_PI / 180); +const float IrStream::VERTICAL_FOV = 45.6 * (M_PI / 180); + +IrStream::IrStream(libfreenect2::Freenect2Device* pDevice, Freenect2Driver::Registration *reg) : VideoStream(pDevice, reg) +{ + video_mode = makeOniVideoMode(ONI_PIXEL_FORMAT_GRAY16, 512, 424, 30); + setVideoMode(video_mode); + pDevice->start(); +} + +// Add video modes here as you implement them +IrStream::FreenectIrModeMap IrStream::getSupportedVideoModes() +{ + FreenectIrModeMap modes; + // pixelFormat, resolutionX, resolutionY, fps + modes[makeOniVideoMode(ONI_PIXEL_FORMAT_GRAY16, 512, 424, 30)] = std::pair(FREENECT2_IR_RAW, FREENECT2_RESOLUTION_512x424); + + + return modes; +} + +OniStatus IrStream::setVideoMode(OniVideoMode requested_mode) +{ + FreenectIrModeMap supported_video_modes = getSupportedVideoModes(); + FreenectIrModeMap::const_iterator matched_mode_iter = supported_video_modes.find(requested_mode); + if (matched_mode_iter == supported_video_modes.end()) + return ONI_STATUS_NOT_SUPPORTED; + + video_mode = requested_mode; + return ONI_STATUS_OK; +} + +void IrStream::populateFrame(libfreenect2::Frame* srcFrame, int srcX, int srcY, OniFrame* dstFrame, int dstX, int dstY, int width, int height) const +{ + dstFrame->sensorType = sensor_type; + dstFrame->stride = dstFrame->width * sizeof(uint16_t); + + // copy stream buffer from freenect + copyFrame(static_cast((void*)srcFrame->data), srcX, srcY, srcFrame->width, + static_cast(dstFrame->data), dstX, dstY, dstFrame->width, + width, height, mirroring); +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/IrStream.hpp b/drivers/OpenNI2-Freenect2Driver/src/IrStream.hpp new file mode 100644 index 000000000..5d59df3f5 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/IrStream.hpp @@ -0,0 +1,106 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include // for transform() +#include // for M_PI +#include // for memcpy +#include "libfreenect2/libfreenect2.hpp" +#include "Driver/OniDriverAPI.h" +#include "PS1080.h" +#include "VideoStream.hpp" + +namespace Freenect2Driver +{ + class IrStream : public VideoStream + { + public: + // from NUI library and converted to radians + static const float HORIZONTAL_FOV; + static const float VERTICAL_FOV; + + private: + typedef std::map< OniVideoMode, std::pair > FreenectIrModeMap; + static const OniSensorType sensor_type = ONI_SENSOR_IR; + + static FreenectIrModeMap getSupportedVideoModes(); + OniStatus setVideoMode(OniVideoMode requested_mode); + void populateFrame(libfreenect2::Frame* srcFrame, int srcX, int srcY, OniFrame* dstFrame, int dstX, int dstY, int width, int height) const; + + public: + IrStream(libfreenect2::Freenect2Device* pDevice, Freenect2Driver::Registration *reg); + //~IrStream() { } + + static OniSensorInfo getSensorInfo() + { + FreenectIrModeMap supported_modes = getSupportedVideoModes(); + OniVideoMode* modes = new OniVideoMode[supported_modes.size()]; + std::transform(supported_modes.begin(), supported_modes.end(), modes, ExtractKey()); + OniSensorInfo sensors = { sensor_type, static_cast(supported_modes.size()), modes }; + return sensors; + } + + // from StreamBase + OniBool isPropertySupported(int propertyId) + { + switch(propertyId) + { + default: + return VideoStream::isPropertySupported(propertyId); + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: + case ONI_STREAM_PROPERTY_VERTICAL_FOV: + return true; + } + } + + OniStatus getProperty(int propertyId, void* data, int* pDataSize) + { + switch (propertyId) + { + default: + return VideoStream::getProperty(propertyId, data, pDataSize); + + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: // float (radians) + if (*pDataSize != sizeof(float)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_HORIZONTAL_FOV"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = HORIZONTAL_FOV; + return ONI_STATUS_OK; + case ONI_STREAM_PROPERTY_VERTICAL_FOV: // float (radians) + if (*pDataSize != sizeof(float)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_VERTICAL_FOV"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = VERTICAL_FOV; + return ONI_STATUS_OK; + } + } + }; +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/Registration.cpp b/drivers/OpenNI2-Freenect2Driver/src/Registration.cpp new file mode 100644 index 000000000..c3d6fac9c --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/Registration.cpp @@ -0,0 +1,69 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#include +#include "Driver/OniDriverAPI.h" +#include "Registration.hpp" + +using namespace Freenect2Driver; + +Registration::Registration(libfreenect2::Freenect2Device* dev) : + dev(dev), + reg(NULL), + enabled(false) +{ + } + +Registration::~Registration() { + delete reg; +} + +void Registration::depthFrame(libfreenect2::Frame* frame) { + lastDepthFrame = frame; + } + +void Registration::colorFrameRGB888(libfreenect2::Frame* colorFrame, libfreenect2::Frame* registeredFrame) +{ + if (!reg) { + libfreenect2::Freenect2Device::ColorCameraParams colCamParams = dev->getColorCameraParams(); + libfreenect2::Freenect2Device::IrCameraParams irCamParams = dev->getIrCameraParams(); + { + libfreenect2::Freenect2Device::ColorCameraParams cp = colCamParams; + std::cout << "fx=" << cp.fx << ",fy=" << cp.fy << + ",cx=" << cp.cx << ",cy=" << cp.cy << std::endl; + libfreenect2::Freenect2Device::IrCameraParams ip = irCamParams; + std::cout << "fx=" << ip.fx << ",fy=" << ip.fy << + ",ix=" << ip.cx << ",iy=" << ip.cy << + ",k1=" << ip.k1 << ",k2=" << ip.k2 << ",k3=" << ip.k3 << + ",p1=" << ip.p1 << ",p2=" << ip.p2 << std::endl; + } + reg = new libfreenect2::Registration(irCamParams, colCamParams); + } + + libfreenect2::Frame undistorted(lastDepthFrame->width, lastDepthFrame->height, lastDepthFrame->bytes_per_pixel); + + reg->apply(colorFrame, lastDepthFrame, &undistorted, registeredFrame); + } diff --git a/drivers/OpenNI2-Freenect2Driver/src/Registration.hpp b/drivers/OpenNI2-Freenect2Driver/src/Registration.hpp new file mode 100644 index 000000000..8aa4a5356 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/Registration.hpp @@ -0,0 +1,46 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#include + +namespace Freenect2Driver { + class Registration { + private: + libfreenect2::Freenect2Device* dev; + libfreenect2::Registration* reg; + libfreenect2::Frame* lastDepthFrame; + bool enabled; + + public: + Registration(libfreenect2::Freenect2Device* dev); + ~Registration(); + + void depthFrame(libfreenect2::Frame* frame); + void colorFrameRGB888(libfreenect2::Frame* srcFrame, libfreenect2::Frame* dstFrame); + void setEnable(bool enable = true) { enabled = enable; } + bool isEnabled() { return enabled; } + }; +} diff --git a/drivers/OpenNI2-Freenect2Driver/src/S2D.h b/drivers/OpenNI2-Freenect2Driver/src/S2D.h new file mode 100644 index 000000000..3c421b99b --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/S2D.h @@ -0,0 +1,233 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +const uint16_t S2D[] = { +0, 315, 315, 315, 316, 316, 316, 316, 317, 317, +317, 318, 318, 318, 319, 319, 319, 319, 320, 320, +320, 321, 321, 321, 322, 322, 322, 322, 323, 323, +323, 324, 324, 324, 325, 325, 325, 326, 326, 326, +326, 327, 327, 327, 328, 328, 328, 329, 329, 329, +330, 330, 330, 331, 331, 331, 332, 332, 332, 332, +333, 333, 333, 334, 334, 334, 335, 335, 335, 336, +336, 336, 337, 337, 337, 338, 338, 338, 339, 339, +339, 340, 340, 340, 341, 341, 341, 342, 342, 343, +343, 343, 344, 344, 344, 345, 345, 345, 346, 346, +346, 347, 347, 347, 348, 348, 348, 349, 349, 350, +350, 350, 351, 351, 351, 352, 352, 352, 353, 353, +354, 354, 354, 355, 355, 355, 356, 356, 356, 357, +357, 358, 358, 358, 359, 359, 359, 360, 360, 361, +361, 361, 362, 362, 363, 363, 363, 364, 364, 364, +365, 365, 366, 366, 366, 367, 367, 368, 368, 368, +369, 369, 370, 370, 370, 371, 371, 372, 372, 372, +373, 373, 374, 374, 374, 375, 375, 376, 376, 377, +377, 377, 378, 378, 379, 379, 379, 380, 380, 381, +381, 382, 382, 382, 383, 383, 384, 384, 385, 385, +385, 386, 386, 387, 387, 388, 388, 389, 389, 389, +390, 390, 391, 391, 392, 392, 393, 393, 393, 394, +394, 395, 395, 396, 396, 397, 397, 398, 398, 398, +399, 399, 400, 400, 401, 401, 402, 402, 403, 403, +404, 404, 405, 405, 406, 406, 407, 407, 408, 408, +409, 409, 409, 410, 410, 411, 411, 412, 412, 413, +413, 414, 414, 415, 415, 416, 416, 417, 418, 418, +419, 419, 420, 420, 421, 421, 422, 422, 423, 423, +424, 424, 425, 425, 426, 426, 427, 427, 428, 429, +429, 430, 430, 431, 431, 432, 432, 433, 433, 434, +435, 435, 436, 436, 437, 437, 438, 438, 439, 440, +440, 441, 441, 442, 442, 443, 444, 444, 445, 445, +446, 446, 447, 448, 448, 449, 449, 450, 451, 451, +452, 452, 453, 454, 454, 455, 455, 456, 457, 457, +458, 458, 459, 460, 460, 461, 462, 462, 463, 463, +464, 465, 465, 466, 467, 467, 468, 468, 469, 470, +470, 471, 472, 472, 473, 474, 474, 475, 476, 476, +477, 478, 478, 479, 480, 480, 481, 482, 482, 483, +484, 484, 485, 486, 487, 487, 488, 489, 489, 490, +491, 491, 492, 493, 494, 494, 495, 496, 496, 497, +498, 499, 499, 500, 501, 502, 502, 503, 504, 504, +505, 506, 507, 507, 508, 509, 510, 511, 511, 512, +513, 514, 514, 515, 516, 517, 517, 518, 519, 520, +521, 521, 522, 523, 524, 525, 525, 526, 527, 528, +529, 529, 530, 531, 532, 533, 534, 534, 535, 536, +537, 538, 539, 540, 540, 541, 542, 543, 544, 545, +546, 546, 547, 548, 549, 550, 551, 552, 553, 554, +554, 555, 556, 557, 558, 559, 560, 561, 562, 563, +564, 565, 565, 566, 567, 568, 569, 570, 571, 572, +573, 574, 575, 576, 577, 578, 579, 580, 581, 582, +583, 584, 585, 586, 587, 588, 589, 590, 591, 592, +593, 594, 595, 596, 597, 598, 599, 600, 601, 602, +603, 604, 606, 607, 608, 609, 610, 611, 612, 613, +614, 615, 616, 618, 619, 620, 621, 622, 623, 624, +625, 627, 628, 629, 630, 631, 632, 634, 635, 636, +637, 638, 640, 641, 642, 643, 644, 646, 647, 648, +649, 650, 652, 653, 654, 655, 657, 658, 659, 661, +662, 663, 664, 666, 667, 668, 670, 671, 672, 674, +675, 676, 678, 679, 680, 682, 683, 684, 686, 687, +688, 690, 691, 693, 694, 696, 697, 698, 700, 701, +703, 704, 706, 707, 708, 710, 711, 713, 714, 716, +717, 719, 720, 722, 723, 725, 727, 728, 730, 731, +733, 734, 736, 738, 739, 741, 742, 744, 746, 747, +749, 750, 752, 754, 755, 757, 759, 761, 762, 764, +766, 767, 769, 771, 773, 774, 776, 778, 780, 781, +783, 785, 787, 789, 790, 792, 794, 796, 798, 800, +802, 803, 805, 807, 809, 811, 813, 815, 817, 819, +821, 823, 825, 827, 829, 831, 833, 835, 837, 839, +841, 843, 845, 847, 849, 851, 854, 856, 858, 860, +862, 864, 867, 869, 871, 873, 875, 878, 880, 882, +885, 887, 889, 891, 894, 896, 898, 901, 903, 906, +908, 910, 913, 915, 918, 920, 923, 925, 928, 930, +933, 935, 938, 940, 943, 946, 948, 951, 954, 956, +959, 962, 964, 967, 970, 973, 975, 978, 981, 984, +987, 989, 992, 995, 998, 1001, 1004, 1007, 1010, 1013, +1016, 1019, 1022, 1025, 1028, 1031, 1034, 1038, 1041, 1044, +1047, 1050, 1054, 1057, 1060, 1063, 1067, 1070, 1073, 1077, +1080, 1084, 1087, 1090, 1094, 1097, 1101, 1105, 1108, 1112, +1115, 1119, 1123, 1126, 1130, 1134, 1138, 1141, 1145, 1149, +1153, 1157, 1161, 1165, 1169, 1173, 1177, 1181, 1185, 1189, +1193, 1197, 1202, 1206, 1210, 1214, 1219, 1223, 1227, 1232, +1236, 1241, 1245, 1250, 1255, 1259, 1264, 1268, 1273, 1278, +1283, 1288, 1292, 1297, 1302, 1307, 1312, 1317, 1322, 1328, +1333, 1338, 1343, 1349, 1354, 1359, 1365, 1370, 1376, 1381, +1387, 1392, 1398, 1404, 1410, 1415, 1421, 1427, 1433, 1439, +1445, 1452, 1458, 1464, 1470, 1477, 1483, 1489, 1496, 1503, +1509, 1516, 1523, 1529, 1536, 1543, 1550, 1557, 1564, 1572, +1579, 1586, 1594, 1601, 1609, 1616, 1624, 1632, 1639, 1647, +1655, 1663, 1671, 1680, 1688, 1696, 1705, 1713, 1722, 1731, +1739, 1748, 1757, 1766, 1776, 1785, 1794, 1804, 1813, 1823, +1833, 1843, 1853, 1863, 1873, 1883, 1894, 1904, 1915, 1926, +1936, 1947, 1959, 1970, 1981, 1993, 2005, 2016, 2028, 2040, +2053, 2065, 2078, 2090, 2103, 2116, 2129, 2143, 2156, 2170, +2184, 2198, 2212, 2226, 2241, 2256, 2271, 2286, 2301, 2317, +2333, 2349, 2365, 2381, 2398, 2415, 2432, 2450, 2467, 2485, +2503, 2522, 2541, 2560, 2579, 2598, 2618, 2639, 2659, 2680, +2701, 2723, 2744, 2767, 2789, 2812, 2835, 2859, 2883, 2908, +2933, 2958, 2984, 3010, 3037, 3064, 3092, 3120, 3149, 3178, +3208, 3238, 3269, 3300, 3333, 3365, 3399, 3433, 3468, 3503, +3539, 3576, 3614, 3653, 3692, 3732, 3774, 3816, 3859, 3903, +3948, 3994, 4041, 4089, 4139, 4190, 4241, 4295, 4349, 4405, +4463, 4522, 4582, 4645, 4708, 4774, 4842, 4911, 4983, 5056, +5132, 5210, 5291, 5374, 5460, 5548, 5640, 5734, 5832, 5933, +6038, 6146, 6259, 6375, 6497, 6622, 6753, 6889, 7030, 7178, +7332, 7492, 7660, 7835, 8019, 8212, 8413, 8626, 8849, 9084, +9331, 9593, 9870, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0}; + diff --git a/drivers/OpenNI2-Freenect2Driver/src/Utility.hpp b/drivers/OpenNI2-Freenect2Driver/src/Utility.hpp new file mode 100644 index 000000000..443d8cbe7 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/Utility.hpp @@ -0,0 +1,103 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +// This file contains symbols that may be used by any class or don't really go anywhere else. +#pragma once + +#include +#include "Driver/OniDriverAPI.h" + + +// Oni helpers + +static OniVideoMode makeOniVideoMode(OniPixelFormat pixel_format, int resolution_x, int resolution_y, int frames_per_second) +{ + OniVideoMode mode; + mode.pixelFormat = pixel_format; + mode.resolutionX = resolution_x; + mode.resolutionY = resolution_y; + mode.fps = frames_per_second; + return mode; +} +static bool operator==(const OniVideoMode& left, const OniVideoMode& right) +{ + return (left.pixelFormat == right.pixelFormat && left.resolutionX == right.resolutionX + && left.resolutionY == right.resolutionY && left.fps == right.fps); +} +static bool operator<(const OniVideoMode& left, const OniVideoMode& right) +{ + return (left.resolutionX * left.resolutionY < right.resolutionX * right.resolutionY); +} + +static bool operator<(const OniDeviceInfo& left, const OniDeviceInfo& right) +{ + return (strcmp(left.uri, right.uri) < 0); +} + + +/// Extracts `first` from `pair`, for transforming a map into its keys. +struct ExtractKey +{ + template typename T::first_type + operator()(T pair) const + { + return pair.first; + } +}; + + +// holding out on C++11 +template +static std::string to_string(const T& n) +{ + std::ostringstream oss; + oss << n; + return oss.str(); +} + + +// global logging +namespace Freenect2Driver +{ + // DriverServices is set in DeviceDriver.cpp so all files can call errorLoggerAppend() + static oni::driver::DriverServices* DriverServices; + + // from XnLog.h + typedef enum XnLogSeverity { + XN_LOG_VERBOSE = 0, + XN_LOG_INFO = 1, + XN_LOG_WARNING = 2, + XN_LOG_ERROR = 3, + XN_LOG_SEVERITY_NONE = 10, + } XnLogSeverity; +} +#define FN2DRV_LOG_MASK "Freenect2Driver" +#define WriteVerbose(str) do { if (DriverServices != NULL) DriverServices->log(XN_LOG_VERBOSE, __FILE__, __LINE__, FN2DRV_LOG_MASK, std::string(str).c_str()); } while(0) +#define WriteInfo(str) do { if (DriverServices != NULL) DriverServices->log(XN_LOG_INFO, __FILE__, __LINE__, FN2DRV_LOG_MASK, std::string(str).c_str()); } while(0) +#define WriteWarning(str) do { if (DriverServices != NULL) DriverServices->log(XN_LOG_WARNING, __FILE__, __LINE__, FN2DRV_LOG_MASK, std::string(str).c_str()); } while(0) +#define WriteError(str) do { if (DriverServices != NULL) DriverServices->log(XN_LOG_ERROR, __FILE__, __LINE__, FN2DRV_LOG_MASK, std::string(str).c_str()); } while(0) +#define WriteMessage(str) WriteInfo(str) +#define LogError(str) WriteError(str) diff --git a/drivers/OpenNI2-Freenect2Driver/src/VideoStream.hpp b/drivers/OpenNI2-Freenect2Driver/src/VideoStream.hpp new file mode 100644 index 000000000..9e7c1d184 --- /dev/null +++ b/drivers/OpenNI2-Freenect2Driver/src/VideoStream.hpp @@ -0,0 +1,263 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2015 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include +#include +#include "libfreenect2.h" +#include "libfreenect2/libfreenect2.hpp" +#include "PS1080.h" +#include "Utility.hpp" +#include "Registration.hpp" + +namespace Freenect2Driver +{ + class VideoStream : public oni::driver::StreamBase + { + private: + unsigned int frame_id; // number each frame + + virtual OniStatus setVideoMode(OniVideoMode requested_mode) = 0; + virtual void populateFrame(libfreenect2::Frame* lf2Frame, int srcX, int srcY, OniFrame* oniFrame, int tgtX, int tgtY, int width, int height) const = 0; + + protected: + static const OniSensorType sensor_type; + libfreenect2::Freenect2Device* device; + bool running; // buildFrame() does something iff true + OniVideoMode video_mode; + OniCropping cropping; + bool mirroring; + Freenect2Driver::Registration* reg; + bool callPropertyChangedCallback; + + static void copyFrame(float* srcPix, int srcX, int srcY, int srcStride, uint16_t* dstPix, int dstX, int dstY, int dstStride, int width, int height, bool mirroring) + { + srcPix += srcX + srcY * srcStride; + dstPix += dstX + dstY * dstStride; + + for (int y = 0; y < height; y++) { + uint16_t* dst = dstPix + y * dstStride; + float* src = srcPix + y * srcStride; + if (mirroring) { + dst += width; + for (int x = 0; x < width; x++) + *dst-- = *src++; + } else { + for (int x = 0; x < width; x++) + *dst++ = *src++; + } + } + } + void raisePropertyChanged(int propertyId, const void* data, int dataSize) { + if (callPropertyChangedCallback) + StreamBase::raisePropertyChanged(propertyId, data, dataSize); + } + + public: + VideoStream(libfreenect2::Freenect2Device* device, Freenect2Driver::Registration* reg) : + frame_id(1), + device(device), + reg(reg), + callPropertyChangedCallback(false), + mirroring(false) + { + // joy of structs + memset(&cropping, 0, sizeof(cropping)); + memset(&video_mode, 0, sizeof(video_mode)); + } + //~VideoStream() { stop(); } + + void setPropertyChangedCallback(oni::driver::PropertyChangedCallback handler, void* pCookie) { + callPropertyChangedCallback = true; + StreamBase::setPropertyChangedCallback(handler, pCookie); + } + + bool buildFrame(libfreenect2::Frame* lf2Frame) + { + if (!running) + return false; + + OniFrame* oniFrame = getServices().acquireFrame(); + oniFrame->frameIndex = frame_id++; + oniFrame->timestamp = lf2Frame->timestamp; + oniFrame->videoMode = video_mode; + oniFrame->width = video_mode.resolutionX; + oniFrame->height = video_mode.resolutionY; + + if (cropping.enabled) + { + oniFrame->height = cropping.height; + oniFrame->width = cropping.width; + oniFrame->cropOriginX = cropping.originX; + oniFrame->cropOriginY = cropping.originY; + oniFrame->croppingEnabled = true; + } + else + { + oniFrame->cropOriginX = 0; + oniFrame->cropOriginY = 0; + oniFrame->croppingEnabled = false; + } + int width = std::min(oniFrame->width, (int)lf2Frame->width); + int height = std::min(oniFrame->height, (int)lf2Frame->height); + + populateFrame(lf2Frame, oniFrame->cropOriginX, oniFrame->cropOriginY, oniFrame, 0, 0, width, height); + raiseNewFrame(oniFrame); + getServices().releaseFrame(oniFrame); + + return false; + } + + // from StreamBase + + OniStatus start() + { + running = true; + return ONI_STATUS_OK; + } + void stop() { running = false; } + + // only add to property handlers if the property is generic to all children + // otherwise, implement in child and call these in default case + OniBool isPropertySupported(int propertyId) + { + switch(propertyId) + { + case ONI_STREAM_PROPERTY_VIDEO_MODE: + case ONI_STREAM_PROPERTY_CROPPING: + case ONI_STREAM_PROPERTY_MIRRORING: + return true; + default: + return false; + } + } + + virtual OniStatus getProperty(int propertyId, void* data, int* pDataSize) + { + switch (propertyId) + { + default: + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: // float: radians + case ONI_STREAM_PROPERTY_VERTICAL_FOV: // float: radians + case ONI_STREAM_PROPERTY_MAX_VALUE: // int + case ONI_STREAM_PROPERTY_MIN_VALUE: // int + case ONI_STREAM_PROPERTY_STRIDE: // int + case ONI_STREAM_PROPERTY_NUMBER_OF_FRAMES: // int + // camera + case ONI_STREAM_PROPERTY_AUTO_WHITE_BALANCE: // OniBool + case ONI_STREAM_PROPERTY_AUTO_EXPOSURE: // OniBool + // xn + case XN_STREAM_PROPERTY_INPUT_FORMAT: // unsigned long long + case XN_STREAM_PROPERTY_CROPPING_MODE: // XnCroppingMode + return ONI_STATUS_NOT_SUPPORTED; + + case ONI_STREAM_PROPERTY_VIDEO_MODE: // OniVideoMode* + if (*pDataSize != sizeof(OniVideoMode)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_VIDEO_MODE"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = video_mode; + return ONI_STATUS_OK; + + case ONI_STREAM_PROPERTY_CROPPING: // OniCropping* + if (*pDataSize != sizeof(OniCropping)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_CROPPING"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = cropping; + return ONI_STATUS_OK; + + case ONI_STREAM_PROPERTY_MIRRORING: // OniBool + if (*pDataSize != sizeof(OniBool)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_MIRRORING"); + return ONI_STATUS_ERROR; + } + *(static_cast(data)) = mirroring; + return ONI_STATUS_OK; + } + } + virtual OniStatus setProperty(int propertyId, const void* data, int dataSize) + { + switch (propertyId) + { + default: + case ONI_STREAM_PROPERTY_HORIZONTAL_FOV: // float: radians + case ONI_STREAM_PROPERTY_VERTICAL_FOV: // float: radians + case ONI_STREAM_PROPERTY_MAX_VALUE: // int + case ONI_STREAM_PROPERTY_MIN_VALUE: // int + case ONI_STREAM_PROPERTY_STRIDE: // int + case ONI_STREAM_PROPERTY_NUMBER_OF_FRAMES: // int + // camera + case ONI_STREAM_PROPERTY_AUTO_WHITE_BALANCE: // OniBool + case ONI_STREAM_PROPERTY_AUTO_EXPOSURE: // OniBool + // xn + case XN_STREAM_PROPERTY_INPUT_FORMAT: // unsigned long long + case XN_STREAM_PROPERTY_CROPPING_MODE: // XnCroppingMode + return ONI_STATUS_NOT_SUPPORTED; + + case ONI_STREAM_PROPERTY_VIDEO_MODE: // OniVideoMode* + if (dataSize != sizeof(OniVideoMode)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_VIDEO_MODE"); + return ONI_STATUS_ERROR; + } + if (ONI_STATUS_OK != setVideoMode(*(static_cast(data)))) + return ONI_STATUS_NOT_SUPPORTED; + raisePropertyChanged(propertyId, data, dataSize); + return ONI_STATUS_OK; + + case ONI_STREAM_PROPERTY_CROPPING: // OniCropping* + if (dataSize != sizeof(OniCropping)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_CROPPING"); + return ONI_STATUS_ERROR; + } + cropping = *(static_cast(data)); + raisePropertyChanged(propertyId, data, dataSize); + return ONI_STATUS_OK; + + case ONI_STREAM_PROPERTY_MIRRORING: // OniBool + if (dataSize != sizeof(OniBool)) + { + LogError("Unexpected size for ONI_STREAM_PROPERTY_MIRRORING"); + return ONI_STATUS_ERROR; + } + mirroring = *(static_cast(data)); + raisePropertyChanged(propertyId, data, dataSize); + return ONI_STATUS_OK; + } + } + + + /* todo : from StreamBase + virtual OniStatus convertDepthToColorCoordinates(StreamBase* colorStream, int depthX, int depthY, OniDepthPixel depthZ, int* pColorX, int* pColorY) { return ONI_STATUS_NOT_SUPPORTED; } + */ + }; +}