Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I've modified <code>GetOutlinePoints</code> by adding a helper variable than verifies the position at which new points should be added. </p> <p>The idea of the outline detection algorithm in your code is something like looking at the image while standing each of its edges and writing down all non-transparent pixels that are visible. It's ok, however, you always added pixels to the end of the collection, which caused issues. I've added a variable that remembers position of the last pixel visible from the previous edge and the current one and uses it to determine index where the new pixel should be added. I suppose it should work as long as the outline is continuous, but I suppose it is in your case.</p> <p>I've tested it on several images and it seems to work correctly:</p> <pre><code>public static Point[] GetOutlinePoints(Bitmap image) { List&lt;Point&gt; outlinePoints = new List&lt;Point&gt;(); BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); byte[] originalBytes = new byte[image.Width * image.Height * 4]; Marshal.Copy(bitmapData.Scan0, originalBytes, 0, originalBytes.Length); //find non-transparent pixels visible from the top of the image for (int x = 0; x &lt; bitmapData.Width; x++) { for (int y = 0; y &lt; bitmapData.Height; y++) { byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3]; if (alpha != 0) { Point p = new Point(x, y); if (!ContainsPoint(outlinePoints, p)) outlinePoints.Add(p); break; } } } //helper variable for storing position of the last pixel visible from both sides //or last inserted pixel int? lastInsertedPosition = null; //find non-transparent pixels visible from the right side of the image for (int y = 0; y &lt; bitmapData.Height; y++) { for (int x = bitmapData.Width - 1; x &gt;= 0; x--) { byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3]; if (alpha != 0) { Point p = new Point(x, y); if (!ContainsPoint(outlinePoints, p)) { if (lastInsertedPosition.HasValue) { outlinePoints.Insert(lastInsertedPosition.Value + 1, p); lastInsertedPosition += 1; } else { outlinePoints.Add(p); } } else { //save last common pixel from visible from more than one sides lastInsertedPosition = outlinePoints.IndexOf(p); } break; } } } lastInsertedPosition = null; //find non-transparent pixels visible from the bottom of the image for (int x = bitmapData.Width - 1; x &gt;= 0; x--) { for (int y = bitmapData.Height - 1; y &gt;= 0; y--) { byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3]; if (alpha != 0) { Point p = new Point(x, y); if (!ContainsPoint(outlinePoints, p)) { if (lastInsertedPosition.HasValue) { outlinePoints.Insert(lastInsertedPosition.Value + 1, p); lastInsertedPosition += 1; } else { outlinePoints.Add(p); } } else { //save last common pixel from visible from more than one sides lastInsertedPosition = outlinePoints.IndexOf(p); } break; } } } lastInsertedPosition = null; //find non-transparent pixels visible from the left side of the image for (int y = bitmapData.Height - 1; y &gt;= 0; y--) { for (int x = 0; x &lt; bitmapData.Width; x++) { byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3]; if (alpha != 0) { Point p = new Point(x, y); if (!ContainsPoint(outlinePoints, p)) { if (lastInsertedPosition.HasValue) { outlinePoints.Insert(lastInsertedPosition.Value + 1, p); lastInsertedPosition += 1; } else { outlinePoints.Add(p); } } else { //save last common pixel from visible from more than one sides lastInsertedPosition = outlinePoints.IndexOf(p); } break; } } } // Added to close the loop outlinePoints.Add(outlinePoints[0]); image.UnlockBits(bitmapData); return outlinePoints.ToArray(); } </code></pre> <p><strong>Update:</strong> This algorithm won't work correctly for images that have outline parts not "visible" from any of the edges. See comments for suggested solutions. I'll try to post a code snippet later.</p> <p><strong>Update II</strong></p> <p>I've prepared another algorithm as described in my comments. It just crawls around the object and saves the outline. </p> <p>First, it finds the first pixel of the outline using a method from the previous algorithm. Then, it looks throught the neighbouring pixels in clockwise order, find the first one that is transparent and then continues browsing, but looking for a non-transparent one. The first non-transparent pixel found is the next one in the outline. The loop continues until it goes around the whole object and gets back to the starting pixel.</p> <pre><code>public static Point[] GetOutlinePointsNEW(Bitmap image) { List&lt;Point&gt; outlinePoints = new List&lt;Point&gt;(); BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); Point currentP = new Point(0, 0); Point firstP = new Point(0, 0); byte[] originalBytes = new byte[image.Width * image.Height * 4]; Marshal.Copy(bitmapData.Scan0, originalBytes, 0, originalBytes.Length); //find non-transparent pixels visible from the top of the image for (int x = 0; x &lt; bitmapData.Width &amp;&amp; outlinePoints.Count == 0; x++) { for (int y = 0; y &lt; bitmapData.Height &amp;&amp; outlinePoints.Count == 0; y++) { byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3]; if (alpha != 0) { Point p = new Point(x, y); outlinePoints.Add(p); currentP = p; firstP = p; break; } } } Point[] neighbourPoints = new Point[] { new Point(-1, -1), new Point(0, -1), new Point(1, -1), new Point(1, 0), new Point(1, 1), new Point(0, 1), new Point(-1, 1), new Point(-1, 0) }; //crawl around the object and look for the next pixel of the outline do { bool transparentNeighbourFound = false; bool nextPixelFound = false; int i; //searching is done in clockwise order for (i = 0; (i &lt; neighbourPoints.Length * 2) &amp;&amp; !nextPixelFound; ++i) { int neighbourPosition = i % neighbourPoints.Length; int x = currentP.X + neighbourPoints[neighbourPosition].X; int y = currentP.Y + neighbourPoints[neighbourPosition].Y; byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3]; //a transparent pixel has to be found first if (!transparentNeighbourFound) { if (alpha == 0) { transparentNeighbourFound = true; } } else //after a transparent pixel is found, a next non-transparent one is the next pixel of the outline { if (alpha != 0) { Point p = new Point(x, y); currentP = p; outlinePoints.Add(p); nextPixelFound = true; } } } } while (currentP != firstP); image.UnlockBits(bitmapData); return outlinePoints.ToArray(); } </code></pre> <p>One thing to remember is that it does work IF object does NOT ends at the edge of the image (there has to be a transparent space between the object and each of the edges). </p> <p>This can be easily done if you just make the image one line larger at each side before passing it to the <code>GetOutlinePointsNEW</code> method.</p>
    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. This table or related slice is empty.
    1. 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