/// <summary> /// Looks for the brightest pixel after applying a redness filter. Narrows search first using a resampled copy of the image to eliminate edge dots. /// Expects an image that is already cropped to the interested area for faster processing. /// </summary> /// <param name="img"></param> /// <param name="mouse"></param> /// <param name="maxDistanceFromMouse"></param> /// <returns></returns> public unsafe Point FindMaxPixel(UnmanagedImage img, PointF mouse, float maxDistanceFromMouse) { int width = 15; int height = (int)Math.Ceiling((double)img.Height / (double)img.Width * width); if (width <= img.Width && height <= img.Height + 1) { width = img.Width; height = img.Height; } double scale = (double)img.Width / (double)width; UnmanagedImage lowRed = null; try { if (width != img.Width && height != img.Height) { using (Bitmap reduced = new Bitmap(width, height, PixelFormat.Format24bppRgb)) using (Graphics g = Graphics.FromImage(reduced)) using (ImageAttributes ia = new ImageAttributes()) { g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; ia.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY); g.DrawImage(img.ToManagedImage(false), new Rectangle(0, 0, width, height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ia); //TODO: Not sure if ToManagedImage will stick around after the underying image is disposed. I know that the bitmap data will be gone, guess that's most of it. using (UnmanagedImage rui = UnmanagedImage.FromManagedImage(reduced)) { lowRed = new RedEyeFilter(2).Apply(rui); // Make an copy using the red eye filter } } } else { //Don't resample unless needed lowRed = new RedEyeFilter(2).Apply(img); } Point max = GetMax(lowRed, new PointF(mouse.X / (float)scale, mouse.Y / (float)scale), maxDistanceFromMouse / scale); //We weren't scaling things? OK, cool... if (scale == 0) return max; //Otherwise, let's get the unscaled pixel. //Calculate the rectangle surrounding the selected pixel, but in source coordinates. int tinySize = (int)Math.Ceiling(scale) + 1; Rectangle tinyArea = new Rectangle((int)Math.Floor(scale * (double)max.X), (int)Math.Floor(scale * (double)max.Y), tinySize, tinySize); if (tinyArea.Right >= img.Width) tinyArea.Width -= img.Width - tinyArea.Right + 1; if (tinyArea.Bottom >= img.Height) tinyArea.Height -= img.Height - tinyArea.Bottom + 1; //Filter it and look using (UnmanagedImage tiny = new Crop(tinyArea).Apply(img)) { using (UnmanagedImage tinyRed = new RedEyeFilter(2).Apply(tiny)) { max = GetMax(tinyRed); max.X += tinyArea.X; max.Y += tinyArea.Y; } } return max; } finally { if (lowRed != null) lowRed.Dispose(); } }
/// <summary> /// /// </summary> /// <param name="image"></param> /// <param name="start"></param> /// <param name="maxEyeRadius">Should be 2-3 percent of max(width/height)</param> /// <param name="maxPointSearchDistance">In source pixels, the max distance from 'start' from which to look for the starting point. Good default: roughly 24 display pixels.</param> public static void MarkEye(UnmanagedImage image, Point start, int maxEyeRadius, float maxPointSearchDistance = 0) { int maxRadius = maxEyeRadius * 2 + (int)Math.Ceiling(maxPointSearchDistance); //Find subset Rectangle subset = new Rectangle(start.X - maxRadius, start.Y - maxRadius, maxRadius * 2, maxRadius * 2); if (subset.X < 0) { subset.Width += subset.X; subset.X = 0; } if (subset.Y < 0) { subset.Height += subset.Y; subset.Y = 0; } if (subset.Right >= image.Width) { subset.Width -= (subset.Right - image.Width + 1); } if (subset.Bottom >= image.Height) { subset.Height -= (subset.Bottom - image.Height + 1); } start.X -= subset.X; start.Y -= subset.Y; //Skip processing if we're slightly out of bounds if (subset.X < 0 || subset.Y < 0 || subset.Width < 0 || subset.Height < 0 || subset.Right >= image.Width || subset.Bottom >= image.Height) { return; } UnmanagedImage red = null; try{ Point startAt = start; using (UnmanagedImage c = new Crop(subset).Apply(image)) { red = new RedEyeFilter(2).Apply(c); if (maxPointSearchDistance > 0) { startAt = new ManualSearcher().FindMaxPixel(c, start, maxPointSearchDistance); } } var fill = new AdaptiveCircleFill(red, startAt, start, maxEyeRadius * 2); fill.FirstPass(); fill.SecondPass(); fill.CorrectRedEye(image, subset.X, subset.Y); //fill.MarkFilledPixels(image, subset.X, subset.Y, new byte[] { 0, 255, 0, 0 }); //fill.SetPixel(image, startAt.X + subset.X, startAt.Y + subset.Y, new byte[] { 255, 255, 0, 0 }); }finally{ if (red != null) { red.Dispose(); } } }
/// <summary> /// Looks for the brightest pixel after applying a redness filter. Narrows search first using a resampled copy of the image to eliminate edge dots. /// Expects an image that is already cropped to the interested area for faster processing. /// </summary> /// <param name="img"></param> /// <param name="mouse"></param> /// <param name="maxDistanceFromMouse"></param> /// <returns></returns> public unsafe Point FindMaxPixel(UnmanagedImage img, PointF mouse, float maxDistanceFromMouse) { int width = 15; int height = (int)Math.Ceiling((double)img.Height / (double)img.Width * width); if (width <= img.Width && height <= img.Height + 1) { width = img.Width; height = img.Height; } double scale = (double)img.Width / (double)width; UnmanagedImage lowRed = null; try { if (width != img.Width && height != img.Height) { using (Bitmap reduced = new Bitmap(width, height, PixelFormat.Format24bppRgb)) using (Graphics g = Graphics.FromImage(reduced)) using (ImageAttributes ia = new ImageAttributes()) { g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; ia.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY); g.DrawImage(img.ToManagedImage(false), new Rectangle(0, 0, width, height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ia); //TODO: Not sure if ToManagedImage will stick around after the underlying image is disposed. I know that the bitmap data will be gone, guess that's most of it. using (UnmanagedImage rui = UnmanagedImage.FromManagedImage(reduced)) { lowRed = new RedEyeFilter(2).Apply(rui); // Make an copy using the red eye filter } } } else { //Don't resample unless needed lowRed = new RedEyeFilter(2).Apply(img); } Point max = GetMax(lowRed, new PointF(mouse.X / (float)scale, mouse.Y / (float)scale), maxDistanceFromMouse / scale); //We weren't scaling things? OK, cool... if (scale == 0) { return(max); } //Otherwise, let's get the unscaled pixel. //Calculate the rectangle surrounding the selected pixel, but in source coordinates. int tinySize = (int)Math.Ceiling(scale) + 1; Rectangle tinyArea = new Rectangle((int)Math.Floor(scale * (double)max.X), (int)Math.Floor(scale * (double)max.Y), tinySize, tinySize); if (tinyArea.Right >= img.Width) { tinyArea.Width -= img.Width - tinyArea.Right + 1; } if (tinyArea.Bottom >= img.Height) { tinyArea.Height -= img.Height - tinyArea.Bottom + 1; } //Filter it and look using (UnmanagedImage tiny = new Crop(tinyArea).Apply(img)) { using (UnmanagedImage tinyRed = new RedEyeFilter(2).Apply(tiny)) { max = GetMax(tinyRed); max.X += tinyArea.X; max.Y += tinyArea.Y; } } return(max); } finally { if (lowRed != null) { lowRed.Dispose(); } } }