Example #1
0
def PilImageToNp(im: Image.Image):
    im.load()

    encoder = Image._getencoder(im.mode, 'raw', im.mode)
    encoder.setimage(im.im)

    shape, typestr = Image._conv_type_shape(im)
    data = np.empty(shape, dtype=np.dtype(typestr))
    mem = data.data.cast('B', (data.data.nbytes,))

    bufsize, s, offset = 65536, 0, 0
    while not s:
        _, s, d = encoder.encode(bufsize)
        mem[offset:offset + len(d)] = d
        offset += len(d)
    if s < 0:
        raise GraphicsException(f"Encoder error: {s}")
		
    return data
def asarray(im: Image.Image,
            padding_channel='stride',
            allow_copy=True) -> np.ndarray:
    """
    Return an numpy.ndarray that shares the underlying buffer with the given image (if possible).

    Parameters
    ----------
    im : PIL.Image.Image
        The image.
    padding_channel : str
        Handling of padding channel in 3-channel image. 'stride' or 'passthrough' or 'copy_remove'.
        
        'stride':      The padding channel will be hidden from the result array. NOTE: ndarray with channel stride cannot be passed to OpenCV.
        'passthrough': The padding channel will be available with undetermined values in the result array.
        'copy_remove': The result array will be a copy of the image buffer with padding channel removed.
    allow_copy : bool
        Allow copying pixels when buffer sharing is unavailable, or raise an exception instead.

    Returns
    -------
    numpy.ndarray

    """

    imdata = im.getdata()
    make_copy = False
    shape, typestr = Image._conv_type_shape(im)
    pilbuf = dict(imdata.unsafe_ptrs)
    if pilbuf.get('pixel32', 0) != 0:
        pixel_advance = 4
        if shape[-1] == 3:
            if padding_channel == 'passthrough':
                shape = (shape[0], shape[1], 4)
            elif padding_channel == 'stride':
                pass
            elif padding_channel == 'copy_remove':
                if not allow_copy:
                    raise ValueError(
                        'cannot remove padding channel without copying')
                make_copy = True
            else:
                raise ValueError('invalid padding_channel')
    elif im.mode == 'LA':
        pixel_advance = 2
    else:
        pixel_advance = 1
    # examine if we can represent the PIL-style buffer as an numpy-style buffer (fixed stride)
    line_ptrs = np.asarray(
        _ArrayInterfaceForPointer(pilbuf['image'], (im.height, ),
                                  _ptrtypestr,
                                  owner_object=imdata))
    pil_strides = np.diff(line_ptrs)
    if np.all(pil_strides == pil_strides[0]):
        # found fixed stride, construct numpy array interface
        data = (line_ptrs[0], False)
        arr_strides = (pil_strides[0], pixel_advance,
                       1) if pixel_advance > 1 else (pil_strides[0], 1)
        array_intf = {
            'version': 3,
            'shape': shape,
            'typestr': typestr,
            'strides': arr_strides,
            'data': data,
        }
        result = np.asarray(_ArrayInterfaceForObject(array_intf, imdata))
        if make_copy:
            result = result.copy()
        return result
    elif allow_copy:
        # the PIL buffer cannot fit in numpy layout, so we need to copy it
        if pixel_advance == 4 and shape[-1] == 3:
            arr_strides = (4, 1)
        else:
            arr_strides = None
        # use ndarray for writable buffer
        return np.concatenate([
            np.asarray(
                _ArrayInterfaceForPointer(p,
                                          shape[1:],
                                          typestr,
                                          strides=arr_strides,
                                          readonly=False,
                                          owner_object=imdata))
            for p in line_ptrs
        ])
    raise ValueError('this PIL buffer cannot be represented as a numpy array')