已解决:使用 OpenCV 和 libmagic 创建适用于 MacOS 的独立可执行文件

问题描述 投票:0回答:1

我目前正在尝试将我的程序编译为独立的。 我使用的是 MacOS M2,目标操作系统也是 MacOS M2。我只想将可执行文件从一台 Mac 拖放到另一台 Mac 上,然后基本上运行它无需安装任何东西...

首先,我不确定这完全可能,其次,我不太擅长这些事情(正确编译、静态链接等)。

我尝试静态编译OpenCV和libmagic(来自“文件”),我真的不知道我做错了什么,但我认为OpenCV没有正确构建...... 这是我使用的来源:

(编辑1:) 当前错误是当我使用big g++命令进行编译时(您可以在本文的下面看到)。 有问题的错误:

ld: library not found for -lopencv_viz clang: error: linker command failed with exit code 1 (use -v to see invocation)
我检查了可视化模块的来源(我可以找到它们)。 我正确地包含了viz模块的“include”文件夹,所以我不知道为什么它不能用-lopencv_viz找到它...

(编辑2:) 我终于找到了如何编译为独立版本。 首先,您需要为 OpenCV 构建 VTK (https://vtk.org/download/)。 之后,您需要构建 OpenCV(使用 VTK 和 Contrib 模块)。为此,我使用了这个命令:

cmake -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules -DVTK_DIR=../../VTK-9.3.0/dylibs-build/final-install/usr/local/lib/cmake/vtk-9.3/ -DBUILD_ZLIB=ON -DBUILD_JPEG=ON -DBUILD_PNG=ON -DBUILD_TIFF=ON -DBUILD_WEBP=ON -DBUILD_OPENJPEG=ON -DBUILD_JASPER=ON -DBUILD_OPENEXR=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_JPEG_TURBO_DISABLE=ON -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF ..

接下来,您应该构建 libmagic。为此,我需要使用

autoreconf --install
(我在 autoconf 方面遇到了一些问题,但在 autoreconf 方面却没有......)。之后,只需按照 git 中的“INSTALL”文件操作即可。 最后,我将所有生成的库捆绑到 .cpp 根目录下的一个文件夹中,并对包含文件执行了相同的操作。所以我最终的编译命令是:

g++ -std=c++17 -I./include/libmagic -L./lib/libmagic -lmagic -llzma -lbz2 -lz -I./include/opencv -L./lib/opencv -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_sfm -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_xfeatures2d -lopencv_shape -lopencv_ml -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core -o image_processing image_processing.cpp

最后,我可以将程序以 .zip 文件形式传送到其他 MacOS M2。 这不是最好的方法。但它有效,所以没关系。 我尝试在 g++ 命令上设置“-rpath”,这样可以更有效,但是当我使用“otool -l image_processing”时,每个库都会通过 rpath(以“@rpath”开头的路径)搜索,除了libmagic(我不知道为什么......)。

这是我当前用来编译的命令:

g++ -std=c++17 
pkg-config --static --cflags --libs libmagic
 
pkg-config --static --cflags --libs opencv4
 -o image_indexer image_indexer.cpp

翻译过来就是:

g++ -std=c++17 -I/opt/homebrew/Cellar/libmagic/5.45/include -L/opt/homebrew/Cellar/libmagic/5.45/lib -lmagic -llzma -lbz2 -lz -I/opt/homebrew/opt/opencv/include/opencv4 -L/opt/homebrew/opt/opencv/lib -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_sfm -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_xfeatures2d -lopencv_shape -lopencv_ml -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core -o image_indexer image_indexer.cpp

所以就有了这个程序:

#include <iostream>
#include <magic.h>
#include <opencv2/opencv.hpp>
#include <filesystem>
#include <vector>
#include <string>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <thread>
#include <mutex>
#include <functional>
#include <queue>
#include <condition_variable>

using namespace std;
namespace fs = filesystem;
mutex mtx;

class ThreadPool {
    public:
        ThreadPool( size_t length ) : stop( false ), finishedThreads( 0 ), totalThreads( 0 ) {
            for( size_t i = 0; i < length; ++i ) {
                threads.emplace_back( [this] {
                    while( true ) {
                        function<void()> task;
                        {
                            unique_lock<mutex> lock( queueMutex );
                            condition.wait( lock, [this] { return stop || ! tasks.empty(); } );
                            if( stop && tasks.empty() ) return;
                            task = std::move( tasks.front() );
                            tasks.pop();
                        }
                        task();
                        {
                            lock_guard<mutex> lock( counterMutex );
                            ++finishedThreads;
                            if( finishedThreads == totalThreads ) {
                                threadsFinished.notify_one();
                            }
                        }
                    }
                } );
            }
        }
        template<typename F>
        void push( F&& task ) {
            {
                unique_lock<mutex> lock( queueMutex );
                tasks.push( std::forward<F>( task ) );
            }
            condition.notify_one();
        }
        void waitAll( int total ) {
            unique_lock<mutex> lock( counterMutex );
            totalThreads = static_cast<size_t>( total );
            threadsFinished.wait( lock, [this] { return finishedThreads == totalThreads; } );
        }
        ~ThreadPool() {
            {
                unique_lock<mutex> lock( queueMutex );
                stop = true;
            }
            condition.notify_all();
            for( auto& thread : threads )
                thread.join();
        }
    private:
        vector<thread> threads;
        queue<function<void()>> tasks;
        mutex queueMutex;
        mutex counterMutex;
        condition_variable condition;
        condition_variable threadsFinished;
        size_t finishedThreads;
        size_t totalThreads;
        bool stop;
};

bool isFileReadable( const string& filePath ) {
    try {
        fs::file_status status = fs::status( filePath );
        return fs::is_regular_file( status ) && ( ( status.permissions() & fs::perms::owner_read ) != fs::perms::none );
    } catch( const fs::filesystem_error& e ) {
        cerr << "ERREUR: " << e.what() << endl;
        return false;
    }
}

bool isHiddenFile( const string& filePath ) {
    fs::path path( filePath );
    #ifdef _WIN32
    DWORD attributes = GetFileAttributes( path.c_str() );
    if( attributes != INVALID_FILE_ATTRIBUTES ) {
        return attributes & FILE_ATTRIBUTE_HIDDEN;
    }
    return false;
    #else
    return ! path.empty() && path.filename().string().front() == '.';
    #endif
}

bool startsWith( const string& input, const string& needle ) {
    return input.substr( 0, needle.size() ) == needle;
}

void writeHTML( const string& content, const string outputFilePath ) {
    lock_guard<mutex> lock( mtx );
    ofstream outputFile( outputFilePath, ios::app );
    if( outputFile.is_open() ) {
        outputFile << content;
        outputFile.close();
    }
}

string base64_encode( const unsigned char* to_encode, unsigned int in_len ) {
    string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    string result;
    int i = 0;
    int j = 0;
    unsigned char char_array_3[ 3 ];
    unsigned char char_array_4[ 4 ];
    while( in_len-- ) {
        char_array_3[ i++ ] = *( to_encode++ );
        if( i == 3 ) {
            char_array_4[ 0 ] = ( char_array_3[ 0 ] & 0xFC ) >> 2;
            char_array_4[ 1 ] = (( char_array_3[ 0 ] & 0x03 ) << 4 ) + ( ( char_array_3[ 1 ] & 0xF0 ) >> 4 );
            char_array_4[ 2 ] = (( char_array_3[ 1 ] & 0x0F ) << 2 ) + ( ( char_array_3[ 2 ] & 0xC0 ) >> 6 );
            char_array_4[ 3 ] = char_array_3[ 2 ] & 0x3F;
            for( i = 0; i < 4; i++ ) {
                result += base64_chars[ char_array_4[ i ] ];
            }
            i = 0;
        }
    }
    if( i ) {
        for( j = i; j < 3; j++ ) {
            char_array_3[ j ] = '\0';
        }
        char_array_4[ 0 ] = ( char_array_3[ 0 ] & 0xFC ) >> 2;
        char_array_4[ 1 ] = ( ( char_array_3[ 0 ] & 0x03 ) << 4 ) + ( ( char_array_3[ 1 ] & 0xF0 ) >> 4 );
        char_array_4[ 0 ] = ( ( char_array_3[ 1 ] & 0x0F ) << 2 ) + ( ( char_array_3[ 2 ] & 0xC0 ) >> 6 );
        char_array_4[ 0 ] = char_array_3[ 2 ] & 0x3F;
        for( j = 0; j < i + 1; j++ ) {
            result += base64_chars[ char_array_4[ j ] ];
        }
        while( i++ < 3 ) {
            result += '=';
        }
    }
    return result;
}

cv::Mat resizeImage( const cv::Mat& image, const int& width, const int& height ) {
    cv::Mat resized;
    cv::resize( image, resized, cv::Size( width, height ), 0, 0, cv::INTER_AREA );
    return resized;
}

string encodeToBase64( const cv::Mat& image ) {
    vector<uchar> buffer;
    cv::imencode( ".jpg", image, buffer );
    return base64_encode( buffer.data(), buffer.size() );
}

string getFileMime( const string& filePath ) {
    magic_t magicCookie = magic_open( MAGIC_MIME_TYPE );
    if( magicCookie == NULL ) {
        return "unknown";
    }
    magic_load( magicCookie, NULL );
    const char* mimeType = magic_file( magicCookie, filePath.c_str() );
    string mimeTypeString = mimeType ? mimeType : "unknown";
    magic_close( magicCookie );
    return mimeTypeString;
}

void processImage( const string outputFile, const string& filePath, const int& width, const int& height, bool verbose=false ) {
    if( ! isFileReadable( filePath ) ) return;
    fs::directory_entry fileEntry( filePath );
    string mime = getFileMime( filePath );
    string fileName = fileEntry.path().filename().string();
    if( startsWith( mime, "image/" ) ) {
        cv::Mat image = cv::imread( filePath );
        if( ! image.empty() ) {
            cv::Mat resizedImage = resizeImage( image, width, height );
            string base64Data = encodeToBase64( resizedImage );
            string tempContent = R"(
    <div style="position: relative;">
            )";
            tempContent += "<img src=\"data:" + mime + ";base64," + base64Data + "\" alt=\"" + fileName + "\" title=\"" + fileName + "\" />";
            tempContent += R"(
        <div style="position: absolute;bottom: 0;left: 0;display: flex;align-items: center;justify-content: center;width: 100%;height: fit-content;padding-block: 1rem;padding-inline: .75rem;background: rgba(255 255 255 / .5);">
            <span>Nom : )";
            tempContent += fileName;
            tempContent += R"(</span>
        </div>
    </div>
            )";
            writeHTML( tempContent, outputFile );
            if( verbose ) cout << "Image actuelle : " << filePath << endl;
        }
    }
}

int processImagesInFolder( const string outputFile, ThreadPool& threadPool, const string& folderPath, const int& width, const int& height, bool verbose=false, int count=0 ) {
    vector<string> filePaths;
    vector<thread> threads;
    try {
        for( const auto& entry : fs::directory_iterator( folderPath ) ) {
            if( fs::is_regular_file( entry ) && ! isHiddenFile( entry.path().string() ) ) {
                string filePath = entry.path().string();
                ++count;
                threadPool.push( [=]() {
                    processImage( outputFile, filePath, width, height, verbose );
                } );
            } else if( fs::is_directory( entry ) && ! isHiddenFile( entry.path().string() ) ) {
                count += processImagesInFolder( outputFile, threadPool, fs::canonical( entry.path() ).string(), width, height, verbose, count );
            }
        }
    } catch( fs::filesystem_error e ) {
        cerr << "HANDLING: " << e.what() << endl;
    }
    return count;
}

int main( int argc, char* argv[] ) {
    unordered_map<string, string> args;
    for( int i = 0; i < argc; ++i ) {
        string argument = argv[ i ];
        if( argument.substr( 0, 2 ) == "--" ) {
            size_t position = argument.find( '=' );
            if( position != string::npos ) {
                string key = argument.substr( 2, position - 2 );
                string value = argument.substr( position + 1 );
                args[ key ] = value;
            }
        }
    }
    int width = 300;
    int height = 250;
    int threadsCount = 10;
    bool verbose = false;
    string filePath = "./";
    string filename = "index.html";
    if( args.count( "width" ) ) {
        width = stoi( args[ "width" ] );
    }
    if( args.count( "height" ) ) {
        height = stoi( args[ "height" ] );
    }
    if(
        args.count( "verbose" ) &&
        (
            args[ "verbose" ] == "true" ||
            args[ "verbose" ] == "yes" ||
            args[ "verbose" ] == "1" ||
            args[ "verbose" ] == "y"
        )
    ) {
        verbose = true;
    }
    if( args.count( "threads") ) {
        threadsCount = stoi( args[ "threads" ] );
    }
    if( args.count( "outFolder" ) ) {
        filePath = args[ "outFolder" ];
    }
    if( args.count( "outFilename" ) ) {
        filename = args[ "outFilename" ];
    }
    if( args.count( "folder" ) ) {
        cout << "L'indexation peut prendre un certain temps...." << endl;
        ThreadPool threadPool( static_cast<size_t>( threadsCount ) );
        string finalHTML = R"(
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Indexation photo</title>
</head>
<body style="background-color: rgb(31 41 55);">
    <div style="display: flex;flex-wrap: wrap;justify-content: space-around;gap: 1.5rem;padding-block: 1rem;padding-inline: 2.5rem;">)";
        if( filePath.back() != '/' ) filePath += '/';
        ofstream outputFile( filePath + filename );
        if( outputFile.is_open() ) {
            outputFile << finalHTML;
            outputFile.close();
        }
        string folderPath = args[ "folder" ];
        int total = processImagesInFolder( filePath + filename, threadPool, folderPath, width, height, verbose );
        if( verbose ) cout << total <<" fichiers à traiter." << endl;
        threadPool.waitAll( total );
        finalHTML = R"(
    </div>
</body>
</html>)";
        writeHTML( finalHTML, filePath + filename );
        cout << endl << "Fichier index.html généré avec succès.\nIl répertori les images du dossier : " << folderPath << endl;
        return 0;
    } else {
        cout << "Pour utiliser cet indexeur, veuillez préciser au moins l'argument 'folder'" << endl;
        cout << "--> Utilisation: ./image_indexer --folder=/Users/name/Pictures" << endl << endl;
        cout << "Arguments :" << endl;
        cout << "--folder: Permet de préciser le dossier a indexer" << endl;
        cout << "--width: Précise la longueur en pixel de l'image retaillée" << endl;
        cout << "--height: Précise la hauteur en pixel de l'image retaillée" << endl;
        cout << "--verbose: Affiche l'image actuellement traitée (préciser 'y'/'yes'/'1')" << endl;
        cout << "--threads: Nombre de threads en simulatané (défaut: 10)" << endl;
        cout << "--outFolder: Chemin du dossier de génération" << endl;
        cout << "--outFilename: Nom du fichier HTML généré" << endl;
        return 1;
    }
}

我尝试静态编译 OpenCV 和 libmagic。我认为 libmagic 没有问题(没有错误等),但对于 OpenCV 来说这是另一段历史......

我使用此命令通过模块构建 OpenCV:

mkdir build && cd build && cmake -D CMAKE_BUILD_TYPE=Release -D BUILD_SHARED_LIBS=OFF -D OPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules ../opencv/ && make -j8
这需要一段时间来构建。

之后,我基本上尝试将所有必需的库链接到 G++ 命令。 这给了我类似的东西(我替换了所有绝对路径):

g++ -std=c++17 -lbz2 -lz -o image_processing image_processing.cpp \
-I/content/opencv/modules/core/include \
-I/content/file/src \
-I/content/opencv4/opencv/include \
-I/content/opencv/modules/features2d/include \
-I/content/opencv/modules/calib3d/include \
-I/content/modules/opencv_contrib/modules/core/include \
-I/content/opencv4/build \
-I/content/opencv/modules/flann/include \
-I/content/opencv/modules/dnn/include \
-I/content/opencv/modules/highgui/include \
-I/content/opencv/modules/imgcodecs/include \
-I/content/opencv/modules/videoio/include \
-I/content/opencv/modules/imgproc/include \
-I/content/opencv/modules/gapi/include \
-I/content/opencv/modules/ml/include \
-I/content/opencv/modules/objdetect/include \
-I/content/opencv/modules/photo/include \
-I/content/opencv/modules/stitching/include \
-I/content/opencv/modules/video/include \
-I/content/opencv/modules/shape/include \
-I/content/opencv/modules/superres/include \
-I/content/opencv/modules/videostab/include \
-I/content/opencv/modules/aruco/include \
-I/content/opencv/modules/bgsegm/include \
-I/content/opencv/modules/bioinspired/include \
-I/content/opencv/modules/ccalib/include \
-I/content/opencv/modules/datasets/include \
-I/content/opencv/modules/dpm/include \
-I/content/opencv/modules/face/include \
-I/content/opencv/modules/freetype/include \
-I/content/opencv/modules/fuzzy/include \
-I/content/opencv/modules/hdf/include \
-I/content/opencv/modules/line_descriptor/include \
-I/content/opencv/modules/optflow/include \
-I/content/modules/opencv_contrib/modules/video/include \
-I/content/modules/opencv_contrib/modules/plot/include \
-I/content/modules/opencv_contrib/modules/reg/include \
-I/content/modules/opencv_contrib/modules/saliency/include \
-I/content/modules/opencv_contrib/modules/stereo/include \
-I/content/modules/opencv_contrib/modules/structured_light/include \
-I/content/modules/opencv_contrib/modules/phase_unwrapping/include \
-I/content/modules/opencv_contrib/modules/rgbd/include \
-I/content/modules/opencv_contrib/modules/viz/include \
-I/content/modules/opencv_contrib/modules/surface_matching/include \
-I/content/modules/opencv_contrib/modules/text/include \
-I/content/modules/opencv_contrib/modules/ximgproc/include \
-I/content/modules/opencv_contrib/modules/xobjdetect/include \
-I/content/modules/opencv_contrib/modules/xphoto/include \
-I/content/modules/opencv_contrib/modules/xobjdetect/include \
-L/content/modules/build/lib \
-L/content/file/src/.libs \
-lopencv_shape \
-lopencv_stitching \
-lopencv_superres \
-lopencv_videostab \
-lopencv_aruco \
-lopencv_bgsegm \
-lopencv_bioinspired \
-lopencv_ccalib \
-lopencv_datasets \
-lopencv_dpm \
-lopencv_face \
-lopencv_freetype \
-lopencv_fuzzy \
-lopencv_hdf \
-lopencv_line_descriptor \
-lopencv_optflow \
-lopencv_video \
-lopencv_plot \
-lopencv_reg \
-lopencv_saliency \
-lopencv_stereo \
-lopencv_structured_light \
-lopencv_phase_unwrapping \
-lopencv_rgbd \
-lopencv_viz \
-lopencv_surface_matching \
-lopencv_text \
-lopencv_ximgproc \
-lopencv_calib3d \
-lopencv_features2d \
-lopencv_flann \
-lopencv_xobjdetect \
-lopencv_objdetect \
-lopencv_ml \
-lopencv_xphoto \
-lopencv_highgui \
-lopencv_videoio \
-lopencv_imgcodecs \
-lopencv_photo \
-lopencv_imgproc \
-lopencv_core
c++ macos compilation static c++17
1个回答
0
投票

我终于知道如何编译为独立版了。

这不是一个很好的解决方案,因为它的目的只是与其他 MacOS M2 共享,并且还有其他方法可以做得更好。

首先,您需要为 OpenCV 构建 VTK (https://vtk.org/download/)。

之后,您将需要使用 VTK 和 Contrib 模块构建 OpenCV。 为此,我使用了这个命令:

cmake -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules -DVTK_DIR=../../VTK-9.3.0/dylibs-build/final-install/usr/local/lib/cmake/vtk-9.3/ -DBUILD_ZLIB=ON -DBUILD_JPEG=ON -DBUILD_PNG=ON -DBUILD_TIFF=ON -DBUILD_WEBP=ON -DBUILD_OPENJPEG=ON -DBUILD_JASPER=ON -DBUILD_OPENEXR=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_JPEG_TURBO_DISABLE=ON -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF ..

接下来,您需要构建 libmagic。为此,您应该遵循 github 中的说明。

就我而言,我需要使用

autoreconf --install
而不是
autoconf
。 (我在 autoconf 方面遇到了一些问题,但在 autoreconf 方面却没有......)。

最后,我将所有生成的库和所有包含的文件捆绑到程序根目录的文件夹中。

然后我使用此命令构建了程序:

g++ -std=c++17 -I./include/libmagic -L./lib/libmagic -lmagic -llzma -lbz2 -lz -I./include/opencv -L./lib/opencv -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_sfm -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_xfeatures2d -lopencv_shape -lopencv_ml -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core -o image_processing image_processing.cpp

正如我所说,这不是最好的方法(我想)。 我尝试使用 g++ 中的“rpath”选项,但由于某种原因,我无法使其适用于 libmagic。 (为了检查,我使用了

otool -l image_processing
,每个 lib 路径都以“@rpath”开头,除了来自 libmagic 的路径)

(感谢@Botje和@user12002570)

© www.soinside.com 2019 - 2024. All rights reserved.