#include <glog/logging.h>
#include <opencv2/opencv.hpp>
#include <turbojpeg.h>
std::pair<int, int> JpegTurboDecode(const std::string& raw_jpeg_data, std::vector<unsigned char>* result_data) {
if (nullptr == result_data) {
LOG(INFO) << "result_data_ptr is nullptr";
return { -1, -1 };
}
auto start_time = std::chrono::high_resolution_clock::now();
// 初始化解压缩对象
tjhandle tjInstance = tjInitDecompress();
if (tjInstance == nullptr) {
LOG(ERROR) << "无法初始化libjpeg-turbo解压缩对象";
return { -1, -1 };
}
int width, height, jpegSubsamp;
// 获取图像信息
if (tjDecompressHeader2(tjInstance, reinterpret_cast<unsigned char*>(const_cast<char*>(raw_jpeg_data.data())),
raw_jpeg_data.size(), &width, &height, &jpegSubsamp) < 0) {
LOG(ERROR) << "无法读取JPEG头信息: " << tjGetErrorStr() << std::endl;
tjDestroy(tjInstance);
return { -1, -1 };
}
// 分配内存用于存储解压缩后的图像
result_data->resize(width * height * 3);
// std::vector<unsigned char> imgBuffer(width * height * 3);
// 解压缩图像; 这里注意TJPF_BGR和TJFLAG_ACCURATEDCT可以按需设置
if (tjDecompress2(tjInstance, reinterpret_cast<unsigned char*>(const_cast<char*>(raw_jpeg_data.data())),
raw_jpeg_data.size(), result_data->data(), width, 0, height, TJPF_BGR, TJFLAG_ACCURATEDCT) < 0) {
LOG(ERROR) << "解压缩失败: " << tjGetErrorStr();
tjDestroy(tjInstance);
return { -1, -1 };
}
auto end_time = std::chrono::high_resolution_clock::now();
auto latency_us1 = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
LOG(INFO) << "jpeg-turbo 解码耗时:" << latency_us1 << "us";
return { height, width };
}
void JpegTurboVSOpencv() {
// std::ifstream fs("./1c0b187eb1f7087d.png");
std::ifstream fs("load.jpg");
if (!fs) {
LOG(ERROR) << "fs_ptr is invalid!";
return;
}
std::stringstream buffer;
buffer << fs.rdbuf(); // 读取文件内容到 stringstream
std::string str_tmp(buffer.str());
// cv解码↓
auto start_time1 = std::chrono::high_resolution_clock::now();
// 零拷贝构造cv::Mat
cv::Mat input_cvmat(1, str_tmp.size(), CV_8UC1, reinterpret_cast<char*>(str_tmp.data()));
// 解码
auto decode_by_cv = cv::imdecode(input_cvmat, CV_LOAD_IMAGE_COLOR);
auto latency_us1 = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - start_time1).count();
LOG(INFO) << "cv-解码耗时:" << latency_us1 << "us";
// cv解码↑
// jpeg-turbo解码↓
std::vector<unsigned char> result_vec;
const auto& h_w_pair = JpegTurboDecode(str_tmp, &result_vec);
cv::Mat cv_mat_from_jpeg_turbo(h_w_pair.first, h_w_pair.second, CV_8UC3,
reinterpret_cast<char*>(result_vec.data()));
// jpeg-turbo解码↑
int idx = 0;
// const auto cv_decode_result_data = decode_by_cv.data();
LOG(INFO) << "size from cv:" << decode_by_cv.total()
<< "; size from jpeg-turbo:" << cv_mat_from_jpeg_turbo.total();
LOG(INFO) << "type:" << decode_by_cv.type() << "; type:" << cv_mat_from_jpeg_turbo.type();
// 比较两者的结果↓
// 获取矩阵的行数和列数
int rows = decode_by_cv.rows;
int cols = decode_by_cv.cols;
// 获取矩阵的通道数
int channels = decode_by_cv.channels();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (channels == 1) {
// 单通道图像(如灰度图)
std::cout << static_cast<int>(decode_by_cv.at<uchar>(i, j)) << std::endl;
} else if (channels == 3) {
// 三通道图像(如BGR彩色图)
cv::Vec3b pixel = decode_by_cv.at<cv::Vec3b>(i, j);
const auto& another = cv_mat_from_jpeg_turbo.at<cv::Vec3b>(i, j);
if (static_cast<int>(pixel[0]) - static_cast<int>(another[0]) != 0 ||
static_cast<int>(pixel[1]) - static_cast<int>(another[1]) != 0 ||
static_cast<int>(pixel[2]) - static_cast<int>(another[2]) != 0) {
std::cout << "位置 (" << i << "," << j << "): ";
std::cout << "B:" << static_cast<int>(pixel[0]) << "," << static_cast<int>(another[0])
<< " G:" << static_cast<int>(pixel[1]) << "," << static_cast<int>(another[1])
<< " R:" << static_cast<int>(pixel[2]) << "," << static_cast<int>(another[2])
<< std::endl;
}
} else {
std::cout << "不支持的通道数: " << channels << std::endl;
return;
}
}
}
}
int main() {
JpegTurboVSOpencv();
return 0;
}
运行结果

结论
- libjpeg-turbo 解码操作耗时优于opencv 35%+
- 参数(色彩通道,解码选项)一致情况下,二者的解码数据可以做到无diff