Extracting Black pixels from an image

397/5000

After a medical break of almost two months, I wanted to make a little program that extracts and displays black dots in an image.

Result:
a program which turns a color image into black and white by adding the three color components (R + G + B).

I completely forgot how to do it.

After a nice nap (several hours!), Nothing.

A little help from my friends is most welcome.

Picture
RGBSurface
Pixel(x,y)

maybe something like if red < 10 and green < 10 and blue < 10 then set pixel black, otherwise white.
If you can use RGBSurface.Transform, you can even get it fast without plugin.

Thank you Christian,

Picture
RGBSurface
Pixel(x,y)

That is what I’ve done, and then:

Black_Clr = surf.Pixel(x,y)
c = (Black_Clr.Red + Black_Clr.Green + Black_Clr.Blue) \ 3
Black_Clr = RGB(C,C,C)

But that does not remove the other colors, just add them.

The target images are scans of old papers (printed in black and white) and then they have a yellowish background. That is why I want to keep Black only.

Here’s my new code in the two loops:

aColor = surf.Pixel(x,y)
If aColor.Red > 16 And aColor.Green < 16 And aColor.Blue < 16 Then
// Set the color to Black
aColor = RGB(0,0,0)
End If

// Draw the new color
BP_Surf.Pixel(x,y) = aColor

Nota: I tried 128 (instead of 16) and get the same result…

Black_Clr = RGB(C,C,C)
surf.pixel( x, y ) = RGB(C,C,C)

You’re creating a grayscale image from the average of the colors, there are many other methods to doing this that will create different results, here’s a couple of examples.

// — Brightness
c = max( Black_Clr.Red, Black_Clr.green, Black_Clr.blue )

// — Luminosity (incomplete as it requires a sRGB converstion).
c = 0.299 * Black_Clr.Red + 0.587 * Black_Clr.green + 0.114 * Black_Clr.blue

Emile, your two pieces of code will result in two different image types. Do you want a grayscale image or a black and white one?

Since you mentioned old pictures I guess you want grayscale images as a result. Also, I guess you are coding this for you own and for fun, right? I ask this because there are free tools that can convert color pictures to grayscale, and even process an entire folder of pictures at once (I use ImageJ and I know it works with entire folders).

What did you mean by this?

Julen

Thanks all.

To be clear, look at the Superman scan there:

all I want is strip the yellow from the image (or as I wrote earlier, keep only the black part).

Yes, it is for fun - my own use -; while you were there, you may see a colored scan with yellow background… Usually, I run GIMP and do a white balance and most of the time the result is GREAT. But this do not really works for the non colored images.

Sam: Brightness and Luminosity.
I tried that with GIMP, but the results were bad (IMHO).

I recall far better results, but it may be with colored images…

So I too am confused… are you attempting to filter out a specific color and leave all the other colors? Or are you attempting to convert a color comic to a grayscale one?

Removing a specific color will be difficult becaulse all the “yellow” pixels are not the same as far as the computer is concerned (what you see as yellow might not be yellow at all at the pixel level).

If you wish to change it to grayscale… THAT is easy to do… just use the equation in the last line of Sams post above

Thanks Dave.

I only want to extrack the Black parts of the (Black and White) image. That image can be a week at the sun rays or as old as you (or me) ;).

THe code that gaves me the best result is:

  For y As Integer = 0 To lastY
    For x As Integer = 0 To lastX
      aColor = surf.Pixel(x,y)
      If aColor.Red < 80 And aColor.Green < 80 And aColor.Blue < 80 Then
        // Set the color to Black
        aColor = RGB(0,0,0)
        
      Else
        // Set the color to White
        aColor = RGB(255,255,255)
      End If
      
      // Plot the new Color
      BP_Surf.Pixel(x,y) = aColor
    Next
  Next

// Display the changed image
cImage.Backdrop = Black_Pict

I was watching TV when I was asking myself “What if I set White in the Else (I probably already do that, but…).
So I tried, then I changed the If test (this is new) and then I increased the value (in steps up to 80).
I will add a Control to set that value as 8 (or 16) step, so I can change the value in one Run ;).

It was rainy today.

Here is some old RB code I wrote way back in the days when I was doing pattern/color recognition for computer vision. Unfortunately, no idea any more how I came up with those formulas :frowning: Documenting stuff was not my forte back then…

    for x = 0 to tPic.width - 1
      for y = 0 to tPic.height - 1
        Col = SrcRGB.Pixel(x,y)
        H = Col.Hue * 255
        V = Col.Value * 255
        S = Col.Saturation * 200
        
        ' you may want to play with the 25 and 195 value to get a better result, making the range wider or more narrow
        minV = 25 - (30/215 * (S - 40)) 
        maxV = 195 + (70/215 * (S - 40))
        
        if V >= minV and V <= maxV then
          tgtRGB.pixel(x,y) = Col
        else
          tgtRGB.pixel(x,y) = &CFFFFFF
        end if
      next
    next

This resulted in something like this:

This is just an extract of the full algorithm, which was able to extract according to the main colors. E.g. here is an example for finding ‘green’:

Alwaysbusy

@ Alwaysbusy
You got it (The Letter).

I also really love the green (any color, I suppose) extraction. But this is a different story (not today’s one).

Thank you.

Try this:

      If aColor.Red < 128 And aColor.Green < 113 And aColor.Blue < 91 Then
        // Set the color to Black
        aColor = RGB(0,0,0)

I have not tested this but using a Brightness/Value threshold of 145 over 255 gave me a good result too, so maybe this could work:

      If aColor.Value< 145/255 Then
        // Set the color to Black
        aColor = RGB(0,0,0)

This is the result I got using those thresholds (they are the same) in ImageJ:

Fantastic result !

Thank you.

I believe the image could be improved further by removing the one pixel black areas and filling the one pixel white areas. Maybe two or three pixel areas could be used too, but I would have to try.

EDIT: I would have no problem to try, if you are interested.

Julen

Look at the RGBSurface Transform for doing something like this, so much faster.

Thanks for the tip. I am not using Xojo for this but it is nice to learn new stuff, and I didn’t know about rgbsurface.transform.

Julen

1 Like

Some feedback:

a. I add a save button to yesterday’s project.
Here’s the files sizes (image: 3603 × 1034):
original: 1 306 035Bytes
black: 452 591Bytes

I loaded the generated image and saved it as Greyscale with GIMP, but I get a far larger file ! (7 473 375Bytes); a second save (on the same) reduce it to 208 111Bytes ???

@anon76569242: EDIT: I would have no problem to try, if you are interested.

go ahead (I am unsure of what this do).

Sam: I never fully understand how RGBSurface.Transform.Map(<one-or-three.maps) works (related to the Color Table).

I know what is a Color Table, how it is used and so on. But not in that context.

RGBSurface.Transform(Map)
the example in the docs to invert an image is nice. But we never get an explanation of what a map is.

Like everybody, I have parts where I am good (sometimes better than that) and parts where I do not understand :slight_smile: or :frowning: (depends on the point of view).

It’s a really simply color table, made from an array of 255 integers. Xojo uses the channel value (0 ~ 255) to look up what you want the value to be.

If you set the first 127 values to zero ( black ) and the remaining values to 255 ( white ), you’d get a black and white image.

The downside to Xojo’s version is that it operates on a per channel basis and not per color, so some of the really cool things that we can do with huge LUTS, we can’t in Xojo. However in it’s own right, it’s a nice thing to have.

Despeckling, removing the small speckles that were not there in the original image.

Here is the result (after removing speckles smaller than 4 square pixels):

Then Emile can’t use it since he is deciding whether each pixel should be black or white based on the values for the three channels.

It depends.

Agreed it is not a lut in the sense that it does not simply lookup a replacement color from a color.

It may still be useful in OPs original quest as it can be used to gather a range of colors.