OpenCV的Java绑定Mat.get()给出了奇怪的结果

时间:2015-09-16 13:31:45

标签: java arrays opencv java-native-interface

我有8UC1 Mat图像。我正在尝试使用[x] [y]坐标将其转换为Java 2D字节数组。 这是我到目前为止所做的:

byte[][] arr = new byte[mat.cols()][mat.rows()];
for (int colIndex = 0; colIndex < mat.cols(); colIndex++) {
    mat.get(0, colIndex, arr[colIndex]);
}

然而,这给了我完全混乱的结果。 例如,带有.dump()的垫子,例如:

[255, 255, 255, 255, 255, 255, 255, 255, 255, 255;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
 255, 255, 255, 255, 255, 255, 255, 255, 255, 255;
 255, 255, 255, 255, 255, 255, 255, 255, 255, 255;
 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]

给了我这个(不介意-1,没关系):

-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 0 
-1 -1 -1 -1 -1 -1 -1 -1 0 0 
-1 -1 -1 -1 -1 -1 -1 0 0 0 
-1 -1 -1 -1 -1 -1 0 0 0 0 

1 个答案:

答案 0 :(得分:1)

它是关于Mat内部的内存布局和Mat.get的使用方式 问题中的代码是尝试构建一个二维数组,其中第一个索引索引列,第二个索引索引行 并且所述代码调用Mat.get期望在每次调用时填充一列。

内存布局。
对于单个通道的二维矩阵,就像这样,opencv按行存储其值。每行填充到一些适当的对齐方式 为了便于解释,我们假设一行占用的10个字节被填充到总共16个字节 我们的矩阵将存储如下:

Position 0  : 255,255,255,255,255,255,255,255,255,255,?,?,?,?,?,?,  
Position 16 :   0,  0,  0,  0,  0,  0,  0,  0,   0, 0,?,?,?,?,?,?,   
Position 32 : 255,255,255,255,255,255,255,255,255,255,?,?,?,?,?,?,  
Position 48 : 255,255,255,255,255,255,255,255,255,255,?,?,?,?,?,?,  
Position 64 : 255,255,255,255,255,255,255,255,255,255,?,?,?,?,?,?,  

位置相对于元素位置(0,0)。从该图中可以清楚地看出,最后一个元素(4,9)位于73位 ?表示未使用的值。所以我们不能对那里的价值做出任何保证 Mat也可以使用大小为0的填充,并且在我所做的测试中就是这种情况。然后布局就像这样(布局2)

Position 0  : 255,255,255,255,255,255,255,255,255,255
Position 10 :   0,  0,  0,  0,  0,  0,  0,  0,   0, 0
Position 20 : 255,255,255,255,255,255,255,255,255,255
Position 30 : 255,255,255,255,255,255,255,255,255,255
Position 40 : 255,255,255,255,255,255,255,255,255,255

<强> Mat.get
这实际上是猜测因为documentation不完整。我必须检查来源以确定。
Mat.get(int i, int j, byte[] arr)使用从(i,j)处的值开始存储的值填充数组arr,直到数组被填充。
那么,当我们使用值为9的mat.get(0, colIndex, arr[colIndex])来调用colIndex时会发生什么? 在上一个表中,(0,9)处的元素位于第9位。arr[colIndex]是一个大小为5的数组,因此它将填充从第9位到第13位的值,它们是:[ 255,?,?,?,? ]使用布局1并且[255,0,0,0,0]具有布局2.这就是OP程序输出的最后一列是[255,0,0,0,0]的原因。请注意,在布局2中,当您按行顺序读取行的最后一个元素并想要更多元素时,下一个元素将成为下一行的第一个元素。
arr[9]应该包含一列,即第10列。但是对Mat.get的调用并没有用这样的列填充它,但是值为(0,9),后面是行顺序(不是列顺序),由于填充(布局1)而未指定这些值或者是下一行(布局2)中的那些。

丑陋的解决方案,将第一个索引保留为列,第二个保留为行:

class SimpleSample
{

    static
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    public static void printArr( byte[][] arr )
    {
        for ( int y=0; y<arr[0].length; ++y )
        {
            for ( int x=0; x<arr.length; ++x )
                System.out.print( arr[x][y] + ", " );
            System.out.println("");
        }

    }

    public static void main(String[] args)
    {
        Mat mat = new Mat(5, 10, CvType.CV_8UC1, new Scalar(255));
        mat.row(1).setTo(new Scalar(0));
        System.out.println( mat.dump() );
        byte[][] arr = new byte[mat.cols()][mat.rows()];

        byte[] tmp = new byte[1];
        for ( int i=0; i<mat.rows(); ++i )
        {
            for ( int j=0; j<mat.cols(); ++j )
            {
                mat.get(i, j, tmp);
                arr[j][i] = tmp[0];
            }
        }
        printArr(arr);
    }

}

我们得到预期的输出:

  

[255,255,255,255,255,255,255,255,255,255;
  0,0,0,0,0,0,0,0,0,0; ​​
  255,255,255,255,255,255,255,255,255,255;
  255,255,255,255,255,255,255,255,255,255;
  255,255,255,255,255,255,255,255,255,255]
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  0,0,0,0,0,0,0,0,0,0,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

将arr的第一个索引更改为行,将第二个索引更改为列的解决方案。

class SimpleSample
{

    static
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    public static void printArr( byte[][] arr )
    {
        for ( int y=0; y<arr.length; ++y )
        {
            for ( int x=0; x<arr[y].length; ++x )
                System.out.print( arr[y][x] + ", " ); // Was arr[x][y] in prev code.
            System.out.println("");
        }

    }

    public static void main(String[] args)
    {
        Mat mat = new Mat(5, 10, CvType.CV_8UC1, new Scalar(255));
        mat.row(1).setTo(new Scalar(0));
        System.out.println( mat.dump() );
        byte[][] arr = new byte[mat.rows()][mat.cols()];

        for ( int i=0; i<mat.rows(); ++i )
                mat.get(i, 0, arr[i]);
        printArr(arr);
    }
}

我们得到相同的输出 请注意,虽然在这里我们必须使用arr作为arr[y][x](第一行,第二列,如opencv),这可能不是OP想要的。