Search code examples
pythonrrpy2

Using rpy2 to return image data from R to Python


I'm creating a plot in R, but then trying to return the resulting image data to Python so that Python can display the image.

Within R, I've used the magick library to hold the image in memory (instead of plotting to the screen).

"We use image_write to export an image in any format to a file on disk, or in memory if path = NULL"

I'm unsure how to process either the SexpExtPtr or ByteVector types that are returned by rpy2 to Python.

import rpy2.robjects as ro

r = ro.r

r('''
    library("magick")
    figure <- image_graph(width = 400, height = 400, res = 96)
    plot(c(1,2,3))
    image <- image_write(figure, path = NULL, format = "png")
    # image_write(figure, path = 'example.png', format = 'png')
''')

figure = ro.globalenv['figure']
image = ro.globalenv['image']

im = Image.open(BytesIO(image))

The code above gives me the error:

Traceback (most recent call last):
  File "stackoverflow.py", line 23, in <module>
    im = Image.open(BytesIO(image))
TypeError: a bytes-like object is required, not 'ByteVector'

In Python:

  • figure has type <class 'rpy2.rinterface.SexpExtPtr'>
  • image has type <class 'rpy2.robjects.vectors.ByteVector'>

Solution

  • So... it turns out that <class 'rpy2.robjects.vectors.ByteVector'> is an iterable and I can use bytes() to construct an array of bytes.

    Also, by putting the code inside a function that uses return to return the PIL image I can get the image to display within a Jupyter notebook (alternatively we could just do image.show())

    from io import BytesIO
    
    import PIL.Image as Image
    import rpy2.robjects as ro
    
    def main():
        r = ro.r
    
        r('''
            library("magick")
            figure <- image_graph(width = 400, height = 400, res = 96)
            plot(c(1,2,3))
            image <- image_write(figure, path = NULL, format = "png")
            image_write(figure, path = 'example.png', format = 'png')
        ''')
    
        image_data = ro.globalenv['image']
    
        image = Image.open(BytesIO(bytes(image_data)))
    
        return image
    
    
    if __name__ == '__main__':
        image = main()
        image.show()