ObjecTips

Swift & Objective-C で iOS とか macOS とか

PDFView の表示設定まとめ

関連記事

koze.hatenablog.jp

※ iOS 11 beta 時点での検証です

検証コード
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    PDFView *view = [[PDFView alloc] initWithFrame:self.view.bounds];
    view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    view.autoScales = YES;
    view.displayDirection = kPDFDisplayDirectionVertical;
    view.displayMode = kPDFDisplaySinglePage;
    view.displaysRTL = NO;
    [self.view addSubview:view];
    
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf"];
    PDFDocument *document = [[PDFDocument alloc] initWithURL:URL];
    view.document = document;
}
検証結果

画像の数字は表示ページ数
白背景は初期状態で表示される部分、灰色部分はスクロール領域の部分

displaysRTL = NO

kPDFDisplayDirectionVertical kPDFDisplayDirectionHorizontal
kPDFDisplaySinglePage f:id:Koze:20170612202100p:plain f:id:Koze:20170612202100p:plain
kPDFDisplaySinglePageContinuous f:id:Koze:20170612202439p:plain f:id:Koze:20170612203519p:plain
kPDFDisplayTwoUp f:id:Koze:20170612202611p:plain f:id:Koze:20170612202611p:plain
kPDFDisplayTwoUpContinuous f:id:Koze:20170612202724p:plain f:id:Koze:20170612202724p:plain

displaysRTL = YES

kPDFDisplayDirectionVertical kPDFDisplayDirectionHorizontal
kPDFDisplaySinglePage f:id:Koze:20170612202100p:plain f:id:Koze:20170612202100p:plain
kPDFDisplaySinglePageContinuous f:id:Koze:20170612202439p:plain f:id:Koze:20170612203519p:plain
kPDFDisplayTwoUp f:id:Koze:20170612204616p:plain f:id:Koze:20170612204616p:plain
kPDFDisplayTwoUpContinuous f:id:Koze:20170612204920p:plain f:id:Koze:20170612204920p:plain

以下の2点がポイント

  • displaysRTL の設定は kPDFDisplayTwoUpkPDFDisplayTwoUpContinuous の時のみ効果有りで見開きの左右を指定するパラメータ
  • kPDFDisplayTwoUpContinuous にしていると kPDFDisplayDirectionHorizontal の設定は効果無し。見開きのまま横スクロールは出来ない。(仮に横スクロール出来たしたとしても kPDFDisplaySinglePageContinuous と見た目が変わらないため?)

という事で、ページ送り(スクロール)自体を右から左にしてくれるオプションは無いようなので日本の小説や漫画など縦書きのコンテンツを表示させる場合は Continuous モードを使わないで自前でページ移動を実装する必要がある。

Vision Framework で水平角検出(傾き) Horizon Detection

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
iOS 11 Core Image の変更点 - ObjecTips
koze.hatenablog.jp

Horizon Detection

Vision Framework を使って傾き検出と得られた結果から表示の補正を行う。
カメラロールの中に程良く傾いた景色の画像があったのでそれを使う。まず何も処理を入れず普通に UIImage を作成して UIImageView に配置する。

f:id:Koze:20170608035929p:plain

実装

Horizon Detection with Vision Framework

まず VNImageRequestHandler を作成。
今回は UIImage 経由で CGImageRef から作成している。
次に VNDetectHorizonRequest 作成しハンドラを設定してリクエストを実行する。

うまく傾きを検出できると request.resultsVNHorizonObservationが入ってくる。
VNHorizonObservation の持つプロパティは以下2つ

@property (readonly, nonatomic, assign) CGAffineTransform transform;
@property (readonly, nonatomic, assign) CGFloat angle;

ドキュメントにもヘッダにも何も書かれていないけど、angle は検出された傾きだろうという事が分かる。ちなみにこの値は radian になっている。 この angle から以下の様に UIImageView を補正表示するための CGAffineTransform を作成する。

CGAffineTransform t = CGAffineTransformMakeRotation(-horizonObservation.angle);

これまで同様に座標系が逆になっているので結果の angle にマイナス値をかけて値を補正している。
この CGAffineTransformUIImageView に設定してやる。

実行結果

以下の様になる。

f:id:Koze:20170608040856p:plain

正しく傾きの補正が出来ている模様。
UIImageView の中心をずらさずに回転のみで補正した形になる。

コメントアウト部分にある様に

CGAffineTransformInvert(CGAffineTransformMakeRotation(horizonObservation.angle));

この様に値を補正してやっても同じ結果になる。

実行結果2

検出結果の VNHorizonObservation にもう1つプロパティがある。

@property (readonly, nonatomic, assign) CGAffineTransform transform;

これを使って UIImageView を変形してみる。
検出結果の座標系は逆になっているので先の例と同じ様に Invert で補正してやる。

CGAffineTransformInvert(horizonObservation.transform);

結果は以下

f:id:Koze:20170608041443p:plain

傾きの補正はOKっぽい。
ただ位置の変形がこれでいいのかどうか、、?
transform プロパティが何を表しているのか、どういった値が入ってくるのか現時点ではドキュメントも無いしWWDCのセッションもまだなので何かしら解説が出るのを待ってみたい。

iOS 11 Core Image の変更点

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
Vision Framework でテキスト検出 Text Detection - ObjecTips
Vision Framework で水平角検出(傾き) Horizon Detection - ObjecTips

Core Image

CIImage - Core Image | Apple Developer Documentation

CIImage
@property (nonatomic, readonly, nullable) AVDepthData *depthData NS_AVAILABLE(10_13,11_0);

Depthデータの取得。
イニシャライザのオプションで kCIImageAuxiliaryDepthkCIImageAuxiliaryDisparity のどちらかを YES に設定すると取得出来るらしい。このオプションを用いるとプライマリの画像の代わりに補助(auxiliary)画像の CIImage が作られて、もし補助画像が無い場合は nil になってしまうらしい。
補助画像は half float のモノクロ画像との事。

/* Returns a new image by changing the receiver's sample mode to bilinear interpolation. */
- (CIImage *)imageBySamplingLinear NS_AVAILABLE(10_13, 11_0)
  NS_SWIFT_NAME(samplingLinear());

/* Returns a new image by changing the receiver's sample mode to nearest neighbor. */
- (CIImage *)imageBySamplingNearest NS_AVAILABLE(10_13, 11_0)
  NS_SWIFT_NAME(samplingNearest());

画像の補間モードをバイリニア補間(bilinear interpolation)とニアレストネイバー(nearest neighbor)という補間方法に変換してくれるらしい。

CIFIlter

iOS 11 で追加された CIFIlter

CIAreaMinMaxRed
CIAttributedTextImageGenerator
CIBarcodeGenerator
CIBicubicScaleTransform
CIBokehBlur
CIColorCubesMixedWithMask
CIColorCurves
CIDepthBlurEffect
CIDepthToDisparity
CIDisparityToDepth
CIEdgePreserveUpsampleFilter
CILabDeltaE
CITextImageGenerator

DepthToDisparity と DepthToDisparity が存在していて Depth と Disparity の相互変換が出来るという事が分かる。

CIContext
- (nullable NSData*) PNGRepresentationOfImage:(CIImage*)image
                                       format:(CIFormat)format
                                   colorSpace:(CGColorSpaceRef)colorSpace
                                      options:(NSDictionary*)options NS_AVAILABLE(10_13,11_0);
- (nullable NSData*) HEIFRepresentationOfImage:(CIImage*)image
                                        format:(CIFormat)format
                                    colorSpace:(CGColorSpaceRef)colorSpace
                                       options:(NSDictionary*)options NS_AVAILABLE(10_13,11_0);
- (BOOL) writePNGRepresentationOfImage:(CIImage*)image
                                 toURL:(NSURL*)url
                                format:(CIFormat)format
                            colorSpace:(CGColorSpaceRef)colorSpace
                               options:(NSDictionary*)options
                                 error:(NSError **)errorPtr NS_AVAILABLE(10_13,11_0);
- (BOOL) writeHEIFRepresentationOfImage:(CIImage*)image
                                  toURL:(NSURL*)url
                                 format:(CIFormat)format
                             colorSpace:(CGColorSpaceRef)colorSpace
                                options:(NSDictionary*)options
                                  error:(NSError **)errorPtr NS_AVAILABLE(10_12,10_0);

これまではTIFFとJPEGに書き出す事が可能だったのが、PNGとHEIFへの書き出しも可能になった。

CORE_IMAGE_EXPORT NSString * const kCIImageRepresentationAVDepthData NS_AVAILABLE(10_13,11_0);
CORE_IMAGE_EXPORT NSString * const kCIImageRepresentationDepthImage NS_AVAILABLE(10_13,11_0);
CORE_IMAGE_EXPORT NSString * const kCIImageRepresentationDisparityImage NS_AVAILABLE(10_13,11_0);

上記の書き出しメソッドにオプションとして指定する事が出来る。ただしヘッダによればTIFFとPNGはオプション指定が不可で、JPEGとHEIFの場合にこれらのオプションが指定可能らしい。

CIRenderTask, CIRenderDestination, CIRenderInfo

非同期レンダリングのためのクラス群 CIRenderTask には

- (nullable CIRenderInfo*) waitUntilCompletedAndReturnError:(NSError**)error;

というメソッドがあるので同期的にも使えそう。

CIKernel
+ (nullable instancetype)kernelWithFunctionName:(NSString *)name
                           fromMetalLibraryData:(NSData *)data
                                          error:(NSError **)error NS_AVAILABLE(10_13, 11_0);

+ (nullable instancetype)kernelWithFunctionName:(NSString *)name
                           fromMetalLibraryData:(NSData *)data
                              outputPixelFormat:(CIFormat)format
                                          error:(NSError **)error NS_AVAILABLE(10_13, 11_0);

data には Core Image Standard Library でコンパイルされた mtallib ファイルのデータ、name には Metal Shading Language で書かれた関数名が必要らしい。

CIBlendKernel

2つの画像をブレンドする CIColorKernel のプリセット群の様なものが用意された。
例えば

@property (class, strong, readonly) CIBlendKernel *componentAdd;
@property (class, strong, readonly) CIBlendKernel *componentMultiply;

などクラスプロパティで簡易的にカーネル処理の色フィルタを呼び出す事が出来る。その数50個

CIBarcodeDescriptor

バーコードデータのモデルクラスでサブクラスとして以下が存在する。

  • CIQRCodeDescriptor
  • CIAztecCodeDescriptor
  • CIPDF417CodeDescriptor
  • CIDataMatrixCodeDescriptor

バーコードの生成と、Core Image, Vision, AVFoundation でのバーコードの検出で使用されるとの事。

CIQRCodeFeature
@property (nullable, readonly) CIQRCodeDescriptor *symbolDescriptor NS_AVAILABLE(10_13, 11_0);

前述のバーコードのモデルクラスを取得可能。

感想

CIBlendKernel のプリセット呼び出しは簡単だけど CIFilter のカラーブレンド系とパフォーマンスが違うのか気になる。呼び出し型が違うだけで中の処理は同じではないか?とも思う。
HEIFデータの扱いとか AVDepthData の活用とかはしらばくは2眼カメラの iPhone 7 Plus に限定されてしまうかも知れないけど面白そう。このあたりの詳細は AVFoundation を調べる必要がありそう。

iOS 11 PDFKit

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 Core Image の変更点 - ObjecTips
Vision Framework でテキスト検出 Text Detection - ObjecTips
Vision Framework で水平角検出(傾き) Horizon Detection - ObjecTips

koze.hatenablog.jp

PDFKit について

PDFKit | Apple Developer Documentation

iOS 11 で登場した PDFKit は macOS では 10.4 Tiger から存在しているPDF周りの高レベルの Framework で、ただの Core Graphics 関数のラップではなくかなり高機能になっている。

NSURLPDFDocument を初期化するだけでPDF内の文字列が取得できて、文字列検索もできて、ページの入れ替え差し替え、書き出し、アウトライン情報の取得まで出来る。
(アウトライン情報は階層構造を持つリンク付きの目次のようなもので、辞書構造の中にはタイトルと階層情報、リンクの飛び先の情報が入っている。)

PDFViewPDFDocument を設定すればページ移動、拡大縮小、文字列選択、リンク移動の機能を持つ高機能ビューアが出来てしまう。見開き表示もにも対応。PDFThumbnailView によるサムネイル一覧表示までサポートされている。

PDFDocument から取り出したページ情報 PDFPage を使ってアノテーションの追加や指定箇所の文字列の選択、1文字毎の bounds の取得なんかも出来てしまう。

今からPDFビューアを作る人は絶対にこの Framework を使った方がいい。(ていうか何で今まで iOS に持ってきてくれなかったんだ、、)
さらに今回のアップデートでは macOS でも初出の機能があるのでその辺を中心に見ていく。

PDFView
@property (nonatomic) PDFDisplayDirection displayDirection PDFKIT_AVAILABLE(10_13, 11_0);
// Display direction.
PDFKIT_ENUM_AVAILABLE(10_13, 11_0)
typedef NS_ENUM(NSInteger, PDFDisplayDirection)
{
    kPDFDisplayDirectionVertical = 0,
    kPDFDisplayDirectionHorizontal = 1,
};

縦書きPDF対応
多分文字選択時のキャレットの向きとかをよしなにやってくれる。最高
※APIを試してみたところ、displayModekPDFDisplaySinglePageContinuouskPDFDisplayTwoUpContinuous の際に縦スクロールにするか横スクロールにするかを指定できる機能でした。

// Specifies presentation of pages from right-to-left. Defaults to NO.
@property (nonatomic) BOOL displaysRTL PDFKIT_AVAILABLE(10_13, 11_0);

ページの右送り、左送りの変更もこれでやってくれる。最高
※APIを試してみたところ、displayModekPDFDisplayTwoUpkPDFDisplayTwoUpContinuous の時に右側に若番のページを表示してその次のページを左側に表示する機能でした。

@property (nonatomic) CGFloat minScaleFactor PDFKIT_AVAILABLE(10_13, 11_0);
@property (nonatomic) CGFloat maxScaleFactor PDFKIT_AVAILABLE(10_13, 11_0);
@property (nonatomic, readonly) CGFloat sizeToFitScaleFactor PDFKIT_AVAILABLE(10_13, 11_0);

拡大縮小のパラメータ設定等

@property (nonatomic) PDFEdgeInsets pageBreakMargins PDFKIT_AVAILABLE(10_13, 11_0);

マージン

- (void)usePageViewController:(BOOL)enable withViewOptions:(nullable NSDictionary*)viewOptions PDFKIT_AVAILABLE(NA, 11_0);
- (BOOL)isUsingPageViewController PDFKIT_AVAILABLE(NA, 11_0);

これは iOS のみ。
UIPageViewController を使ったレイアウトとナビゲーションにしてくれるらしい。

PDFThumbnailView
@property (nonatomic) UIEdgeInsets contentInset;

iOS のみとドキュメントには書いてあるけど、ヘッダでは特に指定なし。

PDFDocumentDelegate

PDFDocumentDelegate - PDFKit | Apple Developer Documentation

既存で用意されている各種通知の delegate 版が追加されている。

PDFDocument
@property (nonatomic, readonly) BOOL allowsPrinting;                            // Printing the document
@property (nonatomic, readonly) BOOL allowsCopying;                             // Extract content (text, images, etc.)
@property (nonatomic, readonly) BOOL allowsDocumentChanges;                     // Modify the document contents except for page management (document attrubutes)
@property (nonatomic, readonly) BOOL allowsDocumentAssembly;                    // Page management: insert, delete, and rotate pages
@property (nonatomic, readonly) BOOL allowsContentAccessibility;                // Extract content, but only for the purpose of accessibility
@property (nonatomic, readonly) BOOL allowsCommenting;                          // Create or modify annotations, including form field entries
@property (nonatomic, readonly) BOOL allowsFormFieldEntry;                      // Modify form field entries

既存APIではPDFがロックされているか、ロック解除後はプリントが許可されているか、コピーが許可されているかといった権限の取得が出来た。
新しいAPIでは上記のように、ドキュメントの変更許可、アクセシビリティ(スクリーンリーダー等)のみで利用可能か、フォームを編集出来るかなど細かく権限を取得できるようになっている。
ちなみに後述の Core Graphics ではもう少し細かく権限を取得できる。

PDFPage
- (PDFKitPlatformImage *)thumbnailOfSize:(PDFSize)size forBox:(PDFDisplayBox)box PDFKIT_AVAILABLE(10_13, 11_0);

サムネイル取得APIが追加された。PDFKitPlatformImage は iOS では UIImage macOS では NSImage が返される。
PDFSize は iOS では CGSize macOS では NSSize を渡す。

PDFSelection
- (void)extendSelectionForLineBoundaries PDFKIT_AVAILABLE(10_13, 11_0);

現在選択中のテキスト範囲をよしなに拡張してくれる。

PDFAnnotation

多いのでドキュメントを参照

PDFAnnotation - PDFKit | Apple Developer Documentation

PDFAppearanceCharacteristics

これもアノテーション周りのAPIらしい。必要になったら調査。

Core Graphics

Core Graphics のPDF周りのAPIも少し追加になっている。

CGPDFDocumentGetAccessPermissions
CG_EXTERN CGPDFAccessPermissions CGPDFDocumentGetAccessPermissions(CGPDFDocumentRef document)
    CG_AVAILABLE_STARTING(__MAC_10_13, __IPHONE_11_0);
typedef CF_OPTIONS(uint32_t, CGPDFAccessPermissions) {
    kCGPDFAllowsLowQualityPrinting    = (1 << 0),   // Print at up to 150 DPI
    kCGPDFAllowsHighQualityPrinting   = (1 << 1),   // Print at any DPI
    kCGPDFAllowsDocumentChanges       = (1 << 2),   // Modify the document contents except for page management
    kCGPDFAllowsDocumentAssembly      = (1 << 3),   // Page management: insert, delete, and rotate pages
    kCGPDFAllowsContentCopying        = (1 << 4),   // Extract content (text, images, etc.)
    kCGPDFAllowsContentAccessibility  = (1 << 5),   // Extract content, but only for the purpose of accessibility
    kCGPDFAllowsCommenting            = (1 << 6),   // Create or modify annotations, including form field entries
    kCGPDFAllowsFormFieldEntry        = (1 << 7)    // Modify form field entries
};

上記の様に PDFKit では取得出来ないプリント品質によるプリント許可ついての権限が取得出来る。

CGPDFDocumentGetOutline
CG_EXTERN __nullable CFDictionaryRef CGPDFDocumentGetOutline(CGPDFDocumentRef document)
    CG_AVAILABLE_STARTING(__MAC_10_13, __IPHONE_11_0);

アウトライン情報の取得が出来る。 アウトラインの辞書情報には以下の情報が入っている。

  • Title - CFString型のタイトル
  • Children - CFArray of CFDictionaries型の子アウトライン情報の配列
  • Destination - CFNumber型のページ番号、または CFURL型のファイルURL
  • DestinationRect - Destination がページ番号の時のみリンク先を示す CFDictionary 型に変換された CGRect の座標
CG_EXTERN void CGPDFContextSetOutline(CGContextRef context, __nullable CFDictionaryRef outline)
  CG_AVAILABLE_STARTING(__MAC_10_13, __IPHONE_11_0);

PDFデータを作成する際にこのアウトライン情報を書き込む事も出来るらしい。
この2つの Core Graphics API は iOS だけでなく macOS でも初出のAPIとなっている。

まとめ

PDFKit 最高、使いましょう。

iOS 11 Messages Framework の変更点

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
iOS 11 Core Image の変更点 - ObjecTips
Vision framework でテキスト検出 TextDetection - ObjecTips
Vision Framework で水平角検出(傾き) Horizon Detection - ObjecTips

Messages Framework

MSConversation
- (void)sendMessage:(MSMessage *)message completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler NS_AVAILABLE_IOS(11_0);
- (void)sendSticker:(MSSticker *)sticker completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler NS_AVAILABLE_IOS(11_0);
- (void)sendText:(NSString *)text completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler NS_AVAILABLE_IOS(11_0);
- (void)sendAttachment:(NSURL *)URL withAlternateFilename:(nullable NSString *)filename completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler NS_AVAILABLE_IOS(11_0);

これまではテキスト入力エリアにテキストや画像、ステッカーを入力、添付する事しか出来ず送信のアクションはユーザ自身が送信ボタンを押す必要があった。
送信の最終決定権はユーザにあるという事でポリシーとして納得していたけど iOS 11 では送信アクション自体をアプリから実行する事が出来るらしい。
アプリがバグってたらユーザの意図しない内容のメッセージが送られたりしてしまうけど本当にユーザのプレビューと送信アクション無しで大丈夫かな?
その心配より利便性を取ったのかも知れない。ユーザは便利になる一方開発者にはより責任が求められる。

MSMessagesAppPresentationStyle
MSMessagesAppPresentationStyleTranscript NS_ENUM_AVAILABLE_IOS(11_0),

MSMessagesAppViewController の表示状態を表すものとして Compact と Expand があったがそれに追加で Transcript という状態が加わったらしい。

MSMessagesAppCompactOrExpandedPresentation
MSMessagesAppTranscriptPresentation

この2つのプロトコルも追加された。

MSMessageLiveLayout
NS_CLASS_AVAILABLE_IOS(11_0)
@interface MSMessageLiveLayout : MSMessageLayout

ライブレイアウトが何かは分からないけど多分WWDCのセッションでデモが行われるだろう。
ライブレイアウト非対応のデバイスでは以下のレイアウトが使われるとの事。

@property (nonatomic, readonly) MSMessageTemplateLayout *alternateLayout;
MSMessageErrorCode
    MSMessageErrorCodeUnknown NS_ENUM_AVAILABLE_IOS(11_0) = -1,
    MSMessageErrorCodeSendWithoutRecentInteraction NS_ENUM_AVAILABLE_IOS(11_0),
    MSMessageErrorCodeSendWhileNotVisible NS_ENUM_AVAILABLE_IOS(11_0),

ドキュメントが無いので詳細が分からないけど

  • 1つめは不明なエラー
  • 2つ目はユーザが何も操作していない状態で送信を実行した時のエラー
  • 3つ目はアプリが表示されていない時に送信を実行した時のエラー。

おそらく後ろ2つについてはプログラムからメッセージの送信を実行出来る様になったとは言え、これらのケースの様なユーザが認識しないタイミングで勝手にメッセージを送信させないというポリシーに基づいてエラーが発生する様になっているんだと思う。

まとめ

アプリからメッセージ送信を実行出来る、でも変なタイミングで送ってもエラーが起きる。
ライブレイアウトはセッションの情報待ち。

Vision Framework でテキスト検出 Text Detection

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
iOS 11 Core Image の変更点 - ObjecTips
koze.hatenablog.jp

Vision Framework

iOS 11で Computer Vision の Framework が追加された。

Vision | Apple Developer Documentation

使えるのは以下

  • Face Detection and Recognition - 顔とパーツの認識
  • Machine Learning Image Analysis - 機械学習による画像分析
  • Barcode Detection - バーコードの検出
  • Image Alignment Analysis - 画像の並び解析
  • Text Detection - テキストの検出
  • Horizon Detection - 水平角の検出(傾き)
  • Object Detection and Tracking - 物体検出と追跡

特に機械学習による画像分析は MLModel -> VNCoreMLModel -> VNCoreMLRequest の流れで任意のモデルから分析を行える様になっているので、機能を限定されず自由に機械学習による画像分析が出来る模様。
これめちゃくちゃパワフルだと思う。

それはまた後で勉強するとして、まずは手始めに以前 Core Image でもやったテキスト検出を Vision framework で試してみる。
現時点でプログラミングガイドが見あたらないけど大体のクラスとメソッド名で理解できる様なシンプルな構成になっている。

実装

Text Detection with Vision framework

まず VNImageRequestHandler を作成。
NSURL の他に CVPixelBufferRef CGImageRef CIImage NSData を引数にするイニシャライザがあってどんな画像ソースも扱える様になっている。
UIImage なら一旦 CIImage を一旦経由すればいい。

次に VNDetectTextRectanglesRequest を作成して CompletionHandler を設定。
init してから設定するなら以下のメソッドでハンドラを設定

@property (readonly, nonatomic, copy, nullable) VNRequestCompletionHandler completionHandler;

init と同時に設定するなら以下のメソッドが使える。

- (instancetype) initWithCompletionHandler:(nullable VNRequestCompletionHandler)completionHandler NS_DESIGNATED_INITIALIZER;

次に reportCharacterBoxesYES に設定。
設定しないと文字領域のかたまりのみを検出する。YES だと1文字ずつの矩形を検出する。

最後に performRequests: でリクエストを実行する 。

実行結果

このコードを以下の画像に対して実行する。

f:id:Koze:20170607111252p:plain

結果は以下

{{0.19375000000000001, 0.546875}, {0.49687500000000007, 0.140625}}
 |-{{0.19375000000000001, 0.546875}, {0.09375, 0.10625}}
 |-{{0.29999999999999999, 0.546875}, {0.087499999999999994, 0.140625}}
 |-{{0.39687499999999998, 0.546875}, {0.087499999999999994, 0.10625}}
 |-{{0.49062499999999998, 0.546875}, {0.090624999999999997, 0.140625}}
 |-{{0.59999999999999998, 0.546875}, {0.090624999999999997, 0.10625}}
{{0.19062499999999999, 0.32187500000000002}, {0.63125000000000009, 0.14374999999999993}}
 |-{{0.19062499999999999, 0.32500000000000001}, {0.11874999999999999, 0.13750000000000001}}
 |-{{0.32500000000000001, 0.32500000000000001}, {0.10625, 0.13750000000000001}}
 |-{{0.44374999999999998, 0.32187499999999997}, {0.121875, 0.14374999999999999}}
 |-{{0.58750000000000002, 0.32500000000000001}, {0.1125, 0.13750000000000001}}
 |-{{0.72187500000000004, 0.32500000000000001}, {0.10000000000000001, 0.13750000000000001}}

検出できてるっぽい。
ちなみに NSStringFromCGRect を使わなくても直接オブジェクトをログ出力する事も出来る。こっちの方が分かりやすいかも。
VNTextObservation の uuid と精度(以下では全て1)も一緒に出力される。

2017-06-07 11:30:09.026853+0900 Test[10183:7799799] <VNTextObservation: 0x600000299f50> D1AA25AD-7749-40F5-A893-C6FCB75AEE94 1 [0.19375 0.546875 0.496875 0.140625]
2017-06-07 11:30:09.027137+0900 Test[10183:7799799] (
    "<_VNTextObservationCharacterBox: 0x6000008f8880> 91A22E36-C52E-436F-B4B0-DAE1B0CFAD8E 1 [0.19375 0.546875 0.09375 0.10625]",
    "<_VNTextObservationCharacterBox: 0x6000008f8980> 20338509-1DA5-408F-BBC2-993AF9EB561B 1 [0.3 0.546875 0.0875 0.140625]",
    "<_VNTextObservationCharacterBox: 0x6000008f8a80> EDD81518-D291-4DEC-932B-72F8FCC3C489 1 [0.396875 0.546875 0.0875 0.10625]",
    "<_VNTextObservationCharacterBox: 0x6000008f8b80> 86DA1678-ABC8-4833-B8A7-D2697A85D89F 1 [0.490625 0.546875 0.090625 0.140625]",
    "<_VNTextObservationCharacterBox: 0x6000008fcf00> 85447B91-87EA-4275-B837-5160FE01C2B3 1 [0.6 0.546875 0.090625 0.10625]"
)
2017-06-07 11:30:09.027244+0900 Test[10183:7799799] <VNTextObservation: 0x60000029ad60> F2E588DD-9AFE-4E2E-ADE6-10230EB22919 1 [0.190625 0.321875 0.63125 0.14375]
2017-06-07 11:30:09.027360+0900 Test[10183:7799799] (
    "<_VNTextObservationCharacterBox: 0x6000008fd480> C336A62D-F607-47F4-B0EB-E98D03E3A9F7 1 [0.190625 0.325 0.11875 0.1375]",
    "<_VNTextObservationCharacterBox: 0x6000008fd580> 3917DF3A-13CF-4E63-B66F-4408EAEB5B0E 1 [0.325 0.325 0.10625 0.1375]",
    "<_VNTextObservationCharacterBox: 0x6000008fd680> 06B97257-B8A5-417A-A0F6-64778AADE759 1 [0.44375 0.321875 0.121875 0.14375]",
    "<_VNTextObservationCharacterBox: 0x6000008f2900> 85A90B06-0F4C-4203-8427-59877D7DCAA2 1 [0.5875 0.325 0.1125 0.1375]",
    "<_VNTextObservationCharacterBox: 0x6000008f2800> 91530E7D-2891-4847-9031-603519071065 1 [0.721875 0.325 0.1 0.1375]"
)
結果のオーバーレイ

検出結果は0-1の範囲の様なのでこれを元にオーバーレイ画像を作成してみる。

Overlay Image from VNTextObservation

Core Image の時と同様、上下逆の座標で結果が返ってくるので途中に CGAffineTransform を使って座標を変換している。
結果は以下

f:id:Koze:20170607115927p:plain

バッチリ

日本語に対しても実行してみる。

f:id:Koze:20170607121311p:plain

平仮名がパーツで分割して認識されていたり漢字が認識されていなかったりするけど、以前は平仮名の行自体が検出されていなかったので検出精度は上がっている。


Core Image バージョンは以下
やってる事はそんなに変わらない。検出結果の座標系が違うのでオーバーレイ画像の作成処理は変わってくる。

koze.hatenablog.jp

ちなみに Core Image バージョンでも iOS 11 であれば Vision と同じで平仮名の検出が出来る様になっていた。
おそらく中身の処理は同じで、結果の違いはOSのバージョンの差異によるものだと思う。 *1

Core Image と Vision では文字の矩形のみの検出だけど、その先の文字内容の認識(OCR)を行う部分は iOS 11 で追加された機械学習の Core ML framework や Vision framework の VNCoreMLRequest で独自のモデルを使って各自どうぞって事なんだと思う。

*1:追記:Session 506 - Vision Framework: Building on Core ML によれば、顔認識については Vision では機械学習を使っていて Core Image の顔認識と比べて精度、処理速度、バッテリー処理の面で Vision の方が優位との事

iOS 11 Foundation の変更点

Foundation | Apple Developer Documentation

Foundation 周りざっくり、網羅はしていない。

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
iOS 11 Core Image の変更点 - ObjecTips
Vision framework でテキスト検出 TextDetection - ObjecTips
Vision Framework で水平角検出(傾き) Horizon Detection - ObjecTips

NSLinguisticTagger

言語、品詞の判定周りのメソッドが色々と増えたっぽい。
Core MLのドキュメントによると NSLinguisticTagger クラスには Machine Learning が生かされているらしい。

NSFileManager
- (BOOL)trashItemAtURL:(NSURL *)url resultingItemURL:(NSURL * _Nullable * _Nullable)outResultingURL error:(NSError **)error API_AVAILABLE(macos(10.8), ios(11.0)) API_UNAVAILABLE(watchos, tvos);

macOS では昔からあったメソッド。iOS にもシステム共通のゴミ箱が登場?

    NSTrashDirectory API_AVAILABLE(macos(10.8), ios(11.0)) API_UNAVAILABLE(watchos, tvos) = 102             // location of Trash directory

証拠発見か?
NSSearchPathDirectoryNSTrashDirectory がiOSでも利用可能に

NSJSONSerialization
    NSJSONWritingSortedKeys API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)) = (1UL << 1)

JSONの保存オプションでキーのソートが可能に

NSProcessInfo
typedef NS_ENUM(NSInteger, NSProcessInfoThermalState) {
    NSProcessInfoThermalStateNominal,
    NSProcessInfoThermalStateFair,
    NSProcessInfoThermalStateSerious,
    NSProcessInfoThermalStateCritical
} API_AVAILABLE(macosx(10.10.3), ios(11.0), watchos(4.0), tvos(11.0));

デバイスのパワー使い過ぎによる熱状態を取得出来る。

FOUNDATION_EXTERN NSNotificationName const NSProcessInfoThermalStateDidChangeNotification API_AVAILABLE(macosx(10.10.3), ios(11.0), watchos(4.0), tvos(11.0));

変更通知も受け取れる。

まとめ

他にも変更のあるクラスはいくつかあったけど、個人的におおっと思う大きな変化は無かったかも。
テキストからの言語判定はこれまでいまいち精度が出なかったので Machine Learning が生かされてる NSLinguisticTagger の言語判定は是非とも試してみたい。