问题
When applying a rank filter with ImageMagick such as erode or delate or median, it takes the minimum (erode
) or maximum (dilate
) or middle (median
) value of all pixels within a certain radius or custom shape around each source pixel.
Is it also possible to take a custom rank of the surrounding pixel values? For example when using a 5x5 square, with the erode
or dilate
or median
filters, respectively the lowest, highest, of middle value of the 25 pixels surrounding each pixel is taken. What I'm looking for is a way to take the 8th or 23rd value, for example.
Perhaps this could be expressed as a percentile, e.g. take the 20% percentile value of an arbitrary area surrounding each pixel. Which in case of a 7x7 square would be the 0.2*7*7 = the 9th value out of the total 49 in order.
The erode
, dilate
and median
filters would correspond to the 0%, 100%, and 50% percentiles, respectively.
回答1:
I can't think of a way to do that easily in ImageMagick - other than compiling in your own "process module". The best introduction to that is by snibgo
and actually for Windows - see the sortpixels.c
example here
One tool that can do this simply from the command-line is libvips with its im_rank()
function.
So, if you want index 8 from a sorted list of 5x5 neighbours, you could do:
vips im_rank input.png result.png 5 5 8
I did a quick test by generating a random image with ImageMagick and choosing successively greater values for index
and the output images got successively brighter - but that was the sum-total of my testing. I have no reason to believe it will not work - it is an excellent library and very fast and frugal.
If you can live with Python, you can do this:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
from scipy.ndimage import generic_filter
from scipy import stats
# Modal filter
def modal(P):
"""
We receive P[0]..P[8] with the pixels in the 3x3 surrounding window
Sort neighbours and take N'th in list
"""
N = 3
P.sort()
return P[N]
# Open image and make into Numpy array
im = Image.open('image.png').convert('L')
im = np.array(im)
# Run modal filter, change filter size here
result = generic_filter(im, modal, (3, 3))
# Save result
Image.fromarray(result).save('result.png')
You can change the filter size/shape to, say 5x5 by changing this line:
result = generic_filter(im, modal, (5, 5))
And as it is, it will take the third smallest neighbour, as counting starts from 0. So, use N=0
if you want the minimum in the 3x3 neighbourhood, or N=8
if you want the maximum in a 3x3 neighbourhood.
Another option might be to use CImg which is a simple-to-use, header-only C++ library (no DLLs/libXXX.a/libXXX.so files) that can actually read and write PGM files natively without needing anything external. So you could run an ImageMagick command and cause it to write a PGM file on stdout
and read that in using CImg, process it and write it out filtered again. That would probably be easier than writing an ImageMagick "process module" and you wouldn't need to build ImageMagick from source anew for every release.
Keywords: ImageMagick, vips, libvips, filter, rank, ranking, median, dilate, erode, window, 3x3, 5x5, NxN, percentile, Python, PIL, Pillow, image, image processing.
来源:https://stackoverflow.com/questions/55557873/imagemagick-custom-rank-filter-like-erode-or-dilate-or-median-but-a-different