8

jpg 画像ファイルの 3 ~ 4 色の主要な色を検出できるようにしたいと考えています。

以下のサンプル画像とサンプルコード:

画像例1- 赤、黒、白

画像例2- 白、緑、ピンク

画像例3- 青、黄、黒

以下を取得するためにいくつかのコードを変更しましたが、色のグループ化にはまだ問題があります。

    public static int RoundColorToGroup(int i)
    {
        int r = ((int)Math.Round(i / 10.0)) * 10;
        if (r > 255)
            r = 255;
        return r;
    }

    [TestMethod]
    public void AverageColorTest_WebExample()
    {          
        Bitmap bm = new Bitmap("C:\\Users\\XXXX\\Desktop\\example1.jpg");

        int width           = bm.Width;
        int height          = bm.Height;
        int red             = 0;
        int green           = 0;
        int blue            = 0;
        int minDiversion    = 15; // drop pixels that do not differ by at least minDiversion between color values (white, gray or black)
        int dropped         = 0; // keep track of dropped pixels                

        int bppModifier     = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4; // cutting corners, will fail on anything else but 32 and 24 bit images
        BitmapData srcData  = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
        int stride          = srcData.Stride;
        IntPtr Scan0        = srcData.Scan0;

        Dictionary<string, Int64> dicColors = new Dictionary<string, long>(); // color, pixelcount i.e ('#FFFFFF',100);

        unsafe
        {
            byte* p = (byte*)(void*)Scan0;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int idx = (y * stride) + x * bppModifier;
                    red     = p[idx + 2];
                    green   = p[idx + 1];
                    blue    = p[idx];

                    red     = RoundColorToGroup(red);
                    green   = RoundColorToGroup(green);
                    blue    = RoundColorToGroup(blue);

                    if (Math.Abs(red - green) > minDiversion || Math.Abs(red - blue) > minDiversion || Math.Abs(green - blue) > minDiversion)
                    {
                        string htmlColorGroup = ColorTranslator.ToHtml(Color.FromArgb(red, green, blue));

                        if (dicColors.ContainsKey(htmlColorGroup))
                        {
                            dicColors[htmlColorGroup]++;
                        }
                        else
                        {
                            dicColors.Add(htmlColorGroup, 1);
                        }
                    }
                    else
                    {
                        dropped++;
                    }
                }
            }
        }

        dicColors = dicColors.OrderByDescending(x => x.Value).ToDictionary(pair => pair.Key, pair => pair.Value);

        Console.WriteLine(dicColors.ElementAt(0).Key); // should ouput main color 1
        Console.WriteLine(dicColors.ElementAt(1).Key); // should ouput main color 2
        Console.WriteLine(dicColors.ElementAt(2).Key); // should ouput main color 3

    }
  • example1.jpg の出力は (#FF6E8C, #FF6482, #FA6E8C) - 赤/ピンクの 3 階調 - 赤、黒、白である必要があります
  • example2.jpg の出力は (#F0C8C8,#C8DC6E,#E6C8C8) - ピンクとグリーンの 2 色 - ライト ピンク、グリーン、ホワイトのはずです
  • example3.jpg の出力は (#FFDC50,#640A28,#8C1E3C) - 青の 3 階調 - 青、黄、黒である必要があります

理想的には、背景色 (#FFFFFF) と黒のアウトライン/シェーディングを無視する必要があります。

ここでHTMLの色をオンラインでコピーして貼り付けることができます

4

4 に答える 4

1

私は、それがどのように機能するかを見ることができるようなカラーアナライザーアプリを実行しました。あなたは完全に正しい軌道に乗っています。パフォーマンスを維持するために、必要に応じて画像を 128x128 などの小さいサイズに縮小します。

カラーアナライザー

次に、 C# を使用して RGB 値を Color Angleに変換する必要があります。

ここで、Color Count の並べ替えを作成する必要があります。たとえば、色の角度を 360° = 360 アイテムから約 36 色アイテムに縮小することを強くお勧めします。これで、ソートされたリストの最初の色を最も多くの色数でピックアップできます。

于 2013-11-08T11:04:36.900 に答える
1

あなたのヒントとこの記事に感謝します。

ここに解決策があります:

  • GetWebColors() - すべての「名前付き」Web カラーのリストを作成します ( http://www.w3schools.com/html/html_colornames.asp )

  • GetNamedWebColor_NearestMatch(Color) - 任意の色 (16,777,216 あります!) について、最も近い「名前付き」Web カラー (140 のうちの 1 つ) を返します。

  • GetMainXColors() - 渡された最小 HSL プロパティによって異なる最初の X 個の色を返します (これにより、同じ色の 3 つの色合いが返されなくなります)

チューニング: 以下のコード値を修正します (つまり、色相については、色間で最小 10 度の変化が必要です)。

    float minHueDiff        = (float)10;  // 0 to 360
    float minBrightDiff     = (float)0.1; // 0 to 1
    float minSatDiff        = (float)0.1; // 0 to 1

出力するようになりました

  • example1.jpg の出力は次のとおりです: Gainsboro、Crimson、DarkSlateGray: 白、赤、黒に近づきました

  • example2.jpg の出力: Gainsboro、DarkKhaki、Pink : 白、緑、ピンクに近づきました

  • example3.jpg の出力: Black、DarkSlateBlue、Gold: 正しい

コード:

public void AverageColorTest_WebExample_FineTuned()
{                
    Bitmap bm = new Bitmap("C:\\Users\\XXX\\Desktop\\example1.jpg");

    int width = bm.Width;
    int height = bm.Height;
    int red = 0;
    int green = 0;
    int blue = 0;
    float minDiversion = 30 / 100; // drop pixels that do not differ by at least minDiversion between color values (white, gray or black)
    int dropped = 0; // keep track of dropped pixels                

    int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4; // cutting corners, will fail on anything else but 32 and 24 bit images
    BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
    int stride = srcData.Stride;
    IntPtr Scan0 = srcData.Scan0;

    Dictionary<Color, Int64> dicColors = new Dictionary<Color, long>(); // color, pixelcount i.e ('#FFFFFF',100);

    unsafe
    {
        byte* p = (byte*)(void*)Scan0;

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                int idx = (y * stride) + x * bppModifier;
                red = p[idx + 2];
                green = p[idx + 1];
                blue = p[idx];

                if (red == 255 && green == 255 && blue == 255)
                continue;

                Color GroupedColor = GetNamedWebColor_NearestMatch(red, green, blue);

                if (dicColors.ContainsKey(GroupedColor))
                {
                    dicColors[GroupedColor]++;
                }
                else
                {
                    dicColors.Add(GroupedColor, 1);
                }
            }
        }
    }

    // sort dictionary of colors so that most used is at top
    dicColors = dicColors.OrderByDescending(x => x.Value).ToDictionary(pair => pair.Key, pair => pair.Value);

    List<Color> MainColors  = null;
    Int16 numberOf          = 3;
    float minHueDiff        = (float)10;
    float minBrightDiff     = (float)0.1;
    float minSatDiff        = (float)0.1;

    MainColors = GetMainXColors(dicColors.Keys.ToList(), numberOf, minHueDiff, minBrightDiff, minSatDiff);

    foreach (Color MainColor in MainColors)
    {
        Console.WriteLine(ColorTranslator.ToHtml(MainColor)); // should ouput main colors
    }

}   

/// <summary>
/// returns first x many colors that differ by min HSL properties passed in
/// </summary>
/// <param name="listIn"></param>
/// <param name="ReturnMaxNumberOfColors"></param>
/// <param name="minHueDiff"></param>
/// <param name="minBrightDiff"></param>
/// <param name="minSatDiff"></param>
/// <returns></returns>
private static List<Color> GetMainXColors(List<Color> listIn, Int32 ReturnMaxNumberOfColors, float minHueDiff, float minBrightDiff, float minSatDiff)
{
    List<Color> response = new List<Color>();

    Int32 i = 0;
    while (response.Count < ReturnMaxNumberOfColors && i < listIn.Count)
    {
        bool  blnUniqueMainColor = true; // want main colors ie dark brown, gold, silver, not 3 shades of brown
        Color nextColor          = listIn[i];

        float brightness    = nextColor.GetBrightness();
        float sat           = nextColor.GetSaturation();
        float hue           = nextColor.GetHue();

        for (Int32 j = 0; j < response.Count; j++)
        {

            float brightnessOther   = response[j].GetBrightness();
            float satOther          = response[j].GetSaturation();
            float hueOther          = response[j].GetHue();

            // hue is 360 degrees of color, to calculate hue difference                        
            // need to subtract 360 when either are out by 180 (i.e red is at 0 and 359, diff should be 1 etc)
            if (hue - hueOther > 180) hue -= 360;
            if (hueOther - hue > 180) hueOther -= 360;

            float brightdiff        = Math.Abs(brightness - brightnessOther);
            float satdiff           = Math.Abs(sat - satOther);
            float huediff           = Math.Abs(hue - hueOther);
            int matchHSL            = 0;

            if (brightdiff <= minBrightDiff)
                matchHSL++;

            if (satdiff <= minSatDiff)
                matchHSL++;

            if (huediff <= minHueDiff) 
                matchHSL++;

            if (matchHSL != 0  & satdiff != 1))
            {
                blnUniqueMainColor = false;
                break;
            }
        }
        if (blnUniqueMainColor)
        {       // color differs by min ammount of HSL so add to response
            response.Add(nextColor);
        }
        i++;
    }
    return response;
}   

private static List<Color> WebColors;
/// <summary>
/// Returns the "nearest" color from a given "color space"
/// </summary>
/// <param name="input_color">The color to be approximated</param>
/// <returns>The nearest color</returns>        
public static Color GetNamedWebColor_NearestMatch(double dbl_input_red, double dbl_input_green, double dbl_input_blue)
{
    // get the colorspace as an ArrayList
    if (WebColors == null) 
        WebColors = GetWebColors();
    // the Euclidean distance to be computed
    // set this to an arbitrary number
    // must be greater than the largest possible distance (appr. 441.7)
    double distance = 500.0;
    // store the interim result
    double temp;
    // RGB-Values of test colors
    double dbl_test_red;
    double dbl_test_green;
    double dbl_test_blue;
    // initialize the result
    Color nearest_color = Color.Empty;
    foreach (Color o in WebColors)
    {
        // compute the Euclidean distance between the two colors
        // note, that the alpha-component is not used in this example                
        dbl_test_red    = Math.Pow(Convert.ToDouble(((Color)o).R) - dbl_input_red, 2.0);
        dbl_test_green  = Math.Pow(Convert.ToDouble(((Color)o).G) - dbl_input_green, 2.0);
        dbl_test_blue   = Math.Pow(Convert.ToDouble(((Color)o).B) - dbl_input_blue, 2.0);
        temp            = Math.Sqrt(dbl_test_blue + dbl_test_green + dbl_test_red);
        // explore the result and store the nearest color
        if (temp < distance)
        {
            distance = temp;
            nearest_color = (Color)o;
        }
    }            
    return nearest_color;
}

/// <summary>
/// Returns an ArrayList filled with "WebColors"
/// </summary>
/// <returns>WebColors</returns>
/// <remarks></remarks>
private static List<Color> GetWebColors()
{
    List<string> listIgnore = new List<string>();
    listIgnore.Add("transparent");

    Type color = (typeof(Color));
    PropertyInfo[] propertyInfos = color.GetProperties(BindingFlags.Public | BindingFlags.Static);
    List<Color> colors = new List<Color>();

    foreach (PropertyInfo pi in propertyInfos)
    {
        if (pi.PropertyType.Equals(typeof(Color)))
        {
            Color c = (Color)pi.GetValue((object)(typeof(Color)), null);
            if (listIgnore.Contains(c.Name.ToLower()))
            continue;
            colors.Add(c);
        }
    }
    return colors;
}
于 2013-11-08T14:30:21.013 に答える
0

Imagemagick や Opencv などの画像処理ライブラリを使用する必要があると思います。これらのライブラリには、ピクセルの色を抽出するメソッドがあります。これらのライブラリをプロジェクトにリンクするだけです。画像から主要な色を抽出するためにimagemagickを使用しましたが、それはcppアプリケーションでした.imagemagickには.netインターフェースもあると思います

ありがとう

于 2013-11-08T06:21:00.020 に答える