private PointData getBorderData(byte[] bytes) { PointData isborderpoint = new PointData(size.Height * size.Width); bool prevtrans = false; bool currenttrans = false; for (int y = 0; y < size.Height; y++) { prevtrans = false; for (int x = 0; x <= 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 && !currenttrans) { isborderpoint.SetPoint(y * size.Width + x, true); } if (prevtrans && !currenttrans) { isborderpoint.SetPoint(y * size.Width + x - 1, true); } if (!prevtrans && currenttrans && x != 0) { isborderpoint.SetPoint(y * size.Width + x, true); } prevtrans = currenttrans; } } for (int x = 0; x < size.Width; x++) { prevtrans = false; for (int y = 0; y <= 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 && !currenttrans) { isborderpoint.SetPoint(y * size.Width + x, true); } if (prevtrans && !currenttrans) { isborderpoint.SetPoint((y - 1) * size.Width + x, true); } if (!prevtrans && currenttrans && y != 0) { isborderpoint.SetPoint(y * size.Width + x, true); } prevtrans = currenttrans; } } return(isborderpoint); }
public List <Point[]> Find(Bitmap bmp, bool outside = true) { this.outside = outside; List <Point> border = new List <Point>(); 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 <List <Point> > regions = new List <List <Point> >(); //Loop until no more borderpoints are available while (borderdata.PointCount > 0) { List <Point> region = new List <Point>(); //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) && valid) { var pos = current; //Check if the area was aready visited if (visited[current.Y * bmp.Width + current.X] < 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 < 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 < 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 <List <Point> > tempregions = new List <List <Point> >(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 < regions.Count; ir++) { var otherregion = regions[ir]; if (region == otherregion) { continue; } for (int ip = 0; ip < otherregion.Count; ip++) { var p = otherregion[ip]; if ((isConnected(pointstart, p) && isConnected(pointend, p)) || (isConnected(pointstart, p) && isConnected(pointstart, p))) { connectionregion = ir; connectionpos = ip; } if ((isConnected(pointend, p) && isConnected(pointend, p))) { region.Reverse(); connectionregion = ir; connectionpos = ip; } } } if (connectionpos == -1) { regions.Add(region); } else { regions[connectionregion].InsertRange(connectionpos, region); } } tempregions = new List <List <Point> >(regions); regions.Clear(); } List <Point[]> returnregions = new List <Point[]>(); foreach (var region in tempregions) { returnregions.Add(region.ToArray()); } return(returnregions); }