static void GroupFlood(GroupFloodData gfd, Location pixel, int depth) { // add current pixel to the group gfd.groupHash.Add(pixel.key(), pixel); //if (gfd.groupHash.Count % 1000 == 0) // MyDebug("groupSize now is " + gfd.groupHash.Count.ToString() + " " + pixel.x.ToString() + "," + pixel.y.ToString()); //if (depth % 100 == 0) //MyDebug("Recursive Depth now is " + depth.ToString()); //Debug.WriteLine("added to group: " + pixel.x.ToString() + "," + pixel.y.ToString()); // recursive call all neighbors of current pixel that are part of the group foreach (var n in gfd.neighbors) { int x = pixel.x + n.x; int y = pixel.y + n.y; Location loc = new Location() { x = x, y = y }; //MyDebug("trying neighbor: " + x.ToString() + "," + y.ToString()); // first check the potential neighbor is in the bitmap if (x >= 0 && x <= gfd.bmpWidth - 1 && y >= 0 && y <= gfd.bmpHeight - 1) { //MyDebug("neighbor in bitmap: " + x.ToString() + "," + y.ToString()); // now check it is a new group memeber byte r = gfd.rgbValues[(y * gfd.stride) + (x * 3)]; byte g = gfd.rgbValues[(y * gfd.stride) + (x * 3) + 1]; byte b = gfd.rgbValues[(y * gfd.stride) + (x * 3) + 2]; byte e = gfd.elp[y * gfd.bmpWidth + x]; //MyDebug("rgbe: " + r.ToString() + "," + g.ToString() + "," + b.ToString() + "," + e.ToString()); // check the pixel color is OK, that pixel is not part of an alread detected // elephant (group thatr is not an ant) and that this pixel is not already part // of the current group if (r < gfd.blackThreshHold && g < gfd.blackThreshHold && b < gfd.blackThreshHold && e != ELP_MEMBER && !gfd.groupHash.ContainsKey(loc.key())) { // we only collect up to ant group size or a bit more (5) pixels if (depth < antThreshold + 5) { GroupFlood(gfd, loc, depth + 1); // recursive call } } } } }
private static void Ants(string[] args) { // read input file // L1: loop over all pixels left -> right , up -> bottom // search for pixel to start groupFlood ,needs to be "black" (have func isBlack()) // create new groupHash, P1: call groupFlood with this pixel // groupFlood: add current pixel to groupHash // get neighbors , (8 max) foreach neighbor pixel - check if it is "black" and not // already in groupHash and not an elephant member (see below) , // if yes call groupFlood with this pixel to add it and // continue flooding // when P1 returns in the L1 loop - check the size of groupHash, and decide if // group is an Ant (too small). // If yes: mark all ant memebers as white in the bitmap array // If no - this is an elephant. Mark each pixel in elephant as such - elephant member // use additional byte for each pixel for this (so not to destroy its exact color // as we are keeping these intact) // open debug file //sw = File.CreateText("ants_debug.txt"); string fname = args[1]; Bitmap bmp = new Bitmap(fname); antThreshold = 100; if (args.Length > 2) { antThreshold = Convert.ToByte(args[2]); } byte blackThreshHold = 160; if (args.Length > 3) { blackThreshHold = Convert.ToByte(args[3]); } // Lock the bitmap's bits. Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); // Get the address of the first line. IntPtr ptr = bmpData.Scan0; // Declare an array to hold the bytes of the bitmap. int bytes = bmpData.Stride * bmp.Height; byte[] rgbValues = new byte[bytes]; // Copy the RGB values into the array. Marshal.Copy(ptr, rgbValues, 0, bytes); // create array for elephant pixel marking, 1 byte per pixel int ElpBytes = bmpData.Width * bmp.Height; byte[] elp = new byte[ElpBytes]; List <Location> n = new List <Location>(); n.Add(new Location() { x = -1, y = -1 }); n.Add(new Location() { x = -1, y = 0 }); n.Add(new Location() { x = -1, y = 1 }); n.Add(new Location() { x = 0, y = -1 }); n.Add(new Location() { x = 0, y = 1 }); n.Add(new Location() { x = 1, y = -1 }); n.Add(new Location() { x = 1, y = 0 }); n.Add(new Location() { x = 1, y = 1 }); // alot of not changing data needed for the GroupFlood recursive function GroupFloodData gfd = new GroupFloodData(); gfd.rgbValues = rgbValues; gfd.elp = elp; gfd.bmpWidth = bmp.Width; gfd.bmpHeight = bmp.Height; gfd.neighbors = n; gfd.stride = bmpData.Stride; gfd.blackThreshHold = blackThreshHold; // loop on all pixels int stride = bmpData.Stride; for (int x = 0; x < bmpData.Width; x++) { for (int y = 0; y < bmpData.Height; y++) { byte r = rgbValues[(y * stride) + (x * 3)]; byte g = rgbValues[(y * stride) + (x * 3) + 1]; byte b = rgbValues[(y * stride) + (x * 3) + 2]; byte e = elp[y * bmpData.Width + x]; // find a pixel to start flooding a group from // check colors bellow threshHold and // not neighbor of elp (if neighbor of elp - make him an elp as well) bool isBlack = (r < blackThreshHold && g < blackThreshHold && b < blackThreshHold); if (isBlack) { // following block checks if the pixel is an elp group member due to neighboehood with other elp member bool isElpMember = false; foreach (var nn in gfd.neighbors) { int xn = x + nn.x; int yn = y + nn.y; if (xn >= 0 && xn <= bmp.Width - 1 && yn >= 0 && yn <= bmp.Height - 1) { // I think it is OK not test on himself isElpMember = elp[yn * bmpData.Width + xn] == ELP_MEMBER; if (isElpMember) { elp[y * bmpData.Width + x] = ELP_MEMBER; // make him an elp member if he has an elp neighbor break; } } } if (!isElpMember) { if (x % 100 == 0 && y % 100 == 0) { MyDebug("First Pixel of Group: " + x.ToString() + "," + y.ToString()); } // new dictionary list for each group under test Dictionary <string, Location> groupHash = new Dictionary <string, Location>(); gfd.groupHash = groupHash; GroupFlood(gfd, new Location() { x = x, y = y }, depth: 0); int groupSize = groupHash.Count; bool isAnt = groupSize < antThreshold; // MyDebug("new group with groupSize: " + groupSize.ToString() + (isAnt ? " ANT!!!!" : "")); if (isAnt) // we have an ant { foreach (var item in groupHash) { // set all ant pixels to white Location p = item.Value; rgbValues[(p.y * stride) + (p.x * 3)] = 255; rgbValues[(p.y * stride) + (p.x * 3) + 1] = 255; rgbValues[(p.y * stride) + (p.x * 3) + 2] = 255; } } else { foreach (var item in groupHash) { // mark all pixels as elephant members, leave them as is in original image Location p = item.Value; elp[p.y * bmpData.Width + p.x] = ELP_MEMBER; } } } } } } // create bitmap back from new values // Commit the changes, and unlock the 50x30 portion of the bitmap. Marshal.Copy(rgbValues, 0, ptr, bytes); bmp.UnlockBits(bmpData); string[] s = fname.Split('.'); string newName = string.Concat(s[0], "_size_", antThreshold.ToString(), "_blackLevel_", blackThreshHold.ToString(), ".", s[1]); bmp.Save(newName, ImageFormat.Jpeg); MyDebug("done!!"); }