rawspeed icon indicating copy to clipboard operation
rawspeed copied to clipboard

ARW2 files may need extra dithering

Open pedrocr opened this issue 10 years ago • 3 comments
trafficstars

ARW2 files use a weird encoding where groups of 16 pixels are encoded as 16 bytes by having the max and minimum pixel encoded with 11 bits and then the other 14 encoded as 7bit differences from the min. When the difference between min and max is too large those 7 bits need to be shifted left so the encoding loses resolution in the low order bits. When the max-min difference is very large and the image is smooth this can lead to noticeable artifacts. Here's an article detailing this including a sample image with the issue:

http://www.rawdigger.com/howtouse/sony-craw-arw2-posterization-detection

I was wondering if this can be improved upon by using dithering. I tried this patch with very limited success:

--- a/src/external/rawspeed/RawSpeed/ArwDecoder.cpp
+++ b/src/external/rawspeed/RawSpeed/ArwDecoder.cpp
@@ -399,6 +399,9 @@ void ArwDecoder::decodeThreaded(RawDecoderThread * t) {
     bits.setAbsoluteOffset((w*8*y) >> 3);
     uint32 random = bits.peekBits(24);

+    // Initialize random state so we always return the same data
+    uint32 randstate = bits.peekBits(32);
+
     // Process 32 pixels (16x2) per loop.
     for (uint32 x = 0; x < w - 30;) {
       bits.checkPos();
@@ -413,7 +416,8 @@ void ArwDecoder::decodeThreaded(RawDecoderThread * t) {
         if (i == _imax) p = _max;
         else if (i == _imin) p = _min;
         else {
-          p = (bits.getBits(7) << sh) + _min;
+          uint32 r = rand_r(&randstate) & ~(0xffffffff << sh);
+          p = ((bits.getBits(7) << sh) | r) + _min;
           if (p > 0x7ff)
             p = 0x7ff;
         }

This just sets the least significant bits to random values instead of 0. At least for the artifact around the star trails on the example image this looks like an improvement but only very slightly.

Any other ideas on how to improve this?

pedrocr avatar Jan 19 '15 23:01 pedrocr

Having thought about this some more and ran some tests I'm wondering if there's something wrong with how we decode these pixels. If sh>0 the encoder has either rounded or truncated the difference to 7 bits. If it rounded we will estimate the diff consistently higher by 0.5. And if it truncated we will estimate the diff as consistently lower by ((2^sh)-1)/2 or 0.5,1.5,3.5,7.5 for sh 1,2,3,4. That's because for example for sh=2 values 0,1,2,3 are all encoded as 0 and thus decoded as 0, but their average is actually (0+1+2+3)/4=1.5.

pedrocr avatar Jan 20 '15 02:01 pedrocr

I have not looked deeper into this. On looking at your code, I assume rand_r transforms the value?

In general, I don't really like the idea of adjusting dithering, since it will lead to an uneven noise floor on the image. That said, the file format is broken by design, and it might lead to slightly better base-image than we would otherwise get.

Do note that simply shifting in bits, you are always adding to the number. You might get better results if you subtract a quarter of the delta value, and add half the delta value, so your average is the 'before' pixel value:

 // rand is 0 to 1, delta is the delta between pixel values.
  out = value + (rand * 0.5 * delta) - (0.25 * delta)

klauspost avatar Mar 03 '15 23:03 klauspost

What may also be happening in this case is that in some cases these cameras seem to drop from 13bit to 12bit output:

http://blog.kasson.com/?p=9674

pedrocr avatar Apr 04 '15 10:04 pedrocr