Аппетитный мир стандартной еды от НИСТ

Национальный институт стандартов и технологий США основали 3 марта 1901 года, сначала как «Национальное бюро стандартов», и переименовали в 1988 году. Правительство поручило этой организации гарантировать единообразие мер и весов по всем США, и помогать промышленности, учёным и всем прочим с выработкой стандартов.

Одна из основных задач НИСТ – выпуск и продажа «Справочных материалов по стандартам», СМС. В них дан широкий спектр стандартов, от проб стали и бетона до геологических материалов типа глины. Существуют стандарты и для еды. Да, в США можно купить баночку стандартного арахисового масла от НИСТ – хотя её стоимость вряд ли сможет конкурировать с тем, что предлагают в обычных магазинах. Давайте разберёмся, почему существует эта стандартная еда, и что стоит на полках национального института стандартов.

Знакомимся с мерами

НИСТ выпускает порядка 1300 различных СМС, и 45 из них относятся к еде и напиткам. Наиболее известны они своим арахисовым маслом – оно прославилось, когда фотография доктора Кэролин Бёрдетт, пробующей материал на вкус, стала вирусной.

Перечень стандартов огромен, хотя большая их часть будет невкусной – к примеру, мясной гомогенат или детская порошковая еда.

image
Различные примеры из СМС от НИСТ из раздела еды и напитков

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

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


Детская еда – относительно простой предмет для испытаний, а смесь Typical Diet будет сложнее проверить.

Технически стандартная еда от НИСТ съедобна, однако она не предназначена для употребления внутрь, а стоит на несколько порядков больше, чем вы привыкли платить за еду в магазине. Упаковка из трёх баночек стандартного арахисового масла обойдётся вам в $881 без доставки, как и пять батончиков шоколада для кондитерских изделий. Купоны на праздники НИСТ тоже не раздаёт. Если бы мы хотели организовать пикник исключительно на основе СМС, мы бы выбрали молоко и яичный порошок, а также муку, чтобы выпечь батон хлеба. Для начинки мы бы взяли устриц (халява: $672 за 25 грамм), говяжью печень или, возможно, молотый шпинат для вегетарианцев.

В целом, эти справочные материалы играют важную роль в поддержании качества еды и напитков, которые мы ежедневно употребляем, на должном уровне. А вместе с формальной бледной эстетикой этикетов эти продукты прекрасно подойдут на роль прикола для холодильника – хотя и обойдутся дороговато. Работа НИСТ облегчает жизнь производителей, и помогает выдавать качественную продукцию для потребителей. Поэтому загадочные справочные материалы по стандартам играют важную роль в индустрии еды и напитков.

ссылка на оригинал статьи https://habr.com/ru/post/540156/

Уроки компьютерного зрения на Python + OpenCV с самых азов

В этом цикле уроков я расскажу о том, как использовать библиотеку OpenCV в языке Python. Но для начала несколько слов о самом компьютерном зрении. Как компьютер вообще видит? Если подключить к нему видеокамеру, это еще не значит, что он будет видеть. Мы получим просто набор нулей и единиц. А человек видит что-то осмысленное. Как же из этих нулей и единиц извлечь что-то осмысленно? В этом и состоит задача компьютерного зрения.

Как правило, анализ изображения алгоритмами компьютерного зрения проходит следующие этапы (но некоторых этапов может и не быть):
1. Предобработка изображения. На этом этапе может происходить улучшения качества изображения, такое как увеличение контрастности, повышение резкости или наоборот, размытие изображения, чтобы удалить из него шумы и мелкие незначительные детали. Все это нужно для того, чтобы в дальнейшем было легче производить анализ изображения.
2. Промежуточная фильтрация. На этом этапе к изображению применяют различные фильтры, для того, чтобы обозначить на изображения области интереса или облегчить работу специальным алгоритмам анализа изображения.
3. Выявление специальных признаков (фич). Это может быть выделение особых точек, выделение контуров или еще каких-либо признаков.
4. Высокоуровневый анализ. На этом этапе по найденным признакам на изображения определяться конкретные объекты, и, как правило, их координаты. Так же на этом этапе может происходить сегментация либо какая-то иная высокоуровневая обработка.

Ну а теперь перейдем к делу. Мы рассмотрим работу с Python + OpenCV в среде PyCharm. Сначала нам надо установить OpenCV. Для этого идем в ImportSettings:

Далее в ProjectInterpreterи там жмем на плюсик:

Ищем там opencv и устанавливаем его:

Теперь напишем наш «Hello, World» — программу, которая отобразит картинку:

import cv2 my_photo = cv2.imread('MyPhoto.jpg') cv2.imshow('MyPhoto', my_photo) cv2.waitKey(0) cv2.destroyAllWindows()  

Вот такое вот окно откроет данная программа:

Что делает программа? Она загружает изображение из файла, отображает его и ждет нажатие клавиши ESC для завершения работы.
Давайте попробуем что-нибудь сделать с этим изображением. Например, можно изменить его размер. Допустим, мы хотим сделать изображение шириной 200. Для этого вычислим его высоту, и применим эти данные для масштабирования:

import cv2 my_photo = cv2.imread('MyPhoto.jpg') cv2.imshow('MyPhoto', my_photo)  #Подготовим новые размеры final_wide = 200 r = float(final_wide) / my_photo.shape[1] dim = (final_wide, int(my_photo.shape[0] * r))  # уменьшаем изображение до подготовленных размеров resized = cv2.resize(my_photo, dim, interpolation = cv2.INTER_AREA) cv2.imshow("Resized image", resized) cv2.waitKey(0)  cv2.destroyAllWindows()  

Вот что у нас получилось:

Часто для облегчения анализа изображения требуется сделать картинку черно-белой. Один из способов – это загрузить картинку сразу в черно-белом цветом пространстве:

import cv2 img = cv2.imread('MyPhoto.jpg', cv2.IMREAD_GRAYSCALE) cv2.imshow('MyPhoto', img) cv2.waitKey(0)  cv2.destroyAllWindows()  

Если нужно преобразовать уже загруженную картинку, то можно воспользоваться функцией cvtColor, которая преобразует изображение из одного цветового пространства в другое:

 import cv2 img = cv2.imread('MyPhoto.jpg', cv2.IMREAD_GRAYSCALE) cv2.imshow('MyPhoto', img) cv2.waitKey(0)  cv2.destroyAllWindows()  

Вот как это будет выглядеть:

Итак, подытожим.
Мы научились устанавливать OpenCV, загружать из файла и отображать изображение, а так же делать в нем простейшие преобразования, такие как масштабирования и конвертацию цветного изображения в черно белое. Если данный урок «зайдет» то будет продолжение.

ссылка на оригинал статьи https://habr.com/ru/post/540166/

Еще раз про try и Try

Исключения, проверяемые и нет

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

Например, есть функция по считыванию числа из файла (или не числа, не важно):

String readStoredData(String id) throws FileNotFoundException, IOException {     File file = new File(storage, id + ".dat");     try (BufferedReader in = new BufferedReader(new FileReader(file))) {         return in.readLine();     } }

Как видно, тут нет кода, решающего что делать в случае ошибки. Да и не ясно что делать – завершить программу, вернуть «», null или еще что-то? Поэтому исключения объявлены в throws и будут обработаны где-то на вызывающей стороне:

int initCounter(String name) throws IOException, NumberFormatException {     try {         return Integer.parseInt(readStoredData(name));     } catch (FileNotFoundException e) {         return 0;     } }

Исключения в Java делятся на проверяемые (checked) и непроверяемые (unchecked). В данном случае IOException проверяемое – вы обязаны объявить его в throws и потом где-то обработать, компилятор это проверит. NumberFormatException же непроверяемое – его обработка остается на совести программиста и компилятор вас контролировать не станет.

Есть еще третий тип исключений – фатальные ошибки (Error), но их обычно нет смысла обрабатывать, поэтому вас они не должны заботить.

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

С этим подходом есть несколько проблем:

  • функциональное программирование в лице функций высших порядков плохо совместимо с проверяемыми исключениями;

  • непроверяемые исключения обычно теряются и обрабатывать их забывают пока тесты (или того хуже – клиенты) не обнаружат ошибку.

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

А что там в Scala?

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

Возьмем к примеру Try[T] – это тип, который содержит либо значение, либо исключение. Перепишем наш код на Scala:

def readStoredData(id: String): Try[String] =   Try {     val file = new File(storage, s"$id.dat")     val source = Source.fromFile(file)     try source.getLines().next()     finally source.close()   }  def initCounter(name: String): Try[Int] = {   readStoredData(name)     .map(_.toInt)     .recover {       case _: FileNotFoundException => 0     } }

Выглядит вполне похоже, разница в том, что тип результата функции readStoredData уже не String, а Try[String] – работая с функцией вы не забудете о возможных исключениях. В этом смысле Try похож на проверяемые исключения в Java – компилятор напомнит вам об исключении, но без проблем с лямбдами.

С другой стороны недостатки тоже есть:

  • вы не знаете какие конкретно виды исключений там могут быть (тут можно использовать Either[Error, T], но это тоже не очень удобно);

  • в целом happy-path требует больше синтаксических ритуалов, чем исключения (Try/get или for/map/flatMap);

  • люди из Java мира часто по-ошибке просто игнорируют результат вызова метода, неявно игнорируя исключения (люди из Java мира потому что такое случается в императивном коде, функциональный таким обычно не грешит).

В целом такой подход хорошо расширяется на другие эффекты (в данном случае Try[String] означает строку с эффектом – возможностью содержать ошибку вместо значения). Примерами могут быть Option[T] – потенциальное отсутствие значения, Future[T] – асинхронное вычисление значения и т.п.

Исключения и ошибки

Возвращаясь к исходной проблеме стоит заметить, что если исключения можно избежать – это стоит сделать. Собственно именно исходя из этой логики были введены проверяемые/непроверяемые типы исключений в Java, когда непроверяемые исключения говорят об ошибке в коде (а не например в файловой системе).

Поэтому в изначальной реализации функции у нас было два скрытых случая ошибки:

  1. FileNotFoundException если файла нет, что вероятно логическая ошибка или ожидаемое поведение

  2. Другие IOException если файл прочитать не удалось – настоящие ошибки среды

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

def readStoredData(id: String): Option[Try[String]] = {   val file = new File(storage, s"$id.dat")   if (file.exists()) Some(     Try {       val source = Source.fromFile(file)       try source.getLines().next()       finally source.close()     }   )   else None }

Тип результата Option[Try[String]] может выглядеть непривычно, но теперь он явно говорит, что результатом могут быть три отдельных случая:

  1. None – нет файла

  2. Some(Success(string)) – собственно строка из файла

  3. Some(Failure(exception)) – ошибка считывания файла, в случае если он существует

Теперь Try содержит только настоящие ошибки среды. В Java в таких случаях часто используются специальные значения, например null. Но если это поведение не выражено в типе его легко пропустить.

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

ссылка на оригинал статьи https://habr.com/ru/post/540172/

Эффект дождя. Частицы в Unity 3D

Учебные материалы для школы программирования. Часть 9

Spoiler

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

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

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

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

Перейдем от слов к делу.

Порядок выполнения

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

В проект импортируем приложенный ассет. Ассет содержит текстуры, звук и стандартный контроллер игрока.
Создадим простую карту, поставим игрока, и внутри него на высоте около 30 метров создадим три системы частиц.

Назовём их Rain, Dust и Storm.

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

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

Также, стоит поставить галочку ColorOverLifetime и выставить ему такое значение, чтобы дождь плавно исчезал и появлялся.

Создадим и настроим материал в соответствии со скриншотом.

Теперь, настроим рендер. Первое, что нужно сделать — это закинуть материал и выставить ему режим отрисовки, как Stratched Billboard с растяжкой по длине 2. Обязательно нужно поменять MaxParticleSize, иначе частицы не будут апскейлиться и будет эффект, как будто они нас облетают.

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

На этот раз, настроек чуть меньше.

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

Самое интересное начинается в материале. Он отличается от остальных, т. к. будет анимирован.

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

Перейдем к грозовым облакам.

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

Первое, что сделаем, это закинем эммитеры тумана и дождя в эммитер облаков. Для корректной работы выставим режим изменения размера на иерархический, в данном занятии нам это не понадобится, но в случае изменения размеров данная галочка должна быть установлена.
Заметьте, что мы используем Bursts для создания двойных облаков.

Также, нельзя обойти стороной и Color Over Lifetime. Он придаст нашим облакам эффект грозовых.

Далее, всё по стандарту — создаём материал и настраиваем. Использована стандартная текстура дыма из Unity, она отлично пойдёт в качестве облаков.

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

Дождь готов!

ссылка на оригинал статьи https://habr.com/ru/post/540146/

Немного о нашей безымянной студии и о том, что мы делаем

Привет всем! Пока мы, на самом деле, безымянная студия и проект, который делаем имеет техническое название «CGDrone». Сегодня мне захотелось написать эту короткую статью. Понимаете, замучился от всей этой работы со скетчами, цветами, алгоритмами и исправлением багов во вращениях, которые реализованы с помощью кватернионов. Последние, к слову, меня порядочно побили.

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

Начало

Дело было в институте, конец магистратуры. Мы с моим товарищем из параллельной группы решаем создать свою супер-пупер 3D-головоломку от первого лица. Такую что просто… ну просто бомба и огонь. Надеюсь вы поняли меня. Почему решили делать игру? У меня есть опыт в рисовании, окончил школу с ИЗО уклоном, у напарника есть опыт в создании 3D-моделей. Ну и оба мы программисты как-никак, поработали в фирмах в т.ч. игры делали. Почему головоломку? Потому, что нам нравятся головоломки.

Работу мы разделили примерно пополам ну или делали кто что может. С одной стороны, если в команде 2 человека, то делить работу можно пополам! Удобно же 🙂 И на контроль за результатами работы всех разработчиков не так много времени уходит как, скажем, ушло бы если бы в нашей команде было 30 человек.

В итоге все встало на свои места: я стал отвечать за весь код и графон (шейдеры, освещение, пост-эффекты), а напарник за уровни и юридические вопросы. Вначале, кстати, он моделил лучше меня. Но со временем стало понятно, что просить его переделывать что-то каждый раз — это дергать человека и отрывать от основной работы. К тому же требования к моделям могут меняться и человек сойдет с ума от 1000 правок в день. Так что позже модели перешли под мою ответственность.

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

Не поверите, но у меня набралось 2 папки со скетчами по проекту и иногда мне все еще приходится набрасывать картинки/перебирать идеи/сопоставлять обьекты. Скетчи, которые я набрасывал выглядят примерно так:

На данный момент мы опубликовали большую часть скетчей в твиттере / инстаграме.

Были еще знакомые, мы попытались подключить их к проекту. Кроме нас в команде было две девушки (дизайнеры-художники), гейм-дизайнер и программист. Через 2-3 недели программист пошел работать в крупную фирму, после попыток прописать структуру проекта и сборки одного игрового обьекта — условный обьект с «глазом» (который следит за игроком). Потом нас покинули дизайнеры. Гейм-дизайнер оказался крепким. Он был с нами до последнего, а вот результатов его работы у нас не было 🙂

Кстати, игра на тот момент выглядела так:

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

Еще несколько скетчей:

Коротко о не коротком, о сети

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

Мы много чего сделали по сетевой части, в том числе и организовали сетевой чат (иконка конверта) и систему общения голосом (иконка микрофона).

Позже я смекнул и понял одну важную вещь: сеть это круто, но если у вас в команде 2 человека и нет опыта в сетевой части(хотя лабы по сетям в институте мы сдавали только так), то лучше повременить с сетью, сделать основное:

Лучше синица в руках, чем журавль в небе

Попытки расширить команду

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

  • профессиональные художники хотят получать денюжки, а у нас есть только энтузиазм

  • по большей части непрофессилнальные художники (из моего личного опыта) любят покреативить и вместо того чтобы сделать все максимально по ТЗ, их заносит и они по-полной берут на себя роль главного ГД и начинают работать уже в этом направлении

  • мотивировать команду — это работа на которую затрачивается много времени

Для себя мы поняли еще одну вещь: у нас нет времени на выдачу ТЗ ребятам, которые работают на энтузиазме потому, что высока вероятность их ухода. Так что мы решили делать все сами и никого не искать, не расширять команду. У этого решения есть плюс, который я описал бы так:

Надеемся только на крепость рук, На руки друга и вбитый крюк — И молимся, чтобы страховка не подвела.

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

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

А вот и ссылки на нас:

https://twitter.com/CGAleksey

https://www.instagram.com/cgaleksey

https://vk.com/treload

ссылка на оригинал статьи https://habr.com/ru/post/540174/