ページ

2014/06/25

[Android]Bitmap のモザイク加工処理

DevCamera [カメラ/連射/無音/無音連射/ビデオ] - Google Play の Android アプリ
顔検出カメラを追加して写真を撮ったらモザイクがかかるようにしてみた。
完全にお遊びで顔検出 API を使って作ってみたのでモザイクくらいしかやることがなかった
あまり意味のないカメラです。

ピクセルを取得して周辺のピクセルの色平均をとってピクセルを戻すだけの処理です。

public static Bitmap mosaic(Bitmap bitmap){
    int dot = 16;
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int square = dot * dot;
    int[] pixels = new int[width * height];
    bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
    // ピクセルデータ分ループ
    for(int w = 0, widthEnd = width / dot; w < widthEnd; w++){
        for(int h = 0, heightEnd = height / dot; h < heightEnd; h++){
            // ドットの中の平均値を使う
            int r = 0;
            int g = 0;
            int b = 0;
            int moveX = w * dot;
            int moveY = h * dot;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    int dotColor = pixels[moveX + dw + (moveY + dh) * width];
                    r += Color.red(dotColor);
                    g += Color.green(dotColor);
                    b += Color.blue(dotColor);
                }
            }
            r = r / square;
            g = g / square;
            b = b / square;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    pixels[moveX + dw + (moveY + dh) * width] = Color.rgb(r, g, b);
                }
            }
        }
    }
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    return bitmap;
}
Bitmap#copyPixelsToBuffer / Bitmap#copyPixelsFromBuffer の方が速いらしいので書き換えてみた。 本当にこの書き方であっているか自信がない。そしてループ処理が遅すぎて微々たる差しかないように感じた。
public static Bitmap mosaicBuffer(Bitmap bitmap){
    int dot = 16;
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int square = dot * dot;
    int[] pixels = new int[width * height];
    IntBuffer buffer = IntBuffer.wrap(pixels);
    buffer.position(0);
    bitmap.copyPixelsToBuffer(buffer);
    // ピクセルデータ分ループ
    for(int w = 0, widthEnd = width / dot; w < widthEnd; w++){
        for(int h = 0, heightEnd = height / dot; h < heightEnd; h++){
            // ドットの中の平均値を使う
            int r = 0;
            int g = 0;
            int b = 0;
            int moveX = w * dot;
            int moveY = h * dot;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    int dotColor = pixels[moveX + dw + (moveY + dh) * width];
                    r += Color.red(dotColor);
                    g += Color.green(dotColor);
                    b += Color.blue(dotColor);
                }
            }
            r = r / square;
            g = g / square;
            b = b / square;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    pixels[moveX + dw + (moveY + dh) * width] = Color.rgb(r, g, b);
                }
            }
        }
    }
    buffer.position(0);
    bitmap.copyPixelsFromBuffer(buffer);
    return bitmap;
}
遅すぎるので[Android]Camera#addCallbackBuffer と Camera#setPreviewCallbackWithBuffer を使うで 書いたように RenderScript で実装するか OpenCV for Android を使うしかないのかもしれない。

参考

Bitmap.setPixel()をsetPixels()に変えたら3倍速くなったよ | Android Techfirm Lab 楓 software: getPixels と copyPixelsToBuffer