Jetbot & PyTorch CNN（４）ライントレースのデータ収集プログラム

Jetbot にはResNet18を使ったライントレースモデルがあり、それをベースにPyTorchのCNNのモデルに書き換えてみました。カメラ視野も制御に必要な下側25％にトリムして、処理の軽量化を狙います。
ここでの作業はdata_collection_gamepadを基に写真のサイズを56X112に変更します。

Import Libraries（追加）

```ruby:data_collection_gamepad
# IPython Libraries for display and widgets
import traitlets
import ipywidgets
import ipywidgets.widgets as widgets
from IPython.display import display

# Camera and Motor Interface for JetBot
from jetbot import Robot, Camera, bgr8_to_jpeg

# Python basic pakcages for image annotation
from uuid import uuid1
import os
import json
import glob
import datetime
import numpy as np
import cv2
import time
```

Display Live Camera Feed（追加）

```ruby:data_collection_gamepad
camera = Camera()

#image_widget = widgets.Image(format='jpeg', width=224, height=224) #コメントアウト
image_widget1 = widgets.Image(format='jpeg', width=224, height=56) #1/4用を追加
target_widget = widgets.Image(format='jpeg', width=224, height=224)

x_slider = widgets.FloatSlider(min=-1.0, max=1.0, step=0.001, description='x')
y_slider = widgets.FloatSlider(min=-1.0, max=1.0, step=0.001, description='y')

def display_xy(camera_image):
    image = np.copy(camera_image)
    x = x_slider.value
    y = y_slider.value
    x = int(x * 224 / 2 + 112)
    y = int(y * 224 / 2 + 112)
    image = cv2.circle(image, (x, y), 8, (0, 255, 0), 3)
    image = cv2.circle(image, (112, 224), 8, (0, 0,255), 3) #これがCNN範囲です。
    image = cv2.line(image, (x,y), (112,224), (255,0,0), 3)
    jpeg_image = bgr8_to_jpeg(image)
    return jpeg_image

def display_xy1(camera_image): #trim upper part
    image1 = np.copy(camera_image)
    image1 = image1[168:224, 0:224] #これがCNN範囲です。
    #image1 = cv2.rectangle(image1,  (0, 0), (224, 112), (0, 0, 0), thickness=-1)
    jpeg_image1 = bgr8_to_jpeg(image1)
    return jpeg_image1


time.sleep(1)
#traitlets.dlink((camera, 'value'), (image_widget, 'value'), transform=bgr8_to_jpeg) #コメントアウト
traitlets.dlink((camera, 'value'), (image_widget1, 'value'), transform=display_xy1) #修正
traitlets.dlink((camera, 'value'), (target_widget, 'value'), transform=display_xy)

#display(widgets.HBox([image_widget1, target_widget]), x_slider, y_slider)
display(image_widget1, target_widget, x_slider, y_slider)
```

Create Gamepad Controller（変更なし）

```ruby:data_collection_gamepad
controller = widgets.Controller(index=1)   #PCの設定に応じてindex番号を修正してください。
display(controller)

```

Connect Gamepad Controller to Label Images（変更有）
GAMEPADでは使いにくいので2行ともコメントアウトします。ステアリングの値は別にスライダーで設定します。

```ruby:data_collection_gamepad
#widgets.jsdlink((controller.axes[2], 'value'), (x_slider, 'value'))
#widgets.jsdlink((controller.axes[3], 'value'), (y_slider, 'value'))
```


Collect data（変更あり）
作業性を考慮し、ステアリング値をスライダーで変更するようにしました。

```ruby:data_collection_gamepad
DATASET_DIR = 'dataset_xy'

# we have this "try/except" statement because these next functions can throw an error if the directories exist already
try:
    os.makedirs(DATASET_DIR)
except FileExistsError:
    print('Directories not created becasue they already exist')

for b in controller.buttons:
    b.unobserve_all()

count_widget = widgets.IntText(description='count', value=len(glob.glob(os.path.join(DATASET_DIR, '*.jpg'))))

def xy_uuid(x, y):
    return 'xy_%03d_%03d_%s' % (x * 50 + 50, y * 50 + 50, uuid1())

def save_snapshot(change):
    if change['new']:
        uuid = xy_uuid(x_slider.value, y_slider.value)
        image_path = os.path.join(DATASET_DIR, uuid + '.jpg')
        with open(image_path, 'wb') as f:
            f.write(image_widget1.value)
        count_widget.value = len(glob.glob(os.path.join(DATASET_DIR, '*.jpg')))

controller.buttons[5].observe(save_snapshot, names='value')

display(widgets.VBox([
    target_widget,
    count_widget,
    x_slider     #追加、ここでステアリングを設定します。
]))
```

close the camera conneciton properly（変更なし）

```ruby:data_collection_gamepad
camera.stop()
```

zip command（不要）