Closed16

VoTTのファイルを引っ越す方法を考える

nabeyangnabeyang

VoTTでアノーテーション付けたファイルは絶対パスを使っているので、ディレクトリを移動させると動かなくなるっぽい。せっかく修正が取り込まれているみたいだけど、リリースが無い。よく見ると本家のコミットが去年の11月から動いていないので、データの方を修正する方法を調べてみる。

ここの話はVoTTがv2.2.0であることを想定しています。

↓それっぽい修正
https://github.com/microsoft/VoTT/pull/1027

nabeyangnabeyang

ターゲット接続で指定したディレクトリに出来るvottの拡張子を持ったファイル。上書き保存しても、プロジェクトを開くと元に戻る。

nabeyangnabeyang

こんな感じでやれば、一応、引っ越せる。

  1. 引っ越し先をターゲット、ソースにしてプロジェクトをvottを使って作る
  2. 元の場所にいた<id>-asset.jsonを1. で作ったidに合わせてターゲットディレクトリにコピーする
nabeyangnabeyang

各画像に対するidは、以下のようにmd5で生成している。

import MD5 from "md5.js"
console.log(new MD5().update("file:/path/to/img.png").digest("hex"));//=> 66f4274a78d046eea54cee35b91e934d

xxxx-asset.jsonxxxxの部分とそのファイルのidキーの値にする。パスを変えたとき、変える必要があるのはこの部分と.vottファイルのassetsくらい。

nabeyangnabeyang

.vottファイルで暗号化されている情報があって、セキュリティトークンで復号化できる。crypto-jsで暗号化されているので、復号化はこれを使えば、何も考えなくても出来る。

↓参照
https://github.com/microsoft/VoTT/blob/v2.2.0/src/common/crypto.ts

activeLearningSettingsは暗号化されていないのに、exportFormatが暗号化されているのが不便。sourceConnectiontargetConnectionでパスが暗号化されているのは分かるけども。

ivが24byteなのでgoのcrypto/aesで復号化しようとしてもブロックサイズが合わなくて出来てない(ブロックサイズは16で固定)。 (後から分かりましたが、VoTTで使われているivは24byteだけれども暗号化、復号化で使われているのは先頭の16byteまでなのでGoの標準ライブラリで対応可能)

import c from "crypto-js"
const AES = c.AES
const enc = c.enc
const lib = c.lib
function decrypt(encodedMessage, secret) {
    const secretBytes = enc.Base64.parse(secret);
    const json = enc.Base64.parse(encodedMessage).toString(enc.Utf8);
    const params = JSON.parse(json);
    const iv = enc.Hex.parse(params.iv);
    const cipherParams = lib.CipherParams.create({
        ciphertext: enc.Hex.parse(params.ciphertext),
        iv: enc.Hex.parse(params.iv),
    });
    const decrypted = AES.decrypt(cipherParams, secretBytes, { iv });
    return decrypted.toString(enc.Utf8);
}
const secret = "secret_key";

//"sourceConnection"."providerOptions"."encrypted"
const encodedMessage1 = "xxx";
console.log(decrypt(encodedMessage1, secret)) //=> {"folderPath":"/path/to/src"}
//"targetConnection"."providerOptions"."encrypted"
const encodedMessage2 = "yyy";
console.log(decrypt(encodedMessage2, secret))// => {"folderPath":"/path/to/dist"}
//"exportFormat"."providerOptions"
const encodedMessage3 = "zzz";
console.log(decrypt(encodedMessage3, secret))//=> {"assetState":"visited","includeImages":true}
nabeyangnabeyang

セキュリティトークンの確認の仕方

  1. ホームで歯車をクリック
  2. セキュリティトークン一覧が出るのでコピー(キーは"<プロジェクト名> Token"になる)
nabeyangnabeyang

goで各jsonを読み込むのに使った構造体を公開しておきます(分かりきった内容なのに書くと手が疲れるので)。
こちらは.vottファイル用

type fileConnection struct {
	Name            string          `json:"name"`
	ProviderType    string          `json:"providerType"`
	ProviderOptions providerOptions `json:"providerOptions"`
	ID              string          `json:"id"`
}
type asset struct {
	Format string `json:"format"`
	ID     string `json:"id"`
	Name   string `json:"name"`
	Path   string `json:"path"`
	Size   struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	} `json:"size"`
	State int `json:"state"`
	Type  int `json:"type"`
}

type vottFile struct {
	Name             string         `json:"name"`
	SecurityToken    string         `json:"securityToken"`
	SourceConnection fileConnection `json:"sourceConnection"`
	TargetConnection fileConnection `json:"targetConnection"`
	VideoSettings    struct {
		FrameExtractionRate int `json:"frameExtractionRate"`
	} `json:"videoSettings"`
	Tags []struct {
		Name  string `json:"name"`
		Color string `json:"color"`
	} `json:"tags"`
	ID                     string `json:"id"`
	ActiveLearningSettings struct {
		AutoDetect    bool   `json:"autoDetect"`
		PredictTag    bool   `json:"predictTag"`
		ModelPathType string `json:"modelPathType"`
	} `json:"activeLearningSettings"`
	ExportFormat struct {
		ProviderType    string          `json:"providerType"`
		ProviderOptions providerOptions `json:"providerOptions"`
	} `json:"exportFormat"`
	Version            string           `json:"version"`
	LastVisitedAssetId string           `json:"lastVisitedAssetId"`
	Assets             map[string]asset `json:"assets"`
}

こちらは*-asset.json

type assetFile struct {
	Asset   asset `json:"asset"`
	Regions []struct {
		ID          string   `json:"id"`
		Type        string   `json:"type"`
		Tags        []string `json:"tags"`
		BoundingBox struct {
			Height float64 `json:"height"`
			Width  float64 `json:"width"`
			Left   float64 `json:"left"`
			Top    float64 `json:"top"`
		} `json:"boundingBox"`
		Points []struct {
			X float64 `json:"x"`
			Y float64 `json:"y"`
		} `json:"points"`
	} `json:"regions"`
	Version string `json:"version"`
}
nabeyangnabeyang

セキュリティトークンを次のコードで生成して使えるか試してみた。一応、使えるみたい。

func generateRandomKey(length int) (string, error) {
	k := make([]byte, length)
	if _, err := io.ReadFull(rand.Reader, k); err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(k), nil
}
nabeyangnabeyang

セキュリティトークンの数を減らしたくて、途中で設定変えて大丈夫か確認。
とりあえずOKみたい。

nabeyangnabeyang

vottファイル読み込んだときに、未登録の接続があれば自動で新規登録されるのか。

nabeyangnabeyang

次のように実行するとtargetオプションで指定した場所に修正したものが出来るようになった。

go run . -target /path/to/project -src /path/to/file.vott -src-key base64_encoded_security_token

あとはアプリにセキュリティトークンのキーとバリューを設定して、ローカルプロジェクトを開くから生成したvottファイルを選択するとロードされる。

今の所imagesに画像をtargetにvottファイルと*-asset.jsonをなどを置くという決め打ちになっている。

project
├── images
└── target
nabeyangnabeyang

セキュリティトークンとコマンドラインで渡すとhistoryに残るので止めよう。パスワードモードで入力受け付けるのと、ファイルから読み込む方法の両方をサポートすることにした。

nabeyangnabeyang

改めてコマンドを考え直してみたい。だいたいこんな感じのことできたら嬉しいかな

  1. go fixみたいに壊れたファイルを上書きして直す
  2. 壊れたファイルを参照しながら、別に直したのを生成する
  3. ターゲット接続または画像ソース接続だけ移動させる
  4. 指定した場所に含む全てのプロジェクトを直す(その都度セキュリティトークン入力求める?)
nabeyangnabeyang

上書きするスタイルに変更して、公開。
https://github.com/nnabeyang/vott-fix/releases/tag/0.0.1
使い方はtargetオプションでvottファイルがあるディレクトリを指定する(オプション指定じゃなくて良いなぁ)。

vott-fix -target /path/to/target

アクセストークンが聞かれるのでコピペで変換完了。アクセストークンはアプリに登録されてないと、開くときエラーになる。変換後、ローカルプロジェクトを開くで.vottファイルを選択。

このスクラップは2021/04/17にクローズされました