OpenCV卡尔曼滤波器错误

3

我正在使用OpenCV来预测通过网络摄像头看到的球的运动。但是,我在cvKalmanPredict状态上一直遇到错误,所以我将代码简化为以下几行,并尝试仅测试过滤器:

        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

然而,当我调用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

我正在使用Cmake和MS Visual C++ 10进行编译。

看起来你有一个大小或类型的问题。在将矩阵复制到 kalman->transition_matrix->data 和其他变量之后,你是否检查过它们的样子? - vsz
在调用kalmanPredic之前,我会打印data.fl中所有矩阵的内容。在我的看来一切都很正常,所以我不知道这个错误是从哪里来的。 - ucl
1个回答

5

合理的解释

仔细阅读错误信息,可以发现断言似乎是在测试A和B是否符合D的大小。很可能在涉及这三个矩阵的计算(cvKalmanPredict)之前进行了这个检查。

在错误信息中指定的矩阵A、B和D可能直接与示例中提供的三个或更多个矩阵相关(A可能对应于kalmA等)。

需要更多的代码来澄清A、B和D与声明的矩阵之间的关系(如果有的话)。在openCV库内设置断点可能会有所帮助。

列出矩阵大小可能会提供一些见解:

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

a. 矩阵大小

由于 kalmR 是唯一一个大小不同的矩阵,因此这个矩阵可能是断言失败的原因。

如果断言仅涉及其他6x6矩阵的任何三元组合,那么断言将不会被触发,但它确实被触发了。这表明矩阵变量 kalmR 是一个有用的变量,可以通过代码进行跟踪。

这种逻辑假设 A、B 和 D 映射到表中列出的三个矩阵之一。

b. 转置矩阵

数组的大小可能是正确的,但可能被错误地转置了。

上述表格假定矩阵以 6x6 格式解释,而不是作为 1x36 向量或其转置的 36x1 列格式。它们在表达式 flags & CV_GEMM_B_T 中关联的标志由编译器提示,可以设置为指示数组是 MxN 还是 NxM 格式的。

或者,有问题的矩阵的初始化值可以重新编写,以符合 openCV 的 GEMM(广义矩阵乘法)函数所期望的格式。该操作被描述为“广义”,因为它不仅包括两个矩阵之间的乘法。

c. 复合错误

错误消息可能是点 a 和 b 的组合。


替代解释

另一种解释是断言编码不正确。错误消息中 B 矩阵的存在似乎与断言的意图不一致。

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

在逻辑运算符 and 的最后一个操作数中,测试了两个矩阵 D 和 A 的类型。

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

在类型比较之前,有一对and操作数分别比较D和A的行数以及D和A的列数。

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

这个断言似乎在检查两个矩阵是否具有相等的维度,然后再明确检查它们的类型。任何不同行或列数的两个矩阵都将排除对type()的调用,因为&&会短路。然而,在原始断言中,只有D和A的一个维度被测试了相等性,这表明检查是为了确定两个矩阵是否适合进行乘法运算。当将A替换为B时,相同的逻辑也适用:
D.rows == .. B.rows && D.cols == .. B.cols && D.type == B.type()
断言在矩阵操作之前进行了检查。通过编译器发出的混乱错误消息,我们可以猜出与断言相关的矩阵操作 - 这可以在没有访问openCV库源代码和缺少openCV文档的情况下实现。在逻辑“and”操作数中有足够的信息来部分恢复与openCV GEMM函数中编程的矩阵方程一致的涉及A、B和D的矩阵方程。这与在文件中定位特定函数和行号不同。区别在于,我们根据在断言中测试的数学属性序列推断矩阵操作序列。逻辑如下:
a. 初始猜测
断言强调变量D,因为它出现在每个and操作数中。断言提示D必须以某种方式与A和B兼容,而没有提到A与B的适用性。也许这对矩阵已经比较并发现在某种意义上是相容的。(在错误消息中,CV_GEMM_A_T、CV_GEMM_B_T比特掩码的存在,涉及A和B,支持这种观点 - 与D不同,A和B的信息包含在单一变量flags中。这也暗示为什么断言中没有类型比较D.type() == B.type(),因为这会是冗余的。)根据此重新编写断言,并重新排序操作数以收集类似项,我们有:
D.rows == .. A.cols && D.type() == A.type() && D.cols == .. B.rows

D的行数与A的列数进行比较,表明执行乘法操作D * A(如果是加法操作,则需要比较每个矩阵维度,这里没有发生)。同样地,对于D和B,在这种情况下,检查D的列大小是否与B的行大小相同,这个测试与乘法操作一致,因此我们有B * D。将这些项收集在一起,建议执行D * A + B * D或可能是D * A - B * D或类似矩阵组合的GEMM函数。
第二个猜测是,D被重新设计为存储A和B之间某个矩阵运算序列的结果。最简单的操作序列是A * B或A + B。断言中的行和列检查支持前一个猜测,D = A * B。配对的变量A、B足够相关,可以排除其他组合,例如B = D * A和A = B * D。D作为结果的目标的角色,由为该变量指定的字母暂时指示。
阅读openCV文档后,确定第二个猜测最接近正确描述。特别是,触发断言的openCV函数可以是三个例程之一:cvMatMul、cvMatMulAdd或cvGEMM,其相应的矩阵操作是:
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 

进一步了解这些函数声明的内容,请点击此处

A和B的按位flags变量

在上述情况中,我们假设由CV_GEMM_A_T和CV_GEMM_B_T所标识的flags 变量中的位域。这些标志设置的各种组合也适用于类似的讨论。
当与CV_GEMM_A_TCV_GEMM_B_T对应的位非零时,第一个断言就适用了(其中的“prime”表示转置操作):
// 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

举例来说,当子表达式 flags & CV_GEMM_A_Tflags & CV_GEMM_B_T 均为零时,断言的形式将有所不同。

错误消息意味着 flags 变量至少表示矩阵 A 和 B。openCV 文档指定此单个变量可表示所有三个输入矩阵 A、B 和 C。

最终要点:在矩阵运算之前进行断言检查

通过预测断言所提到的特定矩阵计算,我们可以缩小代码中可能的路径和矩阵算法所在的位置。

为了促进这种推理猜测,矩阵计算的断言应按照保护的基本矩阵运算的形式编写。

在这里,我们看了一些简单的矩阵表达式。当遭遇由较不常见的矩阵算法生成的类似错误消息时,所描述的方法可能对于解密触发断言的加密矩阵操作会有所帮助。

拓展:自动生成断言语句

也许有一种方法可以编写矩阵代码,以启用使用模板等方式自动生成此类断言。矩阵类和矩阵运算可以被编码以便促进这些检查的生成。


非常感谢您的建议。问题在于我在卡尔曼滤波器中使用了一个临时变量,并将其初始化为3乘1矩阵。在cvKalmanPredict中,他们尝试存储两个6乘6矩阵的乘积,这导致了错误的出现。 - ucl

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