/// <summary>
        /// WARNING!!: This method implements a convolution and like all convolutions is very slow (unless it can be fully parellised);
        ///            Consider using another noise normalisation method such as in the class NoiseRemoval_Briggs.
        ///
        /// This method does local contrast normalisation. Typically LCN normalises by division only and is motivated by what is known
        /// to happen in the visual cortext.
        /// Every matrix element or pixel value is divided by the (scaled) standard deviation of pixel values
        /// in a local field centred on the pixel. Scaling affects the severity of the normalisation.
        /// There are several ways of doing LCN. That is can divide by the local sum of squares. Or can calculate the local z-score
        /// which effectively normalises by both subtraction and division.
        /// This method is based on formula given by LeCun. Python code at the bottom of this class is the actual
        /// code used by LeCun which appears to do something different.
        /// Wish I knew Python!
        ///
        /// </summary>
        /// <param name="inputM"></param>
        /// <param name="fieldSize"></param>
        /// <returns></returns>
        public static double[,] ComputeLCN(double[,] inputM, int fieldSize)
        {
            /*
             * // FOLLOWING LINES ARE FOR DEBUGGING AND TESTING
             * var inputM2 = MatrixTools.MatrixRotate90Anticlockwise(inputM);
             * ImageTools.DrawReversedMatrix(inputM2, @"C:\SensorNetworks\Output\Sonograms\TESTMATRIX0.png");
             * double fractionalStretching = 0.05;
             * inputM2 = ImageTools.ContrastStretching(inputM2, fractionalStretching);
             * ImageTools.DrawReversedMatrix(inputM2, @"C:\SensorNetworks\Output\Sonograms\TESTMATRIX1.png");
             * */

            int rowCount = inputM.GetLength(0);
            int colCount = inputM.GetLength(1);

            int frameWidth = fieldSize / 2;

            /// add frame around matrix to compensate for edge effects.
            double[,] framedM = MatrixTools.FrameMatrixWithZeros(inputM, frameWidth);

            // output matrix is same size as input.
            double[,] outputM = new double[rowCount, colCount];
            double[,] subMatrix;
            double NSquared = fieldSize * fieldSize;

            // alpha is a scaling factor. LeCun set it = 0.00001. Here set much higher to have a noticeable effect!
            double alpha = 1.0;

            // convolve gaussian with the matrix
            for (int r1 = 0; r1 < rowCount; r1++)
            {
                for (int c1 = 0; c1 < colCount; c1++)
                {
                    // get field
                    int r2 = r1 + fieldSize - 1;
                    int c2 = c1 + fieldSize - 1;
                    subMatrix = MatrixTools.Submatrix(framedM, r1, c1, r2, c2);
                    double[] V = MatrixTools.Matrix2Array(subMatrix);
                    double   av, variance;
                    NormalDist.AverageAndVariance(V, out av, out variance);

                    double numerator = inputM[r1, c1];

                    //double numerator = (inputM[r1, c1] - av);
                    double denominator = Math.Sqrt(1 + (alpha * variance));
                    outputM[r1, c1] = numerator / denominator;
                }
            }

            // FOLLOWING LINES ARE FOR DEBUGGING AND TESTING

            /*
             * outputM = MatrixTools.MatrixRotate90Anticlockwise(outputM);
             * ImageTools.DrawReversedMatrix(outputM, @"C:\SensorNetworks\Output\Sonograms\TESTMATRIX2.png");
             * double fractionalStretching = 0.05;
             * outputM = ImageTools.ContrastStretching(outputM, fractionalStretching);
             * ImageTools.DrawReversedMatrix(outputM, @"C:\SensorNetworks\Output\Sonograms\TESTMATRIX3.png");
             */

            return(outputM);
        }