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

Display QPI image data #146

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
159 changes: 116 additions & 43 deletions shapeout2/gui/quick_view/qv_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ def __init__(self, *args, **kwargs):
levels=(0, 255),
)

self.img_info = {
"image": {"view_event": self.imageView_image,
"view_poly": self.imageView_image_poly,
"cmap": pg.colormap.get('CET-L1')},
"qpi_pha": {"view_event": self.imageView_image_pha,
"view_poly": self.imageView_image_poly_pha,
"cmap": pg.colormap.get('CET-D1A')},
"qpi_amp": {
"view_event": self.imageView_image_amp,
"view_poly": self.imageView_image_poly_amp,
"cmap": pg.colormap.get('CET-L1')},
}

# set initial empty dataset
self._rtdc_ds = None
self.slot = None
Expand Down Expand Up @@ -201,7 +214,7 @@ def __setstate__(self, state):
self.checkBox_image_contour.setChecked(event["image contour"])
self.checkBox_image_zoom.setChecked(event["image zoom"])
self.checkBox_image_background.setChecked(
event["image background"])
event["image background"])
self.spinBox_event.setValue(event["index"])
self.checkBox_trace_raw.setChecked(event["trace raw"])
self.checkBox_trace_legend.setChecked(event["trace legend"])
Expand Down Expand Up @@ -250,28 +263,47 @@ def rtdc_ds(self, rtdc_ds):
contains_bg_feat = "image_bg" in rtdc_ds
self.checkBox_image_background.setVisible(contains_bg_feat)

def get_event_image(self, ds, event):
def get_event_image(self, ds, event, feat):
state = self.__getstate__()
imkw = self.imkw.copy()
cellimg = ds["image"][event]
# apply background correction
if "image_bg" in ds:
if state["event"]["image background"]:
bgimg = ds["image_bg"][event].astype(np.int16)
cellimg = cellimg.astype(np.int16)
cellimg = cellimg - bgimg + int(np.mean(bgimg))
# automatic contrast
if state["event"]["image auto contrast"]:
vmin, vmax = cellimg.min(), cellimg.max()
cellimg = (cellimg - vmin) / (vmax - vmin) * 255
# convert to RGB
cellimg = cellimg.reshape(
cellimg.shape[0], cellimg.shape[1], 1)
cellimg = np.repeat(cellimg, 3, axis=2)
# clip and convert to int
cellimg = np.clip(cellimg, 0, 255)
cellimg = np.require(cellimg, np.uint8, 'C')
cellimg = ds[feat][event]

if feat == "image":
# apply background correction
if "image_bg" in ds:
if state["event"]["image background"]:
bgimg = ds["image_bg"][event].astype(np.int16)
cellimg = cellimg.astype(np.int16)
cellimg = cellimg - bgimg + int(np.mean(bgimg))
# automatic contrast
if state["event"]["image auto contrast"]:
vmin, vmax = cellimg.min(), cellimg.max()
cellimg = (cellimg - vmin) / (vmax - vmin) * 255
elif "qpi" in feat:
if "qpi_pha" in feat:
imkw["levels"] = (-3, 3)
elif "qpi_amp" in feat:
imkw["levels"] = (0, 2)
else:
raise ValueError(f"Options for `feat` are 'image', "
f"'qpi_pha' and 'qpi_amp', got {feat}")

PinkShnack marked this conversation as resolved.
Show resolved Hide resolved
if feat == "image":
# convert to RGB
cellimg = cellimg.reshape(
cellimg.shape[0], cellimg.shape[1], 1)
cellimg = np.repeat(cellimg, 3, axis=2)

# clip and convert to int
cellimg = np.clip(cellimg, 0, 255)
cellimg = np.require(cellimg, np.uint8, 'C')

cellimg = self.display_contour(ds, event, state, cellimg, feat, imkw)

return cellimg, imkw

@staticmethod
def display_contour(ds, event, state, cellimg, feat, imkw):
# Only load contour data if there is an image column.
# We don't know how big the images should be so we
# might run into trouble displaying random contours.
Expand All @@ -283,9 +315,18 @@ def get_event_image(self, ds, event):
# https://github.com/DC-analysis/dclab/issues/76
cont = mask ^ binary_erosion(mask)
# set red contour pixel values in original image
cellimg[cont, 0] = int(255*.7)
cellimg[cont, 1] = 0
cellimg[cont, 2] = 0
red_pix = ((imkw["levels"][1] - imkw["levels"][0])
* 0.7) - np.abs(imkw["levels"][0])
if feat == "image":
cellimg[cont, 0] = int(
red_pix) if imkw["levels"][1] == 255 else red_pix
cellimg[cont, 1] = int(imkw["levels"][0])
cellimg[cont, 2] = int(imkw["levels"][0])
else:
# just 2D images (not RGB)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a work in progress (probably going to follow your idea of having specific methods for contour handling).

cellimg[cont] = int(
red_pix) if imkw["levels"][1] == 255 else red_pix

if state["event"]["image zoom"]:
xv, yv = np.where(mask)
idminx = xv.min() - 5
Expand All @@ -298,7 +339,7 @@ def get_event_image(self, ds, event):
idmaxx = idmaxx if idmaxx < shx else shx
idmaxy = idmaxy if idmaxy < shy else shy
cellimg = cellimg[idminx:idmaxx, idminy:idmaxy]
return cellimg, imkw
return cellimg

def get_statistics(self):
if self.rtdc_ds is not None:
Expand Down Expand Up @@ -334,25 +375,43 @@ def on_event_scatter_clicked(self, plot, point):
ds_idx = np.where(plotted)[0][point.index()]
self.show_event(ds_idx)

def display_img(self, feat, view, cellimg, **imkw):
self.img_info[feat][view].setImage(cellimg, **imkw)
self.img_info[feat][view].setColorMap(self.img_info[feat]["cmap"])
self.img_info[feat][view].show()

def on_event_scatter_hover(self, pos):
"""Update the image view in the polygon widget """
if self.rtdc_ds is not None and self.toolButton_poly.isChecked():
ds = self.rtdc_ds
# plotted events
plotted = self.widget_scatter.events_plotted
spos = self.widget_scatter.scatter.mapFromView(pos)
point = self.widget_scatter.scatter.pointAt(spos)
# get corrected index
event = np.where(plotted)[0][point.index()]
if "image" in self.rtdc_ds:
try:
cellimg, imkw = self.get_event_image(self.rtdc_ds, event)
except IndexError:
# the plot got updated, and we still have the old data
cellimg, imkw = self.get_event_image(self.rtdc_ds, 0)

view = "view_poly"
for key in self.img_info.keys():
self.img_info[key][view].hide()

try:
# if we have qpi data, image might be a different shape
if "qpi_pha" in ds:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there should be a separate class entirely that handles image plotting? Generalization is not easy due to the different data types and requirements for visualization.

I think a simple method works for our use-case for image display. Could even make this into a method, seeing as we use it twice.

cellimg, imkw = self.get_event_image(ds, event, "qpi_pha")
self.display_img("qpi_pha", view, cellimg, **imkw)
if "qpi_amp" in ds:
cellimg, imkw = self.get_event_image(ds, event, "qpi_amp")
self.display_img("qpi_amp", view, cellimg, **imkw)
elif "image" in ds:
cellimg, imkw = self.get_event_image(ds, event, "image")
self.display_img("image", view, cellimg, **imkw)

except IndexError:
# the plot got updated, and we still have the old data
cellimg, imkw = self.get_event_image(self.rtdc_ds, 0, "image")
self.imageView_image_poly.setImage(cellimg, **imkw)
self.imageView_image_poly.show()
else:
self.imageView_image_poly.hide()
self.display_img("image", view, cellimg, **imkw)

def on_event_scatter_spin(self, event):
"""Sping control for event selection changed"""
Expand Down Expand Up @@ -494,8 +553,8 @@ def on_tool(self, collapse=False):
else:
# keep everything as-is but update the sizes
show_event = self.stackedWidget.currentWidget() is self.page_event
show_settings = self.stackedWidget.currentWidget() \
is self.page_settings
show_settings = (
self.stackedWidget.currentWidget() is self.page_settings)
show_poly = self.stackedWidget.currentWidget() is self.page_poly

# toolbutton checked
Expand Down Expand Up @@ -606,12 +665,25 @@ def show_event(self, event):
if self.tabWidget_event.currentIndex() == 0:
# update image
state = self.__getstate__()
if "image" in ds:
cellimg, imkw = self.get_event_image(ds, event)
self.imageView_image.setImage(cellimg, **imkw)
self.groupBox_image.show()
else:
self.groupBox_image.hide()
self.groupBox_image.hide()

view = "view_event"
for key in self.img_info.keys():
self.img_info[key][view].hide()

# if we have qpi data, image might be a different shape
if "qpi_pha" in ds:
cellimg, imkw = self.get_event_image(ds, event, "qpi_pha")
self.display_img("qpi_pha", view, cellimg, **imkw)
if "qpi_amp" in ds:
cellimg, imkw = self.get_event_image(ds, event, "qpi_amp")
self.display_img("qpi_amp", view, cellimg, **imkw)
elif "image" in ds:
cellimg, imkw = self.get_event_image(ds, event, "image")
self.display_img("image", view, cellimg, **imkw)

self.groupBox_image.show()

if "trace" in ds:
# remove legend items
for item in reversed(self.legend_trace.items):
Expand Down Expand Up @@ -650,8 +722,9 @@ def show_event(self, event):
range_t[1] = flpos + 1.5 * flwidth
range_t[2] = flmax
# set legend name
ln = "{} {}".format(self.slot.fl_name_dict[
"FL-{}".format(key[2])], key[4:])
ln = "{} {}".format(
self.slot.fl_name_dict[
"FL-{}".format(key[2])], key[4:])
self.legend_trace.addItem(self.trace_plots[key], ln)
self.legend_trace.update()
else:
Expand Down
126 changes: 108 additions & 18 deletions shapeout2/gui/quick_view/qv_main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -714,17 +714,53 @@
</layout>
</item>
<item>
<widget class="SimpleImageView" name="imageView_image">
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
<layout class="QVBoxLayout" name="verticalLayout_11">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<property name="styleSheet">
<string notr="true">background-color:transparent</string>
<property name="bottomMargin">
<number>0</number>
</property>
</widget>
<item>
<widget class="SimpleImageView" name="imageView_image">
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:transparent</string>
</property>
</widget>
</item>
<item>
<widget class="SimpleImageView" name="imageView_image_pha">
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:transparent</string>
</property>
</widget>
</item>
<item>
<widget class="SimpleImageView" name="imageView_image_amp">
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:transparent</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
Expand Down Expand Up @@ -1039,17 +1075,71 @@
</widget>
</item>
<item>
<widget class="SimpleImageView" name="imageView_image_poly">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<layout class="QVBoxLayout" name="verticalLayout_15">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<property name="styleSheet">
<string notr="true">opacity: 0</string>
<property name="bottomMargin">
<number>0</number>
</property>
</widget>
<item>
<widget class="SimpleImageView" name="imageView_image_poly">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">opacity: 0</string>
</property>
</widget>
</item>
<item>
<widget class="SimpleImageView" name="imageView_image_poly_pha">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">opacity: 0</string>
</property>
</widget>
</item>
<item>
<widget class="SimpleImageView" name="imageView_image_poly_amp">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">opacity: 0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
Expand Down