import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.PrintWriter;
import java.io.ByteArrayInputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
public class Images {
private static BufferedImage scale(BufferedImage img, int width, int height) {
BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = temp.createGraphics();
g.scale(1.0*width/img.getWidth(), 1.0*height/img.getHeight());
g.drawImage(img, 0, 0, null);
g.dispose();
return temp;
}
private static int[] splitRGB(int rgb) {
return new int[] {(rgb>>>16)&0xFF, (rgb>>>8)&0xFF, rgb&0xFF};
}
private static double[] averageBrightness(int[] data) {
double[] b = {0, 0, 0};
for(int i = 0; i < data.length; i++) {
int[] pixel = splitRGB(data[i]);
b[0] += pixel[0];
b[1] += pixel[1];
b[2] += pixel[2];
}
b[0] /= data.length;
b[1] /= data.length;
b[2] /= data.length;
return b;
}
private static double[] averageDifference(int[] data1, int[] data2) {
double[] diff = {0,0,0};
for(int i = 0; i < data1.length; i++) {
int[] pixel1 = splitRGB(data1[i]);
int[] pixel2 = splitRGB(data2[i]);
diff[0] += Math.abs(pixel1[0]-pixel2[0]);
diff[1] += Math.abs(pixel1[1]-pixel2[1]);
diff[2] += Math.abs(pixel1[2]-pixel2[2]);
}
diff[0] /= data1.length;
diff[1] /= data1.length;
diff[2] /= data1.length;
return diff;
}
private static int[] getData(BufferedImage image) {
return image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
}
private static int images = 0;
private static synchronized void updateImageCounter() {
images++;
if(images%100 == 0) System.out.print(".");
}
private static synchronized void updateImageCounter(int n) {
for(int i = 0; i < n; i++) updateImageCounter();
}
private static synchronized byte[] readAllBytes(File file) {
try {
return Files.readAllBytes(file.toPath());
} catch(Exception e) {
return new byte[0];
}
}
private static BufferedImage readImage(File file) {
try {
byte[] array = readAllBytes(file);
return ImageIO.read(new ByteArrayInputStream(array));
} catch(Exception e) {
return null;
}
}
private static ArrayList<File> findAllPictures(File path) {
ArrayList<File> list = new ArrayList<>();
if(path == null) return list;
File[] files = path.listFiles();
if(files == null) return list;
for(File file: files) {
if(file.isDirectory()) list.addAll(findAllPictures(file));
else if(file.isFile()) {
String name = file.getName();
if(name.endsWith(".jpg") || name.endsWith(".jpeg") ||
name.endsWith(".png") || name.endsWith(".gif") ||
name.endsWith(".bmp")) {
list.add(file);
updateImageCounter();
}
}
}
return list;
}
private static <T> List<List<T>> splitList(List<T> list, int n) {
int chunkSize = list.size()/n + 1;
ArrayList<List<T>> lists = new ArrayList<>();
for(int start = 0; start < list.size(); start += chunkSize) {
int end = start+chunkSize;
if(end > list.size()) end = list.size();
lists.add(list.subList(start, end));
}
return lists;
}
static class FileAB {
final File file;
final double[] ab;
FileAB(File file, double[] ab) {
this.file = file;
this.ab = ab;
}
@Override
public String toString() {
return "(" + ab[0] + ", " + ab[1] + ", " + ab[2] + ") " + file.getName();
}
}
static class CompRed implements Comparator<FileAB> {
final double value;
CompRed(double value) {
this.value = value;
}
public int compare(FileAB a, FileAB b) {
double va = Math.abs(a.ab[0]-value);
double vb = Math.abs(b.ab[0]-value);
if(va < vb) return -1;
else if(va > vb) return 1;
else return 0;
}
}
static class CompGreen implements Comparator<FileAB> {
final double value;
CompGreen(double value) {
this.value = value;
}
public int compare(FileAB a, FileAB b) {
double va = Math.abs(a.ab[1]-value);
double vb = Math.abs(b.ab[1]-value);
if(va < vb) return -1;
else if(va > vb) return 1;
else return 0;
}
}
static class CompBlue implements Comparator<FileAB> {
final double value;
CompBlue(double value) {
this.value = value;
}
public int compare(FileAB a, FileAB b) {
double va = Math.abs(a.ab[2]-value);
double vb = Math.abs(b.ab[2]-value);
if(va < vb) return -1;
else if(va > vb) return 1;
else return 0;
}
}
private static List<FileAB> calcBrightness(List<File> pictures) {
ArrayList<FileAB> result = new ArrayList<>();
for(File file: pictures) {
double[] b = null;
try {
BufferedImage image = readImage(file);
int[] data = getData(image);
b = averageBrightness(data);
} catch(Exception e) {
}
if(b != null) {
result.add(new FileAB(file, b));
}
}
return result;
}
private static List<List<FileAB>> findMatchingPairs(List<FileAB> list) {
ArrayList<List<FileAB>> result = new ArrayList<>();
ArrayList<int[]> data = new ArrayList<>();
for(FileAB fab: list) {
BufferedImage image = readImage(fab.file);
image = scale(image, 100, 100);
data.add(getData(image));
}
boolean[] isUsed = new boolean[data.size()];
for(int k = 0; k < data.size(); k++) {
if(isUsed[k]) continue;
ArrayList<FileAB> matches = new ArrayList<>();
for(int l = k+1; l < data.size(); l++) {
if(isUsed[l]) continue;
double[] diff = averageDifference(data.get(k), data.get(l));
if(diff[0] < 20 && diff[1] < 20 && diff[2] < 20) {
matches.add(list.get(l));
isUsed[l] = true;
}
}
if(!matches.isEmpty()) {
matches.add(list.get(k));
result.add(matches);
}
}
return result;
}
public static void main(String[] args) throws Exception {
long start = System.nanoTime();
System.out.println("Suche Bilder:");
ArrayList<File> pictures = findAllPictures(new File("e:/downloads"));
System.out.println();
System.out.println(pictures.size() + " Bilder gefunden");
images = 0;
System.out.println();
System.out.println("Berechne durchschnittliche Helligkeit:");
ArrayList<FileAB> picturesPlusAB = new ArrayList<>();
ExecutorService threadPool = Executors.newFixedThreadPool(8);
{
List<List<File>> picturesSplit = splitList(pictures, 256);
ArrayList<Future<List<FileAB>>> futures = new ArrayList<>();
for(List<File> _picturesChunk: picturesSplit) {
final List<File> picturesChunk = _picturesChunk;
futures.add(threadPool.submit(new Callable<List<FileAB>>() {
public List<FileAB> call() {
List<FileAB> result = calcBrightness(picturesChunk);
updateImageCounter(result.size());
return result;
}
}));
}
for(Future<List<FileAB>> future: futures) {
picturesPlusAB.addAll(future.get());
}
}
Collections.sort(picturesPlusAB, new CompRed(0));
images = 0;
System.out.println(); System.out.println();
System.out.println("Vergleiche Bilder:");
ArrayList<Future<List<List<FileAB>>>> futures = new ArrayList<>();
for(int i = 0; i < picturesPlusAB.size();) {
double[] ab = picturesPlusAB.get(i).ab;
int end = i+1;
while(end < picturesPlusAB.size() && Math.abs(picturesPlusAB.get(end).ab[0] - ab[0]) < 2) end++;
List<FileAB> subList1 = picturesPlusAB.subList(i, end);
Collections.sort(subList1, new CompGreen(ab[1]));
end = 0;
while(end < subList1.size() && Math.abs(subList1.get(end).ab[1] - ab[1]) < 2) end++;
List<FileAB> subList2 = subList1.subList(0, end);
Collections.sort(subList2, new CompBlue(ab[2]));
end = 0;
while(end < subList2.size() && Math.abs(subList2.get(end).ab[2] - ab[2]) < 2) end++;
final List<FileAB> subList3 = subList2.subList(0, end);
if(!subList3.isEmpty()) {
futures.add(threadPool.submit(new Callable<List<List<FileAB>>>() {
public List<List<FileAB>> call() {
List<List<FileAB>> result = findMatchingPairs(subList3);
updateImageCounter(subList3.size());
return result;
}
}));
}
i += subList3.size();
subList1 = subList1.subList(subList3.size(), subList1.size());
Collections.sort(subList1, new CompRed(0));
}
PrintWriter out = new PrintWriter(new File("out.txt"));
for(Future<List<List<FileAB>>> future: futures) {
for(List<FileAB> list: future.get()) {
for(FileAB fab: list) {
out.println(fab.file.getPath());
}
out.println("--------------------------------------------------------------------------");
}
}
out.flush();
out.close();
threadPool.shutdown();
long end = System.nanoTime();
double time = (end-start)/1e9;
System.out.println(); System.out.println();
System.out.println("time = " + time);
}
}