[C#] 如何讀取相片Metadata

Metadata 是內容的描述性資訊 (Descriptive information), 例如檔案的建立日期, 作者等. 而圖像則有其自己的metadata, 主要分三類:

  • Technical Metadata:
    主要為圖片的技術相關資訊, 例如光圈, 快門, ISO, 等.  除了相片外, 亦會有攝影器材的資訊, 如相機, 鏡頭等. 這類metadata 亦被統稱為Exif.
  • Descriptive Metadata:
    通常為圖片的內容描述. 如圖片的主題, 檢簽, 位置等.
  • Administrative Metadata:
    若圖片有申請知識產權(Intellengent Property, IP), 則在此會有版權資訊, 使用守則. 而這類Metadata 則以IPTC 形式出現.

在示範中, 會取出相中所有meta data 出來.

先建立Enum PropertyTypeID, 以表示該property 所代表的metadata.
PropertyTagID.cs

public enum PropertyTagID
{
PropertyTagGpsVer = 0x0000,
PropertyTagGpsLatitudeRef = 0x0001,
PropertyTagGpsLatitude = 0x0002,
PropertyTagGpsLongitudeRef = 0x0003,
PropertyTagGpsLongitude = 0x0004,
PropertyTagGpsAltitudeRef = 0x0005,
PropertyTagGpsAltitude = 0x0006,
PropertyTagGpsGpsTime = 0x0007,
PropertyTagGpsGpsSatellites = 0x0008,
PropertyTagGpsGpsStatus = 0x0009,
PropertyTagGpsGpsMeasureMode = 0x000A,
PropertyTagGpsGpsDop = 0x000B,
PropertyTagGpsSpeedRef = 0x000C,
PropertyTagGpsSpeed = 0x000D,
PropertyTagGpsTrackRef = 0x000E,
PropertyTagGpsTrack = 0x000F,
PropertyTagGpsImgDirRef = 0x0010,
PropertyTagGpsImgDir = 0x0011,
PropertyTagGpsMapDatum = 0x0012,
PropertyTagGpsDestLatRef = 0x0013,
PropertyTagGpsDestLat = 0x0014,
PropertyTagGpsDestLongRef = 0x0015,
PropertyTagGpsDestLong = 0x0016,
PropertyTagGpsDestBearRef = 0x0017,
PropertyTagGpsDestBear = 0x0018,
PropertyTagGpsDestDistRef = 0x0019,
PropertyTagGpsDestDist = 0x001A,
PropertyTagNewSubfileType = 0x00FE,
PropertyTagSubfileType = 0x00FF,
PropertyTagImageWidth = 0x0100,
PropertyTagImageHeight = 0x0101,
PropertyTagBitsPerSample = 0x0102,
PropertyTagCompression = 0x0103,
PropertyTagPhotometricInterp = 0x0106,
PropertyTagThreshHolding = 0x0107,
PropertyTagCellWidth = 0x0108,
PropertyTagCellHeight = 0x0109,
PropertyTagFillOrder = 0x010A,
PropertyTagDocumentName = 0x010D,
PropertyTagImageDescription = 0x010E,
PropertyTagEquipMake = 0x010F,
PropertyTagEquipModel = 0x0110,
PropertyTagStripOffsets = 0x0111,
PropertyTagOrientation = 0x0112,
PropertyTagSamplesPerPixel = 0x0115,
PropertyTagRowsPerStrip = 0x0116,
PropertyTagStripBytesCount = 0x0117,
PropertyTagMinSampleValue = 0x0118,
PropertyTagMaxSampleValue = 0x0119,
PropertyTagXResolution = 0x011A,
PropertyTagYResolution = 0x011B,
PropertyTagPlanarConfig = 0x011C,
PropertyTagPageName = 0x011D,
PropertyTagXPosition = 0x011E,
PropertyTagYPosition = 0x011F,
PropertyTagFreeOffset = 0x0120,
PropertyTagFreeByteCounts = 0x0121,
PropertyTagGrayResponseUnit = 0x0122,
PropertyTagGrayResponseCurve = 0x0123,
PropertyTagT4Option = 0x0124,
PropertyTagT6Option = 0x0125,
PropertyTagResolutionUnit = 0x0128,
PropertyTagPageNumber = 0x0129,
PropertyTagTransferFunction = 0x012D,
PropertyTagSoftwareUsed = 0x0131,
PropertyTagDateTime = 0x0132,
PropertyTagArtist = 0x013B,
PropertyTagHostComputer = 0x013C,
PropertyTagPredictor = 0x013D,
PropertyTagWhitePoint = 0x013E,
PropertyTagPrimaryChromaticities = 0x013F,
PropertyTagColorMap = 0x0140,
PropertyTagHalftoneHints = 0x0141,
PropertyTagTileWidth = 0x0142,
PropertyTagTileLength = 0x0143,
PropertyTagTileOffset = 0x0144,
PropertyTagTileByteCounts = 0x0145,
PropertyTagInkSet = 0x014C,
PropertyTagInkNames = 0x014D,
PropertyTagNumberOfInks = 0x014E,
PropertyTagDotRange = 0x0150,
PropertyTagTargetPrinter = 0x0151,
PropertyTagExtraSamples = 0x0152,
PropertyTagSampleFormat = 0x0153,
PropertyTagSMinSampleValue = 0x0154,
PropertyTagSMaxSampleValue = 0x0155,
PropertyTagTransferRange = 0x0156,
PropertyTagJPEGProc = 0x0200,
PropertyTagJPEGInterFormat = 0x0201,
PropertyTagJPEGInterLength = 0x0202,
PropertyTagJPEGRestartInterval = 0x0203,
PropertyTagJPEGLosslessPredictors = 0x0205,
PropertyTagJPEGPointTransforms = 0x0206,
PropertyTagJPEGQTables = 0x0207,
PropertyTagJPEGDCTables = 0x0208,
PropertyTagJPEGACTables = 0x0209,
PropertyTagYCbCrCoefficients = 0x0211,
PropertyTagYCbCrSubsampling = 0x0212,
PropertyTagYCbCrPositioning = 0x0213,
PropertyTagREFBlackWhite = 0x0214,
PropertyTagGamma = 0x0301,
PropertyTagICCProfileDescriptor = 0x0302,
PropertyTagSRGBRenderingIntent = 0x0303,
PropertyTagImageTitle = 0x0320,
PropertyTagResolutionXUnit = 0x5001,
PropertyTagResolutionYUnit = 0x5002,
PropertyTagResolutionXLengthUnit = 0x5003,
PropertyTagResolutionYLengthUnit = 0x5004,
PropertyTagPrintFlags = 0x5005,
PropertyTagPrintFlagsVersion = 0x5006,
PropertyTagPrintFlagsCrop = 0x5007,
PropertyTagPrintFlagsBleedWidth = 0x5008,
PropertyTagPrintFlagsBleedWidthScale = 0x5009,
PropertyTagHalftoneLPI = 0x500A,
PropertyTagHalftoneLPIUnit = 0x500B,
PropertyTagHalftoneDegree = 0x500C,
PropertyTagHalftoneShape = 0x500D,
PropertyTagHalftoneMisc = 0x500E,
PropertyTagHalftoneScreen = 0x500F,
PropertyTagJPEGQuality = 0x5010,
PropertyTagGridSize = 0x5011,
PropertyTagThumbnailFormat = 0x5012,
PropertyTagThumbnailWidth = 0x5013,
PropertyTagThumbnailHeight = 0x5014,
PropertyTagThumbnailColorDepth = 0x5015,
PropertyTagThumbnailPlanes = 0x5016,
PropertyTagThumbnailRawBytes = 0x5017,
PropertyTagThumbnailSize = 0x5018,
PropertyTagThumbnailCompressedSize = 0x5019,
PropertyTagColorTransferFunction = 0x501A,
PropertyTagThumbnailData = 0x501B,
PropertyTagThumbnailImageWidth = 0x5020,
PropertyTagThumbnailImageHeight = 0x5021,
PropertyTagThumbnailBitsPerSample = 0x5022,
PropertyTagThumbnailCompression = 0x5023,
PropertyTagThumbnailPhotometricInterp = 0x5024,
PropertyTagThumbnailImageDescription = 0x5025,
PropertyTagThumbnailEquipMake = 0x5026,
PropertyTagThumbnailEquipModel = 0x5027,
PropertyTagThumbnailStripOffsets = 0x5028,
PropertyTagThumbnailOrientation = 0x5029,
PropertyTagThumbnailSamplesPerPixel = 0x502A,
PropertyTagThumbnailRowsPerStrip = 0x502B,
PropertyTagThumbnailStripBytesCount = 0x502C,
PropertyTagThumbnailResolutionX = 0x502D,
PropertyTagThumbnailResolutionY = 0x502E,
PropertyTagThumbnailPlanarConfig = 0x502F,
PropertyTagThumbnailResolutionUnit = 0x5030,
PropertyTagThumbnailTransferFunction = 0x5031,
PropertyTagThumbnailSoftwareUsed = 0x5032,
PropertyTagThumbnailDateTime = 0x5033,
PropertyTagThumbnailArtist = 0x5034,
PropertyTagThumbnailWhitePoint = 0x5035,
PropertyTagThumbnailPrimaryChromaticities = 0x5036,
PropertyTagThumbnailYCbCrCoefficients = 0x5037,
PropertyTagThumbnailYCbCrSubsampling = 0x5038,
PropertyTagThumbnailYCbCrPositioning = 0x5039,
PropertyTagThumbnailRefBlackWhite = 0x503A,
PropertyTagThumbnailCopyRight = 0x503B,
PropertyTagLuminanceTable = 0x5090,
PropertyTagChrominanceTable = 0x5091,
PropertyTagFrameDelay = 0x5100,
PropertyTagLoopCount = 0x5101,
PropertyTagGlobalPalette = 0x5102,
PropertyTagIndexBackground = 0x5103,
PropertyTagIndexTransparent = 0x5104,
PropertyTagPixelUnit = 0x5110,
PropertyTagPixelPerUnitX = 0x5111,
PropertyTagPixelPerUnitY = 0x5112,
PropertyTagPaletteHistogram = 0x5113,
PropertyTagCopyright = 0x8298,
PropertyTagExifExposureTime = 0x829A,
PropertyTagExifFNumber = 0x829D,
PropertyTagExifIFD = 0x8769,
PropertyTagICCProfile = 0x8773,
PropertyTagExifExposureProg = 0x8822,
PropertyTagExifSpectralSense = 0x8824,
PropertyTagGpsIFD = 0x8825,
PropertyTagExifISOSpeed = 0x8827,
PropertyTagExifOECF = 0x8828,
PropertyTagExifVer = 0x9000,
PropertyTagExifDTOrig = 0x9003,
PropertyTagExifDTDigitized = 0x9004,
PropertyTagExifCompConfig = 0x9101,
PropertyTagExifCompBPP = 0x9102,
PropertyTagExifShutterSpeed = 0x9201,
PropertyTagExifAperture = 0x9202,
PropertyTagExifBrightness = 0x9203,
PropertyTagExifExposureBias = 0x9204,
PropertyTagExifMaxAperture = 0x9205,
PropertyTagExifSubjectDist = 0x9206,
PropertyTagExifMeteringMode = 0x9207,
PropertyTagExifLightSource = 0x9208,
PropertyTagExifFlash = 0x9209,
PropertyTagExifFocalLength = 0x920A,
PropertyTagExifMakerNote = 0x927C,
PropertyTagExifUserComment = 0x9286,
PropertyTagExifDTSubsec = 0x9290,
PropertyTagExifDTOrigSS = 0x9291,
PropertyTagExifDTDigSS = 0x9292,
PropertyTagExifFPXVer = 0xA000,
PropertyTagExifColorSpace = 0xA001,
PropertyTagExifPixXDim = 0xA002,
PropertyTagExifPixYDim = 0xA003,
PropertyTagExifRelatedWav = 0xA004,
PropertyTagExifInterop = 0xA005,
PropertyTagExifFlashEnergy = 0xA20B,
PropertyTagExifSpatialFR = 0xA20C,
PropertyTagExifFocalXRes = 0xA20E,
PropertyTagExifFocalYRes = 0xA20F,
PropertyTagExifFocalResUnit = 0xA210,
PropertyTagExifSubjectLoc = 0xA214,
PropertyTagExifExposureIndex = 0xA215,
PropertyTagExifSensingMethod = 0xA217,
PropertyTagExifFileSource = 0xA300,
PropertyTagExifSceneType = 0xA301,
PropertyTagExifCfaPattern = 0xA302,
}

另一個Enum PropertyTagType, 用作表示該metadata 的value type.

public enum PropertyTagType
{
PixelFormat4bppIndexed = 0,
Byte = 1,
ASCII = 2,
Short = 3,
Long = 4,
Rational = 5,
Undefined = 7,
SLONG = 9,
SRational = 10
}

因為metadata 中會有份數, 所以利用Internal class Fraction, 表示.

internal class Fraction
{
#region Class constructor
public Fraction(int numerator, int denumerator)
{
Numerator = numerator;
Denumerator = denumerator;
}
public Fraction(uint numerator, uint denumerator)
{
Numerator = Convert.ToInt32(numerator);
Denumerator = Convert.ToInt32(denumerator);
}
public Fraction(int numerator)
{
Numerator = numerator;
Denumerator = 1;
}
#endregion
#region Numerator
private int numerator;
public int Numerator
{
get
{
return numerator;
}
set
{
numerator = value;
}
}
#endregion
#region Denumerator
private int denumerator;
public int Denumerator
{
get
{
return denumerator;
}
set
{
denumerator = value;
}
}
#endregion
#region ToString override
public override string ToString()
{
if (Denumerator == 1)
return String.Format("{0}", Numerator);
else
return String.Format("{0}/{1}", Numerator, Denumerator);
}
#endregion
}

Class PropertyTypeValue, 用作metadata 的轉換. 因為取出來的raw value 為Byte Array, 所以須要透過PropertyType 決定如何轉換成presentable 的格式.

public class PropertyTagValue
{
public static object GetValueObject(PropertyItem property)
{
if (property == null)
return null;
switch ((PropertyTagType)property.Type)
{
//ASCII
case PropertyTagType.ASCII:
ASCIIEncoding encoding = new ASCIIEncoding();
return encoding.GetString(property.Value, 0, property.Len - 1);
//BYTE
case PropertyTagType.Byte:
if (property.Len == 1)
return property.Value[0];
else
return property.Value;
//LONG
case PropertyTagType.Long:
uint[] resultLong = new uint[property.Len / 4];
for (int i = 0; i < resultLong.Length; i++)
resultLong[i] = BitConverter.ToUInt32(property.Value, i * 4);
if (resultLong.Length == 1)
return resultLong[0];
else
return resultLong;
//SHORT
case PropertyTagType.Short:
ushort[] resultShort = new ushort[property.Len / 2];
for (int i = 0; i < resultShort.Length; i++)
resultShort[i] = BitConverter.ToUInt16(property.Value, i * 2);
if (resultShort.Length == 1)
return resultShort[0];
else
return resultShort;
//SLONG
case PropertyTagType.SLONG:
int[] resultSLong = new int[property.Len / 4];
for (int i = 0; i < resultSLong.Length; i++)
resultSLong[i] = BitConverter.ToInt32(property.Value, i * 4);
if (resultSLong.Length == 1)
return resultSLong[0];
else
return resultSLong;
//RATIONAL
case PropertyTagType.Rational:
Fraction[] resultRational = new Fraction[property.Len / 8];
uint uNumerator;
uint uDenumerator;
for (int i = 0; i < resultRational.Length; i++)
{
uNumerator = BitConverter.ToUInt32(property.Value, i * 8);
uDenumerator = BitConverter.ToUInt32(property.Value, i * 8 + 4);
resultRational[i] = new Fraction(uNumerator, uDenumerator);
}
if (resultRational.Length == 1)
return resultRational[0];
else
return resultRational;
//SRATIONAL
case PropertyTagType.SRational:
Fraction[] resultSRational = new Fraction[property.Len / 8];
int sNumerator;
int sDenumerator;
for (int i = 0; i < resultSRational.Length; i++)
{
sNumerator = BitConverter.ToInt32(property.Value, i * 8);
sDenumerator = BitConverter.ToInt32(property.Value, i * 8 + 4);
resultSRational[i] = new Fraction(sNumerator, sDenumerator);
}
if (resultSRational.Length == 1)
return resultSRational[0];
else
return resultSRational;
//UNDEFINE
default:
if (property.Len == 1)
return property.Value[0];
else
return property.Value;
}
}
}

最後用Class Photo 將讀取圖片, 並將所有metadata取出來.

 public class Photo
{
private Image _image;
public Photo(string path)
{
this._image = new Bitmap(path);
}
public Dictionary<string, object> GetMetadata()
{
Dictionary<string, object> result = new Dictionary<string, object>();
List<PropertyItem> propertyItems = this._image.PropertyItems.ToList();
propertyItems.ForEach(delegate (PropertyItem item)
{
PropertyTagID key = (PropertyTagID)item.Id;
object value = PropertyTagValue.GetValueObject(item);
string displayKey = Enum.GetName(typeof(PropertyTagID), key);
if (!String.IsNullOrEmpty(displayKey))
{
result.Add(displayKey, value);
}
});
return result;
}
}

利用WPF implement的UI 示範如下.

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btmImage_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog data = new OpenFileDialog();
data.Title = "Open First Image File";
data.Filter = "JPG files (*.jpg*)|*.jpg";
data.FilterIndex = 1;
data.RestoreDirectory = true;
if (data.ShowDialog().Value)
{
string filePath = data.FileName;
txtImgPath.Text = filePath;
Photo photo = new Photo(filePath);
Dictionary<string, object> metadata = photo.GetMetadata();
DataGridMetadata.ItemsSource = metadata;
}
}
}

執行時, 見到圖片及相關metadata, 是為成功.

Reference

About C.H. Ling 262 Articles
a .net / Java developer from Hong Kong and currently located in United Kingdom. Thanks for Google because it solve many technical problems so I build this blog as return. Besides coding and trying advance technology, hiking and traveling is other favorite to me, so I will write down something what I see and what I feel during it. Happy reading!!!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.