その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

[2019年度版]ドラッグ&ドロップで機械学習のモデルがつくれる「Create ML」の使い方(macOS 10.15 Catalina) #WWDC19

ドラッグ&ドロップで超簡単にオリジナルの機械学習モデルがつくれてしまうCreate MLが大幅パワーアップしました。

新機能が増えたのと、使い方が若干変わった(より簡単になった)部分があるので、最新版として本記事をお送りします。

なお、NDAに配慮してスクリーンショットはAppleの公開資料および現行バージョンのもので代用することにします。

developer.apple.com

developer.apple.com

Create MLの起動方法

これまではCreate MLはまだ独立したアプリとして提供されておらず、Playgroundにコードを書いて実行してアプリケーションのUIにアクセスしていました。

しかしmacOS 10.15 Catalina / Xcode 11で提供される最新バージョンでは、独立したアプリ(Create ML.app)として提供されるようになりました。

Xcode 11のメニューから、

[Xcode] > [Open Developer Tool] > [Create ML]

を選択することで起動します。

使用方法:画像分類器(Image Classifier)

プロジェクトの作成

[File] > [New Project] を選択するとテンプレート選択画面が立ち上がるので、"Image Classifier"を選択します。

f:id:shu223:20190608104742j:plain
テンプレート選択画面。これはWWDCセッションスライド内にあったスクショだが、実際に試すとテンプレートはImage ClassifierとSound Classifierの2つしかなかった(2019.6.7現在)

データセットを用意する

どういうデータを用意するかですが、チュートリアルによると、

  • ラベルごとに最低でも10枚の画像
  • ラベルごとに枚数のバランスをとること(チーターは10枚、ゾウは1000枚、みたいなことをしない)

Use at least 10 images per label for the training set, but more is always better. Also, balance the number of images for each label. For example, don’t use 10 images for Cheetah and 1000 images for Elephant.

  • JPEGやPNG等、画像フォーマットはなんでもいい(UTIがpublic.imageに適合していれば)
  • サイズは揃ってなくてもOK
  • サイズもなんでもいいが、最低でも299x299ピクセルはあった方が良い

The images can be in any format whose uniform type identifer conforms to public.image. This includes common formats like JPEG and PNG. The images don’t have to be the same size as each other, nor do they have to be any particular size, although it’s best to use images that are at least 299x299 pixels.

あとは実際に推論を行うのと同じ状況で学習データも収集した方がいいとか、いろんな角度、ライティングの状況のデータがあった方がいい、ということが書かれています。

If possible, train with images collected in a way that’s similar to how images will be collected for prediction.

Provide images with variety. For example, use images that show animals from many different angles and in different lighting conditions. A classifier trained on nearly identical images for a given label tends to have poorer performance than one trained on a more diverse image set.

ラベルをフォルダ名にし、その配下にトレーニングデータ、テストデータを配置します。

f:id:shu223:20180606071547p:plain

学習&モデルの評価

データフォルダをドラッグ&ドロップします。

f:id:shu223:20190608110725p:plain

テスト(モデルの評価)もドラッグ&ドロップ。

f:id:shu223:20190608110811p:plain

オプショナルで、最大イテレーション回数を指定したり、トレーニングデータにノイズやブラーを付加したり、回転を加えたりといったパラメータを指定することもできます。

f:id:shu223:20190608110855p:plain

Playボタンをクリックすると、学習が開始されます。

f:id:shu223:20190608111115p:plain

学習が完了すると、スコアが表示されます。

f:id:shu223:20190608111212p:plain

f:id:shu223:20190608111423p:plain

モデルを保存する

学習したモデルを .mlmodel ファイルとして保存します。

f:id:shu223:20190608111515p:plain

アプリに組み込む

作成した .mlmodel ファイルをiOSアプリに組み込んで使用します。

実装方法はこちらの記事を参考にしてください。

qiita.com

また画像分類の .mlmodel ファイルをドラッグ&ドロップで試せるアプリをGitHubで公開しているので、こちらもぜひ活用してください。

github.com

使用方法:音声分類器(Sound Classifier)

(続く)

関連(SoundAnalysisについてちょっと書いてます):

qiita.com

API Diffsから見るiOS 13の新機能 - その他諸々 #WWDC19

Vision編CoreML公式配布モデル編Core Image編と、巨大な新API群の端っこの方から記事を書き始めたわけですが、その後どのフレームワークについて書こうとしても調査内容が膨大になってしまってうまくまとまらず、もう眠くなってしまったのでざっくり箇条書きで書いておいて明日以降にまたじっくり調査することにします。

f:id:shu223:20190603103013j:plain

SoundAnalysis

developer.apple.com

Analyze streamed and file-based audio to classify it as a particular type.

機械学習ベースで音を分類したりするらしい。何の音を分類してくれるのかAPIリファレンスからはわからなかったのだけど、次のドキュメントで、

The SoundAnalysis framework operates on a model that you’ve trained using a Create ML MLSoundClassifier

と書いてあって、Create MLでつくったCore MLモデルを使って音を判別するようだ。

ってことで、レイヤーとしてはVisionの音声処理版(Visionは画像処理版)と解釈しとくとよさそう。

セマンティック・セグメンテーション / AVSemanticSegmentationMatte

前からしてPortrait Matteの汎用版。Portrait Matteは人間の全身専用マスクだが、もうちょっと汎用的に領域分割するマスクが取れるようになると。

関連メソッドは前回記事にも書いた:

CoreML公式配布モデルの「DeeplabV3」がMatte抽出にあたって内部で使われているのか、別の仕組みなのかは気になる。

AVSemanticSegmentationMatte.MatteTypeという構造体があり、今のところ次のような種類があるようだ。

static let hair: AVSemanticSegmentationMatte.MatteType

A matting image that segments the hair from all people in the visible field of view of an image.

static let skin: AVSemanticSegmentationMatte.MatteType

A matting image that segments the skin from all people in the visible field of view of an image.

static let teeth: AVSemanticSegmentationMatte.MatteType

A matting image that segments the teeth from all people in the visible field of view of an image.

このへんの髪・肌・歯のセグメンテーションについては前回記事にも書いた。

AVCapturePhotoOutputに関連APIがいくつか追加されている。

developer.apple.com

var availableSemanticSegmentationMatteTypes: [AVSemanticSegmentationMatte.MatteType]

An array of semantic segmentation matte types that may be captured and delivered along with the primary photo.

var enabledSemanticSegmentationMatteTypes: [AVSemanticSegmentationMatte.MatteType]

The semantic segmentation matte types that the photo render pipeline delivers.

AVCapturePhotoにも。

func semanticSegmentationMatte(for: AVSemanticSegmentationMatte.MatteType) -> AVSemanticSegmentationMatte?

Retrieves the semantic segmentation matte associated with this photo.

AVCaptureMultiCamSession

developer.apple.com

A subclass of AVCaptureSession that supports simultaneous capture from multiple inputs of the same media type.

"AVMultiCamPiP: Capturing from Multiple Cameras"というサンプルがあるので、あとで実行してみる。

Core Haptics

developer.apple.com

Compose and play haptic patterns to customize your iOS app's haptic feedback.

サンプルもある。

このドキュメントも興味深い。"Apple Haptic and Audio Pattern (AHAP)"なるファイルフォーマットがあるらしい。

Audio Effects

そんなに新機能を期待してなかったAudioToolboxのリファレンスを見ていたら、iOS 13+なサンプルが2つもあった。

どちらも"Audio Effects"関連。どのへんがiOS 13なのかまだちゃんと読んでないが、あとで見てみる。

AVSpeechSynthesisVoiceGender

以前からある音声合成機能の、合成音声に男女の区別がついた。

case female
case male
case unspecified

これに伴い、AVSpeechSynthesisVoiceにはgenderプロパティが追加された。

var gender: AVSpeechSynthesisVoiceGender { get }

AVSpeechSynthesizer

なんかちょこちょこと新APIがある。

var mixToTelephonyUplink: Bool
var synthesizerAudioSession: AVAudioSession
func write(AVSpeechUtterance, toBufferCallback: AVSpeechSynthesizer.BufferCallback)

MKPointOfInterestCategory

MKMapViewに次のようなプロパティが追加されていて、

var pointOfInterestFilter: MKPointOfInterestFilter?

地図上の"Point of Interest"をフィルタできる。めちゃくちゃ多くの種類が定義されているので、ここでは触りだけ。

static let airport: MKPointOfInterestCategory

The point of interest category for airports.

static let amusementPark: MKPointOfInterestCategory

The point of interest category for amusement parks.

MKMultiPolygon

MapKitの新クラス。WWDCキーノートを聞きながら

こんな妄想をしてたのもあって、「ポリゴン」という字面からSceneKitと連携するなにかかと一瞬期待したが、よく考えたらMKPolygonというクラスは昔からあって、3Dメッシュ表現手法としてのポリゴンではなくて普通に本来の意味での「多角形」だった。(で、どういうものなのかはまだわかってない)

Create ML

いずれ別記事で書きたいので省略するが、つくれるモデルの種類が増えている。

Create MLって何だっけ?という方はこちらをどうぞ:

shu223.hatenablog.com

VisionKit

Visionとはまた別の新フレームワーク。ドキュメントスキャナ的なものをつくる機能を提供してくれている?

developer.apple.com

VisionKit is a small framework that lets your app use the system's document scanner. Present the document camera as a view controller, which covers the entire screen like the camera function in Notes. Implement the VNDocumentCameraViewControllerDelegate in your own view controller to receive callbacks from the document camera, such as completed scans.

watchOSのIndependent App

これめっちゃいいじゃないですか。今まで2つのApp Extensionで構成されてたのがどうなるのか気になる。プロジェクト生成してみる。

On-device speech recognition

オンデバイスで音声認識。SFSpeechRecognitionRequestに、以下のプロパティが追加されている。

var requiresOnDeviceRecognition: Bool { get set }

その他もちろん気になるフレームワーク群

(SwiftUIとかCombineとかはもちろんキャッチアップするとして)

  • ARKit 3
  • RealityKit
  • Reality
  • Core ML 3
  • Metal
  • MetalKit
  • Metal Performance Shaders
  • Natural Language

etc...

API Diffsから見るiOS 13の新機能 - Core Image #WWDC19

iOS 13の新API発掘シリーズその3です。今回はCore Image。

髪・肌・歯のマスク

iOS 12の"Portrait Matte"は全身セグメンテーション用のマスクだったわけですが、それの「髪・肌・歯」版が取れるようになったようです。

developer.apple.com

static let auxiliarySemanticSegmentationHairMatte: CIImageOption
static let auxiliarySemanticSegmentationSkinMatte: CIImageOption
static let auxiliarySemanticSegmentationTeethMatte: CIImageOption

関連: "Portrait Matte"とは?

shu223.hatenablog.com

デプスブラー

CIContextにデプスブラーフィルタを生成するメソッドが追加。disparityImage(視差画像)、portraitEffectsMatte(全身セグメンテーション用マスク画像)、hairSemanticSegmentation(髪のセグメンテーション画像)を渡せて、出力としてCIFilterオブジェクトが返ってきます。

developer.apple.com

func depthBlurEffectFilter(for image: CIImage, 
            disparityImage: CIImage, 
      portraitEffectsMatte: CIImage?, 
  hairSemanticSegmentation: CIImage?, 
               orientation: CGImagePropertyOrientation, 
                   options: [AnyHashable : Any]? = nil) -> CIFilter?

allowLowPower

また、CIContextOptionに以下の型プロパティが追加されていました。

static let allowLowPower: CIContextOption

ドキュメントには解説がありませんが、ヘッダになにか書いてあるかも知れません。パフォーマンスかなにかを犠牲にして消費電力を抑えるオプションでしょうか?

CIFilterの各フィルタの初期化メソッド

今までは次のように文字列でフィルタ名を指定して初期化しないといけなかったのですが、

let filter = CIFilter(name: "CISepiaTone")!

iOS 13ではドーンと(たぶん)全フィルタの初期化メソッドが追加されました。

一覧は長いので本記事の末尾に載せますが、たとえば上に示したsepia toneフィルタは次のメソッドで初期化できます。

class func sepiaTone() -> CIFilter & CISepiaTone

CIImageのAVSemanticSegmentationMatteからの初期化メソッド

AVSemanticSegmentationMatteはまだ紹介してませんが、名前からしてPortraitMatteの汎用版(Portrait Matteは人間の全身専用マスクだが、もうちょっと汎用的に領域分割するマスク)で、そこからCIImageを生成するメソッドが追加されています。

init?(semanticSegmentationMatte: AVSemanticSegmentationMatte)
init?(semanticSegmentationMatte: AVSemanticSegmentationMatte, options: [CIImageOption : Any]?)

パパッと既存の(≠オリジナルの)画像処理を適用するにはやっぱりCore Imageが簡単なので、これは必須ですね。

CGImageSourceを渡すイニシャライザも追加されています(今までなかったんだっけ。。?)

init(cgImageSource: CGImageSource, index: Int, options: [CIImageOption : Any]?)

AVSemanticSegmentationMatteオブジェクトにはプロパティからアクセスできる(持っていれば)ようです。

var semanticSegmentationMatte: AVSemanticSegmentationMatte?

一色のCIImageを生成する?

ドキュメントには解説がなかったのですが、CIImageに以下のような型プロパティが追加されています。一色塗りのCIImageオブジェクトを生成するのでしょうか?

class var black: CIImage
class var blue: CIImage
class var clear: CIImage
class var cyan: CIImage
class var gray: CIImage
class var green: CIImage
class var magenta: CIImage
class var red: CIImage
class var white: CIImage
class var yellow: CIImage

CIFilterに追加された初期化メソッド一覧

上で解説した、CIFilterを生成するメソッド一覧です。しれっと新たに追加されたフィルタもあるかもしれません。

class func accordionFoldTransition() -> CIFilter & CIAccordionFoldTransition
class func additionCompositing() -> CIFilter & CICompositeOperation
class func affineClamp() -> CIFilter & CIAffineClamp
class func affineTile() -> CIFilter & CIAffineTile
class func attributedTextImageGenerator() -> CIFilter & CIAttributedTextImageGenerator
class func aztecCodeGenerator() -> CIFilter & CIAztecCodeGenerator
class func barcodeGenerator() -> CIFilter & CIBarcodeGenerator
class func barsSwipeTransition() -> CIFilter & CIBarsSwipeTransition
class func bicubicScaleTransform() -> CIFilter & CIBicubicScaleTransform
class func blendWithAlphaMask() -> CIFilter & CIBlendWithMask
class func blendWithBlueMask() -> CIFilter & CIBlendWithMask
class func blendWithMask() -> CIFilter & CIBlendWithMask
class func blendWithRedMask() -> CIFilter & CIBlendWithMask
class func bloom() -> CIFilter & CIBloom
class func bokehBlur() -> CIFilter & CIBokehBlur
class func boxBlur() -> CIFilter & CIBoxBlur
class func checkerboardGenerator() -> CIFilter & CICheckerboardGenerator
class func circularScreen() -> CIFilter & CICircularScreen
class func cmykHalftone() -> CIFilter & CICMYKHalftone
class func code128BarcodeGenerator() -> CIFilter & CICode128BarcodeGenerator
class func colorBlendMode() -> CIFilter & CICompositeOperation
class func colorBurnBlendMode() -> CIFilter & CICompositeOperation
class func colorClamp() -> CIFilter & CIColorClamp
class func colorControls() -> CIFilter & CIColorControls
class func colorCrossPolynomial() -> CIFilter & CIColorCrossPolynomial
class func colorCube() -> CIFilter & CIColorCube
class func colorCubeWithColorSpace() -> CIFilter & CIColorCubeWithColorSpace
class func colorCubesMixedWithMask() -> CIFilter & CIColorCubesMixedWithMask
class func colorCurves() -> CIFilter & CIColorCurves
class func colorDodgeBlendMode() -> CIFilter & CICompositeOperation
class func colorInvert() -> CIFilter & CIColorInvert
class func colorMap() -> CIFilter & CIColorMap
class func colorMatrix() -> CIFilter & CIColorMatrix
class func colorMonochrome() -> CIFilter & CIColorMonochrome
class func colorPolynomial() -> CIFilter & CIColorPolynomial
class func colorPosterize() -> CIFilter & CIColorPosterize
class func comicEffect() -> CIFilter & CIComicEffect
class func convolution3X3() -> CIFilter & CIConvolution
class func convolution5X5() -> CIFilter & CIConvolution
class func convolution7X7() -> CIFilter & CIConvolution
class func convolution9Horizontal() -> CIFilter & CIConvolution
class func convolution9Vertical() -> CIFilter & CIConvolution
class func copyMachineTransition() -> CIFilter & CICopyMachineTransition
class func coreMLModel() -> CIFilter & CICoreMLModel
class func crystallize() -> CIFilter & CICrystallize
class func darkenBlendMode() -> CIFilter & CICompositeOperation
class func depthOfField() -> CIFilter & CIDepthOfField
class func depthToDisparity() -> CIFilter & CIDepthToDisparity
class func differenceBlendMode() -> CIFilter & CICompositeOperation
class func discBlur() -> CIFilter & CIDiscBlur
class func disintegrateWithMaskTransition() -> CIFilter & CIDisintegrateWithMaskTransition
class func disparityToDepth() -> CIFilter & CIDisparityToDepth
class func dissolveTransition() -> CIFilter & CIDissolveTransition
class func dither() -> CIFilter & CIDither
class func divideBlendMode() -> CIFilter & CICompositeOperation
class func documentEnhancer() -> CIFilter & CIDocumentEnhancer
class func dotScreen() -> CIFilter & CIDotScreen
class func edgePreserveUpsample() -> CIFilter & CIEdgePreserveUpsample
class func edgeWork() -> CIFilter & CIEdgeWork
class func edges() -> CIFilter & CIEdges
class func eightfoldReflectedTile() -> CIFilter & CIEightfoldReflectedTile
class func exclusionBlendMode() -> CIFilter & CICompositeOperation
class func exposureAdjust() -> CIFilter & CIExposureAdjust
class func falseColor() -> CIFilter & CIFalseColor
class func flashTransition() -> CIFilter & CIFlashTransition
class func fourfoldReflectedTile() -> CIFilter & CIFourfoldReflectedTile
class func fourfoldRotatedTile() -> CIFilter & CIFourfoldRotatedTile
class func fourfoldTranslatedTile() -> CIFilter & CIFourfoldTranslatedTile
class func gammaAdjust() -> CIFilter & CIGammaAdjust
class func gaussianBlur() -> CIFilter & CIGaussianBlur
class func gaussianGradient() -> CIFilter & CIGaussianGradient
class func glideReflectedTile() -> CIFilter & CIGlideReflectedTile
class func gloom() -> CIFilter & CIGloom
class func hardLightBlendMode() -> CIFilter & CICompositeOperation
class func hatchedScreen() -> CIFilter & CIHatchedScreen
class func heightFieldFromMask() -> CIFilter & CIHeightFieldFromMask
class func hexagonalPixellate() -> CIFilter & CIHexagonalPixellate
class func highlightShadowAdjust() -> CIFilter & CIHighlightShadowAdjust
class func hueAdjust() -> CIFilter & CIHueAdjust
class func hueBlendMode() -> CIFilter & CICompositeOperation
class func hueSaturationValueGradient() -> CIFilter & CIHueSaturationValueGradient
class func kaleidoscope() -> CIFilter & CIKaleidoscope
class func labDeltaE() -> CIFilter & CILabDeltaE
class func lanczosScaleTransform() -> CIFilter & CILanczosScaleTransform
class func lenticularHaloGenerator() -> CIFilter & CILenticularHaloGenerator
class func lightenBlendMode() -> CIFilter & CICompositeOperation
class func lineOverlay() -> CIFilter & CILineOverlay
class func lineScreen() -> CIFilter & CILineScreen
class func linearBurnBlendMode() -> CIFilter & CICompositeOperation
class func linearDodgeBlendMode() -> CIFilter & CICompositeOperation
class func linearGradient() -> CIFilter & CILinearGradient
class func linearToSRGBToneCurve() -> CIFilter & CILinearToSRGBToneCurve
class func luminosityBlendMode() -> CIFilter & CICompositeOperation
class func maskToAlpha() -> CIFilter & CIMaskToAlpha
class func maskedVariableBlur() -> CIFilter & CIMaskedVariableBlur
class func maximumComponent() -> CIFilter & CIMaximumComponent
class func maximumCompositing() -> CIFilter & CICompositeOperation
class func median() -> CIFilter & CIMedian
class func meshGenerator() -> CIFilter & CIMeshGenerator
class func minimumComponent() -> CIFilter & CIMinimumComponent
class func minimumCompositing() -> CIFilter & CICompositeOperation
class func mix() -> CIFilter & CIMix
class func modTransition() -> CIFilter & CIModTransition
class func morphologyGradient() -> CIFilter & CIMorphologyGradient
class func morphologyMaximum() -> CIFilter & CIMorphologyMaximum
class func morphologyMinimum() -> CIFilter & CIMorphologyMinimum
class func morphologyRectangleMaximum() -> CIFilter & CIMorphologyRectangleMaximum
class func morphologyRectangleMinimum() -> CIFilter & CIMorphologyRectangleMinimum
class func motionBlur() -> CIFilter & CIMotionBlur
class func multiplyBlendMode() -> CIFilter & CICompositeOperation
class func multiplyCompositing() -> CIFilter & CICompositeOperation
class func noiseReduction() -> CIFilter & CINoiseReduction
class func opTile() -> CIFilter & CIOpTile
class func overlayBlendMode() -> CIFilter & CICompositeOperation
class func pageCurlTransition() -> CIFilter & CIPageCurlTransition
class func pageCurlWithShadowTransition() -> CIFilter & CIPageCurlWithShadowTransition
class func paletteCentroid() -> CIFilter & CIPaletteCentroid
class func palettize() -> CIFilter & CIPalettize
class func parallelogramTile() -> CIFilter & CIParallelogramTile
class func pdf417BarcodeGenerator() -> CIFilter & CIPDF417BarcodeGenerator
class func perspectiveCorrection() -> CIFilter & CIPerspectiveCorrection
class func perspectiveTile() -> CIFilter & CIPerspectiveTile
class func perspectiveTransform() -> CIFilter & CIPerspectiveTransform
class func perspectiveTransformWithExtent() -> CIFilter & CIPerspectiveTransformWithExtent
class func photoEffectChrome() -> CIFilter & CIPhotoEffect
class func photoEffectFade() -> CIFilter & CIPhotoEffect
class func photoEffectInstant() -> CIFilter & CIPhotoEffect
class func photoEffectMono() -> CIFilter & CIPhotoEffect
class func photoEffectNoir() -> CIFilter & CIPhotoEffect
class func photoEffectProcess() -> CIFilter & CIPhotoEffect
class func photoEffectTonal() -> CIFilter & CIPhotoEffect
class func photoEffectTransfer() -> CIFilter & CIPhotoEffect
class func pinLightBlendMode() -> CIFilter & CICompositeOperation
class func pixellate() -> CIFilter & CIPixellate
class func pointillize() -> CIFilter & CIPointillize
class func qrCodeGenerator() -> CIFilter & CIQRCodeGenerator
class func radialGradient() -> CIFilter & CIRadialGradient
class func randomGenerator() -> CIFilter & CIRandomGenerator
class func rippleTransition() -> CIFilter & CIRippleTransition
class func sRGBToneCurveToLinear() -> CIFilter & CISRGBToneCurveToLinear
class func saliencyMap() -> CIFilter & CISaliencyMap
class func saturationBlendMode() -> CIFilter & CICompositeOperation
class func screenBlendMode() -> CIFilter & CICompositeOperation
class func sepiaTone() -> CIFilter & CISepiaTone
class func shadedMaterial() -> CIFilter & CIShadedMaterial
class func sharpenLuminance() -> CIFilter & CISharpenLuminance
class func sixfoldReflectedTile() -> CIFilter & CISixfoldReflectedTile
class func sixfoldRotatedTile() -> CIFilter & CISixfoldRotatedTile
class func smoothLinearGradient() -> CIFilter & CISmoothLinearGradient
class func softLightBlendMode() -> CIFilter & CICompositeOperation
class func sourceAtopCompositing() -> CIFilter & CICompositeOperation
class func sourceInCompositing() -> CIFilter & CICompositeOperation
class func sourceOutCompositing() -> CIFilter & CICompositeOperation
class func sourceOverCompositing() -> CIFilter & CICompositeOperation
class func spotColor() -> CIFilter & CISpotColor
class func spotLight() -> CIFilter & CISpotLight
class func starShineGenerator() -> CIFilter & CIStarShineGenerator
class func straighten() -> CIFilter & CIStraighten
class func stripesGenerator() -> CIFilter & CIStripesGenerator
class func subtractBlendMode() -> CIFilter & CICompositeOperation
class func sunbeamsGenerator() -> CIFilter & CISunbeamsGenerator
class func supportedRawCameraModels() -> [String]!
class func swipeTransition() -> CIFilter & CISwipeTransition
class func temperatureAndTint() -> CIFilter & CITemperatureAndTint
class func textImageGenerator() -> CIFilter & CITextImageGenerator
class func thermal() -> CIFilter & CIThermal
class func toneCurve() -> CIFilter & CIToneCurve
class func triangleKaleidoscope() -> CIFilter & CITriangleKaleidoscope
class func triangleTile() -> CIFilter & CITriangleTile
class func twelvefoldReflectedTile() -> CIFilter & CITwelvefoldReflectedTile
class func unsharpMask() -> CIFilter & CIUnsharpMask
class func vibrance() -> CIFilter & CIVibrance
class func vignette() -> CIFilter & CIVignette
class func vignetteEffect() -> CIFilter & CIVignetteEffect
class func whitePointAdjust() -> CIFilter & CIWhitePointAdjust
class func xRay() -> CIFilter & CIXRay
class func zoomBlur() -> CIFilter & CIZoomBlur

API Diffsから見るiOS 13の新機能 - CoreML公式配布モデル #WWDC19

iOS 13の新API発掘シリーズその2です。

f:id:shu223:20190603103013j:plain

以前の公式配布CoreMLモデルと言えば、imagenetベースの物体検出がほとんどで、あまり新鮮味のあるモデルは配布されていませんでした。

qiita.com

なので、自作するか、サードパーティ製のを探して使うことがほとんどでした。

qiita.com

note.mu

・・・が、今日見てみると、楽しそうなモデルがたくさん追加されているではありませんか!

というわけで前回のVision編に続き、本記事では(APIではありませんが)CoreMLの公式配布モデルについて見ていきます。

デプス推定

  • FCRN-DepthPrediction
    • Depth Estimation

redict the depth from a single image.

画像からデプスを「推定」します。つまり、単眼カメラでも、デプスデータを持っていない既存写真からも、デプスが取得できるようになる、ということです。ここからAVDepthDataを生成することもできるはずです。

・・・ということはデプスを利用する機能が実質的にデバイスを限定する機能ではなくなるといえるのではないでしょうか。今まではデプスを使う機能を提供しようにもiPhone X系とか7 Plus, 8 Plus系のハイエンドデバイスのユーザーしか使えなかったので、興味やアイデアはあってもなかなか採用しづらかったのではないかと思います。

これでついにデプス利用機能が多少は普及しそうですね。参考書籍を貼っておきます。

shu223.booth.pm

(もちろん今回の発表をふまえてアップデート予定です)

セグメンテーション(領域分割)

  • DeeplabV3
    • Image Segmentation

Segment the pixels of a camera frame or image into a predefined set of classes.

f:id:shu223:20190604103249p:plain

サムネイル画像によると人間のセグメンテーションのように見えますが、解説を読むと"predefined set of classes"とあり、複数のクラスに対応しているようです。

試しに.mlmodelファイルをダウンロードして埋め込まれているdescriptionを読んでみると、

person, dog, cat and so on

とあります。人の他に犬や猫もセグメンテーションできるようです。

関連: iOS 12のPortrait Matteがすごい/ #iOSDC 2018で登壇します - その後のその後

物体検出(YOLO)

  • YOLOv3

    • Object Detection
  • YOLOv3-Tiny

    • (Real-time) Object Detection

Locate and classify 80 different types of objects present in a camera frame or image.

物体を「画像内のどこにあるか」の情報を含め検出してくれるモデル。リアルタイム検出用の"Tiny"版もあります。

f:id:shu223:20190604103336p:plain

質問への回答を生成

  • BERT-SQuAD
    • Question Answering

Generate answers to questions about paragraphs of text.

公式配布モデルでは唯一の自然言語処理用。

指定したパラグラフ内のテキストに関して、質問を与えると回答を生成してくれるようです。

f:id:shu223:20190604103357p:plain

API Diffsから見るiOS 13の新機能 - Vision #WWDC19

iOSエンジニア諸氏のツイートを見ているとSwiftUIが圧倒的インパクトっぽい今回のWWDCですが、そのへんは識者の方々にお任せして、「その他フレームワーク」で気になった新APIを見ていきたいと思います。

f:id:shu223:20190603103013j:plain

まずはVision。

人間検出

VNDetectHumanRectanglesRequest

人間を矩形で検出。

↓はOpenCV使ったやつですが、こういうのです。

f:id:shu223:20190604093836p:plain

ARKitのpeople segmentationがインパクトありすぎて「矩形で検出」というのは印象薄いですが、「こういうのもあったな」と頭の片隅に置いておくといつか用途があるかもしれません。

文字認識

VNRecognizeTextRequest

文字認識。今まであった文字領域検出じゃなくて、文字認識、いわゆるOCR的なやつです。

今まで無料で使えるやつだとtesseractとSwiftOCRぐらいしか選択肢がなかったのが、ついに標準でサポート・・・!感無量です。(対応言語は後で調べる)

qiita.com

github.com

対応言語はsupportedRecognitionLanguages(for:revision:)というメソッドをたたけば調べられそう。あとでやってみます。

動物検出

VNDetectAnimalRectanglesReques

VNAnimalDetectorのtype propertyとして今のところcatdogが用意されており、つまりイヌネコ認識が可能となります。ペットが写ってる写真とかを判別できるようになったわけですね。

画像の類似度を計算

  • VNGenerateImageFeaturePrintRequest
  • VNFeaturePrintObservation

"Analyzing Image Similarity with Feature Print"というサンプルが公開されており、画像間の類似度が計算できるようになったようです。後でコード読んでみます。

画像分類?

VNClassifyImageRequest

画像分類は前からできたはずだけど、この新クラスは何だろう?

Image Saliency

State of the Unionでも出てきたこのキーワード。"salient"は辞書によると「顕著な」(英英では"most noticeable or important.")の意味。

VNGenerateAttentionBasedSaliencyImageRequest

Generates a heat map that identifies the parts of an image most likely to draw attention.

画像内で、もっとも注意を引く部分を特定するヒートマップを生成・・・?"draw attention"な箇所ってのがよくわかりませんね。試してみます。

VNGenerateObjectnessBasedSaliencyImageRequest

Generates a heat map that identifies the parts of an image most likely to represent objects.

画像内で、もっともオブジェクトを表している部分を特定するヒートマップを生成する。ここでいう「オブジェクト」はこちらで指定できるのでしょうか。あとでもうちょっとAPIを見てみます。

VNSaliencyImageObservation

上2つのrequestの結果として得られるクラス。VNPixelBufferObservationを継承し、グレースケールのヒートマップデータを持つ。

var salientObjects: [VNRectangleObservation]? { get }

というプロパティを持っていて、salient Objectsの矩形を複数保持できるようになっています。

Saliencyのサンプルコードとドキュメント

この"Saliency"関連では、"Highlighting Areas of Interest in an Image Using Saliency"というサンプルコードと、"Cropping Images Using Saliency"というドキュメントがあります。

「iOSエンジニアのためのmacOSアプリ開発入門」という本を書きました #技術書典 #技術書典6

macOS/Cocoaアプリ開発の技術書を書きました。macOSアプリ開発は、

  • 使用言語はSwift
  • IDEはXcode
  • 標準フレームワークの多くがiOSと共通

と、iOSアプリ開発と共通点が多いため、iOSエンジニアにとっては比較的とっつきやすいはずです。

・・・が、「UIKitではなくAppKitだし、なんだかんだと色んな点で違っていてめんどくさそう」という気がして興味はあるけど未だ手付かずという方も多いのではないでしょうか。

そこで本書では、iOSエンジニアの視点から「これ、macOSではどうやるの?」という事項を集めてまとめてみました。各項の解説は「iOSエンジニアに伝われば十分」という観点で非常に簡潔に書いてあるので、本書をパラパラと眺めてみるだけでも「違いといってもこんなもんか。簡単そうだからやってみよう。」という気分になるはずです。

f:id:shu223:20190409214248j:plain:w200:left f:id:shu223:20190409214305j:plain:w200:left f:id:shu223:20190409214318j:plain:w200:left

電子版をBOOTHにて販売中

個人出版で、BOOTHにて電子版を販売しています。

booth.pm

macOS Mojave 10.14, Xcode 10.2, Swift 5.0対応、46ページ。今後価格改定する可能性はありますが、今のところなんとたったの800円です。

ページ数少ないけど大丈夫?

重厚長大な技術書もエンジニア心をくすぐられますが、コンテンツがあふれる昨今、「積まずにサラッと読める」というのもひとつの価値だと考えています。

いつかやるmacOS開発に向けて積んでおくのではなく、DLしてすぐに開き、数分パラパラ眺める読み方を推奨します。

この数分で「ふーんこんな感じなのね」と思っていただければ本書のコンセプトとしては成功です。

macOSアプリをつくる予定がない?

macOSアプリはiOSアプリと比べてまだマネタイズしづらいイメージがあると思います。実際どうなのか、僕もApp StoreでmacOSアプリをリリースしたことがないのでよくわかりません。

しかし、iOSエンジニアであれば開発環境として日々macOSで作業していると思います。開発作業の中で「これめんどいなー、サクッと自分用ユーティリティアプリがつくれたらいいな」と思ったことはないでしょうか。

本書はそれぐらいのライトなmacOSアプリ開発を想定しています。

目次

はじめに

第1章 基礎の基礎

  • 1.1 プロジェクト作成
  • 1.2 座標系
  • 1.3 ビューの背景色
  • 1.4 Interface Builder

第2章 基本 UI コンポーネント

  • 2.1 ボタン
  • 2.2 画像表示
  • 2.3 ラベル
  • 2.4 テーブル
  • 2.5 アラート

第3章 アニメーション

  • 3.1 ビューのアニメーション
  • 3.2 アニメーション速度を変更する
  • 3.3 Auto Layoutのアニメーション

第4章 画面遷移

  • 4.1 標準セグエ
  • 4.2 同じウィンドウ内で画面遷移させる
  • 4.3 遷移アニメーションを自作する

第5章 その他iOSと手順が違う点

  • 5.1 カスタムフォントを利用する
  • 5.2 subviewの順序を変える
  • 5.3 マウスクリックイベントをハンドルする
  • 5.4 カスタムセルを作成する
  • 5.5 xibからのビューを作成する
  • 5.6 画面サイズとウィンドウサイズ
  • 5.7 クロスプラットフォーム

第6章 macOS独自の機能

  • 6.1 ファイルを開く
  • 6.2 メニューを追加する
  • 6.3 コンテキストメニューを追加する
  • 6.4 ステータスバー常駐アプリケーションを作成する
  • 6.5 起動中のアプリケーションのリストを取得する
  • 6.6 他のアプリケーションを操作する

第7章 Cocoa Bindings - 7.1 Cocoa Bindingsで配列をテーブルに表示する

製本版

「技術書典6」にて製本版を販売します。印刷部数が少ないくせに特殊用紙を使ってオフセット印刷したので原価がかなりかかっており次回はたぶんもうないスペシャル仕様です。会場に来られる方はぜひ「け13」までお越しください。

techbookfest.org

try! Swift Tokyo 2019の復習 #tryswiftconf

平日に戻ったら復習の機会もなくなるのでできるだけスライドとかで復習していきます。ここに書いた以外にも素晴らしいセッションがたくさんありましたが、会場で理解が追いつかなかったもの、あまりちゃんと聞けなかったものを中心に。

Keypath入門 - Introduction to Swift Keypaths

speakerdeck.com

SwiftのKeyPathを使って型の抽象化を行う話。例として、以下のような全然違う要素を持つStructを抽象化するのはProtocolだとできないけど、KeyPathを使えばできるよ、と。

f:id:shu223:20190325060340p:plain

こういうStructがあるとして、

struct User {
    var username: String
}

var player = User(username: "マリオ")

で、Swiftのキーパスではこんな感じでUserの持つusernameにアクセスできる。

player[keyPath: \User.username] = "リンク"

(この書式、全く知らなかった)

こんな感じで変数に格納することもできる。

let nameKeyPath = \User.username
player[keyPath: nameKeyPath] = "ルイージ"

で、キーパスには色々種類があるという話と、

  • KeyPath<Root, Value>
  • WritableKeyPath<Root, Value>
  • ReferenceWritableKeyPath<Root, Value>
  • PartialKeyPath
  • AnyKeyPath

合成できるという話があって、

f:id:shu223:20190325060633p:plain

これらの性質を使って、冒頭に挙げたSettingsを表す要素の全然違うStructをどう抽象化するか、という具体例について説明される。

あとKeyPathはHashableなのでDictionaryのキーにも使えますよ、とか。

Swiftにおける音の成形

スライドはGitHubにある。

github.com

このセッションは音の基礎から始まって、で、iOSではどうやるの?というところでCore Audioは難しいからAudioKit使おう、という流れ。

音声処理は興味がある。が、あんまり「音楽」の文脈では興味がなくて、どっちかというと「現実世界をセンシングする」という文脈で興味がある。このセッションは音楽寄りの話なのでそういう意味では僕の興味にストライクではなかったが、AudioKitがディレイやリバーブのような基本的な処理だけじゃなくてAKMoogLadderみたいなかなりアプリケーションよりのところまで含んでいるというのは知見だった。

試しにAudioKitのプロジェクトでvoiceでクラス群をフィルタしてみると、SamplerVoiceとかSynthVoiceとか興味深いクラスがあるし、MIDI系の実装も充実している。あとAudioKitのリポジトリはAudioKitというアカウントにあって、

github.com

サンプルとか、音をパーティクルでビジュアライズするプロジェクトもある。

Metalを使用。あとでちゃんとコードを読んでみようと思う。

f:id:shu223:20190325061041p:plain

(iPhoneでも普通に動いた)

PixarのようなグラフィックをSwiftで実現する

レイトレーシングの話。iOS 12でMetalに追加されたレイトレーシングの話かと思いきや、

developer.apple.com

シンプルなレイトレーサーをSwiftで自前実装する、という話だった。めちゃくちゃいい。自分で実装するというのは理解度が全然違う。

コードはGitHubにある。

github.com

"Pete Shirley"という人の書籍を参考に書いたコードらしい。

Much of the code is derived from Pete Shirley's Ray Tracing Minibooks, with a few added extras.

で、このリポジトリ、Swiftのファイルが2つとビルドスクリプトしか入ってない。Xcodeプロジェクトはなく、コマンドからビルドして各フレームの画像をppmで出力し、それらをffmpegに食わせてmp4として吐き出すというもののようだ。

中身を見ると、Metalを使ってないどころか、SceneKitもCore ImageもCore Graphicsも使ってない。Foundationだけ。

(ということをツイートしたら、レイトレーシングという(iOS界隈では)ニッチな話題にもかかわらず結構な反響があった)

次のようなアルゴリズムらしい。

  • Line sphere intersection
  • Path tracing
  • Lamberian scattering
  • Fresnel equations
  • Schlick approximation
  • Perlin noise
  • Rodrigues rotation formula

これはたぶんコードを読むだけでは何やってるかわからないやつだ。。まぁそれぞれのアルゴリズムの日本語名を調べて、何をやってるのか(中身の詳細はブラックボックスでもいいので)理解する、ぐらいのことはやりたい。

SwiftSyntax で便利を実現する基礎

SwiftSyntaxというSwiftの公式ライブラリがあって、それを使って構文解析したり、コードの一部を書き換えたり(修正コードを生成)する方法の紹介。

speakerdeck.com

めっちゃわかりやすいけど、会場ではボーッとしてて(スライドが日本語だったのでそこが気になって回想モードに入ってしまった)、『SwiftSyntaxは公式ライブラリである』というところを聞き逃してしまい、構文解析がどのように行われるかを解説しているのか、Swiftのコンパイラ実装の一部を取り出してツールとして利用するような話をしているのか、みたいに理解の迷子になってしまった。プレゼンを聞くときは冒頭は絶対に聞き逃してはいけない。。

Swift type metadata

SwiftのType metadataを通じてSwiftのランタイムを理解しよう、というセッション。

speakerdeck.com

let metatype: Int.Type = Int.self

このInt.Typeは「メタタイプ(Metatype)」と呼ばれる型で、クラスや構造体、列挙型、プロトコル等、型を表すための型{型名}.selfでこのメタタイプを型自体から取得できる。

で、このセッションの主役である"type metadata"は、Swiftランタイムにおいて型の情報を表すデータで、Metatypeはtype metadataへのポインタである、とのこと。

これを用いて、Method swizzlingも可能になる。

class Animal {
    func bar() { print("bar") }
    func foo() { print("foo") }
}

struct ClassMetadata {
    ...
    // VTable
    var barRef: FunctionRef
    var fooRef: FunctionRef
}

let metadata = unsafeBitCast(
    Animal.self, to: UnsafeMutablePointer<ClassMetadata>.self
)
let bar = withUnsafeMutablePointer(to: &metadata.pointee.barRef) { $0 }
let foo = withUnsafeMutablePointer(to: &metadata.pointee.fooRef) { $0 }
bar.pointee = foo.pointee
let animal = Animal()
animal.bar() // foo

これを利用したOSSがStubKitで、

github.com

Decodableに準拠している任意のクラスや構造体を1行で初期化できるようにしてくれる、というものらしい。

import StubKit

// Codable struct
struct User: Codable {
  let id: Int
  let name: String
  let sex: Sex
}

let stubUser = try Stub.make(User.self)
// User(id: 1234, name: "This is Stub String", sex: .female)

便利そう。

通常の開発ではあまり見ることのない言語の「内部」を読み解き、さらに実用面での使いどころを考え、OSSにまで落とし込む(しかもかなり普遍性がある)という一連の所業は見習いたい。

ポートレートモードを自作しよう / Making Portrait mode yourself

深度情報が付属しない2D画像、しかも現実世界の写真だけじゃなくてたとえばマンガやアニメの画像も背景をぼかしたりしたい、という試み。

speakerdeck.com

深度推定の機械学習モデル使うのかなと思ったが、違った。それだと期待した性能が出ない(機械学習なので、学習させたシーンの背景しか分離できない)ので、"GrabCut"なるアルゴリズムを採用したとのこと。

LTなのでアルゴリズムの詳細説明はなくサッと次のスライドに行ってしまったので、帰ってググってみたところ、2004年の論文で提案されたアルゴリズムのようだった。

try! Swiftの当該セッションの実装コードを見ると、OpenCVにそのものズバリな関数が用意されていて、それを使っているようだった。

cv::grabCut(sourceMat, maskMat, rectangle, bgModel, fgModel, iterationCount, cv::GC_INIT_WITH_RECT);

これはいいことを知った。デュアルカメラやTrueDepthがハイエンドiPhoneに限定されたスペックである以上、iOSが13になろうと14になろうとデプス取得手段がないデバイスで撮影された画像への対処は必要で、こういう手段もある、というのは知れてよかった。

OpenCVの公式サイトに色々とドキュメントがある。

docs.opencv.org

画像処理界隈ではよく知られたアルゴリズムのようで、"grabcut opencv"でググると日本語情報もたくさん出てくる。

つづく

復習したいセッションのリストはまだまだあるので、つづきます。