Search code examples
pythonpython-3.xnumpyimage-resizingscikit-image

skimage resize changes total sum of array


I want to resize an image in fits format to a smaller dimension. For example, I would like to resize my 100x100 pixel image to a 58x58 pixel image. The values of the array are intensity or flux values. I want the total intensity of the image to be conserved after transformation. This does not work with skimage resize. My total value reduces depending on what factor I scale up or scale down. I have shown below the code I tried so far.

import numpy as np
from skimage.transform import resize


image=fits.open(directory+file1)
cutout=image[0].data
out = resize(cutout, (58,58), order=1, preserve_range=True)
print(np.sum(out),np.sum(cutout))

My output is:

0.074657436655 0.22187 (I want these two values to be equal)

If I scale it to the same dimension using:

out = resize(cutout, (100,100), order=1, preserve_range=True)
print(np.sum(out),np.sum(cutout))

My output is very close to what I want:

0.221869631852 0.22187

I have the same problem if I try to increase the image size as well.

out = resize(cutout, (200,200), order=1, preserve_range=True)
print(np.sum(out),np.sum(cutout))

Output:

0.887316320731 0.22187

I would like to know if there is any workaround to this problem.

EDIT 1:

I just realized that if I multiply my image by the square of the scale of which I want to increase or decrease the size of my image, then my total sum is conserved.

For example:

x=58
out = resize(cutout, (x,x), order=1, preserve_range=True)
test=out*(100/x)**2
print(np.sum(test),np.sum(cutout))

My output is very close to what I want but slightly higher:

0.221930548915 0.22187

I tried this with different dimensions and it works except for really small values. Can anybody explain why this relation is true or is this just a statistical coincidence.


Solution

  • If you treat an image I = Width x Height where N = Width x Height as a set of pixels with intensities in the range of [0,1], it is completely normal that after resizing the image to M = newWidth x newWeight the sum of intensities completely differs from before.

    Assume that an image I with N pixels has intensities uniformly distributed in the range [0,1]. Then the sum of intensities will approximately be 0.5 * N. If you use skimage's resize, the image will be resized to a lower (or larger) size by interpolating. Interpolating does not accumulate values (as you seem to expect), it does instead average values in a neighbourhood to predict the value of each of the pixels in the new image. Thus, the intensity range of the image does not change,the values are modified, and thus, the sum of intensites of the new resized image will approximately be 0.5 * M. If M != N then the sum of intensities will differ a lot.

    What you can do to solve this problem is:

    1. Re-scale your new data proportional to its size:

      >>> y, x = (57, 58)
      >>> out = resize(data, (y,x), order=1, preserve_range=True)
      >>> out = out * (data.shape[0] / float(y)) * (data.shape[1] / float(x))
      

      Which is analogous to what you propose but for any size image (not just square images). This however, compensates for every pixel with a constant factor out[i,j] *= X where X is equal for every pixel in the image, and not all the pixels will be interpolated with the same weight, thus, adding small artificial artifacts.

    2. I think it is just best to replace the total sum of the image (which depends on the number of pixels on the image) with the average intensity in the image (which doesn't rely on the number of pixels)

      >>> meanI = np.sum(I) / float(I.size)   # Exactly the same as np.mean(I) or I.mean()
      >>> meanInew = np.sum(out) / float(out.size)
      >>> np.isclose(meanI, meanInew) # True