opencv FLANN radiusSearch give bad results - c++

I'd like to do radius search to find all valid neighbors, but it seems to give me wrong results. Here is my code
#include "opencv/cv.hpp"
#include <iostream>
#include <vector>
int main () {
// create a group of points
std::vector<cv::Point2f> points;
points.emplace_back(438.6, 268.8);
points.emplace_back(439.1, 268.6);
points.emplace_back(438.2, 268.1);
points.emplace_back(498.3, 285.9);
points.emplace_back(312.9, 245.9);
points.emplace_back(313.4, 245.7);
points.emplace_back(313.1, 245.5);
points.emplace_back(312.5, 245.4);
points.emplace_back(297.6, 388.1);
points.emplace_back(291.7, 309.8);
points.emplace_back(194.1, 369.8);
points.emplace_back(439.9, 314.9);
points.emplace_back(312.8, 246.0);
// create features array
cv::Mat_<float> features(0, 2);
for (auto && point : points) {
//Fill matrix
cv::Mat row = (cv::Mat_<float>(1, 2) << point.x, point.y);
features.push_back(row);
}
std::cout << features << std::endl;
cv::flann::Index flann_index(features, cv::flann::KDTreeIndexParams());
std::vector<float> query{ 300.6f, 268.8f };
std::vector<int> ind;
std::vector<float> d;
unsigned int max_neighbours = 10;
// Here I deliberately increase the radius to contain all the points
double radius = 500.0;
flann_index.radiusSearch(query, ind, d, radius, max_neighbours,
cv::flann::SearchParams());
}
Output of ind is [0,0,0,0,0,0,0,0,0,0], all zeros, which is unexpected.
Anyone knows why?
=-=-=-=-=-=-=-=-=-=-= Update
int main() {
// create a group of points
std::vector<cv::Point2f> points;
points.emplace_back(438.6, 268.8);
points.emplace_back(439.1, 268.6);
points.emplace_back(438.2, 268.1);
points.emplace_back(498.3, 285.9);
points.emplace_back(312.9, 245.9);
points.emplace_back(313.4, 245.7);
points.emplace_back(313.1, 245.5);
points.emplace_back(312.5, 245.4);
points.emplace_back(297.6, 388.1);
points.emplace_back(291.7, 309.8);
points.emplace_back(194.1, 369.8);
points.emplace_back(439.9, 314.9);
points.emplace_back(312.8, 246.0);
// create features array
cv::Mat_<float> features(0, 2);
for (auto && point : points) {
//Fill matrix
cv::Mat row = (cv::Mat_<float>(1, 2) << point.x, point.y);
features.push_back(row);
}
std::cout << features << std::endl;
cv::flann::GenericIndex<cvflann::L2<float> > index(features, cvflann::KDTreeIndexParams());
std::vector<float> query(438.6f, 268.8f);
std::vector<int> ind;
std::vector<float> d;
index.radiusSearch(query, ind, d, 45.f, cvflann::SearchParams());
// I can print std::vector by some method, the reader may not, so I comment this line
//std::cout << d << std::endl;
}
As cv::flann::Index is deprecated, I change to new API, but this time, the program just doesn't work anymore.

If you check the example of the plain FLANN I had used here, you would see that they call buildIndex(), which you don't. Could that be?
Try:
cv::flann::Index flann_index(features, cv::flann::KDTreeIndexParams());
flann_index.buildIndex();

You have to set the size of ind and d.

I encountered this issue, and the solution is that radius must be specified as radius squared (^2). And the length of ind and d will be set by max_neighbors, but the return of radiusSearch must be checked to find out which is less, num_found or max_neighbours;
double radius = 500.0;
int num_found = flann_index.radiusSearch(query, ind, d, radius*radius, max_neighbours, cv::flann::SearchParams());
num_found = min(num_found,(int)ind.size()); // check correct size
for(int i=0; i<num_found; i++) { ... ind[i] ... d[i] ... }
in my case, i also had to adjust the index and search parameters to return correct results:
flann::KDTreeIndexParams indexParams(128);
flann::SearchParams searchParams(1024,0.0,true);

Related

How to get the rows based on condition in eigen?

I need to get the certain rows, when a element is a vector is one.
For an example:
std::vector<bool>index{}; //contains 6000 numbers of elements 0 and 1
Now I have a matrix mat of shape (6000,4)
How can I get the rows in a matrix mat, when the corresponding element is 1 in vector index.
mat = mat[index];
If I understand your question clearly, you may find good answer from this good reply:
Eigen3 select rows out based on column conditions
Using new feature (Eigen 3.4 or 3.3.90 development branch) and take the core code from the previous link:
#include <Eigen/Dense>
#include <iostream>
#include <vector>
using namespace Eigen;
int main() {
MatrixXd mat = MatrixXd::Random(10,5);
std::cout << "original:\n" << mat << std::endl;
std::vector<int> keep_rows;
for (int i = 0; i < mat.rows(); ++i) {
if (mat(i,mat.cols() - 1) > 0.3) {
keep_rows.push_back(i);
}
}
VectorXi keep_cols = VectorXi::LinSpaced(mat.cols(), 0,mat.cols());
MatrixXd mat_sel = mat(keep_rows, keep_cols);
std::cout << "selected:\n" << mat_sel << std::endl;
}
It uses the similar style of the Matlab:
MatrixXd mat_sel = mat(keep_rows, keep_cols);
But the columns and rows that should be kept are stored in an
Eigen::VectorXi
or in a
std::vector<int>

Issue with code for NxN determinant function (C++)

I'm trying to write a matrix inverse calculator (been doing stuff to do with matrices for my maths module in uni so I figured it would be a good way to get practice with recursive functions).
At the moment I'm working on functions for working out the determinant of functions, one for 2x2, one for 3x3 which calls the 2x2 one (recursive formula for determinants I'm sure you know the drill).
Then a third function takes a matrix as input initially checks if it's 2x2 or 3x3, if so sends it to the appropriate prior mentioned function. Next we eliminate rows and columns recursively following the determinant formula until we end up with a value for the determinant.
This code works up to 4x4 matrices, however any matrix larger than this results in the wrong answer.
I'm on my first year at uni and reletively new to programming, this being my first attempt with recursive functions, any advice would be appreciated. My lecturer for maths suggested maybe using cramers rule instead, but it would be interesting to see if I can get this method working.
Appologies if my formatting isn't the best, stuck on old laptop at the moment.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
double MatrixDet2By2(vector<vector<double>> matrix);
double MatrixDet3By3(vector<vector<double>> matrix);
double MatrixDet(vector<vector<double>> matrix);
//vector<vector<double>> CalcMinorMatrix(vector<vector<double>> matrix);
//vector<vector<double>> CalcCofactorMatrix(vector<vector<double>> matrix);
int main(int argc, char** argv)
{
vector<vector<double>> testMatrix = {{1,4},{7,9}};
vector<vector<double>> testMatrix2 = { {5,3,7},{6,-1,0},{4,-11,-2} };
vector<vector<double>> testMatrix3 =
{
{5,3,7,6},
{6,-1,0,4},
{4,-11,-2,3},
{1,3,7,9},
};
vector<vector<double>> testMatrix4 =
{
{1,2,-1,6,1},
{6,-1,0,4,3},
{4,0,-2,3,2},
{1,3,7,2,3},
{-2,7,0,2,5},
};
//cout << MatrixDet2By2(testMatrix) << endl;
cout << MatrixDet(testMatrix4) << endl;
cout << endl;
return 0;
}
double MatrixDet2By2(vector<vector<double>> matrix)
{
return (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0]);
}
double MatrixDet3By3(vector<vector<double>> matrix)
{
vector<vector<double>> subMatrix1 = {
{matrix[1][1], matrix[1][2]},
{matrix[2][1], matrix[2][2]}
};
vector<vector<double>> subMatrix2 = {
{matrix[1][0], matrix[1][2]},
{matrix[2][0], matrix[2][2]}
};
vector<vector<double>> subMatrix3 = {
{matrix[1][0], matrix[1][1]},
{matrix[2][0], matrix[2][1]}
};
return ((matrix[0][0] * MatrixDet2By2(subMatrix1)) - (matrix[0][1] * MatrixDet2By2(subMatrix2)) + (matrix[0][2] * MatrixDet2By2(subMatrix3)));
}
/*
vector<vector<double>> CalcMinorMatrix(vector<vector<double>> matrix)
{
vector<vector<double>> subMatrix1 = {
{matrix[1][1], matrix[1][2]},
{matrix[2][1], matrix[2][2]}
};
vector<vector<double>> subMatrix2 = {
{matrix[1][0], matrix[1][2]},
{matrix[2][0], matrix[2][2]}
};
vector<vector<double>> subMatrix3 = {
{matrix[1][0], matrix[1][1]},
{matrix[2][0], matrix[2][1]}
};
vector<vector<double>> subMatrix4 = {
{matrix[0][1], matrix[0][2]},
{matrix[2][1], matrix[2][2]}
};
vector<vector<double>> subMatrix5 = {
{matrix[0][0], matrix[0][2]},
{matrix[2][0], matrix[2][2]}
};
vector<vector<double>> subMatrix6 = {
{matrix[0][0], matrix[0][1]},
{matrix[2][0], matrix[2][1]}
};
vector<vector<double>> subMatrix7 = {
{matrix[0][1], matrix[0][2]},
{matrix[1][1], matrix[1][2]}
};
vector<vector<double>> subMatrix8 = {
{matrix[0][0], matrix[0][2]},
{matrix[1][0], matrix[1][2]}
};
vector<vector<double>> subMatrix9 = {
{matrix[0][0], matrix[0][1]},
{matrix[1][0], matrix[1][1]}
};
vector<vector<double>> matrixOfMinors = {
{MatrixDet2By2(subMatrix1), MatrixDet2By2(subMatrix2), MatrixDet2By2(subMatrix3)},
{MatrixDet2By2(subMatrix4), MatrixDet2By2(subMatrix5), MatrixDet2By2(subMatrix6)},
{MatrixDet2By2(subMatrix7), MatrixDet2By2(subMatrix8), MatrixDet2By2(subMatrix9)},
};
return matrixOfMinors;
}
vector<vector<double>> CalcCofactorMatrix(vector<vector<double>> matrix)
{
return matrix;
}
*/
double MatrixDet(vector<vector<double>> matrix)
{
vector<vector<double>> tempMatrix{};
static double totalDeterminant = 0;
if (matrix.size() != matrix[0].size())
{
cout << "\r\nPlease enter a valid square matrix" << endl;
}
else if (matrix.size() == 2)
{
return MatrixDet2By2(matrix);
}
else if (matrix.size() == 3)
{
return MatrixDet3By3(matrix);
}
else
{
size_t pos = 0;
for (auto value : matrix[0])
{
tempMatrix = matrix;
tempMatrix.erase(tempMatrix.begin());
for (size_t i = 0; i < tempMatrix.size(); i++)
{
if (tempMatrix[i].size() > pos)
{
tempMatrix[i].erase(tempMatrix[i].begin() + pos);
}
}
cout << "\r\n---------" << endl;
for (auto vec : tempMatrix)
{
for (auto val : vec)
{
cout << val << " ";
}
cout << endl;
}
cout << "\r\n---------" << endl;
//totalDeterminant += MatrixDet(tempMatrix);
if ((pos + 1) % 2 == 0)
{
totalDeterminant += (-value * MatrixDet(tempMatrix));
}
else
{
totalDeterminant += (value * MatrixDet(tempMatrix));
}
pos++;
}
}
return totalDeterminant;
}
Since you define variable totalDeterminant in MatrixDet using the keyword static, there is only one totalDeterminant variable in your program, ever. And the = 0 initializer only applies the very first time the program gets there. So when computing the determinant of the very first 4x4 minor matrix, that goes fine. Then that result is multiplied by matrix[0][0] and added to totalDeterminant. The computation for the second 4x4 minor matrix starts with that strange value (1+matrix[0][0])*detMinor1 and begins adding to it.
In fact, if you just called MatrixDet on two 4x4 matrices in the same program, the second call would return the sum of the two determinants.
You need a separate sum for each main matrix and submatrix computation (since the result of a submatrix determinant needs to be multiplied by an element before being added to anything else). So totalDeterminant needs to NOT be static. When I remove the static from your program, it gives the correct final result of MatrixDet(testMatrix4) == -856.
Note that once the general case is correct, you could remove the code for the 3x3 and possibly even 2x2 case. Don't forget to support a 1x1 matrix: det [[x]] = x.
One error is in the following lines
for (size_t i = 0; i < tempMatrix.size(); i++)
{
if (tempMatrix[i].size() > pos)
{
tempMatrix[i].erase(tempMatrix[i].begin() + pos);
}
}
The check if (tempMatrix[i].size() > pos) is not necessary.
All you need to get the sub-matrix is to just exclude the pos-th column. You need to use:
// Remove the "pos" column of tempMatrix.
for (size_t i = 0; i < tempMatrix.size(); i++)
{
tempMatrix[i].erase(tempMatrix[i].begin() + pos);
}
The second error is the use of a static variable for totalDeterminant, as was pointed out by #aschepler. The line
static double totalDeterminant = 0;
needs to be simply
double totalDeterminant = 0;

OpenCV: can't get segmentation of image using k-means

Before going into deep of my question, I want you to know that I've read other posts on this forum, but none regards my problem.
In particular, the post here answers the question "how to do this?" with k-means, while I already know that I have to use it and I'd like to know why my implementation doesn't work.
I want to use k-means algorithm to divide pixels of an input image into clusters, according to their color. Then, after completing such task, I want each pixel to have the color of the center of the cluster it's been assigned to.
Taking as reference the OpenCV examples and other stuff retrieved on the web, I've designed the following code:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main( int argc, char** argv )
{
Mat src = imread( argv[1], 1 );
// reshape matrix
Mat resized(src.rows*src.cols, 3, CV_8U);
int row_counter = 0;
for(int i = 0; i<src.rows; i++)
{
for(int j = 0; j<src.cols; j++)
{
Vec3b channels = src.at<Vec3b>(i,j);
resized.at<char>(row_counter,0) = channels(0);
resized.at<char>(row_counter,1) = channels(1);
resized.at<char>(row_counter,2) = channels(2);
row_counter++;
}
}
//cout << src << endl;
// change data type
resized.convertTo(resized, CV_32F);
// determine termination criteria and number of clusters
TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, 10, 1.0);
int K = 8;
// apply k-means
Mat labels, centers;
double compactness = kmeans(resized, K, labels, criteria, 10, KMEANS_RANDOM_CENTERS, centers);
// change data type in centers
centers.convertTo(centers, CV_8U);
// create output matrix
Mat result = Mat::zeros(src.rows, src.cols, CV_8UC3);
row_counter = 0;
int matrix_row_counter = 0;
while(row_counter < result.rows)
{
for(int z = 0; z<result.cols; z++)
{
int index = labels.at<char>(row_counter+z, 0);
//cout << index << endl;
Vec3b center_channels(centers.at<char>(index,0),centers.at<char>(index,1), centers.at<char>(index,2));
result.at<Vec3b>(matrix_row_counter, z) = center_channels;
}
row_counter += result.cols;
matrix_row_counter++;
}
cout << "Labels " << labels.rows << " " << labels.cols << endl;
//cvtColor( src, gray, CV_BGR2GRAY );
//gray.convertTo(gray, CV_32F);
imshow("Result", result);
waitKey(0);
return 0;
}
Anyway, at the end of computation, I simply get a black image.
Do you know why?
Strangely, if I initialize result matrix as
Mat result(src.size(), src.type())
at the end of algorithm it will display exactly the input image, without any segmentation.
In particular, I have two doubts:
1) is it correct to lay the RGB values of a pixel on each row of matrix resized the way I've done it? is there a way to do it without a loop?
2) what's exactly the content of centers, after k-means function finishes working? it's a 3 columns matrix, does it contains the RGB values of clusters' centers?
thanks for support.
-The below posted OpenCV program assigns the user preferred color to a particular pixel value in an image
-ScanImageAndReduceC() is a predefined method in OpenCV to scan through all the pixels of an Image
-I.atuchar>(10, 10) = 255; is used to access a particular pixel value of an image
Here is the code:
Mat& ScanImageAndReduceC(Mat& I)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i, j;
uchar* p;
for (i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for (j = 0; j < nCols; ++j)
{
I.at<uchar>(10, 10) = 255;
}
}
return I;
}
-------Main Program-------
Calling the above method in our main program
diff = ScanImageAndReduceC(diff);
namedWindow("Difference", WINDOW_AUTOSIZE);// Create a window for display.
imshow("Difference", diff); // Show our image inside it.
waitKey(0); // Wait for a keystroke in the window
return 0;
}

How to access a particular kmeans cluster in opencv

I am new to opencv, and I am trying to find and save the largest cluster of a kmeaned clustered image. I have:
clustered the image following the method provided by Mercury and Bill the Lizard in the following post (Color classification with k-means in OpenCV),
determined the largest cluster by finding the largest label count from the kmeans output (bestLables)
tried to store the position of the pixels that constitute the largest cluster in an array of Point2i
However, the mystery is that I found myself with a number of stored points that is significantly less than the count
obtained when trying to find the largest cluster. In other words: inc < max. Plus the number given by inc does not even correspond to any other clusters' number of points.
What did I do wrong? or is there a better way to do what I'm trying to do?, any input will be much appreciated.
Thanks in advance for your precious help!!
#include <iostream>
#include "opencv2/opencv.hpp"
#include<opencv2/highgui/highgui.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat img = imread("pic.jpg", CV_LOAD_IMAGE_COLOR);
if (!img.data)
{
cout << "Could not open or find the image" << std::endl;
return -1;
}
//imshow("img", img);
Mat imlab;
cvtColor(img, imlab, CV_BGR2Lab);
/* Cluster image */
vector<cv::Mat> imgRGB;
int k = 5;
int n = img.rows *img.cols;
Mat img3xN(n, 3, CV_8U);
split(imlab, imgRGB);
for (int i = 0; i != 3; ++i)
imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));
img3xN.convertTo(img3xN, CV_32F);
Mat bestLables;
kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);
/*bestLables= bestLables.reshape(0,img.rows);
cv::convertScaleAbs(bestLables,bestLables,int(255/k));
cv::imshow("result",bestLables);*/
/* Find the largest cluster*/
int max = 0, indx= 0, id = 0;
int clusters[5];
for (int i = 0; i < bestLables.rows; i++)
{
id = bestLables.at<int>(i, 0);
clusters[id]++;
if (clusters[id] > max)
{
max = clusters[id];
indx = id;
}
}
/* save largest cluster */
int cluster = 1, inc = 0;
Point2i shape[2000];
for (int y = 0; y < imlab.rows; y++)
{
for (int x = 0; x < imlab.cols; x++)
{
if (bestLables.data[y + x*imlab.cols] == cluster) shape[inc++] = { y, x };
}
}
waitKey(0);
return 0;
}
You are pretty close, but there are a few errors. The code below should work as expected. I also added a small piece of code to show the classification result, where pixels of the larger cluster are red, the other with shades of green.
You never initialized int clusters[5];, so it will contains random numbers at the beginning, compromising it as an accumulator. I recommend to use vector<int> instead.
You access bestLabels with wrong indices. Instead of bestLables.data[y + x*imlab.cols], it should be bestLables.data[y*imlab.cols + x]. That caused your inc < max issue. In the code below I used a vector<int> to contain indices, since it's easier to see the content of the vector. So I access bestLabels a little differently, i.e. bestLables[y*imlab.cols + x] instead of bestLables.data[y*imlab.cols + x], but the result is the same.
You had Point2i shape[2000];. I used a vector<Point>. Note that Point is just a typedef of Point2i. Since you don't know how many points will be there, better use a dynamic array. If you know that there will be, say, 2000 points, you'd better call reserve to avoid reallocations, but that's not mandatory. With Point2i shape[2000]; if you have more than 2000 points you'll go out of bounds, with a vector you're safe. I used emplace_back to avoid a copy when appending the point (just like you did with the initializer list). Note that the contructor of Point is (x,y), not (y,x).
Using vector<Point> you don't need inc, since you append the value at the end. If you need inc to store the number of points in the largest cluster, simply call int inc = shape.size();
You initialized int cluster = 1. That's an error, you should initialize it with the index of the largest cluster, i.e. int cluster = indx;.
You are calling the vector of planes imgRGB, but you're working on Lab. You'd better change the name, but it's not an issue per se. Also, remember that RGB values are stored in OpenCV as BGR, not RGB (reversed order).
I prefer Mat1b, Mat3b, etc... where possible over Mat. It allows easier access and is more readable (in my opinion). That's not an issue, but you'll see that in my code.
Here we go:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int main(int argc, char** argv)
{
Mat3b img = imread("path_to_image");
if (!img.data)
{
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
Mat3b imlab;
cvtColor(img, imlab, CV_BGR2Lab);
/* Cluster image */
vector<cv::Mat3b> imgRGB;
int k = 5;
int n = img.rows * img.cols;
Mat img3xN(n, 3, CV_8U);
split(imlab, imgRGB);
for (int i = 0; i != 3; ++i)
imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));
img3xN.convertTo(img3xN, CV_32F);
vector<int> bestLables;
kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);
/* Find the largest cluster*/
int max = 0, indx= 0, id = 0;
vector<int> clusters(k,0);
for (size_t i = 0; i < bestLables.size(); i++)
{
id = bestLables[i];
clusters[id]++;
if (clusters[id] > max)
{
max = clusters[id];
indx = id;
}
}
/* save largest cluster */
int cluster = indx;
vector<Point> shape;
shape.reserve(2000);
for (int y = 0; y < imlab.rows; y++)
{
for (int x = 0; x < imlab.cols; x++)
{
if (bestLables[y*imlab.cols + x] == cluster)
{
shape.emplace_back(x, y);
}
}
}
int inc = shape.size();
// Show results
Mat3b res(img.size(), Vec3b(0,0,0));
vector<Vec3b> colors;
for(int i=0; i<k; ++i)
{
if(i == indx) {
colors.push_back(Vec3b(0, 0, 255));
} else {
colors.push_back(Vec3b(0, 255 / (i+1), 0));
}
}
for(int r=0; r<img.rows; ++r)
{
for(int c=0; c<img.cols; ++c)
{
res(r,c) = colors[bestLables[r*imlab.cols + c]];
}
}
imshow("Clustering", res);
waitKey(0);
return 0;
}

Constructing a Graph of Strings (Levenshtein Distance)

Currently, in my computer-science course, we are discussing graphs and how to find shortest distance using graphs. I received an assignment about a week ago where the teacher gave us the code for a graph using integers, and we have to adapt it to be able to calculate Levenshtein Distance using a list of words. The problem I'm having though is that I don't understand really how graphs work enough to manipulate one. I've tried googling graphs in c++ but none of the things I found resemble the type of program I was given.
We just finished a unit on linked lists, and I think graphs operate similarly? I understand that each node will point to many other nodes, but in a case where I have 2000 words all pointing to each other, how do I keep track of 2000 pointers per node without declaring that many nodes in my struct? I believe (not 100%) that in the program I was given my teacher used a vector of integer vectors to keep track but I don't know how to implement that.
I'm not asking anyone to fully comment each line as that is an enormous amount of work, but if someone could roughly explain how I would accomplish what I asked above and perhaps read the code and give me a rough understanding of what some sections mean (I'll put comments on some sections I'm specifically having trouble understanding) I would be extremely grateful.
Here is the code we were given:
#include <iostream>
#include <vector>
#include <algorithm> //for max<>
#include <limits>
using namespace std;
typedef vector <int> ivec;
typedef vector <ivec> imatrix; //A vector of vectors, not how this works or how to implement
typedef vector <bool> bvec;
struct graph
{
imatrix edges; //list of attached vertices for each node
int numVertices;
};
//I understand the ostream overloading
ostream & operator << (ostream & stream, ivec &vec)
{
for (int i = 0; i < vec.size(); i++)
{
stream << vec[i] << " ";
}
return stream;
}
ostream & operator << (ostream & stream, graph &g)
{
stream << endl << "numVert = " << g.numVertices << endl;
for (int i = 0; i < g.numVertices; i++)
{
stream << "vertex = " << i+1 << " | edges = " << g.edges[i] << endl;
}
return stream;
}
const int sentinel = -1;
bvec inTree;
ivec distanceNodes;
ivec parents;
void initGraph(graph * g);
void insertEdge(graph * g, int nodeNum, int edgeNum);
void initSearch(graph * g);
void shortestPath(graph * g, int start, int end);
int main()
{
//I understand the main, the two numbers in insertEdge are being hooked together and the two numbers in shortestPath are what we are looking to connect in the shortest way possible
graph g;
initGraph(&g);
insertEdge(&g, 1, 2);
insertEdge(&g, 1, 3);
insertEdge(&g, 2, 1);
insertEdge(&g, 2, 3);
insertEdge(&g, 2, 4);
insertEdge(&g, 3, 1);
insertEdge(&g, 3, 2);
insertEdge(&g, 3, 4);
insertEdge(&g, 4, 2);
insertEdge(&g, 4, 3);
insertEdge(&g, 4, 5);
insertEdge(&g, 5, 4);
insertEdge(&g, 6, 7);
insertEdge(&g, 7, 6);
cout << "The graph is " << g << endl;
shortestPath(&g, 1, 5);
shortestPath(&g, 2, 4);
shortestPath(&g, 5, 2);
shortestPath(&g, 1, 7);
return 0;
}
void initGraph(graph * g)
{
g -> numVertices = 0; //Why set the number of vertices to 0?
}
void insertEdge(graph * g, int nodeNum, int edgeNum)
{
int numVertices = max(nodeNum, edgeNum); //Max finds the larger of two numbers I believe? How can this be used with strings, one is not bigger than the other
numVertices = max(1, numVertices);
if (numVertices > g->numVertices)
{
for (int i = g->numVertices; i <= numVertices; i++)
{
ivec nodes;
if (g->edges.size() < i)
{
g -> edges.push_back(nodes);
}
}
g->numVertices = numVertices;
}
g->edges[nodeNum - 1].push_back(edgeNum);
}
void initSearch(graph * g) //I believe this function simply resets the values from a previous search
{
if (g == NULL)
{
return;
}
inTree.clear();
distanceNodes.clear();
parents.clear();
for (int i = 0; i <= g->numVertices; i++)
{
inTree.push_back(false);
distanceNodes.push_back(numeric_limits <int> :: max());
parents.push_back(sentinel);
}
}
void shortestPath(graph * g, int start, int end)
{
//Very confused about how this function works
initSearch(g);
int edge;
int curr; //current node
int dist;
distanceNodes[start] = 0;
curr = start;
while (! inTree[curr])
{
inTree[curr] = true;
ivec edges = g->edges[curr - 1];
for (int i = 0; i < edges.size(); i++)
{
edge = edges[i];
if (distanceNodes[edge] > distanceNodes[curr] + 1)
{
distanceNodes[edge] = distanceNodes[curr] + 1;
parents[edge] = curr;
}
}
curr = 1;
dist = numeric_limits <int> :: max();
for (int i = 1; i <= g->numVertices; i++)
{
if ((!inTree[i]) && (dist > distanceNodes[i]))
{
dist = distanceNodes[i];
curr = i;
}
}
}
ivec path;
if (distanceNodes[end] == numeric_limits <int> :: max()) //is there a numeric_limits <string> :: max?
{
cout << "No way from " << start << " to " << end << endl;
}
else
{
int temp = end;
while (temp != start)
{
path.push_back(temp);
temp = parents[temp];
}
path.push_back(start);
reverse(path.begin(), path.end());
cout << "From " << start << " to " << end << " is " << path << endl;
}
}
If you can help, that would be most welcome as I most likely will have more projects with graphs and I'm struggling due to not understanding them.
Thank you,
Tristan
typedef vector <ivec> imatrix; //A vector of vectors, not how this works or how to implement
Here the graph is represented as Adjacency Matrix. You can also represent a graph using Adjacency List, where each Node would hold a array/linked list of neighboring nodes.
g -> numVertices = 0; //Why set the number of vertices to 0?
It initializes the graph, at startup number of vertices/nodes is zero. When edges and nodes will be inserted using insertEdge method then this number will be updated.
int numVertices = max(nodeNum, edgeNum); //Max finds the larger of two numbers I believe? How can this be used with strings, one is not bigger than the other
though you have not posted full code, I think that the maximum value is used to add required number of vertices before inserting an edge.
ivec nodes;
if (g->edges.size() < i)
{
g -> edges.push_back(nodes);
}
above code inserts new vertices. You will probably do integer comparison as here for your version, not string, string is the data of node, not number of node. Still if you need string comparison, C++ already has overloaded operators for this.
About initSearch and shortestPath methods, here the latter finds shortest path between nodes using an algorithm( I don't know which, you can search), and before searching for a shortest path, the former method initializes the values that will be used to search. For example it could set the distances between each pair of node to infinity initially, when a path is found between them, it will be updated.
Some answers:
Q. You asked why numVertices is set to 0 in the following:
void initGraph(graph * g)
{
g -> numVertices = 0; //Why set the number of vertices to 0?
}
A. Look at the declaration of g - it is default initialized:
int main()
{
graph g;
....
}
Now look at the definition of graph - it has no constructor:
struct graph
{
imatrix edges; //list of attached vertices for each node
int numVertices;
};
So edges gets initialized properly by default because vectors have a constructor. But numVertices is a primitive type so it will contain whatever random value happens to be in that memory location - so that means it needs to be manually initialized. Thats why initGraph doesn't need to initialize edges but it does need to initalize numVertices.
Q. You asked how you can find the larger of two std::strings knowing that max() returns the larger of two integers:
int numVertices = max(nodeNum, edgeNum); //Max finds the larger of two numbers I believe? How can this be used with strings, one is not bigger than the other
A. According to http://www.cplusplus.com/reference/algorithm/max/ max uses "The function uses operator< (or comp, if provided) to compare the values." but std::strings can be compared using the < operator so there really is no problem.
Q. You asked about a vector of vectors:
typedef vector <int> ivec;
typedef vector <ivec> imatrix; //A vector of vectors, not how this works or how to implement
A. You can access a vector with [] so if you had a variable called x of imatrix type you could say x[0] which would return an ivec (because that is the type of object stored in an imatrix vector. So if you said x[0][0] that would return the first integer stored in the ivec that is returned by x[0]. To change it to use a string just say:
typedef vector <std::string> ivec;
typedef vector <ivec> imatrix;
You could also rename the variables if you wanted.
You would also need to #include <string>