視線認識をしようと思いPythonで書かれたサンプルコードをunityに移植しようとしていたときに困った話です.最初は,モデルの変換が間違ってるのかなとか,カラーフォーマットが違うのかな,など見当外れな方向で迷っていました.そこで原点に帰って,インプットの画像を1ピクセルずつ比較してみると,全然値が違っていて困惑しました.結論としては自身がUnityを何も理解してなかっただけなのですが,画像の原点が左下にあるか左上かの違いでした.
問題のコード
以下のコードの出力結果が一致すると思っていまいました.しかし,結果を見る通り全然違いましたw.
import cv2
def main():
frame = cv2.cvtColor(cv2.imread("test_face.png"), cv2.COLOR_BGR2RGB)
s = "img pixel data\n"
for i in range(10):
for j in range(10):
s += f"({i},{j}){frame[i][j]},"
s += "\n"
print(s)
if __name__ == "__main__":
main()
C#の方はインスペクターで画像をtest_face_spriteに突っ込みます.
using UnityEngine;
namespace MynameSpace
{
public class TestImageInput : MonoBehaviour
{
public Sprite test_face_sprite;
void Start()
{
var texture = test_face_sprite.texture;
var height = texture.height;
var width = texture.width;
texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);
texture.Apply();
Color[] pixels = texture.GetPixels();
string s = "img pixel data\n";
for (int y = 0; y < 10; y++)
{
for (int x = 0; x < 10; x++)
{
var pixel = pixels[y * width + x];
s += $"({y},{x})[{(int)(pixel.r * 255)},{(int)(pixel.g * 255)},{(int)(pixel.b * 255)}],";
}
s += "\n";
}
Debug.Log(s);
}
}
}
実行結果
OpenCVの方はこんな感じです.いい感じですね.

Unityの方はこんな感じです.はっ????

原因
原因はシンプルで,そもそも原点がOpenCVの場合画像の左上が(0,0)になります.
しかし,Unityの場合は左下が(0,0)になります.以下の画像みたいな感じです.

一見Unityはキモキモだなと思います.
ですが,自分なり解釈すると,OpenCvでは画像を単なる二次元配列だと捉え,Unityは現実のモデルをとして扱っているのだと考えました.
上に行けばプラスとういのは現実世界では当たり前なわけで,何なら一貫性があって美しいのではとも思えてきます.
また,本筋とは別になりますが,何でOpenCVはRGBの順じゃなくてBGR何でしょうね?初めて使った頃はこれで悩んだことがありますw
正しいコード
以上を踏まえると,画像を左上から読んでいけばいいわけです.
そのためy=heightから1行ずつデクリメントしていけばOpenCVと同じように扱うことができます.Unity側のコードを治すと以下のようになります.
using UnityEngine;
namespace MynameSpace
{
public class TestImageInput : MonoBehaviour
{
public Sprite test_face_sprite;
void Start()
{
var texture = test_face_sprite.texture;
var height = texture.height;
var width = texture.width;
texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);
texture.Apply();
Color[] pixels = texture.GetPixels();
string s = "img pixel data\n";
for (int y = height - 1; y >= height-10; y--)
{
for (int x = 0; x < 10; x++)
{
var pixel = pixels[y * width + x];
s += $"({y},{x})[{(int)(pixel.r * 255)},{(int)(pixel.g * 255)},{(int)(pixel.b * 255)}],";
}
s += "\n";
}
Debug.Log(s);
}
}
}
結果はこんな感じです.

いい感じですね.
結論
OpenCV:左上が(0,0)
Unity:左下が(0,0)
これだけ覚えておけば大丈夫です.
コメント