letmaik / rawpy

📷 RAW image processing for Python, a wrapper for libraw
https://pypi.python.org/pypi/rawpy
MIT License
587 stars 67 forks source link

Reading 16-bit DNG image returns wrong image sizes #139

Open magsail opened 3 years ago

magsail commented 3 years ago

I was using rawpy to read 16-bit DNG images and unfortunately I found the image size returned was wrong.

To reproduce the issue, let's look at this image https://data.csail.mit.edu/graphics/fivek/img/dng/a0001-jmac_DSC1459.dng

This is a 16-bit DNG image taken by Nikon D70 back in 2004. Either download it and check EXIF locally or check it online gives the resolution to be 3008x2004 (or 2004x3008, this is not the issue).

However, if I read the image with the code snippet below. It will return Numpy tensor size of (3, 2014, 3040), which does not match the above. And surely 3008x2004 is correct.

import rawpy

def read_dng_image(path, bitspersample=16):
  with rawpy.imread(path) as raw:
    np_img = raw.postprocess(output_bps=bitspersample)
    np_img= np_img.transpose(2, 0, 1) # (H, W, C) to (C, H, W)
  return np_img

img = read_dng_image('a0001-jmac_DSC1459.dng')
print(img.shape)

I'm running on Ubuntu 16.04 with python 3.8.3

Any idea about the cause?

What wroth to mention is that if I open such DNG image with Ubuntu's photo editor - shotwell, it also gives the wrong image size. Could it be the because both rawpy and shotwell share the same low level library that resulted in the same wrong value?

magsail commented 3 years ago

To save your time see this online EXIF checker that returns resolution of 3008x2004

http://metapicz.com/#landing?imgsrc=https%3A%2F%2Fdata.csail.mit.edu%2Fgraphics%2Ffivek%2Fimg%2Fdng%2Fa0001-jmac_DSC1459.dng

kmilos commented 3 years ago

From exiv2 output it should be 3008x2000 actually:

Exif.SubImage1.DefaultCropOrigin             Rational    2  15/1 7/1
Exif.SubImage1.DefaultCropSize               Rational    2  3008/1 2000/1

It would be surprising, but it might just happen that the underlying LibRaw library doesn't the deal w/ the rational number format for these tags, which are more commonly integers...

kmilos commented 3 years ago

Scratch that, looks like it can read those values ok:

https://github.com/LibRaw/LibRaw/blob/1dbed6b7e65ef2ebd0c3f82722a0b7f3990845f7/src/metadata/tiff.cpp#L1157

https://github.com/LibRaw/LibRaw/blob/1dbed6b7e65ef2ebd0c3f82722a0b7f3990845f7/src/utils/read_utils.cpp#L101

so the issue is somewhere else...

letmaik commented 3 years ago

https://www.awaresystems.be/imaging/tiff/tifftags/defaultcropsize.html Looks like libraw gives you a few more pixels each side. It's not necessarily wrong, just doesn't correspond to the default crop.

kmilos commented 3 years ago

Digging around LibRaw a bit more, there used to be a LIBRAW_PROCESSING_USE_DNG_DEFAULT_CROP processing flag that had to be enabled to get this crop in 0.19. It seems to be gone in 0.20 in favor of imgdata.sizes.raw_inset_crop, but I guess rawpy doesn't go into exposing this...

Perhaps the raw.sizes property gives the correct margins and crop so it can be done by hand?

kmilos commented 3 years ago

Nope, doesn't come through w/ rawpy 0.16 and LibRaw 0.20:

ImageSizes(raw_height=2014, raw_width=3040, height=2014, width=3040, top_margin=0, left_margin=0, iheight=2014, iwidth=3040, pixel_aspect=1.0, flip=0)

I don't see imgdata.sizes.raw_inset_crop hooked up to anything else in LibRaw source code, so might have to expose it in rawpy by refreshing ImageSizes...

kmilos commented 2 years ago

Yep, this API change is now documented in the latest LibRaw snapshot: https://www.libraw.org/news/libraw-202110-snapshot

jtomori commented 1 week ago

Hello, I'd like to ask if you are planning to expose the crop information in the ImageSizes named tuple.

I have a need to output an image with the cropped dimensions and would prefer avoiding adding another dependency for querying that information.

Best, Juraj