Skip to content

Commit

Permalink
feat: crop frames in dataprep
Browse files Browse the repository at this point in the history
  • Loading branch information
ElpadoCan committed Mar 7, 2025
1 parent a4ab219 commit cf1505e
Show file tree
Hide file tree
Showing 11 changed files with 1,540 additions and 849 deletions.
1 change: 1 addition & 0 deletions cellacdc/acdc_bioio_bioformats/reader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

from .. import printl
from . import install, EXTENSION_PACKAGE_MAPPER

def set_reader(image_filepath, **kwargs):
Expand Down
113 changes: 112 additions & 1 deletion cellacdc/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5629,8 +5629,8 @@ def __init__(
self.lowerZscrollbar.label = QLabel(f'{s}/{SizeZ}')

self.upperZscrollbar = QScrollBar(Qt.Horizontal)
self.upperZscrollbar.setValue(SizeZ-1)
self.upperZscrollbar.setMaximum(SizeZ-1)
self.upperZscrollbar.setValue(SizeZ-1)
self.upperZscrollbar.label = QLabel(f'{SizeZ}/{SizeZ}')

cancelButton = widgets.cancelPushButton('Cancel')
Expand Down Expand Up @@ -16429,3 +16429,114 @@ def steps(self, return_keepInputDataType=False):

keep_input_dtype = self.keepInputDataTypeToggle.isChecked()
return steps, keep_input_dtype # why does this not work???s

class QCropTrangeTool(QBaseDialog):
sigClose = Signal()
sigTvalueChanged = Signal(int)
sigReset = Signal()
sigCrop = Signal(int, int)

def __init__(
self, SizeT,
cropButtonText='Apply crop',
parent=None,
addDoNotShowAgain=False,
title='Select frames range'
):
super().__init__(parent)

self.cancel = True

self.setWindowFlags(Qt.Tool | Qt.WindowStaysOnTopHint)

self.SizeT = SizeT
self.numDigits = len(str(self.SizeT))

self.setWindowTitle(title)

layout = QGridLayout()
buttonsLayout = QHBoxLayout()

self.startFrameScrollbar = QScrollBar(Qt.Horizontal)
self.startFrameScrollbar.setMaximum(SizeT-1)
t = str(1).zfill(self.numDigits)
self.startFrameScrollbar.label = QLabel(f'{t}/{SizeT}')

self.endFrameScrollbar = QScrollBar(Qt.Horizontal)
self.endFrameScrollbar.setMaximum(SizeT-1)
self.endFrameScrollbar.setValue(SizeT-1)
self.endFrameScrollbar.label = QLabel(f'{SizeT}/{SizeT}')

cancelButton = widgets.cancelPushButton('Cancel')
cropButton = widgets.okPushButton(cropButtonText)
buttonsLayout.addWidget(cropButton)
buttonsLayout.addWidget(cancelButton)

row = 0
layout.addWidget(
QLabel('Start frame n. '), row, 0, alignment=Qt.AlignRight
)
layout.addWidget(
self.startFrameScrollbar.label, row, 1, alignment=Qt.AlignRight
)
layout.addWidget(self.startFrameScrollbar, row, 2)

row += 1
layout.setRowStretch(row, 5)

row += 1
layout.addWidget(
QLabel('Stop frame n. '), row, 0, alignment=Qt.AlignRight
)
layout.addWidget(
self.endFrameScrollbar.label, row, 1, alignment=Qt.AlignRight
)
layout.addWidget(self.endFrameScrollbar, row, 2)

row += 1
if addDoNotShowAgain:
self.doNotShowAgainCheckbox = QCheckBox('Do not ask again')
layout.addWidget(
self.doNotShowAgainCheckbox, row, 2, alignment=Qt.AlignLeft
)
row += 1

layout.addLayout(buttonsLayout, row, 2, alignment=Qt.AlignRight)

layout.setColumnStretch(0, 0)
layout.setColumnStretch(1, 0)
layout.setColumnStretch(2, 10)

self.setLayout(layout)

# resetButton.clicked.connect(self.emitReset)
cropButton.clicked.connect(self.emitCrop)
cancelButton.clicked.connect(self.close)
self.startFrameScrollbar.valueChanged.connect(self.TvalueChanged)
self.endFrameScrollbar.valueChanged.connect(self.TvalueChanged)

def emitReset(self):
self.sigReset.emit()

def emitCrop(self):
self.cancel = False
low_z = self.startFrameScrollbar.value()
high_z = self.endFrameScrollbar.value()
self.sigCrop.emit(low_z, high_z)
self.close()

def updateScrollbars(self, start_frame_i, lower_frame_i):
self.startFrameScrollbar.setValue(start_frame_i)
self.endFrameScrollbar.setValue(lower_frame_i)

def TvalueChanged(self, value):
t = str(value+1).zfill(self.numDigits)
self.sender().label.setText(f'{t}/{self.SizeT}')
self.sigTvalueChanged.emit(value)

def showEvent(self, event):
self.resize(int(self.width()*1.5), self.height())

def closeEvent(self, event):
super().closeEvent(event)
self.sigClose.emit()
80 changes: 78 additions & 2 deletions cellacdc/dataPrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,20 @@ def gui_createActions(self):
self.cropZaction.setEnabled(False)
self.cropZaction.setCheckable(True)

self.cropTaction = QAction(
QIcon(":cropT.svg"), "Crop frames (time points)", self
)
self.cropTaction.setToolTip(
'Crop a specified time range.\n\n'
'If the button is disabled you need to click on the Start button '
'first.\n\n'
'USAGE: Click this button, adjust the start and end frame numbers '
'and click on "Apply crop" to activate the save button.\n\n'
'To save the cropped data click the Save button.'
)
self.cropTaction.setEnabled(False)
self.cropTaction.setCheckable(True)

self.saveAction = QAction(
QIcon(":file-save.svg"), "Crop and save", self)
self.saveAction.setEnabled(False)
Expand Down Expand Up @@ -297,6 +311,7 @@ def gui_createToolBars(self):
fileToolBar.addAction(self.startAction)
fileToolBar.addAction(self.cropAction)
fileToolBar.addAction(self.cropZaction)
fileToolBar.addAction(self.cropTaction)
fileToolBar.addAction(self.saveAction)

navigateToolbar = QToolBar("Navigate", self)
Expand Down Expand Up @@ -339,6 +354,7 @@ def gui_connectActions(self):
self.cropAction.triggered.connect(self.applyCropYX)
self.saveAction.triggered.connect(self.saveActionTriggered)
self.cropZaction.toggled.connect(self.openCropZtool)
self.cropTaction.toggled.connect(self.openCropTtool)
self.startAction.triggered.connect(self.prepData)
self.interpAction.triggered.connect(self.interp_z)
self.ZbackAction.triggered.connect(self.useSameZ_fromHereBack)
Expand Down Expand Up @@ -674,6 +690,8 @@ def init_attr(self):
self.navigateScrollbar.valueChanged.connect(
self.navigateScrollbarValueChanged
)
self.startFrameIdxCrop = 0
self.endFrameIdxCrop = None

def navigateScrollbarValueChanged(self, value):
if self.num_pos > 1:
Expand Down Expand Up @@ -715,6 +733,12 @@ def crop(self, data, posData, cropROI):
elif croppedData.ndim == 3:
croppedData = croppedData[lower_z:upper_z+1]
SizeZ = (upper_z-lower_z)+1

if posData.SizeT > 1:
croppedData = croppedData[
self.startFrameIdxCrop:self.endFrameIdxCrop
]

return croppedData, SizeZ

def saveBkgrROIs(self, posData):
Expand Down Expand Up @@ -1232,14 +1256,12 @@ def saveROIcoords(self, doCrop, posData):
def openCropZtool(self, checked):
posData = self.data[self.pos_i]
if checked:
self.zSliceToRestore = self.zSliceScrollBar.value()
self.cropZtool = apps.QCropZtool(posData.SizeZ, parent=self)
self.cropZtool.sigClose.connect(self.cropZtoolClosed)
self.cropZtool.sigZvalueChanged.connect(self.cropZtoolvalueChanged)
self.cropZtool.sigCrop.connect(self.applyCropZslices)
self.cropZtool.show()
else:
self.zSliceToRestore = None
self.cropZtool.close()
self.cropZtool = None
# Restore original z-slice
Expand All @@ -1248,6 +1270,18 @@ def openCropZtool(self, checked):
z = posData.segmInfo_df.at[idx, 'z_slice_used_dataPrep']
self.zSliceScrollBar.setValue(z)

def openCropTtool(self, checked):
posData = self.data[self.pos_i]
if checked:
self.cropTtool = apps.QCropTrangeTool(posData.SizeT, parent=self)
self.cropTtool.sigClose.connect(self.cropTtoolClosed)
self.cropTtool.sigTvalueChanged.connect(self.cropTtoolvalueChanged)
self.cropTtool.sigCrop.connect(self.applyCropTrange)
self.cropTtool.show()
else:
self.cropZtool.close()
self.cropZtool = None

def cropZtoolvalueChanged(self, whichZ, z):
self.zSliceScrollBar.valueChanged.disconnect()
self.zSliceScrollBar.setValue(z)
Expand Down Expand Up @@ -1292,6 +1326,43 @@ def cropZtoolClosed(self):
self.cropZaction.toggled.disconnect()
self.cropZaction.setChecked(False)
self.cropZaction.toggled.connect(self.openCropZtool)

def cropTtoolClosed(self):
self.cropTtool = None
self.cropTaction.toggled.disconnect()
self.cropTaction.setChecked(False)
self.cropTaction.toggled.connect(self.openCropTtool)

def cropTtoolvalueChanged(self, frame_i):
self.navigateScrollbar.setValue(frame_i+1)

def applyCropTrange(self, start_frame_i, end_frame_i):
self.startFrameIdxCrop = start_frame_i
self.endFrameIdxCrop = end_frame_i + 1
self.logger.info(
f'Previewing cropped frames ({start_frame_i+1},{end_frame_i+1})...'
)
for posData in self.data:
posData.img_data[:start_frame_i] = 0
posData.img_data[end_frame_i+1:] = 0

self.update_img()
note_text = (
f'Done. Frames outside of the range ({start_frame_i+1},{end_frame_i+1}) '
'will appear black now. To save cropped data, click on the "Save" '
'button on the top toolbar.'
)
self.logger.info(note_text)

txt = html_utils.paragraph(f"""
Cropping frames applied.<br><br>
Note that this is just a preview where the frames outside of the
range ({start_frame_i+1},{end_frame_i+1}) will look black.<br><br>
<b>To save cropped data</b>, click on the <code>Save cropped data</code>
button on the top toolbar.
""")
msg = widgets.myMessageBox(wrapText=False)
msg.information(self, 'Preview cropped frames', txt)

def getCroppedData(self, askCropping=True):
for p, posData in enumerate(self.data):
Expand Down Expand Up @@ -1484,12 +1555,17 @@ def setEnabledCropActions(self, enabled):
self.cropAction.setEnabled(enabled)
self.cropZaction.setEnabled(enabled)
self.saveAction.setEnabled(enabled)
self.cropTaction.setEnabled(enabled)

if not hasattr(self, 'data'):
return

posData = self.data[self.pos_i]
if posData.SizeZ == 1:
self.cropZaction.setEnabled(False)

if posData.SizeT == 1:
self.cropTaction.setEnabled(False)

def removeAllHandles(self, roi):
for handle in roi.handles:
Expand Down
16 changes: 14 additions & 2 deletions cellacdc/dataStruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,10 +630,20 @@ def saveImgDataChannel(
)
filePath = os.path.join(images_path, filename)
dimsIdx = {'c': ch_idx}
numFrames = len(framesRange)
num_imgs = numFrames*SizeZ
pbar = tqdm(
total=num_imgs,
ncols=100,
desc=f'Reading image (z 0/{SizeZ}, t 0/{numFrames})'
)
for t in framesRange:
imgData_z = []
dimsIdx['t'] = t
for z in range(SizeZ):
pbar.set_description(
f'Reading image (z {z+1}/{SizeZ}, t {t+1}/{numFrames})'
)
dimsIdx['z'] = z
if self.rawDataStruct != 2:
idx = self.getIndex(idxs, dimsIdx, self.DimensionOrder)
Expand All @@ -647,13 +657,15 @@ def saveImgDataChannel(
imgData_ch[t, z] = imgData
else:
imgData_z.append(imgData)

pbar.update()

if not self.to_h5:
imgData_z = np.squeeze(np.array(imgData_z, dtype=imgData.dtype))
imgData_ch.append(imgData_z)

pbar.close()

if not self.to_h5:

imgData_ch = np.squeeze(np.array(imgData_ch, dtype=imgData.dtype))
myutils.to_tiff(
filePath, imgData_ch,
Expand Down
6 changes: 3 additions & 3 deletions cellacdc/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ def save_image_data(filepath, img_data):
myutils.to_tiff(filepath, img_data)
return np.squeeze(img_data)

def savez_compressed(filepath, data, safe=True):
def savez_compressed(filepath, *args, safe=True, **kwargs):
if not os.path.exists(filepath):
np.savez_compressed(filepath, data)
np.savez_compressed(filepath, *args, **kwargs)
return

temp_filepath = filepath.replace('.npz', '.new.npz')
np.savez_compressed(temp_filepath, data)
np.savez_compressed(temp_filepath, *args, **kwargs)
os.replace(temp_filepath, filepath)
Loading

0 comments on commit cf1505e

Please sign in to comment.