FC2ブログ

頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


FFmpegのライブラリをmacOSアプリで使う

macOSアプリからFFmpegを使い複数の動画ファイルをクロップする
↑ここでmacOSからFFmpegを使うというのをやってみたが、どうもmacOSやiOSアプリにFFmpegのライブラリを持ってきて使う、という方法もあるらしい。
組み込むってことはFFmpegのライブラリを使う動画編集アプリなんかが作れるかも?と思いmacOSアプリで試してみた。

FFmpegのバージョンが3.2.2で古かったのでまずは最新の4.1へアップデートしておいた。

macでffmpegをビルドして、アプリ内でffmpegを使う(static libraryとして) - Qiita
↑こちらのページを参考にXcodeからライブラリを参照できるようにパス設定などを行なった。

FFmpegチュートリアル1: キャプチャする - Qiita
macOSアプリ(Objective-C)のプロジェクトで↑こちらのページを参考にし(現時点でdeprecatedがいっぱい)、次のようなコードを書いた。
#import "libavformat/avformat.h"

{
    AVFormatContext *formatContext = avformat_alloc_context();
    
    NSString *filePath = [NSString stringWithFormat:@"%@/sampleVideo.mov",DesktopFolder];
    const char *url = [filePath cStringUsingEncoding:NSASCIIStringEncoding];

    if (avformat_open_input(&formatContext, url, nil, nil) != 0) {
        NSLog(@“Failed to open file");
        return;
    }

    AVDictionary *avdic;
    if(avformat_find_stream_info(formatContext , &avdic) < 0) {
        NSLog(@“Not found info");
        return; 
    }

    av_dump_format(formatContext, 0, url, 0);
}
↑デスクトップに置いてるファイルの情報を表示するってだけなんだけど、、、

動画ファイル情報の出力結果

↑なにやら表示された(一部を表示)。一応動いているっぽい。

さらに参考ページを見つつ書き進めていたのだが、、、いや、ちょっと待て。多機能なFFmpegだから細かいことまでできるんだろうけど、それらのAPIを使いこなすのは大変だ。勉強するつもりならAVFoundationでもええやん。
FFmpegをiOSなりmacOSに組み込んだアプリをリリースする場合のライセンスもややこしそうだし。

まあそんなわけで、これ以上はアプリにFFmpegのライブラリを組み込むという考えはやめておくことにしよう。
数年後にまたライブラリで、、、と考えるかもしれないが、その時はこれを見てやめておくように、数年後の俺!


macOSのNSScrollView上のマウスカーソルの位置を得る

macOSのSpriteKitでマウスカーソルの位置を得る
↑ここではSpriteKitを使ったviewでのマウスカーソル位置を得たが、今回はNSScrollViewの場合。
もちろん、画面をスクロールさせたらそれ相応の位置を取得したい。

NSScrollView関連のファイル構成図

↑NSScrollViewを使う場合、なにやらいろんなクラスが付随しててどこに処理を記述すればいいんだ?となりそうだ。

macOSでのスクロールビュー NSScrollView の最小実装
↑ここでScroll Viewの左上を原点にするためにNSClipViewの一階層下のviewのためにわざわざクラスのファイルを作成しないとだめなことを面倒だな、と思ったのだが、、、
今回のScroll Viewでのマウスカーソルの座標を得る処理をこのクラス(ContentView)に記述できる。

以下はContentViewクラスの実装ファイルの一部。
- (void)drawRect:(NSRect)dirtyRect 
{
    [super drawRect:dirtyRect];
    
    NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingMouseMoved);
    NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
                                                        options:options
                                                          owner:self
                                                       userInfo:nil];
    [self addTrackingArea:trackingArea];
}

-(BOOL)isFlipped
{
    //左上原点にするための記述
    return YES;
}

- (void)mouseMoved:(NSEvent *)event
{
    NSPoint p = event.locationInWindow;
    p = [self convertPoint:p fromView:nil];
    //その後の処理など
}
↑drawRect: メソッドにお決まりの(?)処理を記述し、mouseMoved: で受け取る。
座標を変換し左上原点の位置を得る。
画面をスクロールしても意図する座標の取得ができた。


macOSのSpriteKitでマウスカーソルの位置を得る

macOSアプリでSpriteKitを使う場合にマウスのカーソル(ポインター)の位置を得る方法について。

macOSでSpriteKitを使う場合のファイル構成

↑Xcodeでプロジェクト作成時にSpriteKitを使うと指定するとこのようなファイル構成になる。

NSViewオブジェクトに対してマウスカーソルの位置を得る場合には
- (void)addTrackingArea:(NSTrackingArea *)trackingArea;
↑これで設定すればよかったが、SpriteKitを使うプロジェクトの場合、NSViewのサブクラスのSKViewを継承するファイルはデフォではない。

デフォで作成されているGameSceneというファイルがあり、このクラスはSKSceneを継承している。そのコードの最初の方に、
- (void)didMoveToView:(SKView *)view
{
    // Setup your scene here

}
↑このdidMoveToView:メソッドがあり、ここで初期設定ができる。
ここでSKViewオブジェクトのviewをもらうことになるのでこのviewに対してトラッキングの設定をする。

- (void)didMoveToView:(SKView *)view
{
    NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingMouseMoved);
    NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds]
                                                        options:options
                                                          owner:self
                                                       userInfo:nil];
    [view addTrackingArea:trackingArea];
}

- (void)mouseMoved:(NSEvent *)event
{
    CGPoint p = [event locationInNode:self];
    p =  [self convertPointToView:p];
}
↑得た座標はSKSceneの
- (CGPoint)convertPointToView:(CGPoint)point;
メソッドで座標変換すればウインドウでの座標を得ることができる(左下原点)。

マウスカーソル位置表示の実行結果

↑得た値を表示させているところ(マウスカーソルは撮影されていないが)。


ただし、この方法だとうまく取得できないエリアが出る場合がある。
- (void)didMoveToView:(SKView *)view;
でもらうSKViewオブジェクトはまだViewControllerの
-(void)viewDidLayout;
メソッドより前なので、実際に表示されるウインドウのサイズになっていない(storyboardで指定した値になっている)。上記のコードではこの時点でのviewのプロパティを使ってトラッキングエリアを設定しているので合わなくなる。
よって、
-(void)viewDidLayout;
↑これ以降で設定するなどの対応が必要。


参考サイト
cocoa - Using acceptsMouseMovedEvents for SpriteKit mouse actions with Storyboards and Swift - Stack Overflow


macOS Mojave 10.14


関連記事
macOSのNSScrollView上のマウスカーソルの位置を得る


NSImageからCGImageを得る

macOSアプリでNSImageオブジェクトからCGImageRefを得る方法。
いくつかある方法を試してみた。
{
    CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)[nsimage TIFFRepresentation], NULL);
    CGImageRef cgimage =  CGImageSourceCreateImageAtIndex(source, 0, NULL);

    //解放
    CGImageRelease(cgimage);
    CFRelease(source);
}
↑この方法だとメモリの解放が必要なのでやや使いにくい。

+(CGImageRef)cgimageFromNSImage:(NSImage *)nsimage;
↑こういう形のメソッドにしたくてもできないし(いい方法あれば教えてください)。


+(CGImageRef)cgimageFromNSImage:(NSImage *)nsimage
{
    CGImageRef cgimage = [nsimage CGImageForProposedRect:NULL context:NULL hints:nil];
    return cgimage;
}
↑この方法だとメモリの解放が不要なのでメソッドの形にしておける。
なお、ここで使っているメソッドの引数である NSRect *proposedDestRect の部分だけど、NSRectを変えたらその位置とサイズを指定できるのかと思ったけどそういうものでもないらしい。NULLを与えるとNSImageオブジェクト全体を示すとリファレンスにはあるのだが。

{
    NSImage *nsimage = [[NSImage alloc] initWithCGImage:cgimage size:size];
}
↑なお逆にCGImageRefからNSImageオブジェクトを作成するにはこれでOK。


参考サイト
objective c - Turning an NSImage* into a CGImageRef? - Stack Overflow


CreateMLで作成したmlmodelを使いCoreMLで予測する

Create MLで学習させmlmodelファイルを得る
↑ここでmlmodelファイルを出力することができたので、今回はこれを使って予測を行う。
なお、言語はObjective-C。macOSアプリ、予測に使う画像はNSImageオブジェクトとする。

出力したmlmodelファイルをXcodeのプロジェクトにドラッグ&ドロップしておく。
@import AppKit;
@import CoreML;
@import Vision;

{
    NSError *error = nil;
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"ImageClassifier" ofType:@"mlmodelc”];//—(1)
    NSURL *url = [NSURL fileURLWithPath:filePath];
    MLModel *mlmodel= [MLModel modelWithContentsOfURL:url error:&error];//—(2)
    VNCoreMLModel *vnmodel = [VNCoreMLModel modelForMLModel:mlmodel error:&error];

    NSImage *nsimage = [NSImage imageNamed:@“xxxx.png"];
    CGImageRef cgimage =  [nsimage CGImageForProposedRect:NULL context:NULL hints:nil];//—(3)

    NSDictionary *dic = [NSDictionary dictionary];
    VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithCGImage:cgimage options:dic] ;
    
    VNCoreMLRequest *mlrequest = [[VNCoreMLRequest alloc]
                               initWithModel:vnmodel
                                completionHandler:(VNRequestCompletionHandler) ^(VNRequest *request, NSError *error){
                                    if (!error) {
                                        for (VNClassificationObservation *obs in request.results) {
                                            NSLog(@"%@ %.3f",   obs.identifier , obs.confidence);
                                        }
                                    }
                                }
                                ];
    
    [handler performRequests:@[mlrequest] error:&error];
}
↑エラーメッセージの表示等は省いている最小限の実装。
(2)のようにMLModelオブジェクトが必要だが、Objective-Cの場合少し手間がかかる。(1)でファイル名を指定するが、拡張子は”mlmodelc”だ。ドラッグ&ドロップしたファイルは”mlmodel”なのに!最後のcは誤字ではない。
今回は予測する画像をNSImageオブジェクトとして読み込んで、(3)の方法でCGImageを得ている。


入力画像

↑この画像を与えて予測させてみる。

予測結果

↑その結果。かろうじて正解。
そもそも少ないデータ数から作成したモデルなので、精度は微妙で他の画像を与えると間違いもあるのだが、とりあえずデータセットからCreateMLで学習させ、その結果を使いCoreMLとVisionで予測させることはできた。


参考サイト
CoreML を使って画像認識、Objective-C で書くと・・・ - Satoshi777jp’s diary
xcode - How to use Machine Learning model in objective-C with CoreML - Stack Overflow
[iOS 11] Core MLで焼き鳥を機械学習させてみた | DevelopersIO


macOS Mojave 10.14




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad