提问人:hugh_shaw 提问时间:5/4/2023 最后编辑:hugh_shaw 更新时间:5/4/2023 访问量:50
使用另一张图像(temple.jpg)的压缩指纹压缩图像(org.jpg)
Compressing an image (org.jpg) using the compression fingerprint of another image (temple.jpg)
问:
我有两个像素尺寸相同的图像:org.jpg 和 temple.jpg。我想完成以下工作:
- 使用 temple.jpg 的压缩指纹压缩 org.jpg,并生成一个名为 target.jpg 的新图像。
- 确保目标.jpg中的DQT(量化表)、SOF(帧起始)和DHT(霍夫曼表)信息,包括十六进制代码(从FFD8到FFDA)与temple.jpg中的相同。
我已经确保 org.jpg 和 temple.jpg 的像素尺寸相同。
我也进行了详细的测试,结果如下:
1.Before saving the image, the Huffman and Quantization tables of "target.jpg" have been confirmed to be consistent with those of "temple.jpg".
2.If temple.jpg itself is saved in the IJG standard, I can directly copy the code between FFD8 and FFDA from "temple.jpg" to replace that of "target.jpg" without any errors. However, if the compression of "temple.jpg" itself is not IJG standard, "target.jpg" can be saved, but directly copying the code between FFD8 and FFDA from "temple.jpg" to replace that of "target.jpg" will cause the image to be corrupted.
3.When I analyze the generated "target.jpg" image with libjpeg, the Quantization tables and Huffman table are the same with "temple.jpg", but when I directly look at the hex codes of the two images using a hex editor, they are not consistent.
这是我尝试的代码:
#include <iostream>
#include <string>
#include <cstdio>
#include <jpeglib.h>
#include <jerror.h>
#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>
// Manually copy quantization table
void copy_quant_table(JQUANT_TBL* src, JQUANT_TBL* dst) {
for (int i = 0; i < DCTSIZE2; i++) {
dst->quantval[i] = src->quantval[i];
}
}
// Manually copy huffman table
void copy_huff_table(JHUFF_TBL* src, JHUFF_TBL* dst) {
for (int i = 0; i < 256; i++) {
dst->bits[i] = src->bits[i];
dst->huffval[i] = src->huffval[i];
}
}
void custom_copy_critical_parameters(j_decompress_ptr srcinfo, j_compress_ptr dstinfo) {
dstinfo->image_width = srcinfo->image_width;
dstinfo->image_height = srcinfo->image_height;
dstinfo->input_components = srcinfo->num_components;
dstinfo->in_color_space = srcinfo->jpeg_color_space;
jpeg_set_defaults(dstinfo);
jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space);
dstinfo->data_precision = srcinfo->data_precision;
dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling;
for (int ci = 0; ci < srcinfo->num_components; ci++) {
jpeg_component_info* compptr = &srcinfo->comp_info[ci];
jpeg_component_info* dstcompptr = &dstinfo->comp_info[ci];
dstcompptr->h_samp_factor = compptr->h_samp_factor;
dstcompptr->v_samp_factor = compptr->v_samp_factor;
}
}
void replace_ffda_hex_code(const std::string& ref_filename, const std::string& output_filename) {
// 打开参考文件和输出文件
std::ifstream ref_file(ref_filename, std::ios::binary);
std::ifstream output_file(output_filename, std::ios::binary);
if (!ref_file || !output_file) {
std::cerr << "Error opening files for hex code replacement." << std::endl;
exit(1);
}
// 寻找FFDA标记
const uint8_t marker_ff = 0xFF;
const uint8_t marker_da = 0xDA;
auto find_ffda_marker = [](std::ifstream& file) -> std::streampos {
uint8_t byte1, byte2;
byte1 = file.get();
byte2 = file.get();
while (!file.eof()) {
if (byte1 == marker_ff && byte2 == marker_da) {
return file.tellg() - std::streamoff(2);
}
byte1 = byte2;
byte2 = file.get();
}
return std::streampos(-1);
};
// 在两个文件中找到FFDA标记的位置
std::streampos ref_ffda_pos = find_ffda_marker(ref_file);
std::streampos output_ffda_pos = find_ffda_marker(output_file);
if (ref_ffda_pos == std::streampos(-1) || output_ffda_pos == std::streampos(-1)) {
std::cerr << "FFDA marker not found in one of the files." << std::endl;
exit(1);
}
// 创建临时文件以保存修改后的输出文件内容
std::ofstream temp_file("temp_output.jpg", std::ios::binary);
if (!temp_file) {
std::cerr << "Error creating temporary file." << std::endl;
exit(1);
}
// 将参考文件的FFDA之前的内容复制到临时文件
ref_file.seekg(0, std::ios::beg);
std::copy_n(std::istreambuf_iterator<char>(ref_file), ref_ffda_pos, std::ostreambuf_iterator<char>(temp_file));
// 将输出文件的FFDA之后的内容复制到临时文件
output_file.seekg(output_ffda_pos, std::ios::beg);
temp_file << output_file.rdbuf();
// 关闭文件
ref_file.close();
output_file.close();
temp_file.close();
// 使用修改后的临时文件覆盖原始输出文件
std::remove(output_filename.c_str());
std::rename("temp_output.jpg", output_filename.c_str());
}
void Debug_output_decompress_Huffman_and_quantization(jpeg_decompress_struct& info, const std::string& name) {
std::cout << name << std::endl << " Quantization tables:" << std::endl;
for (int i = 0; i < NUM_QUANT_TBLS; i++) {
std::cout << "Table " << i << ": ";
if (info.quant_tbl_ptrs[i] != nullptr) {
for (int j = 0; j < DCTSIZE2; j++) {
std::cout << info.quant_tbl_ptrs[i]->quantval[j] << " ";
}
}
std::cout << std::endl;
}
std::cout << name << " Huffman tables:" << std::endl;
for (int i = 0; i < NUM_HUFF_TBLS; i++) {
std::cout << "Table " << i << ": ";
if (info.dc_huff_tbl_ptrs[i] != nullptr) {
std::cout << "DC Table " << i << " bits: ";
for (int j = 0; j < 17; j++) {
std::cout << (int)info.dc_huff_tbl_ptrs[i]->bits[j] << " ";
}
std::cout << " values: ";
for (int j = 0; j < 256; j++) {
std::cout << (int)info.dc_huff_tbl_ptrs[i]->huffval[j] << " ";
}
}
std::cout << std::endl;
if (info.ac_huff_tbl_ptrs[i] != nullptr) {
std::cout << "AC Table " << i << " bits: ";
for (int j = 0; j < 17; j++) {
std::cout << (int)info.ac_huff_tbl_ptrs[i]->bits[j] << " ";
}
std::cout << " values: ";
for (int j = 0; j < 256; j++) {
std::cout << (int)info.ac_huff_tbl_ptrs[i]->huffval[j] << " ";
}
}
std::cout << std::endl;
}
std::cout << "**---------------finish output---------------**" << std::endl;
std::cout << std::endl;
}
void Debug_output_compress_Huffman_and_quantization(jpeg_compress_struct& info, const std::string& name) {
std::cout << name << std::endl << " Quantization tables:" << std::endl;
for (int i = 0; i < NUM_QUANT_TBLS; i++) {
std::cout << "Table " << i << ": ";
if (info.quant_tbl_ptrs[i] != nullptr) {
for (int j = 0; j < DCTSIZE2; j++) {
std::cout << info.quant_tbl_ptrs[i]->quantval[j] << " ";
}
}
std::cout << std::endl;
}
std::cout << name << " Huffman tables:" << std::endl;
for (int i = 0; i < NUM_HUFF_TBLS; i++) {
std::cout << "Table " << i << ": ";
if (info.dc_huff_tbl_ptrs[i] != nullptr) {
std::cout << "DC Table " << i << " bits: ";
for (int j = 0; j < 17; j++) {
std::cout << (int)info.dc_huff_tbl_ptrs[i]->bits[j] << " ";
}
std::cout << " values: ";
for (int j = 0; j < 256; j++) {
std::cout << (int)info.dc_huff_tbl_ptrs[i]->huffval[j] << " ";
}
}
std::cout << std::endl;
if (info.ac_huff_tbl_ptrs[i] != nullptr) {
std::cout << "AC Table " << i << " bits: ";
for (int j = 0; j < 17; j++) {
std::cout << (int)info.ac_huff_tbl_ptrs[i]->bits[j] << " ";
}
std::cout << " values: ";
for (int j = 0; j < 256; j++) {
std::cout << (int)info.ac_huff_tbl_ptrs[i]->huffval[j] << " ";
}
}
std::cout << std::endl;
}
std::cout << "**---------------finish output---------------**" << std::endl;
std::cout << std::endl;
}
void copy_jpeg_file(const std::string& input_filename, const std::string& ref_filename, const std::string& output_filename) {
// Open input and reference files
FILE* input_file;
FILE* ref_file;
errno_t err;
err = fopen_s(&input_file, input_filename.c_str(), "rb");
err = fopen_s(&ref_file, ref_filename.c_str(), "rb");
if (!input_file || !ref_file) {
std::cerr << "Error opening files." << std::endl;
exit(1);
}
// Input image decompression
struct jpeg_decompress_struct srcinfo;
struct jpeg_error_mgr srcerr;
srcinfo.err = jpeg_std_error(&srcerr);
jpeg_create_decompress(&srcinfo);
jpeg_stdio_src(&srcinfo, input_file);
jpeg_read_header(&srcinfo, TRUE);
// Reference image decompression
struct jpeg_decompress_struct refinfo;
struct jpeg_error_mgr referr;
refinfo.err = jpeg_std_error(&referr);
jpeg_create_decompress(&refinfo);
jpeg_stdio_src(&refinfo, ref_file);
jpeg_read_header(&refinfo, TRUE);
// Output image compression
struct jpeg_compress_struct dstinfo;
struct jpeg_error_mgr dsterr;
dstinfo.err = jpeg_std_error(&dsterr);
jpeg_create_compress(&dstinfo);
FILE* output_file;
err = fopen_s(&output_file, output_filename.c_str(), "wb");
if (!output_file) {
std::cerr << "Error opening output file." << std::endl;
exit(1);
}
jpeg_stdio_dest(&dstinfo, output_file);
custom_copy_critical_parameters(&refinfo, &dstinfo);
// Set input color space for output image
dstinfo.in_color_space = srcinfo.out_color_space;
// Disable JFIF header
dstinfo.write_JFIF_header = FALSE;
dstinfo.optimize_coding = FALSE;
dstinfo.smoothing_factor = 0;
dstinfo.dct_method = JDCT_ISLOW;
dstinfo.comp_info[0].h_samp_factor = refinfo.comp_info[0].h_samp_factor;
dstinfo.comp_info[0].v_samp_factor = refinfo.comp_info[0].v_samp_factor;
dstinfo.comp_info[1].h_samp_factor = refinfo.comp_info[1].h_samp_factor;
dstinfo.comp_info[1].v_samp_factor = refinfo.comp_info[1].v_samp_factor;
dstinfo.comp_info[2].h_samp_factor = refinfo.comp_info[2].h_samp_factor;
dstinfo.comp_info[2].v_samp_factor = refinfo.comp_info[2].v_samp_factor;
Debug_output_compress_Huffman_and_quantization(dstinfo, "dstinfo huff_table and quant_table before copied");
// Copy DQT、SOF and DHT
for (int i = 0; i < NUM_QUANT_TBLS; i++) {
if (refinfo.quant_tbl_ptrs[i] != nullptr) {
if (dstinfo.quant_tbl_ptrs[i] == nullptr) {
dstinfo.quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)&dstinfo);
}
copy_quant_table(refinfo.quant_tbl_ptrs[i], dstinfo.quant_tbl_ptrs[i]);
}
}
for (int i = 0; i < NUM_HUFF_TBLS; i++) {
if (refinfo.dc_huff_tbl_ptrs[i] != nullptr) {
if (dstinfo.dc_huff_tbl_ptrs[i] == nullptr) {
dstinfo.dc_huff_tbl_ptrs[i] = jpeg_alloc_huff_table((j_common_ptr)&dstinfo);
}
copy_huff_table(refinfo.dc_huff_tbl_ptrs[i], dstinfo.dc_huff_tbl_ptrs[i]);
}
if (refinfo.ac_huff_tbl_ptrs[i] != nullptr) {
if (dstinfo.ac_huff_tbl_ptrs[i] == nullptr) {
dstinfo.ac_huff_tbl_ptrs[i] = jpeg_alloc_huff_table((j_common_ptr)&dstinfo);
}
copy_huff_table(refinfo.ac_huff_tbl_ptrs[i], dstinfo.ac_huff_tbl_ptrs[i]);
}
}
Debug_output_decompress_Huffman_and_quantization(srcinfo, "srcinfo huff_table and quant_table after copied");
Debug_output_compress_Huffman_and_quantization(dstinfo, "dstinfo huff_table and quant_table after copied");
Debug_output_decompress_Huffman_and_quantization(refinfo, "refinfo huff_table and quant_table after copied");
// Start decompression of input image and compression of output image
jpeg_start_decompress(&srcinfo);
jpeg_start_compress(&dstinfo, TRUE);
// Allocate memory for scanlines buffer
JSAMPARRAY buffer = (*srcinfo.mem->alloc_sarray)((j_common_ptr)&srcinfo, JPOOL_IMAGE, srcinfo.output_width * srcinfo.output_components, 1);
// Copy scanlines from input image to output image
while (srcinfo.output_scanline < srcinfo.output_height) {
jpeg_read_scanlines(&srcinfo, buffer, 1);
jpeg_write_scanlines(&dstinfo, buffer, 1);
}
// Finish decompression and compression
jpeg_finish_decompress(&srcinfo);
jpeg_finish_compress(&dstinfo);
// Destroy structures and close files
jpeg_destroy_decompress(&srcinfo);
jpeg_destroy_decompress(&refinfo);
jpeg_destroy_compress(&dstinfo);
fclose(input_file);
fclose(ref_file);
fclose(output_file);
}
int main() {
std::string input_filename = "org.JPG";
std::string ref_filename = "temple.jpg";
std::string output_filename = "target.jpg";
// Copy critical parameters from temple.jpg to target.jpg
copy_jpeg_file(input_filename, ref_filename, output_filename);
//replace_ffda_hex_code(ref_filename, output_filename);
return 0;
}
上面的代码已经生成了target.jpg,但实际上经过对比,target.jpg和temple.jpg之间的HEX代码还是不一样的。我不确定是什么导致了这种差异。我在某个地方做错了什么吗?
来自同一摄像头的两张图像,来自同一摄像头的两张图像进行比较,它们具有相同的理解算法和相同的十六进制代码(从 FFD8 到 FFDA)
输出结果,这是“target.jpg”和“temple.jpg”的比较,带有两个图像的十六进制代码不一样,这就是我问的问题。
答: 暂无答案
评论