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

标签 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 

最佳答案

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

内存布局。
对于单个 channel 的二维矩阵,在这种情况下,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。
?表示未使用的值。所以我们不能对那里的值(value)做出任何保证。
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) 用存储的值填充数组 arr,从 (i,j) 开始,直到数组已满。
那么,当我们用值为 9 的 colIndex 调用 mat.get(0, colIndex, arr[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 想要的。

关于java - OpenCV 的 Java 绑定(bind) Mat.get() 给出了奇怪的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32610057/

相关文章:

Java:从输入文件填充二维数组

java - JME-Jbullet 物理问题

javascript - Puppeteer.js - 抓取域列表和存储数据

java - 如何使边界数组索引的右侧正常工作?

python - 在 QLabel 元素中实时显示 CV2

java - Java 应用程序中的属性框架

java - 读取公钥文件并使用该 key 加密另一个文件时出现问题

php - 通过月度分配支付值(value)

python - 类型错误 : an integer is required (got type str) in python

c++ - 从视频 Opencv Haar Cascade 中获取前一帧