在PowerShell中提取EXIF数据的简单方法



我一直在寻找可以使用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 模块中。您可以在此函数中看到我上面描述的逻辑的具体实现。

最新更新