Why does ImageHistogram not respect the image wave type exactly?

I am not understanding something about the ImageHistogram operation.

I have an 8 bit unsigned image of only one channel (red) from a color image. This should contain 256 values of "gray" from the range starting at 0 (zero) and going to the end of 255 in increments of 1.

I run ImageHistogram on this image. I create a double float 64 bit wave with 256 rows. The Delta value is NOT 1.

Shouldn't the histogram operation create a (double float 64 bit) wave with *255 rows* having a Delta value of 1?

I am using IP 8.03.

OK. We should have 256 rows for grays 0 to 255 (that zero got me). I am however still confused why the DELTA is not exactly 1.

Just to say I can reproduce what you report. In my test, delta is 0.996094 and not 1. I note that 255/256 = 0.996094. There are 255 intervals but 256 values. I guess this is where the problem lies, but the delta should surely be 1.

As an update, I have also tested ImageHistogram/I on a 16bit unsigned image. I get a histogram wave that is 65536 points long but it has a Delta of 0.0347443. I would have expected Delta = 1 here too.

Apart from the special case of 8-bit, the documentation makes it sound like the delta would be set by the min and max of the pixel values: "If the data type of imageMatrix  is single byte, the histogram will have 256 bins from 0 to 255. Otherwise, the 256 bins will be distributed between the minimum and maximum values encountered in the data." A quick test shows that to be the case with unsigned 16-bit.

make/o/w/u/n=(10,10) unsigned16bit
imagehistogram unsigned16bit
•unsigned16bit= p > 5 ? 5 : 10
print (10-5)/256,dimdelta(W_imagehist,0),(10-5)/256/dimdelta(W_imagehist,0)
 //prints 0.0195312  0.0195312  1

//and for the /i case:
imagehistogram/i unsigned16bit
print (10-5)/65536,dimdelta(W_imagehist,0),(10-5)/65536/dimdelta(W_imagehist,0)
  //prints 7.62939e-05  7.62939e-05  1

As for the 8-bit case, in my hands the delta is 255/256, which is at least consistent with the unsigned 16-bit case, since there also delta=range/numBins. This seems to cause the x value to correspond to the start of the current bin, and the next x value corresponds to its end, which would make for a reasonable bar plot.

I can understand the case for a histogram to have the x value calculated as range/numBins to create the "center" of the bin. However, I might expect an image histogram to respect that x should be the intensity value at which the counts (y) are taken on the image.

For an 8-bit image, no problem. I can resort to a SetScale operation or use point number rather than x. I use the former approach in my code.

For a 16-bit image, I am still at a bit of a cross-roads. It seems that Igor Pro and Fiji both fall back to using an 8bit histogram bin approach on 16bit images. So, while I can generate a 16bit histogram on a 16bit image using ImageHistogram/I, that histogram does me no good when I want to apply ImageThreshold.

By comparison

-> Fiji generates an 8bit histogram on an 8bit image with bin separations at 1 and uses this in its image threshold operations. Igor Pro generates an 8bit histogram on an 8bit image with bin separations at 255/256 and uses this in ImageThreshold.

-> Fiji generates an 8bit histogram on a 16bit image with bin separations at 8.895 and uses this in its image threshold operations. Igor Pro generates an 8bit histogram on an 16bit image with bin separations at 8.89453 and uses this in ImageThreshold.

After some playing around further with the ImageHistogram and ImageThreshold operations, I have requests to change the default behavior and to add a new flag (/B).

Let me give some background.

I am testing two identical images, one at 8bit and one at 16bit. The images have a feature sitting on a dark background. I want to use the histogram to separate out the feature. The starting point of the histogram distribution in the feature overlays the ending point of the histogram distribution of the background. Here are the two problems that I face with the way ImageHistogram and ImageThreshold currently operate:

* When I create a histogram on the 8bit image over the full image, the step size is not 1 (as I believe it should be)
* When I create a histogram on the 8bit image over an RoI, the step size does not change (it is the same as the step size for the full image) BUT, when I create a histogram on a 16bit image over an RoI, the step size is different (it is scaled to the data range).

To address the two problems, I propose changes in this way ...

ImageHistogram 8bitimage // 256 point histogram on full image with bin size = 1
ImageHistogram/R=... 8bitimage // 256 point histogram on RoI with bin size = 1
ImageHistogram/R=.../B=(N) 8bitimage // 256 point histogram on RoI with bin size = N
ImageHistogram/R=.../B=0 8bitimage // 256 point histogram on RoI with bin size scaled between min/max of image pixel values
ImageHistogram/I 8bitimage // 2^16 point histogram with bin size = 1
... // other options with /R and /B flag

ImageHistogram 16bitimage // 256 point histogram on full image with bin size as required
ImageHistogram/I 16bitimage // 2^16 point histogram on full image with bin size = 1
... // other options with /R and /B flag

I propose the ImageThreshold operation should respect these same settings.

Hello JJW,

1.  You are correct: the ImageHistogram delta is inconsistent.  I have changed that for the next build.

2.  I would recommend at least testing some of the automatic thresholding methods before attempting to use the histogram to perform your own segmentation.  Some of the automatic methods are pretty effective.  If you have an overlap in the histogram you are going to experience difficulties with any method that performs segmentation using a threshold.

 

A.G.

Thanks AG. I can’t speak for whether the delta when doing a 16bit image is incorrect too. The built in threshold operations do give useful results. I also look at the derivative of the histogram to help define where the threshold separation could be set between the background and features. And I flip back and forth between a region only with background and one with features. My biggest confusion in going between what is see in the built in threshold panel and what is see with my own display of the image histogram is to translate the bin sizes properly. After many trials, I realized that I need to have the histogram of an RoI not use the min/max scaling and instead stay fixed at the full image grayscale range (2^8 or 2^16) to be able to work effectively. Hence my request for the additional flag to define the bin size on both ImageHistogram and ImageThreshold.

Hello JJW,

1.  your description sounds like the bi-modal thresholding method (see ImageThreshold/M=2).

2.   you can already do what you want using the following steps:

Create your ROI mask with 0 for particle pixels and 1 outside.  Then execute:

MatrixOP/o aa=setNaNs(imageWave,M_ROIMask)

Redimension/N=(DimSize(imageWave,0)*dimSize(imageWave,1)) aa

WaveTransform zapNaNs aa

Histogram [flags...] aa

Actually you can skip the redimension and WaveTransform because the Histogram operation ignores NaNs.

I hope this helps,

 

AG