Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {"_chunkSize":0,"_flag":"initialState","initialState":{"url":"https://m.habrahabr.ru/post/333532/","html":"<head record-id=\"2\"><base record-id=\"0\" href=\"https://m.habrahabr.ru/post/333532/\" target=\"_blank\"><!--record-id-13-->\n <meta record-id=\"14\" charset=\"utf-8\"/><!--record-id-15-->\n <title record-id=\"16\">Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне / Хабрахабр</title><!--record-id-17-->\n <meta record-id=\"18\" content=\"Dynamic\" name=\"document-state\"/><!--record-id-19-->\n <meta record-id=\"20\" name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0\"/><!--record-id-21-->\n <link record-id=\"22\" href=\"https://habrahabr.ru/post/333532/\" rel=\"canonical\"/><!--record-id-23-->\n <meta record-id=\"24\" property=\"fb:app_id\" content=\"444736788986613\"/><!--record-id-25-->\n<meta record-id=\"26\" property=\"og:type\" content=\"article\"/><!--record-id-27-->\n<meta record-id=\"28\" property=\"og:url\" content=\"https://habrahabr.ru/post/333532/\"/><!--record-id-29-->\n<meta record-id=\"30\" property=\"og:title\" content=\"Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне\"/><!--record-id-31-->\n\n <meta record-id=\"32\" property=\"og:image\" content=\"https://habrastorage.org/web/b83/a96/f64/b83a96f643824c8aa4fe61f21a9ae3e2.gif\"/><!--record-id-33-->\n <link record-id=\"34\" rel=\"image_src\" href=\"https://habrastorage.org/web/b83/a96/f64/b83a96f643824c8aa4fe61f21a9ae3e2.gif\"/><!--record-id-35-->\n <meta record-id=\"36\" property=\"og:image\" content=\"https://habrastorage.org/web/474/be5/c08/474be5c0846f47e28218bfa8462185ff.jpg\"/><!--record-id-37-->\n <link record-id=\"38\" rel=\"image_src\" href=\"https://habrastorage.org/web/474/be5/c08/474be5c0846f47e28218bfa8462185ff.jpg\"/><!--record-id-39-->\n <meta record-id=\"40\" property=\"og:image\" content=\"https://habrastorage.org/web/9e9/daa/a53/9e9daaa5327049d2ba1fb3ce8b9c3828.jpg\"/><!--record-id-41-->\n <link record-id=\"42\" rel=\"image_src\" href=\"https://habrastorage.org/web/9e9/daa/a53/9e9daaa5327049d2ba1fb3ce8b9c3828.jpg\"/><!--record-id-43-->\n <meta record-id=\"44\" property=\"og:image\" content=\"https://habrastorage.org/web/431/572/6d6/4315726d67f34a8d8f1ff1180bbc7e78.png\"/><!--record-id-45-->\n <link record-id=\"46\" rel=\"image_src\" href=\"https://habrastorage.org/web/431/572/6d6/4315726d67f34a8d8f1ff1180bbc7e78.png\"/><!--record-id-47-->\n <meta record-id=\"48\" property=\"og:image\" content=\"https://habrastorage.org/web/0eb/1dd/e9e/0eb1dde9e6514aa4b01f52479ea0d101.gif\"/><!--record-id-49-->\n <link record-id=\"50\" rel=\"image_src\" href=\"https://habrastorage.org/web/0eb/1dd/e9e/0eb1dde9e6514aa4b01f52479ea0d101.gif\"/><!--record-id-51-->\n <meta record-id=\"52\" property=\"og:image\" content=\"https://habrastorage.org/web/8ba/e0d/937/8bae0d93754645d99869553c36214eaa.png\"/><!--record-id-53-->\n <link record-id=\"54\" rel=\"image_src\" href=\"https://habrastorage.org/web/8ba/e0d/937/8bae0d93754645d99869553c36214eaa.png\"/><!--record-id-55-->\n <meta record-id=\"56\" property=\"og:image\" content=\"https://habrastorage.org/web/e06/b99/04b/e06b9904b00b4ac6967fc6626ae40d52.jpg\"/><!--record-id-57-->\n <link record-id=\"58\" rel=\"image_src\" href=\"https://habrastorage.org/web/e06/b99/04b/e06b9904b00b4ac6967fc6626ae40d52.jpg\"/><!--record-id-59-->\n <meta record-id=\"60\" property=\"og:image\" content=\"https://habrastorage.org/web/f0b/c0d/e72/f0bc0de72b724920b6ae8e4d1fea3836.jpg\"/><!--record-id-61-->\n <link record-id=\"62\" rel=\"image_src\" href=\"https://habrastorage.org/web/f0b/c0d/e72/f0bc0de72b724920b6ae8e4d1fea3836.jpg\"/><!--record-id-63-->\n <meta record-id=\"64\" property=\"og:image\" content=\"https://habrastorage.org/web/ce4/7cd/e5b/ce47cde5b7ea45c69d62416da0d4d19c.png\"/><!--record-id-65-->\n <link reco
- astorage.org/web/ce4/7cd/e5b/ce47cde5b7ea45c69d62416da0d4d19c.png\"/><!--record-id-67-->\n <meta record-id=\"68\" property=\"og:image\" content=\"https://habrastorage.org/getpro/habr/formulas/8e3/e4b/3af/8e3e4b3af7c1321e593b77fb58c61afa.svg\"/><!--record-id-69-->\n <link record-id=\"70\" rel=\"image_src\" href=\"https://habrastorage.org/getpro/habr/formulas/8e3/e4b/3af/8e3e4b3af7c1321e593b77fb58c61afa.svg\"/><!--record-id-71-->\n <meta record-id=\"72\" property=\"og:image\" content=\"https://habrastorage.org/getpro/habr/formulas/e85/9e3/389/e859e33895fd579701b4da0969f53c18.svg\"/><!--record-id-73-->\n <link record-id=\"74\" rel=\"image_src\" href=\"https://habrastorage.org/getpro/habr/formulas/e85/9e3/389/e859e33895fd579701b4da0969f53c18.svg\"/><!--record-id-75-->\n <meta record-id=\"76\" property=\"og:image\" content=\"https://habrastorage.org/web/fd3/4c1/8a8/fd34c18a8e874103a40c365ddaec4fb7.gif\"/><!--record-id-77-->\n <link record-id=\"78\" rel=\"image_src\" href=\"https://habrastorage.org/web/fd3/4c1/8a8/fd34c18a8e874103a40c365ddaec4fb7.gif\"/><!--record-id-79-->\n <meta record-id=\"80\" property=\"og:image\" content=\"https://habrastorage.org/web/1a6/1b8/63c/1a61b863c8b3443a8b90b7531af1ccf3.gif\"/><!--record-id-81-->\n <link record-id=\"82\" rel=\"image_src\" href=\"https://habrastorage.org/web/1a6/1b8/63c/1a61b863c8b3443a8b90b7531af1ccf3.gif\"/><!--record-id-83-->\n <meta record-id=\"84\" property=\"og:image\" content=\"https://habrastorage.org/web/d18/cac/1fd/d18cac1fda3c47a48e43dfbde71fd77b.gif\"/><!--record-id-85-->\n <link record-id=\"86\" rel=\"image_src\" href=\"https://habrastorage.org/web/d18/cac/1fd/d18cac1fda3c47a48e43dfbde71fd77b.gif\"/><!--record-id-87-->\n <meta record-id=\"88\" property=\"og:image\" content=\"https://habrastorage.org/web/9f5/ec4/e19/9f5ec4e19ac842928c7c512c0a8864c0.gif\"/><!--record-id-89-->\n <link record-id=\"90\" rel=\"image_src\" href=\"https://habrastorage.org/web/9f5/ec4/e19/9f5ec4e19ac842928c7c512c0a8864c0.gif\"/><!--record-id-91-->\n <meta record-id=\"92\" property=\"og:image\" content=\"https://habrastorage.org/web/8ae/9f7/71d/8ae9f771d94746b99bbe327b8adcb7d4.gif\"/><!--record-id-93-->\n <link record-id=\"94\" rel=\"image_src\" href=\"https://habrastorage.org/web/8ae/9f7/71d/8ae9f771d94746b99bbe327b8adcb7d4.gif\"/><!--record-id-95-->\n <meta record-id=\"96\" property=\"og:image\" content=\"https://habrastorage.org/web/379/d5e/1a7/379d5e1a72174becb64f991f0b3455fc.gif\"/><!--record-id-97-->\n <link record-id=\"98\" rel=\"image_src\" href=\"https://habrastorage.org/web/379/d5e/1a7/379d5e1a72174becb64f991f0b3455fc.gif\"/><!--record-id-99-->\n <meta record-id=\"100\" property=\"og:image\" content=\"https://habrastorage.org/web/d68/1b4/00b/d681b400b40f49abb86f51e474df8589.gif\"/><!--record-id-101-->\n <link record-id=\"102\" rel=\"image_src\" href=\"https://habrastorage.org/web/d68/1b4/00b/d681b400b40f49abb86f51e474df8589.gif\"/><!--record-id-103-->\n <meta record-id=\"104\" property=\"og:image\" content=\"https://habrastorage.org/web/7b2/e18/fc4/7b2e18fc49164a6689e6b8cc3729e6b6.gif\"/><!--record-id-105-->\n <link record-id=\"106\" rel=\"image_src\" href=\"https://habrastorage.org/web/7b2/e18/fc4/7b2e18fc49164a6689e6b8cc3729e6b6.gif\"/><!--record-id-107-->\n <meta record-id=\"108\" property=\"og:image\" content=\"https://habrastorage.org/web/232/c88/5ec/232c885ec6384eb7a3e8422e4139ab7e.gif\"/><!--record-id-109-->\n <link record-id=\"110\" rel=\"image_src\" href=\"https://habrastorage.org/web/232/c88/5ec/232c885ec6384eb7a3e8422e4139ab7e.gif\"/><!--record-id-111-->\n <meta record-id=\"112\" property=\"og:image\" content=\"https://habrastorage.org/web/ad8/0b5/218/ad80b521856f48fa9a87b46fc6a15a71.gif\"/><!--record-id-113-->\n <link record-id=\"114\" rel=\"image_src\" href=\"https://habrastorage.org/web/ad8/0b5/218/ad80b521856f48fa9a87b46fc6a15a71.gif\"/><!--record-id-115-->\n <meta record-id=\"116\" property=\"og:imag
- e\" content=\"https://habrastorage.org/web/b4b/621/55a/b4b62155a8834025859113e5181236af.gif\"/><!--record-id-117-->\n <link record-id=\"118\" rel=\"image_src\" href=\"https://habrastorage.org/web/b4b/621/55a/b4b62155a8834025859113e5181236af.gif\"/><!--record-id-119-->\n <meta record-id=\"120\" property=\"og:image\" content=\"https://habrastorage.org/getpro/habr/formulas/ce4/434/132/ce443413279406b7072efe33460de6fd.svg\"/><!--record-id-121-->\n <link record-id=\"122\" rel=\"image_src\" href=\"https://habrastorage.org/getpro/habr/formulas/ce4/434/132/ce443413279406b7072efe33460de6fd.svg\"/><!--record-id-123-->\n <meta record-id=\"124\" property=\"og:image\" content=\"https://habrastorage.org/getpro/habr/formulas/08d/9fa/efb/08d9faefbe272bdf8fbb80773542e343.svg\"/><!--record-id-125-->\n <link record-id=\"126\" rel=\"image_src\" href=\"https://habrastorage.org/getpro/habr/formulas/08d/9fa/efb/08d9faefbe272bdf8fbb80773542e343.svg\"/><!--record-id-127-->\n <meta record-id=\"128\" property=\"og:image\" content=\"https://habrastorage.org/web/613/64a/b08/61364ab087054dae8c826c233a53ac16.gif\"/><!--record-id-129-->\n <link record-id=\"130\" rel=\"image_src\" href=\"https://habrastorage.org/web/613/64a/b08/61364ab087054dae8c826c233a53ac16.gif\"/><!--record-id-131-->\n <meta record-id=\"132\" property=\"og:image\" content=\"https://habrastorage.org/web/e52/91c/72f/e5291c72fb3c477da9f268b9a63452f3.gif\"/><!--record-id-133-->\n <link record-id=\"134\" rel=\"image_src\" href=\"https://habrastorage.org/web/e52/91c/72f/e5291c72fb3c477da9f268b9a63452f3.gif\"/><!--record-id-135-->\n <meta record-id=\"136\" property=\"og:image\" content=\"https://habrastorage.org/web/817/3e1/fe3/8173e1fe350243c8b80f9003b26e4818.gif\"/><!--record-id-137-->\n <link record-id=\"138\" rel=\"image_src\" href=\"https://habrastorage.org/web/817/3e1/fe3/8173e1fe350243c8b80f9003b26e4818.gif\"/><!--record-id-139-->\n <meta record-id=\"140\" property=\"og:image\" content=\"https://habrastorage.org/web/934/43a/711/93443a7116b84070a14eaa70a5007311.gif\"/><!--record-id-141-->\n <link record-id=\"142\" rel=\"image_src\" href=\"https://habrastorage.org/web/934/43a/711/93443a7116b84070a14eaa70a5007311.gif\"/><!--record-id-143-->\n <meta record-id=\"144\" property=\"og:image\" content=\"https://habrastorage.org/web/915/9e6/a7f/9159e6a7f5044cdcb3718c411583f0ed.gif\"/><!--record-id-145-->\n <link record-id=\"146\" rel=\"image_src\" href=\"https://habrastorage.org/web/915/9e6/a7f/9159e6a7f5044cdcb3718c411583f0ed.gif\"/><!--record-id-147-->\n <meta record-id=\"148\" property=\"og:image\" content=\"https://habrastorage.org/web/d76/914/cd0/d76914cd0c024cc98cb01a1166388ac5.gif\"/><!--record-id-149-->\n <link record-id=\"150\" rel=\"image_src\" href=\"https://habrastorage.org/web/d76/914/cd0/d76914cd0c024cc98cb01a1166388ac5.gif\"/><!--record-id-151-->\n\n<meta record-id=\"152\" property=\"og:description\" content=\"Привет Хабр! Это заметка о небольшом хобби-проекте, которым я занимался в свободное время. Я расскажу, как с помощью несложных алгоритмов превращать карты...\"/><!--record-id-153-->\n<meta record-id=\"154\" name=\"twitter:card\" content=\"summary\"/><!--record-id-155-->\n<meta record-id=\"156\" name=\"twitter:site\" content=\"@habrahabr\"/><!--record-id-157-->\n <meta record-id=\"158\" property=\"al:android:url\" content=\"habrahabr://post/333532\"/><!--record-id-159-->\n<meta record-id=\"160\" property=\"al:android:app_name\" content=\"Habrahabr\"/><!--record-id-161-->\n<meta record-id=\"162\" property=\"al:android:package\" content=\"ru.habrahabr\"/><!--record-id-163-->\n<meta record-id=\"164\" property=\"al:windows_phone:url\" content=\"habrahabr://post/333532\"/><!--record-id-165-->\n<meta record-id=\"166\" property=\"al:windows_phone:app_name\" content=\"Habrahabr\"/><!--record-id-167-->\n<meta record-id=\"168\" property=\"al:windows_phone:app_id\" conte
- <meta record-id=\"170\" name=\"google-play-app\" content=\"app-id=ru.habrahabr\"/><!--record-id-171-->\n <meta record-id=\"172\" content=\"Хабрахабр\" name=\"apple-mobile-web-app-title\"/><!--record-id-173-->\n\n<link record-id=\"174\" rel=\"apple-touch-icon-precomposed\" sizes=\"57x57\" href=\"/images/favicons/apple-touch-icon-57x57.png\"/><!--record-id-175-->\n<link record-id=\"176\" rel=\"apple-touch-icon-precomposed\" sizes=\"114x114\" href=\"/images/favicons/apple-touch-icon-114x114.png\"/><!--record-id-177-->\n<link record-id=\"178\" rel=\"apple-touch-icon-precomposed\" sizes=\"72x72\" href=\"/images/favicons/apple-touch-icon-72x72.png\"/><!--record-id-179-->\n<link record-id=\"180\" rel=\"apple-touch-icon-precomposed\" sizes=\"144x144\" href=\"/images/favicons/apple-touch-icon-144x144.png\"/><!--record-id-181-->\n<link record-id=\"182\" rel=\"apple-touch-icon-precomposed\" sizes=\"120x120\" href=\"/images/favicons/apple-touch-icon-120x120.png\"/><!--record-id-183-->\n<link record-id=\"184\" rel=\"apple-touch-icon-precomposed\" sizes=\"152x152\" href=\"/images/favicons/apple-touch-icon-152x152.png\"/><!--record-id-185-->\n<link record-id=\"186\" rel=\"icon\" type=\"image/png\" href=\"/images/favicons/favicon-32x32.png\" sizes=\"32x32\"/><!--record-id-187-->\n<link record-id=\"188\" rel=\"icon\" type=\"image/png\" href=\"/images/favicons/favicon-16x16.png\" sizes=\"16x16\"/><!--record-id-189-->\n<meta record-id=\"190\" name=\"application-name\" content=\"Хабрахабр\"/><!--record-id-191-->\n<meta record-id=\"192\" name=\"msapplication-TileColor\" content=\"#FFFFFF\"/><!--record-id-193-->\n<meta record-id=\"194\" name=\"msapplication-TileImage\" content=\"mstile-144x144.png\"/><!--record-id-195-->\n <link record-id=\"196\" href=\"https://habracdn.net/habr/styles/1500301734/stylesheets.mobile.css\" rel=\"stylesheet\" media=\"all\"/><!--record-id-197-->\n <!--record-id-199-->\n <!--record-id-201-->\n<!--record-id-203-->\n </head><!--record-id-206-->\n\n <body record-id=\"207\"><!--record-id-208-->\n <div record-id=\"209\" id=\"layout\"><!--record-id-210-->\n <div record-id=\"211\" id=\"header\"><!--record-id-212-->\n <a record-id=\"213\" href=\"/\" class=\"logo\"></a><!--record-id-214-->\n <div record-id=\"215\" class=\"title\"><!--record-id-216-->Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне / Хабрахабр</div><!--record-id-217-->\n <div record-id=\"218\" class=\"toggle_sidebar\"></div><!--record-id-219-->\n</div><!--record-id-220-->\n <div record-id=\"221\" id=\"content\"><!--record-id-222-->\n <div record-id=\"223\" class=\"banner_header\"><!--record-id-224-->\n <!--record-id-225--><!-- AdRiver code START. Type:AjaxJS Site: m.habr BN:1 --><!--record-id-226-->\n <div record-id=\"227\" id=\"adriver_banner_1939188702\" style=\"margin: 0 auto;\" title=\"\"></div><!--record-id-228-->\n <!--record-id-230-->\n <!--record-id-231--><!-- AdRiver code END --><!--record-id-232-->\n</div><!--record-id-233-->\n\n <div record-id=\"234\" class=\"post_show\"><!--record-id-235-->\n <div record-id=\"236\" class=\"hubs\"><!--record-id-237-->\n <a record-id=\"238\" href=\"/hub/programming/\" class=\"hub\"><!--record-id-239-->Программирование</a><span record-id=\"240\" class=\"profiled_hub\" title=\"Профильный хаб\"><!--record-id-241-->*</span><!--record-id-242-->, \n <a record-id=\"243\" href=\"/hub/algorithms/\" class=\"hub\"><!--record-id-244-->Алгоритмы</a><span record-id=\"245\" class=\"profiled_hub\" title=\"Профильный хаб\"><!--record-id-246-->*</span><!--record-id-247-->, \n <a record-id=\"248\" href=\"/hub/cpp/\" class=\"hub\"><!--record-id-249-->C++</a><span record-id=\"250\" class=\"profiled_hub\" title=\"Профильный хаб\"><!--record-id-251-->*</span><!--record-id-252-->\n </div><!--record-id-253-->\n\n <h3 record-id=\"254\" class=\"title\"><!--record
- /\" class=\"flow\"><!--record-id-257-->Разработка</a><span record-id=\"258\" class=\"arrow\"><!--record-id-259--> → </span><!--record-id-260-->Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне\n </h3><!--record-id-261-->\n\n \n \n \n <div record-id=\"262\" class=\"flag sandbox\"><!--record-id-263-->из песочницы</div><!--record-id-264--> \n\n <div record-id=\"265\" class=\"meta\"><!--record-id-266-->\n <a record-id=\"267\" href=\"/users/Petrenuk/topics/\" class=\"author\"><!--record-id-268-->Petrenuk</a><!--record-id-269-->\n <span record-id=\"270\" class=\"time\"><!--record-id-271-->вчера в 17:42</span><!--record-id-272-->\n <span record-id=\"273\" class=\"viewed icon_view\" title=\"Количество просмотров\"><!--record-id-274-->8,1k</span><!--record-id-275-->\n </div><!--record-id-276-->\n\n <div record-id=\"277\" class=\"text text_html js-mediator-article\" id=\"post_text\"><div record-id=\"278\" style=\"text-align:center;\"><img record-id=\"279\" src=\"https://habrastorage.org/web/b83/a96/f64/b83a96f643824c8aa4fe61f21a9ae3e2.gif\"/></div><br record-id=\"280\"/><!--record-id-281-->\nПривет Хабр! Это заметка о небольшом хобби-проекте, которым я занимался в свободное время. Я расскажу, как с помощью несложных алгоритмов превращать карты глубины от depth-сенсоров в забавный вид контента — динамические 3D сцены (их ещё называют 4D video, volumetric capture или free-viewpoint video). Моя любимая часть в этой работе — алгоритм триангуляции Делоне, который позволяет превращать разреженные облака точек в плотную полигональную сетку. Приглашаю всех, кому интересно почитать про алгоритмы, самописные велосипеды на C++11, и, конечно же, посмотреть на трёхмерных котиков.<br record-id=\"282\"/><!--record-id-283-->\n<br record-id=\"284\"/><!--record-id-285-->\nДля затравки: вот что получается при использовании RealSense R200: <a record-id=\"286\" href=\"https://skfb.ly/6snzt\"><!--record-id-287-->skfb.ly/6snzt</a><!--record-id-288--> (подождите несколько секунд для загрузки текстур, а затем используйте мышку, чтобы поворачивать сцену). Под катом есть ещё!<br record-id=\"289\"/><!--record-id-290-->\nОбладатели лимитированных тарифов, будьте осторожны. В статье много разных изображений и иллюстраций.<br record-id=\"291\"/><!--record-id-292-->\n<a record-id=\"293\" name=\"habracut\"></a><br record-id=\"294\"/><!--record-id-295-->\n<i record-id=\"296\"><!--record-id-297-->Дисклеймер: в этой статье не будет ни слова про искусственный интеллект, блокчейн или гравитационные волны. Это просто небольшая игрушка, которую я написал в основном для того, чтобы освежить C++ и OpenGL. Всё, ожидания сформированы, поехали!</i><br record-id=\"298\"/><!--record-id-299-->\n<br record-id=\"300\"/><!--record-id-301-->\nКакое-то время назад мне в руки попал девайс с 3D сенсором, а именно планшет <a record-id=\"302\" href=\"http://cdn.gsmarena.com/pics/15/05/project-tango-google-store/gsmarena_001.jpg\"><!--record-id-303-->Google Tango Development Kit</a><!--record-id-304-->
- src=\"https://www.youtube.com/embed/SkJG-uFU2yA?rel=0&showinfo=1&start=110\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><br record-id=\"320\"/><!--record-id-321-->\nТакой контент показался мне очень интересным; я решил поэкспериментировать с ним немного и написать своё приложение для генерации 4D роликов. Конечно, у меня нет студии и ресурсов как у Microsoft Research, да и сенсор всего один, поэтому результаты будут намного скромнее. Но это не остановит любителя программировать, правильно? О том, что у меня получилось, я и расскажу в этой статье.<br record-id=\"322\"/><!--record-id-323-->\n<br record-id=\"324\"/><!--record-id-325-->\n<div record-id=\"326\" class=\"spoiler\"><b record-id=\"327\" class=\"spoiler_title\"><!--record-id-328-->Про название статьи</b><div record-id=\"329\" class=\"spoiler_text\"><!--record-id-330-->Надо признать, название «4D видео» — не самое удачное. Первый же вопрос: почему «4D», а не «3D». Ведь обычное видео не называют трёхмерным только потому, что плоская картинка изменяется со временем. Действительно, в серьезных работах обычно используют термин «free viewpoint video». Но для заголовка это слишком скучно, и я решил оставить кликбейтное 4D (хотя до 11D кинотеатров ещё далеко).<br record-id=\"331\"/><!--record-id-332-->\n<div record-id=\"333\" class=\"img_wrapper\"><img record-id=\"334\" src=\"https://habrastorage.org/web/474/be5/c08/474be5c0846f47e28218bfa8462185ff.jpg\"/></div><br record-id=\"335\"/><!--record-id-336-->\n</div></div><br record-id=\"337\"/><!--record-id-338-->\nБольшая часть аудитории, несомненно, знакома с 3D сенсорами. Самый массовый девайс в этой категории — всем известный Kinect, очень успешный продукт компании Microsoft. Однако несмотря на относительную распространённость, для многих людей depth-сенсоры остаются чем-то диковинным. Нам же будет полезно разобраться с принципом их работы прежде чем начинать писать приложения.<br record-id=\"339\"/><!--record-id-340-->\n<br record-id=\"341\"/><!--record-id-342-->\nС Google Tango и другими structured light устройствами всё относительно понятно. Встроенный в девайс инфракрасный прожектор проецирует на сцену нерегулярный паттерн из световых точек, примерно как на этой картинке:<br record-id=\"343\"/><!--record-id-344-->\n<br record-id=\"345\"/><!--record-id-346-->\n<div record-id=\"347\" style=\"text-align:center;\"><div record-id=\"348\" class=\"img_wrapper\"><img record-id=\"349\" src=\"https://habrastorage.org/web/9e9/daa/a53/9e9daaa5327049d2ba1fb3ce8b9c3828.jpg\" width=\"480\"/></div></div><br record-id=\"350\"/><!--record-id-351-->\nЗатем специальный софт превращает искажения этого паттерна на изображении с ИК-камеры (вызванные разнообразной формой объектов) в серию 3D измерений. Для каждого световог
- было достигнуто: координаты точек записались в бинарный файл. На скриншоте показано как выглядит облако точек с одного кадра в программе Meshlab.<br record-id=\"368\"/><!--record-id-369-->\n<br record-id=\"370\"/><!--record-id-371-->\nОк, облака точек это уже интересно. Однако point cloud — весьма “разреженное” представление сцены, ведь разрешение современных structured light сенсоров довольно низкое (обычно в районе 100x100 точек, плюс-минус). Да и в целом, в мире компьютерной графики гораздо привычнее другое представление 3D объектов и сцен, а именно полигональные сетки, или меши. <br record-id=\"372\"/><!--record-id-373-->\n<br record-id=\"374\"/><!--record-id-375-->\nЕсли подумать, задача получения меша по облаку точек с одной 3D камеры довольно проста, значительно проще, чем произвольный meshing в 3D. Для этого нам не нужны никакие <a record-id=\"376\" href=\"https://en.wikipedia.org/wiki/Marching_cubes\"><!--record-id-377-->марширующие кубы</a><!--record-id-378--> или <a record-id=\"379\" href=\"http://www.cs.jhu.edu/~misha/Code/PoissonRecon/Version9.01/\"><!--record-id-380-->реконструкция Пуассона</a><!--record-id-381-->. Вспомним, что точки были получены с помощью одной единственной IR-камеры и естественным образом проецируются обратно на фокальную плоскость:<br record-id=\"382\"/><!--record-id-383-->\n<br record-id=\"384\"/><!--record-id-385-->\n<div record-id=\"386\" style=\"text-align:center;\"><div record-id=\"387\" class=\"img_wrapper\"><img record-id=\"388\" src=\"https://habrastorage.org/web/0eb/1dd/e9e/0eb1dde9e6514aa4b01f52479ea0d101.gif\"/></div></div><br record-id=\"389\"/><!--record-id-390-->\nТеперь мы можем решать задачу в 2D, забыв на время про Z-координату. Чтобы получить полигоны нужно всего лишь триангулировать множество точек на плоскости. Как только это сделано, мы проецируем вершины обратно в 3D используя <a record-id=\"391\" href=\"https://en.wikipedia.org/wiki/Pinhole_camera_model\"><!--record-id-392-->внутренние параметры</a><!--record-id-393--> IR-камеры и известную в каждой точке глубину. Триангуляция будет диктовать связность между вершинами, т.е. каждый треугольник мы интерпретируем как грань меша. Таким образом на каждом кадре получится настоящая 3D модель сцены, которую можно отрендерить в OpenGL, открыть в Blender и т.п.<br record-id=\"394\"/><!--record-id-395-->\n<br record-id=\"396\"/><!--record-id-397-->\nИтак, всё что осталось сделать — найти триангуляцию для точек на каждом кадре. Есть масса способов триангулировать множество точек в 2D, и по сути любая триангуляция даст нам какой-то меш. Но ровно один способ является оптимальным для построения полигональной сетки — триангуляция Делоне.<br record-id=\"398\"/><!--record-id-399-->\n<br record-id=\
- 0%B9%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B3%D1%80%D0%B0%D1%84\"><!--record-id-420-->двойственна </a><!--record-id-421-->диаграмме Вороного:<br record-id=\"422\"/><!--record-id-423-->\n<br record-id=\"424\"/><!--record-id-425-->\n<div record-id=\"426\" style=\"text-align:center;\"><div record-id=\"427\" class=\"img_wrapper\"><img record-id=\"428\" width=\"260\" src=\"https://habrastorage.org/web/ce4/7cd/e5b/ce47cde5b7ea45c69d62416da0d4d19c.png\"/></div></div><br record-id=\"429\"/><!--record-id-430-->\n<br record-id=\"431\"/><!--record-id-432-->\nТриангуляции Делоне обладают множеством замечательных свойств, но нам особенно интересны вот эти два:<br record-id=\"433\"/><!--record-id-434-->\n<br record-id=\"435\"/><!--record-id-436-->\n<ol record-id=\"437\"><!--record-id-438-->\n<li record-id=\"439\"><!--record-id-440-->Описанная около любого из треугольников окружность не содержит в себе точек множества, кроме вершин самого треугольника.</li><!--record-id-441-->\n<li record-id=\"442\"><!--record-id-443-->Триангуляция Делоне максимизирует минимальный угол среди всех углов всех треугольников, тем самым избегаются вырожденные и «тонкие» треугольники.</li><!--record-id-444-->\n</ol><br record-id=\"445\"/><!--record-id-446-->\nСвойство #2 достаточно интуитивно следует из #1. Действительно, чтобы описанные окружности не содержали посторонних точек, они должны быть по возможности небольшими, а радиус окружности для “тонких” треугольников (близких к вырожденным) наоборот очень велик. Таким образом триангуляция Делоне максимизирует количество “хороших” треугольников, далёких от вырожденных. А значит и наш меш должен выглядеть хорошо.<br record-id=\"447\"/><!--record-id-448-->\n<br record-id=\"449\"/><!--record-id-450-->\nСказано — сделано, пишем триангуляцию Делоне. Есть множество известных алгоритмов, <a record-id=\"451\" href=\"https://e-maxx.ru/algo/voronoi_diagram_2d_n4\"><!--record-id-452-->самые простые</a><!--record-id-453--> начинаются где-то с <math record-id=\"454\"></math><img record-id=\"455\" src=\"https://habrastorage.org/getpro/habr/formulas/8e3/e4b/3af/8e3e4b3af7c1321e593b77fb58c61afa.svg\" alt=\"$O(n^4)$\" data-tex=\"inline\"/><!--record-id-456-->. Но раз люди научились считать эти триангуляции за <math record-id=\"457\"></math><img record-id=\"458\" src=\"https://habrastorage.org/getpro/habr/formulas/e85/9e3/389/e859e33895fd579701b4da0969f53c18.svg\" alt=\"$O(nlogn)$\" data-tex=\"inline\"/><!--record-id-459-->, мы просто не можем быть медленнее!<br record-id=\"460\"/><!--record-id-461-->\n<br record-id=\"462\"/><!--record-id-463-->\nКак и во многих других подоб��ых задачах, алгоритм строится по принципу “разделяй и властвуй”. Будем сортировать точки по координатам x и y, а затем рекурсивно генерировать и объединять триангуляции пока не получится один большой граф. Достаточно показать вот этот кусочек кода, чтобы стало понятно, что здесь происходит:<br rec
- dx rre; <span record-id=\"488\" class=\"hljs-comment\"><!--record-id-489-->// CW convex hull edge starting at the rightmost vertex of right triangulation</span><!--record-id-490-->\ntriangulateSubset(lIdx + numLeft, numRight, rle, rre);\ntriangulateSubset(lIdx, numLeft, lle, lre);\nmergeTriangulations(lle, lre, rle, rre, le, re);\n</code></pre><br record-id=\"491\"/><!--record-id-492-->\nВ принципе всё, можно расходиться :)<br record-id=\"493\"/><!--record-id-494-->\nНа самом деле нет, потому что самое интересное начинается в функции mergeTriangulations. Действительно, если мы разделили наше множество пополам достаточное число раз, мы остались с кусочками по две или три точки, с которыми мы как-нибудь разберёмся:<br record-id=\"495\"/><!--record-id-496-->\n<br record-id=\"497\"/><!--record-id-498-->\n<div record-id=\"499\" style=\"text-align:center;\"><img record-id=\"500\" width=\"240\" src=\"https://habrastorage.org/web/fd3/4c1/8a8/fd34c18a8e874103a40c365ddaec4fb7.gif\"/></div><br record-id=\"501\"/><!--record-id-502-->\nА что потом?<br record-id=\"503\"/><!--record-id-504-->\n<br record-id=\"505\"/><!--record-id-506-->\n<div record-id=\"507\" class=\"spoiler\"><b record-id=\"508\" class=\"spoiler_title\"><!--record-id-509-->Про иллюстрации</b><div record-id=\"510\" class=\"spoiler_text\"><!--record-id-511-->Здесь и далее я буду сопровождать описание GIF-анимациями, которые сгенерировала моя программа, а также иллюстрациями из одной хорошей статьи. При описании алгоритма я во многом буду повторять эту <a record-id=\"512\" href=\"http://www.geom.uiuc.edu/~samuelp/del_project.html\"><!--record-id-513-->статью</a><!--record-id-514-->, так что если возникнут вопросы можно смело с ней консультироваться.<br record-id=\"515\"/><!--record-id-516-->\n</div></div><br record-id=\"517\"/><!--record-id-518-->\nНа самом деле тоже ничего сложного. В каждом листе рекурсии мы уже получили валидное решение подзадачи. Осталось подняться вверх по стеку, по пути сливая левое и правое разбиение в единое целое. Но делать это нужно аккуратно, т.к. далеко не каждый способ слияния даст нам на выходе триангуляцию Делоне.<br record-id=\"519\"/><!--record-id-520-->\nАлгоритм Гибаса и Столфи предлагает весьма элегантное решение этой проблемы. Идея в том, чтобы воспользоваться свойством #1, которое я упомянул выше, но обо всём по порядку.<br record-id=\"521\"/><!--record-id-522-->\nСперва немного обозначений; давайте назовём левую триангуляцию L, а правую R. Тогда рёбра, принадлежащие левой триангуляции будем называть LL, т.к. они начинаются и заканчиваются в точках левого подмножества. Ребра правой триангуляции назовём RR, а рёбра, которые мы будем добавлять между левой и правой половиной — LR, точно как на иллюстрации:<br record-id=\"523\"/><!--record-id-524-->\n<br record-id=\"525\"/><!--record-id-526-->\n<div record-id=\"527\" style=\"text-align:center;\"><img record-id=\"528\" src=\"https://habrasto
- метим также, что при слиянии двух непересекающихся разбиений нам всегда нужно будет добавить ровно два новых ребра, чтобы достроить выпуклую оболочку объединённого множества (грубо говоря, “накрыть” точки новыми рёбрами сверху и снизу). Отлично, у нас уже есть два новых LR-ребра! Выберем одно из них, нижнее, и назовём его “базой” (в коде — base).<br record-id=\"537\"/><!--record-id-538-->\n<br record-id=\"539\"/><!--record-id-540-->\n<div record-id=\"541\" style=\"text-align:center;\"><img record-id=\"542\" src=\"https://habrastorage.org/web/9f5/ec4/e19/9f5ec4e19ac842928c7c512c0a8864c0.gif\"/></div><br record-id=\"543\"/><!--record-id-544-->\nТеперь мы должны добавить следующее ребро, смежное с base, при этом один из его концов совпадает с концом base, а другим концом является точка либо из L, либо из R. Собственно, нам и нужно решить какой из двух вариантов является правильным. Начнём с правой стороны. Первым кандидатом будет точка, связанная с правым концом base RR-ребром с наименьшим углом по часовой стрелке относительно base. Следующими по очереди кандидатами будут точки R, соединённые с правой вершиной base RR-рёбрами с увеличивающимся значением угла относительно base, как показано на иллюстрации:<br record-id=\"545\"/><!--record-id-546-->\n<br record-id=\"547\"/><!--record-id-548-->\n<div record-id=\"549\" style=\"text-align:center;\"><img record-id=\"550\" src=\"https://habrastorage.org/web/8ae/9f7/71d/8ae9f771d94746b99bbe327b8adcb7d4.gif\"/></div><br record-id=\"551\"/><!--record-id-552-->\nТеперь для каждого кандидата нужно проверить два условия:<br record-id=\"553\"/><!--record-id-554-->\n<br record-id=\"555\"/><!--record-id-556-->\n<ol record-id=\"557\"><!--record-id-558-->\n<li record-id=\"559\"><!--record-id-560-->Угол между base и кандидатом не должен превышать 180 градусов (нас интересуют только те точки, что “выше” base).</li><!--record-id-561-->\n<li record-id=\"562\"><!--record-id-563-->Окружность, описанная вокруг base и точки-кандидата не должна содержать в себе следующую точку-кандидат.</li><!--record-id-564-->\n</ol><br record-id=\"565\"/><!--record-id-566-->\n<div record-id=\"567\" style=\"text-align:center;\"><img record-id=\"568\" src=\"https://habrastorage.org/web/379/d5e/1a7/379d5e1a72174becb64f991f0b3455fc.gif\"/></div><br record-id=\"569\"/><!--record-id-570-->\nВ зависимости от конфигурации вершин здесь может быть несколько вариантов:<br record-id=\"571\"/><!--record-id-572-->\n<ul record-id=\"573\"><!--record-id-574-->\n<li record-id=\"575\"><!--record-id-576-->Нашлась точка-кандидат, которая удовлетворяет обоим критериям. Отлично, это и будет наш выбор для правой стороны.</li><!--record-id-577-->\n<li record-id=\"578\"><!--record-id-579-->Если не выполняется требование #1 (угол < 180 градусов), значит мы уже достигли “верха”, и более нет необходимости выбирать точки с прав�
- ияние и нужно подниматься дальше по стеку. Если нашлась одна точка (только слева или только справа), мы добавляем LR-ребро с концом в ней. Если точки нашлись с обеих сторон, мы повторяем тест с окружностью: строим описанную окружность вокруг base и точки-кандидата из L и R. Выбираем ту точку, чья окружность не содержит кандидата из противоположной половины. Факт: такая точка всегда будет единственной, если только четыре вершины не образуют прямоугольник. В этом случае можно выбрать любой из вариантов.<br record-id=\"598\"/><!--record-id-599-->\n<br record-id=\"600\"/><!--record-id-601-->\n<div record-id=\"602\" style=\"text-align:center;\"><img record-id=\"603\" src=\"https://habrastorage.org/web/232/c88/5ec/232c885ec6384eb7a3e8422e4139ab7e.gif\"/></div><br record-id=\"604\"/><!--record-id-605-->\nВ данном примере мы выбираем кандидата из L, потому что соответствующая окружность не содержит точку-кандидата из R. <br record-id=\"606\"/><!--record-id-607-->\nПосле того как новое LR-ребро добавлено, оно становится новой “базой” (base). Повторяем обновление base пока не достигнем верхнего ребра выпуклой оболочки:<br record-id=\"608\"/><!--record-id-609-->\n<br record-id=\"610\"/><!--record-id-611-->\n<div record-id=\"612\" style=\"text-align:center;\"><img record-id=\"613\" src=\"https://habrastorage.org/web/ad8/0b5/218/ad80b521856f48fa9a87b46fc6a15a71.gif\"/></div><br record-id=\"614\"/><!--record-id-615-->\nОказывается, если наш алгоритм будет аккуратно следовать описанным правилам, то после слияния левой и правой половины мы получим валидную триангуляцию Делоне для L∪R.<br record-id=\"616\"/><!--record-id-617-->\n”Я нашёл этому поистине чудесное доказательство, но поля данной книги слишком узки для него.” :) На самом деле, если вам интересно, откуда взялись все утверждения, например про необходимость удаления рёбер, рекомендую исследовать <a record-id=\"618\" href=\"http://www.sccg.sk/~samuelcik/dgs/quad_edge.pdf\"><!--record-id-619-->работу Гибаса и Столфи</a><!--record-id-620-->, ключевые леммы под номерами 9.2, 9.3, 9.4, 8.3.<br record-id=\"621\"/><!--record-id-622-->\n<br record-id=\"623\"/><!--record-id-624-->\nЧто ж, теперь соберём всё вместе и посмотрим что получится:<br record-id=\"625\"/><!--record-id-626-->\n<br record-id=\"627\"/><!--record-id-628-->\n<div record-id=\"629\" style=\"text-align:center;\"><img record-id=\"630\" src=\"https://habrastorage.org/web/b4b/621/55a/b4b62155a8834025859113e5181236af.gif\"/></div><br record-id=\"631\"/><!--record-id-632-->\nЭто визуализация работы алгоритма, который у меня получился. Здесь оранжевым отмечено ребро base, зелёным — LL-кандидат, синим — RR-кандидат. Красным помечены рёбра, которые не удовлетворяют критерию #2 и будут удалены.<br record-id=\"633\"/><!--record-id-634-->\n<br record-id=\"635\"/><!--record-id-636-->\nПока опис
- astorage.org/getpro/habr/formulas/e85/9e3/389/e859e33895fd579701b4da0969f53c18.svg\" alt=\"$O(nlogn)$\" data-tex=\"inline\"/><!--record-id-648--> и близка к линейной; нужен какой-нибудь особенно больной датасет, чтобы заставить алгоритм перестраивать триангуляцию с нуля на каждой стадии слияния.<br record-id=\"649\"/><!--record-id-650-->\nЕщё одно замечание — из полученной триангуляции легко получить и выпуклую оболочку, и диаграмму Вороного для соответствующего множества точек. Приятный бонус.<br record-id=\"651\"/><!--record-id-652-->\n<br record-id=\"653\"/><!--record-id-654-->\nИнтересно сказать пару слов про структуру данных, которую я использовал для работы с графом. Обычно графы представляют как списки или матрицы смежности, но в данной задаче нас интересует не только связность, но и некоторые геометрические свойства графа. Вот что у меня получилось:<br record-id=\"655\"/><!--record-id-656-->\n<br record-id=\"657\"/><!--record-id-658-->\n<pre record-id=\"659\"><code record-id=\"660\" class=\"cpp hljs\"><span record-id=\"661\" class=\"hljs-keyword\"><!--record-id-662-->struct</span><!--record-id-663--> TriEdge\n{\n <span record-id=\"664\" class=\"hljs-keyword\"><!--record-id-665-->uint16_t</span><!--record-id-666--> origPnt; <span record-id=\"667\" class=\"hljs-comment\"><!--record-id-668-->// index of edge's origin point</span><!--record-id-669-->\n EdgeIdx symEdge; <span record-id=\"670\" class=\"hljs-comment\"><!--record-id-671-->// index of pair edge, with same endpoints and opposite direction</span><!--record-id-672-->\n EdgeIdx nextCcwEdge; <span record-id=\"673\" class=\"hljs-comment\"><!--record-id-674-->// next counterclockwise (CCW) edge around the origin</span><!--record-id-675-->\n EdgeIdx prevCcwEdge; <span record-id=\"676\" class=\"hljs-comment\"><!--record-id-677-->// previous CCW edge around the origin (or next CW edge)</span><!--record-id-678-->\n};</code></pre><br record-id=\"679\"/><!--record-id-680-->\nЭто в некотором роде урезанная версия структуры, которую предлагают Гибас и Столфи. Как говорится, всё гениальное просто. Для каждого ребра мы храним точку, из которой оно “выходит” (origin), индекс его парного ребра, направленного в противоположную сторону, а также индексы следующего и предыдущего ребра “вокруг” origin-точки. По сути получается двусвязный список, но так как мы поддерживаем относительное расположение рёбер вокруг их концов, многие шаги алгоритма кодируются просто элементарно.<br record-id=\"681\"/><!--record-id-682-->\nВ <a record-id=\"683\" href=\"https://www.cs.cmu.edu/~quake/triangle.html\"><!--record-id-684-->некоторых других работах</a><!--record-id-685--> предлагается хранить не рёбра, а сразу треугольники. Я пробовал экспериментировать с таким вариантом, и могу сказать, что объем кода вырастает в разы, т.к. приходится вводить многочисленные костыли для особых случаев (вырожденные треугольники, точки на бе
- ть никакие рёбра. Но так бывает не всегда.<br record-id=\"702\"/><!--record-id-703-->\n<div record-id=\"704\" style=\"text-align:center;\"><img record-id=\"705\" src=\"https://habrastorage.org/web/b83/a96/f64/b83a96f643824c8aa4fe61f21a9ae3e2.gif\"/></div><br record-id=\"706\"/><!--record-id-707-->\nУже интереснее. Количество точек на стороне решётки стало нечётным и из-за этого алгоритм вынужден делать довольно много исправлений. Как насчёт окружности из точек?<br record-id=\"708\"/><!--record-id-709-->\n<br record-id=\"710\"/><!--record-id-711-->\n<div record-id=\"712\" style=\"text-align:center;\"><img record-id=\"713\" src=\"https://habrastorage.org/web/817/3e1/fe3/8173e1fe350243c8b80f9003b26e4818.gif\"/></div><br record-id=\"714\"/><!--record-id-715-->\nИнтересно! Но мне больше нравится с точкой в центре:<br record-id=\"716\"/><!--record-id-717-->\n<br record-id=\"718\"/><!--record-id-719-->\n<div record-id=\"720\" style=\"text-align:center;\"><img record-id=\"721\" src=\"https://habrastorage.org/web/934/43a/711/93443a7116b84070a14eaa70a5007311.gif\"/></div><br record-id=\"722\"/><!--record-id-723-->\n<br record-id=\"724\"/><!--record-id-725-->\nЯ думаю, к этому моменту статья содержит достаточное количество GIF-анимаций. Чтобы у читателей с <a record-id=\"726\" href=\"https://geektimes.ru/post/290377/\"><!--record-id-727-->ограничениями по трафику</a><!--record-id-728--> осталось хоть немного интернета после загрузки этой статьи, я залил остальные анимации на видео-хостинг.<br record-id=\"729\"/><!--record-id-730-->\n<strong record-id=\"731\"><!--record-id-732-->Рекомендуется залипать в 1080p и 60fps, это оригинальное разрешение видео. Причём лучше развернуть на весь экран, иначе рёбра графа получаются алиасными.</strong><br record-id=\"733\"/><!--record-id-734-->\n<br record-id=\"735\"/><!--record-id-736-->\n<div record-id=\"737\" class=\"oembed\"><div record-id=\"738\"><div record-id=\"739\" style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.2493%;\"><iframe record-id=\"740\" src=\"https://www.youtube.com/embed/G3US_HTZ1-o?rel=0&showinfo=1\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><br record-id=\"741\"/><!--record-id-742-->\nЯ осознаю, что эти видео интересны далеко не всем, но на меня они производят такой же эффект, как заставка “трубопровод” в старых версиях Windows. Оторваться сложно.<br record-id=\"743\"/><!--record-id-744-->\n<br record-id=\"745\"/><!--record-id-746-->\n<div record-id=\"747\" class=\"oembed\"><div record-id=\"748\"><div record-id=\"749\" style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.2493%;\"><iframe record-id=\"750\" src=\"https://www.youtube.com/embed/6WLeexsfNCg?rel=0&showinfo=1\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><br record-id=\"751\"/><!--record-id-752-->\nЭти анимации стали главной причиной, почему статья писалась так долго. Note to self: в следующий раз обойтись дебаггером и трейсами, делать дебажные визуализации опасно :) Если кто-то хочет посмотреть на работу алг�
- вспомнить для чего изначально создавался алгоритм. Мы собирались генерировать полигональный меш из данных с depth-сенсора. Что ж, давайте возьмём облако точек с сенсора, спроецируем на плоскость камеры и запустим алгоритм триангуляции.<br record-id=\"765\"/><!--record-id-766-->\n<br record-id=\"767\"/><!--record-id-768-->\n<div record-id=\"769\" class=\"oembed\"><div record-id=\"770\"><div record-id=\"771\" style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.2493%;\"><iframe record-id=\"772\" src=\"https://www.youtube.com/embed/-qH7y5U7ycE?rel=0&showinfo=1\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><!--record-id-773-->(Не спрашивайте меня сколько рендерился этот ролик. Бедный ffmpeg.)<br record-id=\"774\"/><!--record-id-775-->\n<br record-id=\"776\"/><!--record-id-777-->\nИтак, отлично! Хотя на данном этапе у нас есть только граф из точек и рёбер, из него элементарным образом можно сгенерировать массив треугольников. Осталось только отфильтровать некрасивые полигоны вытянутые по Z-координате, которые возникают на границах объектов и сделать простой проигрыватель на OpenGL:<br record-id=\"778\"/><!--record-id-779-->\n<br record-id=\"780\"/><!--record-id-781-->\n<div record-id=\"782\" class=\"oembed\"><div record-id=\"783\"><div record-id=\"784\" style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.2493%;\"><iframe record-id=\"785\" src=\"https://www.youtube.com/embed/bf1bJuOdQ6k?rel=0&showinfo=1\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><br record-id=\"786\"/><!--record-id-787-->\nЗдесь хорошо видно, что первая версия граббера была очень сырой: половина кадров не успели записаться, из-за этого картинка замирает. Короче, едва ли режиссёрский шедевр. Но это не важно — алгоритмы наконец-то ожили! Для меня это было настоящее “Прибытие поезда”!<br record-id=\"788\"/><!--record-id-789-->\n<br record-id=\"790\"/><!--record-id-791-->\nПосле этого я снял ещё пару роликов, я залил их на ресурс Sketchfab в формате timeframe animation, мне кажется так будет интереснее.<br record-id=\"792\"/><!--record-id-793-->\n<br record-id=\"794\"/><!--record-id-795-->\n<a record-id=\"796\" href=\"https://skfb.ly/6soGq\"><!--record-id-797-->Датасет #2 в WebGL</a><br record-id=\"798\"/><!--record-id-799-->\n<a record-id=\"800\" href=\"https://skfb.ly/6soGr\"><!--record-id-801-->Датасет #3 в WebGL</a><br record-id=\"802\"/><!--record-id-803-->\n<br record-id=\"804\"/><!--record-id-805-->\nЧто ж, пайплайн заработал и стало ясно, что дальнейшее развитие ограничивают возможности железа. Действительно, планшет Tango позволяет снимать только с очень низкой частотой кадров (5 fps), а ведь основная идея была в том, чтобы запечатлеть динамический характер сцен. Есть ещё одно существенное ограничение: в Tango не �
- deo/blob/master/src/libs/realsense/src/realsense_grabber.cpp\"><!--record-id-812-->C++</a><!--record-id-813-->, а не на Java) и продолжить эксперименты.<br record-id=\"814\"/><!--record-id-815-->\n<br record-id=\"816\"/><!--record-id-817-->\nСразу же выяснилось, что с моим пайплайном есть небольшая проблема. С увеличенным разрешением и высокой частотой съемки, которую позволяет R200, производительности уже не хватало, чтобы генерировать меш на каждом кадре в реалтайме. Профилировка показала, что узкое место — это конечно же триангуляция. Первоначальная версия была написана достаточно “расслабленно”, с динамическим выделением памяти, использованием STL и т.п. Кроме того, первая версия работала с координатами типа float, хотя в моей задаче точки всегда имеют целочисленные координаты (позиции в пикселях на картинке). Так что неудивительно, что хороший с точки зрения асимптотики алгоритм работал на моём ноутбуке до 30 миллисекунд на тестовом кадре с Tango (около 12000 точек). Потенциал для ускорения был очевиден и я увлечённо засел оптимизировать. Развивались события как-то так:<br record-id=\"818\"/><!--record-id-819-->\n<br record-id=\"820\"/><!--record-id-821-->\n<ul record-id=\"822\"><!--record-id-823-->\n<li record-id=\"824\"><!--record-id-825-->Первым делом на алтарь скорости отправились числа с плавающей точкой. Все алгоритмы (где это возможно) перешли на целочисленные вычисления.</li><!--record-id-826-->\n<li record-id=\"827\"><!--record-id-828-->Вторым на очереди было динамическое выделение памяти. Теперь вся память для алгоритма выделялась один раз, “на худший случай”, и затем переиспользовалась. Не самое эффективное решение с точки зрения потребления памяти (в духе ACM ICPC), но это дало значительное ускорение.</li><!--record-id-829-->\n<li record-id=\"830\"><!--record-id-831-->На сайте проекта посвящённого численной симуляции землетрясений был найден замечательный <a record-id=\"832\" href=\"https://www.cs.cmu.edu/~quake/robust.html\"><!--record-id-833-->тест</a><!--record-id-834--> на нахождение точки внутри описанной окружности треугольника: <br record-id=\"835\"/><!--record-id-836-->\n<br record-id=\"837\"/><!--record-id-838-->\nВот его реализация:<br record-id=\"839\"/><!--record-id-840-->\n<pre record-id=\"841\"><code record-id=\"842\" class=\"cpp hljs\"><span record-id=\"843\" class=\"hljs-function\"><!--record-id-844-->FORCE_INLINE <span record-id=\"845\" class=\"hljs-keyword\"><!--record-id-846-->bool</span><!--record-id-847--> <span record-id=\"848\" class=\"hljs-title\"><!--record-id-849-->inCircle</span><span record-id=\"850\" class=\"hljs-params\"><!--record-id-851-->(<span record-id=\"852\" class=\"hljs-keyword\"><!--record-id-853-->int</span><!--record-id-854--> x1, <span record-id=\"855\" class=\"hljs-keyword\"><!--record-id-856-->int</span><!--record-id-857--> y1, <span record-i
- -id-888-->int</span><!--record-id-889--> p1p_x = x1 - px;\n <span record-id=\"890\" class=\"hljs-keyword\"><!--record-id-891-->const</span><!--record-id-892--> <span record-id=\"893\" class=\"hljs-keyword\"><!--record-id-894-->int</span><!--record-id-895--> p1p_y = y1 - py;\n\n <span record-id=\"896\" class=\"hljs-keyword\"><!--record-id-897-->const</span><!--record-id-898--> <span record-id=\"899\" class=\"hljs-keyword\"><!--record-id-900-->int</span><!--record-id-901--> p2p_x = x2 - px;\n <span record-id=\"902\" class=\"hljs-keyword\"><!--record-id-903-->const</span><!--record-id-904--> <span record-id=\"905\" class=\"hljs-keyword\"><!--record-id-906-->int</span><!--record-id-907--> p2p_y = y2 - py;\n\n <span record-id=\"908\" class=\"hljs-keyword\"><!--record-id-909-->const</span><!--record-id-910--> <span record-id=\"911\" class=\"hljs-keyword\"><!--record-id-912-->int</span><!--record-id-913--> p3p_x = x3 - px;\n <span record-id=\"914\" class=\"hljs-keyword\"><!--record-id-915-->const</span><!--record-id-916--> <span record-id=\"917\" class=\"hljs-keyword\"><!--record-id-918-->int</span><!--record-id-919--> p3p_y = y3 - py;\n\n <span record-id=\"920\" class=\"hljs-keyword\"><!--record-id-921-->const</span><!--record-id-922--> <span record-id=\"923\" class=\"hljs-keyword\"><!--record-id-924-->int64_t</span><!--record-id-925--> p1p = p1p_x * p1p_x + p1p_y * p1p_y;\n <span record-id=\"926\" class=\"hljs-keyword\"><!--record-id-927-->const</span><!--record-id-928--> <span record-id=\"929\" class=\"hljs-keyword\"><!--record-id-930-->int64_t</span><!--record-id-931--> p2p = p2p_x * p2p_x + p2p_y * p2p_y;\n <span record-id=\"932\" class=\"hljs-keyword\"><!--record-id-933-->const</span><!--record-id-934--> <span record-id=\"935\" class=\"hljs-keyword\"><!--record-id-936-->int64_t</span><!--record-id-937--> p3p = p3p_x * p3p_x + p3p_y * p3p_y;\n\n <span record-id=\"938\" class=\"hljs-comment\"><!--record-id-939-->// determinant of matrix, see paper for the reference</span><!--record-id-940-->\n <span record-id=\"941\" class=\"hljs-keyword\"><!--record-id-942-->const</span><!--record-id-943--> <span record-id=\"944\" class=\"hljs-keyword\"><!--record-id-945-->int64_t</span><!--record-id-946--> res = p1p_x * (p2p_y * p3p - p2p * p3p_y)\n - p1p_y * (p2p_x * p3p - p2p * p3p_x)\n + p1p * (p2p_x * p3p_y - p2p_y * p3p_x);\n\n assert(<span record-id=\"947\" class=\"hljs-built_in\"><!--record-id-948-->std</span><!--record-id-949-->::<span record-id=\"950\" class=\"hljs-built_in\"><!--record-id-951-->abs</span><!--record-id-952-->(res) < <span record-id=\"953\" class=\"hljs-built_in\"><!--record-id-954-->std</span><!--record-id-955-->::numeric_limits<int64>::max() / <span record-id=\"956\" class=\"hljs-number\"><!--record-id-957-->100</span><!--record-id-958-->);\n\n <span record-id=\"959\" class=\"hljs-keyword\"><!--record-id-960-->return</span><!--record-id-961--> res < <span record-id=\"962\" class=\"hljs-number\"><!--record-id-963-->0</span><!--record-id-964-->;\n}</code></pre></li><!--record-id-965-->\n</ul><br record-id=\"966\"/><!--record-id-967-->\nЭти три мероприятия дали очень значительное ускорение, с 30000+ до 6100 микросекунд на кадр. Но можно было ускорить ещё:<br record-id=\"968\"/><!--record-id-969-->\n<br record-id=\"970\"/><!--record-id-971-->\n<ul record-id=\"972\"><!--record-id-973-->\n<li record-id=\"974\"><!--record-id-975-->Сделал некоторым небольшим функциям __forceinline, 6100 -> 5700 микросекунд, редкий случай когда компилятор сам не догадался. </li><!--record-id-976-->\n<li record-id=\"977\"><!--record-id-978-->Удалил #pragma pack(push, 1) для стуктуры TriEdge. И почему я решил, что с запаковкой будет быстрее? 5700 -> 4800 микросекунд.</li><!--record-
- br record-id=\"983\"/><!--record-id-984-->\nИтого получилось ускорить код почти в 10 раз, и я думаю это не предел. Кстати, я не поленился собрать код для Android, и на схожей задаче ARM процессор Tango работал около 10 миллисекунд на кадр, т.е. 100 fps! Короче, получился даже немного overkill, но если кому-то нужна очень быстрая “целочисленная” триангуляция Делоне, то добро пожаловать в репозиторий.<br record-id=\"985\"/><!--record-id-986-->\n<br record-id=\"987\"/><!--record-id-988-->\nТеперь мои алгоритмы были готовы к R200, и вот первый датасет, который я снял:<br record-id=\"989\"/><!--record-id-990-->\n<br record-id=\"991\"/><!--record-id-992-->\n<div record-id=\"993\" class=\"oembed\"><div record-id=\"994\"><div record-id=\"995\" style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 75.0019%;\"><iframe record-id=\"996\" src=\"https://www.youtube.com/embed/7gJUIYt-Lew?rel=0&showinfo=1\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><br record-id=\"997\"/><!--record-id-998-->\n<br record-id=\"999\"/><!--record-id-1000-->\nЗа ним, естественно, последовали другие. Не буду прикладывать видео моего OpenGL viewer’а, по-моему интереснее смотреть сразу на Sketchfab: <a record-id=\"1001\" href=\"https://skfb.ly/6s6Ar\"><!--record-id-1002-->skfb.ly/6s6Ar</a><br record-id=\"1003\"/><!--record-id-1004-->\n<br record-id=\"1005\"/><!--record-id-1006-->\nРекомендуется открывать не больше одного ролика за раз, т.к. Sketchfab грузит в память сразу все кадры датасета, и вообще viewer у них не очень быстрый. Кстати, вот обещанный 4D котик (Вася):<br record-id=\"1007\"/><!--record-id-1008-->\n<br record-id=\"1009\"/><!--record-id-1010-->\n<ul record-id=\"1011\"><!--record-id-1012-->\n<li record-id=\"1013\"><a record-id=\"1014\" href=\"https://skfb.ly/6s6AC\"><!--record-id-1015-->skfb.ly/6s6AC</a></li><!--record-id-1016-->\n<li record-id=\"1017\"><a record-id=\"1018\" href=\"https://skfb.ly/6s6AJ\"><!--record-id-1019-->skfb.ly/6s6AJ</a></li><!--record-id-1020-->\n</ul><br record-id=\"1021\"/><!--record-id-1022-->\nПёс Тотошка был не очень счастлив, что на него светят лазером. Но выбора у него не было:<br record-id=\"1023\"/><!--record-id-1024-->\n<br record-id=\"1025\"/><!--record-id-1026-->\n<ul record-id=\"1027\"><!--record-id-1028-->\n<li record-id=\"1029\"><a record-id=\"1030\" href=\"https://skfb.ly/6s6AP\"><!--record-id-1031-->skfb.ly/6s6AP</a></li><!--record-id-1032-->\n<li record-id=\"1033\"><a record-id=\"1034\" href=\"https://skfb.ly/6s6BH\"><!--record-id-1035-->skfb.ly/6s6BH</a></li><!--record-id-1036-->\n</ul><br record-id=\"1037\"/><!--record-id-1038-->\nЕщё несколько роликов есть в моём аккаунте на Sketchfab. Конечно, качество моделей можно улучшить если тщательно отфильтровать шумы от сенсора, можно уменьшить количество точек, чтобы проигрыватель на сайте не так тормозил, и т.п. Но как говорится, нет предела совершенству; я и так потратил на этот pet project много времени :)<br record-id=\"1039\"/><!--record-id-1040-->\nДа и вообще, учитывая агрессивное наступление AR и VR,
- ght: 0; position: relative; padding-bottom: 56.2493%;\"><iframe record-id=\"1058\" src=\"https://www.youtube.com/embed/SH3MKjwgI80?rel=0&showinfo=1&start=99\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen=\"\" scrolling=\"no\"></iframe></div></div></div><br record-id=\"1059\"/><!--record-id-1060-->\nСогласитесь, выглядит весьма впечатляюще! В общем, за будущее free-viewpoint video переживать не приходится.<br record-id=\"1061\"/><!--record-id-1062-->\n<br record-id=\"1063\"/><!--record-id-1064-->\nНапоследок нужно сказать, что весь код написанный для этого проекта доступен на <a record-id=\"1065\" href=\"https://github.com/alex-petrenko/4dvideo\"><!--record-id-1066-->Github</a><!--record-id-1067--> под свободной лицензией. Хотя сгенерированные 4D ролики получились едва ли впечатляющими, у алгоритмов всё равно есть потенциал для дальнейшего использования и развития.<br record-id=\"1068\"/><!--record-id-1069-->\nГлавное преимущество описанного в статье подхода состоит в том, что 3D сцена генерируется на лету в realtime, в самом проигрывателе. Из-за этого в сжатом виде ролики могут занимать очень мало места, по сути почти столько же, сколько аналогичное традиционное видео. <br record-id=\"1070\"/><!--record-id-1071-->\n<br record-id=\"1072\"/><!--record-id-1073-->\nДействительно, обычное RGB видео нужно нам только для текстур, а 3D координаты точек можно сохранять как разреженные карты глубины и тоже кодировать в видео-файл, уже в grayscale. Таким образом, для рендеринга одного кадра в 3D нам нужны только кадры из двух видео и немного метаданных (параметры камеры, и т.п.). На основе этого можно сделать прикольное приложение, скажем, Skype в augmented reality. Собеседника снимает depth-камера, а ваш iPhone в реальном времени рендерит говорящую 3D голову с помощью ARKit. Почти как голограммы из Star Wars.<br record-id=\"1074\"/><!--record-id-1075-->\n<br record-id=\"1076\"/><!--record-id-1077-->\nЕсть ещё вариант, который может пригодиться пользователям Tango. Дело в том, что Tango SDK предоставляет данные в виде облаков точек в 3D (как я и описывал выше), при этом для многих алгоритмов хочется иметь плотную карту глубины (depth map). Самый распространённый способ её получения — спроецировать точки на фокальную плоскость и проинтерполировать значения глубины между соседними точками, в которых оно известно. Так вот комбинация триангуляции Делоне + OpenGL позволяет делать эту интерполяцию точно и эффективно. За счёт того, что растеризация происходит на GPU, можно генерировать карты глубины в высоком разрешении даже на смартфоне.<br record-id=\"1078\"/><!--record-id-1079-->\n<br record-i
- record-id-1113--> </li><!--record-id-1114-->\n</ul></div><!--record-id-1115-->\n <!--record-id-1117-->\n\n <div record-id=\"1118\" class=\"post_additionals\"><!--record-id-1119-->\n <div record-id=\"1120\" class=\"bottom_buttons_panel panel_vote\" id=\"voting-wjt_333532\"><!--record-id-1121-->\n <div record-id=\"1122\" class=\"label\"><!--record-id-1123-->Проголосовать:</div><!--record-id-1124-->\n <div record-id=\"1125\" class=\"voting-wjt voting-wjt_mobile js-voting \"><!--record-id-1126-->\n <button record-id=\"1127\" type=\"button\" class=\"voting-wjt__button voting-wjt__button_plus voting-wjt__button_disabled\" data-message=\"Голосовать могут только зарегистрированные пользователи. \"><!--record-id-1128-->\n <svg record-id=\"1129\" class=\"icon-svg icon-svg_arrow-up\" aria-hidden=\"true\" aria-labelledby=\"title\" version=\"1.1\" role=\"img\" width=\"15\" height=\"24\" viewBox=\"0 0 15 24\"><path record-id=\"1130\" d=\"M6.802.129l-6.709 7.637c-.211.241-.037.615.289.615h3.629c.21 0 .38.167.38.372v14.875c0 .205.169.372.379.372h4.64c.21 0 .379-.167.379-.372v-14.876c0-.205.17-.372.38-.372h3.63c.325 0 .5-.373.289-.615l-6.709-7.637c-.153-.171-.427-.171-.578.001z\"></path></svg><!--record-id-1131-->\n </button><!--record-id-1132-->\n\n <div record-id=\"1133\" class=\"voting-wjt__counter voting-wjt__counter_post voting-wjt__counter_positive js-mark\"><!--record-id-1134-->\n <span record-id=\"1135\" class=\"voting-wjt__counter-score js-score\" title=\"Общий рейтинг 79: ↑78 и ↓1\"><!--record-id-1136-->+77</span><!--record-id-1137-->\n </div><!--record-id-1138-->\n\n <button record-id=\"1139\" type=\"button\" class=\"voting-wjt__button voting-wjt__button_minus voting-wjt__button_disabled\" data-message=\"Голосовать могут только зарегистрированные пользователи. \"><!--record-id-1140-->\n <svg record-id=\"1141\" class=\"icon-svg icon-svg_arrow-down\" aria-hidden=\"true\" aria-labelledby=\"title\" version=\"1.1\" role=\"img\" width=\"15\" height=\"24\" viewBox=\"0 0 15 24\"><path record-id=\"1142\" d=\"M14.574 16.233c.211-.241.037-.615-.289-.615h-3.629c-.21 0-.38-.167-.38-.372v-14.875c0-.205-.169-.372-.379-.372h-4.64c-.21 0-.379.167-.379.372v14.876c0 .205-.17.372-.38.372h-3.63c-.325 0-.5.373-.289.615l6.709 7.637c.153.171.427.171.578-.001l6.709-7.637z\"></path></svg><!--record-id-1143-->\n </button><!--record-id-1144-->\n </div><!--record-id-1145-->\n</div><!--record-id-1146-->\n <div record-id=\"1147\" class=\"bottom_buttons_panel panel_share\"><!--record-id-1148-->\n <div record-id=\"1149\" class=\"share_to\"><!--record-id-1150-->\n <div record-id=\"1151\" class=\"label\"><!--record-id-1152-->Поделиться:</div><!--record-id-1153-->\n <div record-id=\"1154\" class=\"share_buttons\"><!--record-id-1155-->\n <a record-id=\"1156\" href=\"https://www.facebook.com/sharer/sharer.php?u=https://habrahabr.ru/post/333532/?mobile=no\" title=\"Опубликовать ссылку в Facebook\" onclick=\"window.open(this.href, 'Опубликовать ссылку в Facebook', 'width=640,height=436,toolbar=0,status=0'); return false\" class=\"fb\"></a><!--record-id-1157-->\n <a record-id=\"1158\" href=\"https://twitter.com/intent/tweet?text=Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне+https://habrahabr.ru/post/333532/?mobile=no+via+%40habrahabr\" title=\"Опубликовать ссылку в Twitter\" class=\"tw\" onclick=\"window.open(this.href, 'Опубликовать ссылку в Twitter', 'width=800,height=300,resizable=yes,toolbar=0,status=0'); return false\"></a><!--record-id-1159-->\n <a record-id=\"1160\" href=\"https://vk.com/share.php?url=https://habrahabr.ru/post/333532/?mobile=no\" title=\"Опубликовать ссылку во ВКонт
- d-id=\"1164\" class=\"save_to\"><!--record-id-1165-->\n <div record-id=\"1166\" class=\"label\"><!--record-id-1167-->Сохранить:</div><!--record-id-1168-->\n <div record-id=\"1169\" class=\"save_buttons\"><!--record-id-1170-->\n \n <a record-id=\"1171\" href=\"https://getpocket.com/edit?url=https://habrahabr.ru/post/333532/?mobile=no\" target=\"_blank\" title=\"Сохранить в Pocket\" class=\"pocket pocket-btn\"></a><!--record-id-1172-->\n </div><!--record-id-1173-->\n </div><!--record-id-1174-->\n</div><!--record-id-1175-->\n </div><!--record-id-1176-->\n\n <div record-id=\"1177\" class=\"buttons\"><!--record-id-1178-->\n <a record-id=\"1179\" href=\"https://m.habrahabr.ru/post/333532/comments/\" class=\"btn btn-wide\"><!--record-id-1180-->\n Комментарии (14)\n </a><!--record-id-1181-->\n </div><!--record-id-1182-->\n </div><!--record-id-1183-->\n\n <div record-id=\"1184\" class=\"banner_header\"><!--record-id-1185-->\n <!--record-id-1186--><!-- AdRiver code START. Type:AjaxJS Site: m.habr BN:2 --><!--record-id-1187-->\n <div record-id=\"1188\" id=\"adriver_banner_632548472\" style=\"margin: 0 auto;\" title=\"\"></div><!--record-id-1189-->\n <!--record-id-1191-->\n <!--record-id-1192--><!-- AdRiver code END --><!--record-id-1193-->\n</div><!--record-id-1194-->\n\n\n \n\n <div record-id=\"1195\" class=\"similar_posts\"><!--record-id-1196-->\n <div record-id=\"1197\" class=\"title\"><!--record-id-1198-->Похожие публикации</div><!--record-id-1199-->\n <div record-id=\"1200\" class=\"posts\"><!--record-id-1201-->\n <div record-id=\"1202\" class=\"post\"><!--record-id-1203-->\n <a record-id=\"1204\" href=\"https://m.habrahabr.ru/post/311502/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_similar_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1205-->\n <h3 record-id=\"1206\" class=\"title\"><!--record-id-1207-->Как я читал показания датчиков через SNMP (Python+AgentX+systemd+Raspberry Pi) и соорудил ещё одну мониторилку</h3><!--record-id-1208-->\n\n \n \n \n \n\n <div record-id=\"1209\" class=\"meta\"><!--record-id-1210-->\n <span record-id=\"1211\" class=\"author\"><!--record-id-1212-->homecreate</span><!--record-id-1213-->\n •\n <span record-id=\"1214\" class=\"time\"><!--record-id-1215-->18 октября 2016 в 22:16</span><!--record-id-1216-->\n </div><!--record-id-1217-->\n </a><!--record-id-1218-->\n <a record-id=\"1219\" href=\"/post/311502/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_similar_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1220-->10</a><!--record-id-1221-->\n </div><!--record-id-1222-->\n <div record-id=\"1223\" class=\"post\"><!--record-id-1224-->\n <a record-id=\"1225\" href=\"https://m.habrahabr.ru/post/247555/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_similar_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1226-->\n <h3 record-id=\"1227\" class=\"title\"><!--record-id-1228-->Когда никто не читает Хабр</h3><!--record-id-1229-->\n\n \n \n \n \n\n <div record-id=\"1230\" class=\"meta\"><!--record-id-1231-->\n <span record-id=\"1232\" class=\"author\"><!--record-id-1233-->varagian</span><!--record-id-1234-->\n •\n <span record-id=\"1235\" class=\"time\"><!--record-id-1236-->13 января 2015 в 11:01</span><!--record-id-1237-->\n </div><!--record-id-1238-->\n </a><!--record-id-1239-->\n
- unction') { ga('send', 'event', 'tm_block', 'mobile_similar_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1241-->29</a><!--record-id-1242-->\n </div><!--record-id-1243-->\n <div record-id=\"1244\" class=\"post\"><!--record-id-1245-->\n <a record-id=\"1246\" href=\"https://m.habrahabr.ru/post/144497/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_similar_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1247-->\n <h3 record-id=\"1248\" class=\"title\"><!--record-id-1249-->Никто не читает правил</h3><!--record-id-1250-->\n\n \n \n \n \n\n <div record-id=\"1251\" class=\"meta\"><!--record-id-1252-->\n <span record-id=\"1253\" class=\"author\"><!--record-id-1254-->umputun</span><!--record-id-1255-->\n •\n <span record-id=\"1256\" class=\"time\"><!--record-id-1257-->24 мая 2012 в 22:26</span><!--record-id-1258-->\n </div><!--record-id-1259-->\n </a><!--record-id-1260-->\n <a record-id=\"1261\" href=\"/post/144497/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_similar_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1262-->53</a><!--record-id-1263-->\n </div><!--record-id-1264-->\n </div><!--record-id-1265-->\n </div><!--record-id-1266-->\n\n \n\n <div record-id=\"1267\" class=\"popular_posts\"><!--record-id-1268-->\n <div record-id=\"1269\" class=\"title\"><!--record-id-1270-->Популярное за сутки</div><!--record-id-1271-->\n <div record-id=\"1272\" class=\"posts\"><!--record-id-1273-->\n <div record-id=\"1274\" class=\"post\"><!--record-id-1275-->\n <a record-id=\"1276\" href=\"https://m.habrahabr.ru/post/333522/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1277-->\n <h3 record-id=\"1278\" class=\"title\"><!--record-id-1279-->Яндекс открывает технологию машинного обучения CatBoost</h3><!--record-id-1280-->\n\n \n \n \n \n\n <div record-id=\"1281\" class=\"meta\"><!--record-id-1282-->\n <span record-id=\"1283\" class=\"author\"><!--record-id-1284-->BarakAdama</span><!--record-id-1285-->\n •\n <span record-id=\"1286\" class=\"time\"><!--record-id-1287-->сегодня в 12:19</span><!--record-id-1288-->\n </div><!--record-id-1289-->\n </a><!--record-id-1290-->\n <a record-id=\"1291\" href=\"/post/333522/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1292-->19</a><!--record-id-1293-->\n </div><!--record-id-1294-->\n <div record-id=\"1295\" class=\"post\"><!--record-id-1296-->\n <a record-id=\"1297\" href=\"https://m.habrahabr.ru/post/333532/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1298-->\n <h3 record-id=\"1299\" class=\"title\"><!--record-id-1300-->Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне</h3><!--record-id-1301-->\n\n \n \n \n <div record-id=\"1302\" class=\"flag sandbox\"><!--record-id-1303-->из песочницы</div><!--record-id-1304--> \n\n <div record-id=\"1305\" class=\"meta\"><!--record-id-1306-->\n <span record-id=\"1307\" class=\"author\"><!--record-id-1308-->Petrenuk</span><!--record-id-1309-->\n •\n <span record-id=\"1310
- div><!--record-id-1313-->\n </a><!--record-id-1314-->\n <a record-id=\"1315\" href=\"/post/333532/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1316-->14</a><!--record-id-1317-->\n </div><!--record-id-1318-->\n <div record-id=\"1319\" class=\"post\"><!--record-id-1320-->\n <a record-id=\"1321\" href=\"https://m.habrahabr.ru/post/333510/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1322-->\n <h3 record-id=\"1323\" class=\"title\"><!--record-id-1324-->Paradigm — Дизайн-система Mail.Ru Group, часть 1: Визуальный язык</h3><!--record-id-1325-->\n\n \n \n \n \n\n <div record-id=\"1326\" class=\"meta\"><!--record-id-1327-->\n <span record-id=\"1328\" class=\"author\"><!--record-id-1329-->ASundiev</span><!--record-id-1330-->\n •\n <span record-id=\"1331\" class=\"time\"><!--record-id-1332-->вчера в 15:50</span><!--record-id-1333-->\n </div><!--record-id-1334-->\n </a><!--record-id-1335-->\n <a record-id=\"1336\" href=\"/post/333510/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1337-->15</a><!--record-id-1338-->\n </div><!--record-id-1339-->\n <div record-id=\"1340\" class=\"post\"><!--record-id-1341-->\n <a record-id=\"1342\" href=\"https://m.habrahabr.ru/post/332494/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1343-->\n <h3 record-id=\"1344\" class=\"title\"><!--record-id-1345-->Постквантовая реинкарнация алгоритма Диффи-Хеллмана: вероятное будущее (изогении)</h3><!--record-id-1346-->\n\n \n \n \n \n\n <div record-id=\"1347\" class=\"meta\"><!--record-id-1348-->\n <span record-id=\"1349\" class=\"author\"><!--record-id-1350-->Crittografo</span><!--record-id-1351-->\n •\n <span record-id=\"1352\" class=\"time\"><!--record-id-1353-->вчера в 16:57</span><!--record-id-1354-->\n </div><!--record-id-1355-->\n </a><!--record-id-1356-->\n <a record-id=\"1357\" href=\"/post/332494/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1358-->8</a><!--record-id-1359-->\n </div><!--record-id-1360-->\n <div record-id=\"1361\" class=\"post\"><!--record-id-1362-->\n <a record-id=\"1363\" href=\"https://m.habrahabr.ru/post/333528/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1364-->\n <h3 record-id=\"1365\" class=\"title\"><!--record-id-1366-->Как тысячи игроков Eve Online помогают в расшифровке человеческого тела</h3><!--record-id-1367-->\n\n \n \n \n <div record-id=\"1368\" class=\"flag sandbox\"><!--record-id-1369-->из песочницы</div><!--record-id-1370--> \n\n <div record-id=\"1371\" class=\"meta\"><!--record-id-1372-->\n <span record-id=\"1373\" class=\"author\"><!--record-id-1374-->ruslanjf</span><!--record-id-1375-->\n •\n <span record-id=\"1376\" class=\"time\"><!--record-id-1377-->вчера в 16:38</span><!--record-id-1378-->\n
- /\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_popular_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1382-->13</a><!--record-id-1383-->\n </div><!--record-id-1384-->\n </div><!--record-id-1385-->\n </div><!--record-id-1386-->\n\n\n\n \n\n <div record-id=\"1387\" class=\"geektimes_posts\"><!--record-id-1388-->\n <div record-id=\"1389\" class=\"title\"><!--record-id-1390-->Лучшее на Geektimes</div><!--record-id-1391-->\n <div record-id=\"1392\" class=\"posts\"><!--record-id-1393-->\n <div record-id=\"1394\" class=\"post\"><!--record-id-1395-->\n <a record-id=\"1396\" href=\"https://m.geektimes.ru/post/291189/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1397-->\n <h3 record-id=\"1398\" class=\"title\"><!--record-id-1399-->Мы нашли спутник МАЯК на орбите</h3><!--record-id-1400-->\n\n \n \n \n \n\n <div record-id=\"1401\" class=\"meta\"><!--record-id-1402-->\n <span record-id=\"1403\" class=\"author\"><!--record-id-1404-->Sterpa</span><!--record-id-1405-->\n •\n <span record-id=\"1406\" class=\"time\"><!--record-id-1407-->вчера в 22:29</span><!--record-id-1408-->\n </div><!--record-id-1409-->\n </a><!--record-id-1410-->\n <a record-id=\"1411\" href=\"https://m.geektimes.ru/post/291189/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1412-->60</a><!--record-id-1413-->\n </div><!--record-id-1414-->\n <div record-id=\"1415\" class=\"post\"><!--record-id-1416-->\n <a record-id=\"1417\" href=\"https://m.geektimes.ru/post/291185/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1418-->\n <h3 record-id=\"1419\" class=\"title\"><!--record-id-1420-->Ванечка</h3><!--record-id-1421-->\n\n \n \n \n \n\n <div record-id=\"1422\" class=\"meta\"><!--record-id-1423-->\n <span record-id=\"1424\" class=\"author\"><!--record-id-1425-->ragequit</span><!--record-id-1426-->\n •\n <span record-id=\"1427\" class=\"time\"><!--record-id-1428-->вчера в 20:57</span><!--record-id-1429-->\n </div><!--record-id-1430-->\n </a><!--record-id-1431-->\n <a record-id=\"1432\" href=\"https://m.geektimes.ru/post/291185/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1433-->110</a><!--record-id-1434-->\n </div><!--record-id-1435-->\n <div record-id=\"1436\" class=\"post\"><!--record-id-1437-->\n <a record-id=\"1438\" href=\"https://m.geektimes.ru/post/291195/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1439-->\n <h3 record-id=\"1440\" class=\"title\"><!--record-id-1441-->NucInc: к чёрту технику безопасности</h3><!--record-id-1442-->\n\n \n \n \n \n\n <div record-id=\"1443\" class=\"meta\"><!--record-id-1444-->\n <span record-id=\"1445\" class=\"author\"><!--record-id-1446-->Tiberius</span><!--record-id-1447-->\n •\n <span record-id=\"1448\" class=\"time\"><!--record-id-1449-->сегодня в 08:21</span><!--record-id-1450-->\n </div><!--record-id-1451-->\n </a><!--record-id-1452-->\n <a record-id
- mes.ru/post/291195/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1454-->37</a><!--record-id-1455-->\n </div><!--record-id-1456-->\n <div record-id=\"1457\" class=\"post\"><!--record-id-1458-->\n <a record-id=\"1459\" href=\"https://m.geektimes.ru/post/291177/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1460-->\n <h3 record-id=\"1461\" class=\"title\"><!--record-id-1462-->SoundCloud под угрозой закрытия, добровольцы собираются создать архивную копию всех файлов сервиса</h3><!--record-id-1463-->\n\n \n \n \n \n\n <div record-id=\"1464\" class=\"meta\"><!--record-id-1465-->\n <span record-id=\"1466\" class=\"author\"><!--record-id-1467-->marks</span><!--record-id-1468-->\n •\n <span record-id=\"1469\" class=\"time\"><!--record-id-1470-->вчера в 17:27</span><!--record-id-1471-->\n </div><!--record-id-1472-->\n </a><!--record-id-1473-->\n <a record-id=\"1474\" href=\"https://m.geektimes.ru/post/291177/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1475-->31</a><!--record-id-1476-->\n </div><!--record-id-1477-->\n <div record-id=\"1478\" class=\"post\"><!--record-id-1479-->\n <a record-id=\"1480\" href=\"https://m.geektimes.ru/post/291173/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'post'); }\" class=\"post_inner\"><!--record-id-1481-->\n <h3 record-id=\"1482\" class=\"title\"><!--record-id-1483-->Ученые напечатали мягкое сердце, которое работает как настоящее</h3><!--record-id-1484-->\n\n \n \n \n \n\n <div record-id=\"1485\" class=\"meta\"><!--record-id-1486-->\n <span record-id=\"1487\" class=\"author\"><!--record-id-1488-->krasandm</span><!--record-id-1489-->\n •\n <span record-id=\"1490\" class=\"time\"><!--record-id-1491-->вчера в 16:39</span><!--record-id-1492-->\n </div><!--record-id-1493-->\n </a><!--record-id-1494-->\n <a record-id=\"1495\" href=\"https://m.geektimes.ru/post/291173/comments/\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'tm_block', 'mobile_geektimes_posts', 'comments'); }\" class=\"comments_count\"><!--record-id-1496-->10</a><!--record-id-1497-->\n </div><!--record-id-1498-->\n </div><!--record-id-1499-->\n </div><!--record-id-1500-->\n\n\n \n\n </div><!--record-id-1501-->\n <div record-id=\"1502\" id=\"footer\" class=\"footer\"><!--record-id-1503-->\n <div record-id=\"1504\" class=\"footer__section\"><!--record-id-1505-->\n <span record-id=\"1506\" class=\"footer__text\"><!--record-id-1507-->Мобильное приложение</span><!--record-id-1508-->\n <div record-id=\"1509\" class=\"apps footer__apps js-apps\"><!--record-id-1510-->\n <a record-id=\"1511\" href=\"https://appmetrica.yandex.com/serve/240693928167260216?utm_source=mobilTM&utm_medium=button&utm_campaign=appiOS\" class=\"apps__item js-app_ios hidden\" target=\"_blank\" onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'footer', 'links', 'ios_app'); }\"><!--record-id-1512-->\n <img record-id=\"1513\" src=\"/images/apps/appstore_52.png\" height=\"52\"/><!--record-id-1514-->\n </a><!--record-id-1515-->\n\n <a record-id=\"1516\" href=\"https://appmetrica.yandex.com/serve/745097116363323173?id=ru.habrahabr&utm_sourc
- onclick=\"if (typeof ga === 'function') { ga('send', 'event', 'footer', 'links', 'android_app'); }\" style=\"display: block;\"><!--record-id-1517-->\n <img record-id=\"1518\" src=\"/images/apps/gp_52.png\" height=\"52\"/><!--record-id-1519-->\n </a><!--record-id-1520-->\n </div><!--record-id-1521-->\n </div><!--record-id-1522-->\n\n <div record-id=\"1523\" class=\"footer__section\"><a record-id=\"1524\" href=\"https://habrahabr.ru/post/333532/?mobile=no\" class=\"footer__text\"><!--record-id-1525-->Полная версия</a></div><!--record-id-1526-->\n <div record-id=\"1527\" class=\"footer__section footer__text\"><!--record-id-1528-->2006 – 2017 © TM</div><!--record-id-1529-->\n</div><!--record-id-1530-->\n </div><!--record-id-1531-->\n <div record-id=\"1532\" id=\"sidebar_overlay\"></div><!--record-id-1533-->\n\n<div record-id=\"1534\" id=\"sidebar\"><!--record-id-1535-->\n <a record-id=\"1536\" href=\"https://habrahabr.ru/auth/login/\" class=\"head\"><!--record-id-1537-->\n <img record-id=\"1538\" src=\"/images/mobile.default_avatar.png\" alt=\"\"/><!--record-id-1539-->\n <span record-id=\"1540\" class=\"username\"><!--record-id-1541-->Вход на сайт</span><!--record-id-1542-->\n </a><!--record-id-1543-->\n\n <div record-id=\"1544\" class=\"menu\"><!--record-id-1545-->\n <a record-id=\"1546\" href=\"/\" class=\"posts\"><!--record-id-1547-->Лучшие</a><!--record-id-1548-->\n <a record-id=\"1549\" href=\"/all/\" class=\"posts\"><!--record-id-1550-->Все подряд</a><!--record-id-1551-->\n\n\n <a record-id=\"1552\" href=\"/flows/\" class=\"hubs\"><!--record-id-1553-->Хабы</a><!--record-id-1554-->\n <a record-id=\"1555\" href=\"/companies/\" class=\"companies\"><!--record-id-1556-->Компании</a><!--record-id-1557-->\n\n </div><!--record-id-1558-->\n</div><!--record-id-1559-->\n\n <!--record-id-1561-->\n\n \n<!--record-id-1562--><!-- Yandex.Metrika counter --><!--record-id-1563-->\n<!--record-id-1565-->\n<noscript record-id=\"1566\"><div><img src=\"//mc.yandex.ru/watch/24049213\" style=\"position:absolute; left:-9999px;\" alt=\"\" /></div></noscript><!--record-id-1567-->\n<!--record-id-1568--><!-- /Yandex.Metrika counter --><!--record-id-1569-->\n\n \n <!--record-id-1571-->\n \n \n\n<div record-id=\"1572\" class=\"advblock advertblock ammblock b-banner b-media-banner pub_300x250 pub_300x250m medium_rectangle_300_250 pub_728x90 leaderboard_728_90 wide_skyscraper_160_600 wide_skyscraper_160x600 text-ad textAd text_ad text_ads text-ads text-ad-links ad_text ad_text banner_text text-banner b-rb\" style=\"position: absolute !important; top: -9999px !important; left: -9999px !important; width: 1px !important; height: 1px !important;\"></div><iframe record-id=\"1573\" src=\"https://content.adriver.ru/banners/0002186/0002186173/0/l6.html?626911&0&1&0&8836022&0&0&129&217.12.196.142&merle&0\" style=\"height: 10px; width: 10px; position: absolute; left: -10000px; top: -10000px;\"></iframe><iframe record-id=\"1574\" src=\"https://content.adriver.ru/banners/0002186/0002186173/0/l6.html?626911&0&1&0&3658010&0&0&129&217.12.196.142&merle&0\" style=\"height: 10px; width: 10px; position: absolute; left: -10000px; top: -10000px;\"></iframe></body>","host":"m.habrahabr.ru","title":"Снимаем “4D видео” с помощью depth-сенсора и триангуляции Делоне / Хабрахабр","absoluteTimeStamp":125824,"htmlAttributes":{},"screenWidth":360,"screenHeight":616,"activeElement":207,"scrolls":{"207":{"top":1174,"left":0}}},"_initialStateIndex":2,"_recordName":"1500379941701","_timelineMaxIndex":0,"_userID":28,"_id":"7"}
Advertisement
Add Comment
Please, Sign In to add comment