8

フォルダーにいくつか (38000) の画像/ビデオ ファイルがあります。これらの約 40% は、私が取り除こうとしている重複です。私の質問は、2 つのファイルが同一かどうかをどのように判断できますか? これまでのところ、ファイルの SHA1 を使用しようとしましたが、多くの重複ファイルのハッシュが異なることがわかりました。これは私が使用していたコードです:

public static String getHash(File doc) {
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA1");
        FileInputStream inStream = new FileInputStream(doc);
        DigestInputStream dis = new DigestInputStream(inStream, md);
        BufferedInputStream bis = new BufferedInputStream(dis);
        while (true) {
            int b = bis.read();
            if (b == -1)
                break;
        }

        inStream.close();
        dis.close();
        bis.close();
    } catch (NoSuchAlgorithmException | IOException e) {
        e.printStackTrace();
    }

    BigInteger bi = new BigInteger(md.digest());

    return bi.toString(16);
}

これを何らかの方法で変更できますか? または、別の方法を使用する必要がありますか?

4

7 に答える 7

6

上で概説したように、重複検出はハッシュに基づくことができます。ただし、ほぼ重複検出が必要な場合、つまり、基本的に同じものを表示しているが、拡大縮小、回転などされている画像を検索している場合は、コンテンツ ベースの画像検索アプローチが必要になる場合があります。そのための Java ライブラリである LIRE ( https://code.google.com/p/lire/ ) があり、ダウンロード セクションに「SimpleApplication」があります。次にできることは、

  1. 最初の画像にインデックスを付ける
  2. 次の画像に移動します
  3. インデックスで I を検索
  4. スコアがしきい値を下回る結果がある場合は、それらを重複としてマークします
  5. 索引Ⅰ
  6. (2)へ

私の学生はそれを行いましたが、うまくいきましたが、手元にソースコードがありません。でも安心してください。ほんの数行で、簡単なアプリケーションからすぐに始められます。

于 2013-07-09T07:28:56.443 に答える
4

ハッシュを使用する以外に、複製のサイズが異なる場合 (サイズが変更されたため)、ピクセルごとに比較できます (画像全体ではなく、画像のサブセクションである可能性があります)。

これは画像形式に依存する場合がありますが、高さと幅を比較して比較し、RGB コードを使用してピクセルごとに比較することができます。より効率的にするために、比較のしきい値を決定できます。例えば:

public class Main {
    public static void main(String[] args) throws IOException {
        ImageChecker i = new ImageChecker();
        BufferedImage one = ImageIO.read(new File("D:/Images/460249177.jpg"));
        BufferedImage two = ImageIO.read(new File("D:/Images/460249177a.jpg"));
        if(one.getWidth() + one.getHeight() >= two.getWidth() + two.getHeight()) {
            i.setOne(one);
            i.setTwo(two);
        } else {
            i.setOne(two);
            i.setTwo(one);
        }
        System.out.println(i.compareImages());
    }
}

public class ImageChecker {

    private BufferedImage one;
    private BufferedImage two;
    private double difference = 0;
    private int x = 0;
    private int y = 0;

    public ImageChecker() {

    }

    public boolean compareImages() {
        int f = 20;
        int w1 = Math.min(50, one.getWidth() - two.getWidth());
        int h1 = Math.min(50, one.getHeight() - two.getHeight());
        int w2 = Math.min(5, one.getWidth() - two.getWidth());
        int h2 = Math.min(5, one.getHeight() - two.getHeight());
        for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
            for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
                compareSubset(i, j, f);
            }
        }

        one = one.getSubimage(Math.max(0, x - w1), Math.max(0, y - h1),
                Math.min(two.getWidth() + w1, one.getWidth() - x + w1),
                Math.min(two.getHeight() + h1, one.getHeight() - y + h1));
        x = 0;
        y = 0;
        difference = 0;
        f = 5;
        for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
            for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
                compareSubset(i, j, f);
            }
        }
        one = one.getSubimage(Math.max(0, x - w2), Math.max(0, y - h2),
                Math.min(two.getWidth() + w2, one.getWidth() - x + w2),
                Math.min(two.getHeight() + h2, one.getHeight() - y + h2));
        f = 1;
        for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
            for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
                compareSubset(i, j, f);
            }
        }
        System.out.println(difference);
        return difference < 0.1;
    }

    public void compareSubset(int a, int b, int f) {
        double diff = 0;
        for (int i = 0; i < two.getWidth(); i += f) {
            for (int j = 0; j < two.getHeight(); j += f) {
                int onepx = one.getRGB(i + a, j + b);
                int twopx = two.getRGB(i, j);
                int r1 = (onepx >> 16);
                int g1 = (onepx >> 8) & 0xff;
                int b1 = (onepx) & 0xff;
                int r2 = (twopx >> 16);
                int g2 = (twopx >> 8) & 0xff;
                int b2 = (twopx) & 0xff;
                diff += (Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1
                        - b2)) / 3.0 / 255.0;
            }
        }
        double percentDiff = diff * f * f / (two.getWidth() * two.getHeight());
        if (percentDiff < difference || difference == 0) {
            difference = percentDiff;
            x = a;
            y = b;
        }
    }

    public BufferedImage getOne() {
        return one;
    }

    public void setOne(BufferedImage one) {
        this.one = one;
    }

    public BufferedImage getTwo() {
        return two;
    }

    public void setTwo(BufferedImage two) {
        this.two = two;
    }
}
于 2016-03-18T22:39:26.493 に答える
1

たとえば、imagemagick convertを使用して、ファイルを正規表現と可能な限り少ないメタデータを持つ形式に変換できます。私はPNMを使うと思います。だから、次のようなことを試してください:

convert input.png pnm:- | md5sum -

これにより、以前に比較された 2 つのファイルで同じ結果が得られる場合、実際にはメタデータが問題の原因であり、このようなコマンド ライン アプローチを使用するか、コードを更新して画像を読み取ってハッシュを計算することができます。生の非圧縮データから。

一方、異なるファイルの比較が依然として異なる場合は、実際の画像データにいくつかの変更があります。考えられる原因の 1 つは、特にここで PNG を扱っている場合、アルファ チャネルの追加または削除である可能性があります。一方、JPEG では、画像を圧縮解除してから再度圧縮する可能性が高く、わずかな変更やデータの損失につながります。JPEG は本質的に非可逆コーデックであり、同じアプリケーション (またはライブラリ) を使用して同じ設定で同じ入力データから作成されたものでない限り、2 つの画像は異なる可能性があります。その場合、ファジー画像マッチングを実行する必要があります。Geeqieのようなツールは、そのようなことを実行できます。これを自分で行う場合は、多くの作業が必要になるため、事前に調査を行う必要があります。

于 2013-06-24T22:07:20.867 に答える