1:整体结构

LIRE(Lucene Image REtrieval)提供一种的简单方式来创建基于图像特性的Lucene索引。利用该索引就能够构建一个基于内容的图像检索(content- based image retrieval,CBIR)系统,来搜索相似的图像。在这里就不多进行介绍了,已经写过相关的论文:

LIRE的使用:创建索引

LIRE的使用:搜索相似的图片

LIRe提供的6种图像特征描述方法的评测

因为自己开发的媒资检索系统中用到了LIRe,而且可能还要将实验室自己研究的算法加入其中,因此我研究了一下它源代码的大体结构。

想要看LIRe源代码的话,需要将其源代码包添加进来,相关的教程比较多,在这里就不详细解释了。先来看一看它的目录结构吧。

注:开发环境是MyEclipse 9

乍一看感觉包的数量实在不少,不急,让我们细细来看。所有的包的前缀都是“net.semanticmetadata.lire”,在这里把该目录当成是 “根目录”,根目录中包含的类如上图所示。注:在下面的介绍中就不再提“net.semanticmetadata.lire”了。

根目录主要是一些接口,这些接口可以分为2类:

DocumentBuilder:用于生成索引

ImageSearcher:用于检索

“lire.imageanalysis”里面存储的是lire支持的方法的实现类。每个类以其实现的方法命名。

这些方法的算法有的位于“lire.imageanalysis”的子包中。

比如CEDD算法的实现类位于“lire.imageanalysis.cedd”中;

ColorLayout算法的实现类位于“lire.imageanalysis.mpeg7”中。

“lire.impl”里面存储的是lire支持的方法的DocumentBuilder和ImageSearcher。命名规则是***DocumentBuilder或者***ImageSearcher(***代表方法名称)

2:基本接口(DocumentBuilder)

本文分析LIRe的基本接口。LIRe的基本接口完成的工作不外乎两项:生成索引和检索。生成索引就是根据图片提取特征向量,然后存储特征向量到索引的过程。检索就是根据输入图片的特征向量到索引中查找相似图片的过程。

LIRe的基本接口位于net.semanticmetadata.lire的包中,如下图所示:

将这些接口分为2类:

DocumentBuilder:用于生成索引

ImageSearcher:用于检索

下面来看看与DocumentBuilder相关的类的定义:

(LIRe在代码注释方面做得很好,每个函数的作用都写得很清楚)

DocumentBuilder:接口,定义了基本的方法。

AbstractDocumentBuilder:纯虚类,实现了DocumentBuilder接口。

DocumentBuilderFactory:用于创建DocumentBuilder。

DocumentBuilder相关的类的继承关系如下图所示。可见,各种算法类都继承了AbstractDocumentBuilder,而AbstractDocumentBuilder实现了DocumentBuilder。

详细的源代码如下所示:

DocumentBuilder

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * ~~~~~~~~~~~~~~~~~~~~
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire;
  32.  
  33. import org.apache.lucene.document.Document;
  34.  
  35. import java.awt.image.BufferedImage;
  36. import java.io.IOException;
  37. import java.io.InputStream;
  38.  
  39. /**
  40. * <h2>Creating an Index</h2>
  41. * <p/>
  42. * Use DocumentBuilderFactory to create a DocumentBuilder, which
  43. * will create Lucene Documents from images. Add this documents to
  44. * an index like this:
  45. * <p/>
  46. * <pre>
  47. * System.out.println(">> Indexing " + images.size() + " files.");
  48. * DocumentBuilder builder = DocumentBuilderFactory.getExtensiveDocumentBuilder();
  49. * IndexWriter iw = new IndexWriter(indexPath, new SimpleAnalyzer(LuceneUtils.LUCENE_VERSION), true);
  50. * int count = 0;
  51. * long time = System.currentTimeMillis();
  52. * for (String identifier : images) {
  53. * Document doc = builder.createDocument(new FileInputStream(identifier), identifier);
  54. * iw.addDocument(doc);
  55. * count ++;
  56. * if (count % 25 == 0) System.out.println(count + " files indexed.");
  57. * }
  58. * long timeTaken = (System.currentTimeMillis() - time);
  59. * float sec = ((float) timeTaken) / 1000f;
  60. *
  61. * System.out.println(sec + " seconds taken, " + (timeTaken / count) + " ms per image.");
  62. * iw.optimize();
  63. * iw.close();
  64. * </pre>
  65. * <p/>
  66. * <p/>
  67. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  68. * <br>Date: 31.01.2006
  69. * <br>Time: 23:02:00
  70. *
  71. * @author Mathias Lux, mathias@juggle.at
  72. */
  73. public interface DocumentBuilder {
  74. public static final int MAX_IMAGE_SIDE_LENGTH = 800;
  75.  
  76. public static final String FIELD_NAME_SCALABLECOLOR = "descriptorScalableColor";
  77. public static final String FIELD_NAME_COLORLAYOUT = "descriptorColorLayout";
  78. public static final String FIELD_NAME_EDGEHISTOGRAM = "descriptorEdgeHistogram";
  79. public static final String FIELD_NAME_AUTOCOLORCORRELOGRAM = "featureAutoColorCorrelogram";
  80. public static final String FIELD_NAME_COLORHISTOGRAM = "featureColorHistogram";
  81. public static final String FIELD_NAME_CEDD = "featureCEDD";
  82. public static final String FIELD_NAME_FCTH = "featureFCTH";
  83. public static final String FIELD_NAME_JCD = "featureJCD";
  84. public static final String FIELD_NAME_TAMURA = "featureTAMURA";
  85. public static final String FIELD_NAME_GABOR = "featureGabor";
  86. public static final String FIELD_NAME_SIFT = "featureSift";
  87. public static final String FIELD_NAME_SIFT_LOCAL_FEATURE_HISTOGRAM = "featureSiftHistogram";
  88. public static final String FIELD_NAME_SIFT_LOCAL_FEATURE_HISTOGRAM_VISUAL_WORDS = "featureSiftHistogramVWords";
  89. public static final String FIELD_NAME_IDENTIFIER = "descriptorImageIdentifier";
  90. public static final String FIELD_NAME_CEDD_FAST = "featureCEDDfast";
  91. public static final String FIELD_NAME_COLORLAYOUT_FAST = "featureColorLayoutfast";
  92. public static final String FIELD_NAME_SURF = "featureSurf";
  93. public static final String FIELD_NAME_SURF_LOCAL_FEATURE_HISTOGRAM = "featureSURFHistogram";
  94. public static final String FIELD_NAME_SURF_LOCAL_FEATURE_HISTOGRAM_VISUAL_WORDS = "featureSurfHistogramVWords";
  95. public static final String FIELD_NAME_MSER_LOCAL_FEATURE_HISTOGRAM = "featureMSERHistogram";
  96. public static final String FIELD_NAME_MSER_LOCAL_FEATURE_HISTOGRAM_VISUAL_WORDS = "featureMSERHistogramVWords";
  97. public static final String FIELD_NAME_MSER = "featureMSER";
  98. public static final String FIELD_NAME_BASIC_FEATURES = "featureBasic";
  99. public static final String FIELD_NAME_JPEGCOEFFS = "featureJpegCoeffs";
  100.  
  101. /**
  102. * Creates a new Lucene document from a BufferedImage. The identifier can be used like an id
  103. * (e.g. the file name or the url of the image)
  104. *
  105. * @param image the image to index. Cannot be NULL.
  106. * @param identifier an id for the image, for instance the filename or an URL. Can be NULL.
  107. * @return a Lucene Document containing the indexed image.
  108. */
  109. public Document createDocument(BufferedImage image, String identifier);
  110.  
  111. /**
  112. * Creates a new Lucene document from an InputStream. The identifier can be used like an id
  113. * (e.g. the file name or the url of the image)
  114. *
  115. * @param image the image to index. Cannot be NULL.
  116. * @param identifier an id for the image, for instance the filename or an URL. Can be NULL.
  117. * @return a Lucene Document containing the indexed image.
  118. * @throws IOException in case the image cannot be retrieved from the InputStream
  119. */
  120. public Document createDocument(InputStream image, String identifier) throws IOException;
  121.  
  122. }

从接口的源代码可以看出,提供了两个方法,名字都叫createDocument(),只是参数不一样,一个是从BufferedImage,另一个是从InputStream。

此外,定义了很多的字符串。

AbstractDocumentBuilder

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire;
  32.  
  33. import org.apache.lucene.document.Document;
  34.  
  35. import javax.imageio.ImageIO;
  36. import java.awt.image.BufferedImage;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39.  
  40. /**
  41. * Abstract DocumentBuilder, which uses javax.imageio.ImageIO to create a BufferedImage
  42. * from an InputStream.
  43. * <p/>
  44. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  45. * <br>Date: 31.01.2006
  46. * <br>Time: 23:07:39
  47. *
  48. * @author Mathias Lux, mathias@juggle.at
  49. */
  50. public abstract class AbstractDocumentBuilder implements DocumentBuilder {
  51. /**
  52. * Creates a new Lucene document from an InputStream. The identifier can be used like an id
  53. * (e.g. the file name or the url of the image). This is a simple implementation using
  54. * javax.imageio.ImageIO
  55. *
  56. * @param image the image to index. Please note that
  57. * @param identifier an id for the image, for instance the filename or an URL.
  58. * @return a Lucene Document containing the indexed image.
  59. * @see javax.imageio.ImageIO
  60. */
  61. public Document createDocument(InputStream image, String identifier) throws IOException {
  62. assert (image != null);
  63. BufferedImage bufferedImage = ImageIO.read(image);
  64. return createDocument(bufferedImage, identifier);
  65. }
  66. }

从抽象类的定义可以看出,只有一个createDocument(InputStream image, String identifier),里面调用了createDocument(BufferedImage image, String identifier)。

其实说白了,就是把接口的那两个函数合成了一个函数。

DocumentBuilderFactory

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * ~~~~~~~~~~~~~~~~~~~~
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire;
  32.  
  33. import net.semanticmetadata.lire.imageanalysis.*;
  34. import net.semanticmetadata.lire.impl.ChainedDocumentBuilder;
  35. import net.semanticmetadata.lire.impl.CorrelogramDocumentBuilder;
  36. import net.semanticmetadata.lire.impl.GenericDocumentBuilder;
  37. import net.semanticmetadata.lire.impl.GenericFastDocumentBuilder;
  38.  
  39. /**
  40. * Use DocumentBuilderFactory to create a DocumentBuilder, which
  41. * will create Lucene Documents from images. <br/>
  42. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  43. * <br>Date: 31.01.2006
  44. * <br>Time: 23:00:32
  45. *
  46. * @author Mathias Lux, mathias@juggle.at
  47. */
  48. public class DocumentBuilderFactory {
  49. /**
  50. * Creates a simple version of a DocumentBuilder. In this case the
  51. * {@link net.semanticmetadata.lire.imageanalysis.CEDD} is used as a feature
  52. *
  53. * @return a simple and efficient DocumentBuilder.
  54. * @see net.semanticmetadata.lire.imageanalysis.CEDD
  55. */
  56. public static DocumentBuilder getDefaultDocumentBuilder() {
  57. return new GenericFastDocumentBuilder(CEDD.class, DocumentBuilder.FIELD_NAME_CEDD);
  58. }
  59.  
  60. /**
  61. * Creates a simple version of a DocumentBuilder using the MPEG/-7 visual features features
  62. * all available descriptors are used.
  63. *
  64. * @return a fully featured DocumentBuilder.
  65. * @see net.semanticmetadata.lire.imageanalysis.ColorLayout
  66. * @see net.semanticmetadata.lire.imageanalysis.EdgeHistogram
  67. * @see net.semanticmetadata.lire.imageanalysis.ScalableColor
  68. * @deprecated Use ChainedDocumentBuilder instead
  69. */
  70. public static DocumentBuilder getExtensiveDocumentBuilder() {
  71. ChainedDocumentBuilder cb = new ChainedDocumentBuilder();
  72. cb.addBuilder(DocumentBuilderFactory.getColorLayoutBuilder());
  73. cb.addBuilder(DocumentBuilderFactory.getEdgeHistogramBuilder());
  74. cb.addBuilder(DocumentBuilderFactory.getScalableColorBuilder());
  75. return cb;
  76. }
  77.  
  78. /**
  79. * Creates a fast (byte[] based) version of the MPEG-7 ColorLayout document builder.
  80. *
  81. * @return the document builder.
  82. */
  83. public static DocumentBuilder getColorLayoutBuilder() {
  84. return new GenericFastDocumentBuilder(ColorLayout.class, DocumentBuilder.FIELD_NAME_COLORLAYOUT);
  85. }
  86.  
  87. /**
  88. * Creates a fast (byte[] based) version of the MPEG-7 EdgeHistogram document builder.
  89. *
  90. * @return the document builder.
  91. */
  92. public static DocumentBuilder getEdgeHistogramBuilder() {
  93. return new GenericFastDocumentBuilder(EdgeHistogram.class, DocumentBuilder.FIELD_NAME_EDGEHISTOGRAM);
  94. }
  95.  
  96. /**
  97. * Creates a fast (byte[] based) version of the MPEG-7 ColorLayout document builder.
  98. *
  99. * @return the document builder.
  100. */
  101. public static DocumentBuilder getScalableColorBuilder() {
  102. return new GenericFastDocumentBuilder(ScalableColor.class, DocumentBuilder.FIELD_NAME_SCALABLECOLOR);
  103. }
  104.  
  105. /**
  106. * Creates a simple version of a DocumentBuilder using ScalableColor.
  107. *
  108. * @return a fully featured DocumentBuilder.
  109. * @see net.semanticmetadata.lire.imageanalysis.ScalableColor
  110. * @deprecated Use ColorHistogram and the respective factory methods to get it instead
  111. */
  112. public static DocumentBuilder getColorOnlyDocumentBuilder() {
  113. return DocumentBuilderFactory.getScalableColorBuilder();
  114. }
  115.  
  116. /**
  117. * Creates a simple version of a DocumentBuilder using the ColorLayout feature. Don't use this method any more but
  118. * use the respective feature bound method instead.
  119. *
  120. * @return a simple and fast DocumentBuilder.
  121. * @see net.semanticmetadata.lire.imageanalysis.ColorLayout
  122. * @deprecated use MPEG-7 feature ColorLayout or CEDD, which are both really fast.
  123. */
  124. public static DocumentBuilder getFastDocumentBuilder() {
  125. return DocumentBuilderFactory.getColorLayoutBuilder();
  126. }
  127.  
  128. /**
  129. * Creates a DocumentBuilder for the AutoColorCorrelation feature. Note that the extraction of this feature
  130. * is especially slow! So use it only on small images! Images that do not fit in a 200x200 pixel box are
  131. * resized by the document builder to ensure shorter processing time. See
  132. * {@link net.semanticmetadata.lire.imageanalysis.AutoColorCorrelogram} for more information on the image feature.
  133. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  134. *
  135. * @return the created AutoCorrelation feature DocumentBuilder.
  136. */
  137. public static DocumentBuilder getAutoColorCorrelogramDocumentBuilder() {
  138. return new GenericDocumentBuilder(AutoColorCorrelogram.class, DocumentBuilder.FIELD_NAME_AUTOCOLORCORRELOGRAM, GenericDocumentBuilder.Mode.Fast);
  139. }
  140.  
  141. /**
  142. * Creates a DocumentBuilder for the AutoColorCorrelation feature. Note that the extraction of this feature
  143. * is especially slow, but this is a more fast, but less accurate settings version!
  144. * Images that do not fit in a defined bounding box they are
  145. * resized by the document builder to ensure shorter processing time. See
  146. * {@link net.semanticmetadata.lire.imageanalysis.AutoColorCorrelogram} for more information on the image feature.
  147. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  148. *
  149. * @return the created AutoCorrelation feature DocumentBuilder.
  150. * @deprecated Use #getAutoColorCorrelogramDocumentBuilder instead.
  151. */
  152. public static DocumentBuilder getFastAutoColorCorrelationDocumentBuilder() {
  153. return new CorrelogramDocumentBuilder(AutoColorCorrelogram.Mode.SuperFast);
  154. }
  155.  
  156. /**
  157. * Creates a DocumentBuilder for the CEDD feature. See
  158. * {@link net.semanticmetadata.lire.imageanalysis.CEDD} for more information on the image feature.
  159. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  160. *
  161. * @return the created CEDD feature DocumentBuilder.
  162. */
  163. public static DocumentBuilder getCEDDDocumentBuilder() {
  164. // return new CEDDDocumentBuilder();
  165. return new GenericFastDocumentBuilder(CEDD.class, DocumentBuilder.FIELD_NAME_CEDD);
  166. }
  167.  
  168. /**
  169. * Creates a DocumentBuilder for the FCTH feature. See
  170. * {@link net.semanticmetadata.lire.imageanalysis.FCTH} for more information on the image feature.
  171. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  172. *
  173. * @return the created FCTH feature DocumentBuilder.
  174. */
  175. public static DocumentBuilder getFCTHDocumentBuilder() {
  176. return new GenericDocumentBuilder(FCTH.class, DocumentBuilder.FIELD_NAME_FCTH, GenericDocumentBuilder.Mode.Fast);
  177. }
  178.  
  179. /**
  180. * Creates a DocumentBuilder for the JCD feature. See
  181. * {@link net.semanticmetadata.lire.imageanalysis.JCD} for more information on the image feature.
  182. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  183. *
  184. * @return the created DocumentBuilder
  185. */
  186. public static DocumentBuilder getJCDDocumentBuilder() {
  187. return new GenericFastDocumentBuilder(JCD.class, DocumentBuilder.FIELD_NAME_JCD);
  188. }
  189.  
  190. /**
  191. * Creates a DocumentBuilder for the JpegCoefficientHistogram feature. See
  192. * {@link net.semanticmetadata.lire.imageanalysis.JpegCoefficientHistogram} for more
  193. * information on the image feature.
  194. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  195. *
  196. * @return the created DocumentBuilder
  197. */
  198. public static DocumentBuilder getJpegCoefficientHistogramDocumentBuilder() {
  199. return new GenericDocumentBuilder(JpegCoefficientHistogram.class, DocumentBuilder.FIELD_NAME_JPEGCOEFFS, GenericDocumentBuilder.Mode.Fast);
  200. }
  201.  
  202. /**
  203. * Creates a DocumentBuilder for simple RGB color histograms. See
  204. * {@link net.semanticmetadata.lire.imageanalysis.SimpleColorHistogram} for more
  205. * information on the image feature.
  206. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  207. *
  208. * @return the created feature DocumentBuilder.
  209. */
  210. public static DocumentBuilder getColorHistogramDocumentBuilder() {
  211. return new GenericDocumentBuilder(SimpleColorHistogram.class, DocumentBuilder.FIELD_NAME_COLORHISTOGRAM, GenericDocumentBuilder.Mode.Fast);
  212. }
  213.  
  214. /**
  215. * Creates a DocumentBuilder for three Tamura features. See
  216. * {@link net.semanticmetadata.lire.imageanalysis.Tamura} for more
  217. * information on the image feature.
  218. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  219. *
  220. * @return the created Tamura feature DocumentBuilder.
  221. */
  222. public static DocumentBuilder getTamuraDocumentBuilder() {
  223. return new GenericFastDocumentBuilder(Tamura.class, DocumentBuilder.FIELD_NAME_TAMURA);
  224. }
  225.  
  226. /**
  227. * Creates a DocumentBuilder for the Gabor feature. See
  228. * {@link net.semanticmetadata.lire.imageanalysis.Gabor} for more
  229. * information on the image feature.
  230. * Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  231. *
  232. * @return the created Tamura feature DocumentBuilder.
  233. */
  234. public static DocumentBuilder getGaborDocumentBuilder() {
  235. return new GenericFastDocumentBuilder(Gabor.class, DocumentBuilder.FIELD_NAME_GABOR);
  236. }
  237.  
  238. /**
  239. * Creates and returns a DocumentBuilder, which contains all available features. For
  240. * AutoColorCorrelogram the getAutoColorCorrelogramDocumentBuilder() is used. Therefore
  241. * it is compatible with the respective Searcher.
  242. *
  243. * @return a combination of all available features.
  244. */
  245. public static DocumentBuilder getFullDocumentBuilder() {
  246. ChainedDocumentBuilder cdb = new ChainedDocumentBuilder();
  247. cdb.addBuilder(DocumentBuilderFactory.getExtensiveDocumentBuilder());
  248. cdb.addBuilder(DocumentBuilderFactory.getAutoColorCorrelogramDocumentBuilder());
  249. cdb.addBuilder(DocumentBuilderFactory.getCEDDDocumentBuilder());
  250. cdb.addBuilder(DocumentBuilderFactory.getFCTHDocumentBuilder());
  251. cdb.addBuilder(DocumentBuilderFactory.getColorHistogramDocumentBuilder());
  252. cdb.addBuilder(DocumentBuilderFactory.getTamuraDocumentBuilder());
  253. cdb.addBuilder(DocumentBuilderFactory.getGaborDocumentBuilder());
  254. return cdb;
  255. }
  256. }

DocumentBuilderFactory是用于创建DocumentBuilder的。里面有各种get****DocumentBuilder()。其中以下2种是几个DocumentBuilder的合集:

getExtensiveDocumentBuilder():使用MPEG-7中的ColorLayout,EdgeHistogram,ScalableColor

getFullDocumentBuilder():使用所有的DocumentBuilder

3:基本接口(ImageSearcher)

上篇文章介绍了LIRe源代码里的DocumentBuilder的几个基本接口。本文继续研究一下源代码里的ImageSearcher的几个基本接口。

下面来看看与ImageSearcher相关的类的定义:

ImageSearcher:接口,定义了基本的方法。

AbstractImageSearcher:纯虚类,实现了ImageSearcher接口。

ImageSearcherFactory:用于创建ImageSearcher。

ImageSearcher相关的类的继承关系如下图所示。可见,各种算法类都继承了AbstractImageSearcher,而AbstractImageSearcher实现了ImageSearcher接口。

此外还有一个结构体:

ImageSearchHits:用于存储搜索的结果。

详细的源代码如下所示:

ImageSearcher

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire;
  32.  
  33. import org.apache.lucene.document.Document;
  34. import org.apache.lucene.index.IndexReader;
  35.  
  36. import java.awt.image.BufferedImage;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39. import java.util.Set;
  40.  
  41. /**
  42. * <h2>Searching in an Index</h2>
  43. * Use the ImageSearcherFactory for creating an ImageSearcher, which will retrieve the images
  44. * for you from the index.
  45. * <p/>
  46. * <pre>
  47. * IndexReader reader = IndexReader.open(indexPath);
  48. * ImageSearcher searcher = ImageSearcherFactory.createDefaultSearcher();
  49. * FileInputStream imageStream = new FileInputStream("image.jpg");
  50. * BufferedImage bimg = ImageIO.read(imageStream);
  51. * // searching for an image:
  52. * ImageSearchHits hits = null;
  53. * hits = searcher.search(bimg, reader);
  54. * for (int i = 0; i < 5; i++) {
  55. * System.out.println(hits.score(i) + ": " + hits.doc(i).getField(DocumentBuilder.FIELD_NAME_IDENTIFIER).stringValue());
  56. * }
  57. *
  58. * // searching for a document:
  59. * Document document = hits.doc(0);
  60. * hits = searcher.search(document, reader);
  61. * for (int i = 0; i < 5; i++) {
  62. * System.out.println(hits.score(i) + ": " + hits.doc(i).getField(DocumentBuilder.FIELD_NAME_IDENTIFIER).stringValue());
  63. * }
  64. * </pre>
  65. * <p/>
  66. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  67. * <br>Date: 01.02.2006
  68. * <br>Time: 00:09:42
  69. *
  70. * @author Mathias Lux, mathias@juggle.at
  71. */
  72. public interface ImageSearcher {
  73. /**
  74. * Searches for images similar to the given image.
  75. *
  76. * @param image the example image to search for.
  77. * @param reader the IndexReader which is used to dsearch through the images.
  78. * @return a sorted list of hits.
  79. * @throws java.io.IOException in case exceptions in the reader occurs
  80. */
  81. public ImageSearchHits search(BufferedImage image, IndexReader reader) throws IOException;
  82.  
  83. /**
  84. * Searches for images similar to the given image, defined by the Document from the index.
  85. *
  86. * @param doc the example image to search for.
  87. * @param reader the IndexReader which is used to dsearch through the images.
  88. * @return a sorted list of hits.
  89. * @throws java.io.IOException in case exceptions in the reader occurs
  90. */
  91. public ImageSearchHits search(Document doc, IndexReader reader) throws IOException;
  92.  
  93. /**
  94. * Searches for images similar to the given image.
  95. *
  96. * @param image the example image to search for.
  97. * @param reader the IndexReader which is used to dsearch through the images.
  98. * @return a sorted list of hits.
  99. * @throws IOException in case the image could not be read from stream.
  100. */
  101. public ImageSearchHits search(InputStream image, IndexReader reader) throws IOException;
  102.  
  103. /**
  104. * Identifies duplicates in the database.
  105. *
  106. * @param reader the IndexReader which is used to dsearch through the images.
  107. * @return a sorted list of hits.
  108. * @throws IOException in case the image could not be read from stream.
  109. */
  110. public ImageDuplicates findDuplicates(IndexReader reader) throws IOException;
  111.  
  112. /**
  113. * Modifies the given search by the provided positive and negative examples. This process follows the idea
  114. * of relevance feedback.
  115. *
  116. * @param originalSearch
  117. * @param positives
  118. * @param negatives
  119. * @return
  120. */
  121. public ImageSearchHits relevanceFeedback(ImageSearchHits originalSearch,
  122. Set<Document> positives, Set<Document> negatives);
  123. }

从接口的源代码可以看出,提供了5个方法,其中有3个名字都叫search(),只是参数不一样。一个是BufferedImage,一个是Document,而另一个是InputStream。

AbstractImageSearcher

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30. package net.semanticmetadata.lire;
  31.  
  32. import org.apache.lucene.document.Document;
  33. import org.apache.lucene.index.IndexReader;
  34.  
  35. import javax.imageio.ImageIO;
  36. import java.awt.image.BufferedImage;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39. import java.util.Set;
  40.  
  41. /**
  42. * Abstract ImageSearcher, which uses javax.imageio.ImageIO to create a BufferedImage
  43. * from an InputStream.
  44. * <p/>
  45. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  46. * <br>Date: 01.02.2006
  47. * <br>Time: 00:13:16
  48. *
  49. * @author Mathias Lux, mathias@juggle.at
  50. */
  51. public abstract class AbstractImageSearcher implements ImageSearcher {
  52. /**
  53. * Searches for images similar to the given image. This simple implementation uses
  54. * {@link ImageSearcher#search(java.awt.image.BufferedImage, org.apache.lucene.index.IndexReader)},
  55. * the image is read using javax.imageio.ImageIO.
  56. *
  57. * @param image the example image to search for.
  58. * @param reader the IndexReader which is used to dsearch through the images.
  59. * @return a sorted list of hits.
  60. * @throws IOException in case the image could not be read from stream.
  61. */
  62. public ImageSearchHits search(InputStream image, IndexReader reader) throws IOException {
  63. BufferedImage bufferedImage = ImageIO.read(image);
  64. return search(bufferedImage, reader);
  65. }
  66.  
  67. public ImageSearchHits relevanceFeedback(ImageSearchHits originalSearch, Set<Document> positives, Set<Document> negatives) {
  68. throw new UnsupportedOperationException("Not implemented yet for this kind of searcher!");
  69. }
  70. }

从代码中可以看出AbstractImageSearcher实现了ImageSearcher接口。其中的search(InputStream image, IndexReader reader)方法调用了search(BufferedImage image, IndexReader reader)方法。说白了,就是把2个函数的功能合并为一个函数。

ImageSearcherFactory

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * ~~~~~~~~~~~~~~~~~~~~
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire;
  32.  
  33. import net.semanticmetadata.lire.imageanalysis.*;
  34. import net.semanticmetadata.lire.impl.CorrelogramImageSearcher;
  35. import net.semanticmetadata.lire.impl.GenericFastImageSearcher;
  36. import net.semanticmetadata.lire.impl.SimpleImageSearcher;
  37.  
  38. /**
  39. * <h2>Searching in an Index</h2>
  40. * Use the ImageSearcherFactory for creating an ImageSearcher, which will retrieve the images
  41. * for you from the index.
  42. * <p/>
  43. * <pre>
  44. * IndexReader reader = IndexReader.open(indexPath);
  45. * ImageSearcher searcher = ImageSearcherFactory.createDefaultSearcher();
  46. * FileInputStream imageStream = new FileInputStream("image.jpg");
  47. * BufferedImage bimg = ImageIO.read(imageStream);
  48. * // searching for an image:
  49. * ImageSearchHits hits = null;
  50. * hits = searcher.search(bimg, reader);
  51. * for (int i = 0; i < 5; i++) {
  52. * System.out.println(hits.score(i) + ": " + hits.doc(i).getField(DocumentBuilder.FIELD_NAME_IDENTIFIER).stringValue());
  53. * }
  54. *
  55. * // searching for a document:
  56. * Document document = hits.doc(0);
  57. * hits = searcher.search(document, reader);
  58. * for (int i = 0; i < 5; i++) {
  59. * System.out.println(hits.score(i) + ": " + hits.doc(i).getField(DocumentBuilder.FIELD_NAME_IDENTIFIER).stringValue());
  60. * }
  61. * </pre>
  62. * <p/>
  63. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  64. * <br>Date: 03.02.2006
  65. * <br>Time: 00:30:07
  66. *
  67. * @author Mathias Lux, mathias@juggle.at
  68. */
  69. public class ImageSearcherFactory {
  70. /**
  71. * Default number of maximum hits.
  72. */
  73. public static int NUM_MAX_HITS = 100;
  74.  
  75. /**
  76. * Creates a new simple image searcher with the desired number of maximum hits.
  77. *
  78. * @param maximumHits
  79. * @return the searcher instance
  80. * @deprecated Use ColorLayout, EdgeHistogram and ScalableColor features instead.
  81. */
  82. public static ImageSearcher createSimpleSearcher(int maximumHits) {
  83. return ImageSearcherFactory.createColorLayoutImageSearcher(maximumHits);
  84. }
  85.  
  86. /**
  87. * Returns a new default ImageSearcher with a predefined number of maximum
  88. * hits defined in the {@link ImageSearcherFactory#NUM_MAX_HITS} based on the {@link net.semanticmetadata.lire.imageanalysis.CEDD} feature
  89. *
  90. * @return the searcher instance
  91. */
  92. public static ImageSearcher createDefaultSearcher() {
  93. return new GenericFastImageSearcher(NUM_MAX_HITS, CEDD.class, DocumentBuilder.FIELD_NAME_CEDD);
  94. }
  95.  
  96. /**
  97. * Returns a new ImageSearcher with the given number of maximum hits
  98. * which only takes the overall color into account. texture and color
  99. * distribution are ignored.
  100. *
  101. * @param maximumHits defining how many hits are returned in max (e.g. 100 would be ok)
  102. * @return the ImageSearcher
  103. * @see ImageSearcher
  104. * @deprecated Use ColorHistogram or ScalableColor instead
  105. */
  106. public static ImageSearcher createColorOnlySearcher(int maximumHits) {
  107. return ImageSearcherFactory.createScalableColorImageSearcher(maximumHits);
  108. }
  109.  
  110. /**
  111. * Returns a new ImageSearcher with the given number of maximum hits and
  112. * the specified weights on the different matching aspects. All weights
  113. * should be in [0,1] whereas a weight of 0 implies that the feature is
  114. * not taken into account for searching. Note that the effect is relative and
  115. * can only be fully applied if the {@link DocumentBuilderFactory#getExtensiveDocumentBuilder() extensive DocumentBuilder}
  116. * is used.
  117. *
  118. * @param maximumHits defining how many hits are returned in max
  119. * @param colorHistogramWeight a weight in [0,1] defining the importance of overall color in the images
  120. * @param colorDistributionWeight a weight in [0,1] defining the importance of color distribution (which color where) in the images
  121. * @param textureWeight defining the importance of texture (which edges where) in the images
  122. * @return the searcher instance or NULL if the weights are not appropriate, eg. all 0 or not in [0,1]
  123. * @see DocumentBuilderFactory
  124. * @deprecated Use ColorLayout, EdgeHistogram and ScalableColor features instead.
  125. */
  126. public static ImageSearcher createWeightedSearcher(int maximumHits,
  127. float colorHistogramWeight,
  128. float colorDistributionWeight,
  129. float textureWeight) {
  130. if (isAppropriateWeight(colorHistogramWeight)
  131. && isAppropriateWeight(colorDistributionWeight)
  132. && isAppropriateWeight(textureWeight)
  133. && (colorHistogramWeight + colorDistributionWeight + textureWeight > 0f))
  134. return new SimpleImageSearcher(maximumHits, colorHistogramWeight, colorDistributionWeight, textureWeight);
  135. else
  136. return null;
  137. }
  138.  
  139. /**
  140. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.AutoColorCorrelogram}
  141. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  142. *
  143. * @param maximumHits number of hits returned.
  144. * @return
  145. */
  146. public static ImageSearcher createAutoColorCorrelogramImageSearcher(int maximumHits) {
  147. return new GenericFastImageSearcher(maximumHits, AutoColorCorrelogram.class, DocumentBuilder.FIELD_NAME_AUTOCOLORCORRELOGRAM);
  148. // return new CorrelogramImageSearcher(maximumHits, AutoColorCorrelogram.Mode.SuperFast);
  149. }
  150.  
  151. /**
  152. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.AutoColorCorrelogram}
  153. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  154. *
  155. * @param maximumHits number of hits returned.
  156. * @return
  157. * @deprecated Use #createAutoColorCorrelogramImageSearcher instead
  158. */
  159. public static ImageSearcher createFastCorrelogramImageSearcher(int maximumHits) {
  160. return new CorrelogramImageSearcher(maximumHits, AutoColorCorrelogram.Mode.SuperFast);
  161. }
  162.  
  163. /**
  164. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.CEDD}
  165. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  166. *
  167. * @param maximumHits
  168. * @return
  169. */
  170. public static ImageSearcher createCEDDImageSearcher(int maximumHits) {
  171. // return new CEDDImageSearcher(maximumHits);
  172. return new GenericFastImageSearcher(maximumHits, CEDD.class, DocumentBuilder.FIELD_NAME_CEDD);
  173. }
  174.  
  175. /**
  176. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.FCTH}
  177. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  178. *
  179. * @param maximumHits
  180. * @return
  181. */
  182. public static ImageSearcher createFCTHImageSearcher(int maximumHits) {
  183. // return new GenericImageSearcher(maximumHits, FCTH.class, DocumentBuilder.FIELD_NAME_FCTH);
  184. return new GenericFastImageSearcher(maximumHits, FCTH.class, DocumentBuilder.FIELD_NAME_FCTH);
  185. }
  186.  
  187. /**
  188. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.JCD}
  189. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  190. *
  191. * @param maximumHits
  192. * @return
  193. */
  194. public static ImageSearcher createJCDImageSearcher(int maximumHits) {
  195. return new GenericFastImageSearcher(maximumHits, JCD.class, DocumentBuilder.FIELD_NAME_JCD);
  196. }
  197.  
  198. /**
  199. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.JpegCoefficientHistogram}
  200. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  201. *
  202. * @param maximumHits
  203. * @return
  204. */
  205. public static ImageSearcher createJpegCoefficientHistogramImageSearcher(int maximumHits) {
  206. return new GenericFastImageSearcher(maximumHits, JpegCoefficientHistogram.class, DocumentBuilder.FIELD_NAME_JPEGCOEFFS);
  207. }
  208.  
  209. /**
  210. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.SimpleColorHistogram}
  211. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  212. *
  213. * @param maximumHits
  214. * @return
  215. */
  216. public static ImageSearcher createColorHistogramImageSearcher(int maximumHits) {
  217. // return new GenericImageSearcher(maximumHits, SimpleColorHistogram.class, DocumentBuilder.FIELD_NAME_COLORHISTOGRAM);
  218. return new GenericFastImageSearcher(maximumHits, SimpleColorHistogram.class, DocumentBuilder.FIELD_NAME_COLORHISTOGRAM);
  219. }
  220.  
  221. /**
  222. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.Tamura}
  223. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  224. *
  225. * @param maximumHits
  226. * @return
  227. */
  228. public static ImageSearcher createTamuraImageSearcher(int maximumHits) {
  229. return new GenericFastImageSearcher(maximumHits, Tamura.class, DocumentBuilder.FIELD_NAME_TAMURA);
  230. }
  231.  
  232. /**
  233. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.Gabor}
  234. * image feature. Be sure to use the same options for the ImageSearcher as you used for the DocumentBuilder.
  235. *
  236. * @param maximumHits
  237. * @return
  238. */
  239. public static ImageSearcher createGaborImageSearcher(int maximumHits) {
  240. return new GenericFastImageSearcher(maximumHits, Gabor.class, DocumentBuilder.FIELD_NAME_GABOR);
  241. }
  242.  
  243. /**
  244. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.ColorLayout}
  245. * image feature using the byte[] serialization. Be sure to use the same options for the ImageSearcher as
  246. * you used for the DocumentBuilder.
  247. *
  248. * @param maximumHits
  249. * @return
  250. */
  251. public static ImageSearcher createColorLayoutImageSearcher(int maximumHits) {
  252. return new GenericFastImageSearcher(maximumHits, ColorLayout.class, DocumentBuilder.FIELD_NAME_COLORLAYOUT);
  253. }
  254.  
  255. /**
  256. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.ScalableColor}
  257. * image feature using the byte[] serialization. Be sure to use the same options for the ImageSearcher as
  258. * you used for the DocumentBuilder.
  259. *
  260. * @param maximumHits
  261. * @return
  262. */
  263. public static ImageSearcher createScalableColorImageSearcher(int maximumHits) {
  264. return new GenericFastImageSearcher(maximumHits, ScalableColor.class, DocumentBuilder.FIELD_NAME_SCALABLECOLOR);
  265. }
  266.  
  267. /**
  268. * Create and return an ImageSearcher for the {@link net.semanticmetadata.lire.imageanalysis.EdgeHistogram}
  269. * image feature using the byte[] serialization. Be sure to use the same options for the ImageSearcher as
  270. * you used for the DocumentBuilder.
  271. *
  272. * @param maximumHits
  273. * @return
  274. */
  275. public static ImageSearcher createEdgeHistogramImageSearcher(int maximumHits) {
  276. return new GenericFastImageSearcher(maximumHits, EdgeHistogram.class, DocumentBuilder.FIELD_NAME_EDGEHISTOGRAM);
  277. }
  278.  
  279. /**
  280. * Checks if the weight is in [0,1]
  281. *
  282. * @param f the weight to check
  283. * @return true if the weight is in [0,1], false otherwise
  284. */
  285. private static boolean isAppropriateWeight(float f) {
  286. boolean result = false;
  287. if (f <= 1f && f >= 0) result = true;
  288. return result;
  289.  
  290. }
  291. }

ImageSearcherFactory是用于创建ImageSearcher的。里面有各种create****ImageSearcher()。每个函数的作用在注释中都有详细的说明。

ImageSearchHits

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire;
  32.  
  33. import org.apache.lucene.document.Document;
  34.  
  35. /**
  36. * This class simulates the original Lucene Hits object.
  37. * Please note the only a certain number of results are returned.<br>
  38. * <p/>
  39. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  40. * <br>Date: 02.02.2006
  41. * <br>Time: 23:45:20
  42. *
  43. * @author Mathias Lux, mathias@juggle.at
  44. */
  45. public interface ImageSearchHits {
  46. /**
  47. * Returns the size of the result list.
  48. *
  49. * @return the size of the result list.
  50. */
  51. public int length();
  52.  
  53. /**
  54. * Returns the score of the document at given position.
  55. * Please note that the score in this case is a distance,
  56. * which means a score of 0 denotes the best possible hit.
  57. * The result list starts with position 0 as everything
  58. * in computer science does.
  59. *
  60. * @param position defines the position
  61. * @return the score of the document at given position. The lower the better (its a distance measure).
  62. */
  63. public float score(int position);
  64.  
  65. /**
  66. * Returns the document at given position
  67. *
  68. * @param position defines the position.
  69. * @return the document at given position.
  70. */
  71. public Document doc(int position);
  72. }

该类主要用于存储ImageSearcher类中search()方法返回的结果。

SimpleImageSearchHits是ImageSearcher的实现。该类的源代码如下所示:

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30.  
  31. package net.semanticmetadata.lire.impl;
  32.  
  33. import net.semanticmetadata.lire.ImageSearchHits;
  34. import org.apache.lucene.document.Document;
  35.  
  36. import java.util.ArrayList;
  37. import java.util.Collection;
  38. import java.util.Iterator;
  39.  
  40. /**
  41. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  42. * <br>Date: 02.02.2006
  43. * <br>Time: 23:56:15
  44. *
  45. * @author Mathias Lux, mathias@juggle.at
  46. */
  47. public class SimpleImageSearchHits implements ImageSearchHits {
  48. ArrayList<SimpleResult> results;
  49.  
  50. public SimpleImageSearchHits(Collection<SimpleResult> results, float maxDistance) {
  51. this.results = new ArrayList<SimpleResult>(results.size());
  52. this.results.addAll(results);
  53. // this step normalizes and inverts the distance ...
  54. // although its now a score or similarity like measure its further called distance
  55. for (Iterator<SimpleResult> iterator = this.results.iterator(); iterator.hasNext(); ) {
  56. SimpleResult result = iterator.next();
  57. result.setDistance(1f - result.getDistance() / maxDistance);
  58. }
  59. }
  60.  
  61. /**
  62. * Returns the size of the result list.
  63. *
  64. * @return the size of the result list.
  65. */
  66. public int length() {
  67. return results.size();
  68. }
  69.  
  70. /**
  71. * Returns the score of the document at given position.
  72. * Please note that the score in this case is a distance,
  73. * which means a score of 0 denotes the best possible hit.
  74. * The result list starts with position 0 as everything
  75. * in computer science does.
  76. *
  77. * @param position defines the position
  78. * @return the score of the document at given position. The lower the better (its a distance measure).
  79. */
  80. public float score(int position) {
  81. return results.get(position).getDistance();
  82. }
  83.  
  84. /**
  85. * Returns the document at given position
  86. *
  87. * @param position defines the position.
  88. * @return the document at given position.
  89. */
  90. public Document doc(int position) {
  91. return results.get(position).getDocument();
  92. }
  93.  
  94. private float sigmoid(float f) {
  95. double result = 0f;
  96. result = -1d + 2d / (1d + Math.exp(-2d * f / 0.6));
  97. return (float) (1d - result);
  98. }
  99. }

可以看出检索的结果是存在名为results的ArrayList<SimpleResult> 类型的变量中的。

4:建立索引(DocumentBuilder)[以颜色布局为例]

前几篇文章介绍了LIRe 的基本接口。现在来看一看它的实现部分,本文先来看一看建立索引((DocumentBuilder))部分。不同的特征向量提取方法的建立索引的类各不相同,它们都位于“net.semanticmetadata.lire.impl”中,如下图所示:

由图可见,每一种方法对应一个DocumentBuilder和一个ImageSearcher,类的数量非常的多,无法一一分析。在这里仅分析一个比较有代表性的:颜色布局。

颜色直方图建立索引的类的名称是ColorLayoutDocumentBuilder,该类继承了AbstractDocumentBuilder,它的源代码如下所示:

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30. package net.semanticmetadata.lire.impl;
  31.  
  32. import net.semanticmetadata.lire.AbstractDocumentBuilder;
  33. import net.semanticmetadata.lire.DocumentBuilder;
  34. import net.semanticmetadata.lire.imageanalysis.ColorLayout;
  35. import net.semanticmetadata.lire.utils.ImageUtils;
  36. import org.apache.lucene.document.Document;
  37. import org.apache.lucene.document.Field;
  38.  
  39. import java.awt.image.BufferedImage;
  40. import java.util.logging.Logger;
  41.  
  42. /**
  43. * Provides a faster way of searching based on byte arrays instead of Strings. The method
  44. * {@link net.semanticmetadata.lire.imageanalysis.ColorLayout#getByteArrayRepresentation()} is used
  45. * to generate the signature of the descriptor much faster.
  46. * User: Mathias Lux, mathias@juggle.at
  47. * Date: 30.06.2011
  48. */
  49. public class ColorLayoutDocumentBuilder extends AbstractDocumentBuilder {
  50. private Logger logger = Logger.getLogger(getClass().getName());
  51. public static final int MAX_IMAGE_DIMENSION = 1024;
  52.  
  53. public Document createDocument(BufferedImage image, String identifier) {
  54. assert (image != null);
  55. BufferedImage bimg = image;
  56. // Scaling image is especially with the correlogram features very important!
  57. // All images are scaled to guarantee a certain upper limit for indexing.
  58. if (Math.max(image.getHeight(), image.getWidth()) > MAX_IMAGE_DIMENSION) {
  59. bimg = ImageUtils.scaleImage(image, MAX_IMAGE_DIMENSION);
  60. }
  61. Document doc = null;
  62. logger.finer("Starting extraction from image [ColorLayout - fast].");
  63. ColorLayout vd = new ColorLayout();
  64. vd.extract(bimg);
  65. logger.fine("Extraction finished [ColorLayout - fast].");
  66.  
  67. doc = new Document();
  68. doc.add(new Field(DocumentBuilder.FIELD_NAME_COLORLAYOUT_FAST, vd.getByteArrayRepresentation()));
  69. if (identifier != null)
  70. doc.add(new Field(DocumentBuilder.FIELD_NAME_IDENTIFIER, identifier, Field.Store.YES, Field.Index.NOT_ANALYZED));
  71.  
  72. return doc;
  73. }
  74. }

从源代码来看,其实主要就一个函数:createDocument(BufferedImage image, String identifier),该函数的流程如下所示:

1.如果输入的图像分辨率过大(在这里是大于1024),则将图像缩小。

2.新建一个ColorLayout类型的对象vd。

3.调用vd.extract()提取特征向量。

4.调用vd.getByteArrayRepresentation()获得特征向量。

5.将获得的特征向量加入Document,返回Document。

其实其他方法的DocumentBuilder的实现和颜色直方图的DocumentBuilder差不多。例如CEDDDocumentBuilder的源代码如下所示:

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * ~~~~~~~~~~~~~~~~~~~~
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30. package net.semanticmetadata.lire.impl;
  31.  
  32. import net.semanticmetadata.lire.AbstractDocumentBuilder;
  33. import net.semanticmetadata.lire.DocumentBuilder;
  34. import net.semanticmetadata.lire.imageanalysis.CEDD;
  35. import net.semanticmetadata.lire.utils.ImageUtils;
  36. import org.apache.lucene.document.Document;
  37. import org.apache.lucene.document.Field;
  38.  
  39. import java.awt.image.BufferedImage;
  40. import java.util.logging.Logger;
  41.  
  42. /**
  43. * Provides a faster way of searching based on byte arrays instead of Strings. The method
  44. * {@link net.semanticmetadata.lire.imageanalysis.CEDD#getByteArrayRepresentation()} is used
  45. * to generate the signature of the descriptor much faster.
  46. * User: Mathias Lux, mathias@juggle.at
  47. * Date: 12.03.2010
  48. * Time: 13:21:35
  49. *
  50. * @see GenericFastDocumentBuilder
  51. * @deprecated use GenericFastDocumentBuilder instead.
  52. */
  53. public class CEDDDocumentBuilder extends AbstractDocumentBuilder {
  54. private Logger logger = Logger.getLogger(getClass().getName());
  55. public static final int MAX_IMAGE_DIMENSION = 1024;
  56.  
  57. public Document createDocument(BufferedImage image, String identifier) {
  58. assert (image != null);
  59. BufferedImage bimg = image;
  60. // Scaling image is especially with the correlogram features very important!
  61. // All images are scaled to guarantee a certain upper limit for indexing.
  62. if (Math.max(image.getHeight(), image.getWidth()) > MAX_IMAGE_DIMENSION) {
  63. bimg = ImageUtils.scaleImage(image, MAX_IMAGE_DIMENSION);
  64. }
  65. Document doc = null;
  66. logger.finer("Starting extraction from image [CEDD - fast].");
  67. CEDD vd = new CEDD();
  68. vd.extract(bimg);
  69. logger.fine("Extraction finished [CEDD - fast].");
  70.  
  71. doc = new Document();
  72. doc.add(new Field(DocumentBuilder.FIELD_NAME_CEDD, vd.getByteArrayRepresentation()));
  73. if (identifier != null)
  74. doc.add(new Field(DocumentBuilder.FIELD_NAME_IDENTIFIER, identifier, Field.Store.YES, Field.Index.NOT_ANALYZED));
  75.  
  76. return doc;
  77. }
  78. }

5:提取特征向量[以颜色布局为例]

在上一篇文章中,讲述了建立索引的过程。这里继续上一篇文章的分析。在ColorLayoutDocumentBuilder中,使用了一个类型为ColorLayout的对象vd,并且调用了vd的extract()方法:

  1. ColorLayout vd = new ColorLayout();
  2. vd.extract(bimg);

此外调用了vd的getByteArrayRepresentation()方法:

  1. new Field(DocumentBuilder.FIELD_NAME_COLORLAYOUT_FAST, vd.getByteArrayRepresentation())

在这里我们看一看ColorLayout是个什么类。ColorLayout位于“net.semanticmetadata.lire.imageanalysis”包中,如下图所示:

由图可见,这个包中有很多的类。这些类都是以检索方法的名字命名的。我们要找的ColorLayout类也在其中。看看它的代码吧:

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30. package net.semanticmetadata.lire.imageanalysis;
  31.  
  32. import net.semanticmetadata.lire.imageanalysis.mpeg7.ColorLayoutImpl;
  33. import net.semanticmetadata.lire.utils.SerializationUtils;
  34.  
  35. /**
  36. * Just a wrapper for the use of LireFeature.
  37. * Date: 27.08.2008
  38. * Time: 12:07:38
  39. *
  40. * @author Mathias Lux, mathias@juggle.at
  41. */
  42. public class ColorLayout extends ColorLayoutImpl implements LireFeature {
  43.  
  44. /*
  45. public String getStringRepresentation() {
  46. StringBuilder sb = new StringBuilder(256);
  47. StringBuilder sbtmp = new StringBuilder(256);
  48. for (int i = 0; i < numYCoeff; i++) {
  49. sb.append(YCoeff[i]);
  50. if (i + 1 < numYCoeff) sb.append(' ');
  51. }
  52. sb.append("z");
  53. for (int i = 0; i < numCCoeff; i++) {
  54. sb.append(CbCoeff[i]);
  55. if (i + 1 < numCCoeff) sb.append(' ');
  56. sbtmp.append(CrCoeff[i]);
  57. if (i + 1 < numCCoeff) sbtmp.append(' ');
  58. }
  59. sb.append("z");
  60. sb.append(sbtmp);
  61. return sb.toString();
  62. }
  63.  
  64. public void setStringRepresentation(String descriptor) {
  65. String[] coeffs = descriptor.split("z");
  66. String[] y = coeffs[0].split(" ");
  67. String[] cb = coeffs[1].split(" ");
  68. String[] cr = coeffs[2].split(" ");
  69.  
  70. numYCoeff = y.length;
  71. numCCoeff = Math.min(cb.length, cr.length);
  72.  
  73. YCoeff = new int[numYCoeff];
  74. CbCoeff = new int[numCCoeff];
  75. CrCoeff = new int[numCCoeff];
  76.  
  77. for (int i = 0; i < numYCoeff; i++) {
  78. YCoeff[i] = Integer.parseInt(y[i]);
  79. }
  80. for (int i = 0; i < numCCoeff; i++) {
  81. CbCoeff[i] = Integer.parseInt(cb[i]);
  82. CrCoeff[i] = Integer.parseInt(cr[i]);
  83.  
  84. }
  85. }
  86. */
  87.  
  88. /**
  89. * Provides a much faster way of serialization.
  90. *
  91. * @return a byte array that can be read with the corresponding method.
  92. * @see net.semanticmetadata.lire.imageanalysis.CEDD#setByteArrayRepresentation(byte[])
  93. */
  94. public byte[] getByteArrayRepresentation() {
  95. byte[] result = new byte[2 * 4 + numYCoeff * 4 + 2 * numCCoeff * 4];
  96. System.arraycopy(SerializationUtils.toBytes(numYCoeff), 0, result, 0, 4);
  97. System.arraycopy(SerializationUtils.toBytes(numCCoeff), 0, result, 4, 4);
  98. System.arraycopy(SerializationUtils.toByteArray(YCoeff), 0, result, 8, numYCoeff * 4);
  99. System.arraycopy(SerializationUtils.toByteArray(CbCoeff), 0, result, numYCoeff * 4 + 8, numCCoeff * 4);
  100. System.arraycopy(SerializationUtils.toByteArray(CrCoeff), 0, result, numYCoeff * 4 + numCCoeff * 4 + 8, numCCoeff * 4);
  101. return result;
  102. }
  103.  
  104. /**
  105. * Reads descriptor from a byte array. Much faster than the String based method.
  106. *
  107. * @param in byte array from corresponding method
  108. * @see net.semanticmetadata.lire.imageanalysis.CEDD#getByteArrayRepresentation
  109. */
  110. public void setByteArrayRepresentation(byte[] in) {
  111. int[] data = SerializationUtils.toIntArray(in);
  112. numYCoeff = data[0];
  113. numCCoeff = data[1];
  114. YCoeff = new int[numYCoeff];
  115. CbCoeff = new int[numCCoeff];
  116. CrCoeff = new int[numCCoeff];
  117. System.arraycopy(data, 2, YCoeff, 0, numYCoeff);
  118. System.arraycopy(data, 2 + numYCoeff, CbCoeff, 0, numCCoeff);
  119. System.arraycopy(data, 2 + numYCoeff + numCCoeff, CrCoeff, 0, numCCoeff);
  120. }
  121.  
  122. public double[] getDoubleHistogram() {
  123. double[] result = new double[numYCoeff + numCCoeff * 2];
  124. for (int i = 0; i < numYCoeff; i++) {
  125. result[i] = YCoeff[i];
  126. }
  127. for (int i = 0; i < numCCoeff; i++) {
  128. result[i + numYCoeff] = CbCoeff[i];
  129. result[i + numCCoeff + numYCoeff] = CrCoeff[i];
  130. }
  131. return result;
  132. }
  133.  
  134. /**
  135. * Compares one descriptor to another.
  136. *
  137. * @param descriptor
  138. * @return the distance from [0,infinite) or -1 if descriptor type does not match
  139. */
  140.  
  141. public float getDistance(LireFeature descriptor) {
  142. if (!(descriptor instanceof ColorLayoutImpl)) return -1f;
  143. ColorLayoutImpl cl = (ColorLayoutImpl) descriptor;
  144. return (float) ColorLayoutImpl.getSimilarity(YCoeff, CbCoeff, CrCoeff, cl.YCoeff, cl.CbCoeff, cl.CrCoeff);
  145. }
  146. }

ColorLayout类继承了ColorLayoutImpl类,同时实现了LireFeature接口。其中的方法大部分都是实现了LireFeature接口的方法。先来看看LireFeature接口是什么样子的:

注:这里没有注释了,仅能靠自己的理解了。

  1. /**
  2. * This is the basic interface for all content based features. It is needed for GenericDocumentBuilder etc.
  3. * Date: 28.05.2008
  4. * Time: 14:44:16
  5. *
  6. * @author Mathias Lux, mathias@juggle.at
  7. */
  8. public interface LireFeature {
  9. public void extract(BufferedImage bimg);
  10.  
  11. public byte[] getByteArrayRepresentation();
  12.  
  13. public void setByteArrayRepresentation(byte[] in);
  14.  
  15. public double[] getDoubleHistogram();
  16.  
  17. float getDistance(LireFeature feature);
  18.  
  19. java.lang.String getStringRepresentation();
  20.  
  21. void setStringRepresentation(java.lang.String s);
  22. }

我简要概括一下自己对这些接口函数的理解:

1.extract(BufferedImage bimg):提取特征向量

2.getByteArrayRepresentation():获取特征向量(返回byte[]类型)

3.setByteArrayRepresentation(byte[] in):设置特征向量(byte[]类型)

4.getDoubleHistogram():

5.getDistance(LireFeature feature):

6.getStringRepresentation():获取特征向量(返回String类型)

7.setStringRepresentation(java.lang.String s):设置特征向量(String类型)

其中咖啡色的是建立索引的过程中会用到的。

看代码的过程中发现,所有的算法都实现了LireFeature接口,如下图所示:

不再研究LireFeature接口,回过头来本来想看看ColorLayoutImpl类,但是没想到代码其长无比,都是些算法,暂时没有这个耐心了,以后有机会再看吧。以下贴出个简略版的。注意:该类中实现了extract(BufferedImage bimg)方法。其他方法例如getByteArrayRepresentation()则在ColorLayout中实现。

  1. package net.semanticmetadata.lire.imageanalysis.mpeg7;
  2.  
  3. import java.awt.image.BufferedImage;
  4. import java.awt.image.WritableRaster;
  5.  
  6. /**
  7. * Class for extrcating & comparing MPEG-7 based CBIR descriptor ColorLayout
  8. *
  9. * @author Mathias Lux, mathias@juggle.at
  10. */
  11. public class ColorLayoutImpl {
  12. // static final boolean debug = true;
  13. protected int[][] shape;
  14. protected int imgYSize, imgXSize;
  15. protected BufferedImage img;
  16.  
  17. protected static int[] availableCoeffNumbers = {1, 3, 6, 10, 15, 21, 28, 64};
  18.  
  19. public int[] YCoeff;
  20. public int[] CbCoeff;
  21. public int[] CrCoeff;
  22.  
  23. protected int numCCoeff = 28, numYCoeff = 64;
  24.  
  25. protected static int[] arrayZigZag = {
  26. 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
  27. 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
  28. 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
  29. 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
  30. };
  31.  
  32. ...
  33. public void extract(BufferedImage bimg) {
  34. this.img = bimg;
  35. imgYSize = img.getHeight();
  36. imgXSize = img.getWidth();
  37. init();
  38. }
  39. ...
  40. }

6:检索(ImageSearcher)[以颜色布局为例]

前几篇文章介绍了LIRe 的基本接口,以及建立索引的过程。现在来看一看它的检索部分(ImageSearcher)。不同的方法的检索功能的类各不相同,它们都位于“net.semanticmetadata.lire.impl”中,如下图所示:

在这里仅分析一个比较有代表性的:颜色布局。前文已经分析过ColorLayoutDocumentBuilder,在这里我们分析一下ColorLayoutImageSearcher。源代码如下:

  1. /*
  2. * This file is part of the LIRe project: http://www.semanticmetadata.net/lire
  3. * LIRe is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * LIRe is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with LIRe; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * We kindly ask you to refer the following paper in any publication mentioning Lire:
  18. *
  19. * Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval 鈥�
  20. * An Extensible Java CBIR Library. In proceedings of the 16th ACM International
  21. * Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
  22. *
  23. * http://doi.acm.org/10.1145/1459359.1459577
  24. *
  25. * Copyright statement:
  26. * --------------------
  27. * (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
  28. * http://www.semanticmetadata.net/lire
  29. */
  30. package net.semanticmetadata.lire.impl;
  31.  
  32. import net.semanticmetadata.lire.DocumentBuilder;
  33. import net.semanticmetadata.lire.ImageDuplicates;
  34. import net.semanticmetadata.lire.ImageSearchHits;
  35. import net.semanticmetadata.lire.imageanalysis.ColorLayout;
  36. import net.semanticmetadata.lire.imageanalysis.LireFeature;
  37. import org.apache.lucene.document.Document;
  38. import org.apache.lucene.index.IndexReader;
  39.  
  40. import java.io.FileNotFoundException;
  41. import java.io.IOException;
  42. import java.util.HashMap;
  43. import java.util.LinkedList;
  44. import java.util.List;
  45. import java.util.logging.Level;
  46.  
  47. /**
  48. * Provides a faster way of searching based on byte arrays instead of Strings. The method
  49. * {@link net.semanticmetadata.lire.imageanalysis.ColorLayout#getByteArrayRepresentation()} is used
  50. * to generate the signature of the descriptor much faster. First tests have shown that this
  51. * implementation is up to 4 times faster than the implementation based on strings
  52. * (for 120,000 images)
  53. * <p/>
  54. * User: Mathias Lux, mathias@juggle.at
  55. * Date: 30.06 2011
  56. */
  57. public class ColorLayoutImageSearcher extends GenericImageSearcher {
  58. public ColorLayoutImageSearcher(int maxHits) {
  59. super(maxHits, ColorLayout.class, DocumentBuilder.FIELD_NAME_COLORLAYOUT_FAST);
  60. }
  61.  
  62. protected float getDistance(Document d, LireFeature lireFeature) {
  63. float distance = 0f;
  64. ColorLayout lf;
  65. try {
  66. lf = (ColorLayout) descriptorClass.newInstance();
  67. byte[] cls = d.getBinaryValue(fieldName);
  68. if (cls != null && cls.length > 0) {
  69. lf.setByteArrayRepresentation(cls);
  70. distance = lireFeature.getDistance(lf);
  71. } else {
  72. logger.warning("No feature stored in this document ...");
  73. }
  74. } catch (InstantiationException e) {
  75. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  76. } catch (IllegalAccessException e) {
  77. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  78. }
  79.  
  80. return distance;
  81. }
  82.  
  83. public ImageSearchHits search(Document doc, IndexReader reader) throws IOException {
  84. SimpleImageSearchHits searchHits = null;
  85. try {
  86. ColorLayout lireFeature = (ColorLayout) descriptorClass.newInstance();
  87.  
  88. byte[] cls = doc.getBinaryValue(fieldName);
  89. if (cls != null && cls.length > 0)
  90. lireFeature.setByteArrayRepresentation(cls);
  91. float maxDistance = findSimilar(reader, lireFeature);
  92.  
  93. searchHits = new SimpleImageSearchHits(this.docs, maxDistance);
  94. } catch (InstantiationException e) {
  95. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  96. } catch (IllegalAccessException e) {
  97. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  98. }
  99. return searchHits;
  100. }
  101.  
  102. public ImageDuplicates findDuplicates(IndexReader reader) throws IOException {
  103. // get the first document:
  104. SimpleImageDuplicates simpleImageDuplicates = null;
  105. try {
  106. if (!IndexReader.indexExists(reader.directory()))
  107. throw new FileNotFoundException("No index found at this specific location.");
  108. Document doc = reader.document(0);
  109.  
  110. ColorLayout lireFeature = (ColorLayout) descriptorClass.newInstance();
  111. byte[] cls = doc.getBinaryValue(fieldName);
  112. if (cls != null && cls.length > 0)
  113. lireFeature.setByteArrayRepresentation(cls);
  114.  
  115. HashMap<Float, List<String>> duplicates = new HashMap<Float, List<String>>();
  116.  
  117. // find duplicates ...
  118. boolean hasDeletions = reader.hasDeletions();
  119.  
  120. int docs = reader.numDocs();
  121. int numDuplicates = 0;
  122. for (int i = 0; i < docs; i++) {
  123. if (hasDeletions && reader.isDeleted(i)) {
  124. continue;
  125. }
  126. Document d = reader.document(i);
  127. float distance = getDistance(d, lireFeature);
  128.  
  129. if (!duplicates.containsKey(distance)) {
  130. duplicates.put(distance, new LinkedList<String>());
  131. } else {
  132. numDuplicates++;
  133. }
  134. duplicates.get(distance).add(d.getFieldable(DocumentBuilder.FIELD_NAME_IDENTIFIER).stringValue());
  135. }
  136.  
  137. if (numDuplicates == 0) return null;
  138.  
  139. LinkedList<List<String>> results = new LinkedList<List<String>>();
  140. for (float f : duplicates.keySet()) {
  141. if (duplicates.get(f).size() > 1) {
  142. results.add(duplicates.get(f));
  143. }
  144. }
  145. simpleImageDuplicates = new SimpleImageDuplicates(results);
  146. } catch (InstantiationException e) {
  147. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  148. } catch (IllegalAccessException e) {
  149. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  150. }
  151. return simpleImageDuplicates;
  152.  
  153. }
  154. }

源代码里面重要的函数有3个:

float getDistance(Document d, LireFeature lireFeature):

ImageSearchHits search(Document doc, IndexReader reader):检索。最核心函数。

ImageDuplicates findDuplicates(IndexReader reader):目前还没研究。

在这里忽然发现了一个问题:这里竟然只有一个Search()?!应该是有参数不同的3个Search()才对啊......

经过研究后发现,ColorLayoutImageSearcher继承了一个类——GenericImageSearcher,而不是继承 AbstractImageSearcher。Search()方法的实现是在GenericImageSearcher中实现的。看来这个 ColorLayoutImageSearcher还挺特殊的啊......

看一下GenericImageSearcher的源代码:

  1. package net.semanticmetadata.lire.impl;
  2.  
  3. import net.semanticmetadata.lire.AbstractImageSearcher;
  4. import net.semanticmetadata.lire.DocumentBuilder;
  5. import net.semanticmetadata.lire.ImageDuplicates;
  6. import net.semanticmetadata.lire.ImageSearchHits;
  7. import net.semanticmetadata.lire.imageanalysis.LireFeature;
  8. import net.semanticmetadata.lire.utils.ImageUtils;
  9. import org.apache.lucene.document.Document;
  10. import org.apache.lucene.index.IndexReader;
  11.  
  12. import java.awt.image.BufferedImage;
  13. import java.io.FileNotFoundException;
  14. import java.io.IOException;
  15. import java.util.HashMap;
  16. import java.util.LinkedList;
  17. import java.util.List;
  18. import java.util.TreeSet;
  19. import java.util.logging.Level;
  20. import java.util.logging.Logger;
  21.  
  22. /**
  23. * This file is part of the Caliph and Emir project: http://www.SemanticMetadata.net
  24. * <br>Date: 01.02.2006
  25. * <br>Time: 00:17:02
  26. *
  27. * @author Mathias Lux, mathias@juggle.at
  28. */
  29. public class GenericImageSearcher extends AbstractImageSearcher {
  30. protected Logger logger = Logger.getLogger(getClass().getName());
  31. Class<?> descriptorClass;
  32. String fieldName;
  33.  
  34. private int maxHits = 10;
  35. protected TreeSet<SimpleResult> docs;
  36.  
  37. public GenericImageSearcher(int maxHits, Class<?> descriptorClass, String fieldName) {
  38. this.maxHits = maxHits;
  39. docs = new TreeSet<SimpleResult>();
  40. this.descriptorClass = descriptorClass;
  41. this.fieldName = fieldName;
  42. }
  43.  
  44. public ImageSearchHits search(BufferedImage image, IndexReader reader) throws IOException {
  45. logger.finer("Starting extraction.");
  46. LireFeature lireFeature = null;
  47. SimpleImageSearchHits searchHits = null;
  48. try {
  49. lireFeature = (LireFeature) descriptorClass.newInstance();
  50. // Scaling image is especially with the correlogram features very important!
  51. BufferedImage bimg = image;
  52. if (Math.max(image.getHeight(), image.getWidth()) > GenericDocumentBuilder.MAX_IMAGE_DIMENSION) {
  53. bimg = ImageUtils.scaleImage(image, GenericDocumentBuilder.MAX_IMAGE_DIMENSION);
  54. }
  55. lireFeature.extract(bimg);
  56. logger.fine("Extraction from image finished");
  57.  
  58. float maxDistance = findSimilar(reader, lireFeature);
  59. searchHits = new SimpleImageSearchHits(this.docs, maxDistance);
  60. } catch (InstantiationException e) {
  61. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  62. } catch (IllegalAccessException e) {
  63. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  64. }
  65. return searchHits;
  66. }
  67.  
  68. /**
  69. * @param reader
  70. * @param lireFeature
  71. * @return the maximum distance found for normalizing.
  72. * @throws java.io.IOException
  73. */
  74. protected float findSimilar(IndexReader reader, LireFeature lireFeature) throws IOException {
  75. float maxDistance = -1f, overallMaxDistance = -1f;
  76. boolean hasDeletions = reader.hasDeletions();
  77.  
  78. // clear result set ...
  79. docs.clear();
  80.  
  81. int docs = reader.numDocs();
  82. for (int i = 0; i < docs; i++) {
  83. // bugfix by Roman Kern
  84. if (hasDeletions && reader.isDeleted(i)) {
  85. continue;
  86. }
  87.  
  88. Document d = reader.document(i);
  89. float distance = getDistance(d, lireFeature);
  90. assert (distance >= 0);
  91. // calculate the overall max distance to normalize score afterwards
  92. if (overallMaxDistance < distance) {
  93. overallMaxDistance = distance;
  94. }
  95. // if it is the first document:
  96. if (maxDistance < 0) {
  97. maxDistance = distance;
  98. }
  99. // if the array is not full yet:
  100. if (this.docs.size() < maxHits) {
  101. this.docs.add(new SimpleResult(distance, d));
  102. if (distance > maxDistance) maxDistance = distance;
  103. } else if (distance < maxDistance) {
  104. // if it is nearer to the sample than at least on of the current set:
  105. // remove the last one ...
  106. this.docs.remove(this.docs.last());
  107. // add the new one ...
  108. this.docs.add(new SimpleResult(distance, d));
  109. // and set our new distance border ...
  110. maxDistance = this.docs.last().getDistance();
  111. }
  112. }
  113. return maxDistance;
  114. }
  115.  
  116. protected float getDistance(Document d, LireFeature lireFeature) {
  117. float distance = 0f;
  118. LireFeature lf;
  119. try {
  120. lf = (LireFeature) descriptorClass.newInstance();
  121. String[] cls = d.getValues(fieldName);
  122. if (cls != null && cls.length > 0) {
  123. lf.setStringRepresentation(cls[0]);
  124. distance = lireFeature.getDistance(lf);
  125. } else {
  126. logger.warning("No feature stored in this document!");
  127. }
  128. } catch (InstantiationException e) {
  129. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  130. } catch (IllegalAccessException e) {
  131. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  132. }
  133.  
  134. return distance;
  135. }
  136.  
  137. public ImageSearchHits search(Document doc, IndexReader reader) throws IOException {
  138. SimpleImageSearchHits searchHits = null;
  139. try {
  140. LireFeature lireFeature = (LireFeature) descriptorClass.newInstance();
  141.  
  142. String[] cls = doc.getValues(fieldName);
  143. if (cls != null && cls.length > 0)
  144. lireFeature.setStringRepresentation(cls[0]);
  145. float maxDistance = findSimilar(reader, lireFeature);
  146.  
  147. searchHits = new SimpleImageSearchHits(this.docs, maxDistance);
  148. } catch (InstantiationException e) {
  149. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  150. } catch (IllegalAccessException e) {
  151. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  152. }
  153. return searchHits;
  154. }
  155.  
  156. public ImageDuplicates findDuplicates(IndexReader reader) throws IOException {
  157. // get the first document:
  158. SimpleImageDuplicates simpleImageDuplicates = null;
  159. try {
  160. if (!IndexReader.indexExists(reader.directory()))
  161. throw new FileNotFoundException("No index found at this specific location.");
  162. Document doc = reader.document(0);
  163.  
  164. LireFeature lireFeature = (LireFeature) descriptorClass.newInstance();
  165. String[] cls = doc.getValues(fieldName);
  166. if (cls != null && cls.length > 0)
  167. lireFeature.setStringRepresentation(cls[0]);
  168.  
  169. HashMap<Float, List<String>> duplicates = new HashMap<Float, List<String>>();
  170.  
  171. // find duplicates ...
  172. boolean hasDeletions = reader.hasDeletions();
  173.  
  174. int docs = reader.numDocs();
  175. int numDuplicates = 0;
  176. for (int i = 0; i < docs; i++) {
  177. if (hasDeletions && reader.isDeleted(i)) {
  178. continue;
  179. }
  180. Document d = reader.document(i);
  181. float distance = getDistance(d, lireFeature);
  182.  
  183. if (!duplicates.containsKey(distance)) {
  184. duplicates.put(distance, new LinkedList<String>());
  185. } else {
  186. numDuplicates++;
  187. }
  188. duplicates.get(distance).add(d.getFieldable(DocumentBuilder.FIELD_NAME_IDENTIFIER).stringValue());
  189. }
  190.  
  191. if (numDuplicates == 0) return null;
  192.  
  193. LinkedList<List<String>> results = new LinkedList<List<String>>();
  194. for (float f : duplicates.keySet()) {
  195. if (duplicates.get(f).size() > 1) {
  196. results.add(duplicates.get(f));
  197. }
  198. }
  199. simpleImageDuplicates = new SimpleImageDuplicates(results);
  200. } catch (InstantiationException e) {
  201. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  202. } catch (IllegalAccessException e) {
  203. logger.log(Level.SEVERE, "Error instantiating class for generic image searcher: " + e.getMessage());
  204. }
  205. return simpleImageDuplicates;
  206.  
  207. }
  208.  
  209. public String toString() {
  210. return "GenericSearcher using " + descriptorClass.getName();
  211. }
  212.  
  213. }

下面来看看GenericImageSearcher中的search(BufferedImage image, IndexReader reader)函数的步骤(注:这个函数应该是用的最多的,输入一张图片,返回相似图片的结果集):

1.输入图片如果尺寸过大(大于1024),则调整尺寸。

2.使用extract()提取输入图片的特征值。

3.根据提取的特征值,使用findSimilar()查找相似的图片。

4.新建一个ImageSearchHits用于存储查找的结果。

5.返回ImageSearchHits

在这里要注意一点:

GenericImageSearcher中创建特定方法的类的时候,使用了如下形式:

  1. LireFeature lireFeature = (LireFeature) descriptorClass.newInstance();

即接口的方式,而不是直接新建一个对象的方式,形如:

  1. AutoColorCorrelogram acc = new AutoColorCorrelogram(CorrelogramDocumentBuilder.MAXIMUM_DISTANCE)

相比而言,更具有通用型。

在search()函数中,调用了一个函数findSimilar()。这个函数的作用是查找相似图片的,分析了一下它的步骤:

1.使用IndexReader获取所有的记录

2.遍历所有的记录,和当前输入的图片进行比较,使用getDistance()函数

3.获取maxDistance并返回

在findSimilar()中,又调用了一个getDistance(),该函数调用了具体检索方法的getDistance()函数。

下面我们来看一下ColorLayout类中的getDistance()函数:

  1. public float getDistance(LireFeature descriptor) {
  2. if (!(descriptor instanceof ColorLayoutImpl)) return -1f;
  3. ColorLayoutImpl cl = (ColorLayoutImpl) descriptor;
  4. return (float) ColorLayoutImpl.getSimilarity(YCoeff, CbCoeff, CrCoeff, cl.YCoeff, cl.CbCoeff, cl.CrCoeff);
  5. }

发现其调用了ColorLayoutImpl类中的getSimilarity()函数:

  1. public static double getSimilarity(int[] YCoeff1, int[] CbCoeff1, int[] CrCoeff1, int[] YCoeff2, int[] CbCoeff2, int[] CrCoeff2) {
  2. int numYCoeff1, numYCoeff2, CCoeff1, CCoeff2, YCoeff, CCoeff;
  3.  
  4. //Numbers of the Coefficients of two descriptor values.
  5. numYCoeff1 = YCoeff1.length;
  6. numYCoeff2 = YCoeff2.length;
  7. CCoeff1 = CbCoeff1.length;
  8. CCoeff2 = CbCoeff2.length;
  9.  
  10. //take the minimal Coeff-number
  11. YCoeff = Math.min(numYCoeff1, numYCoeff2);
  12. CCoeff = Math.min(CCoeff1, CCoeff2);
  13.  
  14. setWeightingValues();
  15.  
  16. int j;
  17. int[] sum = new int[3];
  18. int diff;
  19. sum[0] = 0;
  20.  
  21. for (j = 0; j < YCoeff; j++) {
  22. diff = (YCoeff1[j] - YCoeff2[j]);
  23. sum[0] += (weightMatrix[0][j] * diff * diff);
  24. }
  25.  
  26. sum[1] = 0;
  27. for (j = 0; j < CCoeff; j++) {
  28. diff = (CbCoeff1[j] - CbCoeff2[j]);
  29. sum[1] += (weightMatrix[1][j] * diff * diff);
  30. }
  31.  
  32. sum[2] = 0;
  33. for (j = 0; j < CCoeff; j++) {
  34. diff = (CrCoeff1[j] - CrCoeff2[j]);
  35. sum[2] += (weightMatrix[2][j] * diff * diff);
  36. }
  37.  
  38. //returns the distance between the two desciptor values
  39.  
  40. return Math.sqrt(sum[0] * 1.0) + Math.sqrt(sum[1] * 1.0) + Math.sqrt(sum[2] * 1.0);
  41. }

由代码可见,getSimilarity()通过具体的算法,计算两张图片特征向量之间的相似度。

7:算法类[以颜色布局为例]

前面关于LIRe的文章,介绍的都是架构方面的东西,没有细研究具体算法。本文以颜色布局为例,介绍一下算法类的实现。

颜色布局描述符以一种非常紧密的形式有效地表示了图像的颜色空间分布信息。它以非常小的计算代价, 带来高的检索效率。因此, 颜色布局特征在视频镜头关键帧提取中有很重要的意义。颜色布局提取方法如下:

1 将图像从RGB 空间映射到YCbCr空间, 映射公式为

Y= 0.299* R + 0.587* G + 0.114* B

Cb= - 0.169* R – 0.331* G + 0.500* B

Cr = 0.500* R –0.419* G – 0.081* B

2 将整幅图像分成64块, 每块尺寸为(W /8) *(H/8), 其中W 为整幅图像的宽度, H 为整幅图像的高度, 计算每一块中所有像素的各个颜色分量( Y, Cb, Cr )的平均值, 并以此作为该块的代表颜色( Y, Cb, Cr );

3 对帧图像中各块的颜色分量平均值进行DCT 变换, 得到各分量的一系列DCT 系数;

4 对各分量的DCT 系数, 通过之字形扫描和量化, 取出各自DCT 变换的低频分量, 这三组低频分量共同构成该帧图像的颜色布局描述符。

颜色布局算法的实现位于ColorLayoutImpl类中,该类处于“net.semanticmetadata.lire.imageanalysis.mpeg7”包中,如图所示:

ColorLayoutImpl类的代码量很大,很多地方都还没有研究,在这里仅展示部分已经看过的代码:

  1. /* 雷霄骅
  2. * 中国传媒大学/数字电视技术
  3. * leixiaohua1020@126.com
  4. *
  5. */
  6. /**
  7. * Class for extrcating & comparing MPEG-7 based CBIR descriptor ColorLayout
  8. *
  9. * @author Mathias Lux, mathias@juggle.at
  10. */
  11. public class ColorLayoutImpl {
  12. // static final boolean debug = true;
  13. protected int[][] shape;
  14. protected int imgYSize, imgXSize;
  15. protected BufferedImage img;
  16.  
  17. protected static int[] availableCoeffNumbers = {1, 3, 6, 10, 15, 21, 28, 64};
  18. //特征向量(Y,Cb,Cr)
  19. public int[] YCoeff;
  20. public int[] CbCoeff;
  21. public int[] CrCoeff;
  22. //特征向量的大小
  23. protected int numCCoeff = 28, numYCoeff = 64;
  24.  
  25. protected static int[] arrayZigZag = {
  26. 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
  27. 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
  28. 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
  29. 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
  30. };
  31.  
  32. protected static double[][] arrayCosin = {
  33. ...
  34. };
  35. protected static int[][] weightMatrix = new int[3][64];
  36. protected BufferedImage colorLayoutImage;
  37.  
  38. /**
  39. * init used by all constructors
  40. */
  41. private void init() {
  42. shape = new int[3][64];
  43. YCoeff = new int[64];
  44. CbCoeff = new int[64];
  45. CrCoeff = new int[64];
  46. colorLayoutImage = null;
  47. extract();
  48. }
  49.  
  50. public void extract(BufferedImage bimg) {
  51. this.img = bimg;
  52. imgYSize = img.getHeight();
  53. imgXSize = img.getWidth();
  54. init();
  55. }
  56.  
  57. private void createShape() {
  58. int y_axis, x_axis;
  59. int i, k, x, y, j;
  60. long[][] sum = new long[3][64];
  61. int[] cnt = new int[64];
  62. double yy = 0.0;
  63. int R, G, B;
  64.  
  65. //init of the blocks
  66. for (i = 0; i < 64; i++) {
  67. cnt[i] = 0;
  68. sum[0][i] = 0;
  69. sum[1][i] = 0;
  70. sum[2][i] = 0;
  71. shape[0][i] = 0;
  72. shape[1][i] = 0;
  73. shape[2][i] = 0;
  74. }
  75.  
  76. WritableRaster raster = img.getRaster();
  77. int[] pixel = {0, 0, 0};
  78. for (y = 0; y < imgYSize; y++) {
  79. for (x = 0; x < imgXSize; x++) {
  80. raster.getPixel(x, y, pixel);
  81. R = pixel[0];
  82. G = pixel[1];
  83. B = pixel[2];
  84.  
  85. y_axis = (int) (y / (imgYSize / 8.0));
  86. x_axis = (int) (x / (imgXSize / 8.0));
  87.  
  88. k = (y_axis << 3) + x_axis;
  89.  
  90. //RGB to YCbCr, partition and average-calculation
  91. yy = (0.299 * R + 0.587 * G + 0.114 * B) / 256.0;
  92. sum[0][k] += (int) (219.0 * yy + 16.5); // Y
  93. sum[1][k] += (int) (224.0 * 0.564 * (B / 256.0 * 1.0 - yy) + 128.5); // Cb
  94. sum[2][k] += (int) (224.0 * 0.713 * (R / 256.0 * 1.0 - yy) + 128.5); // Cr
  95. cnt[k]++;
  96. }
  97. }
  98.  
  99. for (i = 0; i < 8; i++) {
  100. for (j = 0; j < 8; j++) {
  101. for (k = 0; k < 3; k++) {
  102. if (cnt[(i << 3) + j] != 0)
  103. shape[k][(i << 3) + j] = (int) (sum[k][(i << 3) + j] / cnt[(i << 3) + j]);
  104. else
  105. shape[k][(i << 3) + j] = 0;
  106. }
  107. }
  108. }
  109. }
  110.  
  111. ......(其他代码都已经省略)
  112.  
  113. private int extract() {
  114.  
  115. createShape();
  116.  
  117. Fdct(shape[0]);
  118. Fdct(shape[1]);
  119. Fdct(shape[2]);
  120.  
  121. YCoeff[0] = quant_ydc(shape[0][0] >> 3) >> 1;
  122. CbCoeff[0] = quant_cdc(shape[1][0] >> 3);
  123. CrCoeff[0] = quant_cdc(shape[2][0] >> 3);
  124.  
  125. //quantization and zig-zagging
  126. for (int i = 1; i < 64; i++) {
  127. YCoeff[i] = quant_ac((shape[0][(arrayZigZag[i])]) >> 1) >> 3;
  128. CbCoeff[i] = quant_ac(shape[1][(arrayZigZag[i])]) >> 3;
  129. CrCoeff[i] = quant_ac(shape[2][(arrayZigZag[i])]) >> 3;
  130. }
  131.  
  132. setYCoeff(YCoeff);
  133. setCbCoeff(CbCoeff);
  134. setCrCoeff(CrCoeff);
  135. return 0;
  136. }
  137.  
  138. /**
  139. * Takes two ColorLayout Coeff sets and calculates similarity.
  140. *
  141. * @return -1.0 if data is not valid.
  142. */
  143. public static double getSimilarity(int[] YCoeff1, int[] CbCoeff1, int[] CrCoeff1, int[] YCoeff2, int[] CbCoeff2, int[] CrCoeff2) {
  144. int numYCoeff1, numYCoeff2, CCoeff1, CCoeff2, YCoeff, CCoeff;
  145.  
  146. //Numbers of the Coefficients of two descriptor values.
  147. numYCoeff1 = YCoeff1.length;
  148. numYCoeff2 = YCoeff2.length;
  149. CCoeff1 = CbCoeff1.length;
  150. CCoeff2 = CbCoeff2.length;
  151.  
  152. //take the minimal Coeff-number
  153. YCoeff = Math.min(numYCoeff1, numYCoeff2);
  154. CCoeff = Math.min(CCoeff1, CCoeff2);
  155.  
  156. setWeightingValues();
  157.  
  158. int j;
  159. int[] sum = new int[3];
  160. int diff;
  161. sum[0] = 0;
  162.  
  163. for (j = 0; j < YCoeff; j++) {
  164. diff = (YCoeff1[j] - YCoeff2[j]);
  165. sum[0] += (weightMatrix[0][j] * diff * diff);
  166. }
  167.  
  168. sum[1] = 0;
  169. for (j = 0; j < CCoeff; j++) {
  170. diff = (CbCoeff1[j] - CbCoeff2[j]);
  171. sum[1] += (weightMatrix[1][j] * diff * diff);
  172. }
  173.  
  174. sum[2] = 0;
  175. for (j = 0; j < CCoeff; j++) {
  176. diff = (CrCoeff1[j] - CrCoeff2[j]);
  177. sum[2] += (weightMatrix[2][j] * diff * diff);
  178. }
  179.  
  180. //returns the distance between the two desciptor values
  181.  
  182. return Math.sqrt(sum[0] * 1.0) + Math.sqrt(sum[1] * 1.0) + Math.sqrt(sum[2] * 1.0);
  183. }
  184.  
  185. public int getNumberOfCCoeff() {
  186. return numCCoeff;
  187. }
  188.  
  189. public void setNumberOfCCoeff(int numberOfCCoeff) {
  190. this.numCCoeff = numberOfCCoeff;
  191. }
  192.  
  193. public int getNumberOfYCoeff() {
  194. return numYCoeff;
  195. }
  196.  
  197. public void setNumberOfYCoeff(int numberOfYCoeff) {
  198. this.numYCoeff = numberOfYCoeff;
  199. }
  200.  
  201. public String getStringRepresentation() {
  202. StringBuilder sb = new StringBuilder(256);
  203. StringBuilder sbtmp = new StringBuilder(256);
  204. for (int i = 0; i < numYCoeff; i++) {
  205. sb.append(YCoeff[i]);
  206. if (i + 1 < numYCoeff) sb.append(' ');
  207. }
  208. sb.append("z");
  209. for (int i = 0; i < numCCoeff; i++) {
  210. sb.append(CbCoeff[i]);
  211. if (i + 1 < numCCoeff) sb.append(' ');
  212. sbtmp.append(CrCoeff[i]);
  213. if (i + 1 < numCCoeff) sbtmp.append(' ');
  214. }
  215. sb.append("z");
  216. sb.append(sbtmp);
  217. return sb.toString();
  218. }
  219.  
  220. public void setStringRepresentation(String descriptor) {
  221. String[] coeffs = descriptor.split("z");
  222. String[] y = coeffs[0].split(" ");
  223. String[] cb = coeffs[1].split(" ");
  224. String[] cr = coeffs[2].split(" ");
  225.  
  226. numYCoeff = y.length;
  227. numCCoeff = Math.min(cb.length, cr.length);
  228.  
  229. YCoeff = new int[numYCoeff];
  230. CbCoeff = new int[numCCoeff];
  231. CrCoeff = new int[numCCoeff];
  232.  
  233. for (int i = 0; i < numYCoeff; i++) {
  234. YCoeff[i] = Integer.parseInt(y[i]);
  235. }
  236. for (int i = 0; i < numCCoeff; i++) {
  237. CbCoeff[i] = Integer.parseInt(cb[i]);
  238. CrCoeff[i] = Integer.parseInt(cr[i]);
  239.  
  240. }
  241. }
  242.  
  243. public int[] getYCoeff() {
  244. return YCoeff;
  245. }
  246.  
  247. public int[] getCbCoeff() {
  248. return CbCoeff;
  249. }
  250.  
  251. public int[] getCrCoeff() {
  252. return CrCoeff;
  253. }
  254. }

下面介绍几个主要的函数:

提取:

1.extract(BufferedImage bimg):提取特征向量的函数,里面调用了init()。

2.init():初始化了 YCoeff,CbCoeff, CrCoeff。调用extract()(注意这个extract()是没有参数的)

3.extract():完成了提取特征向量的过程,其中调用了createShape()。

4.createShape():未研究。

获取/设置特征向量(注意:有参数为String和byte[]两种类型的特征向量,按照原代码里的说法,byte[]的效率要高一些):

1.getStringRepresentation():获取特征向量

2.setStringRepresentation():设置特征向量

计算相似度:

getSimilarity(int[] YCoeff1, int[] CbCoeff1, int[] CrCoeff1, int[] YCoeff2, int[] CbCoeff2, int[] CrCoeff2)

主要的变量:

3个存储特征向量(Y,Cb,Cr)的数组:

  1. public int[] YCoeff;
  2. public int[] CbCoeff;
  3. public int[] CrCoeff;

特征向量的大小:

  1. protected int numCCoeff = 28, numYCoeff = 64;

转:LIRe 源代码分析的更多相关文章

  1. LIRe 源代码分析 7:算法类[以颜色布局为例]

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  2. LIRe 源代码分析 6:检索(ImageSearcher)[以颜色布局为例]

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  3. LIRe 源代码分析 5:提取特征向量[以颜色布局为例]

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  4. LIRe 源代码分析 4:建立索引(DocumentBuilder)[以颜色布局为例]

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  5. LIRe 源代码分析 3:基本接口(ImageSearcher)

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  6. LIRe 源代码分析 2:基本接口(DocumentBuilder)

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  7. LIRe 源代码分析 1:整体结构

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  8. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  9. Twitter Storm源代码分析之ZooKeeper中的目录结构

    徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...

随机推荐

  1. LeetCode(二)

    实现Trie树 class TrieNode { public char val; public boolean isWord; public TrieNode[] children = new Tr ...

  2. mybatis 打印sql 语句

    拦截器 package com.cares.asis.mybatis.interceptor; import java.text.DateFormat; import java.util.Date; ...

  3. MySQL日期时间函数大全(转)

    MySQL日期时间函数大全 DAYOFWEEK(date)  返回日期date是星期几(1=星期天,2=星期一,……7=星期六,ODBC标准)mysql> select DAYOFWEEK('1 ...

  4. HTTP学习笔记(1)ULR语法

    1,概述 当你打开一个浏览器则会进入一个主页,也许你会想只是打开了浏览器罢了,但是浏览器默默的把这个主页默认的网址发送出去,你只是不知道而已,你只是没有输入而已.我们来做个实验. 1,双击打开 2,可 ...

  5. (转)mvc Area相关技术

    转自: http://www.cnblogs.com/zgqys1980/archive/2012/08/22/2650774.html ASP.NET MVC中,是依靠某些文件夹以及类的固定命名规则 ...

  6. java复习基础知识——java保留字

    ava 关键字列表 (依字母排序 共51组): abstract, assert,boolean, break, byte, case, catch, char, class, const, cont ...

  7. CF - 405B - Domino Effect

    my english is poor 给你一列全部站立的骨牌,同时向左或向右推其中的几个 保证推得方式为: ...左,右,左,右,左... 即不存在两个相邻的又往相同方向推倒的 刚开始是从左往右一个一 ...

  8. Linux挂载磁盘

    查看 fdisk –l 挂载 mount 磁盘 目录 参考地址:http://blog.csdn.net/tianlesoftware/article/details/5642883 卸载 umoun ...

  9. URAL - 1917 Titan Ruins: Deadly Accuracy(水题)

    水题一个,代码挫了一下: 题意不好理解. 你去一个洞窟内探险,洞窟内有许多宝石,但都有魔法守护,你需要用魔法将它们打下来. 每个宝石都有自己的防御等级,当你的魔法超过它的防御等级时它就会被你打下来. ...

  10. 【书海】《Head First Java》 ——读后总结

    <Head First Java> 中文版 (第二版) IT`huhui前言录 <Head First Java>这本书我不算特别细的看了一遍.认为十分适合初学者,甚至是没接触 ...