Color Coherence VectorをJavaで実装してみた

アンデックス ヴァイスビア

こちらで紹介されてたアルゴリズムJavaで実装してみました。
Color Coherence Vectorを実装してみた


詳しい説明はあちらを見てもらうとして、前処理をした画像はこんな感じ


結果はこんな感じ。ここでは、横を200ドットに制限して、20ドットを閾値にαとβを計算してます。

0 ( 33, 123)
1 ( 2, 110)
4 ( 0, 17)
5 ( 12, 78)
16 ( 2, 64)
17 ( 0, 3)
20 ( 11, 52)
21 ( 44, 236)
22 ( 7, 200)
25 ( 0, 3)
26 ( 10, 184)
37 ( 0, 14)
38 ( 0, 26)
41 ( 0, 3)
42 ( 50, 211)
43 ( 14, 80)
46 ( 0, 1)
47 ( 6, 68)
58 ( 0, 18)
59 ( 0, 28)
62 ( 0, 5)
63 ( 7, 119)


これを使って画像間の距離を求めると、似たような画像が検出できるかも、ってわけですね。
ソースはここから

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;

public class ColorCoherenceVectorFrame {
    public static void main(String[] args) throws IOException{
        JFrame f = new JFrame("CCV");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new GridLayout(3, 1));
        f.setSize(600, 400);

        JLabel l1 = new JLabel();
        JLabel l2 = new JLabel();
        JLabel l3 = new JLabel();
        f.add(l1);
        f.add(l2);
        f.add(l3);

        BufferedImage imgsrc = ImageIO.read(new File("hagi.jpg"));
        int w = imgsrc.getWidth();
        int h = imgsrc.getHeight();
        //サイズ正規化
        int limit = 200;
        if(w < h){
            w = w * limit / h;
            h = limit;
        }else{
            h = h * limit / w;
            w = limit;
        }
        BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D grp = (Graphics2D) img.getGraphics();
        grp.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        grp.drawImage(imgsrc, 0, 0, w, h, null);
        grp.dispose();

        l1.setIcon(new ImageIcon(img));

        //ガウシアンフィルタ
        int[] ctemp = img.getRGB(0, 0, w, h, null, 0, w);
        int[] ctbl = new int[ctemp.length];
        int[][] filter = {
            {1, 2, 1},
            {2, 4, 2},
            {1, 2, 1}};
        for(int y = 0; y < h; ++y){
            for(int x = 0; x < w; ++x){
                int tr = 0;
                int tg = 0;
                int tb = 0;
                int t = 0;
                for(int i = -1; i < 2; ++i){
                    for(int j = -1; j < 2; ++j){
                        if(y + i < 0) continue;
                        if(x + j < 0) continue;
                        if(y + i >= h) continue;
                        if(x + j >= w) continue;
                        t += filter[i + 1][j + 1];
                        int adr = (x + j) + (y + i) * w;
                        tr += filter[i + 1][j + 1] * ((ctemp[adr] >> 16) & 255);
                        tg += filter[i + 1][j + 1] * ((ctemp[adr] >> 8)  & 255);
                        tb += filter[i + 1][j + 1] * ( ctemp[adr]        & 255);
                    }
                }
                ctbl[x + y * w] = ((tr / t) << 16) + ((tg / t) << 8) + tb / t;
            }
        }
        img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        img.setRGB(0, 0, w, h, ctbl, 0, w);
        l2.setIcon(new ImageIcon(img));

        //減色
        for(int i = 0; i < ctbl.length; ++i){
            int r = (ctemp[i] >> 16) & 192;
            int g = (ctemp[i] >> 8) & 192;
            int b = ctemp[i] & 192;
            ctbl[i] = (r << 16) + (g << 8) + b;
        }
        img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        img.setRGB(0, 0, w, h, ctbl, 0, w);
        l3.setIcon(new ImageIcon(img));

        //タグ付け
        int[][] lbl = new int[w][h];
        int id = 0;
        for(int y = 0; y < h; ++y){
            for(int x = 0; x < w; ++x){
                int col = ctbl[y * w + x];
                if(y > 0){
                    if(x > 0){
                        if(ctbl[(y - 1) * w + x - 1] == col){
                            //左上と一緒
                            lbl[x][y] = lbl[x - 1][y - 1];
                            continue;
                        }
                    }
                    if(ctbl[(y - 1) * w + x] == col){
                        //上と一緒
                        lbl[x][y] = lbl[x][y - 1];
                        continue;
                    }
                    if(x < w - 1){
                        if(ctbl[(y - 1) * w + x + 1] == col){
                            //右上と一緒
                            lbl[x][y] = lbl[x + 1][y - 1];
                            continue;
                        }
                    }
                }
                if(x > 0){
                    if(ctbl[y * w + x - 1] == col){
                        //左と一緒
                        lbl[x][y] = lbl[x - 1][y];
                        continue;
                    }
                }
                lbl[x][y] = id;
                ++id;
            }
        }
        //集計
        int[] count = new int[id];
        int[] color = new int[id];
        for(int x = 0; x < w; ++x){
            for(int y = 0; y < h; ++y){
                count[lbl[x][y]]++;
                color[lbl[x][y]] = ctbl[y * w + x];
            }
        }
        int[] alpha = new int[64];
        int[] beta = new int[64];
        for(int i = 0; i < id; ++i){
            int d = color[i];
            color[i] = (((d >> 22) & 3) << 4) + (((d >> 14) & 3) << 2) + ((d >> 6) & 3);
            if(count[i] < 20){
                beta[color[i]] ++;
            }else{
                alpha[color[i]] ++;
            }
        }

        //表示
        for(int i = 0; i < alpha.length; ++i){
            if(alpha[i] == 0 && beta[i] == 0) continue;
            System.out.printf("%2d (%3d, %3d)%n", i, alpha[i], beta[i]);
        }

        f.setVisible(true);
    }
}