質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

3647閲覧

Unity:Texture2D.GetPixels()で正しい色が取得できない

nitoninjin

総合スコア2

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2022/02/27 12:45

初めて質問させていただきます。

1. 環境

Windows 10
Unity 2020.3.29f1

2. 実現したいこと

Texture2Dを任意のサイズでトリミングする関数を作成したいです。
具体的には次のような関数です。

C#

1Texture2D Trim(Texture2D originalTexture, int x, int y, int width, int height) 2{ 3 Texture2D trimmedTexture = new Texture2D(width, height); 4 Color[] pixels = originalTexture.GetPixels(x, y, width, height); 5 trimmedTexture.SetPixels(pixels); 6 trimmedTexture.Apply(); 7 8 return trimmedTexture; 9}

originalTextureはスクリプト中で動的に生成したものを利用したいです。
(具体的には、uWindowCaptureを利用して得られた、ウィンドウのキャプチャ画像(UwcWindow.texture)です。)

3. 発生している問題

上の関数を実行すると、出力されたTextureの全てのピクセルの色が灰色でした。
次のように作成したTextureと同様の見た目です。

C#

1Texture2D texture = new Texture2D(width, height);

ただし、エラーは出ていません。

正しい色が出力されるようにしたいです。

4. 試したこと

Read/Write Enabledの確認

関数内でoriginalTexture.isReadableを確認したところ、trueでした。

C#

1Texture2D Trim(Texture2D originalTexture, int x, int y, int width, int height) 2{ 3 Debug.Log(originalTexture.isReadable); 4 // true 5 6 // 略 7}
GetPixels()によって取得したColor[]の確認

関数内でpixelsの一部をColor32にキャストして確認したところ、全てRGBA(205, 205, 205, 205)でした。

C#

1Texture2D Trim(Texture2D originalTexture, int x, int y, int width, int height) 2{ 3 for (int i = 0; i < 10; i++) 4 { 5 Debug.Log(((Color32)pixels[i]).ToString()); 6 // RGBA(205, 205, 205, 205) 7 } 8 9 // 略 10}
GetPixel()によるColorの取得

1ピクセルの色を取得するTexture2D.GetPixel()を試したところ、同様にRGBA(205, 205, 205, 205)が取得されました。

originalTextureの確認

関数内でRawImageのtextureにoriginalTextureをセットしたところ、正しい画像が表示されました。

C#

1[SerializeField] RawImage rawImage; // インスペクターで設定 2 3Texture2D Trim(Texture2D originalTexture, int x, int y, int width, int height) 4{ 5 rawImage.texture = originalTexture; 6 // GameビューのRawImageに正しい画像(灰色ではない)が表示される 7 8 // 略 9}
Graphics.CopyTexture()を利用した実装

Graphics.CopyTexture()を利用すると、実現したい関数を次のように実装することができると思います。

C#

1Texture2D Trim(Texture2D originalTexture, int x, int y, int width, int height) 2{ 3 Texture2D trimmedTexture = new Texture2D(width, height); 4 Graphics.CopyTexture(originalTexture, 0, 0, x, y, width, height, trimmedTexture, 0, 0, 0, 0); 5 trimmedTexture.Apply(); 6 7 return trimmedTexture; 8}

この場合も、同様の問題が発生しました。

5. 一応の解決方法

こちらの記事に次のような関数が掲載されています。
本来はスクリプトからRead/Write Enabledをtrueにすることを目的とした関数です。

C#

1Texture2D createReadabeTexture2D(Texture2D texture2d) 2{ 3 RenderTexture renderTexture = RenderTexture.GetTemporary( 4 texture2d.width, 5 texture2d.height, 6 0, 7 RenderTextureFormat.Default, 8 RenderTextureReadWrite.Linear); 9 10 Graphics.Blit(texture2d, renderTexture); 11 RenderTexture previous = RenderTexture.active; 12 RenderTexture.active = renderTexture; 13 Texture2D readableTextur2D = new Texture2D(texture2d.width, texture2d.height); 14 readableTextur2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); 15 readableTextur2D.Apply(); 16 RenderTexture.active = previous; 17 RenderTexture.ReleaseTemporary(renderTexture); 18 return readableTextur2D; 19}

この関数を用いて、実現したい関数を次のように書き換えたところ、(理由はわかりませんが)解決しました。

C#

1Texture2D Trim(Texture2D originalTexture, int x, int y, int width, int height) 2{ 3 Texture2D newOriginalTexture = createReadabeTexture2D(originalTexture); // 追加 4 5 Texture2D trimmedTexture = new Texture2D(width, height); 6 Color[] pixels = newOriginalTexture.GetPixels(x, y, width, height); // 変更 7 trimmedTexture.SetPixels(pixels); 8 trimmedTexture.Apply(); 9 10 return trimmedTexture; 11}

また、同様にGraphics.CopyTexture()を利用して実装した場合も、問題ありませんでした。

しかし、この関数を用いた処理は重いため、可能ならば別の方法で解決したいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

質問文に書かれている内容に問題はないように思います。
問題は、uWindowCaptureとやらの使い方ではないですか。

そのuWindowCaptureとやらは知りませんが、軽くサイトを読んだ程度ですが…。

処理は、非同期で行われているようです。
キャプチャ処理も、UwcWindow.RequestCaptureメソッドを呼び出し、UwcWindow.OnCapturedイベントを受け取ってキャプチャ処理の完了を受けてからUwcWindow.textureプロパティにアクセスしてキャプチャ画像を取り出すようになっているようです。
UwcWindow.OnCapturedイベントが発生しない限りは、テクスチャにアクセスしてもダメだと思いますが、その辺りはいかがですか。


関数内でRawImageにテクスチャを直接セットしたところ正しい画像が確認されているので、最初からテクスチャが空であるというわけではないのだろうと思います。

確認というのは、目視ですよね。
セットした時点では灰色の状態でも、1,2フレーム後にテクスチャにキャプチャ画像に書き換えられても、灰色の状態は1,2フレームしか表示されないので、目視では確認しづらいのではないですか。
(下手をすると、表示される前に書き換えられているかもしれませんし)
だから、まずキャプチャされるタイミングを疑っています。

試しに、数フレーム待ってからGetPixelsして試してみたらいかがですか。


と書こうと思って質問文を読み返していたら、「5.一応の解決方法」を読み忘れていることに気づきました。すいません。
(でも、一応先に書いた事も疑った方がいいと思います)

これを見る限り、GPUのみで処理されていて、CPUにはデータがないのではないかなぁ。(CPU→GPUのピクセル転送するのに、
SetPixelsでCPUのピクセル値を設定して、ApplyでCPUからGPUへ転送するのとは逆に、GetPixelsする前にGPUからCPUに転送するApplyみたいな処理が必要)

解決法の通りの手段を取るのが無難ですが、renderTexturereadableTexture2DtrimmedTextureの順に転送していますが、せっかくReadPixelsで座標を指定できるのだから、trimmedTextureReadPixelsしてしまえばreadableTexture2Dは不要になり、無駄な処理が省けてある程度軽くなると思います。

投稿2022/02/27 23:49

編集2022/03/01 13:24
katsuko

総合スコア3471

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nitoninjin

2022/02/28 09:50

ご回答ありがとうございます。 UwcWindow.onCapturedのコールバックで関数を実行してみたところ、解決しませんでした。 関数内でRawImageにテクスチャを直接セットしたところ正しい画像が確認されているので、最初からテクスチャが空であるというわけではないのだろうと思います。
nitoninjin

2022/03/09 06:34

> GPUのみで処理されていて、CPUにはデータがない こちらのご説明が腑に落ちました。5.で述べた解決方法はRenderTextureを経由してGPU上のデータをCPUに転送していたのですね(と理解しました)。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問