<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Мини-курс компьютерного зрения - сопоставляем кусочки пазла, Сириус, 2026</title>
    <description>Мини-курс компьютерного зрения - сопоставляем кусочки пазла, Сириус, 2026</description>
    <link>http://polarnick.com/blogs/courses/Sirius26CVPuzzleSolver/index_ru.html</link>
    <atom:link href="http://polarnick.com/blogs/courses/Sirius26CVPuzzleSolver/feed_ru.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 12 Jul 2026 17:53:14 +0000</pubDate>
    <lastBuildDate>Sun, 12 Jul 2026 17:53:14 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
    
      <item>
        <title>Занятие 7: Сшиваем пазл в единую картинку</title>
        <description>&lt;h1 id=&quot;мем&quot;&gt;Мем&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/meme_unicorn.jpg&quot; alt=&quot;Meme Unicorn&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;используйте-extended-thinkingdeepthink-у-llm&quot;&gt;Используйте Extended Thinking/DeepThink у LLM&lt;/h1&gt;

&lt;p&gt;Очень советую для средних и сложных задач использовать “сильно старайся” режим:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/llm_use_extended_thinking_or_deepthink.png&quot; alt=&quot;LLM use extended thinking or deepthink&quot; /&gt;&lt;/p&gt;

&lt;p&gt;И в целом попробуйте какую-нибудь среднюю по сложности задачку обработать с и без этих галок, посмотрите на разницу в качестве.&lt;/p&gt;

&lt;p&gt;Попробуйте обработать в разных LLM которые вам доступны.&lt;/p&gt;

&lt;p&gt;Не стоит каждый день тратить время на такие эксперименты, но изредка пробовать, или хотя бы один раз выяснить где ваши задачи решаются лушче-хуже - стоит того.&lt;/p&gt;

&lt;h1 id=&quot;описание-преобразования-системы-координат-через-матрицы&quot;&gt;Описание преобразования системы координат через матрицы&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/matrices_shift_and_rotation.png&quot; alt=&quot;Matrices of shift and rotation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://youtu.be/0mQWsbve6FU?t=90&quot;&gt;Видео лекция про это&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;план-решения&quot;&gt;План решения&lt;/h1&gt;

&lt;p&gt;Есть исходная картинка:&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/static/2026/01/downscaled_photo_example.jpg&quot; alt=&quot;Downscaled photo of puzzle example&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;В ней нашли куски-объекты, в каждом из них нашли стороны:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/object_4sides.png&quot; alt=&quot;4 sides&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Для каждой стороны (кроме тех что являются белыми) нашли самую похожую сторону:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/matched_sizes.jpg&quot; alt=&quot;Graph of matched puzzle pieces&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Теперь хотим объединить все это в прямоугольник, например такой:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/merged_puzzle.jpg&quot; alt=&quot;Merged puzzle&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;план&quot;&gt;План&lt;/h1&gt;

&lt;p&gt;Итак у нас есть:&lt;/p&gt;

&lt;p&gt;1) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objOffsets, objImages, objMasks&lt;/code&gt; - извлеченные изображения объектов-кусочков (с маской и смещением указывающим на позицию в целой картинке)&lt;/p&gt;

&lt;p&gt;2) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objSides[obj][side] - vector&amp;lt;point2i&amp;gt;&lt;/code&gt; - координаты пикселей стороны side объекта obj (в его извлеченном изображении)&lt;/p&gt;

&lt;p&gt;3) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objMatchedSides[objA][sideA] = {objB, sideB, ...};&lt;/code&gt; - информация о том с каким (objB, sideB) нас сопоставило, или (-1, -1) если мы являемся белым краем&lt;/p&gt;

&lt;p&gt;План:&lt;/p&gt;

&lt;p&gt;1) Построить граф: вершины - объекты, ребра - сопоставления с другими объектами (из objMatchedSides)&lt;/p&gt;

&lt;p&gt;2) Найти в графе вершины-углы - попробовать выложить паззл начиная с них&lt;/p&gt;

&lt;p&gt;3) Найдя угол выясним сколько кусочков в ширину - прошагаем по графу вправо до упора&lt;/p&gt;

&lt;p&gt;4) Так же выясним сколько кусочков в высоту - шагаем от угла вниз до упора&lt;/p&gt;

&lt;p&gt;5) Сверяем что ширина * высоту = числу кусочков (иначе - пропускаем этот уголок, попробуем начать с другого)&lt;/p&gt;

&lt;p&gt;6) Создаем двумерный массив, каждая ячейка будет хранить номер кусочка-объекта + число поворотов по часовой стрелке (такое чтобы side0 смотрело направо, соответственно side1 - вниз, и т.д.)&lt;/p&gt;

&lt;p&gt;7) Выводим его для проверки в консоль&lt;/p&gt;

&lt;p&gt;8) Заполняем его распространяясь в ширину от кусочка-уголка&lt;/p&gt;

&lt;p&gt;9) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1&lt;/code&gt; Определим ширину/высоту каждого столбика/строки пазла (медиана от ширин/высот назначенных кусочков)&lt;/p&gt;

&lt;p&gt;10) Найдем для каждого кусочка матрицу описывающую переход из его изображения в общий холст&lt;/p&gt;

&lt;p&gt;11) Спроецируем все кусочки этой матрицей&lt;/p&gt;

&lt;h1 id=&quot;используйте-схему-планируем--потом-пишем-код&quot;&gt;Используйте схему: планируем + потом пишем код&lt;/h1&gt;

&lt;p&gt;Это полезно и для LLM, и когда вы кодите самостоятельно!&lt;/p&gt;

&lt;p&gt;Разбейте задачу на кусочки, затем сформулируйте максимально подробно каждый этап, пояснив что под каким термином вы подразумеваете.&lt;/p&gt;

&lt;p&gt;И если задача сложная - лучше даже попросить нейросеть прежде чем писать код - пусть расскажет какой у нее план по реализации каждого этапа.&lt;/p&gt;

&lt;p&gt;Ознакомьтесь с планом, часто по нему можно понять что она не правильно поняла вашу задумку - внесите коррективы, ответьте на ее уточняющие вопросы.&lt;/p&gt;

&lt;p&gt;Пример промпта:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;выполни TODO План в коде приложенном ниже
прежде чем писать код - задай уточняющие вопросы если они есть
и в целом расскажи мне про каждый шаг как примерно будешь реализовывать
если планируешь создать какие-то отдельные новые исходники - так же скажи мне об этом
после этого обсуждения я скажу тебе начать писать код

main.cpp:
...
ПОЛНЫЙ КОД
...
// Занятие 7
// Итак у нас есть:
// 1) objOffsets, objImages, objMasks - извлеченные изображения объектов-кусочков (с маской и смещением указывающим на позицию в целой картинке)
// 2) objSides[obj][side] - vector&amp;lt;point2i&amp;gt; - координаты пикселей стороны side объекта obj (в его извлеченном изображении)
// 3) objMatchedSides[objA][sideA] = {objB, sideB, ...}; - информация о том с каким (objB, sideB) нас сопоставило, или (-1, -1) если мы являемся белым краем
// TODO План:
// 1) Построить граф: вершины - объекты, ребра - сопоставления с другими объектами (из objMatchedSides)
// 2) Найти в графе вершины-углы - попробовать выложить паззл начиная с них
// 3) Найдя угол выясним сколько кусочков в ширину - прошагаем по графу вправо до упора
// 4) Так же выясним сколько кусочков в высоту - шагаем от угла вниз до упора
// 5) Сверяем что ширина * высоту = числу кусочков (иначе - пропускаем этот уголок, попробуем начать с другого)
// 6) Создаем двумерный массив, каждая ячейка будет хранить номер кусочка-объекта + число поворотов по часовой стрелке (такое чтобы side0 смотрело направо, соответственно side1 - вниз, и т.д.) - TODO
// 7) Выводим его для проверки в консоль
// 8) Заполняем его распространяясь в ширину от кусочка-уголка
// 9) Определим ширину/высоту каждого столбика/строки пазла (медиана от ширин/высот назначенных кусочков) - TODO (пока что захардкодить константный размер)
// 10) Найдем для каждого кусочка матрицу описывающую переход из его изображения в общий холст
// 11) Спроецируем все кусочки этой матрицей
...
ОСТАЛЬНОЙ КОД
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;практика&quot;&gt;Практика&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Пред-задание&lt;/code&gt; &lt;a href=&quot;/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/3fork.html#%D0%BA%D0%B0%D0%BA-%D0%BF%D0%BE%D0%B4%D1%82%D1%8F%D0%BD%D1%83%D1%82%D1%8C-%D0%BC%D0%BE%D0%B8-%D0%BD%D0%BE%D0%B2%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%B2%D0%B0%D1%88-fork&quot;&gt;Обновите свой fork&lt;/a&gt; репозиторий, убедитесь что у вас все компилируется, запустите юнит-тесты &lt;strong&gt;All CTest&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/run_all_ctest.png&quot; alt=&quot;Run All CTest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPD:&lt;/code&gt; так как теперь в репозитории выложены все задания, то вам нужно не обновить до самой свежей версии - а скачать версию актуальную для этого занятия 7. Поэтому скачайте исходники &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/releases/tag/lesson07&quot;&gt;по этой ссылке (и там Source code (zip)).&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Пред-задание&lt;/code&gt; Запустите CVPuzzleSolver (выбрав сверху его вместо &lt;strong&gt;All CTest&lt;/strong&gt;) - проверьте что результаты в debug вам нравятся и все хорошо работает. Что строятся графики сопоставляемых сторон.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1&lt;/code&gt; Найдите в коде (поиском по папке &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt;) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO&lt;/code&gt; комментарий, сейчас все строчки и столбцы - 200 пикселей, оцените вместо этого их медианой по кусочкам паззла.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 2&lt;/code&gt; Попробуйте запуститься на других фотографиях - работает ли? Попробуйте понять через визуализации - почему так? Попробуйте улучшить алгоритм. Если все фотографии заработают - здорово! Позовите меня пожалуйста!&lt;/p&gt;

&lt;h1 id=&quot;что-еще-можно-было-бы-добавить&quot;&gt;Что еще можно было бы добавить&lt;/h1&gt;

&lt;p&gt;Можно было бы попробовать поддержать настоящие паззлы:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/real_puzzle.jpg&quot; alt=&quot;Run All CTest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Упражнение&lt;/code&gt; Подумайте, применим ли наш метод сейчас если бы на черном фоне лежали кусочки обычного паззла? Сработает ли наш алгоритм?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Упражнение&lt;/code&gt; Подумайте, как можно сделать стыковку швов результата более четкой? Как склеить идеально?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Упражнение&lt;/code&gt; Подумайте, а если граф найденных сопоставлений не полон, например есть противоречивые не симметричные ребра - можем ли мы в каких-то случаях все-равно найти правильный ответ?&lt;/p&gt;

&lt;h1 id=&quot;курсы-в-cs-space-видеокарты-и-фотограмметрия&quot;&gt;Курсы в &lt;a href=&quot;https://csspace.io/&quot;&gt;CS Space&lt;/a&gt;: видеокарты и фотограмметрия&lt;/h1&gt;

&lt;p&gt;Если вас в целом зацепило то что мы делали в рамках мини-курса, то может быть вам позже (вероятно через несколько лет, скорее курсе на втором-третьем-четвертом) может быть интересно:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=qI1LE8iXuWk&amp;amp;list=PL5p-5hHpsHBoIL4WI6OaPdWpCTkr774zN&amp;amp;index=12&quot;&gt;Курс&lt;/a&gt; по вычислениям на видеокартах (&lt;strong&gt;CUDA, Vulkan, OpenCL&lt;/strong&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=rEF0zkv2cn8&amp;amp;list=PL5p-5hHpsHBp4yTpeZJ_QMSmJPAuov-VF&amp;amp;index=1&quot;&gt;Курс&lt;/a&gt; по фотограмметрии&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Анонсы про свои курсы пишу в &lt;a href=&quot;https://t.me/UnicornGlade&quot;&gt;тг-канале&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 12 Jan 2026 09:31:40 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/12/lecture07.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/12/lecture07.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>Занятия 5-6: Сопоставляем стороны кусочков</title>
        <description>&lt;h1 id=&quot;мем&quot;&gt;Мем&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/mem_iceberg.jpg&quot; alt=&quot;Meme Iceberg exchange rate AHAHAHAhahAHAH&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;что-уже-есть&quot;&gt;Что уже есть&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/object_4sides.png&quot; alt=&quot;4 sides&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Мы нашли стороны каждого куска пазла, причем они обходятся по часовой стрелке.&lt;/p&gt;

&lt;h1 id=&quot;извлекаем-визуальное-описание-каждой-стороны&quot;&gt;Извлекаем визуальное описание каждой стороны&lt;/h1&gt;

&lt;p&gt;Чтобы сравнить две стороны - нужно из каждой стороны извлечь ее текстуру - ее цвета пикселей, это будет ее визуальным описанием (&lt;strong&gt;descriptor&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Любопытно что чаще всего в фотограмметрии (область компьютерного зрения) дескриптором называют описание ключевой точки:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/keypoint_descriptor.jpg&quot; alt=&quot;Keypoint descriptor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;При этом если мы сравним два таких набора (синий и фиолетовый) - все ли будет хорошо?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/two_sides_misdirections.png&quot; alt=&quot;4 sides&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Учитывая обход по часовой стрелке - нужно не забыть инвертировать в обратном порядке вторую сторону с которой сейчас сравниваемся.&lt;/p&gt;

&lt;h1 id=&quot;как-то-нужно-сравнить-две-стороны&quot;&gt;Как-то нужно сравнить две стороны&lt;/h1&gt;

&lt;p&gt;Без отладочной визуализации - как блуждать в потемках! Ни к чему хорошему мы не придем!&lt;/p&gt;

&lt;p&gt;Подумайте, что бы вы хотели с ходу понимать? Что бы вы хотели с ходу увидеть? Какие данные?&lt;/p&gt;

&lt;p&gt;Поэтому:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/sides_comparison_debug_visualization.png&quot; alt=&quot;Two sides comparison debug visualization&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Например чтобы оценить пару сторон - можно взять сумму или медиану по всем попиксельным разницам.&lt;/p&gt;

&lt;p&gt;Какие есть риски? Может ли это не сработать почему-то? А число пикселей (а значит и цветов) сторон вообще совпадает?&lt;/p&gt;

&lt;p&gt;А что если одна из сторон ярче другой на константу?&lt;/p&gt;

&lt;h1 id=&quot;практика&quot;&gt;Практика&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Пред-задание&lt;/code&gt; &lt;a href=&quot;/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/3fork.html#%D0%BA%D0%B0%D0%BA-%D0%BF%D0%BE%D0%B4%D1%82%D1%8F%D0%BD%D1%83%D1%82%D1%8C-%D0%BC%D0%BE%D0%B8-%D0%BD%D0%BE%D0%B2%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%B2%D0%B0%D1%88-fork&quot;&gt;Обновите свой fork&lt;/a&gt; репозиторий, убедитесь что у вас все компилируется, запустите юнит-тесты &lt;strong&gt;All CTest&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/run_all_ctest.png&quot; alt=&quot;Run All CTest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPD:&lt;/code&gt; так как теперь в репозитории выложены все задания, то вам нужно не обновить до самой свежей версии - а скачать версию актуальную для этого занятия 5-6. Поэтому скачайте исходники &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/releases/tag/lesson05-lesson06&quot;&gt;по этой ссылке (и там Source code (zip)).&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Пред-задание&lt;/code&gt; Запустите CVPuzzleSolver (выбрав сверху его вместо &lt;strong&gt;All CTest&lt;/strong&gt;) - проверьте что результаты в debug вам нравятся и все хорошо работает. Что строятся графики сопоставляемых сторон. Что в папке &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/data/&lt;/code&gt; появились новые картинки.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 0&lt;/code&gt; Поищите - где в коде нужно обратить в обратном направлении список цветов второй стороны с которой мы проводим сравнение? Проверьте что там все сделано правильно.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO 1&lt;/code&gt; Посмотрите на RGB графики тех сторон у которых нет и не может быть соседей, то есть у белых полос.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 2&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO 2&lt;/code&gt; Посмотрите на графики и подумайте, может имеет смысл как-то воздействовать на снятые с границы цвета? Какой у нас вообще критерий “что такое хорошая метрика отличия сторон”? Мы хотим чтобы лучшим было правильное соответствие! А чтобы было не просто правильно - а правильно &lt;strong&gt;с большим запасом&lt;/strong&gt; - хотим еще чтобы второй лучший (был шанс что он случайно победит и ответ стал бы не верным) - как можно сильнее отставал от первого. Не запрещено и даже приветствуется консультация с &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;, можете даже код с ее помощью генерить, главное контролируйте что она написала, и старайтесь узнавать идеи - какие есть варианты метрик? Чем они отличаются? А какая решит такую-то конкретную проблему что я вижу на графиках?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 3&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO 3&lt;/code&gt; Реализуйте какую-то метрику сравнивающую насколько два соответствующие цвета colA и colB отличаются. Посмотрите на графики - нравится ли как выглядит график для правильной пары сторон (маленькие числа)? А для неправильной пары сторон (большие числа)?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 4&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO 4&lt;/code&gt; Объедините метрики различности попиксельные в единое число - насколько похожи эти две стороны.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 5&lt;/code&gt; Руководствуясь визуализациями, в т.ч. всего графа с лучшими соответствиями, и руководствуясь выводом в конце лога числа верных-неверных ответов - попробуйте улучшить реализацию. Ищите самую сильно обидную ошибку (где по графикам цветов все кажется должно было бы сработать легко) - и улучшайте способ обсчета чтобы он сделал все правильно.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 6&lt;/code&gt; Если есть желание (особенно если хорошо работает) - можно сфотографировать свой датасет, я взял с собой черный холст-фон (футболку, кек), целые картинки-“пазлы” и ножницы.&lt;/p&gt;

&lt;h1 id=&quot;к-следующему-занятию&quot;&gt;К следующему занятию&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 7&lt;/code&gt; Попробуйте улучшить как можно лучше! Не стесняйтесь делиться в чате результатами, и идеями которые позволили вам получить улучшение, это очень любопытно!&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 8&lt;/code&gt; Подумайте, если у нас все пары правилно сопоставлены - то что нам теперь осталось сделать? Как это можно попробовать реализовать?&lt;/p&gt;
</description>
        <pubDate>Sun, 11 Jan 2026 09:31:40 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/11/45-lecture05-06.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/11/45-lecture05-06.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>Занятия 4: Найдем контура и углы объектов</title>
        <description>&lt;h1 id=&quot;мем&quot;&gt;Мем&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/auf_robots_meme.jpg&quot; alt=&quot;Prompt Engineer VS Sloperator meme&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;что-уже-есть&quot;&gt;Что уже есть&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/downscaled_photo_example.jpg&quot; alt=&quot;Downscaled photo of puzzle example&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;разбиваем-на-куски&quot;&gt;Разбиваем на куски&lt;/h1&gt;

&lt;p&gt;Теперь благодаря морфологии с прошлого занятия мы получили чистую маску:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/mask_good.png&quot; alt=&quot;Good foreground mask after morphology&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Как посчитать сколько у нас объектов? Как извлечь каждый объект чтобы работать с ним отдельно?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/objects_with_bboxes.jpg&quot; alt=&quot;Objects with bboxes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Нам поможет Система Непересекающихся Множеств! (СНМ, Disjoint Set)&lt;/p&gt;

&lt;p&gt;Подробнее можно &lt;a href=&quot;https://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%9D%D0%9C_%28%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_%D0%BB%D0%B5%D1%81%D0%B0_%D0%BA%D0%BE%D1%80%D0%BD%D0%B5%D0%B2%D1%8B%D1%85_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D1%8C%D0%B5%D0%B2%29&quot;&gt;почитать тут&lt;/a&gt;
или &lt;a href=&quot;https://youtu.be/_n_c3bohuU4?t=4058&quot;&gt;послушать тут.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;То есть хочется извлечь каждый объект - его картинку + его маску + его координаты в оригинальной картинке:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/extracted_object_with_mask.png&quot; alt=&quot;Extracted object with mask&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;в-каждом-куске-находим-границы&quot;&gt;В каждом куске находим границы&lt;/h1&gt;

&lt;p&gt;Подумайте, &lt;strong&gt;как можно найти координаты пикселей на периметре&lt;/strong&gt; куска (причем упорядоченными - чтобы они сформировали цикл из пикселей):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/object_border.png&quot; alt=&quot;Good foreground mask after morphology&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Как решить эту задачу? В два шага:&lt;/p&gt;

&lt;p&gt;1) Строим новую маску - множество пикселей лежащих на периметре (по простому критерию - среди 8 соседей должны быть и пиксели с фона, и пиксели объекта)&lt;/p&gt;

&lt;p&gt;2) Эту новую маску обходим чем-то вроде поиска в ширину, но чтобы сохранять направление - выбираем среди соседей того что сохраняет моментум движения - то есть если предыдущий шаг был например направо, то и новый шаг должен быть как можно ближе к этому направлению&lt;/p&gt;

&lt;h1 id=&quot;в-каждом-куске-находим-четыре-стороны&quot;&gt;В каждом куске находим четыре стороны&lt;/h1&gt;

&lt;p&gt;А если у нас есть цикл из этих пикселей-вершин, то &lt;strong&gt;как разбить их на четыре множества&lt;/strong&gt; - четыре стороны куска:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/object_4sides.png&quot; alt=&quot;4 sides&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;чего-мы-хотим&quot;&gt;Чего мы хотим&lt;/h1&gt;

&lt;p&gt;Взяв маску делящую картинку на объект и фон - вытащить отдельные объекты и каждый независимо обработать:&lt;/p&gt;

&lt;p&gt;1) В каждом объекте выделить контур (периметр границы ввиде цепочки координат)&lt;/p&gt;

&lt;p&gt;2) Через упрощение этого контура определить какие вершины были угловыми&lt;/p&gt;

&lt;p&gt;3) Разбить контур на четыре части (четыре стороны объекта) по угловым вершинам&lt;/p&gt;

&lt;p&gt;4) ???&lt;/p&gt;

&lt;p&gt;5) Вы восхитительны - теперь вы знаете четыре цепочки пикселей являющихся стороной кусочка пазла&lt;/p&gt;

&lt;h1 id=&quot;практика&quot;&gt;Практика&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1.1&lt;/code&gt; &lt;a href=&quot;/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/3fork.html#%D0%BA%D0%B0%D0%BA-%D0%BF%D0%BE%D0%B4%D1%82%D1%8F%D0%BD%D1%83%D1%82%D1%8C-%D0%BC%D0%BE%D0%B8-%D0%BD%D0%BE%D0%B2%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%B2%D0%B0%D1%88-fork&quot;&gt;Обновите свой fork&lt;/a&gt; репозиторий, убедитесь что у вас все компилируется, запустите юнит-тесты &lt;strong&gt;All CTest&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/run_all_ctest.png&quot; alt=&quot;Run All CTest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPD:&lt;/code&gt; так как теперь в репозитории выложены все задания, то вам нужно не обновить до самой свежей версии - а скачать версию актуальную для этого занятия 4. Поэтому скачайте исходники &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/releases/tag/lesson04&quot;&gt;по этой ссылке (и там Source code (zip)).&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1.2&lt;/code&gt; Заметьте что падает два набора юнит-тестов - те что проверяют функцию &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildContourMask()&lt;/code&gt; и &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simplifyContour()&lt;/code&gt; - их вам нужно будет &lt;strong&gt;самостоятельно&lt;/strong&gt; реализовать!&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 2.1&lt;/code&gt; Запустите CVPuzzleSolver (выбрав сверху его вместо &lt;strong&gt;All CTest&lt;/strong&gt;) - проверьте что результаты в debug вам нравятся и все хорошо работает. Программа будет падать с &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Error: Function buildContourMask() is not implemented!&lt;/code&gt; т.к. нужно будет реализовать две функции. Проверьте что построенный вами контур для каждого объекта выглядит хорошо.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 2.2&lt;/code&gt; У нас уже есть бинарная маска делящая фото на пиксели объекта и фона. Теперь нам нужно обработать каждый кусочек картинки. Для каждого кусочка постройте маску контура а затем и &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&amp;lt;point2i&amp;gt;&lt;/code&gt; - точки с контура. Проверьте через отладочные debug-визуализации. Теперь вам надо реализовать функцию занимающуюся упрощением геометрии. Проверьте что визуально она строит стороны правильно (цветная раскраска очень помогает):&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildContourMask()&lt;/code&gt; (в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libs/images/libimages/algorithms/extract_contour.cpp&lt;/code&gt;) - реализуйте эту функцию, постройте маску пикселей лежащих на периметре&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simplifyContour()&lt;/code&gt; (в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libs/images/libimages/algorithms/simplify_contours.cpp&lt;/code&gt;) - реализуйте эту функцию, то есть удаляйте самые не-угловатые вершины из контура до тех пор пока не останется четыре угловые вершины&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 3&lt;/code&gt; Ознакомьтесь с &lt;a href=&quot;/blogs/courses/sirius26cvpuzzlesolver/2026/01/10/30-cpu-parallelism-openmp.html&quot;&gt;ускорением через OpenMP&lt;/a&gt; - запустите код и исследуйте - во сколько раз ускоряется морфология на вашем компьютере? Какое ускорение вы бы ожидали? Сколько ядер у вашего CPU? Какое ускорение вы пронаблюдали на практике? Попробуйте запустить несколько раз для надежности. Попробуйте ускорить обработку объектов - чтобы обработка одного объекта работала на отдельном ядре.&lt;/p&gt;

&lt;h1 id=&quot;к-следующему-занятию&quot;&gt;К следующему занятию&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 4&lt;/code&gt; Подумайте, теперь мы знаем где границы каждого куска. Как теперь попробовать для каждой границы найти самую похожую на нее границу какого-то другого куска? Как измерить что такое “самая похожая”?&lt;/p&gt;
</description>
        <pubDate>Sat, 10 Jan 2026 09:31:40 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/10/32-lecture04.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/10/32-lecture04.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>OpenMP: Как ускорить алгоритм за счет многоядерности CPU</title>
        <description>&lt;p&gt;Пусть есть код:&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// do something w.r.t. i&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Пусть алгоритм внутри цикла независимо обрабатывает разные индексы - то есть результат не меняется от того в каком порядке обрабатываются &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;, и не меняется от того что они обрабатываются одновременно.&lt;/p&gt;

&lt;p&gt;Тогда мы могли бы одновременно обрабатывать не просто какой-то один индекс &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;, а столько параллельных значений &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; &lt;strong&gt;сколько у вас ядер процессора&lt;/strong&gt; (CPU). А это линейное ускорение!&lt;/p&gt;

&lt;p&gt;Для этого нужно (&lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/commit/04e79607476b2d617f1c929f0e6037b9961ad67b&quot;&gt;см. коммит&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;1) подключить OpenMP библиотеку (на windows и linux является частью C++ компилятора, на macos бывают сложности)&lt;/p&gt;

&lt;p&gt;2) добавить у этого цикла простое указание что он должен выполняться параллельно:&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#pragma omp parallel for
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// do something w.r.t. i&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание&lt;/code&gt; Попробуйте сделать это с морфологией - какое ускорение вы бы ожидали? Сколько ядер у вашего CPU? Какое ускорение вы пронаблюдали на практике? Попробуйте запустить несколько раз для надежности.&lt;/p&gt;

&lt;h1 id=&quot;не-используйте-на-олимпиадах&quot;&gt;Не используйте на олимпиадах!&lt;/h1&gt;

&lt;p&gt;Потому что там почти наверняка об этом знают, и замеряют процессорное время, а не время в секундах.&lt;/p&gt;
</description>
        <pubDate>Sat, 10 Jan 2026 09:30:00 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/10/30-cpu-parallelism-openmp.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/10/30-cpu-parallelism-openmp.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>Занятия 2-3: Отмаскируем фон</title>
        <description>&lt;h1 id=&quot;быстрый-гауссов-блюр-separable-filter&quot;&gt;Быстрый Гауссов блюр (separable filter)&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Какая асимптотика у размытия картинки через свертку с ядром Гаусса размера KxK?&lt;/p&gt;

&lt;p&gt;А что если обратить внимание что Гауссово ядро является separable ядром?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/separable_filter.png&quot; alt=&quot;Separable filter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Какая будет асимптотика если мы сначала выполним горизонтальные свертки размера K, и затем - вертикальные свертки размера K?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Можно ли еще быстрее? Можно ли ускорить свертку с горизонтальным ядром размера K?&lt;/p&gt;

&lt;h1 id=&quot;анализ-распределения-медиана-и-перцентиль&quot;&gt;Анализ распределения: медиана и перцентиль&lt;/h1&gt;

&lt;p&gt;На этой картинке можно хорошо видеть что среднее значение (“среднее по больнице”) мало чего говорит о данных, т.к. сильно зависит от больших значений (которых может быть мало, но они в абсолюлтных значениях очень большие и поэтому перетягивают на себя одеяло).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Медиана&lt;/strong&gt; же разбивает множество на две половины - одна половина значений меньше медианы, другая - больше.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;90% перцентайль&lt;/strong&gt; разбивает множество на две части - 90% меньших, 10% больших.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/distribution_and_average_median_and_90th_percentile_visualization.png&quot; alt=&quot;Distribution and average, median and 90th percentile visualization&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Изменится ли среднее значение если наибольшее число распределения увеличить в 239 квадрильонов раз? То есть если мы добавили очевидный выброс. А медиана? А перцентайль?&lt;/p&gt;

&lt;h1 id=&quot;как-найти-цвет-фона&quot;&gt;Как найти цвет фона?&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Как найти по какому значению яркости в нашей картинке с паззлом на черном фоне можно различить пиксели на пиксели фона и пиксели объекта? Можно ли взять просто максимум яркости на границе изображения? А просто 90% перцентайль?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/downscaled_photo_example.jpg&quot; alt=&quot;Downscaled photo of puzzle example&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;как-зная-цвет-фона-построить-маску&quot;&gt;Как зная цвет фона построить маску?&lt;/h1&gt;

&lt;h1 id=&quot;как-из-такой-маски-сделать-красоту&quot;&gt;Как из такой маски сделать красоту?&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/mask_noisy.png&quot; alt=&quot;Noisy foreground mask&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;морфология-эрозия-диляция&quot;&gt;Морфология: эрозия, диляция&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/morphology_dilation.png&quot; alt=&quot;Morphology dilation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/morphology_erosion.png&quot; alt=&quot;Morphology dilation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; А как вот такого добиться? Даст ли это dilation/erosion?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/morphology_how.png&quot; alt=&quot;Morphology dilation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; А как вот такого добиться? Даст ли это dilation/erosion?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/morphology_how2.png&quot; alt=&quot;Morphology dilation&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;выводы&quot;&gt;Выводы&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Не запускал - не работает&lt;/strong&gt; - если код не запускали, то он скорее-всего работает неверно, а может быть даже не компилируется&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Не смотрел результат - не работает&lt;/strong&gt; - если вы не проанализировали результат, а желательно - каждый микрошаг алгоритма - то результат скорее-всего не верен (или, что тоже самое, &lt;strong&gt;субоптимален&lt;/strong&gt;, и можно ЛУЧШЕ, если посмотрев - оптимизировать параметры или убрать баги)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Втыкать инварианты&lt;/strong&gt; - если есть идеи “во, тут верно такое-то правило-инвариант” - фиксируйте его явной проверкой, &lt;strong&gt;особенно&lt;/strong&gt; если на это предположение полагается дальше код. Делите на число? &lt;strong&gt;Проверьте&lt;/strong&gt; перед этим что оно не ноль. Берете первый элемент вектора? &lt;strong&gt;Проверьте&lt;/strong&gt; что он не пустой&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Быстрота эксперимента&lt;/strong&gt; - если легко и быстро все работает - больше всякого попробуете (т.к. больше экспериментов успеете), меньше тратите времени на ожидание&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Визуализации и логи&lt;/strong&gt; - как способ держать руку на пульсе, &lt;strong&gt;увидеть&lt;/strong&gt; и глубоко прочувствовать что происходит на каждом этапе&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Поиск рисков и контр-примеров&lt;/strong&gt; - чтобы получить надежный алгоритм нужно в каждом алгоритме-кирпичике думать “А КАК ЭТО МОЖЕТ НЕ СРАБОТАТЬ? КАК Я МОГУ ЭТО СЛОМАТЬ?” + поиск контр-примеров, в т.ч. синтетических&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;C++ и OpenMP - имба&lt;/strong&gt; - многопоточность бесплатно всем и пусть никто не уйдет обиженным!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;практика&quot;&gt;Практика&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1&lt;/code&gt; &lt;a href=&quot;/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/3fork.html#%D0%BA%D0%B0%D0%BA-%D0%BF%D0%BE%D0%B4%D1%82%D1%8F%D0%BD%D1%83%D1%82%D1%8C-%D0%BC%D0%BE%D0%B8-%D0%BD%D0%BE%D0%B2%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%B2%D0%B0%D1%88-fork&quot;&gt;Обновите свой fork&lt;/a&gt; репозиторий, убедитесь что у вас все компилируется, все юнит тесты проходят - запустите &lt;strong&gt;All CTest&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/run_all_ctest.png&quot; alt=&quot;Run All CTest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPD:&lt;/code&gt; так как теперь в репозитории выложены все задания, то вам нужно не обновить до самой свежей версии - а скачать версию актуальную для этого занятия 2-3. Поэтому скачайте исходники &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/releases/tag/lesson02-lesson03&quot;&gt;по этой ссылке (и там Source code (zip)).&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 2&lt;/code&gt; Откройте main.cpp и выполните там все &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO&lt;/code&gt;. После выполнения каждого - запускайте программу и проверяйте что очередной этап выдал ожидаемые хорошие результаты (сверяясь с отладочной визуализацией в папке &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debug/&lt;/code&gt;). Вверху main.cpp есть &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#include&lt;/code&gt; попрыгайте по ним через &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+Левый клик мышки&lt;/code&gt;, там есть вспомогательные функции.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Доп. Задание 3&lt;/code&gt; Возьмите любой алгоритм и ускорьте его с помощью многопоточности и &lt;strong&gt;OpenMP&lt;/strong&gt;! Какой алгоритм лучше всегоу ускорить? Самый медленный, предположу что это морфология! Прежде чем ускорять - убедитесь что она занимает значимое время (можете искусственно замедлить через больший аргумент &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strength&lt;/code&gt;). Пример промпта:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Дорогой ChatGPT, не передать словами как я рад снова с тобой беседовать
Не мог бы ты мне помочь, дорогой друг?
У меня есть libs/images/CMakeLists.txt + цикл который я хочу ускорить (прилагаю ниже)
Обрати внимание что у меня есть цикл преобразующий картинку
1) добавь замер времени сколько занимает этот цикл
2) сделай переменную bool with_openmp = true;
3) если она выставлена в true - цикл обработки картинки должен выполняться многопоточно с помощью OpenMP
4) и кроме кода я бы хотел узнать что делает каждая строка, и что вообще часто пригождается из OpenMP?
5) и любопытно, а как вычислительные задачки распределяются по ядрам процессора? например какие задачи возьмет на себя первое ядро?
CMakeLitst.txt + код цикла который ниже:
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;мем&quot;&gt;Мем&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/sloperator_meme.jpg&quot; alt=&quot;Prompt Engineer VS Sloperator meme&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;к-следующему-занятию&quot;&gt;К следующему занятию&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 4&lt;/code&gt; Подумайте, может у вас появятся вопросы к коду, что-то стало непонятно, или есть идеи вида: &lt;strong&gt;“а почему мы вместо алгоритма А не сделали алгоритм Б? ведь было бы проще/лучше?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 5&lt;/code&gt; Подумайте, имея идеальную маску делящую пиксели на фон и объект - как нам вычленить каждый из кусочков пазла в отдельную картинку? Например - как банально понять &lt;strong&gt;сколько&lt;/strong&gt; у нас кусочков оказалось?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 6&lt;/code&gt; Подумайте, если бы у вас на картинке был ровно один кусочек пазла, но часть фотографии занимал фон (его маска уже известна) - то как нам получить геометрию контура куска пазла? Как получить четырехугольник? Как найти координаты четырех углов?&lt;/p&gt;
</description>
        <pubDate>Fri, 09 Jan 2026 09:31:40 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/09/20lecture02.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/09/20lecture02.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>CMake типы сборки: Debug, Release, RelWithDebInfo</title>
        <description>&lt;p&gt;Изначально у вас код компилируется в ассемблер (команды понятные для процессора) почти напрямую.&lt;/p&gt;

&lt;p&gt;1) Это &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug&lt;/code&gt; сборка - код компилируется без оптимизаций.&lt;/p&gt;

&lt;p&gt;2) Есть &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Release&lt;/code&gt; сборка - тогда код будет работать быстрее т.к. компилятор пытается делать разные хитрые оптимизации. Недостаток в том что при запуске под отладкой не получится узнать даже в какой строке программа упала, т.к. из ассемблерных инструкций которые написал компилятор - не понять какая строка кода их породила (т.е. где произошла проблема).&lt;/p&gt;

&lt;p&gt;3) Поэтому советую в дополнение к &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug&lt;/code&gt; использовать &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RelWithDebInfo&lt;/code&gt; (а не &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Release&lt;/code&gt;) - это &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Release&lt;/code&gt; with Debug Information - в т.ч. с информацией о том из какой строки исходника появилась каждая ассемблерная инструкция.&lt;/p&gt;

&lt;p&gt;Чтобы добавить этот вид сборки:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/how_to_add_relwithdebinfo_build_type_clion.png&quot; alt=&quot;How to add RelWithDebInfo CMake build type in CLion&quot; /&gt;&lt;/p&gt;

&lt;p&gt;И теперь легко менять какой вариант сборки хочется запускать (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug&lt;/code&gt; для отладки, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RelWithDebInfo&lt;/code&gt; если важна скорость):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/choose_build_type.png&quot; alt=&quot;Choose build type to run&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 09 Jan 2026 09:30:00 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/09/10-buildtype-release.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/09/10-buildtype-release.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>Как запустить в Visual Studio, настройка working directory</title>
        <description>&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Файл-&amp;gt;Открыть-&amp;gt;CMake...&lt;/code&gt; и выбираете CMakeLists.txt в корне проекта.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD:&lt;/strong&gt; игнорируйте все что написано ниже, начиная с &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/commit/9147360e6ab0eae3e0dfb4f9d58833de8a5c51a6#diff-34d21af3c614ea3cee120df276c9c4ae95053830d7f1d3deaf009a4625409ad2&quot;&gt;этого commit-а&lt;/a&gt; настривать Working Directory больше не надо, она будет работать автоматически везде (благодаря вызову &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configureWorkingDirectory()&lt;/code&gt; в main.cpp).&lt;/p&gt;

&lt;p&gt;Если вы видите ошибку про путь к картинке (&lt;strong&gt;UPD:&lt;/strong&gt; больше такую проблему вы встречать не должны):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/msvc_path_error.png&quot; alt=&quot;Visual Studio terminal error about working directory path&quot; /&gt;&lt;/p&gt;

&lt;p&gt;То вам нужно изменить рабочую папку (working directory) - то есть папку из которой запускается программа:&lt;/p&gt;

&lt;p&gt;Добавьте в самом начале &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;filesystem&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Default working directory: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filesystem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filesystem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MyProjects&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CVPuzzleSolver&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// TODO UPDATE ME&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Updated working directory: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filesystem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 08 Jan 2026 18:30:52 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/4cmake-in-vs.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/4cmake-in-vs.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>Как сделать fork исходников, как подтянуть изменения</title>
        <description>&lt;p&gt;Логинитесь на github, открываете &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver&quot;&gt;репозиторий задания&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Жмете сверху-справа fork чтобы у вас появилась копия проекта (вы сможете делать &lt;strong&gt;commit+push&lt;/strong&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/fork_github_repo.png&quot; alt=&quot;How to fork a github repo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;После этого у вас есть ваш репозиторий, чтобы скачать его себе локально в CLion:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Жмете на четыре полоски в верхнем левом углу -&amp;gt; Git -&amp;gt; Clone... -&amp;gt; GitHub&lt;/code&gt; (потребуется нажать &lt;strong&gt;Authenticate&lt;/strong&gt; браузере, чтобы дать доступ к своему гитхабу).&lt;/p&gt;

&lt;h2 id=&quot;как-включить-continous-integration-ci-в-fork&quot;&gt;Как включить Continous Integration (CI) в fork&lt;/h2&gt;

&lt;p&gt;Хотя файл конфигурации вы скопировали вместе с остальным кодом - зеленой галочки у ваших коммитов нет.&lt;/p&gt;

&lt;p&gt;Это потому что в fork-нутых проектах по умолчанию выключается GitHub Actions CI, вам надо его явно вклчюить обратно:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/how_to_activate_ci_actions_in_fork.png&quot; alt=&quot;How to activate GitHub CI Actions in repository fork&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;как-подтянуть-мои-новые-изменения-в-ваш-fork&quot;&gt;Как подтянуть мои новые изменения в ваш fork&lt;/h2&gt;

&lt;p&gt;Представим что в основном репозитории появились новые commit-ы (например к новому занятию), как их подтянуть в вашу fork-нутую версию?&lt;/p&gt;

&lt;p&gt;Sync fork:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/github_sync_fork.png&quot; alt=&quot;How to sync fork on github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Если вдруг вы видите merge conflict (см. ниже) из-за того что делали commit+push и изменили файлы, а я тоже изменил эти файлы и теперь случился &lt;strong&gt;merge conflict&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/sync_fork_merge_conflicts.png&quot; alt=&quot;How to sync fork via discard of your commits&quot; /&gt;&lt;/p&gt;

&lt;p&gt;То я вам рекомендую &lt;strong&gt;удалить&lt;/strong&gt; свои сommit-ы, т.е. свои изменения (очевидно, стоит сделать бэкап в любом случае, и особенно - если вам нужен этот код) через кнопку &lt;strong&gt;Discard N commits&lt;/strong&gt;. Это самый простой способ, по-хорошему нужно было бы объединить все изменения вручную, но это сложно. Если же ваши изменения вам нужны - просто перед их откатыванием сделайте бэкап, и после обновления - добавьте обратно себе в код.&lt;/p&gt;

&lt;p&gt;После этого подтяните (&lt;strong&gt;Pull&lt;/strong&gt;) эти изменения в код на вашем компьютере в CLion - через четыре полоски в углу сверху слева -&amp;gt; &lt;strong&gt;Git&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Pull…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Вероятно после этого у вас появится диалоговое окно про &lt;strong&gt;merge conflict&lt;/strong&gt; - предлагаю автоматически удалить все локальные изменения (если какой-то ваш код вам дорог - &lt;strong&gt;забэкапьте в отдельную папку перед этими операциями&lt;/strong&gt;) - для этого нажмите &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Accept theirs&lt;/code&gt; (т.е. принять мой код с полной заменой вашего кода):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/merge_conflict_accept_theirs.png&quot; alt=&quot;Merge conflict accept theirs&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 08 Jan 2026 18:30:50 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/3fork.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/3fork.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>Занятие 1: Что такое картинка? Осветлим, заблюрим, обострим, уменьшим!</title>
        <description>&lt;p&gt;1) Убедитесь что есть компилятор и IDE. Я предпочитаю CLion (&lt;a href=&quot;https://disk.yandex.ru/d/TStFP2QADw0-BQ&quot;&gt;установщик на яндекс.диске&lt;/a&gt;), но любая IDE (или текстовый редактор подойдут):&lt;/p&gt;

&lt;p&gt;1.1) Если вы на &lt;strong&gt;windows&lt;/strong&gt; - советую установить &lt;strong&gt;Visual Studio Community&lt;/strong&gt; (чтобы использовать ее компилятор) и затем утановить &lt;strong&gt;CLion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.2) Если вы на &lt;strong&gt;linux&lt;/strong&gt; - советую установить &lt;strong&gt;CLion&lt;/strong&gt; (нам подойдет любой компилятор g++/clang)&lt;/p&gt;

&lt;p&gt;1.3) Если вы на &lt;strong&gt;macos&lt;/strong&gt; - советую установить &lt;strong&gt;CLion&lt;/strong&gt; (нам подойдет любой компилятор g++/clang)&lt;/p&gt;

&lt;p&gt;2) Создайте пустой CMake + C++ проект, запустите main.cpp, убедитесь что он выводит &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello, World!&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3) Создайте локальный git репозиторий (или даже лучше - выложите свой проект на гитхаб, в частности это даст автоматическую проверку юнит-тестов после каждого коммита), подробнее как это сделать &lt;a href=&quot;/blogs/239/2025/school239_108_2025_2026/2025/12/11/how-to-use-llm.html#%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%BE%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%B4%D0%B0-%D1%87%D0%B5%D1%80%D0%B5%D0%B7-git-%D0%B2-%D0%B8%D0%B4%D0%B5%D0%B0%D0%BB%D0%B5-%D1%85%D1%80%D0%B0%D0%BD%D1%8F-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82-%D0%BD%D0%B0-github&quot;&gt;описано тут&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4) Изучите &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt; (код) и &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMakeLists.txt&lt;/code&gt; (описывает что нужно компилировать). Сделайте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit&lt;/code&gt; (и &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; если выложили на github/gitlab) этих двух файлов с сообщением навроде &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;basic hello world project&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;ci-continous-integration---автоматическая-проверка-коммитов&quot;&gt;CI, Continous Integration - автоматическая проверка коммитов&lt;/h2&gt;

&lt;p&gt;5) Если используете github - добавьте автоматическую проверку что код компилируется (т.н. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Continous Integration&lt;/code&gt; - &lt;strong&gt;CI&lt;/strong&gt;), например просто спросите ChatGPT:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;У меня есть простой C++ проект
В нем два файла main.cpp и CMakeLists.txt (простой hello world)
Я выложил этот проект на github
Как мне сделать так чтобы у меня появился Continous Integration (CI)?
Т.е. чтобы github при каждом моем изменении проекта (commit+push) проверял что мой проект компилируется под windows, linux, macos?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Оказывается достаточно добавить некий файл конфигурации CI &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.github/workflows/ci.yml&lt;/code&gt; - &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/commit/99ed66425bf5be809c5cc4125b32792ded5fa07e&quot;&gt;см. например мой коммит&lt;/a&gt;. Если вам что-то в нем любопытно - &lt;strong&gt;спросите&lt;/strong&gt; меня или ChatGPT!&lt;/p&gt;

&lt;p&gt;Запушьте этот файл и обратите внимание что теперь у коммитов появляются галочки если все компилируется (и красные крестики если случилась какая-то ошибка компиляции).&lt;/p&gt;

&lt;p&gt;Например у меня появился красный крестик из-за того что в CMakeLists.txt требуется версия 4.1 или выше, а на GitHub CI оказалось 3.31:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/CI_fail_example.png&quot; alt=&quot;GitHub CI fail example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Исправляем это снизив требования к версии CMake, делаем commit+push, смотрим на красивую зеленую галочку:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/CI_ok_example.png&quot; alt=&quot;GitHub CI OK example&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;как-добавить-в-проект-считывание-картинок&quot;&gt;Как добавить в проект считывание картинок&lt;/h2&gt;

&lt;p&gt;Когда я готовил черновой проект - многое писала LLM, например создать простой проект без использования внешних библиотек можно вот так: &lt;a href=&quot;https://github.com/UnicornGlade/ComputerVisionPuzzleSolver/blob/main/prompts/01%20%D0%B8%D0%B7%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5%20%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0.txt&quot;&gt;см. на этот промпт&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;практическая-часть&quot;&gt;Практическая часть&lt;/h2&gt;

&lt;p&gt;Предлагаю вам начать с &lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver/releases/tag/lesson01&quot;&gt;проекта-заготовки&lt;/a&gt; - обратите внимание что это его старая версия, актуальная для этого первого задания. Чтобы скачать исходники - откройте эту ссылку и скачайте исходники:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/download_old_repo_sources.png&quot; alt=&quot;GitHub download source code for old revision&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Там уже есть весь скелет проекта и пример фотографии.&lt;/p&gt;

&lt;h2 id=&quot;считаем-картинку-сделаем-ярче-сохраним&quot;&gt;Считаем картинку, сделаем ярче, сохраним&lt;/h2&gt;

&lt;p&gt;Итак, вы скачали проект-заготовку, открыли его в CLion, может быть выложили у себя на github ради CI.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 1&lt;/code&gt; Запустите main и убедитесь что черная картинка сохранилась по пути &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy.jpg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Если кидает ошибку что картинка по относительному пути не найдена - проверьте папку из которой запускается программа - &lt;strong&gt;working directory&lt;/strong&gt; (и выставите ее так чтобы она указала на корень проекта):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/edit_cofiguration_edit_working_directory.png&quot; alt=&quot;Edit configuration to edit working directory&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Картинка - это матрица пикселей. В каждом пикселе значение яркости (одно если черно-белая картинка, три - если трехканальная: RGB - &lt;strong&gt;Red Green Blue&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Индексация бывает разной, но наша конвенция такая (полезно держать либо иллюстрацию, либо на бумажке картинку, чтобы не путаться):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/image_coordinates_system.png&quot; alt=&quot;Image coordintes system&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 2&lt;/code&gt; Теперь давайте как-то воздействуем на картинку, например осветлим ее - пробегите в цикле по всем пикселям, по всем трем каналам - домножьте на два. Запустите, стала ли картинка ярче? Попробуйте еще как-то поэкспериментировать.&lt;/p&gt;

&lt;h2 id=&quot;размытие-картинки-гауссов-блюр&quot;&gt;Размытие картинки (гауссов блюр)&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Что такое резкость? Что такое размытие? Какая из этих двух картинок более резкая, какая более размытая?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/two_images_sharpness.png&quot; alt=&quot;Two images sharpness&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Давайте сделаем картинку более размытой, как этого добиться?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 3&lt;/code&gt; В main.cpp сделайте картинку более размытой - в пикселе-результате посчитайте среднюю яркость в некоторой окрестности, например в окне 10х10.&lt;/p&gt;

&lt;p&gt;Гауссово размытие (Гауссов блюр) - это &lt;strong&gt;взвешенное среднее&lt;/strong&gt; (&lt;strong&gt;свертка&lt;/strong&gt; с ядром Гаусса, &lt;strong&gt;convolution&lt;/strong&gt;), веса выглядят так:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/2026/01/gaussian_filter_1d_2d.png&quot; alt=&quot;Gaussian kernel 1D and 2D&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Какие свойства отличаются у гауссова размытия от усреднения в окне?&lt;/p&gt;

&lt;h2 id=&quot;увеличиваем-резкость-картинки&quot;&gt;Увеличиваем резкость картинки&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Что будет если взять картинку А, размыть ее и получить картинку B. Затем найти картинку C=A+(A-B). Как будет выглядеть картинка?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 4&lt;/code&gt; Попробуйте!&lt;/p&gt;

&lt;h2 id=&quot;уменьшаем-размер-картинки&quot;&gt;Уменьшаем размер картинки&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 5&lt;/code&gt; Постройте картинку в 16 раз меньше. Как вам качество результата? А что делать если коэффициент уменьшения размера не целый?&lt;/p&gt;

&lt;h1 id=&quot;выводы&quot;&gt;Выводы&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Картинка - это функция&lt;/strong&gt;. Картинка мало отличается от дискретизированной функции, например в “картинке” может храниться ландшафт - карта высот местности&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Локальный git&lt;/strong&gt; - это просто и удобно, позволяет сохранять промежуточные изменения&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitHub Actions CI&lt;/strong&gt; - позволяет при каждом commit+push проверять что вы случайно не сломали проект&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Многое можно узнать от LLM&lt;/strong&gt; - если задавать ей вопросы а не просить дать готовый результат&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Следить за конвенциями/системами координат&lt;/strong&gt; - чтобы меньше путаться, меньше багать и легче проверять код на корректность&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;к-следующему-занятию&quot;&gt;К следующему занятию&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 6&lt;/code&gt; Преобразуйте картинку из цветной в черно-белую. Попробуйте сделать не всю картинку ярче, а только какой-то один канал.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Вопрос&lt;/code&gt; Зная что у нас темный фон - как автоматически угадывать его цвет чтобы про каждый пиксель понять - фон это или кусок пазла? А что если в какой-то фотографии фон более темный, а в какой-то более светлый?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Задание 7&lt;/code&gt; Подумайте, может у вас появятся вопросы к коду, структуре проекта или чему-то еще.&lt;/p&gt;
</description>
        <pubDate>Thu, 08 Jan 2026 18:30:40 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/2lecture01-working-with-image.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/2lecture01-working-with-image.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
    
      <item>
        <title>План курса, пререквизиты</title>
        <description>&lt;p&gt;&lt;img src=&quot;/static/2026/01/downscaled_photo_example.jpg&quot; alt=&quot;Downscaled photo of puzzle example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;В рамках курса на C++ изучим и вручную реализуем некоторые алгоритмы компьютерного зрения, которые в совокупности помогут нам решить задачу сборки пазла:&lt;/p&gt;

&lt;p&gt;1) Что вообще такое картинка с точки зрения кода?&lt;/p&gt;

&lt;p&gt;2) Куски пазла лежат на черном фоне, как разделить фотографию на фон и куски пазла?&lt;/p&gt;

&lt;p&gt;3) Найдя на фотографии отдельные куски пазла, как выделить у каждого куска пазла четыре стороны?&lt;/p&gt;

&lt;p&gt;4) Как по цветам вдоль края куска пазла найти другой смежный кусок пазла?&lt;/p&gt;

&lt;p&gt;Пререквизиты:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;вас не пугает написание базового кода на C++&lt;/li&gt;
  &lt;li&gt;не пугает много кода&lt;/li&gt;
  &lt;li&gt;не пугает когда задача оказывается нерешаема и получается лишь приближенный результат&lt;/li&gt;
  &lt;li&gt;подходит любая OS - windows/linux/macos&lt;/li&gt;
  &lt;li&gt;рекомендуется windows + установленная Visual Studio (ради компилятора) + CLion IDE для разработки&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/CompVisionCourses/CVPuzzleSolver&quot;&gt;Исходники с практическими заданиями&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Если у вас есть интерес - можете попробовать сделать все с чистого листа не используя мой код, но это потребует слишком много времени. В таком случае рекомендую хотя бы использовать LLM (с юнит-тестами и визуальной проверкой насколько хорошо он реализовывает каждый этап обработки).&lt;/p&gt;
</description>
        <pubDate>Thu, 08 Jan 2026 18:30:00 +0000</pubDate>
        <link>http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/1course-plan-and-prerequisites.html</link>
        <guid isPermaLink="true">http://polarnick.com/blogs/courses/sirius26cvpuzzlesolver/2026/01/08/1course-plan-and-prerequisites.html</guid>
        
        
        <category>blogs</category>
        
        <category>courses</category>
        
        <category>Sirius26CVPuzzleSolver</category>
        
      </item>
    
    
  </channel>
</rss>
