OpenCV: fast matrix computation - opencv

I have an nxd matrix V=[v_1; v_2;...; v_n] (; means new row) where v_i are 1xd vectors.
I want to compute the following sum: v_1^T*v_1 + v_2^T*v_2 + ... + v_n^T*v_n, which is a dxd matrix (v_i^T is the transpose of v_i).
For the moment I use a for loop, as in the code below, which is not efficient when n is very large (I think so).
#include <iostream>
#include <opencv2/core.hpp>
using namespace cv;
using namespace std;
int main (int argc, char * argv[])
{
int n=5, d=3;
Mat V = Mat(n, d, CV_32F);
randu(V, Scalar::all(0), Scalar::all(10));
cout<<V<<endl<<endl;
Mat M = Mat::zeros(d, d, CV_32F);
for(int i=0; i<n; i++)
{
M = M + V.row(i).t()*V.row(i);
}
cout<<M<<endl<<endl;
return 0;
}
Hope that somebody can suggest a faster way. Thanks in advance.

You can just take V.t()*V
(It took me a minute to realize it too, but if you go through the matrix multiplication you'll see it's the same)

Related

OpenCV convexityDefects drawing

Hi. I have the above image and use the "findContours" function.
And then I use the "convexity defects" functions to find the corner points.
The result is as follows.
The problem with this code is that it can not find the rounded corners.You can not find a point like the following.
This is my code
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <iostream>
#include <sstream>
#include <fstream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
cv::Mat image = cv::imread("find_Contours.png");
//Prepare the image for findContours
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::threshold(image, image, 128, 255, CV_THRESH_BINARY);
//Find the contours. Use the contourOutput Mat so the original image doesn't get overwritten
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = image.clone();
cv::findContours(contourOutput, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
////convexityDefects
vector<vector<Point> >hull(contours.size());
vector<vector<int> > hullsI(contours.size()); // Indices to contour points
vector<vector<Vec4i>> defects(contours.size());
for (int i = 0; i < contours.size(); i++)
{
convexHull(contours[i], hull[i], false);
convexHull(contours[i], hullsI[i], false);
if (hullsI[i].size() > 3) // You need more than 3 indices
{
convexityDefects(contours[i], hullsI[i], defects[i]);
}
}
///// Draw convexityDefects
for (int i = 0; i < contours.size(); ++i)
{
for (const Vec4i& v : defects[i])
{
float depth = v[3]/256;
if (depth >= 0) // filter defects by depth, e.g more than 10
{
int startidx = v[0]; Point ptStart(contours[i][startidx]);
int endidx = v[1]; Point ptEnd(contours[i][endidx]);
int faridx = v[2]; Point ptFar(contours[i][faridx]);
circle(image, ptFar, 4, Scalar(255, 255, 255), 2);
cout << ptFar << endl;
}
}
}
//
cv::imshow("Input Image", image);
cvMoveWindow("Input Image", 0, 0);
//
waitKey(0);
}
Can someone make the code and find the red dot? please help.
now i want find "convexity defects" from inside,not outside like this image:
Someone can help me??
It is very important to use
convexHull(contours[i], hullsI[i], true);
That is, with the last argument "true" for indices. I'm almost certain this is the reason it cannot find all the defects. Before fixing this, it is not much sense try to find other bugs (if any).

How do I set all odd rows of a cv::Mat to a scalar value?

I would like to set all odd rows of an nxm cv::Mat to a scalar value. There are brute force approaches to this problem, but I would like to know if there is something more elegant.
Extending from this problem, I would like to set all even rows of a different channel to a scalar value.
There are no OpenCV built-in functions that do this, but this can be done easily in 3 lines of code.
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
// The new value for the rows
uchar some_value = 100;
// Your matrix (here random initialized)
Mat1b mat(5, 3, uchar(0));
randu(mat, 0, 10);
// Set all even rows to some_value
for (int r = 0; r < mat.rows; r += 2) {
for (int c = 0; c < mat.cols; ++c) {
mat(r, c) = some_value;
}
}
return 0;
}
Yes, this is probably you called "brute force", but this is the method with fewer accesses to the matrix.
It's also very fast, you can eventually implement it with pointers to be even faster (here an example with 3 channels):
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Vec3b some_value(100, 101, 102);
Mat3b mat(5, 3, Vec3b(0,0,0));
randu(mat, Scalar(0, 0, 0), Scalar(10, 10, 10));
for (int r = 0; r < mat.rows; r += 2) {
Vec3b* ptr = mat.ptr<Vec3b>(r);
for (int c = 0; c < mat.cols; ++c) {
ptr[c] = some_value;
}
}
return 0;
}
You can also create a mask with odd rows white (255), and even rows black (0), and use cv::setTo to set values according to the mask. This however is probably much slower, because you need to i) create the mask, and ii) access each pixel in the matrix (probably exploiting optimized code, though).
Not sure of speed but I guess OP referred to elegance that of MATLAB matrix operations, and sure some of them have been imported to OpenCV, e.g.
cv::Mat m(8,15,CV_32FC1,cv::Scalar(0));
for (int i=0;i<m.rows;i+=2)
m.row(i).setTo( 32 );
Likewise, you can construct a cv::Mat header for each column separately using cv::Mat::col(int i) function.

OpenCV and DAISY descriptors

I am trying to do feature matching between 2 perspectives of the same image using DAISY and the FlannBasedMatcher.
I don't think there is even a single match that is correct.
Note: I also get different results each time I run the program but I think this is expected behaviour as explained here: FlannBasedMatcher returning different results
So what am I doing wrong? Why are these matches so bad?
Input Images
Wrong & non-deterministic results
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace cv;
using std::vector;
const float nn_match_ratio = 0.7f; // Nearest neighbor matching ratio
const float keypoint_diameter = 15.0f;
int main(int argc, char ** argv){
// Load images
Mat img1 = imread(argv[1]);
Mat img2 = imread(argv[2]);
vector<KeyPoint> keypoints1, keypoints2;
// Add every pixel to the list of keypoints for each image
for (float xx = keypoint_diameter; xx < img1.size().width - keypoint_diameter; xx++) {
for (float yy = keypoint_diameter; yy < img1.size().height - keypoint_diameter; yy++) {
keypoints1.push_back(KeyPoint(xx, yy, keypoint_diameter));
keypoints2.push_back(KeyPoint(xx, yy, keypoint_diameter));
}
}
Mat desc1, desc2;
Ptr<cv::xfeatures2d::DAISY> descriptor_extractor = cv::xfeatures2d::DAISY::create();
// Compute DAISY descriptors for both images
descriptor_extractor->compute(img1, keypoints1, desc1);
descriptor_extractor->compute(img2, keypoints2, desc2);
vector <vector<DMatch>> matches;
// For each descriptor in image1, find 2 closest matched in image2 (note: couldn't get BF matcher to work here at all)
FlannBasedMatcher flannmatcher;
flannmatcher.add(desc1);
flannmatcher.train();
flannmatcher.knnMatch(desc2, matches, 2);
// ignore matches with high ambiguity -- i.e. second closest match not much worse than first
// push all remaining matches back into DMatch Vector "good_matches" so we can draw them using DrawMatches
int num_good = 0;
vector<KeyPoint> matched1, matched2;
vector<DMatch> good_matches;
for (int i = 0; i < matches.size(); i++) {
DMatch first = matches[i][0];
DMatch second = matches[i][1];
if (first.distance < nn_match_ratio * second.distance) {
matched1.push_back(keypoints1[first.queryIdx]);
matched2.push_back(keypoints2[first.trainIdx]);
good_matches.push_back(DMatch(num_good, num_good, 0));
num_good++;
}
}
Mat res;
drawMatches(img1, matched1, img2, matched2, good_matches, res);
imwrite("_res.png", res);
return 0;
}
Sorry. I found my bug. I have the Indexes reversed in the lines that read:
matched1.push_back(keypoints1[first.queryIdx]);
matched2.push_back(keypoints2[first.trainIdx]);
how can i get the coordinates of the matches find in the two images,that is the coordinates of the matches in the first image and the coordinates of the matches in the second?

split a BGR matrix without use split() function

I am programming with Visual Studio 2012 and the Opencv library, in the 2.4.6 version.
Someone can help me about splitting a BGR image into three images, one for every channel?
I know that there is the split function in OpenCV, but it causes me an unhandled exception, probably because I have a 64 bit processor with the 32 bit library, or probably it's the version of the library, so I want to know how to iterate on the pixel values of a BGR matrix without use split().
Thanks in advance.
If you don't want to use split() then you can read each r,g,b pixel value from your source image and write to destination image and which should be single channel.
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main( int argc, const char** argv ){
Mat src = imread("ball.jpg", 1);
Mat r(src.rows,src.cols,CV_8UC1);
Mat g(src.rows,src.cols,CV_8UC1);
Mat b(src.rows,src.cols,CV_8UC1);
for(int i=0;i<src.rows;i++){
for(int j=0;j<src.cols;j++){
Vec3b pixel = src.at<Vec3b>(i, j);
b.at<uchar>(i,j) = pixel[0];
g.at<uchar>(i,j) = pixel[1];
r.at<uchar>(i,j) = pixel[2];
}
}
imshow("src", src);
imshow("r", r);
imshow("g", g);
imshow("b", b);
waitKey(0);
}

Input matrix to opencv kmeans clustering

This question is specific to opencv:
The kmeans example given in the opencv documentation has a 2-channel matrix - one channel for each dimension of the feature vector. But, some of the other example seem to say that it should be a one channel matrix with features along the columns with one row for each sample. Which of these is right?
if I have a 5 dimensional feature vector, what should be the input matrix that I use:
This one:
cv::Mat inputSamples(numSamples, 1, CV32FC(numFeatures))
or this one:
cv::Mat inputSamples(numSamples, numFeatures, CV_32F)
The correct answer is cv::Mat inputSamples(numSamples, numFeatures, CV_32F).
The OpenCV Documentation about kmeans says:
samples – Floating-point matrix of input samples, one row per sample
So it is not a Floating-point vector of n-Dimensional floats as in the other option. Which examples suggested such a behaviour?
Here is also a small example by me that shows how kmeans can be used. It clusters the pixels of an image and displays the result:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
int main( int argc, char** argv )
{
Mat src = imread( argv[1], 1 );
Mat samples(src.rows * src.cols, 3, CV_32F);
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
for( int z = 0; z < 3; z++)
samples.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z];
int clusterCount = 15;
Mat labels;
int attempts = 5;
Mat centers;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers );
Mat new_image( src.size(), src.type() );
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
{
int cluster_idx = labels.at<int>(y + x*src.rows,0);
new_image.at<Vec3b>(y,x)[0] = centers.at<float>(cluster_idx, 0);
new_image.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1);
new_image.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2);
}
imshow( "clustered image", new_image );
waitKey( 0 );
}
As alternative to reshaping the input matrix manually, you can use OpenCV reshape function to achieve similar result with less code. Here is my working implementation of reducing colors count with K-Means method (in Java):
private final static int MAX_ITER = 10;
private final static int CLUSTERS = 16;
public static Mat colorMapKMeans(Mat img, int K, int maxIterations) {
Mat m = img.reshape(1, img.rows() * img.cols());
m.convertTo(m, CvType.CV_32F);
Mat bestLabels = new Mat(m.rows(), 1, CvType.CV_8U);
Mat centroids = new Mat(K, 1, CvType.CV_32F);
Core.kmeans(m, K, bestLabels,
new TermCriteria(TermCriteria.COUNT | TermCriteria.EPS, maxIterations, 1E-5),
1, Core.KMEANS_RANDOM_CENTERS, centroids);
List<Integer> idx = new ArrayList<>(m.rows());
Converters.Mat_to_vector_int(bestLabels, idx);
Mat imgMapped = new Mat(m.size(), m.type());
for(int i = 0; i < idx.size(); i++) {
Mat row = imgMapped.row(i);
centroids.row(idx.get(i)).copyTo(row);
}
return imgMapped.reshape(3, img.rows());
}
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Highgui.imwrite("result.png",
colorMapKMeans(Highgui.imread(args[0], Highgui.CV_LOAD_IMAGE_COLOR),
CLUSTERS, MAX_ITER));
}
OpenCV reads image into 2 dimensional, 3 channel matrix. First call to reshape - img.reshape(1, img.rows() * img.cols()); - essentially unrolls 3 channels into columns. In resulting matrix one row corresponds to one pixel of the input image, and 3 columns corresponds to RGB components.
After K-Means algorithm finished its work, and color mapping has been applied, we call reshape again - imgMapped.reshape(3, img.rows()), but now rolling columns back into channels, and reducing row numbers to the original image row number, thus getting back the original matrix format, but only with reduced colors.

Resources