Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atlas config #1806

Draft
wants to merge 30 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4958b76
Add the new FIDDLE instrument
Dec 11, 2024
d994e12
Add hdf5 path to known paths list
Dec 11, 2024
0336635
Support manually loading images and dark files
Dec 12, 2024
8a2ae5f
Try to automatically match images and dark files
Dec 12, 2024
3e1e5fc
Improve naming for clarity
Dec 12, 2024
6d87b8e
Make sure that the transform option is enabled on image load
Dec 16, 2024
2a167e5
Add property to simplify check for template
Dec 16, 2024
2896f79
Add some inline comments for clarity
Dec 16, 2024
045e46a
PEP8 fixes & use consistent formatting
Dec 16, 2024
c65ab2f
Infer which values are detectors and which are image plates
Dec 16, 2024
ae36b55
Add support for median filter intensity correction
Dec 17, 2024
f07b930
Only apply one template to an image at a time
Dec 17, 2024
d4104f6
Duplicate image plate image if it exists
Dec 17, 2024
9485377
PEP8 fixes
Dec 17, 2024
47100f2
Add default config for FIDDLE instrument
Dec 18, 2024
0670288
Memoize median filter results for FIDDLE
psavery Dec 18, 2024
dee7912
Refactor median kernel into its own dialog
Dec 22, 2024
8a063f1
Make sure kernel size is always stored as an int
Dec 22, 2024
119ba06
Do not apply the median filter during import
Dec 22, 2024
be4baf0
Re-organize reset function for readability
Dec 22, 2024
599fe0d
Automatically flip FIDDLE image plate
Dec 22, 2024
1a8c68e
Do not assume that dark files end with "003"
bnmajor Feb 26, 2025
78d4aa9
Fix event check for translate/rotate template
bnmajor Feb 28, 2025
b1a1c88
Fix constant value
bnmajor Feb 28, 2025
3d018fa
Improve final dialogs
bnmajor Feb 28, 2025
4fa62aa
Rescale colormap range when a new image is loaded
bnmajor Feb 28, 2025
1a7ef1e
Remember images directory
bnmajor Feb 28, 2025
c6dd44b
Make sure we are not transforming detector images
bnmajor Feb 28, 2025
e11c00c
Disable detecor image load during image plate load
bnmajor Feb 28, 2025
cc13653
Support ATLAS coordinates
bnmajor Mar 14, 2025
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
Prev Previous commit
Next Next commit
Support manually loading images and dark files
Signed-off-by: Brianna Major <[email protected]>
Brianna Major authored and bnmajor committed Mar 3, 2025
commit 0336635d0c9e81bf2aedfef12eb024bf0c4b7bb7
2 changes: 2 additions & 0 deletions hexrdgui/constants.py
Original file line number Diff line number Diff line change
@@ -140,3 +140,5 @@ class LLNLTransform:
"Rotate 180°",
"Rotate 270°"
]

FIDDLE_FRAMES = 4
153 changes: 127 additions & 26 deletions hexrdgui/llnl_import_tool_dialog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import glob
import os
import re
import numpy as np
import yaml
import tempfile
@@ -22,7 +24,13 @@
from hexrdgui.create_hedm_instrument import create_hedm_instrument
from hexrdgui.ui_loader import UiLoader
from hexrdgui.constants import (
UI_TRANS_INDEX_ROTATE_90, YAML_EXTS, LLNLTransform, ViewType)
FIDDLE_FRAMES,
FIDDLE_HDF5_PATH,
UI_TRANS_INDEX_ROTATE_90,
YAML_EXTS,
LLNLTransform,
ViewType
)
import hexrdgui.resources.calibration

from hexrdgui.utils import instr_to_internal_dict, block_signals
@@ -59,6 +67,7 @@ def __init__(self, cmap=None, parent=None):
self.import_in_progress = False
self.loaded_images = []
self.canvas = parent.image_tab_widget.active_canvas
self.simple_images = {}

# Disable these by default.
# If we disable these in Qt Designer, there are some weird bugs
@@ -94,8 +103,10 @@ def setup_connections(self):
self.instrument_selected)
self.ui.load.clicked.connect(self.load_images)
self.ui.detectors.currentIndexChanged.connect(self.detector_selected)
self.ui.simple_detectors.currentIndexChanged.connect(
self.simple_detector_selected)
self.ui.add_transform.clicked.connect(self.add_transform)
self.ui.accept_template.clicked.connect(self.crop_and_mask)
self.ui.accept_template.clicked.connect(self.complete_current_detector)
self.ui.complete.clicked.connect(self.completed)
self.ui.bb_height.valueChanged.connect(self.update_bbox_height)
self.ui.bb_width.valueChanged.connect(self.update_bbox_width)
@@ -112,6 +123,9 @@ def setup_connections(self):
self.get_instrument_defaults)
self.ui.instr_settings.currentIndexChanged.connect(
self.instrument_settings_changed)
self.ui.simple_load.clicked.connect(self.load_simple_images)
self.ui.load_dark.clicked.connect(self.load_simple_images)
self.ui.accept_detector.clicked.connect(self.manually_load_simple_images)

def enable_widgets(self, *widgets, enabled):
for w in widgets:
@@ -178,6 +192,9 @@ def set_detector_options(self):
det_list = list(self.detectors)
self.ui.detectors.clear()
self.ui.detectors.addItems(det_list)
if self.instrument == 'FIDDLE':
# FIXME: Need to differentiate between detectors and image plates
self.ui.simple_detectors.addItems(det_list)

def load_config(self):
selected_file, selected_filter = QFileDialog.getOpenFileName(
@@ -320,6 +337,15 @@ def config_loaded_from_menu(self):
return
self.load_instrument_config()

def simple_detector_selected(self, selected):
# Don't allow the color map range to change while changing detectors.
self.cmap.block_updates(True)
try:
self.ui.instrument.setDisabled(selected)
self.detector = self.ui.simple_detectors.currentText()
finally:
self.cmap.block_updates(False)

def detector_selected(self, selected):
# Don't allow the color map range to change while we are changing
# detectors. Otherwise, it gets reset to something like "1 - 6".
@@ -360,6 +386,70 @@ def _set_transform(self):
flip = LLNLTransform.IP4
HexrdConfig().load_panel_state['trans'] = [flip]

def accept_detector(self, data_file, dark_file):
img = [[]] * FIDDLE_FRAMES
first, last = '/'.join(FIDDLE_HDF5_PATH).rsplit('0', 1)
for frame in range(FIDDLE_FRAMES):
path = f'{first}{frame}{last}'
with h5py.File(data_file) as data:
data_raw = np.array(data[path])
with h5py.File(dark_file) as dark:
dark_raw = np.array(dark[path])
img[frame] = data_raw - dark_raw

ims = ImageFileManager().open_file(img)
self.simple_images[self.detector]['img'] = ims
self.complete_current_detector()

def manually_load_simple_images(self):
data_file = self.simple_images[self.detector]['data']
dark_file = self.simple_images[self.detector]['dark']
self.accept_detector(data_file, dark_file)
img = self.edited_images[self.detector]['img']
HexrdConfig().imageseries_dict['default'] = img
ImageLoadManager().read_data(
ui_parent=self.ui.parent(),
postprocess=True
)

def load_simple_images(self):
selected_file, _ = QFileDialog.getOpenFileName(
self.ui,
'Select file(s)',
dir=HexrdConfig().images_dir
)
if not selected_file:
return

# Create regex pattern to try to match data and dark images
# ex: TD_TC000-000_FIDDLE_CAMERA-02-DB_SHOT_RAW-FIDDLE-CAMERA_N240717-001-003.h5
# -> TD_TC000-000_FIDDLE_CAMERA-*-DB_SHOT_RAW-FIDDLE-CAMERA_N240717-001-*.h5
image = re.sub("CAMERA-\d{2}-", "CAMERA-*-", selected_file)
data_files = re.sub("-\d{3}.h", "-999.h", image)
dark_files = re.sub("-\d{3}.h", "-003.h", image)

data_matches = sorted(glob.glob(data_files))
dark_matches = sorted(glob.glob(dark_files))

if len(data_matches) == len(dark_matches) == len(self.detectors):
# FIXME: Complete all matched pathway
return
else:
self.simple_images.setdefault(self.detector, {})
self.simple_images[self.detector].setdefault('data', None)
self.simple_images[self.detector].setdefault('dark', None)
if Path(selected_file).stem.endswith('999'):
self.ui.simple_files_label.setText(Path(selected_file).stem)
self.loaded_images.append(selected_file)
self.simple_images[self.detector]['data'] = selected_file
elif Path(selected_file).stem.endswith('003'):
self.ui.dark_files_label.setText(Path(selected_file).stem)
self.simple_images[self.detector]['dark'] = selected_file
sid = self.simple_images[self.detector]
self.enable_widgets(
self.ui.accept_detector,
enabled=bool(sid['data'] and sid['dark']))

def load_images(self):
self._set_transform()

@@ -528,45 +618,56 @@ def swap_bounds_for_cropped(self):
self.update_bbox_width(1330)
self.update_bbox_height(238)

def crop_and_mask(self):
self.save_boundary_position()
if self.detector == 'IMAGE-PLATE-3':
self.swap_bounds_for_cropped()
def complete_current_detector(self):
if self.it is not None:
self.save_boundary_position()
if self.detector == 'IMAGE-PLATE-3':
self.swap_bounds_for_cropped()
self.finalize()
self.completed_detectors.append(self.detector)
self.enable_widgets(self.ui.file_selection, self.ui.transform_img,
self.ui.complete, enabled=True)
self.enable_widgets(self.ui.outline_appearance,
self.ui.template_instructions,
self.ui.accept_template, self.ui.transform_img,
enabled=False)
self.enable_widgets(
self.ui.file_selection,
self.ui.transform_img,
self.ui.complete,
enabled=True)
self.enable_widgets(
self.ui.outline_appearance,
self.ui.template_instructions,
self.ui.accept_template,
self.ui.transform_img,
self.ui.accept_detector,
enabled=False)
self.ui.completed_dets.setText(
', '.join(set(self.completed_detectors)))

def finalize(self):
detectors = self.detector_defaults['default_config'].get(
'detectors', {})
det = detectors.setdefault(self.detector, {})
img = self.simple_images[self.detector]['img']
width = det.setdefault('pixels', {}).get('columns', 0)
height = det.setdefault('pixels', {}).get('rows', 0)
panel_buffer = [0., 0.]
tilt = 0.

if self.instrument == 'PXRDIP':
# Boundary is currently rotated 90 degrees
width, height = height, width
self.it.cropped_image(height, width)
if self.it is not None:
if self.instrument == 'PXRDIP':
# Boundary is currently rotated 90 degrees
width, height = height, width
self.it.cropped_image(height, width)

img, panel_buffer = self.it.masked_image
if self.instrument == 'PXRDIP':
panel_buffer = panel_buffer.T[::-1, :] # !!! need to rotate buffers
img, panel_buffer = self.it.masked_image
if self.instrument == 'PXRDIP':
panel_buffer = panel_buffer.T[::-1, :] # !!! need to rotate buffers
tilt = self.it.rotation
self.it.completed()

self.edited_images[self.detector] = {
'img': img,
'tilt': self.it.rotation,
'tilt': tilt,
'panel_buffer': panel_buffer,
}

self.it.completed()

def clear(self):
self.clear_boundry()
self.enable_widgets(
@@ -575,14 +676,16 @@ def clear(self):
self.ui.template_instructions, enabled=False)

def check_for_unsaved_changes(self):
if self.it.shape is None and self.detector in self.completed_detectors:
# FIXME: Make sure self.it is always reset to None after completing a templated detector
# FIXME: Turn self.it == None check into property ("has_template"?)
if self.it is None or self.it.shape is None and self.detector in self.completed_detectors:
return
msg = ('The currently selected detector has changes that have not been'
+ ' accepted. Keep changes?')
response = QMessageBox.question(
self.ui, 'HEXRD', msg, (QMessageBox.Cancel | QMessageBox.Save))
if response == QMessageBox.Save:
self.crop_and_mask()
self.complete_current_detector()

def reset_panel(self):
HexrdConfig().enable_image_mode_widget.emit(True)
@@ -592,8 +695,6 @@ def reset_panel(self):
self.ui.files_label.setText('')
self.ui.simple_files_label.setText('')
self.ui.dark_files_label.setText('')
self.ui.simple_files_label.setToolTip('')
self.ui.dark_files_label.setToolTip('')
self.ui.completed_dets.setText('')
self.edited_images.clear()
self.enable_widgets(
96 changes: 52 additions & 44 deletions hexrdgui/resources/ui/llnl_import_tool_dialog.ui
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>565</width>
<height>854</height>
<height>1057</height>
</rect>
</property>
<property name="windowTitle">
@@ -198,56 +198,53 @@
<string>Simple Raw Image</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QLabel" name="simple_detector_label">
<property name="enabled">
<bool>true</bool>
<item row="3" column="0" colspan="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="text">
<string>Current Detector</string>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</widget>
</spacer>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="simple_load">
<item row="3" column="2">
<widget class="QPushButton" name="accept_detector">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="text">
<string>Select Image</string>
<string>Accept Detector</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="load_dark">
<item row="0" column="0">
<widget class="QLabel" name="simple_detector_label">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Select Dark Image</string>
<string>Current Detector</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="simple_detectors">
<property name="enabled">
<bool>true</bool>
</property>
</spacer>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="accept_detector">
<item row="1" column="0">
<widget class="QPushButton" name="simple_load">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>Accept Detector</string>
<string>Select Image</string>
</property>
</widget>
</item>
@@ -264,6 +261,16 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="load_dark">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Select Dark Image</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLabel" name="simple_files_label">
<property name="text">
@@ -277,12 +284,8 @@
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="simple_detectors">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
<item row="4" column="0" colspan="3">
<layout class="QVBoxLayout" name="matches_layout"/>
</item>
</layout>
</widget>
@@ -430,13 +433,6 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="transform_label">
<property name="text">
<string>Transform Image:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="transforms">
<item>
@@ -476,6 +472,13 @@
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="transform_label">
<property name="text">
<string>Transform Image:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -824,9 +827,14 @@
</widget>
<tabstops>
<tabstop>instruments</tabstop>
<tabstop>instr_settings</tabstop>
<tabstop>config_selection</tabstop>
<tabstop>config_settings</tabstop>
<tabstop>load_config</tabstop>
<tabstop>simple_detectors</tabstop>
<tabstop>simple_load</tabstop>
<tabstop>load_dark</tabstop>
<tabstop>accept_detector</tabstop>
<tabstop>detectors</tabstop>
<tabstop>load</tabstop>
<tabstop>transforms</tabstop>
@@ -837,8 +845,8 @@
<tabstop>line_color</tabstop>
<tabstop>line_style</tabstop>
<tabstop>line_size</tabstop>
<tabstop>cancel</tabstop>
<tabstop>complete</tabstop>
<tabstop>cancel</tabstop>
</tabstops>
<resources/>
<connections/>