iDOFの使い方

2月 24th, 2011
このエントリーを含むはてなブックマークはてなブックマーク - iDOFの使い方

*この記事はAppBank様に寄稿させていただいた記事の一部を抜粋&変更して使用しております。詳しい説明はこちらを御覧ください。

 

iDOFの特徴

iDOFは、iPhoneで撮影した画像を一眼レフカメラで撮影されたような写真に加工することができるアプリです。

iDOFでは、一眼トイカメラやTiltShift Generatorと違い、ピントをあわせる部分とぼかす部分を手動で選択できるため、より自然な写真を作成することが出来ます。

 

iDOFの使い方

この画像をiDOFで処理してみたいと思います。


 
 
画像を読み込むと、読み込んだ画像が画面に表示されるので、焦点を当てたい部分を指でなぞっていきます。すると選択された範囲に赤いマスクがかかります。また、Paintボタンをタップするとペンの太さなどが変更できます。操作を取り消したい場合は右上のUndoボタンを利用してください。


 
 
 
細かい部分を選択したい場合、画面上でピンチイン・アウトのジェスチャを行うことで画像の拡大縮小ができます。また、ConfigボタンやColorボタンでぼかしの強さや焦点サイズ、画像の色調補正などが行えます。
 

 
 
右端のPreviewボタンを押すと結果が表示されます。気に入った場合はSaveボタンを、修正したい場合は各操作に戻ってください。結果はこのようになりました。
 

 
 

Tips: ミニチュア風写真の作成方法

ぼかしを少し弱めて焦点サイズを最大にし、彩度とコントラストをプラス、明度をマイナスにすることでミニチュア風の写真へと加工することができます。
 
 
 
 
 

ご購入はこちらから

iDOFを購入したい方は下の画像をクリックしていただくと、iTunesのページへジャンプすることが出来ます。

UITabBarで吹出しメニューを実装

2月 3rd, 2011
このエントリーを含むはてなブックマークはてなブックマーク - UITabBarで吹出しメニューを実装

 

UITabBarを使って、上の図のような吹出しメニューを作る方法を説明します。動作は次のようになります。TabBarItemを選択すると対応するメニューが出現し、もう一度押すと消えます。また、べつのTabBarItemを選択すると、現在のメニューが消えて対応するメニューが出現します。
 
 

親画面を作成する

まずはプロジェクトを作成します。今回もView-Based Applicationのテンプレートを使用します。プロジェクト名はTestにしました。


 
 
プロジェクトが作成できたら親画面を作成するため、TestViewController.xibファイルをダブルクリックしてInterface Builderを起動します。次にViewにライブラリからUITabBarをD&Dし、そのUITabBarにUITabBarItemをD&Dし、タブを3つ作成します。
 

吹出しメニュー画面を作成する

次に、各メニュー画面を作成します。メニュー画面1つに対して、1つのxibファイルを作成します。まずはResourceフォルダ上で「メニュー」→「追加」→「新規追加」を選択し、空のxibファイルを選択します。今回は3つのメニュー画面をつくるため、それぞれtab0.xib, tab1.xib, tab2.xibという名前でxibファイルを作成しました。

まずは、xibファイルにViewを配置するため、ライブラリからViewコンポーネントをD&Dしてきます。

つぎに、配置したViewをカスタマイズしていきます。Viewを選択肢た状態でインスペクタで高さを410(画面の高さからTabBarの高さを引いたもの)に設定します。また、Backgroundの背景色を透明に、OpaqueとUser Interaction Enabledのチェックを外します。ここまでで、透明なViewが完成しました。

 

つぎに、作成したView上にImageVIewを配置し、そこに吹出しの画像をのせます。下図の白い部分がimageViewを配置した場所になります。

このようなxibファイルを、メニューごとに3つ作成します。
 

メニュー画面をプログラムから呼び出す

ヘッダファイルではUITabBarへのアウトレットと吹出しメニュー用のviewを3つ宣言しています。UITabBarのアウトレットはIBを使って接続しておいてください。

TestViewController.h

@interface TestViewController : UIViewController
<UITabBarDelegate>{
	IBOutlet UITabBar *tabBar;
	UIView *views[3];
}

 
 
viewDIdLoad内では、作成した3つのxibファイルをロードしてviewsの配列にそれぞれ格納し、同時にaddSubViewで親画面に吹出しメニュー画面を登録しています。ただ、alpha値を0.0に設定しているため、画面上には表示されません。

tabBar:didSelectItem:メソッドでタブバーが押されたときの処理を行っています。このメソッドでは押されたタブによって、吹出しメニューの表示・非表示を制御しています。

TestViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
	
	tabBar.delegate = self;
	
	for(int i = 0; i < 3; ++i){
		NSString *nibName = &#91;NSString stringWithFormat:@"tab%d", i&#93;;
		views&#91;i&#93; = &#91;&#91;&#91;NSBundle mainBundle&#93; loadNibNamed:nibName owner:self options:nil&#93; objectAtIndex:0&#93;;	
		views&#91;i&#93;.alpha = 0.0f;
		&#91;self.view addSubview:views&#91;i&#93;&#93;;		
	}
}


- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{	
	for(int i = 0; i < 3; i++){
		if( i != item.tag ){
			views&#91;i&#93;.alpha = 0.0;
		} else {
			views&#91;i&#93;.alpha = 1.0 - views&#91;i&#93;.alpha;
		}
	}
}
&#91;/cpp&#93;
 
 
<h3>メニュー上のコントロールに対応する</h3>

最後に吹出しメニュー画面上のコントロール(ボタンやスライダ)のイベントをひろえるようにしましょう。まずはボタンが押された際に呼び出される関数を作成します。ヘッダファイルにも忘れず宣言を書いておいてください。

<b>TestViewController.m</b>
[cpp]
-(IBAction)buttonPressed:(id)sender
{
	NSLog(@"button pressed!");
}

次に、Interface Builderを使ってtab0.xibを開き、吹出しメニュー上にボタンを配置します。

通常でしたら、このボタンとFile’s OwnerのIBActionを接続して終わりなのですが、作成したtab0.xibファイルではFile’s OwnerがNSObjectに設定されているため、接続することが出来ません。

そこで、File’s Ownerを選択した状態でインスペクタを使ってFile’s OwnerのクラスをTestViewControllerクラスへと変更します。

クラスを変更すれば、Interface Builderから、ソースファイルで設定したIBOutletとIBActionが見えるようになるので接続を行ってください。
 
ソースファイル一式をココにおいておくので参考にしてください。
参考ソースファイル

 

DFTでガウシアンフィルタを高速化する

1月 27th, 2011
このエントリーを含むはてなブックマークはてなブックマーク - DFTでガウシアンフィルタを高速化する

 
 
DFTを使って、ガウシアンフィルタ(Gaussian Filter)を高速化する方法を説明します。基本的なアイデアは、対象画像を一旦フーリエ変換し、フーリエ領域(Fourie domain)でマスク処理を行うことで、計算量を減らそうというものです。

ガウシアンフィルタとは?

ガウシアンフィルタでは、対象画素に近い画素に大きな重みをつけ、遠い画素には小さい重みを付けた加重平均を取ることで、自然なぼかしを実現しています。この重みづけを下のガウス関数を用いて行っているのでガウシアンフィルタと呼ばれます。

この式からも分かるようにσの値が小さいとぼかし効果は小さく、σの値が大きくなるとそれに従ってぼかし効果も大きくなります。
 
 
ガウシアンフィルタはボックスフィルタに比べて非常にきれいなぼかしが可能です。ボックスフィルタの場合、ぼかしを強くしたとき画質劣化がひどいのに対して、ガウシアンフィルタでは自然なぼかしを実現しています。


 
 
 

高速化のアイデア

ガウシアンフィルタを高速化する方基本的なアイデアは、上にも書いたように、時間のかかる畳み込み演算の部分をフーリエ領域に持ち込んで、計算量をへらそうというものです。図示するとこんな感じ。

cvtemplatematch5

 
空間領域(spatial domain)での畳み込み演算がフーリエ領域(fourie domain)では乗算に帰着できる理論はこの辺の論文を参考にしてみてください。

  • P. M. Morse and H. Feshbach, “Fourier transforms,” in Methods of Theoretical Physics (Part I, pp. 453–471), New York: McGraw-Hill, 1953.
  • R. Bracewell, “Convolution” and “Two-dimensional convolution,” in The Fourier Transform and Its Applications (pp. 25–50 and 243–244), New York: McGraw-Hill, 1965.

 
 
 

ガウシアンフィルタの高速化

1. たたみ込み積分での処理

畳み込み演算をした場合には、下の図のようにガウシアンフィルタのカーネルと、対象画像の画素との畳み込み演算( convolution )を行うことで処理を行います。

ガウシアンフィルタのカーネルサイズをMxM、対象の画像サイズをNxNとした場合、たたみ込み積分を行うと計算量は O(N2M2)になります。このように、畳み込み演算では、ガウシアンフィルタのカーネルサイズが大きくなればなるほど処理時間がかかるという問題があります。
 
 
 
2. フーリエ変換を用いた処理

一方で、DFTを用いて処理した場合の処理の流れは下のようになります。対象画像をDFTでフーリエ変換し、その結果に対して高周波部分を全て0にするようなマスクをかけます(乗算処理)。こうすることで高周波部分が除去され、ローパスフィルタをかけたのと同じ効果が得られます。
 

DFTを用いた場合の計算量はO(N2logN)に比例します。このように、DFTを用いることでガウシアンフィルタのカーネルサイズにかかわらず一定時間で処理できるようになるのです。
 
 
 

高速ガウシアンフィルタのプログラム

上に書いたように、DFTを用いた高速ガウシアンフィルタのプログラムのコア部分を載せます。このプログラムはこの本を参考させて頂きました。

詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識
Gary Bradski Adrian Kaehler
オライリージャパン
売り上げランキング: 15137

この関数の引数、srcとfilterがそれぞれ元画像とガウシアンフィルタのカーネルに相当します。まずは、それぞれの行列をcvDFT()関数を用いてフーリエ変換し、その結果どおしをcvMulSpectrums()関数で乗算し、最後に逆フーリエ変換することで、ぼかしのかかった画像を作成しています。
 

void speedy_conv (
	 const CvMat* src, 
	 const CvMat* filter,
	 CvMat*	dst
 ){
	int dft_M = cvGetOptimalDFTSize( src->rows+dst->rows-1 ); 
	int dft_N = cvGetOptimalDFTSize( src->cols+dst->cols-1 );
	CvMat* dft_A = cvCreateMat( dft_M, dft_N, CV_32F ); 
	CvMat* dft_B = cvCreateMat( dft_M, dft_N, CV_32F); 
	CvMat tmp;

	// copy A to dft_A and pad dft_A with zeros 
	// 
	cvGetSubRect( dft_A, &tmp, cvRect(0,0,src->cols,src->rows)); 
	cvCopy( src, &tmp );
	cvGetSubRect( dft_A, &tmp, cvRect( src->cols, 0, dft_A->cols-src->cols, src->rows));
	cvZero( &tmp );

	// no need to pad bottom part of dft_A with zeros because of 
	// use nonzero_rows parameter in cvDFT() call below 
	// 
	cvDFT( dft_A, dft_A, CV_DXT_FORWARD, src->rows );
	
	// repeat the same with the second array 
	// 
	cvGetSubRect( dft_B, &tmp, cvRect(0,0,filter->cols,filter->rows) ); 
	cvCopy( filter, &tmp ); 
	cvGetSubRect(dft_B, &tmp, cvRect( filter->cols, 0, dft_B->cols-filter->cols, filter->rows ));
	cvZero( &tmp );
	
	// no need to pad bottom part of dft_B with zeros because of 
	// use nonzero_rows parameter in cvDFT() call below 
	// 
	cvDFT( dft_B, dft_B, CV_DXT_FORWARD, filter->rows );
	
	// or CV_DXT_MUL_CONJ to get correlation rather than convolution 
	//
	cvMulSpectrums( dft_A, dft_B, dft_A, 0 );
	
	// calculate only the top part 
	// 
	cvDFT( dft_A, dft_A, CV_DXT_INV_SCALE, dst->rows ); 
	cvGetSubRect( dft_A, &tmp, cvRect(0,0,dst->cols,dst->rows) );
	cvCopy( &tmp, dst );
	cvReleaseMat( &dft_A );
	cvReleaseMat( &dft_B );
}

ドライバプログラムも含めたソースファイルをここに置いておきます。参考にしてみてください。

高速ガウシアンフィルタプログラム
 
 

OpenCVでQRコード検出器を書く

1月 20th, 2011
このエントリーを含むはてなブックマークはてなブックマーク - OpenCVでQRコード検出器を書く

qrcodedetector0
 
 
OpenCVを使ってQRコードを検出するプログラムを作成したので、その手順をまとめてみた。このプログラムはlibdecodeqrを参考にさせていただきました。(本家のサイトは閉鎖してしまっているようです)

作成したプログラム+ドライバプログラムを置いておきますので参考にしてください。このプログラムを用いて下記にQRコード検出アルゴリズムを紹介していきます。
 
QRコード検出器プログラム
 
 
 

1. 画像中から正方形の部分を検出する

qrcodedetector1
 
まずは、ファインダパタン(3隅にある目玉画像)を検出するために、画像中から輪郭を抽出し、抽出された輪郭から正方形の輪郭のみを保存します。
 
具体的には、まずcvFindContoursメソッドを用いて輪郭情報をcont変数に格納します。次に格納された輪郭情報のうち正方形のもののみを検出するため、面積と縦横比をチェックし、そのチェックに通ったもののみcandidates変数に保存します。
 
 

cvFindContours(tmp,
				   _stor_tmp,&cont,sizeof(CvContour),
				   CV_RETR_LIST,CV_CHAIN_APPROX_NONE,cvPoint(0,0));
	
	// for marker candidates list
	CvSeq *candidates=cvCreateSeq(CV_SEQ_ELTYPE_GENERIC,
								  sizeof(CvSeq),sizeof(ImageReaderCandidate),
								  _stor_tmp);
	//
	// check each block
	//
	CvSeq *cont_head = cont;
	for(; cont; cont = cont->h_next ){
		CvRect feret = cvBoundingRect(cont);
		double area = fabs(cvContourArea(cont));
		double area_ratio = area/(double)(feret.width*feret.height);
		double feret_ratio = ((feret.width<feret.height)?
							(double)feret.width/(double)feret.height:
							(double)feret.height/(double)feret.width);
		
		//
		// search square(正方形を探索)
		//
		if(area>=MIN_AREA && area_ratio>=MIN_AREA_RATIO && feret_ratio>=MIN_FERET_RATIO){
			ImageReaderCandidate c;
			c.feret.x=feret.x;
			c.feret.y=feret.y;
			c.feret.width=feret.width;
			c.feret.height=feret.height;
			c.contour=cont;
			cvSeqPush(candidates, &c);
		}
		else{
			cvClearSeq(cont);
		}
	}

 
 
 

2. ファインダパタンの検出

qrcodedetector2
 
Step1で検出された正方形の中から、ファインダパタンを見つけるため、正方形の中にもう一つ正方形が含まれている箇所を検出し、seq_finder_patternに追加します。
 
ファインダパタンの位置を示す型としてCvBox2D型を使用しています。このCvBox2D型はCvRect型と同様、長方形を表す型ですが、CvBox2D型では回転まで考慮できるため傾いた長方形にも対応できるのがCvRect型との違いです。
 
 

	for(i = 0; i < candidates->total; i++){
		ImageReaderCandidate *cand1= (ImageReaderCandidate *)cvGetSeqElem(candidates,i);
		
		int inner_contour=0;
		int j;
		for(j = 0; j < candidates->total; j++){
			if(i == j) continue;
			
			ImageReaderCandidate *cand2 = (ImageReaderCandidate *)cvGetSeqElem(candidates,j);
			CvRect max_rect=cvMaxRect(&(cand1->feret),&(cand2->feret));
			if(cand1->feret.x==max_rect.x&&
			   cand1->feret.y==max_rect.y&&
			   cand1->feret.width==max_rect.width&&
			   cand1->feret.height==max_rect.height)
				inner_contour++;
		}
		
		//
		// There were 2 squires (white and black) inside a squire,
		// the most outer squire assumed as position marker.
		//
		if(inner_contour==2){
			CvBox2D box = cvMinAreaRect2(cand1->contour);
			cvSeqPush(seq_finder_pattern, &box);			
		}
	}

 
 
 

3. QRコードの輪郭を抽出する

 qrcodedetector3
 
検出された3つのファインダパタンを用いてQRコードの輪郭を抽出します。ここではQRコードの輪郭を、ファインダパタンをすべて含むような矩形として定義しています。
 
具体的には、cvBoxPoints関数を用いて、ファインダパタンを示すCvBox2D型の変数から4つの頂点座標を取得し、合計12個の頂点座標を含むような矩形をcvMinAreaRect2関数で作成します。最後に再びcvBoxPoints関数を使って頂点座標を検出することでQRコードの4つの頂点座標を取得しています。
 
 

	int c = seq_finder_pattern->total, i;
	for(i = 0; i < c; i++){
		
		box = *(CvBox2D *)cvGetSeqElem(seq_finder_pattern,i);
		_finderpattern_boxes&#91;i&#93; = box;

		cvBoxPoints(box, pt_32f);
		for(int j = 0; j < 4; j++){
			CvPoint p = cvPointFrom32f(pt_32f&#91;j&#93;);
			cvSeqPush(markers_vertex,&p);
		}
	}
	
	//
	// create Minimal-area bounding rectangle which condist
	// every position makers
	// 点列を包含する最小矩形をboxに格納
	//
	box = cvMinAreaRect2(markers_vertex);
	cvRelease((void **)&markers_vertex);
	
	//
	// create code area mask
	// pointsに四隅の座標がはいる
	//
	cvBoxPoints(box, pt_32f);
	CvPoint *points=new CvPoint&#91;4&#93;;
	for(i = 0; i < 4; i++){
		//squarePoints&#91;i&#93;=cvPointFrom32f(pt_32f&#91;i&#93;);
		points&#91;i&#93; = cvPointFrom32f(pt_32f&#91;i&#93;);	
	}

&#91;/cpp&#93;
 
 
 
<span style="font-size:large;color:RoyalBlue"><strong>
4. QRコード部分のみをトリミングする
</strong></span>
<a href="http://iphone.moo.jp/app/wp-content/uploads/g4.jpg"><img src="http://iphone.moo.jp/app/wp-content/uploads/g4.jpg" alt="qrcodedetector4" title="qrcodedetector4" width="150" height="150" class="alignnone size-full wp-image-919" /></a>
 
最後に、検出されたQRコードの4頂点から正方形への射影変換を行います。OpenCVを使った射影変換のコードはここを参考にさせていただきました。
 
<a href="http://chihara.naist.jp/opencv/?%BC%CD%B1%C6%CA%D1%B4%B9">射影変換 | OpenCV@Chihara-Lab.</a>
 
[cpp]
	for(int i = 0; i < 4; ++i){
		a&#91;j++&#93; = p&#91;i&#93;.x;
		a&#91;j++&#93; = p&#91;i&#93;.y;		
	}
    src_point = cvMat( 4, 2, CV_64FC1, a );
	
    CvMat    dst_point;
    double    b&#91;&#93; = {
        0, 0,
        0, dst->height - 1,
        dst->width - 1, dst->height - 1,
        dst->width - 1, 0
    };
    dst_point = cvMat( 4, 2, CV_64FC1, b );
	
    CvMat    *h = cvCreateMat( 3, 3, CV_64FC1 );	
    cvFindHomography( &src_point, &dst_point, h );
    cvWarpPerspective( src, dst, h );

 
 
 

5. まとめ

OpenCVでQRコードの検出器を実装してみました。検出の流れとしては以下のようになります。

  • 画像中かの輪郭情報を取得し
  • 得られた輪郭情報から正方形を探索
  • 正方形が2重になっているものがファインダパタン
  • ファインダパタンから4頂点を決定
  • その頂点座標を用いて射影変換

 
 
その他の検出方法
 
QRコードを機械学習から検出しちゃおうという面白いサイトがありましたので、ここに紹介しておきます。Haar cascadeも公開されているので一度試してみてはいかがでしょうか?

QRコード検出 | CanI’s Diary

アイデアを生み出す13の方法

1月 13th, 2011
このエントリーを含むはてなブックマークはてなブックマーク - アイデアを生み出す13の方法

 


 
 

アイデアを出すときには、自分の中にある情報雲とでも言うべき”もやもや”っとしたモノから数カ所をちぎりとり、型を変えて、見方を変えて、姿をかえて、そして本質をつくりだす。その火種となる情報雲を絶やさないように・・・。
 
 
 

ちょっと慣れてきた人の為のハテブの使い方

効率よく人気記事を探すためには、はてなブックマークの人気エントリーや注目エントリーを利用するのが一番便利だと思います。このサイトでは、これらのエントリーだけでなく、一歩踏み込んだ使い方まで解説されているので、はてなブックマークをさらに利用したい方にはオススメです!
 
 
 

今さら聞けない?初心者に送る「Twitter」の始め方・使い方

インスタントでライトなコミュニケーションはもとより、研究や技術といった深〜い情報の交換までできてしまう優れもの。ただ、RTや@や#など、使われる用語が特殊なため、少し敷居が高いと感じてしまう人も多いはず。そこで、このサイトを読んで、Twitterの世界へ飛び込んでみてはいかがでしょう?
 
 
 

ウェブを保存して後で読む「Instapaper」のある快適生活の始め方

ウェブの情報を保存しておいて、後からモバイルデバイスで読むことができるようにする「Instapaper」。オフラインでも閲覧可能なため、大量の情報を読みこなすには必須のアイテムです。このサイトではInstapaperの設定方法から使い方まで詳しく説明されています。
 
 
 

整理するということは、ものに属性をつけ、カテゴリで縛ること。そうやって、整理していけば、どんどん自由度が失われて、どんどん不自由に、そしてどんどん明快になっていく。すべてを曖昧なまま、そして、自由なままにしておきたい。そうすれば、いろんな物がいろんな次元で繋がり始める。
 
 

机の上も頭の中もスッキリ!?Evernoteの使い方みつけたよ!

Evernoteは気に入ったもの、気になったもの、ピンと来たもの、何でも全て放り込める道具箱のような存在。このサイトでは、そんなEvernoteの様々な利用方法が紹介されています。
 
 
 

素敵BOXという名のフォルダ

Evernoteも便利なんですけれども、やはりフォルダの手軽さには及びません。僕のデスクトップの左上にはいつも素敵BOXという名のフォルダがあって、綺麗なものを全部キャプチャしてほうりこんでいます。
 
 
 

RHODIA

メモ帳といえば、Windows・・・じゃなくてRHODIAのメモ帳です。このメモにとにかくアイデアを走り書きしています。方眼紙になっているので、図を描きやすいのが特徴ですね。
 
 
 

 
電車でぼ〜っと流れる景色を眺めているとき、風呂でぼ〜っと天井を眺めているとき、そんな時にアイデアは降ってくる、決して必死で考えているときには得られない素晴らしいインスピレーションが。たまにはゆとりを持って映画や本やアニメを見るのも一興です。
 
 
 

ヒックとドラゴン

2010年に見た映画ではイチオシ。いつも映画は予告編が一番面白い!と言っているのですが、予告編よりも遥かに面白かった希少な映画。主役のドラゴンが猫にしか見えない(笑)猫好きなら絶対見とけ、的な映画です。
 
 
 

イリヤの空、UFOの夏

「イリヤの空、UFOの夏」は僕が初めて読んだライトノベルス。毎年、夏になると無性に読みたくなる本です。とにかくこの作家さん、情景を書くのが上手くて本を読んでいても映像として入ってくるのがすごい!
 
 
 

電脳コイル

日本のAR(Augmented Reality)研究者でこのアニメを知らなかったらモグリだろ?っていうくらい有名なアニメ。Webとバーチャルの世界に生きる現代人に痛快なメッセージを届けてくれます。そしてなにより、素晴らしく面・白・い!!
 
 
 

先人のアイデアにインスパイアされてみよう。 優れたものを見たとき、確実に自分の中の想像力、もしくは発想力の根源がざわつく。それは聞こえないほど微小かもしれない、触れられないほど微細かもしれない。でもきっと。
 
  
 

インターネットミュージアム

家の近くにある美術館や博物館を検索できるほか、ジャンル別でイベント情報なども検索したり、タイムリーなニュースを読むことも出来ます。
 
 
 

Klimt HD

グスタフクリムトの画集がiPadで鑑賞できるアプリ。他にもゴッホの作品を見ることが出来るVincent van Gogh – Classic Painters Galleryや、1000以上の有名な芸術家による4万点の絵画や彫像を鑑賞することができるArt Authority for iPadなどのアプリもあります。