7

Got the book 'Learning OpenCV' by O'Reilly not long ago, and since then I have been busy converting all of the example code I see there from OpenCV to JavaCV, usually followed by a little of my own modification too. All the while, I'm trying to keep to the pure OpenCV (C language) code as much as possible and avoid Java. For example, I implemented all of the interface elements directly through the OpenCV highgui package in JavaCV, rather than via Java Swing. By doing this I hope to both learn the OpenCV library and some C in relatively short order, as well as establishing a library of useful functions that I will be able to convert to C easily if I decide to later switch to pure OpenCV.

Anyway, I have little knowledge of C and sometimes get into trouble when dealing with pointers. The book recommends the following code as an optimal means by which to iterate through a 3-channel matrix:

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 );
}

Here is the included explanation for this code:

When computing the pointer into the matrix, remember that the matrix element data is a union. Therefore, when de-referencing this pointer, you must indicate the correct element of the union in order to obtain the correct pointer type. Th en, to off set that pointer, you must use the step element of the matrix. As noted previously, the step element is in bytes. To be safe, it is best to do your pointer arithmetic in bytes and > then cast to the appropriate type, in this case float. Although the CVMat structure has > the concept of height and width for compatibility with the older IplImage structure, we > use the more up-to-date rows and cols instead. Finally, note that we recompute ptr for > every row rather than simply starting at the beginning and then incrementing that pointer every read. Th is might seem excessive, but because the CvMat data pointer could just point to an ROI within a larger array, there is no guarantee that the data will be > contiguous across rows.

However I am having trouble converting it to JavaCV. The ptr field (pointer) seems to be a float, which confuses me. I presume that it's not actually a 'pointer', but rather a value to which the value of each pixel is added? Or is it actually a pointer, which the s value finds the total sum of for all columns within a given row?

Anyway, I would be appreciative if someone were to post for me some JavaCV code for an equivelent loop. I know there are other ways of accessing every pixel in a CvMat, but AFAIK they are all less efficient or inaccurate.

flamming_python
  • 690
  • 7
  • 13
  • As far as I can see from the example, the matrix is addressed as 2-dimensional, gray-scale, float data-type. All pixel values are summed into s which will become the cumulative brightness. The ptr is actually a pointer that points into the array at an arbitrary point and `ptr[x]` is equal to `(const float*)(mat->data.ptr + row * mat->step)[x];`. ptr is initialized for a given pixel and incremented, so `[x]` is not necessary and it is simply dereferenced with `*`, equal to `[0]`. – Mark Jeronimus Mar 29 '12 at 07:14
  • @zom How is the pointer incremented? From what I understand of the code, the pointer is assigned the value of the pixel? Or is it assigned the position of the pixel? If the earlier case, then when the operation `ptr++` occurs, would not the assigned pixel value increase, rather than causing the pointer to point to the next pixel? And if the later case, then when and where is the actual value of the pixel assigned to `s`? So many questions. – flamming_python Mar 29 '12 at 07:33
  • go read this first: http://c-faq.com/ptrs/index.html – Mark Jeronimus Mar 29 '12 at 10:24
  • As for that long sentence, read it as this: `mat` is the matrix. `->data` is the data field from mat. `.ptr` is the ptr field from data, which is a char* (pointer to character, which can be singular or array). `+ row * mat->step` performs arithmetic on the pointer to point to the specified array index for x,y=0,row. `(const float*)` casts to [pointer to float that doesn't allow changes]. The pointer value doesn't change, but when using it, a float is accessed, not a char, and when performing pointer arithmetic, one element is now assumed to be the length of float (4 bytes) so ptr++ adds 4. – Mark Jeronimus Mar 29 '12 at 10:37

2 Answers2

6

The particular example you provide would be optimally converted to Java as

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;
}
Samuel Audet
  • 4,964
  • 1
  • 26
  • 33
0

Here is a variant I got to eventually through trial & error; for iterating through a 3-channel matrix and applying a very simple filter (I believe Samuel's example already covers the summing of greyscale values nicely).

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;
}   
flamming_python
  • 690
  • 7
  • 13