|
|
I was very disappointed when I found GDI does not support some GPS tags on windows XP.
Then I found your code perfectly fit to my project.
Thank you very much!
|
|
|
|
|
Hi Simon,
thanks for your article.
A question : how to get the gps coordinates (lat/long), these don't appear in the list ? (my pic contains these data according to a commercial software)
Thanks for your answer,
Domi.
|
|
|
|
|
Hi Domi,
The GPS data can be extracted using any of the ExifTags.Gps... enums:
double[] latitude;
string latitudeRef;
if (reader.GetTagValue(ExifTags.GPSLatitude, out latitude) &&
reader.GetTagValue(ExifTags.GPSLatitudeRef, out latitudeRef))
{
Console.WriteLine("Latitude: {0}°{1}'{2}\" {3}", latitude[0], latitude[1], latitude[2], latitudeRef);
}
Cheers,
Simon
modified 16-Jan-14 16:05pm.
|
|
|
|
|
I will check this ASAP !
thanks for your fast reply & for this info, Simon.
Sincerly,
Domi.
|
|
|
|
|
Hello,
I just want to try your library, but can't find the dll to reference you're talking about (Ok, I'm a noob with .net stuff but want to improve my image gallery media center app). Do I have to compile the dll on my own? I first tried to import your two files (ExifReader.cs and ExifTag.cs) into my project. But it won't rebuild anymore with mainly following errors:
* line 285: ("convertedData.SetValue(converter(buffer), elementCount);") => Error: "Delegate 'MyProject.Code.Exif.ExifReader.ConverterMethod<t>' has some invalid arguments"
* line 452: ("private result = (T) (object) GetArray(tagData, fieldLength, ToUShort);" and all other calls to GetArray) => Error "The type arguments for method 'MyProject.Code.Exif.ExifReader.GetArray<t>(byte[], int, MyProject.Code.Exif.ExifReader.ConverterMethod<t>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Could you please give me a hint with this generics stuff or at least how I can get a compiled dll.
Thanks,
Sebastian
|
|
|
|
|
Hi Sebastian,
The easiest way to get the DLL is to just open ExifLibrary.sln and build it on its own, then take a copy of the compiled DLL from the \bin\Debug or \bin\Release folder. Alternatively, just add the whole ExifLib project to your solution and add a project reference to ExifLib to your existing project.
It's hard for me to tell what your type usage problem is, but I think it'll go away if you follow the above steps.
Cheers,
Simon
|
|
|
|
|
When converting the tagdata to ushort, the bytes are reversed in case of big endian.
For example: orientation 0800 becomes 0080. This results always in zero when converting to ushort.
This is due to a bug in GetTagBytes.
From the spec "4.6.2 IFD Structure"
"In cases where the value fits in 4 bytes, the value itself is recorded.
If the value is smaller than 4 bytes, the value is stored in the 4-byte area starting from the left,
i.e., from the lower end of the byte offset area. For example, in big endian format,
if the type is SHORT and the value is 1, it is recorded as 00010000.H.
So in the method GetTagBytes, we should add
if (dataSize < 4)
{
byte[] data = new byte[dataSize];
Buffer.BlockCopy(tagData, 0, data, 0, dataSize);
return data;
}
|
|
|
|
|
Thanks for the pickup! I've updated GetTagBytes , although I went with Array.Resize instead of Buffer.BlockCopy . Note that Array.Resize doesn't do anything when no size change is required.
Cheers,
Simon
|
|
|
|
|
I modified your speed test method to instead call these respective methods at the bottom of this. And testing with two large (3 and 5mb) reading all the tags yours was around 80% of the speed of GetPropertyItem.
Assuming my tests are relatively accurate, it appears your library should be much faster to get a handful of exif data, but if you wanted to read all the tags from number of images you'd be better off using a different library.
public static void LoadAllTags(ExifReader reader)
{
StringBuilder props = new StringBuilder();
foreach (ushort tagID in Enum.GetValues(typeof(ExifTags)))
{
object val;
if (reader.GetTagValue<object>(tagID, out val))
{
props.Append(val.ToString());
}
}
}
public static void LoadAllTags(Image image)
{
StringBuilder props = new StringBuilder();
foreach (ushort tagID in Enum.GetValues(typeof(ExifTags)))
{
PropertyItem propertyItem = image.GetPropertyItem((int)ExifTags.Model);
string val = BitConverter.ToString(propertyItem.Value, 0);
props.Append(val.ToString());
}
}
|
|
|
|
|
Hi Thymine, and thanks for the feedback.
While my test counts tags read in 5 seconds, yours presumably works out time taken to read all tags. In the end, however, the result is the same - tags per second. The difference you've observed may be one of 2 things:
1. The code was originally written for .NET 2.0 and the imaging libraries in the framework may have improved since then. I certainly haven't tried it with newer frameworks
2. You haven't included the image load time in your test. Since the Image class loads the tags on construction, that's the item you really need to be timing.
If your purpose is to load every tag from the image, you may find it worthwhile altering the "on demand" mode of ExifLib into a serial reader, which would take minimal coding to implement. This would improve efficiency, as it would remove the need for all the stream seek operations performed during tag retrieval.
In the end, for the vast majority of applications, where you don't need to read every EXIF tag from a file, ExifLib will perform much faster than GDI+, which is the purpose for which is was originally written.
All the best,
Simon
|
|
|
|
|
Yes the point I believe I was meaning to make was that for the few cases where someone will want to read every EXIF tag from a file, your library is not optimized.
It sounds like it could be optimized by "altering the "on demand" mode ExifLib into a serial reader" as you mention, so thank you for pointing that out.
When I read only a handful of tags I got similar speed differences as others who have posted (where yours is unbelievably faster), I just wanted to point out that for reading every tag how the code was released is not optimal.
Your library is very useful, and seems like a good way to see how EXIF works.
|
|
|
|
|
Hello!
Thanks for sharing. I ran into a problem trying to extract the ExifTags.FocalLengthIn35mmFilm. I took images from Nikon, Canon, Logitech and some mobiles but no success. ExifTags.Copyright, ExifTags.Make, ExifTags.FocalLength and others are returning correct values while ExifTags.FocalLengthIn35mmFilm comes back empty or 0.0 .
What could I have done wrong? Thanks.
Where do I find a list of the Types for each ExifTags.Tag? That would be helpful.
Which ExifTags.Tag contains the dimensions of the CCD chip?
cheers carin
modified on Sunday, September 5, 2010 11:14 AM
|
|
|
|
|
Hi,
I've just run through some photos from a Canon compact, a Fujifilm bridge, and a Nikon SLR, and they all had the focal length populated (it was a double). Unfortunately data types aren't fixed & may vary between manufacturers, as you can see from some of the previous posts.
There's no sensor size in the EXIF data, but on cameras which populate the FocalLengthIn35mmFilm property, you can work it out from that.
To get a rough list of datatypes, I'd recommend modifying the test app to spit out val.GetType() .
|
|
|
|
|
Thanks for that quick answer. The .FocalLength tag has a value in most cases but so far I wasn't able to find any image with a .FocalLengthIn35mmFilm tag. So I thought I must have done something wrong. But maybe there are not many cams with that tag.
|
|
|
|
|
You're right about the 35mm focal length - only my SLR filled in that field for me.
I was thinking about using EXIF data for a measurement tool using focal length, sensor size & subject distance, but so few cameras filled in the required fields that I gave up!
|
|
|
|
|
Calling it from VB.net values such as flength, make and model are coming back. But ImageWidth is not. The images obviously have this set!
EXIF.GetTagValue(ExifTags.ImageWidth, exif_double) ; 0
EXIF.GetTagValue(ExifTags.ImageWidth, exif_integer) ; 0
EXIF.GetTagValue(ExifTags.ImageWidth, exif_decimal) ; 0D??
EXIF.GetTagValue(ExifTags.ImageWidth, exif_string) ; ""
EXIF.GetTagValue(ExifTags.ImageWidth, exif_single) ; 0.0
EXIF.GetTagValue(ExifTags.ImageWidth, exif_ushort) ; 0
EXIF.GetTagValue(ExifTags.ImageWidth, exif_short) ; 0
EXIF.GetTagValue(ExifTags.ImageWidth, exif_long) ; 0
Infact looking at it, "nothing" is passed back for ImageWidth.
If I run the test app, with pictures from my Nikon D90, most of the response seems a huge lumps of numbers (incorrect breaking down of the EXIF?) and no where in the list of values are the width or height of the images...
I wondering if there's a bug in this routine such that it's not decoding correctly?
I can supply example images, but even images from older cameras refuse to return a ImageWidth.
modified on Thursday, August 19, 2010 12:48 PM
|
|
|
|
|
Hi Neil,
By a strange coincidence, I have a D90 too, so I've had a look at some of my images. Here's what I found:
Although the ImageWidth tag isn't populated, the PixelXDimension tag is, and that's the one containing the width.
Nikon (like many other manufacturers) encodes its proprietary image data inside the MakerNote exif tag, which is the huge lump of number that you're seeing. It's quite likely that this MakerNote is actually another Exif IFD section, so if you want more details from your Nikon, you may be able to alter the library to decode the makernote separately.
Cheers,
Simon
|
|
|
|
|
Thanks... But the next problem I'm getting is the returned data type is changing from photo to photo!
eg: This works for one photo:-
Dim exif_uint32 As UInt32
EXIF.GetTagValue(ExifTags.PixelXDimension, exif_uint32)
...but the next one fails, seemingly as it's now returning a Uint16?
The first photo is from a Canon G7, the second from a Nikon D90.
Without playing around with each individual request, how do I know the datatype of the returned value, and more importantly why isn't it consistant?
|
|
|
|
|
If you have a look at the source, you'll see that the TIFF datatype is encoded into each value. If you're getting back different datatypes, it's due to the camera manufacturer's choice. If you need a more general approach, just use GetTagValue with an Object , then use GetType() or the Is operator to process the result accordingly.
|
|
|
|
|
Don't suppose there's a simple way to return all values/tags in a single call? For example, with ImageMagick you can get a CRLF delimited list of all values as tag=value.
You can easily then just process/obtain the values you need from that string...
Cheers...
|
|
|
|
|
The purpose of the library is speed - reading in all values regardless of whether they're required is a big sap of time - if you do want all fields, I'd suggest modifying the ExifReader class, and in the line after CreateTagIndex() , just read all the values into a Dictionary object.
|
|
|
|
|
I'm utterly new to VB.net so excuse the noobness.
I was using exifworks (The ExifWorks class[^]) but noticed some of the values returned by it seemed odd when compared to other packages (or even Windows properties).
eg: - http://a.imageshack.us/img705/2123/exif1.gif[^]
Note:-
Exposure Time: 0.0435s vs 1/125 sec
Focal Length: 8.5 vs 34 mm
It also seems to return an ISO value of 0 instead of 100!
Anyway, I'd like to try this library in its place. So I've included the DLL into my project, but then what code do I need to add into the VB.net application to include it and call it?
Thanks!
EDIT: Done it:-
[code] Dim EXIF As New ExifLib.ExifReader(fi.FullName)
Dim res As Boolean
Dim datetaken As DateTime
res = EXIF.GetTagValue(ExifTags.DateTimeOriginal, datetaken)
Dim focallen As Double
res = EXIF.GetTagValue(ExifTags.FocalLength, focallen)
Dim expos As Double
res = EXIF.GetTagValue(ExifTags.ExposureTime, expos)[/code]
modified on Thursday, August 19, 2010 10:22 AM
|
|
|
|
|
I read with great interest the article by Simon McKenzie from Oct 2009 called "ExifLib - A Fast Exif Data Extractor for .NET 2.0" but was disappointed when he said that it wouldn't actually work with the subset of the .NET libraries that you have in Silverlight 4. But Simon also explained exactly what one would have to do in order to "bridge the gap", so I set about to do exactly that and he was right - it wasn't overly complicated.
I spent a majority of my time just finding a panoply of different .jpg's from lots of different Digital Cameras, old and new, just to make sure that my modifications not only worked for my pictures, but also for "everything" that I could come up with. So aside from the code changes, in the new project, I've included this set the .jpg's (Big-endian and Little-endian) along with all of the regression testing data that I accumulated to ensure that the Silverlight and non-Silverlight versions of this library produce exactly the same results. You can build either of those versions of the library by just defining a conditional compilation that I made up.
Since I don't see a way to upload anything in this type of response, I'll just put the new version of the project on my company web site, fyi.
see: http://bit.ly/Exif-Silverlight-zip
Of course, I'd like to know if anything finds any flaws in this library as I am in the process of using it for a new Silverlight Application that I will soon write about and publish.
Kevin Pammett, DigitalMediaMagik.com
PammettKevin@gmail.com
|
|
|
|
|
Add a function to test if the file has an exif block rather then throwing an exception.
Great work!
|
|
|
|
|