OpenCV如何在使用单个静态图像时将速度矢量绘制为箭头
Posted
技术标签:
【中文标题】OpenCV如何在使用单个静态图像时将速度矢量绘制为箭头【英文标题】:OpenCV How to Plot velocity vectors as arrows in using single static image 【发布时间】:2012-04-15 10:52:09 【问题描述】:我正在尝试绘制速度矢量,就像在 matlab 中我们使用“quiver”函数 http://www.mathworks.com/help/techdoc/ref/quiver.html
我需要使用 OpenCV 库在 C++ 中移植相同的方法。
听说有几种光流方法,即Lucas and Kanade (cvCalOpticalFlowLK) or Horn and Schunck (cvCalOpticalFlowHS) or Block Matching method (cvCalOpticalFlowBM)
但是所有这些功能都需要两张图像,而我需要使用一张图像,因为我正在处理指纹。
请帮帮我...
[编辑] 找到解决方案
void cvQuiver(IplImage*Image,int x,int y,int u,int v,CvScalar Color,
int Size,int Thickness)
cv::Point pt1,pt2;
double Theta;
double PI = 3.1416;
if(u==0)
Theta=PI/2;
else
Theta=atan2(double(v),(double)(u));
pt1.x=x;
pt1.y=y;
pt2.x=x+u;
pt2.y=y+v;
cv::line(Image,pt1,pt2,Color,Thickness,8); //Draw Line
Size=(int)(Size*0.707);
if(Theta==PI/2 && pt1.y > pt2.y)
pt1.x=(int)(Size*cos(Theta)-Size*sin(Theta)+pt2.x);
pt1.y=(int)(Size*sin(Theta)+Size*cos(Theta)+pt2.y);
cv::line(Image,pt1,pt2,Color,Thickness,8); //Draw Line
pt1.x=(int)(Size*cos(Theta)+Size*sin(Theta)+pt2.x);
pt1.y=(int)(Size*sin(Theta)-Size*cos(Theta)+pt2.y);
cv::line(Image,pt1,pt2,Color,Thickness,8); //Draw Line
else
pt1.x=(int)(-Size*cos(Theta)-Size*sin(Theta)+pt2.x);
pt1.y=(int)(-Size*sin(Theta)+Size*cos(Theta)+pt2.y);
cv::line(Image,pt1,pt2,Color,Thickness,8); //Draw Line
pt1.x=(int)(-Size*cos(Theta)+Size*sin(Theta)+pt2.x);
pt1.y=(int)(-Size*sin(Theta)-Size*cos(Theta)+pt2.y);
cv::line(Image,pt1,pt2,Color,Thickness,8); //Draw Line
【问题讨论】:
【参考方案1】:我在这里完成了当前的答案,但未能给出每个箭头尖端的正确大小。 MATLAB 这样做的方式是,当箭头接近一个点时,它没有任何尖端,而对于长箭头,它会显示一个大尖端,如下图所示。
为了获得这种效果,我们需要在箭头长度范围内对每个箭头的“尖端大小”进行标准化。以下代码可以解决问题
double l_max = -10;
for (int y = 0; y < img_sz.height; y+=10) // First iteration, to compute the maximum l (longest flow)
for (int x = 0; x < img_sz.width; x+=10)
double dx = cvGetReal2D(velx, y, x); // Gets X component of the flow
double dy = cvGetReal2D(vely, y, x); // Gets Y component of the flow
CvPoint p = cvPoint(x, y);
double l = sqrt(dx*dx + dy*dy); // This function sets a basic threshold for drawing on the image
if(l>l_max) l_max = l;
for (int y = 0; y < img_sz.height; y+=10)
for (int x = 0; x < img_sz.width; x+=10)
double dx = cvGetReal2D(velx, y, x); // Gets X component of the flow
double dy = cvGetReal2D(vely, y, x); // Gets Y component of the flow
CvPoint p = cvPoint(x, y);
double l = sqrt(dx*dx + dy*dy); // This function sets a basic threshold for drawing on the image
if (l > 0)
double spinSize = 5.0 * l/l_max; // Factor to normalise the size of the spin depeding on the length of the arrow
CvPoint p2 = cvPoint(p.x + (int)(dx), p.y + (int)(dy));
cvLine(resultDenseOpticalFlow, p, p2, CV_RGB(0,255,0), 1, CV_AA);
double angle; // Draws the spin of the arrow
angle = atan2( (double) p.y - p2.y, (double) p.x - p2.x );
p.x = (int) (p2.x + spinSize * cos(angle + 3.1416 / 4));
p.y = (int) (p2.y + spinSize * sin(angle + 3.1416 / 4));
cvLine( resultDenseOpticalFlow, p, p2, CV_RGB(0,255,0), 1, CV_AA, 0 );
p.x = (int) (p2.x + spinSize * cos(angle - 3.1416 / 4));
p.y = (int) (p2.y + spinSize * sin(angle - 3.1416 / 4));
cvLine( resultDenseOpticalFlow, p, p2, CV_RGB(0,255,0), 1, CV_AA, 0 );
这是 OpenCV 代码的外观示例
我希望这可以帮助其他人在谷歌上搜索相同的问题。
【讨论】:
@wolvorinePk 很高兴它对某人有所帮助! @Dan :我建议您编辑代码并将其放在像“arrowedLine()”这样的函数中。哪个语法与 cv::line() 相似?【参考方案2】:根据 Dan 的代码和 mkuse 的建议,这里有一个与 cv::line() 语法相同的函数:
static void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
int thickness=1, int line_type=8, int shift=0, double tipLength=0.1)
const double tipSize = norm(pt1-pt2)*tipLength; // Factor to normalize the size of the tip depending on the length of the arrow
line(img, pt1, pt2, color, thickness, line_type, shift);
const double angle = atan2( (double) pt1.y - pt2.y, (double) pt1.x - pt2.x );
Point p(cvRound(pt2.x + tipSize * cos(angle + CV_PI / 4)),
cvRound(pt2.y + tipSize * sin(angle + CV_PI / 4)));
line(img, p, pt2, color, thickness, line_type, shift);
p.x = cvRound(pt2.x + tipSize * cos(angle - CV_PI / 4));
p.y = cvRound(pt2.y + tipSize * sin(angle - CV_PI / 4));
line(img, p, pt2, color, thickness, line_type, shift);
我们将看看那些维护 OpenCV 存储库的人是否会喜欢它:-)
【讨论】:
下一个 OpenCV 版本将有 cv::arrowedLine 可用【参考方案3】:cvCalOpticalFlowLK 不绘制速度矢量,它计算这些速度矢量。如果您没有这些向量,则必须使用两个图像调用此函数。我猜你已经有了这些向量,你只是想绘制它们。
在这种情况下,您可以使用cv::line function,例如:
cv::line(yourImage, cv::Point(baseX, baseY), cv::Point(endX, endY));
希望对您有所帮助!
【讨论】:
感谢我尝试了一系列不同的方法来使用单个图像创建箭头,现在它可以工作了。 绘制没有符号(方向/箭头提示)的向量是不好的做法。我推荐下面的答案。以上是关于OpenCV如何在使用单个静态图像时将速度矢量绘制为箭头的主要内容,如果未能解决你的问题,请参考以下文章