Adaptive Smoothing - noise removal with edges preserving.

The filter is aimed to perform image smoothing, but keeping sharp edges. This makes it applicable to additive noise removal and smoothing objects' interiors, but not applicable for spikes (salt and pepper noise) removal.

The next calculations are done for each pixel: weights are calculate for 9 pixels - pixel itself and 8 neighbors: w(x, y) = exp( -1 * (Gx^2 + Gy^2) / (2 * factor^2) ) Gx(x, y) = (I(x + 1, y) - I(x - 1, y)) / 2 Gy(x, y) = (I(x, y + 1) - I(x, y - 1)) / 2 , where factor is a configurable value determining smoothing's quality. sum of 9 weights is calclated (weightTotal); sum of 9 weighted pixel values is calculatd (total); destination pixel is calculated as total / weightTotal.

Description of the filter was found in "An Edge Detection Technique Using the Facet Model and Parameterized Relaxation Labeling" by Ioannis Matalas, Student Member, IEEE, Ralph Benjamin, and Richard Kitney.

The filter accepts 8 bpp grayscale images and 24 bpp color images for processing.

Sample usage:

// create filter AdaptiveSmoothing filter = new AdaptiveSmoothing( ); // apply the filter filter.ApplyInPlace( image );

Initial image:

Result image:

Inheritance: BaseUsingCopyPartialFilter
Example #1
0
 public static Bitmap AdaptiveSmoothing(Bitmap bmp)
 {
     // create filter
     AdaptiveSmoothing filter = new AdaptiveSmoothing();
     // apply the filter
     filter.ApplyInPlace(bmp);
     return bmp;
 }
        public Bitmap Detect(Bitmap bitmap)
        {
            Bitmap grayscaleBitmap = Grayscale.CommonAlgorithms.BT709.Apply(bitmap);

            IFilter smoothingFilter = null;
            switch (_smoothMode)
            {
                case "None": smoothingFilter = null; break;
                case "Mean": smoothingFilter = new Mean(); break;
                case "Median": smoothingFilter = new Median(); break;
                case "Conservative": smoothingFilter = new ConservativeSmoothing(); break;
                case "Adaptive": smoothingFilter = new AdaptiveSmoothing(); break;
                case "Bilateral": smoothingFilter = new BilateralSmoothing(); break;
            }
            Bitmap smoothBitmap = smoothingFilter != null ? smoothingFilter.Apply(grayscaleBitmap) : grayscaleBitmap;

            IFilter edgeFilter = null;
            switch (_edgeMode)
            {
                case "Homogenity": edgeFilter = new HomogenityEdgeDetector(); break;
                case "Difference": edgeFilter = new DifferenceEdgeDetector(); break;
                case "Sobel": edgeFilter = new SobelEdgeDetector(); break;
                case "Canny": edgeFilter = new CannyEdgeDetector(); break;
            }
            Bitmap edgeBitmap = edgeFilter != null ? edgeFilter.Apply(smoothBitmap) : smoothBitmap;

            IFilter threshholdFilter = new Threshold(_threshold);
            Bitmap thresholdBitmap = _threshold == 0 ? edgeBitmap : threshholdFilter.Apply(edgeBitmap);

            BlobCounter blobCounter = new BlobCounter();
            blobCounter.FilterBlobs = true;
            blobCounter.MinHeight = _minHeight;
            blobCounter.MinWidth = _minWidth;
            blobCounter.ProcessImage(thresholdBitmap);
            Blob[] blobs = blobCounter.GetObjectsInformation();

            Bitmap outputBitmap = new Bitmap(thresholdBitmap.Width, thresholdBitmap.Height, PixelFormat.Format24bppRgb);
            Graphics bitmapGraphics = Graphics.FromImage(outputBitmap);
            Bitmap inputBitmap = null;
            switch (_drawMode)
            {
                case "Original": inputBitmap = bitmap; break;
                case "Grayscale": inputBitmap = grayscaleBitmap; break;
                case "Smooth": inputBitmap = smoothBitmap; break;
                case "Edge": inputBitmap = edgeBitmap; break;
                case "Threshold": inputBitmap = thresholdBitmap; break;
            }
            if (inputBitmap != null)
                bitmapGraphics.DrawImage(inputBitmap, 0, 0);

            Pen nonConvexPen = new Pen(Color.Red, 2);
            Pen nonRectPen = new Pen(Color.Orange, 2);
            Pen cardPen = new Pen(Color.Blue, 2);

            SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
            List<IntPoint> cardPositions = new List<IntPoint>();

            for (int i = 0; i < blobs.Length; i++)
            {
                List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]);
                List<IntPoint> corners;

                if (shapeChecker.IsConvexPolygon(edgePoints, out corners))
                {
                    PolygonSubType subType = shapeChecker.CheckPolygonSubType(corners);

                    if ((subType == PolygonSubType.Parallelogram || subType == PolygonSubType.Rectangle) && corners.Count == 4)
                    {
                        // Check if its sideways, if so rearrange the corners so it's vertical.
                        RearrangeCorners(corners);

                        // Prevent detecting the same card twice by comparing distance against other detected cards.
                        bool sameCard = false;
                        foreach (IntPoint point in cardPositions)
                        {
                            if (corners[0].DistanceTo(point) < _minDistance)
                            {
                                sameCard = true;
                                break;
                            }
                        }
                        if (sameCard)
                            continue;

                        // Hack to prevent it from detecting smaller sections of the card instead of the whole card.
                        if (GetArea(corners) < _minArea)
                            continue;

                        cardPositions.Add(corners[0]);

                        bitmapGraphics.DrawPolygon(cardPen, ToPointsArray(corners));
                    }
                    else
                    {
                        foreach (IntPoint point in edgePoints.Take(300))
                        {
                            bitmapGraphics.DrawEllipse(nonRectPen, point.X, point.Y, 1, 1);
                        }
                    }
                }
                else
                {
                    foreach (IntPoint point in edgePoints.Take(300))
                    {
                        bitmapGraphics.DrawEllipse(nonConvexPen, point.X, point.Y, 1, 1);
                    }
                }
            }

            bitmapGraphics.Dispose();
            nonConvexPen.Dispose();
            nonRectPen.Dispose();
            cardPen.Dispose();

            return outputBitmap;
        }