代码简介
这个代码主要是项目需要,因为海康的超脑给的demo是处理bg文件格式,且由于为了迎合原本yolov5模型的极小目标检测场景。海康摄像头采集的大图resize检测效果太差,只能考虑把大图裁剪成多个小图做推理提升效果。代码有C++和opencv两个版本。其他的都是基于基本库实现的。
代码结构
整体上,该程序主要包括以下几个功能模块:
-
文件遍历模块:使用
listAllFiles
函数对指定目录进行递归遍历,获取所有图片文件的路径列表。 -
文件夹创建模块:使用
createDirectory
函数创建输出文件夹,用于存储裁剪后的图片。 -
图像裁剪与保存模块:使用
cropAndSave
函数对每张图片进行裁剪,并将裁剪后的图像数据以二进制格式保存到指定的文件中。 -
主函数模块:在
main
函数中调用以上模块,实现整体的功能流程。
函数详解
头文件
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <dirent.h>
#include <sys/stat.h>
listAllFiles函数
- 功能:遍历指定目录下的所有文件,并返回所有图片文件的路径列表。
- 参数:
- rootdir:根目录路径。
- 返回值:包含所有图片文件路径的字符串向量。
- 实现原理:使用
opendir
和readdir
函数遍历指定目录下的所有文件和子目录。对于每一个文件和子目录,使用stat
函数获取其文件信息,判断是否是文件夹或者图片文件。对于文件夹,递归调用listAllFiles
函数;对于图片文件,将其路径添加到结果列表中。std::vector<std::string> listAllFiles(const std::string& rootdir) { std::vector<std::string> files; DIR* dir = opendir(rootdir.c_str()); if (dir == nullptr) { return files; } dirent* entry; while ((entry = readdir(dir)) != nullptr) { std::string path = rootdir + "/" + entry->d_name; struct stat statbuf; if (stat(path.c_str(), &statbuf) == -1) { continue; } if (S_ISDIR(statbuf.st_mode)) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } std::vector<std::string> subFiles = listAllFiles(path); files.insert(files.end(), subFiles.begin(), subFiles.end()); } else if (S_ISREG(statbuf.st_mode)) { std::string extension = path.substr(path.find_last_of(".") + 1); std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); if (extension == "jpg" || extension == "png" || extension == "jpeg" || extension == "bmp") { files.push_back(path); } } } closedir(dir); return files; }
createDirectory函数
- 功能:创建指定路径的文件夹。
- 参数:
- path:要创建的文件夹路径。
- 返回值:创建成功返回true,否则返回false。
- 实现原理:使用
mkdir
函数创建指定路径的文件夹,并设置权限。bool createDirectory(const std::string& path) { int ret = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if (ret == -1) { return false; } return true; }
cropAndSave函数
- 功能:对输入的图像进行裁剪并保存为二进制文件。
- 参数:
- img:输入的图像。
- output_path:保存输出文件的路径。
- crop_size:裁剪的尺寸。
- 返回值:无。
- 实现原理:首先计算图像的高度和宽度,然后计算裁剪时需要添加的底部和右侧填充大小。使用
copyMakeBorder
函数对图像进行填充,然后根据裁剪尺寸和填充后的图像大小计算裁剪的行数和列数。使用循环对图像进行裁剪,并将每个通道的数据分别保存到二进制文件中。void cropAndSave(const cv::Mat& img, const std::string& output_path, const cv::Size& crop_size) { int height = img.rows; int width = img.cols; // Calculate the padding size int pad_bottom = crop_size.height - (height % crop_size.height); if (pad_bottom == crop_size.height) { pad_bottom = 0; } int pad_right = crop_size.width - (width % crop_size.width); if (pad_right == crop_size.width) { pad_right = 0; } cv::Mat padded_img; cv::copyMakeBorder(img, padded_img, 0, pad_bottom, 0, pad_right, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); int num_cols = padded_img.cols / crop_size.width; int num_rows = padded_img.rows / crop_size.height; int count = 0; for (int row = 0; row < num_rows; ++row) { for (int col = 0; col < num_cols; ++col) { int y1 = row * crop_size.height; int y2 = y1 + crop_size.height; int x1 = col * crop_size.width; int x2 = x1 + crop_size.width; cv::Mat crop_img = padded_img(cv::Rect(x1, y1, crop_size.width, crop_size.height)); std::string output_file = output_path + "/" + std::to_string(count) + ".bgr"; cv::Mat channels[3]; cv::split(crop_img, channels); std::ofstream outfile(output_file, std::ios::binary); outfile.write(reinterpret_cast<char*>(channels[0].data), crop_size.width * crop_size.height); outfile.write(reinterpret_cast<char*>(channels[1].data), crop_size.width * crop_size.height); outfile.write(reinterpret_cast<char*>(channels[2].data), crop_size.width * crop_size.height); outfile.close(); ++count; } } }
主函数(main函数)
- 功能:代码的入口函数。
- 参数:无。
- 返回值:整型,程序运行结果。
- 实现原理:定义输入和输出的根目录路径、裁剪尺寸等变量。调用
listAllFiles
函数获取所有图片文件的路径列表。遍历每个图片文件,提取文件名并创建对应的输出文件夹。读取图像并调用cropAndSave
函数进行裁剪和保存。同时将裁剪后的文件路径写入到输出文件中。最后关闭输出文件,返回0表示程序正常结束。int main() { std::string input_root = "./image_input"; std::string output_root = "./bgr"; std::string txt_path = "./txt文本"; cv::Size crop_size(512, 512); // 切割小图的大小 std::vector<std::string> img_list = listAllFiles(input_root); int count = 0; std::ofstream outfile(txt_path); for (const std::string& img_name : img_list) { // std::string dir_name = img_name.substr(img_name.find_last_of("/") + 1); std::string file_name = img_name.substr(img_name.find_last_of("/") + 1); size_t pos = file_name.find_last_of("."); if (pos != std::string::npos) { file_name = file_name.substr(0, pos); } std::string dir_name = file_name; std::string output_dir = output_root + "/" + dir_name; createDirectory(output_dir); cv::Mat img = cv::imread(img_name); if (!img.empty()) { cropAndSave(img, output_dir, crop_size); std::string relative_output_dir = output_dir.substr(output_root.length() - 7); // 计算相对路径 std::string file_name = std::to_string(count) + ".bgr"; outfile << "." << relative_output_dir << "/" << file_name << std::endl; count++; // 递增计数器变量 } } outfile.close(); // 关闭txt文件 return 0; }
总体
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <dirent.h>
#include <sys/stat.h>
std::vector<std::string> listAllFiles(const std::string& rootdir) {
std::vector<std::string> files;
DIR* dir = opendir(rootdir.c_str());
if (dir == nullptr) {
return files;
}
dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
std::string path = rootdir + "/" + entry->d_name;
struct stat statbuf;
if (stat(path.c_str(), &statbuf) == -1) {
continue;
}
if (S_ISDIR(statbuf.st_mode)) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
std::vector<std::string> subFiles = listAllFiles(path);
files.insert(files.end(), subFiles.begin(), subFiles.end());
} else if (S_ISREG(statbuf.st_mode)) {
std::string extension = path.substr(path.find_last_of(".") + 1);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
if (extension == "jpg" || extension == "png" || extension == "jpeg" || extension == "bmp") {
files.push_back(path);
}
}
}
closedir(dir);
return files;
}
bool createDirectory(const std::string& path) {
int ret = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret == -1) {
return false;
}
return true;
}
void cropAndSave(const cv::Mat& img, const std::string& output_path, const cv::Size& crop_size) {
int height = img.rows;
int width = img.cols;
// Calculate the padding size
int pad_bottom = crop_size.height - (height % crop_size.height);
if (pad_bottom == crop_size.height) {
pad_bottom = 0;
}
int pad_right = crop_size.width - (width % crop_size.width);
if (pad_right == crop_size.width) {
pad_right = 0;
}
cv::Mat padded_img;
cv::copyMakeBorder(img, padded_img, 0, pad_bottom, 0, pad_right, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
int num_cols = padded_img.cols / crop_size.width;
int num_rows = padded_img.rows / crop_size.height;
int count = 0;
for (int row = 0; row < num_rows; ++row) {
for (int col = 0; col < num_cols; ++col) {
int y1 = row * crop_size.height;
int y2 = y1 + crop_size.height;
int x1 = col * crop_size.width;
int x2 = x1 + crop_size.width;
cv::Mat crop_img = padded_img(cv::Rect(x1, y1, crop_size.width, crop_size.height));
std::string output_file = output_path + "/" + std::to_string(count) + ".bgr";
cv::Mat channels[3];
cv::split(crop_img, channels);
std::ofstream outfile(output_file, std::ios::binary);
outfile.write(reinterpret_cast<char*>(channels[0].data), crop_size.width * crop_size.height);
outfile.write(reinterpret_cast<char*>(channels[1].data), crop_size.width * crop_size.height);
outfile.write(reinterpret_cast<char*>(channels[2].data), crop_size.width * crop_size.height);
outfile.close();
++count;
}
}
}
int main() {
std::string input_root = "./image_input";
std::string output_root = "./bgr";
std::string txt_path = "./txt";
cv::Size crop_size(512, 512);
std::vector<std::string> img_list = listAllFiles(input_root);
int count = 0;
std::ofstream outfile(txt_path);
for (const std::string& img_name : img_list) {
// std::string dir_name = img_name.substr(img_name.find_last_of("/") + 1);
std::string file_name = img_name.substr(img_name.find_last_of("/") + 1);
size_t pos = file_name.find_last_of(".");
if (pos != std::string::npos) {
file_name = file_name.substr(0, pos);
}
std::string dir_name = file_name;
std::string output_dir = output_root + "/" + dir_name;
createDirectory(output_dir);
cv::Mat img = cv::imread(img_name);
if (!img.empty()) {
cropAndSave(img, output_dir, crop_size);
std::string relative_output_dir = output_dir.substr(output_root.length() - 7); // 计算相对路径
std::string file_name = std::to_string(count) + ".bgr";
outfile << "." << relative_output_dir << "/" << file_name << std::endl;
count++; // 递增计数器变量
}
}
outfile.close(); // 关闭txt文件
return 0;
}
总结
代码其实不难,主要是为了迎合设备的环境所以写的时候限制很多。不得不说gpt现在真的很好用,在解决bug的过程中帮我节省了很大的时间。整体思路就是这样了,python代码就不附了。python写起来比C++简单太多了。