Всем доброго дня. Совсем недавно я закончила продвинутый курс от Deep Learning School. Курс объемный, много свежей информации. Мне, как закончившей прикладную математику (нейросетки, генетика, fuzzy logics) было не сильно сложно, но мега-увлекательно за счет того, что ребята очень заинтересованные и рассказывали про свежие интересные модели, еще и на русском языке. Приятно видеть, что ИИ-сфера в нашей стране тоже не стоит на месте.

В качестве проекта на курсе мне хотелось сделать своими руками что-нибудь эдакое и полезное, и одновременно мега-современное, и вот что я придумала. У нас есть частный дом, там есть дворовые коты, которых надо кормить и в мое отсутствие. А так же есть еще птицы, кроты, чужие вездесущие собаки и другая живность, которых не стоит кормить, если не хотим, чтобы они у нас все поселились. Так вот, а что, если прикрутить модель детекции изображений к умной кормушке? Далее было бы здорово научиться использовать голосовые команды, например, на закрытие кормушки. Интересненько :-)

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

Логика предполагалась следующая:

  1. Кот приходит к кормушке, его снимает рядом стоящая IP камера и отправляет снимок в телеграмм-бот.

  2. Бот обрабатывает сообщение, если это фото, то отправляет его в модель детекции.

  3. Модель детекции распознает кота или не распознает.

  4. Если это кот, бот посылает сигнал умной кормушке открыться и уведомляет об этом владельца.

  5. Умная кормушка открывается (и потом закрывается по таймеру, например).

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

  7. Команда из голосовой преобразуется в текст.

  8. Если будет обнаружена команда "закрыть кормушку", бот отправит сигнал на закрытие кормушки и сообщение владельцу, что принял команду.

Моделей детекции в современном мире тьма, в целях данной работы я выбрала Single Shot MultiBox Detector model for object detection (далее SSD-модель), разработанную компанией NVIDIA. Эта модель архитектуры глубоких нейронных сетей на основе модели ResNet-50 (победившей на соревнованиях ImageNet 2015 года), специально заточенная под детекцию изображений. Статья у нас практическая, не будем вдаваться в сложности архитектуры, только подчеркнем, что данная архитектура нейронных сетей стала практически стандартом качества и часто используется в задачах обнаружения объектов и сегментации изображений. SSD-модель заточена под быструю обработку и детекцию объектов даже в реальном времени, она обеспечивает высокую точность и быстроту обработки изображений.

1 часть: подготовка к работе

Писать будем на Python 3.11

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

pip install numpy scipy scikit-image matplotlib
pip install pyTelegramBotAPI
pip install SpeechRecognition
pip install pydub
pip install pydub[ffmpeg]
pip install pydub opencv-python-headless opuslib

Скачиваем и настраиваем выбранную модель: переводим ее в "боевой" режим. Расчеты следует производить на GPU или на CUDA, т.к. CPU будет считать очень долго и медленно. Я делала проект в Google Colab GPU

import torch
precision = 'fp32'
ssd_model = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 
                           'nvidia_ssd', model_math=precision)
utils = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 
                       'nvidia_ssd_processing_utils')
ssd_model.to('cuda')
ssd_model.eval()

Монтируем google disk, на котором будем хранить все материалы

from google.colab import drive
drive.mount('/content/drive')
torch.save(ssd_model.state_dict(), 'ssd_model.pth')

Подгружаем необходимые библиотеки

import telebot
import cv2
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

from matplotlib import pyplot as plt
import matplotlib.patches as patches

import os
import speech_recognition as sr

from pydub import AudioSegment
import subprocess

import time

2 часть: реализация логики

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

def filter(uri):
    # Инициализация флага обнаружения объекта "cat"
    is_detected_cat = 0
    
    # Подготовка входных данных
    inputs = [utils.prepare_input(uri)]
    
    # Подготовка тензора для модели с учетом точности 'fp16'
    tensor = utils.prepare_tensor(inputs, precision == 'fp16')

    # Выполнение обнаружения объектов с использованием SSD-модели
    with torch.no_grad():
        detections_batch = ssd_model(tensor)

    # Декодирование результатов обнаружения
    results_per_input = utils.decode_results(detections_batch)
    
    # Выбор лучших результатов с пороговой уверенностью 0.20
    best_results_per_input = [utils.pick_best(results, 0.20) for results in results_per_input]
    
    # Получение словаря с соответствием классов меткам
    classes_to_labels = utils.get_coco_object_dictionary()

    # Итерация по изображениям с лучшими результатами
    for image_idx in range(len(best_results_per_input)):
        # Создание графика для визуализации результатов
        fig, ax = plt.subplots(1)
        
        # Отображение оригинального изображения
        image = inputs[image_idx] / 2 + 0.5
        ax.imshow(image)
        
        # Отображение прямоугольников вокруг обнаруженных объектов и меток с уверенностью
        bboxes, classes, confidences = best_results_per_input[image_idx]
        for idx in range(len(bboxes)):
            left, bot, right, top = bboxes[idx]
            x, y, w, h = [val * 300 for val in [left, bot, right - left, top - bot]]
            rect = patches.Rectangle((x, y), w, h, linewidth=1, edgecolor='r', facecolor='none')
            ax.add_patch(rect)
            
            # Проверка, является ли обнаруженный объект классом "cat"
            class_cat = classes_to_labels[classes[idx] - 1]
            if (class_cat == 'cat'):
                is_detected_cat = 1
            
            # Вывод метки класса и уверенности
            ax.text(x, y, "{} {:.0f}%".format(classes_to_labels[classes[idx] - 1], confidences[idx]*100), bbox=dict(facecolor='white', alpha=0.5))
   
    # Сохранение отфильтрованного изображения
    plt.savefig("filtered_image.jpg")
    
    # Возвращение флага обнаружения объекта "cat"
    return is_detected_cat

3 часть: создание телеграмм-бота

Бота создаем через @BotFather, в целях безопасности отдельно в файле сохраняем токен.

Пишем логику бота, наш бот будет уметь:

Здороваться

@bot.message_handler(commands=['start'])
def start_message(message):
    bot.send_message(message.chat.id, 'Привет! Пожалуйста, загрузи картинку.')

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

Посылать сигнал на открытие кормушки при обнаружении кота

@bot.message_handler(content_types=['photo'])
def process_image(message):

    # Получаем информацию о картинке
    file_id = message.photo[-1].file_id
    file_info = bot.get_file(file_id)
    file_path = file_info.file_path

    # Скачиваем картинку
    downloaded_file = bot.download_file(file_path)

    # Сохраняем картинку на диск
    with open('image.jpg', 'wb') as f:
        f.write(downloaded_file)

    is_detected_cat = filter("image.jpg")

    # Отправляем пользователю преобразованное изображение
    with open('filtered_image.jpg', 'rb') as f:
        bot.send_photo(message.chat.id, f)

    if is_detected_cat == True:
        bot.reply_to(message, "Пришел твой кот. Кормушка открыта")

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

@bot.message_handler(content_types=['voice'])
def process_voice_message(message):
    # Получаем информацию о голосовом сообщении
    file_id = message.voice.file_id
    file_info = bot.get_file(file_id)
    file_path = file_info.file_path

    # Скачиваем голосовое сообщение
    downloaded_file = bot.download_file(file_path)

    # Сохраняем голосовое сообщение на диск
    voice_file_path = 'voice_message.ogg'
    with open(voice_file_path, 'wb') as f:
        f.write(downloaded_file)

    # Преобразование голосового сообщения в текст
    input_file = 'voice_message.ogg'
    audio = AudioSegment.from_file(input_file, format='ogg')

    # Конвертация в формат WAV
    output_file = 'audio.wav'
    audio.export(output_file, format='wav')


    recognizer = sr.Recognizer()
    with sr.AudioFile(output_file) as source:
        audio_data = recognizer.record(source)
        text = recognizer.recognize_google(audio_data, language='ru')  # Здесь можно указать другой язык, если нужно
        print(text)

    # Отправляем текст пользователю
    bot.reply_to(message, text)

    # Удаляем временные файлы
    os.remove(output_file)

4 часть: запуск!

Напрямую в Google Colab можно запустить бота вот так:

bot.polling()

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

Вот такое интересное и практичное применение возможно моделям искусственного интеллекта, а не вот эти вот все скучные захваты мира))))

А что же дальше...

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

Источники

SSD: Single Shot MultiBox Detector

Школа глубокого обучения ФПМИ МФТИ

Прыжок до небес: запускаем телеграм бота на Python в serverless облаке

Мой исходник на Github

Послесловие

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