star/image/exif.go

232 lines
8.5 KiB
Go
Raw Normal View History

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
}