diff --git a/.gitignore b/.gitignore index 894a44c..bf591d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# MacOS files +.DS_Store +images + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/output/5d1f390486bf42548051ea74353277d5_lg.jpg b/output/5d1f390486bf42548051ea74353277d5_lg.jpg new file mode 100644 index 0000000..570bf56 Binary files /dev/null and b/output/5d1f390486bf42548051ea74353277d5_lg.jpg differ diff --git a/output/5d1f390486bf42548051ea74353277d5_md.jpg b/output/5d1f390486bf42548051ea74353277d5_md.jpg new file mode 100644 index 0000000..570bf56 Binary files /dev/null and b/output/5d1f390486bf42548051ea74353277d5_md.jpg differ diff --git a/output/5d1f390486bf42548051ea74353277d5_sm.jpg b/output/5d1f390486bf42548051ea74353277d5_sm.jpg new file mode 100644 index 0000000..911f586 Binary files /dev/null and b/output/5d1f390486bf42548051ea74353277d5_sm.jpg differ diff --git a/output/5d1f390486bf42548051ea74353277d5_thumb.jpg b/output/5d1f390486bf42548051ea74353277d5_thumb.jpg new file mode 100644 index 0000000..28d3c81 Binary files /dev/null and b/output/5d1f390486bf42548051ea74353277d5_thumb.jpg differ diff --git a/output/629220597dfb462ab5cf18fa9ea6a592_lg.jpg b/output/629220597dfb462ab5cf18fa9ea6a592_lg.jpg new file mode 100644 index 0000000..4fd770b Binary files /dev/null and b/output/629220597dfb462ab5cf18fa9ea6a592_lg.jpg differ diff --git a/output/629220597dfb462ab5cf18fa9ea6a592_md.jpg b/output/629220597dfb462ab5cf18fa9ea6a592_md.jpg new file mode 100644 index 0000000..c9f4f59 Binary files /dev/null and b/output/629220597dfb462ab5cf18fa9ea6a592_md.jpg differ diff --git a/output/629220597dfb462ab5cf18fa9ea6a592_sm.jpg b/output/629220597dfb462ab5cf18fa9ea6a592_sm.jpg new file mode 100644 index 0000000..195afe5 Binary files /dev/null and b/output/629220597dfb462ab5cf18fa9ea6a592_sm.jpg differ diff --git a/output/629220597dfb462ab5cf18fa9ea6a592_thumb.jpg b/output/629220597dfb462ab5cf18fa9ea6a592_thumb.jpg new file mode 100644 index 0000000..30f2816 Binary files /dev/null and b/output/629220597dfb462ab5cf18fa9ea6a592_thumb.jpg differ diff --git a/output/755071b1433e4bfb97e46266db2cd723_lg.jpg b/output/755071b1433e4bfb97e46266db2cd723_lg.jpg new file mode 100644 index 0000000..398a70f Binary files /dev/null and b/output/755071b1433e4bfb97e46266db2cd723_lg.jpg differ diff --git a/output/755071b1433e4bfb97e46266db2cd723_md.jpg b/output/755071b1433e4bfb97e46266db2cd723_md.jpg new file mode 100644 index 0000000..f72961f Binary files /dev/null and b/output/755071b1433e4bfb97e46266db2cd723_md.jpg differ diff --git a/output/755071b1433e4bfb97e46266db2cd723_sm.jpg b/output/755071b1433e4bfb97e46266db2cd723_sm.jpg new file mode 100644 index 0000000..0c46095 Binary files /dev/null and b/output/755071b1433e4bfb97e46266db2cd723_sm.jpg differ diff --git a/output/755071b1433e4bfb97e46266db2cd723_thumb.jpg b/output/755071b1433e4bfb97e46266db2cd723_thumb.jpg new file mode 100644 index 0000000..c9914af Binary files /dev/null and b/output/755071b1433e4bfb97e46266db2cd723_thumb.jpg differ diff --git a/output/7b8a991a427a4f0c8a446c0afa13ee91_lg.jpg b/output/7b8a991a427a4f0c8a446c0afa13ee91_lg.jpg new file mode 100644 index 0000000..55b6eb4 Binary files /dev/null and b/output/7b8a991a427a4f0c8a446c0afa13ee91_lg.jpg differ diff --git a/output/7b8a991a427a4f0c8a446c0afa13ee91_md.jpg b/output/7b8a991a427a4f0c8a446c0afa13ee91_md.jpg new file mode 100644 index 0000000..c50b472 Binary files /dev/null and b/output/7b8a991a427a4f0c8a446c0afa13ee91_md.jpg differ diff --git a/output/7b8a991a427a4f0c8a446c0afa13ee91_sm.jpg b/output/7b8a991a427a4f0c8a446c0afa13ee91_sm.jpg new file mode 100644 index 0000000..1808ec2 Binary files /dev/null and b/output/7b8a991a427a4f0c8a446c0afa13ee91_sm.jpg differ diff --git a/output/7b8a991a427a4f0c8a446c0afa13ee91_thumb.jpg b/output/7b8a991a427a4f0c8a446c0afa13ee91_thumb.jpg new file mode 100644 index 0000000..d83d0b9 Binary files /dev/null and b/output/7b8a991a427a4f0c8a446c0afa13ee91_thumb.jpg differ diff --git a/processor.py b/processor.py new file mode 100644 index 0000000..f5c31ee --- /dev/null +++ b/processor.py @@ -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}") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..423e011 --- /dev/null +++ b/requirements.txt @@ -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 diff --git a/server.py b/server.py new file mode 100644 index 0000000..ec7194b --- /dev/null +++ b/server.py @@ -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) \ No newline at end of file