I want to build a 3 channel matrix from a 1D array of data of arbitrary data type, row dimension, column dimension, and channel dimension. In my example, I have a 1x12 double array of data, and I want to transform it into a 2x2x3 OpenCv matrix.
double rawData[] = {1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0};
My goal is to to have :
Channel 1:
[ 1, 1;
1, 1]
Channel 2:
[ 2, 2;
2, 2]
Channel 3:
[ 3, 3;
3, 3]
This is what I've tried:
cv::Mat aMat = cv::Mat(2, 2, CV_64FC3, rawData)
But OpenCv doesn't agree with how it should use that rawData buffer. When I split the matrix by channels and print each individual channel with :
cv::Mat channels[3];
cv::split(aMat ,channels);
It does:
Channel 1:
[ 1, 1;
2, 3]
Channel 2:
[ 1, 2;
2, 3]
Channel 3:
[ 1, 2;
3, 3]
I've also tried loading the data in a 1D Mat and then using cv::Reshape but the result is the same.
How can I get the channels to look the way I want? Do I really have to manually assign every index or is there a more elegant/optimized way?
Edit: Problem Constraints
The order of the data in rawData should not be rearranged, as that would introduce additional complexity to determine the dimensions and data type. I would prefer to use OpenCV interfaces if possible.
My solution is close to berak's, with splitting the data into separate channels. Except one important (I think) difference. I initialize a single row matrix with the datatype already encoded, ie:
cv::Mat aMat = cv::Mat(1, 12, CV_64F, data);
I then use the colRange method to extract parts of the column:
std::vector<cv::Mat> channelVector(3);
channelVector[0] = aMat.colRange(0,4);
channelVector[1] = aMat.colRange(4,8);
channelVector[2] = aMat.colRange(8,12);
I do this to avoid pointer arithmetic. The strength here is that I can have my data buffer be of type void* and I do not have to worry about first casting the pointer to the correct type in order to increment the buffer pointer.
I then iterate with some reshape action.
for(cv::Mat& channel : channelVector)
channel = channel.reshape(1,2);
And finally merge.
cv::Mat combinedMatrix;
cv::merge(channelVector, combinedMatrix);
This should be efficient, as these operations are O(1). I'm not sure about the merge, I think that actually copies data but I can't verify that... but I was going to clone() the final result anyway, so that works out for me.
just rearrange your data:
double rawData[] = {1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0};
or build seperate 'channel' Mats from your data, and later merge:
double rawData[] = {1.0, 1.0, 1.0, 1.0,
2.0, 2.0, 2.0, 2.0,
3.0, 3.0, 3.0, 3.0};
Mat chan[3] = {
Mat(2,2,CV_64F, rawData),
Mat(2,2,CV_64F, rawData+4),
Mat(2,2,CV_64F, rawData+8)
};
Mat merged;
cv::merge(chan,3,merged);
Related
I have a square matrix A
use LinearAlgebra;
proc main() {
var A = Matrix(
[4.0, 0.8, 1.1, 0.0, 2.0]
,[0.8, 9.0, 1.3, 1.0, 0.0]
,[1.1, 1.3, 1.0, 0.5, 1.7]
,[0.0, 1.0, 0.5, 4.0, 1.5]
,[2.0, 0.0, 1.7, 1.5, 16.0]
);
}
And I want to construct the diagonal matrix D = 1/sqrt(a_ii). It seems like I have have to extract the diagonal, then operate on each element. I expect this matrix is be very large and sparse, if that changes the answer.
Here's a solution using the LinearAlgebra module in 1.16 (pre-release):
use LinearAlgebra;
var A = Matrix(
[4.0, 0.8, 1.1, 0.0, 2.0],
[0.8, 9.0, 1.3, 1.0, 0.0],
[1.1, 1.3, 1.0, 0.5, 1.7],
[0.0, 1.0, 0.5, 4.0, 1.5],
[2.0, 0.0, 1.7, 1.5, 16.0]
);
var S = sqrt(1.0/diag(A));
// Type required because of promotion-flatting
// See Linear Algebra documentation for more details..
var B: A.type = diag(S);
writeln(B);
Did you try this approach?
use Math;
var D: [A.domain];
forall i in D.dim( 1 ) {
D[i,i] = 1 / Math.sqrt( A[i,i] ); // ought get fused-DIV!0 protection
}
( A.T.M. <TiO>-IDE has not so far fully functional the LinearAlgebra package, so cannot show you the results live, yet hope you would enjoy the way forwards )
Here's some code that works with a sparse diagonal array in version 1.15 today without linear algebra library support:
config const n = 10; // problem size; override with --n=1000 on command-line
const D = {1..n, 1..n}, // dense/conceptual matrix size
Diag: sparse subdomain(D) = genDiag(n); // sparse diagonal matrix
// iterator that yields indices describing the diagonal
iter genDiag(n) {
for i in 1..n do
yield (i,i);
}
// sparse diagonal matrix
var DiagMat: [Diag] real;
// assign sparse matrix elements in parallel
forall ((r,c), elem) in zip(Diag, DiagMat) do
elem = r + c/10.0;
// print sparse matrix elements serially
for (ind, elem) in zip(Diag, DiagMat) do
writeln("A[", ind, "] is ", elem);
// dense array
var Dense: [D] real;
// assign from sparse to dense
forall ij in D do
Dense[ij] = DiagMat[ij];
// print dense array
writeln(Dense);
I have a 3xN Mat data, which is saved in yaml file and looks like:
%YAML:1.0
data1: !!opencv-matrix
rows: 50
cols: 3
dt: d
data: [ 7.1709999084472656e+01, -2.5729999542236328e+01,
-1.4074000549316406e+02, 7.1680000305175781e+01,
-2.5729999542236328e+01, -1.4075000000000000e+02,
7.1639999389648438e+01, -2.5729999542236328e+01,
-1.4075000000000000e+02, 7.1680000305175781e+01,
-2.5729999542236328e+01, -1.4075000000000000e+02, ...
I want to reduce the dimension of my 3D data to 1D or rather 2D and after that visualize it on a QwtPlotCurve. In order to do that, I have implemented pca function under opencv, but have no idea how to get the calculated x and y coordinates from pca result:
int numOfComponents= 100;
PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, numOfComponents);
Mat mean= pca.mean.clone();
Mat eigenvalues= pca.eigenvalues.clone();
Mat eigenvectors= pca.eigenvectors.clone();
Here's an example of a 2D data set.
x=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2, 1, 1.5, 1.1];
y=[2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9];
We can write these arrays in OpenCV with the following code.
float X_array[]={2.5,0.5,2.2,1.9,3.1,2.3,2,1,1.5,1.1};
float Y_array[]={2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9};
cv::Mat x(10,1,CV_32F,X_array); //Copy X_array to Mat (PCA need Mat form)
cv::Mat y(10,1,CV_32F,Y_array); //Copy Y_array to Mat
Next, we will combine x and y into a unified cv::Mat data. Because the whole data must be in one place for PCA function to work, we have to combine our data. (If your data is in 2D format, such as as an image, then you can simply convert the 2D to 1D signals and combine them)
x.col(0).copyTo(data.col(0)); //copy x into first column of data
y.col(0).copyTo(data.col(1)); //copy y into second column of data
the data after the last code will look like the following:
data=
[2.5, 2.4;
0.5, 0.7;
2.2, 2.9;
1.9, 2.2;
3.1, 3;
2.3, 2.7;
2, 1.6;
1, 1.1;
1.5, 1.6;
1.1, 0.9]
With cv::PCA, we can calculate the eigenvalues and eigenvectors of the 2D signal.
cv::PCA pca(data, //Input Array Data
Mat(), //Mean of input array, if you don't want to pass it simply put Mat()
CV_PCA_DATA_AS_ROW, //int flag
2); // number of components that you want to retain(keep)
Mat mean=pca.mean; // get mean of Data in Mat form
Mat eigenvalue=pca.eigenvalues;
Mat eigenvectors=pca.eigenvectors;
our eigenValue and eigenvectors will be as the below:
EigenValue=
[1.155625;
0.044175029]
EigenVectors=
[0.67787337, 0.73517865;
0.73517865, -0.67787337]
As you can see in the eigenValue, the first-row value is 1.55 and is much bigger than 0.044. So in eigenvectors, the first row is most important than the second row and if you retain the correspond row in EigenVectors, you can have almost whole data in 1D (Simply you have compressed the data, but your 2D pattern available in new 1D data)
How we can Extract final Data??
To extract final data you can multiply the eigenVector by the original data and get new data, for example, if I want to convert my data to 1D, I can use the below code
Mat final=eigenvectors.row(0)*data.t(); //firts_row_in_eigenVector * tranpose(data)
In your example, if you want to convert 3D to 2D then set the dimension to retain 2, and if you want to convert to 1D then set this argument to 1 like the below
1D
int numOfComponents= 1;
PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, numOfComponents);
2
int numOfComponents= 2;
PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, numOfComponents);
everyone.
My question is if I have three arrays as following
float a[7] = {1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0};
float b[7] = {2.0, 2.0, 2.0, 2.0,
2.0, 2.0, 2.0};
float c[7] = {0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0};
And I want to perform element-wise multiply operation as following
c[i] = a[i] * b[i], i = 0, 1, ..., 6
For the first four element, I can use SSE intrinsics as following
__m128* sse_a = (__m128*) &a[0];
__m128* sse_b = (__m128*) &b[0];
__m128* sse_c = (__m128*) &c[0];
*sse_c = _mm_mul_ps(*sse_a, *sse_b);
And the content in c will be
c[0] = 2.0, c[1] = 4.0, c[2] = 6.0, c[3] = 8.0
c[4] = 0.0, c[5] = 0.0, c[6] = 0.0
Remaining three numbers in index 4, 5, and 6, I use following code
to perform element-wise multiply operation
sse_a = (__m128*) &a[4];
sse_b = (__m128*) &b[4];
sse_c = (__m128*) &c[4];
float mask[4] = {1.0, 1.0, 1.0, 0.0};
__m128* sse_mask = (__m128*) &mask[0];
*sse_c = _mm_add_ps( *sse_c,
_mm_mul_ps( _mm_mul_ps(*sse_a, *sse_b), *sse_mask ) );
And the content in c[4-6] will be
c[4] = 10.0, c[5] = 12.0, c[6] = 14.0, which is the expected result.
_mm_add_ps() add four floating-point in parallel, and the first, second, and third floating-point number are allocated in index 4, 5, and 6 in array a, b, and c respectively.
But the fourth floating-point number is not allocated to the arrays.
To avoid invalid memory access, I multiply on sse_mask to make the fourth number be zero before add the result back to sse_c (array c).
But I'm wondering whether it is safe?
Many thanks.
You seem to have the mathematical operations right but I'm really not sure using casts like you do is the way to go to load and store data in __m128 vars.
Loading and storing
To load data from an array to a __m128 variable, you should use either __m128 _mm_load_ps (float const* mem_addr) or __m128 _mm_loadu_ps (float const* mem_addr) . Pretty easy to figure what's what here, but a few precisions :
For operations involving an access or manipulation of memory, you usualy have two functions doing the same thing, for exemple load and loadu . The first requires your memory to be aligned on a 16-byte boundary, while the u version does not have this requirement. If you don't know about memory alignement, use the u versions.
You also have load_ps and load_pd. The difference : the s stands for single as in single precision (good old float), the d stands for double as in double precision. Of course, you can only puts two doubles per __m128 variable, but 4 floats.
So loading data from an array is pretty easy, just do : __m128* sse_a = _mm_loadu_ps(&a[0]);. Do the same for b, but for c that really depends. If you only want to have the result of the multiplication in it, it's useless to initialize it at 0, load it, then add the result of the multiplication to it then finally get it back.
You should use the pending operation of load for storing data which is void _mm_storeu_ps (float* mem_addr, __m128 a). So once the mutliplication is done and the result in sse_c, just do _mm_storeu_ps(&c[0#, sse_c) ;
Algorithm
The idea behind using the mask is good but you have something easier : load ans store data from a[3] (same for b and c). That way, it will have 4 elements, so there will be no need to use any mask? Yes one operation has already have done on the third element but that will be completely transparent : the store operation will just replace the old value with the new one. Since both are equal, that's not a problem.
One alternative is to store 8 elements in your array even if you need only 7. That way you don't have to worry about memory being allocated or not, no need for special logic like above for the cost of 3 floats, which is nothing on all recent computers.
I have the following MATLAB code which I want to transport into C++
Assume Gr is 2d matrix and 1/newscale == 0.5
Gr = imresize(Gr, 1 / newScale);
in the MATLAB documentation:
B = imresize(A, scale) returns image B that is scale times the size of
A. The input image A can be a grayscale, RGB, or binary image. If
scale is between 0 and 1.0, B is smaller than A. If scale is greater
than 1.0, B is larger than A.
So this means I will get a 2D matrix == matrix_width/2 and matrix_height/2
How do I calculate the values? The default according to the docs are coming from cubic interpolation for nearest 4X4.
I can't find a sample code for C++ that does the same. Can you please provide a link to such code?
I also found this OpenCV function, resize.
Does it do the same as the MATLAB one?
Yes, just be aware that MATLAB's imresize has anti-aliasing enabled by default:
imresize(A,scale,'bilinear')
vs. what you would get with cv::resize(), which does not have anti-aliasing:
imresize(A,scale,'bilinear','AntiAliasing',false)
And as Amro mentioned, the default in MATLAB is bicubic, so be sure to specify.
Bilinear
No code modifications are necessary to get matching results with bilinear interpolation.
Example OpenCV snippet:
cv::Mat src(4, 4, CV_32F);
for (int i = 0; i < 16; ++i)
src.at<float>(i) = i;
std::cout << src << std::endl;
cv::Mat dst;
cv::resize(src, dst, Size(0, 0), 0.5, 0.5, INTER_LINEAR);
std::cout << dst << std::endl;
Output (OpenCV)
[0, 1, 2, 3;
4, 5, 6, 7;
8, 9, 10, 11;
12, 13, 14, 15]
[2.5, 4.5;
10.5, 12.5]
MATLAB
>> M = reshape(0:15,4,4).';
>> imresize(M,0.5,'bilinear','AntiAliasing',true)
ans =
3.125 4.875
10.125 11.875
>> imresize(M,0.5,'bilinear','AntiAliasing',false)
ans =
2.5 4.5
10.5 12.5
Note that the results are the same with anti-aliasing turned off.
Bicubic Difference
However, between 'bicubic' and INTER_CUBIC, the results are different on account of the weighting scheme! See here for details on the mathematical difference. The issue is in the interpolateCubic() function that computes the cubic interpolant's coefficients, where a constant of a = -0.75 is used rather than a = -0.5 like in MATLAB. However, if you edit imgwarp.cpp and change the code :
static inline void interpolateCubic( float x, float* coeffs )
{
const float A = -0.75f;
...
to:
static inline void interpolateCubic( float x, float* coeffs )
{
const float A = -0.50f;
...
and rebuild OpenCV (tip: disable CUDA and the gpu module for short compile time), then you get the same results:
MATLAB
>> imresize(M,0.5,'bicubic','AntiAliasing',false)
ans =
2.1875 4.3125
10.6875 12.8125
OpenCV
[0, 1, 2, 3;
4, 5, 6, 7;
8, 9, 10, 11;
12, 13, 14, 15]
[2.1875, 4.3125;
10.6875, 12.8125]
More about cubic HERE.
In OpenCV, the call would be:
cv::Mat dst;
cv::resize(src, dst, Size(0,0), 0.5, 0.5, INTER_CUBIC);
You might then have to do some smoothing/blurring to emulate the anti-aliasing which MATLAB also performs by default (see #chappjc's answer)
Casting normal double* to a _m128d* is pretty easy and comprehensible.
Suppose you have an array like this:
double arr[8] = {1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0};
Then the _m128d presentation kinda would look like this:
_m128d m_arr[8] = { [1.0,2.0] , [3.0,4.0] , [5.0,6.0] , [7.0,8.0] };
because always 2 values are stored, if you can say that (that's how I imagine it).
But how will the values be split up if I use a 3x3 matrix instead??? E.g.:
double mat[3][3] = { {1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}, {1.0, 2.0, 3.0} };
I am trying to just sum up all values in a matrix but don't really get how to do this effictively with SSE, therefore I need to understand how the matrix is dealt with _m128d**.
Does anybody know?
The thing is that multidimensional arrays are stored in one continuous block of memory (the C standard guarantees this). So you can use a simple pointer to reference them:
double mat[3][3] = { {1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}, {1.0, 2.0, 3.0} };
double *ptr = (double *)mat;
Then you can iterate over the 9 numbers using this pointer and dereference it to obtain a double, which then can be cast/converted to another type.