Время прочтения: 5 мин.

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

Так как сжатые строки и ограниченные ресурсы коррелируют между собой, то нужно разобраться с ними в первую очередь. Эти проблемы подтолкнули меня к решению отказаться от обработки потокового видео, я решила обрабатывать не все кадры, а только 1 кадр с дельтой по времени в 4 секунды (цифра выбрана мной исходя из опыта проб и шибок). Для получения скриншотов моя задача оказалась очень подходящей, т.к. я могу их скачивать через url адрес с помощью библиотеки python request.

Отсутствие обучающей выборки заставило меня обратить внимание на размеченные датасеты MS COCO. На просторах интернета я нашла open source решения PythonAPI (pycocotools) – библиотека для использования СOCO API на языке python с предобученными весами на Mask R-CNN. Найденная информация подтолкнула меня к использованию именно этой реализации нейросети на Python 3, Keras и TensorFlow, генерирующую ограничительные рамки и маски сегментации для каждого из экземпляров объектов в изображении. Он основан на функциональной пирамидальной сети (FPN) и магистрали ResNet101.

           С установкой пакета pycocotools у меня возник ряд трудностей, повлекших за собой временные потери. Я хочу рассказать, как вам избежать моих ошибок и не тратить время на поиски ответов на различных форумах.

Для установки пакета запустите из командной строки pip install. То же самое нужно проделать с Mask R-CNN (github).  Пакеты необходимо именно клонировать из этого git репозитория, чтобы избежать ошибок. Еще одним важным условием успешного инсталлирования библиотеки является наличие установленного и прописанного в системные пути компонента Visual C ++ 2015 Build Tools (файл visualcppbuildtools_full.exe).  

При запуске pip install pycocotools у меня возникла проблема:

Исправить ошибку получилось после внесения изменений в файл setup.py – библиотеки — там нужно было заменить строку

extra_compile_args=['-Wno-cpp', '-Wno-unused-function', '-std=c99'] на extra_compile_args={'gcc': ['/Qstd=c99']}

После данных манипуляций ошибок больше не возникало, и библиотека pycocotools установилась успешно.

    Чтобы воспользоваться предобученными весами на датасете COCO вам нужно будет скачать файл mask_rcnn_coco.h5 и положить его в клонированную директорию Mask R-CNN. В папке samples есть файл demo, именно его я и использовала как baseline для своего решения. При импорте библиотек, у вас может возникнуть ошибка:

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

Далее я использую модель, обученную на наборе данных MS-COCO и загружаю уже существующие веса.

maskrcnn_model = modellib.MaskRCNN(mode="inference", model_dir=MODEL_FILE, config=config)
maskrcnn_model.load_weights(COCO_MODEL, by_name=True) 

Модель классифицирует объекты и возвращает id классов, представляющие собой целочисленные значения, идентифицирующие каждый класс. Некоторые наборы данных присваиваются целочисленные значения в свои классы, а некоторые нет. Чтобы получить список имен классов, необходимо загрузить набор данных, а затем использовать свойство class_names

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

from PIL import Image
arr = os.listdir('images/1-10')
count_all=0
count_sotr = 0
z=0
res = pd.DataFrame(columns=['path','frame','people_all','sotr','clients',
'max_sotr'])
for v in arr:
    image_list = os.listdir('images/'+v)
    for i in image_list:
        try
            image = Image.open('images/'+v+'/'+i)
            area = (1185,297,1435,635) # область анализа
            cropped_img = image.crop(area)
results = maskrcnn_model.detect([np.array(cropped_img)],      verbose=False)
            r = results[0]
            count_all =0
            count_sotr=0
            for k in (range(len(r['rois']))):
                if r['class_ids'][k] == 1:
                
                    if r['rois'][k][0] > 233 :
                        count_sotr = count_sotr + 1
                    count_all =count_all +1
        except:
            print ('cannot open')
        cl = count_all-count_sotr
        print (v,i,count_all,count_sotr)
        new_row =  [v,i,count_all,count_sotr,cl,0]
        res.loc[z] = new_row
        z = z +1
    res['max_sotr'] = res.groupby(['path'])['sotr'].transform('max')
    writer = pd.ExcelWriter('images/result.xlsx')
    res.to_excel(writer, 'Sheet1',index=False)
    writer.save()

Для проверки адекватности модели, попробуем вывести результат распознавания на примере любой фотографии используя следующий код

my_image = skimage.io.imread("foto.jpg")
result = maskrcnn_model.detect([my_image], verbose=1)
detect_results = result[0]
visualize.display_instances(my_image, detect_results['rois'], detect_results['masks'], detect_results['class_ids'], 
                            class_names, detect_results['scores'])

 В результате распознавания получим вот такую картинку:

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

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