YOLO Usage on Windows

  • 컴파일, Linux에서의 사용법은 다루지 않았습니다.
  • AlexeyAB의 darknet을 참고하여 작성했습니다.
  • 순서를 실제로 사용하면서 보게 되는 순서대로 바꿨습니다.
  • 최근 수정 : 2019년 4월 2일

Pre-trained models Download

How to Train

  • 쓰려고 하는 cfg 파일을 수정한다.
    • batch=64
    • subdivision=8
    • heightwidth는 32의 배수로 크면 클수록 정확도가 높다.
    • classes=을 검색해서 자신의 class 갯수로 수정한다.
    • classes=을 검색했을 때, 위에 나오는 filters= 역시 수정해야하는데, 그 값은 (classes+5)*3이다.
    • 다른 해상도에 대한 정확도를 높이려면 파일의 맨아래 random=1로 수정한다.
    • Small Object(416*416으로 Resizing 했을때 16*16보다 작은 경우)라면 Line #720에 layers = -1, 11, Line #717에 stride=4로 수정한다.
    • anchors=를 수정한다.
      • anchors 계산 : darknet.exe detector calc_anchors data/obj.data -num_of_clusters 9 -width 416 -height 416
    • 좌우 구별 감지를 원하면 Line #17 flip=0을 입력한다.
  • data 파일을 만들어야 한다.

    classes = 3
    train = EXAMPLE1/train.txt
    valid = EXAMPLE1/test.txt
    names = EXAMPLE1/obj.names
    backup = EXAMPLE1/backup/  # 중간 weights를 저장하는 경로
  • names 파일을 만들어야한다.

    • class 0, 1, 2 ... 이름을 적는다
      bird
      dog
      cat
  • Training 및 Testing 시에 쓸 이미지 파일을 build\darknet\x64\EXAMPLE1에 저장한다.

    • 물론 이 이미지들은 Bounding Box처리가 되어 있어야한다.
    • 다음 링크에서 marking을 하는 툴을 받을 수 있다. Box 작업에는 큰 인내와 끈기가 필요하다.
  • 이미지 리스트의 상대 경로가 적혀있는 txt 파일을 생성해야한다.

    • 이미지가 3개이고 경로가 darknet.exe가 있는 디렉토리에 있는 EXAMPLE1에 있는 경우 train.txt가 아래와 같은 내용이여야한다.

      EXAMPLE/img1.jpg
      EXAMPLE/img2.jpg
      EXAMPLE/img3.jpg
  • darknet.exe detector train .data .cfg .weights로 Training 시킬 수 있다.

    • Training 시에 Loss-Window를 띄우지 않으려면 -dont_show 옵션을 설정하면 된다.
  • Training 후에는 아래 명령어로 어느 weights가 어느 정도의 성능을 보이는지 확인할 수 있다.

    darknet.exe detector map .data .cfg .weights
  • mAP-chart

    • darknet.exe detector train .data .cfg .weights -map

Usage (After Training)

  • https://github.com/AlexeyAB/darknet#how-to-use-on-the-command-line
  • cfg 파일에서 heightwidth를 늘린다.(608 or 832: 32의 배수로)
    • Out of memory 오류가 난다면 subdivision을 16, 32, 64 등으로 증가시킨다.
  • Image : darknet.exe detector test .data .cfg .weights -thresh THRESH OPTION
    • OPTION
      • Output coordinates : -ext_output
      • Use GPU 1 : -i 1
      • List of Image에 대한 결과 저장 : -thresh 0.25 -dont_show -save_labels < list.txt
  • Video : darknet.exe detector demo .data .cfg .weights .videofile OPTION
    • OPTION
      • WebCam 0 : -c 0
      • Net-videocam : http://192.168.0.80:8080/video?dummy=param.mjpg
      • Save result : -out_filename OUT.videofile
  • Check accuracy mAP@IoU=75 : darknet.exe detector map .data .cfg .weights -iou_thresh 0.75

Using Android smartphone(Network Video-Camera)

  1. 어플 다운로드
  1. WiFi 혹은 USB로 컴퓨터와 스마트폰 연결
  2. 어플 실행
  3. darknet.exe detector demo .data .cfg .weights http://192.168.0.80:8080/video?dummy=param.mjpg -i 0으로 실행

YOLO in other frameworks

YOLO Build on Windows

  • CUDA, cuDNN, OpenCV, VS 2017을 사용합니다.
  • AlexyAB의 darknet을 이용했습니다.

빌드하기 전 준비사항

  • VS 2017은 설치되어있다는 가정하에 진행하겠습니다.

    • Visual Studio Installer에서 데스크톱용 VC++ 2015.3 v14.00(v140) 도구집합 먼저 설치 하시고 진행하셔야 합니다(CUDA 설치 전에 해야하기 때문).
      Image
  • 위 github에서 repository를 Downlaoad 받습니다. 이 경로를 darknet이라고 부르겠습니다.

  • CUDA와 cuDNN을 설치하셔야 하는데, 버전을 기억해두셔야 합니다.

    • CUDA : https://developer.nvidia.com/cuda-downloads
      • Visual Studio Integration 체크 해서 설치해야합니다.
        Image
      • 자신의 CUDA 버전에 맞는 cuDNN을 다운받고 압축을 푼 후 CUDA가 설치된 경로에 붙여넣기 하면 됩니다. CUDA와 똑같은 경로를 cudnn이라는 이름으로 환경변수 등록까지 마쳐야합니다.
  • 다음으로 OpenCV를 다운받습니다.

    • OpenCV Download 링크에서 들어가서 다운받습니다.
    • 저는 2.4.13.6 버전을 다운받았습니다.
  • OpenCV를 설치한 경로는 환경변수에 아래와 같이 등록해줘야합니다.

    • 제 opencv 경로는 C:\opencv\opencv2.4.13.6\으로 시작합니다.

빌드

  • darknet\build\darknet\darknet.sln을 실행합니다.
  • 먼저 상단의 메뉴에서 Release, x64로 바꿔줍니다.
    Image
  • 프로젝트 속성 - C/C++ - 일반 - 추가 포함 디렉터리에서 opencv 경로를 자신의 경로에 맞게 수정합니다.

Image

  • 프로젝트 속성 - 링커 - 일반 - 추가 라이브러리 디렉터리에서 opencv 경로를 수정합니다.

Image

  • 빌드 종속성을 자신의 CUDA 버전에 맞게 수정합니다.

Image

Image

  • 빌드(F7)를 누르면 끝입니다!

Yolo-mark로 Image에 bounding box 그리기 on Windows

  • Yolo_mark github 페이지를 참고했습니다.
  • OpenCV, VS 2017을 사용합니다.

빌드

  • 먼저 OpenCV를 다운받습니다.

  • OpenCV Download 링크에서 들어가서 다운받습니다.

    • 저는 2.4.13.6 버전을 다운받았습니다.
  • OpenCV를 설치한 경로는 환경변수에 아래와 같이 등록해줘야합니다.
    Image

  • 위 github에서 repository를 Downlaoad 받습니다.

  • yolo_mark.sln을 실행합니다.

  • 프로젝트 속성에서 아래를 수정합니다.

Image

  • opencv\build\include 추가

Image

  • 링커에 opencv\build\x64\vc14\lib 추가

  • Debug -> Release 모드로 바꾼뒤 빌드를 합니다.

  • \x64\Release\yolo_mark.cmd를 실행합니다.

  • Box를 그릴 Image를 \x64\Release\data\img 폴더에 넣고, Own Dataset을 위해\x64\Release\data\obj.names를 수정해야합니다.

Tensorflow-gpu 설치 및 Jupyter 등록 on Windows

  • Windows, Cuda 9.0, cudnn 7.4, Anaconda는 설치되어 있는 환경에서 진행합니다.
  • Anacomda prompt를 실행합니다.
  • 새 가상환경을 만들기 위해 아래를 입력합니다. tf는 가상환경의 이름이고 python version은 3.6으로 했습니다.
    • conda create -n tf pip python=3.6
  • 가상환경을 활성화시킵니다.
    • activate tf or source activate tf
  • pip를 업그레이드 시킵니다.
    • python -m pip install --upgrade pip
  • tensorflow-gpu를 설치합니다.
    • pip install --ignore-installed --upgrade tensorflow-gpu
  • Jupyter에 등록하기 위해 아래 명령을 입력합니다.
    • conda install notebook ipykernel
    • python -m ipykernel install --user --name tf --display-name "Tensorflow"
  • 환경 확인 : conda info --envs
  • conda 환경 삭제 : conda remove --name ENV_NAME --all
  • Jupyter에서 커널 삭제 : jupyter kernelspec uninstall tf

'머신러닝 > 기타' 카테고리의 다른 글

[PyTorch] PyTorch 설치 및 Jupyter 등록  (0) 2018.11.11

Darkflow 설치 및 사용법 on Windows

  • 이 github 페이지를 참조했습니다.

  • Anaconda, tensorflow-gpu, opencv3, numpy가 설치되어 있는 환경에서 진행했습니다.

  • Python 3.5가 권장된다고 합니다. 저는 3.6.5에서 진행했습니다.

  • 선택사항으로 가상 환경(virtualenv, conda, pipenv)에서 진행해도 된다고 하는데, 저는 아직 가상 환경에 대한 체감이 없어서 그냥 base env에서 진행했습니다.

사전 준비

  • pip3 install --upgrade tensorflow
  • pip3 install cython
  • pip3 install opencv-python

repo Download

  • Darkflow github Page에서 repo를 다운로드 받고 그 디렉토리에서 다음을 실행합니다.
    • python setup.py build_ext --inplace
    • python flow --h
    • Microsoft Visual C++ 14.0 is required error가 생기는 경우 이 곳을 참조해서 해보세요. 저는 해당되지 않아서 자세히는 잘 모르겠습니다.

시작 방법

  • 3가지 방법이 있다.
    • python setup.py build_ext --inplace
      • 이 방법을 쓰면 ./flow로 명령을 실행해야한다고 한다.
    • pip install -e .
    • pip install .
  • 나는 세번째가 제일 짧아서 세번째걸로 했다. 별 문제 없이 됐다.

학습 방법(Training on your own dataset)

  • cfg 디렉토리에서 cfg 파일을 복사해서 원하는 대로 이름을 변경한다.(Original cfg를 건드리지 않는것이 좋다.)

  • 복사한 cfg 파일의 [region] 부분 밑에 class 수를 원하는대로 변경한다. 나는 일단 신호등 하나만 할거라서 1로 했다.

    [region]
    anchors =  0.57273, 0.677385, 1.87446, 2.06253, 3.33843, 5.47434, 7.88282, 3.52778, 9.77052, 9.16828
    bias_match=1
    classes=1
    coords=4
    num=5
    softmax=1
    jitter=.3
    rescore=1
  • 바로 위의 [convolutional] 부분을 수정해야한다. filters 값을 num * (class + 5)로 한다. 나는 num이 5고 class는 1이어서 5 * (1 + 5) = 30으로 바꿨다.

    [convolutional]
    size=1
    stride=1
    pad=1
    filters=30
    activation=linear
  • labels.txt에 class명을 적는다.

    traffic light
  • Train Command 예시

    • bash
      • python flow --model cfg/my.cfg --train --load yolo.weights --dataset data/train_traffic_light --annotation data/annotations --gpu 1.0
    • cmd
      • python flow --model cfg\my.cfg --train --load yolo.weights --dataset data\train_traffic_light --annotation data\annotations --gpu 1.0
  • Argument

    • --train : 학습 시킬때
    • --load : weights 파일 Load, -1을 주면 ckpt/checkpoint에서 가장 최근걸 불러온다.
    • --trainer adam : Adam optimizer로 완전히 새로운 weights 파일로 시작.
    • --model : cfg 파일 경로
    • --annotation : xml 파일 경로
    • --dataset : 학습 image 파일 경로
    • --demo : video test시 video 파일 경로 입력
    • --imgdir : image test시 image 파일 경로 입력
  • defaults.py

    • 수정할 수 있는 부분이 많다. 추후 업데이트.

YOLO Annotation (txt) to VOC Annotation (xml)

YOLO 학습때 사용한 annotation 파일은 txt 형식이고, 간결하다.

darkflow로 학습하려니 annotation 형식이 달라서 학습이 되지 않았다.

xml 형식이 필요하대서 찾아보니까 txt에 비해 많이 복잡했다.

하나하나 다시 박스 처리를 해주는건 미친짓이기에 txt 파일을 xml로 변환했다.

나와 같은 어려움을 겪는 사람이 있을게 분명하기에 코드를 공유해봅니다..

convert.py

from lxml import etree
from PIL import Image
import csv
import os

# fw is txt file that composed train image file path

IMG_PATH = "D:/__Project__/darkflow-master/data/train_traffic_light"
fw = os.listdir(IMG_PATH)
# path of save xml file
save_path = 'D:/__Project__/darkflow-master/data/annotations/'

# txt_folder is txt file root that using darknet rectbox
txt_folder = 'D:/__Project__/darkflow-master/data/train_traffic_light_TXT'

# edit ypur label set
labels = ['traffic light']


def csvread(fn):
    with open(fn, 'r') as csvfile:
        list_arr = []
        reader = csv.reader(csvfile, delimiter=' ')

        for row in reader:
            list_arr.append(row)
    return list_arr


def convert_label(txt_file):
    if((txt_file[0]) == str(0)):
        label = 'traffic light'

    return label

# core code = convert the yolo txt file to the x_min,x_max...


def extract_coor(txt_file, img_width, img_height):
    x_rect_mid = float(txt_file[1])
    y_rect_mid = float(txt_file[2])
    width_rect = float(txt_file[3])
    height_rect = float(txt_file[4])

    x_min_rect = ((2 * x_rect_mid * img_width) - (width_rect * img_width)) / 2
    x_max_rect = ((2 * x_rect_mid * img_width) + (width_rect * img_width)) / 2
    y_min_rect = ((2 * y_rect_mid * img_height) -
                  (height_rect * img_height)) / 2
    y_max_rect = ((2 * y_rect_mid * img_height) +
                  (height_rect * img_height)) / 2

    return x_min_rect, x_max_rect, y_min_rect, y_max_rect


for line in fw:
    root = etree.Element("annotation")

    # try debug to check your path
    img_style = IMG_PATH.split('/')[-1]
    img_name = line
    image_info = IMG_PATH + "/" + line
    img_txt_root = txt_folder + "/" + line[:-4]
    txt = ".txt"

    txt_path = img_txt_root + txt
    txt_file = csvread(txt_path)
    ######################################

    # read the image  information
    img_size = Image.open(image_info).size

    img_width = img_size[0]
    img_height = img_size[1]
    img_depth = Image.open(image_info).layers
    ######################################

    folder = etree.Element("folder")
    folder.text = "%s" % (img_style)

    filename = etree.Element("filename")
    filename.text = "%s" % (img_name)

    path = etree.Element("path")
    path.text = "%s" % (IMG_PATH)

    source = etree.Element("source")
    ##################source - element##################
    source_database = etree.SubElement(source, "database")
    source_database.text = "Unknown"
    ####################################################

    size = etree.Element("size")
    ####################size - element##################
    image_width = etree.SubElement(size, "width")
    image_width.text = "%d" % (img_width)

    image_height = etree.SubElement(size, "height")
    image_height.text = "%d" % (img_height)

    image_depth = etree.SubElement(size, "depth")
    image_depth.text = "%d" % (img_depth)
    ####################################################

    segmented = etree.Element("segmented")
    segmented.text = "0"

    root.append(folder)
    root.append(filename)
    root.append(path)
    root.append(source)
    root.append(size)
    root.append(segmented)

    for ii in range(len(txt_file)):

        label = convert_label(txt_file[ii][0])
        x_min_rect, x_max_rect, y_min_rect, y_max_rect = extract_coor(
            txt_file[ii], img_width, img_height)

        object = etree.Element("object")
        ####################object - element##################
        name = etree.SubElement(object, "name")
        name.text = "%s" % (label)

        pose = etree.SubElement(object, "pose")
        pose.text = "Unspecified"

        truncated = etree.SubElement(object, "truncated")
        truncated.text = "0"

        difficult = etree.SubElement(object, "difficult")
        difficult.text = "0"

        bndbox = etree.SubElement(object, "bndbox")
        #####sub_sub########
        xmin = etree.SubElement(bndbox, "xmin")
        xmin.text = "%d" % (x_min_rect)
        ymin = etree.SubElement(bndbox, "ymin")
        ymin.text = "%d" % (y_min_rect)
        xmax = etree.SubElement(bndbox, "xmax")
        xmax.text = "%d" % (x_max_rect)
        ymax = etree.SubElement(bndbox, "ymax")
        ymax.text = "%d" % (y_max_rect)
        #####sub_sub########

        root.append(object)
        ####################################################

    file_output = etree.tostring(root, pretty_print=True, encoding='UTF-8')
    # print(file_output.decode('utf-8'))
    ff = open('%s%s.xml' % (save_path, img_name[:-4]), 'w', encoding="utf-8")
    ff.write(file_output.decode('utf-8'))

PyTorch 설치 및 Jupyter 등록

  • 환경은 Windows 10, Anaconda를 사용하고 있습니다.

  • conda create -y -n pytorch ipykernel

  • activate pytorch

  • PyTorch 링크를 보고 자신한테 맞는 환경을 골라 명령어를 입력한다.

  • conda install pytorch cuda90 -c pytorch

  • pip install torchvision

  • 설치가 완료되면 예제 코드를 다운받아 실행 시킨다.

  • python tensor_tutorial.py

  • 제대로 실행되면 pytorch 설치가 완료된 것이다.

  • 주피터에 등록을 하기 위해선 다음 명령을 입력한다.

    • python -m ipykernel install --user --name pytorch --display-name "PyTorch"

tensor_tutorial.py

# -*- coding: utf-8 -*-
"""
What is PyTorch?
================

It’s a Python-based scientific computing package targeted at two sets of
audiences:

-  A replacement for NumPy to use the power of GPUs
-  a deep learning research platform that provides maximum flexibility
   and speed

Getting Started
---------------

Tensors
^^^^^^^

Tensors are similar to NumPy’s ndarrays, with the addition being that
Tensors can also be used on a GPU to accelerate computing.
"""

from __future__ import print_function
import torch

###############################################################
# Construct a 5x3 matrix, uninitialized:

x = torch.empty(5, 3)
print(x)

###############################################################
# Construct a randomly initialized matrix:

x = torch.rand(5, 3)
print(x)

###############################################################
# Construct a matrix filled zeros and of dtype long:

x = torch.zeros(5, 3, dtype=torch.long)
print(x)

###############################################################
# Construct a tensor directly from data:

x = torch.tensor([5.5, 3])
print(x)

###############################################################
# or create a tensor based on an existing tensor. These methods
# will reuse properties of the input tensor, e.g. dtype, unless
# new values are provided by user

x = x.new_ones(5, 3, dtype=torch.double)      # new_* methods take in sizes
print(x)

x = torch.randn_like(x, dtype=torch.float)    # override dtype!
print(x)                                      # result has the same size

###############################################################
# Get its size:

print(x.size())

###############################################################
# .. note::
#     ``torch.Size`` is in fact a tuple, so it supports all tuple operations.
#
# Operations
# ^^^^^^^^^^
# There are multiple syntaxes for operations. In the following
# example, we will take a look at the addition operation.
#
# Addition: syntax 1
y = torch.rand(5, 3)
print(x + y)

###############################################################
# Addition: syntax 2

print(torch.add(x, y))

###############################################################
# Addition: providing an output tensor as argument
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)

###############################################################
# Addition: in-place

# adds x to y
y.add_(x)
print(y)

###############################################################
# .. note::
#     Any operation that mutates a tensor in-place is post-fixed with an ``_``.
#     For example: ``x.copy_(y)``, ``x.t_()``, will change ``x``.
#
# You can use standard NumPy-like indexing with all bells and whistles!

print(x[:, 1])

###############################################################
# Resizing: If you want to resize/reshape tensor, you can use ``torch.view``:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)  # the size -1 is inferred from other dimensions
print(x.size(), y.size(), z.size())

###############################################################
# If you have a one element tensor, use ``.item()`` to get the value as a
# Python number
x = torch.randn(1)
print(x)
print(x.item())

###############################################################
# **Read later:**
#
#
#   100+ Tensor operations, including transposing, indexing, slicing,
#   mathematical operations, linear algebra, random numbers, etc.,
#   are described
#   `here <http://pytorch.org/docs/torch>`_.
#
# NumPy Bridge
# ------------
#
# Converting a Torch Tensor to a NumPy array and vice versa is a breeze.
#
# The Torch Tensor and NumPy array will share their underlying memory
# locations, and changing one will change the other.
#
# Converting a Torch Tensor to a NumPy Array
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

a = torch.ones(5)
print(a)

###############################################################
#

b = a.numpy()
print(b)

###############################################################
# See how the numpy array changed in value.

a.add_(1)
print(a)
print(b)

###############################################################
# Converting NumPy Array to Torch Tensor
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# See how changing the np array changed the Torch Tensor automatically

import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)

###############################################################
# All the Tensors on the CPU except a CharTensor support converting to
# NumPy and back.
#
# CUDA Tensors
# ------------
#
# Tensors can be moved onto any device using the ``.to`` method.

# let us run this cell only if CUDA is available
# We will use ``torch.device`` objects to move tensors in and out of GPU
if torch.cuda.is_available():
    device = torch.device("cuda")          # a CUDA device object
    y = torch.ones_like(x, device=device)  # directly create a tensor on GPU
    x = x.to(device)                       # or just use strings ``.to("cuda")``
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # ``.to`` can also change dtype together!

+ Recent posts