I'm trying to convert heic file in jpeg importing also all metadadata (like gps info and other stuff), unfurtunately with the code below the conversion is ok but no metadata are stored on the jpeg file created.
Anyone can describe me what I need to add in the conversion method?
heif_file = pyheif.read("/transito/126APPLE_IMG_6272.HEIC")
image = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
heif_file.mode,
heif_file.stride,
)
image.save("/transito/126APPLE_IMG_6272.JPEG", "JPEG")
Thanks, i found a solution, I hope can help others:
# Open the file
heif_file = pyheif.read(file_path_heic)
# Creation of image
image = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
heif_file.mode,
heif_file.stride,
)
# Retrive the metadata
for metadata in heif_file.metadata or []:
if metadata['type'] == 'Exif':
exif_dict = piexif.load(metadata['data'])
# PIL rotates the image according to exif info, so it's necessary to remove the orientation tag otherwise the image will be rotated again (1° time from PIL, 2° from viewer).
exif_dict['0th'][274] = 0
exif_bytes = piexif.dump(exif_dict)
image.save(file_path_jpeg, "JPEG", exif=exif_bytes)
HEIF to JPEG:
from PIL import Image
import pillow_heif
if __name__ == "__main__":
pillow_heif.register_heif_opener()
img = Image.open("any_image.heic")
img.save("output.jpeg")
JPEG to HEIF:
from PIL import Image
import pillow_heif
if __name__ == "__main__":
pillow_heif.register_heif_opener()
img = Image.open("any_image.jpg")
img.save("output.heic")
Rotation (EXIF of XMP) will be removed automatically when needed.
Call to register_heif_opener can be replaced by importing pillow_heif.HeifImagePlugin instead of pillow_heif
Metadata can be edited in Pillow's "info" dictionary and will be saved when saving to HEIF.
Here is an other approach to convert iPhone HEIC images to JPG preserving exif data
Pyhton 3.9 (I'm on Rasperry PI 4 64 bit)
install pillow_heif (0.8.0)
And run following code and you'll find exif data in the new JPEG image.
The trick is to get the dictionary information. No additional conversion required.
This is sample code, built your own wrapper around.
from PIL import Image
import pillow_heif
# open the image file
heif_file = pillow_heif.read_heif("/mnt/pictures/test/IMG_0001.HEIC")
#create the new image
image = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
heif_file.mode,
heif_file.stride,
)
print(heif_file.info.keys())
dictionary=heif_file.info
exif_dict=dictionary['exif']
# debug
print(exif_dict)
image.save('/tmp/test000.JPG', "JPEG", exif=exif_dict)
Related
I am trying to fix the corrupt exif warnings from tiff images and here's a code I am using
from PIL import Image
def remove_exif(image_name):
image = Image.open(image_name)
if not image.getexif():
return
print('removing EXIF from', image_name, '...')
data = list(image.getdata())
image_without_exif = Image.new(image.mode, image.size)
image_without_exif.putdata(data)
image_without_exif.save(image_name)
remove_exif('ggg.tiff')
print('Done')
The code is working and removed the exif but I got one page only while the tiff before the exif remove was of two pages.
Is it possible to keep all the pages of the tiff image?
I would like to convert an image obtained from the Windows Clipboard to PNG format without having to save and then reload.
As per the code below, I am saving the clipboard image and then reloading it.
Is there a way to convert the image to PNG format without those extra steps, such that the
PIL.BmpImagePlugin.DibImageFile gets converted to
PIL.PngImagePlugin.PngImageFile
Here is the current code:
from PIL import ImageGrab, Image
# Get the clipboard image
img1 = ImageGrab.grabclipboard()
# Save the image from the clipboard to file
img1.save('paste.png', 'PNG')
print("Image Type1:", type(img1))
# Load the image back in
img2 = Image.open('paste.png')
print("Image Type2:", type(img2))
OUTPUT:
Image Type1: <class 'PIL.BmpImagePlugin.DibImageFile'>
Image Type2: <class 'PIL.PngImagePlugin.PngImageFile'>
As per some help from Seon's comment, this got me on the right track, and fulfilled my requirements.
As per Seon:
"the idea is to save the image to an in-memory BytesIO object, and reload it from there. We're still saving and loading, but not to disk."
Which is exactly what I wanted.
Here is the code I used:
from PIL import ImageGrab, Image
import io
def convertImageFormat(imgObj, outputFormat="PNG"):
newImgObj = imgObj
if outputFormat and (imgObj.format != outputFormat):
imageBytesIO = io.BytesIO()
imgObj.save(imageBytesIO, outputFormat)
newImgObj = Image.open(imageBytesIO)
return newImgObj
# Get the clipboard image and convert to PNG
img1 = ImageGrab.grabclipboard()
img2 = convertImageFormat(img1)
# Check the types
print("Image Type1:", type(img1))
print("Image Type2:", type(img2))
OUTPUT:
Image Type1: <class 'PIL.BmpImagePlugin.DibImageFile'>
Image Type2: <class 'PIL.PngImagePlugin.PngImageFile'>
For a little lesson in steganography, I am appending an image to another image file like so:
my_image = open(output_image_path, "wb")
my_image.write(open(visible_image, "rb").read())
my_image.write(open(hidden_image, "rb").read())
my_image.close()
Now I want to extract the hidden image again. How would I do this? I tried with PIL by reading the image or by reading in the file as a bytes stream and then converting it, but I only get the visible image.
In case it matters, I should specify that all images are saved in .jpg format
I was preparing an answer, and just while typing you added your solution. Nevertheless, here's my version, capable extracting all images stored in the output image:
from io import BytesIO
from PIL import Image
# Create "image to the world"
my_image = open('to_the_world.jpg', 'wb')
my_image.write(open('images/0.jpg', 'rb').read()) # size=640x427
my_image.write(open('images/1.jpg', 'rb').read()) # size=1920x1080
my_image.write(open('images/2.jpg', 'rb').read()) # size=1920x1200
my_image.close()
# Try to read "image to the world" via Pillow
image = Image.open('to_the_world.jpg')
print('Read image via Pillow:\n{}\n'.format(image))
# Read "image to the world" via binary data
image = open('to_the_world.jpg', 'rb').read()
# Look for JPG "Start Of Image" segments, and split byte blocks
images = image.split(b'\xff\xd8')[1:]
# Convert byte blocks to Pillow Image objects
images = [Image.open(BytesIO(b'\xff\xd8' + image)) for image in images]
for i, image in enumerate(images):
print('Extracted image #{}:\n{}\n'.format(i+1, image))
Of course, I also used the binary data of the output image, and split the binary data using the JPEG file format structure, the "Start of Image" segment FF D8 to be precise.
For the set of images, I used, the output would be the following:
Read image via Pillow:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=640x427 at 0x1ECC333FF40>
Extracted image #1:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=640x427 at 0x1ECC333FF10>
Extracted image #2:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1080 at 0x1ECC37D4C70>
Extracted image #3:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1200 at 0x1ECC37D4D30>
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1.1
Pillow: 8.2.0
----------------------------------------
Ok got it, this is how to show the hidden image:
from io import BytesIO
import cv2
from PIL import Image
with open(my_image, 'rb') as img_bin:
buff = BytesIO()
buff.write(img_bin.read())
buff.seek(0)
bytesarray = buff.read()
img = bytesarray.split(b"\xff\xd9")[1] + b"\xff\xd9"
img_out = BytesIO()
img_out.write(img)
img = Image.open(img_out)
img.show()
Context
I have made a simple web app for uploading content to a blog. The front sends AJAX requests (using FormData) to the backend which is Bottle running on Python 3.7. Text content is saved to a MySQL database and images are saved to a folder on the server. Everything works fine.
Image processing and PIL/Pillow
Now, I want to enable processing of uploaded images to standardise them (I need them all resized and/or cropped to 700x400px).
I was hoping to use Pillow for this. My problem is creating a PIL Image object from the file object in Bottle. I cannot initialise a valid Image object.
Code
# AJAX sends request to this route
#post('/update')
def update():
# Form data
title = request.forms.get("title")
body = request.forms.get("body")
image = request.forms.get("image")
author = request.forms.get("author")
# Image upload
file = request.files.get("file")
if file:
extension = file.filename.split(".")[-1]
if extension not in ('png', 'jpg', 'jpeg'):
return {"result" : 0, "message": "File Format Error"}
save_path = "my/save/path"
file.save(save_path)
The problem
This all works as expected, but I cannot create a valid Image object with pillow for processing. I even tried reloading the saved image using the save path but this did not work either.
Other attempts
The code below did not work. It caused an internal server error, though I am having trouble setting up more detailed Python debugging.
path = save_path + "/" + file.filename
image_data = open(path, "rb")
image = Image.open(image_data)
When logged manually, the path is a valid relative URL ("../domain-folder/images") and I have checked that I am definitely importing PIL (Pillow) correctly using PIL.PILLOW_VERSION.
I tried adapting this answer:
image = Image.frombytes('RGBA', (128,128), image_data, 'raw')
However, I won’t know the size until I have created the Image object. I also tried using io:
image = Image.open(io.BytesIO(image_data))
This did not work either. In each case, it is only the line trying to initialise the Image object that causes problems.
Summary
The Bottle documentation says the uploaded file is a file-like object, but I am not having much success in creating an Image object that I can process.
How should I go about this? I do not have a preference about processing before or after saving. I am comfortable with the processing, it is initialising the Image object that is causing the problem.
Edit - Solution
I got this to work by adapting the answer from eatmeimadanish. I had to use a io.BytesIO object to save the file from Bottle, then load it with Pillow from there. After processing, it could be saved in the usual way.
obj = io.BytesIO()
file.save(obj) # This saves the file retrieved by Bottle to the BytesIO object
path = save_path + "/" + file.filename
# Image processing
im = Image.open(obj) # Reopen the object with PIL
im = im.resize((700,400))
im.save(path, optimize=True)
I found this from the Pillow documentation about a different function that may also be of use.
PIL.Image.frombuffer(mode, size, data, decoder_name='raw', *args)
Note that this function decodes pixel data only, not entire images.
If you have an entire image file in a string, wrap it in a BytesIO object, and use open() to load it.
Use StringIO instead.
From PIL import Image
try:
import cStringIO as StringIO
except ImportError:
import StringIO
s = StringIO.StringIO()
#save your in memory file to this instead of a regular file
file = request.files.get("file")
if file:
extension = file.filename.split(".")[-1]
if extension not in ('png', 'jpg', 'jpeg'):
return {"result" : 0, "message": "File Format Error"}
file.save(s)
im = Image.open(s)
im.resize((700,400))
im.save(s, 'png', optimize=True)
s64 = base64.b64encode(s.getvalue())
From what I understand, you're trying to resize the image after it has been saved locally (note that you could try to do the resize before it is saved). If this is what you want to achieve here, you can open the image directly using Pillow, it does the job for you (you do not have to open(path, "rb"):
image = Image.open(path)
image.resize((700,400)).save(path)
I have an uploaded file in memory. I want to manipulate the file with cv2. Currently, I write the file to disk then read it with cv2. How can I skip writing the file and load it directly with cv2?
file = request.files['file']
# if file and allowed_file(file.filename):
# save file
filename = secure_filename(file.filename)
file_path = os.path.join(dressrank.config['SHOW_IMG_FOLDER'], filename);
file.save(file_path)
img_path = file_path
# COLOR FATURE EXTRACTION
img = read_img(img_path)
img =img_resize(img, 500)
Build a numpy array using the uploaded data. Decode this array using cv2.
img = cv2.imdecode(numpy.fromstring(request.files['file'].read(), numpy.uint8), cv2.IMREAD_UNCHANGED)
Prior to OpenCV 3.0, use cv2.CV_LOAD_IMAGE_UNCHANGED instead.
See also: Python OpenCV load image from byte string
If working with BaseHTTPRequestHandler, one should first create a FieldStorage form:
fm = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST'})
then:
if "file" in fm:
image = cv2.imdecode(np.frombuffer(fm['file'].file.read(), np.uint8), cv2.IMREAD_UNCHANGED)
Also, note that fromstring is deprecated, and that's why I'm updating davidism's answer with frombuffer.