[patch] Read ISO data from Nikon Makernote, Fallback ApertureValue to FNumber



Attached is a patch which adds a couple of things to ExifUtils:

  * Fallback to EXIF FNumber when ApertureValue is not present

  * Read the ISO-speed exposure information from the Makernote field
of certain models
   of Nikon cameras (tested with my GF's D70) as a fallback for when
the data is not
   available in the normal EXIF spot.

This is my Hello World in C#.  I hadn't touched C#, image file
formats, EXIF or any of that before a couple of days ago, so take this
code with a pinch of salt, all I can say is it seems to work for me. 
I tried to make the code look as much as possible like the code around
it, but I'm sure there are C# idioms that I haven't picked up,
guidance would be much appreciated.  In particular, I assume there's a
better solution than copying the makernote array, but everything I
thought of involved an unsafe block, so I assume I just haven't seen
the light yet.

One thing that's kind of baffling is that the info I had said the
maker note was an array of ushorts, and an exif dump confirmed this,
but I had to use GetDataUInt to get the data.  I suspect there's a bug
with shorts and endian translation, but maybe I was just being
boneheaded somewhere.  Can someone please confirm this for me?
--- f-spot-0.1.3/src/ExifUtils.cs	2005-09-29 00:56:25.000000000 -0700
+++ f-spot-0.1.3.hacked/src/ExifUtils.cs	2005-10-04 22:27:24.000000000 -0700
@@ -19,32 +19,35 @@
 	{
 		Exif.ExifData exif_data = new Exif.ExifData (path);
 
-#if UNSED_CODE
 		MakerType maker = MakerType.Unknown;
-		string maker_tag_value = exif_data.LookupString (ExifTag.Make);
+		string maker_tag_value = exif_data.LookupFirstValue (Exif.Tag.Make);
 		if (maker_tag_value != null) {
-			if (maker_tag_value.ToLower () == "nikon")
+		        if (maker_tag_value.ToLower () == "nikon" ||
+			    maker_tag_value.ToLower () == "nikon corporation") {
 				maker = MakerType.Nikon;
+		        }
 			else if (maker_tag_value.ToLower () == "canon")
 				maker = MakerType.Canon;
 		}
-#endif
 
 		ExposureInfo info = new ExposureInfo ();
 		info.ApertureValue = exif_data.LookupFirstValue (Exif.Tag.ApertureValue);
+		if(info.ApertureValue == null) info.ApertureValue = exif_data.LookupFirstValue (Exif.Tag.FNumber);
 		info.ExposureTime = exif_data.LookupFirstValue (Exif.Tag.ExposureTime);
 		info.DateTime = exif_data.LookupFirstValue (Exif.Tag.DateTimeOriginal);
 		info.IsoSpeed = exif_data.LookupFirstValue (Exif.Tag.ISOSpeedRatings);
 
-// FIXME not sure why, this doesn't work.
-#if BROKEN_CODE
 		// Use the maker note to figure out the ISO speed rating if it's not in the standard spot.
-		if (info.IsoSpeed == null && exif_data.LookupData (ExifTag.MakerNote) != null) {
+		Exif.ExifEntry maker_note = exif_data.LookupFirst (Exif.Tag.MakerNote);
+		if (info.IsoSpeed == null && maker_note != null) {
+			byte [] maker_note_data = maker_note.Data;
 			switch (maker) {
+// FIXME not sure why, this doesn't work.
+//   may need JFIF "Exif\0" bumper -- see Nikon type 3 below
+#if BROKEN_CODE
 			case MakerType.Canon:
-				byte [] maker_note = exif_data.LookupData (ExifTag.MakerNote);
-				ExifData maker_ifd = new ExifData (maker_note_copy, (uint) maker_note_copy.Length);
-				byte [] data = maker_ifd.LookupData ((ExifTag) 0x1);
+				ExifData maker_ifd = new Exif.ExifData (maker_note_data, (uint) maker_note_data.Length);
+				byte [] data = maker_ifd.LookupData ((Exif.Tag) 0x1);
 
 				if (data.Length > 0x10) {
 					switch (data [0x10]) {
@@ -59,12 +62,42 @@
 					}
 				}
 				break;
+#endif
+			case MakerType.Nikon:
+				// see: http://www.ozhiker.com/electronics/pjmt/jpeg_info/nikon_mn.html
+				if(maker_note_data.Length >= 6
+				   && System.Text.Encoding.ASCII.GetString (maker_note_data, 0, 6) == "Nikon\0") {
+					if (maker_note_data[6] == 0x1 && maker_note_data[7] == 0x0) {
+						// Nikon Type 1 -- Not Implemented
+					}
 
+					if (maker_note_data[6] == 0x2
+					    && (maker_note_data[7] == 0x0 || maker_note_data[7] == 0x10)) {
+						// Nikon Type 3
+						const int offset = 10; // nikon header is 10 bytes
+
+						byte [] maker_note_copy = new byte [maker_note_data.Length-offset+6];
+						// Apparently libexif doesn't recognize TIFF exif data
+						// because it lacks the JFIF "Exif\0" bumper
+						Array.Copy (System.Text.Encoding.ASCII.GetBytes ("Exif\0\0"), 0, maker_note_copy, 0, 6);
+						Array.Copy (maker_note_data, offset, maker_note_copy, 6, maker_note_data.Length-offset);
+						Exif.ExifData maker_ifd = new Exif.ExifData (maker_note_copy, (uint) maker_note_copy.Length);
+
+						Exif.ExifEntry iso_entry = maker_ifd.LookupFirst ((Exif.Tag) 0x2);
+						if (iso_entry != null) {
+							/* type should be ushort, but doesn't seem to work (?) */
+							uint [] iso_entry_data = iso_entry.GetDataUInt ();
+							if (iso_entry_data != null && iso_entry_data.Length > 0x1)
+								info.IsoSpeed = iso_entry_data[0x1].ToString();
+						}
+					}
+				}
+				// Nikon Type 2 makernotes jump straight to TIFF data -- Not Implemented
+				break;
 			// FIXME: Add support for other MakerNotes, see:
 			// http://www.fifi.org/doc/jhead/exif-e.html#APP1
 			} 
 		}
-#endif
 
 		return info;
 	}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]