How to find the most present color in an image ?

Hello everyone, i’m trying to find the most present color in an image.
My program is scanning all the pixels of an image and I wish it returns me the color that is the most present in the image.
I have no apparent error, but when I run the program, unity seems to freeze or loop forever.

The problem is inside Colors.cs and within the function “MainColorFromTexture”

Can anyone help me please ?

EDIT:

Ok, i found the problem. This code is just not optimized at all. It runs correctly, but is just really slow with big pictures. The more colors there are and the more pixels there are inside the picture, and the more it will take time to complete. Sounds obvious said like that, but i didn’t expect this code to be this slow.

The slow loop is inside colors.cs line 32.

Anyone could give me hints to optimize this ?

Here is my code :

DrawColor.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DrawColor : MonoBehaviour
{
    [SerializeField] private Texture2D tex2d;
    [SerializeField] private RawImage rImg;
    [SerializeField] private Colors co;

    private Color32 col;

    void Start()
    {
        //col = co.AverageColorFromTexture(tex2d);
        col = co.MainColorFromTexture(tex2d);
        rImg.color = col;
    }
}

Colors.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Colors : MonoBehaviour
{
    public Color32 AverageColorFromTexture(Texture2D tex)
    {
        Color32[] texColors = tex.GetPixels32();
        int total = texColors.Length;
        float r = 0;
        float g = 0;
        float b = 0;

        for (int i = 0; i < total; i++)
        {
            r += texColors*.r;*

g += texColors*.g;*
b += texColors*.b;*
}

return new Color32((byte)(r / total), (byte)(g / total), (byte)(b / total), 255);
}

public Color32 MainColorFromTexture(Texture2D tex)
{
Color32[] texColors = tex.GetPixels32();
int total = texColors.Length;
List colNumList = new List();

for (int i = 0; i < total; i++)
{
if (colNumList.Count > 0)
{
for (int j = 0; j < colNumList.Count; j++)
{
if (colNumList[j].col.Equals(texColors*)) colNumList[j].quantity += 1;*
else AddColNumToList(texColors*, colNumList);*
}
}
else AddColNumToList(texColors*, colNumList);*
}

Color32 col = new Color32();
int quan = 0;

foreach(colNum cn in colNumList)
{
if (cn.quantity > quan)
{
col = cn.col;
quan = cn.quantity;
}
}

col.a = 255;

return col;

}

private void AddColNumToList(Color32 col, List li)
{
colNum cn = new colNum();
cn.col = col;
cn.quantity = 1;
li.Add(cn);
}
}

public class colNum
{
public Color32 col;
public int quantity;
}

@greyven

Hi Greyven. This made it fast for me, but I’m only using small 32x32 Sprites so this is untested on a large texture. I’m new to C# so there may be a better way to read the Color32 as raw bytes/int, which I learned from a stackoverflow post [c# - Convert Color32 array to byte array to send over network - Stack Overflow][1]

Instead of making a list of colors and quantities as you originally did, I’m using the quick lookup of a hashmap (Dictionary). The tricky part here was creating a hash key from the Color32. But luckily a Color32 is a struct laid out as 4 bytes, which is the same as an int32. So the custom struct Color32Array (name is a bit of a misnomer here) lets us read the 4 bytes as an int for the key. Then drop your keys into the dictionary with value as your quantity of that color.

as a bonus we can find the most used color in the same loop.

using System.Runtime.InteropServices;

    public Color32 MainColorFromTexture(Texture2D tex)
    {
        Color32[] texColors = tex.GetPixels32();
        int total = texColors.Length;
        Dictionary<int, int> colors = new Dictionary<int, int>();

        int max = 1;
        Color32 mostCol = texColors[0]; //default to first texel
        mostCol.GetHashCode();
        for (int i = 0; i < total; i++)
        {

            Color32Array c = new Color32Array();
            c.color = texColors*;*

if (colors.ContainsKey(c.key))
{
int count = ++colors[c.key];
if (count > max)
{
max = count;
mostCol = c.color;
}
}
else colors.Add(c.key, 0);
}

//mostCol.a = 255; // maybe you want this

return mostCol;

}

}

[StructLayout(LayoutKind.Explicit)]
public struct Color32Array
{
[FieldOffset(0)]
public int key;

[FieldOffset(0)]
public Color32 color;
}
[1]: c# - Convert Color32 array to byte array to send over network - Stack Overflow