...

суббота, 12 мая 2018 г.

Почему Google уменьшает «время жизни» cookies, полученных с помощью HTTP

Еще в начале года в компании Google сообщили, что с июля (когда выходит Chrome 68) все сайты, использующие HTTP, будут помечаться как небезопасные. В компании считают, что это позволит повысить конфиденциальность пользователей в сети.

Однако на этом работа ИТ-гиганта с HTTP не закончилась. В прошлом месяце стало известно, что Google дополнительно уменьшит «время жизни» cookies, полученных с применением незащищенного протокола, до одного года. Подробнее о ситуации расскажем далее.


/ Flickr / Jeff Herbst / PD

Отправка cookies по HTTP в Google называют риском безопасности. Представители компании отмечают, что «cookie-долгожители» позволяют проводить атаку, получившую название «всеобъемлющий мониторинг» (Pervasive Monitoring). Это масштабное (и часто скрытое) отслеживание передаваемой информации путем сбора артефактов протокола, метаданных (например, заголовков) и данных приложений. Примером ситуации, в которой был замешан этот тип мониторинга, может быть история, когда NSA использовало PREF cookie для слежения за пользователями сети.

Google заявляют, что от подобного типа атак защищает HTTPS. Но так как на более защищенный протокол перешли не все (лишь 81 из 100 сайтов используют HTTPS по умолчанию), исследователи решили пойти дальше и уменьшить время жизни cookies.

Согласно данным телеметрии Google, cookie, полученные по HTTP, «живут» больше года. Разработчики Chrome планируют ограничить время жизни cookie. И сделать это постепенно: сперва сократить до одного года, потом — до нескольких дней. Они убеждены, что так будет сложнее отследить действия пользователей в интернете на разных сайтах.

Это изменение реализуют в обновлении Chrome 70, которое выйдет в конце октября 2018 года.

Суть предложения


Инженеры Google предлагают изменить формат передачи cookie следующим образом.

При формировании заголовка для исходящего запроса на незащищенный URL, сперва будет проверяться дата создания каждого cookie. Если «возраст» больше некоторого порогового значения (12 месяцев, а позднее — несколько дней), то cookie не добавляется в заголовок, а удаляется. Также предлагается изменить алгоритм установки времени создания cookie. Если содержимое осталось прежним, то время создания нового cookie согласуется со временем создания старого.

Как это скажется на работе веб-сервисов?


По заверениям разработчиков, проблем с обратной совместимостью возникнуть не должно. Однако это может сказаться на работе сервисов, использующих незащищенные долговременные cookies. Поэтому в Google предлагают рассмотреть следующие варианты:
  1. Все же перейти на HTTPS.
  2. Внедрить систему, похожую на ротацию ID в DoubleClick, значение которых повторно шифруется и обновляется каждый день. Это решение подойдет для тех, кто по каким-то причинам не может перейти на HTTPS.
  3. Отказаться от cookies как идентификаторов и использовать вместо них localStorage.


/ Flickr / Joi Ito / CC

А что с другими браузерами?


Разработчики других браузеров также пытались внедрить что-то подобное. Например, 2 года назад представители Mozilla предлагали превратить некоторые cookie браузера Firefox в сеансовые (1 и 2), но от этого предложения отказались.

Идея была в том, чтобы устанавливать cookies на сессию, если они не имели флага secure. Однако тестирование инициативы показало, что слишком малое число сайтов выставляют этот флаг. Даже сайты, которые используют HTTPS (в том числе google.com), пренебрегали этим.

Что касается предложения Google, то в компании надеются, что их решение сократить время жизни cookie подтолкнет сообщество к тому, чтобы сделать HTTPS «стандартом» в веб-среде.

О чем еще мы пишем в корпоративном блоге 1cloud:

Let's block ads! (Why?)

Навигация с архитектурными компонентами от Google. Часть 1. Знакомство

Одной из проблем, с которыми сталкивается разработчик немного подразросшегося приложения — навигация между экранами. Когда сценарии становятся нелинейными, уже тяжело обойтись стандартными startActivity и changeFragment. Эту проблему каждый решал по-своему: делал какое-то свое решение для навигации, использовал стороннее (к примеру, Cicerone) или же оставлял все как есть и городил кучу флагов и if else. Это очень огорчало инженеров Google, и вот уже на Google I/O 2018 появилось решение Navigation, которое идёт в комплекте с остальными Архитектурными компонентами!

В данной статье мы разберемся, что вообще требуется от фреймворка для навигации, познакомимся с тем, как устроено решение от Google и какими абстракциями оно оперирует.

Требования к навигации
Начнём с требований, которые мы предъявляем фреймворку для навигации. В цикле статей мы будем к ним возвращаться, когда поймём, что они выполняются или нет.

Приоритет 1 (Обязательно):
[1] Отвязка от жизненного цикла.
[2] Возможность осуществлять вложенную навигацию.
[3] Автоматически открывает предыдущий экран при команде «Назад».
[4] Есть возможность добавить анимацию для смены экранов.
[5] Передача аргументов.
[6] Возможность перестраивать навигацию в рантайме.
[7] Поддержка Shared element.
[8] Возможность открывать и закрывать цепочку экранов.
Приоритет 2 (желательно):
[9] Удобный механизм работы с deeplink.
[10] Возможность имплементации на Activity, Fragment, View.
[11] Тестирование навигации.
[12] Гибкость при изменениях.
[13] Возможность навигации из бизнес-логики.
[14] Возможность подменять экраны, которые находятся в навигационном стеке.
[15] Проверка аргументов во время компиляции.
[16] Имеет визуальное представление для простого проектирования.

Постулаты навигации

Итак, для начала разберёмся с новыми принципами, которые диктует нам Google:

Принцип 1. Фиксированная точка старта
Все ссылки в приложение ведут в одну точку.Это означает, что мы всегда стартуем с одного экрана (по сути это SplashScreen), а дальше уже решаем, куда идти в приложении.

Принцип 2. Навигация работает в виде стека LIFO
Мы можем только делать операции push и pop, а экраны, которые лежат в середине стека, неприкосновенны.

Принцип 3. Кнопки Back и Up работают одинаково
Наконец-то стрелка тулбара и хардварная «Назад» будут работать одинаково! Больше не смущаем пользователей!

Принцип 4. Диплинки в приложение порождают стек экранов, аналогичный тому, если бы мы дошли до экрана самостоятельно
Если мы открыли приложение по диплинку, нажимая кнопку «Назад», мы пройдем по всем экранам, аналогично тому, как если бы мы их открывали без диплинка.

Определения


Для быстрого старта

Для быстрого погружения можно скачать сэмпл с Github, чтобы попутно смотреть всё на практике.

В конце статьи также написано как побороть Android Studio, если сразу же не завелось.

Итак, как же устроен новый навигационный фреймворк. Он состоит из следующих компонентов:

Destination — экран, который открывается при навигации. Сохраним именно это обозначение, чтобы удобно было обращаться к документации. Это может быть Fragment, Activity, View и вообще всё, что вы подразумеваете под экраном навигации. В дальнейшем я буду говорить о фрагментах, но не забывайте, что можно использовать не только их.

NavHost интерфейс — это view-контейнер, по которому переключаются экраны (Destination), когда пользователь перемещается по приложению. Реализация по умолчанию для него — это NavHostFragment. В него посредством xml устанавливается граф для навигации.

NavGraph — это xml-предоставление навигации. В нём описываются экраны Destination и связи между ними.

NavController — сущность, которая осуществляет механизм навигации. Именно к нему мы будем обращаться, когда будем переключать между собой экраны (Destination). NavController устанавливается в NavHost. Если мы имеем дело с готовым NavHostFragment, то вся логика создания и предоставления NavController уже сделана за нас.

Action — команда переключения на другой экран. Её отправляем NavController-у для смены Destination

FragmentNavigator — внутренний класс, который инкапсулирует в себе транзакции фрагментов.

Принцип работы

Построенный NavGraph устанавливается в NavHostFragment (как частный случай NavHost). Теперь он знает, какие есть Action и какие экраны им соответствуют.
NavHostFragment предоставляет NavController, который сообщает ему о предстоящей транзакции. К NavController мы обращаемся из кода, отдавая нужную команду Action. За конечное переключение экранов отвечает FragmentNavigator, с которым взаимодействует NavHostFragment.

Имплементация

Настало время разобраться, как с этим зверем работать.

Для начала подключаем все необходимые библиотеки и плагины. Актуальную информацию проверяйте в соответствующем разделе на сайте Google.

В корневом **build.gradle**

buildscript {
   ext.kotlin_version = '1.2.41'
   repositories {
       google()
       jcenter()
   }
   dependencies {
       classpath 'com.android.tools.build:gradle:3.2.0-alpha14'
       classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
   }
}

В app/build.gradle

dependencies {
   def nav_version = '1.0.0-alpha01'
   implementation "android.arch.navigation:navigation-fragment:$nav_version"
   implementation "android.arch.navigation:navigation-ui:$nav_version"

   //другие зависимости
}

Создаём навигационный граф
Далее создаём карту навигации. Для неё зафиксирован отдельный тип ресурса — navigation, для которого предусмотрен специальный редактор.

Для этого создаём файл res/navigation/nav_graph.xml с содержимым. Можно это сделать не руками, а через стандартный конструктор

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools">

</navigation>

Переключаем редактор на вкладку Design, и в нём мы накидываем экраны (Destination) и связываем их при помощи стрелок Actions.
Механика очень интуитивна и похожа на взаимодействие с ConstraintLayout. Если возникли проблемы, то можно посмотреть на детальный гайд от Google. (Выполнено требование [3]).

Конструктор состоит из двух видов сущностей: Destination (экраны) и Action (стрелочки).

Destination имеет набор атрибутов:


  • ID — идентификатор экрана, для связи с другими посредством Action.
  • Class — класс данного экрана (к примеру, LoginFragment).
    Дополнительно имеется три группы параметров:
  • Arguments — аргументы, которые его параметризуют.
  • Actions — команды, определяющие, куда можно перейти с текущего экрана.
  • Deeplinks — диплинки для перехода на экран из-за пределов приложения.

Также существует возможность сделать этот экран стартовым.


В редакторе это выглядит так

Action имеет набор атрибутов.


  • ID — идентификатор Action: его мы будем использовать для переключения экранов.
  • Destination — ID Destination, на который ведёт Action.
    Дополнительно имеется 4 группы параметров:
  • Transaction — анимация для переключения. Можно указать различные виды для действий: Enter, Exit, Pop Enter, Pop Exit. Анимации берутся из xml ресурсов. Легко определить свои, для этого просто добавьте соответствующий ресурс в папку res/anim. (См пример с nav_custom_enter_anim.xml)
  • ArgumentDefaultValue — значение аргументов для фрагмента по умолчанию.
  • PopBehavior — как навигации вести себя при закрытии фрагмента. К примеру, мы можем захотеть закрыть все экраны из стека и вернуться к стартовому. (См пример с Action action_notificationFragment_to_dashboardFragment — в нем мы как раз переходим на главный экран). (Выполнено требование [16])
  • LaunchOption — опции по работе со стеком, напоминают launch Mode при работе со стеком Activity.

В редакторе это выглядит так

В итоге получается картинка похожая на титульную из статьи.

Привязываем навигационный граф к контейнеру

Навигационный граф есть, теперь привязываем его к контейнеру. Для этого добавляем в xml MainActivity, который выступает холдером для всей навигации NavHostFragment, и устанавливаем ему атрибуты:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@+id/container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true" />

</FrameLayout>

Предпоследняя строчка говорит о том, что навигация будет осуществляться по созданному нами навигационному графу.
Последняя строчка означает, что NavHostFragment установлен дефолтным, это гарантирует обработку системного 'back'. Для выполнения принципа 3 навигации также необходимо переопределить в Activity действие на стрелку назад:

MainActivity

override fun onSupportNavigateUp() 
 = findNavController(R.id.nav_host_fragment).navigateUp()

Выполнение команды навигации

Для того, чтобы переключиться на другой фрагмент, нам необходимо получить Navigator.
Это возможно сделать несколькими способами.

 NavHostFragment.findNavController(Fragment)
 Navigation.findNavController(View)
 Navigation.findNavController(Activity, @IdRes int viewId)

По сути, это команды из разных контекстов для поиска View, имплементирующую NavHost, и получения у неё NavController-а.

Далее у полученного NavController вызывается один из методов для навигации.

HomeFragment

fragment_home_button_dashboard.setOnClickListener {
   activity?.let { 
  findNavController(it, R.id.nav_host_fragment)
  .navigate(R.id.action_homeFragment_to_dashboardFragment)
   }
}

Для осуществления навигации при помощи NavController
у нас имеются в распоряжении:


  1. Несколько перегруженных методов navigate(). В них параметрами являются
    • ActionId — тот самый ID Action из xml
    • Bundle — набор аргументов,
    • NavOptions — аргументы для Action, по которым определяется анимация, стек и пр.),
    • NavDirections — абстракция, оборачивающая Bundle и ActionId
  2. Пара перегруженных методов popBackStack() для перехода к экранам из текущего стека.

В нашем примере используются только Action для перехода вперед. Команда 'Back' обрабатывается автоматически и вызывает обратное действие у Action (тот самый атрибут PopBehavior)

Так, при переходе назад после Action (команды) action_notificationFragment_to_dashboardFragment с экрана DashboardFragment мы попадаем не на NotificationFragment, а на HomeFragment.

nav_graph.xml

<action
   android:id="@+id/action_notificationFragment_to_dashboardFragment"
   app:destination="@id/dashboardFragment"
   app:enterAnim="@anim/nav_custom_enter_anim"
   app:popUpTo="@+id/homeFragment" />

Итоги
На первый взгляд, фреймворк от Google не вызвал резкого осуждения и выглядит как нечто, на что необходимо посмотреть внимательнее.
В этой части мы удостоверились, что выполнены требования [3] и [16]. После первого взгляда, таблица наших требований к навигации для решения от Google выглядит так:

Приоритет 1 (обязательно):
[1] Отвязка от жизненного цикла.
[2] Возможность осуществлять вложенную навигацию.
[3] Автоматически открывает предыдущий экран при команде «Назад» [✓].
[4] Есть возможность добавить анимацию для смены экранов.
[5] Передача аргументов.
[6] Возможность перестраивать навигацию в рантайме.
[7] Поддержка Shared element.
[8] Возможность открывать и закрывать цепочку экранов.
Приоритет 2 (желательно):
[9] Удобный механизм работы с deeplink.
[10] Возможность имплементацию на Activity, Fragment, View.
[11] Тестирование навигации.
[12] Гибкость при изменениях.
[13] Возможность навигация из бизнес логики.
[14] Возможность подменять экраны которые находятся в навигационном стеке.
[15] Проверка аргументов во время компиляции.
[16] Имеет визуальное представление для простого проектирования [✓].

Что дальше?

В следующих частях рассмотрим (может быть порядок будет меняться):


  • Транзакции для открытия фрагментов с аргументами.
  • Диплинки.
  • Встроенные средства для работы с BottomNavigation и Toolbar.
  • Разные типы навигации: вложенную и с несколькими Activity.
  • Как всё устроено внутри и насколько хорошо работает с жизненным циклом.
  • Как сделать навигацию на вью.
  • Как собрать навигационный граф из кода.
  • Как тестировать навигацию.
  • На сколько всё это готово к бою и какие абстракции надо добавить.

Как запустить прямо сейчас

Описанный пример лежит у меня на Github. Для того, чтобы его запустить, необходима Android Studio не ниже 3.2 Canary.


Как заставить Android Studio отображать навигационный граф

У меня завелось только с версией SDK Build Tools 28.0.0-rc1 (на 28.0.0-rc2 все отправлялось в бесконечную загрузку)

Если вдруг и это не помогло, то сделайте clean — меня в паре случаев выручало.

Let's block ads! (Why?)

[recovery mode] Битва за сетевой нейтралитет: Новая надежда

Почти пять месяцев прошло со дня решения Федеральной комиссии по связи (FCC) об отмене правил сетевого нейтралитета в США. В марте мы писали, что это событие вызвало сопротивление со стороны сенаторов, технологических компаний и даже целых штатов. Движение за сохранение сетевого нейтралитета с тех пор только набирало обороты.

Разберемся, кто и как противостоит решению FCC и когда новые правила вступят в силу.


/ Flickr / Backbone Campaign / CC

Битва за «свой» сетевой нейтралитет


В марте сразу несколько штатов начали готовиться к сохранению правил сетевого нейтралитета на своих территориях. В их числе Монтана, Вашингтон, Калифорния, Нью-Йорк, Род-Айленд, Небраска, Массачусетс.

На сегодняшний день движение за законодательное сохранение сетевого нейтралитета ведется уже почти в половине штатов. Защитникам отмененных правил противостоят крупные телекоммуникационные компании, такие как AT&T и Comcast. На протяжении всей истории вопроса они выступали против сетевого нейтралитета. Однако, штаты не сдаются.

В апреле губернатор Орегона Кейт Браун (Kate Brown) подписала законопроект, который запрещает госорганам заключать контракты с поставщиками, не соблюдающими «правила справедливого доступа». Канзас, Теннесси и Висконсин пошли по тому же пути. Такой подход только отчасти помогает штатам добиться своего, но не возвращает сетевой нейтралитет — локальный закон не распространяется на местных монополистов. Если у телекоммуникационной компании в регионе нет конкурентов, ограничить ее таким образом не получится.

Губернатор штата Род-Айленд Джина Раймондо (Gina Raimondo), как и ее орегонская коллега, в апреле выпустила распоряжение для госорганов. Им также можно пользоваться услугами только тех провайдеров, что соблюдают правила сетевого нейтралитета.

Тем временем в Калифорнии голосованием поддержали законопроект, который призван стать альтернативой решению FCC. Два профильных комитета выступили за запрет на замедление или блокирование доступа к контенту. Это первый этап на пути к принятию закона штата. AT&T, Comcast, Charter, Cox и другие телекоммуникационные компании выступают против локального регулирования в Калифорнии. Противники возврата к сетевому нейтралитету считают, что предлагаемый законопроект является «слишком строгим» по сравнению с правилами FCC.

Законодатели Нью-Йорка собираются воспользоваться опытом Калифорнии. Если закон пройдет в этих двух штатах, почти на пятую часть населения США будут распространяться правила сетевого нейтралитета, несмотря на решения FCC.

Схожий законопроект был отклонен в апреле в Коннектикуте. Один из республиканских сенаторов воспользовался правом на недопуск проекта до голосования. Однако, в начале мая законопроект вернулся к рассмотрению и теперь ждет одобрения.

Всеобщий протест


Защитники сетевого нейтралитета — это не только законодатели, но и обычные граждане. Результаты апрельского опроса Университета штата Мэриленд показывают, что 86% страны выступает против решения FCC.

Отмену правил сетевого нейтралитета наряду со скандалом об утечке данных из Facebook называют причиной роста популярности VPN среди американцев. Согласно отчету Edison Trends, за две недели после декабрьского голосования Комиссии, число платных подписок на VPN возросло более чем в два раза.

Правозащитные организации Free Press, Demand Progress и Daily Kos запустили интернет-кампанию, которая позволяет гражданам США отправить письмо мэрам своих городов с призывом поддержать сетевой нейтралитет. Благодаря этому более 100 мэров подписали соглашение о привлечении интернет-провайдеров к ответственности за нарушения правил сетевого нейтралитета.

Раскол в составе Комиссии привел к отставке одного из пяти комиссаров FCC — Мигнон Клайберн (Mignon Clyburn), бывшего руководителя Комиссии. В апреле она по собственному желанию ушла из FCC после восьми лет работы. Мигнон — сторонница сетевого нейтралитета. Она считает, что сейчас ее деятельность может принести «больше пользы», если она выйдет из состава Комиссии. После ухода Мигнон в Комиссии осталась одна защитница отмененных правил — Джессика Рознворсел (Jessica Rosenworcel).

Частные компании, выступившие против решения FCC, — Etsy, Tumblr, Postmates, Foursquare, Twilio и другие, — не отступают от своей позиции. Вместе они подготовили акцию «Red Alert for Net Neutrality». На своих сайтах они информируют о проблеме сетевого нейтралитета и призывают посетителей дать Конгрессу знать о своем несогласии с решением FCC.

Ранее несколько технологических компаний, таких как Vimeo, Mozilla, Kickstarter, Foursquare и Etsy, обратились в суд с исками, чтобы сохранить правила сетевого нейтралитета. Эти дела все еще находятся в производстве. К их протесту собирается присоединиться Американская ассоциация производителей ПО и компьютерных игр (The Entertainment Software Association, ESA). В ней состоят такие крупные игровые разработчики, как Electronic Arts, Konami и Capcom.

В начале мая ESA подала ходатайство о предоставлении разрешения на участие в судебном деле против FCC. Официальная позиция ассоциации такая: отказ от правил сетевого нейтралитета может препятствовать загрузке приложений и работе многопользовательских онлайн-игр. Решение FCC отменяет правила, запрещающие провайдерам замедлять трафик, а игры требуют надежного доступа в интернет.

Напомним, в число технологических компаний, выступающих за сохранение сетевого нейтралитета и готовых бороться за это в суде, входит уже свыше 40 участников, в том числе такие гиганты, как Amazon, Google, Facebook и Netflix.


/ Flickr / Tim Pierce / CC

Когда отменят сетевой нейтралитет?


Решение FCC вступает в силу поэтапно. В декабре 2017 года члены Комиссии проголосовали за отмену правил, но только в феврале решение было официально опубликовано в Федеральном реестре. Часть новых правил вступила в силу 23 апреля, через 60 дней после публикации в реестре. Но фактически сетевой нейтралитет в США может быть отменен через 50 дней после того, как Административно-бюджетное управление завершит рассмотрение изменений.

23 апреля запустилась процедура «отката» к прежним правилам — так это назвали в Twitter законодатели. Значительные изменения произойдут после окончательного решения Управления.

Сетевой нейтралитет все еще может выстоять, несмотря на усилия операторов и Комиссии. Препятствием для FCC стали не только законопроекты штатов и иски технологических компаний, но и Congressional Review Act. Это закон, который уполномочивает Конгресс пересматривать новые федеральные правила, изданные государственными органами, и отменять их путем принятия совместной резолюции Сенатом. Если Конгресс проголосует за восстановление правил сетевого нейтралитета в полном объеме, FCC не сможет отменить эти правила в будущем.

Как сообщает Free Press, большинство сенаторов настроено на отмену решения FCC. Все 49 демократов поддерживают резолюцию вместе с сенатором-республиканцем от штата Мэн. У сторонников сетевого нейтралитета в Конгрессе есть всего 60 дней, чтобы воспользоваться законом о пересмотре. Дедлайн — 12 июня. 9 мая сенаторы подали петицию, которая позволит провести голосование за отмену решения FCC.

Как признают сами сенаторы, вероятность законодательного возвращения сетевого нейтралитета не высока. Решение об отмене декабрьских результатов голосования FCC должно быть одобрено Палатой представителей и президентом Дональдом Трампом. Сложность заключается в том, что президент и большая часть конгрессменов нижней палаты поддерживают декабрьское решение Комиссии. Поэтому исход голосования в Сенате, даже если оно будет проведено, может никак не изменить ситуацию.

P.S. Еще несколько статей из корпоративного блога VAS Experts:


P.P.S. Другие части рассказа о «Битве за сетевой нейтралитет»:

Let's block ads! (Why?)

[Перевод] Туториал по Unreal Engine 4: фильтр Paint

image

С течением времени внешний вид игр становится всё лучше и лучше. В эпоху потрясающей графики сложно сделать так, чтобы собственная игра выделялась на фоне остальных. Один из способов сделать игру графически более уникальной — использование нефотореалистичного рендеринга.

Нефотореалистичный рендеринг включает в себя множество техник рендеринга. В них входят cel shading, toon-контуры и штриховка. Можно даже сделать так, что игра будет похожа на картину! Одним из способов получения такого эффекта является размытие фильтром Кавахары.

Для реализации фильтрации Кавахары мы научимся следующему:

  • Вычислять среднее и дисперсию для нескольких ядер
  • Выводить среднее значение для ядра с наименьшей дисперсией
  • Использовать оператор Собеля для нахождения локальной ориентации пикселя
  • Поворачивать ядра сэмплирования на основании локальной ориентации пикселя

Примечание: в этом туториале подразумевается, что вы уже знакомы с основами Unreal Engine. Если только осваиваете Unreal Engine, то изучите нашу серию туториалов Unreal Engine для начинающих из десяти частей.

Так как в этом туториале применяется HLSL, вы должны быть знакомы с ним или похожим на него языком, например, с C#.


Примечание: этот туториал является четвёртой частью серии туториалов, посвящённых шейдерам:

Приступаем к работе


Начните со скачивания материалов для туториала. Распакуйте их, перейдите PaintFilterStarter и откройте PaintFilter.uproject. Вы увидите следующую сцену:

Для экономии времени в сцене уже есть Post Process Volume с PP_Kuwahara. Это материал (и файлы шейдера), которые мы будем изменять.

Сначала давайте разберёмся, что такое фильтр Кавахары и как он работает.

Фильтр Кавахары


При съёмке фотографий вы можете заметить на изображении зернистую текстуру. Это шум, который нам совершенно не нужен.

Обычно от шума избавляются использованием фильтра низких частот, например blur. Ниже показано зашумлённое изображение после применения к нему box blur с радиусом 5.

Бо́льшая часть шума исчезла, но все границы потеряли свою резкость. Вот бы существовал фильтр, который мог сгладить изображение и сохранить при этом границы объектов!

Как вы могли уже догадаться, фильтр Кавахары соответствует всем этим требованиям. Давайте посмотрим, как он работает.

Как работает фильтрация Кавахары


Как и в свёртках, при фильтрации Кавахары применяются ядра, но вместо одного ядра используется четыре. Ядра расставлены таким образом, что перекрываются в одном пикселе (в текущем). Ниже показан пример ядер для фильтра Кавахары.

Сначала мы вычисляем среднее (средний цвет) для каждого ядра. Так мы размываем ядро, то есть сглаживаем шум.

Также для каждого ядра мы вычисляем дисперсию. По сути, это мера того, насколько варьируется цвет в ядре. Например, у ядра с близкими цветами будет низкая дисперсия. Если цвета различны, то дисперсия ядра будет высокой.

Примечание: если вы не знакомы с концепцией дисперсии или не знаете, как её вычислять, то изучите статью Standard Deviation and Variance на Math is Fun.

Наконец, мы находим ядро с наименьшей дисперсией и выводим его среднее значение. Именно благодаря выбору на основании дисперсии фильтр Кавахары позволяет сохранять границы. Давайте рассмотрим несколько примеров.

Примеры фильтрации Кавахары


Ниже показано изображение в оттенках серого размером 10×10. Видно, что на нём есть граница, идущая из левого нижнего в верхний правый угол. Также можно заметить, что в некоторых областях изображения присутствует шум.

Сначала мы выберем пиксель и определим, какое ядро имеет наименьшую дисперсию. Вот пиксель рядом с границей и связанные с ним ядра:

Как вы видите, у ядер, лежащих на границе, сильно варьируются цвета. Это говорит нам о высокой дисперсии и означает, что фильтр не будет их выбирать. Избегая выбора ядер, лежащих на границе, фильтр устраняет проблему размытых границ.

Для этого пикселя фильтр выберет зелёное ядро, потому что оно является наиболее однородным. Выходным значением тогда будет среднее значение для зелёного ядра, то есть цвет, близкий к чёрному.

Вот ещё один пиксель границы и его ядра:


На этот раз наименьшую дисперсию имеет жёлтое ядро, потому что оно единственное не находится на границе. Поэтому выходным значением будет среднее жёлтого ядра, то есть цвет, близкий к белому.

Ниже показано сравнение между box blur и фильтрацией Кавахары с радиусом 5.


Как видите, фильтрация Кавахары замечательно справляется со сглаживанием и сохранением границ. В нашем случае фильтр даже сделал границу резче!

Так совпало, что эта функция сглаживания с сохранением границ может придать изображению вид нарисованной картины. Так как у мазков кисти обычно получаются резкие границы и низкий шум, фильтр Кавахары оказывается удобным выбором для преобразования реалистичных изображений в художественный стиль.

Вот результат выполнения для фотографии фильтрации Кавахары с переменным размером:


Выглядит довольно красиво, правда? Давайте приступим к созданию фильтра Кавахары.

Создание фильтра Кавахары


В этом туториале фильтр разбит на два файла шейдера: Global.usf и Kuwahara.usf. В первом файле будет храниться функция вычисления среднего значения и дисперсии ядра. Второй файл — это входная точка фильтра, которая будет вызывать вышеупомянутую функцию для каждого ядра.

Сначала мы создадим функцию для вычисления среднего значения и дисперсии. Откройте папку проекта в ОС и перейдите в папку Shaders. Затем откройте Global.usf. Внутри вы найдёте функцию GetKernelMeanAndVariance().

Прежде чем приступать к созданию функции, нам необходим дополнительный параметр. Измените сигнатуру функции следующим образом:

float4 GetKernelMeanAndVariance(float2 UV, float4 Range)

Для сэмплирования сетки нам понадобится два цикла for: один — для горизонтальных смещений. второй — для вертикальных. В первых двух каналах Range будут содержаться границы горизонтального цикла. Во вторых двух будут содержаться границы вертикального цикла. Например, если мы сэмплируем верхнее левое ядро, а фильтр имеет радиус 2, то Range будет иметь значения:
Range = float4(-2, 0, -2, 0);

Теперь настало время приступать к сэмплированию.

Сэмплирование пикселей


Для начала нам нужно создать два цикла for. Добавьте в GetKernelMeanAndVariance() следующий код (под переменными):
for (int x = Range.x; x <= Range.y; x++)
{
    for (int y = Range.z; y <= Range.w; y++)
    {
        
    }
}

Это даст нам все смещения ядра. Например, если мы сэмплируем верхнее левое ядро и фильтр имеет радиус 2, то смещения будут находиться в интервале от (0, 0) до (-2, -2).

Теперь нам нужно получить цвет пикселя выборки. Добавьте во внутренний цикл for следующий код:
float2 Offset = float2(x, y) * TexelSize;
float3 PixelColor = SceneTextureLookup(UV + Offset, 14, false).rgb;

Первая строка получает смещение пикселя выборки и преобразует его в UV-пространство. Вторая строка использует смещение для получения цвета пикселя выборки.

Теперь нам нужно вычислить среднее значение и дисперсию.

Вычисление среднего значения и дисперсии


Вычисление среднего — достаточно простая задача. Мы просто суммируем все цвета и делим их на количество пикселей выборки. Для дисперсии мы используем показанную ниже формулу, где x — это цвет пикселя выборки:

Первое, что нам нужно сделать — вычислить суммы. Для получения среднего нам достаточно сложить цвета в переменной Mean. Для получения дисперсии нам нужно возвести цвет в квадрат, а затем прибавить его к Variance. Добавьте следующий код под предыдущим:
Mean += PixelColor;
Variance += PixelColor * PixelColor;
Samples++;

Далее добавьте следующее после циклов for:
Mean /= Samples;
Variance = Variance / Samples - Mean * Mean;
float TotalVariance = Variance.r + Variance.g + Variance.b;
return float4(Mean.r, Mean.g, Mean.b, TotalVariance);

Первые две строки вычисляют среднее и дисперсию. Однако возникает проблема: дисперсия распределена между каналами RGB. Чтобы решить её, в третьей строке мы суммируем каналы, чтобы получить общую дисперсию.

В конце функция возвращает среднее и дисперсию в виде float4. Среднее значение находится в каналах RGB, а дисперсия — в канале A.

Теперь, когда у нас есть функция для вычисления среднего значения и дисперсии, нам нужно вызвать её для каждого ядра. Вернитесь в папку Shaders и откройте Kuwahara.usf. Сначала нам нужно создать несколько переменных. Замените код внутри на следующий:

float2 UV = GetDefaultSceneTextureUV(Parameters, 14);
float4 MeanAndVariance[4];
float4 Range;

Вот, для чего используется каждая переменная:
  • UV: UV-координаты текущего пикселя
  • MeanAndVariance: массив для хранения среднего и дисперсии каждого ядра
  • Range: используется для хранения границ циклов for текущего ядра

Теперь нам нужно вызвать для каждого ядра GetKernelMeanAndVariance(). Для этого добавим следующее:
Range = float4(-XRadius, 0, -YRadius, 0);
MeanAndVariance[0] = GetKernelMeanAndVariance(UV, Range);

Range = float4(0, XRadius, -YRadius, 0);
MeanAndVariance[1] = GetKernelMeanAndVariance(UV, Range);

Range = float4(-XRadius, 0, 0, YRadius);
MeanAndVariance[2] = GetKernelMeanAndVariance(UV, Range);

Range = float4(0, XRadius, 0, YRadius);
MeanAndVariance[3] = GetKernelMeanAndVariance(UV, Range);

Так мы получим среднее и дисперсию каждого ядра в следующем порядке: верхнее левое, верхнее правое, нижнее левое и нижнее правое.

Далее нам нужно выбрать ядро с наименьшей дисперсией и вывести его среднее значение.

Выбор ядра с наименьшей дисперсией


Чтобы выбрать ядро с наименьшей дисперсией, добавьте следующий код:
// 1
float3 FinalColor = MeanAndVariance[0].rgb;
float MinimumVariance = MeanAndVariance[0].a;

// 2
for (int i = 1; i < 4; i++)
{
    if (MeanAndVariance[i].a < MinimumVariance)
    {
        FinalColor = MeanAndVariance[i].rgb;
        MinimumVariance = MeanAndVariance[i].a;
    }
}

return FinalColor;

Вот, что делает каждая из частей:
  1. Создаёт две переменные для хранения конечного цвета и наименьшей дисперсии. Инициализирует их обе со значениями среднего и дисперсии первого ядра.
  2. Обходит в цикле оставшиеся три ядра. Если дисперсия текущего ядра ниже наименьшего, то его среднее и дисперсия становятся новыми FinalColor и MinimumVariance. После выполнения циклов выводится FinalColor который будет средним значением ядра с наименьшей дисперсией.

Вернитесь в Unreal и перейдите к Materials\PostProcess. Откройте PP_Kuwahara, внесите ни на что не влияющие изменения и нажмите Apply. Вернитесь в основной редактор и посмотрите на результаты!

Выглядит довольно неплохо, но если приглядеться, то можно увидеть, что на изображении присутствуют странные блочные области. Я выделил некоторые из них:

Это побочный эффект применения ядер, выровненных относительно осей. Мы можем уменьшить этот эффект, применив улучшенную версию фильтра, которую я называю направленным фильтром Кавахары.

Направленный фильтр Кавахары


Этот фильтр похож на исходный, но теперь ядра будут выровнены относительно локальной ориентации пикселей. Вот пример ядра в направленном фильтре Кавахары:

Примечание: так как мы можем представить ядро в виде матрицы, мы записываем измерения в виде высота x ширина вместо привычного ширина x высота. Подробнее о матрицах мы поговорим ниже.

Здесь фильтр определяет ориентацию пикселя так, чтобы он располагался вдоль границы. После чего можно соответствующим образом повернуть всё ядро.

Для вычисления локальной ориентации фильтр выполняет проход свёртки с помощью оператора Собеля. Если термин «оператор Собеля» звучит для вас знакомо, то это потому, что он является популярной техникой распознавания границ. Но если это приём распознавания границ, то как можно использовать его для получения локальной ориентации? Чтобы ответить на этот вопрос, нам нужно понять, как работает оператор Собеля.

Как работает оператор Собеля


Вместо одного ядра в операторе Собеля используется два.

Gx даёт нам градиент в горизонтальном направлении. Gy даёт нам градиент в вертикальном направлении. Давайте воспользуемся в качестве примера таким изображением в оттенках серого размером 3×3:

Для начала выполним свёртку среднего пикселя для каждого ядра.

Если нанести каждое значение на 2D-плоскость, то мы увидим, что получившийся вектор указывает в том же направлении, что и граница.

Чтобы найти угол между вектором и осью X, мы подставляем значения градиентов в функцию арктангенса (atan). После чего мы можем использовать получившийся угол для поворота ядра.

Именно так мы можем использовать оператор Собеля для получения локальной ориентации пикселя. Давайте попробуем это сделать.

Нахождение локальной ориентации


Откройте Global.usf и добавьте внутрь GetPixelAngle() следующий код:
float GradientX = 0;
float GradientY = 0;
float SobelX[9] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
float SobelY[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
int i = 0;

Примечание: Заметьте, что последняя скобка в GetPixelAngle() отсутствует. Это сделано намеренно! Если хотите знать, зачем так делать, прочитайте наш туториал по шейдерам на HLSL.

Вот, для чего используется каждая переменная:

  • GradientX: хранит градиент для горизонтального направления
  • GradientY: хранит градиент для вертикального направления
  • SobelX: ядро горизонтального оператора Собеля в виде массива
  • SobelY: ядро вертикального оператора Собеля в виде массива
  • i: используется для доступа к каждому элементу в SobelX и SobelY

Далее нам необходимо выполнить свёртку с помощью ядер SobelX и SobelY. Добавьте следующий код:
for (int x = -1; x <= 1; x++)
{
    for (int y = -1; y <= 1; y++)
    {
        // 1
        float2 Offset = float2(x, y) * TexelSize;
        float3 PixelColor = SceneTextureLookup(UV + Offset, 14, false).rgb;
        float PixelValue = dot(PixelColor, float3(0.3,0.59,0.11));
        
        // 2
        GradientX += PixelValue * SobelX[i];
        GradientY += PixelValue * SobelY[i];
        i++;
    }
}

Вот, что происходит в каждой части:
  1. Первые две строки получают цвет пикселя выборки. Третья строка снижает насыщенность цвета, преобразуя его в значение оттенков серого. Это упрощает вычисление градиентов изображения в целом, вместо получения градиентов для каждого цветового канала.
  2. Для обоих ядер умножаем значение пикселя в оттенках серого на соответствующий элемент ядра. Затем прибавляем результат к соответствующей переменной градиента. Затем происходит инкремент i, чтобы она содержала в себе индекс следующего элемента ядра.

Для получения угла мы используем функцию atan() и подставляем наши значения градиентов. Под циклами for добавьте следующий код:
return atan(GradientY / GradientX);

Теперь, когда у нас есть функция для получения угла пикселя, нужно каким-то образом применить его для поворота ядра. Мы можем сделать это с помощью матрицы.

Что такое матрица?


Матрица — это двухмерный массив чисел. Например, вот матрица 2×3 (с двумя строками и тремя столбцами):

Сама по себе матрица не выглядит особо интересной. Но истинная мощь матриц проявляется, когда мы перемножаем её с вектором. Это позволяет нам выполнять такие действия, как поворот и масштабирование (в зависимости от вида матрицы). Но как же нам создать матрицу для поворота?

В системе координат у нас есть вектор для каждого измерения. Это базисные векторы, определяющие положительные направления осей.

Ниже представлено несколько примеров разных базисных векторов для двухмерной системы координат. Красной стрелкой показано положительное направление по X. Зелёная стрелка задаёт положительное направление по Y.


Чтобы повернуть вектор, мы можем использовать эти базисные векторы для построения матрицы поворота. Она является просто матрицей, содержащей позиции базисных векторов после поворота. Например, представьте, что у нас есть вектор (оранжевая стрелка) в координатах (1, 1).

Допустим, мы хотим повернуть его на 90 градусов по часовой стрелке. Сначала мы поворачиваем на ту же величину базисные векторы.

Затем мы строим матрицу 2×2, применяя новые позиции базисных векторов. Первый столбец — это позиция красной стрелки, а второй — позиция зелёной стрелки. Это и есть наша матрица поворота.

Наконец, мы выполняем матричное умножение, используя оранжевый вектор и матрицу поворота. Результат будет новой позицией оранжевого вектора.

Примечание: вам необязательно знать, как выполняется матричное умножение, потому что в HLSL есть для этого встроенная функция. Но если вы хотите узнать, то изучите статью How to Multiply Matrices на Math is Fun.

Разве это не здорово? Но ещё лучше то, что мы можем использовать показанную выше матрицу для поворота любого 2D-вектора на 90 градусов по часовой стрелке. Если говорить о нашем фильтре, то это значит, что нам достаточно один раз создать матрицу поворота для каждого пикселя и использовать её для всего ядра.

Теперь настало время поворота ядра с помощью матрицы поворота.

Поворот ядра


Для начала нам нужно изменить GetKernelMeanAndVariance(), чтобы она получала матрицу 2×2. Это нужно потому, что мы будем создавать матрицу поворота в Kuwahara.usf и передавать её. Измените сигнатуру GetKernelMeanAndVariance() следующим образом:
float4 GetKernelMeanAndVariance(float2 UV, float4 Range, float2x2 RotationMatrix)

Далее замените первую строку внутреннего цикла for на такой код:
float2 Offset = mul(float2(x, y) * TexelSize, RotationMatrix);

mul() будет выполнять матричное умножение, используя смещение и RotationMatrix. Так мы будем поворачивать смещение вокруг текущего пикселя.

Далее нам нужно создать матрицу поворота.

Создание матрицы поворота


Для создания матрицы поворота мы применим функции синуса и косинуса следующим образом:

Закройте Global.usf и откройте Kuwahara.usf. Затем добавьте под списком переменных следующее:
float Angle = GetPixelAngle(UV);
float2x2 RotationMatrix = float2x2(cos(Angle), -sin(Angle), sin(Angle), cos(Angle));

В первой строке вычисляется угол текущего пикселя. Вторая строка создаёт матрицу поворота с использованием угла.

Наконец, нам нужно передать для каждого ядра RotationMatrix. Измените каждый вызов GetKernelMeanAndVariance() следующим образом:

GetKernelMeanAndVariance(UV, Range, RotationMatrix)

И на этом мы закончили создание направленного фильтра Кавахары! Закройте Kuwahara.usf и вернитесь в PP_Kuwahara. Внесите ни на что не влияющие изменения, нажмите Apply и закройте его.

Ниже показано изображение со сравнением обычного и направленного фильтров Кавахары. Заметьте, что направленный фильтр не создаёт блочности.


Примечание: можно использовать PPI_Kuwahara для изменения размеров фильтра. Рекомендую изменить размер фильтра таким образом, чтобы радиус по X был больше, чем радиус по Y. Это увеличит размер ядра вдоль границы и поможет в создании направленности.

Куда двигаться дальше?


Скачать готовый проект можно по ссылке.

Если вы хотите больше узнать о фильтре Кавахары, то можете прочитать статью про анизотропную фильтрацию Кавахары. На самом деле направленный фильтр Кавахары является упрощённой версией фильтра, представленного в этой статье.

Рекомендую вам поэкспериментировать с матрицами, чтобы с помощью них попробовать создать новые эффекты. Например, можно использовать сочетание матриц поворота и размытия (blurring) для создания радиального или кругового размытия. Если вы хотите больше узнать о матрицах и о том, как они работают, то изучите серию видео 3Blue1Brown Essence of Linear Algebra.

Let's block ads! (Why?)

пятница, 11 мая 2018 г.

Мысли про порог входа в технологии в 2018, пример простого мобильного приложения и не только


Когда-то я учился в 5-ом классе и сейчас мне почему-то кажется, что между мной тогдашним и ребятами, которые сейчас ходят в 5-ый класс, огромная пропасть с точки зрения доступа к технологиям. А так как технологии развиваются все быстрее и быстрее, интересно, что будет происходить, когда ребята, которые сейчас ходят в 5-ый класс, станут моими ровесниками.

В этой короткой статье я на примере простого мобильного приложения для iPhone хочу показать, насколько доступны технологии.

Немного лирики


Прошло около 20 лет с момента, когда мои глаза расширились до предела при виде анимированной скрепки на экране компьютера, которая предлагала мне помочь. Скрепка тогда казалась всемогущей, между тем, что могла она и магией для меня не было большой разницы.

Всего 13 лет назад я впервые подержал в руках КПК (это карманный персональный компьютер). Компьютер. Карманный. C Windows. Работает не от сети 220 В., а от батарейки, имеет доступ в Интернет. Без провода и карточки «Вэб Плас». Доступ в Интернет. В тот самый Интернет, 5 минутный доступ в который получила в подарок за большие заслуги наша группа компьютерного кружка. Нам было разрешено посетить 1 сайт (модем на 33 Кбит). Вся группа долго обсуждала — какой именно это будет сайт. Голосование, дебаты.

Перенесемся в май 2018 года


В Калифорнии проходит конференция Google I/O 2018, в числе десятков прочих анонсов: в сервис Firebase добавили ML Kit — инструмент, дающий возможность на смартфоне или в облаке распознавать содержимое картинок, лица, текст и много другого разного, даже свои модели TensorFlow. Эка невидаль? Что мы не знаем про машинное обучение и нейронные сети?

Ладно, а давайте сделаем приложение, чтобы текст распознавало.
Открываем Xcode — создаем новый проект. Создаем Podfile куда пишем код ниже и устанавливаем:

  pod 'Firebase/Core'
  pod 'Firebase/MLVision'
  pod 'Firebase/MLVisionTextModel'


Интерфейс простой:
  • один UIImageView, куда выведем картину с камеры,
  • две UIButton, первая запустит камеру, вторая запустит чтение

Чтобы iPhone дал доступ к камере, добавим в Info.plist

<key>NSCameraUsageDescription</key>
<string>Предоставьте приложению доступ к камере</string>


Создадим контроллер, подключим к нему UIImageView и нажатия наших двух UIButton.
А пусть нам ещё то, что удалось распознать, смартфон произнесет голосом.
import UIKit
import Firebase
import AVKit
import AVFoundation

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    //разпознование текста из картинки
    lazy var vision = Vision.vision()
    var textDetector: VisionTextDetector?
    
    //синтез речь
    let synthesizer = AVSpeechSynthesizer()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        textDetector = vision.textDetector()
    }

    //подключаем UIImageView
    @IBOutlet weak var imagePicked: UIImageView!
    
    //включаем камеру после нажатия на кнопку
    @IBAction func openCamera(_ sender: Any) {
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            var imagePicker = UIImagePickerController()
            imagePicker.delegate = self
            imagePicker.sourceType = .camera
            imagePicker.allowsEditing = false
            self.present(imagePicker, animated: true, completion: nil)
        }
    }
    
    //после нажатия на кнопку текст нужно распознать и произнести
    @IBAction func getText(_ sender: Any) {
        if let image = imagePicked.image as? UIImage {
    let visionImage = VisionImage(image: image)
    
    textDetector?.detect(in: visionImage, completion: { [weak self] (visionTextItems, error) in
        if error != nil {
            print("произошла ошибка \(error)")
            return
        }
        
        if let foundItems = visionTextItems {
            if foundItems.count > 0 {
                for item in foundItems {
                    print("распознан текст: \(item.text)")
                    
                    let utterance = AVSpeechUtterance(string: item.text)
                    utterance.rate = 0.4
                    utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
                    self?.synthesizer.speak(utterance)
                }
            } else {
                print("no images found")
            }
        }
        })
}
     }
    
    //переносим фотографию с камеры в UIImageView
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
        let image = info[UIImagePickerControllerOriginalImage] as! UIImage
        imagePicked.image = image
        dismiss(animated:true, completion: nil)
     }
}


Запускаем, пишем ручкой на листике (с печатным текстом совсем неинтересно), фотографируем

и смотрим в консоль:

Ах да! Все это работает в авиарежиме, то есть без доступа к Интернету. И распознавание рукописного текста, и синтез речи.


Инструменты, методы и задачи, которые казались очень сложными ещё вчера, сегодня доступны быстро и бесплатно прямо «из коробки». А что будет дальше, лет так через 20, а через 50?

Let's block ads! (Why?)

Почему корпорациям и стартапам так сложно работать вместе

Стартапы, конечно, должны вести разведку своих потенциальных клиентов везде: используя, например, открытые банковские конференции, хакатоны, организуемые банками, и другие мероприятия. Однако надо иметь в виду, что возможности, которые там возникают, достаточно ограничены. В терминологии маркетинга каждое такое мероприятие — это единичное касание, которое само по себе не может дать полной картины. Но информацию, на которой базируется стратегическое планирование бизнеса, следует получать из нескольких источников.

Let's block ads! (Why?)

[Перевод] Марвин Мински «The Emotion Machine»: Глава 3 «Обучаясь на Неудачах»

Отец искусственного интеллекта размышляет о том, как сделать машину, которая бы гордилась нами. Марвин Мински был довольно жестким ученым и то, что он своим «скальпелем познания» исследует тему чувств и эмоций, что же делает нас людьми, довольно интересно и полезно. Книга — отличный образец того, как «ИТишным подходом» попробовать осмыслить «человеческое»: ценности, идеалы, любовь, боль, здравый смысл.
image

§3-5 Корректоры, Супрессоры и Цензоры


«Не обращайте внимание на критиков. И даже не пытайтесь игнорировать их»
— Сэм Голдуин.

Было бы замечательно никогда не ошибаться и никогда не иметь ложных представлений. Но совершенство всегда будет недостижимо: мы постоянно делаем ошибки и недочёты.

Боль в колене Джоан стала ухудшаться. Сегодня ей было больно весь день, даже когда она не трогала больное место. Она думает: «Я не должна была поворачиваться, когда поднимала эту коробку. И я наконец-то должна приложить лёд к моему колену»

Нам нравится думать о ситуации в позитивном ключе: «Эксперт – это человек, который знает что он делает». И вы знаете, как хорошо делать большинство вещей, что вы едва ли задумываетесь о том как их нужно делать. Вы вспоминаете большинство вещей, которые вы видите, а также вы общаетесь не задаваясь вопросом о том, как вы это делаете. Тем не менее, опыт также имеет и негативную сторону: «Эксперт – это тот, кто редко терпит неудачу из-за того, что он знает, что делать не надо». Именно из-за этого мы обычно не сталкиваемся со стеной. Мы редко пытаемся вставить какие-либо вещи в наши глаза. Мы никогда не говорим незнакомцам, насколько они страшные.
Какая часть человеческих знаний основана на знании какие действия не стоит предпринимать, для того чтобы избежать всяческих ошибок? Мы многого не знаем о подобном «негативном опыте», потому что эта тема крайне редко обсуждается в Психологии, за исключением трудов Зигмунда Фрейда.

Возможно, этот факт оставался не обнаруженным, из-за того, что мы не можем наблюдать извне того, что люди не делают для достижения какой-либо цели. Изучать подобные вещи также сложно, как и заставлять свой разум думать над тем, что заставляет вас держаться подальше от абсурдных идей. Чтобы учесть этот неприятный факт, мы предположим, что в нашем мозге имеются особые ресурсы, назовём их Критиками, каждый из которых распознаёт определённый вид ошибки. Ниже приведено несколько видов Критиков; мы рассмотрим их подробнее в главе §7.

Критик-Корректор предупреждает вас, когда вы начинаете делать что-либо опасное. «Ты должен прекратить делать это действие прямо сейчас, потому что ты тянешь свою руку в открытый огонь». Но подобные предупреждения могут приходить слишком поздно.

Супрессор может предупредить об опасности, с которой вы сталкиваетесь, и может наложить вето на определённые действия, которые вы хотели бы совершить, остановив их, пока не стало слишком поздно – например, говоря вам: «Нет, не иди в этом направлении!» Или он может сказать вам использовать какую-то технику отладки, для какого-либо события.

Цензор (или Корректор) срабатывает ещё раньше, и сдерживает вас от наличия опасных мыслей – поэтому вы даже не можете и помыслить о том, чтобы засунуть свой палец в огонь. Цензоры могут настолько эффективно работать, что вы даже не будете знать, что он выполняет какое-либо действие.

Самоконтроллер распознаёт, что вы не смогли выполнить то или иное действие, потому что, вместо того чтобы выполнять это действие так, как ранее было задумано, вы «передумывали» делать его.

Супрессоры действуют гораздо безопаснее Корректоров, но они оба, как правило, заставляют вас действовать более осмотрительно, в то время, когда вы думаете о чем-то отвлечённом. Тем не менее, цензоры совершенно не потребляют времени на обработку данных, потому что они отталкивают вас от рискованных действия, без прерывания исполнений других ваших мыслей таким образом, они вас ускоряют. Именно поэтому некоторые эксперты могут делать дела быстро: они попросту не задумываются о неправильных вещах.

Студент: Как цензор может отбросить плохую мысль до того, как вы будете знать о чём вы, вероятнее всего, будете думать? Раз здесь нет какого-либо парадокса?

Программист ИИ: Это не проблема. Просто спроектируйте каждый цензор как самообучающуюся машину, который будет фиксировать какие решения приводят к ошибкам. Затем, когда он будет обнаруживать подобный выбор, он будет направлять вашу мысль в другое русло, что, в конечном итоге, не приведёт вас к принятию такого же решения.

Студент: В таком случае, не будет ли такой Цензор потреблять определённое время вашего мозга? Кроме того, что если оба варианта привели к одинаково плохим последствиям? В таком случае Цензор должен работать ещё раньше, для того чтобы вы не попали в плохую ситуацию из-за решения самого Цензора.

Программист ИИ: Мы можем решить эту проблему, попросту дав каждому Цензору достаточно памяти для запоминания некоторых предыдущих шагов, которые привели к развитию негативной ситуации.

Студент: Может быть подобная игра не стоит свеч? Если ваши Цензоры могут спасти от любой вашей ошибки, это может сделать ваше поведения ультра консервативным и вы не сможете генерировать новые идеи.

В самом деле, некоторые эксперты знают так много вещей и способов, из-за чего устройство или действие может не сработать, что им становится крайне трудно генерировать новые идеи вообще.

Чрезмерное переключение


Последнее время — а почему, я и сам не знаю — я утратил всю свою веселость, забросил все привычные занятия; и, действительно, на душе у меня так тяжело, что эта прекрасная храмина, земля, кажется мне пустынным мысом; этот несравненнейший полог, воздух, видите ли, эта великолепно раскинутая твердь, эта величественная кровля, выложенная золотым огнем, — все это кажется мне не чем иным, как мутным и чумным скоплением паров.
— Гамлет

Что происходит, когда включаются (или выключаются) слишком много Критиков? Вот краткое описание, что происходит в этом случае:

Кэй Редфилд Джеймисон: «Клиническая картина маниакально-депрессивого синдрома гораздо более смертельна и бесконечно сложнее, чем предполагаемая сейчас психологической номенклатурой биполярное расстройство. Циклы колебания настроения и внутренней энергии служат показателем постоянной смены мыслей, поведения и чувств. Болезнь охватывает крайние точки человеческого опыта. Мышление может представлять собой то, витиеватый психоз, или «безумие», то систему невероятно ясных, быстрых и креативных ассоциаций, то такую сильную деградацию, что при таком состоянии невозможно различить никакой мыслительной деятельности. Поведение может быть бешеное, экспансивное, причудливое или соблазнительное или же оно может быть параноидальным, вялым, и опасно суицидальным. Настроение может колебаться между эйфорией и отчаянием или раздражительностью и отчаянием. … Но подобные максимумы, связанные с манией, как правило приятны и продуктивны, во время ранних, более легких стадий»

В более поздней работе автор больше говорит о подобных ментальных каскадах:
«Похоже, что как качество, так и количество мыслей формируются во время гиопмании. Их количество может варьировать от очень небольшого ускорения, до полной психотической непоследовательности. Пока неясно, что вызывает это качественное изменение в умственной работе. Тем не менее, подобное изменение когнитивных состояний может способствовать формированию уникальных идей и ассоциаций. … Человек, страдающий депрессией имеет тенденцию задавать тягучие и витиеватые вопросы, в то время как человек, страдающий манией, имеет тенденцию отвечать с необузданной энергией и уверенностью. Постоянные переходы из крайне скованного образа мысли до невероятно экспансивного образа мысли, от подавленного до вспыльчивого состояния, от мрачного до невероятно живого настроения, от открытых до закрытых поз, от холодного до обжигающе теплого отношения к окружающим, а также скорость и текучесть движений, происходящих во время подобного изменения состояний – может быть крайне болезненным и сбивающим с толку.»

Нам легко увидеть крайности в поведении людей, болеющих теми психическими заболеваниями, которыми мы называем «биполярными» расстройствами, на в главе §7 будет высказано предположение, что мы все используем подобные процессы в нашей обыденной жизни. Таким образом, вы можете использовать следующую процедуру, когда сталкиваетесь с новой для вас проблемой:

Вначале, выключите большинство ваших Критиков. Это поможет вам начать думать о тех вещах, о которых вы ранее думать не могли – не волнуясь о том как хорошо могут эти вещи сработать. Таким образом вовлеките в себя в короткий период «маниакального» состояния.

Затем, вы можете включить большинство критиков, для того чтобы исследовать выданные вами результаты с долей скепсиса – так, если бы у вас была лёгкая депрессия.

И наконец, выберите один подход, который кажется вам многообещающим, а затем начните его разрабатывать до того момента, пока один из ваших Критиков не начнёт жаловаться, на то, что вы перестали добиваться прогресса.

Иногда вы можете переходить между этими фразами сознательно. Одна из моих догадок заключается в том, что мы постоянно бессознательно переходим между этими тремя состояниями настолько быстро, что едва можем заметить, что в нашем мозге что-либо происходит.

Обучаясь на Неудачах


«Никогда не прерывайте противника, когда он совершает ошибку»
— Наполеон Бонапарт

Многие вещи, к которым мы относимся позитивно ( например красота, юмор, а также само удовольствие) могут основываться на цензуре — таким образом, их можно, в конечном итоге, отнести к негативным вещам. Таким образом, удовольствие может присваиваться «позитивному» процессу, который в данный момент времени находится «под контролем» — независимо от того, что другие процессы (который в данный момент времени подавляются) могут рассмотреть конкретное действие как «негативное». (См. §9-2 из SoM) Таким образом, фраза «Я получаю от этого удовольствие» означает как «Я хочу остаться в нынешнем состоянии», так и «Я хочу предотвратить любые изменения этого состояния».

Студент: Но мне казалось, что широко распространено мнение, что обучение работает путём «усиления» связей, которые привели к успеху, и «ослабления тех связей, которые привели к неудаче. Многие преподаватели говорят, что нам всегда должно быть приятно учиться, потому что удовольствие — это наша награда за успех, тогда как неудача сдерживает наши стремления и демотивирует нас.

Этот популярный взгляд основывается на исследованиях(которые в основном проводились на голубях и крысах), которое показало, что более быстрое вознаграждение ускоряет обучение. Это уверило многих учителей в вышеописанной идее, что обучение должно быть приятным опытом. Однако, мы не должны необдуманно применять эту идею к нам самим, так как мы можем обучаться также размышляя над сделанными действиями!

Я не говорю, что „теория усиления“ неверна — для людей эта теория может быть частью большого паззла; в §8-5 мы будем говорить о том, что наша способность обучаться основанная на осмыслении неудач может быть более важна, чем „подкрепление“ обучения — во всяком случае, так обстоят дела с высокоуровневыми видами мышления. Ибо, когда как удовольствие может помочь нам изучать простые вещи, в разделе §9-4 будет обсуждаться, что нам могут понадобиться испытать страдания, чтобы в нашем мозге случились масштабные перестройки. Если это действительно обстоит именно таким образом, древний стоик сказал бы: вознаграждение за успех может привести вас к празднованию этого события, нежели к исследованию его. Вот ещё несколько причин, почему „обучение на основании успеха“ может не всегда быть оправданным, в особенности, когда этот успех был ожидаемым.

Подкрепление может привести к Ригидности: если система уже работает как надо, тогда дополнительный „позитивный стимул“ может сделать соединения между нейронами гораздо сильнее, чем они должны быть, что может затруднить адаптацию этой системы к новым ситуациям в будущем.

Зависимость может привести к Побочным Эффектам: если определённый ресурс Р начал работать настолько хорошо, что все остальные ресурсы стали зависеть от него, тогда любое изменение, которое вы внесёте в ресурс Р с большей долей вероятности повредит эти зависимые ресурсы. Иными словами, как говорится в поговорке: „Не чини эту, пока оно не сломалось“.

Негативная Экспертиза: одним из способов избежать подобных побочных эффектов — оставить оформленный ресурс неизменным, но ввести в систему дополнительного Критика и Цензора, для мониторинга условий и избежания ситуации, когда этот ресурс бы не сработал. Другими словами, начать относиться к этому ресурсу как к некоторым исключениям в правиле.

Радикальное обучение: вы можете „настроить“ определённый навык путём применения в обучении большого количества малых шагов, но вскоре никакие мелкие изменения вам не помогу, ибо вы попадёте в область локального максимума. В таком случае, дальнейшие изменения могут потребовать от вас некоторого дискомфорта и принятия разочарования в ваших навыках. Для больше информации смотрите §9-4.

Принцип Паперта: когда два или больше применяемых методов конфликтуют, то, вместо того, чтобы искать компромисс между ними, выгоднее отказаться от них и попробовать что-нибудь другое. Многие шаги, которые определяют умственный рост основаны меньше на приобретении новых навыков, и больше на обучении использования старых знаний но совершенно новыми способами.

Таким образом, по вышеописанным причинам нам нужно учиться не только путём использования методов, которые работали в прошлом, но и с помощью методов, которые не дали результатов, а также на размышлении почему эти методы не возымели успехов, что позволит избежать часто встречающихся ошибок.

Студент: Да, но почему мы не можем обучаться попросту нарушив связи в том случае, когда вы совершаете досадную ошибку, чтобы ваш мозг никогда не сделал подобного действия снова?

Одна из причин, почему это очень плохая идея заключается в том, что вы теряете шанс узнать что всё-таки пошло не так, а вы совершили ошибку (для того, чтобы в будущем вы смогли избежать ошибок подобного рода). Вторая проблема заключается в том, что используя эту тактику и меняете что-либо в системе, то эта действие приводит к изменению поведения других систем, которые ранее уже сформировали связи с определёнными ресурсами т.к. эти сязи также будут основаны на тех соединениях. Если вы не знаете как работает ваша выбранная система, вы можете сделать гораздо хуже, если будете „исправлять“ имеющиеся ошибки.

Программист: Я абсолютно понимаю то, что вы имеете в виду. Каждая попытка улучшить программу будет приводить к появлению новых ошибок. Вот почему новые программы часто имеют огромные участи старого кода: никто не помнит как он работает, и поэтому все боятся что-либо менять в нём.

Студент: Но что если у вас попросту нет альтернативных вариантов, потому что что-то произошло и вам это что-то нужно исправить.

Возможно нашим самым важным шагом в улучшении себя — это научиться думать о самом мышлении, то есть научиться „размышлять“ о том, что и как наш мозг думает. Однако, для этого необходимо научиться получать удовольствие от стресса, который мы испытываем в то время, когда изучаем себя. Для больших деталей смотрите §8-5 и §9-4.

Вариации Негативного Опыта


Креативность: Почему у некоторых людей появляются идеи лучше, чем у других людей? Я не говорю о «новых» идеях, потому что можно с лёгкостью построить машину, которая бы выдавала бесконечный поток новых вещей, которые никто никогда не видел; что отличает мыслителей от остальных людей – это «креативность», которая проявляется не в том, как много вещей мыслитель может придумать, но насколько эти вещи полезны для окружающего мира. Это значит, что у креативных людей должны существовать какие-то механизмы, которые должны подавлять, а может и вовсе заставлять не генерировать, вещи, которые имеют в себе слишком много новизны, заостряя внимание на вещах, которые немного отличаются от существующих и которые приносят пользу.

Юмор: Юмор также обычно воспринимается как положительная вещь, но в действительности, шутки в основном фокусируются на отрицательных вещах — в том смысле, что они почти всегда с вещами или деятельностью, которые человек не должен делать, потому что эти они запрещены, отвратительны или претворение их в жизнь крайне глупо.

Решительность: Точно так же мы склонны думать о принятии решений как о положительной вещи. Но в те моменты, когда мы делаем выбор (и который мы описываем как «акт свободной воли») наши внутренние процессы могут функционировать совершенно иначе, чем мы думаем. Момент, когда вы принимаете решение, может быть ни больше ни меньше, чем отключением сложных процессов, которые мозг использует для сравнения имеющихся альтернатив.

Удовольствие: если мы будем рассматривать ум как область различных конкурирующих ресурсов, то чем больше удовольствия мы получаем (в смысле для самого себя), тем сильнее мы можем вредить всем имеющимся психическим процессам! Ибо, в этом случае какой-то конкретный процесс захватывает полный контроль над вашим разумом, отключая все остальные процессы. Подобный захват контроля каким-то одним процессом затрудняет фокусировку на какой-то другой деятельности (подобные ощущения сможет описать каждый наркоман). Об влиянии удовольствия будет рассказано в 9 главе.

Существуют и другие способы отключения ресурсов, кроме прямых попыток их подавления. Одним из способов подавить ресурс является активирование одного из конкурентов данного ресурса. Например, вы можете «отодвинуть» время сна, если вступите с кем-либо в схватку.

Еще одним вариантом подавления определённых ресурсов является повторение определённого стимула, который активирует ресурс, пока конкретный ресурс не перестанет на него реагировать – этот способ очень похож на сказку «О пастухе и волке».

Воспитание: а теперь задумайтесь о том, что человек должен делать в процессе воспитания ребенка. Он должен его кормить, мыть, защищать, одевать, учить и помогать; в течение многих лет человек должен жертвовать богатством и вниманием. Какое побуждение может заставить человека отказаться от многих других удовольствий и целей, для того, чтобы стать настолько самоотверженным? Такие сильные ограничения, если они навязываются извне, кажутся жестокими и весьма изощрёнными пытками. Очевидно, что естественный отбор благоприятствовал тем, кто развивал способы подавления психических Критиков, ответственных за подобное поведение; ни один человек, не имеющий у себя вышеописанных критиков не сможет выносить подобные продолжительный стресс, что в итоге будет уменьшать количество потомков.

Красота: мы склонны расценивать красоту как положительную вещи. Но когда кто-то говорит, что что-то «красиво», и вы спрашиваете: «Что заставляет вас испытывать это чувство к данному объекту?», ваш собеседник может расценить подобный вопрос как нападение на его личность, или объяснить тем, что «у каждого есть свой вкус», или же может ответить вам совершенно по-детски: «Мне просто нравится эта вещь». Подобные ответы свидетельствуют (как мы видели в параграфе 1-1), что симпатия этих людей проистекает из-за подавления их внутренних Критиков. Мы все знаем, что если кто-то попытается найти какой-либо изъян в объекте – он непременно найдёт его.
Мистический опыт: Отключение внутренних Критиков выливается как в уменьшении количества окружающих вас проблем, так и в уменьшении количества целей, которые вы преследуете. И если внутренних Критиков выключается слишком много, то весь ваш окружающий мир может внезапно измениться – всё вокруг будет казаться великолепным. Если вы хотите испытать это данное ощущение мировосприятия – есть хорошо известные методики самоконтроля, которые помогут вам в этом. Данные методики помогают снять различные виды болей и стресса, а также ощущение голода и холода. Схожие эффекты могут быть вызваны различными психоактивными препаратами, и различными методами медитации. Если вы решитесь на подобный эксперимент, не забудьте проводить его в каком-нибудь тихом месте, которое сможет обеспечить вам сенсорную депривацию, которая будет способствовать отключению большинства внутренних Критиков. Затем создайте ритмический гул, в котором повторяется какая-то однообразная фраза или тон, и через некоторое этот звук потеряет всякий смысл и суть – что произойдёт практически со всеми остальными ощущениями! Если вы все сделали правильно, вы можете внезапно оказаться переполненным каким-то чрезвычайно манящим ощущением Бытия — и тогда вы можете потратить всю оставшуюся жизнь, пытаясь обрести это ощущение вновь. Я подозреваю, что данное ощущение маскирует любые упоминания и следы ваших самых ранних Импраймеров, которые скрываются в глубинах вашего разума.

Люди ввели множество разных слов для описания состояния Бытия: «Экстаз», «Упоение», «Эйфория», «Блаженство и мистический опыт». Когда вы испытываете это ощущение, вам начинает казать, что вы познали Великую Истину, что ничего другое кроме неё не имеет значения, и что вам больше не нужно никаких доказательств её существования; ваш разум гасит все попытки задать вопрос о том, что именно открылось вам в момент познания этого ощущения, а когда вы попытаетесь объяснить своим друзьям то что вы ощутили, вы обнаружите, что едва ли можете сказать что-то еще, кроме того насколько ощущения от данного состояния были «великолепными». Однако состояние, в котором вы не обнаружили никаких негативных ощущений, из-за того, что вы отключили всех своих внутренних Критиков, лучше всего описать как «безупречное».

За перевод спасибо Станиславу Суханицкому

Оглавление книги The Emotion Machine
Введение
Chapter 1. Falling in Love
Chapter 2. ATTACHMENTS AND GOALS
Chapter 3. FROM PAIN TO SUFFERING
Chapter 4. CONSCIOUSNESS
Chapter 5. LEVELS OF MENTAL ACTIVITIES
Chapter 6. COMMON SENSE
Chapter 7. Thinking.
Chapter 8. Resourcefulness.
Chapter 9. The Self.

Об авторе


image

Марвин Ли Минский (англ. Marvin Lee Minsky; 9 августа 1927 — 24 января 2016) — американский учёный в области искусственного интеллекта, сооснователь Лаборатории искусственного интеллекта в Массачусетском технологическом институте. [Википедия]

Интересные факты:

  • Минский дружил с критиком Харольдом Блумом из Йельского университета (Yale University), который отзывался о нём не иначе как «зловещий Марвин Минский».
  • Айзек Азимов описывал Минского как одного из двух людей, которые умнее, чем он сам; вторым, по его мнению, был Карл Саган.
  • Марвин — робот с искусственным интеллектом из цикла романов Дугласа Адамса Автостопом по галактике и фильма Автостопом по галактике (фильм).
  • Минский имеет контракт на заморозку своего мозга после смерти для того, чтобы его «воскресили» в будущем.
  • В честь Минского назван пес главного героя в фильме Трон: Наследие. [Википедия]



Про #philtech
image

#philtech (технологии + филантропия) — это открытые публично описанные технологии, выравнивающие уровень жизни максимально возможного количества людей за счёт создания прозрачных платформ для взаимодействия и доступа к данным и знаниям. И удовлетворяющие принципам филтеха:

1. Открытые и копируемые, а не конкурентно-проприетарные.
2. Построенные на принципах самоорганизации и горизонтального взаимодействия.
3. Устойчивые и перспективо-ориентированные, а не преследующие локальную выгоду.
4. Построенные на [открытых] данных, а не традициях и убеждениях
5. Ненасильственные и неманипуляционные.
6. Инклюзивные, и не работающие на одну группу людей за счёт других.

Акселератор социальных технологических стартапов PhilTech — программа интенсивного развития проектов ранних стадий, направленных на выравнивание доступа к информации, ресурсам и возможностям. Второй поток: март–июнь 2018.

Чат в Telegram
Сообщество людей, развивающих филтех-проекты или просто заинтересованных в теме технологий для социального сектора.

#philtech news
Телеграм-канал с новостями о проектах в идеологии #philtech и ссылками на полезные материалы.

Подписаться на еженедельную рассылку

Let's block ads! (Why?)

Органайзер-каталог для карт Heroes III и более 7700 карт в придачу


Я играю в Heroes of Might and Magic со времен царя Гороха королевы Ламанды, и за это время накопилось такое количество карт для 3-х Героев, что я решил их как-то упорядочить и структурировать.

Цели были следующие:

  1. Избавиться от дубликатов карт
  2. Точно знать, играл я в эту карту или нет, если играл, то знать статус: выиграл, проиграл или пропустил
  3. Иметь возможность загружать в органайзер новые карты с проверкой на уникальность
  4. Писать свой личный комментарий к карте
  5. Иметь возможность мгновенно отсортировать каталог карт по следующим параметрам:
    названию, статусу, версии, кодировке, размеру карты, наличию подземелья, уровню сложности и размеру файла

Скриншоты


Скриншоты кликабельные

Полный каталог карт


Работа с фильтрами. Здесь я отсортировал карты, в которые я не играл, версии SOD (The Shadow of Death), кодировка ascii, размер Extra Large, на картах есть подземелье, уровень сложности Impossible. Зеленая иконка — для загрузки карты из каталога на локальный компьютер.


Работа с картой, если нажать на иконку с ручкой. В модальном окне все очевидно и видно, когда я последний раз играл в эту карту.


Попытка загрузки карты, которая уже есть в каталоге.

Установка и запуск


  1. Скачиваем и распаковываем архив с виртуальной-машиной:
    Скачать с Dropbox 4,69GB
  2. Скачиваем и устанавливаем VMware Workstation Player (Пропускаем этот пункт, если VMware-Workstation установлен).
  3. Запускаем виртуальную машину. Дожидаемся, когда она загрузится, и смотрим IP адрес, который ей присвоил VMware:

    Если IP не назначился
    То либо перезагрузите виртуалку средствами vmware, либо авторизуйтесь
    Логин: root
    Пароль: hero

    И введите команду:
    ifconfig
    Увидите текущий IP.
    А с помощью команды:
    nmtui
    Можно его зафиксировать, чтобы он не менялся, настроив статику.


    Вбиваем этот адрес в браузер и попадем в органайзер со всеми картами, как на скриншотах.

Подробности для программистов и для интересующихся


Все это «хозяйство» написано на python3.
В качестве фреймворка выбрал Flask.
База данных: MongoDB.

Фронтенд: bootstrap3 и DataTables

Парсить карту Heroes III можно с помощь стандартных python модулей: gzip и struct. Модуль gzip распаковывает файл, так как он хранится в сжатом виде. Модуль struct предлагает функцию unpack() для работы с форматами двоичных записей переменной длины, а функция calcsize возвращает размер структуры. Код «I» для четырех байтовых без знаковых чисел. Код "<" обозначает, что числа стандартного размера и байты записаны в порядке «сначала младший» (little-endian).

Нижеследующий код получает версию Героев у файла с именем Zyconia.h3m

>>> h3m_versions = {0x0E: "RoE", 0x15: "AB", 0x1C: "SoD", 0x33: "WoG"}
>>> import gzip
>>> import struct
>>> h3m_data = gzip.open('Zyconia.h3m')
>>> def r(s):
...     return struct.unpack(s, h3m_data.read(struct.calcsize(s)))
... 
>>> print(h3m_versions[r("<I")[0]])
RoE

  • RoE — The Restoration of Erathia
  • AB — Armageddon's Blade
  • SoD — The Shadow of Death
  • WoG — In the Wake of Gods

Полный код парсера можно посмотреть на гитхабе.
Используя работу с двоичными данными я добрался до следующих данных:
  • Размер карты
  • Наличие подземелья
  • Название карты
  • Описание карты
  • Уровень сложности

Уникальный идентификатор для каждой карты (файла) посчитал обычным md5.

tmp = res['Version'] + res['underground'] + res['mapsize'] + res['name'].lower() + res['difficulty']
res['_id'] = hashlib.md5(str(tmp).encode('utf-8')).hexdigest()


Засунул в md5-хэш все данные, кроме описания, потому что в описание карты иногда добавляют название сайта, откуда ее скачали, и получится дубль, это одна из причин, почему нельзя просто взять и посчитать хэш сумму у файла с картой для проверки на уникальность.

Ссылка на гитхаб

Let's block ads! (Why?)