//http://www.gutgames.com/post/Matrix-Convolution-Filters-in-C.aspx
        public static MatrixData ApplyConvolutionFilter(this MatrixData Input, int[,] filter)
        {
            MatrixData NewBitmap = new MatrixData(Input.Width(), Input.Height());
            MatrixData OldData   = Input;
            double     MeanPixel = Input.ModePixel();
            int        Width     = filter.GetLength(1);
            int        Height    = filter.GetLength(0);

            for (int x = 0; x < Input.Width(); ++x)
            {
                for (int y = 0; y < Input.Height(); ++y)
                {
                    double Value    = 0;
                    double Weight   = 0;
                    int    XCurrent = -Width / 2;
                    for (int x2 = 0; x2 < Width; ++x2)
                    {
                        if (XCurrent + x < Input.Width() && XCurrent + x >= 0)
                        {
                            int YCurrent = -Height / 2;
                            for (int y2 = 0; y2 < Height; ++y2)
                            {
                                if (YCurrent + y < Input.Height() && YCurrent + y >= 0)
                                {
                                    double Pixel;

                                    try
                                    {
                                        Pixel = Input[YCurrent + y, YCurrent + x];
                                    }
                                    catch
                                    {
                                        Pixel = MeanPixel;
                                    }
                                    Value  += filter[x2, y2] * Pixel;
                                    Weight += filter[x2, y2];
                                }
                                ++YCurrent;
                            }
                        }
                        ++XCurrent;
                    }
                    double PixelVal = Input[y, x];
                    if (Weight == 0)
                    {
                        Weight = 1;
                    }
                    if (Weight > 0)
                    {
                        Value    = System.Math.Abs(Value);
                        Value    = (Value / Weight);
                        Value    = (Value > 255)? 255 : Value;
                        PixelVal = Value;
                    }
                    NewBitmap[y, x] = PixelVal;
                }
            }
            return(NewBitmap);
        }
        public static MatrixData DownScale(this MatrixData input, int factor, bool favorBrightPx = true)
        {
            MatrixData output = new MatrixData(input.Height() / factor, input.Width() / factor);

            for (int r = 0; r < output.NumberOfRows; r++)
            {
                for (int c = 0; c < output.NumberOfColumns; c++)
                {
                    MatrixData inpx = new MatrixData(input, r * factor, c * factor, factor, factor);
                    output[r, c] = (favorBrightPx)? (inpx.MeanPixel() + inpx.Percentile(25)) / 2: inpx.MeanPixel();
                }
            }
            return(output);
        }
        public static MatrixData UpScale(this MatrixData input, int factor)
        {
            MatrixData output = new MatrixData(input.Height() * factor, input.Width() * factor);

            for (int r = 0; r < input.NumberOfRows; r++)
            {
                for (int c = 0; c < input.NumberOfColumns; c++)
                {
                    for (int oR = r * factor; oR < (r * factor) + factor; oR++)
                    {
                        for (int oC = c * factor; oC < (c * factor) + factor; oC++)
                        {
                            output[oR, oC] = input[r, c];
                        }
                    }
                }
            }
            return(output);
        }
        public static MatrixData Convert18To6Px(this MatrixData input, bool favorBrightPx = true)
        {
            MatrixData output = new MatrixData(6, 6);
            int        outR   = 0;
            int        outC   = 0;

            for (int r = 0; r < input.Height(); r += 3)
            {
                outC = 0;
                for (int c = 0; c < input.Width(); c += 3)
                {
                    MatrixData m = input.CopyData(r, c, 3, 3);
                    output[outR, outC] = (favorBrightPx)? (m.MeanPixel() + m.Percentile(25)) / 2 : m.MeanPixel();
                    outC++;
                }
                outR++;
            }
            return(output);
        }
        public static MatrixData ReduceDimensionByOne(this MatrixData input, int dimension)
        {
            bool       reduceRow = dimension == 0;
            int        height    = (reduceRow)? input.Height() - 1 : input.Height();
            int        width     = (reduceRow)?  input.Width() : input.Width() - 1;
            MatrixData output    = new MatrixData(height, width);
            Random     rand      = new Random();

            int[] rToSkip = Enumerable
                            .Repeat(0, width)
                            .Select(i => rand.Next(0, width))
                            .ToArray();

            int[] cToSkip = Enumerable
                            .Repeat(0, height)
                            .Select(i => rand.Next(0, height))
                            .ToArray();


            int rInIndex = 0;
            int cInIndex = 0;

            if (reduceRow)
            {
                int rowToSkip;
                for (int c = 0; c < width; c++)
                {
                    rowToSkip = rToSkip[c];
                    rInIndex  = 0;
                    for (int r = 0; r < height; r++)
                    {
                        if (r == rowToSkip)
                        {
                            rInIndex++;
                        }
                        output[r, c] = input[rInIndex, cInIndex];
                        rInIndex++;
                    }
                    cInIndex++;
                }
            }
            else
            {
                int colToSkip;
                for (int r = 0; r < height; r++)
                {
                    colToSkip = cToSkip[r];
                    cInIndex  = 0;
                    for (int c = 0; c < width; c++)
                    {
                        if (c == colToSkip)
                        {
                            cInIndex++;
                        }
                        output[r, c] = input[rInIndex, cInIndex];
                        cInIndex++;
                    }
                    rInIndex++;
                }
            }
            return(output);
        }