Posts: 3,458
Threads: 101
Joined: Sep 2016
I'm working on a project where I'm using numpy for generative art, and I've got a question with how to filter an array without copying the data (so the original array will still be edited by a change to the filtered version). It works great for index slices, but apparently copies the data if you slice using another array as a mask?
I'm hoping this is actually super simple, and I just don't know what word numpy calls it.
import numpy as np
from PIL import Image
# small image, all black
df = np.zeros((100, 100, 3), dtype=np.uint8)
mask = np.zeros((100, 100), dtype=bool)
band = 10
for rndx, row in enumerate(mask):
for cndx, col in enumerate(mask[rndx]):
if abs(cndx - rndx) <= band:
mask[rndx, cndx] = True
use_working_method = False
if use_working_method:
# red diagonal line
df[mask, 0] = 255
else:
view = df[mask, :]
# ...sometime later
view[:, 0] = 255
# for reference, this does work:
view1 = df[45:55, 45:55, :]
view1[:, :, 2] = 255 # blue, centered
im = Image.fromarray(df)
im.show()
Posts: 4,786
Threads: 76
Joined: Jan 2018
I think you could get something by using numpy's MaskedArray type, something along the line of
import numpy as np
import numpy.ma as ma
N = 10
band = 3
x = np.tile(np.arange(N, dtype=int), (N, 1))
mask = (abs(x-x.T) <= band)
print(mask)
m = np.repeat(mask[:,:,np.newaxis], 3, axis=2)
df = np.zeros((N, N, 3), dtype=np.uint8)
view = ma.masked_array(df, mask=m)
view[:, 0] = 255
print(df)
Posts: 3,458
Threads: 101
Joined: Sep 2016
That sounds great, but I'm not sure it's actually working? That modification to the view was applied to the entire array, not just the masked portion. But the MaskedArray module is new to me, so I'll keep playing around with it and see if I can figure it out. Thanks :)
>>> view.size
300
>>> df.size
300
Posts: 3,458
Threads: 101
Joined: Sep 2016
Dec-04-2021, 10:28 PM
(This post was last modified: Dec-04-2021, 10:28 PM by nilamo.)
I've got it working :)
The mask maintains all data, and you need to specifically filter out the masked values when altering the view. It means I'll need to rewrite the rest of my code to work with masks, but at least now I can do what I wanted :)
Working code to select a diagonal bar of the image, and paint just one channel of the selection: import numpy as np
import numpy.ma as ma
from PIL import Image
def get_array():
# small image, all black
df = np.zeros((100, 100, 3), dtype=np.uint8)
# convert to masked array, without masking anything
return ma.array(df)
def select_data(df):
mask = np.ones(df.shape[0:2], dtype=bool)
band = 10
for rndx, row in enumerate(mask):
for cndx, col in enumerate(mask[rndx]):
if abs(cndx - rndx) <= band:
mask[rndx, cndx] = False
mask = np.repeat(mask[:, :, np.newaxis], 3, 2)
view = ma.MaskedArray(df, mask=mask)
return view
def alter_data(df):
# only change red
channels = df.mask.copy()
channels[:, :, :] = True
channels[:, :, 0] = False
view = ma.MaskedArray(df, mask=channels)
view[~view.mask] = 255
def show_image(df):
im = Image.fromarray(df)
im.show()
def main():
df = get_array()
work_set = select_data(df)
alter_data(work_set)
show_image(df)
if __name__ == "__main__":
main() This was needed, because I'm working on a generative algorithm that combines selectors together, and then makes changes to the selected regions. For example, I might combine a Diagonal selector with a Blinds selector to get rectangular boxes in a diagonal pattern, evenly spaced, and equal widths, that I then paint over. It's really just an excuse for me to play with numpy, lol
Gribouillis likes this post
|