[C++] 如何使用opencv对多个图像进行横向或者纵向拼接

这篇具有很好参考价值的文章主要介绍了[C++] 如何使用opencv对多个图像进行横向或者纵向拼接。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是图像拼接?

图像拼接是指将多张图片按照一定的规则和算法进行组合,形成一张大图的过程。在实际应用中,常常需要将多张拍摄的图片拼接成一幅完整的场景或物体照片。

图像拼接是一个图像处理过程中常见的应用场景。我们可以通过opencv库非常容易实现多个图像的横向或者纵向的拼接。

使用的opencv函数或者类

Mat类

[C++] opencv - Mat类的介绍和使用场景-CSDN博客

copyTo函数

[C++] opencv - copyTo函数介绍和使用案例-CSDN博客

convertTo函数

[C++] opencv - convertTo函数介绍和使用场景-CSDN博客

imwrite函数

[C++] opencv - imwrite函数介绍和使用场景_c++ opencv imwrite-CSDN博客

imread函数

[C++] opencv - imwrite函数介绍和使用场景_c++ opencv imwrite-CSDN博客

源代码

/**
 * 处理结果,可用于表示某个方法的处理状态。
*/
struct ProcResult
{
    int StatusCode; // 状态码
    std::string StatusDetail; // 状态详细信息,可以用来提供与对应状态的详细信息
};


struct ImageCombinerParam
{
    std::vector<std::string> subImagePaths; // 要进行拼接的图像路径的列表
    std::vector<cv::Mat> subImageMats;  // 要进行拼接的图像的列表
    bool byPath = true;  // 按照路径subImagePaths来进行拼接,如果为false,则自己来设置subImageMats
    std::string saveImagePath;  // 拼接后图像保存的路径
    bool enableImgBoarder = false;  // 是否给要拼接的图像,进行边框绘制,方便观察原始图像在拼接完之后图像中的位置
    int direction = 1; // 1 - 横向, 2- 纵向
};

std::vector<std::string> Utils::findFilesByExt(const std::string dir, const std::string ext){
    std::vector<std::string> foundFiles;
    for (const auto& entry : fs::directory_iterator(dir)) {
        if (entry.is_regular_file() && entry.path().extension() == ext) {
            foundFiles.push_back(entry.path().string());
        }
    }
    return foundFiles;
};


cv::Vec3b CVHelper::getRandColor(){
    // 获取当前时间点
    auto now = std::chrono::system_clock::now();
    
    // 将时间点转换为time_t类型
    std::time_t currentTime = std::chrono::system_clock::to_time_t(now);

    // 初始化随机数种子
    // srand(static_cast<unsigned int>(currentTime)); 
    // srand(static_cast<unsigned int>(time(0)));

    // 生成随机颜色
    cv::Vec3b randomColor(rand() % 256, rand() % 256, rand() % 256);
    // cv::Scalar randomColor(rand() % 256, rand() % 256, rand() % 256);

    return randomColor;
};

ProcResult ImageCombiner::combineSubImages(ImageCombinerParam param){
    ProcResult result;
    if((param.byPath && param.subImagePaths.size() <= 0) || (!param.byPath && param.subImageMats.size() <=0)){
        std::cerr << "there is no images." << std::endl;
        result.StatusCode = 1;
        result.StatusDetail = "there is no images.";
        return result;
    }
    std::vector<cv::Mat> imageMats;
    if(param.byPath){
        for(size_t i = 0; i < param.subImagePaths.size(); i++){
            cv::Mat img = cv::imread(param.subImagePaths[i]);
            if(!img.empty()){
                if(imageMats.size() > 0 && img.type()!= imageMats[0].type()){
                    img.convertTo(img, imageMats[0].type());
                }
                imageMats.push_back(img);
            }else{
                std::string errMsg = "fail to read image. img_path:" + param.subImagePaths[i];
                SPDLOG_ERROR(errMsg);
            }
        }
    }else{
        imageMats = param.subImageMats;
    }

    if(param.byPath && imageMats.size() != param.subImagePaths.size()){
        result.StatusCode = 2;
        std::string errorMsg = "fail to read all images.";
        result.StatusDetail = errorMsg;
        return result;
    }else{
        if(fs::exists(param.saveImagePath)){
            fs::remove(param.saveImagePath);
        }
        fs::path savePath(param.saveImagePath);
        if(!fs::exists(savePath.parent_path())){
            fs::create_directories(savePath.parent_path());
        }
        if (param.direction == 1){ // 横向合并
            int combinedWidth = 0;
            int combinedHeight = 0;
            for(size_t i = 0; i < imageMats.size(); i++){
                cv::Mat img = imageMats[i];
                combinedWidth += img.cols;
                if(img.rows > combinedHeight){
                    combinedHeight = img.rows;
                }
                if(param.enableImgBoarder){
                    cv::Vec3b randomColor = CVHelper::getRandColor();
                    cv::rectangle(img, cv::Point(0, 0), cv::Point(img.cols, img.rows),  randomColor, 5, cv::LINE_8);
                }
            }
    
            cv::Mat combinedImg = cv::Mat(combinedHeight, combinedWidth , imageMats[0].type(), cv::Scalar(0, 0, 0));
            int startX = 0;
            int startY = 0;
            for(size_t i = 0; i < imageMats.size(); i++){
                cv::Mat img = imageMats[i];
                imageMats[i].copyTo(combinedImg(cv::Rect(startX, startY, img.cols, img.rows)));
                startX += img.cols;
            }
            cv::imwrite(param.saveImagePath, combinedImg);
            result.StatusCode = common::STATUS_CODE_OK;
            result.StatusDetail = "combine success by cols";
            return result;
        }else{ // 纵向合并
            int combinedWidth = 0;
            int combinedHeight = 0;
            for(size_t i = 0; i < imageMats.size(); i++){
                cv::Mat img = imageMats[i];
                combinedHeight += img.rows;
                if(img.cols > combinedWidth){
                    combinedWidth = img.cols;
                }
                
                if(param.enableImgBoarder){
                    cv::Vec3b randomColor = CVHelper::getRandColor();
                    cv::rectangle(img, cv::Point(0, 0), cv::Point(img.cols, img.rows),  randomColor, 5, cv::LINE_8);
                }
            }
    
            cv::Mat combinedImg = cv::Mat(combinedHeight, combinedWidth , imageMats[0].type(), cv::Scalar(0, 0, 0));
            int startX = 0;
            int startY = 0;
            for(size_t i = 0; i < imageMats.size(); i++){
                cv::Mat img = imageMats[i];
                imageMats[i].copyTo(combinedImg(cv::Rect(startX, startY, img.cols, img.rows)));
                startY += img.rows;
            }
            
            cv::imwrite(param.saveImagePath, combinedImg);
            result.StatusCode = common::STATUS_CODE_OK;
            result.StatusDetail = "combine success by rows";
            return result;
        }
    }
};

测试代码

测试代码中使用了命令行参数,如何配置cxxopts,可以阅读 [C++] 第三方库命令行解析库argparse和cxxopts介绍和使用-CSDN博客文章来源地址https://www.toymoban.com/news/detail-801936.html

#include <filesystem>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <algorithm.hpp>
#include <cxxopts.hpp>

using namespace cv;
using namespace std;
namespace fs = std::filesystem;
namespace fdect = flaw_detect;

int main(int argc,char **argv){
    try{
        cxxopts::Options options("combine_image", "Cut multiple images to a big by a image list");
        options.add_options()
            ("imgdir", "要合并的图像所在目录", cxxopts::value<std::string>()->default_value("D:/LocalTest/ResizeImages/img_for_combine"))
            ("img_bd", "是否给要合并的图像添加边框", cxxopts::value<bool>()->default_value("true"))
            ("reverse", "按图像名称进行倒序", cxxopts::value<bool>()->default_value("true"))
            ("savepath", "合并之后的图像保存的路径", cxxopts::value<std::string>()->default_value("D:/LocalTest/ResizeImages/resize_combined.bmp"))
            ("help", "使用帮助");
        
        auto cliArgs = options.parse(argc, argv);
        if (cliArgs.count("help"))
        {
            std::cout << options.help() << std::endl;
            exit(0);
        }
        std::string imgDir = cliArgs["imgdir"].as<std::string>();
        bool enableImgBorder =  cliArgs["img_bd"].as<bool>();
        bool reverse =  cliArgs["reverse"].as<bool>();
        std::string savePath = cliArgs["savepath"].as<std::string>();
        
        std::cout << "imgDir:" << imgDir << ", enableImgBorder:" << enableImgBorder << ", savePath:" << savePath 
        << std::endl;

        std::vector<std::string> imageFiles = fdect::common::Utils::findFilesByExt(imgDir, ".bmp");
        if (reverse){
            sort(imageFiles.rbegin(), imageFiles.rend());
        }else{
            sort(imageFiles.begin(), imageFiles.end());
        }
        fdect::algorithm::ImageCombinerParam param;
        param.subImagePaths = imageFiles;
        param.enableImgBoarder = enableImgBorder;
        param.saveImagePath = savePath;
        param.direction = 1;
        fdect::algorithm::ImageCombiner combiner;
        fdect::common::ProcResult result = combiner.combineSubImages(param);
        std::cout << "处理结果: (" << result.StatusCode << ", " << result.StatusDetail << ")" << std::endl;

        return 0;
    }catch( Exception e){
        std::cerr << "发生异常. 异常信息:" << e.what() << std::endl;
        return -1;
    }
}

到了这里,关于[C++] 如何使用opencv对多个图像进行横向或者纵向拼接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 详细介绍如何使用 OpenCV 对图像进行锐化

    将了解锐化图像的过程 , 我们将使用内核来突出显示每个特定像素并增强其发出的颜色。 它与模糊过程非常相似,只不过现在我们不是创建一个内核来平均每个像素强度,而是创建一个内核,该内核将使像素强度更高,因此对人眼来说更加突出。 很高兴知道 内核用于模糊

    2024年02月12日
    浏览(26)
  • 如何使用Python中的OpenCV对图像进行调整大小?

    OpenCV  提供了函数  cv2.resize()  用于调整图像大小。OpenCV中的调整大小被称为  缩放  。我们可以通过指定图像大小或缩放因子来调整图像大小。当我们指定缩放因子时,宽高比会得到保留。 cv2.resize()  函数中有不同的插值方法: cv2.INTER_AREA  —— 用于缩小图像。 cv2.INTER

    2024年02月04日
    浏览(39)
  • 关于uniapp使用srcoll-view视图是横向及纵向滚动

            今天在用HBuilder X写uniapp项目时遇到了需要使用滚动视图的地方,为了简单省事自然就选择了srcoll-view这个标签。在写完后实验了一下,微信小程序时没有问题,但是h5页面却出问题了。         问题大致分为两类:横向、纵向         共有的问题有:给scroll-view加上

    2024年02月16日
    浏览(39)
  • 如何在Flutter应用中使用 OpenCV和 CC++库进行图像流处理

    本文将帮助你在 Android 和 iOS 中为 Flutter 应用程序集成 C/C++ 插件。 问题1: Flutter camera 插件没有为快速复杂的图像流处理提供完整的指南。 问题2: Flutter camera 插件处理图像流太慢。 问题3: 图像处理需要OpenCV包 问题4: 你当前的图像流处理实现正在阻塞 UI 并导致你的应用程

    2024年02月08日
    浏览(48)
  • Baumer工业相机堡盟工业相机如何使用BGAPISDK和OpenCV设置图像进行比例显示(C++)

    Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩

    2024年02月10日
    浏览(32)
  • 轮播图横向和纵向同时滚动

    轮播图横向和纵向同时滚动: 横向和纵向同时滚动

    2024年02月09日
    浏览(32)
  • Excel·VBA表格横向、纵向相互转换

    如图:对图中区域 A1:M6 横向表格,转换成区域 A1:C20 纵向表格,即 B:M 列转换成每2列一组按行写入,并删除空行。同理,反向操作就是纵向表格转换成横向表格 实现方法1 对 本文图1 中,按“交期和交货数量”每5行2列为一组,依次按行写入,即按“交期”顺序排列 转换结果

    2024年02月14日
    浏览(33)
  • 微信小程序如何让多个按钮横向排列

    WXML界面添加如下代码: WXSS界面添加如下代码: 使用了Felx布局,要调整按键位置主要操控margin、width这几个属性 调整效果如下:  

    2024年02月05日
    浏览(38)
  • echarts实现横向和纵向滚动条、dataZoom

    使用 echarts 会遇到这种情况,以柱状图为例子,当数据过多时, echarts 图就会堆叠在一起,看起来十分难看。通常解决办法是通过减小 barWidth 值来缩小柱子宽度,但是若数据达到上百条,这场面是相当壮观。另一个很常用的就是在外部容器 div 添加 overflow: scroll; ,这确实能解

    2024年02月11日
    浏览(38)
  • 原生微信小程序 动态(横向,纵向)公告(广告)栏

    先看一下动态效果 Y轴滚动公告的原理是 swiper 组件在页面中的Y轴滚动,属性 vertical ,其余属性也设置一下 autoplay circular interval=\\\"3000\\\" X轴滚动的原理是,利用动画效果,将内容从右往左过渡过去 wxml: wxss: less:   js:

    2024年02月10日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包