Commit 29bcb2a5 authored by Wallenfang, Nils's avatar Wallenfang, Nils

Introduce classes for measurement data and qc results

parent d2e075d5
from os import listdir
from os.path import isdir, join
import numpy as np
from qchecks.quality_check import read_data
def analyze_section(root_path, quality_check, on_tile_done=None):
"""
Args:
root_path (str): Path of the root directory of the raw data
quality_check (QualityCheck): object of type QualityCheck that defines an error measure for each tile.
on_tile_done (function(int, int, list)): Callback function. If passed, this function
will be called after every processed tile. The function will be called with the x- and
y-indices of the tile as int and a list that contains outlier image indices (or is empty).
Returns:
[np.array()]: 2d-array with dimensions corresponding to number of tiles. Each entry
of the 2d-array is a boolean that indicated whether the respective tile was marked an outlier.
If the passed find_outlier method supports it, a further 2d-array with the calculated error measure for each
tile will be returned.
"""
# get last (alphabetic ordering) directory name in root_path to extract x- and y-dimension
directories = sorted([f for f in listdir(root_path) if isdir(join(root_path, f))])
last_dir = directories[-1]
xdim, ydim = last_dir.split("_")
# tiles are zero indexed
xdim, ydim = int(xdim) + 1, int(ydim) + 1
error_measure_matrix = np.zeros(shape=(xdim, ydim))
# iterate over each tile
for x in range(xdim):
for y in range(ydim):
# call analysis and save outlier indices
img_path = f"{root_path}/{str(x).zfill(2)}_{str(y).zfill(2)}/"
data = read_data(img_path)
error_measure = quality_check.calc_error_measure(data)
error_measure_matrix[x, y] = error_measure
is_outlier = quality_check.threshold(error_measure)
if on_tile_done is not None:
on_tile_done(x, y, is_outlier, error_measure)
return error_measure_matrix
# def analyze_section_with_coords(root_path, quality_check, on_tile_done=None):
import numpy as np
from data.qc_result import QCResult
import os
from os import listdir
import pytiff
from os.path import isdir, join
import re
date_format = re.compile(r'\d\d\d\d-\d\d-\d\d_\d\d-\d\d-\d\d') # for example 2019-03-01_12-07-28
class Measurement:
......@@ -14,10 +21,24 @@ class Measurement:
measurement_path: Path of the root directory of the raw data
"""
self.measurement_path = measurement_path
pass
def read(self):
return self
def read_tile(self, x, y):
"""
Load all images belonging to a single tile.
:param x: Full path to tile directory (e.g. '.../00_01/')
:param y: Full path to tile directory (e.g. '.../00_01/')
:return: list of images ([np.array])
"""
path_dir = f"{self.measurement_path}/{str(x).zfill(2)}_{str(y).zfill(2)}/"
filenames = next(os.walk(path_dir))[2]
# image stack, Dimensions: (number of images, horizontal px, vertical px), eg. (18, 2048, 2048)
imgs = []
for filename in sorted(filenames):
if filename.endswith('.tiff'):
img = np.array(pytiff.Tiff(f"{path_dir}/{filename}"))
imgs.append(img)
imgs = np.array(imgs)
return imgs
def run_quality_check(self, quality_check, on_tile_done) -> QCResult:
"""
......@@ -39,7 +60,7 @@ class Measurement:
"""
# get last (alphabetic ordering) directory name in root_path to extract x- and y-dimension
directories = sorted([f for f in listdir(root_path) if isdir(join(root_path, f))])
directories = sorted([f for f in listdir(self.measurement_path) if isdir(join(self.measurement_path, f))])
last_dir = directories[-1]
xdim, ydim = last_dir.split("_")
# tiles are zero indexed
......@@ -52,10 +73,9 @@ class Measurement:
for x in range(xdim):
for y in range(ydim):
# call analysis and save outlier indices
img_path = f"{root_path}/{str(x).zfill(2)}_{str(y).zfill(2)}/"
data = read_data(img_path)
tile_data = self.read_tile(x, y)
error_measure, outlier_coords, filtered_coords = quality_check.calc_error_measure(data,
error_measure, outlier_coords, filtered_coords = quality_check.calc_error_measure(tile_data,
return_coords=True)
error_measure_matrix[x, y] = error_measure
outlier_matrix[x, y] = outlier_coords
......@@ -65,4 +85,35 @@ class Measurement:
if on_tile_done is not None:
on_tile_done(x, y, is_outlier, error_measure)
return QCResult(error_measure_matrix, outlier_matrix, filtered_matrix)
return QCResult(self.measurement_path, error_measure_matrix, outlier_matrix, filtered_matrix)
def is_valid_measurement_directory(path):
"""
Returns a boolean that signifies whether the given path is a valid measurement directory.
A measurement directory contains subdirectories
:param path: path to be checked
:return: bool
"""
if not os.path.isdir(path):
return False
# directory name must conform to a special date format
directory_name = os.path.basename(path)
if not date_format.match(directory_name):
return False
subdirectories = sorted([f for f in listdir(path) if isdir(os.path.join(path, f))])
if not subdirectories:
return False
# TODO requirement section directory must contain section index "s0123"
# additionally check if there is a single valid tile subdirectory
for tile_directory in subdirectories:
coord_indices = tile_directory.split('_')
if len(coord_indices) == 2 and coord_indices[0].isdigit() and coord_indices[1].isdigit():
return True
else:
return False
......@@ -246,16 +246,21 @@ class Preview:
for process in Preview.processes:
process.terminate()
def add_outlier_markings(self, matrix, outlier_coords, filtered_coords):
def add_outlier_markings(self, qc_result):
"""
Draw outlier markings in a new Preview.
Args:
matrix: Error measure matrix with dimensions corresponding to number of tiles in x/y
outlier_coords: Coordinates of outlier pixels per tile (unscaled)
filtered_coords: Coordinates of filtered pixels per tile (unscaled)
qc_result (QCResult): Instance of QCResult, encapsulating
error_matrix: Error measure matrix with dimensions corresponding to number of tiles in x/y
outlier_coords: Coordinates of outlier pixels per tile (unscaled)
filtered_coords: Coordinates of filtered pixels per tile (unscaled)
Returns:
Marked preview object.
"""
matrix = qc_result.error_matrix
filtered_coords = qc_result.filtered_matrix
outlier_coords = qc_result.outlier_matrix
preview_data = self.data.copy()
res_x, res_y = preview_data.shape[0] // matrix.shape[0], preview_data.shape[1] // matrix.shape[1]
......
import os
import numpy as np
from utils.config import config
class QCResult:
"""
Class representing the result of a quality check (QC).
"""
def __init__(self, error_matrix, outlier_matrix, filtered_matrix):
def __init__(self, tiff_path, error_matrix, outlier_matrix, filtered_matrix):
self.tiff_path = tiff_path
self.error_matrix = error_matrix
self.outlier_matrix = outlier_matrix
self.filtered_matrix = filtered_matrix
def save_error_matrix(self):
pass
# use name of section as file name for matrix
# try creating error matrix directory in case it doesn't exist already
if not os.path.isdir(config['GUI']['error_matrix_path']):
try:
os.mkdir(config['GUI']['error_matrix_path'])
except OSError as error:
print(error)
section_name = os.path.normpath(self.tiff_path).split(os.sep)[-2]
np.save(f"{config['GUI']['error_matrix_path']}/{section_name}_errors.npy",
self.error_matrix)
return f"{config['GUI']['error_matrix_path']}/{section_name}_errors.npy"
......@@ -8,23 +8,6 @@ import pytiff
import os
def read_data(path_dir):
"""
Load all images belonging to a single tile.
:param path_dir: Full path to tile directory (e.g. '.../00_01/')
:return:
"""
filenames = next(os.walk(path_dir))[2]
# image stack, Dimensions: (number of images, horizontal px, vertical px), eg. (18, 2048, 2048)
imgs = []
for filename in sorted(filenames):
if filename.endswith('.tiff'):
img = np.array(pytiff.Tiff(f"{path_dir}/{filename}"))
imgs.append(img)
imgs = np.array(imgs)
return imgs
class QualityCheck(ABC):
@abstractmethod
def calc_error_measure(self, data):
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment