我在Linux上使用OpenCV 2.4.2。我正在用C++编写。我想跟踪简单的对象(例如,白色背景上的黑色矩形)。首先,我使用goodFeaturesToTrack,然后使用calcOpticalFlowPyrLK在另一张图像上找到这些点。问题在于calcOpticalFlowPyrLK找不到这些点。
我发现在C中可以执行此操作的代码,在我的情况下不起作用:http://dasl.mem.drexel.edu/~noahKuntz/openCVTut9.html
我已经将其转换为C++:
int main(int, char**) {
Mat imgAgray = imread("ImageA.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat imgBgray = imread("ImageB.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat imgC = imread("ImageC.png", CV_LOAD_IMAGE_UNCHANGED);
vector<Point2f> cornersA;
goodFeaturesToTrack(imgAgray, cornersA, 30, 0.01, 30);
for (unsigned int i = 0; i < cornersA.size(); i++) {
drawPixel(cornersA[i], &imgC, 2, blue);
}
// I have no idea what does it do
// cornerSubPix(imgAgray, cornersA, Size(15, 15), Size(-1, -1),
// TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 20, 0.03));
vector<Point2f> cornersB;
vector<uchar> status;
vector<float> error;
// winsize has to be 11 or 13, otherwise nothing is found
int winsize = 11;
int maxlvl = 5;
calcOpticalFlowPyrLK(imgAgray, imgBgray, cornersA, cornersB, status, error,
Size(winsize, winsize), maxlvl);
for (unsigned int i = 0; i < cornersB.size(); i++) {
if (status[i] == 0 || error[i] > 0) {
drawPixel(cornersB[i], &imgC, 2, red);
continue;
}
drawPixel(cornersB[i], &imgC, 2, green);
line(imgC, cornersA[i], cornersB[i], Scalar(255, 0, 0));
}
namedWindow("window", 1);
moveWindow("window", 50, 50);
imshow("window", imgC);
cvWaitKey(0);
return 0;
}
我发现它仅适用于winsize =11。我尝试在移动的矩形上使用它来检查它与原点的距离。它几乎检测不到所有四个角。
int main(int, char**) {
std::cout << "Compiled at " << __TIME__ << std::endl;
Scalar white = Scalar(255, 255, 255);
Scalar black = Scalar(0, 0, 0);
Scalar red = Scalar(0, 0, 255);
Rect rect = Rect(50, 100, 100, 150);
Mat org = Mat(Size(640, 480), CV_8UC1, white);
rectangle(org, rect, black, -1, 0, 0);
vector<Point2f> features;
goodFeaturesToTrack(org, features, 30, 0.01, 30);
std::cout << "POINTS FOUND:" << std::endl;
for (unsigned int i = 0; i < features.size(); i++) {
std::cout << "Point found: " << features[i].x;
std::cout << " " << features[i].y << std::endl;
}
bool goRight = 1;
while (1) {
if (goRight) {
rect.x += 30;
rect.y += 30;
if (rect.x >= 250) {
goRight = 0;
}
} else {
rect.x -= 30;
rect.y -= 30;
if (rect.x <= 50) {
goRight = 1;
}
}
Mat frame = Mat(Size(640, 480), CV_8UC1, white);
rectangle(frame, rect, black, -1, 0, 0);
vector<Point2f> found;
vector<uchar> status;
vector<float> error;
calcOpticalFlowPyrLK(org, frame, features, found, status, error,
Size(11, 11), 5);
Mat display;
cvtColor(frame, display, CV_GRAY2BGR);
for (unsigned int i = 0; i < found.size(); i++) {
if (status[i] == 0 || error[i] > 0) {
continue;
} else {
line(display, features[i], found[i], red);
}
}
namedWindow("window", 1);
moveWindow("window", 50, 50);
imshow("window", display);
if (cvWaitKey(300) > 0) {
break;
}
}
}
Lucas-Kanade的OpenCV实现似乎无法跟踪二进制图像上的矩形。我是在做错什么还是该功能不起作用?
最佳答案
Lucas Kanade方法通过使用该区域中的梯度来估计该区域的运动。这是在梯度下降方法的情况下。因此,如果您在x和y方向上没有渐变,则该方法将失败。第二个重要说明是Lucas Kanade方程
E = sum_ {winsize}(Ix * u + Iy * v * It)²
是强度常数约束的一阶泰勒近似。
I(x,y,t)= I(x + u,y + v,t + 1)
因此,对没有级别的方法(图像金字塔)的限制是图像需要是线性函数。实际上,这意味着只能根据您选择的winsize来估计小 Action 。这就是为什么要使用使图像线性化的水平(It)的原因。因此,从5点到3点就足够了。在您的情况下,顶层图片的尺寸为640x480/2 ^ 5 = 20 x 15。
最后,代码中的问题是这一行:
if (status[i] == 0 || error[i] > 0) {
您从lucas kanade方法获得的错误是生成的SSD,这意味着:
错误= sum(winSize)(I(x,y,0)-I(x + u,y + u,1)^ 2)/(winsize * winsize)
错误不太可能是0。因此,最后您跳过了所有功能。通过忽略错误,我有很好的经验,这只是一个信心指标。有很好的替代置信度度量,如前/后置信度。如果丢弃了过多的功能,您也可以通过忽略状态标志来开始实验