使用 PowerShell 从字节数组中提取 GPS 数值

Extracting GPS numerical values from byte array using PowerShell

我想从图像文件的 EXIF 属性中提取 GPS 位置数据。我正在使用 System.Drawing.Bitmap class 来获取原始值。我能够提取我正在寻找的值,但它们以字节或可能的字节数组形式返回,我需要帮助将字节转换为合理的数字。这是我目前所拥有的:

$imagePath = 'C:\temp\picTest\WP_20150918_003.jpg'
$imageProperties = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $imagePath

EXIF GPS Tag Reference 我知道我正在寻找的 属性 标签是:

首先我得到 GPSLatitudeRef:

$imageProperies.PropertyItems | ? ID -eq 0x0001

我得到:

 Id Len Type Value
-- --- ---- -----
 1   2    2 {78, 0}

根据 System.Drawing library 的 MSDN 文档,“2”的 PropertyItem.Type 是 ASCII。

我将值加载到一个变量中:

$GPSLatRef = $imageProperties.PropertyItems| Where ID -eq 0x0001
$GPSLatRef = $GPSLatRef.Value
$GPSLatRef
78
0

Get-Member 在变量 returns 上键入 System.Byte。我知道如何将字节转换回 ASCII:

$GPSLatRef = [System.Text.Encoding]::ASCII.GetString($GPSLatRef.Value)
$GPSLatRef

returns:

N

但是 GPSLatitude 值 (0x0002):

$imageProperies.PropertyItems | ? ID -eq 0x0002

returns:

Id Len Type Value
-- --- ---- -----
 2  24    5 {37, 0, 0, 0...}

将值加载到变量中:

$GPSLatitiude = $imageProperties.PropertyItems| Where ID -eq 0x0002
$GPSLatitiude.Value

返回值为:

37
0
0
0
1
0
0
0
40
0
0
0
1
0
0
0
220
182
0
0
232
3
0
0

根据上面引用的 MSDN 文档,PropertyItem.Type 个“5” "指定值数据成员是 数组 无符号长整数。每对代表一个分数;第一个整数是分子,第二个整数是分母。

在 Windows 资源管理器中查看文件本身的属性,我看到了十进制形式的 GPS 位置值。

"37;40;46.812000000005156"

根据上面的值数据和文档中数据类型的描述,以及与 Windows Explorer 中的十进制值进行比较,我可以推测 $GPSLatitude.Value 实际上向我展示了三个不同的每组两个整数。例如,

37
0
0
0

= 37

1
0
0
0

= 1

和 37/1 = 37,与 Windows Explorer 中的十进制值相匹配,所以我知道我在正确的轨道上。

如何从 GPSLatitude 属性 中找到的(数组?)字节拆分三组值?尽管字节中的数据被描述为长整数,但 Windows Explorer 中显示的十进制值显示结果可能是小数点右边十五位的数字,这让我认为乘积来自 属性 值的字节中两个长整数的除法可能需要存储在 [double] 或 [decimal]?

此代码段将为您提供纬度和经度详细信息,我正在使用 BitConverter 从字节数组中提取数据:

[double]$LatDegrees = (([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 0)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 4)));
[double]$LatMinutes = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 8)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 12));
[double]$LatSeconds = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 16)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 20));
[double]$LonDegrees = (([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 0)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 4)));
[double]$LonMinutes = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 8)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 12));
[double]$LonSeconds = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 16)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 20));
 "Latitude: $LatDegrees;$LatMinutes;$LatSeconds"
 "Longitude: $LonDegrees;$LonMinutes;$LonSeconds"