下の記事では簡単な画像処理アプリを作成しました。
モノクロ化処理や回転処理を自作の関数で行なっていましたが、このページではAndroid Developersが提供しているMatrixクラスとColorMatrixクラスを使用してもっと簡単かつエレガントに画像処理する方法を解説します。
プロジェクトの作成から画面レイアウト作成まで
MatrixクラスとColorMatrixクラスを使うプロジェクトや画面レイアウトは下記事と同じのものを想定しています。
ソースコード
実際にMatrixクラスColorMatrixを使用して画像回転と画像モノクロ化を行なっているとソースコードを下記に示します。
・MainActivity.kt
/* ↓ はパッケージ名に合わせて編集 */
package com.daeudaeu.easyimageprocessing
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageView
import android.widget.Button
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.ColorMatrix
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageFrame : ImageView = findViewById(R.id.cat)
val rotateButton : Button = findViewById(R.id.rotate)
val monoButton : Button = findViewById(R.id.mono)
val resetButton : Button = findViewById(R.id.reset)
/* bitmap1は元画像 */
var bitmap1 : Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cat)
/* bitmap2は表示中のBitmap */
var bitmap2 : Bitmap = Bitmap.createBitmap(bitmap1)
/* bitmap3は編集用のテンポラリのBitmap */
var bitmap3 : Bitmap
rotateButton.setOnClickListener {
/* Matrixオブジェクト生成 */
val matrix = Matrix()
/* 90度回転する行列を生成 */
matrix.setRotate(90f)
/* bitmap2(表示中の画像オブジェクト)に
回転行列で回転した画像オブジェクトbitmap3を生成 */
bitmap3 = Bitmap.createBitmap(bitmap2, 0, 0, bitmap2.width, bitmap2.height, matrix, true)
/* bitmap3をImageViewで表示 */
imageFrame.setImageBitmap(bitmap3)
bitmap2 = bitmap3
}
monoButton.setOnClickListener {
val value : FloatArray = floatArrayOf(
0.33f, 0.33f, 0.33f, 0f, 0f,
0.33f, 0.33f, 0.33f, 0f, 0f,
0.33f, 0.33f, 0.33f, 0f, 0f,
0f, 0f, 0f, 1f, 0f
)
/* ↑の行列をColorMatrixオブジェクトにセット */
val matrix = ColorMatrix()
matrix.set(value)
/* 行列からフィルタ生成 */
val filter = ColorMatrixColorFilter(matrix)
/* ImageViewオブジェクトの画像にそのフィルタをかける */
imageFrame.setColorFilter(filter)
}
resetButton.setOnClickListener {
/* 回転方向戻すために元画像をImageViewで表示 */
imageFrame.setImageBitmap(bitmap1)
/* 色を戻すために行列をリセットし、
その行列で生成したフィルタでImageViewの画像を処理 */
val matrix = ColorMatrix()
matrix.reset()
val filter = ColorMatrixColorFilter(matrix)
imageFrame.setColorFilter(filter)
bitmap2 = bitmap1
}
}
}
動作確認すると、たしかに画像回転とモノクロ化が実行できることが確認できると思います。
以下ではMatrixクラスとColorMatrixクラスがどういうものでなぜ回転や色変換が行われているかを解説します。
スポンサーリンク
Matrixクラス
Matrixというのは行列です。あの数学で学ぶ行列のことです。
なんで画像処理で行列?!と思う人もいるかもしれませんが、画像処理と行列は密接な関係にあります。画像の各座標を行列で変換すれば(行列を掛けてやれば)画像の形状(角度・大きさ・場所など)を変更することが可能です。
例えば画像の回転は行列の演算で行うことが可能であることを下記で説明していますので興味があれば見てみてください(C言語の記事ですが考え方は伝わるはず)。
MatrixクラスはAndroid Developersが提供するクラスで、この行列演算を実行してくれるものになっています。
例えば上のソースコードでは下記のような処理を行っています。
/* Matrixオブジェクト生成 */
val matrix = Matrix()
/* 90度回転する行列を生成 */
matrix.setRotate(90f)
/* bitmap2(表示中の画像オブジェクト)に
回転行列で回転した画像オブジェクトbitmap3を生成 */
bitmap3 = Bitmap.createBitmap(bitmap2, 0, 0, bitmap2.width, bitmap2.height, matrix, true)
/* bitmap3をImageViewで表示 */
imageFrame.setImageBitmap(bitmap3)
処理の流れはコメントに記載しているつもりです。
下記で画像の各座標に対して演算を行う行列を設定しています。具体的には画像を90度回転する行列を設定しています。
matrix.setRotate(90f)
さらに下記でビットマップオブジェクトbitmap2に対して行列演算を実行した後のビットマップオブジェクトを生成しています。
bitmap3 = Bitmap.createBitmap(bitmap2, 0, 0, bitmap2.width, bitmap2.height, matrix, true)
このソースコードでは画像の回転を行う行列によって画像の回転処理を行っていますが、Matrixクラスでは他にも様々な行列を生成し、様々な画像処理を行うことが可能です。例えば画像の拡大縮小や画像の移動などが可能です。
下記ページでMatrixクラスの詳細な説明が記載されていますので参考にして色々な画像処理を行ってみてください。
参考 MatrixAndroid DevelopersColorMatrixクラス
こちらも行列に関するクラスになります。ただし、Matrixが座標に対する行列演算に関するクラスであるのに対し、ColorMatrixは画素の色に対する行列演算になります。
ColorMatrixクラスの行列では下記のような5 x 4の行列を扱います。
[ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]
さらに、元画像の画素の色RGBAに対して下記のような計算を行うことで色変換後の画素の色R’G’B’A’を算出します。これを画像の全画素に対して行うことで画像全体の色を変更する処理を行っています。
R’ = a*R + b*G + c*B + d*A + e G’ = f*R + g*G + h*B + i*A + j B’ = k*R + l*G + m*B + n*A + o A’ = p*R + q*G + r*B + s*A + t
例えば上のソースコードでは下記のような処理を行っています。
val value : FloatArray = floatArrayOf(
0.33f, 0.33f, 0.33f, 0f, 0f,
0.33f, 0.33f, 0.33f, 0f, 0f,
0.33f, 0.33f, 0.33f, 0f, 0f,
0f, 0f, 0f, 1f, 0f
)
/* ↑の行列をColorMatrixオブジェクトにセット */
val matrix = ColorMatrix()
matrix.set(value)
/* 行列からフィルタ生成 */
val filter = ColorMatrixColorFilter(matrix)
/* ImageViewオブジェクトの画像にそのフィルタをかける */
imageFrame.setColorFilter(filter)
このソースコードで用いている行列は下記です。
val value : FloatArray = floatArrayOf(
0.33f, 0.33f, 0.33f, 0f, 0f,
0.33f, 0.33f, 0.33f, 0f, 0f,
0.33f, 0.33f, 0.33f, 0f, 0f,
0f, 0f, 0f, 1f, 0f
)
この行列で生成されるフィルタで画像を処理することで、下記のような結果が得られます。
R’ = 0.33R + 0.33G + 0.33B G’ = 0.33R + 0.33G + 0.33B B’ = 0.33R + 0.33G + 0.33B A’ = A
各画素のR’G’B’は元画素のRGBに0.33掛けた値の和の値になります。要はRとGとBの平均値を各画素のR’G’B’に設定しています。R’=G’=B’になりますので色変換後の画像はグレースケールの画像になります。
A’はそのままAとしています。A’はアルファチャネルと呼ばれるパラメータで一般に透明度を表すパラメータになります、これが0だと画像が透明に、1だと画像が不透明になります。0にすると画像が透明で見えなくなってしまうのでA’の値はそのままAを代入するように行列を生成しています。
このColorMatrixクラスで使用する行列を調整してやれば画像の様々な色変換が可能です。例えばセピア風にしたり、赤・青・緑のどれかの色のみを強調したりすることも可能です。
例えば下記行列であれば色を反転することが可能です。
val value : FloatArray = floatArrayOf(
-1f, 0f, 0f, 0f, 255f,
0f, -1f, 0f, 0f, 255f,
0f, 0f, -1f, 0f, 255f,
0f, 0f, 0f, 1f, 0f
)
ColorMatrixクラスについても下記ページで詳細が記載されていますので是非ご参考に。
参考 ColorMatrixAndroid Developers