CvMat CalculateBackProjection(CvMat _image, CvHistogram hist)
    {
        CvMat _backProject = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix);

        using (CvMat _imageHSV = ConvertToHSV(_image)) // Convert the image to HSV
            using (CvMat imgH = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix))
                using (CvMat imgS = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix))
                    using (CvMat imgV = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix))
                    {
                        // Break image into H, S, V planes
                        // If the image were BGR, then it would split into B, G, R planes respectively
                        _imageHSV.CvtPixToPlane(imgH, imgS, imgV, null); // Cv.Split also does this

                        // Store HSV planes as an IplImage array to pass to openCV's hist function

                        // TODO:  Why can't BackProjection accept CvMat?
                        IplImage[] hsvPlanes = { Cv.GetImage(imgH), Cv.GetImage(imgS),
                                                 Cv.GetImage(imgV) };

                        hist.CalcBackProject(hsvPlanes, _backProject);
                    }
        return(_backProject);
    }
    //  Takes an image and calculates its histogram for one channel.
    // Color images have 3 channels (4 if you count alpha?)
    // Webcam captures them in (R)ed, (G)reen, (B)lue.
    // Convert to (H)ue, (S)aturation (V)alue to get better separation for thresholding
    CvHistogram CalculateOneChannelHistogram(CvMat _image, int channelNum, float channelMax)
    {
        // Hue, Saturation, Value or HSV is a color model that describes colors (hue or tint)
        // in terms of their shade (saturation or amount of gray)
        //	and their brightness (value or luminance).
        // For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255]

        if (channelNum > imColorChannels)
        {
            Debug.LogError("Desired channel number " + channelNum + " is out of range.");
        }

        float channelMin = 0;

        float[] channelRanges = new float[2] {
            channelMin, channelMax
        };

        float[][] ranges = { channelRanges };

        // Note: You don't need to use all 3 channels for the histogram.
        int channelBins = 32;  // Number of bins in the Hue histogram (more bins = narrower bins)

        // Number of bins per histogram channel
        // If we use all 3 channels (H, S, V) then the histogram will have 3 dimensions.
        int[] hist_size = new int[] { channelBins };

        CvHistogram hist = new CvHistogram(hist_size, HistogramFormat.Array, ranges, true);

        using (CvMat _imageHSV = ConvertToHSV(_image)) // Convert the image to HSV
                                                       // We could keep the image in B, G, R, A if we wanted to.
                                                       // Just split the channels into B, G, R planes

            using (CvMat imgChannel = new CvMat(_imageHSV.Rows, _imageHSV.Cols, MonoColorMatrix))
            {
                // Break image into H, S, V planes
                // If the image were BGR, then it would split into B, G, R planes respectively

                switch (channelNum)
                {
                case 0:
                    _imageHSV.CvtPixToPlane(imgChannel, null, null, null);  // Cv.Split also does this
                    break;

                case 1:
                    _imageHSV.CvtPixToPlane(null, imgChannel, null, null);  // Cv.Split also does this
                    break;

                case 2:
                    _imageHSV.CvtPixToPlane(null, null, imgChannel, null);  // Cv.Split also does this
                    break;

                default:
                    Debug.LogError("Channel is out of range");
                    _imageHSV.CvtPixToPlane(imgChannel, null, null, null);  // Cv.Split also does this
                    break;
                }

                hist.Calc(Cv.GetImage(imgChannel), false, null); // Call hist function (no accumulatation, no mask)
            }

        return(hist);  // Return the histogram
    }
    //  Takes an image and calculates its histogram in HSV color space
    // Color images have 3 channels (4 if you count alpha?)
    // Webcam captures them in (R)ed, (G)reen, (B)lue.
    // Convert to (H)ue, (S)aturation (V)alue to get better separation for thresholding
    CvHistogram CalculateHSVHistogram(CvMat _image)
    {
        // Hue, Saturation, Value or HSV is a color model that describes colors (hue or tint)
        // in terms of their shade (saturation or amount of gray)
        //	and their brightness (value or luminance).
        // For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255]

        // hue varies from 0 to 179, see cvtColor
        float hueMin = 0, hueMax = 179;

        float[] hueRanges = new float[2] {
            hueMin, hueMax
        };
        // saturation varies from 0 (black-gray-white) to
        // 255 (pure spectrum color)
        float satMin = 0, satMax = 255;

        float[] saturationRanges = new float[2] {
            satMin, satMax
        };

        float valMin = 0, valMax = 255;

        float[] valueRanges = new float[2] {
            valMin, valMax
        };

        float[][] ranges = { hueRanges, saturationRanges, valueRanges };

        // Note: You don't need to use all 3 channels for the histogram.
        int hueBins   = 32;               // Number of bins in the Hue histogram (more bins = narrower bins)
        int satBins   = 32;               // Number of bins in the Saturation histogram (more bins = narrower bins)
        int valueBins = 8;                // Number of bins in the Value histogram (more bins = narrower bins)

        float maxValue = 0, minValue = 0; // Minimum and maximum value of calculated histogram

        // Number of bins per histogram channel
        // If we use all 3 channels (H, S, V) then the histogram will have 3 dimensions.
        int[] hist_size = new int[] { hueBins, satBins, valueBins };

        CvHistogram hist = new CvHistogram(hist_size, HistogramFormat.Array, ranges, true);

        using (CvMat _imageHSV = ConvertToHSV(_image)) // Convert the image to HSV
                                                       // We could keep the image in B, G, R, A if we wanted to.
                                                       // Just split the channels into B, G, R planes

            using (CvMat imgH = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix))
                using (CvMat imgS = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix))
                    using (CvMat imgV = new CvMat(_image.Rows, _image.Cols, MonoColorMatrix))
                    {
                        // Break image into H, S, V planes
                        // If the image were RGB, then it would split into R, G, B planes respectively
                        _imageHSV.CvtPixToPlane(imgH, imgS, imgV, null); // Cv.Split also does this

                        // Store HSV planes as an IplImage array to pass to openCV's hist function
                        IplImage[] hsvPlanes = { Cv.GetImage(imgH), Cv.GetImage(imgS), Cv.GetImage(imgV) };

                        hist.Calc(hsvPlanes, false, null); // Call hist function (no accumulatation, no mask)

                        // Do we need to normalize??
                        hist.GetMinMaxValue(out minValue, out maxValue);
                        // Scale the histogram to unity height
                        hist.Normalize(_imageHSV.Width * _imageHSV.Height * hist.Dim * hueMax / maxValue);
                    }

        return(hist);  // Return the histogram
    }