さやべえのARコンテンツをWebブラウザで

そういえば、なぜ前回がGitHubでWeb公開の話になったかといいますと、実はこれのためだったのだ!!!
というわけで、今日はWebブラウザでみられるARコンテンツを作ったお話です。

【WebブラウザでAR?なにそれ?】
百聞は一見に如かず。まずはこちらをご覧ください。
(カメラ付きのWindows PCで、Google ChromeかFireFoxで開いてください)
https://revetronique.github.io/sayabeewebar/
hiro_marker.png
開いたら、上の画像(Hiroマーカー)を印刷するか、スマホなどで出してカメラの前に置くと、
なんと3DCGがマーカーの上で踊るのが見られます。
sayabee_test1.png
技術の進歩ってすごい...

ソースコードはこちら。
https://github.com/Revetronique/sayabeewebar/tree/master/docs

【簡単な解説】
AR.jsというライブラリを使いました。
https://github.com/jeromeetienne/AR.js/blob/master/README.md

これを導入すると、PC側には何もインストールせずにブラウザを開くだけでマーカーARのコンテンツが楽しめます。
技術的な詳細は、AR.jsやThree.jsなどのGitHubを見たほうが早いです...。

ただ、使い方とか備忘録を兼ねて残しておくのも良さそうなので、
時間ができたら書いていきたいです。

テーマ : プログラミング
ジャンル : コンピュータ

開発したWebコンテンツをGitHubですぐに公開する

おはようございます、Reveです。
つい先日、とあるWebコンテンツを作りたくて夜通しで開発をしていたのですが、簡単にネット上で公開できないか調べていたところ、GitHubを使えば可能だと知り、さっそく試してました。

今日は、そんなGitHubを使ったネット上でのWeb公開の手順をつづっていきます。

【なぜGitHubを使うのか】
(1)準備するコストが低い
一般的に、ネット上で何かを公開する際は「ドメインを買う」、「サーバー用のPCを用意する」、「サーバー用ソフトウェアのインストール」などの準備が必要となってきます。
最近ではクラウドサーバーも出てきたので実機のPCを購入する必要はないのですが、やはり月額の利用料金といった金銭的なコストがかかってしまいます。

一方で、GitHubはソフトウェアの導入からネット上での公開まで、全て無料で行えます。

ただしGitの概念やGitHubの使い方といった、学習コストはある程度必要になります。
とはいえ、今回の目的で必要な知識はあまり多くはないですし、後から覚えていっても問題ありません。

(2)ローカル上でテストする際のエラーを避ける
HTMLファイル自体はわざわざネット上に公開せずとも、PC内のフォルダから選んでブラウザで開くことも可能です。
ですが、処理内容によってはローカル環境での実行が原因のエラーを発生することがあり、解決のためにサーバーを用意する必要性が出てくることがあります。

(ex. Google Chromeでローカル上のファイルを読み込もうとすると「XMLHttpRequest cannot load」というエラーが発生)
Qiita [*その他*] ChromeにてAjaxでローカルファイルにアクセス
http://qiita.com/cigalecigales/items/33afaa42f91542ffa62e

XAMPPなどを使い、ローカルサーバーを立てるという手段もありますが、わざわざインストールしてサーバーを立ち上げるというのも面倒なため、代わりにGitHub上にアップすればすぐに条件を満たすわけです。

(3)ソース管理にもってこい
これはGit自体の特徴ですが、ソースの変更や前の状態に戻すといった操作も簡単に行える(というか、後者はローカル環境では難しい)ので、管理が楽になります(あと、結果としてネット上にバックアップも残ります)。

ただし、今回のようにネット上へWebサイトやコンテンツとして公開する場合、少なくともソースも公開する必要があるため、ソースコードを秘密にしたい場合は今回の手法は使えません。
(非公開にすること自体は可能ですが、Webサイトとして開けなくなります。)

【公開の手順】
では、一通り理由を説明したところで、いよいよ手順を書いていきます。

(1)リポジトリを新規作成
とにかく、まずはリポジトリを作ります。名前はなんでもOK。
ただし、公開(Public)設定にしておく必要があります。
github_web1.png

(2)ローカル環境へクローン
リポジトリを作成したら、次はローカル環境へリポジトリをクローンします。

要は、自分のPCにリポジトリをコピーすることなのですが、GitHubのソフトウェアをインストールしている場合、「Set up in Desktop」というボタンを押せば、自動的にソフトが起動してクローンの手続きに入れます。
(ターミナルソフトを起動してコマンドを打ちこんでも可能です。)
github_web3.png

ちなみに、すでにファイルなどをアップロードしたリポジトリの場合は下のような画面になります。
github_web2.png

(3)コミット&同期(Sync)
PCにリポジトリをダウンロードした後は、必要なファイルを入れてネット上(リモート)のリポジトリと同期します。

まずはGitHubソフトウェアを起動します。
起動したら、左のリストからリポジトリを指定し、「Sync」ボタンの真下にあるバーの一番右の丸をクリックすると、変更をコミットする画面が出てきます。

github_web5.png
ファイルを入れる場所は、手順(2)でリポジトリを落とす場所を指定するのですが、そこで作られたフォルダ内に入れるとリポジトリが変わったことを認識してくれるので、概要(Summary)と詳細(Description)を入力してコミットボタン(Commit to ○○)を押すとコミットは完了です。

コミットが終わったら、右上の「Sync」ボタンを押してリモートと同期させます。
なお、変更を同期してからWebページに反映されるまで少し時間がかかるので、ページの確認はしばらく待ってからしたほうがよいでしょう。

(4)Webページ設定
リモートのリポジトリに変更が反映されたら、GitHubのWebページから作成したリポジトリのページに移動し、「Setting」メニューを選択します。

少し下に「GitHub Pages」という項目があるので、「Source」を「master branch」にすれば、masterブランチ全体をWebページとして公開できるようになります。
github_web4.png

(5)Webページへのアクセス
あとは、WebブラウザでURLを入力すればページが表示されます。
URLは 「https://(ユーザー名).github.io/(レポジトリ名)」 となっています。

以上がすべての手順となっています。
あとは、作ったWebコンテンツを

【参考】
GitHub Pages 公式チュートリアル
https://pages.github.com/
Qiita GitHubを使って3分でHPを公開する。
http://qiita.com/budougumi0617/items/221bb946d1c90d6769e9

ただ、公式ページは少し情報が古い(?)ので、本記事と一部異なる記述があります。

【雑記】
Gitの使い方は適当に使ったり調べたりして覚えていったのですが、知識を整理したいとも思っているので、解説書として最適な本を探していたりします。
とりあえず、入門書的なものとより専門的なもの2冊で考えてたりします。

まず、これを入門書として読み、改めてGitを理解しようかなと。


というのも、CodeIQ Magazine で読んだこの特集がとても分かりやすかったので、本でも読んでみたいなと思ったからで。
https://codeiq.jp/magazine/category/git-ai/

より専門書的なものだと、例えばこの書籍でしょうか?

テーマ : webサイト作成
ジャンル : コンピュータ

AndroidのAlertDialogで躓いた話

どうも、Reveです。
最近Androidアプリの開発をする機会がぐんと増えたのですが、なんか初歩的なところで躓いていたので備忘録でも残します。

【ボタンを押すとエラーで落ちる】
エラーが発生したのは、ボタン(FAB)を押すとAlertDialogが呼ばれる機能を実装していた時のことで、
ビルドは通るものの実行してボタンを押すと、以下のエラーが発生して強制終了してしまいました。

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity

どうやらTheme.AppCompatというテーマを適用する必要があると...
かれこれ色々悩んでいたのですが、原因はAlertDialog.Builderを作る際のContextにありました。

【原因】
AlertDialog.Builderのインスタンスを作る際、引数にContextを指定する必要があるのですが、
これが曲者で、間違えてApplicationContextを指定していたのが原因でした。

//AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext()); //エラー発生
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); //正解

正確には、builder.show()を実行してダイアログを表示するときにエラーが発生するのですが、getApplicationContextで得られるContextでは、どうやらテーマのリソースにアクセスできないようでした。

そのため、ActivityのContextを取得することでエラーが解消された、とみています。
いや、Androidアプリの開発はめんどくさい...(ただの勉強不足)

【参考】
ちなみに、対処法はここで見つけました。
(stack overflow: Android AlertDialog error during showing)
https://stackoverflow.com/questions/41894944/android-alertdialog-error-during-showing

どうやら、以前にも、同じような現象に陥っていた方がいらっしゃいました。
(SLUMBERS: Android で アラートダイアログを表示しようとすると落ちる)
http://slumbers99.blogspot.jp/2012/01/android.html

また、こちらの記事では各Contextの違いについてまとめられており、記事の内容からテーマのリソースなどを参照するにはActivityのContextが必要で、getApplicationContextでは取得できないと推察しているのですが、果たして正解なのか...
(Yukiの枝折: Android:引数はthisか?getApplicationContextか?ActivityとApplicationの違い)
http://yuki312.blogspot.jp/2012/02/thisgetapplicationcontextactivityapplic.html

テーマ : Android
ジャンル : 携帯電話・PHS

07/19のツイートまとめ

Revetronique

Oculus rift + touchが5万!?安い! 買える!! https://t.co/ECGZKYVv3I
07-19 00:56

UnityでOpenCV その4

さて、なんやかんや2か月以上も過ぎていましたがorz
いよいよ前回の実装に入ります。
OpenCVUnity_findcontour1.png

抽出処理については、基本的にこちらの記事を参考にしました。
OpenCVで輪郭抽出から隣接領域の切り出し(その1)輪郭抽出まで

では、抽出処理の流れに沿って、それぞれの処理を見てみましょう。
なお、ここから先は画像処理に関係する処理を主に抜粋して載せています。
追記に当方の作成した全ソースコードを載せているため、先にそちらをご覧いただいてもかまいません。

【前処理】
まずは、元画像、および画像処理とマスク用のMat型インスタンスをそれぞれ用意しておきます。
画像処理用のMatには、元画像のグレースケールを格納します。

//元画像を、リソースフォルダからTexture2Dとして読み込む
Texture2D texture_src = Resources.Load(texturePath) as Texture2D;

//元画像のMatを用意
Mat imgMat= new Mat(src.height, src.width, CvType.CV_8UC4);
//Texture2DをMatに変換(OpenCVUnityのUtilクラス内メソッド)
Utils.texture2DToMat(src, imgMat);

//画像処理用Mat画像
Mat mat_proc = new Mat(imgMat.rows(), imgMat.cols(), CvType.CV_8UC1);
//元画像のグレースケールを入れておく
Imgproc.cvtColor(imgMat, mat_proc, Imgproc.COLOR_RGBA2GRAY);
//マスク用Mat画像
Mat maskMat = new Mat(imgMat.rows(), imgMat.cols(), CvType.CV_8UC1);


また、二値化処理をかけて輪郭抽出に適した画像(モノクロ画像)にしておきます。
二値化処理はいくつか方法があり、まず手動で調整するとこのようになります。

//mat_procは画像処理の対象(Mat)、thresは二値化の閾値(0~255)
//Imgproc.THRESH_BINARY_INV: 閾値より大きい値は0に,それ以外はmaxValに
Imgproc.threshold(mat_proc, mat_proc, thres, 255, Imgproc.THRESH_BINARY_INV);


ただ、閾値は画像の状態によって大きく左右されるため、いちいち手動で値を設定するのは面倒です。
そこで、画像に適した閾値を自動で計算してくれるアルゴリズムを用いた方法もあるため、下で紹介していきます。
まずは大津の手法による二値化処理です。

//大津の手法による、単純2値化処理
Imgproc.threshold(mat_proc, mat_proc, 0, 255, Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);


ただし、先ほどの手法でも画素内の明るさが大きく異なる部分があると、処理の結果が芳しくないことがあります。
そこで、それぞれの場所に応じた閾値を設定できる、適応的閾値処理を使うことも可能です(引数の詳しい説明などは省きます)。

//GaussianCによる適応的閾値処理
//adaptiveMethod: Imgproc.ADAPTIVE_THRESH_MEAN_C と分けて使う
Imgproc.adaptiveThreshold(mat_proc, mat_proc, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 201, 11);


【輪郭抽出】
ここまで、輪郭抽出に必要な二値化画像が手に入った段階です。
いよいよ輪郭の抽出に入っていきます。

まずは全体の流れから見ていきましょう。

//procMode: 輪郭抽出の手法を決める列挙型ExtractMode
//ThresArea: 輪郭を抽出したい領域の最小面積

//輪郭
List<MatOfPoint> contours = new List<MatOfPoint>();

//処理状態によってマスク処理に使うMat画像を切り替える
//2値化画像を直接使う
if (procMode == (int)ExtractMode.BINARY)
{
//縮小処理(erode)
Imgproc.erode(maskMat, maskMat, new Mat(), new Point(-1, 1), 1);
//コピー
mat_proc.copyTo(maskMat);
}
//2値化画像の輪郭を切り取って使う
else if (procMode == (int)ExtractMode.CONTOUR)
{
//輪郭抽出の処理
Imgproc.findContours(mat_proc, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
//Imgproc.findContours(mat_proc, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); //輪郭内にある輪郭もツリー構造で抽出

//領域(面積)の割合が一定以上の輪郭を探す: getCertainAreaContour
List<MatOfPoint> areas = getCertainAreaContour(contours, ThresArea);
contours.Clear();
foreach (MatOfPoint point in areas)
{
contours.Add(point);
}

//マスク領域の生成
//Imgproc.drawContours の線の太さを負の値にすると、内部の領域も塗りつぶす
Imgproc.drawContours(maskMat, contours, -1, new Scalar(255), -1);
}
//2値化画像の輪郭を直線近似して領域を求める
else if (procMode == (int)ExtractMode.POLYLINE)
{
//輪郭抽出の処理
Imgproc.findContours(mat_proc, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

//領域(面積)の割合が一定以上の輪郭を直線近似
List<MatOfPoint> areas = getCertainAreaContour(contours, ThresArea);
contours.Clear();
foreach (MatOfPoint point in areas)
{
MatOfPoint approxf = getLineApproxContour(point, 0.001);
contours.Add(approxf);
}

//マスク領域の生成
Imgproc.fillPoly(maskMat, contours, new Scalar(255));
}
//2値化画像の輪郭を凸包して領域を求める
else if (procMode == (int)ExtractMode.HULL)
{
//輪郭抽出の処理
Imgproc.findContours(mat_proc, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

//領域(面積)の割合が一定以上の輪郭を直線近似
List<MatOfPoint> areas = getCertainAreaContour(contours, ThresArea);
contours.Clear();
foreach (MatOfPoint point in areas)
{
MatOfPoint mopOut = getHullContour(point);
contours.Add(mopOut);
}

//マスク領域の生成
Imgproc.fillPoly(maskMat, contours, new Scalar(255));
}


実は、上記のスクリプトでは一部、自作のメソッドを用いていたのですが、
各メソッドの実装内容は以下の通りです。

/// <summary>
/// 抽出した輪郭リストから一定以上の面積を持つ輪郭を返す
/// </summary>
/// <param name="contours">輪郭のリスト</param>
/// <param name="thresArea">面積の閾値</param>
/// <returns>面積が閾値以上の輪郭すべて</returns>
List<MatOfPoint> getCertainAreaContour(List<MatOfPoint> contours, double thresArea = 10)
{
List<MatOfPoint> result = new List<MatOfPoint>();

foreach (var each in contours)
{
//面積を求める
double area = Imgproc.contourArea(each);
if (area > thresArea) result.Add(each);
}

return result;
}

/// <summary>
/// 輪郭(MatOfPoint)から直線近似を施した輪郭を返す
/// </summary>
/// <param name="mopIn">変換元の輪郭(MatOfPoint)</param>
/// <param name="approxRate">カーブの細かさ</param>
/// <returns>変換後の輪郭</returns>
MatOfPoint getLineApproxContour(MatOfPoint mopIn, double approxRate = 0.001)
{
MatOfPoint approxf1 = new MatOfPoint();
MatOfPoint2f curveContour = new MatOfPoint2f(mopIn.toArray());
MatOfPoint2f approxContour = new MatOfPoint2f();
Imgproc.approxPolyDP(curveContour, approxContour, approxRate * Imgproc.arcLength(curveContour, true), true);

//直線近似した輪郭を変換
approxContour.convertTo(approxf1, CvType.CV_32S);

return approxf1;
}

/// <summary>
/// 輪郭(MatOfPoint)から凸包近似を施した輪郭を返す
/// </summary>
/// <param name="mopIn">変換元の輪郭(MatOfPoint)</param>
/// <returns>変換後の輪郭</returns>
MatOfPoint getHullContour(MatOfPoint mopIn)
{
MatOfPoint mopOut = new MatOfPoint();

//輪郭を凸包
MatOfInt hull = new MatOfInt();
Imgproc.convexHull(mopIn, hull);

//凸包した輪郭を変換
int size = (int)hull.size().height;
mopOut.create(size, 1, CvType.CV_32SC2);
for (int i = 0; i < size; i++)
{
int index = (int)hull.get(i, 0)[0];
double[] point = new double[] { mopIn.get(index, 0)[0], mopIn.get(index, 0)[1] };
mopOut.put(i, 0, point);
}

return mopOut;
}


【仕上げ】
ここまで、画像の特定の領域に対する輪郭抽出ができました。
ですが、わずかに残るノイズを処理するため、以下の処理を加えて不要な部分を除去します。

//ノイズ除去
//縮小処理(erode)
Imgproc.erode(maskMat, maskMat, new Mat(), new Point(-1, 1), 1);
//膨張処理(dilate)
Imgproc.dilate(maskMat, maskMat, new Mat(), new Point(-1, 1), 1);
//オープニング
Imgproc.morphologyEx(maskMat, maskMat, Imgproc.MORPH_OPEN, new Mat());


【マスク処理】
あとは抽出できた輪郭を使い、画像の一部分だけを取り出す処理を行ってみましょう。

//マスク処理
Mat imgResult = new Mat(src.height, src.width, CvType.CV_8UC4, new Scalar(255, 255, 255));
imgMat.copyTo(imgResult, maskMat); //元画像のマットにマスクを当てはめたうえで結果画像(Mat)にコピー


こうして、輪郭抽出の大まかな流れは終わりです。
以上の処理をもとに輪郭抽出した結果はこちらです。
OpenCVUnity_findcontour2.png

Unityで画像処理をする機会は少ないかもしれませんが、当方の記事が少しでも参考になれば幸いです。
ではまた。

続きを読む

07/08のツイートまとめ

Revetronique

東大制作展(SUKIMANIAC)では、吊革でゲームができるコミュニケーション作品や、グネグネと動いたり固くなったりする3Dプリンタで作成された作品や、言葉が所々欠けていても脳が補填して聞こえるという作品もありました! https://t.co/dXn9XY5iJT
07-08 16:19

東大制作展SUKIMANIAC(スキマニアック)を見学!アプリで2着連動可能なLED Tシャツは黒Tが良かった!HMDはいらないんじゃないかという、大きな半円の中に顔を埋めて動画を見る作品や、アクリル板で作られた透明な地図上で玉を… https://t.co/GRe4FNRIk6
07-08 16:12

07/03のツイートまとめ

Revetronique

4DマシンSWAY CHAIRにも1時間並んでギリギリ。GearVR用コントローラーを長押ししてのシューティングゲーム。UIなのか、使われすぎてなのか、中の展示と違ってトリガーが押せているのか不明…でも5万点超えたからできてたのか… https://t.co/h3tTeuldWK
07-03 20:45

原宿のBANK GALLERYにて、Gear VRを体験中。一番乗りたかったVR PULSAR、これを体験するために来て何度も並んだけれどこれだけ乗れず。挙げ句、メンテナンス中…残念な最終日の #galaxystudio 一回転し… https://t.co/DbmdoSDO2K
07-03 19:18

RT @GalaxyMobileJP: #GalaxyStudio Tokyo🎢5/26(金)スタート❗#GalaxyS8 や日本初上陸のVRアトラクションが勢揃い!GalaxyのSNSフォロー&RTで特典も。詳しくは会場で✨詳細👉 https://t.co/CL3CXC
07-03 16:26

07/01のツイートまとめ

Revetronique

初めての番組協力The music day@幕張メッセに来ております!めっちゃ楽しみ‼ #musicday https://t.co/jBNXNMNxF5
07-01 17:17

プロフィール

Reveちゃん

Author:Reveちゃん
コンビでやってます。
夢担当と技術担当がいます。

大学院卒業 → ロボットベンチャー(漆黒)就職 → 1年で退職 → ベトナムで仕事中(今ここ) → メディアアーティスト(未来☆)

リンクフリーです。

最新記事
最新コメント
月別アーカイブ
カテゴリ
アクセス数
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR