Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented new features: first issue and second issue #5

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0a911bf
First commit: prepared the system and solved error about Torchvision
davide-scintu Nov 27, 2022
ad93561
Created new files and started testing some code
davide-scintu Dec 7, 2022
c17dfc1
Deleted home_histogram.html because it is better to instead modify th…
davide-scintu Dec 7, 2022
df5faf7
Returned back home.py, deleting mistakes.
davide-scintu Dec 7, 2022
789518d
Modified classification.py file, deleting everything useless
davide-scintu Dec 7, 2022
495accf
Test code to plot the histogram of an image
davide-scintu Dec 10, 2022
8e3a0cf
Test code to plot the histogram of an image modified using functions
davide-scintu Dec 10, 2022
6d05217
Attempts to get the complete code to work correctly
davide-scintu Dec 10, 2022
21324a9
Modified test code to save the image from plt
davide-scintu Dec 11, 2022
0515473
Tested functionality adding code to classification.py file and saving…
davide-scintu Dec 11, 2022
fc6e1b5
Almost finished just need to link the correct "result" id
davide-scintu Dec 11, 2022
0eb214f
Finished first issue task. Tested, it works correctly ( just need to …
davide-scintu Dec 12, 2022
60eb858
Front end upgrade
davide-scintu Dec 12, 2022
68ef0ac
Back end upgrade
davide-scintu Dec 12, 2022
1a5da56
Modified code to also use histogram rgb function
davide-scintu Dec 13, 2022
9edb7d6
Modified comment
davide-scintu Dec 13, 2022
2cf0d6b
Set the code for the new handler
davide-scintu Dec 13, 2022
714564c
Set the code for the new functionality and tested templates output to…
davide-scintu Dec 14, 2022
a7d5d4e
Modified transformation form
davide-scintu Dec 14, 2022
b4c63a7
Tested transformation using PIL library
davide-scintu Dec 14, 2022
ead6fe7
Test progress for the new handler
davide-scintu Dec 17, 2022
ab09b89
Test the new handler with default random parameters and adjusted the …
davide-scintu Dec 19, 2022
8788661
Deleted mistakes in config.py
davide-scintu Dec 19, 2022
5ad144a
Modified form for the transformation using Float Field and NumberInpu…
davide-scintu Dec 19, 2022
096744d
Modified select html for the transformation, according to the form shape
davide-scintu Dec 19, 2022
e9a119c
Modified transformations.py file to complete the new API functionality
davide-scintu Dec 19, 2022
a0d98bc
Modified transformations.py comment to the function transformations()
davide-scintu Jan 18, 2023
63fc87c
Modified the code in order to save every single image by unique ID us…
davide-scintu Jan 27, 2023
dd3aa81
Merge pull request #5 from davide-scintu/first_issue
davide-scintu Jan 27, 2023
b1ba73e
Merge branch 'master' into second_issue
davide-scintu Jan 27, 2023
d2db52b
Merge pull request #6 from davide-scintu/second_issue
davide-scintu Jan 27, 2023
1cfbda2
Removed not necessary comments in test_histogram.py
davide-scintu Jan 30, 2023
35995a7
Removed not necessary comments in trans_test.py
davide-scintu Jan 30, 2023
c36ec17
Merge remote-tracking branch 'origin/second_issue' into second_issue
davide-scintu Jan 30, 2023
86d2ce4
Correct push -> forgot to import uuid
davide-scintu Jan 30, 2023
b881634
Merge pull request #7 from davide-scintu/first_issue
davide-scintu Jan 30, 2023
4038946
Merge pull request #8 from davide-scintu/second_issue
davide-scintu Jan 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/forms/classification_form_histogram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from flask_wtf import FlaskForm
from wtforms import SubmitField, SelectField
from wtforms.validators import DataRequired

from app.utils.list_images import list_images
from config import Configuration

conf = Configuration()


class ClassificationFormHistogram(FlaskForm):
image = SelectField('image', choices=list_images(), validators=[DataRequired()])
submit = SubmitField('Submit')
20 changes: 20 additions & 0 deletions app/forms/transformation_form.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from flask_wtf import FlaskForm
from wtforms import SubmitField, SelectField, FloatField
from wtforms.validators import DataRequired
from wtforms.widgets import NumberInput

from app.utils.list_images import list_images
from config import Configuration

conf = Configuration()


class TransformationForm(FlaskForm):

color = FloatField(widget=NumberInput(step=0.1, min=-10.0, max=10.0), default=1.0, validators=[DataRequired()])
brightness = FloatField(widget=NumberInput(step=0.1, min=-10.0, max=10.0), default=1.0, validators=[DataRequired()])
contrast = FloatField(widget=NumberInput(step=0.1, min=-10.0, max=10.0), default=1.0, validators=[DataRequired()])
sharpness = FloatField(widget=NumberInput(step=0.1, min=-10.0, max=10.0), default=1.0, validators=[DataRequired()])

image = SelectField('image', choices=list_images(), validators=[DataRequired()])
submit = SubmitField('Submit')
1 change: 1 addition & 0 deletions app/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .classifications import *
from .transformations import *
from .classifications_id import *
from .info import *
from .home import *
31 changes: 29 additions & 2 deletions app/routes/classifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from app import app
from app.forms.classification_form import ClassificationForm
from ml.classification_utils import classify_image
from app.forms.classification_form_histogram import ClassificationFormHistogram
from ml.classification_utils import classify_image, plot_histogram, clear_dir, plot_histogram_rgb
from config import Configuration

config = Configuration()
Expand All @@ -20,7 +21,6 @@ def classifications():
if form.validate_on_submit(): # POST
image_id = form.image.data
model_id = form.model.data

redis_url = Configuration.REDIS_URL
redis_conn = redis.from_url(redis_url)
with Connection(redis_conn):
Expand All @@ -38,3 +38,30 @@ def classifications():
# otherwise, it is a get request and should return the
# image and model selector
return render_template('classification_select.html', form=form)


@app.route('/classifications_histogram', methods=['GET', 'POST'])
def classifications_histogram():
"""API for selecting an image and return the histogram
of the image"""
form = ClassificationFormHistogram()
if form.validate_on_submit(): # POST
image_id = form.image.data

img_path = f'app/static/imagenet_subset/{image_id}'
histogram_img_path = f'app/static/imagenet_histogram/hist_{image_id}'

plot_histogram(img_path, histogram_img_path)
# plot_histogram_rgb(img_path, histogram_img_path)

# histogram viewer with the corresponding image feedback
return render_template('histogram_output.html', image_id=image_id)
# image selector
return render_template('histogram_template.html', form=form)







2 changes: 2 additions & 0 deletions app/routes/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
@app.route('/home', methods=['GET'])
@app.route('/index', methods=['GET'])
@app.route('/', methods=['GET'])

def home():
return render_template('home.html')

37 changes: 37 additions & 0 deletions app/routes/transformations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from flask import render_template
from app import app
from app.forms.transformation_form import TransformationForm
from ml.classification_utils import transformation_image
from config import Configuration
import uuid

config = Configuration()


@app.route('/transformations', methods=['GET', 'POST'])
def transformations():
"""API for selecting an image and running a
transformation. Returns the enhanced image saving it by unique ID."""
form = TransformationForm()
if form.validate_on_submit(): # POST
image_id = form.image.data

unique_id = str(uuid.uuid4())

img_path = f'app/static/imagenet_subset/{image_id}'

transformation_img_path = f'app/static/imagenet_transform/{unique_id}.JPEG'

color_id = form.color.data
brightness_id = form.brightness.data
contrast_id = form.contrast.data
sharpness_id = form.sharpness.data

transformation_image(img_path, transformation_img_path, color_factor=color_id, brightness_factor=brightness_id,
contrast_factor=contrast_id,
sharpness_factor=sharpness_id)
# transformation output
return render_template("transformation_output.html", image_id=image_id, unique_id=unique_id)

# image and transformation selector
return render_template('transformation_select.html', form=form)
39 changes: 39 additions & 0 deletions app/templates/histogram_output.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{% extends "base.html" %}

{% block content %}

<style>
.large-front-thumbnail {
position: relative;
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
}

</style>
<div class="row">
<div class="col">
<div class="card">
<h4>
<th scope="col">Feedback Image</th>
</h4>
<img class="large-front-thumbnail"
src="{{ url_for('static', filename='imagenet_subset/'+image_id) }} "/>
</div>
</div>
<div class="col">
<div class="card ">
<h4>
<th scope="col">Histogram</th>
</h4>
<table class="table">
<img class="large-front-thumbnail"
src="{{ url_for('static', filename='imagenet_histogram/hist_'+image_id) }} "/>
</table>
<a class="btn btn-dark mb-2" href="/classifications_histogram" role="button">Back</a>
</div>
</div>
</div>
{% endblock %}

16 changes: 16 additions & 0 deletions app/templates/histogram_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends "base.html" %}

{% block content %}

<h1>Select Image</h1>
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<h4>
Image:
</h4>
<p>
{{ form.image }}
</p>
<button type="submit" class="btn btn-dark mb-2">Submit</button>
</form>
{% endblock %}
10 changes: 7 additions & 3 deletions app/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

<p style="margin-top:1cm;">This service provides the output scores for popular deep networks
from the <a href="https://pytorch.org/docs/stable/torchvision/models.html">
Torchvision model zoo</a>. Go
<a href={{ url_for("classifications") }}>here</a> for
trying out the service.</p>
Torchvision model zoo</a>. </p>
<p> Go <a href={{ url_for("classifications") }}>here</a> for trying out the service.</p>

<p> Go <a href={{ url_for("transformations") }}>here</a> for trying out the trying out the transformation service.</p>

<p> Go <a href={{ url_for("classifications_histogram") }}>here</a> if you want to show the histogram for a specific image</p>

<p style="margin-top:1cm;">For the list of models and images
available, click
<a href={{ url_for("info") }}>here</a>.</p>
Expand Down
48 changes: 48 additions & 0 deletions app/templates/transformation_output.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{% extends "base.html" %}

{% block content %}

<style>
.large-front-thumbnail {
position: relative;
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
}

</style>
<div class="row">
<div class="col">
<div class="card">
<thead>
<h3>
<th scope="col">Original Image</th>
</h3>
</thead>
<img class="large-front-thumbnail"
src="{{ url_for('static', filename='imagenet_subset/'+image_id) }} "
alt={{ image_id }}/>
</div>
</div>
<div class="col">
<div class="card ">
<thead>
<h3>
<th scope="col">Transformation</th>
</h3>
</thead>
<table class="table">
<div class="col">
<div class="card">
<img class="large-front-thumbnail"
src="{{ url_for('static', filename='imagenet_transform/'+unique_id+'.JPEG') }} "/>
</div>
</div>
</table>
<a class="btn btn-dark mb-2" href="/transformations" role="button">Back</a>
</div>
</div>
</div>
{% endblock %}

36 changes: 36 additions & 0 deletions app/templates/transformation_select.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% extends "base.html" %}

{% block content %}

<h1>Select Transformation and Image</h1>
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<h4>
Transformation:
</h4>
<p>
Color:
{{ form.color() }}
</p>
<p>
Brightness:
{{ form.brightness() }}
</p>
<p>
Contrast:
{{ form.contrast() }}
</p>
<p>
Sharpness:
{{ form.sharpness() }}
</p>

<h4>
Image:
</h4>
<p>
{{ form.image }}
</p>
<button type="submit" class="btn btn-dark mb-2">Submit</button>
</form>
{% endblock %}
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Configuration:
# classification
image_folder_path = os.path.join(project_root, 'app/static/imagenet_subset')
models = ('resnet18', 'alexnet', 'vgg16', 'inception_v3',)

# web server
SECRET_KEY = os.environ.get('SECRET_KEY') or '9cj328s61hsd8'
# queue
Expand Down
59 changes: 58 additions & 1 deletion ml/classification_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@
import os
import time
import torch
import numpy as np
import matplotlib.pyplot as plt
import cv2
import glob
from PIL import Image

from torchvision import transforms
from PIL import Image
from PIL import ImageEnhance

from config import Configuration

Expand Down Expand Up @@ -40,7 +47,7 @@ def get_model(model_id):
if model_id in conf.models:
try:
module = importlib.import_module('torchvision.models')
return module.__getattribute__(model_id)(pretrained=True)
return module.__getattribute__(model_id)(weights='DEFAULT')
except ImportError:
logging.error("Model {} not found".format(model_id))
else:
Expand Down Expand Up @@ -84,3 +91,53 @@ def classify_image(model_id, img_id):
img.close()
time.sleep(5)
return output


def transformation_image(image, path, color_factor=1.0, brightness_factor=1.0, contrast_factor=1.0,
sharpness_factor=1.0): # default values
"""Modify an input image sequentially adding enhancements saving the results in a specific directory"""

im = Image.open(image)
col = ImageEnhance.Color(im)
im_col = col.enhance(color_factor)
brh = ImageEnhance.Brightness(im_col)
im_col_brh = brh.enhance(brightness_factor)
con = ImageEnhance.Contrast(im_col_brh)
im_cal_brh_con = con.enhance(contrast_factor)
sharp = ImageEnhance.Sharpness(im_cal_brh_con)
im_cal_brh_con_sharp = sharp.enhance(sharpness_factor)
im_cal_brh_con_sharp.save(path)

def plot_histogram(path, hist_path):
"""A function to plot and save the histogram f a specific image and
save the histogram in a specific directory"""
im = cv2.imread(path)
vals = im.mean(axis=2).flatten()
counts, bins = np.histogram(vals, range(257))
plt.clf() # to not overlap the images
plt.bar(bins[:-1] - 0.5, counts, width=1, edgecolor='none')
plt.xlim([-0.5, 255.5])
# plt.show()
saved_image = plt.savefig(hist_path)
return saved_image


def plot_histogram_rgb(path, hist_path):
im = cv2.imread(path)
color = ('b', 'g', 'r')
for i, col in enumerate(color):
histr = cv2.calcHist([im], [i], None, [256], [0, 256])
plt.plot(histr, color=col)
plt.xlim([0, 256])
# plt.show()
saved_image = plt.savefig(hist_path)
plt.clf() # to not overlap the images
return saved_image


def clear_dir():
"""A function to delete the content of a directory"""
files = glob.glob('app/static/imagenet_histogram')
for f in files:
os.remove(f)

2 changes: 1 addition & 1 deletion prepare_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def prepare_models():
try:
module = importlib.import_module('torchvision.models')
# download model
_ = module.__getattribute__(model_name)(pretrained=True)
_ = module.__getattribute__(model_name)(weights='DEFAULT')
del _ # free up memory
except ImportError:
logging.error("Model {} not found".format(model_name))
Expand Down
Loading