By Noël Jung
from functools import partial
from matplotlib import ticker
import matplotlib as mpl
import matplotlib.pyplot as plt
import graph_style
from project_utils import (
standard_load_transform,
load_image_from_path,
zoom_in,
scale_to_percent,
)
We load the data and add a few new columns.
IMAGE_DIR = r"../runV7"
CSV_FILE = r"../CSV/runV7.csv"
EX_ID = "V7"
display_only = True
ferm_data = standard_load_transform(CSV_FILE, new_columns=["h_passed", "min_passed"])
We adjust the image loader for the images and task at hand, and overwrite matplotlib's default styles.
# Define a default image loader that zooms-in on the image.
default_im_loader = partial(load_image_from_path,
dir_path=IMAGE_DIR, as_gray=True,
img_transformers=[(zoom_in, (1050, 3300, 1230, 2000))])
# Overwrite matplotlib default rcParams with custom styles.
for param, value in mpl.rcParamsDefault.items():
mpl.rcParams[param] = graph_style.style.get(param, value)
# Some example data points.
sample_rows = ferm_data.iloc[[50,100,150,200]]
# Create a figure with subplots to display images.
fig, axs = plt.subplots(2, int(len(sample_rows)/2), figsize=(20, 5),)
fig.subplots_adjust(left=0, right=1, wspace=-0.60, hspace=0.6)
fig.suptitle("Images taken at different times in grayscale", **graph_style.super_title_style, y=1.1)
# Create subplots.
for ax, (index, row) in zip(axs.flatten(), sample_rows.iterrows()):
im = default_im_loader(row["im"])
ax.set_title(label=f"{row['h_passed']} h fermentation", **graph_style.title_style)
ax.set(xticks=[], yticks=[])
ax.imshow(im, cmap='gray')
Okay. The pictures look different in terms of how much mycelium can be seen. To be honest, I personally find it hard to say whether the later images are more white (brighter) than the early ones. Maybe because I am not used to looking at the images in grayscale. Grayscale histograms indicate how many pixels correspond to each gray value.
# This function is used to scale y-axis values to a more readable format.
def y_to_k(y_val, _):
"""Convert y-axis: 1000 -> k"""
return f"{int(y_val / 1000)}k"
# Produce a figure with four subplots.
fig, axs = plt.subplots(2, int(len(sample_rows)/2), figsize=(10, 10))
fig.subplots_adjust(left=0, right=1, wspace=0.4, hspace=0.6)
fig.suptitle("Gray value distribution of images taken at different times",
**graph_style.super_title_style, y=1.0)
# Populate figure with histograms subplots.
for ax, (index, row) in zip(axs.flatten(), sample_rows.iterrows()):
# Load image and plot histogram.
im = default_im_loader(row["im"])
ax.hist(im.flatten(), bins=40, **graph_style.histogram_kwargs)
ax.set_title(label=f"{row['h_passed']} h fermentation", **graph_style.title_style)
# Style the histogram.
ax.set_xlim(0, 1)
ax.set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1.0])
ax.set_xlabel(xlabel="Gray value", **graph_style.axes_style)
ax.set_ylim(0,20000)
ax.set_yticks([1000 * y for y in [0, 50, 100, 150, 200]])
ax.yaxis.set_major_formatter(ticker.FuncFormatter(y_to_k))
ax.set_ylabel(ylabel="Counts", **graph_style.axes_style)
ax.tick_params(**graph_style.tick_style)
# Draw a vertical line at the mean value.
ax.vlines(x=im.flatten().mean(), ymin=0, ymax=200*1000,
colors=graph_style.colors_pomegranate[0], ls='-', lw=2)
Displayed like this, the change over time becomes even clearer. We see that the number of values between 0.0 and 0.2 does not change much. These pixels are the black background. On the 8h image, we have a lot of values spread out between 0.5 and 0.9. Over time these values wander to the right. Consequently, we see the mean value (red line) increase from below 0.6 to ca. 0.7.
We can plot this mean gray value over time as proxy for the fungal growth.
# Collect the mean gray values for all images.
mean_gray_values = ferm_data["im"].apply(lambda x: default_im_loader(x).flatten().mean())
ferm_data['mean_gray_value'] = mean_gray_values
ferm_data.to_csv(f"../OutputCSVs/{EX_ID}-gray_values.csv")
# Plot the mean gray values over fermentation time.
fig, ax = plt.subplots()
ax.plot(ferm_data['min_passed']/60, mean_gray_values,
**graph_style.lineplot_kwargs, color=graph_style.colors_pomegranate[1])
# Apply graph styles.
ax.set_title(label="Development of mean gray value", **graph_style.title_style)
ax.set_xlabel(xlabel="Time in hours", **graph_style.axes_style)
ax.set_ylabel(ylabel="Gray value", **graph_style.axes_style)
ax.tick_params(**graph_style.tick_style)
ax.set(xlim=(0, ferm_data["min_passed"].iloc[-1]/60), ylim=(0.55, 0.7));
Indeed, we see something resembling a growth curve. Let's scale to percentage as the raw gray value is arbitrarily dependent on the camera settings and the color and size of the background.
# Plot the scaled mean gray values over fermentation time.
fig, ax = plt.subplots()
ax.plot(ferm_data['min_passed']/60, scale_to_percent(mean_gray_values),
**graph_style.lineplot_kwargs, color=graph_style.colors_pomegranate[1])
# Apply graph styles.
ax.set_title(label="Scaled development of mean gray value", **graph_style.title_style)
ax.set_xlabel(xlabel="Time in hours", **graph_style.axes_style)
ax.set_ylabel(ylabel=r"% of max. gray value", **graph_style.axes_style)
ax.set(xlim=(0, ferm_data["min_passed"].iloc[-1]/60), ylim=(0, 101))
ax.tick_params(**graph_style.tick_style)
Perfect. One could consider smoothing the line to make it look nicer, but that is not our concern now.