我一直在寻找可以使用PowerShell提取EXIF数据的各种方法,但到目前为止,我已经看到它非常复杂。一些在这里和这里。
我正在寻找一种(相对)简单的方法来使用 powershell 提取基本的 EXIF 数据,以便我可以在本地使用它。
特别是,我对 Date Taken 属性特别感兴趣,我正在尝试找到一种可以通过 Get-Date 运行它的方法,并根据我自己的需要自定义格式。像这样:
$exifdatetaken = $mypicture.'Date taken' | Get-Date -Format yyyyMMdd-HHmmss
有谁知道是否有办法在PowerShell中本机执行此操作?
提取 EXIF 元数据通常如此复杂的原因是图像元数据存储为文件内容的一部分(而不是文件元数据,它由操作系统与文件分开存储和索引)。除此之外,EXIF是一种可扩展的格式,制造商经常使用(和滥用)以专有格式存储数据,并且情况很快就会变得混乱。
链接到的两个引用都使用 GDI+ Win32 API 中的类[System.Drawing]
类,该类公开 200 多个图像属性,其中多个属性可能反映拍摄照片的日期时间。它们还对单个属性36867 (0x9003)
即PropertyTagExifDTOrig
值进行硬编码。
利用 [System.Drawing.Bitmap]
类公开所有可用的元数据相当简单,例如:
$path = 'C:myimagesCanon.jpg'
$bitmap = [System.Drawing.Bitmap]::new($path)
$bitmap.PropertyItems
这将给出一组这样的结果:
Id Len Type Value
-- --- ---- -----
271 6 2 {67, 97, 110, 111, 110, 0}
272 24 2 {67, 97, 110, 111, 110, 32, 69, 79, 83, 32…}
274 2 3 {1, 0}
282 8 5 {180, 0, 0, 0, 1, 0, 0, 0}
283 8 5 {180, 0, 0, 0, 1, 0, 0, 0}
296 2 3 {2, 0}
306 20 2 {50, 48, 48, 51, 58, 49, 50, 58, 48, 52…}
531 2 3 {1, 0}
33434 8 5 {4, 0, 0, 0, 1, 0, 0, 0}
33437 8 5 {14, 0, 0, 0, 1, 0, 0, 0}
34855 2 3 {100, 0}
36864 4 7 {48, 50, 50, 49}
# etc.
这是容易的部分。 从这些数据中获得有用的东西是它变得棘手的地方。Id
引用属性标记,以告诉您正在查看哪个属性。Value
是一个字节数组,需要解码为有意义的值(它本身可能是一个或两个步骤的过程)。要了解如何解码字节,您需要查看可用于查找数据类型的Type
值。解码字节后,您可能会留下:
- 有意义的值(例如,包含相机制造商名称的 ASCII 字符串)
- 一个值,它要么是对EXIF标准中查找的引用(例如,对于Id 37383(0x9207 PropertyTagExifMeteringMode),值0表示"未知",1表示"平均值",依此类推),或者需要一些额外的计算才能获得有意义的值
- 可以从某些外部引用(例如ExifTool的标签列表)解码的未知值
- 无法解码的未知值(或需要原始软件进行解码)
例如,若要使用 Id 306(0x0132 PropertyTagDateTime)获取创建映像的日期和时间(ASCII 字符串),如下所示:
$pi = $bitmap.GetPropertyItem(306)
$ascii = [System.Text.ASCIIEncoding]::new()
$piDecoded = $ascii.GetString($pi.Value[0..$($pi.Len-2)]) # trim off the trailing null terminator
$piDecoded
$piDecoded.GetType().FullName
返回例如:
2022:06:11 13:25:36
System.String
然后,您可以将其直接操作为所需的格式,或者对其进行分析并将其发送到 Get-Date 以获取 DateTime 对象。
另一个示例,要使用 Id 34855 (0x8827 PropertyTagExifISOSpeed 获取图像(SHORT (UInt16) 类型)的 ISO:
$pi = $bitmap.GetPropertyItem(34855)
$piDecoded = [System.BitConverter]::ToUInt16($pi.Value,0)
$piDecoded
$piDecoded.GetType().FullName
返回例如:
100
System.UInt16
等等。
我曾想解决完全相同的问题,主要是在我的个人存档中对图像进行大规模重命名,但也想有一种简单的方法来过滤图像,例如,我将其内置到 PowerShell 模块中。您可以在此函数中看到我上面描述的逻辑的具体实现。