|
|
/*
|
|
|
* @Author: your name
|
|
|
* @Date: 2022-04-20 15:50:00
|
|
|
* @LastEditTime: 2025-09-13 17:12:17
|
|
|
* @LastEditors: xiewenji 527774126@qq.com
|
|
|
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
|
* @FilePath: /ZCXD_MonitorPlatform/src/CoreLogicModule/src/CamDeal.cpp
|
|
|
*/
|
|
|
#include <turbojpeg.h>
|
|
|
#include "ExtractInstance.h"
|
|
|
#include <opencv2/opencv.hpp>
|
|
|
#include <vector>
|
|
|
#include <algorithm>
|
|
|
#include <iostream>
|
|
|
#include <sys/time.h>
|
|
|
#include <fstream>
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
|
ExtractInstance::ExtractInstance()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
ExtractInstance::~ExtractInstance()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
int ExtractInstance::ReadImgData(const std::string &file_path, unsigned char *&buffer, size_t *fileSize)
|
|
|
{
|
|
|
|
|
|
std::ifstream file(file_path, std::ios::binary);
|
|
|
if (!file.is_open())
|
|
|
{
|
|
|
std::cerr << "Error opening file!" << std::endl;
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
// 获取文件的大小
|
|
|
file.seekg(0, std::ios::end);
|
|
|
size_t fileSize123 = file.tellg();
|
|
|
file.seekg(0, std::ios::beg);
|
|
|
// printf("file size %d \n", fileSize);
|
|
|
// 分配内存来存储文件内容
|
|
|
buffer = new unsigned char[fileSize123];
|
|
|
|
|
|
// 读取文件内容到内存
|
|
|
file.read(reinterpret_cast<char *>(buffer), fileSize123);
|
|
|
file.close(); // 关闭文件
|
|
|
*fileSize = fileSize123;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ExtractInstance::CalAllImgPtrDate(unsigned char *buffer, size_t fileSize, std::vector<JPGImgDataPtr> &ptrImgdataList)
|
|
|
{
|
|
|
|
|
|
int count = 0;
|
|
|
int start = 0;
|
|
|
|
|
|
// 循环提取图片,最多提取 3 张
|
|
|
while (count < 3)
|
|
|
{
|
|
|
unsigned char *ptr = buffer + start;
|
|
|
bool found_soi = false;
|
|
|
bool found_eoi = false;
|
|
|
unsigned char *soi1 = nullptr;
|
|
|
unsigned char *eoi1 = nullptr;
|
|
|
|
|
|
// 寻找SOI标记(0xFF 0xD8)
|
|
|
for (; ptr < buffer + fileSize - 1; ++ptr)
|
|
|
{
|
|
|
if (*ptr == 0xFF && *(ptr + 1) == 0xD8)
|
|
|
{
|
|
|
soi1 = ptr;
|
|
|
found_soi = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!found_soi)
|
|
|
break;
|
|
|
|
|
|
// 在找结束标志时又找到了一个 开始表示。
|
|
|
bool brestart = false;
|
|
|
// 寻找EOI标记(0xFF 0xD9)
|
|
|
for (ptr = soi1 + 2; ptr < buffer + fileSize - 1; ++ptr)
|
|
|
{
|
|
|
if (*ptr == 0xFF && *(ptr + 1) == 0xD9)
|
|
|
{
|
|
|
eoi1 = ptr + 2; // 包括EOI的结束部分
|
|
|
found_eoi = true;
|
|
|
break;
|
|
|
}
|
|
|
// 在找结束标志时又找到了一个 开始表示。
|
|
|
if (*ptr == 0xFF && *(ptr + 1) == 0xD8)
|
|
|
{
|
|
|
eoi1 = ptr - 1;
|
|
|
printf("error s %x %x -> %p \n ", *ptr, *(ptr + 1), ptr);
|
|
|
brestart = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (brestart)
|
|
|
{
|
|
|
start = eoi1 - buffer;
|
|
|
continue;
|
|
|
}
|
|
|
if (!found_eoi)
|
|
|
break;
|
|
|
|
|
|
size_t jpegSize = eoi1 - soi1;
|
|
|
|
|
|
// 更新起始位置
|
|
|
start = eoi1 - buffer;
|
|
|
|
|
|
JPGImgDataPtr temdata;
|
|
|
temdata.buffer = buffer;
|
|
|
temdata.imgDataSize = jpegSize;
|
|
|
temdata.ptr_start = soi1;
|
|
|
temdata.ptr_end = eoi1;
|
|
|
ptrImgdataList.push_back(temdata);
|
|
|
|
|
|
count++;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ExtractInstance::DecodeJpgImg(std::vector<JPGImgDataPtr> &ptrImgdataList, cv::Mat &outImg)
|
|
|
{
|
|
|
|
|
|
if (ptrImgdataList.empty())
|
|
|
return -1;
|
|
|
|
|
|
int commonWidth = 0;
|
|
|
int totalHeight = 0;
|
|
|
|
|
|
// 第一步:读取所有头信息,检查宽度一致,累加高度
|
|
|
for (auto &imgData : ptrImgdataList)
|
|
|
{
|
|
|
tjhandle handle = tjInitDecompress();
|
|
|
if (!handle)
|
|
|
{
|
|
|
std::cerr << "tjInitDecompress failed!" << std::endl;
|
|
|
return -2;
|
|
|
}
|
|
|
|
|
|
int jpegSubsamp = 0, jpegColorspace = 0;
|
|
|
if (tjDecompressHeader3(handle,
|
|
|
imgData.ptr_start, imgData.imgDataSize,
|
|
|
&imgData.width, &imgData.height,
|
|
|
&jpegSubsamp, &jpegColorspace) != 0)
|
|
|
{
|
|
|
std::cerr << "tjDecompressHeader3 error: " << tjGetErrorStr() << std::endl;
|
|
|
tjDestroy(handle);
|
|
|
return -3;
|
|
|
}
|
|
|
tjDestroy(handle);
|
|
|
|
|
|
if (commonWidth == 0)
|
|
|
commonWidth = imgData.width;
|
|
|
else if (commonWidth != imgData.width)
|
|
|
{
|
|
|
std::cerr << "All images must have the same width!" << std::endl;
|
|
|
return -4;
|
|
|
}
|
|
|
|
|
|
totalHeight += imgData.height;
|
|
|
}
|
|
|
|
|
|
// 第二步:分配大图
|
|
|
outImg = cv::Mat(totalHeight, commonWidth, CV_8UC1);
|
|
|
// 第三步:多线程解码到不同位置
|
|
|
auto worker = [&](JPGImgDataPtr &imgData, int yOffset)
|
|
|
{
|
|
|
tjhandle handle = tjInitDecompress();
|
|
|
if (!handle)
|
|
|
{
|
|
|
std::cerr << "tjInitDecompress failed!" << std::endl;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (tjDecompress2(handle,
|
|
|
imgData.ptr_start, imgData.imgDataSize,
|
|
|
outImg.ptr(yOffset), imgData.width,
|
|
|
outImg.step, imgData.height,
|
|
|
TJPF_GRAY, TJFLAG_FASTDCT) != 0)
|
|
|
{
|
|
|
std::cerr << "tjDecompress2 error: " << tjGetErrorStr() << std::endl;
|
|
|
}
|
|
|
|
|
|
tjDestroy(handle);
|
|
|
};
|
|
|
|
|
|
std::vector<std::thread> threads;
|
|
|
threads.reserve(ptrImgdataList.size());
|
|
|
|
|
|
int yOffset = 0;
|
|
|
for (auto &imgData : ptrImgdataList)
|
|
|
{
|
|
|
threads.emplace_back(worker, std::ref(imgData), yOffset);
|
|
|
yOffset += imgData.height; // 下一张图拼在下方
|
|
|
}
|
|
|
|
|
|
for (auto &t : threads)
|
|
|
{
|
|
|
if (t.joinable())
|
|
|
t.join();
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ExtractInstance::ReadCELLImg(std::string strimgPath, cv::Mat &outImg, bool bdecode)
|
|
|
{
|
|
|
int re = -1;
|
|
|
|
|
|
// 不解码模式:直接用 opencv 读取图片
|
|
|
if (!bdecode)
|
|
|
{
|
|
|
outImg = cv::imread(strimgPath, cv::IMREAD_GRAYSCALE);
|
|
|
if (outImg.empty())
|
|
|
{
|
|
|
std::cerr << "错误:无法读取图片,请检查路径是否正确!" << std::endl;
|
|
|
return -1;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 解码模式: 处理 .ytimage 格式
|
|
|
// if(bdecode)
|
|
|
// {
|
|
|
// printf("ReadCELLImg========= %s \n", strimgPath.c_str());
|
|
|
// int re = decode_Img(strimgPath, outImg);
|
|
|
// }
|
|
|
// else
|
|
|
{
|
|
|
size_t lastSlashIndex = strimgPath.find_last_of("/");
|
|
|
std::string basePath;
|
|
|
std::string pureFileName;
|
|
|
if (lastSlashIndex != std::string::npos) {
|
|
|
basePath = strimgPath.substr(0, lastSlashIndex);
|
|
|
pureFileName = strimgPath.substr(lastSlashIndex + 1);
|
|
|
} else {
|
|
|
pureFileName = strimgPath;
|
|
|
}
|
|
|
|
|
|
// 4. 拼接出目标图片的完整路径
|
|
|
std::string targetImgPath;
|
|
|
if (pureFileName.find("CA") != std::string::npos || pureFileName.find("CF") != std::string::npos) {
|
|
|
// 如果是 CA 开头,读取 CELL_CF.jpg
|
|
|
targetImgPath = basePath + "/CELL_CF.jpg";
|
|
|
} else if (pureFileName.find("TA") != std::string::npos || pureFileName.find("TFT") != std::string::npos) {
|
|
|
// 如果是 TA 开头,读取 CELL_TFT.jpg
|
|
|
targetImgPath = basePath + "/CELL_TFT.jpg";
|
|
|
} else {
|
|
|
std::cerr << "未知文件名: " << pureFileName << std::endl;
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
printf("ReadCELLImg========= %s \n", targetImgPath.c_str());
|
|
|
|
|
|
outImg = cv::imread(targetImgPath, cv::IMREAD_GRAYSCALE);
|
|
|
|
|
|
// 6. 检查是否成功加载
|
|
|
if (outImg.empty()) {
|
|
|
std::cerr << "错误:无法读取图片,请检查路径是否正确!" << std::endl;
|
|
|
return -1;
|
|
|
}
|
|
|
re = 0;
|
|
|
}
|
|
|
|
|
|
return re;
|
|
|
}
|
|
|
std::string ExtractInstance::GetVersion()
|
|
|
{
|
|
|
std::string str = "";
|
|
|
return str;
|
|
|
}
|
|
|
|
|
|
std::string ExtractInstance::GetErrorInfo()
|
|
|
{
|
|
|
std::string str = "";
|
|
|
return str;
|
|
|
}
|
|
|
bool ExtractInstance::decode_Img(const std::string &file_path, cv::Mat &img)
|
|
|
{
|
|
|
long t1 = getcurTime();
|
|
|
unsigned char *buffer = NULL;
|
|
|
size_t fileSize;
|
|
|
// 1、读入图片
|
|
|
int re = ReadImgData(file_path, buffer, &fileSize);
|
|
|
if (re != 0 || buffer == NULL)
|
|
|
{
|
|
|
printf("error re %d buffer == NULL\n", re);
|
|
|
return false;
|
|
|
}
|
|
|
long t2 = getcurTime();
|
|
|
|
|
|
// 2、获取图片地址指针
|
|
|
std::vector<JPGImgDataPtr> ptrImgdataList;
|
|
|
re = CalAllImgPtrDate(buffer, fileSize, ptrImgdataList);
|
|
|
if (re != 0 || ptrImgdataList.size() <= 0)
|
|
|
{
|
|
|
printf("error re %d size %ld \n", re, ptrImgdataList.size());
|
|
|
return false;
|
|
|
}
|
|
|
long t3 = getcurTime();
|
|
|
|
|
|
// 3、解码 jpg 图片
|
|
|
re = DecodeJpgImg(ptrImgdataList, img);
|
|
|
long t4 = getcurTime();
|
|
|
|
|
|
// printf("time %ld read %ld call ptrdata %ld decode %ld\n", t4 - t1, t2 - t1, t3 - t2, t4 - t3);
|
|
|
|
|
|
if (re != 0 || img.empty())
|
|
|
{
|
|
|
printf("error re %d outimg.empty()\n", re);
|
|
|
return false;
|
|
|
}
|
|
|
// if (true)
|
|
|
// {
|
|
|
// cv::imwrite("outimg_img11.png", outimg);
|
|
|
// }
|
|
|
// getchar();
|
|
|
|
|
|
delete[] buffer;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
long ExtractInstance::getcurTime()
|
|
|
{
|
|
|
struct timeval tv;
|
|
|
gettimeofday(&tv, NULL);
|
|
|
return ((long)tv.tv_sec) * 1000 + ((long)tv.tv_usec) / 1000;
|
|
|
}
|