private void initalizeHistograms()
        { 
            int[] binSizes = new int[] { 32, 32 }; //X bins per channel

            IntRange[] ranges = new IntRange[] 
            { 
                new IntRange(0, 180), //Hue (for 8bpp image hue>> range is (0-180) otherwise (0-360)
                new IntRange(0, 255)
            };

            originalObjHist = new DenseHistogram(binSizes, ranges);
            backgroundHist = originalObjHist.CopyBlank();
        }
        private static unsafe void calculateHistByte(DenseHistogram hist, IImage[] channels, Image<Gray<byte>> mask)
        {
            /******************************* prepare data *****************************/
            float* bins = (float*)hist.HistogramData;
            float[][] valueToIndexMultipliers = hist.ValueToIndexMultipliers;
            int[] strides = hist.Strides;

            int width = channels[0].Width;
            int height = channels[0].Height;
            int channelStride = channels[0].Stride;
            int numDimensions = channels.Length;

            byte*[] channelPtrs = new byte*[channels.Length];
            for (int i = 0; i < channelPtrs.Length; i++)
            {
                channelPtrs[i] = (byte*)channels[i].ImageData;
            }

            byte* maskPtr = (byte*)mask.ImageData;
            int maskStride = mask.Stride;
            /******************************* prepare data *****************************/

            for (int row = 0; row < height; row++)
            {
                for (int col = 0; col < width; col++)
                {
                    if (maskPtr[col] == 0)
                        continue;

                    float* binsTemp = bins;
                    for (int d = 0; d < numDimensions; d++)
                    {
                        var idxDecimal = channelPtrs[d][col] * valueToIndexMultipliers[d][0] + valueToIndexMultipliers[d][1];
                        int idx = (int)(idxDecimal);

                        binsTemp += idx * strides[d];

                        //index checking
                        Debug.Assert(idx >= 0 && idx < hist.binSizes[d], string.Format("Calculated index does not match specified bin size. Dimension = {0}", d));
                    }

                    binsTemp[0]++;
                }

                for (int d = 0; d < numDimensions; d++)
                    channelPtrs[d] += channelStride;

                maskPtr += maskStride;
            }
        }
        private static void backProjectByte(DenseHistogram hist, IImage[] channels, IImage projImg)
        {
            /******************************* prepare data *****************************/
            float* bins = (float*)hist.HistogramData;
            float[][] valueToIndexMultipliers = hist.ValueToIndexMultipliers;
            int[] strides = hist.Strides;

            int width = channels[0].Width;
            int height = channels[0].Height;
            int channelStride = channels[0].Stride;
            int numDimensions = channels.Length;

            byte*[] channelPtrs = new byte*[channels.Length];
            for (int i = 0; i < channelPtrs.Length; i++)
            {
                channelPtrs[i] = (byte*)channels[i].ImageData;
            }

            byte* projImgPtr = (byte*)projImg.ImageData;
            int projImgStride = projImg.Stride;
            /******************************* prepare data *****************************/

            for (int row = 0; row < height; row++)
            {
                for (int col = 0; col < width; col++)
                {
                    float* binsTemp = bins;
                    for (int d = 0; d < numDimensions; d++)
                    {
                        var idxDecimal = channelPtrs[d][col] * valueToIndexMultipliers[d][0] + valueToIndexMultipliers[d][1];
                        int idx = (int)(idxDecimal);

                        binsTemp += idx * strides[d];
                    }

                    projImgPtr[col] = (byte)binsTemp[0];
                }

                for (int d = 0; d < numDimensions; d++)
                    channelPtrs[d] += channelStride;

                projImgPtr += projImgStride;
            }
        }
        private void initTracking(Image<Bgr, byte> frame)
        {
            //get hue channel from search area
            var hsvImg = frame.Convert<Hsv, byte>(); //<<parallel operation>>
            //user constraints...
            Image<Gray, byte> mask = hsvImg.InRange(new Hsv(0, 0, minV), new Hsv(0, 0, maxV), Byte.MaxValue, 2);

            originalObjHist.Calculate(hsvImg.GetSubRect(roi).SplitChannels(0, 1), !false, mask.GetSubRect(roi));
            originalObjHist.Scale((float)1 / roi.Area());
            //originalObjHist.Normalize(Byte.MaxValue);

            var backgroundArea = roi.Inflate(1.5, 1.5, frame.Size);
            backgroundHist.Calculate(hsvImg.GetSubRect(backgroundArea).SplitChannels(0, 1), !false, mask.GetSubRect(backgroundArea));
            backgroundHist.Scale((float)1 / backgroundArea.Area());
            //backgroundHist.Normalize(Byte.MaxValue);
            
            //how good originalObjHist and objHist match (suppresses possible selected background)
            ratioHist = originalObjHist.CreateRatioHistogram(backgroundHist, Byte.MaxValue, 10);

            searchArea = roi;
            roi = Rectangle.Empty;
        }
        private void initTracking(Bgr<byte>[,] frame)
        {
            //get hue channel from search area
            var hsvImg = frame.ToHsv();
            //user constraints...
            Gray<byte>[,] mask = hsvImg.InRange(new Hsv<byte>(0, 0, (byte)minV), new Hsv<byte>(0, 0, (byte)maxV), Byte.MaxValue, 2);

            originalObjHist.Calculate(hsvImg.SplitChannels<Hsv<byte>, byte>(roi, 0, 1), !false, mask, roi.Location);
            originalObjHist.Scale((float)1 / roi.Area());
            //originalObjHist.Normalize(Byte.MaxValue);

            var backgroundArea = roi.Inflate(1.5, 1.5, frame.Size());
            backgroundHist.Calculate(hsvImg.SplitChannels<Hsv<byte>, byte>(backgroundArea, 0, 1), !false, mask, backgroundArea.Location);
            backgroundHist.Scale((float)1 / backgroundArea.Area());
            //backgroundHist.Normalize(Byte.MaxValue);
            
            //how good originalObjHist and objHist match (suppresses possible selected background)
            ratioHist = originalObjHist.CreateRatioHistogram(backgroundHist, Byte.MaxValue, 10);

            searchArea = roi;
            roi = Rectangle.Empty;
        }
        private static void initalize(DenseHistogram hist, int[] binSizes, IntRange[] ranges)
        {
            hist.binSizes = binSizes;
            hist.ranges = ranges;

            var valueToIndexMultipliers = new float[binSizes.Length][];

            /***********************************************************************************/
            //                                          binSizes[i]
            //idx = (val - range[i].Min) * -----------------------------------
            //                              range[i].Max - range[i].Min + 1
            for (int bin = 0; bin < binSizes.Length; bin++)
            {
                valueToIndexMultipliers[bin] = new float[2];
                valueToIndexMultipliers[bin][0] = (float)binSizes[bin] / (ranges[bin].Max - ranges[bin].Min + 1);
                valueToIndexMultipliers[bin][1] = -ranges[bin].Min * valueToIndexMultipliers[bin][0];
            }
            hist.valueToIndexMultipliers = valueToIndexMultipliers;
            /***********************************************************************************/

            /***********************************************************************************/
            var strides = new int[binSizes.Length];
            strides[binSizes.Length - 1] = 1;
            for (int bin = (binSizes.Length - 1) - 1; bin >= 0; bin--)
            {
                strides[bin] = strides[bin + 1] * (binSizes[bin + 1]);
            }
            hist.strides = strides;
            /***********************************************************************************/

            hist.NumberOfElements = binSizes.Aggregate((a, b) => a * b);

            hist.histogram = new float[hist.NumberOfElements];
            hist.histogramHandle = GCHandle.Alloc(hist.histogram, GCHandleType.Pinned);
            hist.histPtr = (float*)hist.histogramHandle.AddrOfPinnedObject();
        }
        /// <summary>
        /// Creates ratio histogram [0.. <paramref name="histogramsNormalizationFactor"/>].
        /// </summary>
        /// <param name="hist2">Second histogram. This histogram will be element-wise dived with it. Both histograms must be normalized to the same value!</param>
        /// <param name="histogramsNormalizationFactor">Histogram normalization factor. The maximum value in the ratio histogram will be the provided value.</param>
        /// <param name="hist2Gain">Second histogram gain.</param>
        /// <returns>Normalized ratio histogram</returns>
        public unsafe DenseHistogram CreateRatioHistogram(DenseHistogram hist2, float histogramsNormalizationFactor = 1, float hist2Gain = 1)
        { 
            //histograms must be normalized!
            DenseHistogram ratioHist = this.CopyBlank();

            float* hist1Ptr = (float*)this.HistogramData;
            float* hist2Ptr = (float*)hist2.HistogramData;
            float* ratioHistPtr = (float*)ratioHist.HistogramData;
            int numOfElems = this.NumberOfElements;

            for (int i = 0; i < numOfElems; i++)
            {
                if (hist2Ptr[i] != 0)
                    ratioHistPtr[i] = (hist1Ptr[i] / (hist2Gain * hist2Ptr[i])) * histogramsNormalizationFactor;
                else
                    ratioHistPtr[i] = 0; //in original Accord's implementation. Why ?
                    //ratioHistPtr[i] = hist1Ptr[i];

                ratioHistPtr[i] = System.Math.Min(histogramsNormalizationFactor, ratioHistPtr[i]);
            }

            return ratioHist;
        }
 /// <summary>
 /// Gets the same histogram representation but without data. (data is default =  zero)
 /// </summary>
 /// <returns></returns>
 public DenseHistogram CopyBlank()
 {
     DenseHistogram hist = new DenseHistogram();
     initalize(hist, (int[])this.binSizes.Clone(), (IntRange[])this.ranges.Clone());
     return hist;
 }