我正在用C ++构建我的第一个基于OpenCV的应用程序。我的目标是构建一个中间docker映像,该映像可以静态编译应用程序,以便它可以在生成的较小映像中独立运行。我愿意在此步骤中使用任何docker映像,但只是为了使您可以准确地看到我所拥有的,这里是可重现整个环境的dockerfile:
FROM ubuntu:18.04 as compiler
ENV OPENCV_VERSION='3.4.2' DEBIAN_FRONTEND=noninteractive
RUN apt-get -y update && \
apt-get -y upgrade && \
apt-get -y dist-upgrade && \
apt-get -y autoremove && \
apt-get install -y build-essential cmake
RUN apt-get install -y qt5-default libvtk6-dev
RUN apt-get install -y zlib1g-dev libjpeg-dev libwebp-dev libpng-dev libtiff5-dev libopenexr-dev libgdal-dev
RUN apt-get install -y libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev yasm libopencore-amrnb-dev libopencore-amrwb-dev libv4l-dev libxine2-dev
RUN apt-get install -y unzip wget
RUN wget --progress=dot:giga https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip && \
unzip -q ${OPENCV_VERSION}.zip && \
rm ${OPENCV_VERSION}.zip && \
mv opencv-${OPENCV_VERSION} OpenCV && \
cd OpenCV && \
mkdir build && \
cd build && \
cmake \
-D BUILD_SHARED_LIBS=OFF \
-D WITH_QT=ON \
-D WITH_OPENGL=ON \
-D FORCE_VTK=ON \
-D WITH_TBB=ON \
-D WITH_GDAL=ON \
-D WITH_XINE=ON \
-D BUILD_EXAMPLES=OFF \
-D ENABLE_PRECOMPILED_HEADERS=OFF \
-D BUILD_DOCS=OFF \
-D BUILD_PERF_TESTS=OFF \
-D BUILD_TESTS=OFF \
-D BUILD_opencv_apps=OFF \
.. && \
make -j4 && \
make install && \
ldconfig
COPY compile-test.cpp compile-test.cpp
RUN g++ -std=c++11 -static compile-test.cpp -o /app $(pkg-config --cflags --libs opencv)
我目前可以使用强力库来编译我的c ++应用程序,而不会出现问题,但这会创建一个庞大的docker映像,我真的希望能够以最小的大小构建独立的二进制文件进行分发。
如您所见,我正在从源代码编译OpenCV,其中包括标志BUILD_SHARED_LIBS=OFF
,以确保我获得了.a
静态库,而不是.so
动态库。我从highly recommended build script那里得到了一个提示,并对其进行了修改,以便与docker一起使用,从而在我使用c ++时省略了一些python内容。
因为我的实际应用程序遇到了很多麻烦,所以我继续前进并创建了一个更简单的应用程序,该应用程序在编译过程中也会崩溃。我认为这与所包含的cflags
和libs
有关。目前,这个问题超出了我的理解范围。当我调整编译命令中的一个include时,我会得到很多似乎会改变的错误。这是我尝试编译的最简单的应用程序。它确实没有任何作用,但是它确实包含一个lib。
#include "opencv2/imgcodecs.hpp"
using namespace cv;
Mat img;
int main( int argc, char** argv ) {
img = cv::imread( argv[1], IMREAD_COLOR );
}
然后我尝试像这样编译它:
g++ -std=c++11 -static compile-test.cpp -o /app $(pkg-config --cflags --libs opencv)
它最终会陷入一堆错误,时间太长而无法完全粘贴到这里。
//usr/local/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): In function `cv::JpegEncoder::write(cv::Mat const&, std::vector<int, std::allocator<int> > const&)':
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0xf8): undefined reference to `jpeg_CreateCompress'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x105): undefined reference to `jpeg_std_error'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x2b5): undefined reference to `jpeg_set_defaults'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x2d0): undefined reference to `jpeg_set_quality'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x2fe): undefined reference to `jpeg_quality_scaling'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x30d): undefined reference to `jpeg_quality_scaling'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x367): undefined reference to `jpeg_default_qtables'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x379): undefined reference to `jpeg_start_compress'
grfmt_jpeg.cpp:
...
collect2: error: ld returned 1 exit status
opencv-dev
软件包而不是自己编译它,但是看来您无法做到这一点,并且期望使用静态库。答案 0 :(得分:5)
经过大量的实验,我终于找到了可行的方法! Dockerfile
中解决了一些问题。为了重现此内容,请在以下目录中创建一个具有以下内容的Dockerfile,并使用上述问题中的简单代码在同一文件夹中创建另一个名为app.cpp
的文件。
我将解释以下问题:
FROM alpine:3.8 as compiler
RUN echo -e '@edgunity http://nl.alpinelinux.org/alpine/edge/community \
@edge http://nl.alpinelinux.org/alpine/edge/main \
@testing http://nl.alpinelinux.org/alpine/edge/testing \
@community http://dl-cdn.alpinelinux.org/alpine/edge/community' \
>> /etc/apk/repositories
RUN apk add --update --no-cache \
build-base \
openblas-dev \
unzip \
wget \
cmake \
g++ \
libjpeg \
libjpeg-turbo-dev \
libpng-dev \
jasper-dev \
tiff-dev \
libwebp-dev \
clang-dev \
linux-headers
ENV CC /usr/bin/clang
ENV CXX /usr/bin/g++
ENV OPENCV_VERSION='3.4.2' DEBIAN_FRONTEND=noninteractive
RUN mkdir /opt && cd /opt && \
wget https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip && \
unzip ${OPENCV_VERSION}.zip && \
rm -rf ${OPENCV_VERSION}.zip
RUN mkdir -p /opt/opencv-${OPENCV_VERSION}/build && \
cd /opt/opencv-${OPENCV_VERSION}/build && \
cmake \
-D BUILD_DOCS=OFF \
-D BUILD_EXAMPLES=OFF \
-D BUILD_opencv_apps=OFF \
-D BUILD_opencv_python2=OFF \
-D BUILD_opencv_python3=OFF \
-D BUILD_PERF_TESTS=OFF \
-D BUILD_SHARED_LIBS=OFF \
-D BUILD_TESTS=OFF \
-D CMAKE_BUILD_TYPE=RELEASE \
-D ENABLE_PRECOMPILED_HEADERS=OFF \
-D FORCE_VTK=OFF \
-D WITH_FFMPEG=OFF \
-D WITH_GDAL=OFF \
-D WITH_IPP=OFF \
-D WITH_OPENEXR=OFF \
-D WITH_OPENGL=OFF \
-D WITH_QT=OFF \
-D WITH_TBB=OFF \
-D WITH_XINE=OFF \
-D BUILD_JPEG=ON \
-D BUILD_TIFF=ON \
-D BUILD_PNG=ON \
.. && \
make -j$(nproc) && \
make install && \
rm -rf /opt/opencv-${OPENCV_VERSION}
RUN wget --progress=dot:giga https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.0-linux-x86-64.tar.gz && \
pwd && \
tar -xzf libwebp-1.0.0-linux-x86-64.tar.gz && \
mv /libwebp-1.0.0-linux-x86-64/lib/libwebp.a /usr/lib && \
rm -rf /libwebp*
RUN wget --progress=dot:giga http://www.ece.uvic.ca/~frodo/jasper/software/jasper-2.0.10.tar.gz && \
tar -xzf jasper-2.0.10.tar.gz && \
cd jasper-2.0.10 && \
mkdir BUILD && \
cd BUILD && \
cmake -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_SKIP_INSTALL_RPATH=YES \
-DCMAKE_INSTALL_DOCDIR=/usr/share/doc/jasper-2.0.10 \
-DJAS_ENABLE_SHARED=FALSE \
.. && \
make install && \
rm -rf /jasper-2.0.10*
ENV PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig:/usr/lib/pkgconfig
COPY app.cpp app.cpp
RUN g++ -Wl,-Bstatic -static-libgcc -std=c++11 \
app.cpp \
-o /app \
$(pkg-config --cflags --libs -static opencv) \
-lgfortran -lquadmath
FROM alpine
COPY --from=compiler /app /bin/app
确实存在不需要链接的文件,这有两个原因:
pkg-config
命令发出所有编译所需的标志,但是在我较早的尝试中,我没有将-static
标志包括到pkg-config
中。添加-static
标志时,请确保链接所需的额外软件包。我看到一些人遇到了添加-pthread
之类的额外标志的解决方案,但是我发现-static
标志对我有用,因此更可取。ld: cannot find -lgcc_s
错误。通过在-static-libgcc
上添加g++
标志可以解决此问题。对于我来说,其中有些仍然是个谜。我想将两个库作为静态库包含在内,这些库需要从apk
以外的来源获取。它们是libjasper
和libwebp
。上面有一些构建步骤,可以根据需要进行获取和构建,并将资源复制到所需位置。
出于某种原因,我仍无法解释pkg-config
没有提供最后两个必要的标志。分别是-lgfortran
和-lquadmath
。
我转而使用Alpine Linux,只是因为我已经读到一些人在此方面取得了成功,所以我确信使用Ubuntu也可以做到。它的确缩小了图像,所以我喜欢这样。中间映像大约为900mb,虽然很大,但比1.9GB Ubuntu映像小得多。
包括所有静态链接的OpenCV库,实际生成的图像约为44mb。对于那些需要小的docker映像来运行单个C ++ bin的用户来说,这似乎是一个不错的解决方案。