imagej / imagej-ops

ImageJ Ops: "Write once, run anywhere" image processing
https://imagej.net/libs/imagej-ops
BSD 2-Clause "Simplified" License
88 stars 42 forks source link

Yen threshold differs from IJ1 #636

Open hinerm opened 3 years ago

hinerm commented 3 years ago

Sample Data

When using Image > Adjust > Threshold... and selecting Yen to get the ImageJ 1.x threshold on a test image, we get: image

Running a simple groovy script to threshold with ops

#@ OpService ops
#@ ImgPlus inputData

return ops.threshold().yen(inputData)

on the same image produces a much more aggressively thresholded image: image

I don't think this is just a different in image type because if I convert the image to 8-bit and run the IJ1 auto threshold it's actually even more generous:

image

However I can produce a similar image in the IJ1 auto threshold by operating on the 16-bit image and turning up the minValue cutoff: image

It looks like the issue is that Ops is computing the min/max for the histogram off the 16-bit input but then always converts the output to 8-bit, resulting in a too-high threshold.

First converting the test image to 8-bit and then running the above script results in a reasonable output: image

hinerm commented 3 years ago

Note however that performing the conversion with ops produces yet a different result:

#@ OpService ops
#@ ImgPlus inputData

converted = ops.convert().int8(inputData)
return ops.threshold().yen(converted)

image

imagejan commented 3 years ago

You should convert to unsigned 8-bit when converting with ops, no?

converted = ops.convert().uint8(inputData)
hinerm commented 3 years ago

You should convert to unsigned 8-bit when converting with ops, no?

@imagejan honestly I don't know. Here's uint8 version - it just looked even farther from the IJ1 version:

image

imagejan commented 3 years ago

Ah yes, that's because convert doesn't scale the intensity values at all, and high values simply "wrap around" the 8-bit limit.