add 添加解码存图测试

dev_lsy
xiewenji 7 days ago
parent ef7793736e
commit a43e9fb10d

@ -24,7 +24,8 @@ enum Camera_IDX
static const std::string strCameraName[] =
{
"left",
"right"};
"right"
};
struct Camera_Info
{
Camera_IDX camera_ID;

@ -20,39 +20,6 @@ ConfigManager::~ConfigManager()
{
}
std::vector<std::string> QX_Result_Names =
{
"OK",
"aotudian",
"other",
"line",
"zangwu",
"edge",
"ymhs",
"dianzhuang",
"posun",
"xianwei",
"shuizi",
"danban",
"fuchen",
};
std::vector<std::string> QX_Result_Code =
{
"P0000",
"MA507",
"MA508",
"MA506",
"MA504",
"MA503",
"MA502",
"MA505",
"MA501",
"P0001",
"P0002",
"P0003",
"P0004",
};
int ReadFlawCodeConfig(std::string json_path)
{
m_FlawCodeList.erase(m_FlawCodeList.begin(), m_FlawCodeList.end());

@ -0,0 +1,21 @@
/*
* @Description: JPEG JPEG
* JPEGSOI/EOI
*/
#ifndef JPG_DECODER_H_
#define JPG_DECODER_H_
#include <opencv2/opencv.hpp>
#include <string>
/**
* @brief JPEG
* JPEG SOI/EOI
*
*
* @param filePath
* @return cv::Mat CV_8UC1 Mat
*/
cv::Mat decodeJpgImage(const std::string &filePath);
#endif // JPG_DECODER_H_

@ -0,0 +1,246 @@
/*
* @Description: JPEG
* JPEG
*/
#include "JpgDecoder.h"
#include <turbojpeg.h>
#include <opencv2/opencv.hpp>
#include <fstream>
#include <iostream>
#include <thread>
#include <vector>
namespace {
// ---- 内部数据结构 ----
struct JpgSegment
{
size_t imgDataSize = 0; // JPEG 数据字节数
unsigned char *ptr_start = nullptr; // 指向 SOI (0xFF 0xD8)
unsigned char *ptr_end = nullptr; // 指向 EOI 之后的位置
int width = 0;
int height = 0;
};
// ---- 步骤 1读取文件到内存 ----
int readFileToBuffer(const std::string &filePath,
std::vector<unsigned char> &buffer)
{
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open())
{
std::cerr << "[JpgDecoder] 无法打开文件: " << filePath << std::endl;
return -1;
}
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
buffer.resize(fileSize);
file.read(reinterpret_cast<char *>(buffer.data()), fileSize);
file.close();
if (file.fail())
{
std::cerr << "[JpgDecoder] 读取文件失败: " << filePath << std::endl;
return -2;
}
return 0;
}
// ---- 步骤 2在内存中定位所有 JPEG 片段SOI→EOI ----
int findJpgSegments(unsigned char *buffer, size_t fileSize,
std::vector<JpgSegment> &segments)
{
int count = 0;
int offset = 0;
const int MAX_IMAGES = 3; // 最多提取 3 张
while (count < MAX_IMAGES)
{
unsigned char *ptr = buffer + offset;
unsigned char *soi = nullptr;
unsigned char *eoi = nullptr;
// 找 SOI (0xFF 0xD8)
for (; ptr < buffer + fileSize - 1; ++ptr)
{
if (*ptr == 0xFF && *(ptr + 1) == 0xD8)
{
soi = ptr;
break;
}
}
if (soi == nullptr)
break; // 没有更多 JPEG
// 找 EOI (0xFF 0xD9),注意可能遇到下一个 SOI
bool foundNextSoi = false;
for (ptr = soi + 2; ptr < buffer + fileSize - 1; ++ptr)
{
if (*ptr == 0xFF && *(ptr + 1) == 0xD9)
{
eoi = ptr + 2; // 包含 EOI 的两字节
break;
}
if (*ptr == 0xFF && *(ptr + 1) == 0xD8)
{
// 还没找到 EOI 就又遇到 SOI截断当前段
eoi = ptr - 1;
foundNextSoi = true;
break;
}
}
if (foundNextSoi)
{
offset = static_cast<int>(eoi - buffer);
continue;
}
if (eoi == nullptr)
break; // 没找到 EOI文件可能损坏
JpgSegment seg;
seg.ptr_start = soi;
seg.ptr_end = eoi;
seg.imgDataSize = eoi - soi;
segments.push_back(seg);
offset = static_cast<int>(eoi - buffer);
count++;
}
return 0;
}
// ---- 步骤 3turbojpeg 多线程解码,纵向拼接为一张灰度图 ----
int decodeJpgSegments(std::vector<JpgSegment> &segments, cv::Mat &outImg)
{
if (segments.empty())
return -1;
int commonWidth = 0;
int totalHeight = 0;
// 第一遍:读取所有 JPEG 头信息,验证宽度一致,累加高度
for (auto &seg : segments)
{
tjhandle handle = tjInitDecompress();
if (!handle)
{
std::cerr << "[JpgDecoder] tjInitDecompress 失败!" << std::endl;
return -2;
}
int jpegSubsamp = 0, jpegColorspace = 0;
if (tjDecompressHeader3(handle,
seg.ptr_start, seg.imgDataSize,
&seg.width, &seg.height,
&jpegSubsamp, &jpegColorspace) != 0)
{
std::cerr << "[JpgDecoder] tjDecompressHeader3 错误: "
<< tjGetErrorStr() << std::endl;
tjDestroy(handle);
return -3;
}
tjDestroy(handle);
if (commonWidth == 0)
commonWidth = seg.width;
else if (commonWidth != seg.width)
{
std::cerr << "[JpgDecoder] 所有图片宽度必须一致!" << std::endl;
return -4;
}
totalHeight += seg.height;
}
// 分配输出大图(单通道灰度)
outImg = cv::Mat(totalHeight, commonWidth, CV_8UC1);
// 多线程解码到不同纵向偏移位置
auto worker = [&](JpgSegment &seg, int yOffset)
{
tjhandle handle = tjInitDecompress();
if (!handle)
{
std::cerr << "[JpgDecoder] tjInitDecompress 失败!" << std::endl;
return;
}
if (tjDecompress2(handle,
seg.ptr_start, seg.imgDataSize,
outImg.ptr(yOffset), seg.width,
outImg.step, seg.height,
TJPF_GRAY, TJFLAG_FASTDCT) != 0)
{
std::cerr << "[JpgDecoder] tjDecompress2 错误: "
<< tjGetErrorStr() << std::endl;
}
tjDestroy(handle);
};
std::vector<std::thread> threads;
threads.reserve(segments.size());
int yOffset = 0;
for (auto &seg : segments)
{
threads.emplace_back(worker, std::ref(seg), yOffset);
yOffset += seg.height;
}
for (auto &t : threads)
{
if (t.joinable())
t.join();
}
return 0;
}
} // anonymous namespace
// ============================================================
// 公开接口
// ============================================================
cv::Mat decodeJpgImage(const std::string &filePath)
{
// 1. 读取文件到内存
std::vector<unsigned char> buffer;
if (readFileToBuffer(filePath, buffer) != 0)
{
return cv::Mat();
}
// 2. 定位所有 JPEG 片段
std::vector<JpgSegment> segments;
if (findJpgSegments(buffer.data(), buffer.size(), segments) != 0 ||
segments.empty())
{
std::cerr << "[JpgDecoder] 未找到有效的 JPEG 数据: " << filePath << std::endl;
return cv::Mat();
}
// 3. 解码并拼接
cv::Mat result;
if (decodeJpgSegments(segments, result) != 0 || result.empty())
{
std::cerr << "[JpgDecoder] JPEG 解码失败: " << filePath << std::endl;
return cv::Mat();
}
return result;
}

@ -6,6 +6,12 @@
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "JpgDecoder.h"
#include <filesystem>
#include <algorithm>
#include <sys/time.h>
namespace fs = std::filesystem;
void handler(int sig)
{
printf("Get handler sig");
@ -85,6 +91,7 @@ int main(int argc, char *argv[])
cout << "*** 5、./test_JBL_Check -falign filepath num : 批量测试定位算法num 至多处理张数,结果:/home/aidlux/BOE/Align" << endl;
cout << "*** 6、./test_JBL_Check -rjson filepath: 单张复测filepath xx/xx/3A3K380001B1DK_20240408_152157_Main_0_2_L255" << endl;
cout << "*** 6、./test_JBL_Check -rjsonall filepath: 一套图复测filepath xx/xx/" << endl;
cout << "*** 7、./test_CellAOI -decode [dir]: 批量解码 .ytimage → .png 到 /home/aidlux/BOE/CELL_AOI/decode_img" << endl;
cout << "******************************************************" << endl;
return 0;
@ -96,12 +103,24 @@ int main(int argc, char *argv[])
printf("argv[%d]=%s\n", i, argv[i]);
}
bool bDecodeMode = false;
std::string decodeInputDir;
if (argc > 1 && string(argv[1]) != "-h")
{
for (int i = 1; i < argc; i++)
{
if (string(argv[i]) == "-rs")
if (string(argv[i]) == "-decode")
{
bDecodeMode = true;
if (i + 1 < argc && argv[i + 1][0] != '-')
{
decodeInputDir = string(argv[i + 1]);
i++;
}
}
else if (string(argv[i]) == "-rs")
{
test.runConfig.run_Type = Process_Run_SaveImg;
}
@ -156,6 +175,129 @@ int main(int argc, char *argv[])
}
}
// ====== 批量解码模式 (-decode) ======
if (bDecodeMode)
{
std::string inputDir = decodeInputDir.empty()
? std::string("../data/img/t1")
: decodeInputDir;
std::string outputDir = "/home/aidlux/BOE/CELL_AOI/decode_img";
printf("\n========== 批量解码模式 ==========\n");
printf(" 输入目录: %s\n", inputDir.c_str());
printf(" 输出目录: %s\n", outputDir.c_str());
printf("===================================\n\n");
// 检查输入目录
if (!fs::exists(inputDir) || !fs::is_directory(inputDir))
{
printf("[decode] 输入目录不存在: %s\n", inputDir.c_str());
return -1;
}
// 创建输出目录
_sysmkdirs(outputDir);
// 只解码 .ytimage 原始多JPEG文件
auto isYtimageFile = [](const std::string &ext) -> bool {
std::string lower = ext;
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
return (lower == ".ytimage");
};
// 扫描所有 .ytimage 文件
std::vector<fs::path> imageFiles;
try
{
for (const auto &entry : fs::recursive_directory_iterator(inputDir))
{
if (entry.is_regular_file() && isYtimageFile(entry.path().extension().string()))
imageFiles.push_back(entry.path());
}
}
catch (const fs::filesystem_error &e)
{
printf("[decode] 扫描目录出错: %s\n", e.what());
return -1;
}
if (imageFiles.empty())
{
printf("[decode] 未找到 .ytimage 文件: %s\n", inputDir.c_str());
return 0;
}
printf("[decode] 找到 %zu 个 .ytimage 文件,开始批量解码...\n", imageFiles.size());
// 逐个解码并保存
int successCount = 0;
int failCount = 0;
struct timeval tvTotalStart, tvTotalEnd;
gettimeofday(&tvTotalStart, nullptr);
for (size_t i = 0; i < imageFiles.size(); ++i)
{
const auto &imgPath = imageFiles[i];
std::string inputPath = imgPath.string();
printf("[%zu/%zu] %s ... ", i + 1, imageFiles.size(), imgPath.filename().c_str());
fflush(stdout);
struct timeval tv1, tv2;
gettimeofday(&tv1, nullptr);
// ★ 调用 JpgDecoder 独立模块解码
cv::Mat decoded = decodeJpgImage(inputPath);
gettimeofday(&tv2, nullptr);
long elapsed = (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000;
if (decoded.empty())
{
printf("失败 (%ld ms)\n", elapsed);
failCount++;
continue;
}
// 构造输出路径(保持相对目录结构)
fs::path relativePath = fs::relative(imgPath, inputDir);
fs::path outputPath = fs::path(outputDir) / relativePath;
outputPath.replace_extension(".png"); // 统一保存为 PNG
// 确保输出子目录存在
std::string outDir = outputPath.parent_path().string();
_sysmkdirs(outDir);
// 保存
if (cv::imwrite(outputPath.string(), decoded))
{
printf("OK → %s [%dx%d] (%ld ms)\n",
outputPath.filename().c_str(), decoded.cols, decoded.rows, elapsed);
successCount++;
}
else
{
printf("保存失败\n");
failCount++;
}
}
gettimeofday(&tvTotalEnd, nullptr);
long totalElapsed = (tvTotalEnd.tv_sec - tvTotalStart.tv_sec) * 1000 +
(tvTotalEnd.tv_usec - tvTotalStart.tv_usec) / 1000;
printf("\n========================================\n");
printf("[decode] 批量解码完成!\n");
printf(" 输入目录: %s\n", inputDir.c_str());
printf(" 输出目录: %s\n", outputDir.c_str());
printf(" 成功: %d 张\n", successCount);
printf(" 失败: %d 张\n", failCount);
printf(" 总耗时: %ld ms\n", totalElapsed);
printf("========================================\n");
return (successCount > 0) ? 0 : -1;
}
test.runConfig.print("config");
// getchar();
signal(SIGINT, handler);

Loading…
Cancel
Save