我正在从文件中加载图像,我想知道在从文件中完全读取图像之前如何验证图像.
string filePath = "image.jpg"; Image newImage = Image.FromFile(filePath);
当image.jpg不是真正的jpg时会出现问题.例如,如果我创建一个空文本文件并将其重命名为image.jpg,则在加载image.jpg时将抛出OutOfMemory Exception.
我正在寻找一个功能,它将在给定图像的流或文件路径的情况下验证图像.
示例函数原型
bool IsValidImage(string fileName); bool IsValidImage(Stream imageStream);
Alex.. 68
这是我的图像检查.我不能依赖文件扩展名,必须自己检查格式.我正在从字节数组加载WPF中的BitmapImages,并且不知道预先格式化.WPF检测格式正常,但没有告诉你BitmapImage对象的图像格式(至少我不知道这个属性).我不想再使用System.Drawing加载图像来检测格式.这个解决方案很快,对我来说很好.
public enum ImageFormat { bmp, jpeg, gif, tiff, png, unknown } public static ImageFormat GetImageFormat(byte[] bytes) { // see http://www.mikekunz.com/image_file_header.html var bmp = Encoding.ASCII.GetBytes("BM"); // BMP var gif = Encoding.ASCII.GetBytes("GIF"); // GIF var png = new byte[] { 137, 80, 78, 71 }; // PNG var tiff = new byte[] { 73, 73, 42 }; // TIFF var tiff2 = new byte[] { 77, 77, 42 }; // TIFF var jpeg = new byte[] { 255, 216, 255, 224 }; // jpeg var jpeg2 = new byte[] { 255, 216, 255, 225 }; // jpeg canon if (bmp.SequenceEqual(bytes.Take(bmp.Length))) return ImageFormat.bmp; if (gif.SequenceEqual(bytes.Take(gif.Length))) return ImageFormat.gif; if (png.SequenceEqual(bytes.Take(png.Length))) return ImageFormat.png; if (tiff.SequenceEqual(bytes.Take(tiff.Length))) return ImageFormat.tiff; if (tiff2.SequenceEqual(bytes.Take(tiff2.Length))) return ImageFormat.tiff; if (jpeg.SequenceEqual(bytes.Take(jpeg.Length))) return ImageFormat.jpeg; if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length))) return ImageFormat.jpeg; return ImageFormat.unknown; }
MusiGenesis.. 33
使用Windows窗体:
bool IsValidImage(string filename) { try { using(Image newImage = Image.FromFile(filename)) {} } catch (OutOfMemoryException ex) { //The file does not have a valid image format. //-or- GDI+ does not support the pixel format of the file return false; } return true; }
否则,如果您使用的是WPF,则可以执行以下操作:
bool IsValidImage(string filename) { try { using(BitmapImage newImage = new BitmapImage(filename)) {} } catch(NotSupportedException) { // System.NotSupportedException: // No imaging component suitable to complete this operation was found. return false; } return true; }
您必须释放创建的图像.否则,当您多次调用此函数时,这会抛出OutOfMemoryException,因为系统资源不足,而不是因为图像损坏导致结果不正确,并且如果在此步骤之后删除图像,则可能会删除好的.
这是我的图像检查.我不能依赖文件扩展名,必须自己检查格式.我正在从字节数组加载WPF中的BitmapImages,并且不知道预先格式化.WPF检测格式正常,但没有告诉你BitmapImage对象的图像格式(至少我不知道这个属性).我不想再使用System.Drawing加载图像来检测格式.这个解决方案很快,对我来说很好.
public enum ImageFormat { bmp, jpeg, gif, tiff, png, unknown } public static ImageFormat GetImageFormat(byte[] bytes) { // see http://www.mikekunz.com/image_file_header.html var bmp = Encoding.ASCII.GetBytes("BM"); // BMP var gif = Encoding.ASCII.GetBytes("GIF"); // GIF var png = new byte[] { 137, 80, 78, 71 }; // PNG var tiff = new byte[] { 73, 73, 42 }; // TIFF var tiff2 = new byte[] { 77, 77, 42 }; // TIFF var jpeg = new byte[] { 255, 216, 255, 224 }; // jpeg var jpeg2 = new byte[] { 255, 216, 255, 225 }; // jpeg canon if (bmp.SequenceEqual(bytes.Take(bmp.Length))) return ImageFormat.bmp; if (gif.SequenceEqual(bytes.Take(gif.Length))) return ImageFormat.gif; if (png.SequenceEqual(bytes.Take(png.Length))) return ImageFormat.png; if (tiff.SequenceEqual(bytes.Take(tiff.Length))) return ImageFormat.tiff; if (tiff2.SequenceEqual(bytes.Take(tiff2.Length))) return ImageFormat.tiff; if (jpeg.SequenceEqual(bytes.Take(jpeg.Length))) return ImageFormat.jpeg; if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length))) return ImageFormat.jpeg; return ImageFormat.unknown; }
使用Windows窗体:
bool IsValidImage(string filename) { try { using(Image newImage = Image.FromFile(filename)) {} } catch (OutOfMemoryException ex) { //The file does not have a valid image format. //-or- GDI+ does not support the pixel format of the file return false; } return true; }
否则,如果您使用的是WPF,则可以执行以下操作:
bool IsValidImage(string filename) { try { using(BitmapImage newImage = new BitmapImage(filename)) {} } catch(NotSupportedException) { // System.NotSupportedException: // No imaging component suitable to complete this operation was found. return false; } return true; }
您必须释放创建的图像.否则,当您多次调用此函数时,这会抛出OutOfMemoryException,因为系统资源不足,而不是因为图像损坏导致结果不正确,并且如果在此步骤之后删除图像,则可能会删除好的.
JPEG没有正式的标头定义,但它们确实有少量可用的元数据.
偏移0(两个字节):JPEG SOI标记(FFD8十六进制)
偏移2(两个字节):图像宽度(以像素为单位)
偏移4(两个字节):图像高度(以像素为单位)
偏移6(字节):组件数量(1 =灰度,3 = RGB)
之后还有其他一些事情,但那些并不重要.
您可以使用二进制流打开文件,并读取此初始数据,并确保OffSet 0为0,OffSet 6为1,2或3.
这至少可以让你更精确.
或者你可以捕获异常并继续前进,但我认为你想挑战:)
好吧,我继续编写了一组函数来解决问题.它首先检查标头,然后尝试在try/catch块中加载图像.它仅检查GIF,BMP,JPG和PNG文件.您可以通过向imageHeaders添加标题来轻松添加更多类型.
static bool IsValidImage(string filePath) { return File.Exists(filePath) && IsValidImage(new FileStream(filePath, FileMode.Open, FileAccess.Read)); } static bool IsValidImage(Stream imageStream) { if(imageStream.Length > 0) { byte[] header = new byte[4]; // Change size if needed. string[] imageHeaders = new[]{ "\xFF\xD8", // JPEG "BM", // BMP "GIF", // GIF Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71})}; // PNG imageStream.Read(header, 0, header.Length); bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0; if (isImageHeader == true) { try { Image.FromStream(imageStream).Dispose(); imageStream.Close(); return true; } catch { } } } imageStream.Close(); return false; }
你可以通过嗅探标题进行粗略的输入.
这意味着您实现的每种文件格式都需要具有可识别的标头...
JPEG:前4个字节是FF D8 FF E0(实际上只有前两个字节可以用于非jfif jpeg,这里有更多信息).
GIF:前6个字节是"GIF87a"或"GIF89a"(此处有更多信息)
PNG:前8个字节是:89 50 4E 47 0D 0A 1A 0A(更多信息在这里)
TIFF:前4个字节是:II42或MM42(此处有更多信息)
等等...您可以找到关于您关心的任何图形格式的标题/格式信息,并根据需要添加到它处理的内容中.这不会做的,是告诉你文件是否是该类型的有效版本,但它会给你一个关于"图像不是图像?"的提示.它仍然可能是一个损坏或不完整的图像,因此在打开时会崩溃,所以仍然需要尝试捕捉.FromFile调用.
这应该可以解决问题 - 您不必从标头中读取原始字节:
using(Image test = Image.FromFile(filePath)) { bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg)); }
当然,您也应该捕获OutOfMemoryException,如果文件根本不是图像,这将节省您的时间.
并且,ImageFormat为GDI +支持的所有其他主要图像类型预先设置了项目.
注意,您必须在ImageFormat对象上使用.Equals()而不是==(它不是枚举),因为operator ==没有重载以调用Equals方法.