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

IMAQdxCamera: optional autoscaling, and background subtraction #87

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 45 additions & 0 deletions labscript_devices/IMAQdxCamera/blacs_tab.ui
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,51 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonAutoLevels">
<property name="toolTip">
<string>Auto scale image levels and histogram range</string>
</property>
<property name="text">
<string>Auto Levels</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/qtutils/fugue/gradient.png</normaloff>:/qtutils/fugue/gradient.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_set_bg">
<property name="toolTip">
<string>Set current image as the background, to be subtracted from displayed images</string>
</property>
<property name="text">
<string>Set background</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/qtutils/fugue/image--plus.png</normaloff>:/qtutils/fugue/image--plus.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_clear_bg">
<property name="toolTip">
<string>Clear the background image</string>
</property>
<property name="text">
<string>Clear background</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/qtutils/fugue/image--minus.png</normaloff>:/qtutils/fugue/image--minus.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_snap">
<property name="toolTip">
Expand Down
67 changes: 55 additions & 12 deletions labscript_devices/IMAQdxCamera/blacs_tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ class ImageReceiver(ZMQServer):
"""ZMQServer that receives images on a zmq.REP socket, replies 'ok', and updates the
image widget and fps indicator"""

def __init__(self, image_view, label_fps):
def __init__(self, image_view, label_fps, autolevels_button):
ZMQServer.__init__(self, port=None, dtype='multipart')
self.image_view = image_view
self.label_fps = label_fps
self.last_frame_time = None
self.frame_rate = None
self.update_event = None
self.image = None
self.bg_image = None
self.autolevels_button = autolevels_button

@inmain_decorator(wait_for_return=True)
def handler(self, data):
Expand All @@ -80,14 +82,10 @@ def handler(self, data):
else:
self.frame_rate = 1 / dt
self.last_frame_time = this_frame_time
if self.image_view.image is None:
# First time setting an image. Do autoscaling etc:
self.image_view.setImage(image.swapaxes(-1, -2))
else:
# Updating image. Keep zoom/pan/levels/etc settings.
self.image_view.setImage(
image.swapaxes(-1, -2), autoRange=False, autoLevels=False
)

self.image = image
self.update_image()

# Update fps indicator:
if self.frame_rate is not None:
self.label_fps.setText(f"{self.frame_rate:.01f} fps")
Expand All @@ -105,6 +103,34 @@ def handler(self, data):
QtGui.QApplication.instance().sendPostedEvents()
return self.NO_RESPONSE

@inmain_decorator(wait_for_return=True)
def update_image(self):
image = self.image
if self.bg_image is not None:
image = self.image.astype(float) - self.bg_image.astype(float)
autolevels = self.autolevels_button.isChecked()
if self.image_view.image is None:
# First time setting an image. Do autoscaling etc:
self.image_view.setImage(image.swapaxes(-1, -2))
else:
self.image_view.setImage(
image.swapaxes(-1, -2),
autoRange=False,
autoLevels=autolevels,
autoHistogramRange=autolevels,
)

@inmain_decorator()
def set_bg_image(self):
self.bg_image = self.image
self.update_image()

@inmain_decorator()
def clear_bg_image(self):
self.bg_image = None
self.update_image()



class IMAQdxCameraTab(DeviceTab):
# Subclasses may override this if all they do is replace the worker class with a
Expand All @@ -128,6 +154,8 @@ def initialise_GUI(self):
self.ui.pushButton_snap.clicked.connect(self.on_snap_clicked)
self.ui.pushButton_attributes.clicked.connect(self.on_attributes_clicked)
self.ui.toolButton_nomax.clicked.connect(self.on_reset_rate_clicked)
self.ui.pushButton_set_bg.clicked.connect(self.on_set_bg_clicked)
self.ui.pushButton_clear_bg.clicked.connect(self.on_clear_bg_clicked)

self.attributes_dialog = UiLoader().load(attributes_ui_filepath)
self.attributes_dialog.setParent(self.ui.parent())
Expand All @@ -146,6 +174,7 @@ def initialise_GUI(self):
)
self.ui.horizontalLayout.addWidget(self.image)
self.ui.pushButton_stop.hide()
self.ui.pushButton_clear_bg.hide()
self.ui.doubleSpinBox_maxrate.hide()
self.ui.toolButton_nomax.hide()
self.ui.label_fps.hide()
Expand All @@ -163,7 +192,9 @@ def initialise_GUI(self):
widget.setSizePolicy(size_policy)

# Start the image receiver ZMQ server:
self.image_receiver = ImageReceiver(self.image, self.ui.label_fps)
self.image_receiver = ImageReceiver(
self.image, self.ui.label_fps, self.ui.pushButtonAutoLevels
)
self.acquiring = False

self.supports_smart_programming(self.use_smart_programming)
Expand All @@ -173,7 +204,8 @@ def get_save_data(self):
'attribute_visibility': self.attributes_dialog.comboBox.currentText(),
'acquiring': self.acquiring,
'max_rate': self.ui.doubleSpinBox_maxrate.value(),
'colormap': repr(self.image.ui.histogram.gradient.saveState())
'colormap': repr(self.image.ui.histogram.gradient.saveState()),
'autolevels': self.ui.pushButtonAutoLevels.isChecked(),
}

def restore_save_data(self, save_data):
Expand All @@ -188,6 +220,7 @@ def restore_save_data(self, save_data):
self.image.ui.histogram.gradient.restoreState(
ast.literal_eval(save_data['colormap'])
)
self.ui.pushButtonAutoLevels.setChecked(save_data.get('autolevels', False))


def initialise_workers(self):
Expand Down Expand Up @@ -259,6 +292,16 @@ def on_stop_clicked(self, button):
self.acquiring = False
self.stop_continuous()

def on_set_bg_clicked(self, button):
self.image_receiver.set_bg_image()
self.ui.pushButton_set_bg.hide()
self.ui.pushButton_clear_bg.show()

def on_clear_bg_clicked(self, button):
self.image_receiver.clear_bg_image()
self.ui.pushButton_set_bg.show()
self.ui.pushButton_clear_bg.hide()

def on_copy_clicked(self, button):
text = self.attributes_dialog.plainTextEdit.toPlainText()
clipboard = QtGui.QApplication.instance().clipboard()
Expand Down