C# How can I test a file is a jpeg? - c#

Using C# how can I test a file is a jpeg? Should I check for a .jpg extension?
Thanks

Several options:
You can check for the file extension:
static bool HasJpegExtension(string filename)
{
// add other possible extensions here
return Path.GetExtension(filename).Equals(".jpg", StringComparison.InvariantCultureIgnoreCase)
|| Path.GetExtension(filename).Equals(".jpeg", StringComparison.InvariantCultureIgnoreCase);
}
or check for the correct magic number in the header of the file:
static bool HasJpegHeader(string filename)
{
using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read)))
{
UInt16 soi = br.ReadUInt16(); // Start of Image (SOI) marker (FFD8)
UInt16 marker = br.ReadUInt16(); // JFIF marker (FFE0) or EXIF marker(FFE1)
return soi == 0xd8ff && (marker & 0xe0ff) == 0xe0ff;
}
}
Another option would be to load the image and check for the correct type. However, this is less efficient (unless you are going to load the image anyway) but will probably give you the most reliable result (Be aware of the additional cost of loading and decompression as well as possible exception handling):
static bool IsJpegImage(string filename)
{
try
{
using (System.Drawing.Image img = System.Drawing.Image.FromFile(filename))
{
// Two image formats can be compared using the Equals method
// See http://msdn.microsoft.com/en-us/library/system.drawing.imaging.imageformat.aspx
//
return img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (OutOfMemoryException)
{
// Image.FromFile throws an OutOfMemoryException
// if the file does not have a valid image format or
// GDI+ does not support the pixel format of the file.
//
return false;
}
}

Open the file as a stream and look for the magic number for JPEG.
JPEG image files begin with FF D8 and
end with FF D9. JPEG/JFIF files
contain the ASCII code for 'JFIF' (4A
46 49 46) as a null terminated string.
JPEG/Exif files contain the ASCII code
for 'Exif' (45 78 69 66) also as a
null terminated string

OMG, So many of these code examples are wrong, wrong wrong.
EXIF files have a marker of 0xff*e1*, JFIF files have a marker of 0xff*e0*. So all code that relies on 0xffe0 to detect a JPEG file will miss all EXIF files.
Here's a version that will detect both, and can easily be altered to return only for JFIF or only for EXIF. (Useful when trying to recover your iPhone pictures, for example).
public static bool HasJpegHeader(string filename)
{
try
{
// 0000000: ffd8 ffe0 0010 4a46 4946 0001 0101 0048 ......JFIF.....H
// 0000000: ffd8 ffe1 14f8 4578 6966 0000 4d4d 002a ......Exif..MM.*
using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.ReadWrite)))
{
UInt16 soi = br.ReadUInt16(); // Start of Image (SOI) marker (FFD8)
UInt16 marker = br.ReadUInt16(); // JFIF marker (FFE0) EXIF marker (FFE1)
UInt16 markerSize = br.ReadUInt16(); // size of marker data (incl. marker)
UInt32 four = br.ReadUInt32(); // JFIF 0x4649464a or Exif 0x66697845
Boolean isJpeg = soi == 0xd8ff && (marker & 0xe0ff) == 0xe0ff;
Boolean isExif = isJpeg && four == 0x66697845;
Boolean isJfif = isJpeg && four == 0x4649464a;
if (isJpeg)
{
if (isExif)
Console.WriteLine("EXIF: {0}", filename);
else if (isJfif)
Console.WriteLine("JFIF: {0}", filename);
else
Console.WriteLine("JPEG: {0}", filename);
}
return isJpeg;
return isJfif;
return isExif;
}
}
catch
{
return false;
}
}

You could try loading the file into an Image and then check the format
Image img = Image.FromFile(filePath);
bool isBitmap = img.RawFormat.Equals(ImageFormat.Jpeg);
Alternatively you could open the file and check the header to get the type

You could find documentation on the jpeg file format, specifically the header information. Then try to read this information from the file and compare it to the expected jpeg header bytes.

Read the header bytes. This article contains info on several common image formats, including JPEG:
Using Image File Headers To Verify Image Format
JPEG Header Information

Once you have the extension you could use a regular expression to validate it.
^.*\.(jpg|JPG)$

This will loop through each file in the current directory and will output if any found files with JPG or JPEG extension are Jpeg images.
foreach (FileInfo f in new DirectoryInfo(".").GetFiles())
{
if (f.Extension.ToUpperInvariant() == ".JPG"
|| f.Extension.ToUpperInvariant() == ".JPEG")
{
Image image = Image.FromFile(f.FullName);
if (image.RawFormat == ImageFormat.Jpeg)
{
Console.WriteLine(f.FullName + " is a Jpeg image");
}
}
}

Depending on the context in which you're looking at this file, you need to remember that you can't open the file until the user tells you to open it.
(The link is to a Raymond Chen blog entry.)

The code here:
http://mark.michaelis.net/Blog/RetrievingMetaDataFromJPEGFilesUsingC.aspx
Shows you how to get the Meta Data. I guess that would throw an exception if your image wasn't a valid JPEG.

Checking the file extension is not enough as the filename might be lying.
A quick and dirty way is to try and load the image using the Image class and catching any exceptions:
Image image = Image.FromFile(#"c:\temp\test.jpg");
This isn't ideal as you could get any kind of exception, such as OutOfMemoryException, FileNotFoundException, etc. etc.
The most thorough way is to treat the file as binary and ensure the header matches the JPG format. I'm sure it's described somewhere.

The best way would to try and create an image from it using the Drawing.Bitmap (string) constructor and see if it fails to do so or throws an exception. The problem with some of the answers are this: firstly, the extension is purely arbitrary, it could be jpg, jpeg, jpe, bob, tim, whatever. Secondly, just using the header isn't enough to be 100% sure. It can definitely determine that a file isn't a jpeg but can't guarantee that a file is a jpeg, an arbitrary binary file could have the same byte sequence at the start.

Just take the media type of file and verify:
private bool isJpeg()
{
string p = currFile.Headers.ContentType.MediaType;
return p.ToLower().Equals("image/jpeg") || p.ToLower().Equals("image/pjpeg") || p.ToLower().Equals("image/png");
}

after check extention of file read first four byte of image and two last byte of image like this, do it for two last byte for value 255 , 217
for other file can do it
Validate image from file in C#
http://www.garykessler.net/library/file_sigs.html
// after check extention of file
byte[] ValidFileSignture = new byte[] { 255, 216, 255, 224 };
byte[] bufferforCheck = new byte[ValidFileSignture.Length];
Stream _inputStream = file.InputStream;
byte[] bufferforCheck1 = new byte[] { 255, 216, 255, 224 };
_inputStream.Read(bufferforCheck, 0, ValidFileSignture.Length);
if (!Enumerable.SequenceEqual(bufferforCheck, ValidFileSignture))
{
//file OK
}

System.Web.MimeMapping.GetMimeMapping(filename).StartsWith("image/");
MimeMapping.GetMimeMapping produces these results:
file.jpg: image/jpeg
file.gif: image/gif
file.jpeg: image/jpeg
file.png: image/png
file.bmp: image/bmp
file.tiff: image/tiff
file.svg: application/octet-stream

You can use the Path.GetExtension Method.

Related

How to check if image file is valid?

I want to check if the file is an actual .png/.jpg file that can be displayed (for example not being a .txt file with a changed file extension to .png). I also want to check if the width and height of the image is in a valid range. How can I achieve this?
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public async Task<IActionResult> Create(IFormFile image)
{
// validate image
}
First of all, don't try to use System.Drawing in .NET Core applications. It's deprecated and works only on Windows anyway. The MSDN docs themselves suggest using ImageSharp or SkiaSharp instead.
Image files start with bytes that identify the file format. You'll have to read at least some of the file's contents to read image metadata like the image size, resolution etc. You can use ImageSharp's Identify method to read only the format and image properties, without loading the entire image.
You can read an uploaded file's contents using IFormFile.OpenReadStream.
using var stream=image.OpenReadStream();
try
{
var imageInfo=Image.Identify(stream, out var format);
if(imageInfo!=null)
{
var formatName=format.Name;
var width=imageInfo.Width;
var height=imageInfo.Height;
}
}
catch(InvalidImageContentException exc)
{
//Invalid content ?
}
The format parameter is an IImageFormat value that contains information about the image format, including its name and mime types.
The IImageInfo object returned contains the image dimensions, pixel type, resolution etc.
The method documentation explains that the return value will be null if no suitable decoder is found:
The IImageInfo or null if a suitable info detector is not found.
But an exception will be thrown if the content is invalid:
InvalidImageContentException Image contains invalid content.
Without testing this, I assume that a text file will result in a null but a file with just a GIF header without valid content will result in an exception.
You can use ImageSharp to resize the image or convert it to another format. In that case it's not enough to just load the metadata. You can use Load to load the image from the stream, detect its format and then manipulate it.
using var stream=image.OpenReadStream();
var image=Image.Load(stream, out var format);
var formatName=format.Name;
if (notOk(formatName,image.Height,image.Width))
{
using var outStream=new MemoryStream();
image.Mutate(x => x.Resize(desiredWidth, desiredHeight));
image.SaveAsPng(outStream);
outStream.Position=0;
//Store the contents of `outStream`
}
Check the file for a known header. (Info from link also mentioned in this answer)
The first eight bytes of a PNG file always contain the following (decimal) values: 137 80 78 71 13 10 26 10
You can also check Path.GetExtension Method
Here's a simple sample:
public static readonly List<string> ImageExtensions = new List<string> { ".JPG", ".JPE", ".BMP", ".GIF", ".PNG" };
private void button_Click(object sender, RoutedEventArgs e)
{
var folder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var files = Directory.GetFiles(folder);
foreach(var f in files)
{
if (ImageExtensions.Contains(Path.GetExtension(f).ToUpperInvariant()))
{
// process image
}
}
}

Exporting a 3D double array to a tiff image stack in C# [duplicate]

I load a multiframe TIFF from a Stream in my C# application, and then save it using the Image.Save method. However, this only saves the TIFF with the first frame - how can I get it to save a multiframe tiff?
Since you don't provide any detailed information... just some general tips:
Multi-Frame TIFF are very complex files - for example every frame can have a different encoding... a single Bitmap/Image can't hold all frames with all relevant information (like encoding and similar) of such a file, only one at a time.
For loading you need to set parameter which tells the class which frame to load, otherwise it just loads the first... for some code see here.
Similar problems arise when saving multi-frame TIFFs - here you need to work with EncoderParameters and use SaveAdd etc. - for some working code see here.
Since the link to code provided by #Yahia is broken I have decided to post the code I ended up using.
In my case, the multi-frame TIFF already exists and all I need to do is to load the image, rotate by EXIF (if necessary) and save. I won't post the EXIF rotation code here, since it does not relate to this question.
using (Image img = System.Drawing.Image.FromStream(sourceStream))
{
using (FileStream fileStream = System.IO.File.Create(filePath))
{
int pages = img.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
if (pages == 1)
{
img.Save(fileStream, img.RawFormat); // if there is just one page, just save the file
}
else
{
var encoder = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == fileInfo.MediaType);
var encoderParams = new System.Drawing.Imaging.EncoderParameters(1);
encoderParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(System.Drawing.Imaging.EncoderValue.MultiFrame));
img.Save(fileStream, encoder, encoderParams); // save the first image with MultiFrame parameter
for (int f = 1; f < pages; f++)
{
img.SelectActiveFrame(FrameDimension.Page, f); // select active page (System.Drawing.Image.FromStream loads the first one by default)
encoderParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(System.Drawing.Imaging.EncoderValue.FrameDimensionPage));
img.SaveAdd(img, encoderParams); // save add with FrameDimensionPage parameter
}
}
}
}
sourceStream is a System.IO.MemoryStream which holds the byte array of the file content
filePath is absolute path to cache directory (something like 'C:/Cache/multiframe.tiff')
fileInfo is a model holding the actual byte array, fileName, mediaType and other data

How do I read and write XMP metadata in C#?

I have this method for resizing images, and I have managed to input all of the metadata into the new image except for the XMP data. Now, I can only find topics on how manage the XMP part in C++ but I need it in C#. The closest I've gotten is the xmp-sharp project which is based on some old port of Adobe's SDK, but I can't get that working for me. The MetaDataExtractor project gives me the same results - that is, file format/encoding not supported. I've tried this with .jpg, .png and .tif files.
Is there no good way of reading and writing XMP in C#?
Here is my code if it's of any help (omitting all irrelevant parts):
public Task<Stream> Resize(Size size, Stream image)
{
using (var bitmap = Image.FromStream(image))
{
var newSize = new Size(size.Width, size.Height);
var ms = new MemoryStream();
using (var bmPhoto = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format24bppRgb))
{
// This saves all metadata except XMP
foreach (var id in bitmap.PropertyIdList)
bmPhoto.SetPropertyItem(bitmap.GetPropertyItem(id));
// Trying to use xmp-sharp for the XMP part
try
{
IXmpMeta xmp = XmpMetaFactory.Parse(image);
}
catch (XmpException e)
{
// Here, I always get "Unsupported Encoding, XML parsing failure"
}
// Trying to use MetadataExtractor for the XMP part
try
{
var xmpDirs = ImageMetadataReader.ReadMetadata(image).Where(d => d.Name == "XMP");
}
catch (Exception e)
{
// Here, I always get "File format is not supported"
}
// more code to modify image and save to stream
}
ms.Position = 0;
return Task.FromResult<Stream>(ms);
}
}
The reason you get "File format is not supported" is because you already consumed the image from the stream when you called Image.FromStream(image) in the first few lines.
If you don't do that, you should find that you can read out the XMP just fine.
var xmp = ImageMetadataReader.ReadMetadata(stream).OfType<XmpDirectory().FirstOrDefault();
If your stream is seekable, you might be able to seek back to the origin (using the Seek method, or by setting Position to zero.)

Losing image quality in c# using Image class (reduces amount of colors)

I have a c# program that opens a .tif image and later offers the option to save it. However, there is always a drop in quality when saving the image.
(EDIT:I passed some parameters while saving the image so that the quality is at 100 % and there is no compression, but the number of actual unique colors go from 254 to 16, even though the image properties show 8bpp)
(EDIT2: The image in question is a grayscale image at 8 bits per pixel - 256 colors/shades of gray - This doesn't happen with a 24 bits per pixel color image that I tested where all the colors are retained. I am starting to think that the image class may only support 16 shades of gray)
How do I avoid this?
Here's the code for opening the image:
public Image imageImport()
{
Stream myStream = null;
OpenFileDialog openTifDialog = new OpenFileDialog();
openTifDialog.Title = "Open Desired Image";
openTifDialog.InitialDirectory = #"c:\";
openTifDialog.Filter = "Tiff only (*.tif)|*.tif";
openTifDialog.FilterIndex = 1;
openTifDialog.RestoreDirectory = true;
if (openTifDialog.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = openTifDialog.OpenFile()) != null)
{
using (myStream)
{
String tifFileName= openTifDialog.FileName;
imgLocation = tifFileName;
Bitmap tifFile = new Bitmap(tifFileName);
return tifFile;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
return null;
}
This is the way I save the image:
private void saveImage(Image img)
{
SaveFileDialog sf = new SaveFileDialog();
sf.Title = "Select File Location";
sf.Filter = " bmp (*.bmp)|*.bmp|jpeg (*.jpg)|*.jpg|tiff (*.tif)|*.tif";
sf.FilterIndex = 4;
sf.RestoreDirectory = true;
sf.ShowDialog();
// If the file name is not an empty string open it for saving.
if (sf.FileName != "")
{
// Saves the Image via a FileStream created by the OpenFile method.
System.IO.FileStream fs =
(System.IO.FileStream)sf.OpenFile();
// Saves the Image in the appropriate ImageFormat based upon the
// File type selected in the dialog box.
// NOTE that the FilterIndex property is one-based.
switch (sf.FilterIndex)
{
case 1:
img.Save(fs,
System.Drawing.Imaging.ImageFormat.Bmp);
break;
case 2:
img.Save(fs,
System.Drawing.Imaging.ImageFormat.Jpeg);
break;
case 3://EDITED -STILL DOES NOT RESOLVE THE ISSUE
ImageCodecInfo codecInfo = ImageClass.GetEncoderInfo(ImageFormat.Tiff);
EncoderParameters parameters = new EncoderParameters(2);
parameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality,100L);
parameters.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionNone);
img.Save(fs,codecInfo, parameters);
break;
}
fs.Close();
}
}
Even if I don't resize or change the image in any ways, I experience a loss in quality. any advice?
System.Drawing has poor support for 8-bit images. When converting from 24 or 32-bit images to 8-bit; it'll always use a fixed default color palette. That default color palette only contains 16 shades of grey, the other entries are various colors.
Do you have the same problem when saving as '.bmp'? If yes, then the conversion to the 8-bit format already happened earlier, you'll have to figure out where your program does that and fix the issue there.
If it's only the tiff encoder that converts to 8-bit, you'll have to do the 8-bit conversion in a separate step first. Create an 8-bit image, fill Image.Palette with a gray-scale palette, and then copy the bitmap data over.
But System.Drawing has poor support for 8-bit images, and several methods (e.g. SetPixel) will just throw InvalidOperationException when dealing with such images. You will probably have to use unsafe code (with LockBits etc.) to copy the bitmap data. If I were you, I'd look if there are alternative graphics libraries you could use.
I had issues with using the .NET libraries to find good balances of image quality and size. I gave up rolling my own and tried out a few imaging libraries. I found http://imageresizing.net/ to produce consistently good results, much better than I was able to do.
Just throwing that out there as a plan B in case the roll your own method doesn't wind up working well on a consistent basis for you.
Image.Save by default uses a quality setting of 75%. You could try using one of the other overloads of the method that allows you to specify quality setting parameters. See this question.
Only one suggestion really....when loading the Image you use new Bitmap(fileName)... Rather than using Bitmap have you considered using
Image tiffImage = Image.FromFile(tiffFileName, true);
The true tells it to use "embedded color management", and using Image instead of Bitmap avoids any image casting that might be occurring behind the scenes.

difference between Image.Save and FileStream.Write() in c#

I have to read image binary from database and save this image binary as a Tiff image on filesystem. I was using the following code
private static bool SavePatientChartImageFileStream(byte[] ImageBytes, string ImageFilePath, string IMAGE_NAME)
{
bool success = false;
try
{
using (FileStream str = new FileStream(Path.Combine(ImageFilePath, IMAGE_NAME), FileMode.Create))
{
str.Write(ImageBytes, 0, Convert.ToInt32(ImageBytes.Length));
success = true;
}
}
catch (Exception ex)
{
success = false;
}
return success;
}
Since these image binaries are being transferred through merge replication, sometimes it happens that image binary is not completely transferred and we are sending request to fetch Image Binary with a nolock hint. This returns in ImageBytes having 1 byte data and it saves it as a 0 kb corrupted tiff image.
I have changed the above code to :-
private static bool SavePatientChartImage(byte[] ImageBytes, string ImageFilePath, string IMAGE_NAME)
{
bool success = false;
System.Drawing.Image newImage;
try
{
using (MemoryStream stream = new MemoryStream(ImageBytes))
{
using (newImage = System.Drawing.Image.FromStream(stream))
{
newImage.Save(Path.Combine(ImageFilePath, IMAGE_NAME));
success = true;
}
}
}
catch (Exception ex)
{
success = false;
}
return success;
}
In this case if ImageBytes is of 1 byte or incomplete, it won't save image and will return success as false.
I cannot remove NOLOCK as we are having extreme locking.
The second code is slower as compared to first one. I tried for 500 images. there was a difference of 5 seconds.
I couldn't understand the difference between these 2 pieces of code and which code to use when. Please help me understand.
In the first version of the code, you are essentially taking a bunch of bytes and writing them to the filesystem. There's no verification of a valid TIFF file because the code neither knows nor cares it's a TIFF file. It's just a bunch of bytes without any business logic attached.
In the second code, you're taking the bytes, wrapping them in a MemoryStream, and then feeding them into an Image object, which parses the entire file and reads it as a TIFF file. This give you the validation you need - it can tell when the data is invalid - but you're essentially going over the entire file twice, once to read it in (with additional overhead for parsing) and once to write it to disk.
Assuming you don't need any validation that requires deep parsing of the image file (# of colors, image dimensions, etc) you can skip this overhead by simply checking if the byte[] ImageBytes is of length 1 (or find any other good indicator of corrupt data) and skip writing if it doesn't match. In effect, do your own validation, rather than using the Image class as a validator.
I think the main difference between the two is that in the second code you are writing the source byte[] to a MemoryStream object first which would mean that if the data becomes essentially independent of the database. So, you could potentially incorporate this MemoryStream into the first code to achieve the same results.

Categories