I want to store image from web to MongoDB, but first I will check the image by opencv to make sure it is an Blood Test Report image, just like followiing snippet:
if 'imagefile' not in request.files:
abort(400)
imgfile = request.files['imagefile']
if imgfile.filename == '':
abort(400)
if imgfile:
#pil = StringIO(imgfile)
#pil = Image.open(pil)
img = cv2.imdecode(numpy.fromstring(imgfile.read(), numpy.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
filtered = ImageFilter(image=img).filter()
if filtered is None:
return jsonify({"error": "please make sure your picture is perfect"})
# save to mongo
content = StringIO()
filtered.save(content, format="JPEG")
fid, filename= save_file(content,imgfile.name)
The ImageFilter accepts a opencv format image, and do something such as filtering, and then return a PIL image, and it succeed! then I save the PIL image to MongoDB, code like this:
def save_file(content, name):
# content = StringIO(f.read())
try:
mime = Image.open(content).format.lower()
if mime not in app.config['ALLOWED_EXTENSIONS']:
raise IOError()
except IOError:
abort(400)
c = dict(content=bson.binary.Binary(content.getvalue()),
filename=secure_filename(name), mime=mime)
db.files.save(c)
return c['_id'], c['filename']
And it succeed! Then i have another function to find a image by id from MongoDB, then I will use it to do OCR.
def get_report(fid):
try:
file = db.files.find_one(bson.objectid.ObjectId(fid))
if file is None:
raise bson.errors.InvalidId()
print(type(file['content']))
img = cv2.imdecode(numpy.fromstring(dumps(file['content']), numpy.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
if img is None:
return jsonify({"error": "please make sure your picture is perfect"})
report_data = ImageFilter(image=img).ocr(22)
print report_data
if report_data is None:
return jsonify({"error": "can't ocr'"})
return jsonify(report_data)
except bson.errors.InvalidId:
flask.abort(404)
Again, I will use it in opencv format, so I will convert the bson.binary.Binary to opencv image, but it failed! because img always none by
img = cv2.imdecode(numpy.fromstring(dumps(file['content']), numpy.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
So, my last question is what is the real image format in python, How i convert it in web mongodb opencv pil and memory!,Following is one method i tried, but it failed! I want to convert the Binary to PIL image use Image.frombytes first, then i convert the PIL to opencv. But error:ValueError: not enough image data
# -*- coding: utf-8 -*-
import os
from pymongo import MongoClient
import bson
from PIL import Image
from imageFilter import ImageFilter
import cv2
import numpy
from bson.json_util import dumps
db = MongoClient('localhost', 27017).test
file =db.files.find_one(bson.objectid.ObjectId("58454666a235ec451d3bf2e6"))
if file is None:
raise bson.errors.InvalidId()
print(type(file['content']))
# this is success, I use Flask Response
#return Response(file['content'], mimetype='image/' + file['mime'])
# file['content']是整个图片文件的二进制对象,也就是说是一个文件,不应该直接作为二进制数据传递给Image
Image.frombytes(mode='RGB',size=(1000,760),data=file['content'])
img = cv2.imdecode(numpy.fromstring(dumps(file['content']), numpy.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
if img is None:
print "img is None"
# ImageFilter accept opencv img to process it by opencv
report_data = ImageFilter(image=img).ocr(22)
print report_data
Related
I have a stream of images and have to display it in Google Colab notebook such that it looks like a video, But what I get is a image under image ...
from google.colab import drive
drive.mount('/content/drive')
# importing cv2
import cv2
import imutils
from google.colab.patches import cv2_imshow
from IPython.display import clear_output
import os
folder = r'/content/drive/images/'
for filename in os.listdir(folder) :
VALID_FORMAT = (".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG")
if filename.upper().endswith(VALID_FORMAT):
path = folder + filename
image = cv2.imread(path)
# resize image
frame = imutils.resize(image, width=1200)
# show the image
cv2_imshow(frame)
cv2.waitKey(20)
I don't know if some function can display image in the same place.
But I have code which I used with cv2 to display frames from webcam as video.
Here reduced version.
imshow(name, image) creates <img id="name"> and replaces src/url with image converted to string base64 and browser shows it as image.
imshow() uses name to check if already exist <img id="name"> and it replaces previous image.
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64encode
import cv2
def imshow(name, img):
"""Put frame as <img src="data:image/jpg;base64,...."> """
js = Javascript('''
async function showImage(name, image, width, height) {
img = document.getElementById(name);
if(img == null) {
img = document.createElement('img');
img.id = name;
document.body.appendChild(img);
}
img.src = image;
img.width = width;
img.height = height;
}
''')
height, width = img.shape[:2]
ret, data = cv2.imencode('.jpg', img) # compress array of pixels to JPG data
data = b64encode(data) # encode base64
data = data.decode() # convert bytes to string
data = 'data:image/jpg;base64,' + data # join header ("data:image/jpg;base64,") and base64 data (JPG)
display(js)
eval_js(f'showImage("{name}", "{data}", {width}, {height})') # run JavaScript code to put image (JPG as string base64) in <img>
# `name` and `data` in needs `" "` to send it as text, not as name of variabe.
And here code which uses it to display image Lenna from Wikipedia.
import requests
import cv2
import numpy as np
import time
url = 'https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png'
data = requests.get(url)
frame1 = cv2.imdecode(np.frombuffer( data.content, np.uint8), 1)
frame2 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
for _ in range(3):
imshow("temp", frame1)
time.sleep(1)
imshow("temp", frame2)
time.sleep(1)
EDIT
Display images in two "windows" using imshow("img1", ...) and imshow("img2", ...)
import os
import cv2
import imutils
import time
folder = r'/content/drive/images/'
VALID_FORMAT = (".JPG", ".JPEG", ".PNG")
for number, filename in enumerate(os.listdir(folder)):
if filename.upper().endswith(VALID_FORMAT):
path = os.path.join(folder, filename)
image = cv2.imread(path)
frame = imutils.resize(image, width=400)
number = number % 2
imshow(f"img{number}", frame)
time.sleep(1)
I try using face recognition from this link: face recognition
then modif the code like this, main.py:
#!/usr/bin/env python
import cgitb, cgi
cgitb.enable()
print("Content-Type: text/html;charset=utf-8")
print "Content-type:text/html\r\n\r\n"
import base64
import simplejson as json
import re
import face_recognition
import numpy as np
import io
from imageio import imread
from PIL import Image
import datetime
import os, errno
import shutil
params = cgi.FieldStorage()
now = datetime.datetime.now()
date = str(now)
date2 = date.replace(" ","")
img = params.getvalue('img')
data1 = json.loads(img)
data2 = data1['img2']['data']
numparray = data1['img1']
numparray2 = numparray.replace(" ", "+")
b=bytes(numparray2)
imgdata = base64.b64decode(b)
os.makedirs(date2)
with open(date2+"/img1.png", "wb") as f:
f.write(imgdata)
image = face_recognition.load_image_file(date2+'/img1.png')
try:
face_encode = face_recognition.face_encodings(image)[0]
#print("face_encode = ".format(face_encode))
except IndexError:
print("encode image failed")
quit()
known_faces = []
y = 1
for images in data2:
ir = images.replace(" ", "+")
ib = bytes(ir)
imagedata = base64.b64decode(ib)
x = str(y)
with open(date2+"/compare"+x+".png", "wb") as g:
g.write(imagedata)
compare = face_recognition.load_image_file(date2+"/compare"+x+".png")
try:
compare_encode = face_recognition.face_encodings(compare)[0]
#print("face_encode = ".format(face_encode))
except IndexError:
print("encode image compare failed")
quit()
known_faces.append(compare_encode)
y = y+1
results = face_recognition.face_distance(known_faces, face_encode)
datahasil = []
#hasilakhir = "{"
for i, face_distance in enumerate(results):
h = "{:.2}".format(face_distance, i)
#hasilakhir = hasilakhir+"compare{}"
datahasil.append(h)
hasilakhir = ','.join(datahasil)
shutil.rmtree(date2, ignore_errors=True)
print("{\"hasilcompare\" : \"" +hasilakhir+ "\"}")
the final result is compare between 2 image and give the score, in case photo of image is potrait compare is successfull, but when one of image is not on potrait (face not on potrait potision) or like face angle more than 90 degree, that give error message in catch encode failed..
i have try another way with face detection before sending 2 image to main.py to detect the image in face but when it can't detect faces i try to rotate the image untill the code detect face, but sometimes face detection can detect face with angle 90degree but in face recognition(main.py) still can't read the face.
code of rotate is here, rotate.py:import numpy as np
import cv2
from scipy import ndimage, misc
import os
from PIL import Image
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
for counter in range (0, 4):
img = cv2.imread('img/1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
if(len(faces) == 1):
i = False
print ("face found")
break
else:
print("no face found")
i = False
img = Image.open("img/1.jpg")
img.rotate(90).save("img/1.jpg")
Yes, I think this is the problem with the library. I tested it on the same image. Once after rotation and once on the original image. It does not detect in the rotated image, both the face_locations and face_encodings are empty lists.
When taking pictures with my NIKON D5100 I shoot both in raw (.NEF) and jpeg. Most of the time I don't use that raw image, but from time to time I do.
To ease the tagging and selection of the images I want to automatically move the raw images to a different location. As a check in the python script to move the raw images I want to compare the image resolution of the jpeg to the raw resolution. So far I have not found a method that returns the same size for the raw and jpeg image.
This is a summary of the methods I tried using Python:
from PIL import Image as PIL_Image
from wand.image import Image as wand_Image
import imageio
from PIL.ExifTags import TAGS
import exifread
from collections import namedtuple
def get_exif(fname):
# http://www.blog.pythonlibrary.org/2010/03/28/getting-photo-metadata-exif-using-python/
ret = {}
i = PIL_Image.open(fname)
info = i._getexif()
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
ret[decoded] = value
return ret
def get_imagesize_PIL(fname):
with PIL_Image.open(fname) as im:
return im.size
def get_imagesize_PIL2(fname):
exif = get_exif(fname)
return exif['ExifImageHeight'], exif['ExifImageWidth']
def get_imagesize_PIL3(fname):
with PIL_Image.open(fname) as im:
return im.info
def get_imagesize_imageioraw(fname):
return imageio.imread(fname, "raw").shape[:-1]
def get_imagesize_imageio(fname):
return imageio.imread(fname).shape[:-1]
def get_imagesize_exifread(fname): # http://stackoverflow.com/a/18027454/1562285
exif = exifread.process_file(open(fname, 'rb'), strict=True)
return exif['Image XResolution'], exif['Image YResolution']
def get_imagesize_exifread2(fname):
exif = exifread.process_file(open(fname, 'rb'), strict=True)
return exif['Image ImageLength'], exif['Image ImageWidth']
def get_imagesize_exifread3(fname):
exif = exifread.process_file(open(fname, 'rb'), strict=True)
return exif['MakerNote CropHiSpeed']
def get_imagesize_exifread4(fname):
exif = exifread.process_file(open(fname, 'rb'), strict=True)
return exif['EXIF ExifImageLength'], exif['EXIF ExifImageWidth']
def get_imagesize_wand(fname):
with wand_Image(filename=fname) as img:
return img.size
def get_imagesize_wand2(fname):
with wand_Image(filename=fname) as img:
return img.page
# def get_imagesize_wand3(fname):
# with wand_Image(filename=fname) as img:
# return img.info
def get_imagesize_libraw(fname):
with rawkit_Raw(filename=fname) as raw:
print(raw.Metadata.height, raw.Metadata.width)
def create_eval(fmethod, fname):
try:
eval_str = "get_imagesize_%s('%s')" % (fmethod, fname)
# print(eval_str)
return eval(eval_str)
except BaseException as e:
return str(e)
if __name__ == '__main__':
file_nt = namedtuple("image_file", "filename tag")
filetypes = list()
filetypes.append(file_nt("20120917_131155 DSC_0159.JPG", "jpeg"))
filetypes.append(file_nt("20120917_131155 DSC_0159.NEF", "nef"))
# filetypes.append(file_nt("20120917_131155 DSC_0159.xmp", "xmp"))
# #TODO: add method to check into xmp?
methods = [
"PIL",
"PIL2",
"PIL3",
"imageioraw",
"imageio",
"exifread",
"exifread2",
"exifread3",
"exifread4",
"wand",
"wand2",
"libraw",
]
for method in methods:
for filetype in filetypes:
print("%s %s: %s" % (filetype.tag, method, repr(create_eval(method, filetype.filename))))
# #TODO: add timers to check the fastest method
with this result
jpeg PIL: (4928, 3264)
nef PIL: (160, 120)
jpeg PIL2: (3264, 4928)
nef PIL2: "'TiffImageFile' object has no attribute '_getexif'"
jpeg PIL3: {'exif': b'Exif\x00\x00MM\x00*\x00\x00\x00\x08\x00\x0b\x01\x0f\x00\x02\x00\x00\x00\x12\x00\x00\x00\x94\x01\x10\x00\x02\x00\x00\x00\x0c\x00\x00\x00\xa8\x01\x12\x00...'}
nef PIL3: {'compression': 'raw', 'dpi': (300.0, 300.0)}
jpeg imageioraw: 'Could not load bitmap "C:\\Users\\maarten\\PycharmProjects\\fotosize\\20120917_131155 DSC_0159.JPG": LibRaw : failed to open input stream (unknown format)'
nef imageioraw: (3280, 4948)
jpeg imageio: (3264, 4928)
nef imageio: (120, 160)
jpeg exifread: ((0x011A) Ratio=300 # 180, (0x011B) Ratio=300 # 188)
nef exifread: ((0x011A) Ratio=300 # 356, (0x011B) Ratio=300 # 364)
jpeg exifread2: "'Image ImageLength'"
nef exifread2: ((0x0101) Long=120 # 42, (0x0100) Long=160 # 30)
jpeg exifread3: (0x001B) Short=[0, 4992, 3280, 4992, 3280, 0, 0] # 1676
nef exifread3: (0x001B) Short=[0, 4992, 3280, 4992, 3280, 0, 0] # 1852
jpeg exifread4: ((0xA003) Short=3264 # 526, (0xA002) Short=4928 # 514)
nef exifread4: "'EXIF ExifImageLength'"
jpeg wand: (4928, 3264)
nef wand: (4948, 3280)
jpeg wand2: (4928, 3264, 0, 0)
nef wand2: (4948, 3280, 0, 0)
jpeg libraw: 'Cannot find LibRaw on your system!'
nef libraw: 'Cannot find LibRaw on your system!'
The ones returning 160x120 is probably for the embedded preview image
Windows explorer does seem to find the correct dimensions
screenshot
I've tried rawkit too, but it doesn't seem to find the libraw. I found no clear instructions on how to 'install' libraw, but I tried copying it named as libraw.dll or Raw.dll into "C:\Anaconda3\Library\bin", and when I try
In[3]: ctypes.util.find_library("libraw")
Out[3]:
'C:\\Anaconda3\\Library\\bin\\libraw.dll'
In[4]: ctypes.util.find_library("Raw")
Out[4]:
'C:\\Anaconda3\\Library\\bin\\Raw.dll'
it does seem to find it.
Does anyone know what method I can use? It doesn't have to be the same method for the jpeg and the NEF-image, but that'd be welcome.
At this moment performance isn't a key issue, but the faster the better
The code and a sample image can be found in my github repo
ImageMagick identifies them as follows:
identify sign.jpg
Output
sign.jpg JPEG 4928x3264 4928x3264+0+0 8-bit sRGB 4.401MB 0.000u 0:00.009
and for the NEF:
identify ~/Desktop/*NEF
Output
/Users/... NEF 4948x3280 4948x3280+0+0 16-bit sRGB 75.99MB 0.010u 0:00.009
I've found a different solution employing win32com thanks to this postP
from pprint import pprint
import win32com.client
import os
import re
from pathlib import Path
# https://bytes.com/topic/python/answers/802917-finding-file-details
def _get_dimensions_subdir(folder_name):
sh = win32com.client.Dispatch('Shell.Application')
ns = sh.NameSpace(folder_name)
results = {}
for c in range(0, 255):
colname = ns.GetDetailsOf(None, c)
if colname == 'Dimensions':
for i in ns.Items():
dimensions = ns.GetDetailsOf(i, c)
if dimensions:
results[ns.GetDetailsOf(i, 0)] = _parse_dimensions(dimensions)
return results
dim_pat = re.compile("(\d+) x (\d+)")
def _parse_dimensions(dim):
x, y = dim_pat.findall(dim)[0]
return int(x), int(y)
def get_combined_results(subdirs, src_path):
for subdir in subdirs:
dim = _get_dimensions_subdir(str(subdir))
if dim:
yield str(subdir.relative_to(src_path)), dim
if __name__ == '__main__':
DATA_DIR = Path(".")
subdirs = DATA_DIR.glob("**")
results = get_combined_results(subdirs, DATA_DIR)
pprint(dict(results))
I'm using PIL. How do I turn the EXIF data of a picture into a dictionary?
You can use the _getexif() protected method of a PIL Image.
import PIL.Image
img = PIL.Image.open('img.jpg')
exif_data = img._getexif()
This should give you a dictionary indexed by EXIF numeric tags. If you want the dictionary indexed by the actual EXIF tag name strings, try something like:
import PIL.ExifTags
exif = {
PIL.ExifTags.TAGS[k]: v
for k, v in img._getexif().items()
if k in PIL.ExifTags.TAGS
}
For Python3.x and starting Pillow==6.0.0, Image objects now provide a "public"/official getexif() method that returns a <class 'PIL.Image.Exif'> instance or None if the image has no EXIF data.
From Pillow 6.0.0 release notes:
getexif() has been added, which returns an Exif instance. Values can
be retrieved and set like a dictionary. When saving JPEG, PNG or WEBP,
the instance can be passed as an exif argument to include any changes
in the output image.
As stated, you can iterate over the key-value pairs of the Exif instance like a regular dictionary. The keys are 16-bit integers that can be mapped to their string names using the ExifTags.TAGS module.
from PIL import Image, ExifTags
img = Image.open("sample.jpg")
img_exif = img.getexif()
print(type(img_exif))
# <class 'PIL.Image.Exif'>
if img_exif is None:
print('Sorry, image has no exif data.')
else:
for key, val in img_exif.items():
if key in ExifTags.TAGS:
print(f'{ExifTags.TAGS[key]}:{val}')
# ExifVersion:b'0230'
# ...
# FocalLength:(2300, 100)
# ColorSpace:1
# ...
# Model:'X-T2'
# Make:'FUJIFILM'
# LensSpecification:(18.0, 55.0, 2.8, 4.0)
# ...
# DateTime:'2019:12:01 21:30:07'
# ...
Tested with Python 3.8.8 and Pillow==8.1.0.
You can also use the ExifRead module:
import exifread
# Open image file for reading (binary mode)
f = open(path_name, 'rb')
# Return Exif tags
tags = exifread.process_file(f)
I use this:
import os,sys
from PIL import Image
from PIL.ExifTags import TAGS
for (k,v) in Image.open(sys.argv[1])._getexif().items():
print('%s = %s' % (TAGS.get(k), v))
or to get a specific field:
def get_field (exif,field) :
for (k,v) in exif.items():
if TAGS.get(k) == field:
return v
exif = image._getexif()
print get_field(exif,'ExposureTime')
import sys
import PIL
import PIL.Image as PILimage
from PIL import ImageDraw, ImageFont, ImageEnhance
from PIL.ExifTags import TAGS, GPSTAGS
class Worker(object):
def __init__(self, img):
self.img = img
self.exif_data = self.get_exif_data()
self.lat = self.get_lat()
self.lon = self.get_lon()
self.date =self.get_date_time()
super(Worker, self).__init__()
#staticmethod
def get_if_exist(data, key):
if key in data:
return data[key]
return None
#staticmethod
def convert_to_degress(value):
"""Helper function to convert the GPS coordinates
stored in the EXIF to degress in float format"""
d0 = value[0][0]
d1 = value[0][1]
d = float(d0) / float(d1)
m0 = value[1][0]
m1 = value[1][1]
m = float(m0) / float(m1)
s0 = value[2][0]
s1 = value[2][1]
s = float(s0) / float(s1)
return d + (m / 60.0) + (s / 3600.0)
def get_exif_data(self):
"""Returns a dictionary from the exif data of an PIL Image item. Also
converts the GPS Tags"""
exif_data = {}
info = self.img._getexif()
if info:
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
if decoded == "GPSInfo":
gps_data = {}
for t in value:
sub_decoded = GPSTAGS.get(t, t)
gps_data[sub_decoded] = value[t]
exif_data[decoded] = gps_data
else:
exif_data[decoded] = value
return exif_data
def get_lat(self):
"""Returns the latitude and longitude, if available, from the
provided exif_data (obtained through get_exif_data above)"""
# print(exif_data)
if 'GPSInfo' in self.exif_data:
gps_info = self.exif_data["GPSInfo"]
gps_latitude = self.get_if_exist(gps_info, "GPSLatitude")
gps_latitude_ref = self.get_if_exist(gps_info, 'GPSLatitudeRef')
if gps_latitude and gps_latitude_ref:
lat = self.convert_to_degress(gps_latitude)
if gps_latitude_ref != "N":
lat = 0 - lat
lat = str(f"{lat:.{5}f}")
return lat
else:
return None
def get_lon(self):
"""Returns the latitude and longitude, if available, from the
provided exif_data (obtained through get_exif_data above)"""
# print(exif_data)
if 'GPSInfo' in self.exif_data:
gps_info = self.exif_data["GPSInfo"]
gps_longitude = self.get_if_exist(gps_info, 'GPSLongitude')
gps_longitude_ref = self.get_if_exist(gps_info, 'GPSLongitudeRef')
if gps_longitude and gps_longitude_ref:
lon = self.convert_to_degress(gps_longitude)
if gps_longitude_ref != "E":
lon = 0 - lon
lon = str(f"{lon:.{5}f}")
return lon
else:
return None
def get_date_time(self):
if 'DateTime' in self.exif_data:
date_and_time = self.exif_data['DateTime']
return date_and_time
if __name__ == '__main__':
try:
img = PILimage.open(sys.argv[1])
image = Worker(img)
lat = image.lat
lon = image.lon
date = image.date
print(date, lat, lon)
except Exception as e:
print(e)
I have found that using ._getexif doesn't work in higher python versions, moreover, it is a protected class and one should avoid using it if possible.
After digging around the debugger this is what I found to be the best way to get the EXIF data for an image:
from PIL import Image
def get_exif(path):
return Image.open(path).info['parsed_exif']
This returns a dictionary of all the EXIF data of an image.
Note: For Python3.x use Pillow instead of PIL
Here's the one that may be little easier to read. Hope this is helpful.
from PIL import Image
from PIL import ExifTags
exifData = {}
img = Image.open(picture.jpg)
exifDataRaw = img._getexif()
for tag, value in exifDataRaw.items():
decodedTag = ExifTags.TAGS.get(tag, tag)
exifData[decodedTag] = value
To read image url and get tags
from PIL import Image
from urllib.request import urlopen
from PIL.ExifTags import TAGS
def get_exif(filename):
image = Image.open(filename)
image.verify()
return image._getexif()
def get_labeled_exif(exif):
labeled = {}
for (key, val) in exif.items():
labeled[TAGS.get(key)] = val
return labeled
my_image= urlopen(url)
exif = get_exif(my_image)
labeled = get_labeled_exif(exif)
print(labeled)
and to get GPS coordinate, Jayson DeLancey has excellent blog post.
Feb 2023 Pillow information
Starting version 8.2.0 API of PIL changed slightly, hiding most of tags a bit deeper into methods of Exif. All other answers became slightly outdated, showing only few tags (around 14).
The modern way of doing it:
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS, IFD
from pillow_heif import register_heif_opener # HEIF support
register_heif_opener() # HEIF support
def print_exif(fname: str):
img = Image.open(fname)
exif = img.getexif()
print('>>>>>>>>>>>>>>>>>>', 'Base tags', '<<<<<<<<<<<<<<<<<<<<')
for k, v in exif.items():
tag = TAGS.get(k, k)
print(tag, v)
for ifd_id in IFD:
print('>>>>>>>>>', ifd_id.name, '<<<<<<<<<<')
try:
ifd = exif.get_ifd(ifd_id)
if ifd_id == IFD.GPSInfo:
resolve = GPSTAGS
else:
resolve = TAGS
for k, v in ifd.items():
tag = resolve.get(k, k)
print(tag, v)
except KeyError:
pass
Only some of useful tags are available on the root level of Exif now (e.g. Make, Model, DateTime, Orientation, Software. In order to access other useful tags, such as ShutterSpeedValue, ApertureValue, ISOSpeedRatings, WhiteBalance, DateTimeOriginal, DateTimeDigitized, ExposureBiasValue, FocalLength, ExifImageWidth, ExifImageHeight, etc, you need to get an IFD called Exif. For GPS information, use IFD GPSInfo. Also note that GPS tags have another tag-to-int encoding dictionary.
These two lines
from pillow_heif import register_heif_opener
register_heif_opener()
are required only if you want to have support of HEIF format, that is configured by default on modern Apple devices (.HEIC file extension). If you don't need to work with HEIF, you can omit them, the code will work for the rest of image formats supported by PIL.
Package references:
Pillow, the way to work with images in Python.
pillow-heif or it's lighter version pi-heif (no save() support).
I usually use pyexiv2 to set exif information in JPG files, but when I import the library in a script QGIS script crash.
I found a solution using the library exif:
https://pypi.org/project/exif/
It's so easy to use, and with Qgis I don,'t have any problem.
In this code I insert GPS coordinates to a snapshot of screen:
from exif import Image
with open(file_name, 'rb') as image_file:
my_image = Image(image_file)
my_image.make = "Python"
my_image.gps_latitude_ref=exif_lat_ref
my_image.gps_latitude=exif_lat
my_image.gps_longitude_ref= exif_lon_ref
my_image.gps_longitude= exif_lon
with open(file_name, 'wb') as new_image_file:
new_image_file.write(my_image.get_file())
I'm trying to convert an UploadedFile to a PIL Image object to thumbnail it, and then convert the PIL Image object that my thumbnail function returns back into a File object. How can I do this?
The way to do this without having to write back to the filesystem, and then bring the file back into memory via an open call, is to make use of StringIO and Django InMemoryUploadedFile. Here is a quick sample on how you might do this. This assumes that you already have a thumbnailed image named 'thumb':
import StringIO
from django.core.files.uploadedfile import InMemoryUploadedFile
# Create a file-like object to write thumb data (thumb data previously created
# using PIL, and stored in variable 'thumb')
thumb_io = StringIO.StringIO()
thumb.save(thumb_io, format='JPEG')
# Create a new Django file-like object to be used in models as ImageField using
# InMemoryUploadedFile. If you look at the source in Django, a
# SimpleUploadedFile is essentially instantiated similarly to what is shown here
thumb_file = InMemoryUploadedFile(thumb_io, None, 'foo.jpg', 'image/jpeg',
thumb_io.len, None)
# Once you have a Django file-like object, you may assign it to your ImageField
# and save.
...
Let me know if you need more clarification. I have this working in my project right now, uploading to S3 using django-storages. This took me the better part of a day to properly find the solution here.
I've had to do this in a few steps, imagejpeg() in php requires a similar process. Not to say theres no way to keep things in memory, but this method gives you a file reference to both the original image and thumb (usually a good idea in case you have to go back and change your thumb size).
save the file
open it from filesystem with PIL,
save to a temp directory with PIL,
then open as a Django file for this to work.
Model:
class YourModel(Model):
img = models.ImageField(upload_to='photos')
thumb = models.ImageField(upload_to='thumbs')
Usage:
#in upload code
uploaded = request.FILES['photo']
from django.core.files.base import ContentFile
file_content = ContentFile(uploaded.read())
new_file = YourModel()
#1 - get it into the DB and file system so we know the real path
new_file.img.save(str(new_file.id) + '.jpg', file_content)
new_file.save()
from PIL import Image
import os.path
#2, open it from the location django stuck it
thumb = Image.open(new_file.img.path)
thumb.thumbnail(100, 100)
#make tmp filename based on id of the model
filename = str(new_file.id)
#3. save the thumbnail to a temp dir
temp_image = open(os.path.join('/tmp',filename), 'w')
thumb.save(temp_image, 'JPEG')
#4. read the temp file back into a File
from django.core.files import File
thumb_data = open(os.path.join('/tmp',filename), 'r')
thumb_file = File(thumb_data)
new_file.thumb.save(str(new_file.id) + '.jpg', thumb_file)
This is actual working example for python 3.5 and django 1.10
in views.py:
from io import BytesIO
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import InMemoryUploadedFile
def pill(image_io):
im = Image.open(image_io)
ltrb_border = (0, 0, 0, 10)
im_with_border = ImageOps.expand(im, border=ltrb_border, fill='white')
buffer = BytesIO()
im_with_border.save(fp=buffer, format='JPEG')
buff_val = buffer.getvalue()
return ContentFile(buff_val)
def save_img(request)
if request.POST:
new_record = AddNewRecordForm(request.POST, request.FILES)
pillow_image = pill(request.FILES['image'])
image_file = InMemoryUploadedFile(pillow_image, None, 'foo.jpg', 'image/jpeg', pillow_image.tell, None)
request.FILES['image'] = image_file # really need rewrite img in POST for success form validation
new_record.image = request.FILES['image']
new_record.save()
return redirect(...)
Putting together comments and updates for Python 3+
from io import BytesIO
from django.core.files.base import ContentFile
import requests
# Read a file in
r = request.get(image_url)
image = r.content
scr = Image.open(BytesIO(image))
# Perform an image operation like resize:
width, height = scr.size
new_width = 320
new_height = int(new_width * height / width)
img = scr.resize((new_width, new_height))
# Get the Django file object
thumb_io = BytesIO()
img.save(thumb_io, format='JPEG')
photo_smaller = ContentFile(thumb_io.getvalue())
To complete for those who, like me, want to couple it with Django's FileSystemStorage:
(What I do here is upload an image, resize it to 2 dimensions and save both files.
utils.py
def resize_and_save(file):
size = 1024, 1024
thumbnail_size = 300, 300
uploaded_file_url = getURLforFile(file, size, MEDIA_ROOT)
uploaded_thumbnail_url = getURLforFile(file, thumbnail_size, THUMBNAIL_ROOT)
return [uploaded_file_url, uploaded_thumbnail_url]
def getURLforFile(file, size, location):
img = Image.open(file)
img.thumbnail(size, Image.ANTIALIAS)
thumb_io = BytesIO()
img.save(thumb_io, format='JPEG')
thumb_file = InMemoryUploadedFile(thumb_io, None, file.name, 'image/jpeg', thumb_io.tell, None)
fs = FileSystemStorage(location=location)
filename = fs.save(file.name, thumb_file)
return fs.url(filename)
In views.py
if request.FILES:
fl, thumbnail = resize_and_save(request.FILES['avatar'])
#delete old profile picture before saving new one
try:
os.remove(BASE_DIR + user.userprofile.avatarURL)
except Exception as e:
pass
user.userprofile.avatarURL = fl
user.userprofile.thumbnailURL = thumbnail
user.userprofile.save()
Here is an app that can do that: django-smartfields
from django.db import models
from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor
class ImageModel(models.Model):
image = fields.ImageField(dependencies=[
FileDependency(processor=ImageProcessor(
scale={'max_width': 150, 'max_height': 150}))
])
Make sure to pass keep_orphans=True to the field, if you want to keep old files, otherwise they are cleaned up upon replacement.
For those using django-storages/-redux to store the image file on S3, here's the path I took (the example below creates a thumbnail of an existing image):
from PIL import Image
import StringIO
from django.core.files.storage import default_storage
try:
# example 1: use a local file
image = Image.open('my_image.jpg')
# example 2: use a model's ImageField
image = Image.open(my_model_instance.image_field)
image.thumbnail((300, 200))
except IOError:
pass # handle exception
thumb_buffer = StringIO.StringIO()
image.save(thumb_buffer, format=image.format)
s3_thumb = default_storage.open('my_new_300x200_image.jpg', 'w')
s3_thumb.write(thumb_buffer.getvalue())
s3_thumb.close()