FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »画像ファイル読み込み
このページの記事一覧

【Unity】【C#】BMP をランタイムで読み込む  


 実は VRM Live Viewer には既に導入されているのだが、BMP を画像素材(テクスチャ)として利用したいこともあるだろう。実装当時、ググっても英語サイトしか出てこなかったので、需要は少ないのかも知れないが、探すのに苦労しないように備忘録として残しておく。

 今回紹介する方法は、オープンソースの BMPLoader を使う方法だ。

 以前「ランタイム時にファイルをドラッグ&ドロップして取得する」で紹介した「UnityWindowsFileDrag-Drop」の作者の Bunny83 さんの配布ライブラリで、とても簡単に扱えたので、その方法を書いておこう。


(※) Unity 2018.4.8f1 / Windows10(x64) で確認



■BMPLoader を導入する

 導入方法は簡単だ。以下の公開サイトへ行き、コピペで C# ファイルを作って欲しい。ページの一番下の方に「Raw Paste Data」という欄があるので、ここからコピーするのが簡単だろう。

BMPLoader.cs





■簡単なサンプルを作ってみる(基本的なコード)

 ググってみるといくつか出てくるので、以下などを参考にするのも良いだろう。

How to load a BMP file in binary?
How can I use a .bmp file and create a Texture in Unity at runtime?

 実際には BMPLoader.LoadBMP() はいくつかオーバーロードがあるので、利用方法によって使い分けるのも良いかも知れない。ここでは後述する「非同期ロードに対応してみる」に合わせた書き方となっている。定義をみて自分なりに書き換えるのも良いだろう。

●BMPLoadTest.cs(※名前は任意)
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using B83.Image.BMP;

public class BMPLoadTest : MonoBehaviour
{
public RawImage image; //読み込み先(※インスペクタで UI を設定)
public string filePath = "c:/test/image/sample.bmp"; //※ファイルは任意

// Use this for initialization
private void Start()
{
var bytes = File.ReadAllBytes(filePath);
var loader = new BMPLoader();
//loader.ForceAlphaReadWhenPossible = true; // can be uncomment to read alpha
var bmpImage = loader.LoadBMP(bytes);
image.texture = bmpImage.ToTexture2D();
}

private void OnDestroy()
{
if (image.texture != null)
{
Destroy(image.texture);
image.texture = null;
}
}
}
#endif

 このサンプルではエラーのチェック等は省略しているので、あくまで全て必要なもの(ファイルとか)が揃っている場合であることを留意しておいて欲しい。

 とりあえず、BMP をロードして表示できたなら成功だ。





■非同期ロードに対応してみる

 とりあえず前述の基本的なコードでも十分なのだが、動作確認が取れたなら、ちょっと手を加えて非同期読み込みすると良いかも知れない。ただし、以下のコードは .NET 4.x 以降である必要がある。

●BMPLoadTest.cs(※名前は任意)
using System.IO;
using System.Threading.Tasks; //※追加
using UnityEngine;
using UnityEngine.UI;
using B83.Image.BMP;

public class BMPLoadTest : MonoBehaviour
{
public RawImage image; //読み込み先(※インスペクタで UI を設定)
public string filePath = "c:/test/image/sample.bmp"; //※ファイルは任意

// Use this for initialization
private async void Start() //※async を追加
{
var bytes = await Task.Run(() => File.ReadAllBytes(filePath)); //※Task で別スレッドで読み込み
var loader = new BMPLoader();
//loader.ForceAlphaReadWhenPossible = true; // can be uncomment to read alpha
var bmpImage = await Task.Run(() => loader.LoadBMP(bytes)); //※Task で別スレッドでロード
image.texture = bmpImage.ToTexture2D();
}

private void OnDestroy()
{
if (image.texture != null)
{
Destroy(image.texture);
image.texture = null;
}
}
}
#endif

 ファイルの大きさにもよるが、私が計測したところ、非同期にすれば約1.5倍くらいの速度で読み込める。もし、UniRx を使っているのなら、Task は UniTask にした方が良いだろう。

 ちなみに私が提供しているプラグインを使えば、Android でも BMP を読み込めた。実際にスマホで全天球ビューワを作ることも可能だ(これが VRM Live Viewer で実装されている)。

 ここでは簡単なサンプルなので Start() でやってしまったが、static なメソッドにしてしまえば、使い回しもできて非常に便利だ。その場合にはファイル名やファイル自体の存在チェック、非同期キャンセルのための CancellationToken など入れた方が良いだろう。Task.Run() は2回に分けてあるが、ファイル読み込み/テクスチャとしてロードするのに時間がかかることを考慮して、それぞれにキャンセルチェックをした方が良いと思う(きちんと実装すると結構長くなるため、今回は要点だけにした)。その辺りはご自由に(笑)。






(関連記事)
【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)
【Unity】スマホで簡易360度(パノラマ, 全天球)ビューワを作る
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】AssetStore版 FantomPlugin のセットアップ


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  画像ファイル読み込み 
tb: 0   cm: --

【Java】【Android】【PHP】画像を PHP から送信して、Android で受信する  


 以前に、JSON (テキストデータ) を PHP から送信して、Java で受け取ることができたので、今回はそれと同じ考え方で、画像の送受信に置き換えてみる。わざわざ PHP を通してダウンロードしているわけだが、もちろん直接画像のURLにアクセスしてダウンロードしても構わない。PHP でダウンロードする利点はファイル名をデータベースなどに入れて置いて、同じURLで内容を入れ替えられる点だ。この方法なら、画像ファイルを直接アクセスできない場所に置いて、ファイル自体を隠蔽化することもできる。

 クライアント側は Java にしたいので、とりあえず Android にする。画像のデコードはプラットフォームごとに異なるので、その部分だけ入れ替えれば、流用もできるだろう。


 PHP のサンプルコードは超絶簡単なものにする。エラーなどはあまり考えてないので、必要なら適当に付け加えた方が良いだろう。とりあえずファイル名を「image_out.php」としておく。

■画像送信用 PHP(image_out.php)
<?php
$fileName = "img/sample.jpg"; //ファイル名

if (file_exists($fileName)) {
header('Content-type: image/jpeg');
header('Content-Length: '.filesize($fileName)); //あった方が良い
readfile($fileName); //ファイルを読み込んで標準出力に書き出し
exit;
}
?>

 header() で Content-type を出力してるので、「<?php」タグより以前に文字列などを書かないように注意。改行1つでも入ると、PHP では text/html として出力されてしまう。

 このサンプルの場合は、画像ファイル名が固定されているので Content-type が固定されているが、データベースなどからファイル名を読み込んで、画像を切替える場合は、拡張子などで Content-type を切り替えるようにした方が良いだろう。

 ちなみに png と gif の Content-type は、
header('Content-Type: image/png');
header('Content-Type: image/gif');

となる。また、Android では gif はサポート対象外なので、あまり使わない方が良いかも知れない。透過したい場合は png が良いだろう。

 一緒に Content-Length も出力しているが、画像ファイルの場合はあった方が良い。ないと以下の Java コードで Content-Length = -1 (不明)となる。あらかじめファイルサイズを取得して、縮小してダウンロードするときなどには必要になるだろう。ちなみにテキストファイル(text/plain)の場合はコードを書かなくても、Content-Length は勝手に付いてくる(書いても問題ない)。


 あとは、Android 側で画像の受信とデコードをする。以前の JSON 用コードとあまり変わらないので、目新しいものは無いかも知れない。また、Android の場合、マニフェストファイル(AndroidManifest.xml)にインターネット接続用のパーミッションが必要になる。

■インターネット接続用のパーミッション(AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET" />

■画像受信用 Java (Android 用)
//テストのメイン... (※例外処理は簡略)
final int CONNECT_TIMEOUT = 15 * 1000; //接続タイムアウト[ms]

String phpUrl = "http://(テストするサーバー)/image_out.php"; //画像送信用 PHPのURL

Bitmap bitmap = null; //受信した画像を入れる

try {
URL url = new URL(phpUrl);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(CONNECT_TIMEOUT); //接続タイムアウト
con.setRequestMethod("GET");
con.connect();

bitmap = BitmapFactory.decodeStream(con.getInputStream()); //画像デコード

//確認用
System.out.println("con.getContentType() = " + con.getContentType());
System.out.println("con.getContentLength() = " + con.getContentLength());
System.out.println("con.getResponseCode() = " + con.getResponseCode());
System.out.println("con.getResponseMessage() = " + con.getResponseMessage());

con.disconnect();

} catch (Exception e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}

// ・・・・
// onDraw() などで確認する場合
if (bitmap != null) {
canvas.drawBitmap(bitmap, 0, 0, null);
}

 例外処理はかなり手抜きしてるので、必要に応じて適当に。サーバー接続タイムアウトを取得したいなら、catch 節に SocketTimeoutException を加えれば、捕えることができる。

 URL は HttpURLConnection を使ってるので「http://」前提だが、「https://」なら、HttpsURLConnection を使った方が良いかも知れない(テスト環境がないので、やったことない)。

 また本来なら、サーバーのレスポンスも考慮に入れて、ネットからのダウンロード等は別スレッドでやった方が良いだろう。Android なら ServiceAsyncTask がよく使われているようだ。


(関連記事)
【Java】【PHP】【JSON】JSON を PHP から送信して、Java で受信する
【Java】【PHP】【JSON】JSON を Java から送信して、PHP で受信する
【PHP】【Android】apk ファイルを PHP でダウンロードする


関連記事

category: Android

thread: プログラミング

janre: コンピュータ

tag: PHP  画像ファイル読み込み  通信 
tb: 0   cm: --

【Android】【Java】リソース名で、res/drawable-~ フォルダから、画像を読み込む(Drawable)  


もうひとつ、Android 特有の Drawable オブジェクトでも、「res/raw フォルダから、テキストファイルを読み込む」と同じように、リソース名を引数にした画像読み込み関数を書いておく。

//リソース名で、res/drawable-~ フォルダから、画像を読み込む(Drawable) (Android 用)
public static final Drawable loadDrawableResource(String resName, Context context) throws IOException, FileNotFoundException {
final int id = context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
if (id == 0) { //エラーにはならない
throw new FileNotFoundException("Not Found - " + resName);
}
return context.getResources().getDrawable(id);
}

//メインでは... (※例外処理は省略)
String resName = "sample"; //"res/drawable-nodpi/sample.jpg" 等
Drawable drawable = loadDrawableResource(resName, this); //this は起動した Activity が良い(Context)

例外処理は手抜きしてるので、必要に応じて適当に。

リソースIDで画像読み込みを行うならば、return 文の1行だけでも良い。

Drawable drawable = getContext().getResources().getDrawable(R.drawable.sample);

ただ、ファイル読み込みでは通常、String 型のファイル名等がほとんどなので、int 型のリソースID形式でコードを大量に書いてしまうと汎用性の面では劣る。例えば、リソースIDが割り振られない場所(assets フォルダ等)に移動したときなどは、コードの大幅修正が必要になる。後々の使い回しも考えてコーディングするなら、String 型のリソース名で書いておいた方が、修正も楽だろう。ファイル名やリソース名は設定ファイル等に書いておき、メインコードを interface などを噛ませて置けば、ほとんど修正しなくて良いという利点がある。そういうときに利用すると良い。

ちなみに、「drawable-nodpi」というフォルダを作って、画像を入れておくと、Drawable オブジェクト特有の端末の解像度による自動リサイズがされなくなり、元の画像サイズでロードされる。

実のところ、私はコードの互換性を考えて、ほとんど Drawable オブジェクトは使わない。しかし、画像の拡大・縮小が手軽にできるので、そのためだけに一時的に利用する事がある。そういうときも interface を噛ませておいて、必要に応じて、別仕様の入れ替えができるように作って置くと良いだろう。なるべくプラットフォーム特有仕様は、避ける工夫をしておいた方が、長い目で見れば有益な事が多い。


(関連記事)
【Android】リソース名で、res/drawable-~ フォルダから、画像を読み込む(Bitmap)
【Android】res/raw リソースフォルダからテキストファイルを読み込む
【Android】画像リソースIDをコードで取得する
【Android】アプリ名 app_name タグをコードで取得する
【Android】assets フォルダから画像ファイルを読み込む
【Android】SDカードから画像ファイルを読み込む
【Android】内部ストレージから画像ファイルを読み込む
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


関連記事

category: Android

thread: プログラミング

janre: コンピュータ

tag: 画像ファイル読み込み 
tb: 0   cm: --

【Android】【Java】リソース名で、res/drawable-~ フォルダから、画像を読み込む(Bitmap)  


ついでなので、「res/raw フォルダから、テキストファイルを読み込む」と同じように、リソース名を引数にした画像読み込み関数を書いておこう。

//リソース名で、res/drawable-~ フォルダから、画像を読み込む(Bitmap) (Android 用)
public static final Bitmap loadBitmapResource(String resName, Context context) throws IOException, FileNotFoundException {
final int id = context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
if (id == 0) { //エラーにはならない
throw new FileNotFoundException("Not Found - " + resName);
}
return BitmapFactory.decodeResource(context.getResources(), id);
}

//メインでは... (※例外処理は省略)
String resName = "sample"; //"res/drawable-nodpi/sample.jpg" 等
Bitmap bitmap = loadBitmapResource(resName, this); //this は起動した Activity が良い(Context)

例外処理は手抜きしてるので、必要に応じて適当に。

リソースIDで画像読み込みを行うならば、return 文の1行だけでも良い。

Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sample);

ただ、ファイル読み込みでは通常、String 型のファイル名等がほとんどなので、int 型のリソースID形式でコードを大量に書いてしまうと汎用性の面では劣る。例えば、リソースIDが割り振られない場所(assets フォルダ等)に移動したときなどは、コードの大幅修正が必要になる。後々の使い回しも考えてコーディングするなら、String 型のリソース名で書いておいた方が、修正も楽だろう。ファイル名やリソース名は設定ファイル等に書いておき、メインコードを interface などを噛ませて置けば、ほとんど修正しなくて良いという利点がある。そういうときに利用すると良い。


(関連記事)
【Android】リソース名で、res/drawable-~ フォルダから、画像を読み込む(Drawable)
【Android】res/raw リソースフォルダからテキストファイルを読み込む
【Android】画像リソースIDをコードで取得する
【Android】アプリ名 app_name タグをコードで取得する
【Android】assets フォルダから画像ファイルを読み込む
【Android】SDカードから画像ファイルを読み込む
【Android】内部ストレージから画像ファイルを読み込む
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


関連記事

category: Android

thread: プログラミング

janre: コンピュータ

tag: 画像ファイル読み込み 
tb: 0   cm: --

【Android】【Java】内部ストレージから画像ファイルを読み込む  


 これも基本は「内部ストレージからテキストファイルを読み込む」と同じ。変更点はストリームを開いた後のデコーダーを、画像用に書き換えれば良いだけ。

//内部ストレージから、画像ファイルを読み込む(Android 用)
public static final Bitmap loadBitmapLocalStorage(String fileName, Context context)
throws IOException, FileNotFoundException {
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(context.openFileInput(fileName));
return BitmapFactory.decodeStream(bis);
} finally {
try {
bis.close();
} catch (Exception e) {
//IOException, NullPointerException
}
}
}


//メインでは... (※例外処理は省略)
String fileName = "sample.jpg"; // "data/data/[パッケージ名]/files/sample.jpg" になる
Bitmap bitmap = loadBitmapLocalStorage(fileName, this); //this は起動した Activity が良い(Context)

 例外処理は手抜きしてるので、必要あれば適当に。

 DDMS で覗いて、保存フォルダが作られていれば、右上の方にある [push] ボタンからファイル転送もできるが、無い場合は、Context クラスの openFileOutput() で書き込めばフォルダが自動的に作られる。


(関連記事)
【Android】内部ストレージに画像ファイルを保存する(png)
【Android】内部ストレージに画像ファイルを保存する(jpg)
【Android】内部ストレージからテキストファイルを読み込む
【Android】内部ストレージにテキストファイルを保存する
【Android】SDカードから画像ファイルを読み込む
【Android】assets フォルダから画像ファイルを読み込む
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


関連記事

category: Android

thread: プログラミング

janre: コンピュータ

tag: 画像ファイル読み込み  内部ストレージ 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop