forked from jamesmcm/StimScripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
183 lines (155 loc) · 5.13 KB
/
utils.py
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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# ./mondrian.py
#
# (c) 2012 James McMurray, Konstantin Sering, Nora Umbach
# <colorlab[at]psycho.uni-tuebingen.de>
#
# GPL 3.0+ or (cc) by-sa (http://creativecommons.org/licenses/by-sa/3.0/)
#
# content: produce mondrian stimuli
#
# input: --
# output: --
#
# created
# last mod 2012-10-30 15:48 KS
import numpy as np
try:
import Image
except ImportError:
print "Could not import Image, utils.write_array_to_image will not work."
Image = None
def write_array_to_image(filename, arr):
"""
Save a 2D numpy array as a grayscale image file.
Parameters
----------
filename : str
full path to the file to be creaated.
arr : 2D numpy array
The data to be stored in the image. Values will be cropped to
[0,255].
"""
if Image:
imsize = arr.shape
im = Image.new('L', (imsize[1], imsize[0]))
im.putdata(arr.flatten())
im.save(filename)
def luminance2munsell(lum_values, reference_white):
"""
Transform luminance values into Munsell values.
The luminance values do not have to correspond to specific units, as long
as they are in the same unit as the reference white, because Munsell values
are a perceptually uniform scale of relative luminances.
Parameters
----------
lum_values : numpy-array
reference_white : number
Returns
-------
munsell_values : numpy-array
Reference
---------
H. Pauli, "Proposed extension of the CIE recommendation
on 'Uniform color spaces, color difference equations, and metric color
terms'," J. Opt. Soc. Am. 66, 866-867 (1976)
"""
x = lum_values / float(reference_white)
idx = x <= (6. / 29) ** 3
y1 = 841. / 108 * x[idx] + 4. / 29
y2 = x[~idx] ** (1. / 3)
y = np.empty(x.shape)
y[idx] = y1
y[~idx] = y2
return 11.6 * y - 1.6
def munsell2luminance(munsell_values, reference_white):
"""
Transform Munsell values to luminance values.
The luminance values will be in the same unit as the reference white, which
can be arbitrary as long as the scale is linear.
Parameters
----------
munsell_values : numpy-array
reference_white : number
Returns
-------
lum_values : numpy-array
Reference
---------
H. Pauli, "Proposed extension of the CIE recommendation
on 'Uniform color spaces, color difference equations, and metric color
terms'," J. Opt. Soc. Am. 66, 866-867 (1976)
"""
lum_values = (munsell_values + 1.6) / 11.6
idx = lum_values <= 6. / 29
lum_values[idx] = (lum_values[idx] - 4. / 29) / 841 * 108
lum_values[~idx] **= 3
return lum_values * reference_white
def degrees_to_pixels(degrees, ppd):
"""
convert degrees of visual angle to pixels, given the number of pixels in
1deg of visual angle.
Parameters
----------
degrees : number or ndarray
the degree values to be converted.
ppd : number
the number of pixels in the central 1 degree of visual angle.
Returns
-------
pixels : number or ndarray
"""
return np.tan(np.radians(degrees / 2.)) / np.tan(np.radians(.5)) * ppd
def pad_array(arr, amount, pad_value=0):
"""
Pad array with an arbitrary value. So far, only works for 2D arrays.
Parameters
----------
arr : numpy ndarray
the array to be padded
amount : number or numpy ndarray
the amount of padding in each direction. Has to be of shape
len(arr.shape) X 2. the n-th row specifies the amount of padding
to be added to the n-th dimension of arr. The first value is the
amount of padding added before, the second value after the array.
If amount is a single number, it is used for padding in all
directions.
pad_value : number, optional
the value to be padded. Default is 0.
Returns
-------
output : numpy ndarray
the padded array
"""
# if amount is a single number, use it for padding in all directions
if type(amount) is int or type(amount) is float:
amount = np.array(((amount, amount), (amount, amount)))
assert amount.min() >=0
if len(arr.shape) != 2:
raise NotImplementedError(
"pad_array currently only works for 2D arrays")
if amount.sum() == 0:
return arr
output_shape = [x + y.sum() for x, y in zip(arr.shape, amount)]
output = np.ones(output_shape) * pad_value
output[amount[0][0]:output_shape[0] - amount[0][1],
amount[1][0]:output_shape[1] - amount[1][1]] = arr
return output
def resize_array(arr, factor):
"""
Return a copy of an array, resized by the given factor. Every value is
repeated factor[d] times along dimension d.
Parameters
----------
arr : 2D array
the array to be resized
factor : tupel of 2 ints
the resize factor in the y and x dimensions
Returns
-------
An array of shape (arr.shape[0] * factor[0], arr.shape[1] * factor[1])
"""
x_idx = np.arange(0, arr.shape[1], 1. / factor[1]).astype(int)
y_idx = np.arange(0, arr.shape[0], 1. / factor[0]).astype(int)
return arr[:, x_idx][y_idx, :]