Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 39 additions & 9 deletions .github/workflows/readme.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,56 @@ on:
paths:
- README.Rmd

# Needed for the commit step to push the re-rendered README back
permissions:
contents: write

jobs:
render:
name: Render README
runs-on: macOS-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-pandoc@v2
- name: Install Core Dependencies
run: Rscript -e 'install.packages(c("rmarkdown", "dplyr", "remotes"))'
- name: Install Python

- uses: actions/setup-python@v5
with:
python-version: "3.x"

# README.Rmd imports pyllusion itself, so install the package (which brings in numpy, pandas,
# Pillow and scipy) rather than a hand-listed subset of its dependencies.
- name: Install Python dependencies
run: |
Rscript -e "remotes::install_github('rstudio/reticulate')"
Rscript -e "reticulate::install_miniconda()"
Rscript -e "reticulate::py_install(c('Pillow', 'scipy'), pip = TRUE)"
python -m pip install --upgrade pip
python -m pip install .

# reticulate comes from CRAN. It used to be installed from GitHub via remotes, which failed
# because an unauthenticated install_github() falls back to the bundled PAT and gets rate
# limited; there is no need for the development version here anyway.
- name: Install R dependencies
run: Rscript -e 'install.packages(c("rmarkdown", "reticulate"))'

- name: Render README
env:
# Point reticulate at the interpreter set up above, instead of the local path in README.Rmd
RETICULATE_PYTHON: ${{ env.pythonLocation }}/bin/python
run: Rscript -e 'rmarkdown::render("README.Rmd")'

- name: Commit results
run: |
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit README.md -m 'Re-build README.Rmd' || echo "No changes to commit"
git push origin || echo "No changes to commit"
git add README.md docs/img
# Only commit and push when the render actually changed something. Guarding these with
# `|| echo` instead would hide auth, branch-protection and non-fast-forward failures behind
# a green run (a push with nothing to send already exits 0, so it never needed a guard).
if git diff --staged --quiet; then
echo "Render produced no changes"
else
git commit -m 'Re-build README.Rmd'
git push origin HEAD:"${GITHUB_REF_NAME}"
fi
21 changes: 21 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
News
=====

1.5
---------

**Fixes**

- ``Delboeuf`` and ``Ebbinghaus``: fixed ``Size_Inner_Difference`` in ``get_parameters()``. An operator
precedence slip (``np.pi * a**2 / np.pi * b**2``, which evaluates as ``(pi * a**2 / pi) * b**2``) made
it return the product of the two squared radii instead of a difference of areas - it reported
0.000244140625 for two circles of *identical* size. It is now the signed difference between the areas
of the two inner circles, positive when the left circle is larger, as its docstring always described.
Both illusions were affected, since ``Ebbinghaus`` reuses the same helper. **Any analysis that used
this value will need re-running.**
- Corrected the name of that entry in the ``Delboeuf`` and ``Ebbinghaus`` docstrings, where it was
listed as ``Sine_Inner_Difference``.

**Misc**

- Repaired the "Render README" workflow, which could not run: it installed reticulate from GitHub
(rate limited without a token), never installed Pyllusion itself although ``README.Rmd`` imports it,
and relied on a hard-coded local path to a Python distribution.

1.4
---------

Expand Down
16 changes: 8 additions & 8 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ knitr::opts_chunk$set(



# Setup python - you need to change the path to your python distribution
# Setup python - locally, point `local_python` at your own python distribution. On CI the
# interpreter comes from RETICULATE_PYTHON instead, so the local path is skipped there (it also
# gets skipped if it does not exist, so a wrong path does not break anyone else's render).
library(reticulate)
# reticulate::use_python("D:/Downloads/WPy64-3902/python-3.9.0.amd64/")
reticulate::use_python("C:/Users/Zen Juen/Downloads/WPy64-3850/python-3.8.5.amd64")
# reticulate::use_python("C:/Users/Pham Thanh Tam/Desktop/WPy-3710b/python-3.7.1.amd64")
local_python <- "C:/Users/Zen Juen/Downloads/WPy64-3850/python-3.8.5.amd64"
if (!nzchar(Sys.getenv("CI")) && dir.exists(local_python)) {
reticulate::use_python(local_python)
}


# matplotlib <- import("matplotlib")
Expand Down Expand Up @@ -77,10 +80,7 @@ If you have used **Pyllusion** in your experiments, and have made your scripts a

You can cite the package as follows:

```
Makowski, D., Pham, T., Lau, Z. J. (2020). Pyllusion: A Parametric Framework to Generate Visual Illusions using Python. GitHub.
Retrieved from https://github.com/RealityBending/Pyllusion
```
- Makowski, D., Lau, Z. J., Pham, T., Paul Boyce, W., & Annabel Chen, S. H. (2021). [A Parametric Framework to Generate Visual Illusions Using Python](https://journals.sagepub.com/doi/abs/10.1177/03010066211057347). Perception, 50(11), 950-965.

## Features

Expand Down
57 changes: 31 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

![](https://github.com/RealityBending/Pyllusion/blob/master/docs/img/logo.png)
![](https://img.shields.io/pypi/pyversions/pyllusion.svg?logo=python&logoColor=FFE873)
![](https://img.shields.io/pypi/v/pyllusion.svg?logo=pypi&logoColor=FFE873)
[![Tests](https://github.com/RealityBending/Pyllusion/actions/workflows/tests.yml/badge.svg)](https://github.com/RealityBending/Pyllusion/actions/workflows/tests.yml)
[![](https://img.shields.io/badge/paper-Pyllusion-9C27B0)](https://journals.sagepub.com/doi/10.1177/03010066211057347)

Expand All @@ -19,9 +20,9 @@ The parametric approach implemented in this software proposes to
the *difference* and *illusion strength*. These two parameters can be
modulated to independently impact either the objective difference of the
core components of the illusion (e.g., the difference between the two
segments in the [Müller-Lyer](#müller-lyer-illusion) illusion)
or the intensity of the illusion effect (e.g., the angle of the
“distractors” arrows).
segments in the [Müller-Lyer](#müller-lyer-illusion) illusion) or the
intensity of the illusion effect (e.g., the angle of the “distractors”
arrows).

Describing illusions using a set of parameters aims at fostering
**reproducible science**, allowing neuroscientists to easily report,
Expand Down Expand Up @@ -53,7 +54,11 @@ issue](https://github.com/RealityBending/Pyllusion/issues) with the

You can cite the package as follows:

- Makowski, D., Lau, Z. J., Pham, T., Paul Boyce, W., & Annabel Chen, S. H. (2021). [A Parametric Framework to Generate Visual Illusions Using Python](https://journals.sagepub.com/doi/abs/10.1177/03010066211057347). Perception, 50(11), 950-965.
- Makowski, D., Lau, Z. J., Pham, T., Paul Boyce, W., & Annabel
Chen, S. H. (2021). [A Parametric Framework to Generate Visual
Illusions Using
Python](https://journals.sagepub.com/doi/abs/10.1177/03010066211057347).
Perception, 50(11), 950-965.

## Features

Expand Down Expand Up @@ -262,16 +267,16 @@ white.to_image()

The **Kanizsa Square** is an [illusory
contour](https://en.wikipedia.org/wiki/Illusory_contours) illusion. See
[Keane et
al., 2019](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6667222/).
[Keane et al.,
2019](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6667222/).

Some studies have tested the effect of the Kanizsa Square in individuals
with schizophrenia, but the finding of greater resistance to the
illusion is not robust (King et al., 2017).

![](https://www.researchgate.net/profile/Ken_Manktelow/publication/6821211/figure/fig2/AS:277716826902534@1443224259391/Kanizsa-square-An-illusory-figure-induced-by-edges-showing-an-illusory-contour-and-an.png)

- **TO DO** *(consider helping\!)*
- **TO DO** *(consider helping!)*

### Autostereograms

Expand Down Expand Up @@ -305,7 +310,7 @@ it’s a mind-blowing experience 🤯

The function is highly customisable, and we can use a black and white
image as a **depth mask** (in this case, the [picture of a
skull](https://raw.githubusercontent.com/RealityBending/Pyllusion/master/docs/img/depthmask.png)
skull](https://github.com/DominiqueMakowski/Pyllusion/docs/img/depthmask.png)
that you will see as emerging from the background), and customise the
pattern used by providing another function (here, the `image_circles()`
function to which we can provide additional arguments like `blackwhite`,
Expand Down Expand Up @@ -357,9 +362,9 @@ by blending images of faces with noise-like images.
Blending of images can be achieved: as followed

``` python
pareidolia = pyllusion.Pareidolia(pattern="docs/img/snake.png",
n=[20, 300, 4000],
sd=[4, 2, 1],
pareidolia = pyllusion.Pareidolia(pattern="docs/img/snake.png",
n=[20, 300, 4000],
sd=[4, 2, 1],
weight=[3, 2, 1],
alpha=80,
blur=0.5)
Expand All @@ -374,18 +379,18 @@ In visual perception, the **[kinetic depth
effect](https://en.wikipedia.org/wiki/Kinetic_depth_effect)** refers to
the phenomenon whereby the three-dimensional structural form of an
object can be perceived when the object is moving (Wallach & O’Connell,
1953; [Mamassian &
Wallace, 2010](https://jov.arvojournals.org/article.aspx?articleid=2191769)).
One of its derivative is the **Transparency-From-Motion** illusion,
1953; [Mamassian & Wallace,
2010](https://jov.arvojournals.org/article.aspx?articleid=2191769)). One
of its derivative is the **Transparency-From-Motion** illusion,
consisting in the superposition of two dot clouds moving in different
directions that results in the perception of two transparent layers (See
;
[Schütz, 2014](https://jov.arvojournals.org/article.aspx?articleid=2193903);
[Wexler et
al., 2015](https://www.pnas.org/content/pnas/112/48/14990.full.pdf);
[Schütz &
Mamassian, 2016](https://jov.arvojournals.org/article.aspx?articleid=2548036)
and <http://lab-perception.org/demo/p/tfm> for a demo).
; [Schütz,
2014](https://jov.arvojournals.org/article.aspx?articleid=2193903);
[Wexler et al.,
2015](https://www.pnas.org/content/pnas/112/48/14990.full.pdf); [Schütz
& Mamassian,
2016](https://jov.arvojournals.org/article.aspx?articleid=2548036) and
<http://lab-perception.org/demo/p/tfm> for a demo).

``` python
parameters = pyllusion.motiontransparency_parameters(angle=45)
Expand All @@ -402,10 +407,10 @@ pyllusion.images_to_gif(images, path="Transparency_From_Motion.gif", fps=paramet

### Pinna illusion

See also **[Zeljic et
al., 2021](https://www.sciencedirect.com/science/article/pii/S0278584620303717)**.
See also **[Zeljic et al.,
2021](https://www.sciencedirect.com/science/article/pii/S0278584620303717)**.

- **TO DO** *(consider helping\!)*
- **TO DO** *(consider helping!)*

![](http://psychedelic-information-theory.com/upload/img/pinna-illusion-image.jpg)

Expand All @@ -416,7 +421,7 @@ See also [David Novick’s](https://twitter.com/NovickProf) tweets
and
[**here**](https://twitter.com/NovickProf/status/1358787676326031360).

- **TO DO** *(consider helping\!)*
- **TO DO** *(consider helping!)*

![](https://pbs.twimg.com/media/Erdge3AXIAcisZj.png)

Expand All @@ -431,7 +436,7 @@ and
## PsychoPy Integration

Pyllusion can be easily integrated into
[PsychoPy](https://www.psychopy.org/) for running experiments as well\!
[PsychoPy](https://www.psychopy.org/) for running experiments as well!

# Load packages
import pyllusion
Expand Down
Binary file modified docs/img/README_contrast1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_delboeuf1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_ebbinghaus1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_mullerlyer1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_pareidolia.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_poggendorff1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_white1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/README_zollner1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pyllusion/Delboeuf/Delboeuf.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def get_parameters(self):
- **Illusion_Type** : `Congruent` if the illusion enhances the perception of the objective difference in the illusion, and `Incongruent` if it reduces the perceived difference.
- **Size_Inner_Left** : Size of the inner left circle.
- **Size_Inner_Right** : Size of the inner right circle.
- **Sine_Inner_Difference** : Difference in areas of the left and right inner circles.
- **Size_Inner_Difference** : Difference in areas of the left and right inner circles (signed, positive when the left circle is larger).
- **Size_Outer_Left** : Size of the outer left rim.
- **Size_Outer_Right** : Size of the outer right rim.
- **Distance** : Distance between the circles, by modifying `distance` of delboeuf_parameters().
Expand Down
9 changes: 8 additions & 1 deletion pyllusion/Delboeuf/delboeuf_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,17 @@ def _delboeuf_parameters_sizeinner(difference=0, size_min=0.25):
inner_size_right = size_bigger
inner_size_left = size_min

# Signed difference between the areas of the two inner circles, positive when the left one is
# larger. Note the parentheses: written as `np.pi * a**2 / np.pi * b**2` this evaluates left to
# right as `(pi * a**2 / pi) * b**2`, i.e. the product of the squared radii, which is what this
# returned before.
area_left = np.pi * (inner_size_left / 2) ** 2
area_right = np.pi * (inner_size_right / 2) ** 2

parameters = {
"Difference": difference,
"Size_Inner_Left": inner_size_left,
"Size_Inner_Right": inner_size_right,
"Size_Inner_Difference": np.pi * (size_bigger / 2) ** 2 / np.pi * (size_min / 2) ** 2,
"Size_Inner_Difference": area_left - area_right,
}
return parameters
2 changes: 1 addition & 1 deletion pyllusion/Ebbinghaus/Ebbinghaus.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def get_parameters(self):
- **Illusion_Type** : `Congruent` if the illusion enhances the perception of the objective difference in the illusion, and `Incongruent` if it reduces the perceived difference.
- **Size_Inner_Left** : Size of the inner left circle.
- **Size_Inner_Right** : Size of the inner right circle.
- **Sine_Inner_Difference** : Difference in areas of the left and right inner circles.
- **Size_Inner_Difference** : Difference in areas of the left and right inner circles (signed, positive when the left circle is larger).
- **Size_Outer_Left** : Size of the surrounding circles around the left inner circle.
- **Size_Outer_Right** : Size of the surrounding circles around the right inner circle.
- **Distance**: Distance between the circles, by modifying `distance` of ebbinghaus_parameters().
Expand Down
2 changes: 1 addition & 1 deletion pyllusion/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
The Pyllusion module.
"""
__version__ = "1.4"
__version__ = "1.5"

from .Autostereogram import *

Expand Down
25 changes: 25 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import numpy as np
import pytest

import pyllusion


Expand Down Expand Up @@ -498,3 +500,26 @@ def test_analyze_luminance():

out = pyllusion.analyze_luminance(image, average=False)
assert out["Luminance"].shape == (50, 50)


def test_size_inner_difference():
# `Size_Inner_Difference` is the signed difference between the areas of the two inner circles.
# It used to be mis-parenthesised (`pi * a**2 / pi * b**2`, which is the product of the squared
# radii) and returned 0.000244140625 for two circles of identical size.
for illusion in [pyllusion.Delboeuf, pyllusion.Ebbinghaus]:
# Identical circles -> no difference
parameters = illusion(difference=0).get_parameters()
assert parameters["Size_Inner_Difference"] == 0

# Positive difference -> left circle larger -> positive area difference
parameters = illusion(difference=1).get_parameters()
area_left = np.pi * (parameters["Size_Inner_Left"] / 2) ** 2
area_right = np.pi * (parameters["Size_Inner_Right"] / 2) ** 2
assert parameters["Size_Inner_Difference"] == pytest.approx(area_left - area_right)
assert parameters["Size_Inner_Difference"] > 0
# difference=1 doubles the area, as the size is scaled by sqrt(1 + abs(difference))
assert area_left == pytest.approx(2 * area_right)

# Sign follows the side the larger circle is on
parameters = illusion(difference=-1).get_parameters()
assert parameters["Size_Inner_Difference"] < 0
Loading