Related
Im working on a painting game but i can't get the flood fill algorithm to work on large areas. Its a recursive algorithm and i read that implementing a stack might work however i can get it to work as well. Here's the code;
private void FillCluster(int x, int y, int colorIndex, HashSet<string> traversedCells)
{
Debug.Log(colorIndex);
// Check if this cell is within the bounds of the picture and has a color number and the
if(x < 0 || x >= ActivePictureInfo.XCells ||
y < 0 || y >= ActivePictureInfo.YCells ||
ActivePictureInfo.ColorNumbers[y][x] == -1 ||
ActivePictureInfo.ColorNumbers[y][x] != colorIndex)
{
return;
}
string cellKey = string.Format("{0}_{1}", x, y);
// Check if this cell has already been traversed by FillBlob
if (traversedCells.Contains(cellKey))
{
return;
}
// Check if this cell is already colored in with the correct color
if (!ActivePictureInfo.HasProgress || ActivePictureInfo.Progress[y][x] != -1)
{
ColorCell(x, y, colorIndex);
}
// Add this cells key to the traversed hashset to indicate it has been processed
traversedCells.Add(cellKey);
// Recursively call recursively with the four cells adjacent to this cell
FillCluster(x - 1, y, colorIndex, traversedCells);
FillCluster(x + 1, y, colorIndex, traversedCells);
FillCluster(x, y - 1, colorIndex, traversedCells);
FillCluster(x, y + 1, colorIndex, traversedCells);
FillCluster(x - 1, y - 1, colorIndex, traversedCells);
FillCluster(x - 1, y + 1, colorIndex, traversedCells);
FillCluster(x + 1, y - 1, colorIndex, traversedCells);
FillCluster(x + 1, y + 1, colorIndex, traversedCells);
}
a version without having to remember all visited cells because they are already marked by the color
private void FillCluster(int x, int y, int colorIndex)
{
Debug.Log(colorIndex);
var currentSeam = new Queue<PointDirection>();
if (FillPoint(x, y, colorIndex))
{
currentSeam.Enqueue(new PointDirection(x - 1, y, Direction.Left));
currentSeam.Enqueue(new PointDirection(x + 1, y, Direction.Right));
currentSeam.Enqueue(new PointDirection(x, y - 1, Direction.Up));
currentSeam.Enqueue(new PointDirection(x, y + 1, Direction.Down));
}
while (currentSeam.Count > 0)
{
var current = currentSeam.Dequeue();
if (FillPoint(current.X, current.Y, colorIndex))
{
if (current.Direction != Direction.Right)
currentSeam.Enqueue(new PointDirection(x - 1, y, Direction.Left));
if (current.Direction != Direction.Left)
currentSeam.Enqueue(new PointDirection(x + 1, y, Direction.Right));
if (current.Direction != Direction.Down)
currentSeam.Enqueue(new PointDirection(x, y - 1, Direction.Up));
if (current.Direction != Direction.Up)
currentSeam.Enqueue(new PointDirection(x, y + 1, Direction.Down));
}
}
}
private bool FillPoint(int x, int y, int colorIndex)
{
if (x < 0 || x >= ActivePictureInfo.XCells ||
y < 0 || y >= ActivePictureInfo.YCells ||
ActivePictureInfo.ColorNumbers[y][x] == -1 ||
ActivePictureInfo.ColorNumbers[y][x] == colorIndex)
{
return false;
}
ActivePictureInfo.ColorNumbers[y][x] = colorIndex;
return true;
}
private struct PointDirection
{
public PointDirection(int x, int y, Direction direction)
{
X = x;
Y = y;
Direction = direction;
}
public int X { get; }
public int Y { get; }
public Direction Direction { get; }
}
private enum Direction : byte
{
Up,
Right,
Down,
Left
}
So the reason you are getting a stackOverflow is because the state of the previous iteration is saved on the stack unless both of these condition are met:
You are doing tail recursion (look it up, but it isn't relevant right now)
Your language optimizes tail recursion (C# doesn't)
As the stack size is limited and your algorithm very quickly reaches thousands of calls one after the other, you cannot make the recursive version work in C#, that is impossible, but it seems like you understood this already
Here is a pseudocode solution to do it without recursion. I don't know C# so the synthax is not correct, but the idea is correct, you will have to transform it into proper C#
private void FillCluster(int x, int y, int colorIndex, HashSet<string> traversedCells) {
Debug.Log(colorIndex);
// Check if this cell is within the bounds of the picture and has a color number and the
//Declare a set, add inside the current node
Set<Tuple> nodesToFill = new Set<>()
nodesToFill.add(new Tuple(x, y));
//Replace the recursion by a loop
for (Tuple tuple in nodesToFill) {
//initialize the current position
x = tuple.first
y = tuple.second
//Deal with the current node
if(FillClusterInner(x, y, colorIndex, traversedCells)) {
//Add the new nodes to the set instead of using recursion
nodesToFill.add(new Tuple(x-1, y))
nodesToFill.add(new Tuple(x + 1, y))
nodesToFill.add(new Tuple(x, y - 1))
nodesToFill.add(new Tuple(x, y + 1))
nodesToFill.add(new Tuple(x - 1, y - 1))
nodesToFill.add(new Tuple(x - 1, y + 1))
nodesToFill.add(new Tuple(x + 1, y - 1))
nodesToFill.add(new Tuple(x + 1, y + 1))
}
//Remove the current tuple from the set as you have dealt with it
nodesToFill.remove(tuple)
}
}
//This is a non-recursive method which fills a single specified node
bool FillClusterInner(int x, int y, int colorIndex, HashSet<string> traversedCells) {
if(x < 0 || x >= ActivePictureInfo.XCells ||
y < 0 || y >= ActivePictureInfo.YCells ||
ActivePictureInfo.ColorNumbers[y][x] == -1 ||
ActivePictureInfo.ColorNumbers[y][x] != colorIndex)
{
return false;
}
string cellKey = string.Format("{0}_{1}", x, y);
// Check if this cell has already been traversed by FillBlob
if (traversedCells.Contains(cellKey))
{
return false;
}
// Check if this cell is already colored in with the correct color
if (!ActivePictureInfo.HasProgress || ActivePictureInfo.Progress[y][x] != -1)
{
ColorCell(x, y, colorIndex);
}
// Add this cells key to the traversed hashset to indicate it has been processed
traversedCells.Add(cellKey);
return true;
}
You can see the idea: instead of recursion, we use a set, which contains all the indexes of the nodes we have yet to fill. You add to this set instead of calling the recursive function, and you remove from the set each position you have handled. When the set is empty you have filled every cell that had to be filled.
2x + 4y + 6z = 1200
x + y + z = 300
how can I find the possible x, y, z integer values in a c# method?, I am trying to find a better solution instead of using brute force nested for loops since it is not a good solution.
public List<Tuple<int, int, int>> Calculate()
{
var result = new List<Tuple<int, int, int>>();
int maxValue = 300;
for(int i = 0; i< maxValue; i++)
for (int j = 0; j < maxValue; j++)
for (int k = 0; k < maxValue; k++)
if (i + j + k == maxValue && 2 * i + 4 * j + 6 * k == 1200)
result.Add(new Tuple<int, int, int>(i, j, k));
return result;
}
Thank you in advance.
Well, having
2x + 4y + 6z = 1200
x + y + z = 300
you can put it as
x + 2y + 3z = 600
x + y + z = 300
subtract 2nd from the 1st and you get
y + 2z = 300
or
y = 300 - 2z
Since x = 300 - y - z we can put it as
x = 300 - y - z =
= 300 - (300 - 2z) - z =
= 300 - 300 + 2z - z =
= z
Finally, for the arbitrary z (which is free variable)
x = z
y = 300 - 2 * z;
Possible c# code:
private static (int x, int y, int z) Solution(int x) => (x, 300 - 2 * x, x);
Demo:
string solutions = string.Join(Environment.NewLine, Enumerable
.Range(0, 10)
.Select(x => Solution(x)));
...
// 10 solutions for x = 0..9
string solutions = string.Join(Environment.NewLine, Enumerable
.Range(0, 10)
.Select(x => Solution(x)));
Console.Write(solutions);
Outcome:
(0, 300, 0)
(1, 298, 1)
(2, 296, 2)
(3, 294, 3)
(4, 292, 4)
(5, 290, 5)
(6, 288, 6)
(7, 286, 7)
(8, 284, 8)
(9, 282, 9)
If you are looking for non-negative solutions only (you've mentioned probabilities in the comments for the code), then use x in [0..150] range:
(0, 300, 0)
(1, 298, 1)
(2, 296, 2)
...
(148, 4, 148)
(149, 2, 149)
(150, 0, 150)
Edit: your Calculate() method improved:
public static List<Tuple<int, int, int>> Calculate() {
var result = new List<Tuple<int, int, int>>();
const int maxValue = 300;
int start = Math.Max(150 - maxValue / 2, 0);
for (int x = start; ; ++x) {
int y = 300 - 2 * x;
int z = x;
if (y < 0 || x > maxValue)
break;
result.Add(new Tuple<int, int, int>(x, y, z));
}
return result;
}
Math Theory
If you want a general method you should use some Linear Algebra theory.
Let's rewrite the equations in matrix form as A(x,y) + bz = c,
where:
A: is the matrix that contains the coefficents of the x,y coordinates
A = (2,4,1,1)
b = (6,1) (transposed) is the vector containing the z coefficents.
c = (1200, 300) (transposed) is the vector containing the constant terms.
then (x,y) = AInverse * (c - bz). That is a function of z (Let's say f(z)). So for every z you obtain a valid solution: (f(z),z). f(z) is a vector of 2 components.
Note
If A is not invertible because the equations (rstricted to x and y) are linearly dependent (i.e. the 2 equations are the "same" equation in x and y), this method fails.
Code
We can code that like that:
Step1
We must code the Matrix2x2. I do it from scratch now, but you maybe want to use some framework if you really need in production:
public record struct Matrix2x2 (float A00, float A01, float A10, float A11)
{
public float Determinant => A00 * A11 - A01 * A10;
public Matrix2x2 Invert() => Determinant != 0
? 1 / Determinant * new Matrix2x2() { A11 = A00, A00 = A11, A01 = -A01, A10 = -A10 }
: throw new InvalidOperationException($"Cannot Invert this matrix");
public static Matrix2x2 operator *(Matrix2x2 a, float number) => new()
{
A00 = a.A00 * number,
A01 = a.A01 * number,
A10 = a.A10 * number,
A11 = a.A11 * number,
};
public static Matrix2x2 operator *(float number, Matrix2x2 a) => a * number;
public static Vector2 operator *(Matrix2x2 a, Vector2 vector)
{
var x = a.A00 * vector.X + a.A01 * vector.Y;
var y = a.A10 * vector.X + a.A11 * vector.Y;
return new(x, y);
}
}
Step2
We must encode the equation. We need the Matrix A, the z coefficents and b, and the vector c of constant terms:
public delegate Vector3 SolutionSpace(float z);
public static class EquationHelper
{
public static SolutionSpace SolveEquation(Matrix2x2 equationMatrix, Vector2 b, Vector2 c) => z =>
{
var v = equationMatrix.Invert() * (c - z * b);
return new Vector3(v.X, v.Y, z);
};
}
Note: The object returned from this function is the solution space, that depends from 1 parameter (the z value). So we could see it as a function that maps (float z) => (x(z), y(z), z) that in c# is a delegate that returns a Vector3 and takes in input a float.
Usage
var A = new Matrix2x2(2, 4, 1, 1);
var b = new Vector2(6, 1);
var c = new Vector2(1200, 300);
var solution = EquationHelper.SolveEquation(A, b, c);
foreach(var z in Enumerable.Range(-10, 21))
Console.WriteLine(solution(z));
Output
I am working on an edge points extraction algorithm.
I have a List of points representing a blob (a group of connected pixels) and I want to extract edge points.
I have an example algorithm below, but I am wondering if there is a faster way.
I am using class for BlobPoint, because there is more to it than shown in the example below.
BlobPoint index value represent index value in the original image (image is 1D array of pixels, hence required width information).
This algorithm is build on idea that if a pixel on right or bottom or left or top does not exist in the list, then this point is an edge point.
The list will usually contain between 20 000 to 1 000 000 elements.
yr and yl are added for clarity.
public static List<BlobPoint> GetEdgePoints(this List<BlobPoint> points, int width)
{
int length = points.Count;
List<BlobPoint> temp = new List<BlobPoint>();
for (int i = 0; i < length; i++)
{
BlobPoint point = points[i];
int x = point.X;
int y = point.Y;
int xr = x + 1;
int yr = y;
int xb = x - width;
int yb = y - 1;
int xl = x - 1;
int yl = y;
int xt = x + width;
int yt = y + 1;
if (!points.Any(p => p.X == xb && p.Y == yb) || !points.Any(p => p.X == xl && p.Y == yl) || !points.Any(p => p.X == xr && p.Y == yr) || !points.Any(p => p.X == xt && p.Y == yt))
{
temp.Add(point);
}
}
return temp;
}
public class BlobPoint
{
public int X = 0;
public int Y = 0;
public int Index = 0;
public BlobPoint(int x, int y, int index)
{
X = x;
Y = y;
Index = index;
}
}
My solution:
Ok, I have done some testing and the method above is way too slow for my needs.
I will leave question as this might be of use to someone who looks for faster iteration method.
This is a solution I came up with, but it is still a bit slow:
public static Dictionary<int, List<BlobPoint>> GetEdgePoints(BlobPoint[] points, int[] labels, int image_width, int image_height)
{
int length = labels.Length;
if (image_height * image_width != length) throw new ArgumentException("image_width x image_height does not match labels.Length");
if (length == 0) throw new ArgumentException("label array cannot be empty!");
if (points.Length != length) throw new ArgumentException("points array length cannot be different from labels array length!");
var dict = new Dictionary<int, List<BlobPoint>>();
for (int i = 0; i < length; i++)
{
int label = labels[i];
if (label <= 0) continue;
BlobPoint point = points[i];
int x = point.X;
int y = point.Y;
int width_offset = image_width - 1;
int height_offset = image_height - 1;
if (x > 0 && x < width_offset && y > 0 && y < height_offset)
{
if (labels[i + 1] == label && labels[i - 1] == label && labels[i + image_width] == label && labels[i - image_width] == label)
{
continue;
}
}
if (dict.ContainsKey(label))
dict[label].Add(point);
else
dict.Add(label, new List<BlobPoint>() { point });
}
return dict;
}
Personally, I'd use a two-dimensional Boolean array representing the image for this, in which the indices for the points you have are set to true. In exchange for some memory, and a single loop over the points in advance, this allows lightning-fast checks of whether a point is inside the list, removing all internal iterations for lookups on the points list. There is not a single .Contains or .Any or .All performed here. Just two loops over the main list, and eight very simple checks inside the second loop.
public static List<BlobPoint> GetEdgePoints(this List<BlobPoint> points, Int32 imageWidth, Int32 imageHeight)
{
Boolean[,] pointInList = new Boolean[imageHeight, imageWidth];
foreach (BlobPoint p in points)
pointInList[p.Y, p.X] = true;
List<BlobPoint> edgePoints = new List<BlobPoint>();
Int32 lastX = imageWidth - 1;
Int32 lastY = imageHeight - 1;
foreach (BlobPoint p in points)
{
Int32 x = p.X;
Int32 y = p.Y;
// Image edge is obviously a blob edge too.
// Coordinates checks are completely safe after the edge checks.
if (x == 0 || y == 0 || x == lastX || y == lastY
|| !pointInList[y - 1, x]
|| !pointInList[y, x - 1]
|| !pointInList[y, x + 1]
|| !pointInList[y + 1, x])
edgePoints.Add(p);
}
return edgePoints;
}
This technically works without the image width and height, but then you need to do another loop in advance to get the maximum x and y present in your points, so you can make a Boolean[,] array that can contain all coordinates. If you have that image data, though, it's obviously a lot more efficient to just use it.
Not sure why you bother with the BlobPoint class over the standard Point struct, though. The index in the original array is just p.Y * stride + p.X anyway.
Essentially, I have a list in C# full of structs that look something like this (code may not be syntax appropriate)
public struct Numbers {
public int DistanceFromArbitraryPoint;
public int X;
public int Y;
}
What I would like to do is sort a list such that:
Sort list by distance (already did this no problem).
For each Numbers that has a distance < SomeCriteria, sort those list items by the sum of X and Y, while not creating a new list. Simply rearrange the list.
The result would make this:
Distance 12, x 0, y 0;
Distance 4, x 20, y 20;
Distance 6, x 0, y 3;
After Step One:
Distance 4, x 20, y 20;
Distance 6, x 0, y 3;
Distance 12, x 0, y 0;
After step 2 (sort all by sum of X and Y if distance < 10)
Distance 6, x 0, y 3; // lower X and Y Sum, goes first
Distance 4, x 20, y 20;
Distance 12, x 0, y 0; // Distance higher than 10, remains unsorted
more complex comparison method required here
var numbers = new List<Numbers>{ new Numbers(6,0,3), new Numbers(12,0,0),
new Numbers(4,20,20), new Numbers(5,20,20)};
int d = 10;
numbers.Sort((a,b)=>
{
if (a.DistanceFromArbitraryPoint >= d && b.DistanceFromArbitraryPoint >= d)
return a.DistanceFromArbitraryPoint.CompareTo(b.DistanceFromArbitraryPoint);
if (a.DistanceFromArbitraryPoint >= d)
return 1;
if (b.DistanceFromArbitraryPoint >= d)
return -1;
int c = (a.X+a.Y).CompareTo(b.X+b.Y);
if (c == 0)
c = a.DistanceFromArbitraryPoint.CompareTo(b.DistanceFromArbitraryPoint);
return c;
});
output:
6 (0,3); 4 (20,20); 5 (20,20); 12 (0,0)
Sort(Comparison<T> comparison) from List<T> is used, not Linq
fiddle
public struct Numbers
{
public Numbers(int d, int x, int y)
{
DistanceFromArbitraryPoint = d;
X = x;
Y = y;
}
public int DistanceFromArbitraryPoint;
public int X;
public int Y;
public override string ToString()
{
return String.Format("{0} ({1},{2})", DistanceFromArbitraryPoint, X,Y);
}
}
I am currently creating a little paint program for exercise. Right now i'm trying to do the paint bucket tool, or in other words a flood fill. The funny thing is: if the number of pixels which have to be filled is small, everything works fine. If hate number of to filling pixels is higher, it gives me a SO-Exception. Here is my code:
private void FloodFill(Bitmap picture, int x, int y)
{
if (x <= 0 || y <= 0 || x >= DrawingPanel.Width || y >= DrawingPanel.Height)
{
return;
}
if (picture.GetPixel(x, y) != löschFarbe)
{
return;
}
if (picture.GetPixel(x, y) == löschFarbe)
{
picture.SetPixel(x, y, ColorButton.BackColor);
}
FloodFill(picture, x + 1, y);
FloodFill(picture, x, y + 1);
FloodFill(picture, x - 1, y);
FloodFill(picture, x, y - 1);
FloodFill(picture, x + 1, y + 1);
FloodFill(picture, x - 1, y + 1);
FloodFill(picture, x + 1, y - 1);
FloodFill(picture, x - 1, y - 1);
}
"löschFarbe" is the color which is clicked (which will be erased/overwritten with another color)
Error occurring: If i want to fill the complete picture or a big space, I get an error here:
if (picture.GetPixel(x, y) != löschFarbe)
{
return;
}
Anyone knows how I can solve this?
BTW this is a picture of my program:
Even a moderate sized floodfill will blow your call stack.
Try converting this recursion based method to a stack based method as in this question.