赞
踩
图像拼接是指将多张图片按照一定的规则和算法进行组合,形成一张大图的过程。在实际应用中,常常需要将多张拍摄的图片拼接成一幅完整的场景或物体照片。
图像拼接是一个图像处理过程中常见的应用场景。我们可以通过opencv库非常容易实现多个图像的横向或者纵向的拼接。
[C++] opencv - Mat类的介绍和使用场景-CSDN博客
[C++] opencv - copyTo函数介绍和使用案例-CSDN博客
[C++] opencv - convertTo函数介绍和使用场景-CSDN博客
[C++] opencv - imwrite函数介绍和使用场景_c++ opencv imwrite-CSDN博客
[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博客
- #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;
- }
- }
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。