Remove remainders from bitmap after background removal - c#

I have this image and I want to remove the background to isolate the green picture. The background is not completely black but it contains some pixels having the same color of other pixels inside the green picture.
I have used this
private void ButtonFilterClick(object sender, EventArgs e)
{
PixelFormat pxf = PixelFormat.Format24bppRgb;
Bitmap bitmap = ((Bitmap)(_smartLabForm.pictureBox1.Image));
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData =
bitmap.LockBits(rect, ImageLockMode.ReadWrite, pxf);
IntPtr ptr = bmpData.Scan0;
int numBytes = bmpData.Stride * bitmap.Height;
byte[] rgbValues = new byte[numBytes];
Marshal.Copy(ptr, rgbValues, 0, numBytes);
for (int counter = 0; counter < rgbValues.Length; counter += 3)
{
if (rgbValues[counter] < 15 &&
rgbValues[counter + 1] < 15 &&
rgbValues[counter + 2] < 15)
{
rgbValues[counter] = 255;
rgbValues[counter + 1] = 255;
rgbValues[counter + 2] = 255;
}
}
Marshal.Copy(rgbValues, 0, ptr, numBytes);
bitmap.UnlockBits(bmpData);
_smartLabForm.Refresh();
}
and what I obtain is this:
How can I remove the "noise" remaining without damage the green picture and without affecting the performance?
Thank you?

This is actually a quite complex topic in computer vision (image segmentation). Covering the advanced techniques would be way too broad. But here is quick and simple idea that may get the job done:
Increase the threshold enough that all background pixels fall below it. When checking if a pixel should be removed, also compare all pixels in a certain neighborhood (e.g. circular radius) with the threshold. Only remove it if they are all below the threshold.
That way you remove pixels less agressively when you are near the feature region.

Related

How to do Bitmap processing using BitmapData?

I've built a small test example where the goal is to change all pixels in my .png to white. I'm doing it using BitmapData, because as I understand it, the performance is better. If I can get it working; then I can change which pixels I'm changing and add different conditions to altering a pixel color. But I'm stuck on just this simple test.
Here's my C# :
public static void TestConvertAllBlackBitmapToAllWhite()
{
string allBlackPNGFullFilePath = #"C:\Users\{Username}\Desktop\50x50AllBlack.png";
Bitmap allBlackBitmap = new Bitmap(allBlackPNGFullFilePath);
Bitmap newBitmap = (Bitmap)allBlackBitmap.Clone();
Size size = newBitmap.Size;
PixelFormat pixelFormat = newBitmap.PixelFormat;
byte bitDepth = (byte)(pixelFormat == PixelFormat.Format32bppArgb ? 4 : 3);
Rectangle rectangle = new Rectangle(Point.Empty, size);
BitmapData bitmapData = newBitmap.LockBits(rectangle, ImageLockMode.ReadOnly, pixelFormat);
int dataSize = bitmapData.Stride * bitmapData.Height;
byte[] data = new byte[dataSize];
Marshal.Copy(bitmapData.Scan0, data, 0, dataSize);
Color white = Color.White;
for (int y = 0; y < size.Height; y++)
{
for (int x = 0; x < size.Width; x++)
{
// Get Index
int index = y * bitmapData.Stride + x * bitDepth;
// Set Pixel Color
data[index] = white.B;
data[index + 1] = white.G;
data[index + 2] = white.R;
}
}
Marshal.Copy(data, 0, bitmapData.Scan0, data.Length);
newBitmap.UnlockBits(bitmapData);
// Save New Converted Bitmap
string originalFileName = Path.GetFileNameWithoutExtension(allBlackPNGFullFilePath);
string directory = Path.GetDirectoryName(allBlackPNGFullFilePath);
string newBitmapFileName = originalFileName + "_Converted";
string newBitmapFullFileName = directory + Path.DirectorySeparatorChar.ToString() + newBitmapFileName + ".png";
newBitmap.Save(newBitmapFullFileName, ImageFormat.Png);
}
My input is an all black 50x50 .png :
The problem is the output I'm getting is another all black .png instead of an all white one.
How can I fix up my simple example code to produce an all white .png as a result?
Any help / guidance will be really appreciated.
As pointed out by #Taw
It's a little thing on this line :
BitmapData bitmapData = newBitmap.LockBits(rectangle, ImageLockMode.ReadOnly, pixelFormat);
The ImageLockMode is set to ReadOnly. Since I'm making changes to the BitmapData while looping; the ImageLockMode should be ReadWrite

Strange behaviour of BitmapData loaded from memory (PixelFormat.Format24bppRgb)

The method for editing Bitmap using BitmapData and Lock and UnlockBits taken from the MSDN seems to work very differently on Bitmaps with with different sizes. It only does this on PixelFormat.Format24bppRgb and changing it seems to fix it, but I'd like to know what's going on.
Here's the method for reference:
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
It works just fine for Bitmap(200, 200) or Bitmap(200,250). But when it's used on Bitmap(50, 50) or most of the other sizes it goes completely crazy...
The rgbValues array has unexpected length of 7600. (The expected length would be Width * Height * Bytes per pixel = 50 * 50 * 3 = 7500)
This causes it to throw IndexOutOfRange Exception in most cases
I tried to change the value added to the counter but none seems to work
for (int counter = 10; counter + 10 < rgbValues.Length; counter += n)
rgbValues[counter] = 255;
//Doesn't work for any number put for n from 2 to 10
//I also limited it from beginning and end just to try
//if it's not changing some crucial value in the bytes
//(I know it's a nonsense but I tried)
If 3 is put for n (referring to the code above) it yields a picture with 1st row red, 2nd blue, 3rd green, repeat
If 1 is put there it actually makes the whole picture white
Can someone please explain why is this happening? And how can this be fixed? (besides changing the format)
Note: I'm not keen on this format but I'd like to know what's actually going on.

treshould filter in Aforge doesn't seem to work properly

hope you all doing well. I did write a bit of codes in C# using Aforge library. I wanted to crop my main image captured from webcam so as to have a nice ROI. When I use threshold value of 0 everything should be in white pixels (total of lets say 26880 pixels) but it seems that I have some black pixels (578 pixels) within my cropped image. any idea of what may caused it? when I don't crop my image everything is fine.
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
Bitmap bmp = new Bitmap(x2box, y2box);
bmp = img.Clone(new Rectangle(x1box, y1box, x2box, y2box), eventArgs.Frame.PixelFormat);
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
Bitmap img1 = filter.Apply(bmp);
Threshold tresh = new Threshold((int)tresh1); // tresh1 is 0-255 but is set to zero here
tresh.ApplyInPlace(img1);
int iterator = 1; int xrow = 0; // here i use these constant to calculate location of the pixels
byte[] arraybyte = BitmapToByteArray(img1);
for (int i = 0; i < arraybyte.Length; i++)
{
if (i - iterator * img1.Width == 0)
{
xrow++;
iterator++;
}
if (arraybyte[i] == 0) // if pixel is black
{
X_val.Add(i - xrow * img1.Width);
Y_val.Add(iterator);
}
}
for (int i = 0; i < X_val.Count; i++)
{
YAve += Y_val[i];
XAve += X_val[i];
}
MessageBox.Show(X_val.Count.ToString()); // shows non-zero value!
the BitmapToByteArray method is as follow:
public static byte[] BitmapToByteArray(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
The number of bytes for each row of the Bitmap will be enforced to be a multiple of 4. If roi width * bytes per pixel is not a multiple of 4, you will have padding bytes at the end of each row.
They will not be thresholded as they aren't really part of the Bitmap, so their value may be 0. Your BitmapToByteArray method might not be padding-aware and read every byte.

BitLock-GetPixels. Time and PixelFormat

Okay, I've created two programs. One that uses GetPixels and another that uses LockBits. My GetPixels program is as follows...
The stripe picture referred is a 200x200 jpg
Stopwatch GetTime = new Stopwatch();
Bitmap img = new Bitmap("stripe.jpg");
GetTime.Start();
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color pixel = img.GetPixel(i, j);
output += " " + pixel;
}
}
GetTime.Stop();
Now this one reads out about 20 secs to process this image and output all the pixels. Great, but my LockBits one should, theoretically be faster. My code for the LockBits is...
Bitmap bmp = new Bitmap("stripe.jpg");
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height); //Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, Pixels); //Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0; //Scans the first line of data
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
string Pix = " ";
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
bmp.UnlockBits(bmpData);
Stopwatch Timer = new Stopwatch();
pictureBox1.Image = bmp;
Timer.Start();
for (int p = 0; p < DataBytes; p++)
{
Pix += " " + rgbValues[p];
}
Timer.Stop();
and the time on that is 37secs. Now I dont understand why my time is longer for the Lockbits than it is for the GetPixels.
Also my output files don't match up in terms of where they are listed. It is almost as if they are out of order.
This is a big problem to tackle so thank you all in advance for reading and trying to solve my problem.
You have a few problems that I can see. The biggest issue is that your image has a width of 200, but in memory, its stride is 600 (for me - probably similar for you). This means you are writing out a lot more data, because you don't ignore the 400 padding pixels per row.
Other issues:
You're timing string concatenation only. By the time you start your timer, the lockbits stuff has finished.
Your string concatenation would be faster using StringBuilder.
You are locking the bitmap for read/write access, when you only need read. No discernible effect on performance for me, with this image, but still might as well change it to ReadOnly.
Most of your comments are unnecessary at best (// creates array)- some are misleading (//Scans the first line of data - no, it returns a pointer to the data which has already been loaded).
The following code completes in only a few milliseconds on my machine.
Bitmap bmp = new Bitmap(#"d:\stripe.jpg");
//pictureBox1.Image = bmp;
Stopwatch Timer = new Stopwatch();
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(
bmpRec, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr Pointer = bmpData.Scan0;
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[DataBytes];
Marshal.Copy(Pointer, rgbValues, 0, DataBytes);
bmp.UnlockBits(bmpData);
StringBuilder pix = new StringBuilder(" ");
Timer.Start();
for (int i = 0; i < bmpData.Width; i++)
{
for (int j = 0; j < bmpData.Height; j++)
{
// compute the proper offset into the array for these co-ords
var pixel = rgbValues[i + j*Math.Abs(bmpData.Stride)];
pix.Append(" ");
pix.Append(pixel);
}
}
Timer.Stop();
Console.WriteLine(Timer.Elapsed);

improving speed of GetPixel function in c#

i need to read getpixel of bmp with speed but is very low
i used of LockBits
private void LockUnlockBitsExample(Bitmap bmp)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
}
and this function
private Color GetMyPixel(byte[] rgbValues,Bitmap bmp, int x,int y )
{
int index= (bmp.Width*y+x)*3;
Color MyColor = Color.FromArgb(rgbValues[index], rgbValues[index + 1], rgbValues[index + 2]);
return MyColor;
}
but output of my function is different from original getpixel
In this line:
int index= (bmp.Width*y+x)*3;
I believe that bmp.Stride must be used instead of bmp.Width. Also check assumption that PixelFormat is 24 bits per pixel.
Another thing are color indexes: blue is first (index), then green (index+1), then red (index + 2).
I have code in VB for some reason that does almost the exact same thing as you, so I hope this helps. You can try the following modification to GetMyPixel:
Use Stride instead of Width and invert the byte order in your call to FromArgb.
private Color GetMyPixel(byte[] rgbValues,Bitmap bmp, int x,int y )
{
int index= (bmp.Stride*y+x*3);
if (index > rgbValues.Length - 3)
index = rgbValues.Length - 3;
Color MyColor = Color.FromArgb(rgbValues[index+2], rgbValues[index + 1], rgbValues[index]);
return MyColor;
}
You should check this post out :working with lockbits
it helped me a lot when i did something similar

Categories