Хабрахабр

Ночью спит спокойно мама — мы собираем OpenCV для Raspbian’a

Выпускали OpenCV 4, а вместе с ним готовились к Intel's OpenVINO toolkit R4, в состав которого входит OpenCV. Последние пару недель были непростыми для нашей команды. Думаешь, отвлекусь на время, посмотрю, как обычно, форумы про OpenCV, да комментарии пользователей, и тут на тебе, модно стало говорить что OpenCV не IoT, что под Raspberry Pi собрать — припоя не хватает, что на ночь make -j2 ставить — утром будет готово, если повезёт.

Колдовство Кросс-компиляция, не иначе! Поэтому предлагаю дружно взяться за руки и посмотреть, как же можно собирать библиотеку OpenCV для 32-битной операционной системы, исполняемой на ARM процессоре, используя ресурсы машины с 64-битной OS, движимой отличной архитектурой CPU.

Постановка задачи

Более того, обе машины могут иметь разные архитектуры CPU. Компиляция непосредственно на микроконтроллере, принято названная нативной, действительно трудоёмка, поэтому мы здесь рассмотрим такой способ сборки проекта, который позволяет более сильным вычислительным устройствам (назовём их хостами) подготавливать бинарники для своих малых сородичей. Это и есть кросс-компиляция.

Итак, для приготовления малинового пирога с начинкой из OpenCV нам потребуются:

  • Тушка docker образа Ubuntu 16.04
  • Хостовая машина мощнее Raspberry Pi (иначе какой смысл, isn't it?)
  • Кросс-компилятор под ARMhf, а так же библиотеки соответствующей архитектуры

У себя я использую Ubuntu. Весь процесс сборки OpenCV будет происходить именно на хостовой машине. Для пользователей Windows — мои искренние пожелания не сдаваться и попробовать разобраться самим. С другой версией Linux проблем с воcпроизведением возникнуть не должно.

Установка Docker

Нам же с тобой хватит трёх ингредиентов — Dockerfile, понятие образа и контейнера. Своё знакомство с docker начал около недели назад, поэтому гурманам — соль и синтаксический сахар добавлять по вкусу.

Dockerfile — это набор shell команд, которые вы обычно используете на хостовой машине, но в данном случае — все они применяются к так называемому docker образу. Сам по себе docker — инструмент по созданию и воспроизведению конфигурации любой операционной системы с необходимым набором компонент.

Для того, чтобы поставить docker, рассмотрим самый простой способ: закажем пакет через сервис доставки apt-get:

sudo apt-get install -y docker.io

Дадим docker демону всё, что он попросит и сделаем logout из системы (замет login соответственно).

sudo usermod -a -G docker $USER

Подготавливаем рабочее пространство

Мы же создадим docker образ на основе Ubuntu 16. Raspberry Pi (в моём случае RPI 2 Model B) в самом распространнёном приготовлении — это ARMv7 CPU с операционной системой Raspbian (Debian based). 04, в которую доложим кросс-компилятор, армовые библиотеки и там же соберём OpenCV.

Создаём папочку, где будет лежать наш Dockerfile:

mkdir ubuntu16_armhf_opencv && cd ubuntu16_armhf_opencv
touch Dockerfile

Добавим информацию о базовой OS и armhf архитектуру для установщика пакетов apt-get:

FROM ubuntu:16.04 USER root RUN dpkg --add-architecture armhf RUN apt-get update

Обратите внимание, команды типа FROM ..., RUN ... — это синтаксис docker и пишутся в созданном тестовом файле Dockerfile.

Вернёмся в родительскую директорию ubuntu16_armhf_opencv и попробуем создать наш docker образ:

docker image build ubuntu16_armhf_opencv

Во время выполнения команды apt-get update вам должно повести увидеть ошибки следующего рода: Err:[число] [url] xenial[чего-нибудь] armhf Packages

Ign:30 http://archive.ubuntu.com/ubuntu xenial-backports/main armhf Packages
Ign:32 http://archive.ubuntu.com/ubuntu xenial-backports/universe armhf Packages
Err:7 http://archive.ubuntu.com/ubuntu xenial/main armhf Packages 404 Not Found
Ign:9 http://archive.ubuntu.com/ubuntu xenial/restricted armhf Packages
Ign:18 http://archive.ubuntu.com/ubuntu xenial/universe armhf Packages
Ign:20 http://archive.ubuntu.com/ubuntu xenial/multiverse armhf Packages
Err:22 http://archive.ubuntu.com/ubuntu xenial-updates/main armhf Packages 404 Not Found
Ign:24 http://archive.ubuntu.com/ubuntu xenial-updates/restricted armhf Packages
Ign:26 http://archive.ubuntu.com/ubuntu xenial-updates/universe armhf Packages
Ign:28 http://archive.ubuntu.com/ubuntu xenial-updates/multiverse armhf Packages
Err:30 http://archive.ubuntu.com/ubuntu xenial-backports/main armhf Packages 404 Not Found
Ign:32 http://archive.ubuntu.com/ubuntu xenial-backports/universe armhf Packages

Если подсмотреть в файлик /etc/apt/sources.list то каждой такой ошибке соответствует какая-то строчка, например:

Ошибка

Err:22 http://archive.ubuntu.com/ubuntu xenial-updates/main armhf Packages 404 Not Found

Строчка в /etc/apt/sources.list:

deb http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted

Решение:
Разбить на две:

deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted
deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted

В нашем докере мы заменим их все одной командой: Таким образом придётся заменить несколько источников пакетов.

RUN sed -i -E 's|^deb ([^ ]+) (.*)$|deb [arch=amd64] \1 \2\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ \2|' /etc/apt/sources.list

Теперь apt-get update должен отработать без ошибок.

Ставим необходимые пакеты

Нам необходимо поставить хостовые пакеты такие как git, python-pip, cmake и pkg-config, а так же crossbuild-essential-armhf, что есть набор из gcc/g++ кросс-компиляторов (arm-linux-gnueabihf-gcc и arm-linux-gnueabihf-g++) и системных библиотек соответствующей архитектуры:

RUN apt-get install -y git python-pip cmake pkg-config crossbuild-essential-armhf

Из необычного — мы так же скачаем GTK (используется для рисования окошек в модуле highgui), GStreamer и Python, но уже с явным указанием инородной архитектуры:

RUN apt-get install -y --no-install-recommends \ libgtk2.0-dev:armhf \ libpython-dev:armhf \ libgstreamer1.0-dev:armhf \ libgstreamer-plugins-base1.0-dev:armhf \ libgstreamer-plugins-good1.0-dev:armhf \ libgstreamer-plugins-bad1.0-dev:armhf

A дальше — клонируем и собираем, указывая нужные флаги:

RUN git clone https://github.com/opencv/opencv --depth 1
RUN mkdir opencv/build && cd opencv/build && \ export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig && \ cmake -DCMAKE_BUILD_TYPE=Release \ -DOPENCV_CONFIG_INSTALL_PATH="cmake" \ -DCMAKE_TOOLCHAIN_FILE="../opencv/platforms/linux/arm-gnueabi.toolchain.cmake" \ -DWITH_IPP=OFF \ -DBUILD_TESTS=OFF \ -DBUILD_PERF_TESTS=OFF \ -DOPENCV_ENABLE_PKG_CONFIG=ON \ -DPYTHON2_INCLUDE_PATH="/usr/include/python2.7" \ -DPYTHON2_NUMPY_INCLUDE_DIRS="/usr/local/lib/python2.7/dist-packages/numpy/core/include" \ -DENABLE_NEON=ON \ -DCPU_BASELINE="NEON" ..

где

  • CMAKE_TOOLCHAIN_FILE — путь к cmake файлу, который определяет процесс кросс-компиляции (выставляет нужный компилятор, ограничивает использование хостовых библиотек.

  • WITH_IPP=OFF, — отключаем тяжеловесные зависимости.

  • BUILD_TESTS=OFF, BUILD_PERF_TESTS=OFF, отключаем сборку тестов.

  • PKG_CONFIG_PATH — правильный путь, где pkg-config будет искать библиотеки. OPENCV_ENABLE_PKG_CONFIG=ON — чтобы pkg-config смог найти такие зависимости как GTK.

  • PYTHON2_INCLUDE_PATH, PYTHON2_NUMPY_INCLUDE_DIRS — пути, необходимые для кросс-компиляции обёрток для python2.

  • ENABLE_NEON=ON, CPU_BASELINE="NEON" — разрешаем NEON оптимизации.

  • OPENCV_CONFIG_INSTALL_PATH — регулирует расположение файлов в install директории.

Главное, на что стоит обратить внимание после исполнения cmake, что все необходимые модули собираются (python2, например):

-- OpenCV modules:
-- To be built: calib3d core dnn features2d flann gapi highgui imgcodecs imgproc java_bindings_generator ml objdetect photo python2 python_bindings_generator stitching ts video videoio
-- Disabled: world
-- Disabled by dependency: -
-- Unavailable: java js python3
-- Applications: tests perf_tests apps
-- Documentation: NO
-- Non-free algorithms: NO

а необходимые зависимости, такие как GTK, нашлись:

-- GUI:
-- GTK+: YES (ver 2.24.30)
-- GThread : YES (ver 2.48.2)
-- GtkGlExt: NO
--
-- Video I/O:
-- GStreamer: -- base: YES (ver 1.8.3)
-- video: YES (ver 1.8.3)
-- app: YES (ver 1.8.3)
-- riff: YES (ver 1.8.3)
-- pbutils: YES (ver 1.8.3)
-- v4l/v4l2: linux/videodev2.h

Остаётся только вызвать make, make install и дождаться окончания сборки:

Successfully built 4dae6b1a7d32

Изпользуйте этот id образа для того, чтобы поставить тег и создать контейнер:

docker tag 4dae6b1a7d32 ubuntu16_armhf_opencv:latest
docker run ubuntu16_armhf_opencv

Сперва подсмотрим идентификатор созданного контейнера: А нам осталось выкачать собранную OpenCV из контейнера.

$ docker container ls --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e94667fe60d2 ubuntu16_armhf_opencv "/bin/bash" 6 seconds ago Exited (0) 5 seconds ago clever_yalow

И скопируем install директорию с установленной OpenCV:

docker cp e94667fe60d2:/opencv/build/install/ ./
mv install ocv_install

Накрываем на стол

Копируем ocv_install на Raspberry Pi, устанавливаем пути и пробуем запустить OpenCV из питона.

export LD_LIBRARY_PATH=/path/to/ocv_install/lib/:$LD_LIBRARY_PATH
export PYTHONPATH=/path/to/ocv_install/python/:$PYTHONPATH

Запустим пример по детектированию, используя нейронную сеть MobileNet-SSD из https://github.com/chuanqi305/MobileNet-SSD:

import cv2 as cv
print cv.__file__ classes = ['backgroud', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']
cap = cv.VideoCapture(0) net = cv.dnn.readNet('MobileNetSSD_deploy.caffemodel', 'MobileNetSSD_deploy.prototxt') cv.namedWindow('Object detection', cv.WINDOW_NORMAL) while cv.waitKey(1) != 27: hasFrame, frame = cap.read() if not hasFrame: break frame_height, frame_width = frame.shape[0], frame.shape[1] blob = cv.dnn.blobFromImage(frame, scalefactor=0.007843, size=(300, 300), mean=(127.5, 127.5, 127.5)) net.setInput(blob) out = net.forward() for detection in out.reshape(-1, 7): classId = int(detection[1]) confidence = float(detection[2]) xmin = int(detection[3] * frame_width) ymin = int(detection[4] * frame_height) xmax = int(detection[5] * frame_width) ymax = int(detection[6] * frame_height) if confidence > 0.5: cv.rectangle(frame, (xmin, ymin), (xmax, ymax), color=(255, 0, 255), thickness=3) label = '%s: %.2f' % (classes[classId], confidence) labelSize, baseLine = cv.getTextSize(label, cv.FONT_HERSHEY_SIMPLEX, 0.5, 1) ymin = max(ymin, labelSize[1]) cv.rectangle(frame, (xmin, ymin - labelSize[1]), (xmin + labelSize[0], ymin + baseLine), (255, 0, 255), cv.FILLED) cv.putText(frame, label, (xmin, ymin), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0)) cv.imshow('Object detection', frame)

Прикладываю финальный вариант Dockerfile ниже и пользуясь случаем предлагаю пройти небольшой опрос от команды OpenCV для тех кто когда-то имел опыт работы с библиотекой: https://opencv.org/survey-2018.html. Вот и всё, полная сборка занимает не более 20 минут.

Это не просто работа отдельной команды, это работа всего комьюнити — OpenCV 4 you. И да, поздравляю с OpenCV 4!

FROM ubuntu:16.04 USER root RUN dpkg --add-architecture armhf RUN sed -i -E 's|^deb ([^ ]+) (.*)$|deb [arch=amd64] \1 \2\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ \2|' /etc/apt/sources.list RUN apt-get update && \ apt-get install -y --no-install-recommends \ cmake \ pkg-config \ crossbuild-essential-armhf \ git \ python-pip \ libgtk2.0-dev:armhf \ libpython-dev:armhf \ libgstreamer1.0-dev:armhf \ libgstreamer-plugins-base1.0-dev:armhf \ libgstreamer-plugins-good1.0-dev:armhf \ libgstreamer-plugins-bad1.0-dev:armhf RUN pip install numpy==1.12.1 RUN git clone https://github.com/opencv/opencv --depth 1
RUN mkdir opencv/build && cd opencv/build && \ export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig && \ cmake -DCMAKE_BUILD_TYPE=Release \ -DOPENCV_CONFIG_INSTALL_PATH="cmake" \ -DCMAKE_TOOLCHAIN_FILE="../opencv/platforms/linux/arm-gnueabi.toolchain.cmake" \ -DWITH_IPP=OFF \ -DBUILD_TESTS=OFF \ -DBUILD_PERF_TESTS=OFF \ -DOPENCV_ENABLE_PKG_CONFIG=ON \ -DPYTHON2_INCLUDE_PATH="/usr/include/python2.7" \ -DPYTHON2_NUMPY_INCLUDE_DIRS="/usr/local/lib/python2.7/dist-packages/numpy/core/include" \ -DENABLE_NEON=ON \ -DCPU_BASELINE="NEON" .. && make -j4 && make install

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть