我正在用C++构建我的第一个基于OpenCV的应用程序.我的目标是构建一个中间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)
我现在可以使用dyanmic libs编译我的c ++应用程序而没有问题,但这会创建一个庞大的docker镜像,我真的希望能够以最小的大小构建独立的二进制文件以进行分发.
正如您所看到的,我正在从源代码编译OpenCV,包括标志BUILD_SHARED_LIBS=OFF
,以确保我获得.a
静态库,而不是.so
动态库.我从一个强烈推荐的构建脚本中提取了一个提示,并修改了它,以便在我使用c ++时省略一些python的东西.
因为我在实际应用程序中遇到了很多麻烦,所以我已经开始创建一个更简单的应用程序,它在编译时也会爆炸.我相信这与包含cflags
和包含有关libs
.这个问题目前无法理解.当我在编译命令中调整单个包含时,我会发现大量错误似乎发生了变化.这是我正在尝试编译的最简单的应用程序.它确实没有做任何事情,但确实包含了一个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> 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
包而不是自己编译它,但似乎你不能这样做并期望使用静态库.
counterbeing.. 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图像要小得多.
实际得到的图像大约是44mb,包括所有静态链接的OpenCV库.对于那些需要小型docker镜像来运行单个C++ bin的人来说,这似乎是一个很好的解决方案.
经过大量的实验,我终于有了一些工作!有一些问题都已解决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图像要小得多.
实际得到的图像大约是44mb,包括所有静态链接的OpenCV库.对于那些需要小型docker镜像来运行单个C++ bin的人来说,这似乎是一个很好的解决方案.