Juravskiy Vitaliy`s blog
Чтение EXIF данных из JPEG файла
По просьбе читателей глянул в сторону чтения мета данных (в формате EXIF) из файлов изображений, в частности из JPEG изображений. Когда цифровой формат производит снимок, он заносит в мета данные информацию о фото, например могут быть такие параметры как производитель фотокамеры, модель, информация о вспышке и другие интересные данные. Например в формате TIFF, который широко используется в геоинформационной сфере, хранят географические данные об объектах. В процессе поиска путей решения, выяснилось, что средствами стандартного JDK проблему задачу решить можно, но писать кучу кода реализующего чтение (запись) данных в файл изображения по стандарту EXIF не хотелось. Я пошел по пути наименьшего сопротивления.
В начале думалось, что можно все решить с помощью пакета javax.imageio.* в частности у класса javax.imageio.ImageReader есть метод getImageMetadata который подкупал своим названием, казалось пробелам решена.
Приведу пример кода, который я написал для работы с IIOMetadata. Может кому то пригодиться.
public static void displayMetadata(IIOMetadata metadata) { String[] names = metadata.getMetadataFormatNames(); for (int i = 0; i < names.length; ++i) { System.out.println(); System.out.println("METADATA FOR FORMAT: " + names[i]); System.out.println(displayTree(metadata.getAsTree(names[i]), 0, null).toString()); } } public static StringBuffer displayTree(Node node, int indent, StringBuffer b) { if(b == null) b = new StringBuffer(); indent(indent, b); String name = node.getNodeName(); b.append("<").append(name); if (node.hasAttributes()) { NamedNodeMap attrs = node.getAttributes(); for (int i = 0, ub = attrs.getLength(); i < ub; ++i) { Node attr = attrs.item(i); b.append(" ") .append(attr.getNodeName()) .append("=") .append(attr.getNodeValue()) .append(""); } } if (node.hasChildNodes()) { b.append(">\n"); NodeList children = node.getChildNodes(); for (int i = 0, ub = children.getLength(); i < ub; ++i) displayTree(children.item(i), indent + 4, b); indent(indent, b); b.append("</") .append(name) .append(">\n"); } else b.append("/>\n"); return b; } static void indent(int indent, StringBuffer b) { for (int i = 0; i < indent; ++i) b.append(" "); } public static void main(String[] args) throws IOException { ImageReader r = getImageReaderByFileExtension("c:/test_image/nikon.jpg"); r.setInput(new FileImageInputStream(new File("c:/test_image/nikon.jpg"))); IIOMetadata meta = r.getImageMetadata(0); displayMetadata(meta); System.out.println(displayTree(meta.getAsTree(meta.getNativeMetadataFormatName()), 0, null)); }Результат выполнения программы следующий:
METADATA FOR FORMAT: javax_imageio_jpeg_image_1.0
<javax_imageio_jpeg_image_1.0>
<JPEGvariety>
<app0JFIF majorVersion=1 minorVersion=2 resUnits=1 Xdensity=72 Ydensity=72 thumbWidth=0 thumbHeight=0/>
</JPEGvariety>
<markerSequence>
<unknown MarkerTag=225/>
<unknown MarkerTag=237/>
<app14Adobe version=100 flags0=0 flags1=0 transform=1/>
<dqt>
<dqtable elementPrecision=0 qtableId=0/>
<dqtable elementPrecision=0 qtableId=1/>
</dqt>
<sof process=0 samplePrecision=8 numLines=600 samplesPerLine=800 numFrameComponents=3>
<componentSpec componentId=1 HsamplingFactor=1 VsamplingFactor=1 QtableSelector=0/>
<componentSpec componentId=2 HsamplingFactor=1 VsamplingFactor=1 QtableSelector=1/>
<componentSpec componentId=3 HsamplingFactor=1 VsamplingFactor=1 QtableSelector=1/>
</sof>
<dri interval=100/>
<dht>
<dhtable class=0 htableId=0/>
<dhtable class=0 htableId=1/>
<dhtable class=1 htableId=0/>
<dhtable class=1 htableId=1/>
</dht>
<sos numScanComponents=3 startSpectralSelection=0 endSpectralSelection=63 approxHigh=0 approxLow=0>
<scanComponentSpec componentSelector=1 dcHuffTable=0 acHuffTable=0/>
<scanComponentSpec componentSelector=2 dcHuffTable=1 acHuffTable=1/>
<scanComponentSpec componentSelector=3 dcHuffTable=1 acHuffTable=1/>
</sos>
</markerSequence>
</javax_imageio_jpeg_image_1.0>
METADATA FOR FORMAT: javax_imageio_1.0
<javax_imageio_1.0>
<Chroma>
<ColorSpaceType name=YCbCr/>
<NumChannels value=3/>
</Chroma>
<Compression>
<CompressionTypeName value=JPEG/>
<Lossless value=false/>
<NumProgressiveScans value=1/>
</Compression>
<Dimension>
<PixelAspectRatio value=1.0/>
<ImageOrientation value=normal/>
<HorizontalPixelSize value=0.35277778/>
<VerticalPixelSize value=0.35277778/>
</Dimension>
</javax_imageio_1.0>
<javax_imageio_jpeg_image_1.0>
<JPEGvariety>
<app0JFIF majorVersion=1 minorVersion=2 resUnits=1 Xdensity=72 Ydensity=72 thumbWidth=0 thumbHeight=0/>
</JPEGvariety>
<markerSequence>
<unknown MarkerTag=225/>
<unknown MarkerTag=237/>
<app14Adobe version=100 flags0=0 flags1=0 transform=1/>
<dqt>
<dqtable elementPrecision=0 qtableId=0/>
<dqtable elementPrecision=0 qtableId=1/>
</dqt>
<sof process=0 samplePrecision=8 numLines=600 samplesPerLine=800 numFrameComponents=3>
<componentSpec componentId=1 HsamplingFactor=1 VsamplingFactor=1 QtableSelector=0/>
<componentSpec componentId=2 HsamplingFactor=1 VsamplingFactor=1 QtableSelector=1/>
<componentSpec componentId=3 HsamplingFactor=1 VsamplingFactor=1 QtableSelector=1/>
</sof>
<dri interval=100/>
<dht>
<dhtable class=0 htableId=0/>
<dhtable class=0 htableId=1/>
<dhtable class=1 htableId=0/>
<dhtable class=1 htableId=1/>
</dht>
<sos numScanComponents=3 startSpectralSelection=0 endSpectralSelection=63 approxHigh=0 approxLow=0>
<scanComponentSpec componentSelector=1 dcHuffTable=0 acHuffTable=0/>
<scanComponentSpec componentSelector=2 dcHuffTable=1 acHuffTable=1/>
<scanComponentSpec componentSelector=3 dcHuffTable=1 acHuffTable=1/>
</sos>
</markerSequence>
</javax_imageio_jpeg_image_1.0>
Вроде это и мета данные но не те, которые нам надо, где информация о фотокамере, и все остальное. Не в даваясь в подробности что мы получили занялся поиском библиотеки или решения стандартными средствами JDK. Нашел такую небольшую библиотеку metadata extraction in java она позволяет извлекать EXIF данные из изображений, и писать при этом мало кода.
Скачиваем последнюю библиотеку из списка релизов и добавляем в проект среды разработки. Примеры использования библиотеки можно глянуть на странице примеров.
У меня получился следующий код:
public static void main(String[] args) throws JpegProcessingException { File jpegFile = new File("c:/test_image/nikon.jpg"); Metadata metadata = new ExifReader(jpegFile).extract(); Iterator<Directory> directories = metadata.getDirectoryIterator(); while (directories.hasNext()) { Directory directory = directories.next(); Iterator<Tag> tags = directory.getTagIterator(); while (tags.hasNext()) { Tag tag = tags.next(); System.out.println(tag); } } }А вывод EXIF данных следующий:
[Exif] Image Description -
[Exif] Make - NIKON
[Exif] Model - E950
[Exif] Orientation - Top, left side (Horizontal / normal)
[Exif] X Resolution - 300 dots per inch
[Exif] Y Resolution - 300 dots per inch
[Exif] Resolution Unit - Inch
[Exif] Software - v981-79
[Exif] Date/Time - 2001:04:06 11:51:40
[Exif] YCbCr Positioning - Datum point
[Exif] Exposure Time - 1/77 sec
[Exif] F-Number - F5,5
[Exif] Exposure Program - Program normal
[Exif] ISO Speed Ratings - 80
[Exif] Exif Version - 2.10
[Exif] Date/Time Original - 2001:04:06 11:51:40
[Exif] Date/Time Digitized - 2001:04:06 11:51:40
[Exif] Components Configuration - YCbCr
[Exif] Compressed Bits Per Pixel - 4 bits/pixel
[Exif] Exposure Bias Value - 0 EV
[Exif] Max Aperture Value - F2,5
[Exif] Metering Mode - Multi-segment
[Exif] Light Source - Unknown
[Exif] Flash - Flash did not fire
[Exif] Focal Length - 12,8 mm
[Exif] User Comment -
[Exif] FlashPix Version - 1.00
[Exif] Color Space - sRGB
[Exif] Exif Image Width - 1600 pixels
[Exif] Exif Image Height - 1200 pixels
[Exif] File Source - Digital Still Camera (DSC)
[Exif] Scene Type - Directly photographed image
[Exif] Compression - JPEG (old-style)
[Exif] Thumbnail Offset - 2036 bytes
[Exif] Thumbnail Length - 4662 bytes
[Exif] Thumbnail Data - [4662 bytes of thumbnail data]
[Nikon Makernote] Makernote Unknown 1 - 08.00
[Nikon Makernote] Quality - Unknown (12)
[Nikon Makernote] Color Mode - Color
[Nikon Makernote] Image Adjustment - Contrast +
[Nikon Makernote] CCD Sensitivity - ISO80
[Nikon Makernote] White Balance - Auto
[Nikon Makernote] Focus - 0
[Nikon Makernote] Makernote Unknown 2 -
[Nikon Makernote] Digital Zoom - No digital zoom
[Nikon Makernote] Fisheye Converter - None
[Nikon Makernote] Makernote Unknown 3 - 0 0 16777216 0 -1609193200 0 34833 6931 16178 4372 4372 -972290529 -921882880 15112 0 0 1151495 252903424 17 0 0 844038208 55184128 218129428 1476410198 370540566 -250604010 16711749 204629079 1729
[Interoperability] Interoperability Index - Recommended Exif Interoperability Rules (ExifR98)
[Interoperability] Interoperability Version - 1.00
Информация очень полезна при обработке изображений или например сборе статистических данных на больших архивах изображений. Например сервис Flickr на основе EXIF данных предоставляет статистическую информацию о том какими фотоаппаратами пользователи делают снимки. А все современные интернет сервисы связанные с фотографиями предоставляют просмотр EXIF информации о фото, ее обычно можно найти рядом с просматриваемой фотографией.
Теги:
Java
Images
Exif
чтение Exif в java