Phát Hiện Đối Tượng Theo Thời Gian Thực Với Python

Phát Hiện Đối Tượng Theo Thời Gian Thực Với Python

Hôm nay mình sẽ giới thiệu đến các bạn một chủ đề khá “hot” đó là Computer Vision hay gọi tắt là CV. Cho đến hiện tại, các nhà nghiên cứu đã tìm ra cách cho máy móc có thể phát hiện đối tượng thông qua tầm nhìn của thiết bị. Và từ đó có hàng tá các ứng dụng thời thượng ra đời.

Từ việc sử dụng những chiếc xe có khả năng tự động phát hiện các đối tượng di chuyển trên đường cho đến nhận diện ngôn ngữ cơ thể, phát hiện hành vi của các đối tượng khả nghi cho việc phòng chống tội phạm, rồi đến nhận diện khuôn mặt v.v…. Ngày nay CV được sử dụng khá phổ biến và rộng rãi và không thể phủ nhận sự thật “Phát hiện đối tượng” (Object Detection) là một trong những ứng dụng tuyệt vời nhất của CV

Nếu bạn chưa từng làm việc với Python và OpenCV đừng lo ngại, mình sẽ viết bài giới thiệu về OpenCV sau, hoặc các bạn có thể tham khảo thêm một số bài viết trên mạng.

Modern-day CV là các công cụ cho phép dễ triển khai một ứng dụng thời thượng để xác định đối tượng thông qua các hình ảnh hoặc thậm chí qua các Video live stream. Trong bài viết này chúng ta sẽ cùng xem một màn trình diễn đơn giản của phát hiện đối tượng trong thời gian thực cùng với TensorFlow.

Yêu cầu chuẩn bị TensorFlow

Yêu cầu: Tensorflow >= 1.15.0

Các bạn hãy dùng pip để cài đặt phiên bản mới nhất của Tensorflow.

Các bạn đã nắm được những gì yêu cầu cần chuẩn bị cho viết ứng dụng, bây giờ chúng ta sẽ bắt tay vào cài đặt môi trường theo từng bước dưới đây.

Cài đặt môi trường

Bước 1: Tải xuống Tensorflow từ github.

Mở gitbash ra thực thi câu lệnh dưới đây để clone về Tensorflow:

git clone https://github.com/tensorflow/models.git

Nếu bạn chưa cài đặt git trên máy tính, bạn có thể chọn cách download file zip tại đây. 

Bước 2: Cài đặt các gói phụ thuộc.

Ở bước này chúng ta sẽ đảm bảo rẳng đã cài đặt đầy đủ các thư viện và các module cần thiết để chạy Object Detector trên thiết bị của chúng ta.

Dưới đây là dánh sách các thư viện cần thiết để chạy Object Detector (Hầu hết các thư viện cần thiết đều nằm trong các thư viện mặc định khi chúng ta cài Tensorflow):

  • Cython
  • contextlib2
  • pillow
  • lxml
  • matplotlib

Trong trường hợp nếu trình biên dịch phát hiện bất cứ module nào bị thiếu bạn chỉ cần dùng pip để cài đặt lại (pip là một trình quản lí các gói của python như composer trong php, npm trong nodejs, angular ...).

Bước 3: Cài đặt trình biên dịch Protobuf.

Protobuf hay Protocol buffers là những ngôn ngữ, nền tảng trung gian của Google, là những cơ chế mở rộng để tuần tự hóa dữ liệu có cấu trúc. Nó giúp chúng ta tổ chức giữ liệu dưới dạng cấu trúc và khi dữ liệu đã ở dưới dạng cấu trúc chúng ta có thể dễ dàng đọc ghi trong nhiều luồng dữ liệu và sử dụng nhiều ngôn ngữ.

Đây là một trong các phần phụ thuộc của ứng dụng OD, các bạn có thể tìm hiểu thêm về Protobuf tại đây.

Chọn phiên bản phù hợp với hệ điều hành bạn đang sử dụng, mở của sổ dòng lệnh trên thiết bị của bạn, điều hướng đến thư mục bạn chứa ứng dụng, dán và thực thi câu lệnh dưới đây:

cd models/research \ wget -O protobuf.zip https://github.com/protocolbuffers/protobuf/releases/download/v3.9.1/protoc-3.9.1-osx-x86_64.zip \ unzip protobuf.zip

Ghi nhớ: hãy chắc chắn rằng Protobuf.zip đã được giải nén trong thư mục models/research.

Bước 4: Khởi động biên dịch Protobuf.

Chạy dòng lệnh dưới đây trong thư mục research/ để biên dịch Protocol Buffer.

./bin/protoc object_detection/protos/*.proto --python_out=.

Triển khai Object Detection trong Python.


Bây giờ chúng ta đã có tất cả các thư viện và các thành phần phụ thuộc, việc còn lại là triển khai OD (phát hiện đối tượng) trên Python

Trong thư mục chứa ứng dụng đã được tải về trên git server (guthub), mở thư mục models/research/object_detection, trong thư mục này bạn sẽ thấy một file ghi chú của Python được đặt tên là object_detection_tutorial.ipynb, file này chưa Demo của OB khi thực thi sẽ sử dụng đối tượng chỉ định:

ssd_mobilenet_v1_coco_2017_11_17 là mẫu được dùng để phân loại hai hình ảnh thử nghiệm được cung cấp trong "kho lưu trữ" (repository được đã được clone về thiết bị của bạn) hay thư mục chúng ta giải nén khi tải về trên git server nếu bạn chưa cài đặt git.

Và dây là kết quả chạy thử:

Thêm một số thay đổi nhỏ để chúng ta phát hiện các đối tượng từ video phát trực tiếp (livestream). Tạo một sổ ghi chép Jupyter (Jupyter notebook) mới trong cùng một thư mục và làm theo đoạn mã mình đưa ra dưới đây để làm được "chuyện đó".

[1]:

import numpy as np
import os
import six.moves.urllib as urllib
import sys
import tarfile
import tensorflow as tf
import zipfile
from distutils.version import StrictVersion
from collections import defaultdict
from io import StringIO
from matplotlib import pyplot as plt
from PIL import Image
# This is needed since the notebook is stored in the object_detection folder.
sys.path.append("..")
from utils import ops as utils_ops
if StrictVersion(tf.__version__) < StrictVersion('1.12.0'):
    raise ImportError('Please upgrade your TensorFlow installation to v1.12.*.')
[2]:
# This is needed to display the images.
get_ipython().run_line_magic('matplotlib', 'inline')
[3]:
# Object detection imports
# Here are the imports from the object detection module.
from utils import label_map_util
from utils import visualization_utils as vis_util

[4]:
# Model preparation 
# Any model exported using the `export_inference_graph.py` tool can be loaded here simply by changing `PATH_TO_FROZEN_GRAPH` to point to a new .pb file.
# By default we use an "SSD with Mobilenet" model here. 
#See https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
#for a list of other models that can be run out-of-the-box with varying speeds and accuracies.
# 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_FROZEN_GRAPH = 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('data', 'mscoco_label_map.pbtxt')

[5]:
#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())

[6]:
# 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_FROZEN_GRAPH, 'rb') as fid:
        serialized_graph = fid.read()
        od_graph_def.ParseFromString(serialized_graph)
        tf.import_graph_def(od_graph_def, name='')

[7]:
# Loading label map
# Label maps map indices to category names, so that when our convolution network predicts `5`,
#we know that this corresponds to `airplane`.  Here we use internal utility functions, 
#but anything that returns a dictionary mapping integers to appropriate string labels would be fine
category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS, use_display_name=True)

[8]:
def run_inference_for_single_image(image, graph):
    with graph.as_default():
        with tf.Session() as sess:
            # Get handles to input and output tensors
            ops = tf.get_default_graph().get_operations()
            all_tensor_names = {output.name for op in ops for output in op.outputs}
            tensor_dict = {}
            for key in [
                  'num_detections', 'detection_boxes', 'detection_scores',
                  'detection_classes', 'detection_masks']:
                tensor_name = key + ':0'
                if tensor_name in all_tensor_names:
                    tensor_dict[key] = tf.get_default_graph().get_tensor_by_name(tensor_name)
            if 'detection_masks' in tensor_dict:
                # The following processing is only for single image
                detection_boxes = tf.squeeze(tensor_dict['detection_boxes'], [0])
                detection_masks = tf.squeeze(tensor_dict['detection_masks'], [0])
                # Reframe is required to translate mask from box coordinates to image coordinates and fit the image size.
                real_num_detection = tf.cast(tensor_dict['num_detections'][0], tf.int32)
                detection_boxes = tf.slice(detection_boxes, [0, 0], [real_num_detection, -1])
                detection_masks = tf.slice(detection_masks, [0, 0, 0], [real_num_detection, -1, -1])
                detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks(
                detection_masks, detection_boxes, image.shape[1], image.shape[2])
                detection_masks_reframed = tf.cast(
                tf.greater(detection_masks_reframed, 0.5), tf.uint8)
                # Follow the convention by adding back the batch dimension
                tensor_dict['detection_masks'] = tf.expand_dims(
                                    detection_masks_reframed, 0)
            image_tensor = tf.get_default_graph().get_tensor_by_name('image_tensor:0')
            # Run inference
            output_dict = sess.run(tensor_dict, feed_dict={image_tensor: image})
            # all outputs are float32 numpy arrays, so convert types as appropriate
            output_dict['num_detections'] = int(output_dict['num_detections'][0])
            output_dict['detection_classes'] = output_dict[
                      'detection_classes'][0].astype(np.int64)
            output_dict['detection_boxes'] = output_dict['detection_boxes'][0]
            output_dict['detection_scores'] = output_dict['detection_scores'][0]
            if 'detection_masks' in output_dict:
                output_dict['detection_masks'] = output_dict['detection_masks'][0]
        return output_dict

[9]:
import cv2
cam = cv2.cv2.VideoCapture(0)
rolling = True
while (rolling):
    ret, image_np = cam.read()
    image_np_expanded = np.expand_dims(image_np, axis=0)
    # Actual detection.
    output_dict = run_inference_for_single_image(image_np_expanded, detection_graph)
    # Visualization of the results of a detection.
    vis_util.visualize_boxes_and_labels_on_image_array(
      image_np,
      output_dict['detection_boxes'],
      output_dict['detection_classes'],
      output_dict['detection_scores'],
      category_index,
      instance_masks=output_dict.get('detection_masks'),
      use_normalized_coordinates=True,
      line_thickness=8)
    cv2.imshow('image', cv2.resize(image_np,(1000,800)))
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break
        cv2.destroyAllWindows()
        cam.release()

Ghi chú: Khi bạn chạy Jupyter notebook, webcam hệ thống sẽ mở ra và sẽ phát hiện tất cả các lớp đối tượng mà mô hình ban đầu đã được "đào tạo" để phát hiện. 

Kết luận

Chúng ta vừa tìm hiểu về Object Detection phát hiển đối tượng cùng với Python, Tensorflow, Protocol Buffer. Hẹn gặp lại các bạn trong các bài viết sắp tới trong seri "Sành điệu cùng Python" của mình. 

Thanks for Reading!

Dung Tran - Push Manager / Software Engineer

Nguồn: tham khảo internet