I am having a problem to translate numpy's ndarray functions to their equivalent OpenCV C++ calls to reshape/split a n-dimensional cv::Mat into appropriate slices. In particular i am trying to convert the OpenCV python2 sample "texture_flow.py" (>= OpenCV 2.4.3) to C++. I've marked the lines in question in the snippet below.
# [......]
img = cv2.imread(fn)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# simple width and height tuple
h, w = img.shape[:2]
eigen = cv2.cornerEigenValsAndVecs(gray, 15, 3)
print eigen.shape # prints: (height, widht, 6), i.e. 6 channels
# Problem 1:
# OpenCV's reshape function is not sufficient to do this.
# probably must be split into several steps...
eigen = eigen.reshape(h, w, 3, 2) # [[e1, e2], v1, v2]
print eigen.shape # prints: (height, width, 3, 2)
# Problem 2:
# I assume this is meant to get the the v1 and v2 matrices
# from the previous reshape
flow = eigen[:,:,2]
print flow.shape # prints: (height, width, 2), i.e. 2 channels
vis = img.copy()
# C++: vis.data[i] = (uchar)((192 + (int)vis.data[i]) / 2);
vis[:] = (192 + np.uint32(vis)) / 2
d = 12
# Problem 3:
# Can probably be split into 2 nested for-loops
points = np.dstack( np.mgrid[d/2:w:d, d/2:h:d] ).reshape(-1, 2)
# [......]
Can someone help me to translate the lines in question to C++?
After thoroughly thinking about it it was all easier than expected. It's only the funny numpy array syntax that was confusing me. Reshaping of numpy's arrays is just the python way to access the single channels of the resulting cv::Mat "eigen". The following code is the C++ version of OpenCV's "texture_flow.py" (taken from OpenCV 2.4.3). The resulting flow image is not 100% identical to the python version, but it is close enough.
#include <opencv2/opencv.hpp>
#include <iostream>
int main (int argc, char** argv)
{
cv::TickMeter tm;
tm.start();
cv::Mat img = cv::imread(argv[1]);
cv::Mat gray = cv::Mat();
cv::cvtColor(img, gray, CV_BGR2GRAY);
// to preserve the original image
cv::Mat flow = gray.clone();
int width = img.cols;
int height = img.rows;
int graySize = width * height;
// "brighten" the flow image
// C++ version of:
// vis[:] = (192 + np.uint32(vis)) / 2
for (unsigned int i=0; i<graySize; ++i)
{
flow.data[i] = (uchar)((192 + (int)flow.data[i]) / 2);
}
cv::Mat eigen = cv::Mat(height, width, CV_32FC(6));
cv::cornerEigenValsAndVecs(gray, eigen, 15, 3);
// this is the equivalent to all the numpy's reshaping etc. to
// generate the flow arrays
// simply use channel 4 and 5 as the actual flow array in C++
std::vector<cv::Mat> channels;
cv::split(eigen, channels);
int d = 12;
cv::Scalar col(0, 0, 0);
// C++ version of:
// points = np.dstack( np.mgrid[d/2:w:d, d/2:h:d] ).reshape(-1, 2)
// including the actual line drawing part
for (unsigned int y=(d/2); y<flow.rows; y+=d)
{
for (unsigned int x=(d/2); x<flow.cols; x+=d)
{
if (x < flow.cols && y < flow.rows)
{
cv::Point p(x, y);
float dx = channels[4].at<float>(p) * (d/2);
float dy = channels[5].at<float>(p) * (d/2);
cv::Point p0(p.x - dx, p.y - dy);
cv::Point p1(p.x + dx, p.y + dy);
cv::line(flow, p0, p1, col, 1);
}
}
}
tm.stop();
std::cout<<"Flow image generated in "<<tm.getTimeMilli()<<" ms."<<std::endl;
cv::imshow("FLOW", flow);
cv::waitKey();
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With