在光流路径区域周围创建边界框
creating a bounding box around a field of optical flow paths
我已经使用 cv::calcOpticalFlowFarneback
在 openFrameworks 中使用 ofxOpenCv 计算当前和先前视频帧中的光流。
然后我在顶部绘制带有光流场的视频,然后绘制矢量显示高于特定阈值的区域中的运动流。
我现在要做的是为这些运动区域创建一个边界框并获取质心并将 x
、y
位置存储在变量中以进行跟踪。
如果有帮助的话,这就是我绘制流场的方式。
if (calculatedFlow){
ofSetColor( 255, 255, 255 );
video.draw( 0, 0);
int w = gray1.width;
int h = gray1.height;
//1. Input images + optical flow
ofPushMatrix();
ofScale( 4, 4 );
//Optical flow
float *flowXPixels = flowX.getPixelsAsFloats();
float *flowYPixels = flowY.getPixelsAsFloats();
ofSetColor( 0, 0, 255 );
for (int y=0; y<h; y+=5) {
for (int x=0; x<w; x+=5) {
float fx = flowXPixels[ x + w * y ];
float fy = flowYPixels[ x + w * y ];
//Draw only long vectors
if ( fabs( fx ) + fabs( fy ) > .5 ) {
ofDrawRectangle( x-0.5, y-0.5, 1, 1 );
ofDrawLine( x, y, x + fx, y + fy );
}
}
}
}
对于您的问题,没有简单的答案。这是一个建议的解决方案。它涉及多个步骤,但如果您的域足够简单,您可以简化它。
对于每一帧,
将流量计算为两个图像 flow_x
,flow_y
使用 farneback 方法将当前帧与前一帧进行比较。(你似乎在这样做,在你的代码中)
将流图像转换成hsv图像,其中每个像素的色调分量表示流的角度atan2(flow_y/flow_x)
,每个像素的值分量表示流的大小sqrt(flow_x\*\*2 + flow_y\*\*2)
- 在上面的步骤中,使用您的阈值机制来抑制幅度低于特定阈值的流像素(使它们变黑)。
根据颜色范围分割 HSV 图像。您可以使用有关您的域的先验信息,或者您可以获取色调分量的直方图并确定突出的色调范围以 class 化像素。作为此步骤的结果,您可以为每个像素分配 class。
将属于每个class的像素分成多幅图像。属于分段 class-1 的所有像素将转到 image-1,属于分段 class-2 的所有像素将转到 image-2 等。现在每个分段图像都包含 HSV 图像中的像素,在一个特定的颜色范围。
将每个分割后的图像转换为黑白图像,并使用opencv的形态学操作使用连通性分割成多个区域。 (连接的组件)。
找到每个连通分量的质心。
我发现 this reference 在这方面很有帮助。
我通过从 flowX 和 flowY 创建新图像解决了我的问题。这是通过将 flowX 和 flowY 添加到新的 CV FloatImage 来完成的。
flowX +=flowY;
flowXY = flowX;
然后我能够从新创建的图像的像素中找到轮廓,然后我可以存储所有运动斑点的所有质心。
像这样:
contourFinder.findContours( mask, 10, 10000, 20, false );
//Storing the objects centers with contour finder.
vector<ofxCvBlob> &blobs = contourFinder.blobs;
int n = blobs.size(); //Get number of blobs
obj.resize( n ); //Resize obj array
for (int i=0; i<n; i++) {
obj[i] = blobs[i].centroid; //Fill obj array
}
我最初注意到,由于负值,在 x-axis 和 y-axis 中仅在一个方向上跟踪运动。我通过调用 cv::Mat.
中的 abs() 函数来更改我的光流计算来解决这个问题
Mat img1( gray1.getCvImage() ); //Create OpenCV images
Mat img2( gray2.getCvImage() );
Mat flow;
calcOpticalFlowFarneback( img1, img2, flow, 0.7, 3, 11, 5, 5, 1.1, 0 );
//Split flow into separate images
vector<Mat> flowPlanes;
Mat newFlow;
newFlow = abs(flow); //abs flow so values are absolute. Allows tracking in both directions.
split( newFlow, flowPlanes );
//Copy float planes to ofxCv images flowX and flowY
IplImage iplX( flowPlanes[0] );
flowX = &iplX;
IplImage iplY( flowPlanes[1] );
flowY = &iplY;
我已经使用 cv::calcOpticalFlowFarneback
在 openFrameworks 中使用 ofxOpenCv 计算当前和先前视频帧中的光流。
然后我在顶部绘制带有光流场的视频,然后绘制矢量显示高于特定阈值的区域中的运动流。
我现在要做的是为这些运动区域创建一个边界框并获取质心并将 x
、y
位置存储在变量中以进行跟踪。
如果有帮助的话,这就是我绘制流场的方式。
if (calculatedFlow){
ofSetColor( 255, 255, 255 );
video.draw( 0, 0);
int w = gray1.width;
int h = gray1.height;
//1. Input images + optical flow
ofPushMatrix();
ofScale( 4, 4 );
//Optical flow
float *flowXPixels = flowX.getPixelsAsFloats();
float *flowYPixels = flowY.getPixelsAsFloats();
ofSetColor( 0, 0, 255 );
for (int y=0; y<h; y+=5) {
for (int x=0; x<w; x+=5) {
float fx = flowXPixels[ x + w * y ];
float fy = flowYPixels[ x + w * y ];
//Draw only long vectors
if ( fabs( fx ) + fabs( fy ) > .5 ) {
ofDrawRectangle( x-0.5, y-0.5, 1, 1 );
ofDrawLine( x, y, x + fx, y + fy );
}
}
}
}
对于您的问题,没有简单的答案。这是一个建议的解决方案。它涉及多个步骤,但如果您的域足够简单,您可以简化它。
对于每一帧,
将流量计算为两个图像
flow_x
,flow_y
使用 farneback 方法将当前帧与前一帧进行比较。(你似乎在这样做,在你的代码中)将流图像转换成hsv图像,其中每个像素的色调分量表示流的角度
atan2(flow_y/flow_x)
,每个像素的值分量表示流的大小sqrt(flow_x\*\*2 + flow_y\*\*2)
- 在上面的步骤中,使用您的阈值机制来抑制幅度低于特定阈值的流像素(使它们变黑)。
根据颜色范围分割 HSV 图像。您可以使用有关您的域的先验信息,或者您可以获取色调分量的直方图并确定突出的色调范围以 class 化像素。作为此步骤的结果,您可以为每个像素分配 class。
将属于每个class的像素分成多幅图像。属于分段 class-1 的所有像素将转到 image-1,属于分段 class-2 的所有像素将转到 image-2 等。现在每个分段图像都包含 HSV 图像中的像素,在一个特定的颜色范围。
将每个分割后的图像转换为黑白图像,并使用opencv的形态学操作使用连通性分割成多个区域。 (连接的组件)。
找到每个连通分量的质心。
我发现 this reference 在这方面很有帮助。
我通过从 flowX 和 flowY 创建新图像解决了我的问题。这是通过将 flowX 和 flowY 添加到新的 CV FloatImage 来完成的。
flowX +=flowY;
flowXY = flowX;
然后我能够从新创建的图像的像素中找到轮廓,然后我可以存储所有运动斑点的所有质心。
像这样:
contourFinder.findContours( mask, 10, 10000, 20, false );
//Storing the objects centers with contour finder.
vector<ofxCvBlob> &blobs = contourFinder.blobs;
int n = blobs.size(); //Get number of blobs
obj.resize( n ); //Resize obj array
for (int i=0; i<n; i++) {
obj[i] = blobs[i].centroid; //Fill obj array
}
我最初注意到,由于负值,在 x-axis 和 y-axis 中仅在一个方向上跟踪运动。我通过调用 cv::Mat.
中的 abs() 函数来更改我的光流计算来解决这个问题Mat img1( gray1.getCvImage() ); //Create OpenCV images
Mat img2( gray2.getCvImage() );
Mat flow;
calcOpticalFlowFarneback( img1, img2, flow, 0.7, 3, 11, 5, 5, 1.1, 0 );
//Split flow into separate images
vector<Mat> flowPlanes;
Mat newFlow;
newFlow = abs(flow); //abs flow so values are absolute. Allows tracking in both directions.
split( newFlow, flowPlanes );
//Copy float planes to ofxCv images flowX and flowY
IplImage iplX( flowPlanes[0] );
flowX = &iplX;
IplImage iplY( flowPlanes[1] );
flowY = &iplY;