update:upload
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
|||||||
|
# Xmake cache
|
||||||
|
.xmake/
|
||||||
|
build/
|
||||||
|
images/
|
||||||
|
output/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# MacOS Cache
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
|
||||||
+467
@@ -0,0 +1,467 @@
|
|||||||
|
#include <opencv2/opencv.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cmath>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
std::string inputDir = "占位符字段";
|
||||||
|
std::string outputFile = "占位符字段.yml";
|
||||||
|
std::string diagnosticsDir = "占位符字段";
|
||||||
|
|
||||||
|
// 内角点数量,不是黑白方格数量
|
||||||
|
int cols = 占位符字段;
|
||||||
|
int rows = 占位符字段;
|
||||||
|
|
||||||
|
// 每个方格的实际边长,单位 mm
|
||||||
|
double squareSize = 占位符字段;
|
||||||
|
|
||||||
|
int minImages = 10;
|
||||||
|
bool visualize = false;
|
||||||
|
bool saveDiagnostics = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void printUsage(const char* program)
|
||||||
|
{
|
||||||
|
std::cout
|
||||||
|
<< "OpenCV C++ Camera Calibrator\n\n"
|
||||||
|
<< "Usage:\n"
|
||||||
|
<< " " << program << " [options]\n\n"
|
||||||
|
<< "Options:\n"
|
||||||
|
<< " --input DIR Image directory (default: images)\n"
|
||||||
|
<< " --output FILE Output YAML (default: "
|
||||||
|
"output/camera_intrinsics.yml)\n"
|
||||||
|
<< " --cols N Inner corners per row (default: 9)\n"
|
||||||
|
<< " --rows N Inner corners per column (default: 6)\n"
|
||||||
|
<< " --square MM Square side length in mm (default: 25)\n"
|
||||||
|
<< " --min-images N Minimum valid images (default: 10)\n"
|
||||||
|
<< " --visualize Show detected corners interactively\n"
|
||||||
|
<< " --no-diagnostics Do not save diagnostic images\n"
|
||||||
|
<< " --help Show this help\n\n"
|
||||||
|
<< "Example for a 10x7-square board with 25 mm squares:\n"
|
||||||
|
<< " " << program
|
||||||
|
<< " --input images --cols 9 --rows 6 --square 25\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseInt(const std::string& s, int& value)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
size_t pos = 0;
|
||||||
|
int v = std::stoi(s, &pos);
|
||||||
|
if (pos != s.size())
|
||||||
|
return false;
|
||||||
|
value = v;
|
||||||
|
return true;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseDouble(const std::string& s, double& value)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
size_t pos = 0;
|
||||||
|
double v = std::stod(s, &pos);
|
||||||
|
if (pos != s.size() || !std::isfinite(v))
|
||||||
|
return false;
|
||||||
|
value = v;
|
||||||
|
return true;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseArgs(int argc, char** argv, Options& opt)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
const std::string arg = argv[i];
|
||||||
|
|
||||||
|
if (arg == "--help") {
|
||||||
|
printUsage(argv[0]);
|
||||||
|
return false;
|
||||||
|
} else if (arg == "--visualize") {
|
||||||
|
opt.visualize = true;
|
||||||
|
} else if (arg == "--no-diagnostics") {
|
||||||
|
opt.saveDiagnostics = false;
|
||||||
|
} else if (arg == "--input" ||
|
||||||
|
arg == "--output" ||
|
||||||
|
arg == "--cols" ||
|
||||||
|
arg == "--rows" ||
|
||||||
|
arg == "--square" ||
|
||||||
|
arg == "--min-images") {
|
||||||
|
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
std::cerr << "Missing value for " << arg << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string value = argv[++i];
|
||||||
|
|
||||||
|
if (arg == "--input") {
|
||||||
|
opt.inputDir = value;
|
||||||
|
} else if (arg == "--output") {
|
||||||
|
opt.outputFile = value;
|
||||||
|
} else if (arg == "--cols") {
|
||||||
|
if (!parseInt(value, opt.cols)) {
|
||||||
|
std::cerr << "Invalid --cols value\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (arg == "--rows") {
|
||||||
|
if (!parseInt(value, opt.rows)) {
|
||||||
|
std::cerr << "Invalid --rows value\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (arg == "--square") {
|
||||||
|
if (!parseDouble(value, opt.squareSize)) {
|
||||||
|
std::cerr << "Invalid --square value\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (arg == "--min-images") {
|
||||||
|
if (!parseInt(value, opt.minImages)) {
|
||||||
|
std::cerr << "Invalid --min-images value\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown option: " << arg << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt.cols < 2 || opt.rows < 2 ||
|
||||||
|
opt.squareSize <= 0 || opt.minImages < 3) {
|
||||||
|
std::cerr << "Invalid parameters. Check cols, rows, square "
|
||||||
|
"and min-images.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string lower(std::string s)
|
||||||
|
{
|
||||||
|
std::transform(s.begin(), s.end(), s.begin(),
|
||||||
|
[](unsigned char c) {
|
||||||
|
return static_cast<char>(std::tolower(c));
|
||||||
|
});
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isImageFile(const fs::path& path)
|
||||||
|
{
|
||||||
|
const std::string ext = lower(path.extension().string());
|
||||||
|
|
||||||
|
static const std::set<std::string> extensions = {
|
||||||
|
".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff"
|
||||||
|
};
|
||||||
|
|
||||||
|
return extensions.count(ext) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ViewData {
|
||||||
|
std::string filename;
|
||||||
|
std::vector<cv::Point2f> corners;
|
||||||
|
cv::Mat image;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
Options opt;
|
||||||
|
|
||||||
|
if (!parseArgs(argc, argv, opt)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cv::Size boardSize(opt.cols, opt.rows);
|
||||||
|
|
||||||
|
std::cout << "=== Camera Calibration ===\n"
|
||||||
|
<< "Input directory : " << opt.inputDir << '\n'
|
||||||
|
<< "Output file : " << opt.outputFile << '\n'
|
||||||
|
<< "Inner corners : " << opt.cols
|
||||||
|
<< " x " << opt.rows << '\n'
|
||||||
|
<< "Square size : " << opt.squareSize << " mm\n"
|
||||||
|
<< "Minimum images : " << opt.minImages << "\n\n";
|
||||||
|
|
||||||
|
if (!fs::exists(opt.inputDir) ||
|
||||||
|
!fs::is_directory(opt.inputDir)) {
|
||||||
|
std::cerr << "Input directory does not exist: "
|
||||||
|
<< opt.inputDir << '\n';
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<fs::path> files;
|
||||||
|
|
||||||
|
for (const auto& entry : fs::directory_iterator(opt.inputDir)) {
|
||||||
|
if (entry.is_regular_file() && isImageFile(entry.path())) {
|
||||||
|
files.push_back(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(files.begin(), files.end());
|
||||||
|
|
||||||
|
if (files.empty()) {
|
||||||
|
std::cerr << "No supported images found in "
|
||||||
|
<< opt.inputDir << '\n';
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Found " << files.size() << " candidate images.\n";
|
||||||
|
|
||||||
|
std::vector<ViewData> views;
|
||||||
|
cv::Size imageSize;
|
||||||
|
int readFailures = 0;
|
||||||
|
int cornerFailures = 0;
|
||||||
|
int sizeFailures = 0;
|
||||||
|
|
||||||
|
const cv::TermCriteria subpixCriteria(
|
||||||
|
cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER,
|
||||||
|
40, 0.001
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const auto& path : files) {
|
||||||
|
cv::Mat image = cv::imread(path.string(), cv::IMREAD_COLOR);
|
||||||
|
|
||||||
|
if (image.empty()) {
|
||||||
|
std::cout << "[READ FAIL] " << path.filename().string()
|
||||||
|
<< '\n';
|
||||||
|
++readFailures;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageSize.empty()) {
|
||||||
|
imageSize = image.size();
|
||||||
|
} else if (image.size() != imageSize) {
|
||||||
|
std::cout << "[SIZE MISMATCH] "
|
||||||
|
<< path.filename().string() << " is "
|
||||||
|
<< image.cols << "x" << image.rows
|
||||||
|
<< "; expected " << imageSize.width
|
||||||
|
<< "x" << imageSize.height << '\n';
|
||||||
|
++sizeFailures;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat gray;
|
||||||
|
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
|
||||||
|
|
||||||
|
std::vector<cv::Point2f> corners;
|
||||||
|
|
||||||
|
const bool found = cv::findChessboardCorners(
|
||||||
|
gray,
|
||||||
|
boardSize,
|
||||||
|
corners,
|
||||||
|
cv::CALIB_CB_ADAPTIVE_THRESH |
|
||||||
|
cv::CALIB_CB_NORMALIZE_IMAGE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!found || corners.size() !=
|
||||||
|
static_cast<size_t>(opt.cols * opt.rows)) {
|
||||||
|
std::cout << "[NO CORNERS] "
|
||||||
|
<< path.filename().string() << '\n';
|
||||||
|
++cornerFailures;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::cornerSubPix(
|
||||||
|
gray,
|
||||||
|
corners,
|
||||||
|
cv::Size(11, 11),
|
||||||
|
cv::Size(-1, -1),
|
||||||
|
subpixCriteria
|
||||||
|
);
|
||||||
|
|
||||||
|
ViewData view;
|
||||||
|
view.filename = path.filename().string();
|
||||||
|
view.corners = corners;
|
||||||
|
view.image = image;
|
||||||
|
views.push_back(view);
|
||||||
|
|
||||||
|
cv::Mat display = image.clone();
|
||||||
|
cv::drawChessboardCorners(
|
||||||
|
display, boardSize, corners, true
|
||||||
|
);
|
||||||
|
|
||||||
|
std::cout << "[OK] " << view.filename << '\n';
|
||||||
|
|
||||||
|
if (opt.saveDiagnostics) {
|
||||||
|
fs::create_directories(opt.diagnosticsDir);
|
||||||
|
fs::path outPath =
|
||||||
|
fs::path(opt.diagnosticsDir) /
|
||||||
|
(path.stem().string() + "_corners.png");
|
||||||
|
cv::imwrite(outPath.string(), display);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt.visualize) {
|
||||||
|
cv::imshow("Detected checkerboard", display);
|
||||||
|
const int key = cv::waitKey(0);
|
||||||
|
if (key == 27) {
|
||||||
|
cv::destroyAllWindows();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt.visualize)
|
||||||
|
cv::destroyAllWindows();
|
||||||
|
|
||||||
|
std::cout << "\n=== Detection Summary ===\n"
|
||||||
|
<< "Total files : " << files.size() << '\n'
|
||||||
|
<< "Valid views : " << views.size() << '\n'
|
||||||
|
<< "Read failures : " << readFailures << '\n'
|
||||||
|
<< "Corner failures : " << cornerFailures << '\n'
|
||||||
|
<< "Size mismatches : " << sizeFailures << '\n';
|
||||||
|
|
||||||
|
if (views.size() < static_cast<size_t>(opt.minImages)) {
|
||||||
|
std::cerr << "\nNot enough valid images. Need at least "
|
||||||
|
<< opt.minImages << ", got " << views.size()
|
||||||
|
<< ".\n";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 棋盘格位于 Z=0 平面,所有点使用同一单位。
|
||||||
|
// squareSize 以 mm 输入,因此 tvecs 的单位也是 mm。
|
||||||
|
std::vector<cv::Point3f> objectTemplate;
|
||||||
|
|
||||||
|
for (int row = 0; row < opt.rows; ++row) {
|
||||||
|
for (int col = 0; col < opt.cols; ++col) {
|
||||||
|
objectTemplate.emplace_back(
|
||||||
|
static_cast<float>(col * opt.squareSize),
|
||||||
|
static_cast<float>(row * opt.squareSize),
|
||||||
|
0.0f
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point3f>> objectPoints;
|
||||||
|
std::vector<std::vector<cv::Point2f>> imagePoints;
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
objectPoints.push_back(objectTemplate);
|
||||||
|
imagePoints.push_back(view.corners);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat cameraMatrix = cv::initCameraMatrix2D(
|
||||||
|
objectPoints, imagePoints, imageSize
|
||||||
|
);
|
||||||
|
|
||||||
|
cv::Mat distCoeffs = cv::Mat::zeros(8, 1, CV_64F);
|
||||||
|
|
||||||
|
std::vector<cv::Mat> rvecs, tvecs;
|
||||||
|
|
||||||
|
const double rms = cv::calibrateCamera(
|
||||||
|
objectPoints,
|
||||||
|
imagePoints,
|
||||||
|
imageSize,
|
||||||
|
cameraMatrix,
|
||||||
|
distCoeffs,
|
||||||
|
rvecs,
|
||||||
|
tvecs,
|
||||||
|
0,
|
||||||
|
cv::TermCriteria(
|
||||||
|
cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER,
|
||||||
|
100, 1e-9
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
double totalSquaredError = 0.0;
|
||||||
|
size_t totalPoints = 0;
|
||||||
|
std::vector<double> perViewErrors;
|
||||||
|
|
||||||
|
std::cout << "\n=== Per-image Reprojection Error ===\n";
|
||||||
|
std::cout << std::fixed << std::setprecision(4);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < views.size(); ++i) {
|
||||||
|
std::vector<cv::Point2f> projected;
|
||||||
|
|
||||||
|
cv::projectPoints(
|
||||||
|
objectPoints[i],
|
||||||
|
rvecs[i],
|
||||||
|
tvecs[i],
|
||||||
|
cameraMatrix,
|
||||||
|
distCoeffs,
|
||||||
|
projected
|
||||||
|
);
|
||||||
|
|
||||||
|
double squaredError = 0.0;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < projected.size(); ++j) {
|
||||||
|
const cv::Point2f delta =
|
||||||
|
imagePoints[i][j] - projected[j];
|
||||||
|
|
||||||
|
squaredError += delta.dot(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
const double viewRms = std::sqrt(
|
||||||
|
squaredError / projected.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
perViewErrors.push_back(viewRms);
|
||||||
|
totalSquaredError += squaredError;
|
||||||
|
totalPoints += projected.size();
|
||||||
|
|
||||||
|
std::cout << views[i].filename << " : "
|
||||||
|
<< viewRms << " px\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
const double globalRms = std::sqrt(
|
||||||
|
totalSquaredError / totalPoints
|
||||||
|
);
|
||||||
|
|
||||||
|
fs::create_directories(
|
||||||
|
fs::path(opt.outputFile).parent_path().empty()
|
||||||
|
? fs::path(".")
|
||||||
|
: fs::path(opt.outputFile).parent_path()
|
||||||
|
);
|
||||||
|
|
||||||
|
cv::FileStorage out(
|
||||||
|
opt.outputFile, cv::FileStorage::WRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!out.isOpened()) {
|
||||||
|
std::cerr << "Cannot write output file: "
|
||||||
|
<< opt.outputFile << '\n';
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "image_width" << imageSize.width;
|
||||||
|
out << "image_height" << imageSize.height;
|
||||||
|
out << "board_inner_corners_cols" << opt.cols;
|
||||||
|
out << "board_inner_corners_rows" << opt.rows;
|
||||||
|
out << "square_size_mm" << opt.squareSize;
|
||||||
|
out << "camera_matrix" << cameraMatrix;
|
||||||
|
out << "distortion_coefficients" << distCoeffs;
|
||||||
|
out << "opencv_rms" << rms;
|
||||||
|
out << "global_reprojection_error_px" << globalRms;
|
||||||
|
|
||||||
|
out << "per_view_errors_px" << "[";
|
||||||
|
for (size_t i = 0; i < views.size(); ++i) {
|
||||||
|
out << "{"
|
||||||
|
<< "filename" << views[i].filename
|
||||||
|
<< "error_px" << perViewErrors[i]
|
||||||
|
<< "}";
|
||||||
|
}
|
||||||
|
out << "]";
|
||||||
|
|
||||||
|
out.release();
|
||||||
|
|
||||||
|
std::cout << "\n=== Calibration Result ===\n"
|
||||||
|
<< "Image size: " << imageSize.width
|
||||||
|
<< " x " << imageSize.height << '\n'
|
||||||
|
<< "Camera matrix:\n" << cameraMatrix << '\n'
|
||||||
|
<< "Distortion coefficients:\n"
|
||||||
|
<< distCoeffs << '\n'
|
||||||
|
<< "OpenCV RMS: " << rms << " px\n"
|
||||||
|
<< "Global reprojection error: "
|
||||||
|
<< globalRms << " px\n"
|
||||||
|
<< "Saved to: " << opt.outputFile << '\n';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
add_rules("mode.debug", "mode.release")
|
||||||
|
|
||||||
|
set_languages("c++17")
|
||||||
|
|
||||||
|
set_policy("generator.compile_commands", true)
|
||||||
|
|
||||||
|
add_requires("cmake::OpenCV", {alias = "opencv", system = true})
|
||||||
|
|
||||||
|
target("camera_calibrator")
|
||||||
|
set_kind("binary")
|
||||||
|
add_packages("opencv")
|
||||||
|
add_files("src/*.cpp")
|
||||||
|
--
|
||||||
|
-- If you want to known more usage about xmake, please see https://xmake.io
|
||||||
|
--
|
||||||
|
-- ## FAQ
|
||||||
|
--
|
||||||
|
-- You can enter the project directory firstly before building project.
|
||||||
|
--
|
||||||
|
-- $ cd projectdir
|
||||||
|
--
|
||||||
|
-- 1. How to build project?
|
||||||
|
--
|
||||||
|
-- $ xmake
|
||||||
|
--
|
||||||
|
-- 2. How to configure project?
|
||||||
|
--
|
||||||
|
-- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release]
|
||||||
|
--
|
||||||
|
-- 3. Where is the build output directory?
|
||||||
|
--
|
||||||
|
-- The default output directory is `./build` and you can configure the output directory.
|
||||||
|
--
|
||||||
|
-- $ xmake f -o outputdir
|
||||||
|
-- $ xmake
|
||||||
|
--
|
||||||
|
-- 4. How to run and debug target after building project?
|
||||||
|
--
|
||||||
|
-- $ xmake run [targetname]
|
||||||
|
-- $ xmake run -d [targetname]
|
||||||
|
--
|
||||||
|
-- 5. How to install target to the system directory or other output directory?
|
||||||
|
--
|
||||||
|
-- $ xmake install
|
||||||
|
-- $ xmake install -o installdir
|
||||||
|
--
|
||||||
|
-- 6. Add some frequently-used compilation flags in xmake.lua
|
||||||
|
--
|
||||||
|
-- @code
|
||||||
|
-- -- add debug and release modes
|
||||||
|
-- add_rules("mode.debug", "mode.release")
|
||||||
|
--
|
||||||
|
-- -- add macro definition
|
||||||
|
-- add_defines("NDEBUG", "_GNU_SOURCE=1")
|
||||||
|
--
|
||||||
|
-- -- set warning all as error
|
||||||
|
-- set_warnings("all", "error")
|
||||||
|
--
|
||||||
|
-- -- set language: c99, c++11
|
||||||
|
-- set_languages("c99", "c++11")
|
||||||
|
--
|
||||||
|
-- -- set optimization: none, faster, fastest, smallest
|
||||||
|
-- set_optimize("fastest")
|
||||||
|
--
|
||||||
|
-- -- add include search directories
|
||||||
|
-- add_includedirs("/usr/include", "/usr/local/include")
|
||||||
|
--
|
||||||
|
-- -- add link libraries and search directories
|
||||||
|
-- add_links("tbox")
|
||||||
|
-- add_linkdirs("/usr/local/lib", "/usr/lib")
|
||||||
|
--
|
||||||
|
-- -- add system link libraries
|
||||||
|
-- add_syslinks("z", "pthread")
|
||||||
|
--
|
||||||
|
-- -- add compilation and link flags
|
||||||
|
-- add_cxflags("-stdnolib", "-fno-strict-aliasing")
|
||||||
|
-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true})
|
||||||
|
--
|
||||||
|
-- @endcode
|
||||||
|
--
|
||||||
|
|
||||||
Reference in New Issue
Block a user