3
votes

I was using the OpenCV to predict the motion of a ball seen with a webcam. However I keep getting an error on the cvKalmanPredict state, so I simplify the code into these few lines and try to test the filter solely:

        CvKalman* kalman = cvCreateKalman(6,3,1);
        kalman->temp2 = cvCreateMat(1, 1, CV_32FC1);

        float alpha = 0.1, beta = 0.2;

        float kalmA[] = {1.0+t0/t1, 0, 0, -t0/t1, 0, 0,
                         0, 1.0+t0/t1, 0, 0, -t0/t1, 0,
                         0, 0, 1.0+t0/t1, 0, 0, -t0/t1,
                         1, 0, 0, 0, 0, 0,
                         0, 1, 0, 0, 0, 0,
                         0, 0, 1, 0, 0, 0};
        float kalmB[] = {0, 0, 1, 0, 0, 0};
        float kalmH[] = {1, 0, 0, 0, 0, 0,
                         0, 1, 0, 0, 0, 0,
                         0, 0, 1, 0, 0, 0};
        float kalmQ[] = {alpha, 0, 0, 0, 0, 0,
                         0, alpha, 0, 0, 0, 0,
                         0, 0, beta, 0, 0, 0,
                         0, 0, 0, alpha, 0, 0,
                         0, 0, 0, 0, alpha, 0,
                         0, 0, 0, 0, 0, beta};
        float kalmR[] = {alpha, 0, 0,
                         0, alpha, 0,
                         0, 0, beta};
        float kalmS[] = {0,0,0, 0, 0, 0};
        float kalmP[] = {480, 0, 0, 0, 0, 0,
                         0, 480, 0, 0, 0, 0,
                         0, 0, 480, 0, 0, 0,
                         0, 0, 0, 480, 0, 0,
                         0, 0, 0, 0, 480, 0,
                         0, 0, 0, 0, 0, 480};

        memcpy( kalman->transition_matrix->data.fl, kalmA, sizeof(kalmA) );
        memcpy( kalman->control_matrix->data.fl, kalmB, sizeof(kalmB) );
        memcpy( kalman->measurement_matrix->data.fl, kalmH, sizeof(kalmH) );
        memcpy( kalman->process_noise_cov->data.fl, kalmQ, sizeof(kalmQ) );
        memcpy( kalman->measurement_noise_cov->data.fl, kalmR, sizeof(kalmR) );


        // initialize state and covariance
        memcpy( kalman->state_post->data.fl, kalmS, sizeof(kalmS) );
        cvSetIdentity( kalman->error_cov_post, cvRealScalar(3));

        // update the control
        float t0 = 0.3;
        cvSetReal2D( kalman->temp2, 0, 0, -490 * t0 * t0 );

        const CvMat* kalmanPred = cvKalmanPredict(kalman, kalman->temp2);

        CvMat* kalmMeas = cvCreateMat(3,1,CV_32FC1);
        cvSetReal2D(kalmMeas, 0, 0, 3);
        cvSetReal2D(kalmMeas, 1, 0, 2);
        cvSetReal2D(kalmMeas, 2, 0, 5.5);
        cvKalmanCorrect(kalman, kalmMeas);
        cvReleaseMat(&kalmMeas);

        // release memory

However, I still get the same error when calling the cvKalmanPredict:

        OpenCV Error: Assertion failed ((D.rows == ((flags & CV_GEMM_A_T) == 0 ? A.rows : A.cols)) && (D.cols == ((flags & CV_GEMM_B_T) == 0 ? B.cols : B.rows)) && D.type() == A.type() in unknown function. file C:\Users\opencv\modules\core\src\matmul.cpp. line 2930

I'm using cmake with MS Visual C++ 10 to compile.

1
It seems you have a size or type problem. Did you check how your kalman->transition_matrix->data and others look like after you copied your matrices into them?vsz
I print the content in data.fl for all the matrices before calling kalmanPredic. Things look ok to me and that's why I don't know where this error comes from.ucl

1 Answers

5
votes

Plausible Interpretation

In reading the detailed error message, the assertation appears to be testing if A and B conform to the size of D. Presumably this check occurs prior to a computation(cvKalmanPredict) involving these three matrices.

Matrices A, B and D specified in the error message may be directly related to three or more of the matrices provided in the example(A corresponds perhaps to kalmA etc).

More code is required to clarify the relation, if any, between A, B and D and the declared matrices. Setting breakpoints within the openCV library may help.

Listing the matrix sizes may provide an insight:

Variable  Size   Variable  Size   Variable  Size 
kalmA     6x6    kalmQ     6x6    kalmR     3x3
kalmB     6x6    kalmS     6x6         
kalmH     6x6    kalmP     6x6     

a. Matrix size

As kalmR is the only matrix differing in size, this matrix could be a source for the assertion failure.

If the assertion exclusively involved any triple combination of the other 6x6 matrices, the assertion would not be triggered, but it does fire. This suggests the matrix variable kalmR is a useful variable to begin tracking through the code.

This logic presumes A, B and D map to three of the matrices listed in the table.

b. Transposing Matrices

The arrays may be of correct size, but could be incorrectly transposed.

The above table assumes the matrices are interpreted in a 6x6 format rather then as a 1x36 vector or its transpose, 36x1 column-format. Their associated flag in the expression flags & CV_GEMM_B_T prompted by the compiler, could be set to indicate whether the arrays are MxN or NxM formatted.

Alternatively, the initialization values of the offending matrices could be rewritten to conform to the format expected by openCV's GEMM(Generalized Matrix Multiplication) function. The operation is described as generalized since it encompasses more then just multiplications between two matrices.

c. Compounding errors

The error message could be some combination of both points a. and b.


Alternative explanation

Another interpretation is that the assertion is coded incorrectly. The presence of the B matrix in the error message appears to be inconsisent with the intent of the assertion.

//should that solitary B be an A?
D.rows == .. A.rows && D.cols == .. B.cols && D.type() == A.type()

The final operand in the logical and is testing the type of the two matrices D and A.

D.type() == A.type()

Preceding the type-comparison, is the pair of and-operands comparing the number of rows of D and A, and the number of columns of D and A, respectively.

//simplified assertion with B replaced by A
D.rows == .. A.rows && D.cols == .. A.cols  

The assertion appears to be checking if two matrices are of equal dimensions, prior to making an explicit check of their type.

Any two matrices, with differing row or column count would exclude a call to type() in the assertion, since && short-circuits. Yet in the original assertion, only one dimension of D and A is tested for equality, suggesting the check is to determine if the two matrices are compatible for multiplication.

The same logic applies when A is replaced with B:

D.rows == .. B.rows && D.cols == .. B.cols && D.type == B.type()


Assertive Checks Prior to a Matrix operation

Surprisingly, from the convoluted error message emitted by the compiler, we can guess the matrix operation associated with the assertion - this can be achieved without having access to the openCV library's source code and in the absence of any openCV documentation.

There is enough information within the logical and-operands to partially recover a matrix equation(involving A, B and D) that is consistent with the programmed matrix equation in the openCV GEMM function(that fires the error message).

This is different from locating a particular function and line number in a file. The distinction is we are inferring the sequence of matrix operations based on the series of mathematical properties tested in the assertion.

The logic is as follows:

a. Initial guess

The assertion's emphasis is on the variable D as it occurs in each of the and-operands.

The assertion hints that D must be compatible in some way with both A and B, whereas it does not mention the suitability of A with B.

Perhaps these pair of matrices were previously compared and found to be compatible in some sense.(In the error message, the presence of the bitmasks CV_GEMM_A_T, CV_GEMM_B_T pertaining to A and B, supports this view - information for A and B, unlike for D, is contained in the singular variable flags.

This also hints why there is no type-comparison D.type() == B.type() within the assertion as it would be redundant).

With this in mind and rewritting the assertion with the operands reordered to collect similar terms, we have:

D.rows == .. A.cols && D.type() == A.type() && D.cols == .. B.rows

The comparison of D's row size with the column size of A suggests D * A, a multiplication operation(an addition operation would require a comparison of each matrix dimension which doesn't occur here).

Likewise for D and B - on this occasion D's column size is checked against the row size of B - this test is consistent with a multiplication operation, so we have B * D.

Collecting these terms together, suggests D * A + B * D or possibly D * A - B * D or some similar matrix combination is executed by the GEMM function.

b. Second guess

Rather then continuing as a multiplicative or additive operand, D is reinvented to store the result of some sequence of matrix operations between A and B. The simplest operation-sequence is A * B or A + B.

The row and column checks in the assertion supports the former guess, D = A * B.

The paired variables A, B are sufficiently related to discount the other combinations such as B = D * A and A = B * D

The role of D in acting as a destination for the result, is tentatively indicated by the letter designated to this variable.

c. Did any of the guesses recover the matrix operations?

On reading the openCV documentation, the second guess is identified as being closest to the correct description.

In particular, the openCV function, triggering the assertion, could be one of three routines: cvMatMul, cvMatMulAdd or cvGEMM and whose corresponding matrix operations are:

D =   A * B         // cvMatMul
D =   A * B +   C   // cvMatMulAdd 
D = α A * B + β C   // cvGEMM - Generalized Matrix Multiplication 
                    //          alpha, beta are scalars unlike A, B, C, D 

Further information on these function declarations are documented here

The bitwise flags variable for A and B

In the above, we have assumed that the bit fields in the flags variable identified by CV_GEMM_A_T and CV_GEMM_B_T. A similar discussion applies for various combinations of these flag settings.

When the bits corresponding to CV_GEMM_A_T and CV_GEMM_B_T are non-zero, the first of two assertions applies(the prime indicates the transpose operation):

// matrix-size selected by the conditional operator when the ...
D.rows == .. A'.rows && D.cols == .. B'.cols    // ... bit fields are set 
D.rows == .. A .cols && D.cols == .. B .rows    // ... bit fields are unset

For comparison, when the subexpressions flags & CV_GEMM_A_T and flags & CV_GEMM_B_T are zero, the assertion assumes a different form.

The error message implies the flags variable represents at least the matrices A and B. The openCV documentation specifies that all three input matrices A, B and C are representable by this single variable.

Final Point: Assertive Checks Prior to Matrix operations

In preempting the particular matrix computations the assertion alludes to, we are narrowing down the possible paths through the code and the location where the matrix algorithm resides.

To facilitate this reasoned guessing, assertions for matrix calculations should be written in a form to hint at the underlying matrix operations which they guard.

Here we have looked at simple matrix expressions. When confronted with similar error messages generated by less trivial matrix algorithms, the described approach might prove useful for deciphering the encrypted matrix operations, encoded in the triggered assertion.

Extension: Automatically Generating Assertion Statements

There may be a way to write matrix code to enable the automatic generation of such assertions, using templates perhaps. The matrix class and matrix operations could be coded to facilitate the generation of such checks.