Skip to content

Commit

Permalink
Processing backend (#38)
Browse files Browse the repository at this point in the history
Fixes #5, fixes #6, fixes #31.

* Added upload functionality/image storage/UUID

* Combined segmentation and backend functionality

* Added download functionality

* Bug fixes for test server

* Fixed regex bug for image saving

* Adds example images.

* Uses Flask debug server instead of gunicorn for testing.

* Adds backend testing script.

* Fixes some backend bugs
- user entries are now saved correctly
- image conversion is now more robust
- support for downloading jpeg, png and gif files
- validation to match new endpoints

* Got the segmentation process fixed.
- turns out you cannot send a boolean array to Image.save()

* Implements /list endpoint.
- for getting the ids of the original and processed images

* /download now uses uuid for choosing the file

* Functional frontend.
- all endpoints now implemented
- both original and processed image are displayed

* Added fix for converting RGBA images to JPEG.

* Removes console.log.

* Small fixes
- fixes Icon classname -> className
- adds logout notification
  • Loading branch information
rvshi committed May 1, 2018
1 parent 68b0da3 commit 18dfab4
Show file tree
Hide file tree
Showing 29 changed files with 559 additions and 192 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ ENV/
# database
db

# backend files
secret_key.py
db_debug.py

Expand Down
64 changes: 52 additions & 12 deletions backend/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
from pymodm import connect

from segment import segment
from database import login_user, get_image
from database import(login_user,
get_processed_image,
get_original_image, save_original_image_uuid,
save_processed_image_uuid,
remove_images)
from images import save_image, get_image_by_uuid, get_image_as_b64


def act_login(req):
"""Authenticates login with email
"""Authenticates login with email and password
: param req: json request from client
"""
Expand All @@ -26,26 +31,61 @@ def act_login(req):
return jsonify({'error': 'username or password is incorrect'}), 400


def act_list(username):
"""Lists the original and processed images for a user
:param username: client username
"""
return(jsonify({
'originalID': get_original_image(username),
'processedID': get_processed_image(username)
}), 200)


def act_upload(req):
"""Uploads user image and processes image
"""Uploads original user image
:param req: .mat file from client
:param req: request from client
"""
matfile = req['matfile']
segment(matfile)
params = request.get_json()
username = params.get('username', None)
file = params.get('file', None)

uuid = save_image(file)
if uuid is None:
return (jsonify({'error': 'Error saving image'}), 400)

remove_images(username)
save_original_image_uuid(username, uuid)
return (jsonify({'fileID': uuid}), 200)


def act_process(req):
"""Processes image that has already been uploaded
"""Processes the original image that has been uploaded
:param req: request from client
"""
pass

username = request.get_json().get('username', None)

uuid = get_original_image(username)
if uuid is None:
return (jsonify({'error': 'Original image not found'}), 400)
newUuid = segment(uuid)
save_processed_image_uuid(username, newUuid)
return (jsonify({'fileID': newUuid}), 200)


def act_download(req):
'''Processes download request
'''Handles download request for images
:param req: json request from client
'''
img_str = get_image(req.username)
filetype = req['filetype']
return jsonify({'image': img_str, 'filetype': filetype})
params = request.get_json()
fileID = params.get('fileID', None)
filetype = params.get('filetype', None)

if fileID is None:
return (jsonify({'error': 'Processed image not found'}), 400)
img_str = get_image_as_b64(fileID, filetype=filetype)
return (jsonify({'file': img_str}), 200)
34 changes: 0 additions & 34 deletions backend/convert.py

This file was deleted.

59 changes: 49 additions & 10 deletions backend/database.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from pymodm import fields, MongoModel, connect
from pymodm.errors import DoesNotExist
from passlib.hash import pbkdf2_sha256
Expand All @@ -6,9 +7,10 @@


class User(MongoModel):
email = fields.EmailField(primary_key=True)
username = fields.EmailField(primary_key=True)
password = fields.CharField()
image = fields.CharField() # the most recent image
original_image = fields.CharField() # original image
processed_image = fields.CharField()


def add_user(username, password):
Expand Down Expand Up @@ -48,17 +50,54 @@ def login_user(username, password):
return False


def save_image(username, image_str):
user = User.objects.raw({'_id': username}).first()
user.update(
{'$set': {'image': image_str}})
def save_original_image_uuid(username, uuid):
try:
user = User.objects.raw({'_id': username}).first()
user.original_image = uuid
user.save()
except DoesNotExist:
return None


def get_image(username):
def save_processed_image_uuid(username, uuid):
try:
user = User.objects.raw({'_id': username}).first()
return user.image
user.processed_image = uuid
user.save()
except DoesNotExist:
pass
return None


def get_original_image(username):
try:
user = User.objects.raw({'_id': username}).first()
return user.original_image
except DoesNotExist:
return None


def get_processed_image(username):
try:
user = User.objects.raw({'_id': username}).first()
return user.processed_image
except DoesNotExist:
return None

return None

def delete_image(name):
for f in os.listdir('images/'):
if f.startswith(name):
os.remove('images/' + f)
return


def remove_images(username):
try:
user = User.objects.raw({'_id': username}).first()
if user.original_image is not None:
delete_image(user.original_image)
if user.processed_image is not None:
delete_image(user.processed_image)
return True
except DoesNotExist:
return False
77 changes: 77 additions & 0 deletions backend/debug_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import requests
import base64

baseURL = 'http://localhost:5000/'

test_img = 'exampleImages/test.jpg'
filetype = 'jpeg'

un = 'test@mail.com'
pw = '12345'


def main():
headers = login(un, pw)
print(_list(headers, un))
origID = upload(headers, un, test_img)
procID = process(headers, un)
download(headers, un, procID, filetype)
print(_list(headers, un))


def read_image(path):
with open(path, 'rb') as image_file:
return base64.b64encode(image_file.read()).decode()


def login(username, password):
body = {
'username': username,
'password': password
}
r = requests.post(baseURL + 'login', json=body)
jwt = r.json()['jwt']

__headers__ = {
'authorization': 'Bearer ' + jwt,
'content-type': 'application/json',
}
return __headers__


def _list(headers, username):
r = requests.get(baseURL + 'list', headers=headers)
return r.text


def upload(headers, username, test_img):
body = {
'username': username,
'file': 'data:image/png;base64,' + read_image(test_img)
}
r = requests.post(baseURL + 'upload', headers=headers, json=body)
return r.json()['fileID']


def process(headers, username):
body = {
'username': username
}
r = requests.post(baseURL + 'process', headers=headers, json=body)
return r.json()['fileID']


def download(headers, username, fileID, img_format):
body = {
'username': username,
'fileID': fileID,
'filetype': img_format
}
r = requests.post(baseURL + 'download', headers=headers, json=body)
data = r.json()['file']
with open('exampleImages/temp.{}'.format(img_format), 'wb') as f:
f.write(base64.decodebytes(data.encode()))


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions backend/exampleImages/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
temp*
File renamed without changes.
File renamed without changes.
Binary file added backend/exampleImages/book.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added backend/exampleImages/chess.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added backend/exampleImages/cornea.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added backend/exampleImages/test.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added backend/exampleImages/tower.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added backend/exampleImages/wall.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 102 additions & 0 deletions backend/images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import base64
import numpy as np
from PIL import Image
from mimetypes import guess_extension
from uuid import uuid4
import re
import os
from io import BytesIO


def save_image(img_str):
'''Converts image string to binary and saves onto drive
:param img_str: base64 image string
:return uuid: UUID4 of image'''

uuid = uuid4().hex
str_parts = img_str.split(',')

if(len(str_parts) == 2):
img_type = str_parts[0]
img_raw = str_parts[1]

try:
extension = re.search('data:image/(.+);', img_type).group(1)
except AttributeError: # no match found
return None

with open('images/{}.png'.format(uuid, extension), 'wb') as f:
f.write(base64.b64decode(img_raw))
return uuid

return None


def save_image_from_arr(img_arr):
'''Converts uint array to png file (intermediary format stored on server)
:param img_arr: uint array
:return uuid: uuid of converted image'''

uuid = uuid4().hex
img = Image.fromarray(img_arr)
img.save('images/{}.png'.format(uuid), 'PNG')
return uuid


def get_image_by_uuid(uuid):
'''Converts image base64 string to uint array, saves intermediate image
file to server
:param img_str: base64 image string
:param uuid: UUID of image to save
:return data: grayscale image array
'''

for f in os.listdir('images/'):
if re.match(uuid, f):
im = Image.open('images/' + f)
im = im.convert('L')
im_arr = np.asarray(im)
return im_arr

return None


def get_image_as_b64(uuid, filetype='png'):
'''Gets b64 image string by uuid
:param uuid: uuid of image
:param filetype: file type to output, options are jpeg, png, or gif
:return: b64 string of image
'''

filetype = filetype.lower()
img_format = None
if filetype == 'png':
img_format = 'PNG'
elif filetype == 'jpeg' or filetype == 'jpg':
img_format = 'JPEG'
elif filetype == 'gif':
img_format = 'GIF'
else:
return None # error, incorrect file type

# convert file to desired type

for f in os.listdir('images/'):
if f.startswith(uuid):
output = BytesIO()
image = Image.open('images/' + f.format(uuid))

# handle transparent images
if image.mode in ('RGBA', 'LA'):
background = Image.new(image.mode[:-1], image.size, '#fff')
background.paste(image, image.split()[-1])
image = background

image.save(output, format=img_format)
contents = base64.b64encode(output.getvalue()).decode()
output.close()
return contents
Loading

0 comments on commit 18dfab4

Please sign in to comment.