OpenCV: Computing superpixel centroids - c++

Background:
I have computed SLIC superpixels of an image using gSLICr, which gives a "per-pixel map" of image superpixels as indices (0 to the number of superpixels-1).
This map is a pointer to an integer const array (const int*) containing the indices.
I now want to compute the centroids of each superpixel using OpenCV.
Coming from a Matlab background, I would do this by using regionprops:
segments = vl_slic(myImage, regionSize, regularizer);
stats = regionprops(segments, 'Centroid');
centroids = cat(1, stats.Centroid);
I don't know how this is done using OpenCV.
Questions:
(i) How do I convert a const int* array to a cv::Mat?
(ii) How do I compute the superpixel centroids from the matrix in (i)?

As the first questions seems to be answered, I will focus on you second question. I used the following code to compute the mean coordinates (i.e. spatial centroids) of each superpixel:
/** \brief Compute the mean coordinates of each superpixel (i.e. spatial centroids).
* \param[in] labels a matrix of type CV_32SC1 holding the labels for each pixel
* \param[out] means the spatial centroids (or means in y and x axes) of the superpixels
*/
void getMeans(const cv::Mat &labels, std::vector<cv::Vec2f> &means) {
// Count superpixels or get highest superpixel index:
int superpixels = 0;
for (int i = 0; i < labels.rows; ++i) {
for (int j = 0; j < labels.cols; ++j) {
if (labels.at<int>(i, j) > superpixels) {
superpixels = labels.at<int>(i, j);
}
}
}
superpixels++;
// Setup means as zero vectors.
means.clear();
means.resize(superpixels);
for (int k = 0; k < superpixels; k++)
{
means[k] = cv::Vec2f(0, 0);
}
std::vector<int> counts(superpixels, 0);
// Sum y and x coordinates for each superpixel:
for (int i = 0; i < labels.rows; ++i) {
for (int j = 0; j < labels.cols; ++j) {
means[labels.at<int>(i, j)][0] += i; // for computing mean i (i.e. row or y axis)
means[labels.at<int>(i, j)][1] += j; // for computing the mean j (i.e. column or x axis)
counts[labels.at<int>(i, j)]++;
}
}
// Obtain averages by dividing by the size (=number of pixels) of the superpixels.
for (int k = 0; k < superpixels; ++k) {
means[k] /= counts[k];
}
}
// Do something with the means ...
If you also need the mean color, the method would require the image as an argument, but the remaining code can easily be adapted for computing the mean colors.

Related

How do I fill the contents of a matrix using 9 other matrixes?

I am creating a sudoku game
I have 9 3x3 matrixes and a large 9x9 matrix. The methods I tried didn't work so please help me:
void matrix_full_creation()
{
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
matrix_full[i][j]==matrix1[i][j];
matrix_full[i][j+3]=matrix2[i][j];
matrix_full[i][j+6]=matrix3[i][j];
matrix_full[i+3][j]=matrix4[i][j];
matrix_full[i+3][j+3]=matrix5[i][j];
matrix_full[i+3][j+3]=matrix6[i][j];
matrix_full[i+6][j]=matrix7[i][j];
matrix_full[i+6][j+3]=matrix8[i][j];
matrix_full[i+6][j+6]=matrix9[i][j];
}
}
}
Given a submatrix index n, and a pair of indexes within that submatrix, i.e. row index i and column index j, and
considering you want to place each submatrix at positions (0,0), (0,1)..., (3,3) of the full matrix,
you can walk each cell of each submatrix, and
use the following formula to calculate the destination cell in the full matrix:
matrix_full[3*(n / 3) + i][3*(n % 3) + j] = submatrix[i][j];
[Demo]
Let's break your task into smaller pieces.
First, write a function that copies your small matrix into specific position
void Copy(int** small, int i, int j);
So if you pass 0, 0 for i, j... It should copy contents of small into top left 3x3 submatrix of the big 9x9 matrix.
When you complete this task, just run a for loop (similar to the one you already did)
int** bigMatrix; // assume we have it here
void Copy(int** small, int i, int j) {
for(int ii = 0; ii < 3; ++ii) {
for(int jj = 0; jj < 3; ++jj) {
bigMatrix[i + ii][j + jj] = small[ii][jj];
}
}
}
int main() {
int smalls[3][3][3] = {...}; // assume we have 3x3 init here
for(int i = 0; i < 9; i += 3) {
for(int j = 0; j < 9; j += 3) {
Copy(smalls[0], i, j];
}
}
return 0;
}
Good tip: divide and conquer!
I have corrected your code. Try this:
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
matrix_full[i][j]=matrix1[i][j];
matrix_full[i][j+3]=matrix2[i][j];
matrix_full[i][j+6]=matrix3[i][j];
matrix_full[i+3][j]=matrix4[i][j];
matrix_full[i+3][j+3]=matrix5[i][j];
matrix_full[i+3][j+6]=matrix6[i][j];
matrix_full[i+6][j]=matrix7[i][j];
matrix_full[i+6][j+3]=matrix8[i][j];
matrix_full[i+6][j+6]=matrix9[i][j];
}
}
Hope this helps! :D

I'm new to c++, is there an algorithm to find the distance to nearest 0 in a 3d array?

In a 3D array, the diagonal elements are 0 i.e. a[k][k][k]=0 for k=1 to 10. I need to fill the remaining matrix with distance to the nearest 0.
I came across the breadth first search, but can i do it without using data structures? (not familiar with data structures)
unsigned short d = 10, r = 10, c = 10;
double*** a = new double** [d];
for (int i = 0; i < d; ++i) {
a[i] = new double* [r];
for (int j = 0; j < r; ++j)
a[i][j] = new double[c];
}
for (int i = 0; i < 10; i++) {
a[i][i][i] = 0;
}
return a 3d distance array.
you can find all zeros and find the closest by calculating the manhattan distance.
another efficient way to this will be to use bfs algorithm. the first zero found in bfs is the closest zero from source.
here is a tutorial for 2d array sortest distance
Just fill your matrix with this formula for all i, j, k :
a[i][j][k] = MAX(i,j,k) - MIN(i,j,k)
The Manhattan distance to the diagonal is indeed the difference between the highest index and the lowest one.
This formula is so simple that it seems somewhat magical, but it works !

Mapping the "lower diagonal" of a tensor to a matrix, as a generalization of the extraction of the lower triangular part of a matrix into a vector

Given a rank-4 tensor (each rank with dimension K), for example T(p,q,r,s), we can 1-to-1 map all the tensor elements into a matrix of dimension K^2 x K^2, for example M(i,j) in which the two first tensor indices p,q and the last two indices r,s are combined in a column major way:
i = p + K * q
j = r + K * s
Exploiting some (anti-)symmetries of the given tensor, for example T(p,q,r,s) = -T(q,p,r,s) = -T(p,q,s,r) = T(q,p,s,r) and T(p,q,r,s) = T(r,s,p,q), we would like to be able to construct a matrix H(m,n) that only contains the unique elements (i.e. those not related by the previously defined symmetries), such that p>q and r>s into the matrix H(m,n), which would then be of dimension K(K-1)/2 x K(K-1)/2.
How could we find an algorithm (or even better: how can we use the C++ library Eigen) to accomplish these index transformations? Furthermore, can we write down m and n algebraically in terms of p,q and r,s, like we can do in the case where we would want to extract the strict lower triangular matrix (no diagonal) into a vector?
For reference, given a square matrix Eigen::MatrixXd M (K,K), here is an algorithm that extracts the strict lower triangle of a given square matrix M into a vector m of size, using the C++ Eigen library:
Eigen::VectorXd strictLowerTriangle(const Eigen::MatrixXd& M) {
auto K = static_cast<size_t>(M.cols()); // the dimension of the matrix
Eigen::VectorXd m = Eigen::VectorXd::Zero((K*(K-1)/2)); // strictly lower triangle has K(K-1)/2 parameters
size_t vector_index = 0;
for (size_t q = 0; q < K; q++) { // "column major" ordering for, so we do p first, then q
for (size_t p = q+1; p < K; p++) {
m(vector_index) = M(p,q);
vector_index++;
}
}
return m;
}
We are able to extend this algorithm to the requested general case:
Eigen::MatrixXd strictLowerTriangle(const Eigen::Tensor<double, 4>& T) {
auto K = static_cast<size_t> (dims[0]);
Eigen::MatrixXd M (K*(K-1)/2, K*(K-1)/2);
size_t row_index = 0;
for (size_t j = 0; j < K; j++) { // "column major" ordering for row_index<-i,j so we do j first, then i
for (size_t i = j+1; i < K; i++) { // in column major indices, columns are contiguous, so the first of two indices changes more rapidly
// require i > j
size_t column_index = 0;
for (size_t l = 0; l < K; l++) { // "column major" ordering for column_index<-k,l so we do l first, then k
for (size_t k = l+1; k < K; k++) { // in column major indices, columns are contiguous, so the first of two indices changes more rapidly
// require l > k
M(row_index,column_index) = T(i,j,k,l);
column_index++;
}
}
row_index++;
}
}
return M;
}

how to find euclidean distance between pixels within a image in opencv

int main(){
Mat cmp, Ref, Diff;
cmp = imread("image1.tif", CV_LOAD_IMAGE_UNCHANGED);
Ref = imread("image2.tif", CV_LOAD_IMAGE_UNCHANGED);
ShiftChk(cmp, Ref);
absdiff(cmp, Ref, Diff);
imshow("difference image", Diff);
waitKey(0);
double min, max;
minMaxLoc(Diff, &min, &max);
Point min_loc, max_loc;
minMaxLoc(Diff, &min, &max, &min_loc, &max_loc);
Size sz = Diff.size();
cout << "max val : " << max << endl;//5
cout << "max val: " << max_loc << endl; //[26,38]
vector<vector<double>>test;
for (int i = 0; i < Diff.cols; i++) {
for (int j = 0; j < Diff.rows; j++) {
Point difference = Diff.at<uchar>(26, 38) - Diff.at<uchar>(j, i);
double dist = sqrt(difference.x*difference.x + difference.y*difference.y);
test.push_back(dist);
}
}
}
I am trying to find the Euclidean distance between a single point in an image to all other pixels. The distance values are to be stored in vector test but its showing some error in it. And also I don't know whether the logic I have used is correct to give the right answer(Euclidean distance). Can anyone help me out. Thanks in advance
Error message is:
error C2664:
'void std::vector<std::vector<double,std::allocator<_Ty>>,std::allocator<std::vector<_Ty,std::allocator<_Ty>>>>::push_back(const std::vector<_Ty,std::allocator<_Ty>> &)' :
cannot convert argument 1 from 'double' to 'std::vector<double,std::allocator<_Ty>> &&'
There are two major issues:
You're appending the values to the test vector wrong. You need either to create an intermediate vector and push_back it to test (as shown in #0X0nosugar answer), or better initialize your vectors with correct dimensions and put the value at the right place.
vector<vector<double>> test(Diff.rows, vector<double>(Diff.cols));
for (int i = 0; i < Diff.rows; i++) {
for (int j = 0; j < Diff.cols; j++) {
test[i][j] = ...
}
}
As shown in the snippet above, it's better (and faster) to scan by rows, becuase OpenCV stores images row-wise.
You are not computing the distance between two points. You are in fact taking the difference of the values at two given points and creating a Point object out of this (which makes no sense). Also you can avoid to compute explicitly the euclidean distance. You can use cv::norm:
test[i][j] = norm(Point(38, 26) - Point(j, i)); // Pay attention to i,j order!
Putting all together:
Point ref(38, 26);
vector<vector<double>> test(Diff.rows, vector<double>(Diff.cols));
for (int i = 0; i < Diff.rows; i++) {
for (int j = 0; j < Diff.cols; j++) {
test[i][j] = norm(ref - Point(j,i));
}
}

Find similar distances between all values in vector and subset them

Given is a vector with double values. I want to know which distances between any elements of this vector have a similar distance to each other. In the best case, the result is a vector of subsets of the original values where subsets should have at least n members.
//given
vector<double> values = {1,2,3,4,8,10,12}; //with simple values as example
//some algorithm
//desired result as:
vector<vector<double> > subset;
//in case of above example I would expect some result like:
//subset[0] = {1,2,3,4}; //distance 1
//subset[1] = {8,10,12}; //distance 2
//subset[2] = {4,8,12}; // distance 4
//subset[3] = {2,4}; //also distance 2 but not connected with subset[1]
//subset[4] = {1,3}; //also distance 2 but not connected with subset[1] or subset[3]
//many others if n is just 2. If n is 3 (normally the minimum) these small subsets should be excluded.
This example is simplified as the distances of integer numbers could be iterated and tested for the vector which is not the case for double or float.
My idea so far
I thought of something like calculating the distances and storing them in a vector. Creating a difference distance matrix and thresholding this matrix for some tolerance for similar distances.
//Calculate distances: result is a vector
vector<double> distances;
for (int i = 0; i < values.size(); i++)
for (int j = 0; j < values.size(); j++)
{
if (i >= j)
continue;
distances.push_back(abs(values[i] - values[j]));
}
//Calculate difference of these distances: result is a matrix
Mat DiffDistances = Mat::zero(Size(distances.size(), distances.size()), CV_32FC1);
for (int i = 0; i < distances.size(); i++)
for (int j = 0; j < distances.size(); j++)
{
if (i >= j)
continue;
DiffDistances.at<float>(i,j) = abs(distances[i], distances[j]);
}
//threshold this matrix with some tolerance in difference distances
threshold(DiffDistances, DiffDistances, maxDistTol, 255, CV_THRESH_BINARY_INV);
//get points with similar distances
vector<Points> DiffDistancePoints;
findNonZero(DiffDistances, DiffDistancePoints);
At this point I get stuck with finding the original values corresponding to my similar distances. It should be possible to find them, but it seems very complicated to trace back the indices and I wonder if there isn't an easier way to solve the problem.
Here is a solution that works, as long as there are no branches meaning, that there are no values closer together than 2*threshold. That is the valid neighbor region because neighboring bonds should differ by less than the threshold, if I understood #Phann correctly.
The solution is definitively neither the fastest nor the nicest possible solution. But you might use it as a starting point:
#include <iostream>
#include <vector>
#include <algorithm>
int main(){
std::vector< double > values = {1,2,3,4,8,10,12};
const unsigned int nValues = values.size();
std::vector< std::vector< double > > distanceMatrix(nValues - 1);
// The distanceMatrix has a triangular shape
// First vector contains all distances to value zero
// Second row all distances to value one for larger values
// nth row all distances to value n-1 except those already covered
std::vector< std::vector< double > > similarDistanceSubsets;
double threshold = 0.05;
std::sort(values.begin(), values.end());
for (unsigned int i = 0; i < nValues-1; ++i) {
distanceMatrix.at(i).resize(nValues-i-1);
for (unsigned j = i+1; j < nValues; ++j){
distanceMatrix.at(i).at(j-i-1) = values.at(j) - values.at(i);
}
}
for (unsigned int i = 0; i < nValues-1; ++i) {
for (unsigned int j = i+1; j < nValues; ++j) {
std::vector< double > thisSubset;
double thisDist = distanceMatrix.at(i).at(j-i-1);
// This distance already belongs to another cluster
if (thisDist < 0) continue;
double minDist = thisDist - threshold;
double maxDist = thisDist + threshold;
thisSubset.push_back(values.at(i));
thisSubset.push_back(values.at(j));
//Indicate that this is already clustered
distanceMatrix.at(i).at(j-i-1) = -1;
unsigned int lastIndex = j;
for (unsigned int k = j+1; k < nValues; ++k) {
thisDist = distanceMatrix.at(lastIndex).at(k-lastIndex-1);
// This distance already belongs to another cluster
if (thisDist < 0) continue;
// Check if you found a new valid pair
if ((thisDist > minDist) && (thisDist < maxDist)){
// Update the valid distance interval
minDist = thisDist - threshold;
minDist = thisDist - threshold;
// Add the newly found point
thisSubset.push_back(values.at(k));
// Indicate that this is already clustered
distanceMatrix.at(lastIndex).at(k-lastIndex-1) = -1;
// Continue the search from here
lastIndex = k;
}
}
if (thisSubset.size() > 2) {
similarDistanceSubsets.push_back(thisSubset);
}
}
}
for (unsigned int i = 0; i < similarDistanceSubsets.size(); ++i) {
for (unsigned int j = 0; j < similarDistanceSubsets.at(i).size(); ++j) {
std::cout << similarDistanceSubsets.at(i).at(j);
if (j != similarDistanceSubsets.at(i).size()-1) {
std::cout << " ";
}
else {
std::cout << std::endl;
}
}
}
}
The idea is to precompute the distances and then look for every pair of particles, starting from the smallest and its larger neighbors, if there is another valid pair above it. If so these are all collected in a subset and this is added to the subset vector. For every new value the valid neighbor region has to be updated to ensure that neighboring distances differ by less than the threshold. Afterwards, the program continues with the next smallest value and its larger neighbors and so on.
Here is an algorithm which is slightly different from yours, which is O(n^3) in the length n of the vector - not very efficient.
It is based on the premise that you want to have subsets of at least size 2. So what you can do is consider all the two-element subsets of the vector, then find all other elements that also match.
So given a function
std::vector<int> findSubset(std::vector<int> v, int baseValue, int distance) {
// Find the subset of all elements in v that differ by a multiple of
// distance from the base value
}
you can do
std::vector<std::vector<int>> findSubsets(std::vector<int> v) {
for(int i = 0; i < v.size(); i++) {
for(int j = i + 1; j < v.size(); j++) {
subsets.push_back(findSubset(v, v[i], abs(v[i] - v[j])));
}
}
return subsets;
}
Only remaining problem is keeping track of the duplicates, maybe you can keep a hashed list of (baseValue % distance, distance) pairs for all the subsets you have already found.