I am trying to use opencv to solve the following problem. As input, I will have two png files with each pixel set to a value between 0 and 10. For each of the 11 values, I want to see how many pixels of overlap there are between the two input files. For example, say img1.png has pixels (0,0) through (0,26) set to a value of 3. img2.png has pixels (0,2) through (0,30) and (1,0) through (1,5) set to a value of 3. So img1 has 27 pixels set to 3. img2 has 35 pixels set to 3. Of those, there are 25 overlapping pixels, namely, the pixels from (0,2) to (0,26).
What I want is a way of quickly extracting this information: How many pixels overlap for values 0-10? How many pixels does each image have with values 0-10? Using this information, I want to formulate some kind of overlap score for each value.
I know that naively, I could look pixel by pixel at each image, and count this information using accumulators. But it seems like this would be very slow, and opencv allows numpy array indexing for images, so I know that there is probably a way of speeding up these calculations. However, I am not familiar with opencv or numpy, and am not quite sure how to go about this.
Currently looking at this as reference: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_core/py_basic_ops/py_basic_ops.html
Approach #1 : First approach would involve the following steps :
Get the mask of equality between the two arrays (image arrays).
Get the mask of equality between one of the arrays and the range of labels.
Finally get the tensor sum-reduction between the two arrays obtained in previous two steps, giving us the count for all of the labels.
Thus, we would have one vectorized solution ab-using NumPy broadcasting and np.einsum for the tensor sum-reduction, like so -
def overlap_count(a, b, num_label):
eq_mask = a==b
id_mask = a == np.arange(num_label)[:,None, None]
count = np.einsum('ij,aij->a',eq_mask, id_mask.astype(int))
return count
Sample run -
In [95]: a
Out[95]:
array([[0, 1, 2, 1],
[2, 0, 2, 2],
[0, 1, 1, 0]])
In [96]: b
Out[96]:
array([[0, 0, 1, 1],
[1, 1, 1, 0],
[1, 0, 1, 0]])
In [97]: overlap_count(a, b, num_label=3)
Out[97]: array([2, 2, 0])
Approach #2 : We can improve the first approach on memory efficiency and hence performance with np.bincount. The idea being - We could avoid creating the huge array id_mask, by instead doing the count on the scaled version of eq_mask. This scaled version would be scaled by the values of one of the arrays. Now, this would do a lot more counting for the 0 labelled pixels, so we would count for those separately.
Thus, this second approach would look something like this -
def overlap_count_improved(a, b, num_label):
eq_mask = a==b
r = a * eq_mask
count = np.bincount(r.ravel())
count[0] = (eq_mask*(a == 0)).sum()
# or count[0] = np.einsum('ij,ij->', eq_mask, (a==0).astype(int))
return count
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With