Last active
December 17, 2023 08:02
-
-
Save DT3264/3a88e38ffcc498f77fb1cfd3192ea516 to your computer and use it in GitHub Desktop.
John’s Background Switcher For Windows, Dominant fill color and ordered photos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //Copy-pasted from https://github.com/MrMDavidson/code-samples, http://csharpexamples.com/fast-image-processing-c/ and https://stackoverflow.com/questions/30103425/find-dominant-color-in-an-image | |
| //Modifications | |
| //*The redimension size of the image is 1024 since is fast enough and not too heavy | |
| //*Before calculating the means I add the most used color the number of times it appeared to bias the result more to that color. | |
| //*The parameter of threshold in 25 gived good results in my case | |
| //*The k in the calculated method is not used anymore | |
| //*It inserts at most 4 clusters from deterministic points to always have the same result | |
| //Note: A lot of the code might be expanded, it was extracted from the decompiler | |
| private unsafe static Color GetDominantColour(Image originalImage) | |
| { | |
| Color result; | |
| using (Image image = (Image)originalImage.Clone()) | |
| { | |
| Size newSize; | |
| if (image.Width > image.Height) | |
| { | |
| newSize = new Size(1024, (int)Math.Floor((double)((float)image.Height / ((float)image.Width * 1f) * 1024f))); | |
| } | |
| else | |
| { | |
| newSize = new Size((int)Math.Floor((double)((float)image.Width / ((float)image.Width * 1f) * 1024f)), 1024); | |
| } | |
| using (Bitmap bitmap = new Bitmap(image, newSize)) | |
| { | |
| List<Color> list = new List<Color>(bitmap.Width * bitmap.Height); | |
| Dictionary<int, int> dictionary = new Dictionary<int, int>(); | |
| BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); | |
| int num = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8; | |
| int height = bitmapData.Height; | |
| int num2 = bitmapData.Width * num; | |
| byte* ptr = (byte*)((void*)bitmapData.Scan0); | |
| for (int i = 0; i < height; i++) | |
| { | |
| byte* ptr2 = ptr + i * bitmapData.Stride; | |
| for (int j = 0; j < num2; j += num) | |
| { | |
| int blue = (int)ptr2[j]; | |
| int green = (int)ptr2[j + 1]; | |
| Color item = Color.FromArgb((int)ptr2[j + 2], green, blue); | |
| int key = item.ToArgb(); | |
| list.Add(item); | |
| if (dictionary.ContainsKey(key)) | |
| { | |
| Dictionary<int, int> dictionary2 = dictionary; | |
| int num3 = dictionary2[key]; | |
| dictionary2[key] = num3 + 1; | |
| } | |
| else | |
| { | |
| dictionary.Add(key, 1); | |
| } | |
| } | |
| } | |
| bitmap.UnlockBits(bitmapData); | |
| Color item2 = Color.FromArgb(dictionary.OrderByDescending(delegate(KeyValuePair<int, int> x) | |
| { | |
| KeyValuePair<int, int> keyValuePair = x; | |
| return keyValuePair.Value; | |
| }).ToDictionary((KeyValuePair<int, int> x) => x.Key, (KeyValuePair<int, int> x) => x.Value).First<KeyValuePair<int, int>>().Key); | |
| int num4 = dictionary[item2.ToArgb()]; | |
| for (int k = 0; k < num4; k++) | |
| { | |
| list.Add(item2); | |
| } | |
| result = new ImageUtilities.KMeansClusteringCalculator().Calculate(1, list, 25.0)[0]; | |
| } | |
| } | |
| return result; | |
| } | |
| public class KCluster | |
| { | |
| public KCluster(Color centre) | |
| { | |
| this.Centre = centre; | |
| this._colours = new List<Color>(); | |
| } | |
| public Color Centre { get; set; } | |
| public int PriorCount { get; set; } | |
| public void Add(Color colour) | |
| { | |
| this._colours.Add(colour); | |
| } | |
| public bool RecalculateCentre(double threshold = 0.0) | |
| { | |
| Color updatedCentre; | |
| if (this._colours.Count > 0) | |
| { | |
| float r = 0f; | |
| float g = 0f; | |
| float b = 0f; | |
| foreach (Color color in this._colours) | |
| { | |
| r += (float)color.R; | |
| g += (float)color.G; | |
| b += (float)color.B; | |
| } | |
| int r = (int)Math.Round((double)(r / (float)this._colours.Count)); | |
| int g = (int)Math.Round((double)(g / (float)this._colours.Count)); | |
| int b = (int)Math.Round((double)(b / (float)this._colours.Count)); | |
| int red = Math.Min(Math.Max(r, 0), 255); | |
| int green = Math.Min(Math.Max(g, 0), 255); | |
| int blue = Math.Min(Math.Max(b, 0), 255); | |
| updatedCentre = Color.FromArgb(red, green, blue); | |
| } | |
| else | |
| { | |
| updatedCentre = Color.FromArgb(0, 0, 0, 0); | |
| } | |
| double num = ImageUtilities.KCluster.EuclideanDistance(this.Centre, updatedCentre); | |
| this.Centre = updatedCentre; | |
| this.PriorCount = this._colours.Count; | |
| this._colours.Clear(); | |
| return num > threshold; | |
| } | |
| public double DistanceFromCentre(Color colour) | |
| { | |
| return ImageUtilities.KCluster.EuclideanDistance(colour, this.Centre); | |
| } | |
| public static double EuclideanDistance(Color c1, Color c2) | |
| { | |
| return Math.Sqrt(Math.Pow((double)(c1.R - c2.R), 2.0) + Math.Pow((double)(c1.G - c2.G), 2.0) + Math.Pow((double)(c1.B - c2.B), 2.0)); | |
| } | |
| private readonly List<Color> _colours; | |
| } | |
| public class KMeansClusteringCalculator | |
| { | |
| public IList<Color> Calculate(int k, IList<Color> colours, double threshold = 0.0) | |
| { | |
| List<ImageUtilities.KCluster> clusters = new List<ImageUtilities.KCluster>(); | |
| new Random(); | |
| new List<int>(); | |
| ImageUtilities.KCluster cluster = new ImageUtilities.KCluster(colours[colours.Count - 1]); | |
| clusters.Add(cluster); | |
| cluster = new ImageUtilities.KCluster(colours[colours.Count / 2 - 1]); | |
| int i = 0; | |
| while (i < clusters.Count && !(clusters[i].Centre == cluster.Centre)) | |
| { | |
| if (i == clusters.Count - 1) | |
| { | |
| clusters.Add(cluster); | |
| } | |
| i++; | |
| } | |
| cluster = new ImageUtilities.KCluster(colours[colours.Count / 3 - 1]); | |
| while (i < clusters.Count && !(clusters[i].Centre == cluster.Centre)) | |
| { | |
| if (i == clusters.Count - 1) | |
| { | |
| clusters.Add(cluster); | |
| } | |
| i++; | |
| } | |
| cluster = new ImageUtilities.KCluster(colours[colours.Count / 4 - 1]); | |
| while (i < clusters.Count && !(clusters[i].Centre == cluster.Centre)) | |
| { | |
| if (i == clusters.Count - 1) | |
| { | |
| clusters.Add(cluster); | |
| } | |
| i++; | |
| } | |
| bool updated = false; | |
| do | |
| { | |
| updated = false; | |
| foreach (Color colour in colours) | |
| { | |
| double shortestDistance = 3.4028234663852886E+38; | |
| ImageUtilities.KCluster closestCluster = null; | |
| foreach (ImageUtilities.KCluster cluster2 in clusters) | |
| { | |
| double distance = cluster2.DistanceFromCentre(colour); | |
| if (distance < shortestDistance) | |
| { | |
| shortestDistance = distance; | |
| closestCluster = cluster2; | |
| } | |
| } | |
| closestCluster.Add(colour); | |
| } | |
| using (List<ImageUtilities.KCluster>.Enumerator enumerator2 = clusters.GetEnumerator()) | |
| { | |
| while (enumerator2.MoveNext()) | |
| { | |
| if (enumerator2.Current.RecalculateCentre(threshold)) | |
| { | |
| updated = true; | |
| } | |
| } | |
| } | |
| } | |
| while (updated); | |
| return (from c in clusters | |
| orderby c.PriorCount descending | |
| select c.Centre).ToList<Color>(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //Finds the nearest file with the greatest path match | |
| //The longer the match, the closer to the file | |
| private int GetIndexOfLastOrNearest(List<SwitchPhoto> photos) | |
| { | |
| string text = ""; | |
| try | |
| { | |
| text = File.ReadAllText(Globals.LastFile); | |
| } | |
| catch (Exception) | |
| { | |
| } | |
| if (text.Equals("")) | |
| { | |
| return -1; | |
| } | |
| int num = 0; | |
| int num2 = 0; | |
| for (int i = 0; i < photos.Count; i++) | |
| { | |
| int match = this.GetMatch(text, photos[i].Photo.OriginalLocation); | |
| if (match > num) | |
| { | |
| num = match; | |
| num2 = i; | |
| } | |
| } | |
| if (num2 - 1 != -1) | |
| { | |
| return num2; | |
| } | |
| return photos.Count - 1; | |
| } | |
| private int GetMatch(string path1, string path2) | |
| { | |
| int lastMatch = 0; | |
| int i = 0; | |
| while (i < Math.Min(path1.Length, path2.Length) && path1[i] == path2[i]) | |
| { | |
| lastMatch = i; | |
| i++; | |
| } | |
| return lastMatch; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| private bool GetPicturesStandardImpl(List<BackgroundSwitcherSettingsPicture> pictures, List<Rectangle> screen_sizes, int pictures_to_download, List<SwitchPhoto> photos, BackgroundWorker worker, DoWorkEventArgs e) | |
| { | |
| //Sorts the files so they are always in the same order | |
| photos.Sort((SwitchPhoto p1, SwitchPhoto p2) => string.Compare(p1.Photo.OriginalLocation, p2.Photo.OriginalLocation)); | |
| List<SwitchPhoto> list = new List<SwitchPhoto>(photos); | |
| while (pictures.Count < pictures_to_download && photos.Count > 0) | |
| { | |
| if (SwitchEngine.IsCancelPending(worker, e)) | |
| { | |
| return false; | |
| } | |
| //Gets the next file in order | |
| int num = this.GetIndexOfLastOrNearest(photos) + 1; | |
| //If the last file was the last, the next is the first | |
| if (num >= photos.Count) | |
| { | |
| num = 0; | |
| } | |
| SwitchPhoto switchPhoto = photos[num]; | |
| string originalLocation = photos[num].Photo.OriginalLocation; | |
| //Save the last file (the one to apply now) | |
| File.WriteAllText(Globals.LastFile, originalLocation); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public static string LastFile | |
| { | |
| get | |
| { | |
| return Path.Combine(Globals.RoamingAppPath, "lastFile.txt"); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| The following modifications are on the JBSCore.dll | |
| For the dominant color | |
| 1) Go to the ImageUtilities class, then dit the class and add the code in GetDominantColorFast.cs | |
| 2) Then modify the AutoBackgroundColor method replacing | |
| color = ((colour != null) ? colour.Value : ImageUtilities.GetBaseColour(image as Bitmap)); | |
| with | |
| color = ImageUtilities.GetDominantColour(image); | |
| For the sorted files | |
| 1) Go to the Globals class, edit the class and add the code inside Global.cs | |
| 2) On the SwitchEngine class, edit the class and add the code in GetNextFile.cs | |
| 3) On the SwitchEngine class, edit the method GetPicturesStandardImpl and replace the code with the code in GetPicturesStandardImpl.cs before the first try-catch |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment