将OpenCV矩阵循环转换为JavaCV

7
我不久前得到了《学习OpenCV》这本O'Reilly出版的书籍,自那以后,我一直忙于将其中所有的示例代码从OpenCV转换为JavaCV,并且通常还会进行一些我自己的修改。同时,我尽可能地遵循纯OpenCV(C语言)代码,避免Java。例如,我通过JavaCV中的OpenCV highgui包直接实现了所有界面元素,而不是通过Java Swing。通过这样做,我希望能够在相对短的时间内学习OpenCV库和一些C语言,建立一个有用的函数库,如果以后决定转向纯OpenCV,我可以轻松地将其转换为C语言。
无论如何,我对C语言知之甚少,有时在处理指针时遇到麻烦。该书推荐以下代码作为迭代3通道矩阵的最佳方法:
float sum( const CvMat* mat ) {
    float s = 0.0f;
    for(int row=0; row<mat->rows; row++ ) {
        const float* ptr = (const float*)(mat->data.ptr + row * mat->step);
        for( col=0; col<mat->cols; col++ ) {
            s += *ptr++;
        }
    }
    return( s );
}

这里是代码的解释:
在计算矩阵指针时,记住矩阵元素数据是一个联合体。因此,在取消引用此指针时,必须指示联合体的正确元素,以便获得正确的指针类型。然后,要偏移该指针,必须使用矩阵的步骤元素。如前所述,步长元素以字节为单位。为了安全起见,最好将指针算术运算转换为字节,然后再转换为适当的类型,即float。尽管CVMat结构具有与旧的IplImage结构兼容的高度和宽度概念,但我们改用更现代的行和列。最后,请注意,我们为每一行重新计算ptr,而不是仅从开头开始,然后在每次读取时递增该指针。这可能看起来过于复杂,但由于CvMat数据指针可以指向较大数组中的ROI,因此不能保证数据在行之间连续。
然而,我在将其转换为JavaCV时遇到了问题。 ptr字段(指针)似乎是一个浮点数,这让我感到困惑。我认为它实际上不是一个“指针”,而是将每个像素的值相加的值?或者它实际上是一个指针,s值在给定行内找到所有列的总和?
无论如何,如果有人为我发布一些JavaCV代码以进行等效循环,我将不胜感激。我知道还有其他访问每个像素的CvMat的方法,但据我所知,它们都不够高效或准确。

就我所看到的例子,矩阵被视为二维、灰度、浮点数据类型。所有像素值都被累加到s中,成为累积亮度。ptr实际上是一个指针,它指向数组中的任意点,ptr[x]等于(const float*)(mat->data.ptr + row * mat->step)[x];。ptr被初始化为给定像素并递增,因此[x]不是必需的,只需使用*进行解引用,相当于[0] - Mark Jeronimus
@zom 指针是如何递增的?根据我对代码的理解,指针被赋予像素的值?还是被赋予像素的位置?如果是前者,那么当操作 ptr++ 发生时,不会使指定的像素值增加,而是会导致指针指向下一个像素吗?如果是后者,那么实际的像素值何时何地被分配给 s?太多问题了。 - flamming_python
先去阅读这个:http://c-faq.com/ptrs/index.html - Mark Jeronimus
关于那个长句子,可以这样理解:mat是矩阵。->data是从mat中获取的数据字段。.ptr是从data中获取的ptr字段,它是一个char*(指向字符的指针,可以是单个或数组)。+ row * mat->step对指针执行算术运算,以指向x、y=0、row的指定数组索引。(const float*)将其强制转换为[不允许更改的float指针]。指针值不会改变,但在使用它时,将访问float而不是char,并且在执行指针算术时,现在假定一个元素是float的长度(4个字节),因此ptr++会增加4。 - Mark Jeronimus
2个回答

6
您提供的示例最好转换为Java,如下所示:
float sum(CvMat mat) {
    final int rows = mat.rows();
    final int cols = mat.cols();
    final int step = mat.step()/4;
    FloatBuffer buf = mat.getFloatBuffer();
    float s = 0.0f;
    for (int row = 0; row < rows; row++) {
        buf.position(row * step);
        for (int col = 0; col< cols; col++) {
            s += buf.get();
        }
    }
    return s;
}

谢谢Sam,很高兴能从JavaCV的开发者那里得到答案!不过我已经解决了那个问题。 - flamming_python
好的,太棒了!但是如果它回答了你最初发布的问题,你仍然应该接受这个答案... - Samuel Audet

0

这里是我通过尝试和错误最终得到的变体; 用于迭代3通道矩阵并应用非常简单的滤镜(我相信Samuel的示例已经很好地涵盖了灰度值求和)。

static IplImage setSaturate_sv(IplImage imgIn) {
    IplImage imgOut = cvCloneImage(imgIn);
    ByteBuffer pointer = imgOut.getByteBuffer();

    int height = imgIn.height();
    int width = imgIn.width();
    int widthStep = imgIn.widthStep();
    int nChannels = imgIn.nChannels();
    int rowIndex;

    for (int row = 0; row < height; row++) {
        rowIndex = row * widthStep;
        for (int col = 0; col < width; col++) {
            pointer.put((rowIndex + (col * nChannels) + 1), (byte)255);
            pointer.put((rowIndex + (col * nChannels) + 2), (byte)255);
            pointer.put((rowIndex + (col * nChannels) + 3), /* leave alone */);
        }
    }
    return imgOut;
}   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接