Color resemblance for motion detection - c#

Given 2 consecutive frames, how can I search for pixels that changed?
I tried the following:
if (old != null)
{
for (int i = 0; i < b.Width; i++)
{
for (int j = 0; j < b.Height; j++)
{
if (!b.GetPixel(i, j).Equals(old.GetPixel(i, j)))
s.SetPixel(i, j, b.GetPixel(i, j));
else
s.SetPixel(i, j, Color.White);
}
}
}
Where "old" is the previous frame and "s" is the new frame. The code basically paints the pixels that didn't change in white.
But since the webcam produces a very low quality frame almost all of the pixels change.
How can I eliminate the pixels that didn't change "greatly"?

A very basic approach is to convert your Color pixel to an 0 - 255 based grey value.
So you can compare your pixels as an integer and make some delta error difference.
Consider this method which convert a color to a integer grayscale value
private static int GreyScaleRange(Color originalColor)
{
return (int)((originalColor.R * .3) + (originalColor.G * .59)
+ (originalColor.B * .11));
}
So instead of doing equal function, you should do
int deltadifference = 5 ;
if (Math.abs((GreyScaleRange(b.GetPixel(i, j)) - (GreyScaleRange(old.GetPixel(i, j)) > deltadifference)
s.SetPixel(i, j, b.GetPixel(i, j));
else
s.SetPixel(i, j, Color.White);

Related

Approximate image from byte array

I wrote an application where some dots are floating around and if i assign a dot a Point, it will move on this position. Now i want to load an image, convert it to a monochrome image (Only pure black or white pixels - no shades of gray) and make each dot floating to a position where its representing a black pixel.
I've already done loading and converting a image that way and extraceted the pixels as a 1 dimensional byte[]. I've managed to iterate though this array with the following code:
int stride = width * 4;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
int index = y * stride + 4 * x;
// array[index] <- Red
// array[index + 1] <- Green
// array[index + 2] <- Blue
// array[index + 3] <- Alpha
}
The byte array holds every pixel with 4 bytes (RGBA). So the array length is ImageHeight*ImageWidth*4 bytes. Either a pixel is Black (0, 0, 0, 255) or White (255, 255, 255, 255).
My problem now is that i'm not able to correctly approximate the black areas of the image with just n dots. In most cases I will have much less floating dots than there are black pixels in the array. So what i need is a method that gives me a Point[] that contains n Points that will represent only the black areas of the image as good as possible. Can someone help me out?
Loop though the array and find the points that their red, green and blue are 0
to get the Black dots:
List<Point> blackPoints = new List<Points>()
for(int i=0; i<array.Length; i+=4)
if(array[i] == 0 && array[i+1] == 0 && array[i+2] ==0) //alpha is not important
{
int tmp = i / 4;
blackPoints.Add(new Point(tmp%width, tmp/width));
}
Create Methods, to get the weight of a pixel based on its own and neighbors colors, and also a Method to find the weight of a block:
public static class Exts
{
public static int Weight(this points ps, int x, int y, int width, int height)
{
int weight = 0;
for(int i=Math.Max(x - 1, 0); i<Math.Min(width, x+1))
for(int j= Math.Max(y-1, 0), j<Math.Min(height, y+1))
if(ps.Any(a => a.X == i && a.Y == j)) weight++;
return weight;
}
public static int BlockWeight(this Point[] ps, int x, int y)
{
return ps.Count(a => a.X <= x+2 && a.Y<= y+2);
}
}
Now loop through the bitmap, with blocks of nine pixels (3x3) and if a blocks wieght is more than half (in this case more than or equal to 5), select a the point in this block that has heighest weight, to represent the black point:
List<Point> result = new List<Point>();
for(int i=0; i<width; i+=3)
for(int j=0; j< height; j+=3)
if(points.BlockWeight(i,j) >= 5)
result.Add(ps.Where(a => a.X <= x+2 && a.Y<= y+2).OrderByDescending(a => a.Weight(i, j, width, height)).First())

Count black pixels using red value

I'm using the following code on an image who has only black/white values so that if a color is black it should be counted but somehow the following if statement doesn't work. Is it written correctly or Im just using a good logic here
for (int y = 0; y < image.Height; y++)
{
Color pixel = image.GetPixel(x, y);
if(pixel.R > 0)
{
//some code here
}
}
Assuming no transparency, try
if(pixel == Color.Black)
....
(pixel.R>0 just checks color's Red component. It is 0 for Black.)
For barcodes, it might be better to use some thresholds to differentiate colors, e.g.:
int threshold = (255 + 255 + 255) / 2;
if (pixel.R + pixel.G + pixel.B < threshold)
....

Calculating brightness of an image in grey scale (32bbparb)

everyone. I was trying to calculate the brightness of a laser spot image, the image was originally in green colour as the laser is monochromatic in green colour. but the photo was then converted to grey scale image, using grey scale filter, I tried to define two for- loop to get the pixel value of the picture, but it seems I got something really unexpected, I have not much clue what's going on. I think I need someone to shed a light for me.
Situation: I have a black and white image, the algorithm below didn't give the brightest point i.e. the white spot, instead it points at some random position ( which is not black nor white).
EDIT:I use for loop to loop over the picture to find the pixel values.
for (int i = xstart; i < xend; i++)
{
for (int j = ystart; j < yend; j++)
{
Color pixelColor = myBitmap.GetPixel(i, j);
brightness = pixelColor.GetBrightness();
//brightness = 0.2126 * pixelColor.R + 0.7152 * pixelColor.G + 0.0722 * pixelColor.B;
//brightness = 0.333 * pixelColor.R + 0.333 * pixelColor.G + (1 - 0.333 * 2) * pixelColor.B;
brightness_array[k, 0] = i;
brightness_array[k, 1] = j;
brightness_array[k, 2] = brightness;
k++;
}
}
to find brightness all these algorithms gave a wrong position for the brightest point, i wonder it's because i had an extra alpha channel for transparency which affects the result.
double max_brightness = 0.0;
int positionX = 0;
int positionY = 0;
for (int m = 0; m < k; m++)
{
if (brightness_array[m, 2] > max_brightness)
{
positionX = Convert.ToInt32(brightness_array[m, 0]);
positionY = Convert.ToInt32(brightness_array[m, 1]);
max_brightness = brightness_array[m, 2];
}
}
The above code is how I found the maximum brightness, I scan the pixel one by one, and set the new max_brightness pixel as max_brightness , so that after you loop over the whole picture, you should get the max_brightness.
The code to work out the brightest point on the image is working fine. The problem is that the picture is displayed in a picture box which scales the image down and your mousemove code to determine where position of the cursor within the picturebox doesn't match the point on the raw image.

fixing color saturation using histogram normalization

I am developing a filter to work on images. After applying this filter to images, I am getting some RGB values which are more than 255 or less than 0. I can saturate this pixels to 255 and 0 respectively, but then the image is not good.
I want to find a way to normalize the pixel histograms so after normalization the values for RGB be between 0,255.
The code is as follow:
double max = outputPixel.Cast<double>().Max();
double targetMax = 300;
double min = outputPixel.Cast<double>().Min();
double targetMin = -50;
for (int c = 0; c < 3; c++)
{
for (int i = 0; i < outputPixel.GetLength(0); i++)
{
for (int j = 0; j < outputPixel.GetLength(1); j++)
{
outputPixel[i, j, c] = wb[c] * (((outputPixel[i, j, c] - min) * (targetMax-targetMin) / (max - min))+targetMin);
outputPixel[i, j, c] = Saturate(outputPixel[i, j, c]);
}
}
}
}
The outputPixel defined as
private static double[, ,] outputPixel;
and initialized as follow:
outputPixel = new double[inputImage.Width, inputImage.Height, 3];
The problem is:
If I don't do histogram normalization, then there is a lot of specke type noise on dark areas and most of the image is bleached out.
If I use histogram normalization, then I can see a red tint on white area (mainly cloud area).
One solution is to normalize histogram in HSB, but I can not use internal C# calculation for HSB as values for RGB are bigger (or smalle) than 255 (0).
What Can I do in C#?

XNA Draw a filled circle

In another thread on XNA, Callum Rogers wrote some code which creates a texture with the outline of a circle, but I'm trying to create a circle filled with a color. What I have to modify on this code to fill the circle with color?
public Texture2D CreateCircle(int radius)
{
int outerRadius = radius*2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f/radius;
for (double angle = 0; angle < Math.PI*2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}
Don't use a texture for stuff like this (especially for things being in one single color!) - also don't try to do it pixel by pixel. You've got 3D acceleration for a reason.
Just draw the circle similar to a pie using a triangle fan. You'll need the following vertices.
Center of the circle
x points on the circle's border.
The first two points will define a line between the center of the circle and its border. The third vertex will define the first polygon. Vertices 1, 3 and 4 will then define the second polygon, etc.
To get the points on the circle's border use the formulas from your example. The first angle will be 0°, the following ones multiples of (360° / points on circle). To get a full circle you'll need one additional point that matches the second point (the first point on the border).
Depending on the number of vertices on the circle you'll get different n-gons. The more vertices you use the rounder the shape will look (at some performance cost):
(Less than 2 vertices aren't possible as a polygon requires at least 3 vertices to be drawn.)
Total of 4 points (3 points on circle) will result in a triangle.
Total of 5 points (4 point on circle) will result in a square.
Total of 6 points (5 points on circle) will result in a pentagon
...
Actually the XNA example for drawing primites show how to draw a circle (or n-gon) using a triangle fan.
well for anyone who wants to do it pixel by pixel ... i made a solution based on the information given. In your 2d texture method add the following code to fill the circle. I'm making a game and wanted to be able to make circles different colors and sizes. So inside CreateCircle(int radius) method, add the following code after the outline has been created :
bool finished = false;
int firstSkip = 0;
int lastSkip = 0;
for (int i = 0; i <= data.Length - 1; i++)
{
if (finished == false)
{
//T = transparent W = White;
//Find the First Batch of Colors TTTTWWWTTTT The top of the circle
if ((data[i] == Color.White) && (firstSkip == 0))
{
while (data[i + 1] == Color.White)
{
i++;
}
firstSkip = 1;
i++;
}
//Now Start Filling TTTTTTTTWWTTTTTTTT
//circle in Between TTTTTTW--->WTTTTTT
//transaparent blancks TTTTTWW--->WWTTTTT
// TTTTTTW--->WTTTTTT
// TTTTTTTTWWTTTTTTTT
if (firstSkip == 1)
{
if (data[i] == Color.White && data[i + 1] != Color.White)
{
i++;
while (data[i] != Color.White)
{
//Loop to check if its the last row of pixels
//We need to check this because of the
//int outerRadius = radius * 2 + -->'2'<--;
for (int j = 1; j <= outerRadius; j++)
{
if (data[i + j] != Color.White)
{
lastSkip++;
}
}
//If its the last line of pixels, end drawing
if (lastSkip == outerRadius)
{
break;
finished = true;
}
else
{
data[i] = Color.White;
i++;
lastSkip = 0;
}
}
while (data[i] == Color.White)
{
i++;
}
i--;
}
}
}
}
// Set the data when finished
//-- don't need to paste this part, already given up above
texture.SetData(data);
return texture;
If you need to do it from scratch (though I'm guessing there are easier ways), change the way you perform the rendering. Instead of iterating through angles and plotting pixels, iterate through pixels and determine where they are relative to the circle. If they are <R, draw as fill color. If they are ~= R, draw as border color.
I know that I'm a little late, but I modified your code to fill in the center
public static Texture2D CreateCircle(GraphicsDevice importedGraphicsDevice, int radius)
{
int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(importedGraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f / radius;
for (double angle = 0; angle < Math.PI * 2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
//width
for (int i = 0; i < outerRadius; i++)
{
int yStart = -1;
int yEnd = -1;
//loop through height to find start and end to fill
for (int j = 0; j < outerRadius; j++)
{
if (yStart == -1)
{
if (j == outerRadius - 1)
{
//last row so there is no row below to compare to
break;
}
//start is indicated by Color followed by Transparent
if (data[i + (j * outerRadius)] == Color.White && data[i + ((j + 1) * outerRadius)] == Color.Transparent)
{
yStart = j + 1;
continue;
}
}
else if (data[i + (j * outerRadius)] == Color.White)
{
yEnd = j;
break;
}
}
//if we found a valid start and end position
if (yStart != -1 && yEnd != -1)
{
//height
for (int j = yStart; j < yEnd; j++)
{
data[i + (j * outerRadius)] = new Color(10, 10, 10, 10);
}
}
}
texture.SetData(data);
return texture;
}

Categories

Resources