Thread Subject: Detect black pixels in an image

Subject: Detect black pixels in an image

From: Hugh Thompson

Date: 14 Mar, 2010 10:07:05

Message: 1 of 11

Hello,

I have an image and I only want the black pixels. In the end, I need a binary image with all the black pixels in white and everything else black. What I've been doing is something like image_new = image < 40, but this is not very robust. I want my code to work on any image I load. I also tried using the histogram to find the optimal threshold and then dividing this threshold by 2. Then I would do image_new = image < hist_thresh/2. It was actually ok, but I'm still not entirely convinced it's a reliable method. It sometimes gives errors.
I've checked different posts and read the demo by ImageAnalyst which separates the red, blue, and green color bands, but I'm not really sure what to do if I only want black.
Any help would be appreciated!
Hugh

Subject: Detect black pixels in an image

From: John D'Errico

Date: 14 Mar, 2010 10:50:08

Message: 2 of 11

"Hugh Thompson" <nads_2589@hotmail.com> wrote in message <hnicg9$fj0$1@fred.mathworks.com>...
> Hello,
>
> I have an image and I only want the black pixels. In the end, I need a binary image with all the black pixels in white and everything else black. What I've been doing is something like image_new = image < 40, but this is not very robust. I want my code to work on any image I load. I also tried using the histogram to find the optimal threshold and then dividing this threshold by 2. Then I would do image_new = image < hist_thresh/2. It was actually ok, but I'm still not entirely convinced it's a reliable method. It sometimes gives errors.
> I've checked different posts and read the demo by ImageAnalyst which separates the red, blue, and green color bands, but I'm not really sure what to do if I only want black.
> Any help would be appreciated!
> Hugh

Nothing that you do will be perfect, since it sounds
like your definition of black will change with each
image, in context.

You can try my fuzzycolor tool, from the file exchange.

http://www.mathworks.com/matlabcentral/fileexchange/12326

It will return exactly the binary image where a pixel
was that I chose to define as black, or you could
refine that definition yourself.

Alternatively, you could transform your image to
CIE Lab, then take only those pixels that have an L*
less than some threshhold. Or convert to an HSV
color space, and do the same.

HTH,
John

Subject: Detect black pixels in an image

From: ImageAnalyst

Date: 14 Mar, 2010 14:17:22

Message: 3 of 11

Hugh Thompson:
I agree with John in that it seems like your definition of black seems
to change with each image. So you really need some kind of
thresholding algorithm - I assume you want an autothresholding method,
perhaps with some visual verification/acceptance by the user.

image_new = image < hist_thresh/2 should always work as long as you
have a monochrome image. Why doesn't it work? Do you mean is doesn't
"work" because you have chosen a threshold that's not appropriate? If
so, again, you need a good thresholding algorithm. But you should
realize that not every image has a nice flat, uniform background with
contrasty objects sitting on it so a global threshold like you're
doing may not work. What if the objects are on a background that is
like a ramp, or varies wildly depending on where you are in the
image? You may have to do some preprocessing of the image to flatten
the background so you can detect the objects by thresholding. And we
can't make suggestions on that until we see a wide variety of images.
So post some.

And like has been said many times before, if you have control over the
image capture situation, it's far better to get a good image there
than to try fancy algorithms later to try to fix it up.

If you have a color image, John's suggestion of transforming your
color space is a good one. There is a function rgb2hsv() that you can
look into. No such nice and easy function for rgb-to-lab - I don't
know why - but you can do it with the makecform function as described
in this demo on color segmentation:
http://www.mathworks.com/products/image/demos.html?file=/products/demos/shipping/images/ipexfabric.html
Good luck,
ImageAnalyst

Subject: Detect black pixels in an image

From: Hugh Thompson

Date: 14 Mar, 2010 15:10:20

Message: 4 of 11

Thanks, John. The fuzzycolor tool looks like a good option, but how can I make it work for an image to get a '1' in the exact locations where black is detected in the image? Iscolor is returing an mx1 array, just one long line of numbers, mostly zeros as far as I can see. Is there a way I can actually get the image as it is with everything black replaced by 1 and everything else 0, or something like that?
And also, the picture I am using is more or less the same every time, with a few minor changes. I have a picture of a black line on a brown background. The shape of the line might change, or the brown color might be lighter or darker, or there might be a bit more light in the room, etc. But the essence of the image is the same every time. Does this make things any easier??
Thanks again,
Hugh

"John D'Errico" <woodchips@rochester.rr.com> wrote in message <hnif10$l5m$1@fred.mathworks.com>...
> "Hugh Thompson" <nads_2589@hotmail.com> wrote in message <hnicg9$fj0$1@fred.mathworks.com>...
> > Hello,
> >
> > I have an image and I only want the black pixels. In the end, I need a binary image with all the black pixels in white and everything else black. What I've been doing is something like image_new = image < 40, but this is not very robust. I want my code to work on any image I load. I also tried using the histogram to find the optimal threshold and then dividing this threshold by 2. Then I would do image_new = image < hist_thresh/2. It was actually ok, but I'm still not entirely convinced it's a reliable method. It sometimes gives errors.
> > I've checked different posts and read the demo by ImageAnalyst which separates the red, blue, and green color bands, but I'm not really sure what to do if I only want black.
> > Any help would be appreciated!
> > Hugh
>
> Nothing that you do will be perfect, since it sounds
> like your definition of black will change with each
> image, in context.
>
> You can try my fuzzycolor tool, from the file exchange.
>
> http://www.mathworks.com/matlabcentral/fileexchange/12326
>
> It will return exactly the binary image where a pixel
> was that I chose to define as black, or you could
> refine that definition yourself.
>
> Alternatively, you could transform your image to
> CIE Lab, then take only those pixels that have an L*
> less than some threshhold. Or convert to an HSV
> color space, and do the same.
>
> HTH,
> John

Subject: Detect black pixels in an image

From: John D'Errico

Date: 14 Mar, 2010 15:18:05

Message: 5 of 11

"Hugh Thompson" <nads_2589@hotmail.com> wrote in message <hniu8s$8t6$1@fred.mathworks.com>...
> Thanks, John. The fuzzycolor tool looks like a good option, but how can I make it work for an image to get a '1' in the exact locations where black is detected in the image? Iscolor is returing an mx1 array, just one long line of numbers, mostly zeros as far as I can see. Is there a way I can actually get the image as it is with everything black replaced by 1 and everything else 0, or something like that?
> And also, the picture I am using is more or less the same every time, with a few minor changes. I have a picture of a black line on a brown background. The shape of the line might change, or the brown color might be lighter or darker, or there might be a bit more light in the room, etc. But the essence of the image is the same every time. Does this make things any easier??
> Thanks again,
> Hugh
>
> "John D'Errico" <woodchips@rochester.rr.com> wrote in message <hnif10$l5m$1@fred.mathworks.com>...
> > "Hugh Thompson" <nads_2589@hotmail.com> wrote in message <hnicg9$fj0$1@fred.mathworks.com>...
> > > Hello,
> > >
> > > I have an image and I only want the black pixels. In the end, I need a binary image with all the black pixels in white and everything else black. What I've been doing is something like image_new = image < 40, but this is not very robust. I want my code to work on any image I load. I also tried using the histogram to find the optimal threshold and then dividing this threshold by 2. Then I would do image_new = image < hist_thresh/2. It was actually ok, but I'm still not entirely convinced it's a reliable method. It sometimes gives errors.
> > > I've checked different posts and read the demo by ImageAnalyst which separates the red, blue, and green color bands, but I'm not really sure what to do if I only want black.
> > > Any help would be appreciated!
> > > Hugh
> >
> > Nothing that you do will be perfect, since it sounds
> > like your definition of black will change with each
> > image, in context.
> >
> > You can try my fuzzycolor tool, from the file exchange.
> >
> > http://www.mathworks.com/matlabcentral/fileexchange/12326
> >
> > It will return exactly the binary image where a pixel
> > was that I chose to define as black, or you could
> > refine that definition yourself.
> >
> > Alternatively, you could transform your image to
> > CIE Lab, then take only those pixels that have an L*
> > less than some threshhold. Or convert to an HSV
> > color space, and do the same.
> >
> > HTH,
> > John

Subject: Detect black pixels in an image

From: John D'Errico

Date: 14 Mar, 2010 15:23:02

Message: 6 of 11

"Hugh Thompson" <nads_2589@hotmail.com> wrote in message <hniu8s$8t6$1@fred.mathworks.com>...
> Thanks, John. The fuzzycolor tool looks like a good option, but how can I make it work for an image to get a '1' in the exact locations where black is detected in the image? Iscolor is returing an mx1 array, just one long line of numbers, mostly zeros as far as I can see. Is there a way I can actually get the image as it is with everything black replaced by 1 and everything else 0, or something like that?

Sorry, I replied too soon just now. Misclick.

The result of fuzzycolor is a vector. I should have
done a reshape on it to return it to the same size
and shape as your original image. I'll repair that
immediately.

But all you need to do is to use reshape yourself.


> And also, the picture I am using is more or less the same every time, with a few minor changes. I have a picture of a black line on a brown background. The shape of the line might change, or the brown color might be lighter or darker, or there might be a bit more light in the room, etc. But the essence of the image is the same every time. Does this make things any easier??

No. Why should it help?

John

Subject: Detect black pixels in an image

From: ImageAnalyst

Date: 14 Mar, 2010 15:24:02

Message: 7 of 11

Hugh Thompson:
I'll let John answer about the fuzzycolor part but I hope you don't
mind me commenting on the rest. For a black line on a brown
background you'll probably want to look at the red channel only since
that color channel will have the most contrast and information. For
example
redChannel = rgbImage(:,:,1);
binaryImage = redChannel < threshold; % Ignore green and blue
channels.
If you're taking a picture of a room, with lots of clutter in it
(desks, chairs, etc) then I suspect no global threshold will work.
Have you brought this image into something like Photoshop to do
interactive thresholding on it to see if there is ANY threshold that
will work? Again, post your images if you can.

Subject: Detect black pixels in an image

From: Hugh Thompson

Date: 15 Mar, 2010 08:57:04

Message: 8 of 11

Thanks for all the help, John and ImageAnalyst. John, I tried to reshape but the number of elements in iscolor is not the same as the number of pixels in the original image. So I didn't know what to do next.
Unfortunately, I can't post any of the images I'm working on, but the images are basically very simple. Just a black line, brown background. The biggest problem is the different lighting, and the appearance of borders sometimes when the photographer lets in some of the unwanted background by mistake. But we can arrange that there is nothing black in the unwanted background.
I wanted to ask about the threshold routine that I am doing. As I said, it is giving good results, but I'm not really sure exactly why it is working the way it is! So what I do is input any threshold (eg 35) and a pic, then find the histogram, then the average value above and below the input threshold, and then the average of these two. If the new average is equal to the input threshold, stop. Otherwise repeat with the new threshold. Something like this:

[counts, x] = imhist(picbw);

down = 0;
for i=1:threshi
    down = down + counts(i)*x(i);
end

up = 0;
for i=(threshi+1):length(x)
    up = up + counts(i)*x(i);
end

avgdown = down/sum(counts(1:threshi));
avgup = up/sum(counts((threshi+1):length(counts)));
  
threshnew = fix((histdown + histup)/2)

if (abs(threshnew - threshi) < eps)
    disp('early exit')
    thresh = threshnew;
    return;
end
while (abs(threshnew - threshi) > eps)
etc...

Then I take half of the final threshold and somehow it almost always works to get the black line in the middle. What exactly is going on? Is there any way I can build on this to make it more robust/correct??


If not, I repeat the same thing but this time with threshnew as the threshold.
ImageAnalyst <imageanalyst@mailinator.com> wrote in message <1826fac2-5b04-4c08-9e73-5285681a317a@f8g2000yqn.googlegroups.com>...
> Hugh Thompson:
> I'll let John answer about the fuzzycolor part but I hope you don't
> mind me commenting on the rest. For a black line on a brown
> background you'll probably want to look at the red channel only since
> that color channel will have the most contrast and information. For
> example
> redChannel = rgbImage(:,:,1);
> binaryImage = redChannel < threshold; % Ignore green and blue
> channels.
> If you're taking a picture of a room, with lots of clutter in it
> (desks, chairs, etc) then I suspect no global threshold will work.
> Have you brought this image into something like Photoshop to do
> interactive thresholding on it to see if there is ANY threshold that
> will work? Again, post your images if you can.

Subject: Detect black pixels in an image

From: ImageAnalyst

Date: 15 Mar, 2010 11:43:08

Message: 9 of 11

A question first: What are histdown and histup? They don't seem to
be defined yet. Did you mean avgdown and avgup?

Subject: Detect black pixels in an image

From: Hugh Thompson

Date: 15 Mar, 2010 21:36:06

Message: 10 of 11

Yes. Sorry about that!! I changed the names before posting to make them a bit easier to read...:S

ImageAnalyst <imageanalyst@mailinator.com> wrote in message <18bf82e4-9a46-48c7-8750-3eb96ad7679e@z4g2000yqa.googlegroups.com>...
> A question first: What are histdown and histup? They don't seem to
> be defined yet. Did you mean avgdown and avgup?

Subject: Detect black pixels in an image

From: ImageAnalyst

Date: 16 Mar, 2010 00:42:39

Message: 11 of 11

It looks like it iterates until it settles on a threshold that is
stable and is located halfway between the dark mean and the bright
mean. Since it only looks at the means, it doesn't matter the size.
Let's say you have a big hump in your histogram up around 100 for the
brown background, and a small hump (because there are fewer pixels)
down around 20 for the black line. Then this algorithm would pick the
threshold as 60 - half way between the big wide tall hump at 100 and
the short thin hump at 20. Should work ok for well separated humps.
I'm not sure how it would work with a more continuous histogram.

Tags for this Thread

Everyone's Tags:

Add a New Tag:

Separated by commas
Ex.: root locus, bode

What are tags?

A tag is like a keyword or category label associated with each thread. Tags make it easier for you to find threads of interest.

Anyone can tag a thread. Tags are public and visible to everyone.

Tag Activity for This Thread
Tag Applied By Date/Time
color Matías Alonso 26 Aug, 2010 20:28:27
rssFeed for this Thread

Contact us at files@mathworks.com