Tìm Hiểu Mask R-CNN Và Ví Dụ Phân Vùng Quả Bóng Bay Sử Dụng Deep Learning

Bắt đầu

Đầu tiên, chúng ta sẽ download tập dataset balloon tại https://github.com/matterport/Mask_RCNN/releases/download/v2.1/balloon_dataset.zip, giải nén và bỏ trong thư mục datasets. Tiếp đó, các bạn donwload file balloon.py và visualize.py về. File đầu tiên hỗ trợ chúng ta đọc dữ liệu của dataset balloon và file thứ hai hỗ trợ visualize hình ảnh một cách trực quan. Cả hai file mình đều lấy mã nguồn của Matterport trên https://github.com/matterport/Mask_RCNN/ Tiến hành import các thư viện cần thiết về.

 1import os
 2import sys
 3import itertools
 4import math
 5import logging
 6import json
 7import re
 8import random
 9from collections import OrderedDict
10import numpy as np
11import matplotlib
12import matplotlib.pyplot as plt
13import matplotlib.patches as patches
14import matplotlib.lines as lines
15from matplotlib.patches import Polygon
16
17
18import balloon
19import utils
20import visualize
21
22config = balloon.BalloonConfig()
23BALLOON_DIR = "datasets/balloon"

Thông tin của tập train bao gồm

 1dataset = balloon.BalloonDataset()
 2dataset.load_balloon(BALLOON_DIR, "train")
 3
 4# Must call before using the dataset
 5dataset.prepare()
 6
 7print("Image Count: {}".format(len(dataset.image_ids)))
 8print("Class Count: {}".format(dataset.num_classes))
 9for i, info in enumerate(dataset.class_info):
10    print("{:3}. {:50}".format(i, info['name']))
1Image Count: 61
2Class Count: 2
3  0. BG
4  1. balloon

Vậy là có tổng cộng 61 hình train. Dữ liệu được đánh làm 2 nhãn, một nhãn là background, một nhãn là balloon.

Visualize dữ liệu

Chúng ta sẽ load một vài hình lên xem người ta đã mask dữ liệu như thế nào. Ở đây, với mỗi hình ảnh, mình sẽ load 1 hình gốc và 4 hình của 4 quả bóng tương ứng trong hình, nếu trong hình có nhiều hơn 4 quả bóng thì chỉ vẽ 4 quả bóng đầu tiên

 1
 2
 3n_col = 5
 4
 5# Load and display random samples
 6fig, axs = plt.subplots(nrows=4, ncols=n_col, figsize=(9.3, 6),subplot_kw={'xticks': [], 'yticks': []})
 7fig.subplots_adjust(left=0.03, right=0.97, hspace=0.3, wspace=0.05)
 8image_ids = np.random.choice(dataset.image_ids, 4)
 9# for image_id in image_ids:
10# for ax, image_id in zip(axs.flat, image_ids):
11
12for index in range(0,4):
13    image_id = image_ids[index]
14
15    image = dataset.load_image(image_id)
16    mask, class_ids = dataset.load_mask(image_id)
17    print(mask.shape)
18    print(len(class_ids))
19
20    axs.flat[index*n_col].imshow(image)
21    axs.flat[index*n_col].set_title('img')
22
23    for sub_index in range(0,len(class_ids)):
24        if sub_index >= n_col:
25            break
26        axs.flat[index*n_col +1 + sub_index].imshow(mask[:,:,sub_index])
27        axs.flat[index*n_col + 1+sub_index].set_title(str(dataset.class_names[class_ids[sub_index]]))
28
29
30plt.tight_layout()
31plt.show()

Hình ảnh

Các bạn có thể sử dụng hàm display_top_masks của tác giả Mask R-CNN để xem thử, hàm của họ hơi khác của mình một chút.

1
2image_ids = np.random.choice(dataset.image_ids, 4)
3for image_id in image_ids:
4    image = dataset.load_image(image_id)
5    mask, class_ids = dataset.load_mask(image_id)
6    visualize.display_top_masks(image, mask, class_ids, dataset.class_names)

Hình ảnh

Bounding Boxes

Chúng ta có 2 cách để lấy Bounding Boxes của các hình. Một là lấy trực tiếp từ tập dataset (đối với những dataset có lưu bounding box), hai là rút trích bounding box từ các toạ độ mask. Chúng ta nên thực hiện cách hai, lý do là chúng ta sẽ dùng các kỹ thuật Data Generator để sinh nhiều ảnh hơn cung cấp cho thuật toán train. Lúc này, việc tính lại bounding box sẽ dễ dàng hơn.

 1
 2# Load random image and mask.
 3image_id = random.choice(dataset.image_ids)
 4image = dataset.load_image(image_id)
 5mask, class_ids = dataset.load_mask(image_id)
 6
 7# Compute Bounding box
 8bbox = utils.extract_bboxes(mask)
 9
10# Display image and additional stats
11print("image_id ", image_id, dataset.image_reference(image_id))
12
13# Display image and instances
14visualize.display_instances(image, bbox, mask, class_ids, dataset.class_names)

Hình ảnh

Resize Images

Các ảnh trong tập train có các kích thước khác nhau. Các bạn có thể xem các hình ở trên, có ảnh có kích thước này, có ảnh có kích thước kia. Chúng ta sẽ resize chúng về cùng một kích thước (ví dụ 1024x1024) để làm đầu vào cho tập huấn luyện. Và chúng ta sẽ sử dụng zero padding để lấp đầy những khoảng trống của những ảnh không đủ kích thước.

 1
 2
 3
 4# Load random image and mask.
 5image_id = np.random.choice(dataset.image_ids, 1)[0]
 6image = dataset.load_image(image_id)
 7mask, class_ids = dataset.load_mask(image_id)
 8original_shape = image.shape
 9# Resize
10image, window, scale, padding, _ = utils.resize_image(
11    image,
12    min_dim=config.IMAGE_MIN_DIM,
13    max_dim=config.IMAGE_MAX_DIM,
14    mode=config.IMAGE_RESIZE_MODE)
15mask = utils.resize_mask(mask, scale, padding)
16# Compute Bounding box
17bbox = utils.extract_bboxes(mask)
18
19# Display image and additional stats
20print("image_id: ", image_id, dataset.image_reference(image_id))
21print("Original shape: ", original_shape)
22print("Resize shape: ", image.shape)
23# Display image and instances
24visualize.display_instances(image, bbox, mask, class_ids, dataset.class_names)

Kết quả

1image_id:  9 datasets/balloon\train\15290896925_884ab33fd3_k.jpg
2Original shape:  (1356, 2048, 3)
3Resize shape:  (1024, 1024, 3)

Hình ảnh

Lưu ý một điều là ở đây, mình sử dụng random image, nên nếu các bạn chạy lại câu lệnh như mình thì kết quả ra phần nhiều sẽ khác mình. Tuy nhiên, Resize shape luôn là (1024, 1024, 3).

Mini Masks

Một vấn đề khá nghiêm trọng ở đây là chúng ta cần khá nhiều bộ nhớ để lưu các masks. Numpy sử dụng 1 byte để lưu 1 giá trị bit. Do đó, với kích thước ảnh là 1024x1024, chúng ta cần 1MB bộ nhớ ram để lưu trữ. Nếu chúng ta có tập dataset tầm 1000 bức ảnh thì cần đến 1GB bộ nhớ, khá là lớn. Ngoài việc tốn bộ nhớ lữu trữ, chúng còn làm chậm tốc độ huấn luyện mô hình nữa.

Để cải tiến, chúng ta có thể sử dụng một trong hai cách sau:

  • Cách thứ nhất: Thay vì lưu toàn bộ mask của toàn bức ảnh, chúng ta chỉ lưu những pixel của mask trong bounding box. Với việc sử dụng cách này, chúng ta sẽ tiết kiệm kha khá bộ nhớ chính.
  • Cách thứ hai: Chúng ta có thể resize mask về một kích thước chuẩn nào đó, ví dụ 48x48 pixel. Với những mask có kích thước lớn hơn 48x48, chúng sẽ bị mất thông tin.

Mình không thích cách thứ hai cho lắm. Tuy nhiên, theo lý giải của nhóm tác giả Mask R-CNN, thì hầu hết việc gán các đường biên (object annotations) thường không chính xác cho lắm (thừa hoặc thiếu một vài chỗ), cho nên, việc mất mát thông tin với lượng nhỏ này hầu như là không đáng kể.

Để đánh giá hiệu quả của hàm mask resizing, chúng ta sẽ chạy đoạn code bên dưới và xem ảnh kết quả. Đoạn code trên mình sử dụng 2 hàm compose_image_meta và load_image_gt của tác giả ở đường dẫn https://github.com/matterport/Mask_RCNN/blob/master/mrcnn/model.py. Mình có modify lại hàm load_image_gt một chút để hợp với ý mình hơn.

  1##############################
  2#  Data Formatting
  3##############################
  4
  5def compose_image_meta(image_id, original_image_shape, image_shape,
  6                       window, scale, active_class_ids):
  7    """Takes attributes of an image and puts them in one 1D array.
  8    image_id: An int ID of the image. Useful for debugging.
  9    original_image_shape: [H, W, C] before resizing or padding.
 10    image_shape: [H, W, C] after resizing and padding
 11    window: (y1, x1, y2, x2) in pixels. The area of the image where the real
 12            image is (excluding the padding)
 13    scale: The scaling factor applied to the original image (float32)
 14    active_class_ids: List of class_ids available in the dataset from which
 15        the image came. Useful if training on images from multiple datasets
 16        where not all classes are present in all datasets.
 17    """
 18    meta = np.array(
 19        [image_id] +                  # size=1
 20        list(original_image_shape) +  # size=3
 21        list(image_shape) +           # size=3
 22        list(window) +                # size=4 (y1, x1, y2, x2) in image cooredinates
 23        [scale] +                     # size=1
 24        list(active_class_ids)        # size=num_classes
 25    )
 26    return meta
 27
 28
 29def load_image_gt(dataset, config, image_id, augment=False, augmentation=None,
 30                  use_mini_mask=False):
 31    """Load and return ground truth data for an image (image, mask, bounding boxes).
 32    augment: (deprecated. Use augmentation instead). If true, apply random
 33        image augmentation. Currently, only horizontal flipping is offered.
 34    augmentation: Optional. An imgaug (https://github.com/aleju/imgaug) augmentation.
 35        For example, passing imgaug.augmenters.Fliplr(0.5) flips images
 36        right/left 50% of the time.
 37    use_mini_mask: If False, returns full-size masks that are the same height
 38        and width as the original image. These can be big, for example
 39        1024x1024x100 (for 100 instances). Mini masks are smaller, typically,
 40        224x224 and are generated by extracting the bounding box of the
 41        object and resizing it to MINI_MASK_SHAPE.
 42    Returns:
 43    image: [height, width, 3]
 44    shape: the original shape of the image before resizing and cropping.
 45    class_ids: [instance_count] Integer class IDs
 46    bbox: [instance_count, (y1, x1, y2, x2)]
 47    mask: [height, width, instance_count]. The height and width are those
 48        of the image unless use_mini_mask is True, in which case they are
 49        defined in MINI_MASK_SHAPE.
 50    """
 51    # Load image and mask
 52    image = dataset.load_image(image_id)
 53    mask, class_ids = dataset.load_mask(image_id)
 54    original_shape = image.shape
 55    image, window, scale, padding, crop = utils.resize_image(
 56        image,
 57        min_dim=config.IMAGE_MIN_DIM,
 58        min_scale=config.IMAGE_MIN_SCALE,
 59        max_dim=config.IMAGE_MAX_DIM,
 60        mode=config.IMAGE_RESIZE_MODE)
 61    mask = utils.resize_mask(mask, scale, padding, crop)
 62
 63    # Random horizontal flips.
 64    # TODO: will be removed in a future update in favor of augmentation
 65    if augment:
 66        logging.warning("'augment' is deprecated. Use 'augmentation' instead.")
 67        if random.randint(0, 1):
 68            image = np.fliplr(image)
 69            mask = np.fliplr(mask)
 70
 71    # Augmentation
 72    # This requires the imgaug lib (https://github.com/aleju/imgaug)
 73    if augmentation:
 74        import imgaug
 75
 76        # Augmenters that are safe to apply to masks
 77        # Some, such as Affine, have settings that make them unsafe, so always
 78        # test your augmentation on masks
 79        MASK_AUGMENTERS = ["Sequential", "SomeOf", "OneOf", "Sometimes",
 80                           "Fliplr", "Flipud", "CropAndPad",
 81                           "Affine", "PiecewiseAffine"]
 82
 83        def hook(images, augmenter, parents, default):
 84            """Determines which augmenters to apply to masks."""
 85            return augmenter.__class__.__name__ in MASK_AUGMENTERS
 86
 87        # Store shapes before augmentation to compare
 88        image_shape = image.shape
 89        mask_shape = mask.shape
 90        # Make augmenters deterministic to apply similarly to images and masks
 91        det = augmentation.to_deterministic()
 92        image = det.augment_image(image)
 93        # Change mask to np.uint8 because imgaug doesn't support np.bool
 94        mask = det.augment_image(mask.astype(np.uint8),
 95                                 hooks=imgaug.HooksImages(activator=hook))
 96        # Verify that shapes didn't change
 97        assert image.shape == image_shape, "Augmentation shouldn't change image size"
 98        assert mask.shape == mask_shape, "Augmentation shouldn't change mask size"
 99        # Change mask back to bool
100        mask = mask.astype(np.bool)
101
102    # Note that some boxes might be all zeros if the corresponding mask got cropped out.
103    # and here is to filter them out
104    _idx = np.sum(mask, axis=(0, 1)) > 0
105    mask = mask[:, :, _idx]
106    class_ids = class_ids[_idx]
107    # Bounding boxes. Note that some boxes might be all zeros
108    # if the corresponding mask got cropped out.
109    # bbox: [num_instances, (y1, x1, y2, x2)]
110    bbox = utils.extract_bboxes(mask)
111
112    # Active classes
113    # Different datasets have different classes, so track the
114    # classes supported in the dataset of this image.
115    active_class_ids = np.zeros([dataset.num_classes], dtype=np.int32)
116    source_class_ids = dataset.source_class_ids[dataset.image_info[image_id]["source"]]
117    active_class_ids[source_class_ids] = 1
118
119    # Resize masks to smaller size to reduce memory usage
120    if use_mini_mask:
121        if USE_MINI_MASK_SHAPE:
122            mask = utils.minimize_mask(bbox, mask, MINI_MASK_SHAPE)
123        else:
124            mask = utils.minimize_mask(bbox, mask, mask.shape[:2])
125
126    # Image meta data
127    image_meta = compose_image_meta(image_id, original_shape, image.shape,
128                                    window, scale, active_class_ids)
129
130    return image, image_meta, class_ids, bbox, mask
131
132
133image_id = np.random.choice(dataset.image_ids, 1)[0]
134image, image_meta, class_ids, bbox, mask = load_image_gt(
135    dataset, config, image_id, use_mini_mask=False)
136
137
138visualize.display_images([image]+[mask[:,:,i] for i in range(min(mask.shape[-1], 5))])
139
140image, image_meta, class_ids, bbox, mask = load_image_gt(
141    dataset, config, image_id, use_mini_mask=True)
142
143
144visualize.display_images([image]+[mask[:,:,i] for i in range(min(mask.shape[-1], 5))])
145
146USE_MINI_MASK_SHAPE = True
147
148image, image_meta, class_ids, bbox, mask = load_image_gt(
149    dataset, config, image_id, use_mini_mask=True)
150
151
152visualize.display_images([image]+[mask[:,:,i] for i in range(min(mask.shape[-1], 5))])
153
154mask = utils.expand_mask(bbox, mask, image.shape)
155visualize.display_instances(image, bbox, mask, class_ids, dataset.class_names)

Hình ảnh

Với ảnh ở line 1 là ảnh gốc ban đầu và các full mask của bức ảnh, ảnh ở line 2 là chỉ lấy mask của bounding box, ảnh ở line 3 là lấy mask ở bounding box và scale ảnh (do scale ảnh nên ở line 3 các bạn sẽ thấy mask có hình răng cưa, khác với các mask line 2). Line 4 là ảnh ở line 3 được revert back lại hình gốc ban đầu. Các bạn có để ý thấy rằng nó sẽ bị răng cưa ở biên cạnh chứ không được smooth như ảnh gốc. Nếu chúng ta không làm object annotations kỹ, thì object cũng sẽ bị răng cưa như trên.

Anchors

Thứ tự của các anchor thật sự rất quan trọng. Trong quá trình train, thứ tự của các anchor như thế nào thì trong quá trình test, validation, prediction phải dùng y hệt vậy.

Trong mạng FPN, các anchor phải được xắp xếp theo cách mà chúng ta có thể dễ dàng liên kết với giá trị output

  • Xắp xếp các anchor theo thứ tự các lớp của pyramid. Tất cả các anchor của level đầu tiên, tiếp theo là các anchor của các lớp thứ hai, lớp thư ba… Việc xắp xếp theo cách này sẽ giúp chúng ta dễ dàng phân tách các lớp anchor và dễ hiểu theo lẽ tự nhiên.

  • Trong mỗi level, xắp xếp các anchor trong mỗi level bằng thứ tự xử lý của các feature map. Thông thường, một convolution layer sẽ dịch chuyển trên feature map bắt đầu từ vị trí trái - trên (top - left) đi xuống phải dưới (từ trái qua phải, xuống hàng rồi lại từ trái qua phải).

  • Trên mỗi cell của feature map, chúng ta sẽ xắp xếp các anchor theo các ratios.

Anchor Stride:

 1
 2backbone_shapes = modellib.compute_backbone_shapes(config, config.IMAGE_SHAPE)
 3anchors = utils.generate_pyramid_anchors(config.RPN_ANCHOR_SCALES,
 4                                          config.RPN_ANCHOR_RATIOS,
 5                                          backbone_shapes,
 6                                          config.BACKBONE_STRIDES,
 7                                          config.RPN_ANCHOR_STRIDE)
 8
 9# Print summary of anchors
10num_levels = len(backbone_shapes)
11anchors_per_cell = len(config.RPN_ANCHOR_RATIOS)
12print("Total anchors: ", anchors.shape[0])
13print("ANCHOR Scales: ", config.RPN_ANCHOR_SCALES)
14print("BACKBONE STRIDE: ", config.BACKBONE_STRIDES)
15print("ratios: ", config.RPN_ANCHOR_RATIOS)
16print("Anchors per Cell: ", anchors_per_cell)
17# print("Anchors stride: ", config.RPN_ANCHOR_STRIDE)
18print("Levels: ", num_levels)
19anchors_per_level = []
20for l in range(num_levels):
21    num_cells = backbone_shapes[l][0] * backbone_shapes[l][1]
22    print("backbone_shapes in level ",l,' ',backbone_shapes[l][0],'x',backbone_shapes[l][1])
23    print("num_cells in level ",l,' ',num_cells)
24    anchors_per_level.append(anchors_per_cell * num_cells // config.RPN_ANCHOR_STRIDE**2)
25    print("Anchors in Level {}: {}".format(l, anchors_per_level[l]))
 1Total anchors:  261888
 2ANCHOR Scales:  (32, 64, 128, 256, 512)
 3BACKBONE STRIDE:  [4, 8, 16, 32, 64]
 4ratios:  [0.5, 1, 2]
 5Anchors per Cell:  3
 6Levels:  5
 7backbone_shapes in level  0   256 x 256
 8num_cells in level  0   65536
 9Anchors in Level 0: 196608
10backbone_shapes in level  1   128 x 128
11num_cells in level  1   16384
12Anchors in Level 1: 49152
13backbone_shapes in level  2   64 x 64
14num_cells in level  2   4096
15Anchors in Level 2: 12288
16backbone_shapes in level  3   32 x 32
17num_cells in level  3   1024
18Anchors in Level 3: 3072
19backbone_shapes in level  4   16 x 16
20num_cells in level  4   256
21Anchors in Level 4: 768

Trong kiến trức FPN, feature map tại một số layer đầu tiên là những feature map có độ phân giải lớn. Ví dụ, nếu bức ảnh đầu vào có kích thước là 1024x1024 pixel, và kích thước của mỗi anchor lớp đầu tiên là 32x32 pixel (giá trị đầu tiên của RPN_ANCHOR_SCALES (32, 64, 128, 256, 512)) và bước nhảy (STRIDE) của lớp đầu tiên là 4 (giá trị đầu tiên của BACKBONE_STRIDES ([4, 8, 16, 32, 64])). Từ những dữ kiện này, ta có thể suy ra được là sẽ sinh ra backbone cell có kích thước 256x256 pixel => 256x256 = 65536 anchor. Với mỗi backbone cell, chúng ta thực hiện phép scale với 3 tỷ lệ khác nhau là [0.5, 1, 2], vậy chúng ta có tổng cộng là 65536x3 = 196608 anchor (xấp xỉ 200k anchor). Để ý một điều là kích thước của một anchor là 32x32 pixel, và bước nhảy là 4, cho nên chúng ta sẽ bị chống lấn (overlap) 28 pixel của anchor 1 và anchor 2 ngay sau nó.

Một điều thú vị là, nếu ta tăng bước nhảy lên gấp 2 lần, ví dụ từ 4 pixel lấy một anchor lên 8 pixel lấy một anchor, thì số lượng anchor giảm đi đến 4 lần (196608 anchor ở level 0 so với 49152 anchor ở level 1).

Thử vẽ tất cả các anchor của tất cả các level ở điểm giữa một bức ảnh bức kỳ lên, mỗi một level sẽ dùng một màu khác nhau, chúng ta được một hình như bên dưới.

 1# Visualize anchors of one cell at the center of the feature map of a specific level
 2
 3# Load and draw random image
 4image_id = np.random.choice(dataset.image_ids, 1)[0]
 5image, image_meta, _, _, _ = modellib.load_image_gt(dataset, config, image_id)
 6fig, ax = plt.subplots(1, figsize=(10, 10))
 7ax.imshow(image)
 8levels = len(backbone_shapes)
 9
10kn_color =np.array( [(255,0,0),(0,255,0),(0,0,255),(128,0,0),(0,128,0),(0,0,128)])/255.
11
12for level in range(levels):
13    # colors = visualize.random_colors(levels)
14    colors = kn_color
15    # Compute the index of the anchors at the center of the image
16    level_start = sum(anchors_per_level[:level]) # sum of anchors of previous levels
17    level_anchors = anchors[level_start:level_start+anchors_per_level[level]]
18    print("Level {}. Anchors: {:6}  Feature map Shape: {} ".format(level, level_anchors.shape[0],
19                                                                  backbone_shapes[level]))
20    center_cell = backbone_shapes[level] // 2
21    center_cell_index = (center_cell[0] * backbone_shapes[level][1] + center_cell[1])
22    level_center = center_cell_index * anchors_per_cell
23    center_anchor = anchors_per_cell * (
24        (center_cell[0] * backbone_shapes[level][1] / config.RPN_ANCHOR_STRIDE**2) \
25        + center_cell[1] / config.RPN_ANCHOR_STRIDE)
26    level_center = int(center_anchor)
27
28    # Draw anchors. Brightness show the order in the array, dark to bright.
29    for i, rect in enumerate(level_anchors[level_center:level_center+anchors_per_cell]):
30        y1, x1, y2, x2 = rect
31        p = patches.Rectangle((x1, y1), x2-x1, y2-y1, linewidth=2, facecolor='none',
32                              edgecolor=np.array(colors[level]) / anchors_per_cell)
33        print(i)
34        ax.add_patch(p)
35
36
37plt.show()

Hình ảnh

Nhìn ảnh trên,các bạn phần nào đó mường tượng ra các anchor sẽ như thế nào rồi phải không.

Prediction

Để tiến hành detect vị trí quả bóng và mask của quả bóng, chúng ta download một ảnh small party nhỏ trên internet về và kiểm chứng.

 1
 2import os
 3
 4import tensorflow as tf
 5
 6import cv2
 7
 8DEVICE = "/cpu:0"
 9ROOT_DIR = os.path.abspath("../../")
10MODEL_DIR = os.path.join(ROOT_DIR, "logs")
11# Create model in inference mode
12
13class InferenceConfig(config.__class__):
14    # Run detection on one image at a time
15    GPU_COUNT = 1
16    IMAGES_PER_GPU = 1
17
18config = InferenceConfig()
19config.display()
20
21with tf.device(DEVICE):
22    model = modellib.MaskRCNN(mode="inference", model_dir=MODEL_DIR,
23                              config=config)
24
25
26weights_path = "mask_rcnn_balloon.h5"
27
28# Load weights
29print("Loading weights ", weights_path)
30# model.load_weights(weights_path, by_name=True)
31
32imgpath = "datasets\\balloon\\test\\t1.png"
33# imgpath = "datasets/balloon/val/14898532020_ba6199dd22_k.jpg"
34
35image = cv2.imread(imgpath)
36
37image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
38
39
40
41ds_name = ['BG', 'balloon']
42
43
44results = model.detect([image], verbose=1)
45
46def get_ax(rows=1, cols=1, size=16):
47    """Return a Matplotlib Axes array to be used in
48    all visualizations in the notebook. Provide a
49    central point to control graph sizes.
50
51    Adjust the size attribute to control how big to render images
52    """
53    _, ax = plt.subplots(rows, cols, figsize=(size*cols, size*rows))
54    return ax
55# Display results
56ax = get_ax(1)
57r = results[0]
58visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'],
59                            dataset.class_names, r['scores'], ax=ax,
60                            title="Predictions")
61plt.show()

Hình ảnh

Kết quả nhận dạng khá chính xác phải không các bạn.

Cảm ơn các bạn đã theo dõi. Hẹn gặp bạn ở các bài viết tiếp theo.

Comments