Skip to content

Commit

Permalink
Remove pandas
Browse files Browse the repository at this point in the history
  • Loading branch information
Atika-Syeda authored Dec 30, 2022
2 parents fad4078 + 42f5442 commit 05f8732
Show file tree
Hide file tree
Showing 23 changed files with 627 additions and 490 deletions.
10 changes: 0 additions & 10 deletions .github/workflows/test_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,6 @@ jobs:
sudo apt-get install -y libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 \
libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 \
libxcb-xinerama0 libxcb-xinput0 libxcb-xfixes0
# strategy borrowed from vispy for installing opengl libs on windows
- name: Install Windows OpenGL
if: runner.os == 'Windows'
run: |
git clone --depth 1 git://github.com/pyvista/gl-ci-helpers.git
powershell gl-ci-helpers/appveyor/install_opengl.ps1
# note: if you need dependencies from conda, considering using
# setup-miniconda: https://github.com/conda-incubator/setup-miniconda
# and
# tox-conda: https://github.com/tox-dev/tox-conda
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ jupyter/__pycache__/
.ipynb_checkpoints/
__pycache__/
dist/
suite2p.egg-info/
.eggs/
.vscode/
build/
FaceMap/ops_user.npy
*.pth
Expand Down
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

4 changes: 0 additions & 4 deletions facemap/.vscode/settings.json

This file was deleted.

97 changes: 91 additions & 6 deletions facemap/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@
from facemap import process, roi, utils
from facemap.gui import cluster, guiparts, help_windows, io, menus
from facemap.neural_prediction import neural_activity, prediction_utils
from facemap.pose import datasets, model_loader, pose, pose_gui, refine_pose
from facemap.pose import model_loader, pose, pose_gui, refine_pose
import torch

istr = ["pupil", "motSVD", "blink", "running", "movSVD"]

# TODO: Add pose instructions for CLI commands
# TODO: Add GPU checkbox to add on GUI


class MainW(QtWidgets.QMainWindow):
Expand Down Expand Up @@ -542,6 +544,12 @@ def make_buttons(self):
if self.ops["fullSVD"]:
self.multivideo_svd_checkbox.toggle()
self.process_groupbox.layout().addWidget(self.multivideo_svd_checkbox, 1, 1)
# Add a checkbox to check if GPU is enabled
self.gpu_checkbox = QCheckBox("GPU")
self.gpu_checkbox.setStyleSheet("color: gray;")
self.gpu_checkbox.toggled.connect(self.update_gpu)
self.gpu_checkbox.toggle()
self.process_groupbox.layout().addWidget(self.gpu_checkbox, 1, 2)

# ~~~~~~~~~~~~~~~~~~~~~~~~ Process buttons ~~~~~~~~~~~~~~~~~~~~~~~~
self.process_buttons_groupbox = QGroupBox("Process buttons:")
Expand Down Expand Up @@ -793,6 +801,25 @@ def update_pose_model_combo_box(self):
def set_saturation_label(self):
self.saturation_level_label.setText(str(self.saturation_sliders[0].value()))

def update_gpu(self):
"""
_summary_: Toggle checkbox to use GPU or CPU. If GPU is selected, check if it is available. If not, use CPU instead.
"""
if self.gpu_checkbox.isChecked():
self.device = torch.device("cuda")
try:
_ = torch.zeros([1, 2, 3]).to(self.device)
self.gpu_checkbox.setChecked(True)
print("** TORCH CUDA version installed and working. **")
except:
self.gpu_checkbox.setChecked(False)
self.device = torch.device("cpu")
print("TORCH CUDA version not installed/working. Using CPU version.")
else:
self.device = torch.device("cpu")
self.gpu_checkbox.setChecked(False)
print("** Using CPU version. **")

def set_ROI_saturation_label(self, val=None):
if val is None:
self.roi_saturation_label.setText(str(self.saturation_sliders[1].value()))
Expand Down Expand Up @@ -1371,11 +1398,60 @@ def load_keypoints(self):
"""
Load keypoints from h5py file using h5py
"""
# Read Pose file
self.keypoints_labels = []
self.pose_x_coord = []
self.pose_y_coord = []
self.pose_likelihood = []

for video_id in range(len(self.poseFilepath)):
"""
try:
print("Using old method to load keypoints")
print("Loading keypoints:", self.poseFilepath[video_id])
pose_data = pd.read_hdf(self.poseFilepath[video_id], "df_with_missing")
# Append pose data to list for each video_id
self.keypoints_labels.append(
pd.unique(pose_data.columns.get_level_values("bodyparts"))
)
self.pose_x_coord.append(
pose_data.T[
pose_data.columns.get_level_values("coords").values == "x"
].values
) # size: key points x frames
self.pose_y_coord.append(
pose_data.T[
pose_data.columns.get_level_values("coords").values == "y"
].values
) # size: key points x frames
self.pose_likelihood.append(
pose_data.T[
pose_data.columns.get_level_values("coords").values == "likelihood"
].values
) # size: key points x frames
# Choose colors for each label: provide option for paltter that is color-blindness friendly
colors = cm.get_cmap("jet")(
np.linspace(0, 1.0, len(self.keypoints_labels[video_id]))
)
colors *= 255
colors = colors.astype(int)
self.keypoints_brushes.append(
np.array([pg.mkBrush(color=c) for c in colors])
)
self.is_pose_loaded = True
self.keypoints_checkbox.setChecked(True)
self.plot_trace(
wplot=1,
proctype=5,
wroi=None,
color=None,
keypoints_group_selected=["Eye"],
)
except Exception as e:
print("Using new method to load keypoints")
"""
print("Loading keypoints:", self.poseFilepath[video_id])
pose_data = h5py.File(self.poseFilepath[video_id], "r")["Facemap"]
bodyparts = np.array([])
Expand All @@ -1392,13 +1468,13 @@ def load_keypoints(self):
self.keypoints_labels.append(bodyparts)
self.pose_x_coord = np.array(
[self.pose_x_coord]
) # size: key points x frames
) # size: keypoints x frames
self.pose_y_coord = np.array(
[self.pose_y_coord]
) # size: key points x frames
) # size: keypoints x frames
self.pose_likelihood = np.array(
[self.pose_likelihood]
) # size: key points x frames
) # size: keypoints x frames
# TODO: Choose colors for each label: provide option for palette that is color-blind friendly
colors = cm.get_cmap("jet")(
np.linspace(0, 1.0, len(self.keypoints_labels[video_id]))
Expand Down Expand Up @@ -2517,7 +2593,8 @@ def run_neural_predictions(self, clicked, dialog):
msg.setWindowTitle("Error")
msg.exec_()
return
keypoints = prediction_utils.get_normalized_keypoints(self.poseFilepath[0])
# keypoints = keypoints_utils.get_normalized_keypoints(self.poseFilepath[0])
keypoints = utils.get_keypoints_for_neuralpred(self.poseFilepath[0])
# If the number of timestamps is not equal to the number of frames, then interpolate
if len(self.behavior_timestamps) != self.nframes:
keypoints = keypoints[
Expand All @@ -2534,6 +2611,8 @@ def run_neural_predictions(self, clicked, dialog):
print("Vt shape: ", Vt.shape)
else:
neural_target = self.neural_activity.data.T.copy()
print("Neural target shape: ", neural_target.shape)
print("Keypoints shape: ", keypoints.shape)
(
varexp,
varexp_neurons,
Expand All @@ -2553,10 +2632,15 @@ def run_neural_predictions(self, clicked, dialog):
weight_decay=float(dialog.weight_decay_line_edit.text()),
gui=dialog,
GUIobject=QtWidgets,
device=self.device,
)
# TODO: Use num neurons input provided by the user
predictions, _ = prediction_utils.get_trained_model_predictions(
keypoints, model, self.behavior_timestamps, self.neural_timestamps
keypoints,
model,
self.behavior_timestamps,
self.neural_timestamps,
device=self.device,
)

if dialog.neural_pcs_yes_radio_button.isChecked():
Expand Down Expand Up @@ -2613,6 +2697,7 @@ def run_neural_predictions(self, clicked, dialog):
neural_target,
tbin=dialog.tbin_spinbox.value(),
lam=float(dialog.lambda_line_edit.text()),
device=self.device,
)
ranks = np.argmax(varexp)
print(
Expand Down
Binary file modified facemap/gui/ops_user.npy
Binary file not shown.
95 changes: 2 additions & 93 deletions facemap/keypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from scipy.ndimage import gaussian_filter1d, uniform_filter1d
from scipy.stats import zscore
from sklearn.decomposition import PCA
from .utils import gabor_wavelet, filter_outliers


def get_confidence_threshold(conf, baseline_window=200):
Expand All @@ -18,85 +19,6 @@ def get_confidence_threshold(conf, baseline_window=200):
return conf_baseline, threshold


def nanmedian_filter(x, win=7):
"""nanmedian filter array along last axis"""
nt = x.shape[-1]
# pad so that x will be divisible by win
pad = (win - (nt + 2 * (win // 2)) % win) % win
xpad = np.pad(x, (win // 2, win // 2 + win + pad), mode="edge")
xmed = np.zeros_like(x)
for k in range(win):
xm = np.nanmedian(xpad[k : k - win].reshape(-1, win), axis=-1)
xmed[..., k::win] = xm[: len(np.arange(k, nt, win))]
return xmed


def filter_outliers(
x, y, filter_window=15, baseline_window=50, max_spike=25, max_diff=25
):

# remove frames with large jumps
x_diff = np.abs(
np.append(
np.zeros(
1,
),
np.diff(x),
)
)
y_diff = np.abs(
np.append(
np.zeros(
1,
),
np.diff(y),
)
)
replace_inds = np.logical_or(x_diff > max_diff, y_diff > max_diff)
x[replace_inds] = np.nan
y[replace_inds] = np.nan

# remove frames with large deviations from baseline
x_baseline = nanmedian_filter(x, baseline_window)
y_baseline = nanmedian_filter(y, baseline_window)
replace_inds = np.logical_or(
np.abs(x - x_baseline) > max_spike, np.abs(y - y_baseline) > max_spike
)
x[replace_inds] = np.nan
y[replace_inds] = np.nan
replace_inds = np.isnan(x)

# filter x and y
x_filt = nanmedian_filter(x, filter_window)
y_filt = nanmedian_filter(y, filter_window)
# x_filt = x_baseline
# y_filt = y_baseline

# this in theory shouldn't add more frames
replace_inds = np.logical_or(replace_inds, np.isnan(x_filt))
ireplace = np.nonzero(replace_inds)[0]

# replace outlier frames with median
if len(ireplace) > 0:
# good indices
iinterp = np.nonzero(np.logical_and(~replace_inds, ~np.isnan(x_filt)))[0]
x[replace_inds] = np.interp(ireplace, iinterp, x_filt[iinterp])
y[replace_inds] = np.interp(ireplace, iinterp, y_filt[iinterp])

if 0:
# replace overall outlier deflections from baseline
x_baseline = x.mean() # nanmedian_filter(x, baseline_window)
y_baseline = y.mean() # nanmedian_filter(y, baseline_window)
max_spike = x.std() * 5, y.std() * 5
replace_inds = np.logical_or(
np.abs(x - x_baseline) > max_spike[0], np.abs(y - y_baseline) > max_spike[1]
)
x[replace_inds] = x.mean() # _baseline[replace_inds]
y[replace_inds] = y.mean() # _baseline[replace_inds]

return x, y


def keypoint_labels_per_cam(cam_type=0):
if cam_type == 0:
keypoints_labels = [
Expand Down Expand Up @@ -194,19 +116,6 @@ def compute_dists(xy):
return xy_dists


def gabor_wavelet(sigma, f, ph, n_pts=201, is_torch=False):
x = np.linspace(0, 2 * np.pi, n_pts + 1)[:-1].astype("float32")
cos = np.cos
sin = np.sin
exp = np.exp
xc = x - x.mean()
cosine = cos(ph + f * xc)
gaussian = exp(-(xc**2) / (2 * sigma**2))
G = gaussian * cosine
G /= (G**2).sum() ** 0.5
return G


def get_gabor_transform(data, freqs=np.geomspace(1, 10, 5)):
"""data is time points by features"""
n_time, n_features = data.shape
Expand All @@ -220,7 +129,7 @@ def get_gabor_transform(data, freqs=np.geomspace(1, 10, 5)):
filt0 = np.convolve(zscore(data[:, j]), gw0, mode="same")
filt1 = np.convolve(zscore(data[:, j]), gw1, mode="same")
gabor_transform[:, 2 * k, j] = filt0
gabor_transform[:, 2 * k + 1, j] = (filt0**2 + filt1**2) ** 0.5
gabor_transform[:, 2 * k + 1, j] = (filt0 ** 2 + filt1 ** 2) ** 0.5
return gabor_transform


Expand Down
Loading

0 comments on commit 05f8732

Please sign in to comment.