Оригинальный автор: Чад Харт
Оригинальный адрес:Computer Vision on the Web with WebRTC and TensorFlow - webrtcHacks
Аннотация: Автор этой статьи представляет конкретный процесс объединения WebRTC и TensorFlow для реализации обнаружения изображений, который имеет справочное значение как для пользователей TensorFlow, так и для разработчиков WebRTC. Из-за длины статьи мы разделим ее на две части для сериализации.
TensorFlow — одна из самых популярных сред машинного обучения. Одна из замечательных особенностей TensorFlow заключается в том, что многие из его библиотек активно поддерживаются и обновляются. И одна из моих любимых библиотек — TensorFlow Object Detection API. API обнаружения объектов Tensorflow может классифицировать несколько объектов на графике и указывать их конкретное местоположение. Предварительно обученный на почти 1000 классах объектов, API предоставляет множество предварительно обученных моделей, которые позволяют найти компромисс между скоростью и точностью.
Хорошо иметь рекомендации для этих моделей, но для работы всех них требуются изображения, и эти изображения нужно добавлять в папку самостоятельно. Я действительно хотел бы соединить его с потоковой передачей WebRTC в реальном времени для компьютерного зрения в реальном времени по сети. Не имея возможности найти какие-либо примеры или руководства по этому поводу, я решил написать этот пост в блоге о том, как это сделать. Для тех, кто использует RTC, эта статья может быть использована в качестве краткого руководства по использованию TensorFlow для обработки потоков WebRTC. Для тех, кто использует TensorFlow, вы можете использовать эту статью как краткое введение в то, как добавить WebRTC в свой проект. Люди, использующие WebRTC, должны быть знакомы с Python. Те, кто использует TensorFlow, должны быть знакомы с сетевым взаимодействием и некоторым JavaScript.
Эта статья не предназначена для использования в качестве руководства по началу работы с WebRTC или TensorFlow. Если вам нужно такое руководство, вам следует обратиться к Руководству по началу работы с TensorFlow, Руководству по началу работы с WebRTC и т. д. В Интернете есть бесчисленное множество соответствующих вводных материалов и руководств.
Просто скажи мне, как это сделать
Если вы здесь только для того, чтобы быстро найти справочную информацию, или вам лень читать подробное текстовое введение, следуйте приведенным ниже методам, чтобы быстро приступить к работе. Сначала установите Докер. Загрузите окно командной строки и введите следующую команду:
docker run -it -p 5000:5000 chadhart/tensorflow-object-detection:runserver
Затем введите адресную строку браузера и перейдите по адресу http://localhost:5000/local, примите запрос на разрешение камеры, и вы должны увидеть интерфейс, подобный следующему:
Базовая архитектура
Сначала мы настроили базовую архитектуру для локальной отправки потока локальной веб-камеры из getUserMedia WebRTC на сервер Python, используя веб-сервер Flask и API обнаружения объектов TensorFlow. Конкретные настройки примерно такие, как показано на рисунке ниже.
Flask будет обслуживать файлы html и JavaScript для рендеринга в браузере. getUserMedia.js отвечает за захват локального видеопотока. Затем objDetect.js использует метод HTTP POST для отправки изображения в API обнаружения объектов TensorFlow, который возвращает объект, который он видит (он называет его «классом»), и местоположение объекта на изображении. Мы инкапсулируем эти сведения в объект JSON и отправим этот объект обратно в objDetect.js, чтобы мы могли отображать поля и метки объектов, которые мы видим.
настроить
Настройка и предпосылки
Прежде чем мы начнем, нам нужно выполнить некоторые настройки для Tensorflow и API обнаружения объектов.
Простая настройка с помощью Docker
Я устанавливал его несколько раз на OSX, Windows 10 и Raspbian (это не простой процесс). Различные зависимости от версии сложны, и их исправление может быть трудным, особенно когда вы просто пытаетесь увидеть, сработает ли какая-то предварительная работа. Я рекомендую использовать Docker, чтобы избежать этих сложных проблем. Вам нужно будет изучить Docker, что является обязательным, и более эффективно потратить некоторое время на его изучение, чем пытаться создать правильную версию Protobuf. Проект TensorFlow поддерживает некоторые официальные образы Docker, такие как tensorflow/tensorflow.
Если вы используете Docker, мы можем использовать образ, который я создал для этого сообщения в блоге. Из командной строки выполните следующую команду:
git clone https://github.com/webrtcHacks/tfObjWebrtc.git
cd tfObjWebrtc
docker run -it -p 5000:5000 --name tf-webrtchacks -v $(pwd):/code chadhart/tensorflow-object-detection:webrtchacks
Обратите внимание, что $(pwd) в docker run работает только в Linux и Windows Powershell. В командной строке Windows 10 используйте %cd%.
Увидев это, вы должны были войти в контейнер Docker. Теперь запустите:
python setup.py install
Это будет использовать последний образ TensorFlow Docker, подключить порт 5000 на хосте Docker к порту 5000, назвать контейнер tf-webrtchacks, сопоставить локальный каталог с новым каталогом /code в контейнере и установить для этого каталога значение tf-webrtchacks. это каталог по умолчанию (с которым мы будем работать дальше) и запустите bash для взаимодействия с командной строкой. После того, как эти приготовления сделаны, мы можем начать.
Если вы новичок в TensorFlow, вам может потребоваться запустить начальную записную книжку Jupyter, как описано в tensorflow/tensorflow, а затем вернуться и выполнить приведенную выше команду.
Еще один громоздкий способ реализации
Если вы планируете начать с нуля, вам нужно будет установить TensorFlow, который сам по себе имеет множество зависимостей, таких как Python. Проект TensorFlow предоставляет руководства для различных платформ, пожалуйста, посетитеwww.tensorflow.org/install. API обнаружения объектов также имеет собственные инструкции по установке, а также некоторые дополнительные зависимости. После завершения этих приготовлений выполните следующую команду:
git clone https://github.com/webrtcHacks/tfObjWebrtc.git
cd tfObjWebrtc
python setup.py install
При этом все зависимости Python должны быть установлены, соответствующие файлы API обнаружения объектов Tensorflow должны быть скопированы, а Protobufs должны быть установлены. Если это не сработает, я рекомендую проверить файл setup.py и вручную запустить в нем команды, чтобы исправить все существующие проблемы.
Часть 1. Убедитесь, что Tensorflow работает
Чтобы убедиться, что API обнаружения объектов TensorFlow работает, мы начнем с измененной версии официального JupyterNotebook, демонстрирующего обнаружение объектов. Я сохраняю этот файл как object_detection_tutorial.py.
Если вырезать и вставлять каждую часть блокнота, результат должен быть следующим: (Поскольку этот код длинный, скриншот повлияет на чтение, мы заменяем его текстовым макетом, перетащите влево и вправо, чтобы просмотреть длинный код)
# IMPORTS
import numpy as np
import os
import six.moves.urllib as urllib
import sys
import tarfile
import tensorflow as tf
import zipfile
from collections import defaultdict
from io import StringIO
# from matplotlib import pyplot as plt ### CWH
from PIL import Image
if tf.__version__ != '1.4.0':
raise ImportError('Please upgrade your tensorflow installation to v1.4.0!')
# ENV SETUP ### CWH: remove matplot display and manually add paths to references
'''
# This is needed to display the images.
%matplotlib inline
# This is needed since the notebook is stored in the object_detection folder.
sys.path.append("..")
'''
# Object detection imports
from object_detection.utils import label_map_util ### CWH: Add object_detection path
#from object_detection.utils import visualization_utils as vis_util ### CWH: used for visualization
# Model Preparation
# What model to download.
MODEL_NAME = 'ssd_mobilenet_v1_coco_2017_11_17'
MODEL_FILE = MODEL_NAME + '.tar.gz'
DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'
# Path to frozen detection graph. This is the actual model that is used for the object detection.
PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb'
# List of the strings that is used to add correct label for each box.
PATH_TO_LABELS = os.path.join('object_detection/data', 'mscoco_label_map.pbtxt') ### CWH: Add object_detection path
NUM_CLASSES = 90
# Download Model
opener = urllib.request.URLopener()
opener.retrieve(DOWNLOAD_BASE + MODEL_FILE, MODEL_FILE)
tar_file = tarfile.open(MODEL_FILE)
for file in tar_file.getmembers():
file_name = os.path.basename(file.name)
if 'frozen_inference_graph.pb' in file_name:
tar_file.extract(file, os.getcwd())
# Load a (frozen) Tensorflow model into memory.
detection_graph = tf.Graph()
with detection_graph.as_default():
od_graph_def = tf.GraphDef()
with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
serialized_graph = fid.read()
od_graph_def.ParseFromString(serialized_graph)
tf.import_graph_def(od_graph_def, name='')
# Loading label map
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
category_index = label_map_util.create_category_index(categories)
# Helper code
def load_image_into_numpy_array(image):
(im_width, im_height) = image.size
return np.array(image.getdata()).reshape(
(im_height, im_width, 3)).astype(np.uint8)
# Detection
# For the sake of simplicity we will use only 2 images:
# image1.jpg
# image2.jpg
# If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.
PATH_TO_TEST_IMAGES_DIR = 'object_detection/test_images' #cwh
TEST_IMAGE_PATHS = [ os.path.join(PATH_TO_TEST_IMAGES_DIR, 'image{}.jpg'.format(i)) for i in range(1, 3) ]
# Size, in inches, of the output images.
IMAGE_SIZE = (12, 8)
with detection_graph.as_default():
with tf.Session(graph=detection_graph) as sess:
# Definite input and output Tensors for detection_graph
image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
# Each box represents a part of the image where a particular object was detected.
detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
# Each score represent how level of confidence for each of the objects.
# Score is shown on the result image, together with the class label.
detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
num_detections = detection_graph.get_tensor_by_name('num_detections:0')
for image_path in TEST_IMAGE_PATHS:
image = Image.open(image_path)
# the array based representation of the image will be used later in order to prepare the
# result image with boxes and labels on it.
image_np = load_image_into_numpy_array(image)
# Expand dimensions since the model expects images to have shape: [1, None, None, 3]
image_np_expanded = np.expand_dims(image_np, axis=0)
# Actual detection.
(boxes, scores, classes, num) = sess.run(
[detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: image_np_expanded})
### CWH: below is used for visualizing with Matplot
'''
# Visualization of the results of a detection.
vis_util.visualize_boxes_and_labels_on_image_array(
image_np,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=8)
plt.figure(figsize=IMAGE_SIZE)
plt.imshow(image_np)
'''
Я не буду вдаваться в подробности того, что делает фактический код TensorFlow, который можно найти в демонстрациях Jupyter и других руководствах. Я сосредоточусь на изменениях, которые мы внесли в код.
Я прокомментировал несколько подразделов:
- Изменены некоторые ссылки на местоположение
- Удалены все ссылки на матплот Python. Матплот Python используется для визуализации результатов вывода в среде с графическим интерфейсом. Он не установлен в моей среде Docker — в зависимости от того, как вы его запускаете, эти ссылки можно сохранить по своему усмотрению.
Вывод API обнаружения объектов
Как показано в строке 111, API обнаружения объектов выводит 4 типа объектов:
- класс - массив имен объектов
- scores - массив оценок достоверности
- Коробка - где находится каждый обнаруженный объект
- Count - общее количество обнаруженных объектов
Классы, оценки и поля представляют собой сопоставленные массивы одинакового размера, поэтому классы [n] имеют однозначное соответствие с оценками [n] и ящиками [n].
Поскольку я удалил визуализацию, нам нужен какой-то способ увидеть результаты, поэтому мы добавим в конец файла следующую команду:
### CWH: Print the object details to the console instead of visualizing them with the code above
classes = np.squeeze(classes).astype(np.int32)
scores = np.squeeze(scores)
boxes = np.squeeze(boxes)
threshold = 0.50 #CWH: set a minimum score threshold of 50%
obj_above_thresh = sum(n > threshold for n in scores)
print("detected %s objects in %s above a %s score" % ( obj_above_thresh, image_path, threshold))
for c in range(0, len(classes)):
if scores[c] > threshold:
class_name = category_index[classes[c]]['name']
print(" object %s is a %s - score: %s, location: %s" % (c, class_name, scores[c], boxes[c]))
Первая часть np.squeeze просто уменьшает вывод многомерного массива до одного измерения, как в исходном коде визуализации. Я думаю, что это побочный продукт TensorFlow, поскольку он обычно выводит многомерные массивы.
Затем мы хотим установить порог для оценки, которую он выводит. Похоже, TensorFlow по умолчанию вернет 100 объектов. Многие из этих объектов вложены или перекрываются с объектами с более высокой степенью достоверности. Я не нашел никаких рекомендаций по выбору порога, но для этих примеров изображений 50% кажется подходящим.
Наконец, нам нужно пройтись по этим массивам и напрямую вывести те баллы, которые превышают порог.
Если вы выполните следующую команду:
python object_detection_tutorial.py
Вы должны получить следующий результат:
detected 2 objects in object_detection/test_images/image1.jpg above a 0.5 score
object 0 is a dog - score: 0.940691, location: [ 0.03908405 0.01921503 0.87210345 0.31577349]
object 1 is a dog - score: 0.934503, location: [ 0.10951501 0.40283561 0.92464608 0.97304785]
detected 10 objects in object_detection/test_images/image2.jpg above a 0.5 score
object 0 is a person - score: 0.916878, location: [ 0.55387682 0.39422381 0.59312469 0.40913767]
object 1 is a kite - score: 0.829445, location: [ 0.38294643 0.34582412 0.40220094 0.35902989]
object 2 is a person - score: 0.778505, location: [ 0.57416666 0.057667 0.62335181 0.07475379]
object 3 is a kite - score: 0.769985, location: [ 0.07991442 0.4374091 0.16590245 0.50060284]
object 4 is a kite - score: 0.755539, location: [ 0.26564282 0.20112294 0.30753511 0.22309387]
object 5 is a person - score: 0.634234, location: [ 0.68338078 0.07842994 0.84058815 0.11782578]
object 6 is a kite - score: 0.607407, location: [ 0.38510025 0.43172216 0.40073246 0.44773054]
object 7 is a person - score: 0.589102, location: [ 0.76061964 0.15739655 0.93692541 0.20186904]
object 8 is a person - score: 0.512377, location: [ 0.54281253 0.25604743 0.56234604 0.26740867]
object 9 is a person - score: 0.501464, location: [ 0.58708113 0.02699314 0.62043804 0.04133803]
Часть 2. Создание веб-службы Object API
В этом разделе мы внесем некоторые изменения в код руководства, чтобы запустить его как веб-службу. Мой опыт работы с Python довольно ограничен (в основном в проектах Raspberry Pi), поэтому, если что-то не так, добавьте примечание или отправьте запрос на включение, чтобы я мог это исправить.
2.1 Превратите демо-код в услугу
До сих пор мы заставили API объектов TensorFlow работать правильно, а затем мы инкапсулируем его в вызываемую функцию. Я скопировал демонстрационный код в новый файл Python с именем object_detection_api.py. Как видите, я удалил много неиспользуемых или закомментированных строк, а также секцию вывода реквизитов в консоль (пока убрал).
Поскольку мы собираемся выводить эту информацию в сеть, лучше всего инкапсулировать наш вывод в виде объекта JSON. Для этого обязательно добавьте в импорт оператор importjso, за которым следует следующая команда:
# added to put object in JSON
class Object(object):
def __init__(self):
self.name="Tensor Flow Object API Service 0.0.1"
def toJSON(self):
return json.dumps(self.__dict__)
Далее мы собираемся повторно использовать предыдущий код для создания функции get_objects:
def get_objects(image, threshold=0.5):
image_np = load_image_into_numpy_array(image)
# Expand dimensions since the model expects images to have shape: [1, None, None, 3]
image_np_expanded = np.expand_dims(image_np, axis=0)
# Actual detection.
(boxes, scores, classes, num) = sess.run(
[detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: image_np_expanded})
classes = np.squeeze(classes).astype(np.int32)
scores = np.squeeze(scores)
boxes = np.squeeze(boxes)obj_above_thresh = sum(n > threshold for n in scores)
obj_above_thresh = sum(n > threshold for n in scores)
print("detected %s objects in image above a %s score" % (obj_above_thresh, threshold))
В этой функции мы добавляем входной параметр изображения и пороговое значение, которое по умолчанию равно 0,5. Остальной контент переработан на основе демо-кода.
Теперь давайте добавим в эту функцию еще немного кода для запроса конкретных значений и вывода их в объект JSON:
output = []
#Add some metadata to the output
item = Object()
item.numObjects = obj_above_thresh
item.threshold = threshold
output.append(item)
for c in range(0, len(classes)):
class_name = category_index[classes[c]]['name']
if scores[c] >= threshold: # only return confidences equal or greater than the threshold
print(" object %s - score: %s, coordinates: %s" % (class_name, scores[c], boxes[c]))
item = Object()
item.name = 'Object'
item.class_name = class_name
item.score = float(scores[c])
item.y = float(boxes[c][0])
item.x = float(boxes[c][1])
item.height = float(boxes[c][2])
item.width = float(boxes[c][3])
output.append(item)
outputJson = json.dumps([ob.__dict__ for ob in output])
return outputJson
На этот раз мы используем класс Object для создания исходных метаданных и добавления этих метаданных в выходной список. Затем мы добавляем данные объекта в этот список, используя цикл. Наконец, преобразуйте этот список в JSON и верните его.
После этого давайте создадим тестовый файл (напомните себе: сначала проверьте), чтобы проверить, вызывает ли он object_detection_test.py:
import scan_image
import os
from PIL import Image
# If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.
PATH_TO_TEST_IMAGES_DIR = 'object_detection/test_images' #cwh
TEST_IMAGE_PATHS = [ os.path.join(PATH_TO_TEST_IMAGES_DIR, 'image{}.jpg'.format(i)) for i in range(1, 3) ]
for image_path in TEST_IMAGE_PATHS:
image = Image.open(image_path)
response = object_detection_api.get_objects(image)
print("returned JSON: \n%s" % response)
Теперь, когда все готово, следующий шаг — запуск.
python object_detection_test.py
В дополнение к предыдущему выводу консоли вы должны увидеть строку JSON:
returned JSON:
[{"threshold": 0.5, "name": "webrtcHacks Sample Tensor Flow Object API Service 0.0.1", "numObjects": 10}, {"name": "Object", "class_name": "person", "height": 0.5931246876716614, "width": 0.40913766622543335, "score": 0.916878342628479, "y": 0.5538768172264099, "x": 0.39422380924224854}, {"name": "Object", "class_name": "kite", "height": 0.40220093727111816, "width": 0.3590298891067505, "score": 0.8294452428817749, "y": 0.3829464316368103, "x": 0.34582412242889404}, {"name": "Object", "class_name": "person", "height": 0.6233518123626709, "width": 0.0747537910938263, "score": 0.7785054445266724, "y": 0.5741666555404663, "x": 0.057666998356580734}, {"name": "Object", "class_name": "kite", "height": 0.16590245068073273, "width": 0.5006028413772583, "score": 0.7699846625328064, "y": 0.07991442084312439, "x": 0.43740910291671753}, {"name": "Object", "class_name": "kite", "height": 0.3075351119041443, "width": 0.22309386730194092, "score": 0.7555386424064636, "y": 0.26564282178878784, "x": 0.2011229395866394}, {"name": "Object", "class_name": "person", "height": 0.8405881524085999, "width": 0.11782577633857727, "score": 0.6342343688011169, "y": 0.6833807826042175, "x": 0.0784299373626709}, {"name": "Object", "class_name": "kite", "height": 0.40073245763778687, "width": 0.44773054122924805, "score": 0.6074065566062927, "y": 0.38510024547576904, "x": 0.43172216415405273}, {"name": "Object", "class_name": "person", "height": 0.9369254112243652, "width": 0.20186904072761536, "score": 0.5891017317771912, "y": 0.7606196403503418, "x": 0.15739655494689941}, {"name": "Object", "class_name": "person", "height": 0.5623460412025452, "width": 0.26740866899490356, "score": 0.5123767852783203, "y": 0.5428125262260437, "x": 0.25604742765426636}, {"name": "Object", "class_name": "person", "height": 0.6204380393028259, "width": 0.04133802652359009, "score": 0.5014638304710388, "y": 0.5870811343193054, "x": 0.026993142440915108}]
2.2 Добавить веб-сервер
Функция у нас уже есть — давайте воспользуемся ею для создания веб-сервиса.
Сначала используйте тестовый маршрут (Route) для запуска
У нас есть хороший API, который можно легко добавить в веб-сервис. Я обнаружил, что использование Flask — самый простой способ тестирования. Давайте создадим server.py и проведем быстрый тест:
import object_detection_api
import os
from PIL import Image
from flask import Flask, request, Response
app = Flask(__name__)
@app.route('/')
def index():
return Response('Tensor Flow object detection')
@app.route('/test')
def test():
PATH_TO_TEST_IMAGES_DIR = 'object_detection/test_images' # cwh
TEST_IMAGE_PATHS = [os.path.join(PATH_TO_TEST_IMAGES_DIR, 'image{}.jpg'.format(i)) for i in range(1, 3)]
image = Image.open(TEST_IMAGE_PATHS[0])
objects = object_detection_api.get_objects(image)
return objects
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
Теперь запустите сервер:
python server.py
Убедитесь, что сервис работает
Затем вызовите веб-службу. В моем собственном случае я просто выполнил следующую команду с хост-компьютера (поскольку мой экземпляр Docker теперь запускает сервер на переднем плане):
curl http://localhost:5000/test | python -m json.tool
json.tool поможет вам отформатировать вывод. Вы должны увидеть следующие результаты:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 467 100 467 0 0 300 0 0:00:01 0:00:01 --:--:-- 300
[
{
"name": "webrtcHacks Sample Tensor Flow Object API Service 0.0.1",
"numObjects": 2,
"threshold": 0.5
},
{
"class_name": "dog",
"height": 0.8721034526824951,
"name": "Object",
"score": 0.9406907558441162,
"width": 0.31577348709106445,
"x": 0.01921503245830536,
"y": 0.039084047079086304
},
{
"class_name": "dog",
"height": 0.9246460795402527,
"name": "Object",
"score": 0.9345026612281799,
"width": 0.9730478525161743,
"x": 0.4028356075286865,
"y": 0.10951501131057739
}
]
Что ж, далее мы собираемся принять POST, содержащий файл изображения и некоторые другие параметры, и запустить его, используя реальный маршрут. Для этого добавьте новый маршрут /image в функцию маршрута /test:
@app.route('/image', methods=['POST'])
def image():
try:
image_file = request.files['image'] # get the image
# Set an image confidence threshold value to limit returned data
threshold = request.form.get('threshold')
if threshold is None:
threshold = 0.5
else:
threshold = float(threshold)
# finally run the image through tensor flow object detection`
image_object = Image.open(image_file)
objects = object_detection_api.get_objects(image_object, threshold)
return objects
except Exception as e:
print('POST /image error: %e' % e)
return e
Это извлекает изображение из POST с кодировкой формы, необязательно указывая порог, и передает это изображение в наш object_detection_api.
Давайте проверим это:
curl -F "image=@./object_detection/test_images/image1.jpg" http://localhost:5000/image | python -m json.tool
Вы должны увидеть те же результаты, что и при использовании пути /test выше. Продолжая тест, вы можете указать пути к другим локальным образам по вашему выбору.
Как заставить службу работать за пределами локального хоста
Если вы собираетесь запускать свой браузер на локальном хосте, вам, вероятно, больше ничего делать не нужно. Но если это реальный сервис, то даже если нужно запустить много тестов, это не очень реально. Если вы хотите запускать сетевые службы в сети или использовать другие ресурсы для запуска сетевых служб, вам необходимо использовать CORS. К счастью, это можно легко исправить, добавив перед маршрутом следующий код:
# for CORS
@app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,POST') # Put any other methods you need here
return response
Сделайте так, чтобы служба поддерживала безопасное происхождение
Лучше всего использовать WebRTC с HTTPS, поскольку такие браузеры, как Chrome и Safari, поддерживают только безопасные источники, если они не настроены специально (хотя Chrome отлично поддерживает локальный хост, и вы также можете настроить Safari для разрешения захвата на незащищенных сайтах). раздел Инструменты отладки здесь для получения подробной информации). Для этого вам нужно получить несколько SSL-сертификатов или сгенерировать несколько собственных сертификатов. Я поместил свой собственный сертификат в каталог ssl/ и изменил последнюю строку app.run на:
app.run(debug=True, host='0.0.0.0', ssl_context=('ssl/server.crt', 'ssl/server.key'))
Если вы используете самозаверяющий сертификат, вам может понадобиться добавить параметр --insecure при тестировании с помощью CURL:
curl -F "image=@./object_detection/test_images/image2.jpg" --insecure https://localhost:5000/image | python -m json.tool
Нет строгой необходимости генерировать собственный сертификат, и это добавит определенный объем работы, поэтому я по-прежнему оставляю версию SSL закомментированной в нижней части server.py.
Если это производственное приложение, вам, вероятно, потребуется использовать прокси-сервер, такой как nginx, для отправки HTTPS, при этом все еще используя HTTP внутри (плюс множество других улучшений).
Добавьте несколько маршрутов для обслуживания нашей страницы
Прежде чем мы начнем знакомить с работой на стороне браузера, давайте создадим заглушки для некоторых маршрутов, которые будут использоваться позже. Для этого поместите следующий код после маршрута index():
@app.route('/local')
def local():
return Response(open('./static/local.html').read(), mimetype="text/html")
@app.route('/video')
def remote():
return Response(open('./static/video.html').read(), mimetype="
На этом работа на стороне Python заканчивается. Далее мы будем использовать JavaScript, и нам нужно будет написать немного HTML.
В следующей статье мы поделимся опытом разработки и оптимизации на стороне браузера.