Home > Blockchain >  how to change the particuler elements of an array
how to change the particuler elements of an array

Time:09-09

I have an array for an example:

import numpy as np

data=np.array([[4,4,4,0,1,1,1,1,1,1,0,0,0,0,1],
               [3,0,1,0,1,1,1,1,1,1,1,1,1,1,0],
               [6,0,0,0,1,1,1,1,1,1,1,1,1,1,0],
               [2,0,0,0,1,1,1,0,1,0,1,1,1,0,0],
               [2,0,1,0,1,1,1,0,1,0,1,0,1,0,0]])

Requirement : In the data array, if element 1's are consecutive as the square size of ((3,3)) and more than square size no changes. Otherwise, replace element value 1 with zero except the square size.

Expected output :

               [[4 4 4 0 1 1 1 1 1 1 0 0 0 0 0]
                [3 0 0 0 1 1 1 1 1 1 1 1 1 0 0]
                [6 0 0 0 1 1 1 1 1 1 1 1 1 0 0]
                [2 0 0 0 1 1 1 0 0 0 1 1 1 0 0]
                [2 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]

current code:

k = np.ones((3,3))
print(k)

jk=binary_dilation(binary_erosion(data==1, k), k)
print(jk)

current output:

[[False False False False  True  True  True  True  True  True False 
 False False False False]
 [False False False False  True  True  True  True  True  True  True  
  True True False False]
 [False False False False  True  True  True  True  True  True  True  
  True True False False]
 [False False False False  True  True  True False False False  True  
 True True False False]
 [False False False False  True  True  True False False False False 
  False False False False]]

CodePudding user response:

The below proposed solution uses skimage.util.view_as_windows in order to get all of the possible 3x3 views onto the larger array and then looping using them with help of Python loops to set the found hits back to ones after zeroing all the ones in the first step.

It seems that OpenCV, scimage, numpy don't provide a method able to label areas in which a 3x3 square can move around having all the values beneath set to 1's, but I am not sure here. So if you read this and know much about dilations, convolutions, etc. please leave a note pointing me in the right direction allowing to label the area of image by a methond implemented in C-code for better speed on huge arrays.

# https://stackoverflow.com/questions/73649612/how-to-change-the-particuler-elements-of-an-array
# Example array: 
import numpy as np
D_in =np.array([[4,4,4,0,1,1,1,1,1,1,0,0,0,0,1],
                [3,0,1,0,1,1,1,1,1,1,1,1,1,1,0],
                [6,0,0,0,1,1,1,1,1,1,1,1,1,1,0],
                [2,0,0,0,1,1,1,0,1,0,1,1,1,0,0],
                [2,0,1,0,1,1,1,0,1,0,1,0,1,0,0]])
# Requirements: If in the D_in array at least one 3x3 square with 1's 
# is found, replace in D_in all 1's with 0's except the square size and more than square size no changes. 
# Otherwise.
# Expected output :
D_tgt=np.array([[4,4,4,0,1,1,1,1,1,1,0,0,0,0,0],
                [3,0,0,0,1,1,1,1,1,1,1,1,1,0,0],
                [6,0,0,0,1,1,1,1,1,1,1,1,1,0,0],
                [2,0,0,0,1,1,1,0,0,0,1,1,1,0,0],
                [2,0,0,0,1,1,1,0,0,0,0,0,0,0,0]])
# ----------------------------------------------------------------------
import numpy as np
D_out  = np.copy(D_in) # D_out for restoring hit 3x3 ONEs
D_out[D_out==1] = 0     # set all ONEs  to zero 
needle = np.ones((3,3)) # not necessary here, except documentation
# Create all possible 3x3 needle views on larger D_in heystack: 
from skimage.util import view_as_windows as winview
# vvvvvvvvvvvvvvvvvvvvvvvvvvv
all3x3 = winview(D_in,(3,3)) # the CORE of the algorithm
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^
all3x3_rows, all3x3_cols, needle_rows, needle_cols = all3x3.shape
print(f'{all3x3_rows=}, {all3x3_cols=}, {needle_rows=}, {needle_cols=}')
noOfHits = 0 # used also to decide about the output if not found Hits
for row in range(all3x3_rows):
    for col in range(all3x3_cols):
        if np.all(all3x3[row,col,:,:]):
            noOfHits  = 1
            # print(f'3x3 Ones at: {row=}, {col=}')
            D_out[row:row 3, col:col 3] = 1
print('--------------------------')
print(D_in)
print('--------------------------')
if noOfHits > 0:
    print(D_out)
    assert D_out.all() == D_tgt.all() # make sure the result is correct
else: 
    print(D_in)

gives on output:

all3x3_rows=3, all3x3_cols=13, needle_rows=3, needle_cols=3
--------------------------
[[4 4 4 0 1 1 1 1 1 1 0 0 0 0 1]
 [3 0 1 0 1 1 1 1 1 1 1 1 1 1 0]
 [6 0 0 0 1 1 1 1 1 1 1 1 1 1 0]
 [2 0 0 0 1 1 1 0 1 0 1 1 1 0 0]
 [2 0 1 0 1 1 1 0 1 0 1 0 1 0 0]]
--------------------------
[[4 4 4 0 1 1 1 1 1 1 0 0 0 0 0]
 [3 0 0 0 1 1 1 1 1 1 1 1 1 0 0]
 [6 0 0 0 1 1 1 1 1 1 1 1 1 0 0]
 [2 0 0 0 1 1 1 0 0 0 1 1 1 0 0]
 [2 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]

CodePudding user response:

You can use a 2D convolution on the 1s with a 3x3 kernel of 1s to identify the centers of the 3x3 squares, then dilate them and restore the non 1 numbers

from scipy.signal import convolve2d
from scipy.ndimage import binary_dilation

# get 1s (as boolean)
m = data==1

kernel = np.ones((3, 3))

# get centers
conv = convolve2d(m, kernel, mode='same')

# dilate and restore the other numbers
out = np.where(m, binary_dilation(conv == 9, kernel).astype(int), data)

print(out)

Output:

[[4 4 4 0 1 1 1 1 1 1 0 0 0 0 0]
 [3 0 0 0 1 1 1 1 1 1 1 1 1 0 0]
 [6 0 0 0 1 1 1 1 1 1 1 1 1 0 0]
 [2 0 0 0 1 1 1 0 0 0 1 1 1 0 0]
 [2 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]
  • Related