As it was already posted multiple times: The problem comes from an inaccurate definition of correlation in your application.
The Pearson correlation coefficient does require the data to be
- centered, ie the mean must be subtracted
- normalized, ie the data must be divided by the standard deviation
This centering and normalization must be done for the mask as well for each sub-matrix of your larger matrix.
In your example, you would end up with a correlation matrix as:
$\left(
\begin{matrix}
0.0&0.0&0.0\\
-1.0&-0.577&0.0\\
1.0&0.577&0.0
\end{matrix}
\right)
$
Lets have a look at the matrix entries:
0.0 They are there, since there are sub-matrices with all ones in them. The mean of such a matrix is 1 and therefore it is centered to the corresponding matrix with only zeros. Hence the correlation is zero.
1.0 is the correlation that you would like to see. The corresponding sub-matrix fits the mask perfectly, hence full correlation, i.e. 1
-1.0 is the correlation of the sub-matrix that fits the mask, but the sub-matrix entries are switched (i.e. the sub-matrix is 0 where the mask is 1 and vice versa). This is a perfect anti-correlation, i.e. -1
The 0.577 cases: They occur where the sub-matrix deviates from the mask by one number (i.e. there is a 1 where the mask has a 0 and vice versa). It also happens to be a symmetric case in your example, therefore there is a pair that correlates positively with one switch and one that correlates negatively with a corresponding switch.
Here is a pretty ugly brute-force code to illustrate it. It is neither optimized in any way, nor tested for any other case than yours. I hope it is somewhat correct ;)
import numpy as np
In [2]:
img = np.ones([4,4])
img[2,0:2] = 0.0
print(img)
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[0. 0. 1. 1.]
[1. 1. 1. 1.]]
In [3]:
mask = np.ones([2,2])
mask[0,0:2] = 0.0
print(mask)
[[0. 0.]
[1. 1.]]
In [4]:
def simple_convolve(img, mask):
max_row = img.shape[0] - mask.shape[0] + 1
max_col = img.shape[1] - mask.shape[1] + 1
output = np.zeros([max_row, max_col])
for curr_row in range(0, max_row):
for curr_col in range(0, max_col):
for curr_mask_row in range(0, mask.shape[0]):
for curr_mask_col in range(0, mask.shape[1]):
output[curr_row, curr_col] += img[curr_row + curr_mask_row, curr_col + curr_mask_col] * mask[curr_mask_row, curr_mask_col]
return output
def better_convolve(img, mask):
max_row = img.shape[0] - mask.shape[0] + 1
max_col = img.shape[1] - mask.shape[1] + 1
output = np.zeros([max_row, max_col])
mask_mean = mask.mean()
mask_sigma = (((mask - mask_mean)**2).sum())**0.5
mask = mask - mask_mean
mask = mask/mask_sigma
curr_img_mean = 0.0
curr_img_sigma = 0.0
for curr_row in range(0, max_row):
for curr_col in range(0, max_col):
curr_img_mean = 0.0
curr_img_sigma = 0.0
# Compute the mean value of the sub-image of img
ctr = 0.0
for curr_mask_row in range(0, mask.shape[0]):
for curr_mask_col in range(0, mask.shape[1]):
ctr = ctr + 1.0
curr_img_mean += img[curr_row + curr_mask_row, curr_col + curr_mask_col]
curr_img_mean = curr_img_mean / ctr
# Compute the std value of the sub-image of img
ctr = 0.0
for curr_mask_row in range(0, mask.shape[0]):
for curr_mask_col in range(0, mask.shape[1]):
ctr = ctr + 1.0
curr_img_sigma += (img[curr_row + curr_mask_row, curr_col + curr_mask_col] - curr_img_mean)**2.0
curr_img_sigma = curr_img_sigma**0.5
for curr_mask_row in range(0, mask.shape[0]):
for curr_mask_col in range(0, mask.shape[1]):
x = (img[curr_row + curr_mask_row, curr_col + curr_mask_col] - curr_img_mean)
if curr_img_sigma == 0.0:
x = 0.0
else:
x = x/curr_img_sigma
output[curr_row, curr_col] += x * mask[curr_mask_row, curr_mask_col]
return output
In [5]:
simple_convolve(img, mask)
Out[5]:
array([[2., 2., 2.],
[0., 1., 2.],
[2., 2., 2.]])
In [6]:
better_convolve(img, mask)
Out[6]:
array([[ 0. , 0. , 0. ],
[-1. , -0.57735027, 0. ],
[ 1. , 0.57735027, 0. ]])