1 | #------------------------------------------------------------------------------
|
---|
2 | # File: CanonRaw.pm
|
---|
3 | #
|
---|
4 | # Description: Read Canon RAW (CRW) meta information
|
---|
5 | #
|
---|
6 | # Revisions: 11/25/2003 - P. Harvey Created
|
---|
7 | # 12/02/2003 - P. Harvey Completely reworked and figured out many
|
---|
8 | # more tags
|
---|
9 | #
|
---|
10 | # References: 1) http://www.cybercom.net/~dcoffin/dcraw/
|
---|
11 | # 2) http://www.wonderland.org/crw/
|
---|
12 | # 3) http://xyrion.org/ciff/CIFFspecV1R04.pdf
|
---|
13 | # 4) Dave Nicholson private communication (PowerShot S30)
|
---|
14 | #------------------------------------------------------------------------------
|
---|
15 |
|
---|
16 | package Image::ExifTool::CanonRaw;
|
---|
17 |
|
---|
18 | use strict;
|
---|
19 | use vars qw($VERSION $AUTOLOAD %crwTagFormat);
|
---|
20 | use Image::ExifTool qw(:DataAccess :Utils);
|
---|
21 | use Image::ExifTool::Exif;
|
---|
22 | use Image::ExifTool::Canon;
|
---|
23 |
|
---|
24 | $VERSION = '1.58';
|
---|
25 |
|
---|
26 | sub WriteCRW($$);
|
---|
27 | sub ProcessCanonRaw($$$);
|
---|
28 | sub WriteCanonRaw($$$);
|
---|
29 | sub CheckCanonRaw($$$);
|
---|
30 | sub InitMakerNotes($);
|
---|
31 | sub SaveMakerNotes($);
|
---|
32 | sub BuildMakerNotes($$$$$$);
|
---|
33 |
|
---|
34 | # formats for CRW tag types (($tag >> 8) & 0x38)
|
---|
35 | # Note: don't define format for undefined types
|
---|
36 | %crwTagFormat = (
|
---|
37 | 0x00 => 'int8u',
|
---|
38 | 0x08 => 'string',
|
---|
39 | 0x10 => 'int16u',
|
---|
40 | 0x18 => 'int32u',
|
---|
41 | # 0x20 => 'undef',
|
---|
42 | # 0x28 => 'undef',
|
---|
43 | # 0x30 => 'undef',
|
---|
44 | );
|
---|
45 |
|
---|
46 | # Canon raw file tag table
|
---|
47 | # Note: Tag ID's have upper 2 bits set to zero, since these 2 bits
|
---|
48 | # just specify the location of the information
|
---|
49 | %Image::ExifTool::CanonRaw::Main = (
|
---|
50 | GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
|
---|
51 | PROCESS_PROC => \&ProcessCanonRaw,
|
---|
52 | WRITE_PROC => \&WriteCanonRaw,
|
---|
53 | CHECK_PROC => \&CheckCanonRaw,
|
---|
54 | WRITABLE => 1,
|
---|
55 | 0x0000 => { Name => 'NullRecord', Writable => 'undef' }, #3
|
---|
56 | 0x0001 => { #3
|
---|
57 | Name => 'FreeBytes',
|
---|
58 | Format => 'undef',
|
---|
59 | Binary => 1,
|
---|
60 | },
|
---|
61 | 0x0032 => { Name => 'CanonColorInfo1', Writable => 0 },
|
---|
62 | 0x0805 => [
|
---|
63 | # this tag is found in more than one directory...
|
---|
64 | {
|
---|
65 | Condition => '$self->{DIR_NAME} eq "ImageDescription"',
|
---|
66 | Name => 'CanonFileDescription',
|
---|
67 | Writable => 'string[32]',
|
---|
68 | },
|
---|
69 | {
|
---|
70 | Name => 'UserComment',
|
---|
71 | Writable => 'string[256]',
|
---|
72 | },
|
---|
73 | ],
|
---|
74 | 0x080a => {
|
---|
75 | Name => 'CanonRawMakeModel',
|
---|
76 | Writable => 0,
|
---|
77 | SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::MakeModel' },
|
---|
78 | },
|
---|
79 | 0x080b => { Name => 'CanonFirmwareVersion', Writable => 'string[32]' },
|
---|
80 | 0x080c => { Name => 'ComponentVersion', Writable => 'string' }, #3
|
---|
81 | 0x080d => { Name => 'ROMOperationMode', Writable => 'string[8]' }, #3
|
---|
82 | 0x0810 => { Name => 'OwnerName', Writable => 'string[32]' },
|
---|
83 | 0x0815 => { Name => 'CanonImageType', Writable => 'string[32]' },
|
---|
84 | 0x0816 => { Name => 'OriginalFileName', Writable => 'string[32]' },
|
---|
85 | 0x0817 => { Name => 'ThumbnailFileName', Writable => 'string[32]' },
|
---|
86 | 0x100a => { #3
|
---|
87 | Name => 'TargetImageType',
|
---|
88 | Writable => 'int16u',
|
---|
89 | PrintConv => {
|
---|
90 | 0 => 'Real-world Subject',
|
---|
91 | 1 => 'Written Document',
|
---|
92 | },
|
---|
93 | },
|
---|
94 | 0x1010 => { #3
|
---|
95 | Name => 'ShutterReleaseMethod',
|
---|
96 | Writable => 'int16u',
|
---|
97 | PrintConv => {
|
---|
98 | 0 => 'Single Shot',
|
---|
99 | 2 => 'Continuous Shooting',
|
---|
100 | },
|
---|
101 | },
|
---|
102 | 0x1011 => { #3
|
---|
103 | Name => 'ShutterReleaseTiming',
|
---|
104 | Writable => 'int16u',
|
---|
105 | PrintConv => {
|
---|
106 | 0 => 'Priority on shutter',
|
---|
107 | 1 => 'Priority on focus',
|
---|
108 | },
|
---|
109 | },
|
---|
110 | 0x1016 => { Name => 'ReleaseSetting', Writable => 'int16u' }, #3
|
---|
111 | 0x101c => { Name => 'BaseISO', Writable => 'int16u' }, #3
|
---|
112 | 0x1028=> { #PH
|
---|
113 | Name => 'CanonFlashInfo',
|
---|
114 | Writable => 'int16u',
|
---|
115 | Count => 4,
|
---|
116 | Unknown => 1,
|
---|
117 | },
|
---|
118 | 0x1029 => {
|
---|
119 | Name => 'CanonFocalLength',
|
---|
120 | Writable => 0,
|
---|
121 | SubDirectory => { TagTable => 'Image::ExifTool::Canon::FocalLength' },
|
---|
122 | },
|
---|
123 | 0x102a => {
|
---|
124 | Name => 'CanonShotInfo',
|
---|
125 | Writable => 0,
|
---|
126 | SubDirectory => { TagTable => 'Image::ExifTool::Canon::ShotInfo' },
|
---|
127 | },
|
---|
128 | 0x102c => {
|
---|
129 | Name => 'CanonColorInfo2',
|
---|
130 | Writable => 0,
|
---|
131 | # for the S30, the following information has been decoded: (ref 4)
|
---|
132 | # offset 66: int32u - shutter half press time in ms
|
---|
133 | # offset 70: int32u - image capture time in ms
|
---|
134 | # offset 74: int16u - custom white balance flag (0=Off, 512=On)
|
---|
135 | },
|
---|
136 | 0x102d => {
|
---|
137 | Name => 'CanonCameraSettings',
|
---|
138 | Writable => 0,
|
---|
139 | SubDirectory => { TagTable => 'Image::ExifTool::Canon::CameraSettings' },
|
---|
140 | },
|
---|
141 | 0x1030 => { #4
|
---|
142 | Name => 'WhiteSample',
|
---|
143 | Writable => 0,
|
---|
144 | SubDirectory => {
|
---|
145 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
|
---|
146 | TagTable => 'Image::ExifTool::CanonRaw::WhiteSample',
|
---|
147 | },
|
---|
148 | },
|
---|
149 | 0x1031 => {
|
---|
150 | Name => 'SensorInfo',
|
---|
151 | Writable => 0,
|
---|
152 | SubDirectory => { TagTable => 'Image::ExifTool::Canon::SensorInfo' },
|
---|
153 | },
|
---|
154 | # this tag has only be verified for the 10D in CRW files, but the D30 and D60
|
---|
155 | # also produce CRW images and have CustomFunction information in their JPEG's
|
---|
156 | 0x1033 => [
|
---|
157 | {
|
---|
158 | Name => 'CustomFunctions10D',
|
---|
159 | Condition => '$self->{Model} =~ /EOS 10D/',
|
---|
160 | SubDirectory => {
|
---|
161 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
|
---|
162 | TagTable => 'Image::ExifTool::CanonCustom::Functions10D',
|
---|
163 | },
|
---|
164 | },
|
---|
165 | {
|
---|
166 | Name => 'CustomFunctionsD30',
|
---|
167 | Condition => '$self->{Model} =~ /EOS D30\b/',
|
---|
168 | SubDirectory => {
|
---|
169 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
|
---|
170 | TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
|
---|
171 | },
|
---|
172 | },
|
---|
173 | {
|
---|
174 | Name => 'CustomFunctionsD60',
|
---|
175 | Condition => '$self->{Model} =~ /EOS D60\b/',
|
---|
176 | SubDirectory => {
|
---|
177 | # the stored size in the D60 apparently doesn't include the size word:
|
---|
178 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size-2,$size)',
|
---|
179 | # (D60 custom functions are basically the same as D30)
|
---|
180 | TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
|
---|
181 | },
|
---|
182 | },
|
---|
183 | {
|
---|
184 | Name => 'CustomFunctionsUnknown',
|
---|
185 | SubDirectory => {
|
---|
186 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
|
---|
187 | TagTable => 'Image::ExifTool::CanonCustom::FuncsUnknown',
|
---|
188 | },
|
---|
189 | },
|
---|
190 | ],
|
---|
191 | 0x1038 => {
|
---|
192 | Name => 'CanonAFInfo',
|
---|
193 | Writable => 0,
|
---|
194 | SubDirectory => { TagTable => 'Image::ExifTool::Canon::AFInfo' },
|
---|
195 | },
|
---|
196 | 0x1093 => {
|
---|
197 | Name => 'CanonFileInfo',
|
---|
198 | SubDirectory => {
|
---|
199 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
|
---|
200 | TagTable => 'Image::ExifTool::Canon::FileInfo',
|
---|
201 | },
|
---|
202 | },
|
---|
203 | 0x10a9 => {
|
---|
204 | Name => 'ColorBalance',
|
---|
205 | Writable => 0,
|
---|
206 | SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorBalance' },
|
---|
207 | },
|
---|
208 | 0x10b5 => { #PH
|
---|
209 | Name => 'RawJpgInfo',
|
---|
210 | SubDirectory => {
|
---|
211 | Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
|
---|
212 | TagTable => 'Image::ExifTool::CanonRaw::RawJpgInfo',
|
---|
213 | },
|
---|
214 | },
|
---|
215 | 0x10ae => {
|
---|
216 | Name => 'ColorTemperature',
|
---|
217 | Writable => 'int16u',
|
---|
218 | },
|
---|
219 | 0x10b4 => {
|
---|
220 | Name => 'ColorSpace',
|
---|
221 | Writable => 'int16u',
|
---|
222 | PrintConv => {
|
---|
223 | 1 => 'sRGB',
|
---|
224 | 2 => 'Adobe RGB',
|
---|
225 | 0xffff => 'Uncalibrated',
|
---|
226 | },
|
---|
227 | },
|
---|
228 | 0x1803 => { #3
|
---|
229 | Name => 'ImageFormat',
|
---|
230 | Writable => 0,
|
---|
231 | SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::ImageFormat' },
|
---|
232 | },
|
---|
233 | 0x1804 => { Name => 'RecordID', Writable => 'int32u' }, #3
|
---|
234 | 0x1806 => { #3
|
---|
235 | Name => 'SelfTimerTime',
|
---|
236 | Writable => 'int32u',
|
---|
237 | ValueConv => '$val / 1000',
|
---|
238 | ValueConvInv => '$val * 1000',
|
---|
239 | PrintConv => '"$val s"',
|
---|
240 | PrintConvInv => '$val=~s/\s*s.*//;$val',
|
---|
241 | },
|
---|
242 | 0x1807 => {
|
---|
243 | Name => 'TargetDistanceSetting',
|
---|
244 | Format => 'float',
|
---|
245 | PrintConv => '"$val mm"',
|
---|
246 | PrintConvInv => '$val=~s/\s*mm$//;$val',
|
---|
247 | },
|
---|
248 | 0x180b => [
|
---|
249 | {
|
---|
250 | # D30
|
---|
251 | Name => 'SerialNumber',
|
---|
252 | Condition => '$$self{Model} =~ /EOS D30\b/',
|
---|
253 | Writable => 'int32u',
|
---|
254 | PrintConv => 'sprintf("%x-%.5d",$val>>16,$val&0xffff)',
|
---|
255 | PrintConvInv => '$val=~/(.*)-(\d+)/ ? (hex($1)<<16)+$2 : undef',
|
---|
256 | },
|
---|
257 | {
|
---|
258 | # all EOS models (D30, 10D, 300D)
|
---|
259 | Name => 'SerialNumber',
|
---|
260 | Condition => '$$self{Model} =~ /EOS/',
|
---|
261 | Writable => 'int32u',
|
---|
262 | PrintConv => 'sprintf("%.10d",$val)',
|
---|
263 | PrintConvInv => '$val',
|
---|
264 | },
|
---|
265 | {
|
---|
266 | # this is not SerialNumber for PowerShot models (but what is it?) - PH
|
---|
267 | Name => 'UnknownNumber',
|
---|
268 | Unknown => 1,
|
---|
269 | },
|
---|
270 | ],
|
---|
271 | 0x180e => {
|
---|
272 | Name => 'TimeStamp',
|
---|
273 | Writable => 0,
|
---|
274 | SubDirectory => {
|
---|
275 | TagTable => 'Image::ExifTool::CanonRaw::TimeStamp',
|
---|
276 | },
|
---|
277 | },
|
---|
278 | 0x1810 => {
|
---|
279 | Name => 'ImageInfo',
|
---|
280 | Writable => 0,
|
---|
281 | SubDirectory => {
|
---|
282 | TagTable => 'Image::ExifTool::CanonRaw::ImageInfo',
|
---|
283 | },
|
---|
284 | },
|
---|
285 | 0x1813 => { #3
|
---|
286 | Name => 'FlashInfo',
|
---|
287 | Writable => 0,
|
---|
288 | SubDirectory => {
|
---|
289 | TagTable => 'Image::ExifTool::CanonRaw::FlashInfo',
|
---|
290 | },
|
---|
291 | },
|
---|
292 | 0x1814 => { #3
|
---|
293 | Name => 'MeasuredEV',
|
---|
294 | Notes => q{
|
---|
295 | this is the Canon name for what could better be called MeasuredLV, and
|
---|
296 | should be close to the calculated LightValue for a proper exposure with most
|
---|
297 | models
|
---|
298 | },
|
---|
299 | Format => 'float',
|
---|
300 | ValueConv => '$val + 5',
|
---|
301 | ValueConvInv => '$val - 5',
|
---|
302 | },
|
---|
303 | 0x1817 => {
|
---|
304 | Name => 'FileNumber',
|
---|
305 | Writable => 'int32u',
|
---|
306 | Groups => { 2 => 'Image' },
|
---|
307 | PrintConv => '$_=$val;s/(\d+)(\d{4})/$1-$2/;$_',
|
---|
308 | PrintConvInv => '$_=$val;s/-//;$_',
|
---|
309 | },
|
---|
310 | 0x1818 => { #3
|
---|
311 | Name => 'ExposureInfo',
|
---|
312 | Groups => { 1 => 'CIFF' }, # (only so CIFF shows up in group lists)
|
---|
313 | Writable => 0,
|
---|
314 | SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::ExposureInfo' },
|
---|
315 | },
|
---|
316 | 0x1834 => { #PH
|
---|
317 | Name => 'CanonModelID',
|
---|
318 | Writable => 'int32u',
|
---|
319 | PrintHex => 1,
|
---|
320 | Notes => q{
|
---|
321 | this is the complete list of model ID numbers, but note that many of these
|
---|
322 | models do not produce CRW images
|
---|
323 | },
|
---|
324 | SeparateTable => 'Canon CanonModelID',
|
---|
325 | PrintConv => \%Image::ExifTool::Canon::canonModelID,
|
---|
326 | },
|
---|
327 | 0x1835 => {
|
---|
328 | Name => 'DecoderTable',
|
---|
329 | Writable => 0,
|
---|
330 | SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::DecoderTable' },
|
---|
331 | },
|
---|
332 | 0x183b => { #PH
|
---|
333 | # display format for serial number
|
---|
334 | Name => 'SerialNumberFormat',
|
---|
335 | Writable => 'int32u',
|
---|
336 | PrintHex => 1,
|
---|
337 | PrintConv => {
|
---|
338 | 0x90000000 => 'Format 1',
|
---|
339 | 0xa0000000 => 'Format 2',
|
---|
340 | },
|
---|
341 | },
|
---|
342 | 0x2005 => {
|
---|
343 | Name => 'RawData',
|
---|
344 | Writable => 0,
|
---|
345 | Binary => 1,
|
---|
346 | },
|
---|
347 | 0x2007 => {
|
---|
348 | Name => 'JpgFromRaw',
|
---|
349 | Groups => { 2 => 'Preview' },
|
---|
350 | Writable => 'resize', # 'resize' allows this value to change size
|
---|
351 | Permanent => 0,
|
---|
352 | RawConv => '$self->ValidateImage(\$val,$tag)',
|
---|
353 | },
|
---|
354 | 0x2008 => {
|
---|
355 | Name => 'ThumbnailImage',
|
---|
356 | Groups => { 2 => 'Preview' },
|
---|
357 | Writable => 'resize', # 'resize' allows this value to change size
|
---|
358 | WriteCheck => '$self->CheckImage(\$val)',
|
---|
359 | Permanent => 0,
|
---|
360 | RawConv => '$self->ValidateImage(\$val,$tag)',
|
---|
361 | },
|
---|
362 | # the following entries are subdirectories
|
---|
363 | # (any 0x28 and 0x30 tag types are handled automatically by the decoding logic)
|
---|
364 | 0x2804 => {
|
---|
365 | Name => 'ImageDescription',
|
---|
366 | SubDirectory => { },
|
---|
367 | Writable => 0,
|
---|
368 | },
|
---|
369 | 0x2807 => { #3
|
---|
370 | Name => 'CameraObject',
|
---|
371 | SubDirectory => { },
|
---|
372 | Writable => 0,
|
---|
373 | },
|
---|
374 | 0x3002 => { #3
|
---|
375 | Name => 'ShootingRecord',
|
---|
376 | SubDirectory => { },
|
---|
377 | Writable => 0,
|
---|
378 | },
|
---|
379 | 0x3003 => { #3
|
---|
380 | Name => 'MeasuredInfo',
|
---|
381 | SubDirectory => { },
|
---|
382 | Writable => 0,
|
---|
383 | },
|
---|
384 | 0x3004 => { #3
|
---|
385 | Name => 'CameraSpecification',
|
---|
386 | SubDirectory => { },
|
---|
387 | Writable => 0,
|
---|
388 | },
|
---|
389 | 0x300a => { #3
|
---|
390 | Name => 'ImageProps',
|
---|
391 | SubDirectory => { },
|
---|
392 | Writable => 0,
|
---|
393 | },
|
---|
394 | 0x300b => {
|
---|
395 | Name => 'ExifInformation',
|
---|
396 | SubDirectory => { },
|
---|
397 | Writable => 0,
|
---|
398 | },
|
---|
399 | );
|
---|
400 |
|
---|
401 | # Canon binary data blocks
|
---|
402 | %Image::ExifTool::CanonRaw::MakeModel = (
|
---|
403 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
404 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
405 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
406 | DATAMEMBER => [ 0, 6 ], # indices of data members to extract when writing
|
---|
407 | WRITABLE => 1,
|
---|
408 | FORMAT => 'string',
|
---|
409 | GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
|
---|
410 | # (can't specify a first entry because this isn't
|
---|
411 | # a simple binary table with fixed offsets)
|
---|
412 | 0 => {
|
---|
413 | Name => 'Make',
|
---|
414 | Format => 'string[6]', # "Canon\0"
|
---|
415 | DataMember => 'Make',
|
---|
416 | RawConv => '$self->{Make} = $val',
|
---|
417 | },
|
---|
418 | 6 => {
|
---|
419 | Name => 'Model',
|
---|
420 | Format => 'string', # no size = to the end of the data
|
---|
421 | Description => 'Camera Model Name',
|
---|
422 | DataMember => 'Model',
|
---|
423 | RawConv => '$self->{Model} = $val',
|
---|
424 | },
|
---|
425 | );
|
---|
426 |
|
---|
427 | %Image::ExifTool::CanonRaw::TimeStamp = (
|
---|
428 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
429 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
430 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
431 | WRITABLE => 1,
|
---|
432 | FORMAT => 'int32u',
|
---|
433 | FIRST_ENTRY => 0,
|
---|
434 | GROUPS => { 0 => 'MakerNotes', 2 => 'Time' },
|
---|
435 | 0 => {
|
---|
436 | Name => 'DateTimeOriginal',
|
---|
437 | Description => 'Date/Time Original',
|
---|
438 | Shift => 'Time',
|
---|
439 | ValueConv => 'ConvertUnixTime($val)',
|
---|
440 | ValueConvInv => 'GetUnixTime($val)',
|
---|
441 | PrintConv => '$self->ConvertDateTime($val)',
|
---|
442 | PrintConvInv => '$self->InverseDateTime($val)',
|
---|
443 | },
|
---|
444 | 1 => { #3
|
---|
445 | Name => 'TimeZoneCode',
|
---|
446 | Format => 'int32s',
|
---|
447 | ValueConv => '$val / 3600',
|
---|
448 | ValueConvInv => '$val * 3600',
|
---|
449 | },
|
---|
450 | 2 => { #3
|
---|
451 | Name => 'TimeZoneInfo',
|
---|
452 | Notes => 'set to 0x80000000 if TimeZoneCode is valid',
|
---|
453 | },
|
---|
454 | );
|
---|
455 |
|
---|
456 | %Image::ExifTool::CanonRaw::ImageFormat = (
|
---|
457 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
458 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
459 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
460 | WRITABLE => 1,
|
---|
461 | FORMAT => 'int32u',
|
---|
462 | FIRST_ENTRY => 0,
|
---|
463 | GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
|
---|
464 | 0 => {
|
---|
465 | Name => 'FileFormat',
|
---|
466 | Flags => 'PrintHex',
|
---|
467 | PrintConv => {
|
---|
468 | 0x00010000 => 'JPEG (lossy)',
|
---|
469 | 0x00010002 => 'JPEG (non-quantization)',
|
---|
470 | 0x00010003 => 'JPEG (lossy/non-quantization toggled)',
|
---|
471 | 0x00020001 => 'CRW',
|
---|
472 | },
|
---|
473 | },
|
---|
474 | 1 => {
|
---|
475 | Name => 'TargetCompressionRatio',
|
---|
476 | Format => 'float',
|
---|
477 | },
|
---|
478 | );
|
---|
479 |
|
---|
480 | %Image::ExifTool::CanonRaw::RawJpgInfo = (
|
---|
481 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
482 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
483 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
484 | WRITABLE => 1,
|
---|
485 | FORMAT => 'int16u',
|
---|
486 | FIRST_ENTRY => 1,
|
---|
487 | GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
|
---|
488 | # 0 => 'RawJpgInfoSize',
|
---|
489 | 1 => { #PH
|
---|
490 | Name => 'RawJpgQuality',
|
---|
491 | PrintConv => {
|
---|
492 | 1 => 'Economy',
|
---|
493 | 2 => 'Normal',
|
---|
494 | 3 => 'Fine',
|
---|
495 | 5 => 'Superfine',
|
---|
496 | },
|
---|
497 | },
|
---|
498 | 2 => { #PH
|
---|
499 | Name => 'RawJpgSize',
|
---|
500 | PrintConv => {
|
---|
501 | 0 => 'Large',
|
---|
502 | 1 => 'Medium',
|
---|
503 | 2 => 'Small',
|
---|
504 | },
|
---|
505 | },
|
---|
506 | 3 => 'RawJpgWidth', #PH
|
---|
507 | 4 => 'RawJpgHeight', #PH
|
---|
508 | );
|
---|
509 |
|
---|
510 | %Image::ExifTool::CanonRaw::FlashInfo = (
|
---|
511 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
512 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
513 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
514 | WRITABLE => 1,
|
---|
515 | FORMAT => 'float',
|
---|
516 | FIRST_ENTRY => 0,
|
---|
517 | GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
|
---|
518 | 0 => 'FlashGuideNumber',
|
---|
519 | 1 => 'FlashThreshold',
|
---|
520 | );
|
---|
521 |
|
---|
522 | %Image::ExifTool::CanonRaw::ExposureInfo = (
|
---|
523 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
524 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
525 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
526 | WRITABLE => 1,
|
---|
527 | FORMAT => 'float',
|
---|
528 | FIRST_ENTRY => 0,
|
---|
529 | GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
|
---|
530 | 0 => 'ExposureCompensation',
|
---|
531 | 1 => {
|
---|
532 | Name => 'ShutterSpeedValue',
|
---|
533 | ValueConv => 'abs($val)<100 ? 1/(2**$val) : 0',
|
---|
534 | ValueConvInv => '$val>0 ? -log($val)/log(2) : -100',
|
---|
535 | PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
|
---|
536 | PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
|
---|
537 | },
|
---|
538 | 2 => {
|
---|
539 | Name => 'ApertureValue',
|
---|
540 | ValueConv => '2 ** ($val / 2)',
|
---|
541 | ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
|
---|
542 | PrintConv => 'sprintf("%.1f",$val)',
|
---|
543 | PrintConvInv => '$val',
|
---|
544 | },
|
---|
545 | );
|
---|
546 |
|
---|
547 | %Image::ExifTool::CanonRaw::ImageInfo = (
|
---|
548 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
549 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
550 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
551 | FORMAT => 'int32u',
|
---|
552 | FIRST_ENTRY => 0,
|
---|
553 | GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
|
---|
554 | # Note: Don't make these writable (except rotation) because it confuses
|
---|
555 | # Canon decoding software if the are changed
|
---|
556 | 0 => 'ImageWidth', #3
|
---|
557 | 1 => 'ImageHeight', #3
|
---|
558 | 2 => { #3
|
---|
559 | Name => 'PixelAspectRatio',
|
---|
560 | Format => 'float',
|
---|
561 | },
|
---|
562 | 3 => {
|
---|
563 | Name => 'Rotation',
|
---|
564 | Format => 'int32s',
|
---|
565 | Writable => 'int32s',
|
---|
566 | },
|
---|
567 | 4 => 'ComponentBitDepth', #3
|
---|
568 | 5 => 'ColorBitDepth', #3
|
---|
569 | 6 => 'ColorBW', #3
|
---|
570 | );
|
---|
571 |
|
---|
572 | # ref 4
|
---|
573 | %Image::ExifTool::CanonRaw::DecoderTable = (
|
---|
574 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
575 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
576 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
577 | GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
|
---|
578 | FORMAT => 'int32u',
|
---|
579 | FIRST_ENTRY => 0,
|
---|
580 | 0 => 'DecoderTableNumber',
|
---|
581 | 2 => 'CompressedDataOffset',
|
---|
582 | 3 => 'CompressedDataLength',
|
---|
583 | );
|
---|
584 |
|
---|
585 | # ref 1/4
|
---|
586 | %Image::ExifTool::CanonRaw::WhiteSample = (
|
---|
587 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
588 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
589 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
590 | GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
|
---|
591 | FORMAT => 'int16u',
|
---|
592 | FIRST_ENTRY => 1,
|
---|
593 | 1 => 'WhiteSampleWidth',
|
---|
594 | 2 => 'WhiteSampleHeight',
|
---|
595 | 3 => 'WhiteSampleLeftBorder',
|
---|
596 | 4 => 'WhiteSampleTopBorder',
|
---|
597 | 5 => 'WhiteSampleBits',
|
---|
598 | # this is followed by the encrypted white sample values (ref 1)
|
---|
599 | );
|
---|
600 |
|
---|
601 | #------------------------------------------------------------------------------
|
---|
602 | # AutoLoad our writer routines when necessary
|
---|
603 | #
|
---|
604 | sub AUTOLOAD
|
---|
605 | {
|
---|
606 | return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
|
---|
607 | }
|
---|
608 |
|
---|
609 | #------------------------------------------------------------------------------
|
---|
610 | # Process Raw file directory
|
---|
611 | # Inputs: 0) ExifTool object reference
|
---|
612 | # 1) directory information reference, 2) tag table reference
|
---|
613 | # Returns: 1 on success
|
---|
614 | sub ProcessCanonRaw($$$)
|
---|
615 | {
|
---|
616 | my ($et, $dirInfo, $rawTagTable) = @_;
|
---|
617 | my $blockStart = $$dirInfo{DirStart};
|
---|
618 | my $blockSize = $$dirInfo{DirLen};
|
---|
619 | my $raf = $$dirInfo{RAF} or return 0;
|
---|
620 | my $buff;
|
---|
621 | my $verbose = $et->Options('Verbose');
|
---|
622 | my $buildMakerNotes = $et->Options('MakerNotes');
|
---|
623 |
|
---|
624 | # 4 bytes at end of block give directory position within block
|
---|
625 | $raf->Seek($blockStart+$blockSize-4, 0) or return 0;
|
---|
626 | $raf->Read($buff, 4) == 4 or return 0;
|
---|
627 | my $dirOffset = Get32u(\$buff,0) + $blockStart;
|
---|
628 | $raf->Seek($dirOffset, 0) or return 0;
|
---|
629 | $raf->Read($buff, 2) == 2 or return 0;
|
---|
630 | my $entries = Get16u(\$buff,0); # get number of entries in directory
|
---|
631 | # read the directory (10 bytes per entry)
|
---|
632 | $raf->Read($buff, 10 * $entries) == 10 * $entries or return 0;
|
---|
633 |
|
---|
634 | $verbose and $et->VerboseDir('CIFF', $entries);
|
---|
635 | my $index;
|
---|
636 | for ($index=0; $index<$entries; ++$index) {
|
---|
637 | my $pt = 10 * $index;
|
---|
638 | my $tag = Get16u(\$buff, $pt);
|
---|
639 | my $size = Get32u(\$buff, $pt+2);
|
---|
640 | my $valuePtr = Get32u(\$buff, $pt+6);
|
---|
641 | my $ptr = $valuePtr + $blockStart; # all pointers relative to block start
|
---|
642 | if ($tag & 0x8000) {
|
---|
643 | $et->Warn('Bad CRW directory entry');
|
---|
644 | return 1;
|
---|
645 | }
|
---|
646 | my $tagID = $tag & 0x3fff; # get tag ID
|
---|
647 | my $tagType = ($tag >> 8) & 0x38; # get tag type
|
---|
648 | my $valueInDir = ($tag & 0x4000); # flag for value in directory
|
---|
649 | my $tagInfo = $et->GetTagInfo($rawTagTable, $tagID);
|
---|
650 | if (($tagType==0x28 or $tagType==0x30) and not $valueInDir) {
|
---|
651 | # this type of tag specifies a raw subdirectory
|
---|
652 | my $name;
|
---|
653 | $tagInfo and $name = $$tagInfo{Name};
|
---|
654 | $name or $name = sprintf("CanonRaw_0x%.4x", $tag);
|
---|
655 | my %subdirInfo = (
|
---|
656 | DirName => $name,
|
---|
657 | DataLen => 0,
|
---|
658 | DirStart => $ptr,
|
---|
659 | DirLen => $size,
|
---|
660 | Nesting => $$dirInfo{Nesting} + 1,
|
---|
661 | RAF => $raf,
|
---|
662 | Parent => $$dirInfo{DirName},
|
---|
663 | );
|
---|
664 | if ($verbose) {
|
---|
665 | my $fakeInfo = { Name => $name, SubDirectory => { } };
|
---|
666 | $et->VerboseInfo($tagID, $fakeInfo,
|
---|
667 | 'Index' => $index,
|
---|
668 | 'Size' => $size,
|
---|
669 | 'Start' => $ptr,
|
---|
670 | );
|
---|
671 | }
|
---|
672 | $et->ProcessDirectory(\%subdirInfo, $rawTagTable);
|
---|
673 | next;
|
---|
674 | }
|
---|
675 | my ($valueDataPos, $count, $subdir);
|
---|
676 | my $format = $crwTagFormat{$tagType};
|
---|
677 | if ($tagInfo) {
|
---|
678 | $subdir = $$tagInfo{SubDirectory};
|
---|
679 | $format = $$tagInfo{Format} if $$tagInfo{Format};
|
---|
680 | $count = $$tagInfo{Count};
|
---|
681 | }
|
---|
682 | # get value data
|
---|
683 | my ($value, $delRawConv);
|
---|
684 | if ($valueInDir) { # is the value data in the directory?
|
---|
685 | # this type of tag stores the value in the 'size' and 'ptr' fields
|
---|
686 | $valueDataPos = $dirOffset + $pt + 4; # (remember, +2 for the entry count)
|
---|
687 | $size = 8;
|
---|
688 | $value = substr($buff, $pt+2, $size);
|
---|
689 | # set count to 1 by default for normal values in directory
|
---|
690 | $count = 1 if not defined $count and $format and
|
---|
691 | $format ne 'string' and not $subdir;
|
---|
692 | } else {
|
---|
693 | $valueDataPos = $ptr;
|
---|
694 | if ($size <= 512 or ($verbose > 2 and $size <= 65536)
|
---|
695 | or ($tagInfo and ($$tagInfo{SubDirectory}
|
---|
696 | or grep(/^$$tagInfo{Name}$/i, $et->GetRequestedTags()) )))
|
---|
697 | {
|
---|
698 | # read value if size is small or specifically requested
|
---|
699 | # or if this is a SubDirectory
|
---|
700 | unless ($raf->Seek($ptr, 0) and $raf->Read($value, $size) == $size) {
|
---|
701 | $et->Warn(sprintf("Error reading %d bytes from 0x%x",$size,$ptr));
|
---|
702 | next;
|
---|
703 | }
|
---|
704 | } else {
|
---|
705 | $value = "Binary data $size bytes";
|
---|
706 | if ($tagInfo) {
|
---|
707 | if ($et->Options('Binary') or $verbose) {
|
---|
708 | # read the value anyway
|
---|
709 | unless ($raf->Seek($ptr, 0) and $raf->Read($value, $size) == $size) {
|
---|
710 | $et->Warn(sprintf("Error reading %d bytes from 0x%x",$size,$ptr));
|
---|
711 | next;
|
---|
712 | }
|
---|
713 | }
|
---|
714 | # force this to be a binary (scalar reference)
|
---|
715 | $$tagInfo{RawConv} = '\$val';
|
---|
716 | $delRawConv = 1;
|
---|
717 | }
|
---|
718 | $size = length $value;
|
---|
719 | undef $format;
|
---|
720 | }
|
---|
721 | }
|
---|
722 | # set count from tagInfo count if necessary
|
---|
723 | if ($format and not $count) {
|
---|
724 | # set count according to format and size
|
---|
725 | my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
|
---|
726 | my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
|
---|
727 | $count = int($size / $fsiz);
|
---|
728 | }
|
---|
729 | if ($verbose) {
|
---|
730 | my $val = $value;
|
---|
731 | $format and $val = ReadValue(\$val, 0, $format, $count, $size);
|
---|
732 | $et->VerboseInfo($tagID, $tagInfo,
|
---|
733 | Table => $rawTagTable,
|
---|
734 | Index => $index,
|
---|
735 | Value => $val,
|
---|
736 | DataPt => \$value,
|
---|
737 | DataPos => $valueDataPos,
|
---|
738 | Size => $size,
|
---|
739 | Format => $format,
|
---|
740 | Count => $count,
|
---|
741 | );
|
---|
742 | }
|
---|
743 | if ($buildMakerNotes) {
|
---|
744 | # build maker notes information if requested
|
---|
745 | BuildMakerNotes($et, $tagID, $tagInfo, \$value, $format, $count);
|
---|
746 | }
|
---|
747 | next unless defined $tagInfo;
|
---|
748 |
|
---|
749 | if ($subdir) {
|
---|
750 | my $name = $$tagInfo{Name};
|
---|
751 | my $newTagTable;
|
---|
752 | if ($$subdir{TagTable}) {
|
---|
753 | $newTagTable = GetTagTable($$subdir{TagTable});
|
---|
754 | unless ($newTagTable) {
|
---|
755 | warn "Unknown tag table $$subdir{TagTable}\n";
|
---|
756 | next;
|
---|
757 | }
|
---|
758 | } else {
|
---|
759 | warn "Must specify TagTable for SubDirectory $name\n";
|
---|
760 | next;
|
---|
761 | }
|
---|
762 | my $subdirStart = 0;
|
---|
763 | #### eval Start ()
|
---|
764 | $subdirStart = eval $$subdir{Start} if $$subdir{Start};
|
---|
765 | my $dirData = \$value;
|
---|
766 | my %subdirInfo = (
|
---|
767 | Name => $name,
|
---|
768 | DataPt => $dirData,
|
---|
769 | DataLen => $size,
|
---|
770 | DataPos => $valueDataPos,
|
---|
771 | DirStart => $subdirStart,
|
---|
772 | DirLen => $size - $subdirStart,
|
---|
773 | Nesting => $$dirInfo{Nesting} + 1,
|
---|
774 | RAF => $raf,
|
---|
775 | DirName => $name,
|
---|
776 | Parent => $$dirInfo{DirName},
|
---|
777 | );
|
---|
778 | #### eval Validate ($dirData, $subdirStart, $size)
|
---|
779 | if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
|
---|
780 | $et->Warn("Invalid $name data");
|
---|
781 | } else {
|
---|
782 | $et->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
|
---|
783 | }
|
---|
784 | } else {
|
---|
785 | # convert to specified format if necessary
|
---|
786 | $format and $value = ReadValue(\$value, 0, $format, $count, $size);
|
---|
787 | # save the information
|
---|
788 | $et->FoundTag($tagInfo, $value);
|
---|
789 | delete $$tagInfo{RawConv} if $delRawConv;
|
---|
790 | }
|
---|
791 | }
|
---|
792 | return 1;
|
---|
793 | }
|
---|
794 |
|
---|
795 | #------------------------------------------------------------------------------
|
---|
796 | # get information from raw file
|
---|
797 | # Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
---|
798 | # Returns: 1 if this was a valid Canon RAW file
|
---|
799 | sub ProcessCRW($$)
|
---|
800 | {
|
---|
801 | my ($et, $dirInfo) = @_;
|
---|
802 | my ($buff, $sig);
|
---|
803 | my $raf = $$dirInfo{RAF};
|
---|
804 | my $buildMakerNotes = $et->Options('MakerNotes');
|
---|
805 |
|
---|
806 | $raf->Read($buff,2) == 2 or return 0;
|
---|
807 | SetByteOrder($buff) or return 0;
|
---|
808 | $raf->Read($buff,4) == 4 or return 0;
|
---|
809 | $raf->Read($sig,8) == 8 or return 0; # get file signature
|
---|
810 | $sig =~ /^HEAP(CCDR|JPGM)/ or return 0; # validate signature
|
---|
811 | my $hlen = Get32u(\$buff, 0);
|
---|
812 |
|
---|
813 | $raf->Seek(0, 2) or return 0; # seek to end of file
|
---|
814 | my $filesize = $raf->Tell() or return 0;
|
---|
815 |
|
---|
816 | # initialize maker note data if building maker notes
|
---|
817 | $buildMakerNotes and InitMakerNotes($et);
|
---|
818 |
|
---|
819 | # set the FileType tag unless already done (eg. APP0 CIFF record in JPEG image)
|
---|
820 | $et->SetFileType();
|
---|
821 |
|
---|
822 | # build directory information for main raw directory
|
---|
823 | my %dirInfo = (
|
---|
824 | DataLen => 0,
|
---|
825 | DirStart => $hlen,
|
---|
826 | DirLen => $filesize - $hlen,
|
---|
827 | Nesting => 0,
|
---|
828 | RAF => $raf,
|
---|
829 | Parent => 'CRW',
|
---|
830 | );
|
---|
831 |
|
---|
832 | # process the raw directory
|
---|
833 | my $rawTagTable = GetTagTable('Image::ExifTool::CanonRaw::Main');
|
---|
834 | my $oldIndent = $$et{INDENT};
|
---|
835 | $$et{INDENT} .= '| ';
|
---|
836 | unless (ProcessCanonRaw($et, \%dirInfo, $rawTagTable)) {
|
---|
837 | $et->Warn('CRW file format error');
|
---|
838 | }
|
---|
839 | $$et{INDENT} = $oldIndent;
|
---|
840 |
|
---|
841 | # finish building maker notes if necessary
|
---|
842 | $buildMakerNotes and SaveMakerNotes($et);
|
---|
843 |
|
---|
844 | # process trailers if they exist in CRW file (not in CIFF information!)
|
---|
845 | if ($$et{FILE_TYPE} eq 'CRW') {
|
---|
846 | my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
|
---|
847 | $et->ProcessTrailers($trailInfo) if $trailInfo;
|
---|
848 | }
|
---|
849 |
|
---|
850 | return 1;
|
---|
851 | }
|
---|
852 |
|
---|
853 | 1; # end
|
---|
854 |
|
---|
855 | __END__
|
---|
856 |
|
---|
857 | =head1 NAME
|
---|
858 |
|
---|
859 | Image::ExifTool::CanonRaw - Read Canon RAW (CRW) meta information
|
---|
860 |
|
---|
861 | =head1 SYNOPSIS
|
---|
862 |
|
---|
863 | This module is loaded automatically by Image::ExifTool when required.
|
---|
864 |
|
---|
865 | =head1 DESCRIPTION
|
---|
866 |
|
---|
867 | This module contains definitions required by Image::ExifTool to interpret
|
---|
868 | meta information from Canon CRW raw files. These files are written directly
|
---|
869 | by some Canon cameras, and contain meta information similar to that found in
|
---|
870 | the EXIF Canon maker notes.
|
---|
871 |
|
---|
872 | =head1 NOTES
|
---|
873 |
|
---|
874 | The CR2 format written by some Canon cameras is very different the CRW
|
---|
875 | format processed by this module. (CR2 is TIFF-based and uses standard EXIF
|
---|
876 | tags.)
|
---|
877 |
|
---|
878 | =head1 AUTHOR
|
---|
879 |
|
---|
880 | Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
|
---|
881 |
|
---|
882 | This library is free software; you can redistribute it and/or modify it
|
---|
883 | under the same terms as Perl itself.
|
---|
884 |
|
---|
885 | =head1 REFERENCES
|
---|
886 |
|
---|
887 | =over 4
|
---|
888 |
|
---|
889 | =item L<http://www.cybercom.net/~dcoffin/dcraw/>
|
---|
890 |
|
---|
891 | =item L<http://www.wonderland.org/crw/>
|
---|
892 |
|
---|
893 | =item L<http://xyrion.org/ciff/>
|
---|
894 |
|
---|
895 | =item L<https://exiftool.org/canon_raw.html>
|
---|
896 |
|
---|
897 | =back
|
---|
898 |
|
---|
899 | =head1 ACKNOWLEDGEMENTS
|
---|
900 |
|
---|
901 | Thanks to Dave Nicholson for decoding a number of new tags.
|
---|
902 |
|
---|
903 | =head1 SEE ALSO
|
---|
904 |
|
---|
905 | L<Image::ExifTool::TagNames/CanonRaw Tags>,
|
---|
906 | L<Image::ExifTool::Canon(3pm)|Image::ExifTool::Canon>,
|
---|
907 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
908 |
|
---|
909 | =cut
|
---|
910 |
|
---|