/*
* This file is part of the LIRe project: http://lire-project.net
* LIRe is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* LIRe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LIRe; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* We kindly ask you to refer the following paper in any publication mentioning Lire:
*
* Lux Mathias, Savvas A. Chatzichristofis. Lire: Lucene Image Retrieval –
* An Extensible Java CBIR Library. In proceedings of the 16th ACM International
* Conference on Multimedia, pp. 1085-1088, Vancouver, Canada, 2008
*
* http://doi.acm.org/10.1145/1459359.1459577
*
* Copyright statement:
* ~~~~~~~~~~~~~~~~~~~~
* (c) 2002-2011 by Mathias Lux (mathias@juggle.at)
* http://lire-project.net
*/
package liredemo.indexing;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.exif.ExifThumbnailDirectory;
import net.semanticmetadata.lire.builders.DocumentBuilder;
import org.apache.lucene.document.Document;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* ...
* Date: 10.06.2008
* Time: 17:24:32
*
* @author Mathias Lux, mathias@juggle.at
*/
public class ParallelIndexer implements Runnable {
// Vectors are already synchronized, so that's the cheap solution.
Vector<String> imageFiles;
private int NUMBER_OF_SYNC_THREADS = 8;
Hashtable<String, Boolean> indexThreads = new Hashtable<String, Boolean>(8);
DocumentBuilder builder;
Vector<Document> finished = new Vector<Document>();
private boolean started = false;
private final ExecutorService pool;
private int countImagesOut = 0;
private int countImagesProcesses = 0;
public ParallelIndexer(List<String> imageFiles, DocumentBuilder b) {
this.imageFiles = new Vector<String>();
assert (imageFiles != null);
this.imageFiles.addAll(imageFiles);
builder = b;
pool = Executors.newFixedThreadPool(NUMBER_OF_SYNC_THREADS);
}
public void run() {
for (int i = 1; i < NUMBER_OF_SYNC_THREADS; i++) {
PhotoIndexer runnable = new PhotoIndexer(this);
// Thread t = new Thread(runnable);
// t.start();
// indexThreads.put(t.getName(), false);
pool.submit(runnable);
}
started = true;
// while (started) {
// try {
// pool.awaitTermination(15, TimeUnit.MINUTES);
// started = false;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
public void addDoc(Document doc, String photofile) {
if (doc != null) finished.add(doc);
Thread.yield();
}
public Document getNext() {
if (imageFiles.size() < 1) {
boolean fb = true;
for (String t : indexThreads.keySet()) {
fb = fb && indexThreads.get(t);
}
if (started && fb) {
return null;
}
}
while (finished.size() < 1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return finished.remove(0);
}
private String getNextImage() {
if (imageFiles.size() > 0) {
countImagesOut++;
return imageFiles.remove(0);
} else {
System.out.println("countImagesOut = " + countImagesOut);
return null;
}
}
class PhotoIndexer implements Runnable {
String photo;
ParallelIndexer parent;
private boolean hasFinished = false;
PhotoIndexer(ParallelIndexer parent) {
this.parent = parent;
}
public void run() {
parent.indexThreads.put(Thread.currentThread().getName(), false);
while ((photo = parent.getNextImage()) != null) {
try {
BufferedImage image = readFile(photo);
if (image != null) {
Document doc = parent.builder.createDocument(image, photo);
parent.addDoc(doc, photo);
}
} catch (Exception e) {
e.printStackTrace();
parent.addDoc(null, photo);
}
}
parent.indexThreads.put(Thread.currentThread().getName(), true);
}
private BufferedImage readFile(String path) throws IOException {
BufferedImage image = null;
if (path.toLowerCase().endsWith(".jpg")) {
FileInputStream jpegFile = new FileInputStream(path);
try {
Metadata metadata = ImageMetadataReader.readMetadata(jpegFile);
ExifThumbnailDirectory tDir = metadata.getFirstDirectoryOfType(ExifThumbnailDirectory.class);
// byte[] thumb = ((ExifDirectory) metadata.getDirectory(ExifDirectory.class)).getThumbnailData();
// if (thumb != null) image = ImageIO.read(new ByteArrayInputStream(thumb));
// System.out.print("Read from thumbnail data ... ");
// System.out.println(image.getWidth() + " x " + image.getHeight());
} catch (JpegProcessingException e) {
System.err.println("Could not extract EXIF data for " + path);
System.err.println("\t" + e.getMessage());
} catch (Exception e) {
System.err.println("Could not extract EXIF data for " + path);
System.err.println("\t" + e.getMessage());
}
jpegFile.close(); // patch by Simon Micollier
}
// Fallback & PNGs:
if (image == null)
try {
image = ImageIO.read(new File(path));
} catch (Exception e) {
System.err.println("Error reading file " + path + "\n\t" + e.getMessage());
e.printStackTrace();
}
return image;
}
}
}