Resizing image results in: ndarray not C-contiguous error

I’m loading SVG images and everything was going fine except the resizing operation is somehow causing an error when I attempt to display the fastai image in my notebook.

What’s strange is that my assertions show that the ndarray is c-contiguous during my code, but once it gets down deep into PIL, somehow a copy of that data is no longer c-contiguous. Thus I couldn’t figure out how to manipulate my image to get rid of this error. Interestingly enough, the image successfully renders so it feels like more of a warning.

(As an aside, there’s other idiocracy going on here - why am I rasterizing an SVG only to the resize it later; shouldn’t I resize the SVG first. Yes, but I couldn’t figure out how to resize it to my target width. See comments).

You’ll first need:

pip install cairosvg

Then:

import fastai
from fastai import *
from fastai.vision import *
from fastai.callbacks import *
from fastai.utils.mem import *
import cairosvg

def load_svg_image(url, target_width=300):
    """ Load SVG image with Cairo, and convert to FastAI Image
    """
    import urllib.request
    from PIL import Image as PilImage
    import io
    url = 'https://upload.wikimedia.org/wikipedia/commons/b/b2/Tom_Cruise_signature.svg'
    bytestring = urllib.request.urlopen(url).read().decode('utf-8')
    
    # This is really dumb given the point of SVG is scalable, but cairosvg
    # doesn't seem to support target width very well.
    
    # So we can scale it up, rasterize, then we'll resize it down to what 
    # we want, because cairosvg's output-width parameter isn't accesible 
    # from python and doesn't seem to work from the command line.
    scale = 1.0
    sig_bytes = cairosvg.svg2png(bytestring=bytestring, scale=scale)
    sig_img_data = PilImage.open(io.BytesIO(sig_bytes))
    img_tensor = pil2tensor(sig_img_data, np.float32)
    img_tensor.div_(255)
    img = Image(img_tensor)
    
    # Resize the image to desired width.
    ratio = target_width / img.shape[2]
    new_sz = img.shape[0], int(img.shape[1] * ratio), int(img.shape[2] * ratio)
    img = img.resize(size=new_sz).refresh()
    
    # Attempt to make memory contiguous, but this doesn't matter, the problem
    # happens later on.
    #a = img.data.numpy()
    #assert a.flags['C_CONTIGUOUS'] == True
    #a = np.ascontiguousarray(a)
    #assert a.flags['C_CONTIGUOUS'] == True
    #img = Image(torch.from_numpy(a)).refresh()
    #assert img.data.numpy().flags['C_CONTIGUOUS'] == True
    
    return img

img = load_svg_image(sig_file)
img

Results in:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/opt/anaconda3/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    343             method = get_real_method(obj, self.print_method)
    344             if method is not None:
--> 345                 return method()
    346             return None
    347         else:

/opt/anaconda3/lib/python3.7/site-packages/fastai/vision/image.py in _repr_jpeg_(self)
     87     def __repr__(self): return f'{self.__class__.__name__} {tuple(self.shape)}'
     88     def _repr_png_(self): return self._repr_image_format('png')
---> 89     def _repr_jpeg_(self): return self._repr_image_format('jpeg')
     90 
     91     def _repr_image_format(self, format_str):

/opt/anaconda3/lib/python3.7/site-packages/fastai/vision/image.py in _repr_image_format(self, format_str)
     91     def _repr_image_format(self, format_str):
     92         with BytesIO() as str_buffer:
---> 93             plt.imsave(str_buffer, image2np(self.px), format=format_str)
     94             return str_buffer.getvalue()
     95 

/opt/anaconda3/lib/python3.7/site-packages/matplotlib/pyplot.py in imsave(fname, arr, **kwargs)
   2138 @docstring.copy(matplotlib.image.imsave)
   2139 def imsave(fname, arr, **kwargs):
-> 2140     return matplotlib.image.imsave(fname, arr, **kwargs)
   2141 
   2142 

/opt/anaconda3/lib/python3.7/site-packages/matplotlib/image.py in imsave(fname, arr, vmin, vmax, cmap, format, origin, dpi)
   1505             pil_shape = (rgba.shape[1], rgba.shape[0])
   1506             image = Image.frombuffer(
-> 1507                 "RGBA", pil_shape, rgba, "raw", "RGBA", 0, 1)
   1508             if format in ["jpg", "jpeg"]:
   1509                 format = "jpeg"  # Pillow doesn't recognize "jpg".

/opt/anaconda3/lib/python3.7/site-packages/PIL/Image.py in frombuffer(mode, size, data, decoder_name, *args)
   2607             import numpy as np
   2608             #data = np.ascontiguousarray(data)
-> 2609             im = im._new(core.map_buffer(data, size, decoder_name, None, 0, args))
   2610             im.readonly = 1
   2611             return im

ValueError: ndarray is not C-contiguous

Help appreciated!

Forgot to share versions:

fastai==1.0.60
Pillow==7.0.0
matplotlib==3.1.3