Русский
Русский
English
Статистика
Реклама

Face detection

RPi-няня

28.08.2020 10:04:17 | Автор: admin
Периодически меня подмывает сделать что-то странное. Очевидно бесполезную вещь, которая не оправдывает себя по объему вложенных средств, и через полгода после создания пылиться на полке. Но зато полностью оправдывает себя по количеству эмоций, полученному опыту и новым рассказам. На Хабре даже есть две моих статьи про такие эксперименты: Алкоорган и умная кормушка для птиц.
Что ж. Пришло время рассказать о новом эксперименте. Как собрал, что из этого вышло и как повторить.

К новому проекту меня подтолкнуло событие, в каком-то смысле, банальное родился сын. Я заранее устроил себе отпуск на месяц. Но ребёнок оказался тихим было свободное время. И спящий рядом деть.
Дома много разных embedded-железок для computer vision. В итоге решил сделать видео-няню. Но не такую унылую, которыми завалены все магазины. А что-то поумнее и поинтереснее.


Статья будет написана в повествовательном ключе, чтобы понять как шла разработка игрушки, куда она пришла и куда движется дальше.
У статьи есть несколько дополнений:
1) Видео где я показываю и рассказываю как всё работает.
2) Небольшая статья на VC где я рассказываю почему такие штуки скорее всего не придут в нормальный продакшн, и про ограничения ML систем такого плана.
3) Сорсы всего на гитхабе + готовый образ для RPi. В конце статьи описание как пользоваться.

Выбор идеи


Самый банальный функционал видеоняни посмотреть в любой момент что происходит с ребёнком. Но, к сожалению, это не всегда работает. Вы не будете смотреть трансляцию всё время, это не удобно. Младенца можно вообще положить спать рядом в коконе, зачем видео всё время? В итоге, для начала собралась следующая подборка:
  1. Система должна давать возможность посмотреть видео или фото в любой момент с телефона
  2. Система должна реагировать на просыпание ребёнка, и извещать об этом
  3. Система должна детектировать пропадание лица, для предотвращения СВДС


Выбор платформы


У меня была большая статья на Хабре про сравнение различных платформ. Глобально, для прототипа типа того что я делаю есть несколько вариантов:
  1. Jetson Nano. Есть на руках + я много с ним работал (не только с Nano), но меня немного смущает, что он более производственный. Из коробки запустятся только самые простые модели. Чтобы ускорить и оптимизировать память надо переносить на TensorRT. Это требует времени. Но все обученные сети надо искать, тестировать, не факт что запуститься их коробки, не факт что из коробки TensorRT пойдёт.
  2. VIM3. Никогда не использовал, попробовать было бы интересно. Но ради проекта для которого есть уже пять разных вариантов исполнения не хотелось тратить денег.
  3. Raspberry PI + Movidius. Есть большая пачка предобученных сетей. Производительности хватит, удобно работает, более менее стабилен.
    1. Абсолютно непроизводственное решение, но для игрушки сойдёт.
    2. Нереально переобучить выложенные сети. Не все сети обученные с нуля будут работать.
  4. Raspberry PI 4 при работе через OpenCV будет хорошо утилизировать открытые сети, чего должно хватить. Но, было подозрение, что не хватит производительности.
  5. Coral у меня он есть на руках, по производительности прошло бы, но в другой моей статье написано почему я его не люблю:)

Итого я выбрал Rpi+movidius. Есть на руках, умею работать с ним.

Железо


Вычислитель Raspberry Pi 3B, нейропроцессор Movidius Myriad X. С этим понятно.
Остальное поскрёб по сусекам, докупил.



Камера


Я проверил три разных, которые у меня были:
  • Камера от RaspberryPI. Шумная, неудобный кабель, нет удобного крепления. Забил.
  • Какая-то IP камера. Очень удобно потому что не надо включать в RPI. Камера разнесена с вычислителем. Моя камера имела даже два режима, дневной и ночной. Но та что была у меня не давала достаточного качества лица.
  • Веб камера от Genius. Я её использовал уже лет 5. Но что-то в последнее время стала работать нестабильно.А для RPI в самый раз. Более того, оказалось что её можно тривиально разобрать и достать оттуда IR фильтр. Плюс, как потом выяснилось, что это был единственный вариант с микрофоном.


А фильтр меняется вот так:



В целом, понятно, что это не продуктовое решение. Но работает.
Если что, то в коде увидите оставшиеся куски для перехода на другие два типа камер. Возможно даже что-то сходу заработает, если 1-2 параметра поменять.

Освещение


С одной из старых задачек у меня завалялся осветитель:
Подпаял к нему какой-то блок питания. Светит неплохо.

Направляю на потолок комната освещена.


Экран


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


Питание


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


OpenVino


Пройдём немного по OpenVino. Как я сказал выше большим преимуществом OpenVino является большой объём предобученных сетей. Что нам может пригодиться:
Детекция лица. Таких сетей в OpenVino много:
  1. 1
  2. 2
  3. 3

Распознавание ключевых точек на лице. Это нужно нам чтобы запускать следующие сети
Распознавание ориентации лица. Активность ребёнка и куда смотрит.
Распознавание направление взгляда если пробовать взаимодействовать
Анализ глубины? Может быть получится
Анализ скелета
Ну и много других интересных
Основным минусом этих сетей будет их основное преимущество их предобученность
Это можно поправить, но сейчас мы делаем быстрый прототип, наша цель не работа в 100% случаев, а принципиальная работа которая будет приносить хоть какую-то пользу.

Поехали. Общая логика версии 1


Так как мы разрабатываем embedded устройство, то нам надо с ним как-то взаимодействовать. Получать фото/сигналы о тревоге. Так что решил сделать так же как когда делал кормушку, через телеграмм. Но довести до ума.
Для первой версии я решил:
  • Запустить на RPi обозначенные сети (хотелось бы сразу все, вдруг производительность позволит). Это позволит посмотреть больше вариантов решения задачи/вероятных путей развития
  • Написать общий шаблон программы.
  • Придумать алгоритм распознающий просыпание.
  • Сделать алгоритм присылающий уведомление при потере лица

Пошло всё более-менее неплохо, не считая кучи багов всего вокруг. Это свойственно для ComputerVision Я привык к этому:)
Вот краткая сводка того на что я натолкнулся:
  1. OpenVino под RPi не запускается в последней версии (на май 2020) из-за того что не срабатывает from openvino.inference_engine import IECore. Есть способы иначе использовать OpenVino (через OpenCV например), но там логика программы изменится, не хотелось.
  2. OpenVino старой версии не работает на новых сконверченных нейронных сетях, надо конвертить с -generate_deprecated_IR_V7 или брать старые
  3. OpenVino в последней версии (опять же, на май) с Movidius под виндой не может инферить сетки с int 8 из официального репозитория. В int32 может. Под RPi в int8 может. Ничего критичного, но сложнее дебажить.
  4. OpenVino не устанавливается под виндой в нестандартную папку. Точнее ставится, но все дальнейшие проблемы не решились и OpenVino не запустился. Про это много ругани, но судя по тому что у меня то же самое произошло так и не починили.
  5. OpenVino не работает на старых но мощных процах Intel (не везде дебажить удобно, но не критично).
  6. PyTorch в версии 1.5 не смог сконвертировать сети в onnx, пришлось конвертировать из 1.4


Но, тут как Уверен что если бы пошёл через TensorRT, то там бы проблем было как всегда больше.

Итак. Всё сбилжено, сети запущены, получаем что-то такое (запустив стек по голове, ориентации, ключевым точкам):

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

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

Остальные графики грусть/злость/радость/удивление. Даже не особо суть того что где по цветам. К сожалению, данные сети нестабильны, что мы и видим. Нестабильность возникает когда:
  • Лишняя тень на лице (что ночью не редкость)
  • Лиц ребёнка не было в обучающей выборке OpenVino => произвольные переключения на другие эмоции
  • Ребёнок реально корчит рожи, в том числе во сне

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

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

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

(здесь где-то час времени сна)
Значит надо всё же делать нормальное распознавание

Возникшие проблемы версии 1


Просуммируем всё что мне не понравилось в первой версии.
  1. Автозапуск. Не удобно запускать эту игрушку каждый раз заново, подключаться по SSH, запускать скрипт наблюдения. При этом скрипт должен:
    • Проверять состояние камеры. Бывает что камера выключена/не воткнута. Система должна ждать пока пользователь включит камеру.
    • Проверка состояния ускорителя. То же самое что с камерой.
    • Проверка сети. Штуку я хочу использовать и дома и на даче. А может где-то ещё. И опять же, заходить по ssh не хочу => надо сделать алгоритм подключения к wiFi если инета нет.
  2. Просыпание, обучение сети. Простые подходы не зашли, значит надо обучать нейронку на распознавание открытых глаз.

Автозапуск


В целом, схема автозапуска получилась следующей:
  • Запускаю на старте программу свою. Как я это делаю написал отдельную статью, не сказать что это на RPi сделать тривиально. Если кратко:
    • Создаю сервис который инициализирует OpenVino окружение
    • Сервис на старте запускает сначала скрипт проверки окружения, а потом основной рабочий скрипт
  • Проверяю наличие камеры
  • Проверяю наличие Movidius-модуля
  • Проверяю наличие интернета
    • Если нет запускаю камеру и жду QR-кода локальной wifi сети
  • Проверяю наличие информации о канале telegram через который будет управление. Если нет жду QR-код с данными на управление


Обучение сети для распознавания глаз


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

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

Остаётся его разметить и обучить. Более подробно процесс разметки я описал тут (и видео процесса на 10 минут тут). Для разметки использовалась Толока. На настройку задания ушло~2 часа, на разметку 5 минут + 300 рублей бюджета.
При обучении думать особо не хотелось, так что взял заведомо быструю сеть, которая имеет достаточное качество для разрешения задачи mobilenetv2. Весь код, включая загрузку датасета, инициализацию и сохранение занял меньше 100 строк (большей частью взятых из открытых источников, переписал пару десятков строк):
Скрытый текст
import numpy as npimport torchfrom torch import nnfrom torch import optimfrom torchvision import datasets, transforms, modelsdata_dir = 'F:/Senya/Dataset'def load_split_train_test(datadir, valid_size = .1):    train_transforms = transforms.Compose([transforms.Resize(64),                                           transforms.RandomHorizontalFlip(),                                           transforms.ToTensor(),                                       ])    test_transforms = transforms.Compose([transforms.Resize(64),                                      transforms.ToTensor(),                                      ])    train_data = datasets.ImageFolder(datadir,                    transform=train_transforms)    test_data = datasets.ImageFolder(datadir,                    transform=test_transforms)    num_train = len(train_data)    indices = list(range(num_train))    split = int(np.floor(valid_size * num_train))    np.random.shuffle(indices)    from torch.utils.data.sampler import SubsetRandomSampler    train_idx, test_idx = indices[split:], indices[:split]    train_sampler = SubsetRandomSampler(train_idx)    test_sampler = SubsetRandomSampler(test_idx)    trainloader = torch.utils.data.DataLoader(train_data,                   sampler=train_sampler, batch_size=64)    testloader = torch.utils.data.DataLoader(test_data,                   sampler=test_sampler, batch_size=64)    return trainloader, testloadertrainloader, testloader = load_split_train_test(data_dir, .1)print(trainloader.dataset.classes)device = torch.device("cuda" if torch.cuda.is_available()                                  else "cpu")model = models.mobilenet_v2(pretrained=True)model.classifier = nn.Sequential(nn.Linear(1280, 3),                                 nn.LogSoftmax(dim=1))print(model)criterion = nn.NLLLoss()optimizer = optim.Adam(model.parameters(), lr=0.003)model.to(device)epochs = 5steps = 0running_loss = 0print_every = 10train_losses, test_losses = [], []for epoch in range(epochs):    for inputs, labels in trainloader:        steps += 1        inputs, labels = inputs.to(device), labels.to(device)        optimizer.zero_grad()        logps = model.forward(inputs)        loss = criterion(logps, labels)        loss.backward()        optimizer.step()        running_loss += loss.item()        if steps % print_every == 0:            test_loss = 0            accuracy = 0            model.eval()            with torch.no_grad():                for inputs, labels in testloader:                    inputs, labels = inputs.to(device), labels.to(device)                    logps = model.forward(inputs)                    batch_loss = criterion(logps, labels)                    test_loss += batch_loss.item()                    ps = torch.exp(logps)                    top_p, top_class = ps.topk(1, dim=1)                    equals = top_class == labels.view(*top_class.shape)                    accuracy += torch.mean(equals.type(torch.FloatTensor)).item()            train_losses.append(running_loss / len(trainloader))            test_losses.append(test_loss / len(testloader))            print(f"Epoch {epoch + 1}/{epochs}.. "                  f"Train loss: {running_loss / print_every:.3f}.. "                  f"Test loss: {test_loss / len(testloader):.3f}.. "                  f"Test accuracy: {accuracy / len(testloader):.3f}")            running_loss = 0            model.train()torch.save(model, 'EyeDetector.pth')


И ещё пара строк на сохранение модели в ONNX:
Скрытый текст
from torchvision import transformsimport torchfrom PIL import Imageuse_cuda=1mobilenet = torch.load("EyeDetector.pth")mobilenet.classifier = mobilenet.classifier[:-1]mobilenet.cuda()img = Image.open('E:/OpenProject/OpenVinoTest/face_detect/EyeDataset/krnwapzu_left.jpg')mobilenet.eval()transform = transforms.Compose([transforms.Resize(64),                                      transforms.ToTensor(),                                      ])img = transform(img)img = torch.unsqueeze(img, 0)if use_cuda:    img = img.cuda()img = torch.autograd.Variable(img)list_features = mobilenet(img)ps = torch.exp(list_features.data.cpu())top_p, top_class = ps.topk(1, dim=1)list_features_numpy = []for feature in list_features:    list_features_numpy.append(feature.data.cpu().numpy())mobilenet.cpu()x = torch.randn(1, 3, 64, 64, requires_grad=True)torch_out = mobilenet(x)torch.onnx.export(mobilenet, x,"mobilnet.onnx", export_params=True, opset_version=10, do_constant_folding=True,input_names = ['input'],output_names = ['output'])print(list_features_numpy)


Сохранение модели в ONNX нужно для дальнейшего вызова модели в Open Vino. Я не запаривался с преобразованиев в int8, оставил модель как была в 32-битном формате.

Анализ точности, метрики качества?.. Зачем это в любительском проекте. Такие штуки оцениваются по-другому. Никакая метрика не скажет вам система работает. Работает система или нет вы поймёте только на практике. Даже 1% ошибок может сделать систему неприятной для использования. Я бывает обратное. Вроде ошибок 20%, но система сконфигурирована так, что они не видны.

Такие вещи проще смотреть на практике, будет работать или нет. И уже поняв критерий работы вводить метрики, если они будут нужны.

Проблемы версии 2


Текущая реализация качественно другая, но всё же она имеет ряд проблем:
  • Лицо детектируется не всегда. Это особенность вызвана тем, что сеть обучалась для других условий:
    • Целевым объектом сети были явно не младенцы
    • Сеть не обучалась в ИК
    • Сеть явно отдаёт преимущество вертикальным лицам
    • Сеть явно предпочитает лица размером где-то в кадра.

  • Иногда ребёнок может проснуться не открывая глаз. Скорее всего при этом он будет махать головой. Но, как мы показали выше, махание головой не есть однозначное просыпание. Так что хотелось бы детектировать звук, чтобы понять проснулся или нет.
  • Мы получили неплохую платформу детектирования просыпания. А что ещё можно сделать на её базе?


Переобучить детект лица?


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

Звук


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

Идём дальше, что ещё


В какой-то момент я провёл тест, запустив зацикленное 5-секундное видео себя с супругой:



Был видно, что сын залипает на лица людей в поле зрения (монитор подвесил его минут на 30). И родилась идея: сделать управление выражением лица. Это не просто статичное видео, но и вариант взаимодействия. Получилось как-то так (при смене эмоции сына происходит переключение видеоряда):

Папа, ты что, долбанулся?!

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

А потом?


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

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


Как сейчас всё работает (в начале статьи есть более масштабное видео):
  • Всё управление идёт через Telegramm + через камеру.
  • Если не нужно управление видео эмоциями, то вся девайсина выглядит так:

  • Запускается включением питания на павербанке.
  • Если есть подключенная сеть то устройство уже готово к работе
  • Если нет сети то надо показать QR-код с сетью, система автоматически запуститься
  • Через Telegramm можно выбрать набор событий за которыми следить:

  • Каждый раз когда происходит событие которое интересно, присылается уведомление:

  • В любой момент можно запросить фотографию с устройства чтобы посмотреть что твориться


В целом отзывы от себя любимого:
  1. Детектор лица работает не очень. Это реально особенность любых детекторов нефайнтьюненых на детей. Обычно это не мешает детектировать просыпание (хоть парочка нормальных фото с открытыми глазами да придёт). Сейчас нет планов переобучать.
  2. Без экрана немного непрозрачный запуск (считался QR код или нет). А с экраном много проводов. Я думаю, самый правильный вариант будет посадить диодов на GPIO. И зажигать их в зависимости от статуса (есть коннект, не работает камера, не работает Movidius, нет коннекта к telegram'у, и.т.д.). Но пока не сделал
  3. Иногда сложно закрепить камеру. Так как у меня есть пара штативов как то справляюсь. А без них, пожалуй, ничего бы не заработало.
  4. Реально позволяет освободить сколько-то времени и дать свободы перемещения. Больше это чем у нормальной радионяни/видеоняни со стримингом? Не знаю. Может чуть-чуть проще.
  5. Прикольная штука для экспериментов.


Как запускать


Как я и говорил выше я попробовал выложить все исходники. Проект большой и разветвлённый, так что может что-то забыл или не дал подробных инструкий. Не стесняйтесь спрашивать и уточнять.
Есть несколько способов всё развернуть:
  1. Сорсы с гитхаба. Это более сложный способ, придётся долго настраивать RPi, может что-то забыл. Зато вы полностью держите процесс под контролем (включая настройки RPi).
  2. Использовать готовый образ. Тут можно сказать что безблагодатно и несекьюрно. Зато на порядок проще.


GitHub


Основной репозиторий расположен тут github.com/ZlodeiBaal/BabyFaceAnalizer
Он состоит из двух файлов которые надо запускать:
  1. Скрипт инициализации/проверки состсояния/настройки сети QRCode.py (для этого скрипта, напомню, есть более подробное описание). Он подключает WiFi и проверяет что имеется настройки для бота в Telegram.
  2. Основной рабочий скрипт face.py

Кроме того. в Git нет двух вещей:
  1. Файла с креденшоналами WiFi wpa_supplicant_auto.conf
  2. Файла с креденшоналами Telegram бота tg_creedential.txt

Можно позволить система создать их автоматически при следующем запуске. Можно использвать следующие, заполнив пустые поля:
tg_creedential.txt
token to access the HTTP API чтобы его получить, отправьте @BotFather в telegram команду "/newbot"
socks5:// по умолчанию не используется, но должна быть пустая строчка хотя бы
логин для socks5 по умолчанию не используется, но должна быть пустая строчка хотя бы
пароль для socks5 по умолчанию не используется, но должна быть пустая строчка хотя бы


wpa_supplicant_auto.conf
network={
ssid="******"
psk="*******"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}


Свистелки и перделки по настраиванию RPi


К сожалению, просто положить и запустить скрипты на RPi не получиться. Вот что ещё надо для стабильной работы:
  1. Установить l_openvino_toolkit_runtime_raspbian_p_2020.1.023.tgz по инструкции docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_raspbian.html
  2. Установить автозапуск
  3. Удалить мессадж о дефолтном пароле (может не надо, но мне мешало) sudo apt purge libpam-chksshpwd
  4. выключить скринсейвер www.raspberrypi.org/forums/viewtopic.php?t=260355
  5. Для детекции аудио:
    • pip3 install webrtcvad
    • sudo apt-get install python-dev
    • sudo apt-get install portaudio19-dev
    • sudo pip3 install pyaudio
  6. Скачать модели из репозитория OpenVino используя скрипт Get_models.py в папке Models


Образ


Образ выложен тут(5 гигов).
Пара моментов:
  1. Используется стандартный логин-пароль (pi, raspberry)
  2. Включен доступ по SSH
  3. По умолчанию не подключен WiFi и не настроен адрес бота в телеге которого система будет использовать для контроля.


Как настроить WiFi в образе


Первый вариант после запуска показать QR код с текстом:
WIFI:T:WPA;P:qwerty123456;S:TestNet;;

Где после P идёт пароль сети, после S индентификатор сети.
  1. Если у вас есть телефон с Android 10 то там такой QR код генерируется автоматически при нажатии поделиться сетью
  2. Если нет то можно сгенерировать на www.the-qrcode-generator.com


Второй вариант зайти по SSH на RPi (подключившись по проводу). Либо включить монитор и клавиатуру. И положить файл
wpa_supplicant_auto.conf
network={
ssid="*********"
psk="*******"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}

c вашими настройками wi-fi в папку "/home/pi/face_detect".

Как настроить телеграм-бота в образе


Первый вариант после запуска показать QR код с текстом:
tg_creedential.txt
token to access the HTTP API чтобы его получить, отправьте @BotFather в telegram команду "/newbot"
socks5:// по умолчанию не используется, но должна быть пустая строчка хотя бы
логин для socks5 по умолчанию не используется, но должна быть пустая строчка хотя бы
пароль для socks5 по умолчанию не используется, но должна быть пустая строчка хотя бы

сгенерировав его через www.the-qrcode-generator.com
Второй вариант зайти по SSH на RPi (подключившись по проводу). Либо включить монитор и клавиатуру. И положить файл tg_creedential.txt описанный выше в папку "/home/pi/face_detect".

Ремарка про детство


Уже когда собрал первую версию и показывал её своей маме, то получил внезапный ответ:
-О, а мы в твоём детстве почти так же делали.
?!
Ну, коляску с тобой на балкон выставляли, через форточку туда выкидывали микрофон, который был включен в усилитель в квартире.

Вообщем внезапно оказалось что это наследственное.

Ремарка про супругу


А как супруга отнеслась?
А как она позволила тебе эксперименты над сыном ставить?!
Спрашивали не раз.
Но, я жену испортил хорошо. Вот, она даже на Хабре иногда статьи пишет.

P.S.1


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

P.S.2


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

Управляем звуком ПК от активности пользователя с помощью Python

17.06.2021 14:15:17 | Автор: admin

Настройка программного обеспечения

Без промедления начнём. Нам нужно установить следующее ПО:

  • Windows 10

  • Anaconda 3 (Python 3.8)

  • Visual Studio 2019 (Community) - объясню позже, зачем она понадобится.

Открываем Anaconda Prompt (Anaconda3) и устанавливаем следующие пакеты:

pip install opencv-pythonpip install dlibpip install face_recognition

И уже на этом моменте начнутся проблемы с dlib.

Решаем проблему с dlib

Я перепробовал все решения, что нашёл в интернете и они оказались неактуальными - раз, два, три, официальное руководство и видео есть. Поэтому будем собирать пакет вручную.

Итак, первая же ошибка говорит о том, что у нас не установлен cmake.

ERROR: CMake must be installed to build dlib
ERROR: CMake must be installed to build dlibERROR: CMake must be installed to build dlib

Не закрывая консоль, вводим следующую команду:

pip install cmake
Проблем при установке быть не должно

Пробуем установить пакет той же командой (pip install dlib), но на этот раз получаем новую ошибку:

Отсутствуют элементы Visual Studio

Ошибка явно указывает, что у меня, скорее всего, стоит студия с элементами только для C# - и она оказывается права. Открываем Visual Studio Installer, выбираем "Изменить", в вкладке "Рабочие нагрузки" в разделе "Классические и мобильные приложения" выбираем пункт "Разработка классических приложений на С++":

Пошагово
"Изменить""Изменить"Разработка классических приложений на С++Разработка классических приложений на С++Ждем окончания установкиЖдем окончания установки

Почему важно оставить все галочки, которые предлагает Visual Studio. У меня с интернетом плоховато, поэтому я решил не скачивать пакет SDK для Windows, на что получил следующую ошибку:

Не нашли компилятор

Я начал искать решение этой ошибки, пробовать менять тип компилятора (cmake -G " Visual Studio 16 2019"), но только стоило установить SDK, как все проблемы ушли.

Я пробовал данный метод на двух ПК и отмечу ещё пару подводных камней. Самое главное - Visual Studio должна быть 2019 года. У меня под рукой был офлайн установщик только 2017 - я мигом его поставил, делаю команду на установку пакета и получаю ошибку, что нужна свежая Microsoft Visual C++ версии 14.0. Вторая проблема была связана с тем, что даже установленная студия не могла скомпилировать проект. Помогла дополнительная установка Visual C++ 2015 Build Tools и Microsoft Build Tools 2015.

Открываем вновь Anaconda Prompt, используем ту же самую команду и ждём, когда соберется проект (около 5 минут):

Сборка
Всё прошло успешноВсё прошло успешно

Управляем громкостью

Вариантов оказалось несколько (ссылка), но чем проще - тем лучше. На русском язычном StackOverflow предложили использовать простую библиотеку от Paradoxis - ей и воспользуемся. Чтобы установить её, нам нужно скачать архив, пройти по пути C:\ProgramData\Anaconda3\Lib и перенести файлы keyboard.py, sound.py из архива. Проблем с использованием не возникало, поэтому идём дальше

Собираем события мыши

Самым популярным модулем для автоматизации управления мышью/клавиатурой оказался pynput. Устанавливаем так же через (pip install dlib). У модуля в целом неплохое описание - https://pynput.readthedocs.io/en/latest/mouse.html . Но у меня возникли сложности при получении событий. Я написал простую функцию:

from pynput import mousedef func_mouse():        with mouse.Events() as events:            for event in events:                if event == mouse.Events.Scroll or mouse.Events.Click:                    #print('Переместил мышку/нажал кнопку/скролл колесиком: {}\n'.format(event))                    print('Делаю половину громкости: ', time.ctime())                    Sound.volume_set(volum_half)                    break

Самое интересное, что если раскомментировать самую первую строчку и посмотреть на событие, которое привело выходу из цикла, то там можно увидеть Move. Если вы заметили, в условии if про него не слово. Без разницы, делал я только скролл колесиком или только нажатие любой клавиши мыши - все равно просто движение мыши приводит к выходу из цикла. В целом, мне нужно все действия (Scroll, Click, Move), но такое поведение я объяснить не могу. Возможно я где-то ошибаюсь, поэтому можете поправить.

А что в итоге?

Adam Geitgey, автор библиотеки face recognition, в своём репозитории имеет очень хороший набор примеров, которые многие используют при написании статей: https://github.com/ageitgey/face_recognition/tree/master/examples

Воспользуемся одним из них и получим следующий код, который можно скачать по ссылке: Activity.ipynb, Activity.py

Код
# Подключаем нужные библиотекиimport cv2import face_recognition # Получаем данные с устройства (веб камера у меня всего одна, поэтому в аргументах 0)video_capture = cv2.VideoCapture(0) # Инициализируем переменныеface_locations = []from sound import SoundSound.volume_up() # увеличим громкость на 2 единицыcurrent = Sound.current_volume() # текущая громкость, если кому-то нужноvolum_half=50  # 50% громкостьvolum_full=100 # 100% громкостьSound.volume_max() # выставляем сразу по максимуму# Работа со временем# Подключаем модуль для работы со временемimport time# Подключаем потокиfrom threading import Threadimport threading# Функция для работы с активностью мышиfrom pynput import mousedef func_mouse():        with mouse.Events() as events:            for event in events:                if event == mouse.Events.Scroll or mouse.Events.Click:                    #print('Переместил мышку/нажал кнопку/скролл колесиком: {}\n'.format(event))                    print('Делаю половину громкости: ', time.ctime())                    Sound.volume_set(volum_half)                    break# Делаем отдельную функцию с напоминаниемdef not_find():    #print("Cкрипт на 15 секунд начинается ", time.ctime())    print('Делаю 100% громкости: ', time.ctime())    #Sound.volume_set(volum_full)    Sound.volume_max()        # Секунды на выполнение    #local_time = 15    # Ждём нужное количество секунд, цикл в это время ничего не делает    #time.sleep(local_time)        # Вызываю функцию поиска действий по мышке    func_mouse()    #print("Cкрипт на 15 сек прошел")# А тут уже основная часть кодаwhile True:    ret, frame = video_capture.read()        '''    # Resize frame of video to 1/2 size for faster face recognition processing    small_frame = cv2.resize(frame, (0, 0), fx=0.50, fy=0.50)    rgb_frame = small_frame[:, :, ::-1]    '''    rgb_frame = frame[:, :, ::-1]        face_locations = face_recognition.face_locations(rgb_frame)        number_of_face = len(face_locations)        '''    #print("Я нашел {} лицо(лица) в данном окне".format(number_of_face))    #print("Я нашел {} лицо(лица) в данном окне".format(len(face_locations)))    '''        if number_of_face < 1:        print("Я не нашел лицо/лица в данном окне, начинаю работу:", time.ctime())        '''        th = Thread(target=not_find, args=()) # Создаём новый поток        th.start() # И запускаем его        # Пока работает поток, выведем на экран через 10 секунд, что основной цикл в работе        '''        #time.sleep(5)        print("Поток мыши заработал в основном цикле: ", time.ctime())                #thread = threading.Timer(60, not_find)        #thread.start()                not_find()        '''        thread = threading.Timer(60, func_mouse)        thread.start()        print("Поток мыши заработал.\n")        # Пока работает поток, выведем на экран через 10 секунд, что основной цикл в работе        '''        #time.sleep(10)        print("Пока поток работает, основной цикл поиска лица в работе.\n")    else:        #все хорошо, за ПК кто-то есть        print("Я нашел лицо/лица в данном окне в", time.ctime())        Sound.volume_set(volum_half)            for top, right, bottom, left in face_locations:        cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)        cv2.imshow('Video', frame)        if cv2.waitKey(1) & 0xFF == ord('q'):        breakvideo_capture.release()cv2.destroyAllWindows()

Суть кода предельно проста: бегаем в цикле, как только появилось хотя бы одно лицо (а точнее координаты), то звук делаем 50%. Если не нашёл никого поблизости, то запускаем цикл с мышкой.

Тестирование в бою

Ожидание и реальность

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

Признаю честно - до этого момента никогда не сталкивался с многопоточностью на Python, поэтому "с наскоку" тему взять не удалось и результат по видео понятен. Есть неплохая статья на Хабре, описывающая различные методы многопоточности, применяемые в языке. Пока у меня решения нету по этой теме нету - будет повод разобраться лучше и дописать код/статью с учетом этого.

Так же возникает закономерный вопрос - а если вместо живого человека поставить перед монитором картинку? Да, она распознает, что, скорее всего, не совсем верно. Мне попался очень хороший материал по поводу определения живого лица в реальном времени - https://www.machinelearningmastery.ru/real-time-face-liveness-detection-with-python-keras-and-opencv-c35dc70dafd3/ , но это уже немного другой уровень и думаю новичкам это будет посложнее. Но эксперименты с нейронными сетями я чуть позже повторю, чтобы тоже проверить верность и повторяемость данного руководства.

Немаловажным фактором на качество распознавания оказывает получаемое изображение с веб-камеры. Предложение использовать 1/4 изображения (сжатие его) приводит только к ухудшению - моё лицо алгоритм распознать так и не смог. Для повышения качества предлагают использовать MTCNN face detector (пример использования), либо что-нибудь посложнее из абзаца выше.

Другая интересная особенность - таймеры в Питоне. Я, опять же, признаю, что ни разу до этого не было нужды в них, но все статьях сводится к тому, чтобы ставить поток в sleep(кол-во секунд). А если мне нужно сделать так, чтобы основной поток был в работе, а по истечению n-ое количества секунд не было активности, то выполнялась моя функция? Использовать демонов (daemon)? Так это не совсем то, что нужно. Писать отдельную программу, которая взаимодействует с другой? Возможно, но единство программы пропадает.

Заключение

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

P.S. Предлагаю вам, читатели, обсудить в комментариях статью - ваши идеи, замечания, уточнения.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru