今年もクライマックスだぜ
忘年会シーズンお疲れ様でしたああ。
今年はカメラ係を任されることが多かったです。
カメラクラスタとして恐悦至極でございます。
ところが撮影時のポージングについて周囲から非難受けること多数...
![]() |
これ |
ちょ、ちょっと待って下さい... (´;ω;`)
たしかに一般的な構え方ではありませんが、
これは片手で一眼撮影するために独自に編み出した構えなんです...
この構えでは左袖でズームリングを回してるんですよ...
そうすることで片手がふさがってる状況でも撮影が可能なんです...
(ほら上の写真、グラスで左手が塞がってます)
ジョジョを読みすぎてこうなってしまったとかではありません...
ホントですってば... (´;ω;`)
ごあいさつ
はじめまして、かっしー(@kassy_kz)と申します。
冒頭に書きましたが、カメラ大好きクラスタです。
最近の悩みは周囲から「残念」呼ばわりされることが増えてしまったことです。
しかし、このままではいけません!
もういい大人、ここらで一念発起して「脱残念」しておかねば今後の沽券に関わります!
残念呼ばわりされる材料の一つが冒頭に書きましたポージング。
せっかく編み出した必殺ポーズが揶揄されるとは...
でも本当にかっこ良く片手でとれたならば、こんなふうに...
![]() |
凛っ |
まさに優雅!思わず見とれてしまう、これぞ理想の片手持ち。
ということで決定!
こんな優雅な片手撮りを、僕も目指します!
どんなヨコシマな願いも叶えるというAndroid様のお力をお借りして、
脱残念への一歩を歩み出したいと思いますキリ
![]() |
ドロイド君. . . 君が俺の、最後の希望だ. . . |
『オペレーション脱残念』始動
まずはカメラの構え方のおさらいを。
右手にはシャッター、ダイヤル×2(絞り、シャッタースピード調整etc)
左手ではズームリングの制御を行います。
単焦点レンズ使えよというツッコミは却下!僕はズームレンズ大好き人間なのです
これまでは左手での操作を放棄して右手で保持する作戦で行っていましたが、
今回は発想を逆転させて、左手保持を前提とします。
右手を使わずにシャッター、絞り制御などを行う作戦。
カメラとUSB通信
右手を使わずにシャッターとな、
先日のMaker Faire Tokyoなどではレリーズ端子をハックしてシャッターをアプリから押下というデモをいくつか見ました。
今回はUSB端子を使っての制御にチャレンジします。
カメラについたUSB端子からはシャッターだけでなく、絞り、シャッタースピードなどの各種パラメータを制御できるのです。
そして、最近のAndroid 端末(の一部)にはUSBホスト機能が備わっており、繋げばAndroidアプリからシャッターなり絞りなりを制御できるのではないかと...甘い希望を...
カメラとのUSB通信のプロトコルはMedia Transfer Protocol(MTP)というものを使っています。AndroidのUSBホスト機能を使ってこのプロトコルでカメラと通信できれば、カメラを制御できます。しかもなんと、Androidの標準APIにはMTPのライブラリがあるではありませんか!
これぞ僕のために用意されたも同然のAPIだぜヒャッハー!
![]() |
Android MTP ライブラリ |
よく調べてもどこにもシャッターだの絞りだのといった項目はありません。
カメラを制御するコマンドは各メーカーの独自拡張として実装されており、
公開もされていないようで、
Androidがそんなところまでサポートすること義理はないですよね...
はい... ごめんなさい... orz
このライブラリは基本的にカメラから撮影済み画像を抽出するための機能のようです...
ぐぬぬーぐぬぬー... 。゚(゚´Д`゚)゚。
Arduinoで一眼カメラを制御する猛者が?
![]() |
felis ptp github |
やってみた |
試しにと、このソースコードをArduino(Uno) + USBホストシールド上で動作させ、カメラと接続すると
スゴイ!シャッターも切れるし、絞りもシャッタースピードも自在に操れる!SUGEEEEEEE!!貴方が神か!?
しかもソース公開だから出るわ出るわ、シャッター押下コマンドも絞り制御コマンドもあんなコマンドもそんなコマンドも見放題だぜ... ぐへへ...
(*´Д`) ハァハァ
作成方針
良い感じに材料がそろってきた!
というわけで実装方針を検討します。
- このままArduinoで一眼を制御する。ArduinoUSBホストシールドにUSBハブをさしてAndroidとArduinoをADK接続し、Arduinoを介して操作する。
- 上記ソースやAndroidMTPのソースを解読してAndroidUSBHostライブラリ上で動作するMTPプロトコル・スタックを自前で実装する
- 上記ソースを参考に、既存のAndroidMTPライブラリを改造して標準にはないシャッターコマンドなどのAPI追加する
こんなところでしょうか。
1の方式はカメラにAndroid、Arduino、USBハブ、Arduino用電源をくっつける必要がある。だがそこまでいくと片手で持つのが厳しくなるし、合体させるための治具つくるのが大変。見た目もきっとゴテゴテしてまた残念て言われそう...
2の方式、ソースがあるとはいえ、内容はプロトコルスタックの実装... 量的に僕のようなへっぽこプログラマが個人で開発できるようなものじゃなさそう... (;´Д`)
ということで、今回は3の方式にチャレンジしました。
機能を追加といっても、既存ライブラリ中のコマンド命令を示す値を、上記で覗き見した値に置き換えるだけ、既存ライブラリ機能を原則流用しつつ、ちょっとの変更を加えることができれば、比較的簡単に実現できそうじゃないですか... はっはー
既存のAndroidのライブラリにちょっとだけ手を加えたい、そんな方もしいらっしゃったら参考になるかもしれません... (いないかもですが)
ROM焼きをせずに、Androidのライブラリを改造して使う
今回はAndroidのプラットフォーム以下のコードをまるごとアプリ部分に移植(コピー)するという
もしこれが成功すれば、既存のライブラリにオレオレ機能を追加できます。
既存の機能/コードを流用するので、例えば今回の場合だとMTPプロトコルスタックの基本的な実装を全部サボれるというのがポイントです。
環境準備
動作できる端末はUSBホスト機能を備えたもの限定となります。当方ではGalaxy Nexusを用います。
上記のとおり、Androidのオープンソースの移植が必要となるので、AndroidSDKに加え、Androidのソースコードが必要になります。自分はJCROM様からソースをダウンロードさせていただきました。ROM焼きを行いませんが、中間生成ファイルが必要になりますので、一度全体ビルドをかけます。このあたりの手順もJCROM様のサイトにかかれてある手順をそのまま踏襲させていただきました。
また、自分アプリに移植したネイティブコード(C++でかかれたコード)をビルドするため、AndroidNDKもインストールしておきます。
注意点として、Mac OSではビルドができません。おそらく大文字小文字の区別をしないことが起因でビルドがどうしても失敗します。今回はUbuntu上で開発を行いました。
プロジェクト作成
まずはAndroidSDKで普通のアプリケーションのプロジェクトを生成します。
USBでカメラを接続しますので、マニフェストファイルにuses-feature, intent-filter,meta-data の追加記述が必要です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
package="orz.kassy.mtpcustom" | |
android:versionCode="1" | |
android:versionName="1.0" > | |
<uses-sdk | |
android:minSdkVersion="14" | |
android:targetSdkVersion="15" /> | |
<uses-feature android:name="android.hardware.usb.host" /> | |
<application | |
android:icon="@drawable/ic_launcher" | |
android:label="@string/app_name" | |
android:theme="@style/AppTheme" > | |
<activity | |
android:name="orz.kassy.mtpcustom.MainActivity" | |
android:label="@string/title_activity_main" > | |
<intent-filter> | |
<action android:name="android.intent.action.MAIN" /> | |
<category android:name="android.intent.category.LAUNCHER" /> | |
</intent-filter> | |
<intent-filter> | |
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> | |
</intent-filter> | |
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" | |
android:resource="@xml/device_filter" /> | |
</activity> | |
</application> | |
</manifest> |
@kshoji 様の記事とだだ被りです... 申し訳ありません... コピペは使用しておりませんので(何の言い訳だ)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<usb-device class="6" subclass="1" protocol="1" /> | |
</resources> |
Nativeコード移植作業
- AndroidのAPIで使いたい機能(今回の場合はMTP部)を含むファイル名・関数名を選ぶ
- 上記名前でAndroidソース全体をfind, grepコマンドで検索して該当するライブラリモジュールを探す。
- 上記ライブラリモジュールのコードを流し読みして、呼び出し関係にある下位のモジュールをさらに検索。
コードを一行もかきません... orz
今回のケースですと、以下の箇所のソースを移植します。
- Javaライブラリ:/frameworks/base/media/java/android/mtp/
- →アプリのsrc/ フォルダにコピー、
- オリジナルと混同しないようにパッケージ名だけ変更します
- 今回は(android.mtp → android.mtp.custom)としました。
- JNIモジュール:/frameworks/media/jni/
- →アプリのjni/jni_custom フォルダにコピー
- Nativeソース:/frameworks/av/media/mtp/
- →アプリのjni/mtp_custom フォルダにコピー
明らかに不要なファイルがある場合はここで取捨選択してしまいましょう。
移植後のプロジェクトはこんな感じになります。
移植ソースのビルド
移植したnativeソースはAndroidNDKを使ってコンパイルします。
さしあたって移植元にあったAndroid.mkをそのままjniフォルダに配置し、ndk-buildを走らせますが、コンパイラ様に烈火のごとくお説教を喰らいます... orz
![]() |
逃げ出したくなるほどのエラーエラーエラー!! |
![]() |
(イメージ) |
- コンパイラ様「ヘッダファイルが見つからねーぞクズがっ...」
- → Androidソースからヘッダファイルを探し、LOCAL_C_INCLUDESにパスの記述を追加
- コンパイラ様「関数の実体が見当たらねーぞゴミがっ... 」
- →Androidツリーのビルド成果物から該当するsoファイルを探し、LOCAL_LDLIBSに記述を追加
- soファイルはAndroidソースのビルドを行うことで生成されます。
できあがったAndroid.mkはこんな感じになりました...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LOCAL_PATH := $(call my-dir) | |
include $(CLEAR_VARS) | |
ANDROID_ROOT := (Androidソースコードのパス) | |
LOCAL_MODULE := libmtpcustom | |
LOCAL_SRC_FILES := \ | |
mtp_custom/MtpDataPacket.cpp \ | |
mtp_custom/MtpDebug.cpp \ | |
mtp_custom/MtpDevice.cpp \ | |
mtp_custom/MtpEventPacket.cpp \ | |
mtp_custom/MtpDeviceInfo.cpp \ | |
mtp_custom/MtpObjectInfo.cpp \ | |
mtp_custom/MtpPacket.cpp \ | |
mtp_custom/MtpProperty.cpp \ | |
mtp_custom/MtpRequestPacket.cpp \ | |
mtp_custom/MtpResponsePacket.cpp \ | |
mtp_custom/MtpStorageInfo.cpp \ | |
mtp_custom/MtpStringBuffer.cpp \ | |
mtp_custom/MtpStorage.cpp \ | |
mtp_custom/MtpUtils.cpp \ | |
jni_custom/android_mtp_MtpDatabase.cpp \ | |
jni_custom/android_mtp_MtpDevice.cpp \ | |
LOCAL_C_INCLUDES := \ | |
$(ANDROID_ROOT)/system/core/include/ \ | |
$(ANDROID_ROOT)/frameworks/native/include/ \ | |
$(ANDROID_ROOT)/frameworks/native/include/ \ | |
$(ANDROID_ROOT)/bionic/libc/kernel/common/ \ | |
$(ANDROID_ROOT)/external/jhead \ | |
$(ANDROID_ROOT)/libnativehelper/include/ \ | |
$(ANDROID_ROOT)/libnativehelper/include/nativehelper \ | |
$(ANDROID_ROOT)/frameworks/base/include/ \ | |
$(LOCAL_PATH)/../mtp_custom/ \ | |
$(LOCAL_PATH)/mtp_custom/ \ | |
LOCAL_C_INCLUDES += $(call include-path-for, system-core)/cutils | |
LOCAL_CFLAGS := -DMTP_HOST | |
LOCAL_CFLAGS += -DHAVE_PTHREADS | |
LOCAL_LDLIBS := -ldl -lGLESv1_CM -llog -lc \ | |
$(ANDROID_ROOT)/out/target/product/maguro/obj/lib/libutils.so \ | |
$(ANDROID_ROOT)/out/target/product/maguro/obj/lib/libcutils.so \ | |
$(ANDROID_ROOT)/out/target/product/maguro/obj/lib/libusbhost.so \ | |
$(ANDROID_ROOT)/out/target/product/maguro/obj/lib/libexif.so \ | |
$(ANDROID_ROOT)/out/target/product/maguro/obj/lib/libnativehelper.so \ | |
$(ANDROID_ROOT)/out/target/product/maguro/obj/lib/libandroid_runtime.so \ | |
include $(BUILD_SHARED_LIBRARY) |
ヒャッハー
機能追加
無事ビルドがとおったら、移植に成功しています。
それではいよいよ機能追加。ここではシャッターを押す機能の追加について。
といっても、既存関数をそのまま流用し(今回はgetDeviceInfo )
この関数のコードを丸々コピペして定数部分を差し替えるだけ(0x1001 → 0x910F)
・Java部
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public MtpDeviceInfo doDeviceShutter() { | |
return native_do_device_shutter(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// シャッターをきるコマンド、 android_mtp_MtpDevice_get_device_infoをほぼまるパク | |
static jobject | |
android_mtp_MtpDevice_do_device_shutter(JNIEnv *env, jobject thiz) | |
{ | |
MtpDevice* device = get_device_from_object(env, thiz); | |
if (!device) { | |
ALOGD("android_mtp_MtpDevice_get_device_info device is null"); | |
return NULL; | |
} | |
MtpDeviceInfo* deviceInfo = device->doDeviceShutter(); | |
if (!deviceInfo) { | |
ALOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null"); | |
return NULL; | |
} | |
env->NewObject(clazz_deviceInfo, constructor_deviceInfo); | |
return null; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// シャッターを切るコマンド getDeviceInfo()をほぼまるパク | |
MtpDeviceInfo* MtpDevice::doDeviceShutter() { | |
Mutex::Autolock autoLock(mMutex); | |
mRequest.reset(); | |
if (!sendRequest(0x910f)) | |
return NULL; | |
if (!readData()) | |
return NULL; | |
MtpResponseCode ret = readResponse(); | |
if (ret == MTP_RESPONSE_OK) { | |
MtpDeviceInfo* info = new MtpDeviceInfo; | |
info->read(mData); | |
return info; | |
} | |
return NULL; | |
} |
※追記:実は上記記述部の移植だけでは不完全です。補足情報をまとめましたので、必要な方はご参照下さい。
Androidアプリからカスタムしたライブラリの機能を呼び出す
アプリ部の実装です。 先ほど作成したJavaライブラリ部のメソッドを呼び出すだけです。注意としてMtpDeviceはandorid.mtp.MtpDeviceではなく、今回カスタマイズしたandroid.mtp.custom.MtpDeviceを用います。
・MainActivity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 一部抜粋 いろいろ省略している | |
// doMtpConnect -> doMtpShutter の順にコールすればシャッター押下 | |
// ライブラリインポート、 | |
// MtpDeviceだけはカスタムライブラリになっていることに注意 | |
import android.mtp.MtpDeviceInfo; | |
import android.mtp.custom.MtpDevice; | |
// 中略 | |
private UsbManager mManager; | |
private UsbDevice mDevice; | |
private UsbDeviceConnection mDeviceConnection; | |
private UsbInterface mInterface; | |
private android.mtp.custom.MtpDevice mMtpDevice; // カスタムライブラリ | |
/** | |
* MTPデバイスへの接続を行う | |
*/ | |
private void doMtpConnect() { | |
mManager = (UsbManager)getSystemService(Context.USB_SERVICE); | |
// check for existing devices | |
for (UsbDevice device : mManager.getDeviceList().values()) { | |
mDeviceConnection = mManager.openDevice(device); | |
mMtpDevice = new MtpDevice(device); | |
if(mMtpDevice == null){ | |
logToast("device null"); | |
}else { | |
boolean ret; | |
ret = mMtpDevice.open(mDeviceConnection); | |
if(!ret){ | |
logToast("device open NG"); | |
} else { | |
logToast("device open OK"); | |
} | |
} | |
} | |
} | |
/** | |
* MTPデバイスの情報を取得する | |
*/ | |
private void doMtpGetDeviceInfo() { | |
MtpDeviceInfo deviceInfo = mMtpDevice.getDeviceInfo(); | |
String str = deviceInfo.getModel(); | |
logToast(str); | |
} | |
/** | |
* (独自追加APIで)シャッターを切る | |
*/ | |
private void doMtpShutter() { | |
mMtpDevice.doDeviceShutter(); | |
} |
Androidアプリからカメラ制御成功
アプリが完成したらUSBホストケーブルで端末を接続。
かくして、Androidアプリからシャッター押下、パラメータ変更をすることができました。
ヒャッハー!
上でいろいろやったソースコード全体をGitHub上で公開しておきます。 (使用は自己責任でお願いします... 真似したらカメラ壊れたぞどうしてくれるとか言われても何も出来ません... m(_ _)m)
カメラに端末ドッキング、 いざ優雅に片手持ち!


USBとカメラはこんな感じで合体、いい具合に片手保持ができます。
もちろん左手でズーム操作は自在。
あとはAndroid端末のタッチパネルを操作すればシャッターが...
タッチパネルを. . .
くっ. . .
Σ(゚д゚lll) 指が届かねえよ!!!
なんということだ...
カメラ単体だと液晶画面に辛うじて親指が届いていたのだけれど...
合体している端末にまではさすがに指がとどかない.... (右手は禁止)
どうすればいい...
考えろ...
静電タッチパネルでの操作なんだ...
体の一部が触れさえすれば...
体の一部...
舌... か...
![]() |
!!!??? |
![]() |
どうしてこうなった |
ああああああああああああああああああああああああああああああああ
現実みたくねええええええええええええええええええええええええええ
まだだ、まだおわらんよ
希望を失いかけた僕でしたが、そんな中救いの手を差し伸べてくれたのが一冊の本...
我らが @itog 先生の著書!
『Android×Arduinoでつくるクラウド連携デバイス―Android ADKで電子工作をはじめよう!』
この書の第4章に天啓さす記述がっっ
「音声でコントロールする」
これだっ!
そうだよ
音声でコントロールできれば惨たらしい姿を晒すことなく撮影できるじゃないかっ
うははははははは!!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Override | |
public void onResults(Bundle results) { | |
// 音声認識が行われた場合にその結果が渡される | |
ArrayList<String> strings = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); | |
// 結果は文字列配列で渡されるので、その中にコマンドがあるか | |
if (strings.contains("絞れ")) { | |
setLargeAperture(); | |
} else if (strings.contains("開け")) { | |
setSmallAperture(); | |
} else if (strings.contains("速く")) { | |
setHigherShutterSpeed(); | |
} else if (strings.contains("遅く")) { | |
setLowerShutterSpeed(); | |
} else if (strings.contains("シャッター") || strings.contains("shutter")) { | |
doShutter(); | |
} | |
startVoiceRecognition(); | |
} |
こんな簡単なコードで音声認識だなんて...
まったく、Androidは最高だぜえええ!
繰り出せ街へ、 キメるぜ脱残念!
音声認識により、絞りやシャッタースピードの制御、そしてシャッター押下を声で制御することができるようになりました。右手も(舌も)完全フリー!
ダイヤル使わず声で操作する、
フォトグラファー達よ、これがス◯ートなカメラだっ ( ー`дー´)キリッ
さぁ、振り切るぜ
「シャッター」 . . . .
「シャッター!」 . . . .
「シャッター!!」 カシャ. . . .
ぐぬぬ、 どうやら屋外だと相当の大声を出さないと認識が働かない模様です...
「シャッタァァ!!」 カシャ. . . .
「シャッタァァァッーー!!」 カシャ. . . .
結構しんどいぞこれ...(;´Д`)ゼェゼェ
くそっ、負けられるか!
気合だ気合だ気合だああ!
「イッツ タイム フォー、シャッター!!!」
![]() |
ズギューン!!(イメージです) |
まったくの余談ですが、本稿投稿者は「特命戦隊ゴーバスターズ」のファンであることをここに記しておきます。
果たして周囲の評価は...
「超ダサい」 「超キモい」 「ざんねん」
Σ(゚д゚lll). . . . あれ?
なんか... 最初の頃と、何も評価が変わってないような...
ば、ばかな... どうしてこうなった....
僕の努力は... 存在意義は...
視界が... 歪む...
ぐにゃ〜
絶望が俺のゴールだ...
申し訳ありませんでしたああああああああ
今後の展望
ないです。
本当にごめんなさい...
次回作「中二病でも開発がしたい(仮)」にご期待ください...
終わりに
最後までお付き合い下さいまして、本当にありがとうございました。
この度、技術的なご指導をしていただきました @androidsola さん、@noritsuna さん、@marururさん、画像とデザインをご提供くださいました @mt_fieldさん、@tall_zelkovaさん、昨年に続き本企画をまとめてくださいました @youten_redo さん、本当にありがとうございました。
みなさま、今年もお疲れ様でした。
良いお年を。
0 件のコメント:
コメントを投稿