Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python rawkit how read metadata values from RAW file?

I'm writing python script and I need to obtain exif information from raw photo file (.CR2 for example).

I found Python Rawkit offer the ability to do that.

with Raw(filename=image_path) as raw:
  print raw.metadata

Metadata(aperture=-1.2095638073643314e+38, timestamp=4273602232L,
         shutter=-1.1962713245823862e+38, flash=True, 
         focal_length=-1.2228562901462766e+38, height=3753, 
         iso=-1.182978841800441e+38,
         make='Canon', model='EOS 5D Mark II', 
         orientation=0, width=5634)

But I'm a little bit confused, how read this values ?. For example I'm expecting iso value like 100/200/400 but what is -1.182978841800441e+38 ?

My question is not specific for iso, it's also for shutter, aperture, ...

I ckecked libraw and rawkit doc but was not able to find how read / convert this kind of values.

This part in the doc is not very detailed :

float iso_speed;
ISO sensitivity.

float shutter;
Shutter speed.

Can someone help me understand how to read these values?

Thanks

[Update]

As neo suggest, I will use ExifRead. In fact it's a better choice, I'm writting a python script. With ExifRead no need of extra C library dependency.

I was able to open Canon raw file and parse Exif but unfortunately facing a wrong value for the aperture :

EXIF ApertureValue (Ratio): 3
# My photo was taken in 2.8 (maybe a rounded value on this flag ?)

Quick answer : use Fnumber flag

EXIF FNumber (Ratio): 14/5 
14/5 is in fact 2.8 (do the math)

Long answer (how I found / debug that) :

Reading this exelent link Understanding What is stored in a Canon RAW .CR2 file, How and Why ( http://lclevy.free.fr/cr2/ ) I decided to decode myself and know what is going on.

This link send me on the graal to decode a raw file cr2_poster.pdf From that I thought the best value seems to be in my canon specific MakerNote section on the FNumber value. (All values description is here canon_tags)

Tag Id : 3 (In fact 0x0003 that you write 0x3) 
Name : FNumber

I opened my file with an Hexa editor (hexedit) and ... I was totally lost.

Key things :

  • An offset is a address in the file that will contain your value.
  • Read : C8 05 in the file should be read 05C8. Example for an offset, the address is 0x5C8

With that found the MakeNote section is easy.

Quick way is to search directly the 0x927c MarkerNote (so in the file 7C 92) flag that contain the address of the MakerNote section. If you are not able to found that, go throught the IFD section to find the EXIF subsection. And then in that subsection you will find the MakerNote section

Tag     Type   Count        Value
7C 92   07 00  B8 A0 00 00  84 03 00 00

Offset : 84 03 00 00 -> 00 00 03 84 (0x384 address)

Go to this address and search in the MakerNote section the FNumber 0x3

Tag     Type   Count        Value
03 00   03 00  04 00 00 00  C8 05  00 00

Go to the offset 0x5C8 to find our value (count 4 x type 3 ushort, 16 bits)

0x0x5C8 : 00 00 00 00  00 00 00 00

And ... fail, in fact my canon does not filled this section.

Reading http://www.exiv2.org/tags.html The FNumber can be found in EXIF subsection.

Do the same process to find the EXIF subsection and the tag "0x829d Exif.Image.FNumber type 5 Rational" Rational type is composed of 64 bits (numerator and denominator ulongs) Rational_data_type

Tag     Type   Count        Value
9D 82   05 00  01 00 00 00  34 03 00 00

And then read the 0x334 offset

1C 00 00 00  0A 00 00 00

As we can read in Hexa : 0x1C / 0XA In decimal, do the math : 28/10 = 14/5 = 2.8

Verify I have this value in ExifRead

EXIF.py 100EOS5D/IMG_8813.CR2 -vv | grep -i 14/5
EXIF FNumber (Ratio): 14/5

And voila !

I was looking for 2.8 float and this value is stored in fraction format. So the library don't do the math and just simplify the fraction.

This is why we have 14/5 and not 2.8 as expected.

like image 515
zipper Avatar asked Mar 14 '23 05:03

zipper


2 Answers

I suggest you use a library that is focused on EXIF reading. The stuff available in libraw/rawkit is really just a nice extra. I can recommend the ExifRead library. It's pure Python and also damn fast. And it gives you better to understand values.

like image 158
letmaik Avatar answered Mar 23 '23 06:03

letmaik


If compatibility with many formats is more of an issue to you than performance you could call exiftool as a subprocess with -j option to give you a json string which you can turn into a dictionary.

That should set you up for most raw formats and even stuff that isn't images at all. And it is going to squeeze every last bit of exif info out of the file. However in comparison with other options it is rather sluggish (like 200x slower):

from PIL import Image
import PIL.ExifTags
import subprocess
import json
import datetime
import exifread
filePath = "someImage.jpg"
filePath = "someRawImage.CR2"
filePath = "someMovie.mov"
filePath = "somePhotoshopImage.psd"


try:
    start = datetime.datetime.now()
    img = Image.open(filePath)
    exif_0 = {
        PIL.ExifTags.TAGS[k]: v
        for k, v in img.getexif().items()
        if k in PIL.ExifTags.TAGS
        }
    end = datetime.datetime.now()

    print("Pillow time:")
    print(end-start)
    print(str(len(exif_0)), "tags retrieved")
    print (exif_0, "\n")
except:
    pass

try:
    start = datetime.datetime.now()
    exif_1 = json.loads(subprocess.run(["/usr/local/bin/exiftool", "-j", filePath], stdout=subprocess.PIPE).stdout.decode("utf-8"))
    end = datetime.datetime.now()

    print("subprocess time:")
    print(end-start)
    print(str(len(exif_1[0])), "tags retrieved")
    print(exif_1, "\n")
except:
    pass

try:
    start = datetime.datetime.now()
    f = open(filePath, "rb")
    exif_2 = exifread.process_file(f)
    end = datetime.datetime.now()

    print("Exifread time:")
    print(end-start)
    print(str(len(exif_2)), "tags retrieved")
    print(exif_2, "\n")
except:
    pass
like image 30
t1m0 Avatar answered Mar 23 '23 05:03

t1m0