def plot(self): seq = self.getData('input')['seq'] fig = plt.figure(figsize=(7, 10)) f11, f12, f13 = fig.add_subplot(611), fig.add_subplot( 612), fig.add_subplot(613) f2 = [fig.add_subplot(614), fig.add_subplot(615), fig.add_subplot(616)] t0, time_range = 0, [0, np.inf] for iB in range(1, len(seq.block_events)): block = seq.get_block(iB) is_valid = time_range[0] <= t0 <= time_range[1] if is_valid: if block is not None: if 'adc' in block: adc = block['adc'] t = adc.delay + [ (x * adc.dwell) for x in range(0, int(adc.num_samples)) ] f11.plot((t0 + t), np.zeros(len(t))) if 'rf' in block: rf = block['rf'] t = rf.t f12.plot(np.squeeze(t0 + t), abs(rf.signal)) f13.plot(np.squeeze(t0 + t), np.angle(rf.signal)) grad_channels = ['gx', 'gy', 'gz'] for x in range(0, len(grad_channels)): if grad_channels[x] in block: grad = block[grad_channels[x]] if grad.type == 'grad': t = grad.t waveform = 1e-3 * grad.waveform else: t = np.cumsum([ 0, grad.rise_time, grad.flat_time, grad.fall_time ]) waveform = [ 1e-3 * grad.amplitude * x for x in [0, 1, 1, 0] ] f2[x].plot(np.squeeze(t0 + t), waveform) t0 += core.calcduration.calcduration(block) f11.set_ylabel('adc') f12.set_ylabel('rf mag hz') f13.set_ylabel('rf phase rad') [f2[x].set_ylabel(grad_channels[x]) for x in range(3)] # Save the plot temporarily as a PNG file to be displayed by the DisplayBox Widget plt.savefig('plot_temp.png') img = QtGui.QImage() img.load('plot_temp.png') self.setAttr('Sequence Plot', val=img) os.remove('plot_temp.png')
def compute(self): import numpy as np edgeval = self.getVal('edge') # make a copy for changes if self.getVal('LeftRight'): outright = self.getData('inleft') outleft = self.getData('inright') else: outleft = self.getData('inleft') outright = self.getData('inright') if self.getVal('Transition') == 0: # Toggle out = outleft elif self.getVal('Transition') == 1: # Fade cr = 0.01 * float(self.getVal('edge')) cl = 1. - cr out = cl * outleft.astype(np.float) + cr * outright.astype( np.float) elif self.getVal('Transition') == 2: # Horizontal out = np.append(outleft[:edgeval, :, :], outright[edgeval:, :, :], axis=0) elif self.getVal('Transition') == 3: # Vertical out = np.append(outleft[:, :edgeval, :], outright[:, edgeval:, :], axis=1) elif self.getVal('Transition') == 4: # Color out = np.copy(outleft) if self.getVal('edge') <= 1 or self.getVal('edge') == 5: out[:, :, 2] = outright[:, :, 2] # Red if self.getVal('edge') >= 1 and self.getVal('edge') <= 3: out[:, :, 1] = outright[:, :, 1] # Green if self.getVal('edge') >= 3: out[:, :, 0] = outright[:, :, 0] # Blue image1 = out.astype(np.uint8) h, w = out.shape[:2] format_ = QtGui.QImage.Format_RGB32 image = QtGui.QImage(image1.data, w, h, format_) image.ndarray = image1 if image.isNull(): self.log.warn("Image Viewer: cannot load image") self.setAttr('Viewport:', val=image) self.setData('out', image1) return 0
def about(self): # Display image = QtGui.QImage(LOGO_PATH) dispbox = DisplayBox('') dispbox.set_pixmap(QtGui.QPixmap.fromImage(image)) dispbox.set_scale(0.157) dispbox.set_interp(True) # Text tab = ' ' txt = '<center><font size=4><b>Graphical Programming Interface (GPI)</b></font><br><b><a href=http://gpilab.com>gpilab.com</a></b><br><br>' +\ 'Nicholas Zwart<br>Barrow Neurological Institute<br>Phoenix, Arizona' +\ '<br><br>' +\ tab + 'Release: '+ str(VERSION) + ' (' + str(RELEASE_DATE) +')' +\ '<p>'+\ 'GPI is a graphical development environment designed for rapid prototyping '+\ 'of numeric algorithms.'+\ '</p>'+\ '<p>'+\ 'GPI development is sponsored by Philips Healthcare.'+\ '</p></center>' txtbox = TextBox('') txtbox.set_val(txt) txtbox.set_wordwrap(True) txtbox.set_openExternalLinks(True) # set layout wdgvbox = QtGui.QVBoxLayout() wdgvbox.addWidget(dispbox) wdgvbox.addWidget(txtbox) wdgvbox.setStretch(0, 2) # set master widget self.aboutWdg = QtGui.QWidget() self.aboutWdg.setLayout(wdgvbox) # self.aboutWdg.setSizePolicy(QtGui.QSizePolicy.Minimum, \ # QtGui.QSizePolicy.Preferred) # self.aboutWdg.setMaximumWidth(420) self.aboutWdg.show() self.aboutWdg.raise_() log.debug(str(dispbox.sizeHint()))
def compute(self): from matplotlib import cm # make a copy for changes data = self.getData('in').copy() # get extra dimension parameters and modify data dimfunc = self.getVal('Extra Dimension') dimval = self.getVal('Slice/Tile Dimension') if data.ndim == 3 and dimfunc < 2: if dimfunc == 0: # slice data slval = self.getVal('Slice') - 1 if dimval == 0: data = data[slval, ...] elif dimval == 1: data = data[:, slval, :] else: data = data[..., slval] else: # tile data ncol = self.getVal('# Columns') nrow = self.getVal('# Rows') # add some blank tiles data = np.rollaxis(data, dimval) N, xres, yres = data.shape N_new = ncol * nrow pad_vals = ((0, N_new - N), (0, 0), (0, 0)) data = np.pad(data, pad_vals, mode='constant') # from http://stackoverflow.com/a/13990648/333308 data = np.reshape(data, (nrow, ncol, xres, yres)) data = np.swapaxes(data, 1, 2) data = np.reshape(data, (nrow * xres, ncol * yres)) # Read in parameters, make a little floor:ceiling adjustment gamma = self.getVal('Gamma') lval = self.getAttr('L W F C:', 'val') cval = self.getVal('Complex Display') if 'Complex Display' in self.widgetEvents(): if cval == 4: self.setAttr('Color Map', buttons=self.complex_cmaps, collapsed=self.getAttr('Color Map', 'collapsed'), val=0) # elif self.getAttr('Color Map', 'buttons') != self.real_cmaps: # there is no "get_buttons" method, so for now this will reset the # colormap whenever "Complex Display" is changed # this could/will be added in a future framework update else: self.setAttr('Color Map', buttons=self.real_cmaps, collapsed=self.getAttr('Color Map', 'collapsed'), val=0) cmap = self.getVal('Color Map') sval = self.getVal('Scalar Display') zval = self.getVal('Zero Ref') fval = self.getVal('Fix Range') rmin = self.getVal('Range Min') rmax = self.getVal('Range Max') flor = 0.01 * lval['floor'] ceil = 0.01 * lval['ceiling'] if ceil == flor: if ceil == 1.: flor = 0.999 else: ceil += 0.001 # SHOW COMPLEX DATA if np.iscomplexobj(data) and cval == 4: mag = np.abs(data) phase = np.angle(data, deg=True) # normalize the mag data_min = 0. if fval: data_max = rmax else: data_max = mag.max() self.setAttr('Range Max', val=data_max) data_range = data_max - data_min dmask = np.ones(data.shape) new_min = data_range * flor + data_min new_max = data_range * ceil + data_min mag = np.clip(mag, new_min, new_max) if new_max > new_min: if ( gamma == 1 ): # Put in check for gamma=1, the common use case, just to save time mag = (mag - new_min) / (new_max - new_min) else: mag = pow((mag - new_min) / (new_max - new_min), gamma) else: mag = np.ones(mag.shape) # ADD BORDERS edgpix = self.getVal('Edge Pixels') blkpix = self.getVal('Black Pixels') if (edgpix + blkpix) > 0: # new image will be h2 x w2 # frame defines edge pixels to paint with phase table h, w = mag.shape h2 = h + 2 * (edgpix + blkpix) w2 = w + 2 * (edgpix + blkpix) mag2 = np.zeros((h2, w2)) phase2 = np.zeros((h2, w2)) frame = np.zeros((h2, w2)) == 1 frame[0:edgpix, :] = frame[h2 - edgpix:h2, :] = True frame[:, 0:edgpix] = frame[:, w2 - edgpix:w2] = True mag2[edgpix + blkpix:edgpix + blkpix + h, edgpix + blkpix:edgpix + blkpix + w] = mag mag2[frame] = 1 phase2[edgpix + blkpix:edgpix + blkpix + h, edgpix + blkpix:edgpix + blkpix + w] = phase xloc = np.tile(np.linspace(-1., 1., w2), (h2, 1)) yloc = np.transpose(np.tile(np.linspace(1., -1., h2), (w2, 1))) phase2[frame] = np.degrees(np.arctan2(yloc[frame], xloc[frame])) mag = mag2 phase = phase2 # now colorize! if cmap == 0: # HSV phase_cmap = cm.hsv elif cmap == 1: # HSL try: import seaborn as sns except: self.log.warn( "Seaborn (required for HSL map) not available! Falling back on HSV." ) phase_cmap = cm.hsv else: # from http://stackoverflow.com/a/34557535/333308 import matplotlib.colors as col hlsmap = col.ListedColormap(sns.color_palette("hls", 256)) phase_cmap = hlsmap elif cmap == 2: #HUSL try: import seaborn as sns except: self.log.warn( "Seaborn (required for HUSL map) not available! Falling back on HSV." ) phase_cmap = cm.hsv else: # from http://stackoverflow.com/a/34557535/333308 import matplotlib.colors as col huslmap = col.ListedColormap(sns.color_palette( "husl", 256)) phase_cmap = huslmap elif cmap == 3: # coolwarm phase_cmap = cm.coolwarm mag_norm = mag phase_norm = (phase + 180) / 360 # phase shift to match old look better if cmap != 3: phase_norm = (phase_norm - 1 / 3) % 1 colorized = 255 * cm.gray(mag_norm) * phase_cmap(phase_norm) red = colorized[..., 0] green = colorized[..., 1] blue = colorized[..., 2] alpha = colorized[..., 3] # DISPLAY SCALAR DATA elif dimfunc != 2: if np.iscomplexobj(data): if cval == 0: # Real data = np.real(data) elif cval == 1: # Imag data = np.imag(data) elif cval == 2: # Mag data = np.abs(data) elif cval == 3: # Phase data = np.angle(data, deg=True) if sval == 1: # Mag data = np.abs(data) elif sval == 2: # Sign sign = np.sign(data) data = np.abs(data) # normalize the data if fval: data_min = rmin data_max = rmax else: data_min = data.min() data_max = data.max() if sval != 2: if zval == 1: data_min = 0. elif zval == 2: data_max = max(abs(data_min), abs(data_max)) data_min = -data_max elif zval == 3: data_max = 0. data_range = data_max - data_min self.setAttr('Range Min', val=data_min) self.setAttr('Range Max', val=data_max) else: data_min = 0. data_max = max(abs(data_min), abs(data_max)) data_range = data_max self.setAttr('Range Min', val=-data_range) self.setAttr('Range Max', val=data_range) dmask = np.ones(data.shape) new_min = data_range * flor + data_min new_max = data_range * ceil + data_min data = np.minimum(np.maximum(data, new_min * dmask), new_max * dmask) if new_max > new_min: if ( gamma == 1 ): # Put in check for gamma=1, the common use case, just to save time data = 255. * (data - new_min) / (new_max - new_min) else: data = 255. * pow( (data - new_min) / (new_max - new_min), gamma) else: data = 255. * np.ones(data.shape) if sval != 2: #Not Signed Data (Pass or Mag) # Show based on a color map if cmap == 0: # Grayscale red = green = blue = np.uint8(data) alpha = 255. * np.ones(blue.shape) else: rd = np.zeros(data.shape) gn = np.zeros(data.shape) be = np.zeros(data.shape) zmask = np.zeros(data.shape) fmask = np.ones(data.shape) if cmap == 1: # IceFire hue = 4. * (data / 256.) hindex0 = hue < 1. hindex1 = np.logical_and(hue >= 1., hue < 2.) hindex2 = np.logical_and(hue >= 2., hue < 3.) hindex3 = np.logical_and(hue >= 3., hue < 4.) be[hindex0] = hue[hindex0] gn[hindex0] = zmask[hindex0] rd[hindex0] = zmask[hindex0] gn[hindex1] = (hue - 1.)[hindex1] rd[hindex1] = (hue - 1.)[hindex1] be[hindex1] = fmask[hindex1] gn[hindex2] = fmask[hindex2] rd[hindex2] = fmask[hindex2] be[hindex2] = (3. - hue)[hindex2] rd[hindex3] = fmask[hindex3] gn[hindex3] = (4. - hue)[hindex3] be[hindex3] = zmask[hindex3] elif cmap == 2: # Fire hue = 4. * (data / 256.) hindex0 = hue < 1. hindex1 = np.logical_and(hue >= 1., hue < 2.) hindex2 = np.logical_and(hue >= 2., hue < 3.) hindex3 = np.logical_and(hue >= 3., hue < 4.) be[hindex0] = hue[hindex0] rd[hindex0] = zmask[hindex0] gn[hindex0] = zmask[hindex0] be[hindex1] = (2. - hue)[hindex1] rd[hindex1] = (hue - 1.)[hindex1] gn[hindex1] = zmask[hindex1] rd[hindex2] = fmask[hindex2] gn[hindex2] = (hue - 2.)[hindex2] be[hindex2] = zmask[hindex2] rd[hindex3] = fmask[hindex3] gn[hindex3] = fmask[hindex3] be[hindex3] = (hue - 3.)[hindex3] elif cmap == 3: # Hot hue = 3. * (data / 256.) hindex0 = hue < 1. hindex1 = np.logical_and(hue >= 1., hue < 2.) hindex2 = np.logical_and(hue >= 2., hue < 3.) rd[hindex0] = hue[hindex0] be[hindex0] = zmask[hindex0] gn[hindex0] = zmask[hindex0] gn[hindex1] = (hue - 1.)[hindex1] rd[hindex1] = fmask[hindex1] be[hindex1] = zmask[hindex1] rd[hindex2] = fmask[hindex2] gn[hindex2] = fmask[hindex2] be[hindex2] = (hue - 2.)[hindex2] if cmap == 4: # Hot2, from ASIST (http://asist.umin.jp/index-e.htm) rindex0 = data < 20.0 rindex1 = np.logical_and(data >= 20.0, data <= 100.0) rindex2 = np.logical_and(data > 100.0, data < 128.0) rindex3 = np.logical_and(data >= 128.0, data <= 191.0) rindex4 = data > 191.0 rd[rindex0] = data[rindex0] * 4.0 rd[rindex1] = 80.0 - (data[rindex1] - 20.0) rd[rindex3] = (data[rindex3] - 128.0) * 4.0 rd[rindex4] = data[rindex4] * 0.0 + 255.0 rd = rd / 255.0 gindex0 = data < 45.0 gindex1 = np.logical_and(data >= 45.0, data <= 130.0) gindex2 = np.logical_and(data > 130.0, data < 192.0) gindex3 = data >= 192.0 gn[gindex1] = (data[gindex1] - 45.0) * 3.0 gn[gindex2] = data[gindex2] * 0.0 + 255.0 gn[gindex3] = 252.0 - (data[gindex3] - 192.0) * 4.0 gn = gn / 255.0 bindex0 = (data < 1.0) bindex1 = np.logical_and(data >= 1.0, data < 86.0) bindex2 = np.logical_and(data >= 86.0, data <= 137.0) bindex3 = data > 137.0 be[bindex1] = (data[bindex1] - 1.0) * 3.0 be[bindex2] = 255.0 - (data[bindex2] - 86.0) * 5.0 be = be / 255.0 elif cmap == 5: # BGR hue = 4. * (data / 256.) hindex0 = hue < 1. hindex1 = np.logical_and(hue >= 1., hue < 2.) hindex2 = np.logical_and(hue >= 2., hue < 3.) hindex3 = np.logical_and(hue >= 3., hue < 4.) be[hindex0] = hue[hindex0] gn[hindex0] = zmask[hindex0] rd[hindex0] = zmask[hindex0] gn[hindex1] = (hue - 1.)[hindex1] rd[hindex1] = zmask[hindex1] be[hindex1] = fmask[hindex1] gn[hindex2] = fmask[hindex2] rd[hindex2] = (hue - 2.)[hindex2] be[hindex2] = (3. - hue)[hindex2] rd[hindex3] = fmask[hindex3] gn[hindex3] = (4. - hue)[hindex3] be[hindex3] = zmask[hindex3] blue = np.uint8(255. * rd) red = np.uint8(255. * be) green = np.uint8(255. * gn) alpha = np.uint8(255. * np.ones(blue.shape)) else: #Signed data, positive numbers green, negative numbers magenta red = np.zeros(data.shape) green = np.zeros(data.shape) blue = np.zeros(data.shape) red[sign <= 0] = data[sign <= 0] blue[sign <= 0] = data[sign <= 0] green[sign >= 0] = data[sign >= 0] red = red.astype(np.uint8) green = green.astype(np.uint8) blue = blue.astype(np.uint8) alpha = np.uint8(data) # DISPLAY RGB image else: if data.shape[-1] > 3: red = data[:, :, 0].astype(np.uint8) green = data[:, :, 1].astype(np.uint8) blue = data[:, :, 2].astype(np.uint8) if (data.ndim == 3 and data.shape[-1] == 4): alpha = data[:, :, 3].astype(np.uint8) else: alpha = 255. * np.ones(blue.shape) else: self.log.warn("input veclen of " + str(data.shape[-1]) + " is incompatible") return 1 h, w = red.shape[:2] image1 = np.zeros((h, w, 4), dtype=np.uint8) image1[:, :, 0] = red image1[:, :, 1] = green image1[:, :, 2] = blue image1[:, :, 3] = alpha format_ = QtGui.QImage.Format_RGB32 image = QtGui.QImage(image1.data, w, h, format_) #send the RGB values to the output port imageTru = np.zeros((h, w, 4), dtype=np.uint8) imageTru[:, :, 0] = red imageTru[:, :, 1] = green imageTru[:, :, 2] = blue imageTru[:, :, 3] = alpha image.ndarray = imageTru if image.isNull(): self.log.warn("Image Viewer: cannot load image") self.setAttr('Viewport:', val=image) self.setData('out', imageTru) return 0
def __init__(self, image_path): # find the limiting desktop dimension (w or h) pm = QtGui.QPixmap.fromImage(QtGui.QImage(image_path)) g = QtGui.QDesktopWidget().availableGeometry() w = g.width() h = g.height() r = float(pm.width()) / pm.height() # aspect ratio if (w <= pm.width()): h = int(w / r) if (h <= pm.height()): w = int(h * r) # the splash is almost useless below 500pts if w < 500: w = 500 # resize the image based on new width if (w != g.width()) or (h != g.height()): pm = pm.scaledToWidth(int(w * 0.8), mode=QtCore.Qt.SmoothTransformation) # scale subsequent parameters based on new image width iw = pm.width() ih = pm.height() super(Splash, self).__init__(pm) # use a timer instead of the EULA self._timer = QtCore.QTimer() self._timer.timeout.connect(self.terms_accepted.emit) self._timer.setSingleShot(True) if not INCLUDE_EULA: self._timer.start(2000) panel = QtGui.QWidget() pal = QtGui.QPalette(QtGui.QColor(255, 255, 255)) # white panel.setAutoFillBackground(True) panel.setPalette(pal) lic = ''' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' self.lic = QtGui.QTextEdit(lic) self.lic.setReadOnly(True) button_title = 'Agree' self.wdg1 = QtGui.QPushButton(button_title, self) self.wdg1.setCheckable(False) self.wdg1.setFixedSize(int(iw * 0.2), int(iw * 0.05)) self.wdg1.clicked[bool].connect(self.accept) button_title = 'Quit' self.wdg2 = QtGui.QPushButton(button_title, self) self.wdg2.setCheckable(False) self.wdg2.setFixedSize(int(iw * 0.2), int(iw * 0.05)) self.wdg2.clicked[bool].connect(self.reject) buf = 'Click Agree to start GPI or Quit to exit.' new_fw = iw * 0.45 for fw_i in range(20, 0, -1): f = QtGui.QFont('gill sans', fw_i) fm = QtGui.QFontMetricsF(f) cfw = fm.width(buf) if cfw < new_fw: break f = QtGui.QFont('gill sans', fw_i) self.prompt = QtGui.QLabel(buf) self.prompt.setAlignment(QtCore.Qt.AlignCenter) self.prompt.setFont(f) wdgLayout = QtGui.QHBoxLayout() #wdgLayout.setContentsMargins(0, 0, 0, 0) # no spaces around this item #wdgLayout.setSpacing(0) #wdgLayout.addSpacerItem(QtGui.QSpacerItem(iw/2,1,hPolicy=QtGui.QSizePolicy.Minimum)) wdgLayout.addWidget(self.prompt) wdgLayout.addWidget(self.wdg1) #wdgLayout.addSpacerItem(QtGui.QSpacerItem(int(iw*0.01),1,hPolicy=QtGui.QSizePolicy.Minimum)) wdgLayout.addWidget(self.wdg2) #wdgLayout.addSpacerItem(QtGui.QSpacerItem(1,1,hPolicy=QtGui.QSizePolicy.MinimumExpanding)) # a small panel vbox_p = QtGui.QVBoxLayout() vbox_p.setContentsMargins(10, 10, 10, 10) vbox_p.setSpacing(10) vbox_p.addWidget(self.lic) vbox_p.addLayout(wdgLayout) panel.setLayout(vbox_p) # white space | panel vbox = QtGui.QVBoxLayout() #vbox.setContentsMargins(0, 0, 0, int(iw*0.02)) # no spaces around this item vbox.setContentsMargins(0, 0, 0, 0) # no spaces around this item vbox.setSpacing(0) vbox.addSpacerItem( QtGui.QSpacerItem(iw, (1 - 0.28) * ih, hPolicy=QtGui.QSizePolicy.Minimum, vPolicy=QtGui.QSizePolicy.Minimum)) #vbox.addWidget(self.lic) vbox.addWidget(panel) #vbox.addLayout(wdgLayout) if INCLUDE_EULA: self.setLayout(vbox) self._accept = False
def compute(self): import numpy as np from scipy import linalg self.log.node("Virtual Channels node running compute()") twoD_or_threeD = 2 # GETTING WIDGET INFO mtx_xy = self.getVal('mtx') trajectory = self.getVal('trajectory') image_ceiling = self.getVal('image ceiling') crop_left = self.getVal('crop left') crop_right = self.getVal('crop right') crop_top = self.getVal('crop top') crop_bottom = self.getVal('crop bottom') reset_compute_button = self.getVal('reset compute button each time') compute = self.getVal('compute') # number of virtual channels m m = self.getVal('virtual channels') numiter = self.getVal('SDC Iterations') # GETTING PORT INFO data = self.getData('data').astype(np.complex64, copy=False) noise = self.getData('noise') sensitivity_map_uncropped = self.getData('sensitivity map') param = self.getData('params_in') # set dimensions nr_points = data.shape[-1] nr_arms = data.shape[-2] nr_coils = data.shape[0] if data.ndim == 3: extra_dim1 = 1 extra_dim2 = 1 data.shape = [nr_coils, extra_dim2, extra_dim1, nr_arms, nr_points] elif data.ndim == 4: extra_dim1 = data.shape[-3] extra_dim2 = 1 data.shape = [nr_coils, extra_dim2, extra_dim1, nr_arms, nr_points] elif data.ndim == 5: extra_dim1 = data.shape[-3] extra_dim2 = data.shape[-4] elif data.ndim > 5: self.log.warn("Not implemented yet") print("data shape: " + str(data.shape)) if sensitivity_map_uncropped is None: # if cropping or image scaling sliders were changed then use the previously stored csm instead of calcluating a new one has_csm_been_calculated = self.getData('sum of square image') if ((has_csm_been_calculated is not None) and (('crop left' in self.widgetEvents()) or ('crop right' in self.widgetEvents()) or ('crop top' in self.widgetEvents()) or ('crop bottom' in self.widgetEvents()) or ('image ceiling' in self.widgetEvents()))): csm = self.getData('masked and normalized sense map') image = self.getData('sum of square image').copy() if ((csm is None) or (image is None)): self.log.warn("This should not happen.") return 1 csm_mtx = csm.shape[-1] else: # calculate auto-calibration B1 maps import triggeredGASSP.gridding.Kaiser2D_utils as kaiser2D UI_mask_floor = self.getVal('Mask Floor (% of max mag)') if (trajectory == 0): # spiral or radial coords = self.getData('coords').astype(np.float32, copy=False) if (coords is None): self.log.warn( "Either a sensitiviy map or coords to calculate one is required" ) return 1 # parameters from UI UI_width = self.getVal('Autocalibration Width (%)') UI_taper = self.getVal('Autocalibration Taper (%)') #UI_mask_floor = self.getVal('Mask Floor (% of max mag)') UI_average_csm = self.getVal( 'Dynamic data - average all dynamics for csm') csm_mtx = np.int(0.01 * UI_width * mtx_xy) if coords.shape[-3] > 400: is_GoldenAngle_data = True else: is_GoldenAngle_data = False if param is not None: if 'spDYN_GOLDANGLE_ON' in param: if int(param['spDYN_GOLDANGLE_ON'][0]) == 1: is_GoldenAngle_data = True else: is_GoldenAngle_data = False print("is GoldenAngle: " + str(is_GoldenAngle_data)) if is_GoldenAngle_data: nr_arms_cms = self.getVal( '# golden angle dynamics for csm') else: nr_arms_cms = nr_arms self.log.debug("nr_arms_cms: " + str(nr_arms_cms)) csm_data = data[..., 0:nr_arms_cms, :] # oversampling: Oversample at the beginning and crop at the end oversampling_ratio = 2. #1.375 mtx = np.int(csm_mtx * oversampling_ratio) if mtx % 2: mtx += 1 if oversampling_ratio > 1: mtx_min = np.int((mtx - csm_mtx) / 2) mtx_max = mtx_min + csm_mtx else: mtx_min = 0 mtx_max = mtx # average dynamics or cardiac phases for csm if ((extra_dim1 > 1) and UI_average_csm): csm_data = np.sum(csm_data, axis=2) csm_data = np.sum( csm_data, axis=1 ) # Modified by Dan Zhu, simply add extra_dim2 as well extra_dim1_csm = 1 #csm_data.shape = [nr_coils, extra_dim2, extra_dim1_csm, nr_arms_cms, nr_points] extra_dim2_csm = 1 # Modified by Dan Zhu, simply add extra_dim2 as well csm_data.shape = [ nr_coils, extra_dim2_csm, extra_dim1_csm, nr_arms_cms, nr_points ] # Modified by Dan Zhu, simply add extra_dim2 as well else: extra_dim1_csm = extra_dim1 extra_dim2_csm = extra_dim2 self.log.debug("csm_data shape: " + str(csm_data.shape)) self.log.debug("coords shape: " + str(coords.shape)) # coords dimensions: (add 1 dimension as they could have another dimension for golden angle dynamics if coords.ndim == 3: coords.shape = [1, nr_arms, nr_points, twoD_or_threeD] # create low resolution csm # cropping the data will make gridding and FFT much faster magnitude_one_interleave = np.zeros(nr_points) for x in range(nr_points): magnitude_one_interleave[x] = np.sqrt( coords[0, 0, x, 0]**2 + coords[0, 0, x, 1]**2) self.log.debug(magnitude_one_interleave) within_csm_width_radius = magnitude_one_interleave[:] < ( 0.01 * UI_width * 0.5 ) # for BNI spirals should be 0.45 instead of 0.5 nr_points_csm_width = within_csm_width_radius.sum() # now set the dimension lists # Modified by Dan Zhu, simply add extra_dim2 as well out_dims_grid = [ nr_coils, extra_dim2_csm, extra_dim1_csm, mtx, nr_arms_cms, nr_points_csm_width ] out_dims_fft = [ nr_coils, extra_dim2_csm, extra_dim1_csm, mtx, mtx ] csm_data = csm_data[..., 0:nr_points_csm_width] csm_coords = 1. / (0.01 * UI_width) * coords[ ..., 0:nr_arms_cms, 0:nr_points_csm_width, :] self.log.debug("csm_data shape: " + str(csm_data.shape)) self.log.debug("csm_coords shape: " + str(csm_coords.shape)) # generate SDC based on number of arms and nr of points being used for csm import gpi_core.gridding.sdc as sd #csm_weights = sd.twod_sdcsp(csm_coords.squeeze().astype(np.float64), numiter, 0.01 * UI_taper, mtx) cmtxdim = np.array([mtx, mtx], dtype=np.int64) wates = np.ones((nr_arms_cms * nr_points_csm_width), dtype=np.float64) coords_for_sdc = csm_coords.astype(np.float64) coords_for_sdc.shape = [ nr_arms_cms * nr_points_csm_width, twoD_or_threeD ] csm_weights = sd.twod_sdc(coords_for_sdc, wates, cmtxdim, numiter, 0.01 * UI_taper) csm_weights.shape = [1, nr_arms_cms, nr_points_csm_width] # pre-calculate Kaiser-Bessel kernel kernel_table_size = 800 kernel = kaiser2D.kaiserbessel_kernel( kernel_table_size, oversampling_ratio) # pre-calculate the rolloff for the spatial domain roll = kaiser2D.rolloff2D(mtx, kernel) # Grid gridded_kspace = kaiser2D.grid2D( csm_data, csm_coords, csm_weights.astype(np.float32), kernel, out_dims_grid) self.setData('debug', gridded_kspace) # filter k-space - not needed anymore as SDC taper is used now. ## win = kaiser2D.window2(gridded_kspace.shape[-2:], windowpct=UI_taper, widthpct=100) ## gridded_kspace *= win # FFT image_domain = kaiser2D.fft2D(gridded_kspace, dir=0, out_dims_fft=out_dims_fft) # rolloff image_domain *= roll # crop to original matrix size csm = image_domain[..., mtx_min:mtx_max, mtx_min:mtx_max] else: # Cartesian csm_mtx = nr_arms extra_dim2_csm = 1 extra_dim1_csm = 1 mtx_min = (nr_points - nr_arms) // 2 mtx_max = mtx_min + csm_mtx out_dims_fft = [ nr_coils, extra_dim2_csm, extra_dim1_csm, nr_arms, nr_points ] csm = kaiser2D.fft2D(data, dir=0, out_dims_fft=out_dims_fft) csm = csm[..., mtx_min:mtx_max] # normalize by rms (better would be to use a whole body coil image csm_rms = np.sqrt(np.sum(np.abs(csm)**2, axis=0)) csm = csm / csm_rms # zero out points that are below mask threshold thresh = 0.01 * UI_mask_floor * csm_rms.max() csm *= csm_rms > thresh # for ROI selection use csm_rms, which still has some contrast image = csm_rms image.shape = [csm_mtx, csm_mtx] image_sos = image.copy() self.setData('sum of square image', image_sos) else: csm = sensitivity_map_uncropped csm_mtx = csm.shape[-1] # create sum-of-squares of sensitivity map to allow selection of ROI image = np.copy(csm) image = np.sqrt(np.sum(np.abs(image)**2, axis=0)) image.shape = [csm_mtx, csm_mtx] self.setData('masked and normalized sense map', csm) # display sum-of-squares of sensitivity map to allow selection of ROI data_max = image.max() data_min = image.min() image[:, crop_left - 1] = data_max image[:, crop_right - 1] = data_max image[crop_top - 1, :] = data_max image[crop_bottom - 1, :] = data_max data_range = data_max - data_min new_max = data_range * 0.01 * image_ceiling + data_min dmask = np.ones(image.shape) image = np.minimum(image, new_max * dmask) if new_max > data_min: image = 255. * (image - data_min) / (new_max - data_min) red = green = blue = np.uint8(image) alpha = 255. * np.ones(blue.shape) h, w = red.shape[:2] image1 = np.zeros((h, w, 4), dtype=np.uint8) image1[:, :, 0] = red image1[:, :, 1] = green image1[:, :, 2] = blue image1[:, :, 3] = alpha format_ = QtGui.QImage.Format_RGB32 image2 = QtGui.QImage(image1.data, w, h, format_) image2.ndarry = image1 self.setAttr('image', val=image2) # crop sensitivity map csm.shape = [nr_coils, csm_mtx, csm_mtx] sensitivity_map = csm[:, crop_top - 1:crop_bottom, crop_left - 1:crop_right] #self.setData('debug', np.squeeze(sensitivity_map)) # get sizes # number of channels n n = sensitivity_map.shape[-3] x_size = sensitivity_map.shape[-1] y_size = sensitivity_map.shape[-2] nr_pixels = x_size * y_size if compute: # noise covariance matrix Psi noise_cv_matrix = np.cov(noise) # Cholesky decomposition to determine T, where T Psi T_H = 1 L = np.linalg.cholesky(noise_cv_matrix) T = np.linalg.inv(L) # decorrelated sensitivity map S_hat S_hat = np.zeros([nr_pixels, n], dtype=np.complex64) for x in range(x_size): for y in range(y_size): index = y + x * y_size S_hat[index, :] = np.dot(T, sensitivity_map[:, y, x]) self.log.debug("after S_hat") # P = sum of S_hat S_hat_pseudo_inverse over all pixels P = np.zeros([n, n], dtype=np.complex64) S_hat_matrix = np.zeros([n, 1], dtype=np.complex64) for index in range(nr_pixels): # pseudo inverse of S_hat S_hat_matrix[:, 0] = S_hat[index, :] S_hat_pinv = np.linalg.pinv(S_hat_matrix) P = P + np.dot(S_hat_matrix, S_hat_pinv) self.log.debug("after S_hat_pinv") # singular value decomposition of P # if P is symmetric and positive definite, the SVD is P = U d U.H instead of P = U d V.H U, d, V = np.linalg.svd(P) self.log.debug("after svd") # the transformation matrix A is then given by A = C U.H T # C is diagonal matrix with 1 on the first m rows and 0 in the remaining # instead of using C, only assing mxn to A C = np.array(np.zeros([n, n]), dtype=np.float32) self.log.debug("after C") for x in range(m): C[x, x] = 1. A_square = np.dot(C, np.dot(U.T.conjugate(), T)) A = A_square[0:m, :] self.log.debug("after A") # Compress the data if data.ndim == 5: out = np.zeros([m, extra_dim2, extra_dim1, nr_arms, nr_points], dtype=data.dtype) for extra2 in range(extra_dim2): for extra1 in range(extra_dim1): for arm in range(nr_arms): for point in range(nr_points): out[:, extra2, extra1, arm, point] = np.dot( A, data[:, extra2, extra1, arm, point]) # SETTING PORT INFO self.setData('compressed data', np.squeeze(out)) self.setData('A', A) self.setData('noise covariance', noise_cv_matrix) # end of compute if reset_compute_button: self.setAttr('compute', val=False) return 0