JPG文件头结构介绍

目录

       

简介

JPG文件头格式介绍

文件头解析代码


简介

        近期因工作需要,了解了一下JPG文件头的结构,也了解到很多手机厂商,在文件头里存了一些其他用户查看不到的信息,比如存的用于分析图像效果的定位信息,还有icc_profile文件等,还有一些其他信息,利用这些信息可以达到一些想要的目的。

JPG文件头格式介绍

        进入主题,JPG文件头结构如下表所示。

SOI
start of Image
必选
标记代码2 byte0xFFD8
APPn
Application
可选
标记代码2 byte0xFFE0~0xFFEF
数据长度2 byte字段大小,不包括标记代码
n为0~15,每个appn可以重复多次,app0里一般存的版本信息,icc_profile一般在app2里,appn里可能存有exif信息,甚至存有缩略图
DQT
Define Quantization Table
必选
标记代码2 byte0xFFDB
数据长度2 byte字段大小,不包括标记代码
量化表精度及量化表ID1 byte高4位,精度,0表示8位,1表示16位
低4位,量化表ID,取值0~3
表项64x(精度+1) byte8位精度的量化表,长度为64x(0+1)=64byte
量化表可以多次出现,最多4次
SOF0
Start of Frame
必选
标记代码2 byte0xFFC0
数据长度2 byte字段大小,不包括标记代码
精度1 byte每个数据的位数,一般为8位
图像高度2 byte图像高度,单位像素
图像宽度2 byte图像宽度,单位像素
颜色分量数1 byte1表示灰度图,3表示YCrCb或YIQ,4表示CMYK
颜色分量信息颜色分量ID1 byte
水平/垂直采样因子1 byte高4位,水平采样因子
低4位,垂直采样因子
量化表1 byte当前分量使用的量化表ID
颜色分量信息重复出现,次数位颜色分量数
DHT
Difine Huffman Table
必选
标记代码2 byte0xFFC4
数据长度2 byte字段大小,不包括标记代码
哈夫曼表表ID和表类型1 byte高4位,0表示DC直流,1表示AC交流
不同位数的码字数量16 byte
编码内容16个不同位数的码字数量之和
哈夫曼表可以重复出现,一般为4次,也可以为1次
DRI 
Define Restart Interval
可选
标记代码2 byte0xFFDD
数据长度2 byte字段大小,不包括标记代码
MCU块的单元中的重新开始间隔2 byte设其值为n,则表示每n个MCU块就有一个RSTn标记。第一个标记是RST0,第二个是RST1等,RST7后再从RST0重复
如果没有本标记段,或间隔值为0时,就表示不存在重开始间隔和标记RST
SOS
Start of Scan
必选
标记代码2 byte0xFFDA
数据长度2 byte字段大小,不包括标记代码
颜色分量数1 byte1表示灰度图,3表示YCrCb或YIQ,4表示CMYK
颜色分量信息颜色分量ID1 byte
直流/交流系数表号1 byte高4位:直流分量使用的哈夫曼树编号
低4位:交流分量使用的哈夫曼树编号
压缩图像数据谱选择开始1 byte0x00
谱选择结束1 byte0x3F
谱选择1 byte0
颜色分量信息应该重复出现,有多少个颜色分量,就出现多少次(一般为3次)
本段结束后,紧接着就是真正的图像信息了。图像信息直至遇到一个标记代码就自动结束,一般就是以EOI标记表示结束
SOI
End of Image
必选
标记代码2 byte0xFFD9

        通过这些字段,可以提取出图片的些信息,分析图片里存储的结构,比如有的在图片的app里还存有图,有的在JPG文件里存有多张图等等,大光圈场景的深度图,往往也会存在JPG里,把两种JPG接在了一起。

文件头解析代码

        如下代码可以用来分析JPG的结构。

import numpy as np
import os

def openImage(name):
    f = open(name, 'rb')
    text = f.read()
    f.close()
    print(len(text))
    return text

def GetJpgInfo(text, start, end):
    startPos = start
    infoList = []
    flag = 0
    for i in range(startPos, end - 1):
        if text[i] == 0xFF and text[i + 1] == 0xD8:
            info = dict(label='SOI', start=i, end=i+2, size=2)
            infoList.append(info)
            startPos = i + 2
            flag = 1
            break

    while (startPos<end - 1):
        if text[startPos:startPos + 2] == b'\xFF\xE0':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app0', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE1':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app1', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE2':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app2', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE3':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app3', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE4':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app4', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE5':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app5', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE6':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app6', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE7':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app7', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE8':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app8', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xE9':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app9', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xEA':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app10', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xEB':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app11', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xEC':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app12', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xED':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app13', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xEE':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app14', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xEF':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='app15', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos] == 0xFF and text[startPos + 1] == 0xDB:
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='DQT', start=startPos, end=startPos + ll + 2, size=ll + 2))
            # print(text[startPos:startPos + ll + 2])
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xC0':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='SOF0', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xC4':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='DHT', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xDD':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='DRI', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xC4':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='DHT', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        elif text[startPos:startPos + 2] == b'\xFF\xDA':
            ll = int.from_bytes(text[startPos + 2:startPos + 4], byteorder='big', signed=False)
            infoList.append(dict(label='SOS', start=startPos, end=startPos + ll + 2, size=ll + 2))
            startPos = startPos + ll + 2
        else:
            # print(text[startPos:startPos + 100])
            break
    eoi = 0
    if flag == 1:
        for i in range(startPos, end - 1):
            if text[i] == 0xFF and text[i + 1] == 0xD9:
                info = dict(label='EOI', start=i, end=i+2, size=2)
                infoList.append(info)
                startPos = i + 2
                eoi = 1
                break
    if eoi == 0:
        flag = 0
    if flag ==0:
        startPos = end

    return infoList, startPos, flag

def FindALLImage(namefile):
    imgList = []
    text = openImage(namefile)
    infoList, startPos, flag = GetJpgInfo(text, 0, len(text))
    if flag == 1:
        imgList.append(infoList)
        for i in range(len(infoList)):
            # print(infoList[i])
            if infoList[i]['label'][:3] == 'app':
                start = infoList[i]['start']
                end = infoList[i]['end']
                infoList1, startPost, flag = GetJpgInfo(text, start, end)
                if flag == 1:
                    end = infoList1[len(infoList1) - 1]['end']
                    name = infoList[i]['label'] + '_img.jpg'
                    WriteImage(name, text, infoList1[0]['start'], end)
                    imgList.append(infoList1)
    return imgList

def WriteImage(filename, text, start, end):
    f = open(filename, 'wb')
    f.write(text[start:end])
    f.close()


if __name__ == '__main__':
    imgList = FindALLImage('WechatIMG28.jpg')
    print('image num:', len(imgList))
    for i in range(len(imgList)):
        infolist = imgList[i]
        print('image:', i)
        for j in range(len(infolist)):
            print(infolist[j])

跑了张华为手机拍的图片,通过微信传到的电脑,不确定是否有被微信更改过,估计没有,因为文件大小差不多,结果如下:

1865731
image num: 2
image: 0
{'label': 'SOI', 'start': 0, 'end': 2, 'size': 2}
{'label': 'app1', 'start': 2, 'end': 25960, 'size': 25958}
{'label': 'app7', 'start': 25960, 'end': 52620, 'size': 26660}
{'label': 'app8', 'start': 52620, 'end': 87746, 'size': 35126}
{'label': 'app9', 'start': 87746, 'end': 109312, 'size': 21566}
{'label': 'app0', 'start': 109312, 'end': 109330, 'size': 18}
{'label': 'DQT', 'start': 109330, 'end': 109399, 'size': 69}
{'label': 'DQT', 'start': 109399, 'end': 109468, 'size': 69}
{'label': 'SOF0', 'start': 109468, 'end': 109487, 'size': 19}
{'label': 'DHT', 'start': 109487, 'end': 109520, 'size': 33}
{'label': 'DHT', 'start': 109520, 'end': 109703, 'size': 183}
{'label': 'DHT', 'start': 109703, 'end': 109736, 'size': 33}
{'label': 'DHT', 'start': 109736, 'end': 109919, 'size': 183}
{'label': 'DRI', 'start': 109919, 'end': 109925, 'size': 6}
{'label': 'SOS', 'start': 109925, 'end': 109939, 'size': 14}
{'label': 'EOI', 'start': 1860309, 'end': 1860311, 'size': 2}
image: 1
{'label': 'SOI', 'start': 1620, 'end': 1622, 'size': 2}
{'label': 'app0', 'start': 1622, 'end': 1640, 'size': 18}
{'label': 'DQT', 'start': 1640, 'end': 1709, 'size': 69}
{'label': 'DQT', 'start': 1709, 'end': 1778, 'size': 69}
{'label': 'SOF0', 'start': 1778, 'end': 1797, 'size': 19}
{'label': 'DHT', 'start': 1797, 'end': 1830, 'size': 33}
{'label': 'DHT', 'start': 1830, 'end': 2013, 'size': 183}
{'label': 'DHT', 'start': 2013, 'end': 2046, 'size': 33}
{'label': 'DHT', 'start': 2046, 'end': 2229, 'size': 183}
{'label': 'SOS', 'start': 2229, 'end': 2243, 'size': 14}
{'label': 'EOI', 'start': 25958, 'end': 25960, 'size': 2}

        原图分辨率2736×3648,发现在app1里存有一张分辨384x512的图,app7,app8,app9里,不确定存的啥,估计是分析定位的信息,奇怪的是,为啥里面有多个DQT,DHT,看过一些相机拍的图,没有这种情况。手头上没有苹果手机照片,没有进行解析。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值