I want to modify a part of a multi-dimensional matrix using openCV. Basically I want to achieve the same as written in Matlab:
A = zeros(5,5,25);
A(:,:,1) = some_matrix1;
A(:,:,2) = some_matrix2;
I am not sure if I should use a 5x5 matrix with 25 channels or a 5x5x25 matrix with single channel. Here is what I tried:
int dim[3] = { 5,5,25 };
Mat A(3, dim, CV_32FC(1), Scalar::all(0));
A(Range::all(),Range::all(),0) = some_matrix;
But it seems like I can only use Range for two dimensions.
Or
Mat A(5, 5, CV_32FC(25), Scalar::all(0));
A(Range::all(),Range::all())[0] = some_matrix;
But in this case, I don't know how to access the channel.
Can you please help me with it?
OpenCV is optimized for 2D matrices. Multidimensional matrix will work, but are rather inefficient and difficult to access.
This example code will show you how to write and read values from an 3D matrix:
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
int sizes[] = { 5, 5, 25 };
Mat data(3, sizes, CV_32F);
Mat1f some_matrix(sizes[0], sizes[1]);
randu(some_matrix, 0.f, 100.f); // some random values
// Init data with each plane a constant increasing value
for (int z = 0; z < data.size[2]; ++z)
{
// Set each z-plane to some scalar value
Range ranges[] = { Range::all(), Range::all(), Range(z, z + 1) };
data(ranges) = data.size[2] - z;
}
// Set the n-th z-plane to some_matrix
int z = 0;
for (int r = 0; r < sizes[0]; ++r)
{
for (int c = 0; c < sizes[1]; ++c)
{
data.at<float>(r, c, z) = some_matrix(r, c);
}
}
// Access all slices along z dimension
for (int z = 0; z < data.size[2]; ++z)
{
Range ranges[] = { Range::all(), Range::all(), Range(z, z + 1) };
Mat slice3d(data(ranges).clone()); // with clone slice is continuous, but still 3d
Mat slice(2, &data.size[0], data.type(), slice3d.data);
}
return 0;
}
However, it's far easier and practical to store your 5x5x25 3D matrix as a std::vector<Mat>, where the vector has length 25, and each matrix is a 2D 5x5.
See the code:
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
int sizes[] = { 5, 5, 25 };
vector<Mat> data(sizes[2]);
// Init data with each plane a constant increasing value
for (int z = 0; z < sizes[2]; ++z)
{
data[z] = Mat(sizes[0], sizes[1], CV_32F, float(sizes[2] - z));
}
Mat1f some_matrix(sizes[0], sizes[1]);
randu(some_matrix, 0.f, 100.f); // some random values
// Set the n-th z-plane to some_matrix
int z = 0;
data[z] = some_matrix;
return 0;
}
Here is the piece of code to access the pixel from the channel, you can try it.
int dim[3] = { 5,5,25 };
Mat A(3, dim, CV_32FC1, Scalar::all(0));
for (int m = 0; m < 5; m++)
{
for (int n = 0; n < 5; n++)
{
for (int a = 0; a < 25; a++) // no of channels
{
cout << A.at<cv::Vec3f>(m,n)[a] << endl;
}
}
}
Related
i try to build an DLL with the OpenCV PCA included to make it usable under Labview.
I have defined the the function:
extern "C" __declspec(dllexport) int __cdecl doPCA(float *input,int32_t input_rows,int32_t input_cols,double maxComponents,float *output);
And wrote it like:
int __cdecl doPCA(float *input,int32_t input_rows, int32_t input_cols,double maxComponents,float *output)
{
Mat pcaset = Mat(input_rows,input_cols, CV_32FC1, &input); //CV_32FC1 is for float valued pixel
PCA pca(pcaset, // pass the data
Mat(), // we do not have a pre-computed mean vector, // so let the PCA engine to compute it
CV_PCA_DATA_AS_ROW, // indicate that the vectors// are stored as matrix rows// (use PCA::DATA_AS_COL if the vectors are // the matrix columns)
2 // specify, how many principal components to retain
);
int i, j;
for(i = 0; i < input_rows; i++)
{
for(j = 0; j < input_cols; j++)
{
output[(i * input_cols) + j] = pca.eigenvectors.data[(i * input_cols) + j]; // Write Values to 1D output array
}
}
if(pca.eigenvectors.empty()){return 0;} // is empty
if(!pca.eigenvectors.empty()){return 1;} // is not empty
}
At Labview side I access the function by the compiled DLL:
But I canĀ“t figure it out, how to pass value the from pca.eigenvectors cv::Mat to the 1D float output array.
int i, j;
for(i = 0; i < input_rows; i++)
{
for(j = 0; j < input_cols; j++)
{
output[(i * input_cols) + j] = pca.eigenvectors.data[(i * input_cols) + j]; // Write Values to 1D output array
}
}
Could anyone give a hint?
I learn how to do PCA from the page that Miki gives.
This is my code to do the similar thing.
///! Convert pointer to cv::Mat, do PCA, and convert back.
///! 2017.10.05 10:28:25 CST
int doPCA(float* data, int rows, int cols, int maxC, float* eigenvecs ) {
// convert pointer to Mat, CV_32FC1 is for float valued pixel.
Mat pcaset = Mat(rows,cols, CV_32FC1, data);
// let opencv compute the eigenvectors, and treat data as row, extract the first 2 principle components.
// pca.means : eigenvalues as row matrix
// pca.eigenvectors: eigenvectors as row matrix
maxC = (maxC >0 && maxC <= rows)?maxC:rows;
PCA pca(pcaset, Mat(), CV_PCA_DATA_AS_ROW,maxC);
cout << "Eigen values:\n"<< pca.mean <<endl;
cout << "Eigen vectors:\n"<<pca.eigenvectors<<endl;
if(pca.eigenvectors.empty()) {
return 0; // is empty
}
float *pvec = eigenvecs;
// get eigenvector in revered order
for(int i=maxC-1; i>=0; --i){
for(int j=0; j<cols; ++j){
*pvec++ = pca.eigenvectors.at<float>(i,j);
}
}
return 1;
}
int testPCA(){
// row first
float data[4] = {1.0,2.0,2.0,5.0};
int cols = 2;
int rows = 2;
// alloc two eigenvectors length: 2x2=4
float eigenvecs[4]={0};
// max components nums
int maxC = 2;
int res = doPCA(data, rows, cols, maxC, eigenvecs);
Mat eigenvalues(Size(cols, rows), CV_32FC1, eigenvecs);
cout << "Flag:\n" << res << endl;
cout << "Principle Components:\n"<< eigenvalues<<endl;
return 0;
}
Result:
Eigen values:
[1.5, 3.5]
Eigen vectors:
[0.31622776, 0.94868332;
0.94868332, -0.31622776]
Flag:
1
Principle Components:
[0.94868332, -0.31622776;
0.31622776, 0.94868332]
I want to assign value to a 3 dimensional array in opencv but don't know how to do it.
here is the code in matlab that I want to write in opencv
vv = zeros(800,600,2);
for j1=1:m1
for j2=1:m2
w=[-k;vv(j1,j2,1);vv(j1,j2,2)];
w=w/norm(w);
end
end
and this is what I did in opencv, but did not work
int dim2[3] = {800,600,2};
Mat vv(3,dim2,CV_32F,Scalar::all(0));
for(int j1 = 0; j1 < 800; j1++)
{ for(int j2 = 0; j2 < 600; j2++)
{
Mat w(3,dim2,CV_32F, Scalar(1,vv(j1,j2,1),vv(j1,j2,2)));
}
}
use the following syntax:
//initizlizes a matrix zeros, of size 800x600x2
cv::Mat vv = cv::Mat::zeros(cv::Size(600, 800), CV_32FC2);
//do some calculations on vv
//opencv version of the for loop
for (int y = 0; y < vv.rows; y++)
{
for (int x = 0; x < vv.cols; x++)
{
//access indices (y,x,1) and (y,x,2)
cv::Vec2f wVec = vv.at<cv::Vec2f>(cv::Point(x, y));
//calculates the norm
cv::Point3f w(3, wVec[0], wVec[1]);
double normW = cv::norm(w);
//divides w by it's norm. don't forget to verify that normW is not 0
w = w / normW;
//do something with the calculated w vector
}
}
I am trying to do Delaunay Triangulation for a set of points in OpenCV, but encountered a problem.
The function takes a matrix of coordinates and return an adjacency matrix. (If there is and edge connecting the point i and the point j, then adj(i,j) = 1, otherwise 0.)
I didn't get it working. The code below give strange results.
Could you please help?
An example of Delaunay Triangulation is given here.
Thank you in advance.
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
Mat delaunay(const Mat& points, int imRows, int imCols)
/// Return the Delaunay triangulation, under the form of an adjacency matrix
/// points is a Nx2 mat containing the coordinates (x, y) of the points
{
Mat adj(points.rows, points.rows, CV_32S, Scalar(0));
/// Create subdiv and insert the points to it
Subdiv2D subdiv(Rect(0,0,imCols,imRows));
for(int p = 0; p < points.rows; p++)
{
float xp = points.at<float>(p, 0);
float yp = points.at<float>(p, 1);
Point2f fp(xp, yp);
subdiv.insert(fp);
}
/// Get the number of edges
vector<Vec4f> edgeList;
subdiv.getEdgeList(edgeList);
int nE = edgeList.size();
/// Check adjacency
for(int e = 1; e <= nE; e++)
{
int p = subdiv.edgeOrg(e); // Edge's origin
int q = subdiv.edgeDst(e); // Edge's destination
if(p < points.rows && q < points.rows)
adj.at<int>(p, q) = 1;
// else
// {
// cout<<p<<", "<<q<<endl;
// assert(p < points.rows && q < points.rows);
// }
}
return adj;
}
int main()
{
Mat points = Mat(100, 2, CV_32F);
randu(points, 0, 99);
int rows = 100, cols = 100;
Mat im(rows, cols, CV_8UC3, Scalar::all(0));
Mat adj = delaunay(points, rows, cols);
for(int i = 0; i < points.rows; i++)
{
int xi = points.at<float>(i,0);
int yi = points.at<float>(i,1);
/// Draw the edges
for(int j = i+1; j < points.rows; j++)
{
if(adj.at<int>(i,j) > 0)
{
int xj = points.at<float>(j,0);
int yj = points.at<float>(j,1);
line(im, Point(xi,yi), Point(xj,yj), Scalar(255,0,0), 1);
}
/// Draw the nodes
circle(im, Point(xi, yi), 1, Scalar(0,0,255), -1);
}
}
namedWindow("im", CV_WINDOW_NORMAL);
imshow("im",im);
waitKey();
return 0;
}
You are inserting into the adjacency matrix the indices of the Subdiv2d edges, which don't correspond to the indices of the points.
You can fix this, for example, storing the points and their index into a std::map. When you retrieve edges from the Subdiv2d, you check that the edges is formed by your points, and not from boundary points added by Subdiv2d. Having stored the point indices, you're now able to built the adjacency matrix correctly.
Have a look at the code:
#include <map>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
struct lessPoint2f
{
bool operator()(const Point2f& lhs, const Point2f& rhs) const
{
return (lhs.x == rhs.x) ? (lhs.y < rhs.y) : (lhs.x < rhs.x);
}
};
Mat delaunay(const Mat1f& points, int imRows, int imCols)
/// Return the Delaunay triangulation, under the form of an adjacency matrix
/// points is a Nx2 mat containing the coordinates (x, y) of the points
{
map<Point2f, int, lessPoint2f> mappts;
Mat1b adj(points.rows, points.rows, uchar(0));
/// Create subdiv and insert the points to it
Subdiv2D subdiv(Rect(0, 0, imCols, imRows));
for (int p = 0; p < points.rows; p++)
{
float xp = points(p, 0);
float yp = points(p, 1);
Point2f fp(xp, yp);
// Don't add duplicates
if (mappts.count(fp) == 0)
{
// Save point and index
mappts[fp] = p;
subdiv.insert(fp);
}
}
/// Get the number of edges
vector<Vec4f> edgeList;
subdiv.getEdgeList(edgeList);
int nE = edgeList.size();
/// Check adjacency
for (int i = 0; i < nE; i++)
{
Vec4f e = edgeList[i];
Point2f pt0(e[0], e[1]);
Point2f pt1(e[2], e[3]);
if (mappts.count(pt0) == 0 || mappts.count(pt1) == 0) {
// Not a valid point
continue;
}
int idx0 = mappts[pt0];
int idx1 = mappts[pt1];
// Symmetric matrix
adj(idx0, idx1) = 1;
adj(idx1, idx0) = 1;
}
return adj;
}
int main()
{
Mat1f points(10, 2);
randu(points, 0, 99);
int rows = 100, cols = 100;
Mat3b im(rows, cols, Vec3b(0,0,0));
Mat1b adj = delaunay(points, rows, cols);
for (int i = 0; i < points.rows; i++)
{
int xi = points.at<float>(i, 0);
int yi = points.at<float>(i, 1);
/// Draw the edges
for (int j = i + 1; j < points.rows; j++)
{
if (adj(i, j))
{
int xj = points(j, 0);
int yj = points(j, 1);
line(im, Point(xi, yi), Point(xj, yj), Scalar(255, 0, 0), 1);
}
}
}
for (int i = 0; i < points.rows; i++)
{
int xi = points(i, 0);
int yi = points(i, 1);
/// Draw the nodes
circle(im, Point(xi, yi), 1, Scalar(0, 0, 255), -1);
}
imshow("im", im);
waitKey();
return 0;
}
How can I flatten a 3D Matrix and display it in 2d?
Are there simple ways to display it in 3d?
Edit:
So far I simply tile the images in the 3rd dimension together like thus:
void Flatten3DArray(const cv::Mat& In, cv::Mat& Out2d)
{
CV_Assert(In.dims == 3);
int rows = In.size[0];
int cols = In.size[1];
int third = In.size[2];
int rowTiles = ceil(sqrt(third));
int colTiles = ceil(sqrt(third));
Out2d.create(rowTiles*rows, colTiles*cols, In.type());
Out2d = Scalar(0);
int thirdDimIdx = 0;
for (int i = 0; i < rowTiles; ++i)
{
for (int j = 0; j < colTiles; ++j, ++thirdDimIdx)
{
if (thirdDimIdx >= third)
{
break;
}
Mat roi(Out2d(cv::Rect(j*cols, i*rows, cols, rows)));
uint16_t *ind = (uint16_t*)In.data + thirdDimIdx * rows*cols; // sub-matrix pointer
cv::Mat subMatrix(2, In.size, In.type(), ind);
subMatrix.copyTo(roi);
}
}
}
Is there a better way to do this?
I tried to follow this method of drawing orientation map http://answers.opencv.org/question/9493/fingerprint-orientation-map-through-gradient/
And i used a block size of 5x5 on my 480x320 image. The gradients i got was from 0-270 degrees. And there are constant values that keep on repeating like 44.7623 and 224.762. I wonder if my gradients are wrong.
After that, i add all the gradients in the 5x5 block and divide them by 25 (averaging) like what the link said. I divided the degrees into 8 sections of 45degree intervals and plotted them out. But it looks nothing like my original image. Can anyone tell me what's wrong? I just want to detect the core(circle-like) feature of the image.
My original image is this _________________________________________________ But my orientation map is this:
This is what I'm doing
What's wrong ? =(
I got the gradients from this method :
/// Gradient X
cv::Sobel(original_Mat, grad_x, CV_32FC1, 1, 0, 3);
/// Gradient Y
cv::Sobel(original_Mat, grad_y, CV_32FC1, 0, 1, 3);
Mat orientation = Mat(grad_x.rows, grad_y.cols, CV_32F);
for(int i = 0; i < grad_x.rows; i++){
for(int j = 0; j < grad_x.cols; j++){
// Retrieve a single value
float valueX = grad_x.at<float>(i,j);
float valueY = grad_x.at<float>(i,j);
// Calculate the corresponding single direction, done by applying the arctangens function
float result = fastAtan2(valueX,valueY);
// Store in orientation matrix element
orientation.at<float>(i,j) = result;
}
}
Here's the full code.
int main()
{
cv::Mat original_Mat=cv::imread("Source.bmp", 1);
cv::Mat grad = cv::Mat::zeros(original_Mat.size(),CV_64F);
/// Generate grad_x and grad_y
cv::Mat grad_x = cv::Mat::zeros(original_Mat.size(), CV_64F);
cv::Mat grad_y = cv::Mat::zeros(original_Mat.size(), CV_64F);
cv::Mat grad_angle = cv::Mat::zeros(original_Mat.size(), CV_64F);
/// Gradient X
cv::Sobel(original_Mat, grad_x, CV_32FC1, 1, 0, 3);
/// Gradient Y
cv::Sobel(original_Mat, grad_y, CV_32FC1, 0, 1, 3);
Mat orientation = Mat(grad_x.rows, grad_y.cols, CV_32F); //to store the gradients
Mat img=Mat(grad_x.rows, grad_y.cols, CV_32F);//to draw out the map
img = cv::Scalar(255,255,255);//all white
// Calculate orientations of gradients --> in degrees
// Loop over all matrix values and calculate the accompanied orientation
for(int i = 0; i < grad_x.rows; i++){
for(int j = 0; j < grad_x.cols; j++){
// Retrieve a single value
float valueX = grad_x.at<float>(i,j);
float valueY = grad_x.at<float>(i,j);
// Calculate the corresponding single direction, done by applying the arctangens function
float result = fastAtan2(valueX,valueY);
// Store in orientation matrix element
orientation.at<float>(i,j) = result;
}
}
int i=0,j=0;
int x1=0,x2=0;
float results;
for(int l=0;l<96;l++) //to loop all the rows
{
int x1=(5+(l*5)); // to get 5x5 block sizes
for(int k=0;k<64;k++)//to loop all the columns
{
int x2=(5+(k*5)); // to get 5x5 block sizes
results=0;
//to get the total of 5x5 gradient values
for(i=(x1-5); i < x1; i++){
for(j=(x2-5); j < x2; j++){
results=results+orientation.at<float>(i,j);
orientation.at<float>(i,j)=0;
}
}
results=results/25; //averaging the 5x5 block gradients
orientation.at<float>((x1-3),(x2-3))=results; //to store the results in the center of the 5x5 block
}
}
results=0;
//this loop is to draw out the orientation map
for(int i=0;i<480;i++)
{
for(int j=0;j<320;j++)
{
results=orientation.at<float>(i,j);
if ((results<=22.5)&&(results>0)){
results=0;
img.at<int>(i,j)=255;
img.at<int>(i,j+1)=255;
img.at<int>(i,j+2)=255;
}
else if((results>22.5)&&(results<=67.5)){
results=45;
img.at<int>(i,j)=255;
img.at<int>(i-1,j+1)=255;
img.at<int>(i-2,j+2)=255;
}
else if((results>67.5)&&(results<=112.5)){
results=90;
img.at<int>(i,j)=255;
img.at<int>(i-1,j)=255;
img.at<int>(i-2,j)=255;
}
else if((results>112.5)&&(results<=157.5)){
results=135;
img.at<int>(i,j)=255;
img.at<int>(i-1,j-1)=255;
img.at<int>(i-2,j-2)=255;
}
else if((results>157.5)&&(results<=202.5)){
results=180;
img.at<int>(i,j)=255;
img.at<int>(i,j-1)=255;
img.at<int>(i,j-2)=255;
}
else if((results>202.5)&&(results<=247.5)){
results=225;
img.at<int>(i,j)=255;
img.at<int>(i+1,j-1)=255;
img.at<int>(i+2,j-2)=255;
endx=x2-5;
endy=x1-1;
}
else if((results>247.5)&&(results<=292.5)){
results=270;
img.at<int>(i,j)=255;
img.at<int>(i+1,j)=255;
img.at<int>(i+2,j)=255;
}
else if((results>292.5)&&(results<=337.5)){
results=315;
img.at<int>(i,j)=255;
img.at<int>(i+1,j+1)=255;
img.at<int>(i+2,j+2)=255;
}
else
{
results=0;
}
orientation.at<float>(i,j)=results;
}
}
Here is my result:
For image:
I've got result:
The code:
#include <stdio.h>
#include <stdarg.h>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
namedWindow("source");
namedWindow("result");
namedWindow("ang");
Mat img=imread("D:\\ImagesForTest\\binarized_image.png",0);
cv::threshold(img,img,128,255,cv::THRESH_BINARY);
Mat thinned;
thinned=img.clone(); // Just clone the input
//Thinning(img,thinned); // Not actually needed
cv::GaussianBlur(thinned,thinned,Size(3,3),1.0);
Mat gx,gy,ang,mag;
cv::Sobel(thinned,gx,CV_32FC1,1,0);
cv::Sobel(thinned,gy,CV_32FC1,0,1);
cv::phase(gx,gy,ang,false);
cv::magnitude(gx,gy,mag);
cv::normalize(mag,mag,0,1,cv::NORM_MINMAX);
Mat angRes=Mat::zeros(img.rows*3,img.cols*3,CV_8UC1);
for (int i=0;i< img.rows;i+=2)
{
for (int j=0;j< img.cols;j+=2)
{
int x=j*3;
int y=i*3;
float r=5;
float m=r*(mag.at<float>(i,j));
float dx=m*r*cos(ang.at<float>(i,j));
float dy=m*r*sin(ang.at<float>(i,j));
cv::line(angRes,cv::Point(x,y),cv::Point(x+dx,y+dy),Scalar::all(255),1,CV_AA);
}
}
imshow("ang",angRes);
imshow("source",img);
imshow("result",thinned);
cv::waitKey(0);
}
Enother variant (weighted block averages):
#include <stdio.h>
#include <stdarg.h>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
float GetWeightedAngle(Mat& mag,Mat& ang)
{
float res=0;
float n=0;
for (int i=0;i< mag.rows;++i)
{
for (int j=0;j< mag.cols;++j)
{
res+=ang.at<float>(i,j)*mag.at<float>(i,j);
n+=mag.at<float>(i,j);
}
}
res/=n;
return res;
}
int main(int argc, char* argv[])
{
namedWindow("source");
namedWindow("ang");
Mat img=imread("D:\\ImagesForTest\\binarized_image.png",0);
cv::threshold(img,img,128,255,cv::THRESH_BINARY);
Mat thinned;
thinned=img.clone();
//Thinning(img,thinned);
//cv::GaussianBlur(thinned,thinned,Size(3,3),1.0);
Mat gx,gy,ang,mag;
cv::Sobel(thinned,gx,CV_32FC1,1,0,7);
cv::Sobel(thinned,gy,CV_32FC1,0,1,7);
cv::phase(gx,gy,ang,false);
cv::magnitude(gx,gy,mag);
cv::normalize(mag,mag,0,1,cv::NORM_MINMAX);
Mat angRes=Mat::zeros(img.rows,img.cols,CV_8UC1);
int blockSize=img.cols/15-1;
float r=blockSize;
for (int i=0;i< img.rows-blockSize;i+= blockSize)
{
for (int j=0;j< img.cols-blockSize;j+= blockSize)
{
float a=GetWeightedAngle(mag(Rect(j,i,blockSize,blockSize)),ang(Rect(j,i,blockSize,blockSize)));
float dx=r*cos(a);
float dy=r*sin(a);
int x=j;
int y=i;
cv::line(angRes,cv::Point(x,y),cv::Point(x+dx,y+dy),Scalar::all(255),1,CV_AA);
}
}
imshow("ang",angRes);
imshow("source",img);
cv::waitKey(0);
}
It gives the result image: