almost-working prototype

master
michael 2022-10-11 15:46:53 -05:00
parent 63eb8bdb85
commit bd3569bace
7 changed files with 183 additions and 74 deletions

View File

@ -1,5 +1,19 @@
# Pyrometry image processing # Pyrometry image processing
## Developing the web frontend
To serve in production:
```
gunicorn flask_frontend:app
```
To autoreload on source file changes:
```
gunicorn flask_frontend:app --reload
```
## Temperature maps ## Temperature maps
**Grayscale pyrometry:** currently basic; uses grayscale opencv import, then just applies a jet filter. Doesn't yet copy the full impl in the paper. **Grayscale pyrometry:** currently basic; uses grayscale opencv import, then just applies a jet filter. Doesn't yet copy the full impl in the paper.
@ -8,14 +22,14 @@
**Test image:** **Test image:**
![](01-0001.png) ![](examples/01-0001-cropped.png)
**Grayscale pyrometry result:** **Ratio pyrometry result (with convolutional smoothing):**
![](01-0001-transformed-grayscale.png)
**Ratio pyrometry result (with 2x2 convolutional smoothing):**
According to general researcher consensus, ratio pyrometry is supposed to be more accurate. According to general researcher consensus, ratio pyrometry is supposed to be more accurate.
![](01-0001-cropped-transformed-ratio.png) ![](examples/01-0001-cropped-transformed-ratio.png)
**Grayscale pyrometry result:**
![](examples/01-0001-transformed-grayscale.png)

View File

@ -1,16 +0,0 @@
<table class="legend">
<tr>
<th>Color</th>
<th>Temperature</th>
</tr>
<tr>
<td class="legend-cell"><div style="width:20px;height:20px;background-color:rgb(256, 176, 0);"></div></td>
<td class="legend-cell">0°C</td>
</tr>
</table>
<style>
.legend-cell {
padding: 4px 14px;
}
</style>

View File

@ -1,16 +1,34 @@
from flask import Flask, render_template, request from flask import Flask, render_template, request
import numpy as np
from ratio_pyrometry import ratio_pyrometry_pipeline
import base64
app = Flask(__name__) app = Flask(__name__)
@app.route('/', methods=['GET']) @app.route('/', methods=['GET'])
def index(): def index():
return render_template('index.html') return render_template('index.jinja2')
@app.route('/ratio_pyro', methods=['POST']) @app.route('/ratio_pyro', methods=['POST'])
def ratio_pyro(): def ratio_pyro():
f = request.files['file'] f = request.files['file']
I_Darkcurrent = 150.5 f_bytes = np.fromstring(f.read(), np.uint8)
exposure_time = 0.500 img_orig, img_res, key = ratio_pyrometry_pipeline(
f_stop = 2.4 f_bytes,
ISO = 64 ISO=float(request.form['iso']),
return 200 I_Darkcurrent=float(request.form['i_darkcurrent']),
exposure_time=float(request.form['exposure_time']),
f_stop=float(request.form['f_stop']),
MAX_TEMP=float(request.form['max_temp']),
MIN_TEMP=float(request.form['min_temp'])
)
img_orig_bytes = base64.urlsafe_b64encode(img_orig)
img_res_bytes = base64.urlsafe_b64encode(img_res)
return render_template(
'results.jinja2',
img_orig_bytes=img_orig_bytes,
img_res_bytes=img_res_bytes,
legend=key
)

View File

@ -4,25 +4,11 @@ import numpy as np
from numba import jit from numba import jit
import json import json
# camera settings
file = '01-0001.png'
I_Darkcurrent = 150.5
exposure_time = 0.500
f_stop = 2.4
ISO = 64 # basically brightness
# pyrometry config
MAX_TEMP = 1200
MIN_TEMP = 60
# original range from paper
# MAX_GR_RATIO = 1200
# MIN_GR_RATIO = 600
# Cropping config # Cropping config
x1 = 420 # x1 = 420
x2 = 1200 # x2 = 1200
y1 = 400 # y1 = 400
y2 = -1 # y2 = -1
# post-processing # post-processing
smoothing_radius = 2 smoothing_radius = 2
@ -32,7 +18,15 @@ key_entries = 6
@jit(nopython=True) @jit(nopython=True)
def rg_ratio_normalize(imgarr): def rg_ratio_normalize(
imgarr,
I_Darkcurrent,
f_stop,
exposure_time,
ISO,
MIN_TEMP,
MAX_TEMP
):
# set max & min to most extreme values, # set max & min to most extreme values,
# work up & down respectively from there # work up & down respectively from there
tmin = MAX_TEMP tmin = MAX_TEMP
@ -42,8 +36,10 @@ def rg_ratio_normalize(imgarr):
for i in range(len(imgarr)): for i in range(len(imgarr)):
for j in range(len(imgarr[i])): for j in range(len(imgarr[i])):
px = imgarr[i][j] px = imgarr[i][j]
r_norm = normalization_func(px[0])
g_norm = normalization_func(px[1]) # normalize R & G pixels
r_norm = (px[0] - I_Darkcurrent) * (f_stop ** 2) / (ISO * exposure_time)
g_norm = (px[1] - I_Darkcurrent) * (f_stop ** 2) / (ISO * exposure_time)
# apply camera calibration func # apply camera calibration func
temp_C = pyrometry_calibration_formula(g_norm, r_norm) temp_C = pyrometry_calibration_formula(g_norm, r_norm)
@ -62,14 +58,6 @@ def rg_ratio_normalize(imgarr):
return imgnew, tmin, tmax return imgnew, tmin, tmax
@jit(nopython=True)
def normalization_func(i):
"""
does something to the pixels that i don't understand lol
"""
return (i - I_Darkcurrent) * (f_stop ** 2) / (ISO * exposure_time)
@jit(nopython=True) @jit(nopython=True)
def pyrometry_calibration_formula(i_ng, i_nr): def pyrometry_calibration_formula(i_ng, i_nr):
""" """
@ -84,18 +72,31 @@ def pyrometry_calibration_formula(i_ng, i_nr):
(i_ng / i_nr) ** 3 (i_ng / i_nr) ** 3
) + 3753.5 ) + 3753.5
def ratio_pyrometry_pipeline(file): def ratio_pyrometry_pipeline(
file_bytes,
# camera settings
I_Darkcurrent: float,
exposure_time: float,
f_stop: float,
ISO: float,
# pyrometry config
MAX_TEMP: float,
MIN_TEMP: float
):
# read image & crop # read image & crop
file_name = file.split(".")[0] img_orig = cv.imdecode(file_bytes, cv.IMREAD_UNCHANGED)
file_ext = file.split(".")[1] # img = img[y1:y2, x1:x2]
img = cv.imread(file)
img = img[y1:y2, x1:x2]
cv.imwrite(f'{file_name}-cropped.{file_ext}', img)
# img = cv.imread('ember_test.png') img, tmin, tmax = rg_ratio_normalize(
img_orig,
img, tmin, tmax = rg_ratio_normalize(img) I_Darkcurrent,
f_stop,
exposure_time,
ISO,
MIN_TEMP,
MAX_TEMP
)
# build & apply smoothing conv kernel # build & apply smoothing conv kernel
k = [] k = []
@ -107,7 +108,7 @@ def ratio_pyrometry_pipeline(file):
# write colormapped image # write colormapped image
img_jet = cv.applyColorMap(img, cv.COLORMAP_JET) img_jet = cv.applyColorMap(img, cv.COLORMAP_JET)
cv.imwrite(f'{file_name}-cropped-transformed-ratio.{file_ext}', img_jet) # cv.imwrite(f'{file_name}-cropped-transformed-ratio.{file_ext}', img_jet)
# --- Generate temperature key --- # --- Generate temperature key ---
@ -131,3 +132,6 @@ def ratio_pyrometry_pipeline(file):
for i in range(len(temps)): for i in range(len(temps)):
c = key_img_jet[0][i] c = key_img_jet[0][i]
tempkey[temps[i]] = f"rgb({c[0]}, {c[1]}, {c[2]})" tempkey[temps[i]] = f"rgb({c[0]}, {c[1]}, {c[2]})"
# original, transformed, legend
return img_orig, img_jet, tempkey

View File

@ -4,5 +4,6 @@
<link rel="app.css"> <link rel="app.css">
</head> </head>
<body> <body>
{% block content %} {% block content required %}
{% endblock %}
</body> </body>

View File

@ -1,7 +1,38 @@
{% extends "base.html" %} {% extends "base.jinja2" %}
{% block content %} {% block content %}
<div class="form"> <form action="/ratio_pyro" method="POST" enctype="multipart/form-data">
<button onclick=""> <h2>Simple Ratio Pyrometry Interface</h2>
</button> <input type="file" name="file" accept=".png,.jpg,.jpeg" value="Choose Image"/>
</div>
<h4>Camera Settings</h4>
<label for="iso">ISO</label>
<input type="number" name="iso" value="2.4"/>
<br>
<label for="i_darkcurrent">I_Darkcurrent</label>
<input type="number" name="i_darkcurrent" value="150.5"/>
<br>
<label for="exposure_time">Exposure Time <i>t</i></label>
<input type="number" name="exposure_time" value="0.5"/>
<br>
<label for="f_stop">F-stop <i>f</i></label>
<input type="number" name="f_stop" value="2.4"/>
<br>
<h4>Temperature Settings</h4>
<label for="min_temp">Min Temp (°C)</label>
<input type="number" name="min_temp" value="60"/>
<br>
<label for="max_temp">Max Temp (°C)</label>
<input type="number" name="max_temp" value="1200"/>
<br>
<br>
<input type="submit" value="Generate Heatmap"/>
</form>
{% endblock %} {% endblock %}

57
templates/results.jinja2 Normal file
View File

@ -0,0 +1,57 @@
{% extends "base.jinja2" %}
{% block content %}
<table class="img-table">
<tr>
<th class="img-table-heading">Input Image</th>
<th class="img-table-heading">Resultant Heatmap</th>
</tr>
<tr>
{# Original image #}
<td>
<img data="{{ img_orig_bytes }}" alt="original image">
</td>
{# Result image #}
<td>
<img data="{{ img_res_bytes }}" alt="result image">
</td>
</tr>
</table>
<h1>{{ img_orig_bytes }}</h1>
<br><br><br>
<table class="legend">
<tr>
<th>Color</th>
<th>Temperature</th>
</tr>
{% for temp, color in legend.items() %}
<tr>
<td class="legend-cell"><div style="width:20px;height:20px;background-color:{{ color }};"></div></td>
<td class="legend-cell">{{ temp }}°C</td>
</tr>
{% endfor %}
</table>
<style>
.img-table {
width: 80%;
}
.img-table-heading {
padding: 4px 10px;
}
.img-table-cell {
padding: 20px 10px;
}
.legend-cell {
padding: 4px 14px;
}
</style>
{% endblock %}