解决相关代码的低 FPS 问题以计算图像中的偏移
Tackle low FPS for correlation code to compute shift in Image
我正在尝试使用相关性来跟踪对象。我逐帧地在较大的图像中找到较小的补丁。为此,我发现了补丁中的偏移,相关性最大的地方,用新补丁更新补丁。
我的代码是:
cv::Mat im_float_2,imagePart_out;
cv::Mat im_floatBig;
cv::Scalar im1_Mean, im1_Std, im2_Mean, im2_Std;
double covar, correl;
int n_pixels;
void computeShift()
{
int maxRow=0, maxCol=0, TX, TY;
double GMAX=0;
Mat image_window = Mat::zeros(imagePart.rows, imagePart.cols, CV_32F);
imagePart.convertTo(im_float_2, CV_32F);
imageBig.convertTo(im_floatBig,CV_32F);
for(maxRow=0; maxRow<=imageBig.rows-image_window.rows;maxRow++)
{
for(maxCol=0; maxCol<imageBig.cols-image_window.cols;maxCol++)
{
image_window = im_floatBig( cv::Rect( maxCol, maxRow,
image_window.cols, image_window.rows ) );
n_pixels = image_window.rows * image_window.cols;
// Compute mean and standard deviation of both images
meanStdDev(image_window, im1_Mean, im1_Std);
meanStdDev(im_float_2, im2_Mean, im2_Std);
// Compute covariance and correlation coefficient
covar = (image_window - im1_Mean).dot(im_float_2 - im2_Mean) / n_pixels;
correl = covar / (im1_Std[0] * im2_Std[0]);
if (correl > GMAX)
{
GMAX = correl; TX = maxRow; TY=maxCol;
image_window.convertTo(imagePart, CV_8UC1);
}
}
}
cvtColor(imagePart, imagePart_out, CV_GRAY2BGR);
printf("\nComputed shift: [%d, %d] MAX: %f\n", TX, TY,GMAX);
}
但是执行此操作时,即使对于小视频尺寸(帧尺寸 - 262x240
、补丁尺寸 - 25x25
),我的 FPS(1-2) 也非常低。
有什么方法可以达到更高的FPS。我也在寻找相位相关的方向,但不确定如何从这里开始。将其转换为频域会有帮助吗?
现在,我想优化上面的代码以提高速度。
是的,您可能会从使用 FFT 中获益。只需将 im_float_2
填充到 im_floatBig
的大小即可。在采用其中一种变换的复共轭后在傅立叶域中相乘会导致互相关,这与您的 correl
值不同(没有除以标准偏差)。但我认为您实际上不需要通过标准偏差进行归一化以获得良好的模板匹配。互相关本身就非常有效。结果中最大值的位置可以转换为模板的位移w.r.t。图片。
通过FFT互相关的步骤是:
- 将模板(浮动图像)填充到另一个图像的大小(带零)。
- 计算两者的 FFT。
- 翻转其中一个结果的虚部的符号(复共轭)。
- 将两者相乘。
- 计算结果的 IFFT。
- 找到具有最大值的像素的位置。
这个像素的位置表示填充模板的翻译w.r.t。另一个图像。如果它们在没有平移的情况下最佳匹配,则最大像素将位于 (x,y)=(0,0)。如果它在 (1,0) 处,则表示沿 x 移动一个像素。方向取决于您计算复共轭的两个方向中的哪一个。请注意,此结果是周期性的,相反方向的一个像素偏移由位于图像右边缘的最大像素表示。只需稍微试验一下,以确定如何将位置转换为模板的偏移。
关于您的代码:
meanStdDev(im_float_2, im2_Mean, im2_Std);
在循环中计算,即使 im_float_2
没有改变。
但是无论如何你都可以不用它进行归一化,因为你只是在寻找最大的相关性,并且将搜索中的所有值除以相同的数字不会改变哪个是最大的。 n_pixels
.
的除法也是如此
将 image_window.convertTo(imagePart, CV_8UC1)
移到循环外。在最终找到实际最大值之前,您可能会多次更新当前最大值。如果您最终只使用最后一个,那么将这么多子 windows 转换为 CV_U8
是没有意义的。在循环中,您更新最大值的 (x,y) 坐标。仅投射最终位置。
您可能不需要为您的模板搜索整个图像。物体很可能只移动了相对较小的量。您应该只查看先前已知位置周围的一小块区域。这个概念也适用于 FFT 方法:裁剪出大图像的一个区域,然后将模板填充到该大小。较小的 FFT 计算成本较低。
OpenCV 按行存储图像。将循环放在行上作为内部循环以优化缓存使用。
我正在尝试使用相关性来跟踪对象。我逐帧地在较大的图像中找到较小的补丁。为此,我发现了补丁中的偏移,相关性最大的地方,用新补丁更新补丁。
我的代码是:
cv::Mat im_float_2,imagePart_out;
cv::Mat im_floatBig;
cv::Scalar im1_Mean, im1_Std, im2_Mean, im2_Std;
double covar, correl;
int n_pixels;
void computeShift()
{
int maxRow=0, maxCol=0, TX, TY;
double GMAX=0;
Mat image_window = Mat::zeros(imagePart.rows, imagePart.cols, CV_32F);
imagePart.convertTo(im_float_2, CV_32F);
imageBig.convertTo(im_floatBig,CV_32F);
for(maxRow=0; maxRow<=imageBig.rows-image_window.rows;maxRow++)
{
for(maxCol=0; maxCol<imageBig.cols-image_window.cols;maxCol++)
{
image_window = im_floatBig( cv::Rect( maxCol, maxRow,
image_window.cols, image_window.rows ) );
n_pixels = image_window.rows * image_window.cols;
// Compute mean and standard deviation of both images
meanStdDev(image_window, im1_Mean, im1_Std);
meanStdDev(im_float_2, im2_Mean, im2_Std);
// Compute covariance and correlation coefficient
covar = (image_window - im1_Mean).dot(im_float_2 - im2_Mean) / n_pixels;
correl = covar / (im1_Std[0] * im2_Std[0]);
if (correl > GMAX)
{
GMAX = correl; TX = maxRow; TY=maxCol;
image_window.convertTo(imagePart, CV_8UC1);
}
}
}
cvtColor(imagePart, imagePart_out, CV_GRAY2BGR);
printf("\nComputed shift: [%d, %d] MAX: %f\n", TX, TY,GMAX);
}
但是执行此操作时,即使对于小视频尺寸(帧尺寸 - 262x240
、补丁尺寸 - 25x25
),我的 FPS(1-2) 也非常低。
有什么方法可以达到更高的FPS。我也在寻找相位相关的方向,但不确定如何从这里开始。将其转换为频域会有帮助吗?
现在,我想优化上面的代码以提高速度。
是的,您可能会从使用 FFT 中获益。只需将 im_float_2
填充到 im_floatBig
的大小即可。在采用其中一种变换的复共轭后在傅立叶域中相乘会导致互相关,这与您的 correl
值不同(没有除以标准偏差)。但我认为您实际上不需要通过标准偏差进行归一化以获得良好的模板匹配。互相关本身就非常有效。结果中最大值的位置可以转换为模板的位移w.r.t。图片。
通过FFT互相关的步骤是:
- 将模板(浮动图像)填充到另一个图像的大小(带零)。
- 计算两者的 FFT。
- 翻转其中一个结果的虚部的符号(复共轭)。
- 将两者相乘。
- 计算结果的 IFFT。
- 找到具有最大值的像素的位置。
这个像素的位置表示填充模板的翻译w.r.t。另一个图像。如果它们在没有平移的情况下最佳匹配,则最大像素将位于 (x,y)=(0,0)。如果它在 (1,0) 处,则表示沿 x 移动一个像素。方向取决于您计算复共轭的两个方向中的哪一个。请注意,此结果是周期性的,相反方向的一个像素偏移由位于图像右边缘的最大像素表示。只需稍微试验一下,以确定如何将位置转换为模板的偏移。
关于您的代码:
meanStdDev(im_float_2, im2_Mean, im2_Std);
在循环中计算,即使im_float_2
没有改变。但是无论如何你都可以不用它进行归一化,因为你只是在寻找最大的相关性,并且将搜索中的所有值除以相同的数字不会改变哪个是最大的。
n_pixels
. 的除法也是如此
将
image_window.convertTo(imagePart, CV_8UC1)
移到循环外。在最终找到实际最大值之前,您可能会多次更新当前最大值。如果您最终只使用最后一个,那么将这么多子 windows 转换为CV_U8
是没有意义的。在循环中,您更新最大值的 (x,y) 坐标。仅投射最终位置。您可能不需要为您的模板搜索整个图像。物体很可能只移动了相对较小的量。您应该只查看先前已知位置周围的一小块区域。这个概念也适用于 FFT 方法:裁剪出大图像的一个区域,然后将模板填充到该大小。较小的 FFT 计算成本较低。
OpenCV 按行存储图像。将循环放在行上作为内部循环以优化缓存使用。