skimage imread from temporary file - python

I'm trying to skimage.io.imread() an image (say a tiff file, for concreteness) that was previously written to a tempfile.TemporaryFile(). However, skimage complains by saying
ValueError: Cannot determine type of file b'<_io.BufferedRandom name=6>'
I am doing this because another program writes the image to standard output.
I collect it with subprocess.check_output and write it to the temporary file, thus avoiding saving the image to disk.
Does anyone know how to achieve this, or has got a better idea on how to pipe an image from stdout into a python image, ultimately to be treated as a numpy.ndarray?

A solution is the following
with NamedTemporaryFile() as f:
skimage.io.imread(f.name, plugin="tifffile")
Alternatively, one can replace freeimage with tifffile.
Earlier I was passing the file object, but imread actually wants a filename.

Related

Python: SpooledTemporaryFile suffix not working

I want to write an image using opencv to a temporary file, get the path of that temporary file and pass that path to a function.
import cv2 as cv
from tempfile import NamedTemporaryFile, SpooledTemporaryFile
img = create_my_awesome_image()
with NamedTemporaryFile(suffix=".png") as temp:
print(temp.name)
cv.imwrite(temp.name, img) # this one sparks joy
with SpooledTemporaryFile(max_size=1000000, suffix=".png") as temp:
print(temp.name)
cv.imwrite(temp.name, img) # this one does not
The first print prints C:\Users\FLORIA~1\AppData\Local\Temp\tmpl2i6nc47.png.
While the second print prints: None.
Using NamedTemporaryFile works perfectly find. However, because the second print prints None, I cannot use the SpooledTemporaryFile together with opencv. Any ideas why the prefix argument of SpooledTemporaryFile is ignored?
The problem is that a spooled file (such as a SpooledTemporaryFile) doesn't exist on the disk, so it also doesn't have a name.
However, note that cv2.imread() will take a file name as an argument, meaning that it will handle the file opening and it doesn't support spooled files.
If you are only working with png images, they are not encoded, meaning that the variable img already contains the image data in memory and there is nothing else for you to do, just call cv2.imwrite() when you want to save it to the disk. If you want to use a temporary file, it has to be a NamedTemporaryFile.
If you want to handle an encoded image format in memory, such as jpg, you can use cv2.imencode() for that purpose, as in this answer.

How to send embedded image created using PIL/pillow as email (Python 3)

I am creating image that I would like to embed in the e-mail. I cannot figure out how to create image as binary and pass into MIMEImage. Below is the code I have and I have error when I try to read image object - the error is "AttributeError: 'NoneType' object has no attribute 'read'".
image=Image.new("RGBA",(300,400),(255,255,255))
image_base=ImageDraw.Draw(image)
emailed_password_pic=image_base.text((150,200),emailed_password,(0,0,0))
imgObj=emailed_password_pic.read()
msg=MIMEMultipart()
html="""<p>Please finish registration <br/><img src="cid:image.jpg"></p>"""
img_file='image.jpg'
msgText = MIMEText(html,'html')
msgImg=MIMEImage(imgObj)
msgImg.add_header('Content-ID',img_file)
msg.attach(msgImg)
msg.attach(msgText)
If you look at line 4 - I am trying to read image so that I can pass it into MIMEImage. Apparently, image needs to be read as binary. However, I don't know how to convert it to binary so that .read() can process it.
FOLLOW-UP
I edited code per suggestions from jsbueno - thank you very much!!!:
emailed_password=os.urandom(16)
image=Image.new("RGBA",(300,400),(255,255,255))
image_base=ImageDraw.Draw(image)
emailed_password_pic=image_base.text((150,200),emailed_password,(0,0,0))
stream_bytes=BytesIO()
image.save(stream_bytes,format='png')
stream_bytes.seek(0)
#in_memory_file=stream_bytes.getvalue()
#imgObj=in_memory_file.read()
imgObj=stream_bytes.read()
msg=MIMEMultipart()
sender='xxx#abc.com'
receiver='jjjj#gmail.com'
subject_header='Please use code provided in this e-mail to confirm your subscription.'
msg["To"]=receiver
msg["From"]=sender
msg["Subject"]=subject_header
html="""<p>Please finish registration by loging into your account and typing in code from this e-mail.<br/><img src="cid:image.png"></p>"""
img_file='image.png'
msgText=MIMEText(html,'html')
msgImg=MIMEImage(imgObj) #Is mistake here?
msgImg.add_header('Content-ID',img_file)
msg.attach(msgImg)
msg.attach(msgText)
smtpObj=smtplib.SMTP('smtp.mandrillapp.com', 587)
smtpObj.login(userName,userPassword)
smtpObj.sendmail(sender,receiver,msg.as_string())
I am not getting errors now but e-mail does not have image in it. I am confused about the way image gets attached and related to in html/email part. Any help is appreciated!
UPDATE:
This code actually works - I just had minor typo in the code on my PC.
There are a couple of conceptual errors there, both in using PIL and on what format an image should be in order to be incorporated into an e-mail.
In PIL: the ImageDraw class operates inplace, not like the Image class calls, which usually return a new image after each operation. In your code, it means that the call to image_base.text is actually changing the pixel data of the object that lies in your image variable. This call actually returns None and the code above should raise an error like "AttributeError: None object does not have attribute 'read'" on the following line.
Past that (that is, you should fetch the data from your image variable to attach it to the e-mail) comes the second issue: PIL, for obvious reasons, have images in an uncompressed, raw pixel data format in memory. When attaching images in e-mails we usually want images neatly packaged inside a file - PNG or JPG formats are usually better depending on the intent - let's just stay with .PNG. So, you have to create the file data using PIL, and them attach the file data (i.e. the data comprising a PNG file, including headers, metadata, and the actual pixel data in a compressed form). Otherwise you'd be putting in your e-mail a bunch of (uncompressed) pixel data that the receiving party would have no way to assemble back into an image (even if he would treat the data as pixels, raw pixel data does not contain the image shape so-)
You have two options: either generate the file-bytes in memory, or write them to an actual file in disk, and re-read that file for attaching. The second form is easier to follow. The first is both more efficient and "the right thing to do" - so let's keep it:
from io import BytesIO
# In Python 2.x:
# from StringIO import StringIO.StringIO as BytesIO
image=Image.new("RGBA",(300,400),(255,255,255))
image_base=ImageDraw.Draw(image)
# this actually modifies "image"
emailed_password_pic=image_base.text((150,200),emailed_password,(0,0,0))
stream = BytesIO()
image.save(stream, format="png")
stream.seek(0)
imgObj=stream.read()
...
(NB: I have not checked the part dealing with mail and mime proper in your code - if you are using it correctly, it should work now)

having cv2.imread reading images from file objects or memory-stream-like data (here non-extracted tar)

I have a .tar file containing several hundreds of pictures (.png). I need to process them via opencv.
I am wondering whether - for efficiency reasons - it is possible to process them without passing by the disc. In other, words I want to read the pictures from the memory stream related to the tar file.
Consider for instance
import tarfile
import cv2
tar0 = tarfile.open('mytar.tar')
im = cv2.imread( tar0.extractfile('fname.png').read() )
The last line doesn't work as imread expects a file name rather than a stream.
Consider that this way of reading directly from the tar stream can be achieved e.g. for text (see e.g. this SO question).
Any suggestion to open the stream with the correct png encoding?
Untarring to ramdisk is of course an option, although I was looking for something more cachable.
Thanks to the suggestion of #abarry and this SO answer I managed to find the answer.
Consider the following
def get_np_array_from_tar_object(tar_extractfl):
'''converts a buffer from a tar file in np.array'''
return np.asarray(
bytearray(tar_extractfl.read())
, dtype=np.uint8)
tar0 = tarfile.open('mytar.tar')
im0 = cv2.imdecode(
get_np_array_from_tar_object(tar0.extractfile('fname.png'))
, 0 )
Perhaps use imdecode with a buffer coming out of the tar file? I haven't tried it but seems promising.

PIL: How to reopen an image after verifying?

I need open an image, verify the image, then reopen it (see last sentence of below quote from PIL docs)
im.verify()
Attempts to determine if the file is broken, without actually decoding
the image data. If this method finds any problems, it raises suitable
exceptions. This method only works on a newly opened image; if the
image has already been loaded, the result is undefined. Also, if you
need to load the image after using this method, you must reopen the
image file.
This is what I have in my code, where picture is a django InMemoryUploadedFile object:
img = Image.open(picture)
img.verify()
img = Image.open(picture)
The first two lines work fine, but I get the following error for the third line (where I'm attempting to "reopen" the image):
IOError: cannot identify image file
What is the proper way to reopen the image file, as the docs suggest?
This is no different than doing
f = open('x.png')
Image.open(f)
Image.open(f)
The code above does not work because PIL advances in the file while reading its first few bytes to (attempt to) identify its format. Trying to use a second Image.open in this situation will fail as noted because now the current position in the file is past its image's header. To confirm this, you can verify what f.tell() returns. To solve this issue you have to go back to the start of the file either by doing f.seek(0) between the two calls to Image.open, or closing and reopening the file.
Try doing a del img between the verify and second open.

md5 from pil object

how i can get md5 of the pil object without saving to file ?
imq.save('out.png')
hash = hashlib.md5(open('out.png','rb').read()).hexdigest()
Actually there is simpler solution:
hashlib.md5(img.tostring()).hexdigest()
Turning #Ignacio's answer into code, using this answer to help:
import StringIO, hashlib
output = StringIO.StringIO()
img.save(output)
hash = hashlib.md5(output.getvalue()).hexdigest()
As the referenced other answer notes, this might lead to a KeyError if PIL tries to automatically detect the output format. To avoid this problem you can specify the format manually:
img.save(output, format='GIF')
(Note: I've used "img" as the variable, rather than your "imq" which I assumed was a typo.)
You could write it to a StringIO instead, and then take the hash of that.
You could use the following PIL Image class method to get the raw image data to feed to md5().
im.getdata() => sequence
Returns the contents of an image as a
sequence object containing pixel
values. The sequence object is
flattened, so that values for line one
follow directly after the values of
line zero, and so on.
Note that the resulting MD5 hash of using this won't be the same as your sample code because it is (at least partially) independent of the particular image file format used to save the image. It could be useful if you wanted to compare actual images independent of the particular image file format they may be saved in.
To use it you would need to store the MD5 hash of the image data somewhere independent of any image file where it could be retrieved when needed -- as opposed to generating it by reading the entire file into memory as binary data like the code in your question does. Instead you would need to always load the image into PIL and then use the getdata() method on it to compute hashes.

Categories

Resources