Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b337d10
ez: Disable vertical overlap entry when automatic estimate is selected.
stuart-cls Jun 23, 2025
7b8d003
ez: Don't CoR change setting on half-acq stitching
stuart-cls Jun 23, 2025
e63a0c8
ez: load ezvars missing parameter in signal
stuart-cls Jun 23, 2025
3c87106
ez: fix stitch depth level
stuart-cls Jun 23, 2025
edbdffc
ez: auto-stitch only modify output if collides with output or default
stuart-cls Jun 24, 2025
f48b5c1
ez: vertical stitch overlap search on each parent dataset if multiple…
stuart-cls Jun 24, 2025
aae4099
ez: ez-stitch return if user requests to keep data intact
stuart-cls Jun 26, 2025
b7fa103
ez: only adjust auto-vert stitch output directory if it collides with…
stuart-cls Jun 27, 2025
581a045
ez.evaluate_sharpness: Output metric figures as PNG, pass if no matpl…
stuart-cls Aug 28, 2025
ed652fd
ez.stitch_funcs: Guard against invalid vertical overlap.
stuart-cls Aug 28, 2025
fe1adaf
ez.batch_stitch: actually stop when stitched-data is occupied.
stuart-cls Aug 29, 2025
5828b61
ez: ez-stitch cleaner implementation of verify_safe2delete
stuart-cls Oct 2, 2025
04b894e
ezstitch: reuse common functions in get_cube_dims
stuart-cls Oct 27, 2025
27117fa
ezstitch: consolidate slice validation
stuart-cls Oct 27, 2025
8972cd4
ez_exec: re-use slice validation from ez-stitch
stuart-cls Oct 27, 2025
5679ab9
ez: Respect tmp-dir setting for auto vert stitch
stuart-cls Oct 28, 2025
fb61a2b
Just empty the tmp-dir, and also try not rmtree the whole [inout][tmp…
stuart-cls Oct 28, 2025
d7f1d8a
ez: fmt_in_out_path() next step always relative to previous
stuart-cls Oct 29, 2025
c8c8d2c
util: get_first_filename add optional extension filtering.
stuart-cls Oct 31, 2025
0973f37
ez: get_dims() only look for .tif or .tiff files
stuart-cls Oct 31, 2025
065288b
tofu-ez: Support edf
stuart-cls Nov 27, 2025
5c0b8f3
edf: Add fabio optional extra
stuart-cls Nov 27, 2025
ff5be8f
tofu ez: Replace use of os.listdir with os.scandir
stuart-cls Jan 6, 2026
7f5617b
ez: make_sinos_PR() fails on dataset without flats2
stuart-cls Feb 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion tofu/ez/GUI/Main/centre_of_rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 6 additions & 4 deletions tofu/ez/GUI/Main/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
113 changes: 31 additions & 82 deletions tofu/ez/GUI/Stitch_tools_tab/ezstitch_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@
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
import glob

LOG = logging.getLogger(__name__)



class EZStitchGroup(QGroupBox):
def __init__(self):
super().__init__()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()))
Expand Down Expand Up @@ -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})')
Expand All @@ -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']))
Expand Down Expand Up @@ -587,36 +533,39 @@ 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



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'],
Expand Down
2 changes: 1 addition & 1 deletion tofu/ez/Helpers/batch_search_stitch_360.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
24 changes: 12 additions & 12 deletions tofu/ez/Helpers/find_360_overlap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading