Add your own alternative version
Stats
382.2K views 17.2K downloads 176 bookmarked
Posted
13 Aug 2005

Comments and Discussions


Excellent work man!
was working hard for such a class of my own for my software... but u got it right!! great help!





When Im trying to get the exposure time it returns zero, I expected it to return 1/180 sec or something like that. I have uploaded a test picture, and it can be found here: http://qaz.dk/nydraabe.jpg
I also tried to get shutter speed but i returns null.





I uncomented example code and set path to my own jpg.
When THIS LINE
er.setTag(0x320, "http://www.beautifulpakistan.com");
was calling, exception System.ArgumentException was thrown in this function.
private static PropertyItem CreatePropertyItem(short type, int tag, int len, byte[] value)
{
PropertyItem item;
Assembly assembly = Assembly.GetExecutingAssembly();
Stream emptyBitmapStream = assembly.GetManifestResourceStream("EXIFextractor.decoy.jpg");
System.Drawing.Image empty = System.Drawing.Image.FromStream(emptyBitmapStream);
item = empty.PropertyItems[0];
item.Type = type;
item.Len = len;
item.Id = tag;
item.Value = new byte[value.Length];
value.CopyTo(item.Value, 0);
return item;
}
emptyBitmapStream was null.
What is wrong?





If a picture has 1/125 shutter speed, your code wil return 1/125.37 instead.





I cannot find the way of doing that because image.PropertyItems doesn't exist in CF.
Thanks





For those of you who require the GPS info. In the EXIFextractor.cs after
<br />
//5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the//denominator.,<br />
else if( p.Type == 0x5 )<br />
{<br />
I inserted this:
<br />
…<br />
else if( p.Type == 0x5 )<br />
{<br />
//My code starts here<br />
if (p.Id == 0x0004)<br />
{<br />
//lat<br />
// rational<br />
byte[] deg = new byte[4];<br />
byte[] degdiv = new byte[4];<br />
byte[] min = new byte[4];<br />
byte[] mindiv = new byte[4];<br />
byte[] sec = new byte[4];<br />
byte[] secdiv = new byte[4];<br />
Array.Copy(p.Value, 0, deg, 0, 4);<br />
Array.Copy(p.Value, 4, degdiv, 0, 4);<br />
Array.Copy(p.Value, 8, min, 0, 4);<br />
Array.Copy(p.Value, 12, mindiv, 0, 4);<br />
Array.Copy(p.Value, 16, sec, 0, 4);<br />
Array.Copy(p.Value, 20, secdiv, 0, 4);<br />
double a = convertToInt32U(deg);<br />
double b = convertToInt32U(degdiv);<br />
double c = convertToInt32U(min);<br />
double d = convertToInt32U(mindiv);<br />
double e = convertToInt32U(sec);<br />
double f = convertToInt32U(secdiv);<br />
double o =((a/b) + ((c/b)/60) + ((e/f)/3600));<br />
v = o.ToString();<br />
// Rational r = new Rational(a, b);<br />
}<br />
else if (p.Id == 0x0002)<br />
{<br />
//long<br />
byte[] deg = new byte[4];<br />
byte[] degdiv = new byte[4];<br />
byte[] min = new byte[4];<br />
byte[] mindiv = new byte[4];<br />
byte[] sec = new byte[4];<br />
byte[] secdiv = new byte[4];<br />
Array.Copy(p.Value, 0, deg, 0, 4);<br />
Array.Copy(p.Value, 4, degdiv, 0, 4);<br />
Array.Copy(p.Value, 8, min, 0, 4);<br />
Array.Copy(p.Value, 12, mindiv, 0, 4);<br />
Array.Copy(p.Value, 16, sec, 0, 4);<br />
Array.Copy(p.Value, 20, secdiv, 0, 4);<br />
double a = convertToInt32U(deg);<br />
double b = convertToInt32U(degdiv);<br />
double c = convertToInt32U(min);<br />
double d = convertToInt32U(mindiv);<br />
double e = convertToInt32U(sec);<br />
double f = convertToInt32U(secdiv);<br />
double o = ((a / b) + ((c / b) / 60) + ((e / f) / 3600));<br />
v = o.ToString();<br />
}<br />
//My Code ends here<br />
else<br />
{<br />
// rational<br />
byte[] n = new byte[p.Len / 2];<br />
…<br />
Not perfect but it returns valid coordinates
Some Info on how this works:
The GPS coordinates come in as rational byte arrays
d d d d x x x x m m m m x x x x s s s s x x x x
55 0 0 0 1 0 0 0 37 22 0 0 100 0 0 0 0 0 0 0 1 0 0 0
55.94483 is...
55 0 0 0 = 55 / 1 = 55 Degrees
1 0 0 0
37 22 0 0 = 5669 / 100 = 56.69 Minutes (56.69 Minutes / 60 = 0.94483 Degrees)
100 0 0 0
0 0 0 0 = 0 / 1 = 0.0 Seconds (0 Seconds / 3600 = 0 Degrees)
1 0 0 0
modified on Thursday, April 24, 2008 7:11 AM





Thanks a lot!! It works great





This is great, many thanks. I added the code you posted for GPS data, but it doesnt seem to recognise the sign, ie. 117 degrees or +117 degrees? Will look at this, but any ideas appreciated.
Rob





Ok, you can figure out if it is + or minus by reading the Gps LongitudeRef or Gps LatitudeRef.





For all Germans who need a conversion from WGS84 to Gauss Krueger I post my solution which works great:
public Int16 Transform_WGS84_2_GK ( double rLatDec, double rLonDec, double rH, ref double rLongitude, ref double rLatitude)
{
double rWGS_a = 6378137.000;
double rWGS_b = 6356752.314;
double Bessel_a = 6377397.155;
double Bessel_b = 6356078.962;
double TPdx = 598.1;
double TPdy = 73.7;
double TPdz = 418.2;
double TPex = 0.202;
double TPey = 0.045;
double TPez = 2.455;
double TPm = 6.7;
double rWGS_E2, rWGS_N;
double rVecX, rVecY, rVecZ;
double F2, F3, F4, F5, F6, F7, F8;
double rotVec1,rotVec2, rotVec3;
double trans1, trans2, trans3;
double s5;
double T5;
double B5;
double L5;
double N5;
double h5,B,L,h;
double Bessel_e2;
double n6, alpha, beta, gamma, delta, epsilon;
int L0;
double l6, B6, N6_, nano, t6;
double Bogenlaenge;
double h1,h2,r1,r2;
rWGS_E2 = ( Sqr(rWGS_a)  Sqr(rWGS_b))/ (Sqr(rWGS_a));
rWGS_N = (rWGS_a)/(Math.Sqrt( 1  rWGS_E2*Sqr(Math.Sin( rLatDec/180*Math.PI))));
rVecX = (rWGS_N + rH) * Math.Cos(rLatDec / 180 * Math.PI) * Math.Cos(rLonDec / 180 * Math.PI);
rVecY = (rWGS_N + rH) * Math.Cos(rLatDec / 180 * Math.PI) * Math.Sin(rLonDec / 180 * Math.PI);
rVecZ = ( rWGS_N * Sqr(rWGS_b)/Sqr( rWGS_a) + rH) * Math.Sin( rLatDec / 180 * Math.PI);
F5 = TPex * Math.PI / 180 / 3600;
F6 = TPey * Math.PI / 180 / 3600;
F7 = TPez * Math.PI / 180 / 3600;
F8 = 1  TPm/1000000;
F2 = ( TPdx + F7*TPdy  F6*TPdz);
F3 = (F7*TPdx + TPdy + F5*TPdz);
F4 = ( F6*TPdx  F5*TPdy + TPdz);
rotVec1 = ( rVecX + F7*rVecY  F6*rVecZ) * F8;
rotVec2 = (F7*rVecX + rVecY + F5*rVecZ) * F8;
rotVec3 = ( F6*rVecX  F5*rVecY + rVecZ) * F8;
trans1 = rotVec1 + F2;
trans2 = rotVec2 + F3;
trans3 = rotVec3 + F4;
s5 = Math.Sqrt(Sqr(trans1) + Sqr(trans2));
Bessel_e2 = (Sqr(Bessel_a)Sqr(Bessel_b))/Sqr(Bessel_a);
T5 = Math.Atan( trans3 * Bessel_a / (s5 * Bessel_b));
B5 = Math.Atan((trans3 + Bessel_e2 * Sqr(Bessel_a) / Bessel_b * Math.Pow(Math.Sin(T5), 3)) / (s5  Bessel_e2 * Bessel_a * Math.Pow(Math.Cos(T5), 3)));
L5 = Math.Atan( trans2 / trans1);
N5 = Bessel_a/Math.Sqrt(1Bessel_e2 * Sqr(Math.Sin(B5)));
h5 = (s5 / Math.Cos(B5))  N5;
B = B5 * 180 / Math.PI;
L = L5 * 180 / Math.PI;
h = h5;
n6 = (Bessel_a  Bessel_b)/(Bessel_a + Bessel_b);
alpha = ((Bessel_a + Bessel_b)/2) * ( 1 + 0.25*Sqr(n6) + 1/64*Math.Pow(n6,4));
beta = 1.5*n6 + 9/16*Math.Pow(n6,3)  3/32*Math.Pow(n6,5);
gamma = 15/16 * Sqr(n6)  15/32*Math.Pow(n6,4);
delta = 35/48*Math.Pow(n6,3) + 105/256*Math.Pow(n6,5);
epsilon = 315/512*Math.Pow(n6,4);
if( Math.Abs(L6) < 1.5)
L0 = 6;
else if (Math.Abs(L  9) < 1.5)
L0 = 9;
else if (Math.Abs(L  12) < 1.5)
L0 = 12;
else if (Math.Abs(L  15) < 1.5)
L0 = 15;
else
{
MessageBox.Show( "Länge nicht unterstützt! Koordinatentransformation nur für Deutschland!");
L0 = 1;
}
if (L0 > 0)
{
l6 = (L  L0) * Math.PI / 180;
B6 = B * Math.PI / 180;
N6_ = Bessel_a / Math.Sqrt(1  Bessel_e2 * Sqr(Math.Sin(B6)));
nano = Math.Sqrt(Sqr(Bessel_a) / Sqr(Bessel_b) * Bessel_e2 * Sqr(Math.Cos(B6)));
t6 = Math.Tan(B6);
Bogenlaenge = alpha * (B6 + beta * Math.Sin(2 * B6) + gamma * Math.Sin(4 * B6) + delta * Math.Sin(6 * B6) + epsilon * Math.Sin(8 * B6));
h1 = t6 / 2 * N6_ * Sqr(Math.Cos(B6)) * Sqr(l6);
h2 = t6 / 24 * N6_ * Math.Pow(Math.Cos(B6), 4) * (5  Sqr(t6) + 9 * Sqr(nano) + 4 * Math.Pow(nano, 4)) * Math.Pow(l6, 4);
// h3 := t6/720*N6_*Power(Cos(B6),6)*(61  58*Sqr(t6) + 270*Sqr(nano)  330*Sqr(t6)*Sqr(nano))*Power(l6,6);
// h4 := t6/40320*N6_*Power(Cos(B6),8)*(13853111*Sqr(t6) + 543*Power(t6,4)  Power(t6,6))*Power(l6,8);
rLatitude = Bogenlaenge + h1 + h2;
r1 = N6_ * Math.Cos(B6) * l6;
r2 = N6_ / 6 * Math.Pow(Math.Cos(B6), 3) * (1  Sqr(t6) + Sqr(nano)) * Math.Pow(l6, 3);
rLongitude = r1 + r2 + 500000 + L0 / 3 * 1000000;
}
return 0;
}
private double Sqr(double r)
{
return ( Math.Pow( r, 2.0));
}





Hi, Exist a little bug in:
double o = ((a / b) + ((c / b) / 60) + ((e / f) / 3600));
it would to be
double o = ((a / b) + ((c / d) / 60) + ((e / f) / 3600));
Best Regards,
Mario





hi,thank you for ur gps code n for the author coding,i manage to add it into
another program
but there is a problem > [b]Field '.myHash' is never assigned to,and will
always have its default value null[/b]
how should i set it?





I experienced problem calculating md5 of image immediately after using this library for read EXIF. After analyzing the problem I think there is a bug in this library. The following method must be modified to correct it (FileStream is being closed explicitly):
public static PropertyItem[] GetExifProperties(string fileName)
{
FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
System.Drawing.Image image = System.Drawing.Image.FromStream(stream,
/* useEmbeddedColorManagement = */ true,
/* validateImageData = */ false);
PropertyItem[] pi = image.PropertyItems;
stream.Close();
return pi;
}





Here is the fix for your file locked issue:
public static PropertyItem[] GetExifProperties(string fileName, out long size)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
size = stream.Length;
using (System.Drawing.Image image = System.Drawing.Image.FromStream(stream, true, false))
{
return image.PropertyItems;
}
}
// return null;
}
I too found this problem and after modifying the code above, it works fine now.
cheers,





I have a general question related to RAW image data that i run into when i try to read the EXIF data from an image.
I started out with a free library that uses an System.Drawing.Image as an input and besides the fact that is didn't perform so great with JPG it gave me an "out of memory" exception on the following line:
Image imgImage = Image.FromFile(filePath);
I started using the EXIFExtractor class and get a System.ArgumentException exception on the following line:
Bitmap bmp = new Bitmap(filePath);
I did install the (in my case) CANON RAW codec so i can view them using windows image galary. I thought that the .NET framework would use this codec to open the RAW image, but that was not the case .
Does anybody know how to read/DECODE a RAW image to a .NET Image of Bitmap object?.





There is a opensource library on SourceForge called dcraw.net which is a port of the original dcraw. I've been updating it to support newer cameras and formats since it hasn't been updated in about a year, but still haven't made contact with the author to upload my changes.
If you're interested, the project is located on SourceForge here.
I was able to use it very quickly to read RAW files with the following code:
DcRawState state = new DcRawState();
state.inFilename = filename;
state.ifp = new RawStream(filename);
Identifier id = new Identifier(state);
id.identify(state.ifp);
I haven't tried writing files with it, but for reading EXIF data it's great!





I can provide a photo with GPS information:
http://farm2.static.flickr.com/1049/1358747187_496d7bb0df_o.jpg





I was trying to get the exif data out and then move the image to a different location but it was crashing because this class wasn't closing the filestream. Here's what i changed it to and it worked.
public static PropertyItem[] GetExifProperties(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
System.Drawing.Image image = System.Drawing.Image.FromStream(stream,
/* useEmbeddedColorManagement = */ true,
/* validateImageData = */ false);
return image.PropertyItems;
}
}

Also, I needed a few extra cases in the buildDB so I thought I would put them in here in case anyone else wanted them. They belong inside the "else if( p.Type == 0x3 )" statement.
case 0xA001: // Color Space
switch (convertToInt16U(p.Value))
{
case 1: v = "sRGB"; break;
//case 1: v = "Uncalibrated"; break;
default: v = " reserved"; break;
}
break;
case 0x500B:// Halftone LPIUnit
switch (convertToInt16U(p.Value))
{
case 1: v = "lines per inch"; break;
case 2: v = "lines per centimeter"; break;
}
break;
case 0x500D: // Halftone Shape  shape of the halftone dots
switch (convertToInt16U(p.Value))
{
case 0: v = "round"; break;
case 1: v = "ellipse"; break;
case 2: v = "line"; break;
case 3: v = "square"; break;
case 4: v = "cross"; break;
case 6: v = "diamond"; break;
}
break;

And i added some of the other elements to the flash case.
case 0x9209: // Flash
switch(convertToInt16U(p.Value) )
{
case 0: v = "Flash did not fire"; break;
case 1: v = "Flash fired"; break;
case 5: v = "Strobe return light not detected"; break;
case 7: v = "Strobe return light detected"; break;
case 9: v = "Flash fired, compulsory flash mode"; break;
case 13: v = "Flash fired, compulsory flash mode, return light not detected"; break;
case 15: v = "Flash fired, compulsory flash mode, return light detected"; break;
case 16: v = "Flash did not fire, compulsory flash mode"; break;
case 24: v = "Flash did not fire, auto mode"; break;
case 25: v = "Flash fired, auto mode"; break;
case 29: v = "Flash fired, auto mode, return light not detected"; break;
case 31: v = "Flash fired, auto mode, return light detected"; break;
case 32: v = "No flash function"; break;
case 65: v = "Flash fired, redeye reduction mode"; break;
case 69: v = "Flash fired, redeye reduction mode, return light not detected"; break;
case 71: v = "Flash fired, redeye reduction mode, return light detected"; break;
case 73: v = "Flash fired, compulsory flash mode, redeye reduction mode"; break;
case 77: v = "Flash fired, compulsory flash mode, redeye reduction mode, return light not detected"; break;
case 79: v = "Flash fired, compulsory flash mode, redeye reduction mode, return light detected"; break;
case 89: v = "Flash fired, auto mode, redeye reduction mode"; break;
case 93: v = "Flash fired, auto mode, return light not detected, redeye reduction mode"; break;
case 95: v = "Flash fired, auto mode, return light detected, redeye reduction mode"; break;
default: v = "Unknown flash status"; break;
}
break;





Thank you. I integrated everything.





He everyone,
I downloaded the latest version and try to set a Tag property, I got "NullReferenceException was unhandled" error. I trace into the source code and found it happen in this.bmp.SetPropertyItem( p ) inside the function setTag in EXIFextractor.cs.
I just put a line the following line
er2.setTag(0x9286, "My Comment");
before
Console.WriteLine(er2["Maker Note"]); //In Class1.cs
Any help is much appreciated.





I am at a total loss here. When I paste your code and try to compile, I get an error claiming System.Web.UI ist missing, as well as the use of undefined function "translation"... which I guess is due to the missing namespace. I found absolutely no information on System.Web.UI on the internet... Any clue as to what I have to install to get this running?
I am using a fresh installation of Visual C# Express with SP1, .NET 3.0 (2.0 wouldn't work either, so I got the new one)





I dont remember using anything like web.ui. I think its because of some other error. btw why are you copy pasting it? simply link to the dll and call the functions from your code.





Thx for the utility. Very handy.
I'm having the same problem, though. I need to enumerate all the key/value pairs and am having a hard time finding the right type of object to use. The watch window classifies the priperties array as a hashtable, but I'm a little lost as to what type the individual pairs are of.





Sorry, answered my own question.
Coldkatz  you need to add the System.Web reference to your project, and then add "using System.Web.UI".
After that, you'll be able to see the "Pair" type and use it to enumerate all the properties.





Does anyone know how to convert the data in the GPS Coordinates into Degrees, Minutes and seconds?






General News Suggestion Question Bug Answer Joke Praise Rant Admin Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

