Opencv multiply scalar and matrix - opencv

I have been trying to achieve something which should pretty trivial and is trivial in Matlab.
Using methods of OpenCV, I want to simply achieve something such as:
cv::Mat sample = [4 5 6; 4 2 5; 1 4 2];
sample = 5*sample;
After which sample should just be:
[20 25 30; 20 10 25; 5 20 10]
I have tried scaleAdd, Mul, Multiply and neither allow a scalar multiplier and require a matrix of the same "size and type". In this scenario I could create a Matrix of Ones and then use the scale parameter but that seems so very extraneous
Any direct simple method would be great!

OpenCV does in fact support multiplication by a scalar value with overloaded operator*. You might need to initialize the matrix correctly, though.
float data[] = {1 ,2, 3,
4, 5, 6,
7, 8, 9};
cv::Mat m(3, 3, CV_32FC1, data);
m = 3*m; // This works just fine
If you are mainly interested in mathematical operations, cv::Matx is a little easier to work with:
cv::Matx33f mx(1,2,3,
4,5,6,
7,8,9);
mx *= 4; // This works too

For java there is no operator overloading, but the Mat object provides the functionality with a convertTo method.
Mat dst= new Mat(src.rows(),src.cols(),src.type());
src.convertTo(dst,-1,scale,offset);
Doc on this method is here

For big Mats you should use forEach.
If C++11 is available:
m.forEach<double>([&](double& element, const int position[]) -> void
{
element *= 5;
}
);

something like this.
Mat m = (Mat_<float>(3, 3)<<
1, 2, 3,
4, 5, 6,
7, 8, 9)*5;

Mat A = //data;//source Matrix
Mat B;//destination Matrix
Scalar alpha = new Scalar(5)//factor
Core.multiply(A,alpha,b);

Related

Simple Way to Scale Channels in OpenCV

It seems that given a multi-channel image img I cannot do this:
img *= cv::Scalar(1.5,0.5,2.1);
I'd like to scale each channel by a different float factor.
Is there a simple way to do this?
I could use cv::transform() but that seems like overkill (I also obviously don't want to manually and explicitly iterate on all the pixels).
Any suggestions?
You can use multiply:
cv::Mat3b m = ... ;
cv::multiply(m, cv::Scalar(2, 3, 4), m);
or, as suggested by #AdiShavit:
cv::Mat3b m = ... ;
m = m.mul(cv::Scalar(2, 3, 4));

Convert several 1D mat to a single 2D mat in OpenCV [duplicate]

I have three matrices, each of size 4x1. I want to copy all of these matrices to another matrix of size 4x3 and call it R. Is there a smart way to do it?
You can just use hconcat for horizontal concatenation. You can use it per matrix, e.g. hconcat( mat1, mat2, R ), or apply it directly on a vector or array of matrices.
Here's a sample code:
vector<Mat> matrices = {
Mat(4, 1, CV_8UC1, Scalar(1)),
Mat(4, 1, CV_8UC1, Scalar(2)),
Mat(4, 1, CV_8UC1, Scalar(3)),
};
Mat R;
hconcat( matrices, R );
cout << R << endl;
Here's the result:
[1, 2, 3;
1, 2, 3;
1, 2, 3;
1, 2, 3]
Program ended with exit code: 1
Similarly, if you want to do it vertically (stack by rows), use vconcat.
You can use
Mat R(3, 4, CV_32F); // [3 rows x 4 cols] with float values
mat1.copyTo(R.row(0));
mat2.copyTo(R.row(1));
mat3.copyTo(R.row(2));
or
Mat R(4, 3, CV_32F); // [4 rows x 3 cols] with float values
mat1.copyTo(R.col(0));
mat2.copyTo(R.col(1));
mat3.copyTo(R.col(2));
Alternatively, as #sub_o suggested, you can also use hconcat()/vconcat() to concatenate matrices.
For those using OpenCv in Python, if you have arrays A, B, and C, and want array D that is horizontal concatenation of the others:
D = cv2.hconcat((A, B, C))
There is also a vconcat method.

cvCalibrateCamera2 - how to properly define rotation matrix?

I try to use cvCalibrateCamera2, but I get error that rotation matrix is not properly defined:
...calibration.cpp:1495: error: (-5) the output array of rotation vectors must be 3-channel 1xn or nx1 array or 1-channel nx3 or nx9 array, where n is the number of views
I have already tried all possibilities from that info but I still get this error.
My code:
CvMat *object_points = cvCreateMat((int)pp.object_points.size(), 1, CV_32FC3);
CvMat *image_points = cvCreateMat((int)pp.image_points.size(), 1, CV_32FC2);
const CvMat point_counts = cvMat((int)pp.point_counts.size(), 1, CV_32SC1, &pp.point_counts[0]);
for (size_t i=0; i<pp.object_points.size(); i++)
{
object_points->data.fl[i*3+0] = (float)pp.object_points[i].x;
object_points->data.fl[i*3+1] = (float)pp.object_points[i].y;
object_points->data.fl[i*3+2] = (float)pp.object_points[i].z;
image_points->data.fl[i*2+0] = (float)pp.image_points[i].x;
image_points->data.fl[i*2+1] = (float)pp.image_points[i].y;
}
CvMat* tempR = cvCreateMat(1, 3, CV_32F);
cvCalibrateCamera2(object_points, image_points, &point_counts,
cvSize(pp.width, pp.height), camera->m_calib_K,
camera->m_calib_D, tempR, &tempData->m_calib_T,
CV_CALIB_USE_INTRINSIC_GUESS)
// camera->calib_T is defined as:
// double m_calib_T_data[3];
// cvMat(3, 1, CV_64F, camera->m_calib_T_data);
I thought that rotation matrix used in cvCalibrateCamera2 should be 1x3 (then I want to use Rodrigues function to get 3x3 matrix) but it doesn't work as any other combination mentioned in error.
Any ideas?
And I use opencv 2.4.0 (maybe there is bug in that method, but for some reasons I can't use later version of opencv)
I think the statement is clear. I am not confident with C# but I know it requires a strong initialization.
The problem in line
CvMat* tempR = cvCreateMat(1, 3, CV_32F);
is that tempR should have a line 1x3 for every N objects point you use. In this sense, the statement becomes clear
...calibration.cpp:1495: error: (-5) the output array of rotation
vectors must be 3-channel 1xn or nx1 array or 1-channel nx3 or nx9
array, where n is the number of views
You must create a tempR like that (more or less, I do not know how to calculate N in C#)
CvMat* tempR = cvCreateMat(N, 3, CV_32F);
Try to extract N from dimensions of object.point.size. If it does not work, try image.point.size

Generate random numbers matrix in OpenCV

I want to know how can I generate a matrix of random numbers of any given size, for example 2x4. Matrix should consists of signed whole number in range, for example [-500, +500].
I have read the documentation of RNG, but I am not sure on how I should use this.
I referred too this question but this did not provide me the solution I am looking for.
I know this might be a silly question, but any help on it would be truly appreciated.
If you want values to be uniformly distributed, you can use cv::randu
Mat1d mat(2, 4); // Or: Mat mat(2, 4, CV_64FC1);
double low = -500.0;
double high = +500.0;
randu(mat, Scalar(low), Scalar(high));
Note that the upper bound is exclusive, so this example represents data in range [-500, +500).
If you want values to be normally distributed, you can use cv::randn
Mat1d mat(2, 4); // Or: Mat mat(2, 4, CV_64FC1);
double mean = 0.0;
double stddev = 500.0 / 3.0; // 99.7% of values will be inside [-500, +500] interval
randn(mat, Scalar(mean), Scalar(stddev));
This works for matrices up to 4 channels, e.g.:
Mat3b random_image(100,100);
randu(random_image, Scalar(0,0,0), Scalar(256,256,256));

Column-Wise Standard Deviation in OpenCV

Is there a direct way to compute the column-wise standard deviation for a matrix in opencv? Similar to std in Matlab. I've found one for the mean:
cv::Mat col_mean;
reduce(A, col_mean, 1, CV_REDUCE_AVG);
but I cannot find such a function for the standard deviation.
Here's a quick answer to what you're looking for. I added both the standard deviation and mean for each column. The code can easily be modified for rows.
cv::Mat A = ...; // FILL IN THE DATA FOR YOUR INPUT MATRIX
cv::Mat meanValue, stdValue;
cv::Mat colSTD(1, A.cols, CV_64FC1);
cv::Mat colMEAN(1, A.cols, CV_64FC1);
for (int i = 0; i < A.cols; i++){
cv::meanStdDev(A.col(i), meanValue, stdValue);
colSTD.at<double>(i) = stdValue.at<double>(0);
colMEAN.at<double>(i) = meanValue.at<double>(0);
}
The following is not in a single line,but it is another version without loops:
reduce(A, meanOfEachCol, 0, CV_REDUCE_AVG); // produces single row of columnar means
Mat repColMean;
cv::repeat(meanOfEachCol, rows, 1, repColMean); // repeat mean vector 'rows' times
Mat diffMean = A - repColMean; // get difference
Mat diffMean2 = diffMean.mul(diffMean); // per element square
Mat varMeanF;
cv::reduce(diffMean2, varMeanF, 0, CV_REDUCE_AVG); // sum each column's elements to get single row
Mat stdMeanF;
cv::sqrt(varMeanF, stdMeanF); // get standard deviation

Resources