Convert EXIF data to how it should looks like - php

According to PHP, the EXIF information for a image is 20/10 for F-number, 51/10 for focal length, and 10/150 for exposure. This is not how these values should look like! It should looks like F/2 for F-number, 5, mm for focal length, and 1/150 for exposure. These values are just some examples! Please see this link for how I really mean how it should looks like. Note that I will not use any third party software! Just pure PHP.
Is it possible to convert these values (for example 20/10) to the real values (for example F/2) in PHP? If yes, how can I convert them?
Thanks in advance.
EDIT
The following code convert 150/10 to 150 seconds which is 2 minutes and 30 seconds. This is wrong because I took the photo with 15 seconds shutter. How can I make it to calculate to the correct amount of seconds?
list($d1, $d2) = str_split('/', 'P1220379.JPG');
if($d1 > 0 AND $d2 > 0) {
$e = $d1 / $d2;
} else {
$e = 'P1220379.JPG';
}
if($e < 1 AND $e > 0) {
$e = '1/'.round(1 / $e, 0).' sekunder';
} else {
$e = round($e, 1).' sekunder';
}

"drpain" on this link says
Please note that when resizing images with GD and most image
processing scripts or applications you will loose the EXIF
information.What I did as a workaround is book this information into
MySQL before I re-size images.
His little program below
<?php
$camera = cameraUsed("/img/myphoto.jpg");
echo "Camera Used: " . $camera['make'] . " " . $camera['model'] . "<br />";
echo "Exposure Time: " . $camera['exposure'] . "<br />";
echo "Aperture: " . $camera['aperture'] . "<br />";
echo "ISO: " . $camera['iso'] . "<br />";
echo "Date Taken: " . $camera['date'] . "<br />";
?>
does produce these numbers in correct format, according to him
Will display the following, depending on the data:
Camera Used: SONY DSC-S930
Exposure Time: 1/400
Aperture: f/4.3
ISO: 100
Date Taken: 2010:12:10 18:18:45

This is my solution in kotlin if anyone needs
fun convertShutterSpeed(value: String?): String {
if (value.isNullOrBlank()) {
return ""
}
val split = value.split("/")
val ed: Float = split[0].toFloat()
val ed1: Float = split[1].toFloat()
val fl = ed / ed1
return if (ed < 0) {
Math.round(1 / Math.pow(2.toDouble(), fl.toDouble())).toString() + "s"
} else {
"1/" + Math.round(Math.pow(2.toDouble(), fl.toDouble()))
}
}
And here are some tests
#Test
fun convertShutterSpeed() {
assertEquals("1/8", imageExif.convertShutterSpeed("3/1"))
assertEquals("1/10", imageExif.convertShutterSpeed("3321928/1000000"))
assertEquals("1/20", imageExif.convertShutterSpeed("4321928/1000000"))
assertEquals("1/125", imageExif.convertShutterSpeed("6965784f/1000000"))
assertEquals("1/250", imageExif.convertShutterSpeed("7965784/1000000"))
assertEquals("1/320", imageExif.convertShutterSpeed("8321928/1000000"))
assertEquals("1/400", imageExif.convertShutterSpeed("8643856/1000000"))
assertEquals("1/640", imageExif.convertShutterSpeed("9321928/1000000"))
assertEquals("1/800", imageExif.convertShutterSpeed("9643856/1000000"))
assertEquals("1/1000", imageExif.convertShutterSpeed("9965784/1000000"))
assertEquals("2s", imageExif.convertShutterSpeed("-1/1"))
assertEquals("6s", imageExif.convertShutterSpeed("-2584963/1000000"))
}
Values that I got is from Canon images, but it works for other camera models as well.

Related

Wrong calculations in PHP

I am trying to calculate in PHP. When I try to do that I can see that the result is mostly wrong.
The script I am using is the following:
$newrate = 1 / $rate["rate"];
echo '1 /'. $rate["rate"]. ' = '.$newrate;
The result I get is the following:
1 /1.0867 = 1
1 /117.01 = 0.0085470085470085
1 /1.9558 = 1
1 /27.534 = 0.037037037037037
1 /7.4589 = 0.14285714285714
1 /1.0867 should be 0.92021717125 and not 1.
I can reproduce this step by adding the following line to my script:
$rate["rate"] = 1.0867;
When I run the script the result is still 1. When I change my script like:
$newrate = 1 / 1.0867;
echo '1 / 1.0867 = '.$newrate;
I get a correct result. The output is then: 0.92021717125.
Does someone know what is wrong with this script? What is the reason that my script is making incorrect calculations?
Update 1:
Here is my full script:
<?php
$XML=simplexml_load_file("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml");
foreach($XML->Cube->Cube as $rat)
{
foreach($rat as $rate)
{
$newrate = '1' / $rate["rate"];
echo '1 /' . $rate["rate"] . ' = ' . $newrate;
echo '<br /><br />';
}
}
?>
I think there is something wrong with your example. I tested exactly what you described and works as expected.
<?php
$rate = [];
$rate["rate"] = "1.0867";
$newrate = 1 / $rate["rate"];
echo '1 / ' . $rate["rate"] . ' = ' . $newrate;
// 1 / 1.0867 = 0.92021717125242
You can see it running here
https://repl.it/#thiagodamata/NeighboringPeachpuffArchitect
Probably, you are using "," instead of "." when sending the number.
PHP cast the number only until it hits something unexpected. So "1,0867" is cast to 1. It is a classic problem when dealing with numbers in different formats, considering different languages.
<?php
// simulating the "," error
$rate["rate"] = "1,0867";
$newrate = 1 / $rate["rate"];
echo '1 / ' . $rate["rate"] . ' = ' . $newrate;
// 1 / 1,0867 = 1
Take a look in this thread about how to cast the number from different languages masks PHP: Locale aware number format and take a deeper look into the PHP function number-format https://www.php.net/manual/en/function.number_format.php
Update - After getting the full code example
Looking to your full code becomes more clear the problem. The var that you are getting in the loop it is a SimpleXmlElement, not a string. So, SimpleXmlElement is printed as the "1.0867" but it is not its real value.
To make the SimpleXmlElement cast to float properly, you need to use the cast function.
<?php
$XML=simplexml_load_file("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml");
foreach($XML->Cube->Cube as $rat)
{
foreach($rat as $rate)
{
print(var_dump($rate["rate"]));
/*
object(SimpleXMLElement)#7 (1) {
[0]=>
string(6) "1.0867"
}
*/
print("class = [" . get_class($rate["rate"]) . "]\n");
// class = [SimpleXMLElement]
print("as string = [" . $rate["rate"] . "]\n");
// as string = [1.0867]
print("without cast = [" . (1 * $rate["rate"]) . "]\n");
// without cast = [1]
$a = (float)($rate["rate"]);
print("with cast = [" . ($a) . "]\n");
// with cast = [1.0867]
print(serialize($a));
//d:1.0867;
$newrate = '1' / $a;
echo '1 / ' . $a . ' = ' . $newrate . "\n";
// 1 / 1.0867 = 0.92021717125242
echo '<br /><br />';
exit();
}
}
?>
You can see this running here:
https://repl.it/#thiagodamata/FuchsiaLightheartedPrintablecharacter
Just printing the value of a var does not always give you the real value of some object. There are other functions like var_export https://www.php.net/manual/en/function.var-export.php, var_dump https://www.php.net/manual/en/function.var-dump.php and serialize that may help you to see the real value of the vars.
Calculations are fine, and correct
You've not taken into account implicit type casting in PHP. Try this:
<?php
print "integer divided by float = " . (1 /1.0867) . "\n"; // returns 0.9202171712524
print "Integer divided by string = " . ((1/"1.0867") . "\n"; // returns 1
You are expecting PHP to know what your intention is when you throw a tring into an arithmetic operation - that's not how PHP works.
Just because it is dynamically typed doesn't mean you don't need to be careful with your data types.
print (1/(float)$rate['rate'];
try cast all values as a float
echo 1.0 / (float) $rate['rate'];

Division with php

Im trying to get out an average value from a vote function.
<?php
$file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = str_split($textfil);
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum = $sum + intval($vote);
}
echo "Average: " . $sum;
?>
Simple by substitute (+) with a (/), and even tried a (%). But still getting error message.
Would appreciate alot if anyone could help me out and tell me what im doing wrong.
/thanks
Edit
Sidenote: Please read an explanation under "First answer given" further down below.
This version will take into account any blank lines in a file, if the content looks like:
1
2
3
// <- blank line
Sidenote: Please provide a sample of your text file. A comment has already been given to that effect.
PHP
<?php
// first line not required
// $file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = array_filter(array_map("trim", file("textfile.txt")), "strlen");
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
echo "Average: " . $avg;
?>
First answer given
Using the following in a text file: (since no example of file content was given)
5
5
5
IMPORTANT NOTE: There should not be a carriage return after the last entry.
produced
Number of votes: 5
Average: 3
which is false, since there are 3 entries in the text file.
explode() should be used, and not str_split()
The following using the same text file produced:
Number of votes: 3
Average: 5
which is correct. In simple mathematics, averages are done by adding all numbers then dividing them by how many numbers there are.
In this case it's 3 numbers (all 5's) added equals 15, divided by 3 is 5.
Sidenote: The first line is not required $file = file("textfile2.txt");
<?php
// first line not required
// $file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = explode("\n", $textfil);
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
echo "Average: " . $avg;
?>
Footnotes:
If the average comes out to 8.33333 and you would like it to be rounded off to 8, use:
echo "Average: " . floor($avg);
If the average comes out to 8.33333 and would like it to be as 9 you would use:
echo "Average: " . ceil($avg);
ceil() function
floor() function
You may be mixing in stuff that can't be divided, like text, etc. I don't know what your text file looks like. intval may be having a problem with arrays. You may try:
foreach ($textfill as $vote) {
if(is_int($vote) {
$sum += $vote;
}
}
echo "Average: " . $sum;
Lower school math says:
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
The average value is calculated by divide the sum with the number of votes. This line will print the average value:
echo "Average: " . $sum/count($textfill);

unexpected result in php output with larger strings

I am trying to create a binary/hexadecimal converter to convert a denary(base 10) number/value into binary and hexadecimal.
It works fine so far for binary until the input from the form is greater than 11 digits and over(string length), ruffly as it seems to variety. after 11 digits it starts adding " - " into the outcome. Im not sure were this is coming from as I don't have an " - " in the code.
I wasn't sure if this was something to do with large integers as I saw some other questions on that topic(not in php however it was java, so not sure if there is something simpler in php)
That said I was under the impression that form inputs were always strings.
To test if a variable is a number or a numeric string (such as form input, which is always a string), you must use is_numeric(). - from : http://www.php.net/manual/en/function.is-float.php
(haven't yet got to hexadecimal but needed to mention it as some of the following code contains parts for it.)
here is the php code (note: I do check user input just not added it yet)
$inpu = $_POST['number'];
$numinput = $_POST['number'];
if (is_numeric($numinput))
{
while ($numinput >= 1)
{
$binary .= $numinput % 2;
$numinput = $numinput / 2;
}
$mult = strlen($binary) % 4;
echo gettype($numinput) . "<br />";
echo gettype($binary) . "<br />";
echo gettype($mult) . "<br />";
echo $mult . "<br />";
while ($mult < 4)
{
$binary.= "0";
$mult++;
}
$revbinary = strrev($binary);
echo $inpu . " in binary = " . $revbinary ;
echo "<br /> <br />";
echo chunk_split($revbinary, 4);
echo "<br />" . gettype($revbinary) . "<br />";
echo gettype($inpu) . "<br />";
}
else
{
if (is_numeric($numinput))
{
echo "$numinput is over the max value of 255";
}
else
{
echo "your entry is not a vaild number <br />";
echo $numinput;
}
}
Im not looking for completed version of this code as you would ruin my fun, I am just wondering why there is a "-" being entered after 11 digits or so. It also did't add the symbol before I added :
$mult = strlen($binary) % 4;
echo $mult . "<br />";
while ($mult < 4)
{
$binary.= "0";
$mult++;
}
This was to split the binary into 4s ( 0011 1101 0010 0110 ).
Edit: wondered if this would be useful:
echo gettype($numinput); result double
echo gettype($binary); result string
echo gettype($mult); result integer
gettype($revbinary); result string
echo gettype($inpu); result string
still trying to work this out myself.
Any advice is much appreciated Thanks
I would suggest simply using decbin() and dechex(). Those are functions included in php, which do exactly what you're trying to accomplish.
You might want to check if it is a number first (like you are already doing).
Then cast it to an integer (through intval()) and then apply decbin() and dechex()
http://php.net/manual/de/function.decbin.php
http://www.php.net/manual/de/function.dechex.php
http://php.net/manual/de/function.intval.php

Reopened: PHP array_shift(); VS reset(); unset(); array_splice();

Reopened:
Since PHP is a server-side language I had the assumption that it didn't matter what browser I used, but apparently not so. I had only been using Google Chrome.
I installed WAMP on my local machine so I can test it locally to see if it did the same as my shared hosting account. Both codes worked the way they should (in Chrome mind you). Then I looked at the exact same code on my shared hosting -- one worked, the other didn't.
I called my shared hosting support and they tried to duplicate the issue and said they weren't finding it. So I tried it in Firefox with them on the line and in IE, lo and behold... it worked perfectly in both just like on WAMP.
This still isn't making much sense to me. I'm using PHP 5.3 on my shared hosting account with a well known hosting company but not getting anywhere because they can't really troubleshoot code. They can replicate the issue but can't answer why. I'm going to try to find out more in the next weeks and post an update as I know more.
What I'm trying to do is this:
Generate a range of numbers
Shuffle them into a random order
Copy that array of randomly ordered numbers into a session array
Get the first value of the array, delete that value, and shift all
the values down by one
Here's my issue:
I tried using array_shift(); and it worked fine the first run but each time I ran the code after it would remove the first two elements.
To test what was going on I tried to print the array first, do array_shift();, and then print the array again to see what happened.
Expected Results:
Run #1:
[0]=>5 [1]=>2 [2]=>1 [3]=>4 [4]=>3 //print_r($array);
//execute array_shift($array);
[0]=>2 [1]=>1 [2]=>4 [3]=>3 //print_r($array);
Run#2:
[0]=>2 [1]=>1 [2]=>4 [3]=>3 //print_r($array);
//execute array_shift($array);
[0]=>1 [1]=>4 [2]=>3 //print_r($array);
Actual Results:
Run #1:
[0]=>5 [1]=>2 [2]=>1 [3]=>4 [4]=>3 //print_r($array);
//execute array_shift($array);
[0]=>2 [1]=>1 [2]=>4 [3]=>3 //print_r($array);
Run#2:
[0]=>1 [1]=>4 [2]=>3 //print_r($array);
//execute array_shift($array);
[0]=>4 [1]=>3 //print_r($array);
My issue (cont.)
So then I tried using reset($array);, unset($array[0]);, and array_splice($array,1,0); as an alternative to array_shift($array); and it worked! Then I tried to compare them side by side
and cleaned up the code and now they are doing the opposite of each other. Sometimes reset, unset, and array_shift; will even jump through up to 7 cells in the array when called once. array_shift(); is working the way I want it to now but I want to know why. It's driving me nuts! If someone could please help me I'd be so appreciative.
Code Dump:
unset, reset, splice
<?php
session_start();
$min = A;
$max = S;
if((!isset($_SESSION['image'])) || ($_SESSION['image'] == null))
{
$numbers = range($min, $max); //set a range for all images
shuffle($numbers); //shuffle the order for randomness
$_SESSION['image'] = $numbers;
echo "<br />Current value: " . $_SESSION['image'][0] . "<br />";
print_r($_SESSION['image']);
reset($_SESSION['image']);
unset($_SESSION['image'][0]);
array_splice($_SESSION['image'],1,0);
echo "<br />New value: " . $_SESSION['image'][0] . "<br />";
echo "<br />1st exec<br />";
}
else
{
echo "<br />Current value: " . $_SESSION['image'][0] . "<br />";
print_r($_SESSION['image']);
reset($_SESSION['image']);
unset($_SESSION['image'][0]);
array_splice($_SESSION['image'],1,0);
echo "<br />New value: " . $_SESSION['image'][0] . "<br />";
echo "<br />2nd exec<br />";
}
?>
shift
<?php
session_start();
$min = A;
$max = S;
if((!isset($_SESSION['id2'])) || ($_SESSION['id2'] == null))
{
$numbers = range($min, $max); //set a range for all images
shuffle($numbers); //shuffle the order for randomness
$_SESSION['id2'] = $numbers;
echo "<br />Current value: " . $_SESSION['id2'][0] . "<br />";
print_r($_SESSION['id2']);
array_shift($_SESSION['id2']);
echo "<br />New value: " . $_SESSION['id2'][0] . "<br />";
echo "<br />1st execution<br />";
}
else
{
echo "<br />Current value: " . $_SESSION['id2'][0] . "<br />";
print_r($_SESSION['id2']);
array_shift($_SESSION['id2']);
echo "<br />New value: " . $_SESSION['id2'][0] . "<br />";
echo "<br />2nd execution<br />";
}
?>
To ultimately learn what's going on, I propose to register a tick-function. The tick-function can execute at each PHP statement execution.
Here's a simple one, which traces line/file of each statement (feel free to add further details):
// Execute at each single statement
declare(ticks=1);
// Function to get called at each executing statement
function logEachStatement() {
$traceInfo = debug_backtrace();
$lastActivation = $traceInfo[ count( $traceInfo ) - 1 ];
$info = "\n"
. $lastActivation[ 'line' ] . '#' . $lastActivation[ 'file' ]
;
$targetFile = dirname( __FILE__ ) . '/stmnt.log' );
file_put_contents( $targetFile, $info, FILE_APPEND );
}
// using a function as the callback
register_tick_function( 'logEachStatement', true );
While there are other options to track down the problem, this one does not require external infrastructure.
Assuming that you pasted the actual code, the only way for this to happen is if you made an unintentional intermediate request. It would be impossible for us to determine how or why this is the case with only the given information, though.
My suggestion is to use your browser dev tool of choice (e.g. Firebug) and monitor the network tab to make sure that only one request is being sent to the script. If this doesn't turn up your problem, add some simple debug logging, such as this:
$log = date('Y-m-d H:i:s')
. ' :: Request received from '
. $_SERVER['REMOTE_ADDR']
. "\n";
file_put_contents('/path/to/your/log/file', $log, FILE_APPEND);
Then check your log after your test to make sure only one entry was added.

preg_match: trying to identify number formats

I'm trying to identify not only "numbers" in a string, but tag what type of number it is, such as General, Fraction, Percentile, Ratio etc.
Now if I use a tool like http://rubular.com/, my patterns appear to work fine.
Rules?
([-+]?)([0-9]+)([,+]?)([.]?) //General
([-+]?[0-9.,]+[%]) //Percent
([0-9]+[\/][0-9]+(?:st|nd|rd|th)) //Fraction
([-+]?[0-9.,]+[:][-+]?[0-9.,]+) //Ratio
Strings to check?
1
1,000
1.000
-50.5
-1:+3
1,200.6:3.9
+2:-4
1/5th
25%
25.76%
1,001%
It's when I put them in php (if/elseif) constructs I always end up with the "general" number?
if (preg_match('/([-+]?[0-9.,]+[%])/',$string)) {
echo " PERCENTILE ";
} elseif (preg_match('/([0-9]+[\/][0-9]+(?:st|nd|rd|th))/',$string)) {
echo " FRACTION ";
} elseif (preg_match('/([-+]?[0-9.,]+[:][-+]?[0-9.,]+)/',$string)) {
echo " RATIO ";
} elseif (preg_match('/([-+]?)([0-9]+)([,+]?)([.]?)/',$string)) {
echo " CARDINAL ";
} else {
echo " GENERAL ";
}
No matter what, it echos "CARDINAL".
Yes, I've tried switching the order (Card/General first, then the others).
Yes, I've tried making them independant IF()s, and in reverse order (most general first).
Nothing appears to work.
Even a Single IF checking for any of the formats simply fails.
Either my rules are stuffed - or I'm doing something blatantly wrong.
You error must come from somewhere else, I couldn't reproduce it. Try to put your code in an otherwise empty file to test it by itself and it should work fine.
Test.php:
$nums = array(
'1',
'1,000',
'1.000',
'-50.5',
'-1:+3',
'1,200.6:3.9',
'+2:-4',
'1/5th',
'25%',
'25.76%',
'1,001%'
);
foreach ($nums as $string) {
echo $string.': ';
if (preg_match('/([-+]?[0-9.,]+[%])/',$string)) {
echo " PERCENTILE ";
} elseif (preg_match('/([0-9]+[\/][0-9]+(?:st|nd|rd|th))/',$string)) {
echo " FRACTION ";
} elseif (preg_match('/([-+]?[0-9.,]+[:][-+]?[0-9.,]+)/',$string)) {
echo " RATIO ";
} elseif (preg_match('/([-+]?)([0-9]+)([,+]?)([.]?)/',$string)) {
echo " CARDINAL ";
} else {
echo " GENERAL ";
}
echo "\n";
}
$ php test.php
1: CARDINAL
1,000: CARDINAL
1.000: CARDINAL
-50.5: CARDINAL
-1:+3: RATIO
1,200.6:3.9: RATIO
+2:-4: RATIO
1/5th: FRACTION
25%: PERCENTILE
25.76%: PERCENTILE
1,001%: PERCENTILE
Try surrounding each regular expression with ^ and $ so that it matches only if it matches the entire string or line. Without these, if a regular expression matches part of a number, then that's still a match. For example, '1,000' matches your regular expression for CARDINAL numbers, /([-+]?)([0-9]+)([,+]?)([.]?)/, because it matches the substring '1,'. If you add ^ and $, as in /^([-+]?)([0-9]+)([,+]?)([.]?)$/, then it no longer matches.
<?php
function check_format($string) {
if (preg_match('/^([-+]?[0-9.,]+[%])$/',$string)) {
echo " PERCENTILE ";
} elseif (preg_match('/^([0-9]+[\/][0-9]+(?:st|nd|rd|th))$/',$string)) {
echo " FRACTION ";
} elseif (preg_match('/^([-+]?[0-9.,]+[:][-+]?[0-9.,]+)$/',$string)) {
echo " RATIO ";
} elseif (preg_match('/^([-+]?)([0-9]+)([,+]?)([.]?)$/',$string)) {
echo " CARDINAL ";
} else {
echo " GENERAL ";
}
}
array_map("check_format", array(
"1",
"1,000",
"1.000",
"-50.5",
"-1:+3",
"1,200.6:3.9",
"+2:-4",
"1/5th",
"25%",
"25.76%",
"1,001%"
));
outputs:
CARDINAL GENERAL GENERAL GENERAL RATIO RATIO RATIO FRACTION PERCENTILE PERCENTILE PERCENTILE
EDIT: This might be a better regular expression for CARDINAL numbers:
/^([-+]?)([0-9]+)(?:,?[0-9]{3})*([.]?)$/
Please have a look on this .
<?php
$num = 12.20
$num = floatval(preg_replace('/[^\d.]/', '', num));
// output will be 12.2
//it always replace zero and you have number 12.00 then output will be 12
$num = 12.00
$num = floatval(preg_replace('/[^\d.]/', '', num));
// output will be 12

Categories