C# 色相(Hue)の平均値を取得する

# C# 色相(Hue)の平均値を取得する

## 0（360）をまたぐ色相の画像を作成する
色相(Hue)
![image](https://qiita-image-store.s3.amazonaws.com/0/111261/7676f4f9-7d94-5f9b-2b32-fc5ea810e497.png)

339~360 0~23のグラデーションを作成する
![image](https://qiita-image-store.s3.amazonaws.com/0/111261/2e5e8b93-bc8d-0c58-df6b-dc3925b95a62.png)

正方形の画像を作成し、色相の平均値を取得する
![HueAverageTest.png](https://qiita-image-store.s3.amazonaws.com/0/111261/3dfe23d6-70a9-0d3b-4ad8-22bac1da60fb.png)

## 単純な平均

単純に全ピクセルのHue合計から、ピクセル分で割って平均を出すスクリプト。
しかし、これでは平均値が175.6723になってしまった。

![image](https://qiita-image-store.s3.amazonaws.com/0/111261/129c479f-4378-3e0f-6bca-31319bd8ee76.png)


```CSharp:SimpleAverage.cs

        private void btnImageAverageHue_Click(object sender, EventArgs e)
        {
            CalcSimpleHueAverage(@"画像のフルパス");
        }

        private void CalcSimpleHueAverage(string imgFilePath)
        {
            Bitmap bmp = new Bitmap(imgFilePath);

            List<float> HueList = new List<float>();

            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    Color color = bmp.GetPixel(x, y);
                    HueList.Add(color.GetHue());
                }
            }

            float avrageHue = 0;
            foreach(float hue in HueList)
            {
                avrageHue += hue;
            }
            avrageHue /= (float)HueList.Count;

            Console.WriteLine(avrageHue.ToString());
        }

```

## 色相を円形として計算する

灰色部分に色が分布されているので、このエリアから色の平均を取る

![HueAverage.png](https://qiita-image-store.s3.amazonaws.com/0/111261/749cd2dc-99e2-aa01-ba6d-940e583dd467.png)

まだ、あまり理解していないが、sin,cosを使用して、X,y座標からベクトルを算出して、その平均を求めることで、0の境界線の平均が取れるようだ。
0.742729783566296と理想的な赤の色相の値を取得することが出来た。

```CSharp:CalcCircleHueAverage.cs

        private void btnImageAverageHue_Click(object sender, EventArgs e)
        {
            //CalcSimpleHueAverage(@"画像のフルパス");
            CalcCircleHueAverage(@"画像のフルパス");
        }

        private void CalcCircleHueAverage(string imgFilePath)
        {
            Bitmap bmp = new Bitmap(imgFilePath);

            List<double> HueList = new List<double>();

            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    Color color = bmp.GetPixel(x, y);
                    HueList.Add(color.GetHue());
                }
            }

            double X = 0.0;
            double Y = 0.0;

            for (int hue = 0; hue < HueList.Count; ++hue)
            {
                //Add the X and Y values to the sum X and Y
                X += Math.Cos(HueList[hue] / 180.0d * Math.PI);
                Y += Math.Sin(HueList[hue] / 180.0d * Math.PI);
            }

            X /= HueList.Count;
            Y /= HueList.Count;

            double AverageColor = Math.Atan2(Y, X) * 180.0d / Math.PI;

            Console.WriteLine(AverageColor.ToString());
        }


```

### Atan2は-nからnの値を取る

Atan2は-nからnの値を取るため、degree(0°～360°)に直した値を返すようにする。

```CSharp:CalcCircleHueAverage.cs
        private void btnImageAverageHue_Click(object sender, EventArgs e)
        {
            //CalcSimpleHueAverage(@"D:\Projects\private\CSharp\Color\HueAverageTest.png");

            Bitmap bmp = new Bitmap(@"D:\Projects\private\CSharp\Color\HueAverageTest.png");

            List<double> HueList = new List<double>();

            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    Color color = bmp.GetPixel(x, y);
                    HueList.Add(color.GetHue());
                }
            }

            CalcCircleHueAverage(HueList);
        }

        private double fmod(double a, double b)
        {
            return a - Math.Floor(a / b) * b;
        }

        private double CalcCircleHueAverage(List<double> HueList)
        {
            double X = 0.0;
            double Y = 0.0;

            for (int hue = 0; hue < HueList.Count; ++hue)
            {
                //Add the X and Y values to the sum X and Y
                X += Math.Cos(HueList[hue] / 180.0d * Math.PI);
                Y += Math.Sin(HueList[hue] / 180.0d * Math.PI);
            }

            X /= HueList.Count;
            Y /= HueList.Count;

            double AverageColor = fmod(Math.Atan2(Y, X) * 180.0d / Math.PI, 360d);
            return AverageColor;
        }
```

## 参照


Averaging circular values (particularly Hues in HSL color scheme)
http://stackoverflow.com/questions/13959276/averaging-circular-values-particularly-hues-in-hsl-color-scheme


How can i average Hue value in image?
https://jp.mathworks.com/matlabcentral/answers/111370-how-can-i-average-hue-value-in-image?requestedDomain=www.mathworks.com

色相の平均値の算出方法
http://okwave.jp/qa/q3582670.html

How to map atan2() to degrees 0-360
http://stackoverflow.com/questions/1311049/how-to-map-atan2-to-degrees-0-360