5
$\begingroup$

Binarize seems to be giving me incorrect output.

Overall, I am trying to identify characteristics of different objects in my images. I have a label image that corresponds to the positions of each of the different objects. For example, all pixels corresponding to object 1 have value 1; pixels corresponding to object 2 have value 2, etc. Background has value 0.

Please find an example 16-bit label image at https://imgur.com/GuM1AFM (it's not scaled, so it looks all black). Here is a colorized version in which each pixel value corresponds to a different color (and 0 is black):

example label image

In order to identify all the pixels corresponding to object x, I have tried two ways (labelImage is the label image from above), one using Binarize and one using PixelValuePositions. Let's say I'm looking for object #136. Then with Binarize, I use

Binarize[labelImage, {136/2^16, 136/2^16}]

which outputs

Binarize output image

or with PixelValuePositions, I use

ppos = PixelValuePositions[labelImage, 136/2^16];
ReplacePixelValue[labelImage, ppos -> Cyan] // ImageAdjust

which outputs

PixelValuePositions

These are clearly different objects. When I look at the original image in ImageJ, the PixelValuePositions identifies the correct object, whereas Binarize identifies object #137. Any ideas on what's going on here?

UPDATE 1: (Another thought—I had forgotten about the built-in functions MorphologicalComponents and ComponentMeasurements, which can do much of what I’d like to do.)

UPDATE 2: I have been trying to use SelectComponents to get a label image containing only an object of interest. This seems to also give incorrect results in the same way that Binarize does. The command

SelectComponents[labelImage, #Label == 136 &] // ImageAdjust

Gives

SelectComponents output

Finally, this is a tangent, but SelectComponents also seems to be slow. The AbsoluteTiming for the above command is 0.466338, whereas the AbsoluteTiming for the following command using PixelValuePositions is 0.166369.

ReplacePixelValue[
  ConstantImage[0, ImageDimensions[objectIdentitiesImages[[1]]], 
   "Bit16"], 
  PixelValuePositions[labelImage, 136/2^16] -> Cyan] // AbsoluteTiming
$\endgroup$
6
$\begingroup$

It's weird that you get any pixels a tall, because the value 136/2^16 isn't even in the image:

MemberQ[Flatten[DeleteDuplicates[ImageData[img]]], 136/2^16]

False

My guess is that PixelValuePositions and Binarize round the value resp. thresholds to the closest possible pixel values. And (wild guess) Binarize probably takes the Floor of the lower and the Ceiling of the upper threshold, while PixelValuePositions probably rounds to the closest value.

If you use 136/(2^16 - 1) instead (because the highest possible value in a 16 bit image, 2^16-1 is mapped to 1.0), you get the same object.

$\endgroup$
  • 1
    $\begingroup$ Thanks nikie. That sounds like a reasonable hypothesis. Mathematica seems to like rescaling images to values between 0 and 1, which is why I had to divide by 2^16 in the first place and which also seems to complicate things unnecessarily. $\endgroup$ – jeremyc0 Dec 4 '17 at 8:09
  • $\begingroup$ I agree, it's unfortunate that you can't turn off rescaling. You can use imgInteger = img*65535;, though, and only work with integers after that. You just have to be careful because some image processing functions (like ImageAdd, IIRC?) clip the output to the range 0..1 $\endgroup$ – Niki Estner Dec 4 '17 at 8:42
  • 1
    $\begingroup$ You can use, e.g., Clip to preprocess the image. $\endgroup$ – Henrik Schumacher Dec 4 '17 at 8:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.