Module toolbox

Expand source code
import numpy as np
import cv2 as cv
import os
import imutils
import fnmatch
import tifffile as tifio
# import imagej
import pandas as pd
from scipy.spatial.distance import pdist
import re


# Load raw labels
def load_tomo_data(file):
    """Function to load data into a pandas dataframe.

    Args:
        file (string): The path to the csv dataset.

    Returns:
        Dataframe : Pandas df object.
    """
    # filename = re.match(file_number)
    # csv_path = os.path.join(TOMO_LABELS_PATH, filename)
    df = pd.read_csv(file, delimiter=" ")
    return df


# Clean raw labels
def clean_tomo_data(df):
    """Function to select and sort relevant data from the output of porosity
    detection plugin.
    
    The data in question are (x,y,z) coordinates of defect center of mass, defect
    volume and sphericity factor.

    Args:
        df (Dataframe): Pandas dataframe containing porosity data.

    Returns:
        Dataframe : Pandas df object.
    """
    df = df[['xg', 'yg', 'zg', 'volpix', 'volmarch', 'sphericity']]
    df = df.sort_values('zg', ascending=False)
    df = df[df["volpix"] != df["volpix"].max()]
    return df


def label(IMAGEJ_PATH, IN_DIR, OUT_DIR, TOMO_LABELS_PATH):
    """Script Function to automate the labeling process using ImageJ plugin.
    It takes in the paths of local ImageJ installation which must include the plugin
    and of input data in form of Tif image stack.
    
    The user must also specify the paths for the output which includes processed
    images and defect data in form of csv.

    Args:
        IMAGEJ_PATH (string): The path of ImageJ installation.
        IN_DIR (string): The path of input images.
        OUT_DIR (string): The path to which output images will be written.
        TOMO_LABELS_PATH (string): The path to which the data will be written.

    """
    ij = imagej.init(IMAGEJ_PATH, headless=False)
    tif_paths = []
    listOfFiles = os.listdir(IN_DIR)
    pattern = "*.tif"

    for entry in listOfFiles:
        if fnmatch.fnmatch(entry, pattern) and not fnmatch.fnmatch(entry, ".*"):
            print(entry)
            # selects tif files and appends them to a list
            tif_paths.append(entry)

    for index, tif in enumerate(tif_paths):
        # iterate over tif files
        # etval, mats = cv.imreadmulti(tif, flags=cv.IMREAD_GRAYSCALE)
        mats = tifio.imread(tif)
        assert len(mats) != 0, "No images were loaded"
        mats_th = []
        tif_processed = os.path.join(
            OUT_DIR,
            tif[:len(tif) - 4] + "_processed_" + str(index + 1) + ".tif"
        )
        for idx, mat in enumerate(mats):
            # iterates over all slices in each tif files
            mat = cv.fastNlMeansDenoising(
                mat, h=10, templateWindowSize=7, searchWindowSize=21
            )  # denoise
            _, gray_th = cv.threshold(
                mat, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU
            )  # threshold
            # gray_th = imutils.rotate(gray_th, angles[index])  # rotate
            mats_th.append(gray_th)
        cv.imwritemulti(tif_processed, mats_th)

    for index, tif in enumerate(os.listdir(OUT_DIR)):

        ij_img = ij.io().open(OUT_DIR + '/' + tif)
        ij.ui().show('image', ij_img)

        plugin = 'Labeling'

        args = {'color': 0, 'minimum': 0, '3d': 6}
        ij.py.run_plugin(plugin, args)

        tif = os.path.splitext(tif)[0]
        plugin = 'Parameters'
        args = {'save': '/' + TOMO_LABELS_PATH + "/Labels_" + tif + ".dat"}
        ij.py.run_plugin(plugin, args)

        ij.dispose()


def dist(df):
    return sum(pdist(df.values, 'euclid'))


def compute_indicators_porosity_level(df, vol_layer):
    """Function to compute a porosity severity score based on the four metrics :


    1 - Frequency of occurrence or number of discrete pores in a layer.
    
    2 - The proportion of volume in a layer affected by porosity mu2.
    
    3 - The average distance between a pair of pores mu3.
    
    4 - The above three metrics are combined into a single metric called the
    normalized porosity level μ.

    Args:
        df (Dataframe): The path of ImageJ installation.
        vol_layer (float): The volume of part layer.

    Returns:
        Dataframe : Pandas dataframe containing the four mu values for each
        porosity.

    """
    # occurrence frequency
    mu1 = pd.DataFrame(df.pivot_table(index=['numLayer'], aggfunc='size')
                      ).rename(columns={0: 'mu1'})
    mu1_norm = (mu1 - mu1.mean()) / mu1.std()

    # propotion of affected zone
    mu2 = pd.DataFrame(df.groupby('numLayer')['volmarch'].sum().div(vol_layer)
                      ).rename(columns={'volmarch': 'mu2'})
    mu2_norm = (mu2 - mu2.mean()) / mu2.std()

    # average distance between pores
    mu1_serie = df.pivot_table(index=['numLayer'], aggfunc='size')
    mu3 = pd.DataFrame(
        df.groupby('numLayer')[['xg',
                                'yg']].apply(lambda x: dist(x)).div(mu1_serie)
    ).rename(columns={0: 'mu3'})
    mu3_norm = (mu3 - mu3.mean()) / mu3.std()

    # porosity level
    mu = pd.DataFrame(
        mu2_norm['mu2'].mul(mu1_norm['mu1']).div(mu3_norm['mu3'])
    ).rename(columns={0: 'mu'})

    # normalised porosity level
    normalised_mu = (mu - mu.min()) / (mu.max() - mu.min())
    normalised_mu_V2 = (mu - mu.mean()) / mu.std()
    new_df = pd.concat([mu1, mu2, mu3, normalised_mu], axis=1)
    return new_df

Functions

def clean_tomo_data(df)

Function to select and sort relevant data from the output of porosity detection plugin.

The data in question are (x,y,z) coordinates of defect center of mass, defect volume and sphericity factor.

Args

df : Dataframe
Pandas dataframe containing porosity data.

Returns

Dataframe
Pandas df object.
Expand source code
def clean_tomo_data(df):
    """Function to select and sort relevant data from the output of porosity
    detection plugin.
    
    The data in question are (x,y,z) coordinates of defect center of mass, defect
    volume and sphericity factor.

    Args:
        df (Dataframe): Pandas dataframe containing porosity data.

    Returns:
        Dataframe : Pandas df object.
    """
    df = df[['xg', 'yg', 'zg', 'volpix', 'volmarch', 'sphericity']]
    df = df.sort_values('zg', ascending=False)
    df = df[df["volpix"] != df["volpix"].max()]
    return df
def compute_indicators_porosity_level(df, vol_layer)

Function to compute a porosity severity score based on the four metrics :

1 - Frequency of occurrence or number of discrete pores in a layer.

2 - The proportion of volume in a layer affected by porosity mu2.

3 - The average distance between a pair of pores mu3.

4 - The above three metrics are combined into a single metric called the normalized porosity level μ.

Args

df : Dataframe
The path of ImageJ installation.
vol_layer : float
The volume of part layer.

Returns

Dataframe
Pandas dataframe containing the four mu values for each

porosity.

Expand source code
def compute_indicators_porosity_level(df, vol_layer):
    """Function to compute a porosity severity score based on the four metrics :


    1 - Frequency of occurrence or number of discrete pores in a layer.
    
    2 - The proportion of volume in a layer affected by porosity mu2.
    
    3 - The average distance between a pair of pores mu3.
    
    4 - The above three metrics are combined into a single metric called the
    normalized porosity level μ.

    Args:
        df (Dataframe): The path of ImageJ installation.
        vol_layer (float): The volume of part layer.

    Returns:
        Dataframe : Pandas dataframe containing the four mu values for each
        porosity.

    """
    # occurrence frequency
    mu1 = pd.DataFrame(df.pivot_table(index=['numLayer'], aggfunc='size')
                      ).rename(columns={0: 'mu1'})
    mu1_norm = (mu1 - mu1.mean()) / mu1.std()

    # propotion of affected zone
    mu2 = pd.DataFrame(df.groupby('numLayer')['volmarch'].sum().div(vol_layer)
                      ).rename(columns={'volmarch': 'mu2'})
    mu2_norm = (mu2 - mu2.mean()) / mu2.std()

    # average distance between pores
    mu1_serie = df.pivot_table(index=['numLayer'], aggfunc='size')
    mu3 = pd.DataFrame(
        df.groupby('numLayer')[['xg',
                                'yg']].apply(lambda x: dist(x)).div(mu1_serie)
    ).rename(columns={0: 'mu3'})
    mu3_norm = (mu3 - mu3.mean()) / mu3.std()

    # porosity level
    mu = pd.DataFrame(
        mu2_norm['mu2'].mul(mu1_norm['mu1']).div(mu3_norm['mu3'])
    ).rename(columns={0: 'mu'})

    # normalised porosity level
    normalised_mu = (mu - mu.min()) / (mu.max() - mu.min())
    normalised_mu_V2 = (mu - mu.mean()) / mu.std()
    new_df = pd.concat([mu1, mu2, mu3, normalised_mu], axis=1)
    return new_df
def dist(df)
Expand source code
def dist(df):
    return sum(pdist(df.values, 'euclid'))
def label(IMAGEJ_PATH, IN_DIR, OUT_DIR, TOMO_LABELS_PATH)

Script Function to automate the labeling process using ImageJ plugin. It takes in the paths of local ImageJ installation which must include the plugin and of input data in form of Tif image stack.

The user must also specify the paths for the output which includes processed images and defect data in form of csv.

Args

IMAGEJ_PATH : string
The path of ImageJ installation.
IN_DIR : string
The path of input images.
OUT_DIR : string
The path to which output images will be written.
TOMO_LABELS_PATH : string
The path to which the data will be written.
Expand source code
def label(IMAGEJ_PATH, IN_DIR, OUT_DIR, TOMO_LABELS_PATH):
    """Script Function to automate the labeling process using ImageJ plugin.
    It takes in the paths of local ImageJ installation which must include the plugin
    and of input data in form of Tif image stack.
    
    The user must also specify the paths for the output which includes processed
    images and defect data in form of csv.

    Args:
        IMAGEJ_PATH (string): The path of ImageJ installation.
        IN_DIR (string): The path of input images.
        OUT_DIR (string): The path to which output images will be written.
        TOMO_LABELS_PATH (string): The path to which the data will be written.

    """
    ij = imagej.init(IMAGEJ_PATH, headless=False)
    tif_paths = []
    listOfFiles = os.listdir(IN_DIR)
    pattern = "*.tif"

    for entry in listOfFiles:
        if fnmatch.fnmatch(entry, pattern) and not fnmatch.fnmatch(entry, ".*"):
            print(entry)
            # selects tif files and appends them to a list
            tif_paths.append(entry)

    for index, tif in enumerate(tif_paths):
        # iterate over tif files
        # etval, mats = cv.imreadmulti(tif, flags=cv.IMREAD_GRAYSCALE)
        mats = tifio.imread(tif)
        assert len(mats) != 0, "No images were loaded"
        mats_th = []
        tif_processed = os.path.join(
            OUT_DIR,
            tif[:len(tif) - 4] + "_processed_" + str(index + 1) + ".tif"
        )
        for idx, mat in enumerate(mats):
            # iterates over all slices in each tif files
            mat = cv.fastNlMeansDenoising(
                mat, h=10, templateWindowSize=7, searchWindowSize=21
            )  # denoise
            _, gray_th = cv.threshold(
                mat, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU
            )  # threshold
            # gray_th = imutils.rotate(gray_th, angles[index])  # rotate
            mats_th.append(gray_th)
        cv.imwritemulti(tif_processed, mats_th)

    for index, tif in enumerate(os.listdir(OUT_DIR)):

        ij_img = ij.io().open(OUT_DIR + '/' + tif)
        ij.ui().show('image', ij_img)

        plugin = 'Labeling'

        args = {'color': 0, 'minimum': 0, '3d': 6}
        ij.py.run_plugin(plugin, args)

        tif = os.path.splitext(tif)[0]
        plugin = 'Parameters'
        args = {'save': '/' + TOMO_LABELS_PATH + "/Labels_" + tif + ".dat"}
        ij.py.run_plugin(plugin, args)

        ij.dispose()
def load_tomo_data(file)

Function to load data into a pandas dataframe.

Args

file : string
The path to the csv dataset.

Returns

Dataframe
Pandas df object.
Expand source code
def load_tomo_data(file):
    """Function to load data into a pandas dataframe.

    Args:
        file (string): The path to the csv dataset.

    Returns:
        Dataframe : Pandas df object.
    """
    # filename = re.match(file_number)
    # csv_path = os.path.join(TOMO_LABELS_PATH, filename)
    df = pd.read_csv(file, delimiter=" ")
    return df