1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Unity+ARKit3のbody segmentation結果を配列として取得する

Last updated at Posted at 2019-09-15

#既知の問題(2019/9/16更新)
以下の方法は、セグメンテーションのテクスチャに限り、端末の向きを変更するとそれ以降値が変化しなくなる問題が起きています。(デプスのテクスチャは問題なし。Orientation設定は関係なし)
そのため、あまり上手い方法ではないかもしれませんので、そのつもりで利用してください。

#やりたいこと
ARKit3を利用することで人物領域をセグメンテーションすることができる。
しかし、結果がTexture2Dとして取得されるので、そのままでは結果を加工しにくい。
なので、byteなどの配列として結果を取得したい。

#ハマったポイント
Texure2Dなので、GetPixels()などを使えば一瞬だと思ったが、関数がうまく機能しないらしい。

#原因(推定)
UnityのARKit3プラグインを確認していくと、このTexture2Dはネイティブで作られている模様。(CreateExternalTexture/UpdateExternalTextureでの作成・更新処理を発見)
Texure2Dはネイティブで作られたものをラップしているだけなので、GetPixels()などの関数が正しく機能しなかったと思われる。
参考(テラシュールブログさん):
http://tsubakit1.hateblo.jp/entry/20140104/1388830289
http://tsubakit1.hateblo.jp/category/トラブルシューティング?page=1469026740#RenderTextureをTexture2Dに流し込む

#解決策
凹みTipsさんの
Low-Level Native Plugin Interface を利用してネイティブから Unity のテクスチャを高速に更新する方法を調べてみた - 凹みTips
によると、iOSにおいてはid<MTLTexture>がテクスチャの実体らしいので、以下のようなプラグインを書いて、配列として取得することで解決。
idから配列としてデータを取得する関数は以下。
https://developer.apple.com/documentation/metal/mtltexture/1516318-getbytes

/Assets/Plugins/iOS/TextureExtention.mm
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>

extern "C" {
    void GetNativePixels(id<MTLTexture> texture, int width, int height, int sizePerRow, char *buffer) ;
}

void GetNativePixels(id<MTLTexture> texture, int width, int height, int sizePerRow, char *buffer) {
    [texture getBytes:buffer bytesPerRow:sizePerRow fromRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0];
}
/Assets/Plugins/TextureExtention.cs
using System;
using UnityEngine;
using System.Runtime.InteropServices;

public class TextureExtention { 
	[DllImport("__Internal")]
	public static extern void GetNativePixels(IntPtr texturePtr, int width, int height, int sizePerRow, [Out] byte[] buffer);

	public static void GetNativePixels(Texture2D humanTexture, ref byte[] buffer) {
        // humanTexture は ARKit3で取得されたセグメンテーション結果が格納されたテクスチャ
        IntPtr humanTexturePtr = humanTexture.GetNativeTexturePtr();
        int width = humanTexture.width;
        int height = humanTexture.height;
        int sizeParRow = humanTexture.width * 1; // ボディセグメンテーションではテクスチャフォーマットはR8なので 1ピクセル=1Byte
        GetNativePixels(humanTexturePtr, width, height, sizeParRow, buffer);
	}
}

#補足
今回ネイティブプラグインを作って対処しましたが、Unity内でRenderTexture->Texuture2Dへの変換のようにテクスチャを本当のTexutre2Dコピーして、GetPixels()を使う方法でもできるかもしれません。
また、セグメンテーション結果ではなくデプス推定結果でも同じようにすれば大丈夫です。その際はテクスチャフォーマットがRFloatなので、その調整をする必要があります。

#動作環境
Unity 2019.2.5f
Xcode 11 GM Seed

#感想
GetPixels()が使えないという判断までも時間がかかったし、初めてのiOSネイティブプラグイン作成だったので色々苦労してしまいました…。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?