-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathblkavg_rewrite.py
144 lines (106 loc) · 3.79 KB
/
blkavg_rewrite.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
"""
Python implementation of IRAF `blkavg` task.
Examples
--------
>>> import blkavg_rewrite
>>> blkavg_rewrite.test()
"""
from __future__ import division, print_function
import numpy as np
from numpy.lib.stride_tricks import as_strided
def blkavg2d(in_arr, blockshape):
"""
Simple block average on 2-D array.
.. notes::
#. This is the slow version.
#. Only works when array is even divided by block size.
Parameters
----------
in_arr: array_like
2-D input array.
blockshape: tuple of int
Blocking factors for Y and X.
Returns
-------
out_arr: array_like
Block averaged array with smaller size.
TODO
----
Perry Greenfield: To avoid loops, can use Numpy + filter
and then only take certain pix from filtered array as
final result.
"""
yblock, xblock = blockshape
# Calculate new dimensions
x_bin = in_arr.shape[1] / xblock
y_bin = in_arr.shape[0] / yblock
out_arr = np.zeros((y_bin, x_bin))
# Average each block
for j, y1 in enumerate(range(0, in_arr.shape[0], yblock)):
y2 = y1 + yblock
for i, x1 in enumerate(range(0, in_arr.shape[1], xblock)):
x2 = x1 + xblock
out_arr[j, i] = in_arr[y1:y2, x1:x2].mean()
return out_arr
# Written by Erik Bray using codes from
# https://svn.stsci.edu/trac/ssb/stsci_python/browser/stsci.image/branches/blkavg-rewrite/stsci/image/_image.py
def blkavg(array, blockshape):
blocks = blockview(array, blockshape)
axes = range(len(blocks.shape) - 1,
len(blocks.shape) - len(array.shape) - 1, -1)
means = np.apply_over_axes(np.mean, blocks, axes)
# Drop the extra dimensions
return means.reshape(blocks.shape[:len(array.shape)])[:-1, :-1]
def blockview(array, blocks):
if len(blocks) < len(array.shape):
# Extend the block list so that any dimensions not explicitly given are
# 1 by default
blocks = blocks + ((1,) * (len(array.shape) - len(blocks)))
elif len(blocks) > len(array.shape):
raise ValueError('more blocks specified than dimensions in the '
'input array')
original_shape = array.shape
expanded_shape = tuple(a + b - (a % b)
for a, b in zip(original_shape, blocks))
if expanded_shape != original_shape:
# We have to make a new expanded array that can be evenly divided by
# the block size. The expanded array has NaNs on the borders and will
# be returned as a masked array
new_array = np.empty(expanded_shape)
new_array.fill(np.nan)
new_array[tuple(slice(dim) for dim in original_shape)] = array
array = new_array
# Number of blocks in each axis
nblocks = tuple(array.shape[n] / blocks[n] for n in range(len(blocks)))
shape = nblocks + blocks
strides = (tuple(array.strides[n] * blocks[n]
for n in range(len(blocks))) + array.strides)
blocked = as_strided(array, shape, strides)
if original_shape != expanded_shape:
return np.ma.masked_invalid(blocked)
else:
return blocked
def test():
import matplotlib.pyplot as plt
import time
naxis1 = 2070
naxis2 = 2046
blockshape = (6, 6)
a = np.arange(naxis1 * naxis2).reshape(naxis2, naxis1)
t1 = time.time()
b = blkavg(a, blockshape)
t2 = time.time()
print('blkavg took {} s'.format(t2 - t1))
t1 = time.time()
c = blkavg2d(a, blockshape)
t2 = time.time()
print('blkavg2d took {} s'.format(t2 - t1))
np.testing.assert_array_almost_equal(c, b)
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
ax1.imshow(a)
ax1.set_title('Original')
ax2.imshow(b)
ax2.set_title('Block averaged by {}x{}'.format(*blockshape))
plt.draw()