diff --git a/setup.py b/setup.py index f7029eb..d1a6c8e 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ 'flow': ['PyQt5', 'networkx', 'pyqtconsole', 'pyxdg', 'qtpynodeeditor', 'pyqtgraph'], 'test': ['pytest', 'pytest-qt'], 'ez': ['PyQt5', 'PyYAML', 'pyqtgraph', 'matplotlib'], + 'edf': ['fabio'], }, description="A fast, versatile and user-friendly image "\ "processing toolkit for computed tomography", diff --git a/tofu/ez/GUI/Main/centre_of_rotation.py b/tofu/ez/GUI/Main/centre_of_rotation.py index 74b31c2..cc7a7b8 100644 --- a/tofu/ez/GUI/Main/centre_of_rotation.py +++ b/tofu/ez/GUI/Main/centre_of_rotation.py @@ -171,7 +171,7 @@ def set_rButton_from_params(self): self.image_midpoint_rButton.setChecked(True) elif EZVARS['COR']['search-method']['value'] == 5: self.unstitched360_rButton.setChecked(True) - self.enable_360Batch_Group_in_Advanced.emit() + self.enable_360Batch_Group_in_Advanced.emit(True) #TODO: also disable INput/Output in 360-SEARCH #and completely disable 360-MULTI-STITCH diff --git a/tofu/ez/GUI/Main/config.py b/tofu/ez/GUI/Main/config.py index 2ce9d64..b9e15b4 100644 --- a/tofu/ez/GUI/Main/config.py +++ b/tofu/ez/GUI/Main/config.py @@ -607,10 +607,12 @@ def reco_button_pressed(self): self.set_preproc() self.set_preproc_entry() if EZVARS_aux['vert-sti']['dovertsti']['value']: - add_value_to_dict_entry(EZVARS_aux['vert-sti']['output-dir'], - f"{EZVARS['inout']['output-dir']['value']}-ort-stitched") - add_value_to_dict_entry(EZVARS_aux['vert-sti']['tmp-dir'], - f"{EZVARS['inout']['tmp-dir']['value']}") + # Warn if output dir collision + if EZVARS['inout']['output-dir']['value'] == EZVARS_aux['vert-sti']['output-dir']['value']: + vert_out = f"{EZVARS['inout']['output-dir']['value']}-vert-stitched" + print("Vertical stitching output directory should not be the same as input.") + print(f"Adjusting from {EZVARS_aux['vert-sti']['output-dir']['value']} to {vert_out}") + add_value_to_dict_entry(EZVARS_aux['vert-sti']['output-dir'], vert_out) run_reco = partial(self.run_reconstruction, batch_run=False) #I had to add a little sleep as on some Linux ditributions params won't fully set before the main() begins QTimer.singleShot(100, run_reco) diff --git a/tofu/ez/GUI/Stitch_tools_tab/ezstitch_qt.py b/tofu/ez/GUI/Stitch_tools_tab/ezstitch_qt.py index a2cda92..8f4dad5 100644 --- a/tofu/ez/GUI/Stitch_tools_tab/ezstitch_qt.py +++ b/tofu/ez/GUI/Stitch_tools_tab/ezstitch_qt.py @@ -19,7 +19,10 @@ main_sti_mp, main_360sti_ufol_depth1, find_vert_olap_2_vsteps, - find_depth_level_to_CT_sets) + find_depth_level_to_CT_sets, + validate_slice_range, + get_cube_dims, +) from tofu.ez.GUI.message_dialog import warning_message from tofu.ez.util import add_value_to_dict_entry, get_int_validator, get_double_validator from tofu.ez.util import import_values, export_values, read_image, get_dims @@ -27,6 +30,8 @@ LOG = logging.getLogger(__name__) + + class EZStitchGroup(QGroupBox): def __init__(self): super().__init__() @@ -316,7 +321,10 @@ def conf_auto_stitch(self): if self.invoke_after_reco_checkbox.isChecked(): add_value_to_dict_entry(EZVARS_aux['vert-sti']['dovertsti'], True) self.input_dir_entry.setEnabled(False) - self.output_dir_entry.setText(f"{EZVARS['inout']['output-dir']['value']}-ort-stitched") + if self.output_dir_entry.text() in ( + EZVARS['inout']['output-dir']['value'], + EZVARS_aux['vert-sti']['output-dir']['ezdefault']): + self.output_dir_entry.setText(f"{EZVARS['inout']['output-dir']['value']}-vert-stitched") self.set_output_entry() self.tmp_dir_entry.setText(f"{EZVARS['inout']['tmp-dir']['value']}") self.set_temp_entry() @@ -403,8 +411,11 @@ def set_flipud(self): bool(self.flipud_checkbox.isChecked())) def set_est_olap(self): + estimate_overlap = bool(self.est_olap_checkbox.isChecked()) + LOG.debug("Estimate vertical overlap automatically: " + str(estimate_overlap)) add_value_to_dict_entry(EZVARS_aux['vert-sti']['estimate_num_olap_rows'], - bool(self.est_olap_checkbox.isChecked())) + estimate_overlap) + self.num_overlaps_entry.setDisabled(estimate_overlap) def set_overlap(self): LOG.debug("Num overlapping rows: " + str(self.num_overlaps_entry.text())) @@ -447,7 +458,7 @@ def get_z01_ind_stop(self): def validate_row_entries(self): self.validate_input_structure_1set() - nslices, N, M = self.get_cube_dims() + nslices, N, M = get_cube_dims() if EZVARS_aux['vert-sti']['ind_z01_stop']['value'] > nslices: QMessageBox.warning(self, "Error", f'Stop index of the search range ' f'exceeds the total number of slices (max {nslices})') @@ -462,71 +473,6 @@ def validate_row_entries(self): return 1 return 0 - def get_cube_dims(self): - pth = "" - subdirs = sorted(os.listdir(EZVARS_aux['vert-sti']['input-dir']['value'])) - if os.path.exists(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0], - EZVARS_aux['vert-sti']['subdir-name']['value'])): - pth = os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0], - EZVARS_aux['vert-sti']['subdir-name']['value']) - else: - second_subdirs = sorted(os.listdir(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0]))) - if os.path.exists(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0], - second_subdirs[0], EZVARS_aux['vert-sti']['subdir-name']['value'])): - pth = os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0], - second_subdirs[0], EZVARS_aux['vert-sti']['subdir-name']['value']) - im_names = glob.glob(os.path.join(pth, '*.tif')) - nslices = len(im_names) - N, M = read_image(im_names[0]).shape - return nslices, N, M - - def validate_requested_section_indices(self): - nslices, N, M = self.get_cube_dims() - if EZVARS_aux['vert-sti']['reslice_all']['value']: - EZVARS_aux['vert-sti']['start']['value'] = 0 - EZVARS_aux['vert-sti']['stop']['value'] = N - EZVARS_aux['vert-sti']['step']['value'] = 1 - else: - if EZVARS_aux['vert-sti']['start']['value'] > EZVARS_aux['vert-sti']['stop']['value']: - tmp = EZVARS_aux['vert-sti']['start']['value'] - EZVARS_aux['vert-sti']['start']['value'] = EZVARS_aux['vert-sti']['stop']['value'] - EZVARS_aux['vert-sti']['stop']['value'] = tmp - - if EZVARS_aux['vert-sti']['stop']['value'] > N: - EZVARS_aux['vert-sti']['stop']['value'] = N - - self.start_stop_step_entry.setText(f"{EZVARS_aux['vert-sti']['start']['value']}," - f"{EZVARS_aux['vert-sti']['stop']['value']}," - f"{EZVARS_aux['vert-sti']['step']['value']}") - # if EZVARS_aux['vert-sti']['start']['value'] > N or \ - # (EZVARS_aux['vert-sti']['stop']['value'] > N): - # QMessageBox.warning(self, "Error", f"Requested range of sections " - # f"{self.start_stop_step_entry.text()} \n" - # f"exceeds the number of rows in CT slices (max {M})") - # return 1 - return 0 - - def validate_slice_range(self): - dtmp, pth = find_depth_level_to_CT_sets(EZVARS_aux['vert-sti']['input-dir']['value'], - EZVARS_aux['vert-sti']['subdir-name']['value']) - try: - nviews, wh, multipage = get_dims(pth) - except: - self.err = "Problem with validating slice range: cannot read dimensions of Input slices." - return 1 - - if EZVARS_aux['vert-sti']['start']['value'] > EZVARS_aux['vert-sti']['stop']['value']: - tmp = EZVARS_aux['vert-sti']['start']['value'] - EZVARS_aux['vert-sti']['start']['value'] = EZVARS_aux['vert-sti']['stop']['value'] - EZVARS_aux['vert-sti']['stop']['value'] = tmp - - if EZVARS_aux['vert-sti']['stop']['value'] > wh[0]: - EZVARS_aux['vert-sti']['stop']['value'] = wh[0] - - self.start_stop_step_entry.setText(f"{EZVARS_aux['vert-sti']['start']['value']}," - f"{EZVARS_aux['vert-sti']['stop']['value']}," - f"{EZVARS_aux['vert-sti']['step']['value']}") - return 0 def validate_input_structure_1set(self): Vsteps = sorted(os.listdir(EZVARS_aux['vert-sti']['input-dir']['value'])) @@ -587,17 +533,23 @@ def verify_safe2delete(self, dir_path, dir_type): rmtree(dir_path) except: warning_message(f"Error while deleting {dir_type} directory") - return + raise FileExistsError else: - return + raise FileExistsError def stitch_button_pressed(self): LOG.debug("Stitch button pressed") - self.verify_safe2delete(EZVARS_aux['vert-sti']['tmp-dir']['value'], "Temporary") - self.verify_safe2delete(EZVARS_aux['vert-sti']['output-dir']['value'], "Output") + try: + self.verify_safe2delete(EZVARS_aux['vert-sti']['tmp-dir']['value'], "Temporary") + except FileExistsError: + return + try: + self.verify_safe2delete(EZVARS_aux['vert-sti']['output-dir']['value'], "Output") + except FileExistsError: + return # if self.validate_input_structure_1set() or self.validate_row_entries(): # return @@ -605,18 +557,15 @@ def stitch_button_pressed(self): print("======= Begin Stitching =======") - # if overlap has to be estimated: - if EZVARS_aux['vert-sti']['estimate_num_olap_rows']['value']: - olap = find_vert_olap_2_vsteps(EZVARS_aux['vert-sti']['input-dir']['value'], - EZVARS_aux['vert-sti']['ind_z00']['value'], - EZVARS_aux['vert-sti']['ind_z01_start']['value'], - EZVARS_aux['vert-sti']['ind_z01_stop']['value']) - self.num_overlaps_entry.setText(str(olap)) - add_value_to_dict_entry(EZVARS_aux['vert-sti']['num_olap_rows'], olap) # Interpolate overlapping regions and equalize intensity if EZVARS_aux['vert-sti']['task_type']['value'] == 0 or \ EZVARS_aux['vert-sti']['task_type']['value'] == 1: - self.validate_requested_section_indices() + try: + validate_slice_range() + except ValueError as e: + LOG.error(e) + warning_message("Problem with validating slice range: cannot read dimensions of Input slices.") + return main_sti_mp() else: # main_360_mp_depth1(self.parameters['ezstitch_input_dir'], diff --git a/tofu/ez/Helpers/batch_search_stitch_360.py b/tofu/ez/Helpers/batch_search_stitch_360.py index 4123688..19dd473 100644 --- a/tofu/ez/Helpers/batch_search_stitch_360.py +++ b/tofu/ez/Helpers/batch_search_stitch_360.py @@ -11,7 +11,7 @@ def batch_stitch(): if os.path.exists(stitched_data_dir_name) and \ len(os.listdir(stitched_data_dir_name)) > 0: print("# Clean directory for stitched data") - return + return 1 add_value_to_dict_entry(EZVARS_aux['stitch360']['olap_switch'], 2) add_value_to_dict_entry(EZVARS_aux['stitch360']['crop'], True) diff --git a/tofu/ez/Helpers/find_360_overlap.py b/tofu/ez/Helpers/find_360_overlap.py index a68a9ec..3b7a868 100644 --- a/tofu/ez/Helpers/find_360_overlap.py +++ b/tofu/ez/Helpers/find_360_overlap.py @@ -231,23 +231,23 @@ def make_sinos_PR(ctset, dirflats, dirdark, dirflats2, ax_range, sin_tmp_dir): cro = EZVARS_aux['find360olap']['stop']['value'] - axis if axis > wh[1] // 2: cro = axis - EZVARS_aux['find360olap']['start']['value'] - par_dir = os.path.join(EZVARS_aux['find360olap']['tmp-dir']['value'], 'stitched', f"{axis:04}") + stitched = os.path.join(EZVARS_aux['find360olap']['tmp-dir']['value'], 'stitched', f"{axis:04}") print(f"Stitching flats/darks/tomo") - main_360sti_ufol_depth1(path2crop_frames, par_dir, axis, cro) - dirdark = os.path.join(par_dir, dirdark) - dirflats = os.path.join(par_dir, dirflats) - dirtomo = os.path.join(par_dir, 'proj-step1') - dirflats2 = os.path.join(par_dir, dirflats2) - if not os.path.exists(dirflats2): - dirflats2 = None - par_dir = os.path.join(EZVARS_aux['find360olap']['tmp-dir']['value'], 'stitched-pr', f"{axis:04}") - out_pattern = os.path.join(par_dir, "proj-%04i.tif") + main_360sti_ufol_depth1(path2crop_frames, stitched, axis, cro) + dirs = ( + os.path.join(stitched, dirdark), + os.path.join(stitched, dirflats), + os.path.join(stitched, 'proj-step1'), + os.path.join(stitched, dirflats2) if os.path.exists(os.path.join(stitched, dirflats2)) else None + ) + stitched_pr = os.path.join(EZVARS_aux['find360olap']['tmp-dir']['value'], 'stitched-pr', f"{axis:04}") + out_pattern = os.path.join(stitched_pr, "proj-%04i.tif") print(f"Phase retrieval") - cmd = fmt_pr_cmd(dirdark, dirflats, dirtomo, dirflats2, out_pattern) + cmd = fmt_pr_cmd(*dirs, out_pattern) os.system(cmd) print(f"Generating sinogram for the target row") cmd = "tofu sinos" - cmd += " --projections {}".format(par_dir) + cmd += " --projections {}".format(stitched_pr) cmd += " --output {}".format(os.path.join(sin_tmp_dir, 'sin-axis-' + str(axis).zfill(4) + '.tif')) cmd += " --number {}".format(nviews//2) cmd += f" --y={mrg} --height=1" diff --git a/tofu/ez/Helpers/stitch_funcs.py b/tofu/ez/Helpers/stitch_funcs.py index a01fc67..8bc168b 100644 --- a/tofu/ez/Helpers/stitch_funcs.py +++ b/tofu/ez/Helpers/stitch_funcs.py @@ -8,8 +8,8 @@ import shutil import numpy as np import tifffile -from tofu.util import read_image, get_image_shape, get_filenames, TiffSequenceReader -from tofu.ez.util import get_data_cube_info +from tofu.util import read_image, get_image_shape, get_filenames, TiffSequenceReader, SequenceReaderError +from tofu.ez.util import get_data_cube_info, add_value_to_dict_entry, get_dims from tofu.ez.image_read_write import get_image_dtype import multiprocessing as mp from functools import partial @@ -48,7 +48,7 @@ def make_ort_sections(ctset_path): :param ctdir Name of the ctdir - blank string if not using multiple ctdirs: :return: """ - Vsteps = sorted(os.listdir(ctset_path)) + Vsteps = sorted_sub_directories(ctset_path) #determine input data type tmp = os.path.join(ctset_path, Vsteps[0], EZVARS_aux['vert-sti']['subdir-name']['value']) nslices, N, M, indtype_digit, indtype, npasses = get_data_cube_info(tmp) @@ -76,17 +76,51 @@ def make_ort_sections(ctset_path): indir = EZVARS_aux['vert-sti']['input-dir']['value'] return indir, indtype +def sorted_sub_directories(path: os.PathLike) -> list[str]: + """Return a sorted list of sub-directories sorted by sub-directory name""" + with os.scandir(path) as entries: + subdirs = sorted([entry.name for entry in entries if entry.is_dir()]) + return subdirs + def find_depth_level_to_CT_sets(input_dir, slice_dir): - subdirs = sorted(os.listdir(input_dir)) + subdirs = sorted_sub_directories(input_dir) tmp = os.path.join(input_dir, subdirs[0], slice_dir) if os.path.exists(tmp): return 1, tmp - second_subdirs = sorted(os.listdir(os.path.join(tmp, subdirs[0]))) + second_subdirs = sorted_sub_directories(os.path.join(input_dir, subdirs[0])) tmp = os.path.join(input_dir, subdirs[0], second_subdirs[0], slice_dir) if os.path.exists(tmp): return 2, tmp return 0, "" +def get_cube_dims(): + """ + Find the first set of slices and determine the cube dimensions + + Returns (number of slices, image_rows, image_columns) + """ + _, pth = find_depth_level_to_CT_sets(EZVARS_aux['vert-sti']['input-dir']['value'], + EZVARS_aux['vert-sti']['subdir-name']['value'] + ) + nslices, hw, multipage = get_dims(pth) + return nslices, hw[0], hw[1] + + +def validate_slice_range(): + nslices, N, M = get_cube_dims() + if EZVARS_aux['vert-sti']['reslice_all']['value']: + EZVARS_aux['vert-sti']['start']['value'] = 0 + EZVARS_aux['vert-sti']['stop']['value'] = N + EZVARS_aux['vert-sti']['step']['value'] = 1 + else: + if EZVARS_aux['vert-sti']['start']['value'] > EZVARS_aux['vert-sti']['stop']['value']: + tmp = EZVARS_aux['vert-sti']['start']['value'] + EZVARS_aux['vert-sti']['start']['value'] = EZVARS_aux['vert-sti']['stop']['value'] + EZVARS_aux['vert-sti']['stop']['value'] = tmp + + if EZVARS_aux['vert-sti']['stop']['value'] > N: + EZVARS_aux['vert-sti']['stop']['value'] = N + def load_an_image_from_the_input_dir(input_dir, slice_dir): return 0 @@ -95,7 +129,7 @@ def main_sti_mp(): #if indir + some z00 subdir + sli + *.tif does not exist then use original if not os.path.exists(EZVARS_aux['vert-sti']['output-dir']['value']): os.makedirs(EZVARS_aux['vert-sti']['output-dir']['value']) - subdirs = sorted(os.listdir(EZVARS_aux['vert-sti']['input-dir']['value'])) + subdirs = sorted_sub_directories(EZVARS_aux['vert-sti']['input-dir']['value']) if os.path.exists(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0], EZVARS_aux['vert-sti']['subdir-name']['value'])): print(" - Working with one CT directory which contains multiple vertical views") @@ -106,7 +140,7 @@ def main_sti_mp(): conc_one_set(EZVARS_aux['vert-sti']['input-dir']['value'], EZVARS_aux['vert-sti']['output-dir']['value']) else: - second_subdirs = sorted(os.listdir(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0]))) + second_subdirs = sorted_sub_directories(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0])) if os.path.exists(os.path.join(EZVARS_aux['vert-sti']['input-dir']['value'], subdirs[0], second_subdirs[0], EZVARS_aux['vert-sti']['subdir-name']['value'])): print(" - Working with several CT directories which contain multiple vertical views") @@ -125,13 +159,21 @@ def main_sti_mp(): def sti_one_set(in_dir_path, out_dir_path): indir, indtype = make_ort_sections(in_dir_path) outfilepattern = os.path.join(out_dir_path, EZVARS_aux['vert-sti']['subdir-name']['value'] + '-sti-{:>04}.tif') + if EZVARS_aux['vert-sti']['estimate_num_olap_rows']['value']: + olap = find_vert_olap_2_vsteps(in_dir_path, + EZVARS_aux['vert-sti']['ind_z00']['value'], + EZVARS_aux['vert-sti']['ind_z01_start']['value'], + EZVARS_aux['vert-sti']['ind_z01_stop']['value']) + add_value_to_dict_entry(EZVARS_aux['vert-sti']['num_olap_rows'], olap) dx = int(EZVARS_aux['vert-sti']['num_olap_rows']['value']) # second: stitch them - Vsteps = sorted(os.listdir(indir)) + Vsteps = sorted_sub_directories(indir) tmp = glob.glob(os.path.join(indir, Vsteps[0], EZVARS_aux['vert-sti']['subdir-name']['value'], '*.tif'))[0] first = read_image(tmp) N, M = first.shape Nnew = N - dx + if Nnew < 0: + raise ValueError(f"Vertical overlap {dx} larger than input image height {N}") ramp = np.linspace(0, 1, dx) J = range((EZVARS_aux['vert-sti']['stop']['value'] - EZVARS_aux['vert-sti']['start']['value']) // @@ -190,7 +232,7 @@ def exec_sti_mp(indir, pout, N, Nnew, Vsteps, dx, M, ramp, indtype, j): def conc_one_set(indir, pout): indir, indtype = make_ort_sections(indir) outfilepattern = os.path.join(pout, EZVARS_aux['vert-sti']['subdir-name']['value'] + '-sti-{:>04}.tif') - zfold = sorted(os.listdir(indir)) + zfold = sorted_sub_directories(indir) l = len(zfold) tmp = glob.glob(os.path.join(indir, zfold[0], EZVARS_aux['vert-sti']['subdir-name']['value'], '*.tif')) J = range((EZVARS_aux['vert-sti']['stop']['value'] - EZVARS_aux['vert-sti']['start']['value']) // @@ -261,8 +303,7 @@ def main_360_mp_depth1(indir, outdir, ax, cro): if not os.path.exists(outdir): os.makedirs(outdir) - subdirs = [dI for dI in os.listdir(indir) \ - if os.path.isdir(os.path.join(indir, dI))] + subdirs = sorted_sub_directories(indir) for i, sdir in enumerate(subdirs): print(f"Stitching images in {sdir}") @@ -296,8 +337,7 @@ def main_360sti_ufol_depth1(indir, outdir, ax, cro): if not os.path.exists(outdir): os.makedirs(outdir) - subdirs = [dI for dI in os.listdir(indir) \ - if os.path.isdir(os.path.join(indir, dI))] + subdirs = sorted_sub_directories(indir) for i, sdir in enumerate(subdirs): print(f"Stitching images in {sdir}") @@ -369,7 +409,7 @@ def main_360_mp_depth2(): cra = np.max(dax)-dax # Axis on the right ? Must open one file to find out >< tmpname = os.path.join(EZVARS_aux['stitch360']['input-dir']['value'], ctdirs_rel_paths[0]) - subdirs = [dI for dI in os.listdir(tmpname) if os.path.isdir(os.path.join(tmpname, dI))] + subdirs = sorted_sub_directories(tmpname) M = get_image_shape(get_filenames(os.path.join(tmpname, subdirs[0]))[0])[-1] if np.min(dax) > M//2: cra = dax - np.min(dax) @@ -403,7 +443,7 @@ def check_last_index(axis_list): return last_index def find_vert_olap_all_vsteps(ctset_path, ind_z00, ind_start_z01, ind_stop_z01): - Vsteps = sorted(os.listdir(ctset_path)) + Vsteps = sorted_sub_directories(ctset_path) num_vsteps = len(Vsteps) olaps = np.zeros((num_vsteps-1, 1)) for i in range(num_vsteps-1): @@ -418,7 +458,7 @@ def find_vert_olap_all_vsteps(ctset_path, ind_z00, ind_start_z01, ind_stop_z01): def find_vert_olap_2_vsteps(ctset_path, ind_z00, ind_start_z01, ind_stop_z01): #taking two sub-scans near the center of the sample - vsteps = sorted(os.listdir(ctset_path)) + vsteps = sorted_sub_directories(ctset_path) num_vsteps = len(vsteps) z00_name = os.path.join(ctset_path, vsteps[num_vsteps//2-1], EZVARS_aux['vert-sti']['subdir-name']['value']) @@ -439,7 +479,10 @@ def find_vert_overlap(z00_name, z01_name, ind_z00, ind_start_z01, ind_stop_z01): ssim_ind = np.zeros((num_ref_im, 1)) #TODO: must be parallelized for i in range(num_ref_im): - im1 = tsr.read(ind_start_z01 + i) + try: + im1 = tsr.read(ind_start_z01 + i) + except SequenceReaderError: + continue ssim_ind[i] = ssim(im0, im1, data_range=(max(im0.max(), im1.max()) - min(im0.min(), im1.min()))) print(f"Similarity with slice {ind_start_z01 + i} = {ssim_ind[i]}") diff --git a/tofu/ez/ctdir_walker.py b/tofu/ez/ctdir_walker.py index 2d72684..a6f6f7b 100644 --- a/tofu/ez/ctdir_walker.py +++ b/tofu/ez/ctdir_walker.py @@ -7,6 +7,8 @@ import os from tofu.ez.params import EZVARS +VALID_EXTS = ['.tif', '.tiff', '.edf'] + class WalkCTdirs: """ Walks in the directory structure and creates list of paths to CT folders @@ -146,7 +148,7 @@ def checkCTfiles(self): def _checktifs(self, tmpath): """ - Checks each whether item in directory tmppath is a .tif file + Checks each whether item in directory tmppath is a supported file :param tmpath: Path to directory :return: 0 if invalid item found in directory - 1 if no invalid items found in directory """ @@ -154,8 +156,8 @@ def _checktifs(self, tmpath): if os.path.isdir(i): print(f"Directory {tmpath} contains a subdirectory") return 0 - if i.split(".")[-1] != "tif": - print(f"Directory {tmpath} has files which are not tif images or containers") + if os.path.splitext(i)[1] not in VALID_EXTS: + print(f"Directory {tmpath} has files which are not supported images or containers") return 0 return 1 diff --git a/tofu/ez/evaluate_sharpness.py b/tofu/ez/evaluate_sharpness.py index 715944c..f41d65a 100644 --- a/tofu/ez/evaluate_sharpness.py +++ b/tofu/ez/evaluate_sharpness.py @@ -178,13 +178,18 @@ def evaluate_metrics_360_olap_search(images, out_prefix, x_data, *args, **kwargs path = out_prefix + "/" + metric np.savetxt(path+".txt", merged[metric], fmt="%g") #and plot it as well - from matplotlib import pyplot as plt - plt.figure() - plt.plot(x_data, merged[metric]) - plt.title(metric) - plt.grid() - plt.savefig(path + ".tif") - plt.close() + try: + from matplotlib import pyplot as plt + except ImportError: + pass + else: + plt.figure() + plt.plot(x_data, merged[metric]) + plt.title(metric) + plt.grid() + plt.savefig(path + ".png") + plt.close() + return merged diff --git a/tofu/ez/main.py b/tofu/ez/main.py index 8dee7b4..3e083c5 100644 --- a/tofu/ez/main.py +++ b/tofu/ez/main.py @@ -19,7 +19,7 @@ from tofu.config import SECTIONS from tofu.ez.Helpers.batch_search_stitch_360 import batch_stitch, batch_olap_search from tofu.ez.Helpers.stitch_funcs import find_vert_olap_2_vsteps, main_sti_mp, \ - complete_message, find_depth_level_to_CT_sets + complete_message, find_depth_level_to_CT_sets, validate_slice_range from shutil import rmtree LOG = logging.getLogger(__name__) @@ -236,8 +236,6 @@ def execute_reconstruction(): # so we get CT sets from this directory instead of the original input W, lvl0 = get_CTdirs_list(os.path.join( EZVARS_aux['half-acq']['workdir']['value'], 'stitched-data'), fdt_names) - # and set axis of rotation parameter to the image's middle column - add_value_to_dict_entry(EZVARS['COR']['search-method'], 4) # then proceed as usual # get list of already reconstructed sets @@ -287,7 +285,7 @@ def execute_reconstruction(): # print(f" RAM {0.9*ram_amount_bytes}, width {wh[1]}, nrows {nrows}, proj size {(wh[1] * nrows * 4)}, " # f"n_per_pass {int(0.9*ram_amount_bytes/ (wh[1] * nrows * 4))}") # If EZVARS['COR']['search-method']['value'] == 4 then bypass axis search and use image midpoint - if EZVARS['COR']['search-method']['value'] != 4: + if EZVARS['COR']['search-method']['value'] < 4: # Find axis of rotation using auto: correlate first/last projections if EZVARS['COR']['search-method']['value'] == 1: ax = find_axis_corr(ctset, @@ -305,8 +303,8 @@ def execute_reconstruction(): nviews, wh) else: ax = axlist[i]#EZVARS['COR']['user-defined-ax']['value'] + i * EZVARS['COR']['user-defined-dax']['value'] - # If EZVARS['COR']['search-method']['value'] == 4 then bypass axis search and use image midpoint - elif EZVARS['COR']['search-method']['value'] == 4: + # If EZVARS['COR']['search-method']['value'] >= 4 then bypass axis search and use image midpoint + elif EZVARS['COR']['search-method']['value'] >= 4: ax = find_axis_image_midpoint(wh) print("Bypassing axis search and using image midpoint: {}".format(ax)) add_value_to_dict_entry(SECTIONS['cone-beam-weight']['center-position-x'], str(ax)) @@ -348,27 +346,20 @@ def execute_reconstruction(): print("========================================") if EZVARS_aux['vert-sti']['dovertsti']['value']: print("======= Begin Vertical Stitching =======") - rmtree(EZVARS['inout']['tmp-dir']['value']) add_value_to_dict_entry(EZVARS_aux['vert-sti']['input-dir'], EZVARS['inout']['output-dir']['value']) - if EZVARS_aux['vert-sti']['estimate_num_olap_rows']['value']: - # Stitch multiple data set or just one? - dtmp, pth = find_depth_level_to_CT_sets(EZVARS_aux['vert-sti']['input-dir']['value'], - EZVARS_aux['vert-sti']['subdir-name']['value']) - if pth == "": - print(f"Cannot format path to a directory with slices. Check directory structure") - olap = find_vert_olap_2_vsteps(pth, - EZVARS_aux['vert-sti']['ind_z00']['value'], - EZVARS_aux['vert-sti']['ind_z01_start']['value'], - EZVARS_aux['vert-sti']['ind_z01_stop']['value']) - print(f"Number of overlapping lines is {olap}.") - add_value_to_dict_entry(EZVARS_aux['vert-sti']['num_olap_rows'], olap) # validate slice range (common problem) - # TODO: must use function from ezsttich_qt by signal - nviews, wh, multipage = get_dims(pth) - if EZVARS_aux['vert-sti']['stop']['value'] > wh[0]: - EZVARS_aux['vert-sti']['stop']['value'] = wh[0] - main_sti_mp() + try: + validate_slice_range() + except ValueError as e: + print(f"Cannot format path to a directory with slices. Check directory structure") + LOG.error(e) + else: + main_sti_mp() + if not EZVARS['inout']['keep-tmp']['value']: + vert_sti_tmp = EZVARS_aux['vert-sti']['tmp-dir']['value'] + for dir in os.listdir(vert_sti_tmp): + rmtree(os.path.join(vert_sti_tmp, dir)) print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") if (EZVARS['COR']['search-method']['value'] == 5) and EZVARS_aux['vert-sti']['dovertsti']['value']: diff --git a/tofu/ez/util.py b/tofu/ez/util.py index a0b1968..edcebe7 100644 --- a/tofu/ez/util.py +++ b/tofu/ez/util.py @@ -4,6 +4,8 @@ @author: gasilos """ import os, glob, tifffile + +from tofu.ez.ctdir_walker import VALID_EXTS from tofu.ez.params import EZVARS, EZVARS_aux from tofu.config import SECTIONS from tofu.util import get_filenames, get_first_filename, get_image_shape, read_image, restrict_value, tupleize @@ -17,12 +19,14 @@ def get_dims(pth): # get number of projections and projections dimensions - first_proj = get_first_filename(pth) + first_proj = get_first_filename(pth, valid_exts=VALID_EXTS) multipage = False try: shape = get_image_shape(first_proj) + except ImportError: + raise except: - raise ValueError("Failed to determine size and number of projections in {}".format(pth)) + raise ValueError(f"Failed to determine size and number of images in {pth} from {first_proj}") if len(shape) == 2: # single page input return len(get_filenames(pth)), [shape[-2], shape[-1]], multipage elif len(shape) == 3: # multipage input @@ -34,7 +38,8 @@ def get_dims(pth): return -6, [-6, -6] def get_data_cube_info(pth): - im_names = glob.glob(os.path.join(pth, '*.tif')) + ext = os.path.splitext(get_first_filename(pth, valid_exts=VALID_EXTS))[1] + im_names = glob.glob(os.path.join(pth, '*' + ext)) nslices = len(im_names) im = read_image(im_names[0]) N, M = im.shape @@ -136,18 +141,17 @@ def fmt_in_out_path(tmpdir, indir, raw_proj_dir_name, croutdir=True): in_proj_dir, out_proj_dir = "qqq", "qqq" if Nsteps == 0: # no projections in temporary directory in_proj_dir = os.path.join(indir, raw_proj_dir_name) - out_proj_dir = "proj-step1" + out_proj_dir = os.path.join(tmpdir, "proj-step1") elif Nsteps > 0: # there are directories proj-stepX in tmp dir in_proj_dir = proj_dirs[-1] out_proj_dir = "{}{}".format(in_proj_dir[:-1], Nsteps + 1) else: raise ValueError("Something is wrong with in/out filenames") # physically create output directory - tmp = os.path.join(tmpdir, out_proj_dir) - if croutdir and not os.path.exists(tmp): - os.makedirs(tmp) + if croutdir and not os.path.exists(out_proj_dir): + os.makedirs(out_proj_dir) # return names of input directory and output pattern with abs path - return in_proj_dir, os.path.join(tmp, "proj-%04i.tif") + return in_proj_dir, os.path.join(out_proj_dir, "proj-%04i.tif") def enquote(string, escape=False): addition = '\\"' if escape else '"' diff --git a/tofu/util.py b/tofu/util.py index 1ce2055..791a445 100644 --- a/tofu/util.py +++ b/tofu/util.py @@ -217,13 +217,17 @@ def get_image_shape(filename): return shape -def get_first_filename(path): - """Returns the first valid image filename in *path*.""" +def get_first_filename(path, valid_exts: list[str] = None): + """Returns the first valid image filename in *path*. If *valid_exts* is set, only return files + with the extension matching *valid_exts*.""" if not path: raise RuntimeError("Path to sinograms or projections not set.") filenames = get_filenames(path) + if valid_exts is not None: + filenames = [f for f in filenames if os.path.splitext(f)[1] in valid_exts] + if not filenames: raise RuntimeError("No files found in `{}'".format(path))