-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from anras5/feature/webapp
Feature/webapp
- Loading branch information
Showing
9 changed files
with
425 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
.idea/ | ||
temp/ | ||
unet_checkpoint.pth |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,145 @@ | ||
# LOOK INTO notebooks/main.ipynb | ||
import uuid | ||
import os | ||
from typing import Tuple | ||
|
||
import cv2 | ||
import numpy as np | ||
|
||
from flask import Flask, render_template, redirect, url_for, flash | ||
from flask_wtf import FlaskForm | ||
from flask_wtf.file import FileField, FileRequired, FileAllowed | ||
from wtforms import SubmitField, BooleanField | ||
from werkzeug.utils import secure_filename | ||
|
||
from notebooks.classifiers.classic import ClassicClassifier | ||
from notebooks.classifiers.unet import UNetClassifier | ||
from notebooks.utils.confusion_matrix import ConfusionMatrix | ||
|
||
app = Flask(__name__) | ||
app.config.from_mapping( | ||
SECRET_KEY='dev', | ||
) | ||
|
||
|
||
class InputForm(FlaskForm): | ||
input_image = FileField("Prześlij plik", | ||
validators=[FileRequired(), FileAllowed(['jpg', 'png'])]) | ||
input_map = FileField("Prześlij mapę ekspercką (opcjonalne)", | ||
validators=[FileAllowed(['jpg', 'png'])]) | ||
submit = SubmitField() | ||
|
||
|
||
def predict_classic(image_path: str, user_path: str, true_path: str = '') -> Tuple[str, ConfusionMatrix]: | ||
cc = ClassicClassifier([(5, 5), (12, 12), (20, 20)], 200) | ||
image = cv2.imread(image_path) | ||
pred_image = cc.predict(image) | ||
|
||
back = '\\' | ||
pred_path = f"{image_path.split(back)[-1].split('.')[0]}_pred_classic.png" | ||
pred_path = os.path.join(user_path, pred_path) | ||
cv2.imwrite(pred_path, pred_image) | ||
|
||
if true_path: | ||
true_image = cv2.imread(true_path, cv2.IMREAD_GRAYSCALE) | ||
cm = ConfusionMatrix(true_image.flatten(), pred_image.flatten()) | ||
return pred_path, cm | ||
else: | ||
return pred_path, None | ||
|
||
|
||
def predict_unet(image_path: str, user_path: str, true_path: str = '') -> Tuple[str, ConfusionMatrix]: | ||
|
||
def mask_parse(mask): | ||
mask = np.expand_dims(mask, axis=-1) # (512, 512, 1) | ||
mask = np.concatenate([mask, mask, mask], axis=-1) # (512, 512, 3) | ||
return mask | ||
|
||
uc = UNetClassifier('models/unet_checkpoint.pth') | ||
image = cv2.imread(image_path, cv2.IMREAD_COLOR) | ||
y_pred = uc.predict(image) | ||
|
||
pred_image = mask_parse(y_pred) | ||
back = '\\' | ||
pred_path = f"{image_path.split(back)[-1].split('.')[0]}_pred_unet.png" | ||
pred_path = os.path.join(user_path, pred_path) | ||
cv2.imwrite(pred_path, pred_image * 255) | ||
|
||
if true_path: | ||
true_image = cv2.imread(true_path, cv2.IMREAD_GRAYSCALE) | ||
answer = cv2.resize(true_image, dsize=(512, 512), interpolation=cv2.INTER_CUBIC) | ||
ans = answer / 255.0 | ||
ans = ans[np.newaxis, ...] | ||
y_true = ans > 0.5 | ||
y_true = y_true.astype(np.uint8) | ||
y_true = y_true.reshape(-1) | ||
|
||
cm = ConfusionMatrix(y_true, y_pred) | ||
|
||
return pred_path, cm | ||
else: | ||
return pred_path, None | ||
|
||
|
||
@app.route("/", methods=['GET', 'POST']) | ||
def home(): | ||
form = InputForm() | ||
if form.validate_on_submit(): | ||
# ------------------------------------------------------------------------------------------------------------ # | ||
# HANDLE UPLOADED FILES | ||
|
||
# get user's unique id | ||
uid = str(uuid.uuid4()) | ||
user_path = os.path.join('static/temp', uid) | ||
os.makedirs(user_path) | ||
|
||
# get files from form | ||
f = form.input_image.data | ||
f_filename = secure_filename(f.filename) | ||
image_path = os.path.join(user_path, f_filename) | ||
f.save(image_path) | ||
|
||
f_m = form.input_map.data | ||
if f_m is not None: | ||
f_m_filename = secure_filename(f_m.filename) | ||
map_path = os.path.join(user_path, f_m_filename) | ||
f_m.save(map_path) | ||
|
||
classic_path, cm_classic = predict_classic(image_path, user_path, map_path) | ||
unet_path, cm_unet = predict_unet(image_path, user_path, map_path) | ||
else: | ||
classic_path, cm_classic = predict_classic(image_path, user_path) | ||
unet_path, cm_unet = predict_unet(image_path, user_path) | ||
|
||
# ------------------------------------------------------------------------------------------------------------ # | ||
# RESIZE UPLOADED FILES TO 512x512 | ||
|
||
img = cv2.imread(image_path, cv2.IMREAD_COLOR) | ||
img = cv2.resize(img, dsize=(512, 512), interpolation=cv2.INTER_CUBIC) | ||
cv2.imwrite(image_path, img) | ||
|
||
if f_m is not None: | ||
ans = cv2.imread(map_path, cv2.IMREAD_GRAYSCALE) | ||
ans = cv2.resize(ans, dsize=(512, 512), interpolation=cv2.INTER_CUBIC) | ||
cv2.imwrite(map_path, ans) | ||
|
||
pred = cv2.imread(classic_path, cv2.IMREAD_GRAYSCALE) | ||
pred = cv2.resize(pred, dsize=(512, 512), interpolation=cv2.INTER_CUBIC) | ||
cv2.imwrite(classic_path, pred) | ||
|
||
if f_m is not None: | ||
return render_template('result.html', | ||
image_path=image_path, map_path=map_path, | ||
classic_path=classic_path, unet_path=unet_path, | ||
cm_classic=cm_classic, cm_unet=cm_unet) | ||
else: | ||
return render_template('result.html', | ||
image_path=image_path, | ||
classic_path=classic_path, unet_path=unet_path, | ||
cm_classic=cm_classic, cm_unet=cm_unet) | ||
|
||
print(form.errors) | ||
return render_template('index.html', form=form) | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run(debug=True, host='0.0.0.0', port='5000') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import torch | ||
import cv2 | ||
import numpy as np | ||
from notebooks.unet.unet import UNet | ||
|
||
|
||
def mask_parse(mask): | ||
mask = np.expand_dims(mask, axis=-1) # (512, 512, 1) | ||
mask = np.concatenate([mask, mask, mask], axis=-1) # (512, 512, 3) | ||
return mask | ||
|
||
|
||
class UNetClassifier: | ||
|
||
def __init__(self, model_path): | ||
self.model = UNet(3) | ||
self.model.load_state_dict(torch.load(model_path, map_location='cuda')) | ||
self.model.eval() | ||
|
||
def predict(self, image: np.ndarray) -> np.ndarray: | ||
image = cv2.resize(image, dsize=(512, 512), interpolation=cv2.INTER_CUBIC) | ||
img = image / 255.0 | ||
img = np.transpose(img, (2, 0, 1)) | ||
img = img[np.newaxis, ...] | ||
img = torch.from_numpy(img.astype(np.float32)) | ||
|
||
with torch.no_grad(): | ||
pred_y = self.model(img) | ||
|
||
pred_y = pred_y[0].cpu().numpy() | ||
pred_y = np.squeeze(pred_y, axis=0) | ||
pred_y = pred_y > 0.5 | ||
pred_y = np.array(pred_y, dtype=np.uint8) | ||
|
||
return pred_y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
h1 { | ||
padding: 2%; | ||
} | ||
|
||
html, body { | ||
height: 100%; | ||
} | ||
|
||
body { | ||
display: flex; | ||
flex-direction: column; | ||
align-content: center; | ||
text-align: center; | ||
font-family: 'Montserrat', sans-serif; | ||
} | ||
|
||
.content { | ||
flex: 1 0 auto; | ||
} | ||
|
||
.footer { | ||
flex-shrink: 0; | ||
} | ||
|
||
.img-box img { | ||
object-fit: contain; | ||
} | ||
|
||
|
||
/*SCROLL BAR*/ | ||
::-webkit-scrollbar { | ||
display: none; | ||
} | ||
|
||
::-webkit-scrollbar-track { | ||
display: none; | ||
} | ||
|
||
::-webkit-scrollbar-thumb { | ||
display: none; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
{% extends "layout.html" %} | ||
|
||
{% block content %} | ||
|
||
<h1 class="mt-2">WYKRYWANIE NACZYŃ KRWIONOŚNYCH DNA OKA</h1> | ||
|
||
<p>Prześlij swój obraz korzystając poniższego formularza:</p> | ||
<div class="container form"> | ||
<form method="post" class="row g-3" enctype="multipart/form-data" id="photoForm"> | ||
{{ form.hidden_tag() }} | ||
<div class="row my-3"> | ||
<div class="col-12 mx-auto"> | ||
{{ form.input_image.label(class_='form-label') }} | ||
{{ form.input_image(class_='form-control') }} | ||
</div> | ||
</div> | ||
<div class="row my-3"> | ||
<div class="col-12 mx-auto"> | ||
{{ form.input_map.label(class_='form-label') }} <br> | ||
{{ form.input_map(class_='form-control') }} | ||
</div> | ||
</div> | ||
<div class="row my-2"> | ||
<div class="col-12 mx-auto" id="submit-btn"> | ||
{{ form.submit(class_='btn btn-success') }} | ||
</div> | ||
</div> | ||
</form> | ||
<div class="col-12 d-none mt-3" id="spinner"> | ||
<div class="spinner-border text-warning" role="status"></div> | ||
<p>Twoje żądanie jest przetwarzane. Nie zamykaj okna przeglądarki.</p> | ||
</div> | ||
</div> | ||
|
||
{% endblock %} | ||
|
||
{% block js %} | ||
|
||
<script> | ||
document.getElementById("photoForm").addEventListener("submit", (event) => { | ||
document.getElementById("spinner").classList.remove("d-none"); | ||
document.getElementById("submit-btn").classList.add("d-none"); | ||
}); | ||
|
||
</script> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Blood Vessels</title> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"/> | ||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" | ||
integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous"> | ||
|
||
{#Google Fonts#} | ||
<link rel="preconnect" href="https://fonts.googleapis.com"> | ||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
<link | ||
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" | ||
rel="stylesheet"> | ||
|
||
{#custom css#} | ||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> | ||
|
||
{# Notie #} | ||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/notie/dist/notie.min.css"> | ||
|
||
{#Font awesome#} | ||
<script src="https://kit.fontawesome.com/bd9dc0e408.js" crossorigin="anonymous"></script> | ||
</head> | ||
<body> | ||
<div class="content mb-3"> | ||
<nav class="navbar navbar-expand-lg bg-body-tertiary"> | ||
<div class="container-fluid"> | ||
<a class="navbar-brand" href={{ url_for('home') }}><i class="fa-solid fa-eye"></i></a> | ||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" | ||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||
<span class="navbar-toggler-icon"></span> | ||
</button> | ||
<div class="collapse navbar-collapse" id="navbarNav"> | ||
<ul class="navbar-nav"> | ||
<li class="nav-item"> | ||
<a class="nav-link active" aria-current="page" href="{{ url_for('home') }}">Strona główna</a> | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
</nav> | ||
|
||
{% block content %} {% endblock %} | ||
</div> | ||
|
||
<footer class="footer mt-auto py-3 bg-body-tertiary"> | ||
<div class="container"> | ||
<span class="text-body-secondary">Filip Marciniak, Szymon Pasternak</span><br> | ||
<span class="text-body-secondary">2023</span> | ||
</div> | ||
</footer> | ||
|
||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" | ||
integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N" | ||
crossorigin="anonymous"></script> | ||
|
||
{# Notie #} | ||
<script src="https://unpkg.com/notie"></script> | ||
|
||
<script> | ||
{# Notie notifications #} | ||
{# flash messaging #} | ||
{% with messages = get_flashed_messages(with_categories=true) %} | ||
{% if messages %} | ||
{% for category, message in messages %} | ||
notie.alert({ | ||
type: '{{ category }}', // optional, default = 4, enum: [1, 2, 3, 4, 5, 'success', 'warning', 'error', 'info', 'neutral'] | ||
text: '{{ message }}', | ||
stay: false, // optional, default = false | ||
time: 4, // optional, default = 3, minimum = 1, | ||
position: 'bottom' // optional, default = 'top', enum: ['top', 'bottom'] | ||
}) | ||
{% endfor %} | ||
{% endif %} | ||
{% endwith %} | ||
|
||
</script> | ||
|
||
{% block js %} {% endblock %} | ||
</body> | ||
</html> |
Oops, something went wrong.