Using Nikon EXIF Headers
Whenever one takes a photo with a digital camera, various pieces of information are digitally stored with the picture. For example, the model and manufacturer are recorded in the picture, as well as the exposure length, the aperture number, and various other pieces of data. The piece in which I am interested at the moment is the shutter speed. I had this little issue for the past month or two with pictures that wouldn't load because my routine for calculating the shutter speed caused an infinite loop. I have fixed that. Here's what went wrong.
Nikon is kind of weird. When one takes a photo, it records the shutter speed in a weird way. JPEGs and RAW files store them as fractions. For example, 1/4000th of a second is stored as 10/40000. 1/60th of a second is stored as 10/60 and a five second exposure is stored at 50/10. This by itself would be easy to figure out, but enter Nikon Capture.
When one takes a photo into Nikon Capture and manipulates it and saves it again, it changes the numbers. 1/4000th of a second becomes 25/100000, 1/60th of a second becomes 5/300 and a five second exposure becomes 5/1. Even more strange, 1/13 of a second becomes 7692307/100000000. So the question is how to reduce this and present meaningful fractions? There is no function in PHP of which I am aware that will convert from a decimal to a fraction. Searching Google returned no results for functions that would portably convert decimals to fractions. So, I wrote my own.
The first time I wrote a function to do it, it would take an floating point and iterate over each digit and try to construct a fraction out of it. This sort of worked, until I ended up with infinite loops on irrational numbers. And I wondered why some pictures wouldn't load.
After realizing that it was irrational numbers that were causing some pictures to not load, I rewrote the function. Here it is in all its glory.
function decimalToFraction($f) {
list($n, $d) = explode('/', $f);
// if the bottom is 1, then there is no math
if ($d == 1) {
return($n);
}
// if the top is greater than the bottom then we have no
// fraction, it's a decimal of a second
if ($n >= $d) {
return($n / $d);
}
// now reduce the top to 1
return("1/" . round(($d / $n), 1));
}
First, if the bottom of the fraction is one, then we take the one off and we're done. Then, if the top is greater than the bottom, we write out the decimal, because it'll be an improper or mixed fraction otherwise, and those don't fly in exposure times. Third, because at this point we're trying to reduce the fraction down to 1/something, we check to see if the top is already one. Next, if the top isn't one, we reduce it to one by putting 1 on top and dividing the bottom by the top. If there is going to be any decimal on the bottom, such as 1/1.3 seconds, then we just round to one place, because no exposure time is more precise than one decimal place.