Java OpenCv 〜画像データを操作する〜

OpenCVの本を読み学習中です。
しかし、よくわからん。。。切羽詰まったので本当に基礎からやり直していく方向です。

画像データとは

参考にするサイトはこちらです。
とりあえずわかっているのは画像データが下のようなものだということ。ちなみに下のイメージです。(32x32)のPNGファイル

早い話が、RGBのデータとRGBAのデータがあり透過したい場合はRGBAのデータでないとできない。。。
というわけです。

具体的に、自分の場合はデータを操作するために、以下のような経緯で理解しました。

画像の生データを見る

[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0],[x: 255.0 y: 255.0]
 ....

こんな風になっていて「わけわからん!」と思っていたが、
中身を System.out.println()して見ると

// 表示イメージを読み取る
Mat charactor = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_COLOR);
Mat charactor1 = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_UNCHANGED);
System.out.println("Channels = channelA: " + charactor.channels() + " channelB: " + charactor1.channels());
System.out.println("Length = channelA: " + charactor.get(0, 0).length + " channelB: " + charactor1.get(0, 0).length);
System.out.println("Byte1 = channelA: " + charactor.get(5, 24)[0] + " channelB: " + charactor1.get(6, 24)[0]);
System.out.println("Byte last = channelA: " + charactor.get(5, 24)[2] + " channelB: " + charactor1.get(6, 24)[3]);

こんな感じのコードで出力してみたところ。。。

Channels = channelA: 3 channelB: 4
Length = channelA: 3 channelB: 4
Byte1 = channelA: 33.0 channelB: 84.0
Byte last = channelA: 83.0 channelB: 255.0

上のように出力されたました。

つまり

RGBのファイルだと、Mat = (R, G, B) = (xx.xx, xx.xx, xx.xx)という値が入っている

RGBA(透明度付き)の場合だとMat = (R, G, B, A) = (xx.xx, xx.xx, xx.xx, xx.xx)
具体的には、

Mat charactor = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_COLOR);
double[] d = characotr.get(0, 0); // 画像行列(32x32)の左端
d[0]; // Rの値
d[1]; // Gの値
d[2]; // Bの値
d[3]; // Aの値

という風になる。。。

そして、このことを理解してプログラムを実行すると
取得したイメージファイルのRの値が255のデータ(下の画像の白い部分(255,255,255))のアルファ値を0にセットしてやれば、データの背景を透過することができる。。。


ちょっと小さいけど、右側のイメージは背景が透過されています。

実行したコードは下です。

// 表示するイメージを取得
URL url = getClass().getResource("/pipo-charachip001.png");
// 表示イメージを読み取る
// RGBで読み取る
Mat charactor = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_COLOR);
// RGBAで読み取る
Mat charactor1 = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_UNCHANGED);
System.out.println("Channels = channelA: " + charactor.channels() + " channelB: " + charactor1.channels());
System.out.println("Length = channelA: " + charactor.get(0, 0).length + " channelB: " + charactor1.get(0, 0).length);
System.out.println("Byte1 = channelA: " + charactor.get(5, 24)[0] + " channelB: " + charactor1.get(6, 24)[0]);
System.out.println("Byte last = channelA: " + charactor.get(5, 24)[2] + " channelB: " + charactor1.get(6, 24)[3]);
boolean boo = true;

for (int y = 0; y < charactor1.height(); y++) {
    for (int x = 0; x < charactor1.width(); x++) {
        double[] d = charactor1.get(y, x);
        if (d[0] == 255.0) {
            if (boo) {
                System.out.println("*** Testing ***");
                boo = false;
            }
            d[3] = 0.0;
            charactor1.put(y, x, d);
        }
    }
}
System.out.println("Byte last = channelA: " + charactor.get(5, 24)[2] + " channelB: " + charactor1.get(6, 24)[3]);
MatOfByte b = new MatOfByte();
Imgcodecs.imencode(".png", charactor, b);
BufferedImage out = createImage(b);
drawBufferedImage(out, before);

MatOfByte c = new MatOfByte();
Imgcodecs.imencode(".png", charactor1, c);
BufferedImage out1 = createImage(c);
drawBufferedImage(out1, after);

出力したコンソールは下です。

ポイント

取得したdouble型のデータをMatクラスにセットする方法は以下のようにやりました。

// Mat charactor1 = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_UNCHANGED);
double[] d = charactor1.get(y, x);
d[3] = 0.0;
charactor1.put(y, x, d);

こんな感じで、できました。