首先是8个字节的文件头。
CR2的前2个字节是"II",代表INTEL格式的存储顺序,即低字节在前,高字节在后。
接下来是固定的2个字节:0x2a00。
最后的4个字节的整数是指向第一个IFD(IMAGE FILE DIRECTORY图像文件目录)的绝对偏置量(起始位置)。
IFD的结构如下:
第一个2字节的整数表示在此IFD内的entry的数量,每个entry为12字节,顺序排列。最后一个entry之后的4字节整数代表下一个IFD的起始位置,如果是0,表示这是最后一个IFD。
ENTRY的结构如下:
第一个2字节的整数为tag,表示是哪种信息;
第二个2字节的整数为type,表示单个数据的格式,对应的数据类型如表:
type值 |
1 |
2 |
3 |
4 |
5 |
6 |
格式 |
unsigned byte |
ascii strings |
unsigned short |
unsigned long |
unsigned rational |
signed byte |
单个数据字节数 |
1 |
1 |
2 |
4 |
8 |
1 |
type值 |
7 |
8 |
9 |
10 |
11 |
12 |
格式 |
undefined |
signed short |
signed long |
signed rational |
single float |
double float |
单个数据字节数 |
1 |
2 |
4 |
8 |
4 |
8 |
第三个4字节的整数为len,表示数据的个数;
由type值可以确定单个数据占用的字节数,乘以len就是数据的总长度。如果数据总长不超过4个字节,则保存在entry最后的4个字节中;如果超过4个字节,则entry最后的4字节整数代表数据的存储位置相对文件头的偏置量。在该位置上可以读取数据。 根据这样的规则,就可以从raw文件中确定里面所含的每个IFD内的每个tag值,及按照type和len信息确定数据位置并读取。接下来是一个查表解读的过程。
在IFD中对应tag 0x8769的4位整数是指向另一个含有EXIF信息的子IFD的偏置量。
EXIF子IFD的结构与IFD相同,只是只有一个。
在EXIF子IFD中对应tag 0927c的4位整数是指向另一个含有MAKERNOTE信息的子IFD的偏置量。该子IFD的结构与IFD相同,也只有一个。
按照以上规则从CR2文件中顺序读出的内容为:
类别 |
编号 |
(HEX) |
TYPE |
LEN |
名称 |
含义 |
order= |
||||||
val= |
||||||
ifd= |
||||||
offset |
||||||
entries |
||||||
tiff |
1 |
100 |
3 |
1 |
JpegImageWidth |
JPEG图宽度 |
tiff |
2 |
101 |
3 |
1 |
JpegImageLength |
JPEG图高度 |
tiff |
3 |
102 |
3 |
3 |
JpegBitsPerSample |
每通道位数 |
tiff |
4 |
103 |
3 |
1 |
Compression |
压缩 |
tiff |
5 |
10F |
2 |
6 |
Maker |
制造商 |
tiff |
6 |
110 |
2 |
14 |
Model |
相机型号 |
tiff |
7 |
111 |
4 |
1 |
JpegStripOffsets |
JPEG图偏置值 |
tiff |
8 |
112 |
3 |
1 |
Orientation |
方向 |
tiff |
9 |
117 |
4 |
1 |
JpegStripByteConunts |
JPEG图数据长度 |
tiff |
10 |
11A |
5 |
1 |
XResolution |
X分辨率 |
tiff |
11 |
11B |
5 |
1 |
YResolution |
Y分辨率 |
tiff |
12 |
128 |
3 |
1 |
ResolutionUnit |
分辨率单位 |
tiff |
13 |
132 |
2 |
20 |
DateTime |
修改时刻 |
tiff |
14 |
8769 |
4 |
1 |
ExifOffset |
exif信息IFD的偏置量 |
exif entries |
||||||
exif |
1 |
829A |
5 |
1 |
ExposureTime |
曝光时间 |
exif |
2 |
829D |
5 |
1 |
FNumber |
光圈 |
exif |
3 |
8822 |
3 |
1 |
ExposureProgram |
曝光程序 |
exif |
4 |
8827 |
3 |
1 |
ISOSpeedRatings |
ISO值 |
exif |
5 |
9000 |
7 |
4 |
ExifVersion |
exif版本 |
exif |
6 |
9003 |
2 |
14 |
DateTimeOriginal |
拍摄时刻 |
exif |
7 |
9004 |
2 |
14 |
DateTimeDigitized |
数字化时刻 |
exif |
8 |
9101 |
7 |
4 |
ComponentsConfiguration |
? |
exif |
9 |
9201 |
A |
1 |
ShutterSpeedValue |
比1秒快的级数 |
exif |
10 |
9202 |
5 |
1 |
ApertureValue |
比F1小的级数 |
exif |
11 |
9204 |
A |
1 |
ExposureBiasValue |
补偿级数 |
exif |
12 |
9207 |
3 |
1 |
MeteringMode |
测光模式 |
exif |
13 |
9209 |
3 |
1 |
Flash |
闪光灯运行 |
exif |
14 |
920A |
5 |
1 |
FocalLength |
镜头焦距 |
exif |
15 |
927C |
7 |
20A8 |
MakerNoteOffset |
makernote信息IFD的偏置量 |
makernote entries |
||||||
make |
1 |
1 |
3 |
46 |
CameraSettings1 |
相机设置1 |
make |
2 |
2 |
3 |
4 |
CanonFocalLength |
焦距 |
make |
3 |
3 |
3 |
4 |
||
make |
4 |
4 |
3 |
34 |
CameraSettings2 |
相机设置2 |
make |
5 |
6 |
2 |
32 |
Image Type |
图片类型 |
make |
6 |
7 |
2 |
32 |
Firmware Version |
硬件版本 |
make |
7 |
9 |
2 |
32 |
Owner Name |
拥有者 |
make |
8 |
C |
4 |
1 |
Camera Serial Number |
序列号,为4字节整数值 |
make |
9 |
D |
7 |
1024 |
||
make |
10 |
F |
3 |
19 |
Custom Functions |
用户设置 |
make |
11 |
10 |
4 |
1 |
||
make |
12 |
12 |
3 |
28 |
CanonPictureInfo |
图像信息 |
make |
13 |
13 |
3 |
4 |
||
make |
14 |
15 |
4 |
1 |
||
make |
15 |
19 |
3 |
1 |
||
make |
16 |
83 |
4 |
1 |
||
make |
17 |
93 |
3 |
16 |
CanonFileInfo |
文件信息 |
make |
18 |
A0 |
3 |
14 |
CanonColorInfo |
色彩信息 |
make |
19 |
AA |
3 |
5 |
||
make |
20 |
D0 |
4 |
1 |
||
make |
21 |
E0 |
3 |
17 |
||
make |
22 |
4001 |
3 |
582 |
||
make |
23 |
4002 |
3 |
2676 |
||
make |
24 |
4003 |
3 |
22 |
||
exif |
16 |
9286 |
7 |
108 |
UserComment |
用户备注 |
exif |
17 |
A000 |
7 |
4 |
FlashPixVersion |
flashpix版本 |
exif |
18 |
A001 |
3 |
1 |
ColorSpace |
色彩空间 |
exif |
19 |
A002 |
3 |
1 |
ExifImageWidth |
主图像宽度 |
exif |
20 |
A003 |
3 |
1 |
ExifImageHeight |
主图像高度 |
exif |
21 |
A005 |
4 |
1 |
ExifInteroperabilityOffset |
指向IFD。描述互通性 |
exif |
22 |
A20E |
5 |
1 |
FocalPlaneXResolution |
像面分辨率 |
exif |
23 |
A20F |
5 |
1 |
FocalPlaneYResolution |
像面分辨率 |
exif |
24 |
A210 |
3 |
1 |
FocalPlaneResolutionUnit |
像面分辨率单位 |
exif |
25 |
A401 |
3 |
1 |
CustomRendered |
渲染方式? |
exif |
26 |
A402 |
3 |
1 |
ExposureMode |
曝光模式 |
exif |
27 |
A403 |
3 |
1 |
WhiteBalance |
白平衡设置 |
exif |
28 |
A406 |
3 |
1 |
SceneCaptureType |
图像类型? |
ifd |
= |
|||||
offset= |
||||||
entries= |
||||||
tiff |
1 |
201 |
4 |
1 |
JpegIFOffset |
JPEG缩略图偏置量 |
tiff |
2 |
202 |
4 |
1 |
JpegIFByteCount |
JPEG缩略图数据长度 |
ifd |
= |
|||||
offset= |
||||||
entries= |
||||||
tiff |
1 |
100 |
3 |
1 |
RGBImageWidth |
RGB缩略图宽度 |
tiff |
2 |
101 |
3 |
1 |
RGBImageLength |
RGB缩略图高度 |
tiff |
3 |
102 |
3 |
3 |
RGBBitsPerSample |
每通道位数 |
tiff |
4 |
103 |
3 |
1 |
Compression |
压缩(无效?) |
tiff |
5 |
106 |
3 |
1 |
PhotometricInterpretation |
色彩空间 |
tiff |
6 |
111 |
4 |
1 |
RGBStripOffsets |
RGB缩略图偏置值 |
tiff |
7 |
115 |
3 |
1 |
RGBSamplesPerPixel |
每像素通道数 |
tiff |
8 |
116 |
3 |
1 |
RGBRowsPerStrip |
数据块中的行数 |
tiff |
9 |
117 |
4 |
1 |
RGBStripByteConunts |
RGB缩略图数据长度 |
tiff |
10 |
11C |
3 |
1 |
PlanarConfiguration |
如果是YCRCB,排列方式 |
tiff |
11 |
C5D9 |
4 |
1 |
||
ifd |
= |
|||||
offset= |
||||||
entries= |
||||||
tiff |
1 |
103 |
3 |
1 |
Compression |
压缩(无效?) |
tiff |
2 |
111 |
4 |
1 |
StripOffsets |
RAW图偏置值 |
tiff |
3 |
117 |
4 |
1 |
StripByteConunts |
RAW图数据长度 |
tiff |
4 |
C5D8 |
4 |
1 |
||
tiff |
5 |
C5E0 |
4 |
1 |
makernotes的部分内容是整型数组,按照在数组中的位置解析:
名称 |
数组中的位置 |
含义 |
CameraSettings1 |
1 |
Length in bytes |
2 |
Macro mode |
|
3 |
Self-timer in 1/10 second |
|
4 |
Quality |
|
5 |
Flash Mode |
|
6 |
Continuous drive mode |
|
7 |
||
8 |
Focus Mode |
|
9 |
||
10 |
||
11 |
Image Size |
|
12 |
Easy shooting Mode |
|
13 |
Digital Zoom |
|
14 |
Contrast |
|
15 |
Saturation |
|
16 |
Sharpness |
|
17 |
ISO Speed |
|
18 |
Metering Mode |
|
19 |
Focus Type |
|
20 |
Auto Focus Point Selected |
|
21 |
Exposure Mode |
|
22 |
||
23 |
LensType |
|
24 |
long focal length of lens |
|
25 |
short focal length of lens |
|
26 |
focal units per mm |
|
27 |
||
28 |
||
29 |
Flash Activity |
|
30 |
Flash details |
|
31 |
||
32 |
||
33 |
FocusContinuous |
|
34 |
||
35 |
||
36 |
||
37 |
ZoomedResolution |
|
38 |
ZoomedResolutionBase |
|
39 |
||
40 |
||
41 |
||
42 |
||
43 |
ColorTone |
|
44 |
||
45 |
||
46 |
||
Custom Functions |
1 |
Length in Byte |
2 |
SetFunctionWhenShooting |
|
3 |
LongExposureNoiseReduction |
|
4 |
FlashSyncSpeedAv |
|
5 |
Shutter-AELock |
|
6 |
AFAssistBeam |
|
7 |
ExposureLevelIncrements |
|
8 |
FlashFiring |
|
9 |
ISOExpansion |
|
10 |
AEBSequence |
|
11 |
SuperimposedDisplay |
|
12 |
MenuButtonDisplayPosition |
|
13 |
MirrorLockup |
|
14 |
AFPointSelectionMethod |
|
15 |
ETTLII |
|
16 |
ShutterCurtainSync |
|
17 |
SafetyShiftInAVorTV |
|
18 |
LensAFStopButton |
|
19 |
AddOriginalDecisionData |
|
CanonFocalLength |
2 |
FocalLength |
3 |
FocalPlaneXSize in 1/1000 inch |
|
4 |
FocalPlaneYSize in 1/1000 inch |
|
CanonPictureInfo |
3 |
CanonImageWidth |
4 |
CanonImageHeight |
|
5 |
CanonImageWidthAsShot |
|
6 |
CanonImageHeightAsShot |
|
23 |
AFPointsUsed |
|
CanonFileInfo |
1 |
Length in Byte |
2 |
Directory |
|
3 |
filenumber |
|
CanonColorInfo |
1 |
Length in Byte |
10 |
ColorTemperature |
|
CameraSettings2 |
1 |
Length in Byte |
2 |
||
3 |
ISO |
|
4 |
||
5 |
TargetAperture |
|
6 |
TargetExposureTime |
|
7 |
ExposureCompensation |
|
8 |
WhiteBalance |
|
9 |
||
10 |
SequenceNumber |
|
11 |
||
12 |
||
13 |
||
14 |
||
15 |
||
16 |
FlashExposureComp |
|
17 |
AutoExposureBracketing |
|
18 |
AEBBracketValue |
|
19 |
||
20 |
FocusDistanceUpper |
|
21 |
FocusDistanceLower |
|
22 |
FNumber |
|
23 |
ExposureTime |
|
24 |
||
25 |
Bulb Duration in 1/10 second |
|
26 |
||
27 |
||
28 |
AutoRotate |
|
29 |
||
30 |
Self-timer2 |
|
31 |
||
32 |
||
33 |
||
34 |
因为CR2是私有格式,有些tag的含义还没有搞清楚。
有趣的是在CR2中除了主图数据外,还含有3个缩略图,其中一个是大幅的JPEG图,另外2个是小幅的JPEG和位图。通过读取IFD可以找到它们在文件中的位置和数据长度,直接将对应的数据块保存下来就是标准的图像文件。
从RAW图像偏置值出发可以解码原始图像数据。
通过读DCRAW源程序学到了许多东西。终于搞明白了CR2的结构。
下面的一些术语可能用得不甚准确,只是用来描述编程的思路。
从RAW图像偏置值可以定位到RAW图像数据块的起始点。
以下双字节整型高位字节在前,低位字节在后。
第一个是整数0xFFDA。
接下来的信息块类似IFD,包含多个组,每个数据块组结构是:
第一个2字节的整数为tag,表示是哪种信息;
第二个2字节的整数为len,表示以字节为单位的数据长度,包括本整数。接下来是len字节长的数据。
tag(hex) len 说明 |
||
FFC3 |
4 |
2个双字节整数,第一个是存储图像高度,第二个是存储图像的宽度的一半 |
FFC4 |
60 |
解码表 |
FFDA |
- |
CCD压缩数据的开始 |
解码表是60个单字节的数组,1-30字节为奇数列(1,3,5……)的解码表,31-60字节是偶数列(2,4,6……)的解码表。
30字节的编解码表结构:
1:0用于奇数列,1用于偶数列。
2-17:按照顺序分别代表从1到16位码字的数量,其和为13,即共有13种码字。
18-30:每种码字后面对应的数据比特数。
码字的确定:
定义'最大码字'。(最大码字并非真正的码字)
设:‘0'位数的最大码字是0。
如果某个位数的最大码字是N,则下一个位数的最大码字为2*N+M,M是下一个位数的码字的数量。
下一个位数的码字从2*N开始依次增加,直到2*N+M-1。数量M分别由编码表中2-17字节对应的数值确定。
解码树:
按照以上编码表构造出2个二叉解码树。
解码树可以用链表来实现。对每一个码字,从根节点出发,测试每一位对应的节点,如果不存在,则创建节点。最后一位对应的节点存储数据比特数。
解压缩:
从FFDA开始是真正的CCD数据,按行的顺序排列,采用变长压缩形式,注意每个0xFF后面的一个字节被忽略。
每个像元的数据格式是码字+数据。由码字确定数据的长度,即比特数,进而读出数值。如果此数的最高位是0,则表示是负值,需要取补码。
例如,假设码字010对应的比特数是7,则以下比特列 0100010100100…… 解码过程是:
1)读010,解析出数据长度7比特
2)读7个比特:0010100 |b=20
3)最高位是0,实际数值是 20-1111111|b=-107
这样读出的每个像元位置上的数值还不是原始数据,而是经过了变换。恢复的步骤是:
1)第1行第1、2个数值分别加2048
2)第1、2列从第2行起,依次每个像元的值加上上一行同列像元的值
3)从第3列起,依次每个像元的值加上左边第2列同行像元的值
这样就获得了CCD的原始感光数据。20D的CFA排列方式是:
RGRGRGRG
GBGBGBGB
由此加上色彩均衡,即得到彩色图像。