From 97ab6a1ed1f2d8a1f8ac7ebfb7c9e5f2fac4d359 Mon Sep 17 00:00:00 2001 From: turtlebasket Date: Fri, 28 Oct 2022 20:11:50 -0700 Subject: [PATCH] size projections in web interface Co-authored-by: Alex --- Pipfile | 1 + Pipfile.lock | 120 +++++++++++++++++++++++++- README.md | 11 ++- flask_frontend.py | 18 +++- size_projection.py | 71 +++++++++++++++ static/app.css | 6 +- templates/base.html | 1 + templates/index.html | 2 +- templates/projected-area-results.html | 24 ++++++ templates/projected-area.html | 17 ++++ templates/pyrometry-results.html | 2 +- 11 files changed, 265 insertions(+), 8 deletions(-) create mode 100644 size_projection.py diff --git a/Pipfile b/Pipfile index ec2dc07..54251d6 100644 --- a/Pipfile +++ b/Pipfile @@ -14,6 +14,7 @@ matplotlib = "*" plotly = "*" pandas = "*" scipy = "==1.8.1" +scikit-image = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 0b9eb4b..7283513 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3428842daebc7c8a255790fde5231377c05479a39d5ce2977f043e58c7c80826" + "sha256": "4ce52e44137325bfa984f7d78467dce4463d471bfc344ecd116c2beb2af52d60" }, "pipfile-spec": 6, "requires": { @@ -131,6 +131,14 @@ "index": "pypi", "version": "==20.1.0" }, + "imageio": { + "hashes": [ + "sha256:9bdafe9c5a3d336a187f3f554f3e30bcdbf8a1d7d46f0e4d94e4a535adfb64c7", + "sha256:db7010cd10712518819a4187baf61b05988361ea20c23e829918727b27acb977" + ], + "markers": "python_version >= '3.7'", + "version": "==2.22.2" + }, "itsdangerous": { "hashes": [ "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", @@ -348,6 +356,14 @@ "index": "pypi", "version": "==3.6.1" }, + "networkx": { + "hashes": [ + "sha256:15cdf7f7c157637107ea690cabbc488018f8256fa28242aed0fb24c93c03a06d", + "sha256:815383fd52ece0a7024b5fd8408cc13a389ea350cd912178b82eed8b96f82cd3" + ], + "markers": "python_version >= '3.8'", + "version": "==2.8.7" + }, "numba": { "hashes": [ "sha256:0744cf4214ed795eb2df3ed1635d77a6ffcbd990a66a06125548b5fb8ee46323", @@ -413,7 +429,7 @@ "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef", "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7" ], - "markers": "python_version >= '3.10'", + "markers": "python_version >= '3.8'", "version": "==1.23.4" }, "opencv-python": { @@ -565,6 +581,37 @@ ], "version": "==2022.5" }, + "pywavelets": { + "hashes": [ + "sha256:030670a213ee8fefa56f6387b0c8e7d970c7f7ad6850dc048bd7c89364771b9b", + "sha256:058b46434eac4c04dd89aeef6fa39e4b6496a951d78c500b6641fd5b2cc2f9f4", + "sha256:231b0e0b1cdc1112f4af3c24eea7bf181c418d37922a67670e9bf6cfa2d544d4", + "sha256:23bafd60350b2b868076d976bdd92f950b3944f119b4754b1d7ff22b7acbf6c6", + "sha256:3f19327f2129fb7977bc59b966b4974dfd72879c093e44a7287500a7032695de", + "sha256:47cac4fa25bed76a45bc781a293c26ac63e8eaae9eb8f9be961758d22b58649c", + "sha256:578af438a02a86b70f1975b546f68aaaf38f28fb082a61ceb799816049ed18aa", + "sha256:6437af3ddf083118c26d8f97ab43b0724b956c9f958e9ea788659f6a2834ba93", + "sha256:64c6bac6204327321db30b775060fbe8e8642316e6bff17f06b9f34936f88875", + "sha256:67a0d28a08909f21400cb09ff62ba94c064882ffd9e3a6b27880a111211d59bd", + "sha256:71ab30f51ee4470741bb55fc6b197b4a2b612232e30f6ac069106f0156342356", + "sha256:7231461d7a8eb3bdc7aa2d97d9f67ea5a9f8902522818e7e2ead9c2b3408eeb1", + "sha256:754fa5085768227c4f4a26c1e0c78bc509a266d9ebd0eb69a278be7e3ece943c", + "sha256:7ab8d9db0fe549ab2ee0bea61f614e658dd2df419d5b75fba47baa761e95f8f2", + "sha256:875d4d620eee655346e3589a16a73790cf9f8917abba062234439b594e706784", + "sha256:88aa5449e109d8f5e7f0adef85f7f73b1ab086102865be64421a3a3d02d277f4", + "sha256:91d3d393cffa634f0e550d88c0e3f217c96cfb9e32781f2960876f1808d9b45b", + "sha256:9cb5ca8d11d3f98e89e65796a2125be98424d22e5ada360a0dbabff659fca0fc", + "sha256:ab7da0a17822cd2f6545626946d3b82d1a8e106afc4b50e3387719ba01c7b966", + "sha256:ad987748f60418d5f4138db89d82ba0cb49b086e0cbb8fd5c3ed4a814cfb705e", + "sha256:d0e56cd7a53aed3cceca91a04d62feb3a0aca6725b1912d29546c26f6ea90426", + "sha256:d854411eb5ee9cb4bc5d0e66e3634aeb8f594210f6a1bed96dbed57ec70f181c", + "sha256:da7b9c006171be1f9ddb12cc6e0d3d703b95f7f43cb5e2c6f5f15d3233fcf202", + "sha256:daf0aa79842b571308d7c31a9c43bc99a30b6328e6aea3f50388cd8f69ba7dbc", + "sha256:de7cd61a88a982edfec01ea755b0740e94766e00a1ceceeafef3ed4c85c605cd" + ], + "markers": "python_version >= '3.8'", + "version": "==1.4.1" + }, "pyyaml": { "hashes": [ "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", @@ -611,6 +658,67 @@ "index": "pypi", "version": "==6.0" }, + "scikit-image": { + "hashes": [ + "sha256:03779a7e1736fdf89d83c0ba67d44110496edd736a3bfce61a2b5177a1c8a099", + "sha256:0b0a199157ce8487c77de4fde0edc0b42d6d42818881c11f459262351d678b2d", + "sha256:19a21a101a20c587a3b611a2cf6f86c35aae9f8d9563279b987e83ee1c9a9790", + "sha256:24b5367de1762da6ee126dd8f30cc4e7efda474e0d7d70685433f0e3aa2ec450", + "sha256:2a02d1bd0e2b53e36b952bd5fd6118d9ccc3ee51de35705d63d8eb1f2e86adef", + "sha256:2f50b923f8099c1045fcde7418d86b206c87e333e43da980f41d8577b9605245", + "sha256:32fb88cc36203b99c9672fb972c9ef98635deaa5fc889fe969f3e11c44f22919", + "sha256:33dfd463ee6cc509defa279b963829f2230c9e0639ccd3931045be055878eea6", + "sha256:3a01372ae4bca223873304b0bff79b9d92446ac6d6177f73d89b45561e2d09d8", + "sha256:651de1c2ce1fbee834753b46b8e7d81cb12a5594898babba63ac82b30ddad49d", + "sha256:6b6a8f98f2ac9bb73706461fd1dec875f6a5141759ed526850a5a49e90003d19", + "sha256:7f9f8a1387afc6c70f2bed007c3854a2d7489f9f7713c242f16f32ee05934bc2", + "sha256:84baa3179f3ae983c3a5d81c1e404bc92dcf7daeb41bfe9369badcda3fb22b92", + "sha256:8d8917fcf85b987b1f287f823f3a1a7dac38b70aaca759bc0200f3bc292d5ced", + "sha256:9439e5294de3f18d6e82ec8eee2c46590231cf9c690da80545e83a0733b7a69e", + "sha256:9fb0923a3bfa99457c5e17888f27b3b8a83a3600b4fef317992e7b7234764732", + "sha256:a7c3985c68bfe05f7571167ee021d14f5b8d1a4a250c91f0b13be7fb07e6af34", + "sha256:a8714348ddd671f819457a797c97d4c672166f093def66d66c3254cbd1d43f83", + "sha256:ad5d8000207a264d1a55681a9276e6a739d3f05cf4429004ad00d61d1892235f", + "sha256:cc24177de3fdceca5d04807ad9c87d665f0bf01032ed94a9055cd1ed2b3f33e9", + "sha256:ce3d2207f253b8eb2c824e30d145a9f07a34a14212d57f3beca9f7e03c383cbe", + "sha256:cfbb073f23deb48e0e60c47f8741d8089121d89cc78629ea8c5b51096efc5be7", + "sha256:e207c6ce5ce121d7d9b9d2b61b9adca57d1abed112c902d8ffbfdc20fb42c12b", + "sha256:fd9dd3994bb6f9f7a35f228323f3c4dc44b3cf2ff15fd72d895216e9333550c6", + "sha256:fdf48d9b1f13af69e4e2c78e05067e322e9c8c97463c315cd0ecb47a94e259fc", + "sha256:ff3b1025356508d41f4fe48528e509d95f9e4015e90cf158cd58c56dc63e0ac5" + ], + "index": "pypi", + "version": "==0.19.3" + }, + "scipy": { + "hashes": [ + "sha256:02b567e722d62bddd4ac253dafb01ce7ed8742cf8031aea030a41414b86c1125", + "sha256:1166514aa3bbf04cb5941027c6e294a000bba0cf00f5cdac6c77f2dad479b434", + "sha256:1da52b45ce1a24a4a22db6c157c38b39885a990a566748fc904ec9f03ed8c6ba", + "sha256:23b22fbeef3807966ea42d8163322366dd89da9bebdc075da7034cee3a1441ca", + "sha256:28d2cab0c6ac5aa131cc5071a3a1d8e1366dad82288d9ec2ca44df78fb50e649", + "sha256:2ef0fbc8bcf102c1998c1f16f15befe7cffba90895d6e84861cd6c6a33fb54f6", + "sha256:3b69b90c9419884efeffaac2c38376d6ef566e6e730a231e15722b0ab58f0328", + "sha256:4b93ec6f4c3c4d041b26b5f179a6aab8f5045423117ae7a45ba9710301d7e462", + "sha256:4e53a55f6a4f22de01ffe1d2f016e30adedb67a699a310cdcac312806807ca81", + "sha256:6311e3ae9cc75f77c33076cb2794fb0606f14c8f1b1c9ff8ce6005ba2c283621", + "sha256:65b77f20202599c51eb2771d11a6b899b97989159b7975e9b5259594f1d35ef4", + "sha256:6cc6b33139eb63f30725d5f7fa175763dc2df6a8f38ddf8df971f7c345b652dc", + "sha256:70de2f11bf64ca9921fda018864c78af7147025e467ce9f4a11bc877266900a6", + "sha256:70ebc84134cf0c504ce6a5f12d6db92cb2a8a53a49437a6bb4edca0bc101f11c", + "sha256:83606129247e7610b58d0e1e93d2c5133959e9cf93555d3c27e536892f1ba1f2", + "sha256:93d07494a8900d55492401917a119948ed330b8c3f1d700e0b904a578f10ead4", + "sha256:9c4e3ae8a716c8b3151e16c05edb1daf4cb4d866caa385e861556aff41300c14", + "sha256:9dd4012ac599a1e7eb63c114d1eee1bcfc6dc75a29b589ff0ad0bb3d9412034f", + "sha256:9e3fb1b0e896f14a85aa9a28d5f755daaeeb54c897b746df7a55ccb02b340f33", + "sha256:a0aa8220b89b2e3748a2836fbfa116194378910f1a6e78e4675a095bcd2c762d", + "sha256:d3b3c8924252caaffc54d4a99f1360aeec001e61267595561089f8b5900821bb", + "sha256:e013aed00ed776d790be4cb32826adb72799c61e318676172495383ba4570aa4", + "sha256:f3e7a8867f307e3359cc0ed2c63b61a1e33a19080f92fe377bc7d49f646f2ec1" + ], + "index": "pypi", + "version": "==1.8.1" + }, "setuptools": { "hashes": [ "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17", @@ -635,6 +743,14 @@ "markers": "python_version >= '3.6'", "version": "==8.1.0" }, + "tifffile": { + "hashes": [ + "sha256:50b61ba943b866d191295bc38a00191c9fdab23ece063544c7f1a264e3f6aa8e", + "sha256:87f3aee8a0d06b74655269a105de75c1958a24653e1930d523eb516100043503" + ], + "markers": "python_version >= '3.8'", + "version": "==2022.10.10" + }, "werkzeug": { "hashes": [ "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", diff --git a/README.md b/README.md index c323b8d..cdc9431 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Pyrometry image processing +# Fire Lab Work ## Interface Screenshots @@ -8,11 +8,20 @@ ## Using the web version +### Ratio Pyrometry + 1. Go to [pyro.turtlebasket.ml](https://pyro.turtlebasket.ml). 2. Select an input image. 3. Enter your DSLR camera settings. 4. Click "Generate Heatmap". +### Projected Object Area + +1. Go to [pyro.turtlebasket.ml](https://pyro.turtlebasket.ml). +2. Navigate to "Object Area". +3. Select an input image. +4. Click "Generate Projected Sizes". + ## Using the local (batch) version Create a new config file: diff --git a/flask_frontend.py b/flask_frontend.py index f119077..5a8e8bb 100644 --- a/flask_frontend.py +++ b/flask_frontend.py @@ -1,6 +1,7 @@ from flask import Flask, render_template, request import numpy as np from ratio_pyrometry import ratio_pyrometry_pipeline +from size_projection import get_projected_area import base64 import cv2 as cv import plotly.figure_factory as ff @@ -71,6 +72,19 @@ def projected_area(): return render_template('projected-area.html') -@app.route('/projected_area_results') +@app.route('/projected_area_results', methods=['POST']) def projected_area_results(): - return render_template('projected-area-results.html') + f = request.files['file'] + f_bytes = np.fromstring(f.read(), np.uint8) + + img, dtable = get_projected_area( + f_bytes, + int(request.form['area_threshold']), + int(request.form['min_display_threshold']), + ) + + return render_template( + 'projected-area-results.html', + img_b64=img, + dtable=dtable + ) diff --git a/size_projection.py b/size_projection.py new file mode 100644 index 0000000..0e693fe --- /dev/null +++ b/size_projection.py @@ -0,0 +1,71 @@ +import base64 +import cv2 as cv +import numpy as np +import matplotlib.pyplot as plt +from skimage import measure, morphology, color, segmentation +import io + +def get_projected_area(image, area_threshold, display_threshold): + total_px = image.size + total_mm = 60322.46 + + output = [] + original = cv.imdecode(image, cv.IMREAD_UNCHANGED) + original = cv.cvtColor(original, cv.COLOR_BGR2RGB) + + img = cv.cvtColor(original, cv.COLOR_BGR2GRAY) + _retval, thresh_gray = cv.threshold(img, 200, 255, cv.THRESH_BINARY) + + img = morphology.area_closing(thresh_gray, area_threshold=area_threshold, connectivity=1) + + contours = measure.find_contours(array=img, level=100) + + fig, ax = plt.subplots() + ax.imshow(original, cmap=plt.cm.gray, alpha=0.3) + + index = 1 + + for contour in contours: + area = calculate_area(contour) + + if calculate_area(contour) > display_threshold: + ax.plot(contour[:, 1], contour[:, 0], linewidth=0.5, color='orangered') + + cX, cY = center_of_mass(contour) + plt.text(cY, cX, index, color='black', fontsize=6) + + output.append((index, round(area / total_px * total_mm, 2))) + + # print(area, total_px) + + + index += 1 + + ax.axis('image') + ax.set_xticks([]) + ax.set_yticks([]) + + ax.margins(0) + + my_stringIObytes = io.BytesIO() + plt.savefig(my_stringIObytes, format='png', dpi=500, bbox_inches='tight') + my_stringIObytes.seek(0) + image_arr = base64.b64encode(my_stringIObytes.read()).decode(encoding='utf-8') + + return image_arr, output + +def calculate_area(countour): + c = np.expand_dims(countour.astype(np.float32), 1) + c = cv.UMat(c) + + return cv.contourArea(c) + +def center_of_mass(X): + x = X[:,0] + y = X[:,1] + g = (x[:-1]*y[1:] - x[1:]*y[:-1]) + A = 0.5*g.sum() + cx = ((x[:-1] + x[1:])*g).sum() + cy = ((y[:-1] + y[1:])*g).sum() + + return 1./(6*A)*np.array([cx,cy]) diff --git a/static/app.css b/static/app.css index 0944be4..a27de5a 100644 --- a/static/app.css +++ b/static/app.css @@ -13,7 +13,7 @@ html { align-items: center; padding: 0px 2rem; background-color: #e0e0e0; - border-radius: 1rem; + border-radius: 0.8rem; } .navbar-links { @@ -45,6 +45,10 @@ html { width: 32rem; } +.image-out-pa { + width: 60rem; +} + .legend { border-spacing: 0px; border-collapse: collapse; diff --git a/templates/base.html b/templates/base.html index c7e685c..ffa751e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -14,4 +14,5 @@
{% block content required %} {% endblock %} +


diff --git a/templates/index.html b/templates/index.html index 88d3eec..b249c3b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %}
-

Simple Ratio Pyrometry Interface

+

Ratio Pyrometry Interface


diff --git a/templates/projected-area-results.html b/templates/projected-area-results.html index 3b97d52..8a3fcbe 100644 --- a/templates/projected-area-results.html +++ b/templates/projected-area-results.html @@ -1,6 +1,30 @@ {% extends "base.html" %} {% block content %} +

Results

+Process another image +
+ original image + +
+ + + + + + + {% for item in dtable %} + + + + + + {% endfor %} +
IndexAreaCopy
{{ item.0 }}{{ item.1 }} mm² + +
+
+
{% endblock %} \ No newline at end of file diff --git a/templates/projected-area.html b/templates/projected-area.html index 3b97d52..e044a34 100644 --- a/templates/projected-area.html +++ b/templates/projected-area.html @@ -1,6 +1,23 @@ {% extends "base.html" %} {% block content %} + +

Projected Area Interface

+ + +

Settings

+ + + +
+ + + +
+ +
+ +
{% endblock %} \ No newline at end of file diff --git a/templates/pyrometry-results.html b/templates/pyrometry-results.html index b84ac1f..e5032f1 100644 --- a/templates/pyrometry-results.html +++ b/templates/pyrometry-results.html @@ -37,6 +37,6 @@ {# Temperature Frequency Plot #} Temperature Distribution -{{freq_plot}} +{{ freq_plot | safe }} {% endblock %}