I'm looking for a way to efficiently find the band that has the maximum value in a large multi-band image.
For example if we had an image that looked like
band 1 band 2 band 3
[1 2 3] [3 4 5] [0 1 2]
[3 2 1] [1 2 3] [1 0 1]
[4 5 6] [6 7 5] [0 0 7]
I'd like to make something like
bandFind = A.bandmax()
Where bandFind
would look like
band 1 band 2 band 3
[0 0 0] [1 1 1] [0 0 0]
[1 1 0] [0 0 1] [0 0 0]
[0 0 0] [1 1 0] [0 0 1]
Alternatively if I can get an index image I can pretty easily convert it to what I want.
I wrote a function in python that iterates through the bands, accumulating the max value and the index in a buffer, and the converts it to the kind of map shown above, but it doesn't seem very performant on large images.
def pickMax(inImg):
imBandSelect = pyvips.Image.black(inImg.width, inImg.height, bands=1)
imBandMax = pyvips.Image.black(inImg.width, inImg.height, bands=1)
for bandIndex, band in enumerate(inImg.bandsplit()):
runningSelect = band > imBandMax
imBandMax = runningSelect.ifthenelse(band, imBandMax)
imBandSelect = runningSelect.ifthenelse(bandIndex, imBandSelect)
bandList = [ (imBandSelect == bi) / 255.0 for bi in range(inImg.bands) ]
return bandList[0].bandjoin(bandList[1:])
Update:
Thanks to @jcupitt I tried this version of the code using bandrank
:
def pickMaxUchar(inImg):
short = (inImg.cast(pyvips.enums.BandFormat.USHORT)) << 8
index = pyvips.Image.black(short.width, short.height, bands=1).bandjoin_const([b for b in range(1, inImg.bands)]).cast(pyvips.enums.BandFormat.UCHAR)
combo = short | index
list = combo.bandsplit()
ranked = list[0].bandrank(list[1:], index=inImg.bands-1)
rankedindex = (ranked & 255).cast(pyvips.enums.BandFormat.UCHAR)
bandList = [ (rankedindex == bi) / 255.0 for bi in range(inImg.bands) ]
return bandList[0].bandjoin(bandList[1:])
This now assumes the input is a char, where in my original function the input was float. Maybe there's a way to do this without re-casting the data but as I've implemented it there's zero speedup relative to my "naive" code above, so perhaps this just can't be accelerated any further.