std::band

标准库函数bind()和function()定义于头文件中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。bind()接受一个函数(或者函数对象,或者任何你可以通过”(…)”符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如:

        int f(int, char, double);
        // 绑定f()函数调用的第二个和第三个参数,
        // 返回一个新的函数对象为ff,它只带有一个int类型的参数
        auto ff = bind(f, _1, ‘c’, 1.2);
        int x = ff(7);                //  f(7, ‘c’, 1.2);

参数的绑定通常称为”Currying”(译注:Currying—“烹制咖喱烧菜”,此处意指对函数或函数对象进行加工修饰操作), “_1″是一个占位符对象,用于表示当函数f通过函数ff进行调用时,函数ff的第一个参数在函数f的参数列表中的位置。第一个参数称为”_1″, 第二个参数为”_2″,依此类推。例如:

        int f(int, char, double);
        auto frev = bind(f, _3, _2, _1);        // 翻转参数顺序
        int x = frev(1.2, ‘c’, 7);            // f(7, ‘c’, 1.2);

此处,auto关键字节约了我们去推断bind返回的结果类型的工作。

我们无法使用bind()绑定一个重载函数的参数,我们必须显式地指出需要绑定的重载函数的版本:

        int g(int);
        double g(double);

        auto g1 = bind(g, _1);    // 错误:调用哪一个g() ?
        // 正确,但是相当丑陋
        auto g2 = bind( (double(*)(double))g, _1);

bind()有两种版本:一个如上所述,另一个则是“历史遗留”的版本:你可以显式地描述返回类型。例如:

        auto f2 = bind<int> (f, 7, ‘c’, _1);      // 显式返回类型
        int x = f2(1.2);                    // f(7, ‘c’, 1.2);

第二种形式的存在是必要的,并且因为第一个版本((?) “and for a user simplest “,此处请参考原文))无法在C++98中实现。所以第二个版本已经被广泛使用。

function是一个拥有任何可以以”(…)”符号进行调用的值的类型。特别地,bind的返回结果可以赋值给function类型。function十分易于使用。(译注:更直观地,可以把function看成是一种表示函数的数据类型,就像函数对象一样。只不过普通的数据类型表示的是数据,function表示的是函数这个抽象概念。)例如:

        // 构造一个函数对象,
        // 它能表示的是一个返回值为float,
        // 两个参数为int,int的函数
        function<float (int x, int y)> f;   

        // 构造一个可以使用"()"进行调用的函数对象类型
        struct int_div {
                float operator() (int x, int y) const
                     { return ((float)x)/y; };
        };

        f = int_div();                    // 赋值
        cout<< f(5,3) <<endl;  // 通过函数对象进行调用
        std::accumulate(b, e, 1, f);        // 完美传递

成员函数可被看做是带有额外参数的自由函数:

        struct X {
                int foo(int);
        };

        // 所谓的额外参数,
        // 就是成员函数默认的第一个参数,
        // 也就是指向调用成员函数的对象的this指针
        function<int (X*, int)> f;
        f = &X::foo;            // 指向成员函数

        X x;
        int v = f(&x, 5);  // 在对象x上用参数5调用X::foo()
        function<int (int)> ff = std::bind(f, &x, _1);    // f的第一个参数是&x
        v = ff(5);                // 调用x.foo(5)

function对于回调函数、将操作作为参数传递等十分有用。它可以看做是C++98标准库中函数对象mem_fun_t, pointer_to_unary_function等的替代品。同样的,bind()也可以被看做是bind1st()和bind2nd()的替代品,当然比他们更强大更灵活。

/* 
 * File:   main.cpp
 * Author: Vicky.H
 * Email:  eclipser@163.com
 */
#include <iostream>
#include <functional>
#include <typeinfo>
#include <string.h>

int add1(int i, int j, int k) {
    return i + j + k;
}


class Utils {
public:
    Utils(const char* name) {
        strcpy(_name, name);
    }
    
    void sayHello(const char* name) const {
        std::cout << _name << " say: hello " << name << std::endl;
    }
    
    static int getId() {
        return 10001;
    } 
    
    int operator()(int i, int j, int k) const {
        return i + j + k;
    }
    
private:
    char _name[32];
};


/*
 * 
 */
int main(void) {
    
    // 绑定全局函数
    auto add2 = std::bind(add1, std::placeholders::_1, std::placeholders::_2, 10);
    // 函数add2 = 绑定add1函数,参数1不变,参数2不变,参数3固定为10.
    std::cout << typeid(add2).name() << std::endl;
    std::cout << "add2(1,2) = " << add2(1, 2) << std::endl;
    
    std::cout << "\n---------------------------" << std::endl;
    
    // 绑定成员函数
    Utils utils("Vicky");
    auto sayHello = std::bind(&Utils::sayHello, utils/*调用者*/, std::placeholders::_1/*参数1*/);
    sayHello("Jack");
    
    auto sayHelloToLucy = std::bind(&Utils::sayHello, utils/*调用者*/, "Lucy"/*固定参数1*/);
    sayHelloToLucy();
    
    // 绑定静态成员函数
    auto getId = std::bind(&Utils::getId);
    std::cout << getId() << std::endl;
    
    std::cout << "\n---------------------------" << std::endl;
    
    // 绑定operator函数
    auto add100 = std::bind(&Utils::operator (), utils, std::placeholders::_1, std::placeholders::_2, 100);
    std::cout << "add100(1, 2) = " << add100(1, 2) << std::endl;
    
    // 注意:无法使用std::bind()绑定一个重载函数
    
    return 0;
}



// 引入必要的头文件 #include <opencv2/opencv.hpp> // OpenCV 核心库,用于图像处理 #include <opencv2/features2d.hpp> // OpenCV 特征检测库 #include<opencv2/xfeatures2d/xfeatures2d.hpp> // OpenCV 额外特征检测库(SURF) #include <opencv2/stitching.hpp> // OpenCV 图像拼接库 #include <iostream> // 输入输出流,用于控制台输出 #include <vector> // 向量容器,用于存储多个图像 #include <string> // 字符串处理 #include <algorithm> // 算法库,用于排序等操作 #include <regex> // 正则表达式库,用于文件名解析 #include <sstream> // 字符串流,用于字符串处理 #include <iomanip> // 输入输出格式化 #include <chrono> // 时间库,用于性能测量 #include <thread> // 线程库,用于实时显示 #include <atomic> // 原子操作库 #include <mutex> // 互斥锁库 #include <opencv2/stitching/detail/util.hpp> // 可能包含 resultRoi 的定义 // 配置参数 - 这些是程序的设置,可以根据需要修改 const std::string IMAGE_DIR = "C:/Users/zhangqt203387/images"; // 图像文件夹路径 const std::string OUTPUT_DIR = "output"; // 输出文件夹名称 const int RESIZE_THRESHOLD = 1024; // 图像最大尺寸限制 const std::string FEATURE_DETECTOR = "SIFT"; // 使用的特征检测算法 const double RANSAC_THRESHOLD = 5.0; // RANSAC算法的容错阈值 const double MATCH_RATIO = 0.6; // 特征匹配的质量阈值 const int MIN_MATCHES = 10; // 最小匹配点数量 const bool SHOW_PROGRESS = true; // 是否显示拼接过程 const double DISPLAY_SCALE = 0.3; // 显示图像的缩放比例 // 图像预处理参数 const bool ENABLE_PREPROCESSING = true; // 是否启用图像预处理 const int MEDIAN_BLUR_SIZE = 3; // 中值滤波核大小 const double INDANE_CLIP_LIMIT = 2.0; // INDANE算法对比度限制 const int INDANE_TILE_SIZE = 8; // INDANE算法瓦片大小 // 特征提取参数 const int MAX_FEATURES = 5000; // 最大特征点数量 const double CONTRAST_THRESHOLD = 0.04; // 对比度阈值 // 图像融合参数 const bool USE_MULTI_BAND_BLENDING = true; // 是否使用多频带融合 const int BLEND_WIDTH = 5; // 融合宽度 // 定义图像拼接器类 class AdvancedImageStitcher { private: // 类的私有成员变量 std::string detectorType; // 特征检测器类型 cv::Ptr<cv::Feature2D> detector; // OpenCV特征检测器对象 cv::Ptr<cv::DescriptorMatcher> matcher; // 特征匹配器对象 std::vector<cv::Mat> images; // 存储加载的图像 std::vector<std::string> imagePaths; // 存储图像文件路径 cv::Mat panorama; // 存储拼接后的全景图 std::atomic<bool> stopDisplay; // 停止显示标志 std::mutex panoramaMutex; // 全景图互斥锁 // 设置特征检测器和匹配器 void setupDetectors() { // 根据选择的类型创建特征检测器 if (detectorType == "SIFT") { detector = cv::SIFT::create(MAX_FEATURES, 3, CONTRAST_THRESHOLD); } else if (detectorType == "SURF") { detector = cv::xfeatures2d::SURF::create(400); // Hessian阈值 } else if (detectorType == "AKAZE") { detector = cv::AKAZE::create(); } else { // 默认使用ORB detector = cv::ORB::create(MAX_FEATURES); } // 根据特征检测器类型创建匹配器 if (detectorType == "SIFT" || detectorType == "SURF") { matcher = cv::FlannBasedMatcher::create(); // SIFT和SURF使用FLANN匹配器 } else { matcher = cv::BFMatcher::create(cv::NORM_HAMMING, false); // 其他使用暴力匹配器 } } // INDANE图像增强算法 cv::Mat applyINDANE(const cv::Mat& input) { cv::Mat lab; cv::cvtColor(input, lab, cv::COLOR_BGR2Lab); std::vector<cv::Mat> labChannels; cv::split(lab, labChannels); // 对L通道应用CLAHE cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(); clahe->setClipLimit(INDANE_CLIP_LIMIT); clahe->setTilesGridSize(cv::Size(INDANE_TILE_SIZE, INDANE_TILE_SIZE)); clahe->apply(labChannels[0], labChannels[0]); cv::Mat enhancedLab; cv::merge(labChannels, enhancedLab); cv::Mat result; cv::cvtColor(enhancedLab, result, cv::COLOR_Lab2BGR); return result; } // 逆滤波去模糊 cv::Mat applyInverseFilter(const cv::Mat& input) { // 创建模糊核(假设轻微高斯模糊) cv::Mat kernel = cv::getGaussianKernel(15, 1.5); kernel = kernel * kernel.t(); // 将图像转换为浮点型 cv::Mat floatInput; input.convertTo(floatInput, CV_32F); // 应用维纳滤波(逆滤波的稳定版本) cv::Mat result; cv::filter2D(floatInput, result, -1, kernel); // 将结果转换回8位 result.convertTo(result, CV_8U); return result; } // 图像预处理函数 cv::Mat preprocessImage(const cv::Mat& input) { if (!ENABLE_PREPROCESSING) { return input.clone(); } cv::Mat result = input.clone(); // 步骤1: INDANE图像增强 result = applyINDANE(result); // 步骤2: 逆滤波去模糊 result = applyInverseFilter(result); // 步骤3: 中值滤波去噪 cv::medianBlur(result, result, MEDIAN_BLUR_SIZE); return result; } // 自然排序比较函数 - 使文件名中的数字按数值大小排序 static bool naturalCompare(const std::string& a, const std::string& b) { // 将字符串转换为小写,实现不区分大小写的比较 std::string aLower = a; std::string bLower = b; std::transform(aLower.begin(), aLower.end(), aLower.begin(), ::tolower); std::transform(bLower.begin(), bLower.end(), bLower.begin(), ::tolower); // 使用正则表达式分割字符串中的数字和非数字部分 std::regex re("(\\d+)|(\\D+)"); std::sregex_iterator it1(aLower.begin(), aLower.end(), re); std::sregex_iterator it2(bLower.begin(), bLower.end(), re); std::sregex_iterator end; // 逐个比较分割后的部分 while (it1 != end && it2 != end) { std::smatch match1 = *it1; std::smatch match2 = *it2; // 如果两部分都是数字,则按数值比较 if (std::regex_match(match1.str(), std::regex("\\d+")) && std::regex_match(match2.str(), std::regex("\\d+"))) { int num1 = std::stoi(match1.str()); // 将字符串转换为整数 int num2 = std::stoi(match2.str()); if (num1 != num2) return num1 < num2; // 数值比较 } // 否则按字符串比较 else if (match1.str() != match2.str()) { return match1.str() < match2.str(); // 字符串比较 } // 移动到下一个部分 ++it1; ++it2; } // 如果前面的部分都相同,则按长度比较 return aLower.length() < bLower.length(); } // 特征匹配函数 std::vector<cv::DMatch> matchFeatures(const cv::Mat& des1, const cv::Mat& des2) { // 使用KNN匹配(K近邻匹配)找到最佳匹配 std::vector<std::vector<cv::DMatch>> knnMatches; matcher->knnMatch(des1, des2, knnMatches, 2); // k=2表示每个点找两个最佳匹配 // 筛选优质匹配点 std::vector<cv::DMatch> goodMatches; for (const auto& matchPair : knnMatches) { if (matchPair.size() == 2) { // 应用比率测试:最佳匹配的距离应小于次佳匹配距离的一定比例 if (matchPair[0].distance < MATCH_RATIO * matchPair[1].distance) { goodMatches.push_back(matchPair[0]); // 保留优质匹配 } } } // 输出匹配点数量信息 std::cout << "良好匹配点: " << goodMatches.size() << std::endl; return goodMatches; } // 计算单应性矩阵函数 cv::Mat findHomography(const std::vector<cv::KeyPoint>& kp1, const std::vector<cv::KeyPoint>& kp2, const std::vector<cv::DMatch>& matches, int& inliers) { // 检查是否有足够的匹配点 if (matches.size() < MIN_MATCHES) { return cv::Mat(); // 返回空矩阵 } // 提取匹配点的坐标 std::vector<cv::Point2f> srcPoints, dstPoints; for (const auto& match : matches) { srcPoints.push_back(kp2[match.trainIdx].pt); // 第二幅图像中的点 dstPoints.push_back(kp1[match.queryIdx].pt); // 第一幅图像中的点 } // 使用RANSAC算法计算单应性矩阵 cv::Mat mask; // 掩码,标记哪些点是内点(正确匹配) cv::Mat H = cv::findHomography(srcPoints, dstPoints, cv::RANSAC, RANSAC_THRESHOLD, mask); // 计算内点数量(正确匹配的点) inliers = 0; if (!mask.empty()) { inliers = cv::countNonZero(mask); // 统计非零元素数量 } // 输出内点信息 std::cout << "内点数量: " << inliers << "/" << matches.size() << std::endl; return H; // 返回单应性矩阵 } // 多频带融合 cv::Mat multiBandBlend(const std::vector<cv::Mat>& images, const std::vector<cv::Mat>& transforms) { if (images.empty()) return cv::Mat(); // 创建多频带混合器 cv::Ptr<cv::detail::MultiBandBlender> blender = cv::makePtr< cv::detail::MultiBandBlender >(); blender->setNumBands(static_cast<int>(ceil(log2(BLEND_WIDTH)) - 1)); // 准备混合器 cv::Rect dst_roi = cv::detail::resultRoi(transforms, images); blender->prepare(dst_roi); // 混合所有图像 for (size_t i = 0; i < images.size(); i++) { cv::Mat warped; cv::warpPerspective(images[i], warped, transforms[i], dst_roi.size()); // 创建掩码 cv::Mat mask = cv::Mat::ones(images[i].size(), CV_8U) * 255; cv::Mat warped_mask; cv::warpPerspective(mask, warped_mask, transforms[i], dst_roi.size()); // 混合图像 blender->feed(warped, warped_mask, cv::Point(0, 0)); } // 完成混合 cv::Mat result, result_mask; blender->blend(result, result_mask); return result; } // 加权平均融合 cv::Mat weightedBlend(const cv::Mat& img1, const cv::Mat& img2, const cv::Mat& H) { // 计算变换后图像的大小 std::vector<cv::Point2f> corners = { cv::Point2f(0, 0), cv::Point2f(img2.cols, 0), cv::Point2f(img2.cols, img2.rows), cv::Point2f(0, img2.rows) }; std::vector<cv::Point2f> warpedCorners; cv::perspectiveTransform(corners, warpedCorners, H); // 计算边界 float x_min = 0, x_max = img1.cols; float y_min = 0, y_max = img1.rows; for (const auto& pt : warpedCorners) { x_min = std::min(x_min, pt.x); x_max = std::max(x_max, pt.x); y_min = std::min(y_min, pt.y); y_max = std::max(y_max, pt.y); } // 创建结果图像 cv::Mat result(cv::Size(std::ceil(x_max - x_min), std::ceil(y_max - y_min)), img1.type()); // 应用变换 cv::Mat translation = cv::Mat::eye(3, 3, CV_64F); translation.at<double>(0, 2) = -x_min; translation.at<double>(1, 2) = -y_min; cv::Mat H_adjusted = translation * H; cv::warpPerspective(img2, result, H_adjusted, result.size()); // 叠加第一幅图像(使用加权平均) for (int y = 0; y < img1.rows; y++) { for (int x = 0; x < img1.cols; x++) { int result_x = x - x_min; int result_y = y - y_min; if (result_x >= 0 && result_x < result.cols && result_y >= 0 && result_y < result.rows) { cv::Vec3b pixel1 = img1.at<cv::Vec3b>(y, x); cv::Vec3b pixel2 = result.at<cv::Vec3b>(result_y, result_x); // 计算权重(简单线性混合) double weight = 0.5; if (x < img1.cols * 0.1) weight = 0.9; // 边缘区域权重更高 else if (x > img1.cols * 0.9) weight = 0.1; cv::Vec3b blended; for (int c = 0; c < 3; c++) { blended[c] = cv::saturate_cast<uchar>(weight * pixel1[c] + (1 - weight) * pixel2[c]); } result.at<cv::Vec3b>(result_y, result_x) = blended; } } } return result; } // 显示图像函数(用于中间过程) void showImage(const std::string& title, const cv::Mat& image) { if (image.empty()) return; // 检查图像是否为空 // 计算缩放比例,确保图像适合屏幕显示 int origH = image.rows; int origW = image.cols; double scale = std::min(DISPLAY_SCALE, 800.0 / origW); scale = std::min(scale, 600.0 / origH); scale = std::max(scale, 0.1); // 确保最小缩放比例 // 缩放图像 cv::Mat displayImg; cv::resize(image, displayImg, cv::Size(), scale, scale); // 显示图像 cv::imshow(title, displayImg); cv::waitKey(1); // 非阻塞等待,允许图像更新 } // 实时显示线程函数 void displayThreadFunc() { while (!stopDisplay) { { std::lock_guard<std::mutex> lock(panoramaMutex); if (!panorama.empty()) { showImage("实时拼接进度", panorama); } } std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 每100ms更新一次 } cv::destroyWindow("实时拼接进度"); } public: // 构造函数:初始化特征检测器类型并设置检测器 AdvancedImageStitcher(const std::string& detectorType = "SIFT") : detectorType(detectorType), stopDisplay(false) { setupDetectors(); } // 析构函数 ~AdvancedImageStitcher() { stopDisplay = true; } // 加载图像函数 void loadImages(const std::string& imageDir) { // 支持的图像扩展名 std::vector<std::string> extensions = { ".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif" }; // 使用OpenCV的glob函数查找图像文件 std::vector<cv::String> filePaths; cv::glob(imageDir + "/*", filePaths); std::vector<std::string> imagePaths; // 筛选图像文件 for (const auto& path : filePaths) { std::string ext = path.substr(path.find_last_of(".")); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end()) { imagePaths.push_back(path); } } // 检查是否找到图像文件 if (imagePaths.empty()) { std::cerr << "在 '" << imageDir << "' 目录下未找到任何图像文件" << std::endl; return; } // 使用自然排序对图像路径进行排序 std::sort(imagePaths.begin(), imagePaths.end(), naturalCompare); // 输出找到的图像信息 std::cout << "找到 " << imagePaths.size() << " 张图像:" << std::endl; for (size_t i = 0; i < imagePaths.size(); i++) { std::string fileName = imagePaths[i].substr(imagePaths[i].find_last_of("\\/") + 1); std::cout << " " << i + 1 << ": " << fileName << std::endl; } // 保存图像路径 this->imagePaths = imagePaths; // 加载并预处理图像 for (const auto& path : imagePaths) { // 读取图像 cv::Mat img = cv::imread(path); if (img.empty()) { std::cout << "无法加载图像: " << path << std::endl; continue; } // 图像预处理 cv::Mat processedImg = preprocessImage(img); // 缩减图像分辨率(如果太大) int h = processedImg.rows; int w = processedImg.cols; if (w > RESIZE_THRESHOLD || h > RESIZE_THRESHOLD) { double scale = RESIZE_THRESHOLD / static_cast<double>(std::max(w, h)); int newW = static_cast<int>(w * scale); int newH = static_cast<int>(h * scale); cv::resize(processedImg, processedImg, cv::Size(newW, newH), 0, 0, cv::INTER_AREA); std::cout << "已缩放: " << w << "x" << h << " -> " << newW << "x" << newH << std::endl; } // 将图像添加到向量中 images.push_back(processedImg); } // 检查是否成功加载了图像 if (images.empty()) { std::cerr << "未能成功加载任何图像" << std::endl; } } // 拼接图像函数 cv::Mat stitch() { // 如果只有一张或没有图像,直接返回 if (images.size() <= 1) { panorama = images.empty() ? cv::Mat() : images[0].clone(); return panorama; } // 启动实时显示线程 std::thread displayThread; if (SHOW_PROGRESS) { displayThread = std::thread(&AdvancedImageStitcher::displayThreadFunc, this); } // 从第一张图像开始 panorama = images[0].clone(); // 存储所有变换矩阵 std::vector<cv::Mat> transforms; transforms.push_back(cv::Mat::eye(3, 3, CV_64F)); // 第一张图像的变换矩阵是单位矩阵 // 依次拼接后续图像 for (size_t i = 1; i < images.size(); i++) { std::cout << "\n拼接图像 " << i + 1 << "/" << images.size() << std::endl; // 转换为灰度图(特征检测需要灰度图像) cv::Mat panoramaGray, newImgGray; cv::cvtColor(panorama, panoramaGray, cv::COLOR_BGR2GRAY); cv::cvtColor(images[i], newImgGray, cv::COLOR_BGR2GRAY); // 提取特征点和特征描述符 std::vector<cv::KeyPoint> kp1, kp2; cv::Mat des1, des2; detector->detectAndCompute(panoramaGray, cv::noArray(), kp1, des1); detector->detectAndCompute(newImgGray, cv::noArray(), kp2, des2); // 检查是否有足够的特征点 if (des1.empty() || des2.empty() || des1.rows < MIN_MATCHES || des2.rows < MIN_MATCHES) { std::cout << "特征点不足" << std::endl; continue; } // 特征匹配 std::vector<cv::DMatch> matches = matchFeatures(des1, des2); if (matches.size() < MIN_MATCHES) { std::cout << "匹配点不足: " << matches.size() << " < " << MIN_MATCHES << std::endl; continue; } // 计算单应性矩阵 int inliers; cv::Mat H = findHomography(kp1, kp2, matches, inliers); // 检查单应性矩阵是否有效 if (H.empty() || inliers < MIN_MATCHES) { std::cout << "单应性矩阵计算失败或内点不足: " << inliers << std::endl; continue; } // 保存变换矩阵 transforms.push_back(H); // 应用变换并融合图像 if (USE_MULTI_BAND_BLENDING && i == images.size() - 1) { // 如果是最后一张图像,使用多频带融合 std::cout << "使用多频带融合..." << std::endl; std::vector<cv::Mat> allImages(images.begin(), images.begin() + i + 1); cv::Mat newPanorama = multiBandBlend(allImages, transforms); if (!newPanorama.empty()) { std::lock_guard<std::mutex> lock(panoramaMutex); panorama = newPanorama; } } else { // 否则使用加权平均融合 cv::Mat newPanorama = weightedBlend(panorama, images[i], H); if (!newPanorama.empty()) { std::lock_guard<std::mutex> lock(panoramaMutex); panorama = newPanorama; } } // 如果启用进度显示,更新当前拼接状态 if (SHOW_PROGRESS) { std::lock_guard<std::mutex> lock(panoramaMutex); // 显示线程会自动处理显示 } } // 停止显示线程 if (SHOW_PROGRESS) { stopDisplay = true; displayThread.join(); } return panorama; // 返回最终的全景图 } // 显示最终结果 void showResult() { if (panorama.empty()) { std::cout << "没有可显示的结果" << std::endl; return; } // 计算缩放比例,确保图像适合屏幕显示 int h = panorama.rows; int w = panorama.cols; double scale = std::min(DISPLAY_SCALE, 1200.0 / w); scale = std::min(scale, 800.0 / h); scale = std::max(scale, 0.1); // 确保最小缩放比例 // 缩放图像 cv::Mat displayImg; cv::resize(panorama, displayImg, cv::Size(), scale, scale); // 创建窗口并显示结果 cv::namedWindow("拼接结果", cv::WINDOW_NORMAL); cv::imshow("拼接结果", displayImg); // 等待用户按键 std::cout << "按任意键关闭窗口..." << std::endl; cv::waitKey(0); cv::destroyAllWindows(); // 关闭所有OpenCV窗口 } // 保存结果到文件 bool saveResult(const std::string& filename = "panorama.png") { if (panorama.empty()) { std::cout << "没有可保存的结果" << std::endl; return false; } // 保存图像 std::string path = OUTPUT_DIR + "/" + filename; bool success = cv::imwrite(path, panorama); if (success) { std::cout << "结果已保存到: " << path << std::endl; } else { std::cout << "保存失败: " << path << std::endl; } return success; } }; // 主函数 int main() { // 创建拼接器对象,使用配置的特征检测器类型 AdvancedImageStitcher stitcher(FEATURE_DETECTOR); try { // 加载图像 std::cout << "加载图像..." << std::endl; stitcher.loadImages(IMAGE_DIR); // 拼接图像 std::cout << "开始拼接..." << std::endl; auto start_time = std::chrono::high_resolution_clock::now(); cv::Mat result = stitcher.stitch(); auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time); std::cout << "拼接完成,耗时: " << duration.count() << "ms" << std::endl; // 如果有结果,显示并保存 if (!result.empty()) { // 显示结果 std::cout << "显示结果..." << std::endl; stitcher.showResult(); // 保存结果 std::string filename = "industrial_panorama_" + FEATURE_DETECTOR + ".png"; std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); stitcher.saveResult(filename); } else { std::cout << "拼接失败" << std::endl; } } catch (const std::exception& e) { // 捕获并输出异常 std::cerr << "错误: " << e.what() << std::endl; } return 0; // 程序正常退出 }代码改正
最新发布
08-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值