I have an integer matrix and I want to perform an integer division on it. But opencv always rounds the result.
I know I can divide each element manually but I want to know is there a better way for this or not?
Mat c = (Mat_ <int> (1,3) << 80,71,64 );
cout << c/8 << endl;
// result
//[10, 9, 8]
// desired result
//[10, 8, 8]
Similar to #GPPK's optional method, you can hack it by:
Mat tmp, dst;
c.convertTo(tmp, CV_64F);
tmp = tmp / 8 - 0.5; // simulate to prevent rounding by -0.5
tmp.convertTo(dst, CV_32S);
cout << dst;
The problem is with using ints, you cant have decimal points with ints so I'm not sure how you are expecting not to get rounding.
You really have two options here, I do not think you can this without using one of these options:
You have a mathematically correct int matrix division [10, 9, 8]
You spin up your own divide function in order to give you the result you want.
Option 2:
Pseudocode:
Create a double matrix
perform the division to get the output [10.0, 8.875, 8.0]
strip away any numbers after a decimal point [10.0, 8.0, 8.0]
(optional) write these values back to a int matrix
(result) [10, 8, 8]
Related
I have an Nx3 Eigen matrix.
I have an Nx1 Egein marix.
I'm trying to get the coefficient multiplication of each row in the Nx3 by the corresponding scal in the Nx1 so I can scale a bunch of 3d vectors.
I'm sure I'm overlooking something obvious but I can't get it to work.
#include <Eigen/Dense>
MatrixXf m(4, 3);
m << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
MatrixXf dots(4, 1)
dots << 2,2,2,2;
I want to resulting matrix to be Nx3 like so:
2,4,6
8,10,12,
14,16,18,
20,22,24
You can use broadcasting:
m = m.colwise().cwiseProduct(dots);
or observe that all you want to do is to apply a non uniform scaling:
m = dots.asDiagonal() * m;
Both expressions will generate similar code.
Okay, so I got something working. I'm probably doing something wrong but this worked for me so I thought I would share. I wrote my first line of c++ a week ago so I figure I deserve some grace. Anyone with a better solution is encouraged to post.
// scalar/coefficient multiplication (not matrix) on Nx3 x N. For multiplying dot products by vectors
void N3xNcoefIP(MatrixXf &A, MatrixXf &B) {
A.array() *= B.replicate(1, A.size()).array();
}
I am working on a c++ codebase right now which uses a matrix library to calculate various things. One of those things is calculating the inverse of a matrix. It uses gauss elimation to achieve that. But the result is very inaccurate. So much so that multiplying the inverse matrix with the original matrix isn't even close the the identity matrix.
Here is the code that is used to calculate the inverse, the matrix is templated on a numerical type and the rows and columns:
/// \brief Take the inverse of the matrix.
/// \return A new matrix which is the inverse of the current one.
matrix<T, M, M> inverse() const
{
static_assert(M == N, "Inverse matrix is only defined for square matrices.");
// augmented the current matrix with the identiy matrix.
auto augmented = this->augment(matrix<T, M, M>::get_identity());
for (std::size_t i = 0; i < M; i++)
{
// divide the current row by the diagonal element.
auto divisor = augmented[i][i];
for (std::size_t j = 0; j < 2 * M; j++)
{
augmented[i][j] /= divisor;
}
// For each element in the column of the diagonal element that is currently selected
// set all element in that column to 0 except the diagonal element by using the currently selected row diagonal element.
for (std::size_t j = 0; j < M; j++)
{
if (i == j)
{
continue;
}
auto multiplier = augmented[j][i];
for (std::size_t k = 0; k < 2 * M; k++)
{
augmented[j][k] -= multiplier * augmented[i][k];
}
}
}
// Slice of the the new identity matrix on the left side.
return augmented.template slice<0, M, M, M>();
}
Now I have made a unit test which test if the inverse is correct using pre computed values. I try two matrices one 3x3 and one 4x4. I used this website to compute the inverse: https://matrix.reshish.com/ and they do match to a certain degree. since the unit test does succeed. But once I calculate the original matrix * the inverse nothing even resembling an identity matrix is achieved. See the comment in the code below.
BOOST_AUTO_TEST_CASE(matrix_inverse)
{
auto m1 = matrix<double, 3, 3>({
{7, 8, 9},
{10, 11, 12},
{13, 14, 15}
});
auto inverse_result1 = matrix<double,3, 3>({
{264917625139441.28, -529835250278885.3, 264917625139443.47},
{-529835250278883.75, 1059670500557768, -529835250278884.1},
{264917625139442.4, -529835250278882.94, 264917625139440.94}
});
auto m2 = matrix<double, 4, 4>({
{7, 8, 9, 23},
{10, 11, 12, 81},
{13, 14, 15, 11},
{1, 73, 42, 65}
});
auto inverse_result2 = matrix<double, 4, 4>({
{-0.928094660194201, 0.21541262135922956, 0.4117111650485529, -0.009708737864078209},
{-0.9641231796116679, 0.20979975728155775, 0.3562651699029188, 0.019417475728154842},
{1.7099261731391882, -0.39396237864078376, -0.6169346682848 , -0.009708737864076772 },
{-0.007812499999999244, 0.01562499999999983, -0.007812500000000278, 0}
});
// std::cout << (m1.inverse() * m1) << std::endl;
// results in
// 0.500000000 1.000000000 -0.500000000
// 1.000000000 0.000000000 0.500000000
// 0.500000000 -1.000000000 1.000000000
// std::cout << (m2.inverse() * m2) << std::endl;
// results in
// 0.396541262 -0.646237864 -0.689016990 -2.162317961
// 1.206917476 2.292475728 1.378033981 3.324635922
// -0.884708738 -0.958737864 -0.032766990 -3.756067961
// -0.000000000 -0.000000000 -0.000000000 1.000000000
BOOST_REQUIRE_MESSAGE(
m1.inverse().fuzzy_equal(inverse_result1, 0.1) == true,
"3x3 inverse is not the expected result."
);
BOOST_REQUIRE_MESSAGE(
m2.inverse().fuzzy_equal(inverse_result2, 0.1) == true,
"4x4 inverse is not the expected result."
);
}
I am at my wits end. I am by no means a specialist on matrix math since I had to learn it all on the job but this really is stumping me.
The complete code matrix class is available at:
https://codeshare.io/johnsmith
Line 404 is where the inverse function is located.
Any help is appreciated.
As already established in the comments the matrix of interest is singular and thus there is no inverse.
Great, your testing found already the first issue in the code - this case isn't handled properly, no error is raised.
The bigger problem is, that this is not easy to detect: If there where no errors due to rounding errors, it would be a cake of piece - just test that divisor isn't 0! But there are rounding errors in floating operations, so divisor will be a very small nonzero number.
And there is no way to tell, whether this nonzero value due to rounding errors or to the fact that the matrix is near singular (but not singular). However, if matrix is near singular it has a poor condition and thus the results cannot be trusted anyway.
So ideally, the algorithm should not only calculate the inverse, but also (estimate) the condition of the original matrix, so the caller can react upon a bad condition.
Probably it is wise to use well-known and well-tested libraries for this kind of calculation - there is a lot to be considered and what can be done wrong.
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)
I am using C++ and opencv. I have to obtain a transformation matrix when I multiply a matrix,A, with another matrix,B. But matrix B needs to change before multiplying it to A. If B is a 2x3 matrix, it needs to be changed to a 3x3 with the first 2 rows containing the same elements as the original B matrix,but with the last row having all 1's. More simple put,I need to add a last row of 1's to the original B matrix. I want to know whether I can achieve this with any specific Mat matrix operation. Thankyou
You need to use Mat::push_back which will adds elements to the bottom of the matrix.
For example
Mat A = (Mat_<uchar>(3,4) << 1, 2, 3, 4,\
5, 6, 7, 8,\
9, 10, 11, 12); // 3X4 matrix.
Mat B = (Mat_<uchar>(1,4) << 13, 14, 15, 16); // 1X4 matrix
A.push_back(B); // Now A become 4X4 matrix
A straight forward way, but probably not the fastest or prettiest
Mat B_new(3,3,CV_32F);
B_new.row(0) = B.row(0);
B_new.row(1) = B.row(1);
B_new.row(2) = Mat::ones(1,3,CV_32F);
You should take a look at the Mat type documentation
In process of speeding up some processes (can't name them, sorry), I tried to create a
cv::Mat_<uchar> discretization;
Now when I get a depth map in float
cv::Mat_<float> depth_map;
discretization = depth_map / resolution_mtr;
where resolution_mtr is a float. Its value is 0.1 currently.
When I do this, for a value say, 0.48 in depth map , I get the discretization value of 5. My understanding says it should be 4 . I guess it is round off to nearest uchar. Is there a way out of this without getting into for loop ?
Basically I want to use floor values in discretization and not round off .
Why not define an inherited class CvNoRoundMat and override its operator+ ?
You can just subtract 0.5 from the result.
This code
float resolution_mtr = 0.1;
float vals[] = {0.48, 0.4, 0.38, 0.31};
cv::Mat_<float> depth_map(1,4,vals);
cv::Mat_<uchar> discretization( depth_map / resolution_mtr - 0.5);
std::cout << "depth_map: " << depth_map << std::endl;
std::cout << "discretization: " << discretization << std::endl;
will give you following results:
depth_map: [0.47999999, 0.40000001, 0.38, 0.31]
discretization: [4, 4, 3, 3]