ビットコイン対円のティッカーを可視化
前回、pybitflyerを利用して bitFlyerからビットコイン対円のティッカーを2秒ごとに10分間分取得した。
ティッカーを取得、とかさらっと言っているが、実はFX初めて。 ただ株は少しやったことがあって、そうゆう人間からすると、 ティッカーを取得したら売買結果だった、というのはちょっと違和感があった。 と言うのもティッカーって、AppleならAAPLとか、Yahoo!Japanなら4689とか、企業を指すものだと思ってたから。 なので、FXで言うところのティッカーって何なのかを知るために、用語を調べたり、matplotlibで可視化してみた。
用語
まずは用語を少し調べてみたけど、こんな感じだろうか。。 間違ってたら教えて下さい。
- best_ask: 最高買い価格
- best_bid: 最低売り価格
- best_ask_size: 最高買い価格の数
- best_bid_size: 最低売り価格の数
- ltp: 最終取引価格
- total_ask_depth: 買い注文総数
- total_bid_depth: 売り注文総数
- volume_by_product: 価格ごとの出来高
best_ask
, best_bid
, ltp
の単位は円でOKだと思うけど、
best_ask_size
, best_bid_size
, total_ask_depth
,
total_bid_depth
, volume_by_product
は単位はBTCかな?
CSVのファイルの数値は少数第8位まであって、0.00000001BTC=1Satoshi
なのでOKとは思うが。
なお、total_ask_depth
, total_bid_depth
のイメージは以下が分かりやすいと思う。
下のグラフの両端の数値がそれらに該当すると思われる。
https://en.wikipedia.org/wiki/Market_depth
コード
前回のread_csv()
で取得できる
dfを次のplot_ticker()
の引数として渡すと可視化できる。
def plot_ticker(self, df): fig = plt.figure(figsize=(16, 9), dpi=100) fig.patch.set_facecolor('white') ax = fig.add_subplot(3, 1, 1) plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) ax.plot(df.index, df['best_ask'], ls='-', color='red', label='best_ask') ax.plot(df.index, df['best_bid'], ls='-', color='blue', label='best_bid') ax.plot(df.index, df['ltp'], ls='-', color='gray', label='ltp') plt.legend(loc='best') ax.grid() ax = fig.add_subplot(3, 1, 2) plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) ax.plot(df.index, df['total_ask_depth'], ls='-', color='red', label='total_ask_depth') ax.plot(df.index, df['total_bid_depth'], ls='-', color='blue', label='total_bid_depth') plt.legend(loc='best') ax.grid() ax = fig.add_subplot(3, 1, 3) plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) # ax.plot(df.index, df['volume'], ls='-', color='green', label='volume') ax.plot(df.index, df['volume_by_product'], ls='-', color='black', label='volume_by_product') plt.legend(loc='best') ax.grid() plt.tight_layout() plt.show()
結果
- 上グラフ
best_ask
が上、best_bid
が下のラインを支えていて,ltp
がその中間にあることが確認できる。 - 中グラフ
観測した中ではtotal_ask_depth
がtotal_bid_depth
をずっと下回っている。 あとtotal_ask_depth
が一定なのに足して、total_bid_depth
がたまに増える。 - 下グラフ
volume_by_product
が急激に減ったと思ったら、BTCの価格が上がっている。
参考文献
bitFlyerからビットコイン対円のティッカーを取得
かなり前からビットコインが熱かったけど放置してたら完全に乗り遅れた。 今更ながらビットコインの波に乗ろうと思う。 まずは対円の情報をbitFlyerから取得しようと思う。 簡単のため、pybitflyerというPythonライブラリーを使う。
コード
api = pybitflyer.API()
でAPIを取得して、
ticker = api.ticker(product_code='BTC_JPY')
でティッカーが取得できる。
keyもsecretもいらず非常にシンプルだが、
サーバーへ2秒置きにアクセスして10分間分のティッカーを取得し、
CSVファイルに保存して、読み込みデータを表示するまでのコードを書いた。
# coding: utf-8 import pybitflyer import pandas as pd from time import sleep from progressbar import ProgressBar SLEEP_SECOND = 2 # データ取得間隔(秒) N_MINUTE = 10 # データ取得時間(分) DIR_DATA = '../data/' # データ格納フォルダー PRODUCT_CODE = 'BTC_JPY' # 取得するデータ FILE_BTC_JPY = 'BTC_JPY_TICKER.csv' # TICKERファイル名 class Agent: def __init__(self): pass def main(self): self.get_ticker() df = self.read_ticker() """ TICKER """ def get_ticker(self): api = pybitflyer.API() tickers = [] count = 60 // SLEEP_SECOND * N_MINUTE # サーバーへのアクセス回数 pb = ProgressBar(max_value=count) for i in range(count): ticker = api.ticker(product_code=PRODUCT_CODE) tickers.append(ticker) sleep(SLEEP_SECOND) pb.update(i) # Update progressbar df = pd.DataFrame(tickers) df.to_csv(DIR_DATA+FILE_BTC_JPY, index=False) def read_ticker(self): df = pd.read_csv(DIR_DATA+FILE_BTC_JPY) df['timestamp'] = pd.to_datetime(df['timestamp']) # stringからdatetimeへ keys = ['timestamp', 'product_code', 'tick_id', \ 'best_ask', 'best_ask_size', 'best_bid', 'best_bid_size', \ 'total_ask_depth', 'total_bid_depth', 'ltp', \ 'volume', 'volume_by_product'] df = df[keys] # カラムを表示したい順に並べる df = df.set_index('timestamp') # timestampをindexに # debug print(df.head().to_string()) print('') print(df.tail().to_string()) print('') return df if __name__ == "__main__": Agent().main()
結果
上記のコードをagent.py
に保存して、一つ上にdataフォルダーを作って置けば、
実行すればdataフォルダー内にCSVファイルが出力されているはず。
それを読み込んだ結果が以下の通り。
なかなか2秒ごとにデータ取得できないもんですな。
> python .\agent.py product_code tick_id best_ask best_ask_size best_bid best_bid_size total_ask_depth total_bid_depth ltp volume volume_by_product timestamp 2017-11-15 13:57:22.963 BTC_JPY 16622557 811140.0 1.200000 810785.0 1.822747 1781.033755 4285.326827 810785.0 207454.303454 19592.381994 2017-11-15 13:57:25.343 BTC_JPY 16622606 811139.0 0.682363 810490.0 3.907947 1782.946859 4276.717027 810490.0 207466.718872 19603.633094 2017-11-15 13:57:27.250 BTC_JPY 16622669 810678.0 0.028423 810490.0 3.907947 1784.647044 4277.266086 810679.0 207473.906758 19604.627594 2017-11-15 13:57:28.953 BTC_JPY 16622708 811068.0 0.949000 810491.0 0.005685 1785.397923 4273.360839 810490.0 207478.447690 19605.862525 2017-11-15 13:57:31.313 BTC_JPY 16622740 811066.0 0.307494 810491.0 0.012506 1784.854531 4273.544345 811067.0 207487.503203 19607.357628 product_code tick_id best_ask best_ask_size best_bid best_bid_size total_ask_depth total_bid_depth ltp volume volume_by_product timestamp 2017-11-15 14:08:08.573 BTC_JPY 16637242 814999.0 0.800000 814900.0 0.167781 1714.455080 4223.328344 814900.0 206588.403105 19472.318694 2017-11-15 14:08:11.090 BTC_JPY 16637282 815000.0 5.638385 814697.0 0.320000 1714.380850 4225.357163 815000.0 206584.681053 19473.110742 2017-11-15 14:08:12.957 BTC_JPY 16637289 815000.0 4.738385 814698.0 1.120080 1713.758990 4225.517242 815000.0 206591.556565 19474.010742 2017-11-15 14:08:15.550 BTC_JPY 16637359 815000.0 1.123285 814800.0 0.160200 1710.768781 4219.641562 815000.0 206576.876997 19470.398019 2017-11-15 14:08:17.817 BTC_JPY 16637439 815000.0 0.784285 814800.0 0.160200 1700.492640 4216.927261 815000.0 206562.011497 19469.980019
参考文献
Twitter APIを使った検索方法
Twitter分析をすることになったため、APIを使った検索について調査検証を行った。 結論から言うと、公式のAPIは、パラメーターが少なくロクな検索ができないのだが、 クエリにパラメーターを含めることで様々な検索が可能になることが分かった。 以下にその方法を示す。
1. APIとの接続
APIへ接続する前に、CONSUMER_KEY
, CONSUMER_SECRET
, ACCESS_TOKEN
, ACCESS_TOKEN_SECRET
を取得する。
Twitter REST APIの使い方とか読み進めていけばできると思う。
僕の場合は、昔に設定してたのが残っていたので、それを使用することにした。
取得できたら、設定ファイル(今回はconfig.py
)を作成して、以下のように各コードを書き留めておく('xxx'の部分を変更)。
こうすることで、検索ファイルにべた書きするより多少リスクが軽減される。
CONSUMER_KEY = 'xxx' CONSUMER_SECRET = 'xxx' ACCESS_TOKEN = 'xxx' ACCESS_TOKEN_SECRET = 'xxx'
APIには、OAuthを簡単にしてくれるライブラリー
requests_oauthlib のOAuth1Session
を使って接続する。
検索ファイル(今回はagent.py
)を作成して、以下のように設定ファイル(config.py
)から
各コードを取得し、OAuth1Session
に渡すとAPIに接続できる。
from requests_oauthlib import OAuth1Session import config class Agent: ... def connect_api(self): api = OAuth1Session(config.CONSUMER_KEY, config.CONSUMER_SECRET, config.ACCESS_TOKEN, config.ACCESS_TOKEN_SECRET) return api
2. 検索条件の設定
公式ドキュメント
を見ると分かるが、ほとんど条件が指定できない。
しかし、query
に以下のように記述することで、様々な条件で検索することが可能になる。
def make_params(self): query = '猫 filter:images min_replies:10 min_retweets:500 min_faves:500 exclude:retweets' params = { 'q': query, 'count': 20 } return params
上記の条件は以下の通り。 他の条件については、Twitterの検索APIについて が詳しい。
key | value exmaple | discription |
---|---|---|
filter | images | 画像があるツイート |
min_replies | 10 | リプライ数が指定値以上のツイート |
min_retweets | 500 | リツイート数が指定値以上のツイート |
min_faves | 500 | ライク数が指定値以上のツイート |
exclude | retweets | リツイートでないツイート? |
3. tweetの検索
上記で取得したapi
とparams
を引数とする検索用メソッドを以下のように作る。
ツイートはstatuses
に入っているのでresult
として返す。
def search_tweet(self, api, params): url = 'https://api.twitter.com/1.1/search/tweets.json' req = api.get(url, params=params) result = [] if req.status_code == 200: tweets = json.loads(req.text) result = tweets['statuses'] else: print("ERROR!: %d" % req.status_code) result = None assert(len(result) > 0) return result
statusに入っているツイートは各々
Tweet data dictionaries
のTweet Data Dictionary
のkeyを持っている。
とりあえず簡単な分析に必要なkeyを標準出力させるメソッドは次の通り。
def output_tweets(self, result): for r in result: for k,v in r.items(): if k in ['text', 'retweet_count', 'favorite_count', 'id', 'created_at']: print(k+':') print(v) print(' ') print('-----------------------------------------------------------------')
以下が出力結果。条件通りの検索結果が返ってきていることが分かる。
id: 925369434549624832 text: 我が家のネコさま。←むかし いま→ https://t.co/qg3TXEZpUv favorite_count: 63259 created_at: Tue Oct 31 14:30:40 +0000 2017 retweet_count: 30089 ----------------------------------------------------------------- id: 924974322115940357 text: クロネコ https://t.co/JNZLtFHn2z favorite_count: 19209 created_at: Mon Oct 30 12:20:38 +0000 2017 retweet_count: 5106 ----------------------------------------------------------------- id: 924485965237731328 text: 【猫から学ぶ女子力】真枝アキ『彼氏のネコがかわいくない!』 https://t.co/y8lmBlbHkC #ツイ4 https://t.co/3J3AxKVnvV favorite_count: 3255 created_at: Sun Oct 29 04:00:04 +0000 2017 retweet_count: 847 -----------------------------------------------------------------
4. 文字化け対策
なおWindows環境だと、コマンドプロンプト上で、 上記のように日本語等を出力しようとすると、文字化けが起こる可能性が高い。 理由はPythonでutf-8で扱っているのに、コマンドプロンプトはcp932を扱っているから。 もし文字化けが起こった場合は、以下を実行してみるとよい。
これでちょっとだけストレスが少なくなる。
参考文献
Playerと複数生成されるSpawnerの位置を取得
UnityでFlappy Birdを作ってみたが、すぐにPlayer(Flappy Bird)を自動制御したくなったので、まずはPlayerとSpawner(土管)の位置を取得してみた。
Spawnerの設定
Spawnerは土管で、1秒ごとにY軸をランダムにずらして生成し、Playerに向かわせている。 Spawnerが生成された際にタグを付けることで、Player側でSpawnerの位置を取得することができる。
public GameObject wallPrefab; // Spawnerとして土管のprefabを設定する public float interval; // 1秒ごとに生成する private GameObject spawner; // 生成されたSpawnerを格納する public string tag; // Spawnerにタグを付ける // Use this for initialization IEnumerator Start () { while (true) { // 画面外の座標(12, 2)をベースにY軸を0.0~4.0だけランダムにずらしてSpawnerを生成するよう設定 transform.position = new Vector2(transform.position.x, Random.Range(0.0f, 4.0f)); // Spawnerを生成 spawner = Instantiate(wallPrefab, transform.position, transform.rotation); // 生成したSpawnerにタグ付け! spawner.tag = "Wall"; yield return new WaitForSeconds(interval); } }
なお、Spawnerに指定した土管prefabには1秒ごとに5ずつX軸マイナス方法へ向かうように設定してある。
Playerの設定
Playerはフレームごとに状態を観測することができる。 このスクリプトがPlayerのインスタンスになるので、Playerの位置は簡単に取得できるのだが、 Spawnerの位置は、上記のようにタグ付けをすることで、Playerからも取得することができるようになる。
public float jumpPower; // Playerは強さ5でジャンプ private int num_wall = 2; // SpawnerはPlayerに最も近い2つだけ位置を取得 // Update is called once per frame void Update () { // Initialize state // 観測する位置は以下の5つ // [player_pos_y, wall1_pos_x, wall1_pos_y, wall2_pos_x, wall2_pos_y] List<float> state = new List<float>(); // 位置を格納するリスト // Set player position x var pos_player = this.transform.position; // Playerの位置 state.Add(pos_player.y); // リストにPlayerのY座標を追加 // Set wall position x and y GameObject[] walls = GameObject.FindGameObjectsWithTag("Wall"); // タグから全Spawnerを取得 foreach (var wall in walls) { // 全Spawnerについて生成された順に var pos_wall = wall.gameObject.transform.position; // Spawnerの位置を取得 // SpawnerがPlayerより前にいて、かつSpawnerの数がリストに2つ未満の場合 // If a wall is behind the player & the number of wall in state is not max if ((pos_player.x <= pos_wall.x + 1.0f) && (state.Count < 1+2*num_wall)) { state.Add(pos_wall.x); // SpanerのX座標を格納 state.Add(pos_wall.y); // SpanerのY座標を格納 } } // debug // 取得したPlayerとSpanerの位置を出力 string text = ""; foreach (var s in state) { text = text + s.ToString() + " "; } print(text); }
位置の取得結果
Playerのスクリプトで設定したコンソールへの出力は以下のようになる。
数値の内容は[player_pos_y, wall1_pos_x, wall1_pos_y, wall2_pos_x, wall2_pos_y]
。
SpawnerがX座標マイナス方向にずれていくのが観測できる。
PlayerのX座標は変わらないので、Y座標だけ取得している。
全ソースコード
SpawnerとPlayerの全ソースコードを以下に示しておく。
- Spawner.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Spawner : MonoBehaviour { public GameObject wallPrefab; public float interval; private GameObject spawner; public string tag; // Use this for initialization IEnumerator Start () { while (true) { transform.position = new Vector2(transform.position.x, Random.Range(0.0f, 4.0f)); spawner = Instantiate(wallPrefab, transform.position, transform.rotation); spawner.tag = "Wall"; yield return new WaitForSeconds(interval); } } // Update is called once per frame void Update () { } }
- Player.cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { public float jumpPower; private int num_wall = 2; // Use this for initialization void Start () { } // Update is called once per frame void Update () { // Initialize state // [player_pos_y, wall1_pos_x, wall1_pos_y, wall2_pos_x, wall2_pos_y] List<float> state = new List<float>(); // Set player position x var pos_player = this.transform.position; state.Add(pos_player.y); // Set wall position x and y GameObject[] walls = GameObject.FindGameObjectsWithTag("Wall"); foreach (var wall in walls) { var pos_wall = wall.gameObject.transform.position; // If a wall is behind the player & the number of wall in state is not max if ((pos_player.x <= pos_wall.x + 1.0f) && (state.Count < 1+2*num_wall)) { state.Add(pos_wall.x); state.Add(pos_wall.y); } } // debug string text = ""; foreach (var s in state) { text = text + s.ToString() + " "; } print(text); // Manual operation if (Input.GetButtonDown("Jump")) { GetComponent<Rigidbody2D>().velocity = new Vector2(0, jumpPower); } } /* void OnCollisionEnter2D (Collision2D other) { // Application.LoadLevel(Application.loadedLevel); //old function UnityEngine.SceneManagement.SceneManager.LoadScene(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex); } */ void OnCollisionEnter2D (Collision2D other) { Invoke("Restart", 1.0f); } void Restart () { // Application.LoadLevel(Application.loadedLevel); //old function UnityEngine.SceneManagement.SceneManager.LoadScene(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex); } }
参考文献
GitHub Desktop for Windowsの"failed to sync this branch"の対処
GitHub Desktopを立ち上げた状態で別作業をしていたら、GitHub Desktop関連のエラーを示すポップアップが4つ連続で出てきた。
ポップアップを消した後も何度か出てきて、実際、GitHub Desktopで同期するとfailed to sync this branch
と表示され、
GitHubと同期できなくなってしまった。
Shell操作で対処できている記事が幾つか見られたが、僕の環境ではまったく無意味だったため、 コンパネからGitHub Desktopの「アンインストールと変更」を行おうとしたところ、以下のようなウィンドウが出た。
開いたときは、アプリケーションを以前の状態に復元します。
が選択可能になっていたので、
選択して、OKボタンを押して、GitHub Desktopを再起動したら、うまく同期できるようになった。
復元後にGitHub Desktopの「アンインストールと変更」ボタンをクリックしたら、
アプリケーションを以前の状態に復元します。
が選択できなくなっていた。
問題ある時のみ選択できるっぽい。
Windows10でMongoDBの設定
Windows10でMongoDBを使おうとしたら、少しつまづいたのでメモとして残しておく。
1. ダウンロードとインストール
MongoDB Download Centerから
Windows Server 2008 R2 64-bit and later, with SSL support x64
をダウンロードしてインストール。
2. パスを通す
デフォルトのパスC:\Program Files\MongoDB\Server\3.4\bin
をシステム環境のPathに登録。
3. ログとデータの保存場所を確保
僕の環境だとMongoDBをインストールしたCドライブが0.5TBしかないのに対して、Dドライブが1.8TBあるので、 ビッグデータに備えて、以下のようにDドライブにログとデータの保存場所を作成しておく。ここは各自の好みでOK。
D:\db\mongodb\log\
D:\db\mongodb\data\
4. MongoDBの設定ファイルの作成
MongoDBを起動するときは、毎回上記のパスを教える必要がある。
面倒なので、以下の内容の設定ファイルmongod.cfg
を作成して、C:\Program Files\MongoDB\Server\3.4\bin\
に置いておく。
拡張子は、cfgでもconfでもconfigでも何でもいいっぽい。なお、設定ファイルの置き場所も各自の好みでOK。
systemLog: destination: file path: D:\db\mongodb\log\mongod.log storage: dbPath: D:\db\mongodb\data
5. 設定ファイルのWindowsサービス登録
上記で作成した設定ファイルをWindowsサービスに登録することで、Windowsと同時にMongoDBを起動できる。
コマンドプロンプトを管理者権限で立ち上げて、mongod --config "C:\Program Files\MongoDB\Server\3.4\bin\mongod.cfg" --install
と入力して実行する。
しかし、以下のようなエラーが出る可能性がある。
PS C:\WINDOWS\system32> mongod --config "C:\Program Files\MongoDB\Server\3.4\bin\mongod.cfg" --install Error parsing YAML config file: yaml-cpp: error at line 5, column 8: illegal map value try 'C:\Program Files\MongoDB\Server\3.4\bin\mongod.exe --help' for more information
設定ファイルについて、以下の4つが守られていないことが原因と考えられる。
- utf-8で保存
- タブを使わない
- systemLogとstorageの間を開けない
- パスをダブルクォーテーションで囲まない
修正したら、以下の通り上手くいくはず。
PS C:\WINDOWS\system32> mongod --config "C:\Program Files\MongoDB\Server\3.4\bin\mongod.cfg" --install PS C:\WINDOWS\system32>
6. Pythonからの操作
PythonからMongoDBを操作する場合は、pymongo
を使えるようになるとよい。
以下の記事でCRUD操作の基礎を学べる。
参考文献
Swiftで写真を撮影して保存するiPhoneアプリを作成
iPhoneのカメラ機能を利用する入門的なアプリをサクッと作る。 フレームワークの各関数の使い方をよく忘れるのでメモ代わりに記録として残しておく。
画面
画面は1つのみで、プレビュー用のView(cameraView)とシャッター用のButton(take)の2つの部品だけを配置。 下図ではViewを正方形に配置しているが、実際に保存されるアスペクト比に近い形の方が良いとは思う。
コード
以下のコードを実装して、iPhone繋いでBuild&Runして、takeボタン押せば、 プレビュー画面が写真としてカメラロールに保存される。 コードの解説については、コメントを参照。
import UIKit import AVFoundation class FirstViewController: UIViewController, AVCapturePhotoCaptureDelegate { // カメラ等のキャプチャに関連する入出力を管理するクラス var session: AVCaptureSession! // 写真データを取得するクラス var outout: AVCapturePhotoOutput? // カメラでキャプチャした映像をプレビューするクラス var previewLayer: AVCaptureVideoPreviewLayer? // カメラでキャプチャした映像をプレビューするエリア @IBOutlet weak var cameraView: UIView! // ロードされた直後に呼び出される(初回に一度のみ) override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } // 画面が表示される直前に呼び出される override func viewWillAppear(_ animated: Bool) { // キャプチャー入出力と写真データ取得のクラスの初期化 session = AVCaptureSession() outout = AVCapturePhotoOutput() // 解像度の設定 //session.sessionPreset = AVCaptureSessionPreset1920x1080 session.sessionPreset = AVCaptureSessionPreset3840x2160 // カメラの選択(背面・前面など) let camera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) do { // 指定したデバイスをセッションに入力 let input = try AVCaptureDeviceInput(device: camera) // 入力 if (session.canAddInput(input)) { session.addInput(input) // 出力 if (session.canAddOutput(outout)) { session.addOutput(outout) session.startRunning() // カメラ起動 // プレビューレイヤーを生成 previewLayer = AVCaptureVideoPreviewLayer(session: session) // cameraViewの境界をプレビューレイヤーのフレームに設定 previewLayer?.frame = cameraView.bounds // アスペクト比変更とレイヤー収納の有無 previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill // アスペクト比を変ない。レイヤーからはみ出した部分は隠す。 //previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect // アスペクト比を変えない。はみ出さないようにレイヤー内に収める。 // cameraViewのサブレイヤーにプレビューレイヤーを追加 cameraView.layer.addSublayer(previewLayer!) } } } catch { print(error) } } // ボタンをタップした時に呼ばれる @IBAction func takePhoto(_ sender: Any) { // 撮影設定 let settingsForMonitoring = AVCapturePhotoSettings() settingsForMonitoring.flashMode = .auto // フラッシュのモード settingsForMonitoring.isAutoStillImageStabilizationEnabled = true // 手振れ補正 settingsForMonitoring.isHighResolutionPhotoEnabled = false // 最高解像度で撮影するか否か // シャッターを切る outout?.capturePhoto(with: settingsForMonitoring, delegate: self) } // AVCapturePhotoCaptureDelegateのデリゲート // カメラで撮影が完了した後呼ばれる。撮影データを加工したり、アルバムに保存したりする。 func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { if let photoSampleBuffer = photoSampleBuffer { // JPEG形式で画像データを取得 let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer) // UIImage型に変換 let image = UIImage(data: photoData!) // フォトライブラリに保存 UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil) } } // 警告を受け取ったときに呼ばれる override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
トピックス
解像度の設定
// 解像度の設定 //session.sessionPreset = AVCaptureSessionPreset1920x1080 session.sessionPreset = AVCaptureSessionPreset3840x2160
もう少し写真の解像度を上げたいなどの場合は、ここで設定を行う。 指定できる解像度の種類は以下の通り。
Symbol | Description |
---|---|
AVCaptureSessionPresetPhoto | 高解像度の写真品質出力 |
AVCaptureSessionPresetHigh | 高品質のビデオおよびオーディオ出力 |
AVCaptureSessionPresetMedium | WiFi経由での共有出力ビデオおよびオーディオビットレート |
AVCaptureSessionPresetLow | 3G経由での共有出力ビデオおよびオーディオビットレート |
AVCaptureSessionPreset320x240 | 320x240ピクセルビデオ出力 |
AVCaptureSessionPreset352x288 | CIF画質(352x288ピクセル)ビデオ出力 |
AVCaptureSessionPreset640x480 | VGA画質(640x480ピクセル)ビデオ出力 |
AVCaptureSessionPreset960x540 | クオータHD品質(960x540ピクセル)ビデオ出力 |
AVCaptureSessionPreset1280x720 | 720p画質(1280x720ピクセル)のビデオ出力 |
AVCaptureSessionPreset1920x1080 | 1080p品質(1920x1080ピクセル)ビデオ出力 |
AVCaptureSessionPreset3840x2160 | 2160p(UHDまたは4Kとも呼ばれる)画質(3840x2160ピクセル)ビデオ出力 |
AVCaptureSessionPresetiFrame960x540 | AACオーディオで960x540の高品質iFrame H.264ビデオを約30Mbits/secで実現する設定 |
AVCaptureSessionPresetiFrame1280x720 | 1280x720の高品質iFrame H.264ビデオをAACオーディオで約40 Mbits/secで実現する設定 |
AVCaptureSessionPresetInputPriority | キャプチャセッションがオーディオおよびビデオ出力設定を制御しないことを指定 |
[iOS] AVFundationを使用して、「ビデオ録画」や「連写カメラ」や「QRコードリーダー」や「バーコードリーダー」を作ってみた - Developers.IO より
アスペクト比変更とレイヤー収納の有無
// アスペクト比変更とレイヤー収納の有無 previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill // アスペクト比を変ない。レイヤーからはみ出した部分は隠す。 //previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect // アスペクト比を変えない。はみ出さないようにレイヤー内に収める。
プレビューの変更を行う。上記の設定ではcameraViewに実際に撮影される写真の一部しかプレビューで表示されないが、 設定によっては全部表示させることも可能。以下の3種類がある。
Symbol | Description |
---|---|
AVLayerVideoGravityResize | アスペクト比を変えてレイヤーに収める。 |
AVLayerVideoGravityResizeAspect | アスペクト比を変えない。はみ出さないようにレイヤー内に収める。 |
AVLayerVideoGravityResizeAspectFill | アスペクト比を変ない。レイヤーからはみ出した部分は隠す。 |
AVCaptureVideoPreviewLayer の Video Gravity3種 - 木木木 より
参考文献
- AVFoundationのキャプチャ機能について - tomoyaonishiのブログ
- [iOS 10] iOS 10以降のAVFoundationでの撮影方法 - Developers.IO
- AppDelegate,UIViewController,UIViewのライフサイクル/iOS/Swift - Qiita
- [iOS] AVFundationを使用して、「ビデオ録画」や「連写カメラ」や「QRコードリーダー」や「バーコードリーダー」を作ってみた - Developers.IO
- カメラの利用(AVFoundation)(Swift) - モバイル開発系(K)
- AVCaptureVideoPreviewLayer の Video Gravity3種 - 木木木
- プロトコルとデリゲートのとても簡単なサンプルについて - Qiita
- Xcode:didReceiveMemoryWarning() メソッドとは? - オープンデータとプログラミング