edit: It works now, I moved the getting X and Y into a seperate function, and it seemed to have fixed it (I'm not sure exactly what was wrong, sorry)
I've been making a Tile Editor using WPF and C#, and I seem to have come across a rather odd problem.
When I first run the fill algorithm on a blank canvas, it fills up all tiles except one (usually around (2,7)). Depending on where I start, it jumps around slightly in that area.
What really confuses me is if I flood fill that same area, it will fix the missing tile.
I tried the recursive flood fill as well as the one using a Queue. Here's the code for the Recursion one:
private void FloodFill(Tile node, int targetID, int replaceID) {
if (targetID == replaceID) return;
if (node.TileID != targetID) return;
node.TileID = replaceID;
SetImageAtCoord(node.X, node.Y);
if (node.Y + 1 != Map.Height) FloodFill(Map.TileInformation[node.X, node.Y + 1], targetID, replaceID);
if (node.Y - 1 != -1) FloodFill(Map.TileInformation[node.X, node.Y - 1], targetID, replaceID);
if (node.X - 1 != -1) FloodFill(Map.TileInformation[node.X - 1, node.Y], targetID, replaceID);
if (node.X + 1 != Map.Width) FloodFill(Map.TileInformation[node.X + 1, node.Y], targetID, replaceID);
}
Now since I also tried a different method and got the same problem, I thought it might be in the SetImageAtCoord():
private void SetImageAtCoord(int x, int y) {
IEnumerable<Rectangle> rectangles = MapViewer.Children.OfType<Rectangle>();
Map.TileInformation[x, y].TileID = CurrentTile;
foreach (Rectangle rect in rectangles) {
int X = (int)Canvas.GetLeft(rect) / 32;
int Y = (int)Canvas.GetTop(rect) / 32;
if (X == x && Y == y) {
rect.Fill = CurrentImage;
}
}
}
My only guess is that it either misses it on the pass (yet somehow make it every other time), or it has something to do with the placement of the rectangle on the Canvas.
edit:
Here is the code for selecting what CurrentImage (and CurrentTile are):
private void Sprite_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Rectangle curr = (Rectangle)e.OriginalSource;
CurrentImage = (ImageBrush)curr.Fill;
int x = (int)Canvas.GetLeft(curr) / 32;
int y = (int)Canvas.GetTop(curr) / 32;
CurrentTile = y * Map.SpriteSheet.GetLength(0) + x;
}
and the code that calls FloodFill
if (CurrentContol == MapEditControl.Fill) {
Rectangle ClickedRectangle = (Rectangle)e.OriginalSource;
ClickedRectangle.Fill = CurrentImage;
int x = (int)Canvas.GetLeft(ClickedRectangle) / 32;
int y = (int)Canvas.GetTop(ClickedRectangle) / 32;
int oldTile = Map.TileInformation[x, y].TileID;
FloodFill(Map.TileInformation[x, y], oldTile, CurrentTile);
}
Map.TileInformation is actually only really used to store the ID for exporting to JSON, and the actual rectangles aren't bound to that in anyway (probably should have)
Related
I am doing image manipulation, I have a class that I've inherited that I can't change because it's referenced too many times to completely refactor. Here is the class as it stands.
I have a Bitmap and I am trying to get a collection of all of the LinkedPixels. For example pixel
x, y would be a key and a pixel in the center would be attached to four pixels, (x+1, y), (x, y + 1), (x-1, y), (y-1, x). Pixels on the sides would have three attached Pixels, and Pixels in the corners only two.
imWrap.Arr is an array with all of the Pixels inside of a Bitmap, this is built as the program spools up so this is not built on the fly it is already available.
Since I cannot change the class itself (I could change it to a struct if necessary) I need to find the fastest way to create the LinkedPix, I am aware that there are many better ways to do this but I am locked in to this "LinkedPix" class, there are over 100 references to it
public class LinkedPix
{
public Point P;
public override string ToString()
{
return P.ToString() + " " + Links.Count;
}
public HashSet<LinkedPix> Links;
public SegObject MemberOfObject { get; set; }
public bool IsMemberOfObject { get => MemberOfObject != null; }
private void Init()
{
MemberOfObject = null;
Links = new HashSet<LinkedPix>();
}
public LinkedPix(int x, int y)
{
Init();
P = new Point(x, y);
}
public LinkedPix(Point p)
{
Init();
P = p;
}
public LinkedPix RotateCopy(double theta_radians)
{
//Assumes that 0,0 is the origin
LinkedPix LP2 = new LinkedPix(new Point(
(int)(P.X * Math.Cos(theta_radians) - P.Y * Math.Sin(theta_radians)),
(int)(P.Y * Math.Cos(theta_radians) + P.X * Math.Sin(theta_radians))));
return LP2;
}
public void LinksRemove(LinkedPix ToRemove, bool TwoWay = false)
{
//Remove from each others
if (TwoWay) ToRemove.Links.Remove(this);
Links.Remove(ToRemove);
}
/// <summary>
/// Removes this from each of its links and removes these links
/// </summary>
internal void LinksCrossRemove()
{
foreach (LinkedPix tPix in Links) tPix.LinksRemove(this, false);
Links = new HashSet<LinkedPix>();
}
}
Here are the three functions I used to populate the information about how LinkedPix are related.
Dictionary<Point, LinkedPix> PointDict = new Dictionary<Point, LinkedPix>();
void EstablishLinks(int xi, int yi, LinkedPix Pixi)
{
if (imWrap.Arr[xi, yi] > threshold)
{
Point pi = new Point(xi, yi);
LinkedPix Pix2 = GetOrAdd(pi);
Pixi.Links.Add(Pix2);
Pix2.Links.Add(Pixi);
}
}
LinkedPix Pix; Point p;
int height = imWrap.Height;
for (int y = 0; y < height - 1; y++)
{
if (Slice && y % Slice_ModulusFactor < 1) continue; //This is for Axon Degeneration to break up the axons
for (int x = 0; x < height - 1; x++)
{
if (Slice && x % Slice_ModulusFactor < 1) continue;
if (imWrap.Arr[x, y] > threshold)
{
Pixels++;
if (CreateLinks)
{
p = new Point(x, y);
Pix = GetOrAdd(p);
if (y + 1 < height) EstablishLinks(x, y + 1, Pix);
if (x + 1 < height) EstablishLinks(x + 1, y, Pix);
}
}
}
}
}
public LinkedPix GetOrAdd(Point p)
{
if (PointDict.ContainsKey(p))
{
return PointDict[p];
}
else
{
LinkedPix LP = new LinkedPix(p);
PointDict.Add(p, LP);
return LP;
}
}
}
imWrap.Arr[x, y] is an array that has already been pre-populated with the contents of a bitmap.
things I have tried
Switching to ConcurrentDictionaries and running in Parallel. The inferior Hashing Algorithm of ConcurrentDictionary compared to HashSet means running this in Parallel actually takes more time, at least when I tried it.
switching to a different collection with faster adds such as a Que. This gains me some speed but because of the way LinkedPix is used, I ultimately have to convert to a HashSet anyway which costs time in the end so this is not ultimately a win.
Various types of Asynchronicity, I either gain very little or nothing at all.
I'm definitely stuck and could use some outside of the box thinking. I definitely need to speed this up and any help would be greatly appreciated.
These are some timings
GetOrAdd: 4.190254371881144 ticks
LinksAdd: 1.7282694093881512 ticks
LinksAddTwo: 1.6570632131524925 ticks
PointCreateTime: 0.20380008184398646 ticks
ArrayCheck: 0.3061780882729328 ticks
I'm working on a connect 4 game and I've got the following part done:
I've made the board which is able to display tiles of the board in 3 colors (white, red and yellow). I've also written some code to display a box around the column on which the user is hovering such that he can see where he is placing a dish.
I've also created the code which allows a dish to be added to the board. Both of these processes use the following code:
//Calculate the size of a single cell
int cellX = this.Size.Width / this.Columns;
//calculate the cell which was clicked
int nColumn = (int)Math.Floor((Double)x / cellX);
x is the value of MouseEventArgs.X of the pannel on which this is called. For drawing a boundry box this code works perfectly but for dropping a dish it doesn't. sometimes it drops 1 left of where I want it sometimes one right.
Here is the code for both events:
//draws the bounding box around the column for a given x value.
public void drawBoundingBox(int x)
{
//Calculate the size of a single cell
int cellX = this.Size.Width / this.Columns;
//calculate the cell which was clicked
int nColumn = (int)Math.Floor((Double)x / cellX);
if (nColumn != this.lastColumn)
{
this.Refresh();
Graphics g = CreateGraphics();
Pen pRed = new Pen(Color.Red, 3);
if (nColumn < this.Columns)
{
Rectangle rect = new Rectangle(new Point(nColumn * cellX, 0),
new Size(cellX, this.Size.Height));
g.DrawRectangle(pRed, rect);
}
}
this.lastColumn = nColumn;
}
public Boolean move(int mousePosition) {
int cellX = this.Size.Width / this.Columns;
int nColumn = (int)Math.Floor((Double)mousePosition / cellX);
return board.Move(nColumn);
}
Here is the code for board.move():
public bool Move(int x)
{
bool found = false;
for (int i = Rows - 1; i >= 0 && !found; i--)
{
Console.WriteLine("X equals: " + x + " i equals: " + i);
if (States[i,x] == 0) {
found = true;
States[i, x] = (byte)(((moves++) % 2) + 1);
}
}
return found;
}
Here is a gif showing what I mean:
To me it seams like a rounding error but it's wierd to me that the bounding box works well with the mouse but the clicking action doesn't...
I receive images of the same size but with different amounts of information. Examples below (red borders are mine). The background is always white.
I am trying to detect where the information on the image ends - at what pixel height (and crop accordingly). In other words, find the first non-white pixel from the bottom.
Is there a better way to do this other than extract BitmapData out of Image object and loop through all the pixels?
Just to add a suggestion having looked over your images and your solution (below) and your method is fine but you may be able to improve efficiency.
The more you know about your image the better; you're confident the background is always white (according to your post, the code is a more generic utility but the following suggestion can still work); can you be confident on the furthest point in a non-white pixel will be found if the row is not empty?
For example; in your two pictures the furthest in non-white pixel on a row is about 60px in. If this is universally true for your data then you don't need to scan the whole line of the image, which would make your for loop:
for (int y = bitmap.Height - 1; y >= 0; y--) {
for (int x = 0; x < 60; x++) {
Color color = bitmap.GetPixel(x, y);
if (color.R != backColor.R || color.G != backColor.G || color.B != backColor.B) {
foundContentOnRow = y;
break;
}
}
}
(You could make it a parameter on the function so you can easily control it if needed).
Imagine for example that the first non-white row was 80px down. To find it currently you do 640 x 300 = 192,000 checks. If you could confidently say that you would know a row was blank within 100 pixels (an over-estimate based on the data presented) then this would be 100 * 300 = 30,000 checks per image.
If you always knew that the first 10 pixels of the image were always blank you could shave a little bit more off (say 3000 checks).
Musing on a setup where you knew that the first non-white pixel was between 10 and 60 pixels in (range of 50) you could find it at row 80 in 50 x 300 = 15,000 checks which is a good reduction.
Of course the downside about assumptions is that if things change your assumptions may not be valid, but if the data is going to remain fairly constant then it may be worthwhile, especially if you do this for a lot of images.
I've ended up using the following code to trim the image. Hopefully someone finds this useful.
class Program {
static void Main(string[] args) {
Image full = Image.FromFile("foo.png");
Image cropped = full.TrimOnBottom();
}
}
public static class ImageUtilities {
public static Image TrimOnBottom(this Image image, Color? backgroundColor = null, int margin = 30) {
var bitmap = (Bitmap)image;
int foundContentOnRow = -1;
// handle empty optional parameter
var backColor = backgroundColor ?? Color.White;
// scan the image from the bottom up, left to right
for (int y = bitmap.Height - 1; y >= 0; y--) {
for (int x = 0; x < bitmap.Width; x++) {
Color color = bitmap.GetPixel(x, y);
if (color.R != backColor.R || color.G != backColor.G || color.B != backColor.B) {
foundContentOnRow = y;
break;
}
}
// exit loop if content found
if (foundContentOnRow > -1) {
break;
}
}
if (foundContentOnRow > -1) {
int proposedHeight = foundContentOnRow + margin;
// only trim if proposed height smaller than existing image
if (proposedHeight < bitmap.Height) {
return CropImage(image, bitmap.Width, proposedHeight);
}
}
return image;
}
private static Image CropImage(Image image, int width, int height) {
Rectangle cropArea = new Rectangle(0, 0, width, height);
Bitmap bitmap = new Bitmap(image);
return bitmap.Clone(cropArea, bitmap.PixelFormat);
}
}
I am currently writing an OpenGL application using the SharpGL library and I am trying to simply create a 3x3x3 set of cubes arranged in a symmetric grid.
I am currently seeing some strange behaviour exhibited in the following picture:
This has me completely stumped as I can see no reason why the code is missing out the last 3 blocks. The method in charge of creating the cube looks like this:
private void CreateCube2(OpenGL gl, int cubeSize)
{
gl.PushMatrix();
const float spacing = 2.5f;
for (int z = 0; z < cubeSize; z++)
{
for (int y = 0; y < cubeSize; y++)
{
for (int x = 0; x < cubeSize; x++)
{
var cube = new Cube();
ColourCube(cube, cubeSize, x, y, z);
cube.Render(gl, RenderMode.Render);
gl.Translate(spacing, 0, 0);
}
gl.Translate(-spacing * cubeSize, spacing, 0);
}
gl.Translate(0, -spacing * cubeSize, spacing);
}
gl.PopMatrix();
}
where the definition of ColourCube is as follows:
private bool m_blackCubeMiddle = true;
private void ColourCube(Cube cube, int size, int x, int y, int z)
{
cube.Faces[0].Material = (!m_blackCubeMiddle || y == 0) ? WhiteMaterial : BlackMaterial; // Bottom
cube.Faces[1].Material = (!m_blackCubeMiddle || y == size - 1) ? YellowMaterial : BlackMaterial; // Top
cube.Faces[2].Material = (!m_blackCubeMiddle || x == size - 1) ? GreenMaterial : BlackMaterial; // Right
cube.Faces[3].Material = (!m_blackCubeMiddle || x == 0) ? BlueMaterial : BlackMaterial; // Left
cube.Faces[4].Material = (!m_blackCubeMiddle || z == 0) ? OrangeMaterial : BlackMaterial; // Front
cube.Faces[5].Material = (!m_blackCubeMiddle || z == size - 1) ? RedMaterial : BlackMaterial; // Back
}
The entire project can be downloaded from here.
The strange behaviour is caused by a bug in SharpGL 'Polygon.Render' method. It is not caused by your own code. When you call Triangulate on the cube, it already shows 27 cubes on screen in the correct positions. But even then, SharpGL incorrectly renders one of the cubes.
Having looked at the source code for ShapGL's Cube & Polygon classes I'ld suggest to write your own cube class. The implementation of the Cube class, that you are using now, is absolutely horible from several standpoints (GL_POLYGON (deprecated), immediate mode vertex submission (deprecated))
Given the image below, what algorithm might I use to detect whether regions one and two (identified by color) have a border?
http://img823.imageshack.us/img823/4477/borders.png
If there's a C# example out there, that would be awesome, but I'm really just looking for any example code.
Edit: Using Jaro's advice, I came up with the following...
public class Shape
{
private const int MAX_BORDER_DISTANCE = 15;
public List<Point> Pixels { get; set; }
public Shape()
{
Pixels = new List<Point>();
}
public bool SharesBorder(Shape other)
{
var shape1 = this;
var shape2 = other;
foreach (var pixel1 in shape1.Pixels)
{
foreach (var pixel2 in shape2.Pixels)
{
var xDistance = Math.Abs(pixel1.X - pixel2.X);
var yDistance = Math.Abs(pixel1.Y - pixel2.Y);
if (xDistance > 1 && yDistance > 1)
{
if (xDistance * yDistance < MAX_BORDER_DISTANCE)
return true;
}
else
{
if (xDistance < Math.Sqrt(MAX_BORDER_DISTANCE) &&
yDistance < Math.Sqrt(MAX_BORDER_DISTANCE))
return true;
}
}
}
return false;
}
// ...
}
Clicking on two shapes that do share a border returns fairly quickly, but very distance shapes or shapes with a large number of pixels take 3+ seconds at times. What options do I have for optimizing this?
2 regions having border means that within a certain small area there should be 3 colors present: red, black and green.
So a very ineffective solution presents itself:
using Color pixelColor = myBitmap.GetPixel(x, y); you could scan an area for those 3 colors. The area must be larger than the width of the border of course.
There is of course plenty room for optimizations (like going in 50 pixels steps and decreasing the precision continually).
Since black is the least used color, you would search around black areas first.
This should explain what I have written in various comments in this topic:
namespace Phobos.Graphics
{
public class BorderDetector
{
private Color region1Color = Color.FromArgb(222, 22, 46);
private Color region2Color = Color.FromArgb(11, 189, 63);
private Color borderColor = Color.FromArgb(11, 189, 63);
private List<Point> region1Points = new List<Point>();
private List<Point> region2Points = new List<Point>();
private List<Point> borderPoints = new List<Point>();
private Bitmap b;
private const int precision = 10;
private const int distanceTreshold = 25;
public long Miliseconds1 { get; set; }
public long Miliseconds2 { get; set; }
public BorderDetector(Bitmap b)
{
if (b == null) throw new ArgumentNullException("b");
this.b = b;
}
private void ScanBitmap()
{
Color c;
for (int x = precision; x < this.b.Width; x += BorderDetector.precision)
{
for (int y = precision; y < this.b.Height; y += BorderDetector.precision)
{
c = this.b.GetPixel(x, y);
if (c == region1Color) region1Points.Add(new Point(x, y));
else if (c == region2Color) region2Points.Add(new Point(x, y));
else if (c == borderColor) borderPoints.Add(new Point(x, y));
}
}
}
/// <summary>Returns a distance of two points (inaccurate but very fast).</summary>
private int GetDistance(Point p1, Point p2)
{
return Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y);
}
/// <summary>Finds the closests 2 points among the points in the 2 sets.</summary>
private int FindClosestPoints(List<Point> r1Points, List<Point> r2Points, out Point foundR1, out Point foundR2)
{
int minDistance = Int32.MaxValue;
int distance = 0;
foundR1 = Point.Empty;
foundR2 = Point.Empty;
foreach (Point r1 in r1Points)
foreach (Point r2 in r2Points)
{
distance = this.GetDistance(r1, r2);
if (distance < minDistance)
{
foundR1 = r1;
foundR2 = r2;
minDistance = distance;
}
}
return minDistance;
}
public bool FindBorder()
{
Point r1;
Point r2;
Stopwatch watch = new Stopwatch();
watch.Start();
this.ScanBitmap();
watch.Stop();
this.Miliseconds1 = watch.ElapsedMilliseconds;
watch.Start();
int distance = this.FindClosestPoints(this.region1Points, this.region2Points, out r1, out r2);
watch.Stop();
this.Miliseconds2 = watch.ElapsedMilliseconds;
this.b.SetPixel(r1.X, r1.Y, Color.Green);
this.b.SetPixel(r2.X, r2.Y, Color.Red);
return (distance <= BorderDetector.distanceTreshold);
}
}
}
It is very simple. Searching this way only takes about 2 + 4 ms (scanning and finding the closest points).
You could also do the search recursively: first with precision = 1000, then precision = 100 and finally precision = 10 for large images.
FindClosestPoints will practically give you an estimated rectangual area where the border should be situated (usually borders are like that).
Then you could use the vector approach I have described in other comments.
I read your question as asking whether the two points exist in different regions. Is this correct? If so, I would probably use a variation of Flood Fill. It's not super difficult to implement (don't implement it recursively, you will almost certainly run out of stack space) and it will be able to look at complex situations like a U-shaped region that has a border between two points, but are not actually different regions. Basically run flood fill, and return true when your coordinate matches the target coordinate (or perhaps when it's close enough for your satisfaction, depending on your use case)
[Edit] Here is an example of flood fill that I wrote for a project of mine. The project is CPAL-licensed, but the implementation is pretty specific to what I use it for anyway, so don't worry about copying parts of it. And it doesn't use recursion, so it should be able to scale to pixel data.
[Edit2] I misunderstood the task. I don't have any example code that does exactly what you're looking for, but I can say that comparing pixel-per-pixel the entire two regions is not something you want to do. You can reduce the complexity by partitioning each region into a larger grid (maybe 25x25 pixels), and comparing those sectors first, and if any of those are close enough, do a pixel-per-pixel comparison just within those two sectors.
[Edit2.5] [Quadtree]3 might be able to help you too. I don't have a lot of experience with it, but I know it's popular in 2D collision detection, which is similar to what you're doing here. Might be worth researching.