Zimm3r - 1 year ago 108

Java Question

I have the following example code that calculates the percentage of red pixels,

`import org.opencv.core.*;`

public class ImageProcessing {

static

{

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

}

public static void main(String[] args)

{

Mat red = Mat.zeros(5, 1, CvType.CV_8UC1);

Mat sum = Mat.zeros(5, 1, CvType.CV_8UC1); // sum of red, green, and blue

double[] red_data = {1, 1, 1, 1,255};

double[] sum_data = {1,11,12,13,255};

red.put(0, 0, red_data);

sum.put(0, 0, sum_data);

Mat percentage = Mat.zeros(5, 1, CvType.CV_64FC1);

//red.convertTo(red, CvType.CV_64F);

//sum.convertTo(sum, CvType.CV_64F);

Core.divide(red, sum, percentage, CvType.CV_64FC1);

System.out.println("Results");

for (int i = 0; i < 5; i++)

{

System.out.printf("%d: %10f / %10f = %10f (%10f) \n", i+1, red.get(i, 0)[0],

sum.get(i, 0)[0],

percentage.get(i, 0)[0],

red.get(i, 0)[0]/sum.get(i, 0)[0] );

}

}

}

Running it I expect the results in ()s however I obviously get strange results. I thought at first it had to do with integer division ( see: divide two matrices in opencv ). However converting them to 64bit floats (the commented out lines). Only gives other strange results.

Without float conversion

`Results`

1: 1.000000 / 1.000000 = 6.000000 ( 1.000000)

2: 1.000000 / 11.000000 = 1.000000 ( 0.090909)

3: 1.000000 / 12.000000 = 0.000000 ( 0.083333)

4: 1.000000 / 13.000000 = 0.000000 ( 0.076923)

5: 255.000000 / 255.000000 = 6.000000 ( 1.000000)

Process finished with exit code 0

With conversion to 64 bit floats

`Results`

1: 1.000000 / 1.000000 = 6.000000 ( 1.000000)

2: 1.000000 / 11.000000 = 0.545455 ( 0.090909)

3: 1.000000 / 12.000000 = 0.500000 ( 0.083333)

4: 1.000000 / 13.000000 = 0.461538 ( 0.076923)

5: 255.000000 / 255.000000 = 6.000000 ( 1.000000)

I do notice there are consistent results where there is a clear mapping (1.0 maps to 6.0 for both 1.0 / 1.0 and 255 / 255 but I can't find a pattern that would cause this).

Answer Source

It appears that you are using this overload of the `divide`

function:

```
public static void divide(Mat src1, Mat src2, Mat dst, double scale)
```

Parameters:

- src1 - First source array.
- src2 - Second source array of the same size and type as src1.
- dst - Destination array of the same size and type as src2.
- scale - Scalar factor.

Which performs the operation `dst(I) = saturate(src1(I) * scale / src2(I))`

.

**NB:** Note the actual meaning of the 4th parameter, where you pass `Core.CV_64FC1`

.

With a little bit of research, you can find out that the value of the constant `CV_64FC1`

is `6`

.

This means that in reality, the calculations being performed are:

```
1 * 6 / 1
1 * 6 / 11
1 * 6 / 12
1 * 6 / 13
255 * 6 / 255
```

The rest is straightforward.

Note the requirement in documentation that the output array should be *"of the same size and type as src2."* In the first case, when the two input arrays are `CV_8UC1`

, the second condition is not met, hence a new array is allocated.

Since in the first case the output is integer array, you see the rounded results. In the second case it's floating point, so no rounding is done.