#!/usr/bin/env php . *! -----------------------------------------------------------------------------** *! $Log: exif.php,v $ *! Revision 1.2 2010/08/10 21:14:31 elphel *! 8.0.8.39 - added EEPROM support for multiplexor and sensor boards, so autocampars.php uses application-specific defaults. Exif Orientation tag support, camera Model reflects application and optional mode (i.e. camera number in Eyesis) *! *! Revision 1.1.1.1 2008/11/27 20:04:01 elphel *! *! *! Revision 1.2 2008/08/11 19:11:32 elphel *! comments *! *! Revision 1.1 2008/04/07 09:12:14 elphel *! New Exif template generation *! *! $Log: exif.php,v $ *! Revision 1.2 2010/08/10 21:14:31 elphel *! 8.0.8.39 - added EEPROM support for multiplexor and sensor boards, so autocampars.php uses application-specific defaults. Exif Orientation tag support, camera Model reflects application and optional mode (i.e. camera number in Eyesis) *! *! Revision 1.1.1.1 2008/11/27 20:04:01 elphel *! *! *! Revision 1.2 2008/08/11 19:11:32 elphel *! comments *! *! Revision 1.1 2008/04/07 09:12:14 elphel *! New Exif template generation *! *! Revision 1.1.1.1 2007/10/03 19:05:53 elphel *! This is a fresh tree based on elphel353-2.10 *! *! Revision 1.6 2007/10/03 19:05:53 elphel *! cosmetic *! *! Revision 1.5 2007/10/03 18:37:53 elphel *! Made nice html output to see the result data *! *! Revision 1.4 2007/10/03 06:33:31 elphel *! fixed wrong copyright year *! *! Revision 1.3 2007/10/02 19:44:03 elphel *! minor bug fixes, added "manufacturer note" field for raw frame metadata (36 bytes - /include/asm-cris/c313.h frame_params_t) *! *! Revision 1.2 2007/09/25 23:34:02 elphel *! Fixed time strings to the right length *! *! Revision 1.1 2007/09/24 07:27:57 elphel *! Started php script that prepares Exif header information for serving images by imgsrv *! */ //TODO: Decide with the start sequence, more data is availble after the sensor is initialized. // Or leave it to imgsrv (it still needs sensor to start first), just put placeholders // use http://www.exiv2.org/tags.html for reference of the Exif fields if you need to add modify // the data below $chn = 0; $ExifDeviceTemplateFilename="/dev/exif_template"; $ExifDeviceMetadirFilename= "/dev/exif_metadir"; $ExifDeviceExifFilename= "/dev/exif_exif"; $ExifDeviceMetaFilename= "/dev/exif_meta"; $DeviceSNFilename = "/sys/devices/soc0/elphel393-init/serial"; $DeviceRevisionFilename = "/sys/devices/soc0/elphel393-init/revision"; $DeviceBrand = "Elphel"; $DeviceModel = "Elphel393"; //! if called from the command line - accepts just one parameter - configuration file, //! through CGI - accepts more parameters $ExifXMLName="/etc/Exif_template.xml"; $init=false; if ($_SERVER['REQUEST_METHOD']=="GET") { if ($_GET["init"]!==NULL) { if ($_GET["init"]) $ExifXMLName=$_GET["init"]; $init=true; // in any case - filename specified or not $noGPS= ($_GET["noGPS"]!==NULL); $nocompass= ($_GET["nocompass"]!==NULL); } if (isset($_GET['chn'])) $chn = $_GET['chn']; $ExifDeviceExifFilename .= $chn; $ExifDeviceMetaFilename .= $chn; } else { foreach ($_SERVER['argv'] as $param) if (substr($param,0,4)=="init") { $param=substr($param,5); if (strlen($param)>0) $ExifXMLName=$param; $init=true; // break; } if ($init) { $noGPS= in_array ('noGPS' , $_SERVER['argv']); $nocompass= in_array ('nocompass', $_SERVER['argv']); } else { echo <<GPSInfo) { /// remove all tags named "Compass..." if ($nocompass) { $tounset=array(); foreach ($xml_exif->GPSInfo->children() as $entry) if (strpos ($entry->getName() , "Compass" )!==false) $tounset[]=$entry->getName(); foreach ($tounset as $entry) unset ($xml_exif->GPSInfo->{$entry}); } if ($noGPS) { unset($xml_exif->GPSInfo); unset($xml_exif->Image->GPSTag); } } $IFD_offset= count($exif_data); $SUB_IFD_offset= 12*count($xml_exif->Image->children())+2+4+$IFD_offset; $GPSInfo_offset= 12*count($xml_exif->Photo->children())+2+4+$SUB_IFD_offset; $data_offset= $GPSInfo_offset+(($xml_exif->GPSInfo)?(12*count($xml_exif->GPSInfo->children())+2+4):0); //$GPSInfo is optional if ($_SERVER['REQUEST_METHOD']) { echo "
";
		printf ("IFD_offset=0x%x\n",$IFD_offset);
		printf ("SUB_IFD_offset=0x%x\n",$SUB_IFD_offset);
		printf ("GPSInfo_offset=0x%x\n",$GPSInfo_offset);
		printf ("data_offset=0x%x\n",$data_offset);
	}

	/// Now modify variable fields substituting values
	foreach ($xml_exif->Image->children()   as $entry) substitute_value($entry);
	foreach ($xml_exif->Photo->children()   as $entry) substitute_value($entry);
	if ($xml_exif->GPSInfo) {
		foreach ($xml_exif->GPSInfo->children() as $entry) substitute_value($entry);
	}
	$ifd_pointer=$IFD_offset;
	$data_pointer=$data_offset;
	start_ifd(count($xml_exif->Image->children()));
	foreach ($xml_exif->Image->children() as $entry) process_ifd_entry($entry,0);
	finish_ifd();

	$ifd_pointer=$SUB_IFD_offset;
	start_ifd(count($xml_exif->Photo->children()));
	foreach ($xml_exif->Photo->children() as $entry) process_ifd_entry($entry,1);
	finish_ifd();

	if ($xml_exif->GPSInfo) {
		$ifd_pointer=$GPSInfo_offset;
		start_ifd(count($xml_exif->GPSInfo->children()));
		foreach ($xml_exif->GPSInfo->children() as $entry) process_ifd_entry($entry,2);
		finish_ifd();
	}
	$exif_len=count($exif_head)+count($exif_data)-$Exif_length_offset;
	$exif_head[$Exif_length_offset]=  ($exif_len >> 8) & 0xff;
	$exif_head[$Exif_length_offset+1]= $exif_len       & 0xff;

	$Exif_str="";
	for ($i=0; $iImage->children()   as $entry) addDirEntry($entry);
	foreach ($xml_exif->Photo->children()   as $entry) addDirEntry($entry);
	if ($xml_exif->GPSInfo) {
		foreach ($xml_exif->GPSInfo->children() as $entry) addDirEntry($entry);
	}
	array_multisort($dir_sequence,$dir_entries);

	$frame_meta_size=0;
	for ($i=0;$i";
	}
	if ($_SERVER['REQUEST_METHOD']) {
		echo "
\n"; test_print_header(); echo "
\n"; test_print_directory(); } } //if ($init) // configure Exif data /// ===== processing optional parameters ======= /// ===== read template ======= if ($_GET["description"]!==NULL) { /// Read metadir to find the length of the description field $Exif_file = fopen($ExifDeviceMetadirFilename, 'r'); fseek ($Exif_file, 0, SEEK_END) ; fseek ($Exif_file, 0, SEEK_SET) ; $metadir=fread ($Exif_file, 4096); fclose($Exif_file); $dir_entries=array(); for ($i=0; $i\n"; break; } } /// ===== read template ======= if ($_GET["template"]!==NULL) { $Exif_file = fopen($ExifDeviceTemplateFilename, 'r'); fseek ($Exif_file, 0, SEEK_END) ; echo "
\n"; echo "ftell()=".ftell($Exif_file).", "; fseek ($Exif_file, 0, SEEK_SET) ; $template=fread ($Exif_file, 4096); fclose($Exif_file); echo "read ".strlen($template)." bytes
\n"; hexdump($template); } /// ===== read meta directory ======= if ($_GET["metadir"]!==NULL) { $Exif_file = fopen($ExifDeviceMetadirFilename, 'r'); fseek ($Exif_file, 0, SEEK_END) ; echo "
\n"; echo "ftell()=".ftell($Exif_file).", "; fseek ($Exif_file, 0, SEEK_SET) ; $metadir=fread ($Exif_file, 4096); fclose($Exif_file); echo "read ".strlen($metadir)." bytes
\n"; $dir_entries=array(); for ($i=0; $i\n"; printf ("Reading frame %d, ",$frame); $Exif_file = fopen($ExifDeviceExifFilename, 'r'); fseek ($Exif_file, 1, SEEK_END) ; $exif_size=ftell($Exif_file); if ($frame) fseek ($Exif_file, $frame, SEEK_END) ; else fseek ($Exif_file, 0, SEEK_SET) ; echo "ftell()=".ftell($Exif_file).", "; $exif_data=fread ($Exif_file, $exif_size); fclose($Exif_file); echo "read ".strlen($exif_data)." bytes
\n"; hexdump($exif_data); } exit(0); /// ======================================= Functions ====================================== function hexdump($data) { global $exif_head, $exif_data; $l=strlen($data); printf ("

Exif size=%d bytes

\n",$l); printf ("\n"); for ($i=0; $i<$l;$i=$i+16) { printf("\n",$i); for ($j=$i; $j<$i+16;$j++) { printf(""); } printf(""); for ($j=$i; $j< ($i+16);$j++) { printf(""); } printf("\n"); } printf ("
%03x|"); if ($j<$l) { $d=ord($data[$j]); printf(" %02x",$d); } else printf (" "); printf("|"); if ($j<$l) { $d=ord($data[$j]); if ($d<32 or $d>126) printf("."); else printf ("%c",$d); } else printf (" "); printf("
"); } function print_directory($dir_entries) { $meta_size=0; foreach ($dir_entries as $entry) if (($entry[3]+$entry[2])>$meta_size) $meta_size=$entry[3]+$entry[2]; printf ("

Frame meta data size=%d bytes

\n",$meta_size); printf ("\n"); printf ("\n"); foreach ($dir_entries as $entry) { printf ("\n",$entry[1],$entry[3],$entry[4],$entry[2]); } printf ("
ltagmeta offsetExif offsetlength
0x%x0x%x0x%x0x%x
"); } function test_print_header() { global $exif_head, $exif_data; $lh=count($exif_head); $ld=count($exif_data); printf ("

Exif size=%d bytes (head=%d, data=%d)

\n",$lh+$ld,$lh,$ld); printf ("\n"); for ($i=0; $i<$lh+$ld;$i=$i+16) { printf("\n",$i); for ($j=$i; $j<$i+16;$j++) { printf(""); } printf(""); for ($j=$i; $j< ($i+16);$j++) { printf(""); } printf("\n"); } printf ("
%03x|"); if ($j<($lh+$ld)) { $d=($j<$lh)?$exif_head[$j]:$exif_data[$j-$lh]; printf(" %02x",$d); } else printf (" "); printf("|"); if ($j<($lh+$ld)) { $d=($j<$lh)?$exif_head[$j]:$exif_data[$j-$lh]; if ($d<32 or $d>126) printf("."); else printf ("%c",$d); } else printf (" "); printf("
"); } function test_print_directory() { global $dir_entries,$frame_meta_size; printf ("

Frame meta data size=%d bytes

\n",$frame_meta_size); printf ("\n"); printf ("\n"); foreach ($dir_entries as $entry) printf ("\n",$entry["ltag"],$entry["src"],$entry["dst"],$entry["len"]); printf ("
ltagmeta offsetExif offsetlength
0x%x0x%x0x%x0x%x
"); } function start_ifd($count) { global $exif_data, $ifd_pointer; // printf("start_ifd: ifd_pointer=0x%04x \n", $ifd_pointer); $exif_data[$ifd_pointer++]= ($count >> 8) & 0xff; $exif_data[$ifd_pointer++]= $count & 0xff; // may apply & 0xff in the end to all elements } function finish_ifd() { // we do not have additional IFDs global $exif_data, $ifd_pointer; // printf("finish_ifd: ifd_pointer=0x%04x \n", $ifd_pointer); $exif_data[$ifd_pointer++]=0; $exif_data[$ifd_pointer++]=0; $exif_data[$ifd_pointer++]=0; $exif_data[$ifd_pointer++]=0; } //pass2 - building map from frame meta to Exif template function addDirEntry($ifd_entry) { global $dir_sequence,$dir_entries,$exif_head; $lh=count($exif_head); $attrs = $ifd_entry->attributes(); // var_dump($attrs); // if (array_key_exists ( "seq" , $attrs )) { if ($attrs["seq"]) { // echo $attrs["seq"].; $dir_sequence[]=((string) $attrs["seq"])+0; $len= (integer) $ifd_entry->value_length; $offs=$lh+(integer) $ifd_entry->value_offest; // if (array_key_exists ( "dlen" , $attrs )) if ($attrs["dlen"]) $len=min($len,((string) $attrs["dlen"])+0); $dir_entries[]=array("ltag"=>((integer)$ifd_entry->ltag),"dst"=>$offs,"len"=>$len); } } function substitute_value($ifd_entry) { global $SUB_IFD_offset,$GPSInfo_offset; global $DeviceSNFilename, $DeviceRevisionFilename, $DeviceBrand, $DeviceModel; $attrs = $ifd_entry->attributes(); switch ($attrs["function"]) { case "BRAND": $ifd_entry->addChild('value', $DeviceBrand); break; case "MODEL": if (file_exists ('/var/state/APPLICATION')) { $model= file_get_contents('/var/state/APPLICATION'); if (file_exists ('/var/state/APPLICATION_MODE')) { $model.=' CHN'.file_get_contents('/var/state/APPLICATION_MODE'); } } else { $model = $DeviceModel; } $ifd_entry->addChild ('value',$model); break; case "SOFTWARE": if (file_exists("/usr/html/docs/")) { $ifd_entry->addChild ('value',exec("ls /usr/html/docs/")); // filter } break; case "SERIAL": $s = ""; if (file_exists($DeviceSNFilename)) { $s = exec('cat '.$DeviceSNFilename); } $ifd_entry->addChild ('value',substr($s,0,2).":".substr($s,2,2).":".substr($s,4,2).":".substr($s,6,2).":".substr($s,8,2).":".substr($s,10,2)); break; case "EXIFTAG": $ifd_entry->addChild ('value',$SUB_IFD_offset); break; case "GPSTAG": $ifd_entry->addChild ('value',$GPSInfo_offset); break; } } function process_ifd_entry($ifd_entry, $group) { global $exif_data, $ifd_pointer, $data_pointer,$SUB_IFD_offset,$GPSInfo_offset; $attrs = $ifd_entry->attributes(); $ifd_tag= ((string) $attrs["tag"])+0; $ifd_format=constant("EXIF_".$attrs["format"]); $ifd_count= $attrs["count"]; // echo "\nifd_tag=$ifd_tag, entry=";print_r($ifd_entry); // echo "\nifd_count=$ifd_count"; // echo "\nifd_bytes:";var_dump($ifd_bytes); if (!$ifd_count) { if($ifd_format==EXIF_ASCII) $ifd_count=strlen($ifd_entry->value)+1; else $ifd_count=1 ; /// may change? } //echo "\nifd_count=$ifd_count"; $exif_data[$ifd_pointer++]= ($ifd_tag >> 8) & 0xff; $exif_data[$ifd_pointer++]= $ifd_tag & 0xff; $exif_data[$ifd_pointer++]= ($ifd_format >> 8 ) & 0xff; $exif_data[$ifd_pointer++]= $ifd_format & 0xff; $exif_data[$ifd_pointer++]= ($ifd_count >> 24) & 0xff; $exif_data[$ifd_pointer++]= ($ifd_count >> 16) & 0xff; $exif_data[$ifd_pointer++]= ($ifd_count >> 8) & 0xff; $exif_data[$ifd_pointer++]= $ifd_count & 0xff; $ifd_bytes=0; switch ($ifd_format) { case EXIF_SHORT: case EXIF_SSHORT: $ifd_bytes=2; break; case EXIF_LONG: case EXIF_SLONG: $ifd_bytes=4; break; case EXIF_RATIONAL: case EXIF_SRATIONAL: $ifd_bytes=8; break; default: $ifd_bytes=1; //1,2,6,7 } $ifd_bytes=$ifd_bytes*$ifd_count; // now prepare ifd_data - array of bytes switch ($ifd_format) { case EXIF_BYTE: case EXIF_SBYTE: $ifd_data= array (); foreach ($ifd_entry->value as $a) $ifd_data[]= $a & 0xff; break; case EXIF_ASCII: $ifd_data= str_split($ifd_entry->value); foreach($ifd_data as &$d) $d=ord($d); break; case EXIF_SHORT: case EXIF_SSHORT: $ifd_data= array (); foreach ($ifd_entry->value as $a) $ifd_data=array_merge($ifd_data,array(($a >> 8) & 0xff, $a & 0xff)); break; case EXIF_LONG: case EXIF_SLONG: $ifd_data= array (); foreach ($ifd_entry->value as $a) $ifd_data=array_merge($ifd_data,array(($a >> 24) & 0xff,($a >> 16) & 0xff,($a >> 8) & 0xff, $a & 0xff)); break; case EXIF_RATIONAL: case EXIF_SRATIONAL: $nom= array (); foreach ($ifd_entry->nominator as $a) $nom[]= array(($a >> 24) & 0xff,($a >> 16) & 0xff,($a >> 8) & 0xff, $a & 0xff); $denom= array (); foreach ($ifd_entry->denominator as $a) $denom[]=array(($a >> 24) & 0xff,($a >> 16) & 0xff,($a >> 8) & 0xff, $a & 0xff); $ifd_data= array (); /* var_dump($nom); echo "\n"; var_dump($denom); echo "\n"; //exit(0); */ for ($i=0;$iaddChild ('value_length',count($ifd_data)); // if (array_key_exists ( "ltag" , $attrs )) $ltag= ((string) $attrs["ltag"])+0; if ($attr["ltag"]) $ltag= ((string) $attrs["ltag"])+0; else $ltag= $ifd_tag+($group<<16) ; $ifd_entry->addChild ("ltag",$ltag ); if (count($ifd_data) <=4) { $ifd_entry->addChild ('value_offest',$ifd_pointer); $ifd_data= array_pad($ifd_data,-4,0); // add leading zeroes if <4 bytes for ($i=0;$i<4;$i++) $exif_data[$ifd_pointer++]=$ifd_data[$i]; } else { //pointer, not data $ifd_entry->addChild ('value_offest',$data_pointer); $exif_data[$ifd_pointer++]= ($data_pointer >> 24) & 0xff; $exif_data[$ifd_pointer++]= ($data_pointer >> 16) & 0xff; $exif_data[$ifd_pointer++]= ($data_pointer >> 8) & 0xff; $exif_data[$ifd_pointer++]= $data_pointer & 0xff; for ($i=0;$i