恥ずかしげもなく技術ブログ

プログラミング等について書いていきます.

別スレッドで処理した画像を定期的にImageViewに反映する

時間がかかる処理をメインスレッド(ここでは,Activityがうごくスレッドとする)の中に組み込んでしまうと,GUIの更新が停止してしまい,androidではアプリを停止しますか?という警告が出てしまう.

そこで通常はUIを扱うスレッドとは別のスレッドで時間のかかる処理を行う.

この記事ではメインスレッドとは別に

(1)定期的にImageViewを再描画するスレッド <- UIを扱う ただしメインスレッドではない

(2)画像処理を行うスレッド <- UIは扱わない 主に時間がかかるスレッド

を同時に動かし,(2)が終了したら(1)のスレッドも停止させることを実現する.

 

# ただし,そもそもSurfaceViewを用いるとSurfaceView自体が別スレッドなので(1)と(2)のように分けずonDraw()メソッドの中で完結できるということも知っておこう.

 

まず,Activityのフィールドに

private Bitmap image;

private boolean isFinished;

を追加し,onCreateとか適当なところに以下の処理を書く.

 

// (2) 画像処理など時間がかかる処理を行うスレッド

new Thread(new Runnable() {
@Override
public void run() {
/*画像処理*/

ImageProc.foo(image)

isFinished = true;
}
}).start();

 

// (1) UIを担当するスレッド

/* 定期的に画像処理の結果を描画する */
final Handler handler = new Handler();
handler.post( new Runnable() {
@Override
public void run() {
try {
ImageView iv = (ImageView) findViewById(R.id.imageView);
iv.setImageBitmap(image);
iv.invalidate();
handler.postDelayed(this,100);
if (isFinished){
handler.removeCallbacks(this);
}

}catch(Exception e){}
}
});

 

handler.postDelayed(this,100);

は100msで自身を呼び出すコード.すぐ下のコードでは

isFinishedがtrueになったらループを停止している.

 

 

AndroidStudioで予め用意したdbファイルを読み込むとか

Android Studioを導入してみて色々つまづいたところがあったので書き散らします.

パッと思い出せるつまづいた箇所は次の3点です.

①LogCatにLogが出ない

②assetsフォルダの位置がわからん

③予め作ったdbファイルが読み込めない

 

以下,対応.

①LogCatのウィンドウがある下の見出しに「TODO」「Run」「Terminal」とかあると思うけど「Terminal」をクリック

そこで

adb kill-server 

adb start-server 

と打てばしばらく経つとログが流れるはず.注意すべきはLog.vを使った時にeclipseではタグの検索がtag:タグ名でフィルタリングするけど,Android Studioではタグ名だけでフィルタリングできます.

 

②app/src/mainです.つまり,app/src/main/assets.

 

③はじめはmain直下にdbファイルを置いたりして,

これ

xerial / sqlite-jdbc — Bitbucket

とか使ってみたけど上手くいかなかったのでassets直下にdbを置いて

こちらのサイトの通りにやってみれば出来ました.ありがとうございます.

上の方の定数を自分の環境に合わせて書いてみてください.


PCで作ったSQLiteのDBファイルをAndroidで使う | naichilab - Android iOSアプリ開発メモ

これを使ったコードはこんな感じです.

RecordDBHelperってのが上のサイトでいうDBHelperですね.

フラグメントの中で使っているのでcontextを取るところが少し変わっています.

 

RecordDBHelper hlpr = new RecordDBHelper(rootView.getContext());
SQLiteDatabase mydb = hlpr.getWritableDatabase();

// mytbというテーブルから,カラムid,jname,img,archiveを取ってきてそのidを昇順に並べています
Cursor cursor = mydb.query("mytb", new String[] {"id", "jname", "img", "archive"}, null, null, null, null, "id ASC");
while (cursor.moveToNext()){
String jname = cursor.getString(1);
String img_path = cursor.getString(2);
String archive = cursor.getString(3);

}

iOSでsqlite3

iOSでsqlite3を操作するラッパーとしてFMDBというものがあるらしい.

端末にデータを保存するにはNSArray *paths = NSSearchPathForDirectoriesInDomainsNSDocumentDirectoryNSUserDomainMaskYES );

でアプリ毎に用意されているDocumentsディレクトリを用いる.

シミュレータの場合は

Users/(your accont name)/Library/Application\ Support/iPhone\ Simulator/(version)/Applications/(app id?)/Documents

がDocumentsディレクトリです.ここにDBなどファイルを置くとコードから操作することができます.

app idに関してはわけわかんないですが,思い切ってさらに一階層降りると(app name).appファイルがあるのでその名前からアプリを判断できます.

 

 

//DBの呼び出し

NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );

    NSString *dir = [paths objectAtIndex:0];

    //自分で作成したデータベースの名前を最後に入力

    FMDatabase *db = [FMDatabasedatabaseWithPath:[dir stringByAppendingPathComponent:@"test.db"]];

    //時刻の取得

    NSDate *date = [NSDate date];

    NSDateFormatter* formatter = [[NSDateFormatteralloc] init];

    [formatter setDateFormat:@"YYYY_MM_dd_hh_mm_xss"];

    NSString *createtime = [formatter stringFromDate:date];

    //insert文の入力

    //tableはすでに作成されていると仮定

    NSString *insert = [[NSStringalloc] initWithFormat:@"INSERT INTO item(type, r, g, b, path, date) VALUES('%d','%d','%d','%d','%@', '%@')", 1, (int)self.rgb[0], (int)self.rgb[1], (int)self.rgb[2], createtime,  createtime];

    [db open];

    //Queryの実行

    [db executeUpdate:insert];

    [db close];

    NSLog(@"done");

iOSでOpenCVついでにGrabCut

iOSでgrabCutを使う機会があったのでやってみました.

自分はgrabCut自体は知っているのですが実際にOpenCVから使ったことがなかったためiOSOpenCVを使うというのとgrabCutを利用するという2つのチャレンジをしました.

 

(1) まずOpenCVの導入・動作

[1]まずネットからOpenCVをDL.2014/09/05の段階で最新はOpenCV3.0ですがα版なので2.4.9をDLします.

DOWNLOADS | OpenCV

の2.4.9 for iOSをダウンロードしてください.

 

[2]次に適当にxcodeでプロジェクトを作り,左の一覧から自分のプロジェクト名をクリックした後,中央の画面の一番下にあるLinked Frameworks and Librariesで+を選択し先ほどDLしたOpenCVフレームワークを選択.

 

[3]同じく左の一覧からSupporting Files/(自分のプロジェクト名)-Prefix.pchを選択し,

#ifdef __cplusplus

#import <opencv2/opencv.hpp>

#endif

を最後に追加.

 

[4] opencvを使いたいファイルの拡張子を.mから.mmに変える.ViewController.mmとか.これでOpenCVが使えます.

 

(2) grabCutの利用

ネットに落ちているコードを継ぎ接ぎしたり,

OpenCVのリファレンスとサンプルコード:

opencv/grabcut.cpp at master · Itseez/opencv · GitHub

を参考にして作りました.

----

// UIImageとして読み込み,その後cv::Matに変換

cv::Mat srcMat = [self cvMatFromUIImage:[UIImage imageNamed:@"0919.jpg"]];

    cv::Mat dst;

   //今回はひとまず矩形領域だけで全景領域切り出す.

   //なのでこのマスクは初期化の必要なし

    cv::Mat mask;

   //これは反復演算のときに使われるらしい

   //同じ画像に対して同じ行列を使うということだけを意識すれば初期化不要

    cv::Mat output1,output2;

   //矩形領域 これより外側は背景として扱われる

   //ここではパンイベントを取って矩形領域を指定するメソッドを別に書き,

   //それをもとに矩形領域を設定

    cv::Rect rect(cv::Point(self.leftUp.x,self.leftUp.y), cv::Point(self.rightDown.x,self.rightDown.y));

   //ネットに落ちてるのはRGBA画像なのでRGBに変換

    cv::cvtColor(srcMat , srcMat , CV_RGBA2RGB);

    dst = srcMat.clone();

 

    rect.x = (int)fmax(0, rect.x);

    rect.y = (int)fmax(0, rect.y);    

    rect.width = (int)fmin(rect.width, self.imgView.image.size.width);

    rect.height = (int)fmin(rect.height, self.imgView.image.size.height);

 

    cv::grabCut(srcMat, mask, rect, output1, output2, 10, cv::GC_INIT_WITH_RECT);

 

   mask &= 1;

    for (int w=0;w<mask.cols;w++){

        for (int h=0;h<mask.rows;h++){

            if (mask.at<uchar>(cv::Point(w,h))==0){

                dst.at<cv::Vec3b>(cv::Point(w,h))=0;   

            }

        }

    }    

    self.imgView.image = [self UIImageFromCVMat:dst];

----

変換コードはネットに落ちているものをそのまま使わせて頂きました.

/*****/

- (cv::Mat)cvMatFromUIImage:(UIImage *)image

{

    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);

    CGFloat cols = image.size.width;

    CGFloat rows = image.size.height;

    

    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

    //cvMat.convertTo(cvMat, CV_8UC3);

    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to  data

                                                    cols,                       // Width of bitmap

                                                    rows,                       // Height of bitmap

                                                    8,                          // Bits per component

                                                    cvMat.step[0],              // Bytes per row

                                                    colorSpace,                 // Colorspace

                                                    kCGImageAlphaNoneSkipLast |

                                                    kCGBitmapByteOrderDefault); // Bitmap info flags

    

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);

    CGContextRelease(contextRef);

    //CGColorSpaceRelease(colorSpace);

    

    return cvMat;

}

 

- (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat

{

    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];

    CGColorSpaceRef colorSpace;

    

    if (cvMat.elemSize() == 1) {

        colorSpace = CGColorSpaceCreateDeviceGray();

    } else {

        colorSpace = CGColorSpaceCreateDeviceRGB();

    }

    

    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridgeCFDataRef)data);

    

    // Creating CGImage from cv::Mat

    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                 //width

                                        cvMat.rows,                                 //height

                                        8,                                          //bits per component

                                        8 * cvMat.elemSize(),                       //bits per pixel

                                        cvMat.step[0],                              //bytesPerRow

                                        colorSpace,                                 //colorspace

                                        kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info

                                        provider,                                   //CGDataProviderRef

                                        NULL,                                       //decode

                                        false,                                      //should interpolate

                                        kCGRenderingIntentDefault                   //intent

                                        );

    

    

    // Getting UIImage from CGImage

    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];

    CGImageRelease(imageRef);

    CGDataProviderRelease(provider);

    //CGColorSpaceRelease(colorSpace);

    

    return finalImage;

}

/*****/

 

UIImageView上でタップされた位置を検出する

最近iOSで開発する機会がありましたので勉強中.

タップされた位置をどう取るのかわからなかったので色々調べてやってみた.

 

(1) 準備

StoryBoard上でImage Viewを配置し,画像を適当に設置します.

画像の置き方はxcodeで見た自分のフォルダにファインダからファイルをドロップして,

ImageViewのプロパティのうち一番上にあるImageフォームから画像を選択するだけ.

ImageViewをCtrl + ドラッグしてinterfaceの適当なところに配置してください.

名前はimgViewとしました.

 

(2) Gestureの配置

次にImage Viewと同じところにあるTap Gesture Recognizerを引っ張ってきて

先ほど配置したImageViewに重なるようにドロップします.

するとそのアイコンが編集中のiPhone画面の下に追加されると思います.

 

(3) ヒモ付

アシスタント画面に変えて先ほど追加されたGestureのアイコンをCtrl + クリックしながらソースコードのinterfaceに配置.名前はmyTapperとしました.

同じ操作をもう一度implementationの中の適当なところに配置.

名前は適当で.私はhandleTapとしました.

 

(4) 位置情報の取得

このメソッドはImageViewをタップされるたびに呼ばれます.

タップされた位置は

CGPoint loc = [self.myTapper locationOfTouch:inView:self.imgView];

でタップ操作のうち一番目の位置を取得できます.

NSLog(@"%f,%f", loc.x, loc.y);

でタップされた位置を確認することができます.

AndroidでNoClassDefFoundError

@author=poifull10

 

外部ライブラリのビルドパスを通し,ビルドは通ったのが実行時にNoClassDefFoundErrorが出てしまった.

stackoverflowに解答があった.

http://stackoverflow.com/questions/8678630/noclassdeffounderror-for-code-in-an-java-library-on-android

 

要は,ビルド順序のせいでライブラリのメソッドが読めてないらしい.

以下の順序で解決できる.

 

1. プロジェクトを右クリック

2. Android Build Path

3. Configure Build Path

4. Order and Export タブ

この中で,上から順にsrc,genとなっているのがダメ.

なので,genをクリックしてUpし,gen, srcの順番にすれば解決できる.

 

こういうところで躓くのって嫌だよね〜

 

 

SECCON2014

@author = poifull10

 

SECCON 2014 http://2014.seccon.jp/ の予選に初めて参加してきました.

予選方法はCTFで,問題を解いてFlag(=特定の文字列)がゲットできればその問題がクリアとなります.問題は複数あってそれぞれの問題には点数がついていますので,多く得点をゲットした人がランキングの上位になるという仕組みです.

CTFにはグループで参加することができ,予選はランキング上位7チームのみが突破することができます.

 

僕はチーム名KUISAPとして参加してきました.ちなみにOS Xです.

スコアは600点で,ランキング129位.おそらくwriteup書いている人たちの中で最弱だと思いますw

感想としては,初めての参加でパケットキャプチャのやり方も知らず&分野が全く違うドシロウトでチャレンジしましたが,なかなか楽しめました.また参加しようと思います.

 

最後に,完全な素人目線で書いた簡素なwrite upです.

 

----

問 (練習問題) 100点

  Flagが与えられていますので,それをフォームに入力して終わり.サービス問題(つまりランキングで0点の人たちは実質参加していないことになる…).

 

問 パケットキャプチャ 100点

 パケットファイル?が与えられているのでそれをWireSharkで開く.WireSharkX windowシステムで動くのでXQuartzを入れないといけない.WireSharkで通信内容を見ていると,FTPでFlag.txtを通信しているのがわかる.55番目のログの中のデータに該当する部分が通信されたFlag.txtの内容である.FTPではデータをbase64エンコードしているらしく,これはbase64 -D でデコードが出来る.

echo '文字列' | base64 -D

でフラグゲット.

ちなみに,WebでBasic認証をするときIDとPassをコロンでつなげたid:Passbase64エンコードされているらしい.

 

問 encoded.txt 100点

 デコードせよ,という問題.ファイルを開いても全く読めず文字化けが激しく全く読めないが先頭は英語でFRPPBAと書かれていた.友人が「SECCON」か?と言っていたのでPPの部分がCCに対応するとわかった.つまりシーザー暗号.その後13/47という数字も続いていたので,ggると英語にはrot13,日本語にはrot47を使ってエンコードするという記述があったのでnkfコマンドでデコード. nkf -r encoded.txt で正解.

 

問 箱庭SQLi 100点

 とりあえずSQLインジェクションを仕掛ける.全部のレコードが吐出された.それで?状態.レコードでggるとよくあるテンプレのレコードみたい?よくわからん.解けませんでした.

 

アセンブリ 100点

 見た感じひたすら頑張る感じ?便利なソフト無いんかな…友人が頑張ってくれました.

 

問 FileSystem 100点

 とりあえずファイルサイズでバイナリファイルを検索してみる.しかしノーヒット…なんでや… 過去問を解いたブログにはファイルシステムを作って,そのファイルシステムにファイルを追加することでその差分からタイムスタンプを見つけるという方法(http://shoboon.hatenablog.jp/entry/2013/10/10/111139)が書いてあった.同じようにやってみるも不明.ファイル名さえわかればいいのでとりあえず片っ端からファイル名を入力してみる…となんと先頭が答え.まぐれでクリア.

-- 追記

数値の配列が逆順なようですね…例えば0xABCDEFだと実際にはEF CD ABと並んでいる,みたいな.よくよく考えたら当たり前ですね.数値を逆にすれば見つかりました.

 

問 重ねてみよ 100点

 gifアニメーションを全て足しあわせて反転させればQRコードに.それを読み込んで終わり.