Validate image from file in C #

I am loading an image from a file and I want to know how to check the image before it is fully read from the file.

string filePath = "image.jpg"; Image newImage = Image.FromFile(filePath); 

The problem occurs when image.jpg is actually not a jpg. For example, if I create an empty text file and rename it to image.jpg, an OutOfMemory exception will be thrown when image.jpg is loaded.

I am looking for a function that will check the image based on the stream or path to the image file.

Function Prototype Example

 bool IsValidImage(string fileName); bool IsValidImage(Stream imageStream); 
+44
c # file-io image
Oct. 16 '08 at 23:33
source share
12 answers

JPEG does not have a formal header definition, but they do have a small amount of metadata that you can use.

  • Offset 0 (two bytes): JPEG SOI marker (FFD8 hex)
  • Offset 2 (two bytes): image width in pixels
  • Offset 4 (two bytes): image height in pixels
  • Offset 6 (bytes): number of components (1 = shades of gray, 3 = RGB)

After that, there are a few more things, but that doesn't matter.

You can open the file using a binary stream and read this source data and make sure that OffSet 0 is 0 and OffSet 6 is 1.2 or 3.

This will at least give you a bit more accuracy.

Or you can just catch the exception and move on, but I thought you needed a task :)

+21
Oct. 16 '08 at 23:42
source share

here is my image check. I cannot rely on file extensions and I have to check the format myself. I load BitmapImages into WPF from byte arrays and don't know the format in advance. WPF defines the format in order, but does not tell you the image format of BitmapImage objects (at least I don't know about this for this property). And I do not want to upload the image again using System.Drawing just to determine the format. This solution works fast and works great for me.

 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; } 
+50
Feb 25 '12 at 16:59
source share

Using Windows Forms:

 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; } 

Otherwise, if you are using WPF , you can do the following:

 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; } 

You must free the created image. Otherwise, when you call this function many times, it will throw an OutOfMemoryException , because the system has run out of resources, and not because the image is damaged, which leads to an incorrect result, and if you delete images after this step, you can potentially remove the good ones.

+29
Oct. 16 '08 at 23:43
source share

Well, I went ahead and coded a set of functions to solve the problem. First, it checks the header, then tries to load the image into a try / catch block. It only checks GIF, BMP, JPG and PNG files. You can easily add more types by adding a title to 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; } 
+18
Oct 17 '08 at 5:23
source share

You can do rude typing by sniffing the headline.

This means that every file format you implement must have an identification header ...

JPEG: the first 4 bytes are FF D8 FF E0 (in fact, only the first two bytes will do this for non-jfif jpeg, more information here ).

GIF: The first 6 bytes are either "GIF87a" or "GIF89a" (more info here )

PNG: first 8 bytes: 89 50 4E 47 0D 0A 1A 0A (more here )

TIFF: first 4 bytes: II42 or MM42 (more info here )

etc ... you can find header / format information for just about any graphic format you care about and add to what it processes as needed. What it does not do, it will tell you if the file is a valid version of this type, but it will give you a hint about "image not image?". It can still be a damaged or incomplete image and thus fail to open, so you still need to try to catch the .FromFile call.

+11
Oct. 16 '08 at 23:46
source share

This should do the trick - you don't have to read the original bytes from the header:

 using(Image test = Image.FromFile(filePath)) { bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg)); } 

Of course, you should also mask the OutOfMemoryException, which will save you if the file is not an image at all.

And, ImageFormat has predefined elements for all other basic image types supported by GDI +.

Note. You must use .Equals () and not == for ImageFormat objects (this is not an enumeration) because the == operator is not overloaded to call the Equals method.

+6
Jun 29 '10 at 19:53
source share

A method supporting Tiff and Jpeg is also

 private bool IsValidImage(string filename) { Stream imageStream = null; try { imageStream = new FileStream(filename, FileMode.Open); if (imageStream.Length > 0) { byte[] header = new byte[30]; // Change size if needed. string[] imageHeaders = new[] { "BM", // BMP "GIF", // GIF Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71}),// PNG "MM\x00\x2a", // TIFF "II\x2a\x00" // TIFF }; imageStream.Read(header, 0, header.Length); bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0; if (imageStream != null) { imageStream.Close(); imageStream.Dispose(); imageStream = null; } if (isImageHeader == false) { //Verify if is jpeg using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open))) { UInt16 soi = br.ReadUInt16(); // Start of Image (SOI) marker (FFD8) UInt16 jfif = br.ReadUInt16(); // JFIF marker return soi == 0xd8ff && (jfif == 0xe0ff || jfif == 57855); } } return isImageHeader; } return false; } catch { return false; } finally { if (imageStream != null) { imageStream.Close(); imageStream.Dispose(); } } } 
+3
Mar 11 '10 at 13:00
source share

I would create a method like:

 Image openImage(string filename); 

in which I handle the exception. If the return value is Null, an invalid file name / type exists.

+1
Oct. 16 '08 at 23:38
source share

I took the Semicolon answer and converted to VB:

 Private Function IsValidImage(imageStream As System.IO.Stream) As Boolean If (imageStream.Length = 0) Then isvalidimage = False Exit Function End If Dim pngByte() As Byte = New Byte() {137, 80, 78, 71} Dim pngHeader As String = System.Text.Encoding.ASCII.GetString(pngByte) Dim jpgByte() As Byte = New Byte() {255, 216} Dim jpgHeader As String = System.Text.Encoding.ASCII.GetString(jpgByte) Dim bmpHeader As String = "BM" Dim gifHeader As String = "GIF" Dim header(3) As Byte Dim imageHeaders As String() = New String() {jpgHeader, bmpHeader, gifHeader, pngHeader} imageStream.Read(header, 0, header.Length) Dim isImageHeader As Boolean = imageHeaders.Count(Function(str) System.Text.Encoding.ASCII.GetString(header).StartsWith(str)) > 0 If (isImageHeader) Then Try System.Drawing.Image.FromStream(imageStream).Dispose() imageStream.Close() IsValidImage = True Exit Function Catch ex As Exception System.Diagnostics.Debug.WriteLine("Not an image") End Try Else System.Diagnostics.Debug.WriteLine("Not an image") End If imageStream.Close() IsValidImage = False End Function 
+1
09 feb. '12 at 17:37
source share

You can read the first few bytes of Stream and compare them with the header bytes for JPEG.

0
Oct. 16 '08 at 23:43
source share

in case you need data read for other operations and / or for other types of files (for example, PSD), then using the Image.FromStream function is not necessarily a good idea.

0
Jan 14 '10 at 11:17
source share

I noticed a couple of problems with all of the above functions. First of all, Image.FromFile opens the given image, and then causes an open file error, whoever wants to open this image file for any reason. Even the application itself - so I switched using Image.FromStream.

After you change the api, the type of exception will change from OutOfMemoryException to ArgumentException for some reasons I don't understand. (Perhaps a .net network error?)

In addition, if .net adds more image file formats than it currently does, we will check by function - it makes sense to try downloading the image first, if only then failed, only then report an error.

So my code looks like this:

 try { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { Image im = Image.FromStream(stream); // Do something with image if needed. } } catch (ArgumentException) { if( !IsValidImageFormat(path) ) return SetLastError("File '" + fileName + "' is not a valid image"); throw; } 

Where:

 /// <summary> /// Check if we have valid Image file format. /// </summary> /// <param name="path"></param> /// <returns>true if it image file</returns> public static bool IsValidImageFormat( String path ) { using ( FileStream fs = File.OpenRead(path) ) { byte[] header = new byte[10]; fs.Read(header, 0, 10); foreach ( var pattern in new byte[][] { Encoding.ASCII.GetBytes("BM"), Encoding.ASCII.GetBytes("GIF"), new byte[] { 137, 80, 78, 71 }, // PNG new byte[] { 73, 73, 42 }, // TIFF new byte[] { 77, 77, 42 }, // TIFF new byte[] { 255, 216, 255, 224 }, // jpeg new byte[] { 255, 216, 255, 225 } // jpeg canon } ) { if (pattern.SequenceEqual(header.Take(pattern.Length))) return true; } } return false; } //IsValidImageFormat 
0
Dec 04 '15 at 11:33
source share



All Articles