You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
9.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* @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;
}