Exif MakerNotes auswerten

Klein0r

Erfahrenes Mitglied
Hallo zusammen,

ich suche mir hier schon die Finger wund aber irgendwie finde ich nirgends eine entsprechende LIB für mein Problem.

Ich würde gerne die MakerNotes in den EXIF-Daten von JPEG-Files auswerten. Hat jemand damit schon Erfahrungen und evt auch eine passende Klasse?

Das Problem ist das das ganze von Hersteller zu Hersteller unterschiedlich ist und somit sind die Daten bei jeder Kamera anders aufgebaut. Mir würde auch fürs erste Sony und Panasonic reichen.

http://code.creativecommons.org/viewsvn/xmp/pjmt/trunk/?pathrev=4259
Hier habe ich etwas gefunden aber wie es aussieht kann man dort nichts runterladen - oder ich bin zu blöd ;)

Folgendes habe ich unter http://de3.php.net/manual/de/function.exif-read-data.php gefunden:

09-Aug-2004 07:11
I've just released the "PHP JPEG Metadata Toolkit" which allows reading, writing and displaying of EXIF information, and does not need the --enable-exif option in PHP4.

It has been tested on 466 different models of digital cameras!

It can decode the following EXIF makernotes:
Agfa, Canon, Casio, Contax, Epson, Fujifilm, Konica, Minolta, Kyocera, Nikon, Olympus, Panasonic, Pentax (Asahi), Ricoh and Sony

Additionaly it can decode IPTC, XMP, Photoshop IRB and many other types of JPEG metadata

Try it out, and download it at:
http://www.ozhiker.com/electronics/pjmt/index.html

Leider ist der Download tot!
Hilfe ;)

lg
 
heey,

ich kann dir zwar nich sagen wie das geht aber du kannst die datein auf der Seite (http://code.creativecommons.org/view.../?pathrev=4259) eigentlich runterladen. Auf jeden fall klappt das bei mir. Du klickst einfach auf die datei die du brauchst und auf der nächsten Seite steht dann Download. Ich hoffe mal das wir auch wirklich von der gleichen Seite sprechen.
Ich hoffe ich konnte dir ein wenig helfen.

LG Nino
 
Hey ;)

10min nachdem ich hier gepostet hatte habe ich mich einfach nochmal mit der Seite beschäftigt und dann den download-button auch gefunden ;) Aber habe mich mit den Dateien nocht garnich beschäftigt. Scheint mir auch alles nen bischen durcheinander zu sein...

Falls also noch jemand ne gute Lösung hat: Brauche ich immernoch :(

lg
 
So es geht zwar nich um die MakerNotes aber ich muss mir dahin ja irgendwie den Weg freischlagen. Also hier schonmal eine Klasse (zusammengestellt aus oben genannter Quelle) die es ermöglicht einige Informationen aus einem JPEG-File auszulesen.

PHP:
<?

class JPEGInformation {
	
	private $JPEG_Segment_Names = array(
		0xC0 => "SOF0",
		0xC1 => "SOF1",
		0xC2 => "SOF2",
		0xC3 => "SOF4",
		0xC5 => "SOF5",
		0xC6 => "SOF6",
		0xC7 => "SOF7",
		0xC8 => "JPG",
		0xC9 => "SOF9",
		0xCA => "SOF10",
		0xCB => "SOF11",
		0xCD => "SOF13",
		0xCE => "SOF14",
		0xCF => "SOF15",
		0xC4 => "DHT",
		0xCC => "DAC",
		
		0xD0 => "RST0",
		0xD1 => "RST1",
		0xD2 => "RST2",
		0xD3 => "RST3",
		0xD4 => "RST4",
		0xD5 => "RST5",
		0xD6 => "RST6",
		0xD7 => "RST7",
		
		0xD8 => "SOI",
		0xD9 => "EOI",
		0xDA => "SOS",
		0xDB => "DQT",
		0xDC => "DNL",
		0xDD => "DRI",
		0xDE => "DHP",
		0xDF => "EXP",
		
		0xE0 => "APP0",
		0xE1 => "APP1",
		0xE2 => "APP2",
		0xE3 => "APP3",
		0xE4 => "APP4",
		0xE5 => "APP5",
		0xE6 => "APP6",
		0xE7 => "APP7",
		0xE8 => "APP8",
		0xE9 => "APP9",
		0xEA => "APP10",
		0xEB => "APP11",
		0xEC => "APP12",
		0xED => "APP13",
		0xEE => "APP14",
		0xEF => "APP15",
		
		0xF0 => "JPG0",
		0xF1 => "JPG1",
		0xF2 => "JPG2",
		0xF3 => "JPG3",
		0xF4 => "JPG4",
		0xF5 => "JPG5",
		0xF6 => "JPG6",
		0xF7 => "JPG7",
		0xF8 => "JPG8",
		0xF9 => "JPG9",
		0xFA => "JPG10",
		0xFB => "JPG11",
		0xFC => "JPG12",
		0xFD => "JPG13",
		
		0xFE => "COM",
		0x01 => "TEM",
		0x02 => "RES",
	);
	
	private $JPEG_Segment_Descriptions = array(
		
		0xC0 => "Start Of Frame (SOF) Huffman  - Baseline DCT",
		0xC1 => "Start Of Frame (SOF) Huffman  - Extended sequential DCT",
		0xC2 => "Start Of Frame Huffman  - Progressive DCT (SOF2)",
		0xC3 => "Start Of Frame Huffman  - Spatial (sequential) lossless (SOF3)",
		0xC5 => "Start Of Frame Huffman  - Differential sequential DCT (SOF5)",
		0xC6 => "Start Of Frame Huffman  - Differential progressive DCT (SOF6)",
		0xC7 => "Start Of Frame Huffman  - Differential spatial (SOF7)",
		0xC8 => "Start Of Frame Arithmetic - Reserved for JPEG extensions (JPG)",
		0xC9 => "Start Of Frame Arithmetic - Extended sequential DCT (SOF9)",
		0xCA => "Start Of Frame Arithmetic - Progressive DCT (SOF10)",
		0xCB => "Start Of Frame Arithmetic - Spatial (sequential) lossless (SOF11)",
		0xCD => "Start Of Frame Arithmetic - Differential sequential DCT (SOF13)",
		0xCE => "Start Of Frame Arithmetic - Differential progressive DCT (SOF14)",
		0xCF => "Start Of Frame Arithmetic - Differential spatial (SOF15)",
		0xC4 => "Define Huffman Table(s) (DHT)",
		0xCC => "Define Arithmetic coding conditioning(s) (DAC)",
		
		0xD0 => "Restart with modulo 8 count 0 (RST0)",
		0xD1 => "Restart with modulo 8 count 1 (RST1)",
		0xD2 => "Restart with modulo 8 count 2 (RST2)",
		0xD3 => "Restart with modulo 8 count 3 (RST3)",
		0xD4 => "Restart with modulo 8 count 4 (RST4)",
		0xD5 => "Restart with modulo 8 count 5 (RST5)",
		0xD6 => "Restart with modulo 8 count 6 (RST6)",
		0xD7 => "Restart with modulo 8 count 7 (RST7)",
		
		0xD8 => "Start of Image (SOI)",
		0xD9 => "End of Image (EOI)",
		0xDA => "Start of Scan (SOS)",
		0xDB => "Define quantization Table(s) (DQT)",
		0xDC => "Define Number of Lines (DNL)",
		0xDD => "Define Restart Interval (DRI)",
		0xDE => "Define Hierarchical progression (DHP)",
		0xDF => "Expand Reference Component(s) (EXP)",
		
		0xE0 => "Application Field 0 (APP0) - usually JFIF or JFXX",
		0xE1 => "Application Field 1 (APP1) - usually EXIF or XMP/RDF",
		0xE2 => "Application Field 2 (APP2) - usually Flashpix",
		0xE3 => "Application Field 3 (APP3)",
		0xE4 => "Application Field 4 (APP4)",
		0xE5 => "Application Field 5 (APP5)",
		0xE6 => "Application Field 6 (APP6)",
		0xE7 => "Application Field 7 (APP7)",
		
		0xE8 => "Application Field 8 (APP8)",
		0xE9 => "Application Field 9 (APP9)",
		0xEA => "Application Field 10 (APP10)",
		0xEB => "Application Field 11 (APP11)",
		0xEC => "Application Field 12 (APP12) - usually [picture info]",
		0xED => "Application Field 13 (APP13) - usually photoshop IRB / IPTC",
		0xEE => "Application Field 14 (APP14)",
		0xEF => "Application Field 15 (APP15)",
		
		
		0xF0 => "Reserved for JPEG extensions (JPG0)",
		0xF1 => "Reserved for JPEG extensions (JPG1)",
		0xF2 => "Reserved for JPEG extensions (JPG2)",
		0xF3 => "Reserved for JPEG extensions (JPG3)",
		0xF4 => "Reserved for JPEG extensions (JPG4)",
		0xF5 => "Reserved for JPEG extensions (JPG5)",
		0xF6 => "Reserved for JPEG extensions (JPG6)",
		0xF7 => "Reserved for JPEG extensions (JPG7)",
		0xF8 => "Reserved for JPEG extensions (JPG8)",
		0xF9 => "Reserved for JPEG extensions (JPG9)",
		0xFA => "Reserved for JPEG extensions (JPG10)",
		0xFB => "Reserved for JPEG extensions (JPG11)",
		0xFC => "Reserved for JPEG extensions (JPG12)",
		0xFD => "Reserved for JPEG extensions (JPG13)",
		
		0xFE => "Comment (COM)",
		0x01 => "For temp private use arith code (TEM)",
		0x02 => "Reserved (RES)",
		
	);
	
	private $filepath;
	
	private $jpeg_app_seg = array();
	private $jpeg_intrinsic_values = array();
	private $jpeg_comment = null;
	private $jpeg_fif = array();
	
	public function __construct($filepath) {
		$this->filepath = $filepath;
		$this->get_jpeg_header_data();
	}
	
	private function get_jpeg_header_data() {
		// prevent refresh from aborting file operations and hosing file
		ignore_user_abort(true);
		
		$filehnd = @fopen($this->filepath, 'rb');
		
		if (!$filehnd)
		{
			throw new Exception('Could not open file '.$this->filepath);
		}
		
		$data = $this->network_safe_fread($filehnd, 2);
		
		// Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
		if ($data != "\xFF\xD8")
		{
			fclose($filehnd);
			throw new Exception('This probably is not a JPEG file');
		}
		
		// Read the third character
		$data = $this->network_safe_fread($filehnd, 2);
		
		// Check that the third character is 0xFF (Start of first segment header)
		if ( $data{0} != "\xFF" )
		{
			fclose($filehnd);
			throw new Exception('JPEG file corrupted');
		}
		
		// Flag that we havent yet hit the compressed image data
		$hit_compressed_image_data = FALSE;
		
		
		// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
		//                                       2) we have hit the compressed image data (no more headers are allowed after data)
		//                                       3) or end of file is hit
		
		while (($data{1} != "\xD9") && !$hit_compressed_image_data && !feof($filehnd))
		{
			// Found a segment to look at.
			// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
			if (ord($data{1}) < 0xD0 || ord($data{1}) > 0xD7)
			{
				// Segment isn't a Restart marker
				// Read the next two bytes (size)
				$sizestr = $this->network_safe_fread($filehnd, 2);
				
				// convert the size bytes to an integer
				$decodedsize = unpack ("nsize", $sizestr);
				
				// Save the start position of the data
				$segdatastart = ftell($filehnd);
				
				// Read the segment data with length indicated by the previously read size
				$segdata = $this->network_safe_fread( $filehnd, $decodedsize['size'] - 2 );
				
				// Store the segment information in the output array
				$headerdata[] = array(
					"SegType" => ord($data{1}),
					"SegName" => $this->JPEG_Segment_Names[ord($data{1})],
					"SegDesc" => $this->JPEG_Segment_Descriptions[ord($data{1})],
					"SegDataStart" => $segdatastart,
					"SegData" => $segdata
				);
			}
			
			// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
			if ( $data{1} == "\xDA" )
			{
				// Flag that we have hit the compressed image data - exit loop as no more headers available.
				$hit_compressed_image_data = TRUE;
			}
			else
			{
				// Not an SOS - Read the next two bytes - should be the segment marker for the next segment
				$data = $this->network_safe_fread( $filehnd, 2 );
				
				// Check that the first byte of the two is 0xFF as it should be for a marker
				if ( $data{0} != "\xFF" )
				{
					fclose($filehnd);
					throw new Exception('JPEG file corrupted');
				}
			}
		}
		
		// Close File
		fclose($filehnd);
		// Alow the user to abort from now on
		ignore_user_abort(false);
		
		// Fill all Arrays
		$this->fill_jpeg_app_seg($headerdata);
		$this->fill_jpeg_intrinsic_values($headerdata);
		$this->fill_jpeg_comment($headerdata);
		$this->fill_jpeg_fif($headerdata);
	}
	
	private function network_safe_fread($file_handle, $length)
	{
		$data = "";
		
		while (!feof($file_handle) && (strlen($data) < $length))
		{
			$data .= fread($file_handle, $length - strlen($data));
		}
		
		return $data;
	}
	
	
	private function fill_jpeg_app_seg($jpeg_header_data) {
		foreach($jpeg_header_data as $jpeg_header) {
			if ($jpeg_header['SegType'] >= 0xE0 && $jpeg_header['SegType'] <= 0xEF) {
				
				$seg_name = strtok($jpeg_header['SegData'], "\x00");
				
				if ($seg_name == 'http://ns.adobe.com/xap/1.0/') {
					// http://ns.adobe.com/xap/1.0/
					$seg_name = 'XAP/RDF';
				}
				elseif ($seg_name == 'Photoshop 3.0') {
					$seg_name = 'Photoshop IRB (3.0)';
				}
				elseif (strncmp($seg_name, "[picture info]", 14) == 0 || strncmp ($seg_name, "\x0a\x09\x09\x09\x09[picture info]", 19) == 0) {
					$seg_name = '[picture info]';
				}
				elseif (strncmp($seg_name, "Type=", 5) == 0) {
					$seg_name = 'Epson Info';
				}
				elseif (strncmp($seg_name, "HHHHHHHHHHHHHHH", 15) == 0 || strncmp($seg_name, "@s33", 5) == 0) {
					$seg_name = 'HP segment full of "HHHHH"';
				}
				
				$this->jpeg_app_seg[$seg_name] = strlen($jpeg_header['SegData']);
				
			}
		}
	}
	
	public function get_jpeg_app_seg() {
		return $this->jpeg_app_seg;
	}
	
	
	private function fill_jpeg_intrinsic_values($jpeg_header_data) {
		
		//Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments
		$i = 0;
		while ($i < count($jpeg_header_data) && substr($jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF") {
			$i++;
		}
		
		// Check if a SOF segment has been found
		if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" ) {
			// SOF segment was found, extract the information
			
			$data = $jpeg_header_data[$i]['SegData'];
			
			// First byte is Bits per component
			$bpc = ord( $data{0} );
			
			// Forth and fifth bytes are Image Width
			$this->jpeg_intrinsic_values['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } );
			
			
			// Second and third bytes are Image Height
			$this->jpeg_intrinsic_values['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } );
			
			// Sixth byte is number of components
			$numcomponents = ord($data{ 5 });
			
			// Following this is a table containing information about the components
			// for($i = 0; $i < $numcomponents; $i++) {
				// $this->jpeg_intrinsic_values['Components'][] = array (
					// 'Component Identifier' => ord($data{ 6 + $i * 3 }),
					// 'Horizontal Sampling Factor' => (ord($data{ 7 + $i * 3 }) & 0xF0 ) / 16,
					// 'Vertical Sampling Factor' => (ord($data{ 7 + $i * 3 }) & 0x0F ),
					// 'Quantization table destination selector' => ord($data{ 8 + $i * 3 })
				// );
			// }
			
			if (count($this->jpeg_intrinsic_values['Components']) == 1) {
				$this->jpeg_intrinsic_values['Colour Depth'] = $bpc.' Bit Monochrome';
			}
			else {
				$this->jpeg_intrinsic_values['Colour Depth'] = $bpc * $numcomponents.' Bit';
			}
			
		}
		
	}
	
	public function jpeg_intrinsic_values() {
		return $this->jpeg_intrinsic_values;
	}
	
	
	private function fill_jpeg_comment($jpeg_header_data) {
		
		$i = 0;
		while ($i < count($jpeg_header_data) && $jpeg_header_data[$i]['SegName'] != "COM") {
			$i++;
		}
		
		// Check if a COM segment has been found
		if ($i < count($jpeg_header_data)) {
			$this->jpeg_comment = $jpeg_header_data[$i]['SegData'];
		}
	}
	
	public function get_jpeg_comment() {
		return $this->jpeg_comment;
	}
	
	
	private function fill_jpeg_fif($jpeg_header_data) {
		for($i=0; $i < count($jpeg_header_data); $i++) {
			
			// If we find an APP0 header,
			if (strcmp($jpeg_header_data[$i]['SegName'], "APP0") == 0) {
				// And if it has the JFIF label,
				if(strncmp($jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0)
				{
					
					// Found a JPEG File Interchange Format (JFIF) Block
					
					// unpack the JFIF data from the incoming string
					// First is the JFIF label string
					// Then a two byte version number
					// Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm)
					// Then a two byte int X-Axis pixel Density (resolution)
					// Then a two byte int Y-Axis pixel Density (resolution)
					// Then a byte X-Axis JFIF thumbnail size
					// Then a byte Y-Axis JFIF thumbnail size
					// Then the uncompressed RGB JFIF thumbnail data
					
					$this->jpeg_fif = unpack('a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData']);
				}
			}
		}
	}
	
	public function get_jpeg_fif() {
		return $this->jpeg_fif;
	}
	
}

?>

Anwendungsbeispiel (demächst auf meiner Homepage ;) ):
PHP:
			$jpeg_file = new JPEGInformation($filepath_big);
			$jpegappseg = $jpeg_file->get_jpeg_app_seg();
			$jpegintrinsic = $jpeg_file->jpeg_intrinsic_values();
			$jpegcomment = $jpeg_file->get_jpeg_comment();
			$jpegfif = $jpeg_file->get_jpeg_fif();
			
			// Application Metadata Segments
			if (count($jpegappseg) > 0) {
				$this->draw_content_begin();
				echo 'Application Metadata Segments';
				echo '<table class="details">';
				while(list($k, $v) = each($jpegappseg)){
					echo '
					<tr>
						<td class="tableleft">
							'.$k.'
						</td>
						<td class="tableright">
							'.$this->fileSizeFormat($v).'
						</td>
					</tr>
					';
				}
				echo '</table>';
				$this->draw_content_end();
			}
			
			// Intrinsic JPEG Information
			if (count($jpegintrinsic) > 0) {
				$this->draw_content_begin();
				echo 'Intrinsic JPEG Information';
				
				echo '<table class="details">';
				while(list($k, $v) = each($jpegintrinsic)){
					echo '
					<tr>
						<td class="tableleft">
							'.$k.'
						</td>
						<td class="tableright">
							'.$v.'
						</td>
					</tr>
					';
				}
				echo '</table>';
				$this->draw_content_end();
			}
			
			// JPEG Comment
			if ($jpegcomment != null) {
				$this->draw_content_begin();
				echo 'JPEG Comment';
				
				echo '<div class="comment">
				'.$jpegcomment.'
				</div>';
				$this->draw_content_end();
			}
			
			// JPEG File Interchange Format (JFIF) Information
			if (count($jpegfif) > 0) {
				$this->draw_content_begin();
				echo 'JPEG File Interchange Format (JFIF) Information';
				
				echo '<table class="details">';
				while(list($k, $v) = each($jpegfif)){
					echo '
					<tr>
						<td class="tableleft">
							'.$k.'
						</td>
						<td class="tableright">
							'.$v.'
						</td>
					</tr>
					';
				}
				echo '</table>';
				$this->draw_content_end();
			}

Beispielausgabe:
Code:
Application Metadata Segments
JFIF 	14 Byte 
Exif 	4.31 KB 
Photoshop IRB (3.0) 	4.71 KB 
XAP/RDF 	5.49 KB 
ICC_PROFILE 	3.08 KB 
Adobe 	12 Byte 

Intrinsic JPEG InformationImage
Width 	3264 
Image Height 	2448 
Colour Depth 	24 Bit 

JPEG File Interchange Format (JFIF) Information
JFIF 	JFIF 
Version1 	1 
Version2 	2 
Units 	1 
XDensity 	72 
YDensity 	72 
ThumbX 	0 
ThumbY 	0 
ThumbData

Ich arbeite gerade daran die IRB-Informationen von Photoshop auch in eine Klasse zu verpacken. Aber das wird wohl noch eine weile dauern...

Werde Ergebnisse aber hier veröffentlichen!

lg
 
Da es hier irgendwie niemanden so richtig interessiert wir mir scheint poste ich keinen Quellcode mehr dazu. Fragen hier oder per PN.

Daten gibts auch per PN ;)
 
Zurück