Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Like both of you desrcipt you just have to find the first non transparent point and afterward move along the none transparent pixels with a transparent neighbor.</p> <p>Additionaly you'll have to save the point you've already visisted and how often you visited them or you'll end in same cases in an invinity loop. If the point doesn't have a neighbor which already was visited you must go back each point, in revered direction, until a unvisited point is available again. </p> <p>That's it.</p> <hr> <p>//CODE REMOVED - POST WAS TO LONG</p> <hr> <p><strong>EDIT 1</strong></p> <p>Modified code:</p> <hr> <p>//CODE REMOVED - POST WAS TO LONG</p> <hr> <p><strong>EDIT 2</strong></p> <p>Now all regions are returned:</p> <hr> <p>//CODE REMOVED - POST WAS TO LONG</p> <hr> <p><strong>EDIT 3</strong></p> <p>Changes:</p> <ul> <li>Point.EMPTY was replaced by Point(-1,-1), or a non transparent pixel in the upper left corner causes an invinityloop </li> <li>Check for borderpoint at the image border</li> </ul> <hr> <pre><code>class BorderFinder { int stride = 0; int[] visited = null; byte[] bytes = null; PointData borderdata = null; Size size = Size.Empty; bool outside = false; Point zeropoint = new Point(-1,-1); public List&lt;Point[]&gt; Find(Bitmap bmp, bool outside = true) { this.outside = outside; List&lt;Point&gt; border = new List&lt;Point&gt;(); BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); stride = bmpdata.Stride; bytes = new byte[bmp.Width * bmp.Height * 4]; size = bmp.Size; Marshal.Copy(bmpdata.Scan0, bytes, 0, bytes.Length); // Get all Borderpoint borderdata = getBorderData(bytes); bmp.UnlockBits(bmpdata); List&lt;List&lt;Point&gt;&gt; regions = new List&lt;List&lt;Point&gt;&gt;(); //Loop until no more borderpoints are available while (borderdata.PointCount &gt; 0) { List&lt;Point&gt; region = new List&lt;Point&gt;(); //if valid is false the region doesn't close bool valid = true; //Find the first borderpoint from where whe start crawling Point startpos = getFirstPoint(borderdata); //we need this to know if and how often we already visted the point. //we somtime have to visit a point a second time because we have to go backward until a unvisted point is found again //for example if we go int a narrow 1px hole visited = new int[bmp.Size.Width * bmp.Size.Height]; region.Add(startpos); //Find the next possible point Point current = getNextPoint(startpos); if (current != zeropoint) { visited[current.Y * bmp.Width + current.X]++; region.Add(current); } //May occure with just one transparent pixel without neighbors if (current == zeropoint) valid = false; //Loop until the area closed or colsing the area wasn't poosible while (!current.Equals(startpos) &amp;&amp; valid) { var pos = current; //Check if the area was aready visited if (visited[current.Y * bmp.Width + current.X] &lt; 2) { current = getNextPoint(pos); visited[pos.Y * bmp.Width + pos.X]++; //If no possible point was found, search in reversed direction if (current == zeropoint) current = getNextPointBackwards(pos); } else { //If point was already visited, search in reversed direction current = getNextPointBackwards(pos); } //No possible point was found. Closing isn't possible if (current == zeropoint) { valid = false; break; } visited[current.Y * bmp.Width + current.X]++; region.Add(current); } //Remove point from source borderdata foreach (var p in region) { borderdata.SetPoint(p.Y * bmp.Width + p.X, false); } //Add region if closing was possible if (valid) regions.Add(region); } //Checks if Region goes the same way back and trims it in this case foreach (var region in regions) { int duplicatedpos = -1; bool[] duplicatecheck = new bool[size.Width * size.Height]; int length = region.Count; for (int i = 0; i &lt; length; i++) { var p = region[i]; if (duplicatecheck[p.Y * size.Width + p.X]) { duplicatedpos = i - 1; break; } duplicatecheck[p.Y * size.Width + p.X] = true; } if (duplicatedpos == -1) continue; if (duplicatedpos != ((region.Count - 1) / 2)) continue; bool reversed = true; for (int i = 0; i &lt; duplicatedpos; i++) { if (region[duplicatedpos - i - 1] != region[duplicatedpos + i + 1]) { reversed = false; break; } } if (!reversed) continue; region.RemoveRange(duplicatedpos + 1, region.Count - duplicatedpos - 1); } List&lt;List&lt;Point&gt;&gt; tempregions = new List&lt;List&lt;Point&gt;&gt;(regions); regions.Clear(); bool connected = true; //Connects region if possible while (connected) { connected = false; foreach (var region in tempregions) { int connectionpos = -1; int connectionregion = -1; Point pointstart = region.First(); Point pointend = region.Last(); for (int ir = 0; ir &lt; regions.Count; ir++) { var otherregion = regions[ir]; if (region == otherregion) continue; for (int ip = 0; ip &lt; otherregion.Count; ip++) { var p = otherregion[ip]; if ((isConnected(pointstart, p) &amp;&amp; isConnected(pointend, p)) || (isConnected(pointstart, p) &amp;&amp; isConnected(pointstart, p))) { connectionregion = ir; connectionpos = ip; } if ((isConnected(pointend, p) &amp;&amp; isConnected(pointend, p))) { region.Reverse(); connectionregion = ir; connectionpos = ip; } } } if (connectionpos == -1) { regions.Add(region); } else { regions[connectionregion].InsertRange(connectionpos, region); } } tempregions = new List&lt;List&lt;Point&gt;&gt;(regions); regions.Clear(); } List&lt;Point[]&gt; returnregions = new List&lt;Point[]&gt;(); foreach (var region in tempregions) returnregions.Add(region.ToArray()); return returnregions; } private bool isConnected(Point p0, Point p1) { if (p0.X == p1.X &amp;&amp; p0.Y - 1 == p1.Y) return true; if (p0.X + 1 == p1.X &amp;&amp; p0.Y - 1 == p1.Y) return true; if (p0.X + 1 == p1.X &amp;&amp; p0.Y == p1.Y) return true; if (p0.X + 1 == p1.X &amp;&amp; p0.Y + 1 == p1.Y) return true; if (p0.X == p1.X &amp;&amp; p0.Y + 1 == p1.Y) return true; if (p0.X - 1 == p1.X &amp;&amp; p0.Y + 1 == p1.Y) return true; if (p0.X - 1 == p1.X &amp;&amp; p0.Y == p1.Y) return true; if (p0.X - 1 == p1.X &amp;&amp; p0.Y - 1 == p1.Y) return true; return false; } private Point getNextPoint(Point pos) { if (pos.Y &gt; 0) { int x = pos.X; int y = pos.Y - 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.Y &gt; 0 &amp;&amp; pos.X &lt; size.Width - 1) { int x = pos.X + 1; int y = pos.Y - 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.X &lt; size.Width - 1) { int x = pos.X + 1; int y = pos.Y; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.X &lt; size.Width - 1 &amp;&amp; pos.Y &lt; size.Height - 1) { int x = pos.X + 1; int y = pos.Y + 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.Y &lt; size.Height - 1) { int x = pos.X; int y = pos.Y + 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.Y &lt; size.Height - 1 &amp;&amp; pos.X &gt; 0) { int x = pos.X - 1; int y = pos.Y + 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.X &gt; 0) { int x = pos.X - 1; int y = pos.Y; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } if (pos.X &gt; 0 &amp;&amp; pos.Y &gt; 0) { int x = pos.X - 1; int y = pos.Y - 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } } } return zeropoint; } private Point getNextPointBackwards(Point pos) { Point backpoint = zeropoint; int trys = 0; if (pos.X &gt; 0 &amp;&amp; pos.Y &gt; 0) { int x = pos.X - 1; int y = pos.Y - 1; if (ValidPoint(x, y) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.X &gt; 0) { int x = pos.X - 1; int y = pos.Y; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.Y &lt; size.Height - 1 &amp;&amp; pos.X &gt; 0) { int x = pos.X - 1; int y = pos.Y + 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.Y &lt; size.Height - 1) { int x = pos.X; int y = pos.Y + 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.X &lt; size.Width - 1 &amp;&amp; pos.Y &lt; size.Height - 1) { int x = pos.X + 1; int y = pos.Y + 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.X &lt; size.Width - 1) { int x = pos.X + 1; int y = pos.Y; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.Y &gt; 0 &amp;&amp; pos.X &lt; size.Width - 1) { int x = pos.X + 1; int y = pos.Y - 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } if (pos.Y &gt; 0) { int x = pos.X; int y = pos.Y - 1; if ((ValidPoint(x, y)) &amp;&amp; HasNeighbor(x, y)) { if (visited[y * size.Width + x] == 0) { return new Point(x, y); } if (backpoint == zeropoint || trys &gt; visited[y * size.Width + x]) { backpoint = new Point(x, y); trys = visited[y * size.Width + x]; } } } return backpoint; } private bool ValidPoint(int x, int y) { return (borderdata[y * size.Width + x]); } private bool HasNeighbor(int x, int y) { if (y &gt; 0) { if (!borderdata[(y - 1) * size.Width + x]) { return true; } } else if (ValidPoint(x, y)) { return true; } if (x &lt; size.Width - 1) { if (!borderdata[y * size.Width + (x + 1)]) { return true; } } else if (ValidPoint(x, y)) { return true; } if (y &lt; size.Height - 1) { if (!borderdata[(y + 1) * size.Width + x]) { return true; } } else if (ValidPoint(x, y)) { return true; } if (x &gt; 0) { if (!borderdata[y * size.Width + (x - 1)]) { return true; } } else if (ValidPoint(x, y)) { return true; } return false; } private Point getFirstPoint(PointData data) { Point startpos = zeropoint; for (int y = 0; y &lt; size.Height; y++) { for (int x = 0; x &lt; size.Width; x++) { if (data[y * size.Width + x]) { startpos = new Point(x, y); return startpos; } } } return startpos; } private PointData getBorderData(byte[] bytes) { PointData isborderpoint = new PointData(size.Height * size.Width); bool prevtrans = false; bool currenttrans = false; for (int y = 0; y &lt; size.Height; y++) { prevtrans = false; for (int x = 0; x &lt;= size.Width; x++) { if (x == size.Width) { if (!prevtrans) { isborderpoint.SetPoint(y * size.Width + x - 1, true); } continue; } currenttrans = bytes[y * stride + x * 4 + 3] == 0; if (x == 0 &amp;&amp; !currenttrans) isborderpoint.SetPoint(y * size.Width + x, true); if (prevtrans &amp;&amp; !currenttrans) isborderpoint.SetPoint(y * size.Width + x - 1, true); if (!prevtrans &amp;&amp; currenttrans &amp;&amp; x != 0) isborderpoint.SetPoint(y * size.Width + x, true); prevtrans = currenttrans; } } for (int x = 0; x &lt; size.Width; x++) { prevtrans = false; for (int y = 0; y &lt;= size.Height; y++) { if (y == size.Height) { if (!prevtrans) { isborderpoint.SetPoint((y - 1) * size.Width + x, true); } continue; } currenttrans = bytes[y * stride + x * 4 + 3] == 0; if(y == 0 &amp;&amp; !currenttrans) isborderpoint.SetPoint(y * size.Width + x, true); if (prevtrans &amp;&amp; !currenttrans) isborderpoint.SetPoint((y - 1) * size.Width + x, true); if (!prevtrans &amp;&amp; currenttrans &amp;&amp; y != 0) isborderpoint.SetPoint(y * size.Width + x, true); prevtrans = currenttrans; } } return isborderpoint; } } class PointData { bool[] points = null; int validpoints = 0; public PointData(int length) { points = new bool[length]; } public int PointCount { get { return validpoints; } } public void SetPoint(int pos, bool state) { if (points[pos] != state) { if (state) validpoints++; else validpoints--; } points[pos] = state; } public bool this[int pos] { get { return points[pos]; } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload