Как стать автором
Обновить
84.74
Рейтинг
Friflex
Мобильные приложения для миллионов пользователей🚀

Как следить за здоровьем морских свинок с помощью Machine Learning и мобильного приложения

Блог компании Friflex Python *Java *Разработка мобильных приложений *Машинное обучение *
Tutorial

Всем привет! Меня зовут Андрей Нестеров, я занимаюсь компьютерным зрением в  применении к мобильным приложениям (ML на конечных устройствах) в компании Friflex и работаю продуктами по оцифровке спорта. Я стал замечать, что в обычной жизни не хватает технологий компьютерного зрения. Например, мне бы хотелось замерять, сколько времени я провожу за компьютером или трачу на сон. Но отслеживать эти действия можно и самостоятельно. С тех пор я начал думать о том, что действительно будет полезным, какую проблему можно было бы успешно решить с помощью технологий. Такая проблема вскоре нашлась.

Проблема: поддержать здоровье морской свинки 

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

Решение: мобильное приложение на ОС Android для мониторинга состояния морской свинки

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

Hidden text
Крепление с телефоном
Крепление с телефоном
Свинка
Свинка

Сбор данных

Напишем простое приложение на Android для сбора данных, которые мы будем использовать для тестирования качества.

Заранее настроим s3 сервер на амазоне. S3Simple Storage Service позволяет бесплатно (в ограниченном объёме бесплатно, далее за деньги) хранить данные в облаке, а также иметь возможность обращаться к хранилищу через терминал, исполняемые скрипты, а также мобильные приложения. Инструкцию по настройке s3 можно найти тут.

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

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

В Android Studio создадим новый проект с Empty Activity. Это позволит нам сразу сосредоточиться на коде сбора данных. На данном этапе мы имеем MainActivity.java. Зададим в ней поле класса, отвечающее за генерацию рандомного названия файла:

private static String imageName = new String();

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

Amplify.addPlugin(new AWSCognitoAuthPlugin());
Amplify.addPlugin(new AWSS3StoragePlugin());
Amplify.configure(getApplicationContext());

Также для того чтобы андроид студия знала, откуда вызывать библиотеку Amplify, добавим зависимость в build.gradle (Module).

implementation 'com.amplifyframework:aws-storage-s3:1.30.0'
implementation 'com.amplifyframework:aws-auth-cognito:1.30.0'

Определим метод, загружающий файл из памяти устройства на сервер.

// Uploads file to s3
private void uploadFile(File file) {
   Uri uri = Uri.fromFile(file);
  try {
       InputStream inputStream  = getContentResolver().openInputStream(uri);
   Amplify.Storage.uploadInputStream(imageName + “.png”, inputStream,
           result -> { Log.i(“LOAD_TAG”, “Successfully uploaded: ” + imageName); },
           error -> { Log.e(“LOAD_TAG”, “Upload failed”, error); }
   } catch (FileNotFoundException e) {
       Log.i(“SAVE_TAG”, e.getMessage());
   }
   );
}

Теперь мы можем написать финальный метод сохранения и загрузки файлов на сервер, который будет вызываться каждый n-й кадр.

private void saveImage(Bitmap image) {
   imageName = UUID.randomUUID().toString();
   String savePath =  imageName + ".png";
   File file = new File(getExternalFilesDir("/").getAbsolutePath(), savePath);
   try {
       FileOutputStream out = new FileOutputStream(file);
       image.compress(Bitmap.CompressFormat.PNG, 100, out);
       out.close();
       uploadFile(file);
   } catch (IOException e) {
       Log.i("WRITE_TAG", e.getMessage());
   }
}

Для работы с камерой официальная документация Android предлагает три библиотеки: Camera, Camera2, CameraX. Camera –  уже устаревшая и deprecated, Camera2 – довольно сложный вариант для тех, кто только начинает работать с камерой Android, а вот CameraX – идеальное решение. В ней можно настроить стандартный image capture буквально в несколько строк. Лучший способ разобраться с тем, как настроить камеру – официальная документация Android.

Обучение

В качестве эксперимента в данной задаче будем обучать нейросеть только на синтетических данных, а тестировать уже на реальных. Для генерации данных возьмем Blender – движок для работы с 3D объектами. Полноценный обзор о том, как работать с Blender в задачах компьютерного зрения можно посмотреть в статьях моего коллеги Глеба.

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

Это гифка. Чтобы посмотреть, нажмите на изображение
Это гифка. Чтобы посмотреть, нажмите на изображение

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

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

Создадим кастомную архитектуру, в которой на входе принимается изображение (128, 128, 3), а на выходе – четыре числа, [x1, y1, x2, y2] – координаты точек головы и центра свинки.

from tensorflow.keras.layers import Conv2D, MaxPooling2D, BatchNormalization, Flatten, Dense, Input
from tensorflow.keras import Model

def build_model():

    inputs = Input((128, 128, 3))

    x = Conv2D(16, (3,3), padding='same', activation='relu')(inputs)
    x = MaxPooling2D((2,2))(x)
    x = BatchNormalization()(x)

    x = Conv2D(16, (3,3), padding='same', activation='relu')(x)
    x = MaxPooling2D((2,2))(x)
    x = BatchNormalization()(x)

    x = Conv2D(32, (3,3), padding='same', activation='relu')(x)
    x = MaxPooling2D((2,2))(x)
    x = BatchNormalization()(x)

    x = Conv2D(32, (3,3), padding='same', activation='relu')(x)
    x = MaxPooling2D((2,2))(x)
    x = BatchNormalization()(x)

    x = Conv2D(64, (3,3), padding='same', activation='relu')(x)
    x = MaxPooling2D((2,2))(x)
    x = BatchNormalization()(x)

    x = Conv2D(64, (3,3), padding='same', activation='relu')(x)
    x = BatchNormalization()(x)
    x = Flatten()(x)

    x = Dense(128, activation=None)(x)
    outputs = Dense(4, activation=None)(x)

    model = Model(inputs, outputs)

    return model

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

Воспользуемся библиотекой Albumentations, для того чтобы во время обучения применять преобразования ColorJitter, HueSaturationValue, RGBShift, RandomGamma, RandomBrightnessContrast и прочие. Наглядные примеры того, какие аугментации существуют и как они выглядят, можно найти на сайте этой библиотеки в разделе Demo.

Так одна картинка из реальных данных может выглядеть каждый раз по-новому в зависимости от применяемых аугментаций.

Разделим обучающие данные на train и validation, включим callback, который остановит обучение, если функция потерь на валидационной выборке val_loss будет стагнировать долгое время, и запустим обучение.

На выходе получаем .hdf5 файл с моделью обученной нейросети.

Внедрение и проверка

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

Для внедрения снова создадим проект в Android Studio.

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

Для начала сконвертируем модель в формат TFlite:

converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

За запуск предсказаний на входящих изображениях будет отвечать интерпретатор TFLite - interpreter. Инициализируем его в поле класса MainActivity нашего приложения:

private static Interpreter interpreter;

Далее создаем папку assets (на папке app - Folder - Assets Folder).
В этой папке складываем нашу .tflite модель.
Воспользуемся методами, описанными в официальной документации
В onCreate главной активити загружаем модель методом init

init(getAssets(), "model.tflite");

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

Если у вас остались вопросы по интеграции компьютерного зрения в мобильное приложение, пишите в комментариях! 
Если вы хотите присоединиться к нашей ML-команде, присылайте ваше резюме на hr@friflex.com 

Теги:
Хабы:
Всего голосов 2: ↑2 и ↓0 +2
Просмотры 1.4K
Комментарии Комментарии 4

Информация

Дата основания
Местоположение
Россия
Сайт
friflex.com
Численность
101–200 человек
Дата регистрации
Представитель
Friflex_dev