- 投稿日:2020-04-02T22:22:11+09:00
AVFoundationでカメラモジュールを作る
概要
iOSのAVFoundationを用いて汎用カメラモジュールを作ります。
Vision.frameworkと組み合わせて画像認識したり,リアルタイムビデオエフェクトアプリを作る際にも利用できるように,汎用のモジュールとして作りましょう。いろいろと使いまわせて便利です。まずはシングルトンのカメラコントローラクラスを作り,そこにAVCaptureSessionのインスタンスを持たせます。これがカメラのセッションを管理するクラスですね。
このAVCaptureSessionに,インプットとアフトプットを接続し,さらにカメラプレビューとなるAVCaptureVideoPreviewLayerを接続すれば準備完了。
AVCaptureSessionをrunしてやればカメラが起動し,AVCaptureVideoPreviewLayerにカメラの映像が表示されます。CameraControllerの実装
汎用カメラオブジェクトとして,私はいつもCameraControllerというクラスを作っています。
CameraController.swiftclass CameraController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVCapturePhotoCaptureDelegate { // MARK: - lifecycle static let shared = CameraController() private override init() { } }Swiftでのシングルトンの実装方法もいくつかあるようですが,シンプルに。このクラスはSampleBufferDelegateとPhotoCaptureDelegateを採用していますが,必要な機能によっては他にも採用するプロトコルはあり得ます。
また,カメラの起動に必要なものは,プロパティとして宣言しておきます。
CameraController.swift// MARK: - properties let captureSession:AVCaptureSession = AVCaptureSession() let videoOutput:AVCaptureVideoDataOutput = AVCaptureVideoDataOutput() let photoOutput:AVCapturePhotoOutput = AVCapturePhotoOutput() let previewLayer = AVCaptureVideoPreviewLayer() var preview:UIView!最後のpreviewだけ,宣言時にはnilです。
このクラス,例えば他のviewControllerなどから簡単に呼べるように,カメラをスタートするメソッドとストップするメソッドを持たせます。カメラ起動中の映像を表示するために,スタートメソッドにはプレビューを表示するUIViewをパラメータに持たせます。スタートメソッドで受け取ったviewを,プロパティのpreviewにセットするわけですね。次に,カメラをスタートするメソッド,ストップするメソッドを追加します。スタートメソッド内でcapture sessionやinput, outputを設定します。
CameraController.swiftfunc startSession(preview:UIView){ //カメラプレビュー self.preview = preview self.previewLayer.setSessionWithNoConnection(self.captureSession) self.previewLayer.frame = preview.bounds self.previewLayer.videoGravity = .resizeAspectFill preview.layer.addSublayer(self.previewLayer) self.captureSession.beginConfiguration() // input guard let videoDevice = AVCaptureDevice.default(for: .video) else { return } do { let deviceInput = try AVCaptureDeviceInput(device: videoDevice) if self.captureSession.canAddInput(deviceInput){ self.captureSession.addInput(deviceInput) } else{ print("input error") } } catch { } // output if self.captureSession.canAddOutput(self.photoOutput){ self.captureSession.addOutput(self.photoOutput) self.photoOutput.isHighResolutionCaptureEnabled = true self.photoOutput.isLivePhotoCaptureEnabled = self.photoOutput.isLivePhotoCaptureSupported } else{ print("photo output error!") } if self.captureSession.canAddOutput(self.videoOutput){ self.videoOutput.setSampleBufferDelegate(self, queue: self.sampleBufferQueue) self.captureSession.addOutput(self.videoOutput) } else{ print("video output error!") } self.captureSession.commitConfiguration() self.captureSession.startRunning() }本来はマルチスレッドを活用して,UIに関わる部分以外はバックグラウンドのqueueで行いましょう。エラーハンドリングも適切に。このコードでは割愛してます。
続いてカメラをストップするメソッドを書きたいところですが,長くなるのでこれも割愛。基本的には,sessionをstopしてinputとoutputを外しておけばOKです。
最後に,必要なデリゲートプロトコルを実装。ここでは使用頻度が高いと思われる2つだけ書いておきます。メソッドの中身は,必要に応じて頑張って書きましょう。
CameraController.swift// MARK: - capture photo delegate // 写真撮影後に呼ばれる func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?){ } // MARK: - sample buffer delegate // 1フレームごとに呼ばれる func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { }これがCameraControllerの実装は終わり(実際にはもっと書かなきゃいけませんよ!)。viewControllerからカメラを起動する場合には,例えば以下のようにします。
ViewController.swiftoverride func viewDidAppear(_ animated:Bool){ super.viewDidAppear(animated) //ここ CameraController.shared.startSession(self.view) }以上,汎用カメラクラスの基本設計でした。
カメラなど,特定のデバイスを扱うコードはできる限りシングルトンの専用クラスに書いておいた方が良いと思います。しばしばViewControllerにすべて書いた実装を見ますが,それはあまり良くないです。理由は以下。・デバイスは1つしかないので,カメラを制御するコードが複数のインスタンスから同時にコールされないようにするため
・コードの再利用が容易(←重要)
・専用のオブジェクトにまとめておけば,どのviewControllerからでも呼びやすいというところです。
上記コードですが,くれぐれも,これだけではまだ十分ではないのでいろいろと頑張りましょう。改善提案,間違いの指摘は歓迎です。
- 投稿日:2020-04-02T17:03:57+09:00
Unity+ARFoundationでiOS用ARサンプルを動かす
Unity+ARFoundationでiOS用ARサンプルを動かす
1. 開発環境
- MacBook Pro (macOS Catalina 10.15.4)
- Unity 2019.3.7f1
- XCode 11.4
- iPad Pro 11 inch 第2世代 Wi-Fi+Cellular LiDARも搭載!
2. ARFoundationの導入
arfoundation-samples (GitHub)よりクローンを取得します。
ここで紹介する内容は、Update to ARFoundation 4.0.0-preview.1以降あたりのコミット分のクローンとしました。
これをMacのローカルドライブに展開します。ここでは、~/Documents/Unity/arfoundation-samples-master
に展開しました。3. Unity Project を開きビルド
上で展開した
~/Documents/Unity/arfoundation-samples-master
を Unityで開きます。
AR関係パッケージの確認
まず念の為、ARFoundationで必要なパッケージが導入されているか確認します。[Window]-[Package Manager]を開きます。
赤色で囲んだ、AR関係のpreview.1 - 4.0.0
があればOKです。
Bundle Indentifierを変更
[Edit]-[Project Settings...]
でプロジェクト設定を開き、その中のPlayer
タブを選択します。
Bundle Identifier
がそのままでは、このあとのXCodeでのビルドが通らないので、ユニークな識別コードに変更します。
ビルド
[File]-[Build Settings...]
でBuild Settings
ウインドウを開きます。
Scenes In Build
でビルド対象のシーンを1つ選択します。ここではまずScenes/SimpleAR/SimpleAR
を選択します。
Platform
には、もちろんiOS
を選択します。
Build
ボタンを押し、XCode用のプロジェクトを書き出すフォルダを指定します。ここでは、このプロジェクトのフォルダの下にiOS
という名前のフォルダを作成して指定します。4. XCodeでビルドとiPadへ転送
上で保存したプロジェクトフォルダ
iOS
の下にARFoundation
というフォルダがあり、その中のXCodeプロジェクトファイルUnity-iPhone.xcodeproj
をXCodeで開きます。
そして、iPadをMacBookに接続しておきます。
Signing
で次の設定を行います。
-Automatically manage signing
をチェック
-Team
には、自分のApple Developper Accountを指定する
デプロイ先のiPadを右上のリストで選んで再生ボタンを押すと、iPadにプログラムが転送されます。
5. iPadでの実行結果
iPadにプログラムが転送されると、しばらくして "Made with Unity"のロゴが表示されてプログラムが起動します。
机と壁にiPadを向けると、なにやら平面を認識した結果がAR表示されました。画面上をタップすると、キューブも表示されました。
6. 今後やってみたいこと
Unityのサンプルプロジェクトには、今回試した"SimpleAR"というシーン以外にもたくさんのサンプルシーンがあります。それらも順次試したいのですが、たくさんあるシーンを「UnityのBuild Settingsで選んではビルド→XCodeでもビルド→iPadへ転送」と繰り返さなければならないので面倒です。シーン選択用のメニューをUnity上で作ってやろうかと思います。
とりあえず3次元情報を取得して認識していそうなサンプルプログラムは動きました。あとはやっぱり、生の3次元点群を取得して、iPadを完全な3Dスキャナとしていろいろ活用したいところです。特にこの新型iPad Pro には LiDARが搭載されているのですから!ソースをこれから詳しく追っていきます。
参考文献
大変参考になりました。ありがとうございます。
- UnityでiOS向けのARアプリを開発する方法 ( AR Foundation + ARKit-XR-Plugin 導入編 )
- 投稿日:2020-04-02T15:59:08+09:00
貴方に向いた端末は
じゃあまず使用方法を教えてくれるかな
使用方法 Windows MacOSX(MacBookシリーズ) iPhone Android(スマホ) Android(タブレット) iPad ChromeBook 備考 ブラウジング 〇 ◎ △ △ ◎ ◎ ◎ 広い画面でUIが使いやすいとブラウジングしやすい。性能とかより機動性が大事 動画編集 ×~◎ ◎ △ ×~△ ×~〇 △~◎ ×~△ 性能が低かったり画面が小さいと厳しい。 電子書籍 △ 〇 △ △ ◎ ◎ ×~◎ ChromeBookを買うときはAndroidアプリ対応機種を買うこと。 絵を描く △ 〇 △ ×~△ △~◎ 〇~◎ ×~△ 動画編集ほどではないが性能が必要 ゲームをする △~〇 ×~△ 〇 △~◎ △~◎ 〇~◎ × もしあなたがゲームを楽しみたいのなら機動性や可用性が優れたハイエンドタブレットorハイエンドスマホのどちらかが必要。Windowsはエロゲは揃いが良いらしいがそれ以外(特にゲーム性の良いゲーム)はタブレットやスマホと比べると劣る。ChromeBookはゲームが苦手。 Office 〇 △ × × △ △ ◎ 間違いなく一番この用途ではChromeBookが使いやすい。 コスパ △ × × 〇 ◎ △ ◎ Google系がオープンソースであるためかコスパが良い 電話 × × ◎ ◎ △ △ × これは携帯電話から派生したスマホが良い 初期ソフトウェア × ◎ ◎ 〇 〇 ◎ 〇 WindowsはAndroidより要らない初期アプリが多すぎる。無駄にお金がかけられていると思うとイライラする。 セキュリティー × ◎ 〇 〇(野良アプリ(GooglePlayStore以外から入手したアプリ)を使用したときは×) 〇(野良アプリ(GooglePlayStore以外から入手したアプリ)を使用したときは×) 〇~◎ ◎ Windowsはアプリストアが貧弱すぎるため基本的にインターネット上からのダウンロードとなる プログラミング 〇 ◎ × × × × ◎ ChromeBookはプログラミングでも案外使いやすい 各種3DCGソフトウェア(Blender等) △~◎ 〇~◎ × × × △~〇 × Blender等を触りたければChromeBook以外のPCを買いましょう こちらの記事に質問させてもらいました。
【スマホVSパソコン】ハイスペックスマホにお金をかける時代は完全に終わった!
https://www.digigaze.com/pc-vs-phone人形百合姫(私):
2020年3月23日 21:21
これも用途による気がします。
ゲームをするならスマホ、動画編集等をするならPC、効率よくメモを取りたい、Office系ソフトを使いたいならChromeBook(Google製の手軽なノートPC)にそれぞれ金をかけるべきだと思う。ChromeBookならコーディング(MarkDown,HTML,CSS,PHP,JavaScript,C#等)ができるし、Google Playがあると、電子書籍を読めたりするし、ハイエンドでも安いので、PC代わりにもなり、大体の作業はこちらで代用可能です(ゲームと動画編集、3DCGソフトは無理諦めましょう)。返信
上田 龍上田 龍 より:
2020年3月31日 23:54
確かにその通りだと思います。
コメントの人形百合姫様はパソコンやスマホの知識が豊富であるようですが、当記事は最新iphoneなどを購入して機能のうちの10%も使いこなせていない人が多いという現状を知った時に執筆した記事となっています。
使いこなせない機能盛りだくさんな15万円のスマホをショップで契約させられるのであれば
スマホ5万円
パソコン10万円
という選択肢をとったほうは有利に働くことが多いと感じます。
あくまでマクロ的な視点での意見ですので、ミクロ視点だと人形百合姫様の意見が必要な方が多いのも事実です(‘◇’)ゞ
コメントありがとうございました!ゲームをする場合ハイエンドスマホまたはゲーミングスマホ(ゲームに特化した高性能アンドロイドスマホ)もしくはハイエンドタブレットPC(できればiPad pro)があれば快適。
- 投稿日:2020-04-02T15:48:31+09:00
[WKwebview]Monaca+Iosでデバッグビルドしたアプリがスプラッシュ後、画面が真っ白になる
MonacaでWkwebviewを利用してデバッグビルドしたアプリをiphoneで実行したときに画面が真っ白になってしまう
上記の現象に遭遇したので、そのときの解決策を記載します。
前提条件
・AndroidやcloudIDEでは動作している
・iphoneで動作しない
・WKWebViewを利用している最初に結論から言うと、HTMLを取得する際のXHRでエラーとなっていました。
iphoneのログを見ても特にエラーは出てませんでした。3/31に公開されたプラグイン、"Custom Scheme"を追加すると起動するようになりました。
追加方法
CloudIDEの[設定]→[cordovaプラグインの管理]を開き
検索窓に[Custom Scheme]と入れて有効にします。これだけで起動するようになりました。
"Custom Scheme"プラグインが公開されるまでは
"cordova-plugin-wkwebview-file-xhr"プラグインを入れていたのですが、私の場合、初回起動はしたものの、2回目以降の起動で画面が真っ白になる現象が再発してしまっていたので、"Custom Scheme"を使っています。初心者なので、何か間違っていること等あればご指摘ください<(_ _)>
- 投稿日:2020-04-02T12:48:53+09:00
CAMetalLayerを実機でもシミュレータでも良い感じにビルドする
時々、とても奇妙なクラスに出会うことがある。
CAMetalLayerはまさにそれで、実機ではiOS8+、シミュレータではiOS13+が必要になる。
これはシミュレータでMetalが使えるようになったのがiOS13~だったからという歴史的経緯によるものだ。
ちなみに内部的には実機とシミュレータでSDKが別れているため、それぞれのSDKごとにavailableが定義されて実現している。実機SDKAPI_AVAILABLE(macos(10.11), ios(8.0), watchos(2.0), tvos(9.0)) @interface CAMetalLayer : CALayerシミュレータSDK@available(iOS 13.0, *) open class CAMetalLayer : CALayer {さて、問題はアプリで
CAMetalLayer
を利用するときで、iOS12未満もサポートしているアプリだと次のような挙動になる。
実機 シミュレータ iOS12 ○ コンパイルエラー iOS13 ○ ○ この挙動をどう見るか難しいところだが、シミュレータ相手であればさっとコンパイルだけ通したいこともあると思う。
そんなときはpublic protocol CAMetalLayerInterface: class { var pixelFormat: MTLPixelFormat { get set } var framebufferOnly: Bool { get set } var presentsWithTransaction: Bool { get set } func nextDrawable() -> CAMetalDrawable? } #if targetEnvironment(simulator) @available(iOS 13, *) extension CAMetalLayer: CAMetalLayerInterface {} #else @available(iOS 12, *) extension CAMetalLayer: CAMetalLayerInterface {} #endif open class AnimationView: UIView { typealias LayerClass = CAMetalLayerInterface & CALayer override open class var layerClass: Swift.AnyClass { #if targetEnvironment(simulator) if #available(iOS 13, *) { return CAMetalLayer.self } else { preconditionFailure("Not support simurator older than iOS12.") } #else return CAMetalLayer.self #endif } private var gpuLayer: LayerClass { self.layer as! LayerClass } }こんな感じで型を消しつつ、iOS12 & Simulator以外のときに型を入れてあげるのが良い気がする。
#if canImport(QuartzCore.CAMetalLayer)とか
#if targetEnvironment(simulator) && available(iOS 13, *)とか出来ればいいのになーって思ってしまった。レアケースだけど…
https://developer.apple.com/documentation/quartzcore/cametallayer
- 投稿日:2020-04-02T00:47:04+09:00
SwiftUIでiPhoneのバッテリー残量を表示してみる
はじめに
たまたまTwitterで#swiftuiを検索していたら見つけたので、ちょっと作ってみました。
今のバッテリー残量にどうやってアクセスするんだろう…
— ふってぃプログラミング初心者SwiftUI (@Futty_99) March 31, 2020
UIKitのサンプルはたくさんあるのにSwiftUIはとても少ないのが少し大変ですね?#SwiftUI#プログラミング初心者#駆け出しエンジニアと繋がりたいバッテリー残量を取るには
バッテリー残量を取るには、下記のコードで取得できます。
BatteryViewModel.swift//バッテリー監視を開始する UIDevice.current.isBatteryMonitoringEnabled = true //変化通知登録しただけなので初回は更新する必要がある remain = String(format: "%0.1f", UIDevice.current.batteryLevel * 100) status = UIDevice.current.batteryStateその後の変化通知を
NotificationCenter
で受け取れます。BatteryViewModel.swift//バッテリーレベル変化通知を受け取れるようにする NotificationCenter.default.addObserver(self, selector: #selector(batteryLevelChanged(notification:)), name: UIDevice.batteryLevelDidChangeNotification, object: nil) //バッテリー状態編か通知を受け取れるようにする NotificationCenter.default.addObserver(self, selector: #selector(batteryStateChanged(notification:)), name: UIDevice.batteryStateDidChangeNotification, object: nil)バッテリー残量をViewに反映する
ObservableObjectで反映します。
BatteryViewModel.swiftclass BatteryViewModel: ObservableObject { ///バッテリー状態 @Published var status: UIDevice.BatteryState = .unknown ///バッテリー残量 @Published var remain = ""終わりに
サンプルコードは、githubにアップしています。
参考にどうぞ。