您的位置:首页 > 编程语言 > C#

C#读取JPEG图片中Exif信息

2013-05-13 21:25 661 查看

C#读取JPEG图片中Exif信息

抽空把C++代码改为C#代码了,发现C#不如C语言来的灵活,类型转换还是,唉...

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Imaging;
using System.Reflection;
using System.IO;

namespace Exif
{

public struct TIFDEntry
{
public int tag; // Tag的含义可以查阅TIFF的规范文档
public int type; // 指明此Entry中记录的数据类型,TIFF规范只定义了五种类型,EXIF增加了三种
public int size; // 大小
public int val; // 取值, 根据type含义会改变
};

public class ExifReader
{
const int SOI = 0xD8; // 图像开始
const int APP0 = 0xE0; // JFIF应用数据块
const int APP1 = 0xE1; // Exif数据块(APP1)
const int MAX_WORD = 65535;

bool jpegFault;
bool APP0Fault;
bool ExifFault;

FileStream fs = null;
long pos = 0; // 流中的位置
bool isLittleEndian; // 低字节是否在前

byte[] stringData;
int index = 0; // (stringData中的位置)
List<TIFDEntry> entries = new List<TIFDEntry>();

JPEGInfo info = new JPEGInfo();

/// <summary>
/// 构造函数
/// </summary>
/// <param name="filePath">图片路径</param>
public ExifReader(string filePath)
{
jpegFault = false;
APP0Fault = false;
ExifFault = false;

try
{
fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);

if (!readSOI())
{
jpegFault = true;
return;
}

if (!readAPP0())
{
APP0Fault = true;
}

if (!readExif())
{
ExifFault = true;
}
}
catch
{
throw;
}
finally
{
if (fs != null)
{
fs.Close();
fs.Dispose();
}
}
}

// 读取SOI段
private bool readSOI()
{
byte[] title = new byte[2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(title, 0, 2);
pos = fs.Position;

if (title[0] != 0xFF || title[1] != SOI)
{
return false;
}
else
{
return true;
}
}

// 读取APP0段
private bool readAPP0()
{
byte[] title = new byte[2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(title, 0, 2);
pos = fs.Position;

if (title[0] != 0xFF || title[1] != APP0)
{
pos -= 2;
return false;
}

byte[] len = new byte[2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(len, 0, 2);
pos = fs.Position;

int length = (len[0] << 8) + len[1]; // APP0段长度
byte[] data = new byte[length - 2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(data, 0, data.Length);
pos = fs.Position;

// 处理APP0的数据(TIFF格式)
// ......
//Encoding.ASCII.GetString(data, 0, length - 2);

return true;
}

// 读取APP1段
private bool readExif()
{
byte[] title = new byte[2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(title, 0, 2);
pos = fs.Position;

if (title[0] != 0xFF || title[1] != APP1)
{
return false;
}

byte[] len = new byte[2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(len, 0, 2);
pos = fs.Position;

int length = (len[0] << 8) + len[1]; // APP1段长度
stringData = new byte[length - 2];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(stringData, 0, stringData.Length);
pos = fs.Position;

string strExifHeader = Encoding.ASCII.GetString(stringData, index, 6); // 获得EXIF Header
// TIFF Image File Header开始
index += 6;

if (stringData[index] == 'I' && stringData[index + 1] == 'I') // 读取字节顺序方式
{
isLittleEndian = true;
}
else
{
isLittleEndian = false;
}

byte[] bysFlag = GetBytes(stringData, index + 2, 2); // Flag(0x2A)
byte[] bysOffset = GetBytes(stringData, index + 4, 4); // 第一个IFD的偏移量
int offset = BytesToUint(bysOffset);

readIFD(offset);

foreach (TIFDEntry entry in entries)
{
analyseTIFD(entry);
}

return true;
}

private void readIFD(int offset)
{
byte[] bysEntryCount = GetBytes(stringData, index + offset, 2); // Entry个数
int numOfIFD = BytesToInt(bysEntryCount);

for (int i = 0; i < numOfIFD; i++)
{
entries.Add(createFromIndex(offset + 2 + 12 * i));
}

byte[] bysOffsetOfNext = GetBytes(stringData, index + offset + 2 + 12 * numOfIFD, 4);
int offsetOfNext = BytesToUint(bysOffsetOfNext);

if (offsetOfNext != 0)
{
readIFD(offsetOfNext);
}

}

private TIFDEntry createFromIndex(int offset)
{
TIFDEntry entry = new TIFDEntry();
entry.tag = BytesToInt(GetBytes(stringData, index + offset, 2));
entry.type = BytesToInt(GetBytes(stringData, index + offset + 2, 2));
entry.size = BytesToUint(GetBytes(stringData, index + offset + 4, 4));
entry.val = BytesToUint(GetBytes(stringData, index + offset + 8, 4));
return entry;
}

private void analyseTIFD(TIFDEntry entry)
{
switch (entry.tag)
{
case 0x010E: // 图像说明
info.description = getEntryASCII(entry);
break;

case 0x010F: // 制造厂商
info.maker = getEntryASCII(entry);
break;

case 0x0110: // 型号
info.model = getEntryASCII(entry);
break;

case 0x011A: // x分辩率
info.xResolution = getEntryRational(entry);
break;

case 0x011B: // y分辩率
info.yResolution = getEntryRational(entry);
break;

case 0x0128: // 分辩率单位
info.resolutionUnit = entry.val;
break;

case 0x0131: // 创建软件名称
info.software = getEntryASCII(entry);
break;

case 0x0132: // 创建时间
info.createTime = getEntryASCII(entry);
break;

case 0x0213: // YCbCr位置
info.YCbCrPosition = entry.val;
break;

case 0x8298: // 版权信息
info.copyright = getEntryASCII(entry);
break;

case 0x8769: // Exif末尾
//readIFD(entry.val);
break;

case 0x0103: // 压缩信息
info.compression = entry.val;
break;

case 0x829A: // 曝光时间
info.exposureTime = getEntryRational(entry);
break;

case 0x829D: // F-值
info.fNumber = getEntryRational(entry);
break;

case 0x8822: // 曝光设定
info.exposureProgram = entry.val;
break;

case 0x8827: // ISO速率
info.ISOSpeedRatings = entry.val;
break;

case 0x9003: // 拍摄时间
info.orgTime = getEntryASCII(entry);
break;

case 0x9004: // 被软件修改的时间
info.digTime = getEntryASCII(entry);
break;

case 0x9102: // 每像素压缩位数
info.compressBit = getEntryRational(entry);
break;

case 0x9201: // 快门速度
info.shutterSpeed = getEntrySRational(entry);
break;

case 0x9202: // 光圈值
info.aperture = getEntryRational(entry);
break;

case 0x9204: // 曝光补偿值
info.exposureBias = getEntrySRational(entry);
break;

case 0x9205: // 最大光圈
info.maxAperture = getEntryRational(entry);
break;

case 0x9207: // 测光模式
info.meteringMode = entry.val;
break;

case 0x9208: // 光源
info.lightSource = entry.val;
break;

case 0x9209: // 闪光灯
info.flash = entry.val;
break;

case 0x920a: // 焦距
info.focalLength = getEntryRational(entry);
break;

/* case 0x927c:
makerNote = getEntryUndefined(entry);
cout << makerNote << endl;
break;*/

case 0xa001: // 色彩空间
info.colorSpace = entry.val;
break;

case 0xa002: // Exif宽度
info.width = entry.val;
break;

case 0xa003: // Exif高度
info.height = entry.val;
break;

case 0xa215: // 曝光指数
info.exposureIndex = getEntryRational(entry);
break;

case 0xa217:
info.sensingMethod = entry.val;
break;
}

}

/// <summary>
/// 获取图片信息
/// </summary>
/// <returns>JPEG信息</returns>
public JPEGInfo GetJPEGInfo()
{
return info;
}

//取指定偏移量的byte数组
private byte[] GetBytes(byte[] bys, int offset, int length)
{
byte[] retBytes = new byte[length];
for (int i = 0; i < length; i++)
{
retBytes[i] = bys[i + offset];
}
return retBytes;
}

//2byte数组转int
private int BytesToInt(byte[] bstr)
{
if (isLittleEndian)
{
return (bstr[1] << 8) + bstr[0];
}
else
{
return (bstr[0] << 8) + bstr[1];
}
}
//4byte数组转int
private int BytesToUint(byte[] bstr)
{
if (isLittleEndian)
{
return (bstr[3] << 24) + (bstr[2] << 16) + (bstr[1] << 8) + bstr[0];
}
else
{
return (bstr[0] << 24) + (bstr[1] << 16) + (bstr[2] << 8) + bstr[3];
}
}

private string getEntryASCII(TIFDEntry entry)
{
string ret = string.Empty;

if (entry.type != 2)
{
return ret;
}
ret = Encoding.ASCII.GetString(stringData, index + entry.val, entry.size);
return ret;
}

private string getEntryUndefined(TIFDEntry entry)
{
string ret = string.Empty;

if (entry.type != 7)
{
return ret;
}

if (entry.size > 4)
{
ret = Encoding.ASCII.GetString(stringData, index + entry.val, entry.size);
}
return ret;
}

private string getEntrySRational(TIFDEntry entry)
{
string ret = string.Empty;
int a = 0;
int b = 1;

if (entry.type != 10)
{
return ret;
}

byte[] data = GetBytes(stringData, entry.val, 4);
a = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
data = GetBytes(stringData, entry.val + 4, 4);
b = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
ret = string.Format("{0}/{1}", a, b);
return ret;
}

private string getEntryRational(TIFDEntry entry)
{
string ret = string.Empty;
int a = 0;
int b = 1;

if (entry.type != 5)
{
return ret;
}

byte[] data = GetBytes(stringData, entry.val, 4);
a = BytesToUint(data);
data = GetBytes(stringData, entry.val + 4, 4);
b = BytesToUint(data);
ret = string.Format("{0}/{1}", a, b);
return ret;
}

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: