colorfingerprint.java
来自「dump3 morpheus 0.2.9 src」· Java 代码 · 共 420 行
JAVA
420 行
/**
* DuMP3 version morpheus_0.2.9 - a duplicate/similar file finder in Java<BR>
* Copyright 2005 Alexander Grässer<BR>
* All Rights Reserved, http://dump3.sourceforge.net/<BR>
* <BR>
* This file is part of DuMP3.<BR>
* <BR>
* DuMP3 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.<BR>
* <BR>
* DuMP3 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.<BR>
* <BR>
* You should have received a copy of the GNU General Public License along with DuMP3; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.za.grasser.duplicate.fingerprint;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
import net.za.grasser.duplicate.file.FingerprintFile;
import net.za.grasser.duplicate.file.Status;
import net.za.grasser.duplicate.fingerprint.configure.ColorFingerprintConfig;
import net.za.grasser.duplicate.fingerprint.configure.ConfigFactory;
import net.za.grasser.duplicate.util.HexArray;
import org.apache.log4j.Logger;
/**
* REF: <a href="http://www.vldb.org/conf/2001/P221.pdf">An Extendible Hash for Multi-Precision Similarity Querying of Image Databases</a>, S. Lin, M. T. Ozsu,
* V. Oria, R. Ng, 2001<BR>
*
* @author <a href="http://sourceforge.net/sendmessage.php?touser=733840">pyropunk at sourceforge dot net</a>
* @version $Revision: 1.13 $
* @modelguid {AF9A68AD-CFA1-4F86-AF1F-CE02CB6EAC57}
*/
public class ColorFingerprint extends AbstractFingerprint {
/**
* <code>log</code> ColorFingerprint -
*/
private static final Logger log = Logger.getLogger(ColorFingerprint.class);
/**
* This class defines an entry in the rgb picture list
*
* @author <a href="http://sourceforge.net/sendmessage.php?touser=733840">pyropunk at sourceforge dot net</a>
*/
static class Entry {
/**
* TODO greyscale comparison
*
* @param rgb1
* @param rgb2
* @return distance between 2 RGB colors
*/
static double distance(final int rgb1, final int rgb2) {
final int r = (rgb1 >> 16 & 0xFF) - (rgb2 >> 16 & 0xFF);
final int g = (rgb1 >> 8 & 0xFF) - (rgb2 >> 8 & 0xFF);
final int b = (rgb1 & 0xFF) - (rgb2 & 0xFF);
return Math.sqrt(r * r + g * g + b * b);
}
/**
* <code>width</code> Entry -
*/
byte width;
/**
* <code>height</code> Entry -
*/
byte height;
/**
* <code>rgb</code> Entry -
*/
int[] rgb;
/**
* @param pW
* @param pH
* @param pRgb
*/
Entry(final int pW, final int pH, final int[] pRgb) {
width = (byte)pW;
height = (byte)pH;
rgb = pRgb;
}
/**
* @param arg0
* @return average distance between 2 color matixes
*/
public double compareTo(final Object arg0) {
if (arg0 instanceof Entry) {
final Entry ent = (Entry)arg0;
if (ent.height == height && ent.width == width || ent.height == width && ent.width == height) {
if (width == 1 && height == 1) {
return distance(rgb[0], ent.rgb[0]);
}
// test 2 rotations
double r1 = 0.0;
double r2 = 0.0;
if (ent.height == height && ent.width == width) {
for (int i = 0; i < rgb.length; i++) {
r1 += distance(rgb[i], ent.rgb[i]);
r2 += distance(rgb[i], ent.rgb[rgb.length - i - 1]);
}
} else {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
r1 += distance(rgb[y * width + x], ent.rgb[x * height + height - y - 1]);
r2 += distance(rgb[y * width + x], ent.rgb[(width - x - 1) * height + y]);
}
}
}
r1 = Math.min(r1, r2);
if (width == height) {
// 2 more rotations must be tested
double r3 = 0.0;
double r4 = 0.0;
for (int y = 0; y < width; y++) {
for (int x = 0; x < width; x++) {
r3 += distance(rgb[y * width + x], ent.rgb[x * width + width - 1 - y]);
r4 += distance(rgb[y * width + x], ent.rgb[(width - x - 1) * width + y]);
}
}
r1 = Math.min(r1, Math.min(r3, r4));
}
// average
return r1 / rgb.length;
}
throw new IllegalArgumentException("Invalid width and height");
}
throw new IllegalArgumentException("Incorrect Type");
}
}
static {
final Set<String> set = new HashSet<String>();
final String[] as = ImageIO.getWriterFormatNames();
for (final String element : as) {
set.add(element.toUpperCase());
}
String s = "[";
for (final String lString : set) {
s += lString + ", ";
}
if (s.endsWith(", ")) {
s = s.substring(0, s.length() - 2);
}
s += ']';
log.info("Supported file formats:" + s);
}
/**
* <code>config</code> ColorFingerprint -
*/
private static final ColorFingerprintConfig config = (ColorFingerprintConfig)ConfigFactory.getConfig(ColorFingerprint.class);
/**
* <code>MAX_DIST</code> ColorFingerprint - maximum distance in 24bit RGB is from black to white
*/
private static final double MAX_DIST = Math.sqrt(255 * 255 * 3);
/**
* @param pIn
* @return ArrayList of Entry
*/
private static ArrayList<Entry> makeFingerlistFromPrint(final byte[] pIn) {
final ArrayList<Entry> ret = new ArrayList<Entry>(4);
int j = 0;
while (j < pIn.length) {
final int w = pIn[j++];
final int h = pIn[j++];
if (w == 0 || h == 0) {
break;
}
ret.add(new Entry(w, h, HexArray.makeInts(pIn, j, w * h * 3, null, 0, 3)));
j += w * h * 3;
}
return ret;
}
/**
* @param pIn
* @return fingerprint
*/
private static byte[] makeFingerprintFromList(final ArrayList<Entry> pIn) {
int j = 0;
final byte[] fp = new byte[((int)Math.pow(4.0, config.getSubdivide()) - 1 + (config.getSubdivide() << 1))];
for (int i = 0; i < config.getSubdivide(); i++) {
final Entry e = pIn.get(i);
fp[j++] = e.width;
fp[j++] = e.height;
HexArray.makeBytes(e.rgb, 0, e.rgb.length, fp, j, 3);
j += e.rgb.length * 3;
}
return fp;
}
/**
* <code>scaled_rgbs</code> ColorFingerprint - ArrayList of Entry
*/
private ArrayList<Entry> scaled_rgbs = null;
/**
* @param fi FingerprintFile
* @modelguid {1953E44F-41DC-4AEF-84B8-13F9C64ACCFD}
*/
public ColorFingerprint(final FingerprintFile fi) {
super(fi);
}
/**
* This method adds additional info to the FingerprintFile
*/
@Override
public void addInfo() {
super.addInfo();
try {
final BufferedImage image = ImageIO.read(new File(getFile().getPath()));
final Map<String, String> map = getFile().getInfo();
map.put("width", String.valueOf(image.getWidth()));
map.put("height", String.valueOf(image.getHeight()));
map.put("color depth", String.valueOf(image.getColorModel().getPixelSize()));
switch (image.getTransparency()) {
case Transparency.BITMASK:
map.put("transparency", "bit mask");
break;
case Transparency.TRANSLUCENT:
map.put("transparency", "translucent");
break;
case Transparency.OPAQUE:
default:
map.put("transparency", "opaque");
break;
}
final String[] props = image.getPropertyNames();
if (props != null) {
for (final String lString : props) {
map.put(lString, image.getProperty(lString).toString());
}
}
} catch (final Throwable t) {
log.error("Could not get info", t);
}
}
/**
* @param pPF
* @return ratio of similarity
*/
private float compare(final ColorFingerprint pPF) {
try {
double dist = 0.0;
for (int i = 0; i < config.getSubdivide(); i++) {
final Entry a = scaled_rgbs.get(i);
final Entry b = pPF.scaled_rgbs.get(i);
// different size fingerprints are ok - return last calculated distance
if (a == null || b == null) {
return (float)(100.0 - dist * 100.0 / MAX_DIST);
}
dist = a.compareTo(b);
if (dist > (100.0f - config.getSimilarityThreshhold()) / 100.0f * MAX_DIST) {
return (float)(100.0 - dist * 100.0 / MAX_DIST);
// return 0;
}
}
// return (float)(100 - (dist * (distance / 100.0)));
return (float)(100.0 - dist * 100.0 / MAX_DIST);
} catch (final IllegalArgumentException iae) {
return 0.0f;
}
}
/**
* @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#getClassName()
*/
@Override
public String getClassName() {
String cls = super.getClassName();
switch (config.getScalingAlgorithm()) {
default:
case AffineTransformOp.TYPE_NEAREST_NEIGHBOR:
cls += "-NN";
break;
case AffineTransformOp.TYPE_BILINEAR:
cls += "-BL";
break;
case AffineTransformOp.TYPE_BICUBIC:
cls += "-BC";
break;
}
return cls + '-' + (config.isKeepaspect() ? "ASP" : "STR");
}
/**
* @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#getSimilarityThreshhold()
*/
@Override
public float getSimilarityThreshhold() {
return config.getSimilarityThreshhold();
}
/**
* @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#makeFingerprintFromFile()
*/
@Override
protected void makeFingerprintFromFile() {
try {
final BufferedImage image = ImageIO.read(new File(getFile().getPath()));
preRead();
int parts = 0;
for (int i = 0; i < config.getSubdivide(); i++) {
parts = (int)Math.pow(2.0, i);
final BufferedImage sc = scale(image, parts);
final int[] argbs = new int[(sc.getWidth() * sc.getHeight())];
sc.getRGB(0, 0, sc.getWidth(), sc.getHeight(), argbs, 0, sc.getWidth());
scaled_rgbs.add(new Entry(sc.getWidth(), sc.getHeight(), argbs));
}
// make the byte [] fingerprint
postRead();
} catch (final Exception e) {
getFile().setStatus(Status.FILE_CORRUPT);
fingerprint = null;
log.warn("error while reading file [" + getFile().getPath() + "]", e);
}
}
/**
* @param fi AbstractFingerprint
* @return boolean
* @modelguid {BC0953BE-AD94-4207-A1B8-E5F7FAD5DCE7}
*/
@Override
public float matches(final AbstractFingerprint fi) {
final FingerprintFile ff1 = getFile();
final FingerprintFile ff2 = fi.getFile();
if (ff1.getStatus() != Status.FILE_OK && ff1.getStatus() != Status.FILE_SIGNATURE_MISMATCH || ff2.getStatus() != Status.FILE_OK
&& ff2.getStatus() != Status.FILE_SIGNATURE_MISMATCH) {
return 0.0f;
}
if (ff1.getLength() == 0L && ff2.getLength() == 0L) {
return 100.0f;
}
if (fi instanceof ColorFingerprint) {
final ColorFingerprint pf = (ColorFingerprint)fi;
// try to read the files
getFingerprint();
pf.getFingerprint();
if ((ff1.getStatus() == Status.FILE_OK || ff1.getStatus() == Status.FILE_SIGNATURE_MISMATCH)
&& (ff2.getStatus() == Status.FILE_OK || ff2.getStatus() == Status.FILE_SIGNATURE_MISMATCH)) {
if (scaled_rgbs == null) {
scaled_rgbs = makeFingerlistFromPrint(getFingerprint());
}
if (pf.scaled_rgbs == null) {
pf.scaled_rgbs = makeFingerlistFromPrint(pf.getFingerprint());
}
return compare(pf);
}
}
return 0.0f;
}
/**
* @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#postRead()
*/
@Override
protected void postRead() {
fingerprint = makeFingerprintFromList(scaled_rgbs);
}
/**
* @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#preRead()
*/
@Override
protected void preRead() {
scaled_rgbs = new ArrayList<Entry>(config.getSubdivide());
}
/**
* @param bufiSource
* @param parts
* @return a scaled images
*/
private BufferedImage scale(final BufferedImage bufiSource, final int parts) {
double scaleX = 0.0;
double scaleY = 0.0;
final int w = bufiSource.getWidth();
final int h = bufiSource.getHeight();
if (config.isKeepaspect() && parts > 2) {
if (w < h) {
scaleY = (double)parts / (double)h;
scaleX = scaleY;
} else {
scaleX = (double)parts / (double)w;
scaleY = scaleX;
}
} else {
scaleX = (double)parts / (double)w;
scaleY = (double)parts / (double)h;
}
final AffineTransform tx = new AffineTransform();
tx.scale(scaleX, scaleY);
final AffineTransformOp op = new AffineTransformOp(tx, config.getScalingAlgorithm());
return op.filter(bufiSource, null);
}
/**
* @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#update(byte[], int)
*/
@Override
protected void update(final byte[] pBuffer, final int pLength) {
// not used since we are doing our own reading
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?