# painting (1.1)
dtp <- fread(paste0(path,"0_doc/3paintings.txt"),skip=4)
dtp <- dtp[NoOfFaces==1 & MediaTime %in% c(33,433,967)]
cols_to_keep <- grep("AU", names(dtp), value = TRUE)
dtp <- dtp[, ..cols_to_keep]
dtp <- dtp[, lapply(.SD, function(x) ifelse(x < 0, 0, ifelse(x>2,2,x)))]
dtp <- data.table(t(dtp),keep.rownames = T)
dtp[,rn := gsub(pattern = " Evidence",replacement = "",x=rn)]
# Validation using CK data (2.1)
dtck <- data.table(read_excel(paste0(path,"0_doc/ACC_CK.xlsx"), sheet = "Sheet2"))
dtck[,Acc.:= paste(round(Acc.,3)*100,"%")]
dtck[,'URL (source: iMotions)' := ""]
# AUs Data (3.1)
dta1 <- fread(paste0(path,"1_data/A1_freq&sum.csv"))
# ML Data (3.2)
dtml <- as.data.table(read_excel("C:/zm/MA/1_data/erg_arbeit.xlsx", sheet = "vp", range = "A2:E32"))
setnames(dtml,"Dataset","vp")
dtml[,vp:= as.character(1:30)]
0 Introduction
This page documents data visualization content involved in my master
thesis (Zhang (2016)). Basically,
highcharter and kableExtra packages in
R language were employed. The presentation of this
RMarkdown was facilitated using template in
rmdformats package.
1 Background
1.1 FACS
The Facial Action Coding System (FACS, Ekman and Friesen (1978)) can be used to describe facial expressions systematically based on activity in atomic units of facial action, the action units (AUs).
Examples
AU Polar Plot
highchart() %>%
hc_chart(type = "line", polar = TRUE) %>%
hc_xAxis(categories = dtp[,rn]) %>%
hc_yAxis(min=-2, max = 2) %>%
hc_add_series(
name = "Woman",
data = dtp[,V2],
pointPlacement = "on",
type = "line",
color = "#b45c3f",
showInLegend = TRUE
)%>%
hc_add_series(
name = "Man",
data = dtp[,V3],
pointPlacement = "on",
type = "line",
color = "#a2b19b",
showInLegend = TRUE
)
1.2 Emotions and AUs
Emotional facial expressions can be assessed through the evaluation of AUs or the combination of different AUs. Although Ekman and Friesen (1978) suggested that specific combinations of AUs represent a prototypical expression of emotion, the emotion-related expressions are not part of the FACS (Kanade, Cohn, and Tian (2000)). The FACS itself is purely descriptive and does not include inferential labels.
dta <- data.table(
Emotion = c("fear","sadness","surprise","surprise","anger","disgust","sadness",
"fear","surprise","disgust","joy","anger","disgust","disgust",
"joy","sadness","anger","disgust","sadness","anger","fear","fear"),
AU = c("AU 1","AU 1","AU 1","AU 2","AU 4","AU 4","AU 4","AU 5",
"AU 5","AU 6","AU 6","AU 7","AU 9","AU 10","AU 12","AU 15",
"AU 17","AU 17","AU 17","AU 23","AU 25","AU 26")
)
hchart(data_to_sankey(dta), "sankey", name = "Emotions and AUs",
nodes = list(list(id = 'fear' , color = "#00008B"),
list(id = 'sadness' , color = "#778899"),
list(id = 'surprise' , color = "#FFA500"),
list(id = 'anger' , color = "#FF0000"),
list(id = 'disgust' , color = "#808000"),
list(id = 'joy' , color = "#FFD700")))%>%
hc_title(text= "Sankey Diagram") %>%
hc_subtitle(text= "Action Units and Emotions") %>%
hc_caption(text = "<b>based on Ekman and Friesen (1978).<b>")%>%
hc_add_theme(hc_theme_smpl())
2 Method
2.1 Validation of software
The Attention Tool FACET Module (FACET, iMotions), which is a face and AU detection software based on the FACS. This software can track and quantify changes in AUs frame by frame and was validated in studies comparing with human coders (Krumhuber et al. (2021)) and comparing with facial Electromyography (EMG) recording (Kulke, Feyerabend, and Schacht (2020)). Before the application of the software, it was evaluated using the images from the extended Cohn-Kanade Facial Expression Database (CK+, Lucey et al. (2010)). Below are the validation results.
kbl(dtck, escape = F) %>%
kable_paper(full_width = F, bootstrap_options = c("striped", "hover", "condensed", "responsive"))%>%
column_spec(5, image = spec_image(
c("https://imotions.com/wp-content/uploads/2022/10/AU1-FACS.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU2-right-only.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU4-brow-lowerer.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU5.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU6-cheek-raiser.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU7-lid-tightener.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU9-with-410.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU10-with-25.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU12.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU14-dimpler.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU15.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU17.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU18-with-22A-and-25A.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU20-lip-stretcher.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU23-lip-tightener.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU24.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU25-lips-part.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU26-with-25.gif",
"https://imotions.com/wp-content/uploads/2022/10/AU28-with-26.gif"), 150, 70))
| AU | Bedeutung | Acc. | N | URL (source: iMotions) |
|---|---|---|---|---|
| 1 | Heben der inneren Augenbraue | 89.7 % | 175 |
|
| 2 | Heben der äußeren Augenbraue | 88.9 % | 117 |
|
| 4 | Senken der Brauen | 94.3 % | 194 |
|
| 5 | Heben der Oberlider | 95.1 % | 102 |
|
| 6 | Heben der Wangen | 92.7 % | 123 |
|
| 7 | Spannen der Lider | 93.4 % | 121 |
|
| 9 | Nase rümpfen | 100 % | 75 |
|
| 10 | Heben der Oberlippen | 90.5 % | 21 |
|
| 12 | Heben der Mundwinkel | 95.4 % | 131 |
|
| 14 | Grübchen | 67.6 % | 37 |
|
| 15 | Mundwinkel senken | 89.4 % | 94 |
|
| 17 | Kinn anheben | 86.6 % | 202 |
|
| 18 | Lippen spitzen | 88.9 % | 9 |
|
| 20 | Lippen dehnen | 92.4 % | 79 |
|
| 23 | Lippen spannen | 63.3 % | 60 |
|
| 24 | Lippen zusammenpressen | 65.5 % | 58 |
|
| 25 | Öffnen des Mundes | 76.9 % | 324 |
|
| 26 | Unterkiefer fallen lassen | 48 % | 50 |
|
| 28 | Lippen einsaugen | 100 % | 1 |
|
2.2 Experiment Design
the participants drove twelve driving simulator scenarios on a
two-lane urban road. Frustration was induced by a combination of time
pressure and obstacles (Frust). In three scenarios, the participants had
almost free driving in moderate traffic (noFrust).
2.3 Supervised classification
Which classifier has the best accuracy in classifying frustration? According to the experimental design, an occurrence of frustration is expected in the Frust condition, especially during obstacles events. Conversely, a neutral state is expected in the noFrust condition. Therefore, each frame has an experimental label. To evaluate the classification, we selected the following four classifiers: Naive Bayes, Logistic Regression, Random Forest, Support Vector Machine.
2.4 Unsupervised classification
K-Means uses the principle of spatial proximity to assign the observed data. Here, a random selection of the initial cluster centers is made and then iteratively refined in the following process.
x<-c(.5,1,1,2,2,3,3.5,4,4,5,5)
y<-c(1,2,3,2,4,3,5,1,4,4,3)
first<-matrix(c(2,5,1,1,1,2),2,3)
cex1<-2
cex2<-3
initialize
plot(x,y,xlim=c(0,6),ylim=c(0,6),pch=15,main="Data and initial centers in 2D space", xlab="Feature 1",ylab="Feature 2",
cex=cex1)
points(first,col=c("lightblue","tomato"),pch=12,cex=cex2)
clustering
plot(x,y,xlim=c(0,6),ylim=c(0,6),pch=15,main="Clustered data and initial centers in 2D space", xlab="Feature 1",ylab="Feature 2",
cex=cex1)
points(first,col=c("lightblue","tomato"),pch=12,cex=cex2)
points(x[1:6],y[1:6],col="lightblue",pch=15,cex=cex1)
points(x[7:11],y[7:11],col="tomato",pch=15,cex=cex1)
center recalculate
second<-matrix(c(mean(x[1:6]),mean(x[7:11]),mean(y[1:6]),mean(y[7:11])),2,2)
plot(x,y,xlim=c(0,6),ylim=c(0,6),pch=15,main="Clustered data and initial/updated centers", xlab="Feature 1",ylab="Feature 2",cex=cex1)
points(first,col=c("lightblue","tomato"),pch=12,cex=cex1)
points(x[1:6],y[1:6],col="lightblue",pch=15,cex=cex1)
points(x[7:11],y[7:11],col="tomato",pch=15,cex=cex1)
points(mean(x[1:6]),mean(y[1:6]),col="lightblue",pch=12,cex=cex2)
points(mean(x[7:11]),mean(y[7:11]),col="tomato",pch=12,cex=cex2)
arrows(first[1,1],first[1,2],second[1,1],second[1,2],code=2,cex=.1,length=.1)
arrows(first[2,1],first[2,2],second[2,1],second[2,2],code=2,cex=.1,length=.1)
center update
#Zuordnung
plot(x,y,xlim=c(0,6),ylim=c(0,6),pch=15,main="Clustered data and updated centers in 2D space", xlab="Feature 1",ylab="Feature 2")
points(mean(x[1:6]),mean(y[1:6]),col="lightblue",pch=12,cex=cex2)
points(mean(x[7:11]),mean(y[7:11]),col="tomato",pch=12,cex=cex2)
points(x[1:5],y[1:5],col="lightblue",pch=15,cex=cex1)
points(x[6:11],y[6:11],col="tomato",pch=15,cex=cex1)
3 Resutls
3.1 Activated Action Units
temp <- dta1[,.(m = mean(freq050, na.rm=T), ste = sd(freq050, na.rm=T)/sqrt(length(freq050))), by = .(AU, cond)]
temp[,AU := as.factor(AU)]
temp[,cond := factor(cond, level=c("frust","nofrust"))]
hchart(temp, "column",
hcaes(x = AU, y = m, group = cond),
id=c("a","b")) %>%
hc_add_series(temp, "errorbar",
hcaes(x = AU, y = m, low = m - ste, high = m + ste, group = cond),
linkedTo = c("a", "b"),
showInLegend = FALSE) %>%
hc_plotOptions(errorbar = list(color = "black", stemWidth = 1)) %>%
hc_yAxis(title = list(text = "Frequency of Activation"),
labels = list(format = "{value}")) %>%
hc_xAxis(title = list(text = "Action Units")) %>%
hc_add_theme(hc_theme_smpl())
3.2 Evaluation of Machine Learning Models
temp <-copy(dtml)
temp <- melt(temp,id=1)
temp[,value:=round(value,2)]
temp[,variable:=factor(variable, levels=c("svm","NB","Log","RF"), labels = c("Support Vector Machine","Naive Bayes","Logistic Regression","Random Forest"))]
temp %>%
hchart('spline', hcaes(x = vp, y = value, group = variable)) %>%
hc_xAxis(
title = list(text = "Participants"))%>%
hc_yAxis(
title = list(text = "Error rate"),
labels = list(format = "{value} %"))
3.3 Clustering results
3.3.1 Five main clusters
highchart() %>%
hc_chart(type = "line", polar = TRUE) %>%
hc_xAxis(categories = c("AU1", "AU2", "AU4","AU5","AU6","AU7","AU9","AU10","AU12","AU14","AU15","AU17","AU18","AU20","AU23","AU24","AU25","AU28")) %>%
hc_yAxis(min=-2, max = 2) %>%
hc_add_series(
name = "Cluster1",
data = c(-0.00689229534301518, 0.0146658321776044, -0.170238341157532, 0.0108424768892282, 0.0373471212268161, -0.0124238410019452, -0.204127409956917, 0.0120074761554221, 0.252737600694113, 0.0127371720236976, -0.00431673360429348, -0.0636929611773298, -0.221988189483472, -0.000418904436190968, -0.0494849821250738, -0.0898612702937807, 0.0018636612087927, 0.0839875648129451),
pointPlacement = "on",
type = "line",
showInLegend = TRUE
)%>%
hc_add_series(
name = "Cluster2",
data = c(-0.0682134131676777, -0.0157963355638499, -0.30329700926915, 0.0300181668956184, 0.725137714849505, 0.00686445554333491, 0.501447599183491, 0.584408306333753, 1.36293370870626, 0.150859234163142, -0.349015491229172, 0.146295405325107, -0.00775400038698349, -0.0702325045234101, -0.0982615468923018, -0.401903892299507, 0.0595436853079723, 0.0118363813924743),
pointPlacement = "on",
type = "line",
showInLegend = TRUE
)%>%
hc_add_series(
name = "Cluster3",
data = c(-0.0337912212961824, -0.024861465507002, 0.457267069692393, 0.0533649518994984, 0.0346719822087755, 0.220136027679208, 0.447379818849196, 0.0379544123145118, 0.176373236979506, 0.03378366127002, -0.0213953131541545, 0.130191947340001, 0.030047598361894, 0.0404447173541551, 0.152115293936114, 0.172987064827607, 0.00298017172518508, 0.273568941630955),
pointPlacement = "on",
type = "line",
showInLegend = TRUE
)%>%
hc_add_series(
name = "Cluster4",
data = c(-0.0711468968927835, -0.0431345159118597, -0.0988184272962289, -0.0577405841191519, 0.0388219418496092, -0.0604814318247762, 0.346014148049919, 0.0674346791556342, -0.110471983724489, -0.0123121846790221, -0.0533357815158493, 0.26035589623147, 0.808639895163279, -0.223728496617008, 0.100697023786435, 0.311604771472312, -0.00604144108856648, -0.2961903902631),
pointPlacement = "on",
type = "line",
showInLegend = TRUE
)%>%
hc_add_series(
name = "Cluster5",
data = c(-0.0209001964299712, 0.00548049356173341, 0.086884129261727, 0.0450308778978378, -0.0961904866125388, 0.0108757383527421, 0.0265067961786311, 0.0248744789127821, -0.466370851531444, -0.125532497762317, 0.0518096889233274, -0.0120214429301946, 0.120566136315713, -0.0141841168333413, -0.0101437928498482, 0.0130753851846284, -0.00786793449993431, -0.110689917807391),
pointPlacement = "on",
type = "line",
showInLegend = TRUE
)%>%
hc_add_theme(hc_theme_smpl())
3.3.2 Distribution of clusters in Frust/noFrust
temp <- data.table(
Cluster = paste0("Clsuter",1:5),
noFrust = c(22.57, 10.07, 22.82, 10.87, 33.69),
Frust = c(20.56, 10.34, 19.16, 27.29, 22.66)
)
temp <- melt(temp,id=1)
temp[,variable := factor(variable, level=c("Frust","noFrust"))]
# kbl(temp, escape = F,caption = "Distribution of clusters in Frust/noFrust (%)") %>%
# kable_paper(full_width = T, bootstrap_options = c("striped", "hover", "condensed", "responsive"))
hc <- hchart(temp, "packedbubble", hcaes(name = variable, value = value, group = Cluster))
hc %>%
hc_tooltip(
useHTML = TRUE,
pointFormat = "<b>{point.name}:</b> {point.value}"
) %>%
hc_plotOptions(
packedbubble = list(
maxSize = "70%",
zMin = 0,
layoutAlgorithm = list(
gravitationalConstant = 0.05,
splitSeries = TRUE,
seriesInteraction = TRUE,
dragBetweenSeries = TRUE,
parentNodeLimit = TRUE
),
dataLabels = list(
enabled = TRUE,
format = "{point.name}",
style = list(
color = "black",
textOutline = "none",
fontWeight = "normal"
)
)
)
)%>%
hc_add_theme(hc_theme_smpl())