2014年8月20日水曜日

AndroidでOpenCVを使って背景差分を取得する

Androidで画像に移った被写体との背景差分を取りたくて色々調べてみたところOpenCVというライブラリを使えばできることがわかった。

背景差分処理前の画像

背景差分処理後の画像

作ったサンプルコードは以下の通り

public class MainActivity extends Activity {
 static {
        if (!OpenCVLoader.initDebug()) {
            // Handle initialization error
        } else {
        }
    }
 private final static String TAG = "BGsubstractorTest";
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  if (savedInstanceState == null) {
   getFragmentManager().beginTransaction()
     .add(R.id.container, new PlaceholderFragment()).commit();
  }
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }

 /**
  * A placeholder fragment containing a simple view.
  */
 public static class PlaceholderFragment extends Fragment {
  ImageView mImageView;

  public PlaceholderFragment() {
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
   View rootView = inflater.inflate(R.layout.fragment_main, container,
     false);
   return rootView;
  }
  
  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
   super.onActivityCreated(savedInstanceState);
   
   Mat input = new Mat();
   BackgroundSubtractorMOG mog = new BackgroundSubtractorMOG();
   Mat output = new Mat();
   
   Bitmap bitmap1 = loadBitmap(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/IMG_20140818_000833235.jpg");
   Utils.bitmapToMat(bitmap1, input);
   Imgproc.cvtColor(input, input, Imgproc.COLOR_RGBA2RGB);
   
   mog.apply(input, output);
   
   Bitmap bitmap2 = loadBitmap(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/IMG_20140818_000849955.jpg");
   Utils.bitmapToMat(bitmap2, input);
   Imgproc.cvtColor(input, input, Imgproc.COLOR_RGBA2RGB);
   
   mog.apply(input, output);
   
   Bitmap bitmap3 = Bitmap.createBitmap(input.width(), input.height(), Bitmap.Config.ARGB_8888);
   Utils.matToBitmap(output, bitmap3);
   refreshImage(bitmap3);
  }
  
  private Bitmap loadBitmap(String imagefilePath) {
   BitmapFactory.Options options = new BitmapFactory.Options();
         options.inSampleSize = 4;
         options.inPreferredConfig = Config.ARGB_8888;
   Bitmap bm = BitmapFactory.decodeFile(imagefilePath, options);
   return bm;
  }
  
  private void refreshImage(Bitmap bmp) {
      mImageView = (ImageView)getActivity().findViewById(R.id.imageView1);
      mImageView.setImageBitmap(bmp);
  }
 }
}

つまづいた所としてはBackgroundSubtractorMOGに読み込ませる画像がRGBA形式だとエラーになってしまう。
そのため一度RGB形式に直してやらないとうまく動かない。

Imgproc.cvtColor(input, input, Imgproc.COLOR_RGBA2RGB);

2014年5月29日木曜日

レイアウトファイルから作成するFragmentではsetArgumentsは使えない

レイアウトファイルから生成したFragmentに対してsetArgumentsを行ったところ ”Fragment already active” というエラーが起きた。

調べてみたところレイアウトファイルから生成されたFragmentではメンバのBundleがnullになるためsetArguments内で例外を投げられてしまうらしい。

Fragmentはレイアウトファイルから静的に生成されるケースとFragmentManagerより動的に生成・追加されるケースがあるが、それらの使い分けについて詳しく知る必要があると感じた。

参考URL:http://y-anz-m.blogspot.jp/2012/04/android-fragment-fragmenttransaction.html

2014年5月27日火曜日

PlaceholderFragmentとは

AndroidでデフォルトでActivityを作ったときに自動的に定義されるクラスPlaceholderFragment、今まで一体こいつが何なのか理解できていなかったが何となくわかってきたのでそれをメモする。

おそらくActivityにFragmentを乗せる方法としては二通りある。
  1. Activityのレイアウトxmlに独自のFragment要素を追加する。
  2. Activityのクラス内でPlaceholderFragmentのonCreateViewハンドラ内のinflateで独自Fragmentのレイアウトファイルを指定する。
因みに前者の場合、class属性で指定したFragmentを継承したクラス内のonCreateViewで表示したいViewを作成またはレイアウトファイルよりinflateする必要がある。

また、基本的に何かのイベントが起きてFragmentの表示を変えたいときにはFragmentManagerよりIDや名前を指定して指定のViewを引っ張って来れるため必ずしも、ある独自Fragmentに対してそれに対応するクラスを作る必要はない。

つまり、Fragment内のViewはどのクラスからでも操作が可能であるためPlaceholderFragmentという代表者をおいて独自Fragmentのクラスの代わりの処理を行うことができる。

PlaceholderFragmentを使用した場合、直感的にどのFragmentと対応付いているのかわかりづらいため前者の方が好ましい気がする。

追記:
今改めて当時に自分が書いた文章を読むとさっぱり何が言いたいのかがわからない・・・。

思い出すと、当時はFragment = Activityのインナークラスという様に理解をしていた。
また、お作法として独自のFragmentを使いたい場合はレイアウトファイルで定義してPlaceholderFragment内のonCreateViewのinflater.inflate()でそのレイアウトファイルを指定するものだと理解していた。

しかし、実際のお作法はそうではなくて、Fragmentを使いたいActivityのonCreate()内でFragmentManagerを呼び出して表示したいFragmentをインスタンス化してadd&commitするというものだった。

通常、FragmentはActivityとは別のクラスファイルとして作成管理すべきである。
そして、PlaceholderFragmentは単なるデフォルトの空のFragmentであり、ひとまず何でもいいからadd&commitするためインナークラスとして定義しているだけにすぎない。

でも、初めてAndroidでActivityを作ったときにFragmentがインナークラスで定義されていたらそういうものだって思ってしまうよね・・・。