比较图像并删除重复项
Compare images and remove duplicates
我有两个包含图像的文件夹,它们都是 PNG。一个文件夹是另一个文件夹的副本,其中更改了一些图像并添加了一些图像。文件名相同,但图像内容可能不同。不幸的是,时间戳等其他属性是完全随机的。
我想在较新的文件夹中删除重复项(按内容),只保留更新的和新的。
我安装了 ImageMagick 以使用比较命令,但我无法理解。 :-( 你能帮我吗?提前致谢!
补充:我在 Mac OS X。
你不会说你是在 OSX/Linux 还是 Windows,但是,我可以让你开始。 ImageMagick 可以像这样计算图像中所有像素数据的哈希值(校验和),而不管日期或时间戳如何
identify -format "%# %f\n" *.png
25a3591a58550edd2cff65081eab11a86a6a62e006431c8c4393db8d71a1dfe4 blue.png
304c0994c751e75eac86bedac544f716560be5c359786f7a5c3cd6cb8d2294df green.png
466f1bac727ac8090ba2a9a13df8bfb6ada3c4eb3349087ce5dc5d14040514b5 grey.png
042a7ebd78e53a89c0afabfe569a9930c6412577fcf3bcfbce7bafe683e93e8a hue.png
d819bfdc58ac7c48d154924e445188f0ac5a0536cd989bdf079deca86abb12a0 lightness.png
b63ad69a056033a300f23c31f9425df6f469e79c2b9f3a5c515db3b52c323a65 montage.png
a42a5f0abac3bd2f6b4cbfde864342401847a120dacae63294edb45b38edd34e red.png
10bf63fd725c5e02c56df54f503d0544f14f754d852549098d5babd8d3daeb84 sample.png
e95042f227d2d7b2b3edd4c7eec05bbf765a09484563c5ff18bc8e8aa32c1a8e sat.png
因此,如果您在每个文件夹中执行此操作,您将在每个文件夹的单独文件中获得所有文件的校验和及其名称。
如果您随后合并这两个文件并对它们进行排序,您可以很容易地找到重复项,因为重复的文件将彼此相邻出现。
假设,您 运行 在两个文件夹 dira
和 dirb
中执行上述命令,就像这样
cd dira
identify -format "%# %f\n" *.png > $HOME/dira
cd dirb
identify -format "%# %f\n" *.png > $HOME/dirb
然后你可以在awk
中做这样的事情
awk 'FNR==NR{name[]=;next}
{
if( in name){print " duplicates " name[]}
}' $HOME/dir*
因此,$HOME/dir*
部分将两个文件都传递到 awk
。 FNR==NR
之后 {}
中的片段仅适用于读入的第一个文件,并且在读取时,我们保存一个由包含文件名的哈希索引的关联数组。然后,在第二遍中,我们检查每个散列是否已被看到,如果有,我们说它是重复的,并输出我们在第一遍中从散列 name[]
中找到的名称和名称我们在第二遍中找到了 $2.
这不适用于其中包含空格的文件名,因此如果这是一个问题,请更改 identify
命令以在散列和文件名之间放置一个冒号,如下所示:
identify -format "%#:%f\n" *.png
并将 awk
更改为 awk -F":"
,它应该会再次起作用。
这是我针对 Powershell 的丑陋解决方案(现在是一个多平台解决方案)——我一次性编写了它,但它应该可以工作。我试图对它发表一些评论,以弥补它的糟糕程度。
不过,我会在执行此操作之前备份您的图像。以防万一
这里的问题是它只检测每个文件是否与前一个文件重复——如果你需要检查每个文件是否与其他文件重复,你需要嵌套另一个 for()
在那里循环,这应该很容易。
#get the list of files with imagemagick
#powershell handily populates $files as an array, split by line
#this will take a bit
$files = identify -format "%# %f\n" *.png
$arr = @()
foreach($line in $files) {
#add 2 keys to the new array per line (hash and then filename)
$arr += @($line.Split(" "))
}
#for every 2 keys (eg each hash)
for($i = 2; $i -lt $arr.Length; $i += 2) {
#compare it to the last hash
if($arr[$i] -eq $arr[$i-2]) {
#print a helpful message and then delete
echo "$($arr[$i].Substring(0,16)) = $($arr[$i-2].Substring(0,16)) (removing $($arr[$i+1]))"
remove-item ($arr[$i+1])
}
}
奖励:删除任何具有特定哈希值的图像(在我的例子中是全黑 640×480 png):
for($i = 2; $i -lt $arr.Length; $i += 2) {
if($arr[$i] -eq "f824c1a8a1128713f17dd8d1190d70e6012b509606d986e7a6c81e40b628df2b") {
echo "$($arr[$i+1])"
remove-item ($arr[$i+1])
}
}
双重奖励:C 代码,用于检查写入的图像是否与 hash/
文件夹中的给定哈希冲突,如果是则将其删除 — 为 Windows/MinGW 编写,但应该不会太难如有必要,端口。可能是多余的,但我想我会把它扔在那里以防它对任何人有用。
char filename[256] = "output/UNINITIALIZED.ppm";
unsigned long int timeint = time(NULL);
sprintf(filename, "../output/image%lu.ppm", timeint);
if(
writeppm(
filename,
SCREEN_WIDTH,
SCREEN_HEIGHT,
screenSurface->pixels
) != 0
) {
printf("image write error!\n");
return;
}
char shacmd[256];
sprintf(shacmd, "sha256sum %s", filename);
FILE *file = popen(shacmd, "r");
if(file == NULL) {
printf("failed to get image hash!\n");
return;
}
//the hash is 64 characters but we need a 0 at the end too
char sha[96];
int i;
char c;
//get hash until the first space
for(i = 0; (i < 64) && (c != EOF) && (c != 0x32); i++) {
sha[i] = c = fgetc(file);
}
pclose(file);
char hashfilename[256];
sprintf(hashfilename, "../output/hash/%s", sha);
if(_access(hashfilename, 0) != -1) {
//file exists, delete img
if(unlink(filename) != 0) {
printf("image delete error!\n");
}
} else {
FILE *hashfile = fopen(hashfilename, "w");
if(hashfile == NULL)
printf("hash file write error!\nfilename: %s\n", hashfilename);
fclose(hashfile);
}
我有两个包含图像的文件夹,它们都是 PNG。一个文件夹是另一个文件夹的副本,其中更改了一些图像并添加了一些图像。文件名相同,但图像内容可能不同。不幸的是,时间戳等其他属性是完全随机的。
我想在较新的文件夹中删除重复项(按内容),只保留更新的和新的。
我安装了 ImageMagick 以使用比较命令,但我无法理解。 :-( 你能帮我吗?提前致谢!
补充:我在 Mac OS X。
你不会说你是在 OSX/Linux 还是 Windows,但是,我可以让你开始。 ImageMagick 可以像这样计算图像中所有像素数据的哈希值(校验和),而不管日期或时间戳如何
identify -format "%# %f\n" *.png
25a3591a58550edd2cff65081eab11a86a6a62e006431c8c4393db8d71a1dfe4 blue.png
304c0994c751e75eac86bedac544f716560be5c359786f7a5c3cd6cb8d2294df green.png
466f1bac727ac8090ba2a9a13df8bfb6ada3c4eb3349087ce5dc5d14040514b5 grey.png
042a7ebd78e53a89c0afabfe569a9930c6412577fcf3bcfbce7bafe683e93e8a hue.png
d819bfdc58ac7c48d154924e445188f0ac5a0536cd989bdf079deca86abb12a0 lightness.png
b63ad69a056033a300f23c31f9425df6f469e79c2b9f3a5c515db3b52c323a65 montage.png
a42a5f0abac3bd2f6b4cbfde864342401847a120dacae63294edb45b38edd34e red.png
10bf63fd725c5e02c56df54f503d0544f14f754d852549098d5babd8d3daeb84 sample.png
e95042f227d2d7b2b3edd4c7eec05bbf765a09484563c5ff18bc8e8aa32c1a8e sat.png
因此,如果您在每个文件夹中执行此操作,您将在每个文件夹的单独文件中获得所有文件的校验和及其名称。
如果您随后合并这两个文件并对它们进行排序,您可以很容易地找到重复项,因为重复的文件将彼此相邻出现。
假设,您 运行 在两个文件夹 dira
和 dirb
中执行上述命令,就像这样
cd dira
identify -format "%# %f\n" *.png > $HOME/dira
cd dirb
identify -format "%# %f\n" *.png > $HOME/dirb
然后你可以在awk
awk 'FNR==NR{name[]=;next}
{
if( in name){print " duplicates " name[]}
}' $HOME/dir*
因此,$HOME/dir*
部分将两个文件都传递到 awk
。 FNR==NR
之后 {}
中的片段仅适用于读入的第一个文件,并且在读取时,我们保存一个由包含文件名的哈希索引的关联数组。然后,在第二遍中,我们检查每个散列是否已被看到,如果有,我们说它是重复的,并输出我们在第一遍中从散列 name[]
中找到的名称和名称我们在第二遍中找到了 $2.
这不适用于其中包含空格的文件名,因此如果这是一个问题,请更改 identify
命令以在散列和文件名之间放置一个冒号,如下所示:
identify -format "%#:%f\n" *.png
并将 awk
更改为 awk -F":"
,它应该会再次起作用。
这是我针对 Powershell 的丑陋解决方案(现在是一个多平台解决方案)——我一次性编写了它,但它应该可以工作。我试图对它发表一些评论,以弥补它的糟糕程度。
不过,我会在执行此操作之前备份您的图像。以防万一
这里的问题是它只检测每个文件是否与前一个文件重复——如果你需要检查每个文件是否与其他文件重复,你需要嵌套另一个 for()
在那里循环,这应该很容易。
#get the list of files with imagemagick
#powershell handily populates $files as an array, split by line
#this will take a bit
$files = identify -format "%# %f\n" *.png
$arr = @()
foreach($line in $files) {
#add 2 keys to the new array per line (hash and then filename)
$arr += @($line.Split(" "))
}
#for every 2 keys (eg each hash)
for($i = 2; $i -lt $arr.Length; $i += 2) {
#compare it to the last hash
if($arr[$i] -eq $arr[$i-2]) {
#print a helpful message and then delete
echo "$($arr[$i].Substring(0,16)) = $($arr[$i-2].Substring(0,16)) (removing $($arr[$i+1]))"
remove-item ($arr[$i+1])
}
}
奖励:删除任何具有特定哈希值的图像(在我的例子中是全黑 640×480 png):
for($i = 2; $i -lt $arr.Length; $i += 2) {
if($arr[$i] -eq "f824c1a8a1128713f17dd8d1190d70e6012b509606d986e7a6c81e40b628df2b") {
echo "$($arr[$i+1])"
remove-item ($arr[$i+1])
}
}
双重奖励:C 代码,用于检查写入的图像是否与 hash/
文件夹中的给定哈希冲突,如果是则将其删除 — 为 Windows/MinGW 编写,但应该不会太难如有必要,端口。可能是多余的,但我想我会把它扔在那里以防它对任何人有用。
char filename[256] = "output/UNINITIALIZED.ppm";
unsigned long int timeint = time(NULL);
sprintf(filename, "../output/image%lu.ppm", timeint);
if(
writeppm(
filename,
SCREEN_WIDTH,
SCREEN_HEIGHT,
screenSurface->pixels
) != 0
) {
printf("image write error!\n");
return;
}
char shacmd[256];
sprintf(shacmd, "sha256sum %s", filename);
FILE *file = popen(shacmd, "r");
if(file == NULL) {
printf("failed to get image hash!\n");
return;
}
//the hash is 64 characters but we need a 0 at the end too
char sha[96];
int i;
char c;
//get hash until the first space
for(i = 0; (i < 64) && (c != EOF) && (c != 0x32); i++) {
sha[i] = c = fgetc(file);
}
pclose(file);
char hashfilename[256];
sprintf(hashfilename, "../output/hash/%s", sha);
if(_access(hashfilename, 0) != -1) {
//file exists, delete img
if(unlink(filename) != 0) {
printf("image delete error!\n");
}
} else {
FILE *hashfile = fopen(hashfilename, "w");
if(hashfile == NULL)
printf("hash file write error!\nfilename: %s\n", hashfilename);
fclose(hashfile);
}