Adaptive Thresholding Technique to live videos from webcam C# - c#

I am trying to detect light from 2 LED lights (red and blue) I did that using Bernsen thresholding technique. However, I applied that to an image. Now I want to apply that same technique but to a live video from my webcam. Is there anyway I could simply edit the code for this technique on the image to make it work on a video from the webcam? I will add below the code I used for this thresholding technique.
private ArrayList getNeighbours(int xPos, int yPos, Bitmap bitmap)
{
//This goes around the image in windows of 5
ArrayList neighboursList = new ArrayList();
int xStart, yStart, xFinish, yFinish;
int pixel;
xStart = xPos - 5;
yStart = yPos - 5;
xFinish = xPos + 5;
yFinish = yPos + 5;
for (int y = yStart; y <= yFinish; y++)
{
for (int x = xStart; x <= xFinish; x++)
{
if (x < 0 || y < 0 || x > (bitmap.Width - 1) || y > (bitmap.Height - 1))
{
continue;
}
else
{
pixel = bitmap.GetPixel(x, y).R;
neighboursList.Add(pixel);
}
}
}
return neighboursList;
}
private void button5_Click_1(object sender, EventArgs e)
{
//The input image
Bitmap image = new Bitmap(pictureBox2.Image);
progressBar1.Minimum = 0;
progressBar1.Maximum = image.Height - 1;
progressBar1.Value = 0;
Bitmap result = new Bitmap(pictureBox2.Image);
int iMin, iMax, t, c, contrastThreshold, pixel;
contrastThreshold = 180;
ArrayList list = new ArrayList();
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++)
{
list.Clear();
pixel = image.GetPixel(x, y).R;
list = getNeighbours(x, y, image);
list.Sort();
iMin = Convert.ToByte(list[0]);
iMax = Convert.ToByte(list[list.Count - 1]);
// These are the calculations to test whether the
current pixel is light or dark
t = ((iMax + iMin) / 2);
c = (iMax - iMin);
if (c < contrastThreshold)
{
pixel = ((t >= 160) ? 0 : 255);
}
else
{
pixel = ((pixel >= t) ? 0 : 255);
}
result.SetPixel(x, y, Color.FromArgb(pixel, pixel, pixel));
}
progressBar1.Value = y;
}
pictureBox3.Image =result;
}

Related

Bitmap inner contours

I'm trying to calculate inner contours of letters and trying to simulate something like inner contour of corel. I use square to sample image and calculate contours. This is what i got so far.
Only issue are square corners. Does anyone have any idea how to make green one look like the one on right or any lib that does this.
Results image
int n = 12;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
if (IsWhite(bitmap.GetPixel(x, y))) continue;
int minX = x - n;
int maxX = x + n;
if (minX < 0) minX = 0;
if (maxX > width - 1) maxX = width - 1;
int minY = y - n;
int maxY = y + n;
if (minY < 0) minY = 0;
if (maxY > height - 1) maxY = height - 1;
bool blackPxFound = false;
bool redPxFound = false;
for (int y_s = minY; y_s <= maxY; ++y_s)
{
bool breakY = false;
for (int x_s = minX; x_s <= maxX; ++x_s)
{
Color sample = Color.White;
sample = bitmap.GetPixel(x_s, y_s);
if (IsBlack(sample)) blackPxFound = true;
if (IsRed(sample)) redPxFound = true;
if (blackPxFound && redPxFound)
{
traced.SetPixel(x, y, Color.Green);
breakY = true;
break;
}
}
if (breakY) break;
} // end samples
}
} // end image loop
pictureBox2.Image = traced;

Unsafe Edge Detection still slow

I'm trying to implement an Image Edge Detection into a WPF program.
I already have it working, but the converting of the image is quite slow.
The code is not using the slow GetPixel and SetPixel functions. But instead I'm looping through the image in some unsafe code so that I can directly access the value's using a pointer.
Before starting the Edge detection I'm also converting the image to a greyscale image to improve the edge detection speed.
But still it takes the program around 1600ms to convert an image with a size of 1920x1440 pixels, which I think could be much faster.
This is the original image:
Which is converted to this (Snapshot of the application):
This is how I'm converting the image, I'm wondering what I can do to get to some other speed improvements?
Loading the image and create a Greyscale WriteableBitmap:
private void imageData_Loaded(object sender, RoutedEventArgs e)
{
if (imageData.Source != null)
{
BitmapSource BitmapSrc = new FormatConvertedBitmap(imageData.Source as BitmapSource, PixelFormats.Gray8 /* Convert to greyscale image */, null, 0);
writeableOriginalBitmap = new WriteableBitmap(BitmapSrc);
writeableBitmap = writeableOriginalBitmap.Clone();
imageData.Source = writeableBitmap;
EdgeDetection();
}
}
Converting the Image:
private const int TOLERANCE = 20;
private void EdgeDetection()
{
DateTime startTime = DateTime.Now; //Save starting time
writeableOriginalBitmap.Lock();
writeableBitmap.Lock();
unsafe
{
byte* pBuffer = (byte*)writeableBitmap.BackBuffer.ToPointer();
byte* pOriginalBuffer = (byte*)writeableOriginalBitmap.BackBuffer.ToPointer();
for (int row = 0; row < writeableOriginalBitmap.PixelHeight; row++)
{
for (int column = 0; column < writeableOriginalBitmap.PixelWidth; column++)
{
byte edgeColor = getEdgeColor(column, row, pOriginalBuffer); //Get pixel color based on edge value
pBuffer[column + (row * writeableBitmap.BackBufferStride)] = (byte)(255 - edgeColor);
}
}
}
//Refresh image
writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight));
writeableBitmap.Unlock();
writeableOriginalBitmap.Unlock();
//Calculate converting time
TimeSpan diff = DateTime.Now - startTime;
Debug.WriteLine("Loading Time: " + (int)diff.TotalMilliseconds);
}
private unsafe byte getEdgeColor(int xPos, int yPos, byte* pOriginalBuffer)
{
byte Color;
byte maxColor = 0;
byte minColor = 255;
int difference;
//Calculate max and min value of surrounding pixels
for (int y = yPos - 1; y <= yPos + 1; y++)
{
for (int x = xPos - 1; x <= xPos + 1; x++)
{
if (x >= 0 && x < writeableOriginalBitmap.PixelWidth && y >= 0 && y < writeableOriginalBitmap.PixelHeight)
{
Color = pOriginalBuffer[x + (y * writeableOriginalBitmap.BackBufferStride)];
if (Color > maxColor) //If current pixel has higher value as previous max pixel
maxColor = Color; //Save current pixel value as max
if (Color < minColor) //If current pixel has lower value as previous min pixel
minColor = Color; //Save current pixel value as min
}
}
}
//Difference of minimum and maximum pixel with tollerance
difference = maxColor - minColor - TOLERANCE;
if (difference < 0)
difference = 0;
return (byte)difference;
}
Console Output:
Loading Time: 1599
The following code runs your algorithm on a byte array instead of the BackBuffer of a WriteableBitmap. It completes in less than 300 ms with a 1900x1200 image on my PC.
private static BitmapSource EdgeDetection(BitmapSource source)
{
var stopwatch = Stopwatch.StartNew();
var bitmap = new FormatConvertedBitmap(source, PixelFormats.Gray8, null, 0);
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var originalBuffer = new byte[width * height];
var buffer = new byte[width * height];
bitmap.CopyPixels(originalBuffer, width, 0);
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
byte edgeColor = GetEdgeColor(originalBuffer, width, height, x, y);
buffer[width * y + x] = (byte)(255 - edgeColor);
}
}
Debug.WriteLine(stopwatch.ElapsedMilliseconds);
return BitmapSource.Create(
width, height, 96, 96, PixelFormats.Gray8, null, buffer, width);
}
private static byte GetEdgeColor(byte[] buffer, int width, int height, int x, int y)
{
const int tolerance = 20;
byte minColor = 255;
byte maxColor = 0;
var xStart = Math.Max(0, x - 1);
var xEnd = Math.Min(width - 1, x + 1);
var yStart = Math.Max(0, y - 1);
var yEnd = Math.Min(height - 1, y + 1);
for (var j = yStart; j <= yEnd; j++)
{
for (var i = xStart; i <= xEnd; i++)
{
var color = buffer[width * j + i];
minColor = Math.Min(minColor, color);
maxColor = Math.Max(maxColor, color);
}
}
return (byte)Math.Max(0, maxColor - minColor - tolerance);
}

Best way to speed up CropTransparentPixels method

I have just wrote this method to crop transparent pixels from images.
It seems to work ok but it is very slow because of GetPixel - any ideas on how to make the algorithm logic quicker?
I know I can change the GetPixel for faster (but unsafe) access code and I might do so, however I am after ways to avoid doing a full scan. I want advice on how to make the logic behind this algorithm quicker.
public Bitmap CropTransparentPixels(Bitmap originalBitmap)
{
// Find the min/max transparent pixels
Point min = new Point(int.MaxValue, int.MaxValue);
Point max = new Point(int.MinValue, int.MinValue);
for (int x = 0; x < originalBitmap.Width; ++x)
{
for (int y = 0; y < originalBitmap.Height; ++y)
{
Color pixelColor = originalBitmap.GetPixel(x, y);
if (pixelColor.A == 255)
{
if (x < min.X) min.X = x;
if (y < min.Y) min.Y = y;
if (x > max.X) max.X = x;
if (y > max.Y) max.Y = y;
}
}
}
// Create a new bitmap from the crop rectangle
Rectangle cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X, max.Y - min.Y);
Bitmap newBitmap = new Bitmap(cropRectangle.Width, cropRectangle.Height);
using (Graphics g = Graphics.FromImage(newBitmap))
{
g.DrawImage(originalBitmap, 0, 0, cropRectangle, GraphicsUnit.Pixel);
}
return newBitmap;
}
This is the method I ended up writing and it is much faster.
public static Bitmap CropTransparentPixels(this Bitmap bmp)
{
BitmapData bmData = null;
try
{
bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int scanline = bmData.Stride;
IntPtr Scan0 = bmData.Scan0;
Point top = new Point(), left = new Point(), right = new Point(), bottom = new Point();
bool complete = false;
unsafe
{
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
if (p[3] != 0)
{
top = new Point(x, y);
complete = true;
break;
}
p += 4;
}
if (complete)
break;
}
p = (byte*)(void*)Scan0;
complete = false;
for (int y = bmp.Height - 1; y >= 0; y--)
{
for (int x = 0; x < bmp.Width; x++)
{
if (p[x * 4 + y * scanline + 3] != 0)
{
bottom = new Point(x + 1, y + 1);
complete = true;
break;
}
}
if (complete)
break;
}
p = (byte*)(void*)Scan0;
complete = false;
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (p[x * 4 + y * scanline + 3] != 0)
{
left = new Point(x, y);
complete = true;
break;
}
}
if (complete)
break;
}
p = (byte*)(void*)Scan0;
complete = false;
for (int x = bmp.Width - 1; x >= 0; x--)
{
for (int y = 0; y < bmp.Height; y++)
{
if (p[x * 4 + y * scanline + 3] != 0)
{
right = new Point(x + 1, y + 1);
complete = true;
break;
}
}
if (complete)
break;
}
}
bmp.UnlockBits(bmData);
System.Drawing.Rectangle rectangle = new Rectangle(left.X, top.Y, right.X - left.X, bottom.Y - top.Y);
Bitmap b = new Bitmap(rectangle.Width, rectangle.Height);
Graphics g = Graphics.FromImage(b);
g.DrawImage(bmp, 0, 0, rectangle, GraphicsUnit.Pixel);
g.Dispose();
return b;
}
catch
{
try
{
bmp.UnlockBits(bmData);
}
catch { }
return null;
}
}

How to detect black Bullets in Image?

Given the following image, How can I detect black bullets (90 bullet) in this image using C#, EmguCV or AForge?
I tried to use GetPixel(x,y) method but it checks only pixel by pixel, it is very slow and I need to detect the bullets not pixels.
Algorithm/Idea 1
You can divide your image in squares as shown in this sample:
With this logic you only have to check every 20th pixel. As soon as you know where the first dot is, you will know that every other dot have to be in the same horizontal line (in your provided sample).
A sample algorithm would look similar to this (please note that it need further improvement):
Bitmap myBitmap = new Bitmap ("input.png");
int skipX = 12;
int skipY = 12;
int detectedDots = 0;
for (int x = 0; x < myBitmap.Width; x += skipX) {
for (int y = 0; y < myBitmap.Height; y += skipY) {
Color pixelColor = myBitmap.GetPixel (x, y);
if (pixelColor.R + pixelColor.G + pixelColor.B == 0) {
myBitmap.SetPixel (x, y, Color.Red);
detectedDots++;
}
}
}
myBitmap.Save ("output.png");
Console.WriteLine (detectedDots + " dots detected");
I added an output so you can check which dots got detected (all containing red pixels).
Further improvement would be to find the center of a dot. After that you should know the width (and height) and can start at first upper left dot with an offset of the dots width.
Algorithm 2
The second algorithm analyses each pixel and is a lot easier to implement. As soon as there's a black pixel it checks if there was a black pixel in the same vertical or horizontal line before and skips in this case until there is no black pixel in line.
Further improvement would be to store the height of the first dot and to make the condition in the middle of the snippet more beautiful.
Stopwatch watch = new Stopwatch(); watch.Start();
Bitmap myBitmap = new Bitmap ("input.png");
int dotsDetected = 0;
List<int> xFound = new List<int>();
for (int x = 0; x < myBitmap.Width; x++) {
bool yFound = false;
bool dotFound = false;
for (int y = 0; y < myBitmap.Height; y++) {
Color pixelColor = myBitmap.GetPixel (x, y);
if (pixelColor.R + pixelColor.G + pixelColor.B == 0) {
dotFound = true;
if (yFound)
continue;
if (xFound.Contains (y)
|| xFound.Contains (y + 1)
|| xFound.Contains (y + 2)
|| xFound.Contains (y + 3)
|| xFound.Contains (y + 4)
|| xFound.Contains (y - 1)
|| xFound.Contains (y - 2)
|| xFound.Contains (y - 3)
|| xFound.Contains (y - 4)) {
yFound = true;
continue;
}
xFound.Add (y);
//myBitmap.SetPixel (x, y, Color.Red);
dotsDetected++;
yFound = true;
} else
yFound = false;
}
if(!dotFound) //no dot found in this line
xFound.Clear();
}
//myBitmap.Save ("output.png");
watch.Stop();
Console.WriteLine("Picture analyzed in " + watch.Elapsed.TotalSeconds.ToString("#,##0.0000"));
Console.WriteLine (dotsDetected + " dots detected");
Unless you are doing this to learn more about image processing, do not re-invent the wheel. Just use emgucv (or a similar library). The emgucv syntax is rather unfriendly (mostly because of its underlying Win32 OpenCV implementation), but basically it comes down to
Contour<Point> contour = img.FindContours(CV_CHAIN_APPROX_TC89_L1, RETR_TYPE.CV_RETR_LIST);
for (; contour != null; contour = contour.HNext)
{
// You now have the contours. These have characteristics like a boundingRect, which is an easy way to approach the center of a circle.
}
I created a full solution for the problem (relying only on Bitmap.GetPixel(Int32,Int32)).
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
namespace StackOverflow
{
public static class Program
{
static void Main(string[] args)
{
const String PATH = #"C:\sim\sbro6.png";
Stopwatch watch = new Stopwatch(); watch.Start();
List<Bitmap> l_new, l_old;
{
Bitmap bmp = Image.FromFile(PATH) as Bitmap;
// Initialization
l_old = new List<Bitmap>();
l_new = new List<Bitmap>(); l_new.Add(bmp);
// Splitting
while (l_new.Count > l_old.Count)
{
l_old = l_new; l_new = new List<Bitmap>();
l_new.AddRange(SplitBitmapsVertically(SplitBitmapsHorizontally(l_old)));
}
// for (Int32 i = 0; i < l_new.Count; i++)
// {
// l_new[i].Save(#"C:\sim\bitmap_" + i + ".bmp");
// }
}
watch.Stop();
Console.WriteLine("Picture analyzed in ".PadRight(59,'.') + " " + watch.Elapsed.TotalSeconds.ToString("#,##0.0000"));
Console.WriteLine("Dots found ".PadRight(59, '.') + " " + l_new.Count);
Console.WriteLine();
Console.WriteLine("[ENTER] terminates ...");
Console.ReadLine();
}
static List<Bitmap> SplitBitmapsVertically(List<Bitmap> l_old)
{
Int32 x_start = -1; Bitmap bmp; Boolean colContainsData = false;
List<Bitmap> l = new List<Bitmap>();
foreach(Bitmap b in l_old)
{
for (Int32 x = 0; x < b.Width; x++)
{
colContainsData = false;
for (Int32 y = 0; y < b.Height; y++)
{
if (b.GetPixel(x, y).ToArgb() != Color.White.ToArgb())
{
colContainsData = true;
}
}
if (colContainsData) if (x_start < 0) { x_start = x; }
if (!colContainsData || (x == (b.Width - 1))) if (x_start >= 0)
{
bmp = new Bitmap(x - x_start, b.Height);
for (Int32 x_tmp = x_start; x_tmp < x; x_tmp++)
for (Int32 y_tmp = 0; y_tmp < b.Height; y_tmp++)
{
bmp.SetPixel(x_tmp - x_start, y_tmp, b.GetPixel(x_tmp, y_tmp));
}
l.Add(bmp); x_start = -1;
}
}
}
return l;
}
static List<Bitmap> SplitBitmapsHorizontally(List<Bitmap> l_old)
{
Int32 y_start = -1; Bitmap bmp; Boolean rowContainsData = false;
List<Bitmap> l = new List<Bitmap>();
foreach (Bitmap b in l_old)
{
for (Int32 y = 0; y < b.Height; y++)
{
rowContainsData = false;
for (Int32 x = 0; x < b.Width; x++)
{
if (b.GetPixel(x, y).ToArgb() != Color.White.ToArgb())
{
rowContainsData = true;
}
}
if (rowContainsData) if (y_start < 0) { y_start = y; }
if (!rowContainsData || (y == (b.Height - 1))) if (y_start >= 0)
{
bmp = new Bitmap(b.Width, y - y_start);
for (Int32 x_tmp = 0; x_tmp < b.Width; x_tmp++)
for (Int32 y_tmp = y_start; y_tmp < y; y_tmp++)
{
bmp.SetPixel(x_tmp, y_tmp - y_start, b.GetPixel(x_tmp, y_tmp));
}
l.Add(bmp); y_start = -1;
}
}
}
return l;
}
}
}
It takes roughly half a second to process the image (see attached picture)
The idea is to iteratively split the provided image into rows and colums that do only contain a subset of dots until there is exactly one dot contained.
The dots may be arbitrarily spread over the image. Hope this helps

Why is my returning bitmap not returning properly?

I'm passing an image in to this method and fading the right edge of it, it was working and suddenly doesn't any more. It's returning the bitmap that i'm passing in to it, but not keeping the modified pixels, they come back original. Where am i going wrong?
private Bitmap fadedEdge(Bitmap bmp)
{
//img.Save("i.bmp");
//Bitmap bmp = (Bitmap)img;
int howfartofade = bmp.Width / 4;
int i = 0;
if (howfartofade > 255) i = howfartofade / 255;
else i = 255 / howfartofade;
if (i == 0) i = 1;
int alp = 255;
int counter = 0;
for (int x = bmp.Width - howfartofade; x < bmp.Width; x++)
{
if (howfartofade > 255)
{
counter++;
if (counter == i + 1)
{
alp -= 1;
counter = 0;
}
}
else
{
alp -= i;
}
for (int y = 0; y < bmp.Height; y++)
{
if (alp >= 0)
{
Color clr = bmp.GetPixel(x, y);
clr = Color.FromArgb(alp, clr.R, clr.G, clr.B);
bmp.SetPixel(x, y, clr);
}
else
{
Color clr = bmp.GetPixel(x, y);
clr = Color.FromArgb(0, clr.R, clr.G, clr.B);
bmp.SetPixel(x, y, clr);
}
}
}
return bmp;
}
I figured it out. Stupid me. I changed it to pass in an image and return an image, and created a new bitmap from the image being passed in to it.
private Image fadedEdge(Image input)
{
//img.Save("i.bmp");
//Bitmap bmp = (Bitmap)img;
Bitmap output = new Bitmap(input);
int howfartofade = input.Width / 8;
int i = 0;
if (howfartofade > 255) i = howfartofade / 255;
else i = (255 / howfartofade) + 1;
if (i == 0) i = 1;
int alp = 255;
int counter = 0;
for (int x = input.Width - howfartofade; x < input.Width; x++)
{
if (howfartofade > 255)
{
counter++;
if (counter == i + 1)
{
alp -= 1;
counter = 0;
}
}
else
{
alp -= (i);
}
for (int y = 0; y < input.Height; y++)
{
if (alp >= 0)
{
Color clr = output.GetPixel(x, y);
clr = Color.FromArgb(alp, clr.R, clr.G, clr.B);
output.SetPixel(x, y, clr);
}
else
{
Color clr = output.GetPixel(x, y);
clr = Color.FromArgb(0, clr.R, clr.G, clr.B);
output.SetPixel(x, y, clr);
}
}
}
return output;
}

Categories

Resources