1#------------------------------------------------------------------------------ 2# File: PanasonicRaw.pm 3# 4# Description: Read/write Panasonic/Leica RAW/RW2/RWL meta information 5# 6# Revisions: 2009/03/24 - P. Harvey Created 7# 2009/05/12 - PH Added RWL file type (same format as RW2) 8# 9# References: 1) https://exiftool.org/forum/index.php/topic,1542.0.html 10# 2) http://www.cybercom.net/~dcoffin/dcraw/ 11# 3) http://syscall.eu/#pana 12# 4) Klaus Homeister private communication 13# IB) Iliah Borg private communication (LibRaw) 14# JD) Jens Duttke private communication (TZ3,FZ30,FZ50) 15#------------------------------------------------------------------------------ 16 17package Image::ExifTool::PanasonicRaw; 18 19use strict; 20use vars qw($VERSION); 21use Image::ExifTool qw(:DataAccess :Utils); 22use Image::ExifTool::Exif; 23 24$VERSION = '1.25'; 25 26sub ProcessJpgFromRaw($$$); 27sub WriteJpgFromRaw($$$); 28sub WriteDistortionInfo($$$); 29sub ProcessDistortionInfo($$$); 30 31my %jpgFromRawMap = ( 32 IFD1 => 'IFD0', 33 EXIF => 'IFD0', # to write EXIF as a block 34 ExifIFD => 'IFD0', 35 GPS => 'IFD0', 36 SubIFD => 'IFD0', 37 GlobParamIFD => 'IFD0', 38 PrintIM => 'IFD0', 39 InteropIFD => 'ExifIFD', 40 MakerNotes => 'ExifIFD', 41 IFD0 => 'APP1', 42 MakerNotes => 'ExifIFD', 43 Comment => 'COM', 44); 45 46my %wbTypeInfo = ( 47 PrintConv => \%Image::ExifTool::Exif::lightSource, 48 SeparateTable => 'EXIF LightSource', 49); 50 51my %panasonicWhiteBalance = ( #forum9396 52 0 => 'Auto', 53 1 => 'Daylight', 54 2 => 'Cloudy', 55 3 => 'Tungsten', 56 4 => 'n/a', 57 5 => 'Flash', 58 6 => 'n/a', 59 7 => 'n/a', 60 8 => 'Custom#1', 61 9 => 'Custom#2', 62 10 => 'Custom#3', 63 11 => 'Custom#4', 64 12 => 'Shade', 65 13 => 'Kelvin', 66 16 => 'AWBc', # GH5 and G9 (Makernotes WB==19) 67); 68 69# Tags found in Panasonic RAW/RW2/RWL images (ref PH) 70%Image::ExifTool::PanasonicRaw::Main = ( 71 GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'}, 72 WRITE_PROC => \&Image::ExifTool::Exif::WriteExif, 73 CHECK_PROC => \&Image::ExifTool::Exif::CheckExif, 74 WRITE_GROUP => 'IFD0', # default write group 75 NOTES => 'These tags are found in IFD0 of Panasonic/Leica RAW, RW2 and RWL images.', 76 0x01 => { 77 Name => 'PanasonicRawVersion', 78 Writable => 'undef', 79 }, 80 0x02 => 'SensorWidth', #1/PH 81 0x03 => 'SensorHeight', #1/PH 82 0x04 => 'SensorTopBorder', #JD 83 0x05 => 'SensorLeftBorder', #JD 84 0x06 => 'SensorBottomBorder', #PH 85 0x07 => 'SensorRightBorder', #PH 86 # observed values for unknown tags - PH 87 # 0x08: 1 88 # 0x09: 1,3,4 89 # 0x0a: 12 90 # (IB gave 0x08-0x0a as BlackLevel tags, but Klaus' decoding makes more sense) 91 0x08 => { Name => 'SamplesPerPixel', Writable => 'int16u', Protected => 1 }, #4 92 0x09 => { #4 93 Name => 'CFAPattern', 94 Writable => 'int16u', 95 Protected => 1, 96 PrintConv => { 97 0 => 'n/a', 98 1 => '[Red,Green][Green,Blue]', # (CM-1, FZ70) 99 2 => '[Green,Red][Blue,Green]', # (LX-7) 100 3 => '[Green,Blue][Red,Green]', # (ZS100, FZ2500, FZ1000, ...) 101 4 => '[Blue,Green][Green,Red]', # (LC-100, G-7, V-LUX1, ...) 102 }, 103 }, 104 0x0a => { Name => 'BitsPerSample', Writable => 'int16u', Protected => 1 }, #4 105 0x0b => { #4 106 Name => 'Compression', 107 Writable => 'int16u', 108 Protected => 1, 109 PrintConv => { 110 34316 => 'Panasonic RAW 1', # (most models - RAW/RW2/RWL) 111 34826 => 'Panasonic RAW 2', # (DIGILUX 2 - RAW) 112 34828 => 'Panasonic RAW 3', # (D-LUX2,D-LUX3,FZ30,LX1 - RAW) 113 34830 => 'Panasonic RAW 4', #IB (Leica DIGILUX 3, Panasonic DMC-L1) 114 }, 115 }, 116 # 0x0c: 2 (only Leica Digilux 2) 117 # 0x0d: 0,1 118 # 0x0e,0x0f,0x10: 4095 119 0x0e => { Name => 'LinearityLimitRed', Writable => 'int16u' }, #IB 120 0x0f => { Name => 'LinearityLimitGreen', Writable => 'int16u' }, #IB 121 0x10 => { Name => 'LinearityLimitBlue', Writable => 'int16u' }, #IB 122 0x11 => { #JD 123 Name => 'RedBalance', 124 Writable => 'int16u', 125 ValueConv => '$val / 256', 126 ValueConvInv => 'int($val * 256 + 0.5)', 127 Notes => 'found in Digilux 2 RAW images', 128 }, 129 0x12 => { #JD 130 Name => 'BlueBalance', 131 Writable => 'int16u', 132 ValueConv => '$val / 256', 133 ValueConvInv => 'int($val * 256 + 0.5)', 134 }, 135 0x13 => { #IB 136 Name => 'WBInfo', 137 SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::WBInfo' }, 138 }, 139 0x17 => { #1 140 Name => 'ISO', 141 Writable => 'int16u', 142 }, 143 # 0x18,0x19,0x1a: 0 144 0x18 => { #IB 145 Name => 'HighISOMultiplierRed', 146 Writable => 'int16u', 147 ValueConv => '$val / 256', 148 ValueConvInv => 'int($val * 256 + 0.5)', 149 }, 150 0x19 => { #IB 151 Name => 'HighISOMultiplierGreen', 152 Writable => 'int16u', 153 ValueConv => '$val / 256', 154 ValueConvInv => 'int($val * 256 + 0.5)', 155 }, 156 0x1a => { #IB 157 Name => 'HighISOMultiplierBlue', 158 Writable => 'int16u', 159 ValueConv => '$val / 256', 160 ValueConvInv => 'int($val * 256 + 0.5)', 161 }, 162 # 0x1b: [binary data] (something to do with the camera ISO cababilities: int16u count N, 163 # followed by table of N entries: int16u ISO, int16u[3] RGB gains - ref IB) 164 0x1c => { Name => 'BlackLevelRed', Writable => 'int16u' }, #IB 165 0x1d => { Name => 'BlackLevelGreen', Writable => 'int16u' }, #IB 166 0x1e => { Name => 'BlackLevelBlue', Writable => 'int16u' }, #IB 167 0x24 => { #2 168 Name => 'WBRedLevel', 169 Writable => 'int16u', 170 }, 171 0x25 => { #2 172 Name => 'WBGreenLevel', 173 Writable => 'int16u', 174 }, 175 0x26 => { #2 176 Name => 'WBBlueLevel', 177 Writable => 'int16u', 178 }, 179 0x27 => { #IB 180 Name => 'WBInfo2', 181 SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::WBInfo2' }, 182 }, 183 # 0x27,0x29,0x2a,0x2b,0x2c: [binary data] 184 0x2d => { #IB 185 Name => 'RawFormat', 186 Writable => 'int16u', 187 Protected => 1, 188 # 2 - RAW DMC-FZ8/FZ18 189 # 3 - RAW DMC-L10 190 # 4 - RW2 for most other models, including G9 in "pixel shift off" mode and YUNEEC CGO4 191 # (must add 15 to black levels for RawFormat == 4) 192 # 5 - RW2 DC-GH5s; G9 in "pixel shift on" mode 193 # 6 - RW2 DC-S1, DC-S1r in "pixel shift off" mode 194 # 7 - RW2 DC-S1r (and probably DC-S1, have no raw samples) in "pixel shift on" mode 195 # not used - DMC-LX1/FZ30/FZ50/L1/LX1/LX2 196 # (modes 5 and 7 are lossless) 197 }, 198 0x2e => { #JD 199 Name => 'JpgFromRaw', # (writable directory!) 200 Groups => { 2 => 'Preview' }, 201 Writable => 'undef', 202 # protect this tag because it contains all the metadata 203 Flags => [ 'Binary', 'Protected', 'NestedHtmlDump', 'BlockExtract' ], 204 Notes => 'processed as an embedded document because it contains full EXIF', 205 WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)', 206 DataTag => 'JpgFromRaw', 207 RawConv => '$self->ValidateImage(\$val,$tag)', 208 SubDirectory => { 209 # extract information from embedded image since it is metadata-rich, 210 # unless HtmlDump option set (note that the offsets will be relative, 211 # not absolute like they should be in verbose mode) 212 TagTable => 'Image::ExifTool::JPEG::Main', 213 WriteProc => \&WriteJpgFromRaw, 214 ProcessProc => \&ProcessJpgFromRaw, 215 }, 216 }, 217 0x2f => { Name => 'CropTop', Writable => 'int16u' }, 218 0x30 => { Name => 'CropLeft', Writable => 'int16u' }, 219 0x31 => { Name => 'CropBottom', Writable => 'int16u' }, 220 0x32 => { Name => 'CropRight', Writable => 'int16u' }, 221 0x10f => { 222 Name => 'Make', 223 Groups => { 2 => 'Camera' }, 224 Writable => 'string', 225 DataMember => 'Make', 226 # save this value as an ExifTool member variable 227 RawConv => '$self->{Make} = $val', 228 }, 229 0x110 => { 230 Name => 'Model', 231 Description => 'Camera Model Name', 232 Groups => { 2 => 'Camera' }, 233 Writable => 'string', 234 DataMember => 'Model', 235 # save this value as an ExifTool member variable 236 RawConv => '$self->{Model} = $val', 237 }, 238 0x111 => { 239 Name => 'StripOffsets', 240 # (this value is 0xffffffff for some models, and RawDataOffset must be used) 241 Flags => [ 'IsOffset', 'PanasonicHack' ], 242 OffsetPair => 0x117, # point to associated byte counts 243 ValueConv => 'length($val) > 32 ? \$val : $val', 244 }, 245 0x112 => { 246 Name => 'Orientation', 247 Writable => 'int16u', 248 PrintConv => \%Image::ExifTool::Exif::orientation, 249 Priority => 0, # so IFD1 doesn't take precedence 250 }, 251 0x116 => { 252 Name => 'RowsPerStrip', 253 Priority => 0, 254 }, 255 0x117 => { 256 Name => 'StripByteCounts', 257 # (note that this value may represent something like uncompressed byte count 258 # for RAW/RW2/RWL images from some models, and is zero for some other models) 259 OffsetPair => 0x111, # point to associated offset 260 ValueConv => 'length($val) > 32 ? \$val : $val', 261 }, 262 0x118 => { 263 Name => 'RawDataOffset', #PH (RW2/RWL) 264 IsOffset => '$$et{TIFF_TYPE} =~ /^(RW2|RWL)$/', # (invalid in DNG-converted files) 265 PanasonicHack => 1, 266 OffsetPair => 0x117, # (use StripByteCounts as the offset pair) 267 NotRealPair => 1, # (to avoid Validate warning) 268 }, 269 0x119 => { 270 Name => 'DistortionInfo', 271 SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::DistortionInfo' }, 272 }, 273 # 0x11b - chromatic aberration correction (ref 3) (also see forum9366) 274 0x11c => { #forum9373 275 Name => 'Gamma', 276 Writable => 'int16u', 277 # unfortunately it seems that the scaling factor varies with model... 278 ValueConv => '$val / ($val >= 1024 ? 1024 : ($val >= 256 ? 256 : 100))', 279 ValueConvInv => 'int($val * 256 + 0.5)', 280 }, 281 0x120 => { 282 Name => 'CameraIFD', 283 SubDirectory => { 284 TagTable => 'Image::ExifTool::PanasonicRaw::CameraIFD', 285 Base => '$start', 286 ProcessProc => \&Image::ExifTool::ProcessTIFF, 287 }, 288 }, 289 0x121 => { #forum9295 290 Name => 'Multishot', 291 Writable => 'int32u', 292 PrintConv => { 293 0 => 'Off', 294 65536 => 'Pixel Shift', 295 }, 296 }, 297 # 0x122 - int32u: RAWDataOffset for the GH5s/GX9, or pointer to end of raw data for G9 (forum9295) 298 0x2bc => { # PH Extension!! 299 Name => 'ApplicationNotes', # (writable directory!) 300 Writable => 'int8u', 301 Format => 'undef', 302 Flags => [ 'Binary', 'Protected' ], 303 SubDirectory => { 304 DirName => 'XMP', 305 TagTable => 'Image::ExifTool::XMP::Main', 306 }, 307 }, 308 0x001b => { #forum9250 309 Name => 'NoiseReductionParams', 310 Writable => 'undef', 311 Format => 'int16u', 312 Count => -1, 313 Flags => 'Protected', 314 Notes => q{ 315 the camera's default noise reduction setup. The first number is the number 316 of entries, then for each entry there are 4 numbers: an ISO speed, and 317 noise-reduction strengths the R, G and B channels 318 }, 319 }, 320 0x83bb => { # PH Extension!! 321 Name => 'IPTC-NAA', # (writable directory!) 322 Format => 'undef', # convert binary values as undef 323 Writable => 'int32u', # but write int32u format code in IFD 324 WriteGroup => 'IFD0', 325 Flags => [ 'Binary', 'Protected' ], 326 SubDirectory => { 327 DirName => 'IPTC', 328 TagTable => 'Image::ExifTool::IPTC::Main', 329 }, 330 }, 331 0x8769 => { 332 Name => 'ExifOffset', 333 Groups => { 1 => 'ExifIFD' }, 334 Flags => 'SubIFD', 335 SubDirectory => { 336 TagTable => 'Image::ExifTool::Exif::Main', 337 DirName => 'ExifIFD', 338 Start => '$val', 339 }, 340 }, 341 0x8825 => { 342 Name => 'GPSInfo', 343 Groups => { 1 => 'GPS' }, 344 Flags => 'SubIFD', 345 SubDirectory => { 346 DirName => 'GPS', 347 TagTable => 'Image::ExifTool::GPS::Main', 348 Start => '$val', 349 }, 350 }, 351 # 0xffff => 'DCSHueShiftValues', #exifprobe (NC) 352); 353 354# white balance information (ref IB) 355# (PanasonicRawVersion<200: Digilux 2) 356%Image::ExifTool::PanasonicRaw::WBInfo = ( 357 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, 358 WRITE_PROC => \&Image::ExifTool::WriteBinaryData, 359 CHECK_PROC => \&Image::ExifTool::CheckBinaryData, 360 WRITABLE => 1, 361 FORMAT => 'int16u', 362 FIRST_ENTRY => 0, 363 0 => 'NumWBEntries', 364 1 => { Name => 'WBType1', %wbTypeInfo }, 365 2 => { Name => 'WB_RBLevels1', Format => 'int16u[2]' }, 366 4 => { Name => 'WBType2', %wbTypeInfo }, 367 5 => { Name => 'WB_RBLevels2', Format => 'int16u[2]' }, 368 7 => { Name => 'WBType3', %wbTypeInfo }, 369 8 => { Name => 'WB_RBLevels3', Format => 'int16u[2]' }, 370 10 => { Name => 'WBType4', %wbTypeInfo }, 371 11 => { Name => 'WB_RBLevels4', Format => 'int16u[2]' }, 372 13 => { Name => 'WBType5', %wbTypeInfo }, 373 14 => { Name => 'WB_RBLevels5', Format => 'int16u[2]' }, 374 16 => { Name => 'WBType6', %wbTypeInfo }, 375 17 => { Name => 'WB_RBLevels6', Format => 'int16u[2]' }, 376 19 => { Name => 'WBType7', %wbTypeInfo }, 377 20 => { Name => 'WB_RBLevels7', Format => 'int16u[2]' }, 378); 379 380# white balance information (ref IB) 381# (PanasonicRawVersion>=200: D-Lux2, D-Lux3, DMC-FZ18/FZ30/LX1/L10) 382%Image::ExifTool::PanasonicRaw::WBInfo2 = ( 383 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, 384 WRITE_PROC => \&Image::ExifTool::WriteBinaryData, 385 CHECK_PROC => \&Image::ExifTool::CheckBinaryData, 386 WRITABLE => 1, 387 FORMAT => 'int16u', 388 FIRST_ENTRY => 0, 389 0 => 'NumWBEntries', 390 1 => { Name => 'WBType1', %wbTypeInfo }, 391 2 => { Name => 'WB_RGBLevels1', Format => 'int16u[3]' }, 392 5 => { Name => 'WBType2', %wbTypeInfo }, 393 6 => { Name => 'WB_RGBLevels2', Format => 'int16u[3]' }, 394 9 => { Name => 'WBType3', %wbTypeInfo }, 395 10 => { Name => 'WB_RGBLevels3', Format => 'int16u[3]' }, 396 13 => { Name => 'WBType4', %wbTypeInfo }, 397 14 => { Name => 'WB_RGBLevels4', Format => 'int16u[3]' }, 398 17 => { Name => 'WBType5', %wbTypeInfo }, 399 18 => { Name => 'WB_RGBLevels5', Format => 'int16u[3]' }, 400 21 => { Name => 'WBType6', %wbTypeInfo }, 401 22 => { Name => 'WB_RGBLevels6', Format => 'int16u[3]' }, 402 25 => { Name => 'WBType7', %wbTypeInfo }, 403 26 => { Name => 'WB_RGBLevels7', Format => 'int16u[3]' }, 404); 405 406# lens distortion information (ref 3) 407# (distortion correction equation: Ru = scale*(Rd + a*Rd^3 + b*Rd^5 + c*Rd^7), ref 3) 408%Image::ExifTool::PanasonicRaw::DistortionInfo = ( 409 PROCESS_PROC => \&ProcessDistortionInfo, 410 WRITE_PROC => \&WriteDistortionInfo, 411 CHECK_PROC => \&Image::ExifTool::CheckBinaryData, 412 # (don't make this family 0 MakerNotes because we don't want it to be a deletable group) 413 GROUPS => { 0 => 'PanasonicRaw', 1 => 'PanasonicRaw', 2 => 'Image'}, 414 WRITABLE => 1, 415 FORMAT => 'int16s', 416 FIRST_ENTRY => 0, 417 NOTES => 'Lens distortion correction information.', 418 # 0,1 - checksums 419 2 => { 420 Name => 'DistortionParam02', 421 ValueConv => '$val / 32768', 422 ValueConvInv => '$val * 32768', 423 }, 424 # 3 - usually 0, but seen 0x026b when value 5 is non-zero 425 4 => { 426 Name => 'DistortionParam04', 427 ValueConv => '$val / 32768', 428 ValueConvInv => '$val * 32768', 429 }, 430 5 => { 431 Name => 'DistortionScale', 432 ValueConv => '1 / (1 + $val/32768)', 433 ValueConvInv => '(1/$val - 1) * 32768', 434 }, 435 # 6 - seen 0x0000-0x027f 436 7.1 => { 437 Name => 'DistortionCorrection', 438 Mask => 0x0f, 439 # (have seen the upper 4 bits set for GF5 and GX1, giving a value of -4095 - PH) 440 PrintConv => { 0 => 'Off', 1 => 'On' }, 441 }, 442 8 => { 443 Name => 'DistortionParam08', 444 ValueConv => '$val / 32768', 445 ValueConvInv => '$val * 32768', 446 }, 447 9 => { 448 Name => 'DistortionParam09', 449 ValueConv => '$val / 32768', 450 ValueConvInv => '$val * 32768', 451 }, 452 # 10 - seen 0xfc,0x0101,0x01f4,0x021d,0x0256 453 11 => { 454 Name => 'DistortionParam11', 455 ValueConv => '$val / 32768', 456 ValueConvInv => '$val * 32768', 457 }, 458 12 => { 459 Name => 'DistortionN', 460 Unknown => 1, 461 }, 462 # 13 - seen 0x0000,0x01f9-0x02b2 463 # 14,15 - checksums 464); 465 466# Panasonic RW2 camera IFD written by GH5 (ref PH) 467# (doesn't seem to be valid for the GF7 or GM5 -- encrypted?) 468%Image::ExifTool::PanasonicRaw::CameraIFD = ( 469 GROUPS => { 0 => 'PanasonicRaw', 1 => 'CameraIFD', 2 => 'Camera'}, 470 # (don't know what format codes 0x101 and 0x102 are for, so just 471 # map them into 4 = int32u for now) 472 VARS => { MAP_FORMAT => { 0x101 => 4, 0x102 => 4 } }, 473 0x1001 => { #forum9388 474 Name => 'MultishotOn', 475 Writable => 'int32u', 476 PrintConv => { 0 => 'No', 1 => 'Yes' }, 477 }, 478 0x1100 => { #forum9274 479 Name => 'FocusStepNear', 480 Writable => 'int16s', 481 }, 482 0x1101 => { #forum9274 (was forum8484) 483 Name => 'FocusStepCount', 484 Writable => 'int16s', 485 }, 486 0x1102 => { #forum9417 487 Name => 'FlashFired', 488 Writable => 'int32u', 489 PrintConv => { 0 => 'No', 1 => 'Yes' }, 490 }, 491 # 0x1104 - set when camera shoots on lowest possible Extended-ISO (forum9290) 492 0x1105 => { #forum9392 493 Name => 'ZoomPosition', 494 Notes => 'in the range 0-255 for most cameras', 495 Writable => 'int32u', 496 }, 497 0x1200 => { #forum9278 498 Name => 'LensAttached', 499 Notes => 'many CameraIFD tags are invalid if there is no lens attached', 500 Writable => 'int32u', 501 PrintConv => { 0 => 'No', 1 => 'Yes' }, 502 }, 503 # Note: LensTypeMake and LensTypeModel are combined into a Composite LensType tag 504 # defined in Olympus.pm which has the same values as Olympus:LensType 505 0x1201 => { #IB 506 Name => 'LensTypeMake', 507 Condition => '$format eq "int16u"', 508 Writable => 'int16u', 509 # when format is int16u, these values have been observed: 510 # 0 - Olympus or unknown lens 511 # 2 - Leica or Lumix lens 512 # when format is int32u (S models), these values have been observed (ref IB): 513 # 256 - Leica lens 514 # 257 - Lumix lens 515 # 258 - ? (seen once) 516 }, 517 0x1202 => { #IB 518 Name => 'LensTypeModel', 519 Condition => '$format eq "int16u"', 520 Writable => 'int16u', 521 RawConv => q{ 522 return undef unless $val; 523 require Image::ExifTool::Olympus; # (to load Composite LensID) 524 return $val; 525 }, 526 ValueConv => '$_=sprintf("%.4x",$val); s/(..)(..)/$2 $1/; $_', 527 ValueConvInv => '$val =~ s/(..) (..)/$2$1/; hex($val)', 528 }, 529 0x1203 => { #4 530 Name => 'FocalLengthIn35mmFormat', 531 Writable => 'int16u', 532 PrintConv => '"$val mm"', 533 PrintConvInv => '$val=~s/\s*mm$//;$val', 534 }, 535 # 0x1300 - incident light value? (ref forum11395) 536 0x1301 => { #forum11395 537 Name => 'ApertureValue', 538 Writable => 'int16s', 539 Priority => 0, 540 ValueConv => '2 ** ($val / 512)', 541 ValueConvInv => '$val>0 ? 512*log($val)/log(2) : 0', 542 PrintConv => 'sprintf("%.1f",$val)', 543 PrintConvInv => '$val', 544 }, 545 0x1302 => { #forum11395 546 Name => 'ShutterSpeedValue', 547 Writable => 'int16s', 548 Priority => 0, 549 ValueConv => 'abs($val/256)<100 ? 2**(-$val/256) : 0', 550 ValueConvInv => '$val>0 ? -256*log($val)/log(2) : -25600', 551 PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)', 552 PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)', 553 }, 554 0x1303 => { #forum11395 555 Name => 'SensitivityValue', 556 Writable => 'int16s', 557 ValueConv => '$val / 256', 558 ValueConvInv => 'int($val * 256)', 559 }, 560 0x1305 => { #forum9384 561 Name => 'HighISOMode', 562 Writable => 'int16u', 563 RawConv => '$val || undef', 564 PrintConv => { 1 => 'On', 2 => 'Off' }, 565 }, 566 # 0x1306 EV for some models like the GX8 (forum11395) 567 # 0x140b - scaled overall black level? (ref forum9281) 568 # 0x1411 - scaled black level per channel difference (ref forum9281) 569 0x1412 => { #forum11397 570 Name => 'FacesDetected', 571 Writable => 'int8u', 572 PrintConv => { 0 => 'No', 1 => 'Yes' }, 573 }, 574 # 0x2000 - WB tungsten=3, daylight=4 (ref forum9467) 575 # 0x2009 - scaled black level per channel (ref forum9281) 576 # 0x3000-0x310b - red/blue balances * 1024 (ref forum9467) 577 # 0x3000 modifiedTungsten-Red (-2?) 578 # 0x3001 modifiedTungsten-Blue (-2?) 579 # 0x3002 modifiedDaylight-Red (-2?) 580 # 0x3003 modifiedDaylight-Blue (-2?) 581 # 0x3004 modifiedTungsten-Red (-1?) 582 # 0x3005 modifiedTungsten-Blue (-1?) 583 # 0x3006 modifiedDaylight-Red (-1?) 584 # 0x3007 modifiedDaylight-Blue (-1?) 585 # 0x3100 DefaultTungsten-Red 586 # 0x3101 DefaultTungsten-Blue 587 # 0x3102 DefaultDaylight-Red 588 # 0x3103 DefaultDaylight-Blue 589 # 0x3104 modifiedTungsten-Red (+1?) 590 # 0x3105 modifiedTungsten-Blue (+1?) 591 # 0x3106 modifiedDaylight-Red (+1?) 592 # 0x3107 modifiedDaylight-Blue (+1?) 593 # 0x3108 modifiedTungsten-Red (+2?) 594 # 0x3109 modifiedTungsten-Blue (+2?) 595 # 0x310a modifiedDaylight-Red (+2?) 596 # 0x310b modifiedDaylight-Blue (+2?) 597 0x3200 => { #forum9275 598 Name => 'WB_CFA0_LevelDaylight', 599 Writable => 'int16u', 600 }, 601 0x3201 => { #forum9275 602 Name => 'WB_CFA1_LevelDaylight', 603 Writable => 'int16u', 604 }, 605 0x3202 => { #forum9275 606 Name => 'WB_CFA2_LevelDaylight', 607 Writable => 'int16u', 608 }, 609 0x3203 => { #forum9275 610 Name => 'WB_CFA3_LevelDaylight', 611 Writable => 'int16u', 612 }, 613 # 0x3204-0x3207 - user multipliers * 1024 ? (ref forum9275) 614 # 0x320a - scaled maximum value of raw data (scaling = 4x) (ref forum9281) 615 # 0x3209 - gamma (x256) (ref forum9281) 616 0x3300 => { #forum9296/9396 617 Name => 'WhiteBalanceSet', 618 Writable => 'int8u', 619 PrintConv => \%panasonicWhiteBalance, 620 SeparateTable => 'WhiteBalance', 621 }, 622 0x3420 => { #forum9276 623 Name => 'WB_RedLevelAuto', 624 Writable => 'int16u', 625 }, 626 0x3421 => { #forum9276 627 Name => 'WB_BlueLevelAuto', 628 Writable => 'int16u', 629 }, 630 0x3501 => { #4 631 Name => 'Orientation', 632 Writable => 'int8u', 633 PrintConv => \%Image::ExifTool::Exif::orientation, 634 }, 635 # 0x3504 = Tag 0x1301+0x1302-0x1303 (Bv = Av+Tv-Sv) (forum11395) 636 # 0x3505 - same as 0x1300 (forum11395) 637 0x3600 => { #forum9396 638 Name => 'WhiteBalanceDetected', 639 Writable => 'int8u', 640 PrintConv => \%panasonicWhiteBalance, 641 SeparateTable => 'WhiteBalance', 642 }, 643); 644 645# PanasonicRaw composite tags 646%Image::ExifTool::PanasonicRaw::Composite = ( 647 ImageWidth => { 648 Require => { 649 0 => 'IFD0:SensorLeftBorder', 650 1 => 'IFD0:SensorRightBorder', 651 }, 652 ValueConv => '$val[1] - $val[0]', 653 }, 654 ImageHeight => { 655 Require => { 656 0 => 'IFD0:SensorTopBorder', 657 1 => 'IFD0:SensorBottomBorder', 658 }, 659 ValueConv => '$val[1] - $val[0]', 660 }, 661); 662 663# add our composite tags 664Image::ExifTool::AddCompositeTags('Image::ExifTool::PanasonicRaw'); 665 666 667#------------------------------------------------------------------------------ 668# checksum algorithm for lens distortion correction information (ref 3) 669# Inputs: 0) data ref, 1) start position, 2) number of bytes, 3) incement 670# Returns: checksum value 671sub Checksum($$$$) 672{ 673 my ($dataPt, $start, $num, $inc) = @_; 674 my $csum = 0; 675 my $i; 676 for ($i=0; $i<$num; ++$i) { 677 $csum = (73 * $csum + Get8u($dataPt, $start + $i * $inc)) % 0xffef; 678 } 679 return $csum; 680} 681 682#------------------------------------------------------------------------------ 683# Read lens distortion information 684# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref 685# Returns: 1 on success 686sub ProcessDistortionInfo($$$) 687{ 688 my ($et, $dirInfo, $tagTablePtr) = @_; 689 my $dataPt = $$dirInfo{DataPt}; 690 my $start = $$dirInfo{DirStart} || 0; 691 my $size = $$dirInfo{DirLen} || (length($$dataPt) - $start); 692 if ($size == 32) { 693 # verify the checksums (ref 3) 694 my $csum1 = Checksum($dataPt, $start + 4, 12, 1); 695 my $csum2 = Checksum($dataPt, $start + 16, 12, 1); 696 my $csum3 = Checksum($dataPt, $start + 2, 14, 2); 697 my $csum4 = Checksum($dataPt, $start + 3, 14, 2); 698 my $res = $csum1 ^ Get16u($dataPt, $start + 2) ^ 699 $csum2 ^ Get16u($dataPt, $start + 28) ^ 700 $csum3 ^ Get16u($dataPt, $start + 0) ^ 701 $csum4 ^ Get16u($dataPt, $start + 30); 702 $et->Warn('Invalid DistortionInfo checksum',1) if $res; 703 } else { 704 $et->Warn('Invalid DistortionInfo',1); 705 } 706 return $et->ProcessBinaryData($dirInfo, $tagTablePtr); 707} 708 709#------------------------------------------------------------------------------ 710# Write lens distortion information 711# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref 712# Returns: updated distortion information or undef on error 713sub WriteDistortionInfo($$$) 714{ 715 my ($et, $dirInfo, $tagTablePtr) = @_; 716 $et or return 1; # (allow dummy access) 717 my $dat = $et->WriteBinaryData($dirInfo, $tagTablePtr); 718 if (defined $dat and length($dat) == 32) { 719 # fix checksums (ref 3) 720 Set16u(Checksum(\$dat, 4, 12, 1), \$dat, 2); 721 Set16u(Checksum(\$dat, 16, 12, 1), \$dat, 28); 722 Set16u(Checksum(\$dat, 2, 14, 2), \$dat, 0); 723 Set16u(Checksum(\$dat, 3, 14, 2), \$dat, 30); 724 } else { 725 $et->Warn('Error wriing DistortionInfo',1); 726 } 727 return $dat; 728} 729 730#------------------------------------------------------------------------------ 731# Patch for writing non-standard Panasonic RAW/RW2/RWL raw data 732# Inputs: 0) offset info ref, 1) raf ref, 2) IFD number 733# Returns: error string, or undef on success 734# OffsetInfo is a hash by tag ID of lists with the following elements: 735# 0 - tag info ref 736# 1 - pointer to int32u offset in IFD or value data 737# 2 - value count 738# 3 - reference to list of original offset values 739# 4 - IFD format number 740sub PatchRawDataOffset($$$) 741{ 742 my ($offsetInfo, $raf, $ifd) = @_; 743 my $stripOffsets = $$offsetInfo{0x111}; 744 my $stripByteCounts = $$offsetInfo{0x117}; 745 my $rawDataOffset = $$offsetInfo{0x118}; 746 my $err; 747 $err = 1 unless $ifd == 0; 748 $err = 1 unless $stripOffsets and $stripByteCounts and $$stripOffsets[2] == 1; 749 if ($rawDataOffset) { 750 $err = 1 unless $$rawDataOffset[2] == 1; 751 $err = 1 unless $$stripOffsets[3][0] == 0xffffffff or $$stripByteCounts[3][0] == 0; 752 } 753 $err and return 'Unsupported Panasonic/Leica RAW variant'; 754 if ($rawDataOffset) { 755 # update StripOffsets along with this tag if it contains a reasonable value 756 unless ($$stripOffsets[3][0] == 0xffffffff) { 757 # save pointer to StripOffsets value for updating later 758 push @$rawDataOffset, $$stripOffsets[1]; 759 } 760 # handle via RawDataOffset instead of StripOffsets 761 $stripOffsets = $$offsetInfo{0x111} = $rawDataOffset; 762 delete $$offsetInfo{0x118}; 763 } 764 # determine the length of the raw data 765 my $pos = $raf->Tell(); 766 $raf->Seek(0, 2) or $err = 1; # seek to end of file 767 my $len = $raf->Tell() - $$stripOffsets[3][0]; 768 $raf->Seek($pos, 0); 769 # quick check to be sure the raw data length isn't unreasonable 770 # (the 22-byte length is for '<Dummy raw image data>' in our tests) 771 $err = 1 if ($len < 1000 and $len != 22) or $len & 0x80000000; 772 $err and return 'Error reading Panasonic raw data'; 773 # update StripByteCounts info with raw data length 774 # (note that the original value is maintained in the file) 775 $$stripByteCounts[3][0] = $len; 776 777 return undef; 778} 779 780#------------------------------------------------------------------------------ 781# Write meta information to Panasonic JpgFromRaw in RAW/RW2/RWL image 782# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref 783# Returns: updated image data, or undef if nothing changed 784sub WriteJpgFromRaw($$$) 785{ 786 my ($et, $dirInfo, $tagTablePtr) = @_; 787 my $dataPt = $$dirInfo{DataPt}; 788 my $byteOrder = GetByteOrder(); 789 my $fileType = $$et{TIFF_TYPE}; # RAW, RW2 or RWL 790 my $dirStart = $$dirInfo{DirStart}; 791 if ($dirStart) { # DirStart is non-zero in DNG-converted RW2/RWL 792 my $dirLen = $$dirInfo{DirLen} | length($$dataPt) - $dirStart; 793 my $buff = substr($$dataPt, $dirStart, $dirLen); 794 $dataPt = \$buff; 795 } 796 my $raf = new File::RandomAccess($dataPt); 797 my $outbuff; 798 my %dirInfo = ( 799 RAF => $raf, 800 OutFile => \$outbuff, 801 ); 802 $$et{BASE} = $$dirInfo{DataPos}; 803 $$et{FILE_TYPE} = $$et{TIFF_TYPE} = 'JPEG'; 804 # use a specialized map so we don't write XMP or IPTC (or other junk) into the JPEG 805 my $editDirs = $$et{EDIT_DIRS}; 806 my $addDirs = $$et{ADD_DIRS}; 807 $et->InitWriteDirs(\%jpgFromRawMap); 808 # don't add XMP segment (IPTC won't get added because it is in Photoshop record) 809 delete $$et{ADD_DIRS}{XMP}; 810 my $result = $et->WriteJPEG(\%dirInfo); 811 # restore variables we changed 812 $$et{BASE} = 0; 813 $$et{FILE_TYPE} = 'TIFF'; 814 $$et{TIFF_TYPE} = $fileType; 815 $$et{EDIT_DIRS} = $editDirs; 816 $$et{ADD_DIRS} = $addDirs; 817 SetByteOrder($byteOrder); 818 return $result > 0 ? $outbuff : $$dataPt; 819} 820 821#------------------------------------------------------------------------------ 822# Extract meta information from an Panasonic JpgFromRaw 823# Inputs: 0) ExifTool object reference, 1) dirInfo reference 824# Returns: 1 on success, 0 if this wasn't a valid JpgFromRaw image 825sub ProcessJpgFromRaw($$$) 826{ 827 my ($et, $dirInfo, $tagTablePtr) = @_; 828 my $dataPt = $$dirInfo{DataPt}; 829 my $byteOrder = GetByteOrder(); 830 my $fileType = $$et{TIFF_TYPE}; # RAW, RW2 or RWL 831 my $tagInfo = $$dirInfo{TagInfo}; 832 my $verbose = $et->Options('Verbose'); 833 my ($indent, $out); 834 $tagInfo or $et->Warn('No tag info for Panasonic JpgFromRaw'), return 0; 835 my $dirStart = $$dirInfo{DirStart}; 836 if ($dirStart) { # DirStart is non-zero in DNG-converted RW2/RWL 837 my $dirLen = $$dirInfo{DirLen} | length($$dataPt) - $dirStart; 838 my $buff = substr($$dataPt, $dirStart, $dirLen); 839 $dataPt = \$buff; 840 } 841 $$et{BASE} = $$dirInfo{DataPos} + ($dirStart || 0); 842 $$et{FILE_TYPE} = $$et{TIFF_TYPE} = 'JPEG'; 843 $$et{DOC_NUM} = 1; 844 # extract information from embedded JPEG 845 my %dirInfo = ( 846 Parent => 'RAF', 847 RAF => new File::RandomAccess($dataPt), 848 ); 849 if ($verbose) { 850 my $indent = $$et{INDENT}; 851 $$et{INDENT} = ' '; 852 $out = $et->Options('TextOut'); 853 print $out '--- DOC1:JpgFromRaw ',('-'x56),"\n"; 854 } 855 # fudge HtmlDump base offsets to show as a stand-alone JPEG 856 $$et{BASE_FUDGE} = $$et{BASE}; 857 my $rtnVal = $et->ProcessJPEG(\%dirInfo); 858 $$et{BASE_FUDGE} = 0; 859 # restore necessary variables for continued RW2/RWL processing 860 $$et{BASE} = 0; 861 $$et{FILE_TYPE} = 'TIFF'; 862 $$et{TIFF_TYPE} = $fileType; 863 delete $$et{DOC_NUM}; 864 SetByteOrder($byteOrder); 865 if ($verbose) { 866 $$et{INDENT} = $indent; 867 print $out ('-'x76),"\n"; 868 } 869 return $rtnVal; 870} 871 8721; # end 873 874__END__ 875 876=head1 NAME 877 878Image::ExifTool::PanasonicRaw - Read/write Panasonic/Leica RAW/RW2/RWL meta information 879 880=head1 SYNOPSIS 881 882This module is loaded automatically by Image::ExifTool when required. 883 884=head1 DESCRIPTION 885 886This module contains definitions required by Image::ExifTool to read and 887write meta information in Panasonic/Leica RAW, RW2 and RWL images. 888 889=head1 AUTHOR 890 891Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com) 892 893This library is free software; you can redistribute it and/or modify it 894under the same terms as Perl itself. 895 896=head1 REFERENCES 897 898=over 4 899 900=item L<http://www.cybercom.net/~dcoffin/dcraw/> 901 902=back 903 904=head1 SEE ALSO 905 906L<Image::ExifTool::TagNames/PanasonicRaw Tags>, 907L<Image::ExifTool(3pm)|Image::ExifTool> 908 909=cut 910