Hello fellow programmers,
I am currently doing this years advent of code. On day 9 I need to find out wether a pixel in an image has the smallest value of the adjacent pixels.
I first solved this very intuitively and crudely, but now after looking at it for a while was wondering if there was a way to define a convolution kernel for opencv's filter2D
-function or another appropriate library I could try where I can define the kernel as a function, such that I can not only perform linear transformations, but also others.
In this specific example I was thinking about a kernel, that can tell if the middle pixel has the lowest value. Maybe there even is a way to do this using linear transformations, however i am not able to find it.
Any help is appreciated.
CodePudding user response:
I found a solution myself simply using scipy.ndimage.generic_filter
import numpy as np
from scipy.ndimage import generic_filter
# First define the footprint of the filter you are going to use:
footprint = np.array([[False, True, False],
[True, True, True],
[False, True, False]])
# Then define the filter you want to use:
def lowpoint_kernel(a):
return 0 if a[2] < a[0] and a[2] < a[1] and a[2] < a[3] and a[2] < a[4] else 10
# Get your input here
image = ...
# Pad your image
image = np.pad(image, 1, constant_values=10)
lowpoints = generic_filter(image, lowpoint_kernel, footprint=footprint)
lowpoint_indices = lowpoints == 0
This might very well be a very complicated solution, but maybe someone can find this useful. See my full solution here.
CodePudding user response:
if you want to use OpenCV functions to do so, you can first apply an erosion morphology operation (cv::erode) (with your desire windows size and binary mask) to obtain the minimum at each pixel neighborhood and then compare the resulted image with the original one using cv::compare method or the like.
To exclude center pixel in minimum (erosion), you can pass a customized binary kernel to cv::erode, where the center pixel is zero in the generated kernel.
All in all, you should do something like this:
- cv::Mat kernel = cv::Mat::ones(3,3,CV_8UC1);
kernel.at(1, 1)=0; - cv::erode(src, dst_min, kernel);
- cv::compare(src, dst_min, dst, cv::CMP_EQ);
Having said that the only limitation of this method is that the minimum operation is computed over non-zero neighbors in the cv::erode function. If this is not your desire effect, you can add a bias to the src, an then subtract it!
Last but not least, if you are working with cpu (and not gpu), I do recommend cv::ParallelLoopBody to implement all of the procedure in parallel with just one call (memory access).