JPEG附加信息
关于JPEG压缩中附加信息的事
再复习一下JPEG元数据的概念:图片元数据(metadata)是嵌入到图片文件中的一些标签。比较像文件属性,但是种类繁多。
常见的几种标准有:
EXIF:通常被数码相机在拍摄照片时自动添加,比如相机型号、镜头、曝光、图片尺寸等信息。
IPTC:比如图片标题、关键字、说明、作者、版权等信息。
XMP:由Adobe公司制定标准,以XML格式保存。用PhotoShop等Adobe公司的软件制作的图片通常会携带这种信息
XMP和Exif可以共用JPEG编码格式中APP1的标签位,因此要用"EXIF"和"XMP"标识符作为区分。
因此前文所写的将XMP写入APP4的方法是不对的,会导致无法重新读取到XMP元数据。
使用MagicExif软件可以方便的查看JPEG中的信息。

/* Write the JPEG marker header (APP1 code and marker length) */
jpeg_write_m_header(cinfo, JPEG_APP0 + 1,(unsigned int)(length + 9));/* Write the marker identifying string "XMP" (null-terminated). We* code it in this less-than-transparent way so that the code works even if* the local character set is not ASCII.*/
jpeg_write_m_byte(cinfo, 0x58);
jpeg_write_m_byte(cinfo, 0x4d);
jpeg_write_m_byte(cinfo, 0x50);
jpeg_write_m_byte(cinfo, 0x0);/* Add the sequencing info */
jpeg_write_m_byte(cinfo, cur_marker);
jpeg_write_m_byte(cinfo, (int)num_markers);
APPn部分可以写入的最大数据是64KB,因此XMP如果写入了GDepth数据很容易超过APPn的大小限制。在这里参考高通的DebugData写入JPEG方式,已知DebugData的大小也是大于64KB,采用的是每一次都只写入64KB大小的数据(包含TAG),剩下的数据再按照相同的逻辑写入。具体实现在函数EmitAppN()中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TApVI7Z8-1639108045022)(DocPic/image-20211207140735245.png)]
header的长度要和定义的“XMP”字符串的长度相同才行,不然会导致jpeg写不成功。
jpeg_write_m_header(cinfo, JPEG_APP0 + 3,(unsigned int)(length + 4));
EmitAppN()这个函数可以写入超多64KB大小的数据,方法是在写入前先判断data的大小,先只写入部分有效数据(64KB除去header的大小),剩下的部分再用EmitAppN,递归写入。参照EmitAppN()新构建了一个函数EmitGDepth()专门用于写入GDepth的XMP结构数据,重点就在于:
- 构建数据tag,包括字段、tag大小
- 将大小超过64KB(长度超过0xFFFF)的数据分次递归写入。
TctApmResult EmitGDepth(jpeg_compress_struct* cinfo,const char* pPayload,UINT32 payloadLength,UINT32 payloadWritten,UINT8 appN,UINT8 recursionCount)
{TctApmResult result = TAPM_RESULT_SUCCESS;UINT32 overflowLength = 0;TAPM_LOG_VERBOSE(TapmLogGroupAlgo, "headerLength %d payloadLength %d payloadWritten %d",GDEPTH_HEAD_LEN, payloadLength, payloadWritten);if (pPayload == NULL){TAPM_LOG_ERROR(TapmLogGroupAlgo, "payload NULL");return TAPM_RESULT_INVALID_ARG;}// Check overflowif ((payloadLength + GDEPTH_HEAD_LEN + 2) > 0xFFFF){overflowLength = payloadLength + GDEPTH_HEAD_LEN + 2 - 0xFFFF;payloadLength = 0xFFFF - 2 - GDEPTH_HEAD_LEN;TAPM_LOG_VERBOSE(TapmLogGroupAlgo, "large payload, setting payload to %d overflow %d",payloadLength, overflowLength);}jpeg_write_m_header(cinfo, JPEG_APP0 + appN, GDEPTH_HEAD_LEN + payloadLength);// write 'GDEPTH' head mark ASCII codejpeg_write_m_byte(cinfo, 0x47);jpeg_write_m_byte(cinfo, 0x44);jpeg_write_m_byte(cinfo, 0x45);jpeg_write_m_byte(cinfo, 0x50);jpeg_write_m_byte(cinfo, 0x54);jpeg_write_m_byte(cinfo, 0x48);jpeg_write_m_byte(cinfo, 0x00);UINT32 dataLen = payloadLength;const char* dataPtr = reinterpret_cast<const char*>(pPayload + payloadWritten);while (dataLen--) {jpeg_write_m_byte(cinfo, *dataPtr);dataPtr++;}// If overflow data exists recurse till no overflow leftif (overflowLength > 0){EmitGDepth(cinfo, pPayload, overflowLength,(payloadWritten + payloadLength), appN, (recursionCount + 1));}else{TAPM_LOG_INFO(TapmLogGroupAlgo, "Payload write successful to App%d", appN);}return result;
}
目前的这种做法可以保证GDepth数据完整的写入,同时也不会影响到JPEG中其他分区的数据,理论上来说文件大小无限制:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
