Если у вас много видео-контента, но вы не знаете, что за люди на этих видео, вы можете быстренько создать инструмент, который автоматически будет обнаруживать и узнавать людей при помощи Go, Python и Facebox.
Требования
- Языка программирования Go
- Python 2 и OpenCV
- Facebox machinebox.io
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Внедряем видеопоток
Один из вопросов, который может возникнуть – можем ли мы поддерживать видео в боксах, например – при распознавании лиц в Facebox.
Поддержка видео присутствует в Machine Box, но проблема в том, что работа видеопотока может быть очень сложной, кроме того, у разных людей разные требования. Так что вместо поддержки видео сейчас, мы решили составить базовые примеры, чтобы показать, как внедрить video pipeline и интегрировать их с боксами, предоставленными Machine Box.
Извлечение кадров видео в Python и OpenCV
Первое, что нам нужно сделать, это открыть видео файл и извлечь кадры для обработки, для этого мы используем Python и OpenCV. Это несложно, и мы можем семплировать эти фрагменты, так как нам, скорее всего, не нужно рассматривать каждый кадр целого видео. Одного кадра в секунду будет достаточно для распознавания лица.
Давайте взглянем на код Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# video.py def videoStreamer(path, width=600, height=None, skip=None): # Загружаем видео. stream = cv2.VideoCapture(path) frames = int(stream.get(cv2.CAP_PROP_FRAME_COUNT)) FPS = stream.get(cv2.CAP_PROP_FPS) if skip == None: skip = int(FPS) while True: # Пропускаем несколько кадров, и смотрим один из них. for i in range(skip): stream.grab() (grabbed, frame) = stream.read() if not grabbed: return # Меняем размер изображения и отсылаем его в stdout в формате JSON frame = imutils.resize(frame, width=width, height=height) f = stream.get(cv2.CAP_PROP_POS_FRAMES) t = stream.get(cv2.CAP_PROP_POS_MSEC) res = bytearray(cv2.imencode(".jpeg", frame)[1]) obj = {"frame": int(f), "millis": int(t), "total": frames, "image": base64.b64encode(res)} sys.stdout.write(json.dumps(obj)) sys.stdout.write("\n") |
Суть данного скрипта в том, чтобы открыть видео, и при помощи настраиваемой частоте кадров получить информацию об одном из них, а также сам кадр. Его мы получаем в кодировке base64 как JSON и выводим в стандартную выдачу.
Создаем видеопоток при помощи и обработчиков Go и HTTP
Так как мы можем извлекать кадры и направлять их в стандартную выдачу, мы можем использовать Go для управления выполнением команд Python, отправки кадров в Facebox для определения, и получать отчет снова в браузере, при помощи API браузера EventSource. Это нужно нам для трансляции процесса обработки видео в реальном времени.
Выполняем Python скрипт при помощи Go
Мы используем Go для управления жизненным циклом скрипта Python, чтобы извлечь видео. Для этого мы можем использовать exec.CommandContext, где мы можем передать параметры, а используя context.Context из http.Request, мы можем отменить выполнение в любое время.
После выполнения мы можем прочесть stdout и декодировать JSON из команды. Давайте взглянем на фрагмент кода Go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// server.go flags := []string{"--path", path.Join(s.videos, filename), "--json", "True"} cmd := exec.CommandContext(r.Context(), "./video.py", flags...) stdout, err := cmd.StdoutPipe() if err != nil { // обработка ошибок } dec := json.NewDecoder(stdout) for { var f Frame err := dec.Decode(&f) // обработка кадра, вызываем facebox и nudebox // для распознавания лиц и обнаружения контента для взрослых ... } // ожидаем команду, чтобы закончить cmd.Wait() |
Вызов Facebox для распознавания лиц
После того, как мы добавили кадр в память, мы можем проводить любую работу. В данном случае, мы отправляем кадр в Facebox для выполнения распознавания лиц.
Это делается очень просто при помощи SDK Go, при работе в других языках вы можете просто использовать простой вызов HTTP в боксе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// server.go imgDec, err := base64.StdEncoding.DecodeString(f.Image) if err != nil { // обработка ошибок } // отправка кадра в Facebox для распознавания лиц faces, err := s.facebox.Check(bytes.NewReader(imgDec)) thumbnail = nil total = f.Total // проверяем, нашел ли facebox чье-нибудь лицо for _, face := range faces { if face.Matched { thumbnail = &f.Image } } |
После того, как вы поместили кадр в память, вы легко можете отправить его в несколько боксов Machine Box и выполнить проверку на адалт контент при помощи nudebox. Так вам не придется проводить проверку видео дважды.
Полезный совет. Сохраните ответ JSON из Facebox, наряду с кадром и данными видео базе данных или поисковике (например, Elastic Search). Так вы можете выполнять запросы и поиск своих видео.
Используем Server Sent Events для отправления отчетов обратно в браузер
Нам нужно работать в реальном режиме в браузере, и Server Sent Events позволяет нам это делать. В Go это происходит прямолинейно, нам нужно только указать правильные заголовки, вот так:
1 2 3 4 5 6 7 8 9 |
// server.go func (s *Server) check(w http.ResponseWriter, r *http.Request) { // указываем заголовки для Server Sent Events w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") // один кодер для записи событий enc := json.NewEncoder(w) } |
Теперь каждый раз, когда нам нужно отправить что-то в браузер, нам нужно только выслать сообщение с «data:» префикс с двойным окончанием \n\n, чтобы указать, что вы закончили с данным событием, например:
1 |
data: { "hello": "world" } \n\n |
Таким образом, при помощи небольшой функции Go мы можем отправить столько событий, сколько сами захотим:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// server.go func SendEvent(w http.ResponseWriter, enc *json.Encoder, v interface{}) { w.Write([]byte("data: ")) if err := enc.Encode(v); err != nil { // обработка ошибок } w.Write([]byte("\n\n")) if f, ok := w.(http.Flusher); ok { f.Flush() } } |
В браузере мы можем обработать все события даже при помощи нескольких строк Javascript.
1 2 3 4 5 6 7 8 9 10 |
// app.js var es = new EventSource($this.attr('href')) es.onmessage = function(e){ var obj = JSON.parse(e.data) if (obj.thumbnail) { frameData[obj.frame] = obj frames.append(...) } ... } |
Вот полный код http.Handler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
// server.go func (s *Server) check(w http.ResponseWriter, r *http.Request) { var thumbnail *string // отправляем заголовки в Server Sent Events w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") enc := json.NewEncoder(w) // запускаем скрипт обработки видео filename := r.URL.Query().Get("name") flags := []string{"--path", path.Join(s.videos, filename), "--json", "True"} cmd := exec.CommandContext(r.Context(), "./video.py", flags...) stdout, err := cmd.StdoutPipe() if err != nil { // обработка ошибок } cmd.Start() total := 0 dec := json.NewDecoder(stdout) for { var f Frame err := dec.Decode(&f) if err == io.EOF { break } if err != nil { // обработка ошибок } imgDec, err := base64.StdEncoding.DecodeString(f.Image) if err != nil { // обработка ошибок } // отправка кадра в Facebox для распознавания лиц faces, err := s.facebox.Check(bytes.NewReader(imgDec)) thumbnail = nil total = f.Total for _, face := range faces { if face.Matched { thumbnail = &f.Image } } SendEvent(w, enc, VideoData{ Frame: f.Frame, TotalFrames: f.Total, Seconds: (time.Duration(f.Millis/1000)*time.Second).String(), Complete: false, Faces: faces, Thumbnail: thumbnail, }) } cmd.Wait() // говорим браузеру о том, что мы закончили. SendEvent(w, enc, VideoData{ Frame: total, TotalFrames: total, Complete: true, }) } |
Вывод
Вы можете использовать Go и Python для внедрения действительно мощных инструментов распознавания лиц и многих других возможностей.
Создайте аккаунт в machinebox.io чтобы воспользоваться Facebox.
Полный код
Github: https://github.com/machinebox/videoanalysis
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»