diff --git a/vviewer/Image.py b/vviewer/Image.py index c80dfdf6cc3dce2f136d06608e938c7dda6acbc0..94cdc90ee38802b1353956ab4c29eeb5148b45f6 100644 --- a/vviewer/Image.py +++ b/vviewer/Image.py @@ -17,10 +17,10 @@ import ImageDialog from resample import resample_image from quaternions import fillpositive, quat2mat, mat2quat -try: - from pyvista import pyvista -except ImportError: - pass +# try: +# from pyvista import pyvista +# except ImportError: +# pass class Image(object): diff --git a/vviewer/Image3D.py b/vviewer/Image3D.py index 234ad7c9f0ac5d876d8996079b2d46de7fa8817e..773e9e74cf910e66e803fd5d6f21bdc22d306159 100644 --- a/vviewer/Image3D.py +++ b/vviewer/Image3D.py @@ -2,10 +2,10 @@ import pyqtgraph as pg from Image import Image import ColorMapWidget -try: - from pyvista import pyvista -except ImportError: - pass +# try: +# from pyvista import pyvista +# except ImportError: +# pass class Image3D(Image): diff --git a/vviewer/Image4D.py b/vviewer/Image4D.py index 33b5996090bd5a945fe2dc863b80255e5d4ee201..1f93002f200f93263d1d2f3f5093a1785c8dcff7 100644 --- a/vviewer/Image4D.py +++ b/vviewer/Image4D.py @@ -16,10 +16,10 @@ from Image import Image from resample import resample_image from TimePlot import TimePlot from testInputs import testFloat, testInteger -try: - from pyvista import pyvista -except ImportError: - pass +# try: +# from pyvista import pyvista +# except ImportError: +# pass class Image4D(Image): diff --git a/vviewer/VistaLoad.py b/vviewer/VistaLoad.py new file mode 100644 index 0000000000000000000000000000000000000000..e6c1370212a7cc348e58b4cbb082d6e4a77cc726 --- /dev/null +++ b/vviewer/VistaLoad.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +""" +vista loader, native python implementation +""" + +import scipy.io as sio +import numpy as np +import subprocess +import nibabel as nib +import re + + +#%%writer +# dtype = np.int16 +# # data_orig = 13*np.ones((6,6,6), dtype=dtype) +# xdim = 2 +# ydim = 5 +# zdim = 4 +# # tdim = 11 + +# data_1D = np.arange(xdim*ydim*zdim, dtype=np.int32) +# data_orig = np.reshape(data_1D, (xdim, ydim, zdim)) + +# # data_1D = np.arange(xdim*ydim*zdim*tdim, dtype=dtype) +# # data_orig = np.reshape(data_1D, (xdim, ydim, zdim, tdim)) + + +# nii_orig = nib.Nifti1Image(data_orig, np.eye(4)) +# nii_orig.to_filename("/home/morty/tmp/test.nii") +# subprocess.call("vnifti -in /home/morty/tmp/test.nii -out /home/morty/tmp/test.v", shell=True) + +# #%%BINARY writer +# dtype = np.int16 +# # data_orig = 13*np.ones((6,6,6), dtype=dtype) +# xdim = 5 +# ydim = 5 +# zdim = 5 +# # tdim = 11 + +# data_1D = np.ones(xdim*ydim*zdim, dtype=np.int32) +# data_1D = np.random.rand(xdim*ydim*zdim) +# data_1D[data_1D>0.5] = 1 +# data_1D[data_1D<=0.5] = 0 +# # data_1D = np.zeros(xdim*ydim*zdim) +# # data_1D[4] = 1 + + + +# data_orig = np.reshape(data_1D, (xdim, ydim, zdim)) + +# nii_orig = nib.Nifti1Image(data_orig, np.eye(4)) +# nii_orig.to_filename("/home/morty/tmp/test.nii") +# subprocess.call("vnifti -in /home/morty/tmp/test.nii -out /home/morty/tmp/testa.v", shell=True) +# subprocess.call("vbinarize -in /home/morty/tmp/testa.v -out /home/morty/tmp/test.v -min 0.5", shell=True) + + +#%% AUX +def get_property_str(header_img, str_property, repn_property="int"): + idx_begin = header_img.find(str_property) + if idx_begin == -1: + return -1 + idx_val = idx_begin + len(str_property) + 2 + value = header_img[idx_val:].split('\n')[0] + if repn_property == "int": + value = int(value) + elif repn_property == "str": + pass + elif repn_property == "float": + value = float(value) + elif repn_property == "list": + value = value[1:-1].split(" ") + value = [float(l) for l in value] + return value + +def get_subheader(header, section_demark): + idx_start = header.find(section_demark) + if idx_start == -1: + return + idx_stop = -1 + for j in range(idx_start, idx_start+5000): + if header[j] == "}": + idx_stop = j+1 + break + return header[idx_start:idx_stop] + + +#%% reader +fp_input = "/home/morty/tmp/test.v" + +def load_vista(fp_input): + + with open(fp_input, 'rb') as f: + raw=f.read() + + #find the ^L breaker, determining where the header part stops + for i in range(len(raw)): + if raw[i:i+2]==b'\x0c\n': + last_idx_header = i+2 + break + + #now parse the header for the first time to load the data. + header = raw[0:last_idx_header-2].decode("utf-8") + + #find all proper images and parse each of them into dict. store all dicts in list_images + list_imagedict = [] + idx_images = [m.start() for m in re.finditer('image: image {', header)] + + for i in range(len(idx_images)): + dict_image = {} + dict_image['idx_header_begin'] = idx_images[i] + #find end of image definition in header + for j in range(idx_images[i], idx_images[i]+5000): + if header[j] == "}": + dict_image['idx_header_end'] = j+1 + break + + header_img = header[dict_image['idx_header_begin']:dict_image['idx_header_end']] + dict_image["repn"] = get_property_str(header_img, "repn", "str") + dict_image["offset"] = get_property_str(header_img, "data", "int") + last_idx_header + dict_image["length"] = get_property_str(header_img, "length", "int") + + dict_image["nbands"] = get_property_str(header_img, "nbands", "int") + if dict_image["nbands"] == -1: + dict_image["nbands"] = 1 + + + dict_image["nrows"] = get_property_str(header_img, "nrows", "int") + dict_image["ncolumns"] = get_property_str(header_img, "ncolumns", "int") + tr = get_property_str(header_img, "repetition_time", "float") + + if tr > 0: + dict_image["repetition_time"] = tr + + #fix dtype + if dict_image["repn"] == "int": + dict_image["dtype"] = np.int32 + dict_image["length"] = int(dict_image["length"]/4) + elif dict_image["repn"] == "long": + dict_image["dtype"] = np.int64 + dict_image["length"] = int(dict_image["length"]/8) + elif dict_image["repn"] == "float": + dict_image["dtype"] = np.float32 + dict_image["length"] = int(dict_image["length"]/4) + elif dict_image["repn"] == "double": + dict_image["dtype"] = np.float64 + dict_image["length"] = int(dict_image["length"]/8) + elif dict_image["repn"] == "short": + dict_image["dtype"] = np.int16 + dict_image["length"] = int(dict_image["length"]/2) + elif dict_image["repn"] == "bit": + dict_image["dtype"] = np.int16 + dict_image["length"] = int(dict_image["length"]/1) + elif dict_image["repn"] == "ubyte": + dict_image["dtype"] = np.uint8 + dict_image["length"] = int(dict_image["length"]/1) + + else: + raise ValueError("Read error: data representation '{}' unknown. please contact support.".format(dict_image["repn"])) + + list_imagedict.append(dict_image) + + + #decide: was the image 3D or 4D (time series) + if len(list_imagedict) == 1: #<=3D case + dict_image = list_imagedict[0] + xdim = dict_image["ncolumns"] + ydim = dict_image["nrows"] + zdim = dict_image["nbands"] + tdim = 1 + + if dict_image["repn"] != "bit": #default case + img1D = np.frombuffer(raw, dtype=dict_image["dtype"], count=dict_image["length"], offset=dict_image["offset"]).byteswap() + else: #bit representation (masks etc) + img1D_byterepn = np.frombuffer(raw, dtype=np.uint8, count=dict_image["length"], offset=dict_image["offset"])#.byteswap() + img1D_bit_graced = np.array([]) + for l in img1D_byterepn: + bx = bin(l)[2:] + bx = (8-len(bx))*"0"+bx + for j in range(8): + # b = bx[7-j] + b = bx[j] + img1D_bit_graced = np.append(img1D_bit_graced, int(b)) + img1D = img1D_bit_graced[0:xdim*ydim*zdim] + + img3D = np.transpose(np.reshape(img1D, (zdim,ydim,xdim)), (2,1,0)) + data = img3D + dim = "4D" + else: + list_images = [] + for i in range(len(idx_images)): + dict_image = list_imagedict[i] + list_images.append(np.frombuffer(raw, dtype=dict_image["dtype"], count=dict_image["length"], offset=dict_image["offset"]).byteswap()) + if dict_image["repn"] == "bit": + raise ValueError("bit reprensetation not allowed for 4D images!") + #concatenate, form a long 1D vector + xdim = dict_image["ncolumns"] + ydim = dict_image["nrows"] + tdim = dict_image["nbands"] + zdim = len(list_images) + img1D = np.zeros(xdim*ydim*zdim*tdim, dtype=dict_image["dtype"]) + for i in range(len(idx_images)): + img1D[i*xdim*ydim*tdim:(i+1)*xdim*ydim*tdim] = list_images[i] + img4D = np.transpose(np.reshape(img1D, (zdim,tdim,ydim,xdim)), (3,2,0,1)) + data = img4D + dim = "3D" + + + #%% re-parse the header to get the complete header information + mm = get_property_str(header_img, "voxel", "list") + + #get s-form code + sform_code = get_property_str(header, "sform_code", "int") + qform_code = get_property_str(header, "qform_code", "int") + + header_sform = get_subheader(header, "sform: image") + offset_sform = get_property_str(header_sform, "data", "int") + last_idx_header + length_sform = int(get_property_str(header_sform, "length", "int")/4) + sform1D = np.frombuffer(raw, dtype=np.float32, count=length_sform, offset=offset_sform).byteswap() + sform2D = np.reshape(sform1D, (4,4)) + + + header_dim = get_subheader(header, "dim: bundle") + offset_dim = get_property_str(header_dim, "data", "int") + last_idx_header + length_dim = int(get_property_str(header_dim, "length", "int")/4) + dim1D = np.frombuffer(raw, dtype=np.float32, count=length_dim, offset=offset_dim) + + + header_pixdim = get_subheader(header, "pixdim: bundle") + offset_pixdim= get_property_str(header_dim, "data", "int") + last_idx_header + length_pixdim = int(get_property_str(header_dim, "length", "int")/4) + pixdim1D = np.frombuffer(raw, dtype=np.float32, count=length_dim, offset=offset_dim) + + + header_qform = get_subheader(header, "qform: bundle") + offset_qform = get_property_str(header_qform, "data", "int") + last_idx_header + length_qform = int(get_property_str(header_qform, "length", "int")/4) + qformdim1D = np.frombuffer(raw, dtype=np.float32, count=length_qform, offset=offset_qform) + # quatern_b quatern_c quatern_d qoffset_x qoffset_y qoffset_z + + + #%% convert to nii object... fill in data and header + nii_loaded = nib.Nifti1Image(data, affine=np.eye(4)) + nii_loaded.header.set_zooms(mm) + nii_loaded.header['pixdim'][4] = tr + nii_loaded.set_sform(sform2D) + nii_loaded.header['qform_code'] = qform_code + nii_loaded.header['qform_code'] = sform_code + nii_loaded.header['quatern_b'] = qformdim1D[0] + nii_loaded.header['quatern_c'] = qformdim1D[1] + nii_loaded.header['quatern_d'] = qformdim1D[2] + nii_loaded.header['qoffset_x'] = qformdim1D[3] + nii_loaded.header['qoffset_y'] = qformdim1D[4] + nii_loaded.header['qoffset_z'] = qformdim1D[5] + nii_loaded.header.set_xyzt_units(xyz="mm", t="msec") + + return nii_loaded + +# nii_loaded.to_filename("/home/morty/tmp/out.nii") diff --git a/vviewer/config.ini b/vviewer/config.ini index a755b53227378f63e50a7e9d6ef4ab085ab45183..69dab5feeafaf6c56f5d807040bffaa52d7abaea 100644 --- a/vviewer/config.ini +++ b/vviewer/config.ini @@ -1,5 +1,5 @@ [view] -voxel_coord = False +voxel_coord = True link_mode = 0 width = 1272 height = 424 @@ -21,5 +21,5 @@ interpolation = 0 os_ratio = 1.0 [search] -search_radius = 666 +search_radius = 5 diff --git a/vviewer/loadImage.py b/vviewer/loadImage.py index 18b159cebba80be6ffbe52157dbc6f74efca94ae..0d3eaec6c58228ca3804e836391fd6c8af0000a0 100644 --- a/vviewer/loadImage.py +++ b/vviewer/loadImage.py @@ -21,11 +21,12 @@ import ColorMapWidget import ImageDialog import Image3D import Image4D +from VistaLoad import load_vista -try: - import pyvista -except ImportError: - print("Warning: import error, module pyvista is unavailable.") +# try: +# import pyvista +# except ImportError: +# print("Warning: import error, module pyvista is unavailable.") from quaternions import fillpositive, quat2mat, mat2quat @@ -120,42 +121,46 @@ def loadImageFromFile(filename, pref, f_type): print("Cannot load img/hdr pair file given!") elif (filetype == '.v'): - try: - [img_data, dim, pixdim, sform_code, sform, qform_code, qform, - voxel_res, quatern, qoffset, column_vec, row_vec, slice_vec, - index_origin, ca, cp, extent] = pyvista.loadVImage(filename) - hdr = {'pixdim': pixdim} - if len(pixdim) == 1: # version 1 .v file - pixdim.append(voxel_res[1]) - pixdim.append(voxel_res[2]) - pixdim.append(voxel_res[3]) - # Build affine from old version header info - affine = np.eye(4) - affine[0, 0:3] = column_vec - affine[1, 0:3] = row_vec - affine[2, 0:3] = slice_vec - affine[0:3, 3] = np.transpose(index_origin) - # if affine is still build incorrectly, use identity - if np.linalg.det(affine[0:2, 0:2]) == 0: - affine = np.identity(4) - image = Nifti2Image(img_data.astype(np.float32), affine) - else: # version 2 .v file - # sform, or qform, and if not, then fall-back affine - if sform_code != 0: - affine = sform - elif qform_code != 0: - qform_affine = calculateQFormAffine(pixdim, qform_code, qform) - affine = qform_affine - else: - affine = shape_zoom_affine(img_data.shape, pixdim[1:4], False) - image = Nifti2Image(img_data.astype(np.float32), affine) - if sform_code != 0: - image.set_sform(sform) - if qform_code != 0: - qform_affine = calculateQFormAffine(pixdim, qform_code, qform) - image.set_qform(qform_affine) - except RuntimeError: - print("Cannot load .v file given!") + image = load_vista(filename) + hdr = image.header + + #old way of loading vista file! + # try: + # [img_data, dim, pixdim, sform_code, sform, qform_code, qform, + # voxel_res, quatern, qoffset, column_vec, row_vec, slice_vec, + # index_origin, ca, cp, extent] = pyvista.loadVImage(filename) + # hdr = {'pixdim': pixdim} + # if len(pixdim) == 1: # version 1 .v file + # pixdim.append(voxel_res[1]) + # pixdim.append(voxel_res[2]) + # pixdim.append(voxel_res[3]) + # # Build affine from old version header info + # affine = np.eye(4) + # affine[0, 0:3] = column_vec + # affine[1, 0:3] = row_vec + # affine[2, 0:3] = slice_vec + # affine[0:3, 3] = np.transpose(index_origin) + # # if affine is still build incorrectly, use identity + # if np.linalg.det(affine[0:2, 0:2]) == 0: + # affine = np.identity(4) + # image = Nifti2Image(img_data.astype(np.float32), affine) + # else: # version 2 .v file + # # sform, or qform, and if not, then fall-back affine + # if sform_code != 0: + # affine = sform + # elif qform_code != 0: + # qform_affine = calculateQFormAffine(pixdim, qform_code, qform) + # affine = qform_affine + # else: + # affine = shape_zoom_affine(img_data.shape, pixdim[1:4], False) + # image = Nifti2Image(img_data.astype(np.float32), affine) + # if sform_code != 0: + # image.set_sform(sform) + # if qform_code != 0: + # qform_affine = calculateQFormAffine(pixdim, qform_code, qform) + # image.set_qform(qform_affine) + # except RuntimeError: + # print("Cannot load .v file given!") # if data contain NaNs convert to zero if np.isnan(image.get_data()).any(): diff --git a/vviewer/vviewer.py b/vviewer/vviewer.py index 3a693ec1260ba9dd6a79ba77e18c852a0efb473b..bbb831a6f714dd9f1bdc95b4207c78dc3fddd746 100755 --- a/vviewer/vviewer.py +++ b/vviewer/vviewer.py @@ -442,27 +442,29 @@ class vviewer(QtGui.QMainWindow): # Crosshair intensity label showing the value of the current image # label 'cross' - self.cross_int_label = QtGui.QLabel('Cross:') - self.cross_int_label.setAlignment( - QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight) - self.l.addWidget(self.cross_int_label, 6, self.listoffset+2, 1, 2) + # self.cross_int_label = QtGui.QLabel('Cross:') + # self.cross_int_label.setAlignment( + # QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight) + # self.l.addWidget(self.cross_int_label, 6, self.listoffset+2, 1, 2) + + # label for actual value self.intensity_label = QtGui.QLabel('nan') self.intensity_label.setAlignment( QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft) # self.l.addWidget(self.intensity_label, 6, self.listoffset+4, 1, 8) - self.l.addWidget(self.intensity_label, 6, self.listoffset+4, 1, 8) + self.l.addWidget(self.intensity_label, 6, self.listoffset+2, 1, 8) # Label showing the values for mouse position # label 'cursor' - self.cursor_int_label = QtGui.QLabel('Cursor:') - self.cursor_int_label.setAlignment( - QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight) - self.l.addWidget(self.cursor_int_label, 7, self.listoffset+2, 1, 2) - self.intensity_lbl_cursor = QtGui.QLabel('nan') - self.intensity_lbl_cursor.setAlignment( - QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft) - self.l.addWidget(self.intensity_lbl_cursor, 7, self.listoffset+4, 1, 10) + # self.cursor_int_label = QtGui.QLabel('Cursor:') + # self.cursor_int_label.setAlignment( + # QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight) + # self.l.addWidget(self.cursor_int_label, 7, self.listoffset+2, 1, 2) + # self.intensity_lbl_cursor = QtGui.QLabel('nan') + # self.intensity_lbl_cursor.setAlignment( + # QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft) + # self.l.addWidget(self.intensity_lbl_cursor, 7, self.listoffset+4, 1, 10) ## For playing time series movies ## # fast backward button @@ -1542,7 +1544,7 @@ class vviewer(QtGui.QMainWindow): intensity = ("%.6f" %self.images[index].getIntensity()) else: intensity = "nan" - self.intensity_lbl_cursor.setText(intensity) + # self.intensity_lbl_cursor.setText(intensity) if self.value_window.isVisible(): if coords_valid: names = "<b>filename</b><br>" @@ -1594,7 +1596,18 @@ class vviewer(QtGui.QMainWindow): #intensity = ( # str(np.round(self.images[index].getIntensity(),3))) # intensity = (str(self.images[index].getIntensity())) - intensity = ("%.6f" %self.images[index].getIntensity()) + + + # intensity = ("%.6f" %self.images[index].getIntensity()) + #jjj change + nmb_images = len(self.images) + str_intens = "" + for i in range(nmb_images): + if i > 0: + str_intens += "\n" + str_intens += "%.3f" %self.images[i].getIntensity() + str_intens += " (%s)" %str(self.imagelist.item(i).text()).split(".")[0] + intensity = (str_intens) self.intensity_label.setText(intensity) # update ValueWindow