Skip to content

Instantly share code, notes, and snippets.

@DT3264
Last active December 17, 2023 08:02
Show Gist options
  • Select an option

  • Save DT3264/3a88e38ffcc498f77fb1cfd3192ea516 to your computer and use it in GitHub Desktop.

Select an option

Save DT3264/3a88e38ffcc498f77fb1cfd3192ea516 to your computer and use it in GitHub Desktop.
John’s Background Switcher For Windows, Dominant fill color and ordered photos
//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>();
}
}
//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;
}
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);
public static string LastFile
{
get
{
return Path.Combine(Globals.RoamingAppPath, "lastFile.txt");
}
}
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