from flask_script import Manager
from app import create_app
app = create_app()
manager = Manager(app)
@manager.command
def runserver():
app.run(debug=True, host='0.0.0.0', port=5000)
if __name__ == '__main__':
manager.run()
from flask import Flask
def create_app():
app = Flask(__name__)
return app
import os
BASE_DIR = os.getcwd()
CLASSIFIER_STORAGE = os.path.join(BASE_DIR, 'storage/classifier.txt')
def create_root_view(app):
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def root(path):
return render_template("index.html")
class PredictDigitView(MethodView):
def post(self):
pass
import numpy as np
from skimage import exposure
import base64
from PIL import Image
from io import BytesIO
def data_uri_to_image(uri):
encoded_data = uri.split(',')[1]
image = base64.b64decode(encoded_data)
return Image.open(BytesIO(image))
def replace_transparent_background(image):
image_arr = np.array(image)
alpha1 = 0
r2, g2, b2, alpha2 = 255, 255, 255, 255
red, green, blue, alpha = (
image_arr[:, :, 0],
image_arr[:, :, 1],
image_arr[:, :, 2],
image_arr[:, :, 3]
)
mask = (alpha == alpha1)
image_arr[:, :, :4][mask] = [r2, g2, b2, alpha2]
return Image.fromarray(image_arr)
def crop_image_frame(image, color=255):
image_arr = np.array(image)
cropped_image_arr = image_arr[~np.all(image_arr == color, axis=1)]
cropped_image_arr = cropped_image_arr[:, ~np.all(cropped_image_arr == color, axis=0)]
return Image.fromarray(cropped_image_arr)
def pad_image(image, height=0, width=30, color=255):
return Image.fromarray(np.pad(
np.array(image),
((height, height), (width, width)),
'constant',
constant_values=color
))
def resize_image(image):
return image.resize((8, 8), Image.ANTIALIAS)
def white_to_black(image):
image_arr = np.array(image)
image_arr[image_arr > 230] = 0
return Image.fromarray(image_arr)
def to_flat_grayscaled_image_arr(image):
image_arr = np.array(image)
image_arr = exposure.rescale_intensity(image_arr, out_range=(0, 16))
return image_arr.flatten()
def to_classifier_input_format(data_uri):
raw_image = data_uri_to_image(data_uri)
image_with_background = replace_transparent_background(raw_image)
grayscaled_image = image_with_background.convert('L')
cropped_image = crop_image_frame(grayscaled_image)
padded_image = pad_image(cropped_image)
inverted_image = white_to_black(padded_image)
resized_image = resize_image(inverted_image)
return np.array([
to_flat_grayscaled_image_arr(resized_image)
])
import pickle
class ClassifierRepo:
def __init__(self, storage):
self.storage = storage
def get(self):
with open(self.storage, 'rb') as out:
try:
classifier_str = out.read()
if classifier_str != '':
return pickle.loads(classifier_str)
else:
return None
except Exception:
return None
def update(self, classifier):
with open(self.storage, 'wb') as in_:
pickle.dump(classifier, in_)
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
class ClassifierFactory:
@staticmethod
def create_with_fit(data, target):
model = KNeighborsClassifier(n_neighbors=3)
model.fit(data, target)
return model
from sklearn.datasets import load_digits
from app.classifier import ClassifierFactory
from app.utils import to_classifier_input_format
class PredictDigitService:
def __init__(self, repo):
self.repo = repo
def handle(self, image_data_uri):
classifier = self.repo.get()
if classifier is None:
digits = load_digits()
classifier = ClassifierFactory.create(
digits.data, digits.target
)
self.repo.update(classifier)
x = to_classifier_input_format(image_data_uri)
prediction = classifier.predict(x)[0]
return prediction
class PredictDigitView(MethodView):
def post(self):
repo = ClassifierRepo(CLASSIFIER_STORAGE)
service = PredictDigitService(repo)
image_data_uri = request.json['image']
prediction = service.handle(image_data_uri)
return Response(str(prediction).encode(), status=200)
def init_urls(app):
app.add_url_rule(
'/api/predict',
view_func=PredictDigitView.as_view('predict_digit'),
methods=['POST']
)
create_root_view(app)
from flask import Flask
from .urls import init_urls
def create_app():
app = Flask(__name__)
init_urls(app)
return app