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;
Related
I am trying to implement Hough Line Transform.
Input. I am using the following image as input. This single line is expected to produce only one intersection of sine waves in the output.
Desired behavior. my source code is expected to produce the following output as it was generated by the sample application of AForge framework.
Here, we can see:
the dimension of the output is identical to the input image.
the intersection of sine waves are seen at almost at the center.
the intersection pattern of waves is very small and simple.
Present behavior. My source code is producing the following output which is different than that of the output generated by AForge.
the intersection is not at the center.
the wave patterns are also different.
Why is my code producing a different output?
.
Source Code
I have written the following code myself. The following is a Minimal, Complete, and Verifiable source code.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int inWidth = image.GetLength(0);
int inHeight = image.GetLength(1);
int inWidthHalf = inWidth / 2;
int inHeightHalf = inHeight / 2;
int outWidth = (int)Math.Sqrt(inWidth * inWidth + inHeight * inHeight);
int outHeight = 180;
int outHeightHalf = outHeight / 2;
houghMap = new int[outWidth, outHeight];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < inHeight; y++) //|
{ //|
for (int x = 0; x < inWidth; x++)//<-----------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves. So, it may
// vary from -90 to +90 degrees.
for (int theta = -outHeightHalf; theta < outHeightHalf; theta++)
{
double rad = theta * Math.PI / 180;
// respective radius value is computed
//int radius = (int)Math.Round(Math.Cos(rad) * (x - inWidthHalf) - Math.Sin(rad) * (y - inHeightHalf));
//int radius = (int)Math.Round(Math.Cos(rad) * (x + inWidthHalf) - Math.Sin(rad) * (y + inHeightHalf));
int radius = (int)Math.Round(Math.Cos(rad) * (x) - Math.Sin(rad) * (outHeight - y));
// if the radious value is between 1 and
if ((radius > 0) && (radius <= outWidth))
{
houghMap[radius, theta + outHeightHalf]++;
}
}
}
}
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Bitmap bitmap = (Bitmap)pictureBox1.Image as Bitmap;
int[,] intImage = ToInteger(bitmap);
HoughMap houghMap = new HoughMap();
houghMap.image = intImage;
houghMap.Compute();
int[,] normalized = Rescale(houghMap.houghMap);
Bitmap hough = ToBitmap(normalized, bitmap.PixelFormat);
pictureBox2.Image = hough;
}
public static int[,] Rescale(int[,] image)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
int minVal = 0;
int maxVal = 0;
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
double conv = imageCopy[i, j];
minVal = (int)Math.Min(minVal, conv);
maxVal = (int)Math.Max(maxVal, conv);
}
}
int minRange = 0;
int maxRange = 255;
int[,] array2d = new int[Width, Height];
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
array2d[i, j] = (maxRange - minRange) * (imageCopy[i,j] - minVal) / (maxVal - minVal) + minRange;
}
}
return array2d;
}
public int[,] ToInteger(Bitmap input)
{
int Width = input.Width;
int Height = input.Height;
int[,] array2d = new int[Width, Height];
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Color cl = input.GetPixel(x, y);
int gray = (int)Convert.ChangeType(cl.R * 0.3 + cl.G * 0.59 + cl.B * 0.11, typeof(int));
array2d[x, y] = gray;
}
}
return array2d;
}
public Bitmap ToBitmap(int[,] image, PixelFormat pixelFormat)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
Bitmap bitmap = new Bitmap(Width, Height, pixelFormat);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
int iii = imageCopy[x, y];
Color clr = Color.FromArgb(iii, iii, iii);
bitmap.SetPixel(x, y, clr);
}
}
return bitmap;
}
}
I have solved the problem from this link. The source code from this link is the best one I have ever came across.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int Width = image.GetLength(0);
int Height = image.GetLength(1);
int centerX = Width / 2;
int centerY = Height / 2;
int maxTheta = 180;
int houghHeight = (int)(Math.Sqrt(2) * Math.Max(Width, Height)) / 2;
int doubleHeight = houghHeight * 2;
int houghHeightHalf = houghHeight / 2;
int houghWidthHalf = maxTheta / 2;
houghMap = new int[doubleHeight, maxTheta];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < Height; y++) //|
{ //|
for (int x = 0; x < Width; x++)//<-------------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves.
// It may vary from -90 to +90 degrees.
for (int theta = 0; theta < maxTheta; theta++)
{
double rad = theta *Math.PI / 180;
// respective radius value is computed
int rho = (int)(((x - centerX) * Math.Cos(rad)) + ((y - centerY) * Math.Sin(rad)));
// get rid of negative value
rho += houghHeight;
// if the radious value is between
// 1 and twice the houghHeight
if ((rho > 0) && (rho <= doubleHeight))
{
houghMap[rho, theta]++;
}
}
}
}
}
}
}
}
Just look at this C++ code, and this C# code. So, complicated and messy that my brain got arrested. Especially, the C++ one. I never anticipated someone to store 2D values in a 1D array.
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;
}
Explanation
I'm generating textures for UI elements that have a single pixel outline on the edge of the texture.
The method of setting the color data in the texture is limited to passing a one dimensional array of color values.
These textures are 2D, so they are basically rectangles. I need to be able to identify when the current pixel is at an edge. This is how I currently do it:
Color[] colorData = new Color[width * height];
for (int p = 0; p < colorData.Length; p++) {
//Top
if (p < width - 1) {
colorData[p] = DefaultOutlineColor;
}
//Left
else if(p % width == 0) {
colorData[p] = DefaultOutlineColor;
}
//Right
else if(p % height == height - outlineWidth) {
colorData[p] = DefaultOutlineColor;
}
//Bottom
else if(p >= width * (height - outlineWidth)) {
colorData[p] = DefaultOutlineColor;
}
//Fill
else {
colorData[p] = DefaultBaseColor;
}
}
The Problem
Some Modulo math and what not. The problem I am having is with the Right side of the Texture. More specifically calculating the right side edge. A picture is worth a thousand words:
I know it is just a miss calculation in the Right edge part. But I have no idea how to make it work. Any help would be highly appreciated.
EDIT:
Figured it out. Here is the working code:
//Fill
for (int p = 0; p < colorData.Length; p++) {
colorData[p] = DefaultBaseColor;
}
//Top
for (int p = 0; width * outlineWidth > p; p++) {
colorData[p] = DefaultOutlineColor;
}
//Left and Right
for (int p = 1; height > p; p++) {
for (int i = 0; i < outlineWidth; i++) {
colorData[(p * width) + i] = DefaultOutlineColor; //Left
colorData[((p * width) - i) - 1] = DefaultOutlineColor; //Right
}
}
//Bottom
for (int p = width * height - (width * outlineWidth); colorData.Length > p; p++) {
colorData[p] = DefaultOutlineColor;
}
Why not do it in three loops? One for the top, one for the left and right and then one for the bottom? That way you can skip all the items that don't need to be touched at all.
for(int ndx = 0; Width > ndx; ++ndx)
{
colorData[ndx] = DefaultOutlineColor;
}
for(int ndx = 1; Height > ndx; ++ndx)
{
colorDatandx * Widthp] = DefaultOutlineColor;
colorData[ndx*Width + Width] = DefaultOutlineColor;}
}
for(int ndx = Width * Height - Width; Length > ndx; ++ndx)
{
colorDatandxp] = DefaultOutlineColor;
}
Using SoronelHaetir's method I figured it out. Had to edit the math a little bit. Here is the working code if anyone is interested:
for (int ndx = 0; width > ndx; ndx++) {
colorData[ndx] = DefaultOutlineColor;
}
for (int ndx = 1; height > ndx ; ndx++) {
colorData[ndx * width] = DefaultOutlineColor;
colorData[(ndx * width) - 1] = DefaultOutlineColor;
}
for (int ndx = width * height - width; colorData.Length > ndx; ndx++) {
colorData[ndx] = DefaultOutlineColor;
}
In practice you're doing a one-pixel border - but you use outlineWidth in your original code, so here's an alternative method that works fine for arbitrary border sizes.
for (int p = 0; p < colorData.length; p++) {
// retrieving the x/y coords isn't expensive if you do this only once
int x = p % height;
int y = p / height;
if (x < outlineSize || y < outlineSize || x >= width - outlineSize || y >= height - outlineSize) {
colorData[p] = DefaultOutlineColor;
}
}
I'm currently using the following to apply a texture to a polygon formed by TriangleList
public static VertexPositionColorTexture[] TextureMapping(VertexPositionColorTexture[] vertices, float xScale, float yScale)
{
bool initialized = false;
float x, y;
float lX = 0, hX = 0, lY = 0, hY = 0;
for (int i = 0; i < vertices.Length; i++)
{
x = vertices[i].Position.X;
y = vertices[i].Position.Y;
if (!initialized)
{
hX = x;
lX = x;
hX = y;
hY = y;
initialized = true;
}
else
{
if (x > hX)
{
hX = x;
}
else if (x < lX)
{
lX = x;
}
if (y > hY)
{
hY = y;
}
else if (y < lY)
{
lY = y;
}
}
}
float width = (Math.Abs(lX) + Math.Abs(hX)) / xScale;
float height = (Math.Abs(lY) + Math.Abs(hY)) / yScale;
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].TextureCoordinate.X = vertices[i].Position.X / width;
vertices[i].TextureCoordinate.Y = vertices[i].Position.Y / height;
}
return vertices;
This currently works fine for a polygon that has points that all have Z=0 (example: (0,0,0) (0,10,0) (10,10,0) (10,0,0)) but doesn't work for any that are rotated or not flat along the z (example (0,0,0) (0,0,10) (0,10,10) (0,10,0)). The only solution I have come with is to get the plane that the polygon lies on (it will always be flat) and somehow rotate or translate the vertices in the above method to flatten it to the xy line to allow for the correct height and width to be determined. Anyone point me in the right direction, or suggest something else?
Solved this myself by re-writing and rotating the polygon to the z plane.
Basically I'm creating a forest fire program that depends on the wind / dryness of the surround elements. I have an array var Trees [,] that is 20 x 20. The middle square is set "on fire". This is what needs to be done once you click button1: Evaluate each square around the one that is set on fire to determine the probability for the others to catch fire.
Color[,] map = new Color[WIDTH, HEIGHT];
for (int x = 0; x < WIDTH; x++)
for (int y = 0; y < HEIGHT; y++)
{
if (x == WIDTH / 2 && y == HEIGHT / 2)
map[x, y] = Color.Red;
else
map[x, y] = Color.Green;
}
fireBox1.box = map;
This is the 20 x 20 array that I have setup with the middle square set on fire. I just have no idea how to get the squares (array elements) around the one that is currently on fire.
You can start with a simple loop.
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
var tree = Trees[i, j];
// ...
}
}
After you have built your matrix the center should look like this.
[G][G][G]
[G][R][G]
[G][G][G]
Then we can loop through only the points that touch the center point.
int centerX = 9;
int centerY = 9;
int beginX = centerX - 1;
int endX = centerX + 1;
int beginY = centerY - 1;
int endY = centerY + 1;
for (int y = beginY; y <= endY; y++)
{
for (int x = beginX ; x <= endX; x++)
{
//Skip the center
if (x == centerX && y == centerY)
continue;
// Calculate the chance of catching on fire.
if (IsWindyPoint(x, y) || IsDryPoint(x, y))
map[x, y] = Color.Yellow;
}
}
So assuming we have wind blowing east we should see this as the matrix.
[G][G][G]
[G][R][Y]
[G][G][G]
And eventually it will expand out like this.
[G][G][G][G]
[G][G][Y][Y]
[G][R][R][Y]
[G][G][Y][Y]
[G][G][G][G]