Skip to content

Commit

Permalink
Merge pull request #83 from cosanlab/fixVisualization
Browse files Browse the repository at this point in the history
Plotting, analyses, tutorials, and requirement fixes.
  • Loading branch information
jcheong0428 committed Mar 26, 2021
2 parents 7b07937 + da40b7a commit 7209b99
Show file tree
Hide file tree
Showing 93 changed files with 7,007 additions and 4,425 deletions.
3 changes: 1 addition & 2 deletions README.md
@@ -1,7 +1,6 @@
# Py-FEAT
[![Build Status](https://api.travis-ci.org/cosanlab/feat.svg?branch=master)](https://travis-ci.org/cosanlab/feat/)
[![Coverage Status](https://coveralls.io/repos/github/cosanlab/feat/badge.svg?branch=master)](https://coveralls.io/github/cosanlab/feat?branch=master)

[![Coverage Status](https://coveralls.io/repos/github/cosanlab/py-feat/badge.svg?branch=master)](https://coveralls.io/github/cosanlab/py-feat?branch=master)
Python Facial Expression Analysis Toolbox (FEAT)

Py-FEAT is a suite for facial expressions (FEX) research written in Python. This package includes tools to detect faces, extract emotional facial expressions (e.g., happiness, sadness, anger), facial muscle movements (e.g., action units), and facial landmarks, from videos and images of faces, as well as methods to preprocess, analyze, and visualize FEX data.
Expand Down
43 changes: 39 additions & 4 deletions feat/data.py
Expand Up @@ -7,8 +7,6 @@
import numpy as np
import pandas as pd
from pandas import DataFrame, Series, Index
import six
import abc
from copy import deepcopy
from functools import reduce
from nltools.data import Adjacency, design_matrix
Expand All @@ -31,7 +29,7 @@
from feat.plotting import plot_face, draw_lineface, draw_muscles
from nilearn.signal import clean
from scipy.signal import convolve
from scipy.stats import ttest_1samp
from scipy.stats import ttest_1samp, ttest_ind


class FexSeries(Series):
Expand Down Expand Up @@ -689,7 +687,7 @@ def regress(self, X, y, fit_intercept=True, *args, **kwargs):
res_df = pd.DataFrame(res, columns=my.columns)
return b_df, t_df, p_df, df_df, res_df

def ttest(self, popmean=0, threshold_dict=None):
def ttest_1samp(self, popmean=0, threshold_dict=None):
"""Conducts 1 sample ttest.
Uses scipy.stats.ttest_1samp to conduct 1 sample ttest
Expand All @@ -703,6 +701,24 @@ def ttest(self, popmean=0, threshold_dict=None):
"""
return ttest_1samp(self, popmean)

def ttest_ind(self, col, sessions, threshold_dict=None):
"""Conducts 2 sample ttest.
Uses scipy.stats.ttest_ind to conduct 2 sample ttest on column col between sessions.
Args:
col (str): Column names to compare in a t-test between sessions
session_names (tuple): tuple of session names stored in Fex.sessions.
threshold_dict ([type], optional): Dictonary for thresholding. Defaults to None. [NOT IMPLEMENTED]
Returns:
t, p: t-statistics and p-values
"""
sess1, sess2 = sessions
a = self[self.sessions == sess1][col]
b = self[self.sessions == sess2][col]
return ttest_ind(a, b)

def predict(self, X, y, model=LinearRegression, *args, **kwargs):
"""Predicts y from X using a sklearn model.
Expand Down Expand Up @@ -755,6 +771,25 @@ def downsample(self, target, **kwargs):
return df_ds
# return self.__class__(df_ds, sampling_freq=target, features=ds_features)

def isc(self, col, index="frame", columns="input", method="pearson"):
"""[summary]
Args:
col (str]): Column name to compute the ISC for.
index (str, optional): Column to be used in computing ISC. Usually this would be the column identifying the time such as the number of the frame. Defaults to "frame".
columns (str, optional): Column to be used for ISC. Usually this would be the column identifying the video or subject. Defaults to "input".
method (str, optional): Method to use for correlation pearson, kendall, or spearman. Defaults to "pearson".
Returns:
DataFrame: Correlation matrix with index as colmns
"""
if index == None:
index = 'frame'
if columns == None:
columns = "input"
mat = pd.pivot_table(self, index = index, columns=columns, values=col).corr(method=method)
return mat

def upsample(self, target, target_type="hz", **kwargs):
"""Upsample Fex columns. Relies on nltools.stats.upsample,
but ensures that returned object is a Fex object.
Expand Down
16 changes: 8 additions & 8 deletions feat/plotting.py
Expand Up @@ -863,7 +863,7 @@ def plot_face(
Args:
model: sklearn PLSRegression instance
au: vector of action units (same length as model.x_mean_)
au: vector of action units (same length as model.n_components)
vectorfield: (dict) {'target':target_array,'reference':reference_array}
muscles: (dict) {'muscle': color}
ax: matplotlib axis handle
Expand All @@ -883,7 +883,7 @@ def plot_face(
raise ValueError("make sure that model is a PLSRegression instance")

if au is None:
au = np.zeros(len(model.x_mean_))
au = np.zeros(model.n_components)
warnings.warn(
"Don't forget to pass an 'au' vector of len(20), "
"using neutral as default"
Expand Down Expand Up @@ -927,7 +927,7 @@ def plot_face(
vectorfield["target"] = landmarks
draw_vectorfield(ax=ax, **vectorfield)
ax.set_xlim([25, 172])
ax.set_ylim((-240, -50))
ax.set_ylim((240, 50))
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
return ax
Expand All @@ -948,20 +948,20 @@ def predict(au, model=None):
elif not isinstance(model, PLSRegression):
raise ValueError("make sure that model is a PLSRegression instance")

if len(au) != len(model.x_mean_):
if len(au) != model.n_components:
print(au)
print(len(model.x_mean_))
raise ValueError("au vector must be len(", len(model.x_mean_), ").")
print(model.n_components)
raise ValueError("au vector must be len(", model.n_components, ").")

if len(au.shape) == 1:
au = np.reshape(au, (1, -1))

landmarks = np.reshape(model.predict(au), (2, 68))
landmarks[1, :] = -1 * landmarks[1, :] # this might not generalize to other models
# landmarks[1, :] = -1 * landmarks[1, :] # this might not generalize to other models
return landmarks


def _create_empty_figure(figsize=(4, 5), xlim=[25, 172], ylim=[-240, -50]):
def _create_empty_figure(figsize=(4, 5), xlim=[25, 172], ylim=[240, 50]):
"""Create an empty figure"""
plt.figure(figsize=figsize)
ax = plt.gca()
Expand Down
Binary file added feat/resources/pyfeat_aus_to_landmarks.h5
Binary file not shown.
14 changes: 12 additions & 2 deletions feat/tests/test_feat.py
Expand Up @@ -410,5 +410,15 @@ def test_stats():
clf = openface.predict(X = openface[["AU02_c"]], y = openface["AU04_c"])
assert clf.coef_ < 0

t, p = openface[["AU02_c"]].ttest()
assert t > 0
t, p = openface[["AU02_c"]].ttest_1samp()
assert t > 0

a = openface.aus().assign(input = "0")
b = openface.aus().apply(lambda x: x+np.random.rand(100)).assign(input = "1")
doubled = pd.concat([a, b])
doubled.sessions = doubled['input']
t, p = doubled.ttest_ind(col="AU12_r", sessions = ("0", "1"))
assert(t < 0)

frame = np.concatenate([np.array(range(int(len(doubled)/2))), np.array(range(int(len(doubled)/2)))])
assert(doubled.assign(frame = frame).isc(col = "AU04_r").iloc[0,0] == 1)
2 changes: 1 addition & 1 deletion feat/tests/test_plot.py
Expand Up @@ -13,7 +13,7 @@


def assert_plot_shape(ax):
assert ax.get_ylim() == (-240.0, -50.0)
assert ax.get_ylim() == (240.0, 50.0)
assert ax.get_xlim() == (25.0, 172.0)


Expand Down
8 changes: 1 addition & 7 deletions feat/tests/test_utils.py
Expand Up @@ -17,7 +17,6 @@
neutral,
softmax,
load_h5,
load_pickled_model,
)
from nltools.data import Adjacency
import unittest
Expand Down Expand Up @@ -47,9 +46,4 @@ def test_utils():
assert softmax(0) == 0.5
# Test badfile.
with pytest.raises(Exception):
load_h5("badfile.h5")

# Test loading of pickled model
out = load_pickled_model()
with pytest.raises(Exception):
load_pickled_model("badfile.pkl")
load_h5("badfile.h5")
41 changes: 13 additions & 28 deletions feat/utils.py
Expand Up @@ -12,7 +12,6 @@

__all__ = [
"get_resource_path",
"load_pickled_model",
"read_facet",
"read_affdex",
"read_affectiva",
Expand All @@ -27,6 +26,10 @@

import os, math, pywt, pickle, h5py
from sklearn.cross_decomposition import PLSRegression
# setattr(PLSRegression, "_x_mean", None)
# setattr(PLSRegression, "_y_mean", None)
# setattr(PLSRegression, "_x_std", None)
from sklearn import __version__
import numpy as np, pandas as pd
from scipy import signal
from scipy.integrate import simps
Expand Down Expand Up @@ -197,30 +200,7 @@ def get_resource_path():
# return ("F:/feat/feat/") # points to the package folder.
# return os.path.join(os.path.dirname(__file__), 'resources')


def load_pickled_model(file_name="pls_python27.pkl"):
"""Load the pickled PLS model for plotting.
Args:
file_name ([type], optional): Specify model to load. Defaults to pls_python27.pkl.
Returns:
model: PLS model
"""
file_name = os.path.join(get_resource_path(), file_name)
try:
with open(file_name, "rb") as f:
model = pickle.load(f)
except UnicodeDecodeError as e:
with open(file_name, "rb") as f:
model = pickle.load(f, encoding="latin1")
except Exception as e:
print("Unable to load data ", file_name, ":", e)
raise
return model


def load_h5(file_name="blue.h5"):
def load_h5(file_name="pyfeat_aus_to_landmarks.h5"):
"""Load the h5 PLS model for plotting.
Args:
Expand All @@ -237,9 +217,14 @@ def load_h5(file_name="blue.h5"):
d4 = hf.get("x_std")
model = PLSRegression(len(d1))
model.coef_ = np.array(d1)
model.x_mean_ = np.array(d2)
model.y_mean_ = np.array(d3)
model.x_std_ = np.array(d4)
if int(__version__.split(".")[1]) < 24:
model.x_mean_ = np.array(d2)
model.y_mean_ = np.array(d3)
model.x_std_ = np.array(d4)
else:
model._x_mean = np.array(d2)
model._y_mean = np.array(d3)
model._x_std = np.array(d4)
hf.close()
except Exception as e:
print("Unable to load data ", file_name, ":", e)
Expand Down
2 changes: 1 addition & 1 deletion feat/version.py
@@ -1 +1 @@
__version__ = '0.3.3'
__version__ = '0.3.5'
Binary file modified notebooks/_build/.doctrees/api/index.doctree
Binary file not shown.
Binary file modified notebooks/_build/.doctrees/content/analysis.doctree
Binary file not shown.
Binary file modified notebooks/_build/.doctrees/content/intro.doctree
Binary file not shown.
Binary file modified notebooks/_build/.doctrees/content/loadingOtherFiles.doctree
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified notebooks/_build/.doctrees/environment.pickle
Binary file not shown.
2 changes: 1 addition & 1 deletion notebooks/_build/html/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 448bfbd75a7fd9759e78e402a6653eab
config: a5b64bda98e84dd45786b2d9d4c3d7e5
tags: 645f666f9bcd5a90fca523b33c5a78b7
Binary file added notebooks/_build/html/_images/analysis_17_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/analysis_23_0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_12_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_14_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_17_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_2_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_5_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_7_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/_build/html/_images/plotting_9_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7209b99

Please sign in to comment.