什么是圖像拼接?
圖像拼接是指將多張圖片按照一定的規(guī)則和算法進行組合,形成一張大圖的過程。在實際應(yīng)用中,常常需要將多張拍攝的圖片拼接成一幅完整的場景或物體照片。
圖像拼接是一個圖像處理過程中常見的應(yīng)用場景。我們可以通過opencv庫非常容易實現(xiàn)多個圖像的橫向或者縱向的拼接。
使用的opencv函數(shù)或者類
Mat類
[C++] opencv - Mat類的介紹和使用場景-CSDN博客
copyTo函數(shù)
[C++] opencv - copyTo函數(shù)介紹和使用案例-CSDN博客
convertTo函數(shù)
[C++] opencv - convertTo函數(shù)介紹和使用場景-CSDN博客
imwrite函數(shù)
[C++] opencv - imwrite函數(shù)介紹和使用場景_c++ opencv imwrite-CSDN博客文章來源:http://www.zghlxwxcb.cn/news/detail-801936.html
imread函數(shù)
[C++] opencv - imwrite函數(shù)介紹和使用場景_c++ opencv imwrite-CSDN博客
源代碼
/**
* 處理結(jié)果,可用于表示某個方法的處理狀態(tài)。
*/
struct ProcResult
{
int StatusCode; // 狀態(tài)碼
std::string StatusDetail; // 狀態(tài)詳細(xì)信息,可以用來提供與對應(yīng)狀態(tài)的詳細(xì)信息
};
struct ImageCombinerParam
{
std::vector<std::string> subImagePaths; // 要進行拼接的圖像路徑的列表
std::vector<cv::Mat> subImageMats; // 要進行拼接的圖像的列表
bool byPath = true; // 按照路徑subImagePaths來進行拼接,如果為false,則自己來設(shè)置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(){
// 獲取當(dāng)前時間點
auto now = std::chrono::system_clock::now();
// 將時間點轉(zhuǎn)換為time_t類型
std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
// 初始化隨機數(shù)種子
// 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;
}
}
};
測試代碼
測試代碼中使用了命令行參數(shù),如何配置cxxopts,可以閱讀?[C++] 第三方庫命令行解析庫argparse和cxxopts介紹和使用-CSDN博客文章來源地址http://www.zghlxwxcb.cn/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 << "處理結(jié)果: (" << result.StatusCode << ", " << result.StatusDetail << ")" << std::endl;
return 0;
}catch( Exception e){
std::cerr << "發(fā)生異常. 異常信息:" << e.what() << std::endl;
return -1;
}
}
到了這里,關(guān)于[C++] 如何使用opencv對多個圖像進行橫向或者縱向拼接的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!