Skip to content

Commit

Permalink
ASCIIfy + Artify :)
Browse files Browse the repository at this point in the history
  • Loading branch information
Christoph Theiß committed May 8, 2021
1 parent 3db423b commit a233c0f
Show file tree
Hide file tree
Showing 14 changed files with 392 additions and 70 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
backend/app/models/

# VS Code
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
40 changes: 40 additions & 0 deletions backend/app/artify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import numpy as np
import tensorflow as tf
from PIL import Image

def wrap_frozen_graph(graph_def, inputs, outputs, print_graph=False):
def _imports_graph_def():
tf.compat.v1.import_graph_def(graph_def, name="")

wrapped_import = tf.compat.v1.wrap_function(_imports_graph_def, [])
import_graph = wrapped_import.graph

layers = [op.name for op in import_graph.get_operations()]
if print_graph == True:
for layer in layers:
print(layer)
return wrapped_import.prune(
tf.nest.map_structure(import_graph.as_graph_element, inputs),
tf.nest.map_structure(import_graph.as_graph_element, outputs))

def load_artist_graph(artist, model_path):
print(f"Loading model for {artist}.")
try:
with tf.io.gfile.GFile(model_path / artist / "frozen_model.pb", "rb") as f:
graph_def = tf.compat.v1.GraphDef()
loaded = graph_def.ParseFromString(f.read())
frozen_func = wrap_frozen_graph(graph_def, ["placeholder/photo:0"], ["decoder/sub:0"])
except FileNotFoundError:
def frozen_func(x):
return x
return frozen_func

def artify(image, model):
image = image.convert("RGB")
np_image = np.asarray(image)
input_image = (
tf.expand_dims(tf.constant(np_image, dtype=tf.float32), 0) / 127.5 - 1.0
)
artified = (model(input_image)[0] + 1) * 127.5
artified = Image.fromarray(tf.squeeze(artified, 0).numpy().astype(np.uint8))
return artified
16 changes: 16 additions & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ class Config:
["jpg", "png", "jpeg", "tiff", "bmp", "gif", "ppm", "pgm", "tif", "svg"]
)
FONT_FOLDER = "fonts"
MODEL_FOLDER = "models"
ARTISTS = [
"cezanne",
"monet",
"el-greco",
"gauguin",
"kandinsky",
"kirchner",
"morisot",
"munch",
"peploe",
"picasso",
"pollock",
"roerich",
"van-gogh",
]


class ProdConfig(Config):
Expand Down
118 changes: 101 additions & 17 deletions backend/app/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,26 @@
from flask_talisman import Talisman
from PIL import Image
from pathlib import Path
from asciify import ASCIIfy
from io import BytesIO
import base64
import tensorflow as tf

import asciify
import artify

app = Flask(__name__)
app.config.from_object("config.DevConfig")
CORS(app)
Talisman(app)
api = Api(app)

model_selection = dict(
[
(artist, artify.load_artist_graph(artist, Path(app.config["MODEL_FOLDER"])))
for artist in app.config["ARTISTS"]
]
)


def allowed_file(filename):
"""
Expand All @@ -29,6 +39,13 @@ def allowed_file(filename):
)


def pil_to_base64_str(img):
data = BytesIO()
img.save(data, "JPEG")
data64 = base64.b64encode(data.getvalue())
return data64


class Home(Resource):
"""
The REST-Api response for the main directory.
Expand Down Expand Up @@ -62,7 +79,7 @@ def get(self):
}


class ASCIIfy_Image(Resource):
class ASCIIfyImage(Resource):
"""
This class will handle the request on sending an image to the server.
"""
Expand All @@ -75,11 +92,10 @@ def post(self):
"image", type=werkzeug.datastructures.FileStorage, location="files"
)
parser.add_argument("debug", required=False, type=int)
parser.add_argument("resize", required=False, type=int, default=-1)
parser.add_argument("fontsize", required=True, type=int)
parser.add_argument("font", required=True, type=str)
parser.add_argument("resize", required=True, type=int)
args = parser.parse_args()
print(args)

if args["debug"] is not None and args["debug"] == 1:
print(args)
Expand All @@ -89,32 +105,100 @@ def post(self):
image = Image.open(file)

# asciify it:
asciified = ASCIIfy(
asciified = asciify.ASCIIfy(
np.array(image),
args["resize"],
str(Path(app.config["FONT_FOLDER"], f"{args['font']}.ttf")),
args["fontsize"],
1,
)
data64 = pil_to_base64_str(asciified)

return Response(
response=data64.decode("utf-8"), status=200, mimetype="image/jpeg"
)


class Styles(Resource):
"""
returns a list of fonts
"""

def get(self):
"""
Return the list of available fonts.
"""
return {"styles": [artist.title() for artist in app.config["ARTISTS"]]}


class ArtifyImage(Resource):
def post(self):
parser = reqparse.RequestParser()

# we expect an image to be send, if not send message to the client
parser.add_argument(
"image", type=werkzeug.datastructures.FileStorage, location="files"
)
parser.add_argument(
"style", required=True, type=str, choices=app.config["ARTISTS"]
)
args = parser.parse_args()

file = request.files["image"]
image = Image.open(file)
artified = artify.artify(image, model_selection[args["style"]])
data64 = pil_to_base64_str(artified)

return Response(
response=data64.decode("utf-8"), status=200, mimetype="image/jpeg"
)


class BeautifyImage(Resource):
def post(self):
parser = reqparse.RequestParser()

# we expect an image to be send, if not send message to the client
parser.add_argument(
"image", type=werkzeug.datastructures.FileStorage, location="files"
)
parser.add_argument("debug", required=False, type=int)
parser.add_argument("resize", required=False, type=int, default=-1)
parser.add_argument("fontsize", required=True, type=int)
parser.add_argument("font", required=False, type=str)
parser.add_argument(
"style", required=False, type=str, choices=app.config["ARTISTS"]
)
args = parser.parse_args()

file = request.files["image"]
image = Image.open(file)

if args["style"] is not None:
image = artify.artify(image, model_selection[args["style"]])

data = BytesIO()
asciified.save(data, "JPEG")
data64 = base64.b64encode(data.getvalue())
if args["font"] is not None:
image = asciify.ASCIIfy(
np.array(image),
args["resize"],
str(Path(app.config["FONT_FOLDER"], f"{args['font']}.ttf")),
args["fontsize"],
1,
)

#_, cv_image = cv2.imencode(
# ".jpeg", np.array(asciified)[..., ::-1]
#)
data64 = pil_to_base64_str(image)

# headers = {
# 'Content-Type': "image/jpeg", # This is important
# }
# why?
return Response(response=data64.decode('utf-8'), status=200, mimetype="image/jpeg")
return Response(
response=data64.decode("utf-8"), status=200, mimetype="image/jpeg"
)


api.add_resource(Home, "/api/home")
api.add_resource(Fonts, "/api/fonts")
api.add_resource(ASCIIfy_Image, "/api/asciify")
api.add_resource(ASCIIfyImage, "/api/asciify")
api.add_resource(Styles, "/api/styles")
api.add_resource(ArtifyImage, "/api/artify")
api.add_resource(BeautifyImage, "/api/beautify")

# app.run(host, port): Start flask server by specifying host and port
if __name__ == "__main__":
Expand Down
5 changes: 3 additions & 2 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
opencv-python~=4.5
numpy~=1.20
numpy~=1.19
tqdm~=4.60
Pillow~=8.2
flask~=1.1
flask-cors~=3.0
flask-restful~=0.3.8
flask-talisman~=0.7.0
flask-talisman~=0.7.0
tensorflow~=2.5
22 changes: 22 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"vue": "^3.0.0",
"vue-next-select": "^2.1.1",
"vue-round-slider": "^1.0.1",
"vue-slider-component": "^3.2.11",
"vue-upload-drop-images": "^1.0.4",
"vue3-slider": "^1.6.2",
"vuetify": "^3.0.0-alpha.0"
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<v-app>
<v-main>
<h1 class="logo">ASCIIfy</h1>
<h1 class="logo">ArtSCIIfy</h1>
<div class="main-container">
<settings />
<drag-and-drop />
Expand Down Expand Up @@ -62,6 +62,13 @@ body::-webkit-scrollbar-thumb {
margin: auto;
}
@media screen and (max-width: 900px) {
.main-container {
flex-direction: column;
align-items: center;
}
}
.logo {
font-size: 70px;
width: 90%;
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/api/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,24 @@ export default {
let asciifiedImageURL = "data:img/jpeg;base64," + response.data;
return asciifiedImageURL;
},
async artify(formData) {
const response = await axios.post(`${API_URL}/api/artify`, formData);
let artifiedImageURL = "data:img/jpeg;base64," + response.data;
return artifiedImageURL;
},
async beautify(formData) {
const response = await axios.post(`${API_URL}/api/beautify`, formData);
let beautifiedImageURL = "data:img/jpeg;base64," + response.data;
return beautifiedImageURL;
},
async getFonts() {
const response = await axios.get(`${API_URL}/api/fonts`);
let fonts = response.data["fonts"];
return fonts;
},
async getStyles() {
const response = await axios.get(`${API_URL}/api/styles`);
let styles = response.data["styles"];
return styles;
},
};
Loading

0 comments on commit a233c0f

Please sign in to comment.