我试图从以透视角度拍摄的图像中检测台球在桌子上的位置。我使用getPerspectiveTransform()方法来查找转换矩阵,我想将其应用于我使用HoughCircles检测到的圆圈。我试图从一个相当大的梯形形状变成一个较小的矩形形状。我不想先对图像进行转换,然后找到HoughCircles,因为图像因为houghcircles而变得过于扭曲,无法提供有用的结果。
这是我的代码:
CvMat mmat = cvCreateMat(3,3,CV_32FC1);
double srcX1 = 462;
double srcX2 = 978;
double srcX3 = 1440;
double srcX4 = 0;
double srcY = 241;
double srcHeight = 772;
double dstX = 56.8;
double dstY = 33.5;
double dstWidth = 262.4;
double dstHeight = 447.3;
CvSeq seq = cvHoughCircles(newGray, circles, CV_HOUGH_GRADIENT, 2.1d, (double)newGray.height()/40, 85d, 65d, 5, 50);
JavaCV.getPerspectiveTransform(new double[]{srcX1, srcY, srcX2,srcY, srcX3, srcHeight, srcX4, srcHeight},
new double[]{dstX, dstY, dstWidth, dstY, dstWidth, dstHeight, dstX, dstHeight}, mmat);
cvWarpPerspective(seq, seq, mmat);
for(int j=0; j<seq.total(); j++){
CvPoint3D32f point = new CvPoint3D32f(cvGetSeqElem(seq, j));
float xyr[] = {point.x(),point.y(),point.z()};
CvPoint center = new CvPoint(Math.round(xyr[0]), Math.round(xyr[1]));
int radius = Math.round(xyr[2]);
cvCircle(gray, center, 3, CvScalar.GREEN, -1, 8, 0);
cvCircle(gray, center, radius, CvScalar.BLUE, 3, 8, 0);
}
问题是我在warpPerspective()方法上得到了这个错误:
error: (-215) seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size in function cv::Mat cv::cvarrToMat(const CvArr*, bool, bool, int)
此外,我想还值得一提的是我使用JavaCV,以防方法调用看起来与您习惯的有所不同。谢谢你的帮助。
答案 0 :(得分:3)
<强>答案:强>
你想做什么的问题(除了明显的,opencv不会让你)是半径不能正确扭曲。 AFAIK xy坐标非常容易计算x'=((m00x + m01y + m02)/(m20x + m21y + m22))y'=((m10x + m11y + m12)/(m20x + m21y_m22))当m是转换矩阵。你可以通过转换原始圆的所有点然后找到x'y'和那些点之间的最大距离来破解半径(至少如果扭曲图像中的半径预计覆盖所有这些点) 顺便说一句,mIJx = m(i,j)* x(只是为了澄清)
结束答案。
我编写的所有东西都是根据c ++版本,我从来没有使用过JavaCV,但我从中看到它只是一个调用原生c ++ lib的包装器。
CvSeq是一种序列数据结构,其行为类似于链表。 你的应用程序断言的断言是
CV_Assert(seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size);
表示你的seq实例是空的(total是序列中元素的数量)或者内部seq标志被破坏了。
我建议您检查CvSeq的总成员和cvHoughCircles调用。 所有这一切都发生在cvWarpPerspective的实际实现之前(它是实现中的第一行,只将你的CvSeq转换为cv :: Mat)..所以它不是翘曲,而是你之前做的事情。 无论如何,要了解cvHoughCircles的错误,我们需要更多关于newGray和圈子创建的信息。
这是我在javaCV页面(Link)
上找到的一个例子IplImage gray = cvCreateImage( cvSize( img.width, img.height ), IPL_DEPTH_8U, 1 );
cvCvtColor( img, gray, CV_RGB2GRAY );
// smooth it, otherwise a lot of false circles may be detected
cvSmooth(gray,gray,CV_GAUSSIAN,9,9,2,2);
CvMemStorage circles = CvMemStorage.create();
CvSeq seq = cvHoughCircles(gray, circles.getPointer(), CV_HOUGH_GRADIENT,
2, img.height/4, 100, 100, 0, 0);
for(int i=0; i<seq.total; i++){
float xyr[] = cvGetSeqElem(seq,i).getFloatArray(0, 3);
CvPoint center = new CvPoint(Math.round(xyr[0]), Math.round(xyr[1]));
int radius = Math.round(xyr[2]);
cvCircle(img, center.byValue(), 3, CvScalar.GREEN, -1, 8, 0);
cvCircle(img, center.byValue(), radius, CvScalar.BLUE, 3, 8, 0);
}
从我在cvHoughCircles的实现中看到的,答案保存在圈子buff中,最后他们从中创建了CvSeq返回,所以如果你已经分配了圈子buff错了,那就不行了
修改强>
如您所见,在cvHoughCircles返回的情况下,CvSeq实例是一个点值列表,这可能是断言失败的原因。你不能将这个CvSeq转换为cv :: Mat ..因为它不是cv :: Mat。要获得cv :: Mat实例中从cvHoughCircles返回的圆圈,您需要创建一个新的cv :: Mat实例,然后将其绘制到CvSeq中的所有圆圈 - 如上面提供的示例所示。 比warping工作(你将有一个cv :: Mat实例,这就是函数所期望的 - 一个cv :: Mat作为CvSeq中唯一的元素)
结束编辑
here是CvSeq的c ++参考 如果你想摆弄源代码而不是
cvarrToMat在matrix.cpp
中CV_ELEM_SIZE位于types_c.h
中cvWarpPerspective在imgwarp.cpp
中cvHoughCircles在hough.cpp中
我希望这会有所帮助。
顺便说一句,你的下一个错误可能是: C ++ OpencCv中的cv :: warpPerspective断言dst.data != src.data
因此
cvWarpPerspective(seq, seq, mmat);
不会工作导致源垫和目标垫引用相同的数据。 并非OpenCV中的所有函数(以及一般的图像处理)都工作in-situ(因为没有原位算法,或者因为它比其他版本慢,例如n * n mat的转置将在原位工作,但是n * m,其中n!= m将在现场更难做,可能会更慢) 你不能假设使用src矩阵,因为dst将起作用。