joergdietrich/daltonize

Memory Error on large images

roniemartinez opened this issue · 4 comments

I know this is an old repo but this is still being used by several developers (including me).

I noticed that on a 2GB linux machine , MemoryError is raised when converting a 2-3MB image (3933 x 5906 pixels in size). Here is a truncated error message.

  File "./<app>/daltonize.py", line 132, in simulate_from_image
    rgb = np.asarray(img, dtype=float)
  File "/root/.cache/pypoetry/virtualenvs/<path>/lib/python3.6/site-packages/numpy/core/_asarray.py", line 85, in asarray
    return array(a, dtype, copy=False, order=order)
MemoryError: Unable to allocate 532. MiB for an array with shape (3933, 5906, 3) and data type float64

This is very weird that for a 3MB file, it requires a large 532MB amount of memory.

Related line on this repo: https://github.com/joergdietrich/daltonize/blob/master/daltonize.py#L79

Thanks for reporting this. Always very happy to hear that people are actually using this.

The file you read in is probably only 3 MB large because it uses some compressed image format like jpg. The actual image size is correct, though, for a three color channel image with 8 bytes per image:

In [5]: (3933 * 5906 * 3 * 8) / 1024**2                                               
Out[5]: 531.6535491943359

rounded up is 532 MiB. Internally daltonize needs to access the uncompressed image. I don't think anything can be done about this.

@joergdietrich

Thanks for the fast reply. So I experimented with how colors should be represented in an array because I think it is very weird to have that sudden increase from 2MB to 532MB.

And also, I think a color should be only represented by 3 bytes, 1 byte for each R, G, and B. Float datatype can be a bit overkill.

If we use dtype=float (original) we get 532MB:

rgb = np.asarray(img, dtype=float)
print(rgb.nbytes/1024**2)  # 531.6535491943359

However, if we use dtype=np.uint8, we get the same result (at least I think they are visually similar) with lesser memory:

rgb = np.asarray(img, dtype=np.uint8)
print(rgb.nbytes/1024**2)  # 66.45669364929199

I would like to get verification that this is the right fix.

Yes, you're right. Full float precision is not needed. I'm not yet convinced that uint8 provides sufficient resolution for all transformations. I'll look into this when I'll finally recompute the conversion matrices as part of #13. Until then, what you proposed is a viable quick fix.

Thanks @joergdietrich

Well, this is just one part of the several matrices in the code. Transformation (or transformed) matrices are still using float64 though, which is rather big, using the smallest float is probably better. There are around 4 extra matrices using 532MB. I experimented changing the datatype of np.einsum(..., dtype=np.float16, casting="equiv"). Haven't tested it on other images but initial results were good.

Also, the arrays for clipping are using this same float datatype.