Images processor with cli and json flask server.

This commit is contained in:
2019-02-23 23:20:26 +01:00
parent aaa04f28aa
commit 9c95ca8825
20 changed files with 150 additions and 0 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
# MacOS files
.DS_Store
images
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

82
processor.py Normal file
View File

@@ -0,0 +1,82 @@
import glob
import os
from PIL import Image
import concurrent.futures
import argparse
import fileinput
import uuid
import os.path
IMAGE_TYPES = ['.png', '.jpg', '.jpeg', '.JPG', '.PNG']
OUTPUT_EXTENSION = 'jpg'
OUTPUT_FALLBACK = os.path.dirname(__file__)
OUTPUT_SIZES = [
{ 'dimensions': (250, 250), 'name': 'thumb', 'crop': True },
{ 'dimensions': (650, 650), 'name': 'sm', 'crop': False },
{ 'dimensions': (1200, 1200), 'name': 'md', 'crop': False },
{ 'dimensions': (1800, 1800), 'name': 'lg', 'crop': False }]
def processImage(file, outputPath=None):
outputPath = args.output if 'args.output' in globals() else os.path.join(OUTPUT_FALLBACK, 'output')
print('outputpath', outputPath)
image = Image.open(file)
fileID = uuid.uuid4().hex
for size in OUTPUT_SIZES:
temp = image.copy()
if size['crop']:
temp = temp.crop(squareDimensions(temp.size))
temp.thumbnail(size['dimensions'], Image.LANCZOS)
filename = generateFilename(fileID, size['name'], outputPath)
temp.save(filename)
return '.'.join([fileID, OUTPUT_EXTENSION])
def generateFilename(fileID, modifier, outputPath):
filename = "{}_{}.{}".format(fileID, modifier, OUTPUT_EXTENSION)
return os.path.join(outputPath, filename)
def squareDimensions(dimensions):
(width, height) = dimensions
if width > height:
delta = width - height
left = int(delta/2)
upper = 0
right = height + left
lower = height
else:
delta = height - width
left = 0
upper = int(delta/2)
right = width
lower = width + upper
return (left, upper, right, lower)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Process some images')
parser.add_argument('files', metavar="files", type=str, help='Directory of images to process')
parser.add_argument('--output', metavar="DIR", help="Output directory")
class Args:
pass
args = Args()
args = parser.parse_args()
# Create a pool of processes. By default, one is created for each CPU in your machine.
with concurrent.futures.ProcessPoolExecutor() as executor:
# Get a list of files to process
image_files = glob.glob('{}/*'.format(args.files))
print('Processing and generating images in following sizes: {}'.format(OUTPUT_SIZES))
# Process the list of files, but split the work across the process pool to use all CPUs!
for image_file, output_file in zip(image_files, executor.map(processImage, image_files)):
print(f"Processed image {image_file} and save as {output_file}")

7
requirements.txt Normal file
View File

@@ -0,0 +1,7 @@
Click==7.0
Flask==1.0.2
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.0
Pillow==5.4.1
Werkzeug==0.14.1

57
server.py Normal file
View File

@@ -0,0 +1,57 @@
from flask import Flask, request, jsonify
from io import BytesIO
import os
from processor import processImage
app = Flask(__name__)
OUTPUT_PATH = 'thumbnails/'
class InvalidFiletype(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
@app.errorhandler(InvalidFiletype)
def handle_invalid_filetype(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
@app.route("/upload", methods=["POST"])
def upload():
print('Received uploads')
outputs = []
for upload in request.files.getlist('images'):
filename = upload.filename
print('processing file: ', filename)
ext = os.path.splitext(filename)[1][1:].strip().lower()
if ext in set(['jpg', 'jpfg', 'png']):
print('File supported moving on.')
else:
raise InvalidFiletype('Unsupported file type {}'.format(ext), status_code=415)
imageInBytes = BytesIO(upload.read())
outputFilename = processImage(imageInBytes, OUTPUT_PATH)
outputs.append(outputFilename)
response = jsonify({ 'filenames': outputs })
response.status_code = 200
# print(uploaded_files)
return response
if __name__ == '__main__':
app.run(port=5001)