我目前正在尝试将我的程序编译为独立的。 我使用的是 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
我终于知道如何编译为独立版了。
这不是一个很好的解决方案,因为它的目的只是与其他 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)