-
Notifications
You must be signed in to change notification settings - Fork 2
/
visual-perception-functions.Rmd
386 lines (335 loc) · 14.5 KB
/
visual-perception-functions.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
---
title: "Estimating visual perceptions from tracking data"
author: "Eric R. Press"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Estimating visual perceptions from tracking data}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteDepends{ggplot2}
%\VignetteDepends{magrittr}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
## Introduction
Studies of visually guided locomotion in birds, insects or even humans often
involve data gathered from motion capture technologies such as Optitrack's
[(Motive)](https://optitrack.com/software/motive/), or the Straw Lab's
[(Flydra)](https://github.com/strawlab/flydra). For these experiments, it is
important to understand how visual stimuli influence behavior. Although is it
not possible to measure how subjects directly perceive visual stimuli, it is
possible to use motion capture data to calculate estimates of stimulus
properties as they are perceived by the subject. With the tools available in
pathviewr, researchers in ethology or psychology may provide analyses of both
stimulus and response under natural locomotor conditions.
*Inherent to estimations of visual perceptions, we make several assumptions,
which will be discussed below. We welcome suggestions and aim to address any
assumptions that limit the accuracy of these estimates*.
To bridge the gap between objective measures of subject position and estimates
of subjective stimulus perception, we can begin by calculating the angle that a
visual pattern subtends on the subject's retina: the visual angle (θ). Visual
angles can be used to calculate aspects of image motion such as the rate of
visual expansion [(Dakin *et al.*,
2016)](https://www.pnas.org/doi/10.1073/pnas.1603221113/). For a detailed
review of different forms of visual motion and how they're processed by the
brain, see [(Frost, 2010)](https://www.karger.com/Article/Abstract/314284).
Visual angles can be calculated provided there is information about the physical
size of the pattern and its distance from the subject's retina. Because
researchers can control or measure the size of a pattern, and we can calculate
the distance between the subject and pattern using motion capture data, we can
further calculate the visual angle produced by patterns in the visual scene.
Therefore, we first need to calculate the distance from the subject's retina to
the pattern.
*Currently, we assume the subject's gaze is directly frontal or lateral to the
face, in effect estimating image properties at single points in the frontal and
lateral fields of view, respectively. We currently calculate distances to the
center of the subject's head rather than the position of the retina; future
versions of `pathviewr` will include features addressing these limitations, such
as including head orientation information and eye position relative to the
center of the subject's head*.
```{r, echo=FALSE, out.width="100%", fig.cap="Visual angles can be calculated using the size of a visual pattern (`stim_param`) and the distance to the pattern. Larger patterns at shorter distances produce larger visual angles. For dot stimuli, visual angles can be calculated independent of stimulus orientation."}
knitr::include_graphics("https://raw.githubusercontent.com/ropensci/pathviewr/dc728871873b92dc8412bdcb5ef58031457f6788/images/stim_param_angle.jpeg")
```
## Loading packages
To begin, let's load `pathviewr` and a few of the packages in `tidyverse`.
```{r package_loading, message=FALSE, warning=FALSE}
library(pathviewr)
library(ggplot2)
library(magrittr)
```
## Data preparation
Data objects must be prepared as described in [the Data Import and Cleaning
vignette](https://docs.ropensci.org/pathviewr/articles/data-import-cleaning.html)
pipeline prior to their use with these functions. For a detailed description of
the importing and cleaning functions, and when to use them, please see the
linked vignette.
Let's work with a few example datasets included in the package.
`pathviewr_motive_example_data.csv` is a `.csv` file exported from Motive.
`pathviewr_flydra_example_data.mat` is a `.mat` file exported from Flydra. For
more coarse-grained data cleaning tasks, `pathviewr` contains an all-in-one
cleaning function `clean_viewr`. We will use this function for the following
examples.
```{r}
## Import motive data set
motive_data <- # import
read_motive_csv(
system.file("extdata", "pathviewr_motive_example_data.csv",
package = 'pathviewr')
)
## Clean motive data set
motive_full <-
motive_data %>%
clean_viewr(
relabel_viewr_axes = TRUE,
gather_tunnel_data = TRUE,
trim_tunnel_outliers = TRUE,
standardization_option = "rotate_tunnel",
select_x_percent = TRUE,
desired_percent = 50,
rename_viewr_characters = FALSE,
separate_trajectories = TRUE,
max_frame_gap = "autodetect",
get_full_trajectories = TRUE,
span = 0.95
)
```
```{r}
## Import flydra data set
flydra_data <-
read_flydra_mat(
system.file("extdata", "pathviewr_flydra_example_data.mat",
package = 'pathviewr'),
subject_name = "birdie_wooster")
## Clean flydra data set
flydra_full <-
flydra_data %>%
clean_viewr(
relabel_viewr_axes = FALSE,
gather_tunnel_data = FALSE,
trim_tunnel_outliers = FALSE,
standardization_option = "redefine_tunnel_center",
length_method = "middle",
height_method = "user-defined",
height_zero = 1.44,
get_velocity = FALSE,
select_x_percent = TRUE,
desired_percent = 60,
rename_viewr_characters = FALSE,
separate_trajectories = TRUE,
get_full_trajectories = TRUE
)
```
## Add experiment information with `insert_treatments()`
Now that our objects have been cleaned, we must use `insert_treatments()` to add
information about the experiments, including relevant properties of the visual
stimulus and experimental tunnel that are necessary for calculating visual
perceptions.
*`pathviewr` currently supports rectangular (box) or V-shaped experimental
tunnels, though we are interested in including additional tunnel
configurations*.
If you would like to request any additional features such as other tunnel configurations, please create an issue [(here)](https://github.com/ropensci/pathviewr/issues/new/choose)
#### V-shaped tunnel example
The data within `motive_full` were collected from birds flying through a 3m long
V-shaped tunnel in which the height of the origin `(0,0,0)` was set to the
height of the perches that were 0.3855m above the vertex, which was angled at
90 degree. The visual stimuli on the positive and negative walls of the tunnel (where
`position_width` values > 0 and < 0, respectively) were stationary dot-fields.
Each dot was of size 0.05m in diameter. The visual stimuli on the positive and
negative end walls of the tunnel (where `position_length` > 0 and < 0,
respectively) were dot fields with dots 0.1m in diameter. This treatment was
referred to as `"latB"`.
Therefore we will use the following code:
```{r}
motive_treatments <-
motive_full %>%
insert_treatments(tunnel_config = "v",
perch_2_vertex = 0.3855,
vertex_angle = 90,
tunnel_length = 2,
stim_param_lat_pos = 0.05,
stim_param_lat_neg = 0.05,
stim_param_end_pos = 0.1,
stim_param_end_neg = 0.1,
treatment = "latB")
names(motive_treatments)
```
`motive_treatments` now has the variables `tunnel_config`, `perch_2_vertex`,
`vertex_angle`, `tunnel_length`, `stim_param_lat_pos`, `stim_param_lat_neg`,
`stim_param_end_pos`, and `stim_param_end_neg` which are needed to calculate
visual angles. The variable `treatment` has also been included and all of this
information has been stored in the object's metadata.
#### Box-shaped tunnel example
The data within `flydra_full` were collected from birds flying in a 1 x 3m
rectangular tunnel (a box). The visual stimuli were the same as in the motive
example.
```{r}
flydra_treatments <-
flydra_full %>%
insert_treatments(tunnel_config = "box",
tunnel_width = 1,
tunnel_length = 3,
stim_param_lat_pos = 0.05,
stim_param_lat_neg = 0.05,
stim_param_end_pos = 0.1,
stim_param_end_neg = 0.1,
treatment = "latB")
```
`flydra_treatments` similarly has the variables `tunnel_config`, `tunnel_width`,
`tunnel_length`, `stim_param_lat_pos`, `stim_param_lat_neg`,
`stim_param_end_pos`, `stim_param_end_neg` and `treatment`.
## Calculating visual angles
### Start by calculating distances to visual stimuli
To estimate the visual angles perceived by the subject is it moves through the
tunnel, we first need to calculate the distance between the subject and the
visual stimuli. For this, we will use `calc_min_dist_v` or `calc_min_dist_box`
depending on the configuration of the experimental tunnel. These functions
calculate the minimum distance between the subject and the surface displaying a
visual pattern, therefore maximizing the visual angles.
For V-shaped tunnels, several internal calculations are required and can be
added to the output object with `simplify_output = FALSE`. Otherwise, the
minimum distance are computed to the lateral walls and the end wall to which the
subject is facing.
```{r motive_min_dist_pos, fig.height=4, fig.width=7}
motive_min_dist <-
motive_treatments %>%
calc_min_dist_v(simplify_output = FALSE)
## Display minimum distances to the positive lateral walls
## Viewpoint is from the end of the tunnel
motive_min_dist %>%
ggplot(aes(x = position_width, y = position_height)) +
geom_point(aes(color = min_dist_pos), size = 2, shape = 1) +
coord_fixed() +
theme_classic() +
geom_segment(aes(x = 0, # positive wall
y = -0.3855,
xend = 0.5869,
yend = 0.2014)) +
geom_segment(aes(x = 0, # negative wall
y = -0.3855,
xend = -0.5869,
yend = 0.2014))
```
```{r flydra_min_dist_end, fig.height=4, fig.width=7}
flydra_min_dist <-
flydra_treatments %>%
calc_min_dist_box()
## Display minimum distances to the end walls
## Viewpoint is from above the tunnel
flydra_min_dist %>%
ggplot(aes(x = position_length, y = position_width)) +
geom_point(aes(color = min_dist_end), size = 2, shape = 1) +
coord_fixed() +
theme_classic() +
geom_segment(aes(x = -1, # negative wall
y = -0.5,
xend = 1,
yend = -0.5)) +
geom_segment(aes(x = -1, # positive wall
y = 0.5,
xend = 1,
yend = 0.5))
```
### Now get visual angles
```{r motive_vis_angle_pos, fig.height=4, fig.width=7}
motive_vis_angle <-
motive_min_dist %>%
get_vis_angle()
## Visualize the angles produced from stimuli on the positive wall
## Viewpoint is from the end of the tunnel
motive_vis_angle %>%
ggplot(aes(x = position_width, y = position_height)) +
geom_point(aes(color = vis_angle_pos_deg), size = 2, shape = 1) +
coord_fixed()+
theme_classic() +
geom_segment(aes(x = 0, # positive wall
y = -0.3855,
xend = 0.5869,
yend = 0.2014)) +
geom_segment(aes(x = 0, # negative wall
y = -0.3855,
xend = -0.5869,
yend = 0.2014))
```
Notice larger visual angles as the subject approaches the positive wall.
```{r flydra_vis_angle_end, fig.height=4, fig.width=7}
flydra_vis_angle <-
flydra_min_dist %>%
get_vis_angle()
## Visualize the angles produced by stimuli on the end walls
## Viewpoint is from above the tunnel
flydra_vis_angle %>%
ggplot(aes(x = position_length, y = position_width)) +
geom_point(aes(color = vis_angle_end_deg), size = 2, shape = 1) +
coord_fixed() +
theme_classic() +
geom_segment(aes(x = -1, # negative wall
y = -0.5,
xend = 1,
yend = -0.5)) +
geom_segment(aes(x = -1, # positive wall
y = 0.5,
xend = 1,
yend = 0.5))
```
Notice larger visual angles as the subject approaches the end wall that it is
moving towards.
## Calculating spatial frequency
With visual angles, we can now determine the spatial frequency of a visual
pattern as it is perceived by the subject. Spatial frequency refers to the size
of the pattern in visual space. It's often reported as the number of cycles of a
visual pattern per 1 degree of the visual field (cycles/degree). Here, we will define
a cycle length as the length used for the `stim_param`. For a given distance
from the subject, a larger visual pattern produces a smaller spatial frequency,
whereas a smaller visual pattern produces a larger spatial frequency.
To calculate the spatial frequency of the visual stimuli as perceived by the
subject some distance from the stimuli, we will use `get_sf()`.
```{r motive_sf_pos, fig.height=4, fig.width=7}
motive_sf <-
motive_vis_angle %>%
get_sf()
## Visualize the spatial frequency of the stimulus on the positive wall
## point is from the end of the tunnel
motive_sf %>%
ggplot(aes(x = position_width, y = position_height)) +
geom_point(aes(color = sf_pos), size = 2, shape = 1) +
coord_fixed()+
theme_classic() +
geom_segment(aes(x = 0, # positive wall
y = -0.3855,
xend = 0.5869,
yend = 0.2014)) +
geom_segment(aes(x = 0, # negative wall
y = -0.3855,
xend = -0.5869,
yend = 0.2014))
```
Notice the spatial frequency increases the further the subject recedes from
positive wall.
```{r flydra_sf_end, fig.height=4, fig.width=7}
flydra_sf <-
flydra_vis_angle %>%
get_sf()
## Visualize the spatial frequency of the stimulus on the end walls
## Viewpoint is from above the tunnel
flydra_sf %>%
ggplot(aes(x = position_length, y = position_width)) +
geom_point(aes(color = sf_end), size = 2, shape = 1) +
coord_fixed() +
theme_classic() +
geom_segment(aes(x = -1, # negative wall
y = -0.5,
xend = 1,
yend = -0.5)) +
geom_segment(aes(x = -1, # positive wall
y = 0.5,
xend = 1,
yend = 0.5))
```
Notice the spatial frequency decreases as the subject approaches the end wall
that it is moving towards.
### Stay tuned as additional features for image motion estimation are coming soon!