如何使用 OpenCV 在 C++ 中实现高效的 im2col 函数?
How to implement an efficient im2col function in C++ using OpenCV?
我一直在尝试实现 MATLAB 和 GNU Octave 中的 im2col
函数。我发现很难理解 Octave 源代码中的实现,所以我在几个矩阵上运行该函数以了解其背后的逻辑。使用它,我已经使用 OpenCV 在 C++ 中实现了相同的功能,尽管结果似乎相同,但速度非常慢。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat input = Mat::eye(100,100,CV_32FC1);
input.at<float>(1,2) = 2; //Makes it easier to verify the correct solution
int rowBlock = 7;
int colBlock = 5;
int m = input.rows;
int n = input.cols;
int x = m - rowBlock + 1;
int y = n - colBlock + 1;
Mat result = Mat::zeros(1,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< y; i++)
{
for (int j = 0; j< x; j++)
{
Mat temp2 = input.rowRange(j,j+rowBlock).colRange(i,i+colBlock).t();
temp2 = temp2.reshape(1,1);
vconcat(result,temp2,result);
}
}
result = result.rowRange(1,result.rows);
cout << result << endl;
return 0;
}
有什么改进的方法吗?我确定我在这里可能会非常低效地做很多事情。
这个对我来说要快得多:
int main()
{
cv::Mat input = cv::Mat::eye(100,100,CV_32FC1);
input.at<float>(1,2) = 2; //Makes it easier to verify the correct solution
int rowBlock = 7;
int colBlock = 5;
int m = input.rows;
int n = input.cols;
// using right x = col; y = row
int yB = m - rowBlock + 1;
int xB = n - colBlock + 1;
// you know the size of the result in the beginning, so allocate it all at once
cv::Mat result2 = cv::Mat::zeros(xB*yB,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< yB; i++)
{
for (int j = 0; j< xB; j++)
{
// here yours is in different order than I first thought:
//int rowIdx = j + i*xB; // my intuition how to index the result
int rowIdx = i + j*yB;
for(unsigned int yy =0; yy < rowBlock; ++yy)
for(unsigned int xx=0; xx < colBlock; ++xx)
{
// here take care of the transpose in the original method
//int colIdx = xx + yy*colBlock; // this would be not transposed
int colIdx = xx*rowBlock + yy;
result2.at<float>(rowIdx,colIdx) = input.at<float>(i+yy, j+xx);
}
}
}
// check your output here...
}
我将它添加到您的代码中,以测试是否相等(不过,最好为每个函数编写一个函数并进行封装;))
int main()
{
cv::Mat input = cv::Mat::eye(100,100,CV_32FC1);
input.at<float>(1,2) = 2; //Makes it easier to verify the correct solution
int rowBlock = 7;
int colBlock = 5;
int m = input.rows;
int n = input.cols;
// here, your naming of x and y is counter intuitive for me, since I see x being linked to cols normally (e.g. direction of x-axis)
int x = m - rowBlock + 1;
int y = n - colBlock + 1;
cv::Mat result = cv::Mat::zeros(1,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< y; i++)
{
for (int j = 0; j< x; j++)
{
cv::Mat temp2 = input.rowRange(j,j+rowBlock).colRange(i,i+colBlock).t();
temp2 = temp2.reshape(1,1);
cv::vconcat(result,temp2,result);
}
}
result = result.rowRange(1,result.rows);
std::cout << result.rows << " x " << result.cols << std::endl;
char w;
std::cin >> w;
// using right x = col; y = row
int yB = m - rowBlock + 1;
int xB = n - colBlock + 1;
// you know the size of the result in the beginning, so allocate it all at once
cv::Mat result2 = cv::Mat::zeros(x*y,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< yB; i++)
{
for (int j = 0; j< xB; j++)
{
// here yours is in different order than I first thought:
//int rowIdx = j + i*xB; // my intuition how to index the result
int rowIdx = i + j*yB;
for(unsigned int yy =0; yy < rowBlock; ++yy)
for(unsigned int xx=0; xx < colBlock; ++xx)
{
// here take care of the transpose in the original method
//int colIdx = xx + yy*colBlock; // this would be not transposed
int colIdx = xx*rowBlock + yy;
result2.at<float>(rowIdx,colIdx) = input.at<float>(i+yy, j+xx);
}
}
}
std::cout << result2.rows << " x " << result2.cols << std::endl;
std::cin >> w;
// test whether both results are the same:
bool allGood = true;
for(int j=0; j<result.rows; ++j)
for(int i=0; i<result.cols; ++i)
{
if(result.at<float>(j,i) != result2.at<float>(j,i))
{
std::cout << "("<<j<<","<<i<<") = " << result.at<float>(j,i) << " != " << result2.at<float>(j,i) << std::endl;
allGood = false;
}
}
if(allGood) std::cout << "matrices are equal" << std::endl;
std::cin >> w;
return 0;
}
我一直在尝试实现 MATLAB 和 GNU Octave 中的 im2col
函数。我发现很难理解 Octave 源代码中的实现,所以我在几个矩阵上运行该函数以了解其背后的逻辑。使用它,我已经使用 OpenCV 在 C++ 中实现了相同的功能,尽管结果似乎相同,但速度非常慢。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat input = Mat::eye(100,100,CV_32FC1);
input.at<float>(1,2) = 2; //Makes it easier to verify the correct solution
int rowBlock = 7;
int colBlock = 5;
int m = input.rows;
int n = input.cols;
int x = m - rowBlock + 1;
int y = n - colBlock + 1;
Mat result = Mat::zeros(1,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< y; i++)
{
for (int j = 0; j< x; j++)
{
Mat temp2 = input.rowRange(j,j+rowBlock).colRange(i,i+colBlock).t();
temp2 = temp2.reshape(1,1);
vconcat(result,temp2,result);
}
}
result = result.rowRange(1,result.rows);
cout << result << endl;
return 0;
}
有什么改进的方法吗?我确定我在这里可能会非常低效地做很多事情。
这个对我来说要快得多:
int main()
{
cv::Mat input = cv::Mat::eye(100,100,CV_32FC1);
input.at<float>(1,2) = 2; //Makes it easier to verify the correct solution
int rowBlock = 7;
int colBlock = 5;
int m = input.rows;
int n = input.cols;
// using right x = col; y = row
int yB = m - rowBlock + 1;
int xB = n - colBlock + 1;
// you know the size of the result in the beginning, so allocate it all at once
cv::Mat result2 = cv::Mat::zeros(xB*yB,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< yB; i++)
{
for (int j = 0; j< xB; j++)
{
// here yours is in different order than I first thought:
//int rowIdx = j + i*xB; // my intuition how to index the result
int rowIdx = i + j*yB;
for(unsigned int yy =0; yy < rowBlock; ++yy)
for(unsigned int xx=0; xx < colBlock; ++xx)
{
// here take care of the transpose in the original method
//int colIdx = xx + yy*colBlock; // this would be not transposed
int colIdx = xx*rowBlock + yy;
result2.at<float>(rowIdx,colIdx) = input.at<float>(i+yy, j+xx);
}
}
}
// check your output here...
}
我将它添加到您的代码中,以测试是否相等(不过,最好为每个函数编写一个函数并进行封装;))
int main()
{
cv::Mat input = cv::Mat::eye(100,100,CV_32FC1);
input.at<float>(1,2) = 2; //Makes it easier to verify the correct solution
int rowBlock = 7;
int colBlock = 5;
int m = input.rows;
int n = input.cols;
// here, your naming of x and y is counter intuitive for me, since I see x being linked to cols normally (e.g. direction of x-axis)
int x = m - rowBlock + 1;
int y = n - colBlock + 1;
cv::Mat result = cv::Mat::zeros(1,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< y; i++)
{
for (int j = 0; j< x; j++)
{
cv::Mat temp2 = input.rowRange(j,j+rowBlock).colRange(i,i+colBlock).t();
temp2 = temp2.reshape(1,1);
cv::vconcat(result,temp2,result);
}
}
result = result.rowRange(1,result.rows);
std::cout << result.rows << " x " << result.cols << std::endl;
char w;
std::cin >> w;
// using right x = col; y = row
int yB = m - rowBlock + 1;
int xB = n - colBlock + 1;
// you know the size of the result in the beginning, so allocate it all at once
cv::Mat result2 = cv::Mat::zeros(x*y,rowBlock*colBlock,CV_32FC1);
for(int i = 0; i< yB; i++)
{
for (int j = 0; j< xB; j++)
{
// here yours is in different order than I first thought:
//int rowIdx = j + i*xB; // my intuition how to index the result
int rowIdx = i + j*yB;
for(unsigned int yy =0; yy < rowBlock; ++yy)
for(unsigned int xx=0; xx < colBlock; ++xx)
{
// here take care of the transpose in the original method
//int colIdx = xx + yy*colBlock; // this would be not transposed
int colIdx = xx*rowBlock + yy;
result2.at<float>(rowIdx,colIdx) = input.at<float>(i+yy, j+xx);
}
}
}
std::cout << result2.rows << " x " << result2.cols << std::endl;
std::cin >> w;
// test whether both results are the same:
bool allGood = true;
for(int j=0; j<result.rows; ++j)
for(int i=0; i<result.cols; ++i)
{
if(result.at<float>(j,i) != result2.at<float>(j,i))
{
std::cout << "("<<j<<","<<i<<") = " << result.at<float>(j,i) << " != " << result2.at<float>(j,i) << std::endl;
allGood = false;
}
}
if(allGood) std::cout << "matrices are equal" << std::endl;
std::cin >> w;
return 0;
}