1

I have an Array of bytes, representing the RGB values of an image.

How could I group each offset of 3 values (RGB) of this array to apply my tweaks (like removing the repeated colors), maybe using Linq?

["120", "100", "10", "120", "100", "10", "10", "60", "110"]

to

["120", "100", "10", "10", "60", "110"]

mservidio
  • 12,817
  • 9
  • 58
  • 84
Nicke Manarin
  • 3,026
  • 4
  • 37
  • 79
  • 1
    This shows different ways to chunk the list: http://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq – hdz Jul 25 '16 at 01:50
  • 1
    You could use Skeet's [morelinq library](https://github.com/morelinq/MoreLINQ) and its `Batch` method. – Kenneth K. Jul 25 '16 at 02:04

4 Answers4

3

You can use Select to add index to your enumeration and later group by index / 3. A bit of post-processing on each of the groups and you should be able to get what you want:

var grouped = source.Select((x,i) => new { x, i })
                    .GroupBy(x -> x.i / 3)
                    .Select(g => g.ToList())
                    .Select(g => new { R = g[0], G = g[1], B = g[2] })
                    .Distinct();

But that feels quite ugly. If I were you I'd probably write a simple custom LINQ method (an extension method on IEnumerable<int>) to do this more efficiently.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
1

Shorter version that gets the distinct RGB values and their indexes:

string[] a = { "120", "100", "10", "120", "100", "10", "10", "60", "110" };

var l = Enumerable.Range(0, a.Length / 3)
                   .ToLookup(i => new { R = a[i * 3], G = a[i * 3 + 1], B = a[i * 3 + 2] });
Slai
  • 22,144
  • 5
  • 45
  • 53
0

If you don't mind using a loop instead of Linq:

class Program
{
    static void Main(string[] args)
    {
        byte[] array = new byte[] { 120, 100, 10, 120, 100, 10, 10, 60, 110 };
        List<byte[]> grouped = new List<byte[]>();


        // This loop will populate the list grouped with arrays of 3 bytes each, each representing an value for RGB
        for(int i = 0; i + 2 < array.Length; i += 3)
        {
            byte[] currentColor = new byte[]
            {
                array[i],
                array[i + 1],
                array[i + 2]
            };

            grouped.Add(currentColor);
        }

        // Here you will remove repeated elements for RGB
        // Notice you will have to create the ByteArrayComparer class, you will find the code right under this one
        var noRepeatedElements = grouped.Distinct<byte[]>(new ByteArrayComparer());

        // Print the non repeated elements for testing purposes
        foreach(var rgb in noRepeatedElements)
        {
            foreach(var value in rgb)
            {
                Console.Write($"\"{value}\"");
            }
        }

        Console.ReadKey();
    }
}

Where ByteArrayComparer is the following class

// This class will compare two distinct byte arrays and check if their elements are the same
public class ByteArrayComparer : IEqualityComparer<byte[]>
{
    public bool Equals(byte[] x, byte[] y)
    {
        int smallerArrayLength = Math.Min(x.Length, y.Length);
        bool elementsWithSameValue = true;

        for(int i = 0; i < smallerArrayLength; i++)
        {
            // If there is a single element which is different, we know the arrays are different and can break the loop.
            if(x[i] != y[i])
            {
                elementsWithSameValue = false;
                break;
            }
        }

        return elementsWithSameValue;
    }

    public int GetHashCode(byte[] obj)
    {
        int hash = 0;

        for(int i = 0; i < obj.Length; i++)
        {
            hash += obj[i].GetHashCode();
        }

        return hash;
    }
}    

Note that grouped now is a List of arrays of bytes. Each element in grouped has three elements, representing a single RGB value.

Now you can work with the rgb values as you please.

André Sampaio
  • 309
  • 1
  • 5
0

Using Microsoft's Reactive Framework Team's Interactive Extensions (NuGet "Ix-Main") you can do this:

byte[] array = new byte[]
{
    120, 100, 10, 120, 100, 10, 10, 60, 110
};

byte[] results =
    array
        .Buffer(3)
        .Distinct(xs => String.Join(",", xs))
        .SelectMany(x => x)
        .ToArray();

That will give you { 120, 100, 10, 10, 60, 110 }.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172