はじめに
できること
docker上でopencv・GPUを使用してdarknet/yolov3を動かせるようになる。
本記事の流れ
以下の流れでdokcer container上でyoloV3が動く環境を作り動かしていきます。
- opencvのインストールとbuild
- darknetのインストールと重みのダウウンロード
- 実行
ホスト環境
前提としてdockerが使える環境であることを想定しています。
- windows Pro11
- Docker Compose version v2.20.2-desktop.1
- GPU: RTX3070
- nvidia-driver: 536.19
コンテナの環境
- OS: ubuntu20.04
- cuda:11.2.0
- cudnn: 8.0
- OpenCV: 4.5.1
- Python3.8
ディレクトリ構造
yolov3
├── Dockerfile
├── docker-compose.yml
└── workspace
└── darknet
├── Makefile
├── cfg
├── darknet
├── darknet.py
├── darknet_images.py
├── darknet_video.py
├── data
├── json_mjpeg_streams.sh
├── predictions.jpg
└── yolov3.weights
docker環境の構築
コンテナの起動
docker-compose.yml
とDockerfile
は以下のようになります。
FROM nvcr.io/nvidia/cuda:11.2.0-cudnn8-devel-ubuntu20.04
ENV DEBIAN_FRONTEND=noninteractive
ARG OPENCV_VERSION="4.5.1"
ARG PYTHON_VERSION="3.8"
# cudaのレポジトリーのキーが変更されたので既存の情報を削除
RUN rm /etc/apt/sources.list.d/cuda.list
# 必要ツール
RUN apt-get update && apt-get -y upgrade &&\
apt-get install -y --no-install-recommends \
vim unzip byobu wget tree git cmake\
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
# pythonのインストール
RUN apt-get update && apt-get install -y python3-dev python3-pip &&\
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
RUN pip3 install -U pip
RUN pip3 install -U numpy
# OpenCVのためのツール
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc g++ libpng-dev libjpeg-dev libopenexr-dev libtiff-dev libwebp-dev libgtk-3-dev \
libavformat-dev libswscale-dev libhdf5-serial-dev qt5-default \
libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libopenblas-dev libgflags-dev &&\
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
# OpenCVのインストール
RUN mkdir /cv
WORKDIR /cv
RUN git clone https://github.com/opencv/opencv.git
RUN git clone https://github.com/opencv/opencv_contrib.git
WORKDIR /cv/opencv_contrib
RUN git checkout -b ${OPENCV_VERSION} refs/tags/${OPENCV_VERSION}
WORKDIR /cv/opencv
RUN git checkout -b ${OPENCV_VERSION} refs/tags/${OPENCV_VERSION}
WORKDIR /cv/opencv/build
# OpenCVのインストール
RUN cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D PYTHON3_PACKAGES_PATH=/usr/local/lib/python3.8/dist-packages \
-D WITH_TBB=ON \
-D ENABLE_FAST_MATH=1 \
-D CUDA_FAST_MATH=1 \
-D WITH_CUBLAS=1 \
-D WITH_CUDA=ON \
-D WITH_CUDNN=ON \
-D BUILD_opencv_cudacodec=OFF \
-D OPENCV_DNN_CUDA=ON \
-D CUDA_ARCH_BIN="7.5, 8.0" \
-D WITH_V4L=ON \
-D WITH_QT=ON \
-D WITH_OPENGL=ON \
-D WITH_GSTREAMER=ON \
-D OPENCV_GENERATE_PKGCONFIG=ON \
-D OPENCV_ENABLE_NONFREE=ON \
-D OPENCV_EXTRA_MODULES_PATH=/cv/opencv_contrib/modules\
-D INSTALL_PYTHON_EXAMPLES=OFF \
-D INSTALL_C_EXAMPLES=OFF \
-D BUILD_opencv_python2=OFF \
-D BUILD_opencv_python3=ON \
-D PYTHON3_EXECUTABLE=/usr/bin/python3\
-D PYTHON3_INCLUDE_DIR=/usr/include/python3.8\
-D PYTHON3_LIBRARY=/usr/lib/libpython3.8.so\
-D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/local/lib/python3.8/dist-packages/numpy/core/include\
-D BUILD_EXAMPLES=OFF .. &&\
make -j10 && make install && ldconfig
このDockerfileではnvidiaのレポジトリからnvidia-driverが入ったレポジトリを取得します。
そして、その上にOpenCVとPythonの環境を構築しています。
OpemCVのインストールではcudaを使ってGPUが使えるようにしてあります。
またopencv_contrib
のインストールやpythonでOpenCVが使えるように設定してあります。
version: "3.2"
services:
ubuntu:
build:
context: .
dockerfile: Dockerfile
container_name: "y3"
volumes:
- /dev/bus/usb:/dev/bus/usb
- ./workspace:/workspace
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=all
tty: true
docker-compose.ymlではコンテナ上でGPUが使えるようにセッティングしてあります。
またコードの書き換えがしやすいようにworkspace
をマウントしています。
コンテナ名はy3
としていますのでお好きな名前に変更していただいて結構です。
yolov3/
以下で
docker compose build
docker compose up
docker exec -it y3 bash
を行いコンテナの中に入れるか確認します。
※ビルドには結構時間がかかります。
OpenCVの確認
コンテナ内でopencvが使えるか確認します。
opencv_version
4.5.1
python
から見えるか確認
python3
Python 3.8.10 (default, May 26 2023, 14:05:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.5.1'
darknetのインストール
コンテナ外のworkspace
以下にgitファイルをクローン
git clone git@github.com:AlexeyAB/darknet.git
darknet
ディレクトリに入り、その中のMakefile
を編集します。
GPU=1
CUDNN=1
CUDNN_HALF=0
OPENCV=1
AVX=0
OPENMP=0
LIBSO=1
ZED_CAMERA=0
ZED_CAMERA_v2_8=0
# set GPU=1 and CUDNN=1 to speedup on GPU
# set CUDNN_HALF=1 to further speedup 3 x times (Mixed-precision on Tensor Cores) GPU: Volta, Xavier, Turing, Ampere, Ada and higher
# set AVX=1 and OPENMP=1 to speedup on CPU (if error occurs then set AVX=0)
# set ZED_CAMERA=1 to enable ZED SDK 3.0 and above
# set ZED_CAMERA_v2_8=1 to enable ZED SDK 2.X
USE_CPP=0
DEBUG=0
ARCH= -gencode arch=compute_50,code=[sm_50,compute_50] \
-gencode arch=compute_52,code=[sm_52,compute_52] \
-gencode arch=compute_61,code=[sm_61,compute_61]
OS := $(shell uname)
# Naming confusion with recent RTX cards.
# "NVIDIA Quadro RTX x000" and T1000/Tx00 are Turing Architecture Family with Compute Capability of 7.5
# "NVIDIA RTX Ax000" are Ampere Architecture Family with Compute Capability of 8.6
# NVIDIA "RTX x000 Ada" are Ada Lovelace Architecture Family with Compute Capability of 8.9
# Source https://developer.nvidia.com/cuda-gpus
# KEPLER, GeForce GTX 770, GTX 760, GT 740
# ARCH= -gencode arch=compute_30,code=sm_30
# MAXWELL, GeForce GTX 950, 960, 970, 980, 980 Ti, "GTX" Titan X
# ARCH= -gencode arch=compute_52,code=sm_52
# Jetson TX1, Tegra X1, DRIVE CX, DRIVE PX, Jetson Nano (2GB, 4GB)
# ARCH= -gencode arch=compute_53,code=[sm_53,compute_53]
# GP100/Tesla P100 - DGX-1
# ARCH= -gencode arch=compute_60,code=sm_60
# PASCAL, GTX 10x0, GTX 10x0 Ti, Titan Xp, Tesla P40, Tesla P4
# ARCH= -gencode arch=compute_61,code=[sm_61,compute_61]
# For Jetson TX2, Jetson Nano TX2 or Drive-PX2 uncomment:
# ARCH= -gencode arch=compute_62,code=[sm_62,compute_62]
# Tesla V100
# ARCH= -gencode arch=compute_70,code=[sm_70,compute_70]
# Jetson XAVIER, XAVIER NX
# ARCH= -gencode arch=compute_72,code=[sm_72,compute_72]
# GeForce Titan RTX, RTX 20x0, RTX 20x0 Ti, Quadro RTX x000, Tesla T4, XNOR Tensor Cores
# ARCH= -gencode arch=compute_75,code=[sm_75,compute_75]
# Tesla A100 (GA100), DGX-A100, A30, A100, RTX 3080
# ARCH= -gencode arch=compute_80,code=[sm_80,compute_80]
# GeForce RTX 30x0, 30x0 Ti, Tesla GA10x, RTX Axxxx, A2, A10, A16, A40
ARCH= -gencode arch=compute_86,code=[sm_86,compute_86]
# NOT TESTED, THEORETICAL
# Jetson ORIN, ORIN NX, ORIN NANO
# ARCH= -gencode arch=compute_87,code=[sm_87,compute_87]
# NOT TESTED, THEORETICAL
# GeForce RTX 4070 Ti, 4080, 4090, L4, L40
# ARCH= -gencode arch=compute_89,code=[sm_89,compute_89]
# NOT TESTED, THEORETICAL
# Nvidia H100
# ARCH= -gencode arch=compute_90,code=[sm_90,compute_90]
VPATH=./src/
EXEC=darknet
OBJDIR=./obj/
ifeq ($(LIBSO), 1)
LIBNAMESO=libdarknet.so
APPNAMESO=uselib
endif
ifeq ($(USE_CPP), 1)
CC=g++
else
CC=gcc
endif
CPP=g++ -std=c++11
NVCC=nvcc
OPTS=-Ofast
LDFLAGS= -lm -pthread
COMMON= -Iinclude/ -I3rdparty/stb/include
CFLAGS=-Wall -Wfatal-errors -Wno-unused-result -Wno-unknown-pragmas -fPIC -rdynamic
ifeq ($(DEBUG), 1)
#OPTS= -O0 -g
#OPTS= -Og -g
COMMON+= -DDEBUG
CFLAGS+= -DDEBUG
else
ifeq ($(AVX), 1)
CFLAGS+= -ffp-contract=fast -mavx -mavx2 -msse3 -msse4.1 -msse4.2 -msse4a
endif
endif
CFLAGS+=$(OPTS)
ifneq (,$(findstring MSYS_NT,$(OS)))
LDFLAGS+=-lws2_32
endif
ifeq ($(OPENCV), 1)
COMMON+= -DOPENCV
CFLAGS+= -DOPENCV
LDFLAGS+= `pkg-config --libs opencv4 2> /dev/null || pkg-config --libs opencv`
COMMON+= `pkg-config --cflags opencv4 2> /dev/null || pkg-config --cflags opencv`
endif
ifeq ($(OPENMP), 1)
ifeq ($(OS),Darwin) #MAC
CFLAGS+= -Xpreprocessor -fopenmp
else
CFLAGS+= -fopenmp
endif
LDFLAGS+= -lgomp
endif
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda/include/
CFLAGS+= -DGPU
ifeq ($(OS),Darwin) #MAC
LDFLAGS+= -L/usr/local/cuda/lib -lcuda -lcudart -lcublas -lcurand
else
LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand
endif
endif
ifeq ($(CUDNN), 1)
COMMON+= -DCUDNN
ifeq ($(OS),Darwin) #MAC
CFLAGS+= -DCUDNN -I/usr/local/cuda/include
LDFLAGS+= -L/usr/local/cuda/lib -lcudnn
else
CFLAGS+= -DCUDNN -I/usr/local/cudnn/include
LDFLAGS+= -L/usr/local/cudnn/lib64 -lcudnn
endif
endif
ifeq ($(CUDNN_HALF), 1)
COMMON+= -DCUDNN_HALF
CFLAGS+= -DCUDNN_HALF
ARCH+= -gencode arch=compute_70,code=[sm_70,compute_70]
endif
ifeq ($(ZED_CAMERA), 1)
CFLAGS+= -DZED_STEREO -I/usr/local/zed/include
ifeq ($(ZED_CAMERA_v2_8), 1)
LDFLAGS+= -L/usr/local/zed/lib -lsl_core -lsl_input -lsl_zed
#-lstdc++ -D_GLIBCXX_USE_CXX11_ABI=0
else
LDFLAGS+= -L/usr/local/zed/lib -lsl_zed
#-lstdc++ -D_GLIBCXX_USE_CXX11_ABI=0
endif
endif
OBJ=image_opencv.o http_stream.o gemm.o utils.o dark_cuda.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o darknet.o detection_layer.o captcha.o route_layer.o writing.o box.o nightmare.o normalization_layer.o avgpool_layer.o coco.o dice.o yolo.o detector.o layer.o compare.o classifier.o local_layer.o swag.o shortcut_layer.o representation_layer.o activation_layer.o rnn_layer.o gru_layer.o rnn.o rnn_vid.o crnn_layer.o demo.o tag.o cifar.o go.o batchnorm_layer.o art.o region_layer.o reorg_layer.o reorg_old_layer.o super.o voxel.o tree.o yolo_layer.o gaussian_yolo_layer.o upsample_layer.o lstm_layer.o conv_lstm_layer.o scale_channels_layer.o sam_layer.o
ifeq ($(GPU), 1)
LDFLAGS+= -lstdc++
OBJ+=convolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o network_kernels.o avgpool_layer_kernels.o
endif
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard src/*.h) Makefile include/darknet.h
all: $(OBJDIR) backup results setchmod $(EXEC) $(LIBNAMESO) $(APPNAMESO)
ifeq ($(LIBSO), 1)
CFLAGS+= -fPIC
$(LIBNAMESO): $(OBJDIR) $(OBJS) include/yolo_v2_class.hpp src/yolo_v2_class.cpp
$(CPP) -shared -std=c++11 -fvisibility=hidden -DLIB_EXPORTS $(COMMON) $(CFLAGS) $(OBJS) src/yolo_v2_class.cpp -o $@ $(LDFLAGS)
$(APPNAMESO): $(LIBNAMESO) include/yolo_v2_class.hpp src/yolo_console_dll.cpp
$(CPP) -std=c++11 $(COMMON) $(CFLAGS) -o $@ src/yolo_console_dll.cpp $(LDFLAGS) -L ./ -l:$(LIBNAMESO)
endif
$(EXEC): $(OBJS)
$(CPP) -std=c++11 $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS)
$(OBJDIR)%.o: %.c $(DEPS)
$(CC) $(COMMON) $(CFLAGS) -c $< -o $@
$(OBJDIR)%.o: %.cpp $(DEPS)
$(CPP) -std=c++11 $(COMMON) $(CFLAGS) -c $< -o $@
$(OBJDIR)%.o: %.cu $(DEPS)
$(NVCC) $(ARCH) $(COMMON) --compiler-options "$(CFLAGS)" -c $< -o $@
$(OBJDIR):
mkdir -p $(OBJDIR)
backup:
mkdir -p backup
results:
mkdir -p results
setchmod:
chmod +x *.sh
.PHONY: clean
clean:
rm -rf $(OBJS) $(EXEC) $(LIBNAMESO) $(APPNAMESO)
GPU=1
CUDNN=1
OPENCV=1
LIBSO=1
をそれぞれ1にして使えるようにします。
また、ARCH=
のところのコメントアウトを外します。
このPCはGTX3070なので
# GeForce RTX 30x0, 30x0 Ti, Tesla GA10x, RTX Axxxx, A2, A10, A16, A40
のところのコメントアウトを外しました。
そのあとコンテナ内に入りdarknet/
で下のコマンドを実行します。
make
YoloV3の実行
以下はコンテナ内で実行します。
初めに重みをダウンロードします。
今回はYoloV3なので以下のコマンドでV3用の重みをdarknet/
にダウンロードします。
wget https://pjreddie.com/media/files/yolov3.weights
証明書エラーが出たときは--no-check-certificate
をつけて回避します。
続いて以下のコマンドでYoloV3を実行します。
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
実行するとpredictions.jpg
に結果が出力されますので
こんな感じの写真が出ればOKです。
darknet/data/
には他にもデモ写真が含まれています。
python
で実行する場合は
python3 darknet_images.py --input data/giraffe.jpg --weights yolov3.weights --dont_show --config_file cfg/yolov3.cfg --data_file cfg/coco.data
watch -n 0.1 nvidia-smi
で動作を見てるとちゃんとGPUが動いているのがわかります。
'''
参考文献
- https://qiita.com/wakaba130/items/3d215cbf62b7de4100e1
- https://www.rootf-datum.com/blog/2022/05/11/nvidia%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92docker%E3%81%AB%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AF/
- https://qiita.com/usk81/items/98e54e2463e9d8a11415