Hadoop 图像小文件查重方法

https://eldadlevy.wordpress.com/2011/02/05/hadoop-binary-files-processing-entroduced-by-image-duplicates-finder/              翻译


Hadoop 图像去重的二进制文件处理

这篇文章探讨了使用hadoop来处理二进制文件的可能性,同时用一个图片的案例来解释它。图片的副本解决了hadoop job读取多个相对较小的困境,同时也展示了如何在map/reduce job中读取二进制数据。因此,他阐述了Hadoop如何处理二进制文件的过程。本文通过实际操作给出了部分代码,你可以使用它们应用到你的具体环境。
我们大多数时间看到map/reduce算法使在处理文本数据,从著名的wordcount案例到去多其它的hadoop job,一个可以确信的是全部的hadoop框架的设计都是为了处理文本数据。好吧,虽然他可能是真的,hadoop的设计也解决了大量的非文本数据的处理。
为了测试我们所讨论的内容,首先你应该拥有一个hadoop的环境。最快的方法是使用cloudera提供的训练虚拟机,我的好朋友Philippe Adjiman写了一篇文章 nice tutorial 介绍了使用cloudera搭建hadoop环境。


问题:
我们的图片系统通过搜索的字符串从互联网上下载图片,一些图片可能是完全相同的但是被使用在不同的网站,因此我们希望系统能够过滤掉这些重复的图片。我们可以期待的大量的数据,这就是为什么选择hadoop 来解决它的原因。


输入是充满了jpg格式图片的目录,输出的应该是具有唯一的图片的列表。


困境:
Hadoop正在从巨大的数据文件中读取数据,同时将处理分布在多个代理上。使用优化的处理单元为主的本地数据作为输入进行处理。如果我们使用大量的像图片一样的小文件,处理过程将会发生什么?
显然将会产生大量的开销,HDFS为每个文件保持了相对大的开销,以便能够支持在多个机器上分割文件并在不同机器上用多个副本备份每个文件的功能。至于处理时间,也会受到影响。默认情况下,每个输入文件都会被发送到自己的map任务下,因此,大量的文件意味着需要大量的map任务。拥有大量的map任务可以显著的减慢应用程序的每一个任务的开销。对于超多一个文件分割,可以通过反复利用任务的JVM机制和MultiFileInputSplit来减缓开销。
更多的 小文件问题一些处理大量图片项目的总结。


使用hadoop Sequence Files
因此,我们应该采用什么方法来解决大量的图片问题呢? 使用Hadoop sequence files!这些mapfiles可以由map reduce应用程序读取的映射文件——Sequence files的特殊输入格式,它们通过mapreduce 分割,因此,我们可以由许多map任务的输入组成一个巨大的文件。通过使用这些sequence files我们就可以发挥hadoop的优势。我们可以把任务分割成块,以便并行处理,但是分割的块足以使进程保持高效。
由于序列文件是映射文件,所需的格式是键将是文本并保存HDFS文件名,值将为BytesWritable,并且将包含文件的图像内容。备注 -这个例子,它是更好的存储处理的二进制文件,而不是文件的整个字节。我的兴趣在展示如何读取和写入二进制文件,所以我们坚持BytesWritable。
但是如何从图片文件生成一个sequence files呢?事实证明,这并不是一个简单的任务。下面是一些我想到的方法:
1、 如果可能,最好的方法是在获取图像后立即生成序列文件。这种方式,系统总是准备好进行图像处理,而不需要进行一些预处理来生成序列文件。因为sequence files可以被追加,因此不需要一次检索所有的图像文件。我们可以使用org.apache.hadoop.io.SequenceFile.Writer。 example
2、 将所有图像存储为tar文件,并使用由Stuart Sierra编写的工具将其转换为序列文件。tool
3、 使用mapreduce 任务收集所有图像文件,并将其存储为sequence files。Map的输入时这些图片在hdfs上的路径,map的输出则是sequence files(这个案例中没有reduce任务)。使用hadoop的可扩展优势,以防你真的有大量的文件。这是受到Ryan Balfanz编写的一个简单工具的启发。
我将要阐述根据第三种选择的方式来创建sequence files ,因为我相信这是最通用的方式。
在写代码之前我们有一个亟需解决的问题。如果我无法加载所有的图像字节到内存中,以便将它们写入sequence files,我不会遭受所有与上面提到的大量小文件相关的问题吗?而且如果图像加载到内存中不是最好的做图像处理的方法——查找文件副本,在这个阶段,而不是创建一个顺序文件,然后再试着找到重复的?
答案有两个不同的方面——:
如果图像处理过程不仅包含图片查重,并且存在多于一个的需要读取所有图像的mapreduce 作业,那么你希望它作为一个序列文件,以便更好地执行。
如果我们没有全部的图片,可以独立地为每个图像完成将图像转换为序列文件的预处理。然而,重复查找器是需要所有图像被处理的过程。在一些情况下,可以使用将创建sequence files的作业与找到副本的作业解耦,以便即使在所有图像存在之前将在图像上调用序列文件创建的方式来设计系统


预处理作业 - 生成sequence files:
目标索引文件是使文件名当做 key值,把BytesWritable作为value值。输入是一个包含所有图像文件的文件作为HDFS文件名。例如:
hdfs://localhost:8022/user/elevy/smallArchiveImages/WonderWoman.jpg
所以我们的mapreduce作业将只包含一个map一次只读一个文件并使用SequenceFileOutputFormat写入到sequence files中。它使用FileSystem对象为了打开hdfs上的文件,使用FSDataInputStream去读文件。字节数组被作为可替换字符写入上下文。由于作业的输出格式是SequenceFileOutputFormat类,输出的map将被写入sequence files中。
这在 BinaryFilesToHadoopSequenceFile.java中实现了预处理工作。


图像查重工作:
现在我们有一个包含所有文件二进制数据的序列文件,它是过滤重复项的实际作业的时间。我们将使用MD5算法来对每个图像生成一个唯一的key,并通过比较key值发现重复的图片。我们的mapreduce 作业将包括以下几点:


mapper,将读取所有映像文件的二进制映像数据,并将为每个文件创建MD5。它会将此数据传递给reducer,其中键将是MD5字符串,而值将是文件名。因此,所有的重复图片将会通过hadoop framework集合到一起。
public void map(Text key, BytesWritable value, Context context) throws IOException,InterruptedException { //get the md5 for this specific file String md5Str; 
try { md5Str = calculateMd5(value.getBytes()); } 
catch (NoSuchAlgorithmException e) { e.printStackTrace(); context.setStatus("Internal error - can't find the algorithm for calculating the md5"); 
return; } 
Text md5Text = new Text(md5Str);  //put the file in the map where the md5 is the key, so duplicates  //will be grouped together for the reduce function 
context.write(md5Text, key); } static String calculateMd5(byte[] imageData) throws NoSuchAlgorithmException { //get the md5 for this specific data 
MessageDigest md = MessageDigest.getInstance("MD5"); md.update(imageData); byte[] hash = md.digest(); // Below code of converting Byte Array to hex String hexString = new String(); 
for (int i=0; i < hash.length; i++) { 
hexString += Integer.toString( ( hash[i] & 0xff ) + 0x100, 16).substring( 1 ); 
} return hexString; }


一个非常简单的reducer,它只会获取每个MD5哈希的第一个文件名。这样,每个相同图像将只有一个文件名,并且所有重复项都被过滤。输出是一个映射文件,其中键是文件名,值是MD5哈希值。


public void reduce(Text key, Iterable<Text> values,
  Context context) throws IOException, InterruptedException {
   //Key here is the md5 hash while the values are all the image files that
 // are associated with it. for each md5 value we need to take only
 // one file (the first)
  Text imageFilePath = null;
  for (Text filePath : values) {
    imageFilePath = filePath;
    break;//only the first one
  }
  // In the result file the key will be again the image file path. 
  context.write(imageFilePath, key);
}
所有这些东西都在类 ImageDuplicatesRemover.java中演示,它实现了重复删除作业。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值