关于源代码
源代码和用到的支持超过1G像素大小的opencv库(vc17+vs2022)已经上传到csdn,可以通过博文的标题下方提供连接进行下载。
创作背景
最近在做一个电路底板的缺陷检测项目,线扫相机保存下来的bmp图像大概为1.5G,像素大小为30000+ x 80000+,在进行缺陷分析之前,需要把bmp大图先切成1280x1280或者640x640的小图,然后在小图上使用yolov8进行缺陷分析。
本文将会介绍如何通过opencv大的bmp图像,切换成1280 x 1280的小图。
开发思路
通过opencv从本地读取bmp图像,然后在按照输入的图像尺寸进行图像分割,一边分割一边按照一定的命名规则来把小图保存到本地目录。
源代码
#include <filesystem>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
namespace fs = std::filesystem;
/**
* src: image matix from opencv for cut.
* subWidth: The width for sub image.
* subHeight: The height for sub image.
* subSaveDir: The save directory for sub images.
* subImgNamePrefix: The prefix for sub image.
* subImgNameExt: The extension name for sub image.
* clearSubImages: The flag to clear the old sub images in subSaveDir or not.
*/
int cutImage(Mat& src, int subWidth, int subHeight, const cv::String& subSaveDir="./cut_images",
const cv::String& subImgNamePrefix="", const cv::String subImgNameExt=".jpg", bool clearSubImages=false)
{
if (!fs::is_directory(subSaveDir)){
std::cerr << "save dir is not a directory." << std::endl;
return -1;
}
if(clearSubImages && fs::exists(subSaveDir)){
fs::remove_all(subSaveDir);
std::cout << "clear sub dir:" << subSaveDir << std::endl;
}
if (!fs::exists(subSaveDir)){
fs::create_directories(subSaveDir);
std::cout << "create save dir:" << subSaveDir << std::endl;
}
int srcHeight, srcWidth, subImageNumY, subImageNumX;
srcHeight = src.rows;
srcWidth = src.cols;
subImageNumY = srcHeight / subHeight;
if (srcHeight % subHeight > 0) {
subImageNumY += 1;
}
subImageNumX = srcWidth / subWidth;
if (srcWidth % subWidth > 0) {
subImageNumX += 1;
}
std::cout << "y count:" << subImageNumY << ",x count:" << subImageNumX << ", total:" << subImageNumY * subImageNumX << std::endl;
int failCount = 0;
for (int j = 0; j < subImageNumY; j++)
{
for (int i = 0; i < subImageNumX; i++)
{
int endWidth = std::min((i + 1) * subWidth, srcWidth);
int endHeight = std::min((j + 1) * subHeight, srcHeight);
Mat imageROI = src(Range(j * subHeight, endHeight), Range(i * subWidth, endWidth));
std::cout << "y:" << j << ", x:" << i << std::endl;
//输出保存各子图像
string subImgName = subImgNamePrefix + std::to_string(j) + "_" + std::to_string(i) + subImgNameExt;
fs::path subSavePath;
subSavePath.append(subSaveDir);
subSavePath.append(subImgName);
if(!imwrite(subSavePath.string(), imageROI)){
std::cerr << "save sub image failure. sub_img_path: " << subSavePath.string() << std::endl;
failCount ++;
};
}
}
std::cout << "total: " << subImageNumY * subImageNumX << ", fail: " << failCount << std::endl;
return 0;
}
int main(int argc,char **argv){
// ./cut_image.exe D:/LearnFlawDetect/CLearnFlawDetect/CircleCut/Images/camera_3.bmp 1280 ./camera_3
if (argc < 4){
std::cerr << "格式错误. 请使用正确格式 'cut_image.exe 要切割的bmp的路径 切割的子图的大小 切割的子图的保存路径'" << std::endl;
return -1;
}
try{
char *srcImgPath = argv[1];
int cutImgSize = std::stoi(argv[2]);
if (cutImgSize < 320 || cutImgSize > 640 * 2){
std::cerr << "sub img size must >=320 and <=1280" << std::endl;
return -1;
}
char *saveImgDir = argv[3];
std::cout << "src_img_path:" << srcImgPath << ", cut_img_size:" << cutImgSize << ", save_dir:" << saveImgDir << std::endl;
Mat grayImg = imread(srcImgPath, IMREAD_GRAYSCALE);
if (!grayImg.empty()) {
std::cout << "shape:" << grayImg.rows << "," << grayImg.cols << std::endl;
cutImage(grayImg, cutImgSize, cutImgSize, saveImgDir, "", ".jpg", true);
}
return 0;
}catch( Exception e){
std::cerr << "发生异常. 异常信息:" << e.what() << std::endl;
return -1;
}
}
如何运行
运行环境
Windows10 + CMake + VS 2022(C++17)
安装CMake
如何安装Cmake, 可以参考另一篇文章:
[CMake] 基础教程 - CMake安装和验证测试(Windows+Linux)
安装VS 2022社区版
如何安装VS 2022社区版,参考另一篇文章:
[Visual Studio] 基础教程 - Window10下如何安装VS 2022社区版
安装和配置opencv
关于如何安装,可以参考另一篇文章:
[C++] 详细教程 - opencv4.8.0安装和验证测试 (Windows + Linux)
因为需要读取的bmp图像超过了1G,默认从官网下载的库读取超过1G的图像是会报错的,如何解决这个问题,可以参考另一篇文章:
[C++] 问题分析和解决 - 如何使用opencv处理大于1G的bmp图像
下载源代码并解压
通过CMake来构建项目
# 通过控制台你的解压目录
cmake -S . -B build
cmake --build build
通过命令行窗口运行
进阶探讨
因为图像比较大,会切割出1000+的小图,后续考虑增加多线程来进行切割和保存,整体提升一下运行的速度。