Convert bitmap to threshold (pure black and white) - c#

I have tried this code for converting a bitmap to pure black and white - not greyScale, but this gives me a pure black image.
public Bitmap blackwhite(Bitmap source)
{
Bitmap bm = new Bitmap(source.Width,source.Height);
for(int y=0;y<bm.Height;y++)
{
for(int x=0;x<bm.Width;x++)
{
if (source.GetPixel(x, y).GetBrightness() > 0.5f)
{
source.SetPixel(x,y,Color.White);
}
else
{
source.SetPixel(x,y,Color.Black);
}
}
}
return bm;
}
What can cause such a problem? Is there any alternate method to this?

I know this answer is way too late but I just figured it out and hope it helps other people having this problem.
I get the average brightness of the picture and use that as the threshold for setting pixels to black or white. It isn't 100% accurate and definitely isn't optimized for time complexity but it gets the job done.
public static void GetBitmap(string file)
{
using (Bitmap img = new Bitmap(file, true))
{
// Variable for image brightness
double avgBright = 0;
for (int y = 0; y < img.Height; y++)
{
for (int x = 0; x < img.Width; x++)
{
// Get the brightness of this pixel
avgBright += img.GetPixel(x, y).GetBrightness();
}
}
// Get the average brightness and limit it's min / max
avgBright = avgBright / (img.Width * img.Height);
avgBright = avgBright < .3 ? .3 : avgBright;
avgBright = avgBright > .7 ? .7 : avgBright;
// Convert image to black and white based on average brightness
for (int y = 0; y < img.Height; y++)
{
for (int x = 0; x < img.Width; x++)
{
// Set this pixel to black or white based on threshold
if (img.GetPixel(x, y).GetBrightness() > avgBright) img.SetPixel(x, y, Color.White);
else img.SetPixel(x, y, Color.Black);
}
}
// Image is now in black and white
}

Related

How to get color from specific area in an Image in C#

I am trying to get color from specific area in an Image.
Assume that , this is image , and I want to get color inside image.(the result should be red of the above image) This color may be different position in image. Because I don't know exact position of color where it starting, so I can't get exact result.
Until now, I cropped image giving manually position of x and y, and then cropped image and I got average color of cropped image. But I know , this is not exact color.
What I tried :
private RgbDto GetRGBvalueCroppedImage(Image croppedImage)
{
var avgRgb = new RgbDto();
var bm = new Bitmap(croppedImage);
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;
long[] totals = new long[] { 0, 0, 0 };
int width = bm.Width;
int height = bm.Height;
unsafe
{
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
for (int color = 0; color < 3; color++)
{
int idx = (y * stride) + x * 4 + color;
totals[color] += p[idx];
}
}
}
}
avgRgb.avgB = (int)totals[0] / (width * height);
avgRgb.avgG = (int)totals[1] / (width * height);
avgRgb.avgR = (int)totals[2] / (width * height);
return avgRgb;
}
How can I get exact position to crop? May be I can convert image to byte array, then I can find different color and take position of it and then crop. But I have no clue how do this.
You can use something this extension method to get dominant color in a region of an image in case they are not all the same
public static Color GetDominantColor(this Bitmap bitmap, int startX, int startY, int width, int height) {
var maxWidth = bitmap.Width;
var maxHeight = bitmap.Height;
//TODO: validate the region being requested
//Used for tally
int r = 0;
int g = 0;
int b = 0;
int totalPixels = 0;
for (int x = startX; x < (startX + width); x++) {
for (int y = startY; y < (startY + height); y++) {
Color c = bitmap.GetPixel(x, y);
r += Convert.ToInt32(c.R);
g += Convert.ToInt32(c.G);
b += Convert.ToInt32(c.B);
totalPixels++;
}
}
r /= totalPixels;
g /= totalPixels;
b /= totalPixels;
Color color = Color.FromArgb(255, (byte)r, (byte)g, (byte)b);
return color;
}
You can then use it like
Color pixelColor = myBitmap.GetDominantColor(xPixel, yPixel, 5, 5);
there is room for improvement, like using a Point and Size, or even a Rectangle
public static Color GetDominantColor(this Bitmap bitmap, Rectangle area) {
return bitmap.GetDominantColor(area.X, area.Y, area.Width, area.Height);
}
and following this link:
https://www.c-sharpcorner.com/UploadFile/0f68f2/color-detecting-in-an-image-in-C-Sharp/
If you want to get the image colors, you don't need to do any cropping at all. Just loop on image pixels and find the two different colors. (Assuming that you already know the image will have exactly 2 colors, as you said in comments). I've written a small function that will do that. However, I didn't test it in an IDE, so expect some small mistakes:
private static Color[] GetColors(Image image)
{
var bmp = new Bitmap(image);
var colors = new Color[2];
colors[0] = bmp.GetPixel(0, 0);
for (int i = 0; i < bmp.Width; i++)
{
for (int j = 0; j < bmp.Height; j++)
{
Color c = bmp.GetPixel(i, j);
if (c == colors[0]) continue;
colors[1] = c;
return colors;
}
}
return colors;
}

Resize image, issues with overlaying pixel

I'm trying to write a function which can scale image up and down with a scale factor like 1.5 or 0.5. When I scale down the image will appear correct on the screen when I scale up there is some kind of screen overlay on the image. See images below
This is the code I wrote:
public static void ResizeImage(Bitmap inputImage, double scale)
{
int maxWidth = (int) (scale * inputImage.Width);
int maxHeight = (int) (scale * inputImage.Height);
Bitmap scaledImage = new Bitmap(maxWidth, maxHeight);
for(int x = 0; x < inputImage.Width; x++)
{
for(int y = 0; y < inputImage.Height; y++)
{
//Gets current Pixel
Color pixel = inputImage.GetPixel(x, y);
//Calucalte new position Pixel
int newPixelX = (int) (Math.Floor(x * scale));
int newPixelY = (int) (Math.Floor(y * scale));
//Sets pixel in new image
scaledImage.SetPixel(newPixelX, newPixelY, pixel);
}
}
DrawImage(Bitmap2colorm(scaledImage));
}
Does anyone know a solution to this problem and can someone give me an explaintion why this is happening?

c# Android - Find object by color on bitmap

I would like to find an object on a bitmap using its HSV color. I know there already are some topics created about this issue, but they seem not to work on Android (I didn't find System.Drawing.Imaging on Android) or to be less efficient than the algorithm I already created:
public Rect FindObjectOnBitmap(Bitmap bm)
{
bm.GetPixels(pixels, 0, bm.Width, 0, 0, bm.Width, bm.Height);
// Get the matching pixels.
for(int x = 0; x<bm.Width; x++)
for (int y = 0; y < bm.Height; y++)
{
Color color = new Color(pixels[x + bm.Width * y]);
pixelsMatch[x,y] = (color.GetBrightness() > Settings.Tracker.blackFilter && color.GetSaturation() > Settings.Tracker.whiteFilter) && (Angle(Settings.Tracker.hue, color.GetHue()) < Settings.Tracker.sensitivity);
}
Rect objectRect = null;
int maxNbPixel = 0;
// for each matching pixel
for (int x = 0; x < bm.Width; x ++)
for (int y = 0; y < bm.Height; y ++)
if(pixelsMatch[x, y])
{
int nbPixel = 0;
Rect objectRectTemp = new Rect(x, y, x, y);
Queue<Pixel> zone = new Queue<Pixel>();
zone.Enqueue(new Pixel(x, y));
while(zone.Count != 0)
{
Pixel px = zone.Dequeue();
nbPixel++;
// Updates object position
if (px.x < objectRectTemp.Left)
objectRectTemp.Left = px.x;
if (px.x > objectRectTemp.Right)
objectRectTemp.Right = px.x;
if (px.y < objectRectTemp.Bottom)
objectRectTemp.Bottom = px.y;
if (px.y > objectRectTemp.Top)
objectRectTemp.Top = px.y;
// for each nearby pixel
for (int side = 0; side < 4; side++)
{
Pixel sidePx = px.Side(side);
if (sidePx.x >= 0 && sidePx.x < bm.Width && sidePx.y >= 0 && sidePx.y < bm.Height)
{
if (pixelsMatch[sidePx.x, sidePx.y])
{
zone.Enqueue(sidePx);
pixelsMatch[sidePx.x, sidePx.y] = false;
}
}
}
}
// Save the object if it is big enough.
if(nbPixel > maxNbPixel && nbPixel >= 4)
{
maxNbPixel = nbPixel;
objectRect = objectRectTemp;
}
}
return objectRect;
}
// Function comparing hues.
static float Angle(float deg1, float deg2)
{
if (Math.Abs(deg1 - deg2) > 180f)
return (360f - Math.Abs(deg1 - deg2));
else
return Math.Abs(deg1 - deg2);
}
However, I am reading a stream, and this algorithm is also not efficient enough since I get 0.1 fps on a 640x480 image... I get a good fps only if I create the bitmap with a smaller resolution than the real image (for example 40x30). That enables me to detect big or close objects with a bad precision (position and size).
How can I increase the efficiency enough, knowing I am on an ANDROID device...
Thanks in advance.
PS: Sorry for my poor English ^^

showing and saving a 2D-Array of grayscale Pixel

i have a black/white line-sensor wich gives me the grayscale values (0-255) of scanned data. i want to save this values as Bitmap (.bmp) and also show them in a picturebox. my current code looks like this:
PixelFormat px = PixelFormat.Canonical;
Bitmap bitmap = new Bitmap(width, height, px);
int[,] GreyScaleArray2D = new int[width,height];
for (int yy = 0; yy < (lb_SpReceivedData.Count-width); yy += width)
{
for (int xx = 0; xx < width; xx++)
{
GreyScaleArray2D[xx,tmp] = lb_SpReceivedData[(yy+xx)];
}
tmp++;
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int red = GreyScaleArray2D[x,y]; // read from array
int green = GreyScaleArray2D[x,y]; // read from array
int blue = GreyScaleArray2D[x,y]; // read from array
bitmap.SetPixel(x, y, Color.FromArgb(0, red, green, blue));
}
}
pictureBox1.Image = bitmap;
Question) i get an error of invalid parameter if i pass
PixelFormat.Canonical
to the bitmap constructor.
Question) even though i pass the bitmap to my picturebox there is nothing to see in the picturebox! what am i doing wrong?
thanks

C# Bitmap difficulties

What I am trying to do is check an image row or column, and if it contains all white pixels then trim that row or column.
I am not sure why, but when I run this code snippet, img.Width in TrimLeft is 1, before subtracting 1 from it.
The actual image width is 175. I end up with an ArgumentException. I have a similar method for trimming the right side, and that works fine.
class ImageHandler
{
public Bitmap img;
public List<int[]> pixels = new List<int[]>();
public ImageHandler(String path)
{
img = new Bitmap(path);
GetPixels();
}
public void TrimLeft()
{
while (CheckColIfWhite(0, 0))
{
Rectangle cropBox = new Rectangle(1, 0, (img.Width-1), img.Height);
Bitmap cropdImg = CropImage(img, cropBox);
img = cropdImg;
}
}
public bool CheckColIfWhite(int colx, int starty)
{
bool allPixelsWhite = false;
int whitePixels = 0;
for (int y = starty; y < img.Height; y++)
{
if (pixels[y][colx] >= 200) { whitePixels++; }
else { return false; }
if (whitePixels == img.Height) { allPixelsWhite = true; }
}
return allPixelsWhite;
}
public void GetPixels()
{
for (int y = 0; y < img.Height; y++)
{
int[] line = new int[img.Width];
for (int x = 0; x < img.Width; x++)
{
line[x] = (int) img.GetPixel(x, y).R;
}
pixels.Add(line);
}
}
public Bitmap CropImage(Bitmap tImg, Rectangle area)
{
Bitmap bmp = new Bitmap(tImg);
Bitmap bmpCrop = bmp.Clone(area, bmp.PixelFormat);
return bmpCrop;
}
}
Your method seems like it will be remarkably inefficient - if the image is 175 pixels wide, and entirely white, you're going to create 175 copies of it, before (probably) failing when trying to create a 0 pixel wide image.
Why not examine each column in turn until you find a non-white column, and then perform a single crop at that time. Untested code, and with other changes hopefully obvious:
public Bitmap CropImage (Bitmap image)
{
int top = 0;
int bottom = image.Height-1;
int left = 0;
int right = image.Width-1;
while(left < right && CheckColIfWhite(image,left))
left++;
if(left==right) return null; //Entirely white
while(CheckColIfWhite(image,right)) //Because left stopped, we know right will also
right--;
while(CheckRowIfWhite(image,top))
top++;
while(CheckRowIfWhite(image,bottom))
bottom--;
return CropImage(image,new Rectangle(left,top,right-left+1,bottom-top+1));
}
(E.g. I'm now passing the image around, I've modified CheckColIfWhite and CheckRowIfWhite to take the image also, and to assume that one parameter is always fixed at 0)
Also, not sure why you're extracting the pixel array beforehand, so I'm putting my re-written CheckColIfWhite too:
public bool CheckColIfWhite(Bitmap image,int colx)
{
for (int y = 0; y < image.Height; y++)
{
if (image.GetPixel(colx,y).R < 200)
return false;
}
return true;
}

Categories