当前位置:   article > 正文

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

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博客

源代码

  1. /**
  2. * 处理结果,可用于表示某个方法的处理状态。
  3. */
  4. struct ProcResult
  5. {
  6. int StatusCode; // 状态码
  7. std::string StatusDetail; // 状态详细信息,可以用来提供与对应状态的详细信息
  8. };
  9. struct ImageCombinerParam
  10. {
  11. std::vector<std::string> subImagePaths; // 要进行拼接的图像路径的列表
  12. std::vector<cv::Mat> subImageMats; // 要进行拼接的图像的列表
  13. bool byPath = true; // 按照路径subImagePaths来进行拼接,如果为false,则自己来设置subImageMats
  14. std::string saveImagePath; // 拼接后图像保存的路径
  15. bool enableImgBoarder = false; // 是否给要拼接的图像,进行边框绘制,方便观察原始图像在拼接完之后图像中的位置
  16. int direction = 1; // 1 - 横向, 2- 纵向
  17. };
  18. std::vector<std::string> Utils::findFilesByExt(const std::string dir, const std::string ext){
  19. std::vector<std::string> foundFiles;
  20. for (const auto& entry : fs::directory_iterator(dir)) {
  21. if (entry.is_regular_file() && entry.path().extension() == ext) {
  22. foundFiles.push_back(entry.path().string());
  23. }
  24. }
  25. return foundFiles;
  26. };
  27. cv::Vec3b CVHelper::getRandColor(){
  28. // 获取当前时间点
  29. auto now = std::chrono::system_clock::now();
  30. // 将时间点转换为time_t类型
  31. std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
  32. // 初始化随机数种子
  33. // srand(static_cast<unsigned int>(currentTime));
  34. // srand(static_cast<unsigned int>(time(0)));
  35. // 生成随机颜色
  36. cv::Vec3b randomColor(rand() % 256, rand() % 256, rand() % 256);
  37. // cv::Scalar randomColor(rand() % 256, rand() % 256, rand() % 256);
  38. return randomColor;
  39. };
  40. ProcResult ImageCombiner::combineSubImages(ImageCombinerParam param){
  41. ProcResult result;
  42. if((param.byPath && param.subImagePaths.size() <= 0) || (!param.byPath && param.subImageMats.size() <=0)){
  43. std::cerr << "there is no images." << std::endl;
  44. result.StatusCode = 1;
  45. result.StatusDetail = "there is no images.";
  46. return result;
  47. }
  48. std::vector<cv::Mat> imageMats;
  49. if(param.byPath){
  50. for(size_t i = 0; i < param.subImagePaths.size(); i++){
  51. cv::Mat img = cv::imread(param.subImagePaths[i]);
  52. if(!img.empty()){
  53. if(imageMats.size() > 0 && img.type()!= imageMats[0].type()){
  54. img.convertTo(img, imageMats[0].type());
  55. }
  56. imageMats.push_back(img);
  57. }else{
  58. std::string errMsg = "fail to read image. img_path:" + param.subImagePaths[i];
  59. SPDLOG_ERROR(errMsg);
  60. }
  61. }
  62. }else{
  63. imageMats = param.subImageMats;
  64. }
  65. if(param.byPath && imageMats.size() != param.subImagePaths.size()){
  66. result.StatusCode = 2;
  67. std::string errorMsg = "fail to read all images.";
  68. result.StatusDetail = errorMsg;
  69. return result;
  70. }else{
  71. if(fs::exists(param.saveImagePath)){
  72. fs::remove(param.saveImagePath);
  73. }
  74. fs::path savePath(param.saveImagePath);
  75. if(!fs::exists(savePath.parent_path())){
  76. fs::create_directories(savePath.parent_path());
  77. }
  78. if (param.direction == 1){ // 横向合并
  79. int combinedWidth = 0;
  80. int combinedHeight = 0;
  81. for(size_t i = 0; i < imageMats.size(); i++){
  82. cv::Mat img = imageMats[i];
  83. combinedWidth += img.cols;
  84. if(img.rows > combinedHeight){
  85. combinedHeight = img.rows;
  86. }
  87. if(param.enableImgBoarder){
  88. cv::Vec3b randomColor = CVHelper::getRandColor();
  89. cv::rectangle(img, cv::Point(0, 0), cv::Point(img.cols, img.rows), randomColor, 5, cv::LINE_8);
  90. }
  91. }
  92. cv::Mat combinedImg = cv::Mat(combinedHeight, combinedWidth , imageMats[0].type(), cv::Scalar(0, 0, 0));
  93. int startX = 0;
  94. int startY = 0;
  95. for(size_t i = 0; i < imageMats.size(); i++){
  96. cv::Mat img = imageMats[i];
  97. imageMats[i].copyTo(combinedImg(cv::Rect(startX, startY, img.cols, img.rows)));
  98. startX += img.cols;
  99. }
  100. cv::imwrite(param.saveImagePath, combinedImg);
  101. result.StatusCode = common::STATUS_CODE_OK;
  102. result.StatusDetail = "combine success by cols";
  103. return result;
  104. }else{ // 纵向合并
  105. int combinedWidth = 0;
  106. int combinedHeight = 0;
  107. for(size_t i = 0; i < imageMats.size(); i++){
  108. cv::Mat img = imageMats[i];
  109. combinedHeight += img.rows;
  110. if(img.cols > combinedWidth){
  111. combinedWidth = img.cols;
  112. }
  113. if(param.enableImgBoarder){
  114. cv::Vec3b randomColor = CVHelper::getRandColor();
  115. cv::rectangle(img, cv::Point(0, 0), cv::Point(img.cols, img.rows), randomColor, 5, cv::LINE_8);
  116. }
  117. }
  118. cv::Mat combinedImg = cv::Mat(combinedHeight, combinedWidth , imageMats[0].type(), cv::Scalar(0, 0, 0));
  119. int startX = 0;
  120. int startY = 0;
  121. for(size_t i = 0; i < imageMats.size(); i++){
  122. cv::Mat img = imageMats[i];
  123. imageMats[i].copyTo(combinedImg(cv::Rect(startX, startY, img.cols, img.rows)));
  124. startY += img.rows;
  125. }
  126. cv::imwrite(param.saveImagePath, combinedImg);
  127. result.StatusCode = common::STATUS_CODE_OK;
  128. result.StatusDetail = "combine success by rows";
  129. return result;
  130. }
  131. }
  132. };

测试代码

测试代码中使用了命令行参数,如何配置cxxopts,可以阅读 [C++] 第三方库命令行解析库argparse和cxxopts介绍和使用-CSDN博客

  1. #include <filesystem>
  2. #include <iostream>
  3. #include <opencv2/opencv.hpp>
  4. #include <algorithm.hpp>
  5. #include <cxxopts.hpp>
  6. using namespace cv;
  7. using namespace std;
  8. namespace fs = std::filesystem;
  9. namespace fdect = flaw_detect;
  10. int main(int argc,char **argv){
  11. try{
  12. cxxopts::Options options("combine_image", "Cut multiple images to a big by a image list");
  13. options.add_options()
  14. ("imgdir", "要合并的图像所在目录", cxxopts::value<std::string>()->default_value("D:/LocalTest/ResizeImages/img_for_combine"))
  15. ("img_bd", "是否给要合并的图像添加边框", cxxopts::value<bool>()->default_value("true"))
  16. ("reverse", "按图像名称进行倒序", cxxopts::value<bool>()->default_value("true"))
  17. ("savepath", "合并之后的图像保存的路径", cxxopts::value<std::string>()->default_value("D:/LocalTest/ResizeImages/resize_combined.bmp"))
  18. ("help", "使用帮助");
  19. auto cliArgs = options.parse(argc, argv);
  20. if (cliArgs.count("help"))
  21. {
  22. std::cout << options.help() << std::endl;
  23. exit(0);
  24. }
  25. std::string imgDir = cliArgs["imgdir"].as<std::string>();
  26. bool enableImgBorder = cliArgs["img_bd"].as<bool>();
  27. bool reverse = cliArgs["reverse"].as<bool>();
  28. std::string savePath = cliArgs["savepath"].as<std::string>();
  29. std::cout << "imgDir:" << imgDir << ", enableImgBorder:" << enableImgBorder << ", savePath:" << savePath
  30. << std::endl;
  31. std::vector<std::string> imageFiles = fdect::common::Utils::findFilesByExt(imgDir, ".bmp");
  32. if (reverse){
  33. sort(imageFiles.rbegin(), imageFiles.rend());
  34. }else{
  35. sort(imageFiles.begin(), imageFiles.end());
  36. }
  37. fdect::algorithm::ImageCombinerParam param;
  38. param.subImagePaths = imageFiles;
  39. param.enableImgBoarder = enableImgBorder;
  40. param.saveImagePath = savePath;
  41. param.direction = 1;
  42. fdect::algorithm::ImageCombiner combiner;
  43. fdect::common::ProcResult result = combiner.combineSubImages(param);
  44. std::cout << "处理结果: (" << result.StatusCode << ", " << result.StatusDetail << ")" << std::endl;
  45. return 0;
  46. }catch( Exception e){
  47. std::cerr << "发生异常. 异常信息:" << e.what() << std::endl;
  48. return -1;
  49. }
  50. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/1011688
推荐阅读
相关标签
  

闽ICP备14008679号