This algorithm is supposed to organize and visually duplicate images. I compare 2 images at a time; 1 is the first index image of the directory and another for comparing every other image in the directory. If there are duplicates of the first index image, all of the duplicates will be moved to a new folder that is named after the original folder.
It is working fine but the performance time is approximately 5 minutes and 29 seconds. I am dealing with 1090 images.
For example, if 2.png had 5 visually duplicated images, they would be moved to the path file called:
C:\Users\user\Downloads\CaptchaCollection\Large\Duplicates\2
What this code is supposed to do:
Copy all the images from another directory to:
C:\Users\user\Downloads\CaptchaCollection\Large\
This is because I want to back up my raw images just in case I lose them
Collects the amount of images there are in the directory.
Selects the first index and then compares it with all other images in the directory.
If they are equal they are moved to a new folder.
When this is done the original is moved to the
Sorted
folder.Then a new first index will be selected and the loop goes on until everything is finished. At the end of the inner loop, it will recalculate the amount files in the directory.
Move the last file into the
Sorted
folder.Check for any empty folders.
Recursively deletes them.
(Not involved in sorting) Pretty much logs everything into a text file.
private bool compare(Bitmap bmp1, Bitmap bmp2)
{
bool equals = true; // sets it to true first
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadOnly, bmp1.PixelFormat);
BitmapData bmpData2 = bmp2.LockBits(rect, ImageLockMode.ReadOnly, bmp2.PixelFormat);
unsafe
{
byte* ptr1 = (byte*)bmpData1.Scan0.ToPointer();
byte* ptr2 = (byte*)bmpData2.Scan0.ToPointer();
int width = rect.Width * 3; // for 24bpp pixel data
for (int y = 0; equals && y < rect.Height; y++)
{
for (int x = 0; x < width; x++)
{
if (*ptr1 != *ptr2)
{
equals = false;
break;
}
ptr1++;
ptr2++;
}
ptr1 += bmpData1.Stride - width;
ptr2 += bmpData2.Stride - width;
}
}
// unlocks the bits
bmp1.UnlockBits(bmpData1);
bmp2.UnlockBits(bmpData2);
// disposes the image
bmp1.Dispose();
bmp2.Dispose();
return equals; // returns if they're same or not
}
private void sort(object sender, EventArgs e)
{
// testing time
Stopwatch w = new Stopwatch();
w.Start();
// file paths
string sourceDir = @"C:\Users\user\Documents\Original";
string copiedOriginal = @"C:\Users\user\Downloads\CaptchaCollection\Large\";
string duplicatePath = copiedOriginal + "Duplicates\\";
string movedOriginal = copiedOriginal + "Sorted\\";
// creates a log
FileStream outputFileStream = new FileStream(copiedOriginal + "log.txt", FileMode.Create, FileAccess.Write);
StreamWriter writer = new StreamWriter(outputFileStream);
// Copy picture files.
foreach (string f in Directory.GetFiles(sourceDir, "*.png"))
{
// Remove path from the file name.
string fName = f.Substring(sourceDir.Length + 1);
// Use the Path.Combine method to safely append the file name to the path.
// Will overwrite if the destination file already exists.
File.Copy(Path.Combine(sourceDir, fName), Path.Combine(copiedOriginal, fName), true);
}
// creates the folders for the sorting
Directory.CreateDirectory(duplicatePath);
Directory.CreateDirectory(movedOriginal);
// makes sure only n.png is counted
var files = Directory.GetFiles(copiedOriginal)
.Select(nameWithExtension => Path.GetFileNameWithoutExtension(nameWithExtension))
.Where(name => { int number; return int.TryParse(name, out number); })
.Select(name => int.Parse(name))
.OrderBy(number => number).ToArray();
// will move if there is more than 1 file.
while (files.Length > 1)
{
// creates an individual folder based on the original image's name assosiated with the duplicated images
string duplicateOfFolder = Directory.CreateDirectory(duplicatePath + files[0].ToString()).FullName;
// sorting inner loop
for (int j = 1; j < files.Length; j++)
{
// creates 2 images objects for it to run
Bitmap im1 = new Bitmap(copiedOriginal + files[0].ToString() + ".png");
Bitmap im2 = new Bitmap(copiedOriginal + files[j].ToString() + ".png");
// if they are the same
if (compare(im1, im2))
{
// moves the duplicates to the file that was linked to the them
File.Move(copiedOriginal + files[j].ToString() + ".png", duplicateOfFolder + "\\" + files[j].ToString() + ".png");
// logs the moves
writer.WriteLine(files[j].ToString() + ".png" + " is a duplicate of " + files[0].ToString() + ".png");
}
}
// logs that it has finished all duplicates associated
writer.WriteLine(files[0].ToString() + ".png " + "has had its duplicates removed.");
// moves the non-duplicated file to the sorted folder
File.Move(copiedOriginal + files[0].ToString() + ".png", movedOriginal + files[0].ToString() + ".png");
// recalculates the length of the directory
files = Directory.GetFiles(copiedOriginal)
.Select(nameWithExtension => Path.GetFileNameWithoutExtension(nameWithExtension))
.Where(name => { int number; return int.TryParse(name, out number); })
.Select(name => int.Parse(name))
.OrderBy(number => number).ToArray();
}
// moves the non-duplicated file to the sorted folder
File.Move(copiedOriginal + files[0].ToString() + ".png", movedOriginal + files[0].ToString() + ".png");
// deletes and logs any empty folders that were created in this process
processDirectory(duplicatePath);
writer.WriteLine(deletedDirectories);
// finished the timing
w.Stop();
writer.WriteLine(w.Elapsed);
// closes the log.txt file
writer.Close();
outputFileStream.Close();
}
private static void processDirectory(string startLocation)
{
foreach (var directory in Directory.GetDirectories(startLocation))
{
// recursively calls itself
processDirectory(directory);
// if it is empty it will delete
if (Directory.GetFiles(directory).Length == 0 && Directory.GetDirectories(directory).Length == 0)
{
Directory.Delete(directory, false); // deletes
deletedDirectories += Path.GetFileName(directory) + " , "; // logs the deleted directories
}
}
}
Results after every test (2nd test and later):
- 00:05:33.5252512
- 00:05:27.4010562