如何使用 OpenCV 计算图像的结构张量
How to Compute the Structure Tensor of an Image using OpenCV
我正在尝试实现一个使用图像在大型图像数据库中搜索相似图像的应用程序。我正在开发一个用于此搜索的图像描述符,我想将颜色信息与一些渐变信息结合起来。我已经看到在这个领域中使用结构张量来找到图像或子图像中的主要梯度方向。
我想拍摄一张图像,将其分成子图像的网格,例如4x4网格(总共16个子图像),然后找到每个单元格的领先梯度方向。为了找到领先的梯度方向,我想看看计算每个细胞的结构张量是否可以很好地表示图像梯度并改善图像匹配。这是好主意还是坏主意?思路是得到一个类似于本文3.2节思路的特征向量http://cybertron.cg.tu-berlin.de/eitz/pdf/2009_sbim.pdf
将图像分成子图像(单元格)很简单,使用 opencv 我可以使用 Sobel 函数计算偏导数。
Mat dx, dy;
Sobel(im, dx, CV_32F, 1, 0, 3, 1, 0, BORDER_DEFAULT);
Sobel(im, dy, CV_32F, 0, 1, 3, 1, 0, BORDER_DEFAULT);
计算 dx^2、dy^2 和 dxy 应该不是问题,但我不确定如何计算结构张量矩阵并使用张量矩阵找到图像或子图像的主梯度方向-图片。我怎样才能用 OpenCV 实现这个?
编辑
好的,这就是我所做的。
Mat _im; // Image to compute main gradient direction for.
cvtColor(im, _im, CV_BGR2GRAY);
GaussianBlur(_im, _im, Size(3, 3), 0, 0, BORDER_DEFAULT); //Blur the image to remove unnecessary details.
GaussianBlur(_im, _im, Size(5, 5), 0, 0, BORDER_DEFAULT);
GaussianBlur(_im, _im, Size(7, 7), 0, 0, BORDER_DEFAULT);
// Calculate image derivatives
Mat dx2, dy2, dxy;
Sobel(_im, dx2, CV_32F, 2, 0, 3, 1, 0, BORDER_DEFAULT);
Sobel(_im, dy2, CV_32F, 0, 2, 3, 1, 0, BORDER_DEFAULT);
Sobel(_im, dxy, CV_32F, 1, 1, 3, 1, 0, BORDER_DEFAULT);
Mat t(2, 2, CV_32F); // tensor matrix
// Insert values to the tensor matrix.
t.at<float>(0, 0) = sum(dx2)[0];
t.at<float>(0, 1) = sum(dxy)[0];
t.at<float>(1, 0) = sum(dxy)[0];
t.at<float>(1, 1) = sum(dy2)[0];
// eigen decomposition to get the main gradient direction.
Mat eigVal, eigVec;
eigen(t, eigVal, eigVec);
// This should compute the angle of the gradient direction based on the first eigenvector.
float* eVec1 = eigVec.ptr<float>(0);
float* eVec2 = eigVec.ptr<float>(1);
cout << fastAtan2(eVec1[0], eVec1[1]) << endl;
cout << fastAtan2(eVec2[0], eVec2[1]) << endl;
这种方法正确吗?
应用程序使用此图像输出 44.9905、135.01。
这给出 0, 90.
当我使用真实图像的一部分时,我得到 342.743、72.7425,我觉得这很奇怪。我希望沿着颜色变化(90ish)获得一个角度。
经过测试我不确定我的实现是否正确,所以欢迎任何反馈或评论。
我认为您的问题是您计算的是二阶导数而不是一阶导数的平方。它应该是这样的:
// Calculate image derivatives
Mat dx, dy;
Mat dx2, dy2, dxy;
Sobel(_im, dx, CV_32F, 1, 0);
Sobel(_im, dy, CV_32F, 0, 1);
multiply(dx, dx, dx2);
multiply(dy, dy, dy2);
multiply(dx, dy, dxy);
P.S。
哦,对了,没必要一遍又一遍的做高斯模糊。只需使用更大的内核并模糊一次。
D.S.
我正在尝试实现一个使用图像在大型图像数据库中搜索相似图像的应用程序。我正在开发一个用于此搜索的图像描述符,我想将颜色信息与一些渐变信息结合起来。我已经看到在这个领域中使用结构张量来找到图像或子图像中的主要梯度方向。
我想拍摄一张图像,将其分成子图像的网格,例如4x4网格(总共16个子图像),然后找到每个单元格的领先梯度方向。为了找到领先的梯度方向,我想看看计算每个细胞的结构张量是否可以很好地表示图像梯度并改善图像匹配。这是好主意还是坏主意?思路是得到一个类似于本文3.2节思路的特征向量http://cybertron.cg.tu-berlin.de/eitz/pdf/2009_sbim.pdf
将图像分成子图像(单元格)很简单,使用 opencv 我可以使用 Sobel 函数计算偏导数。
Mat dx, dy;
Sobel(im, dx, CV_32F, 1, 0, 3, 1, 0, BORDER_DEFAULT);
Sobel(im, dy, CV_32F, 0, 1, 3, 1, 0, BORDER_DEFAULT);
计算 dx^2、dy^2 和 dxy 应该不是问题,但我不确定如何计算结构张量矩阵并使用张量矩阵找到图像或子图像的主梯度方向-图片。我怎样才能用 OpenCV 实现这个?
编辑 好的,这就是我所做的。
Mat _im; // Image to compute main gradient direction for.
cvtColor(im, _im, CV_BGR2GRAY);
GaussianBlur(_im, _im, Size(3, 3), 0, 0, BORDER_DEFAULT); //Blur the image to remove unnecessary details.
GaussianBlur(_im, _im, Size(5, 5), 0, 0, BORDER_DEFAULT);
GaussianBlur(_im, _im, Size(7, 7), 0, 0, BORDER_DEFAULT);
// Calculate image derivatives
Mat dx2, dy2, dxy;
Sobel(_im, dx2, CV_32F, 2, 0, 3, 1, 0, BORDER_DEFAULT);
Sobel(_im, dy2, CV_32F, 0, 2, 3, 1, 0, BORDER_DEFAULT);
Sobel(_im, dxy, CV_32F, 1, 1, 3, 1, 0, BORDER_DEFAULT);
Mat t(2, 2, CV_32F); // tensor matrix
// Insert values to the tensor matrix.
t.at<float>(0, 0) = sum(dx2)[0];
t.at<float>(0, 1) = sum(dxy)[0];
t.at<float>(1, 0) = sum(dxy)[0];
t.at<float>(1, 1) = sum(dy2)[0];
// eigen decomposition to get the main gradient direction.
Mat eigVal, eigVec;
eigen(t, eigVal, eigVec);
// This should compute the angle of the gradient direction based on the first eigenvector.
float* eVec1 = eigVec.ptr<float>(0);
float* eVec2 = eigVec.ptr<float>(1);
cout << fastAtan2(eVec1[0], eVec1[1]) << endl;
cout << fastAtan2(eVec2[0], eVec2[1]) << endl;
这种方法正确吗?
应用程序使用此图像输出 44.9905、135.01。
经过测试我不确定我的实现是否正确,所以欢迎任何反馈或评论。
我认为您的问题是您计算的是二阶导数而不是一阶导数的平方。它应该是这样的:
// Calculate image derivatives
Mat dx, dy;
Mat dx2, dy2, dxy;
Sobel(_im, dx, CV_32F, 1, 0);
Sobel(_im, dy, CV_32F, 0, 1);
multiply(dx, dx, dx2);
multiply(dy, dy, dy2);
multiply(dx, dy, dxy);
P.S。 哦,对了,没必要一遍又一遍的做高斯模糊。只需使用更大的内核并模糊一次。 D.S.