232 lines
8.5 KiB
Go
232 lines
8.5 KiB
Go
|
package image
|
|||
|
|
|||
|
import (
|
|||
|
"b612.me/exif"
|
|||
|
exifcommon "b612.me/exif/common"
|
|||
|
"encoding/hex"
|
|||
|
"fmt"
|
|||
|
"os"
|
|||
|
)
|
|||
|
|
|||
|
func Exif(path string) error {
|
|||
|
f, err := os.Open(path)
|
|||
|
if err != nil {
|
|||
|
return fmt.Errorf("打开文件失败 %s", path)
|
|||
|
}
|
|||
|
rawExif, err := exif.SearchAndExtractExifWithReader(f)
|
|||
|
f.Close()
|
|||
|
if err != nil {
|
|||
|
return fmt.Errorf("查询 EXIF 信息失败 %s: %w", path, err)
|
|||
|
}
|
|||
|
tags, _, err := exif.GetFlatExifDataUniversalSearch(rawExif, nil, true)
|
|||
|
if err != nil {
|
|||
|
return fmt.Errorf("获取 EXIF 信息失败 %s: %w", path, err)
|
|||
|
}
|
|||
|
ifd0Map := map[string]string{
|
|||
|
// 根 IFD (IFD0) 标签
|
|||
|
"IFD.Software": "生成软件名",
|
|||
|
"IFD.ImageWidth": "图像宽度",
|
|||
|
"IFD.ImageLength": "图像高度",
|
|||
|
"IFD.Make": "相机制造商",
|
|||
|
"IFD.Model": "相机型号",
|
|||
|
"IFD.Orientation": "图像方向",
|
|||
|
"IFD.YResolution": "Y轴分辨率",
|
|||
|
"IFD.XResolution": "X轴分辨率",
|
|||
|
"IFD.ResolutionUnit": "分辨率单位",
|
|||
|
"IFD.DateTime": "最后修改时间",
|
|||
|
"IFD.ImageDescription": "图像描述",
|
|||
|
"IFD.Artist": "图像作者",
|
|||
|
"IFD.Copyright": "版权信息",
|
|||
|
"IFD.HostComputer": "创建图像的计算机",
|
|||
|
"IFD.BitsPerSample": "每个颜色分量的位数",
|
|||
|
"IFD.Compression": "压缩方案",
|
|||
|
"IFD.PhotometricInterpretation": "像素组成方式",
|
|||
|
"IFD.StripOffsets": "图像数据条带偏移量",
|
|||
|
"IFD.SamplesPerPixel": "每个像素的分量数",
|
|||
|
"IFD.RowsPerStrip": "每个条带的行数",
|
|||
|
"IFD.StripByteCounts": "每个条带的字节数",
|
|||
|
"IFD.PlanarConfiguration": "数据排列方式",
|
|||
|
|
|||
|
// Exif 子 IFD 标签
|
|||
|
"IFD/Exif.DateTimeOriginal": "原始拍摄时间",
|
|||
|
"IFD/Exif.DateTimeDigitized": "数字化时间",
|
|||
|
"IFD/Exif.OffsetTimeOriginal": "原始拍摄时区偏移",
|
|||
|
"IFD/Exif.ExposureTime": "曝光时间",
|
|||
|
"IFD/Exif.FNumber": "光圈F值",
|
|||
|
"IFD/Exif.ExposureProgram": "曝光程序",
|
|||
|
"IFD/Exif.ISOSpeedRatings": "ISO感光度",
|
|||
|
"IFD/Exif.ShutterSpeedValue": "快门速度(APEX值)",
|
|||
|
"IFD/Exif.ApertureValue": "光圈值(APEX值)",
|
|||
|
"IFD/Exif.BrightnessValue": "亮度值(APEX值)",
|
|||
|
"IFD/Exif.ExposureBiasValue": "曝光补偿值(EV)",
|
|||
|
"IFD/Exif.MeteringMode": "测光模式",
|
|||
|
"IFD/Exif.Flash": "闪光灯状态",
|
|||
|
"IFD/Exif.FocalLength": "焦距(毫米)",
|
|||
|
"IFD/Exif.SubjectDistance": "拍摄主体距离(米)",
|
|||
|
"IFD/Exif.FlashEnergy": "闪光灯强度",
|
|||
|
"IFD/Exif.SpatialFrequencyResponse": "空间频率响应",
|
|||
|
"IFD/Exif.FocalPlaneXResolution": "焦平面X分辨率",
|
|||
|
"IFD/Exif.FocalPlaneYResolution": "焦平面Y分辨率",
|
|||
|
"IFD/Exif.FocalPlaneResolutionUnit": "焦平面分辨率单位",
|
|||
|
"IFD/Exif.FileSource": "文件来源",
|
|||
|
"IFD/Exif.SceneType": "场景类型",
|
|||
|
"IFD/Exif.CFAPattern": "CFA(彩色滤镜阵列)模式",
|
|||
|
"IFD/Exif.CustomRendered": "自定义图像处理",
|
|||
|
"IFD/Exif.ExposureMode": "曝光模式",
|
|||
|
"IFD/Exif.WhiteBalance": "白平衡",
|
|||
|
"IFD/Exif.DigitalZoomRatio": "数码变焦比率",
|
|||
|
"IFD/Exif.SceneCaptureType": "场景拍摄类型",
|
|||
|
"IFD/Exif.GainControl": "增益控制",
|
|||
|
"IFD/Exif.Contrast": "对比度",
|
|||
|
"IFD/Exif.Saturation": "饱和度",
|
|||
|
"IFD/Exif.Sharpness": "锐度",
|
|||
|
"IFD/Exif.DeviceSettingDescription": "设备设置描述",
|
|||
|
"IFD/Exif.SubjectDistanceRange": "主体距离范围",
|
|||
|
|
|||
|
// GPS 子 IFD 标签 (可选添加)
|
|||
|
"IFD/GPSInfo.GPSLatitude": "纬度",
|
|||
|
"IFD/GPSInfo.GPSLongitude": "经度",
|
|||
|
"IFD/GPSInfo.GPSAltitude": "海拔高度",
|
|||
|
"IFD/GPSInfo.GPSTimeStamp": "GPS时间戳",
|
|||
|
"IFD/GPSInfo.GPSDateStamp": "GPS日期戳",
|
|||
|
"IFD/GPSInfo.GPSDOP": "定位精度",
|
|||
|
}
|
|||
|
for _, tag := range tags {
|
|||
|
sample := "%s.%v {%#04x} [%v] :%-10v\n"
|
|||
|
if name, ok := ifd0Map[tag.IfdPath+"."+tag.TagName]; ok {
|
|||
|
sample = "%s.%v {%#04x} [%v] (" + name + ") :%-10v\n"
|
|||
|
}
|
|||
|
switch tag.IfdPath + "." + tag.TagName {
|
|||
|
case "IFD.ResolutionUnit":
|
|||
|
switch tag.Value.([]uint16)[0] {
|
|||
|
case 1:
|
|||
|
fmt.Printf("%s.%v {%#04x} [%v] (分辨率单位) : 没有单位\n", tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName)
|
|||
|
case 2:
|
|||
|
fmt.Printf("%s.%v {%#04x} [%v] (分辨率单位) : 英寸\n", tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName)
|
|||
|
case 3:
|
|||
|
fmt.Printf("%s.%v {%#04x} [%v] (分辨率单位) : 像素\n", tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName)
|
|||
|
default:
|
|||
|
fmt.Printf("%s.%v {%#04x} [%v] (分辨率单位) : 未知单位:%v\n", tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, tag.Value)
|
|||
|
}
|
|||
|
case "IFD/GPSInfo.GPSLatitude", "IFD/GPSInfo.GPSLongitude":
|
|||
|
switch val := tag.Value.(type) {
|
|||
|
case []exifcommon.Rational:
|
|||
|
var value string
|
|||
|
for i, v := range val {
|
|||
|
switch i {
|
|||
|
case 0:
|
|||
|
value += fmt.Sprintf("%.0f°", float64(v.Numerator)/float64(v.Denominator))
|
|||
|
case 1:
|
|||
|
value += fmt.Sprintf("%.0f′", float64(v.Numerator)/float64(v.Denominator))
|
|||
|
case 2:
|
|||
|
value += fmt.Sprintf("%.3f″", float64(v.Numerator)/float64(v.Denominator))
|
|||
|
}
|
|||
|
}
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, value)
|
|||
|
default:
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val)
|
|||
|
}
|
|||
|
case "IFD/GPSInfo.GPSTimeStamp":
|
|||
|
switch val := tag.Value.(type) {
|
|||
|
case []exifcommon.Rational:
|
|||
|
var value string
|
|||
|
for _, v := range val {
|
|||
|
value += fmt.Sprintf("%.0f:", float64(v.Numerator)/float64(v.Denominator))
|
|||
|
}
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, value[:len(value)-1]) // 去掉最后一个冒号
|
|||
|
default:
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val)
|
|||
|
}
|
|||
|
default:
|
|||
|
switch val := tag.Value.(type) {
|
|||
|
case []exifcommon.Rational:
|
|||
|
switch tag.IfdPath + "." + tag.TagName {
|
|||
|
case "IFD/Exif.ExposureTime":
|
|||
|
sample += "秒"
|
|||
|
}
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, float64(val[0].Numerator)/float64(val[0].Denominator))
|
|||
|
case []uint16:
|
|||
|
if len(val) == 1 {
|
|||
|
switch tag.IfdPath + "." + tag.TagName {
|
|||
|
case "IFD/Exif.ExposureProgram":
|
|||
|
valStr := ""
|
|||
|
switch val[0] {
|
|||
|
case 0:
|
|||
|
valStr = "未知"
|
|||
|
case 1:
|
|||
|
valStr = "手动"
|
|||
|
case 2:
|
|||
|
valStr = "自动"
|
|||
|
case 3:
|
|||
|
valStr = "光圈优先"
|
|||
|
case 4:
|
|||
|
valStr = "快门优先"
|
|||
|
case 5:
|
|||
|
valStr = "创意(慢速)"
|
|||
|
case 6:
|
|||
|
valStr = "动作(高速)"
|
|||
|
case 7:
|
|||
|
valStr = "肖像"
|
|||
|
case 8:
|
|||
|
valStr = "景观"
|
|||
|
case 9:
|
|||
|
valStr = "Bulb"
|
|||
|
}
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, valStr)
|
|||
|
continue
|
|||
|
case "IFD/Exif.MeteringMode":
|
|||
|
/*
|
|||
|
0 = Unknown
|
|||
|
1 = Average
|
|||
|
2 = Center-weighted average
|
|||
|
3 = Spot
|
|||
|
4 = Multi-spot
|
|||
|
5 = Multi-segment
|
|||
|
6 = Partial
|
|||
|
255 = Other
|
|||
|
*/
|
|||
|
valStr := ""
|
|||
|
switch val[0] {
|
|||
|
case 0:
|
|||
|
valStr = "未知"
|
|||
|
case 1:
|
|||
|
valStr = "平均"
|
|||
|
case 2:
|
|||
|
valStr = "中央重点平均"
|
|||
|
case 3:
|
|||
|
valStr = "点测光"
|
|||
|
case 4:
|
|||
|
valStr = "多点测光"
|
|||
|
case 5:
|
|||
|
valStr = "多段测光"
|
|||
|
case 6:
|
|||
|
valStr = "局部测光"
|
|||
|
case 255:
|
|||
|
valStr = "其他"
|
|||
|
}
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, valStr)
|
|||
|
continue
|
|||
|
}
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val[0])
|
|||
|
} else {
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val)
|
|||
|
}
|
|||
|
case []uint32:
|
|||
|
if len(val) == 1 {
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val[0])
|
|||
|
} else {
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val)
|
|||
|
}
|
|||
|
case []uint8:
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, hex.EncodeToString(val))
|
|||
|
case string:
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val)
|
|||
|
default:
|
|||
|
fmt.Printf(sample, tag.IfdPath, tag.TagName, tag.TagId, tag.TagTypeName, val)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
return nil
|
|||
|
}
|