• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

samsmithnz / PuzzleSolver / 3944938080

pending completion
3944938080

Pull #20

github

GitHub
Merge abf63eefc into 9fd794745
Pull Request #20: Adding test form

295 of 302 branches covered (97.68%)

Branch coverage included in aggregate %.

29 of 29 new or added lines in 1 file covered. (100.0%)

379 of 401 relevant lines covered (94.51%)

6726804.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.31
/src/PuzzleSolver/ImageProcessing.cs
1
using SixLabors.ImageSharp;
2
using SixLabors.ImageSharp.PixelFormats;
3
using System.Text;
4

5
namespace PuzzleSolver;
6

7
public class ImageProcessing
8
{
9
    public List<Rgb24> ColorPalette { get; set; }
4,997,633✔
10

11
    public ImageProcessing(List<Rgb24> colorPalette)
10✔
12
    {
10✔
13
        //Add the primary and secondary colors, with black and white for initial buckets
14
        ColorPalette = colorPalette;
10✔
15
    }
10✔
16

17
    public Dictionary<Rgb24, List<Rgb24>> ProcessImageIntoColorGroups(string? sourceFilename = null, Image<Rgb24>? image = null)
18
    {
10✔
19
        Dictionary<Rgb24, List<Rgb24>> groupedColors = new();
10✔
20
        //string destFilename = Path.GetFileNameWithoutExtension(srcFile.Name) + "_sorted.jpg";
21

22
        Image<Rgb24> sourceImage;
23
        if (sourceFilename != null)
10!
24
        {
10✔
25
            FileInfo srcFile = new(sourceFilename);
10✔
26
            sourceImage = Image.Load<Rgb24>(srcFile.FullName);
10✔
27
        }
10✔
28
        else
29
        {
×
30
            sourceImage = image;
×
31
        }
×
32

33
        //int srcWidth = sourceImg.Size().Width;
34
        int srcHeight = sourceImage.Size().Height;
10✔
35

36
        //using var destImg = new Image<Rgb24>(srcWidth, srcHeight);
37
        //Dictionary<Rgb24, int> pixels = new();
38
        sourceImage.ProcessPixelRows(accessor =>
10✔
39
        {
10✔
40
            for (var row = 0; row < srcHeight; row++)
7,196✔
41
            {
3,588✔
42
                Span<Rgb24> pixelSpan = accessor.GetRowSpan(row);
3,588✔
43
                for (var col = 0; col < pixelSpan.Length; col++)
10,002,422✔
44
                {
4,997,623✔
45
                    Rgb24? colorGroup = FindClosestColorGroup(pixelSpan[col]);
4,997,623✔
46
                    if (colorGroup != null)
4,997,623✔
47
                    {
4,997,623✔
48
                        if (!groupedColors.ContainsKey((Rgb24)colorGroup))
4,997,623✔
49
                        {
281✔
50
                            List<Rgb24> colorList = new()
281✔
51
                            {
281✔
52
                                pixelSpan[col]
281✔
53
                            };
281✔
54
                            groupedColors[(Rgb24)colorGroup] = colorList;
281✔
55
                        }
281✔
56
                        else
10✔
57
                        {
4,997,342✔
58
                            List<Rgb24> colorList = groupedColors[(Rgb24)colorGroup];
4,997,342✔
59
                            colorList.Add(pixelSpan[col]);
4,997,342✔
60
                            groupedColors[(Rgb24)colorGroup] = colorList;
4,997,342✔
61
                        }
4,997,342✔
62
                    }
4,997,623✔
63
                }
4,997,623✔
64
            }
3,588✔
65
        });
20✔
66

67
        return groupedColors;
10✔
68
    }
10✔
69

70
    //Group RGB colors into multiple groups
71
    private Rgb24? FindClosestColorGroup(Rgb24 colorToTest)
72
    {
4,997,623✔
73
        Rgb24? closestColorGroup = null;
4,997,623✔
74
        List<KeyValuePair<Rgb24, int>> results = new();
4,997,623✔
75
        foreach (Rgb24 color in ColorPalette)
526,479,687✔
76
        {
255,743,409✔
77
            int colorDifference = GetColorDifference(color, colorToTest);
255,743,409✔
78
            results.Add(new KeyValuePair<Rgb24, int>(color, colorDifference));
255,743,409✔
79
        }
255,743,409✔
80
        //Order the results and save over itself
81
        results = results.OrderBy(t => t.Value).ToList();
260,741,032✔
82

83
        //Check if the largest postive or negative value is closer
84
        if (results.Count > 0)
4,997,623✔
85
        {
4,997,623✔
86
            closestColorGroup = results[0].Key;
4,997,623✔
87
        }
4,997,623✔
88
        return closestColorGroup;
4,997,623✔
89
    }
4,997,623✔
90

91
    //Since it uses Sqrt, it always returns a postive number
92
    private static int GetColorDifference(Rgb24 color1, Rgb24 color2)
93
    {
255,743,409✔
94
        return (int)Math.Sqrt(Math.Pow(color1.R - color2.R, 2) + Math.Pow(color1.G - color2.G, 2) + Math.Pow(color1.B - color2.B, 2));
255,743,409✔
95
    }
255,743,409✔
96

97
    public static string BuildNamedColorsAndPercentsString(Dictionary<Rgb24, List<Rgb24>> colorGroups)
98
    {
6✔
99
        //loop through dictionary and calculate percents for each key
100
        StringBuilder sb = new();
6✔
101
        List<KeyValuePair<string, double>> namePercents = new();
6✔
102
        //Calculate the name and percent and add it into a list
103
        foreach (KeyValuePair<Rgb24, List<Rgb24>> colorGroup in colorGroups)
544✔
104
        {
263✔
105
            double percent = (double)colorGroup.Value.Count / (double)colorGroups.Sum(t => t.Value.Count);
30,854✔
106
            namePercents.Add(new KeyValuePair<string, double>(ColorPalettes.ToName(colorGroup.Key), percent));
263✔
107
        }
263✔
108
        //Return the string ordered by percent
109
        foreach (KeyValuePair<string, double> item in namePercents.OrderByDescending(x => x.Value).ThenBy(x => x.Key))
1,070✔
110
        {
263✔
111
            sb.AppendLine($"{item.Key}: {item.Value:0.00%}");
263✔
112
        }
263✔
113
        return sb.ToString();
6✔
114
    }
6✔
115

116
    //from https://docs.sixlabors.com/articles/imagesharp/pixelbuffers.html#efficient-pixel-manipulation
117
    public static Image<Rgb24> ExtractSubImage(Image<Rgb24> sourceImage, Rectangle areaToExtract)
118
    {
×
119
        Image<Rgb24> targetImage = new(areaToExtract.Width, areaToExtract.Height);
×
120
        int height = areaToExtract.Height;
×
121
        sourceImage.ProcessPixelRows(targetImage, (sourceAccessor, targetAccessor) =>
×
122
        {
×
123
            for (int row = 0; row < height; row++)
×
124
            {
×
125
                //get the row from the Y + row index
×
126
                Span<Rgb24> sourceRow = sourceAccessor.GetRowSpan(areaToExtract.Y + row);
×
127
                //Setup the target row
×
128
                Span<Rgb24> targetRow = targetAccessor.GetRowSpan(row);
×
129
                //Copy the source to the target
×
130
                //Copy the source to the target
×
131
                sourceRow.Slice(areaToExtract.X, areaToExtract.Width).CopyTo(targetRow);
×
132
            }
×
133
        });
×
134

135
        return targetImage;
×
136
    }
×
137

138
    //public static byte[] ToArray(SixLabors.ImageSharp.Image imageIn)
139
    //{
140
    //    using (MemoryStream ms = new MemoryStream())
141
    //    {
142
    //        imageIn.Save(ms, JpegFormat.Instance);
143
    //        return ms.ToArray();
144
    //    }
145
    //}
146

147
    //public static byte[] ToArray(this SixLabors.ImageSharp.Image imageIn, IImageFormat fmt)
148
    //{
149
    //    using (MemoryStream ms = new MemoryStream())
150
    //    {
151
    //        imageIn.Save(ms, fmt);
152
    //        return ms.ToArray();
153
    //    }
154
    //}
155

156

157
    //public static System.Drawing.Bitmap ToBitmap<TPixel>(this Image<TPixel> image) where TPixel : unmanaged, IPixel<TPixel>
158
    //{
159
    //    using (var memoryStream = new MemoryStream())
160
    //    {
161
    //        var imageEncoder = image.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance);
162
    //        image.Save(memoryStream, imageEncoder);
163

164
    //        memoryStream.Seek(0, SeekOrigin.Begin);
165

166
    //        return new System.Drawing.Bitmap(memoryStream);
167
    //    }
168
    //}
169

170
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc