- Timestamp:
- 2011-06-01T12:33:42+12:00 (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/Image/ExifTool/SigmaRaw.pm
r16842 r24107 4 4 # Description: Read Sigma/Foveon RAW (X3F) meta information 5 5 # 6 # Revisions: 10/16/2005 - P. Harvey Created 6 # Revisions: 2005/10/16 - P. Harvey Created 7 # 2009/11/30 - P. Harvey Support X3F v2.3 written by Sigma DP2 7 8 # 8 9 # References: 1) http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf … … 15 16 use Image::ExifTool qw(:DataAccess :Utils); 16 17 17 $VERSION = '1. 03';18 $VERSION = '1.14'; 18 19 19 20 sub ProcessX3FHeader($$$); … … 21 22 sub ProcessX3FProperties($$$); 22 23 24 # sigma LensType lookup (ref PH) 25 my %sigmaLensTypes = ( 26 # 0 => 'Sigma 50mm F2.8 EX Macro', (0 used for other lenses too) 27 # 8 - 18-125mm LENSARANGE@18mm=22-4 28 16 => 'Sigma 18-50mm F3.5-5.6 DC', 29 129 => 'Sigma 14mm F2.8 EX Aspherical', 30 131 => 'Sigma 17-70mm F2.8-4.5 DC Macro', 31 145 => 'Sigma Lens (145)', 32 145.1 => 'Sigma 15-30mm F3.5-4.5 EX DG Aspherical', 33 145.2 => 'Sigma 18-50mm F2.8 EX DG', #(NC) 34 145.3 => 'Sigma 20-40mm F2.8 EX DG', 35 165 => 'Sigma 70-200mm F2.8 EX', # ...but what specific model?: 36 # 70-200mm F2.8 EX APO - Original version, minimum focus distance 1.8m (1999) 37 # 70-200mm F2.8 EX DG - Adds 'digitally optimized' lens coatings to reduce flare (2005) 38 # 70-200mm F2.8 EX DG Macro (HSM) - Minimum focus distance reduced to 1m (2006) 39 # 70-200mm F2.8 EX DG Macro HSM II - Improved optical performance (2007) 40 169 => 'Sigma 18-50mm F2.8 EX DC', #(NC) 41 '100' => 'Sigma 24-70mm f/2.8 DG Macro', # (SD15) 42 8900 => 'Sigma 70-300mm f/4-5.6 DG OS', # (SD15) 43 ); 44 23 45 # main X3F sections (plus header stuff) 24 46 %Image::ExifTool::SigmaRaw::Main = ( 25 47 PROCESS_PROC => \&ProcessX3FDirectory, 26 NOTES => 'These tags are used in Sigma and Foveon RAW (.X3F) images.', 48 NOTES => q{ 49 These tags are used in Sigma and Foveon RAW (.X3F) images. Metadata is also 50 extracted from the JpgFromRaw image if it exists (all models but the SD9 and 51 SD10). Currently, metadata may only be written to the embedded JpgFromRaw. 52 }, 27 53 Header => { 28 54 SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Header' }, … … 39 65 Binary => 1, 40 66 }, 41 IMA2 => { 42 Name => 'PreviewImage', 43 Binary => 1, 44 }, 67 IMA2 => [ 68 { 69 Name => 'PreviewImage', 70 Condition => 'not $$self{IsJpgFromRaw}', 71 Binary => 1, 72 }, 73 { 74 Name => 'JpgFromRaw', 75 Binary => 1, 76 }, 77 ] 45 78 ); 46 79 … … 53 86 ValueConv => '($val >> 16) . "." . ($val & 0xffff)', 54 87 }, 88 2 => { 89 Name => 'ImageUniqueID', 90 # the serial number (with an extra leading "0") makes up 91 # the first 8 digits of this UID, 92 Format => 'undef[16]', 93 ValueConv => 'unpack("H*", $val)', 94 }, 95 6 => { 96 Name => 'MarkBits', 97 PrintConv => { BITMASK => { } }, 98 }, 55 99 7 => 'ImageWidth', 56 100 8 => 'ImageHeight', … … 60 104 Format => 'string[32]', 61 105 }, 106 18 => { #PH (DP2, FileVersion 2.3) 107 Name => 'SceneCaptureType', 108 Format => 'string[32]', 109 }, 62 110 ); 63 111 … … 67 115 NOTES => 'Extended header data found in version 2.1 and 2.2 files', 68 116 0 => 'Unused', 69 1 => { Name => 'ExposureAdjust',PrintConv => 'sprintf("%. 2f",$val)' },70 2 => { Name => 'Contrast', PrintConv => 'sprintf("%. 2f",$val)' },71 3 => { Name => 'Shadow', PrintConv => 'sprintf("%. 2f",$val)' },72 4 => { Name => 'Highlight', PrintConv => 'sprintf("%. 2f",$val)' },73 5 => { Name => 'Saturation', PrintConv => 'sprintf("%. 2f",$val)' },74 6 => { Name => 'Sharpness', PrintConv => 'sprintf("%. 2f",$val)' },75 7 => { Name => 'RedAdjust', PrintConv => 'sprintf("%. 2f",$val)' },76 8 => { Name => 'GreenAdjust', PrintConv => 'sprintf("%. 2f",$val)' },77 9 => { Name => 'BlueAdjust', PrintConv => 'sprintf("%. 2f",$val)' },78 10 => { Name => 'X3FillLight', PrintConv => 'sprintf("%. 2f",$val)' },117 1 => { Name => 'ExposureAdjust',PrintConv => 'sprintf("%.1f",$val)' }, 118 2 => { Name => 'Contrast', PrintConv => 'sprintf("%.1f",$val)' }, 119 3 => { Name => 'Shadow', PrintConv => 'sprintf("%.1f",$val)' }, 120 4 => { Name => 'Highlight', PrintConv => 'sprintf("%.1f",$val)' }, 121 5 => { Name => 'Saturation', PrintConv => 'sprintf("%.1f",$val)' }, 122 6 => { Name => 'Sharpness', PrintConv => 'sprintf("%.1f",$val)' }, 123 7 => { Name => 'RedAdjust', PrintConv => 'sprintf("%.1f",$val)' }, 124 8 => { Name => 'GreenAdjust', PrintConv => 'sprintf("%.1f",$val)' }, 125 9 => { Name => 'BlueAdjust', PrintConv => 'sprintf("%.1f",$val)' }, 126 10 => { Name => 'X3FillLight', PrintConv => 'sprintf("%.1f",$val)' }, 79 127 ); 80 128 … … 106 154 CAMNAME => 'CameraName', 107 155 CAMSERIAL => 'SerialNumber', 156 CM_DESC => 'SceneCaptureType', #PH (DP2) 108 157 COLORSPACE => 'ColorSpace', # observed: sRGB 109 158 DRIVE => { … … 112 161 SINGLE => 'Single Shot', 113 162 MULTI => 'Multi Shot', 114 '2S' => '2 -secTimer',115 '10S' => '10 -secTimer',163 '2S' => '2 s Timer', 164 '10S' => '10 s Timer', 116 165 UP => 'Mirror Up', 117 166 AB => 'Auto Bracket', … … 123 172 Name => 'ExposureCompensation', 124 173 Groups => { 2 => 'Image' }, 125 PrintConv => 'Image::ExifTool::Exif:: ConvertFraction($val)',174 PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)', 126 175 }, 127 176 EXPNET => { 128 177 Name => 'NetExposureCompensation', 129 178 Groups => { 2 => 'Image' }, 130 PrintConv => 'Image::ExifTool::Exif:: ConvertFraction($val)',179 PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)', 131 180 }, 132 181 EXPTIME => { … … 147 196 FLENGTH => { 148 197 Name => 'FocalLength', 149 PrintConv => 'sprintf("%.1fmm",$val)', 150 }, 151 FLEQ35MM => 'FocalLengthIn35mmFormat', 198 PrintConv => 'sprintf("%.1f mm",$val)', 199 }, 200 FLEQ35MM => { 201 Name => 'FocalLengthIn35mmFormat', 202 PrintConv => 'sprintf("%.1f mm",$val)', 203 }, 152 204 FOCUS => { 153 205 Name => 'Focus', … … 159 211 }, 160 212 IMAGERBOARDID => 'ImagerBoardID', 161 IMAGERTEMP => 'SensorTemperature', 213 IMAGERTEMP => { 214 Name => 'SensorTemperature', 215 PrintConv => '"$val C"', 216 }, 217 IMAGEBOARDID=> 'ImageBoardID', #PH (DP2) 162 218 ISO => 'ISO', 163 219 LENSARANGE => 'LensApertureRange', 164 220 LENSFRANGE => 'LensFocalRange', 165 LENSMODEL => 'LensID', 221 LENSMODEL => { 222 Name => 'LensType', 223 Notes => q{ 224 decimal values differentiate lenses which would otherwise have the same 225 LensType, and are used by the Composite LensID tag when attempting to 226 identify the specific lens model 227 }, 228 PrintConv => \%sigmaLensTypes, 229 }, 166 230 PMODE => { 167 231 Name => 'ExposureProgram', … … 210 274 last unless $$chars[$i]; 211 275 } 212 my $buff = pack('v*',@$chars[$pos..$i-1]); 213 my $val = $exifTool->Unicode2Charset($buff, 'II'); 214 return $val; 276 my $buff = pack('v*', @$chars[$pos..$i-1]); 277 return $exifTool->Decode($buff, 'UCS2', 'II'); 215 278 } 216 279 … … 222 285 { 223 286 my ($exifTool, $dirInfo, $tagTablePtr) = @_; 287 my $dataPt = $$dirInfo{DataPt}; 288 my $hdrLen = $$dirInfo{DirLen}; 224 289 my $verbose = $exifTool->Options('Verbose'); 225 290 … … 228 293 229 294 # process extended data if available 230 if ($$dirInfo{DirLen} >= 232) { 231 $verbose and $exifTool->VerboseDir('X3F HeaderExt', 32); 295 if (length $$dataPt >= 232) { 296 if ($verbose) { 297 $exifTool->VerboseDir('X3F HeaderExt', 32); 298 Image::ExifTool::HexDump($dataPt, undef, 299 MaxLen => $verbose > 3 ? 1024 : 96, 300 Out => $exifTool->Options('TextOut'), 301 Prefix => $$exifTool{INDENT}, 302 Start => $$dirInfo{DirLen}, 303 ) if $verbose > 2; 304 } 232 305 $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::HeaderExt'); 233 my $dataPt = $$dirInfo{DataPt}; 234 my @vals = unpack('x72C32V32', $$dataPt); 306 my @vals = unpack("x${hdrLen}C32V32", $$dataPt); 235 307 my $i; 236 308 my $unused = 0; … … 250 322 } 251 323 $exifTool->HandleTag($tagTablePtr, $vals[$i], $val, 252 Index => $i,324 Index => $i, 253 325 DataPt => $dataPt, 254 Start => 104+ $i * 4,255 Size => 4,326 Start => $hdrLen + 32 + $i * 4, 327 Size => 4, 256 328 ); 257 329 } … … 305 377 Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo); 306 378 } 307 379 308 380 $exifTool->HandleTag($tagTablePtr, $tag, $val, 309 381 Index => $index, … … 314 386 } 315 387 return 1; 388 } 389 390 #------------------------------------------------------------------------------ 391 # Write an X3F file 392 # Inputs: 0) ExifTool object reference, 1) DirInfo reference (DirStart = directory offset) 393 # Returns: error string, undef on success, or -1 on write error 394 # Notes: Writes metadata to embedded JpgFromRaw image 395 sub WriteX3F($$) 396 { 397 my ($exifTool, $dirInfo) = @_; 398 my $raf = $$dirInfo{RAF}; 399 my $outfile = $$dirInfo{OutFile}; 400 my ($outDir, $buff, $ver, $entries, $dir, $outPos, $index, $didContain); 401 402 $raf->Seek($$dirInfo{DirStart}, 0) or return 'Error seeking to directory start'; 403 404 # read the X3F directory header (will be copied directly to output) 405 $raf->Read($outDir, 12) == 12 or return 'Truncated X3F image'; 406 $outDir =~ /^SECd/ or return 'Bad section header'; 407 ($ver, $entries) = unpack('x4V2', $outDir); 408 409 # do sanity check on number of entries in directory 410 return 'Invalid X3F directory count' unless $entries > 2 and $entries < 20; 411 # read the directory entries 412 unless ($raf->Read($dir, $entries * 12) == $entries * 12) { 413 return 'Truncated X3F directory'; 414 } 415 # do a quick scan to determine the offset of the first data subsection 416 for ($index=0; $index<$entries; ++$index) { 417 my $pos = $index * 12; 418 my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir); 419 # remember position of first data subsection 420 $outPos = $offset if not defined $outPos or $outPos > $offset; 421 } 422 # copy the file header up to the start of the first data subsection 423 unless ($raf->Seek(0,0) and $raf->Read($buff, $outPos) == $outPos) { 424 return 'Error reading X3F header'; 425 } 426 Write($outfile, $buff) or return -1; 427 428 # loop through directory, rewriting each section 429 for ($index=0; $index<$entries; ++$index) { 430 431 my $pos = $index * 12; 432 my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir); 433 $raf->Seek($offset, 0) or return 'Bad data offset'; 434 435 if ($tag eq 'IMA2' and $len > 28) { 436 # check subsection header (28 bytes) to see if this is a JPEG preview image 437 $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header'; 438 Write($outfile, $buff) or return -1; 439 $len -= 28; 440 441 # only rewrite full-sized JpgFromRaw (version 2.0, type 2, format 18) 442 if ($buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/ and 443 $$exifTool{ImageWidth} == unpack('x16V', $buff)) 444 { 445 $raf->Read($buff, $len) == $len or return 'Error reading JpgFromRaw'; 446 # use same write directories as JPEG 447 $exifTool->InitWriteDirs('JPEG'); 448 # rewrite the embedded JPEG in memory 449 my $newData; 450 my %jpegInfo = ( 451 Parent => 'X3F', 452 RAF => new File::RandomAccess(\$buff), 453 OutFile => \$newData, 454 ); 455 $$exifTool{FILE_TYPE} = 'JPEG'; 456 my $success = $exifTool->WriteJPEG(\%jpegInfo); 457 $$exifTool{FILE_TYPE} = 'X3F'; 458 SetByteOrder('II'); 459 return 'Error writing X3F JpgFromRaw' unless $success and $newData; 460 return -1 if $success < 0; 461 # write new data if anything changed, otherwise copy old image 462 my $outPt = $$exifTool{CHANGED} ? \$newData : \$buff; 463 Write($outfile, $$outPt) or return -1; 464 # set $len to the total subsection data length 465 $len = length($$outPt) + 28; 466 $didContain = 1; 467 } else { 468 # copy original image data 469 Image::ExifTool::CopyBlock($raf, $outfile, $len) or return 'Corrupted X3F image'; 470 $len += 28; 471 } 472 } else { 473 # copy data for this subsection 474 Image::ExifTool::CopyBlock($raf, $outfile, $len) or return 'Corrupted X3F directory'; 475 } 476 # add directory entry and update output file position 477 $outDir .= pack('V2a4', $outPos, $len, $tag); 478 $outPos += $len; 479 # pad data to an even 4-byte boundary 480 if ($len & 0x03) { 481 my $pad = 4 - ($len & 0x03); 482 Write($outfile, "\0" x $pad) or return -1; 483 $outPos += $pad; 484 } 485 } 486 # warn if we couldn't add metadata to this image (should only be SD9 or SD10) 487 $didContain or $exifTool->Warn("Can't yet write SD9 or SD10 X3F images"); 488 # write out the directory and the directory pointer, and we are done 489 Write($outfile, $outDir, pack('V', $outPos)) or return -1; 490 return undef; 316 491 } 317 492 … … 329 504 330 505 # parse the X3F directory structure 331 my ($buff, $ver, $entries, $index );506 my ($buff, $ver, $entries, $index, $dir); 332 507 $raf->Read($buff, 12) == 12 or return 'Truncated X3F image'; 333 508 $buff =~ /^SECd/ or return 'Bad section header'; 334 509 ($ver, $entries) = unpack('x4V2', $buff); 335 510 $verbose and $exifTool->VerboseDir('X3F Subsection', $entries); 336 my $dir;337 511 $raf->Read($dir, $entries * 12) == $entries * 12 or return 'Truncated X3F directory'; 338 512 for ($index=0; $index<$entries; ++$index) { … … 344 518 if ($verbose > 2) { 345 519 $raf->Seek($offset, 0) or return 'Error seeking'; 346 my $n = $verbose > 3 ? $len : 64; 347 $n = $len if $n > $len; 348 $raf->Read($buff, $n) == $n or return 'Truncated image'; 349 Image::ExifTool::HexDump(\$buff, undef, 350 Prefix => $exifTool->{INDENT}, 351 Out => $exifTool->Options('TextOut'), 352 ); 520 $raf->Read($buff, $len) == $len or return 'Truncated image'; 521 $exifTool->VerboseDump(\$buff); 353 522 } 354 523 } … … 358 527 # check image header to see if this is a JPEG preview image 359 528 $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header'; 360 # igore all image data but JPEG compressed (type 18) 361 next unless $buff =~ /^SECi.{4}\x02\0\0\0\x12/s; 529 # ignore all image data but JPEG compressed (version 2.0, type 2, format 18) 530 next unless $buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/; 531 # check preview image size and extract full-sized preview as JpgFromRaw 532 if ($$exifTool{ImageWidth} == unpack('x16V', $buff)) { 533 $$exifTool{IsJpgFromRaw} = 1; 534 $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag); 535 delete $$exifTool{IsJpgFromRaw}; 536 } 537 $offset += 28; 362 538 $len -= 28; 363 539 } … … 369 545 $exifTool->ProcessDirectory(\%dirInfo, $subTable); 370 546 } else { 547 # extract metadata from JpgFromRaw 548 if ($$tagInfo{Name} eq 'JpgFromRaw') { 549 my %dirInfo = ( 550 Parent => 'X3F', 551 RAF => new File::RandomAccess(\$buff), 552 ); 553 $$exifTool{BASE} += $offset; 554 $exifTool->ProcessJPEG(\%dirInfo); 555 $$exifTool{BASE} -= $offset; 556 SetByteOrder('II'); 557 } 371 558 $exifTool->FoundTag($tagInfo, $buff); 372 559 } … … 376 563 377 564 #------------------------------------------------------------------------------ 378 # Extractinformation from a Sigma raw (X3F) image565 # Read/write information from a Sigma raw (X3F) image 379 566 # Inputs: 0) ExifTool object reference, 1) DirInfo reference 380 # Returns: 1 on success, 0 if this wasn't a valid X3F image 567 # Returns: 1 on success, 0 if this wasn't a valid X3F image, or -1 on write error 381 568 sub ProcessX3F($$) 382 569 { 383 570 my ($exifTool, $dirInfo) = @_; 571 my $outfile = $$dirInfo{OutFile}; 384 572 my $raf = $$dirInfo{RAF}; 385 my $buff; 573 my $warn = $outfile ? \&Image::ExifTool::Error : \&Image::ExifTool::Warn; 574 my ($buff, $err); 386 575 387 576 return 0 unless $raf->Read($buff, 40) == 40; … … 395 584 $ver = ($ver >> 16) . '.' . ($ver & 0xffff); 396 585 if ($ver >= 3) { 397 $exifTool->Warn("Can't read version $ver X3F image");586 &$warn($exifTool, "Can't read version $ver X3F image"); 398 587 return 1; 399 } 400 # read version 2.1/2.2 extended header 588 } elsif ($ver > 2.3) { 589 &$warn($exifTool, 'Untested X3F version. Please submit sample for testing', 1); 590 } 591 my $hdrLen = length $buff; 592 # read version 2.1/2.2/2.3 extended header 401 593 if ($ver > 2) { 594 $hdrLen += $ver > 2.2 ? 64 : 32; # SceneCaptureType string added in 2.3 595 my $more = $hdrLen - length($buff) + 160; # (extended header is 160 bytes) 402 596 my $buf2; 403 unless ($raf->Read($buf2, 192) == 192) {404 $exifTool->Warn('Error reading extended header');597 unless ($raf->Read($buf2, $more) == $more) { 598 &$warn($exifTool, 'Error reading extended header'); 405 599 return 1; 406 600 } 407 601 $buff .= $buf2; 408 602 } 603 # extract ImageWidth for later 604 $$exifTool{ImageWidth} = Get32u(\$buff, 28); 409 605 # process header information 410 606 my $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::Main'); 411 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 'Header'); 412 my $subdir = GetTagTable('Image::ExifTool::SigmaRaw::Header'); 607 unless ($outfile) { 608 $exifTool->HandleTag($tagTablePtr, 'Header', $buff, 609 DataPt => \$buff, 610 Size => $hdrLen, 611 ); 612 } 613 # read the directory pointer 614 $raf->Seek(-4, 2) or &$warn($exifTool, 'Seek error'), return 1; 615 unless ($raf->Read($buff, 4) == 4) { 616 &$warn($exifTool, 'Error reading X3F dir pointer'); 617 return 1; 618 } 619 my $offset = unpack('V', $buff); 413 620 my %dirInfo = ( 414 DataPt => \$buff,415 DirStart => 0,416 DirLen => length($buff),417 );418 $exifTool->ProcessDirectory(\%dirInfo, $subdir);419 # read the directory pointer420 $raf->Seek(-4, 2);421 unless ($raf->Read($buff, 4) == 4) {422 $exifTool->Warn('Error reading X3F dir pointer');423 return 1;424 }425 my $offset = unpack('V', $buff);426 %dirInfo = (427 621 RAF => $raf, 428 622 DirStart => $offset, 429 623 ); 430 # process the X3F subsections 431 my $err = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr); 432 $err and $exifTool->Warn($err); 624 if ($outfile) { 625 $dirInfo{OutFile} = $outfile; 626 $err = WriteX3F($exifTool, \%dirInfo); 627 return -1 if $err and $err eq '-1'; 628 } else { 629 # process the X3F subsections 630 $err = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr); 631 } 632 $err and &$warn($exifTool, $err); 433 633 return 1; 434 634 } … … 453 653 =head1 AUTHOR 454 654 455 Copyright 2003-20 07, Phil Harvey (phil at owl.phy.queensu.ca)655 Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca) 456 656 457 657 This library is free software; you can redistribute it and/or modify it
Note:
See TracChangeset
for help on using the changeset viewer.