I have a char pointer to png data provided by a c library.
How do I create a image in python from this data in memory.
The c function looks like this:
char *getImage(int *imgSize);
In Python I got the char* as follows:
imSize = c_int()
img = c_char_p()
img = c_char_p(my_c_api.getImage(byref(imSize)))
The char* is returned into the img variable and the size of the image in bytes is returned in the imSize variable.
When executing the following python script:
im = Image.frombuffer("RGBA", (400,400), img.value, 'raw', "RGBA", 0, 1)
I get a ValueError: buffer is not large enough error.
I suspect the img variable in the frombuffer call.
What do I have to do with the img variable, to pass the image data correctly to the frombuffer call?
You'll need to put the data in a StringIO instance and have PIL parse the data from that.
from cStringIO import StringIO
imgfile = StringIO(img.value)
im = Image.open(imgfile)
.frombuffer assumes raw image data, not PNG-encoded data. The StringIO object provides a file-like object to wrap your data in, so PIL can work with it.
Related
I'm trying to send an OpenCV image in a json and receive it on the other end but I'm running into endless problems encoding and decoding the image
I send it in JSON in the following way:
dumps({"image": b64encode(image[y1:y2, x1:x2]).decode('utf-8')})
On the other end I try to decode it (I need it as a Pillow image):
image = Image.open(BytesIO(base64.b64decode(data['image'])))
But I'm getting Exception cannot identify image file <_io.BytesIO object at 0x7fbd34c98a98>
Also tried:
nparr = np.fromstring(b64decode(data['image']), np.uint8)
image = cv2.imdecode(nparr, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(image)
But then I get 'NoneType' object has no attribute '__array_interface__' coming from Image.fromarray
Any ideas what I'm doing wrong?
Hopefully, this should get you started. I think that what you tried, by sending the unadorned bytes from the Numpy array probably won't work because the receiver will not know the width, height and number of channels in the image, so I used pickle to store that.
#!/usr/bin/env python3
import cv2
import numpy as np
import base64
import json
import pickle
from PIL import Image
def im2json(im):
"""Convert a Numpy array to JSON string"""
imdata = pickle.dumps(im)
jstr = json.dumps({"image": base64.b64encode(imdata).decode('ascii')})
return jstr
def json2im(jstr):
"""Convert a JSON string back to a Numpy array"""
load = json.loads(jstr)
imdata = base64.b64decode(load['image'])
im = pickle.loads(imdata)
return im
# Create solid red image
red = np.full((480, 640, 3), [0, 0, 255], dtype=np.uint8)
# Make image into JSON string
jstr = im2json(red)
# Extract image from JSON string, and convert from OpenCV to PIL reversing BGR to RGB on the way
OpenCVim = json2im(jstr)
PILimage = Image.fromarray(OpenCVim[...,::-1])
PILimage.show()
As you haven't answered my question in the comments about why you want do things this way, it may not be optimal - sending uncompressed, base64-encoded images across a network (presumably) is not very efficient. You might consider JPEG, or PNG encoded data to save network bandwidth, for example.
You could also use cPickle instead.
Note that some folks disapprove of pickle and also the method above uses a lot of network bandwidth. An alternative might be to JPEG compress the image before sending and decompress on the receiving end straight into a PIL Image. Note that this is lossy.
Or change the .JPG extension in the code to .PNG which is loss-less but may be slower and will not work for images with floating point data or 16-bit data (although the latter could be accommodated).
You could also look at TIFF, but again, it depends on the nature of your data, the network bandwidth, the flexibility you need, your CPU's encoding/decoding performance...
#!/usr/bin/env python3
import cv2
import numpy as np
import base64
import json
from io import BytesIO
from PIL import Image
def im2json(im):
_, imdata = cv2.imencode('.JPG',im)
jstr = json.dumps({"image": base64.b64encode(imdata).decode('ascii')})
return jstr
def json2im(jstr):
load = json.loads(jstr)
imdata = base64.b64decode(load['image'])
im = Image.open(BytesIO(imdata))
return im
# Create solid red image
red = np.full((480, 640, 3), [0, 0, 255], dtype=np.uint8)
# Make image into JSON string
jstr = im2json(red)
# Extract image from JSON string into PIL Image
PILimage = json2im(jstr)
PILimage.show()
In C#, I can use Bitmap.lockbits() to access a bitmap as a byte array. How to do this in PIL? I have tried Image.write() but it wrote a full format image to a stream.
from io import BytesIO
from PIL import Image
with BytesIO() as output:
with Image.open(path_to_image) as img:
img.save(output, 'BMP')
data = output.getvalue()
.. warning::
This method returns the raw image data from the internal
storage. For compressed image data (e.g. PNG, JPEG) use
:meth:~.save, with a BytesIO parameter for in-memory
data.
This is the warning in the tobytes method. So we can use the save method with a BytesIO parameter to get a compressed byte array.
import io
byteIO = io.BytesIO()
image.save(byteIO, format='PNG')
byteArr = byteIO.getvalue()
I have generated an image using PIL. How can I save it to a string in memory?
The Image.save() method requires a file.
I'd like to have several such images stored in dictionary.
You can use the BytesIO class to get a wrapper around strings that behaves like a file. The BytesIO object provides the same interface as a file, but saves the contents just in memory:
import io
with io.BytesIO() as output:
image.save(output, format="GIF")
contents = output.getvalue()
You have to explicitly specify the output format with the format parameter, otherwise PIL will raise an error when trying to automatically detect it.
If you loaded the image from a file it has a format property that contains the original file format, so in this case you can use format=image.format.
In old Python 2 versions before introduction of the io module you would have used the StringIO module instead.
For Python3 it is required to use BytesIO:
from io import BytesIO
from PIL import Image, ImageDraw
image = Image.new("RGB", (300, 50))
draw = ImageDraw.Draw(image)
draw.text((0, 0), "This text is drawn on image")
byte_io = BytesIO()
image.save(byte_io, 'PNG')
Read more: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image
sth's solution didn't work for me
because in ...
Imaging/PIL/Image.pyc line 1423 ->
raise KeyError(ext) # unknown
extension
It was trying to detect the format from the extension in the filename , which doesn't exist in StringIO case
You can bypass the format detection by setting the format yourself in a parameter
import StringIO
output = StringIO.StringIO()
format = 'PNG' # or 'JPEG' or whatever you want
image.save(output, format)
contents = output.getvalue()
output.close()
save() can take a file-like object as well as a path, so you can use an in-memory buffer like a StringIO:
buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
With modern (as of mid-2017 Python 3.5 and Pillow 4.0):
StringIO no longer seems to work as it used to. The BytesIO class is the proper way to handle this. Pillow's save function expects a string as the first argument, and surprisingly doesn't see StringIO as such. The following is similar to older StringIO solutions, but with BytesIO in its place.
from io import BytesIO
from PIL import Image
image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
When you say "I'd like to have number of such images stored in dictionary", it's not clear if this is an in-memory structure or not.
You don't need to do any of this to meek an image in memory. Just keep the image object in your dictionary.
If you're going to write your dictionary to a file, you might want to look at im.tostring() method and the Image.fromstring() function
http://effbot.org/imagingbook/image.htm
im.tostring() => string
Returns a string containing pixel
data, using the standard "raw"
encoder.
Image.fromstring(mode, size, data) =>
image
Creates an image memory from pixel
data in a string, using the standard
"raw" decoder.
The "format" (.jpeg, .png, etc.) only matters on disk when you are exchanging the files. If you're not exchanging files, format doesn't matter.
I have an image that I load using cv2.imread(). This returns an NumPy array. However, I need to pass this into a 3rd party API that requires the data in IplImage format.
I've scoured everything I could and I've found instances of converting from IplImage to CvMat,and I've found some references to converting in C++, but not from NumPy to IplImage in Python. Is there a function that is provided that can do this conversion?
You can do like this.
source = cv2.imread() # source is numpy array
bitmap = cv.CreateImageHeader((source.shape[1], source.shape[0]), cv.IPL_DEPTH_8U, 3)
cv.SetData(bitmap, source.tostring(),
source.dtype.itemsize * 3 * source.shape[1])
bitmap here is cv2.cv.iplimage
2-way to apply:
img = cv2.imread(img_path)
img_buf = cv2.imencode('.jpg', img)[1].tostring()
just read the image file:
img_buf = open(img_path, 'rb').read()
I have generated an image using PIL. How can I save it to a string in memory?
The Image.save() method requires a file.
I'd like to have several such images stored in dictionary.
You can use the BytesIO class to get a wrapper around strings that behaves like a file. The BytesIO object provides the same interface as a file, but saves the contents just in memory:
import io
with io.BytesIO() as output:
image.save(output, format="GIF")
contents = output.getvalue()
You have to explicitly specify the output format with the format parameter, otherwise PIL will raise an error when trying to automatically detect it.
If you loaded the image from a file it has a format property that contains the original file format, so in this case you can use format=image.format.
In old Python 2 versions before introduction of the io module you would have used the StringIO module instead.
For Python3 it is required to use BytesIO:
from io import BytesIO
from PIL import Image, ImageDraw
image = Image.new("RGB", (300, 50))
draw = ImageDraw.Draw(image)
draw.text((0, 0), "This text is drawn on image")
byte_io = BytesIO()
image.save(byte_io, 'PNG')
Read more: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image
sth's solution didn't work for me
because in ...
Imaging/PIL/Image.pyc line 1423 ->
raise KeyError(ext) # unknown
extension
It was trying to detect the format from the extension in the filename , which doesn't exist in StringIO case
You can bypass the format detection by setting the format yourself in a parameter
import StringIO
output = StringIO.StringIO()
format = 'PNG' # or 'JPEG' or whatever you want
image.save(output, format)
contents = output.getvalue()
output.close()
save() can take a file-like object as well as a path, so you can use an in-memory buffer like a StringIO:
buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
With modern (as of mid-2017 Python 3.5 and Pillow 4.0):
StringIO no longer seems to work as it used to. The BytesIO class is the proper way to handle this. Pillow's save function expects a string as the first argument, and surprisingly doesn't see StringIO as such. The following is similar to older StringIO solutions, but with BytesIO in its place.
from io import BytesIO
from PIL import Image
image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
When you say "I'd like to have number of such images stored in dictionary", it's not clear if this is an in-memory structure or not.
You don't need to do any of this to meek an image in memory. Just keep the image object in your dictionary.
If you're going to write your dictionary to a file, you might want to look at im.tostring() method and the Image.fromstring() function
http://effbot.org/imagingbook/image.htm
im.tostring() => string
Returns a string containing pixel
data, using the standard "raw"
encoder.
Image.fromstring(mode, size, data) =>
image
Creates an image memory from pixel
data in a string, using the standard
"raw" decoder.
The "format" (.jpeg, .png, etc.) only matters on disk when you are exchanging the files. If you're not exchanging files, format doesn't matter.