how to scan an image for all pixels with certain RGB value

Blake81

Well-known member
Joined
Feb 23, 2006
Messages
304
Location
Georgia, USA
Programming Experience
1-3
I'm doing some image editing for work and I'm running into a problem where the background of the image that the customer submitted isn't truly white. When I zoom in with Paint at 800%, I can see that the parts that are making it not totally white are three different colors that recur in patterns. One has an RGB value of 255,0,0 one is 0,255,0 and one is 0,0,255. If I could have a VB app let me select an RGB value and then remove all pixels of that value from the image, I could get the image exactly how I need it. I haven't worked much with GDI+. I played around with making a program to select pxels and get their RGB values, but that's about it. I'd appreciate any help. Thanks.
 
Those values you mention are actually exact colors red, green and blue repectively. Open for example custom color dialog in MS Paint and input the RGB values to see for yourself.

Load the image into a Bitmap class object and use its GetPixel/SetPixel methods to get/set pixel data.
 
I tried what you said, and I have a few questions, because some things aren't making sense to me. I used a small JPEG to test this because the one I'm doing for the customer is pretty large (and I haven't put any threading into this yet). I kept running into an "index out of range" error for the Y coordinate, but I don't see how, because at the time the error hit, Y was equal to the height of the image. I changed this to x - 1 and y - 1 and I don't get the error, but it doesn't draw what I would expect. This is what I want the application to do:

1. Let you select a color on the image by clicking where the color is.
2. Find all pixels with that RGB value and replace them with the color you specify (or in this case, Aqua is hard coded just to test.)

I started with a simple JPEG that I made in Paint, a square with one half red and one half blue. I clicked on the red and told it to replace all red pixels. The first image is the one I started with, and the second is what it changed. Why did it do it like this, and how can I fix my code? Another question: I thought JPEG was a lossless format. It almost looks like it hit sections where the red wasn't truly red so it didn't change it. Thanks for any help.

square1.jpg
square2.jpg


And here is my code for the relevant section.
VB.NET:
[SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE][SIZE=2] GetColor()
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] bmp2 [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]New[/COLOR][/SIZE][SIZE=2] Bitmap(PictureBox1.Image)
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] wth [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]Integer[/COLOR][/SIZE][SIZE=2] = 0
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] hth [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]Integer[/COLOR][/SIZE][SIZE=2] = 0
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] y [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]Integer[/COLOR][/SIZE][SIZE=2] = 0
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] newpoint [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] Drawing.Point
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] testcolor [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] Color = Label1.BackColor
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] testcolorr [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]String[/COLOR][/SIZE][SIZE=2] = testcolor.R
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] testcolorg [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]String[/COLOR][/SIZE][SIZE=2] = testcolor.G
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] testcolorb [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]String[/COLOR][/SIZE][SIZE=2] = testcolor.B
[/SIZE][SIZE=2][COLOR=#0000ff]For[/COLOR][/SIZE][SIZE=2] wth = 0 [/SIZE][SIZE=2][COLOR=#0000ff]To[/COLOR][/SIZE][SIZE=2] bmp2.Width - 1
newpoint.X = wth
[/SIZE][SIZE=2][COLOR=#0000ff]For[/COLOR][/SIZE][SIZE=2] y = 0 [/SIZE][SIZE=2][COLOR=#0000ff]To[/COLOR][/SIZE][SIZE=2] bmp2.Height - 1
newpoint.Y = y
[/SIZE][SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] mycolor [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] Color = [/SIZE][SIZE=2][COLOR=#0000ff]DirectCast[/COLOR][/SIZE][SIZE=2](PictureBox1.Image, Bitmap).GetPixel(newpoint.X, newpoint.Y)
[/SIZE][SIZE=2][COLOR=#0000ff]If[/COLOR][/SIZE][SIZE=2] Color.FromArgb(255, testcolorr, testcolorg, testcolorb) = mycolor [/SIZE][SIZE=2][COLOR=#0000ff]Then
[/COLOR][/SIZE][SIZE=2]bmp2.SetPixel(newpoint.X, newpoint.Y, Color.Aqua)
[/SIZE][SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]If
[/COLOR][/SIZE][SIZE=2]PictureBox1.Image = bmp2
[/SIZE][SIZE=2][COLOR=#0000ff]Next
[/COLOR][/SIZE][SIZE=2][/SIZE][SIZE=2][COLOR=#0000ff]Next
[/COLOR][/SIZE][SIZE=2][/SIZE][SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]Sub
End[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]Class
[/COLOR][/SIZE]
 
Arrays are zero based. First element is index 0 etc, last element (width-1) (height-1) (count-1) and similar. Elementary really.

Jpeg is a LOSSY image format that utilize various degrees of compression to reduce file size, this may result in 'fragmented' color. It depends on source and conversion. I have also seen solid fills in imaging software that 'feature' such irregularities for what should have been a unanimous color.

About what you expect: Your original image contains 428 unique colors, even if the eye perceives this as only "some red and some blue". When analysing the Hue values you see there are 50 unique values (when rounding Single to Integer). I have told you before about the HSL color space and that you can use for example the MS Paint color palette to explore this. There is a little caveat, MSPaint use a HSL range of 0-240 while .Net framework use range 0-360, but the conversion is simple (360/240). In MSPaint the reds appears as ranges 0-10 and 220-240, translated to .Net this is 0-15 and 330-360. The purples between blue and red you can decide for yourself, perhaps increase the last red range to 300-360?

So to replace all red in your original image just check if the color you get is within relevant Hue ranges.
 
Thanks. I had thought about doing checking to see if each pixel was within a certain range of a given value. That's probably the approach I'll take with this. I keep adding more to this program. I haven't worked much with GDI+ before, but this is looking like I could produce some pretty helpful tools for the new stuff I'm getting into at work. I also have some ideas for creating some "selector" tools to do several things, such as find all of the colors within a certain rectangle. One thing I'd like to do but I don't know how to start would be to make a "magic wand" like Photoshop has that can predict what region you're trying to select.
 
Back
Top