diff --git a/examples/pyrometry/edge_detection.py b/examples/pyrometry/edge_detection.py
index c8292b8..2d19b5a 100644
--- a/examples/pyrometry/edge_detection.py
+++ b/examples/pyrometry/edge_detection.py
@@ -28,7 +28,11 @@ gray = cv.copyMakeBorder(
value=0
)
-contours = measure.find_contours(array=gray, level=100)
+# cv.imshow('gray', gray)
+# cv.waitKey(0)
+
+# contours = measure.find_contours(array=gray, level=100)
+_img, contours = cv.findContours(gray, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)[0]
fig, ax = plt.subplots()
ax.imshow(gray, cmap=plt.cm.gray, alpha=1)
@@ -36,14 +40,37 @@ ax.imshow(gray, cmap=plt.cm.gray, alpha=1)
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])
+
+
+img_new = cv.cvtColor(gray, cv.COLOR_GRAY2BGR)
+
for contour in contours:
area = calculate_area(contour)
-
- if calculate_area(contour) > 250:
- ax.plot(contour[:, 1], contour[:, 0], linewidth=0.5, color='orangered')
+
+ # if area > 250:
+ # cnt = np.array(contour).reshape((-1, 1, 2)).astype(np.int32)
+ # cv.drawContours(img_new, [cnt], -1, (0, 200, 255), thickness=10)
+
+ cv.drawContours(img_new, [contour], -1, (0, 200, 255), thickness=3)
+
+ # ax.plot(contour[:, 1], contour[:, 0], linewidth=0.5, color='orangered')
+
+# cv.imshow('contours', img_new)
+# cv.waitKey(0)
+
+cv.imwrite("firebrand_contours_opencv.png", img_new)
ax.axis('image')
ax.set_xticks([])
diff --git a/examples/pyrometry/edge_detection_figure.png b/examples/pyrometry/edge_detection_figure.png
index 53beaf4..1b6bbb7 100644
Binary files a/examples/pyrometry/edge_detection_figure.png and b/examples/pyrometry/edge_detection_figure.png differ
diff --git a/examples/pyrometry/firebrand_contours_opencv.png b/examples/pyrometry/firebrand_contours_opencv.png
new file mode 100644
index 0000000..7042b12
Binary files /dev/null and b/examples/pyrometry/firebrand_contours_opencv.png differ
diff --git a/flask_frontend.py b/flask_frontend.py
index 5a5d7c8..a592fb4 100644
--- a/flask_frontend.py
+++ b/flask_frontend.py
@@ -1,11 +1,10 @@
from flask import Flask, render_template, request, send_file
import numpy as np
+from plotly_util import generate_plotly_temperature_pdf
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
-import pandas as pd
app = Flask(
__name__,
@@ -21,7 +20,7 @@ def index():
def ratio_pyro():
f = request.files['file']
f_bytes = np.fromstring(f.read(), np.uint8)
- img_orig, img_res, key, ptemps = ratio_pyrometry_pipeline(
+ img_orig, img_res, key, ptemps, indiv_firebrands = ratio_pyrometry_pipeline(
f_bytes,
ISO=float(request.form['iso']),
I_Darkcurrent=float(request.form['i_darkcurrent']),
@@ -31,47 +30,28 @@ def ratio_pyro():
MIN_TEMP=float(request.form['min_temp']),
smoothing_radius=int(request.form['smoothing_radius']),
key_entries=int(request.form['legend_entries']),
- eqn_scaling_factor=float(request.form['equation_scaling_factor'])
+ eqn_scaling_factor=float(request.form['equation_scaling_factor']),
+ firebrand_min_intensity_threshold=float(request.form['intensity_threshold']),
+ firebrand_min_area=float(request.form['min_area']),
)
# get base64 encoded images
img_orig_b64 = base64.b64encode(cv.imencode('.png', img_orig)[1]).decode(encoding='utf-8')
img_res_b64 = base64.b64encode(cv.imencode('.png', img_res)[1]).decode(encoding='utf-8')
- # generate prob. distribution histogram & return embed
- fig = ff.create_distplot(
- [ptemps],
- group_labels=[f.filename],
- show_rug=False,
- show_hist=False,
- )
- fig.update_layout(
- autosize=False,
- width=800,
- height=600,
- )
- fig.update_xaxes(
- title_text="Temperature (°C)",
- )
- fig.update_yaxes(
- title_text="Probability (1/°C)",
- )
- freq_plot = fig.to_html()
+ ptemps_list = [ptemps]
- # create csv-formatted stuff
- # currently only supports 1 firebrand (grabs first object in plot).
- plot_data=fig.to_dict()
- x_data = plot_data["data"][0]["x"]
- y_data = plot_data["data"][0]["y"]
+ for i in range(len(indiv_firebrands)):
+ # base64 encode image data
+ brand_data = indiv_firebrands[i]
+ unencoded = brand_data["img_data"]
+ brand_data["img_data"] = base64.b64encode(cv.imencode('.png', unencoded)[1]).decode(encoding='utf-8')
+ indiv_firebrands[i] = brand_data
+
+ # add ptemp data to list
+ ptemps_list.append(brand_data["ptemps"])
- tdata = [["Temperature", "Frequency"]]
- for i in range(len(x_data)):
- r = []
- r.append(x_data[i])
- r.append(y_data[i])
- tdata.append(r)
-
- csvstr = pd.DataFrame(tdata).to_csv(index=False, header=False)
+ freq_plot, csvstrs = generate_plotly_temperature_pdf(ptemps_list)
return render_template(
'pyrometry-results.html',
@@ -79,7 +59,8 @@ def ratio_pyro():
img_res_b64=img_res_b64,
legend=key,
freq_plot=freq_plot,
- csv_data=csvstr
+ csv_data=csvstrs[0],
+ individual_firebrands=indiv_firebrands,
)
diff --git a/plotly_util.py b/plotly_util.py
new file mode 100644
index 0000000..969fddc
--- /dev/null
+++ b/plotly_util.py
@@ -0,0 +1,64 @@
+from typing import List
+import plotly.figure_factory as ff
+import pandas as pd
+
+def generate_plotly_temperature_pdf(ptemps_list: List[list]):
+ """
+ Generate plotly graph HTML & raw CSV data for temperature pdf
+
+ ptemps: pixel temperature LIST in order of:
+
+ - Ptemps of firebrands "overview" image
+ - Ptemps list for each individual firebrand
+
+ plotname: what to call the plot
+
+ Returns result in form (plot_html, csv_data)
+ """
+
+ # generate prob. distribution histogram & return embed
+ labels = ["Full Image"]
+ for i in range(len(ptemps_list[1:])):
+ labels.append(f"Firebrand {i+1}")
+ labels.reverse()
+
+ fig = ff.create_distplot(
+ ptemps_list,
+ group_labels=labels,
+ show_rug=False,
+ show_hist=False,
+ )
+ fig.update_layout(
+ autosize=False,
+ width=800,
+ height=600,
+ )
+ fig.update_xaxes(
+ title_text="Temperature (°C)",
+ )
+ fig.update_yaxes(
+ title_text="Probability (1/°C)",
+ )
+ freq_plot = fig.to_html()
+
+ # create csv-formatted stuff
+ csvstrs = []
+ plot_data=fig.to_dict()
+ for i in range(len(plot_data["data"])):
+ x_data = plot_data["data"][i]["x"]
+ y_data = plot_data["data"][i]["y"]
+
+ tdata = [["Temperature", "Frequency"]]
+ for i in range(len(x_data)):
+ r = []
+ r.append(x_data[i])
+ r.append(y_data[i])
+ tdata.append(r)
+
+ csvstr = pd.DataFrame(tdata).to_csv(index=False, header=False)
+ csvstrs.append(csvstr)
+
+ return (
+ freq_plot,
+ csvstrs
+ )
\ No newline at end of file
diff --git a/ratio_pyrometry.py b/ratio_pyrometry.py
index 492db10..642868c 100644
--- a/ratio_pyrometry.py
+++ b/ratio_pyrometry.py
@@ -3,6 +3,7 @@ from multiprocessing.sharedctypes import Value
import cv2 as cv
import numpy as np
from numba import jit
+from skimage import measure
@jit(nopython=True)
def rg_ratio_normalize(
@@ -85,11 +86,104 @@ def ratio_pyrometry_pipeline(
smoothing_radius: int,
key_entries: int,
eqn_scaling_factor: float,
+ # firebrand detection
+ firebrand_min_intensity_threshold: float,
+ firebrand_min_area: float
):
-
# read image & crop
img_orig = cv.imdecode(file_bytes, cv.IMREAD_UNCHANGED)
+ # ---------------------------------------------------------
+ # -- Firebrand detection
+ # ---------------------------------------------------------
+
+ img = cv.copyMakeBorder(
+ img_orig,
+ 20,
+ 20,
+ 20,
+ 20,
+ cv.BORDER_CONSTANT,
+ value=0
+ )
+
+ retval, thresh_gray = cv.threshold(img, firebrand_min_intensity_threshold, 255, cv.THRESH_BINARY)
+
+ kernel = np.ones((7, 7), np.uint8)
+ image = cv.morphologyEx(thresh_gray, cv.MORPH_CLOSE, kernel, iterations=1)
+ gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
+
+ retval, gray = cv.threshold(gray, 0, 255, cv.THRESH_BINARY)
+
+ contours = measure.find_contours(array=gray, level=100)
+
+ def calculate_area(countour):
+ c = np.expand_dims(countour.astype(np.float32), 1)
+ c = cv.UMat(c)
+
+ return cv.contourArea(c)
+
+ individual_firebrands = []
+
+ for contour in contours:
+ if calculate_area(contour) > firebrand_min_area:
+ mask = np.zeros(img.shape[0:2], dtype='uint8')
+ cv.fillPoly(mask, pts=np.int32([np.flip(contour, 1)]), color=(255,255,255))
+
+ retval, mask = cv.threshold(mask, 0, 255, cv.THRESH_BINARY)
+
+ #apply the mask to the img
+ masked = cv.bitwise_and(img, img, mask=mask)
+
+ masked_ratio_rg, ptemps_indiv = rg_ratio_normalize(
+ masked,
+ I_Darkcurrent,
+ f_stop,
+ exposure_time,
+ ISO,
+ MIN_TEMP,
+ MAX_TEMP,
+ eqn_scaling_factor,
+ )
+
+ # build & apply smoothing conv kernel
+ k = []
+ for i in range(smoothing_radius):
+ k.append([1/(smoothing_radius**2) for i in range(smoothing_radius)])
+ kernel = np.array(k)
+
+ masked_ratio_rg = cv.filter2D(src=masked_ratio_rg, ddepth=-1, kernel=kernel)
+
+ # write colormapped image
+ masked_ratio_rg_jet = cv.applyColorMap(masked_ratio_rg, cv.COLORMAP_JET)
+
+ # Generate key
+ step = (MAX_TEMP - MIN_TEMP) / (key_entries-1)
+ temps = []
+ key_img_arr = [[]]
+
+ for i in range(key_entries):
+ res_temp = MIN_TEMP + (i * step)
+ res_color = scale_temp(res_temp, MIN_TEMP, MAX_TEMP)
+ temps.append(math.floor(res_temp))
+ key_img_arr[0].append([res_color, res_color, res_color])
+
+ key_img = np.array(key_img_arr).astype(np.uint8)
+ key_img_jet = cv.applyColorMap(key_img, cv.COLORMAP_JET)
+
+ tempkey = {}
+
+ for i in range(len(temps)):
+ c = key_img_jet[0][i]
+ tempkey[temps[i]] = f"rgb({c[2]}, {c[1]}, {c[0]})"
+
+ individual_firebrands.append({
+ "img_data": masked_ratio_rg_jet,
+ "legend": tempkey,
+ "ptemps": ptemps_indiv
+ })
+
+
img, ptemps = rg_ratio_normalize(
img_orig,
I_Darkcurrent,
@@ -112,7 +206,9 @@ def ratio_pyrometry_pipeline(
# write colormapped image
img_jet = cv.applyColorMap(img, cv.COLORMAP_JET)
- # --- Generate temperature key ---
+ # ---------------------------------------------------------
+ # -- Generate temperature key
+ # ---------------------------------------------------------
# Generate key
step = (MAX_TEMP - MIN_TEMP) / (key_entries-1)
@@ -133,4 +229,4 @@ def ratio_pyrometry_pipeline(
tempkey[temps[i]] = f"rgb({c[2]}, {c[1]}, {c[0]})"
# original, transformed, legend
- return img_orig, img_jet, tempkey, ptemps
+ return img_orig, img_jet, tempkey, ptemps, individual_firebrands
diff --git a/templates/index.html b/templates/index.html
index a05d6ed..6a36f70 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -50,8 +50,13 @@
Firebrand Detection Settings
-
-
+
+
+
+
+
+
+
Output Settings
diff --git a/templates/pyrometry-results.html b/templates/pyrometry-results.html
index 494dce6..0490e50 100644
--- a/templates/pyrometry-results.html
+++ b/templates/pyrometry-results.html
@@ -7,6 +7,9 @@
{% block content %}
+
+
General Results
+
Input Image |
@@ -40,6 +43,44 @@
+
+
Individual Firebrands
+
+
+
+ Output Heatmap |
+ Legend |
+
+
+ {% for item in individual_firebrands %}
+
+ {# output heatmap #}
+
+
+ |
+
+
+ {# legend #}
+
+ Firebrand {{ loop.index }}
+
+
+ Color |
+ Temperature |
+
+ {% for temp, color in item["legend"].items() %}
+
+ |
+ {{ temp }}°C |
+
+ {% endfor %}
+
+ |
+
+ {% endfor %}
+
+
+
{# Temperature Frequency Plot #}
@@ -50,6 +91,9 @@ onclick="saveData(`{{csv_data}}`, 'temperature-data.csv')">Download Data as CSV<
{{ freq_plot | safe }}
+
+
+
{% endblock %}