I would like to do the following operation (which is at the current state in Matlab) using cv::Mat variables.
I have matrix mask:
mask =
1 0 0
1 0 1
then matrix M:
M =
1
2
3
4
5
6
3
and samples = M(mask,:)
samples =
1
2
6
My question is, how can I perform the same operation like, M(mask,:), with OpenCV?
With my knowledge the closet function to this thing is copyTo function in opencv that get matrix and mask for inputs. but this function hold original structure of your matrix you can test it.
I think there is no problem to use for loop in opencv(in c++) because it's fast. I propose to use for loop with below codes.
Mat M=(Mat_<uchar>(2,3)<<1,2,3,4,5,6); //Create M
cout<<M<<endl;
Mat mask=(Mat_<bool>(2,3)<<1,0,0,1,0,1); // Create mask
cout<<mask<<endl;
Mat samples;
///////////////////////////////
for(int i=0;i<M.total();i++)
{
if(mask.at<uchar>(i))
samples.push_back(M.at<uchar>(i));
}
cout<<samples<<endl;
above code result below outputs.
[ 1, 2, 3;
4, 5, 6]
[ 1, 0, 0;
1, 0, 1]
[ 1;
4;
6]
with using copyTo your output will be like below
[1 0 0
4 0 6];
Related
I'm new to OpenCV (in C++) and image processing. I want, given a grayscale image to replace the value of each pixel computing the average value of the grayscale in a 3x3 neighborhood.
First of all I open the image
Mat img = imread(samples::findFile(argv[1]), IMREAD_GRAYSCALE);
// Example of image
[4 3 9 1,
2 9 8 0,
3 5 2 1,
7 5 8 3]
In order to get the average value of the 3x3 closest pixels of corners (top left, top right, bottom left and bottom right) I make a padding of the image: an 1x1x1x1 constant border
Mat imgPadding;
copyMakeBorder(img, imgPadding, 1,1,1,1, BORDER_CONSTANT, Scalar(0));
// Padding example
[0 0 0 0 0 0,
0 4 3 9 1 0,
0 2 9 8 0 0,
0 3 5 2 1 0,
0 7 5 8 3 0,
0 0 0 0 0 0]
Now I've got some troubles with the output image. I have tried in various ways, but no way brings me to the solution. I tried this, using mean() function to get the average grayscale value of the i,j-th 3x3 matrix got with Rect() method. The for loop starts from the first non-padding pixel and ends at the last non-padding pixel.
Mat imgAvg = Mat::zeros(img.rows, img.cols, img.type());
// initialization of the output Mat object with same input size and type
for (int i = 1; i < imgAvg.rows; i++)
for (int j = 1; j < imgAvg.cols; j++)
imgAvg.at<Scalar>(Point(j - 1, i - 1)) = mean(imgPadding(Rect(j - 1, i - 1, 3, 3)));
but I got this runtime error
main: malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
I tried also reducing randomly the range
for (int i = 1; i < imgAvg.rows - 35; i++)
for (int j = 1; j < imgAvg.cols - 35; j++)
imgAvg.at<Scalar>(Point(j - 1, i - 1)) = mean(imgPadding(Rect(j - 1, i - 1, 3, 3)));
and I got this weird output: screenshot
Thanks in advance!
EDIT:
Thank you all for the answers, I didn't know yet the blur() function.
In this way I import the image and simply call the blur function
Mat img = imread(samples::findFile(argv[1]), IMREAD_GRAYSCALE);
Mat imgAvg = Mat::zeros(img.rows, img.cols, img.type());
blur(img, imgAvg, Size(3, 3));
But since I'm still a beginner and I think the purpose of the exercise assigned to me was to write a "handmade" code, I tried also this working solution
for (int i = 1; i <= imgAvg.rows; i++)
for (int j = 1; j <= imgAvg.cols; j++)
imgAvg.at<uint8_t>(Point(j - 1, i - 1)) = mean(imgPadding(Rect(j - 1, i - 1, 3, 3)))[0];
Result of the algorithm (identical for both solutions)
Just apply a smoothing filter to the image - the blur function in the imgproc module should accomplish what you need. A good example is in the documentation: https://docs.opencv.org/3.4/dc/dd3/tutorial_gausian_median_blur_bilateral_filter.html
In this case, the arguments you need are the image (img), a destination image (dst), and kernel size (ksize), which is 3 in this case:
src = ...
Mat dst = Mat::zeros( src.size(), src.type() )
blur( src, dst, Size( 3, 3 ))
Smoothing manually will not be as performant, and is more prone to error.
Good luck!
What you want to do is called "box filtering" in image processing. In OpenCV you do:
cv::blur(src_img,
dest_img, // same shape and type as src, cannot be src
cv::Size(3, 3)) // use a kernel of size 3x3
The default padding is to reflect the border pixel, which won't skew the image statistics. See the documentation if you prefer a different border mode.
Say I have
A = [1 2 3]
[4 5 6]
[7 8 9]
I want to pad it with the first row and first column or last row and last column as many times as needed to create A nxn. For example, A 4x4 would be
A = [1 1 2 3]
[1 1 2 3]
[4 4 5 6]
[7 7 8 9]
and A 5x5 would be
A = [1 1 2 3 3]
[1 1 2 3 3]
[4 4 5 6 6]
[7 7 8 9 9]
[7 7 8 9 9]
I'm aware that I could do A.conservativeResize(4,4) which gets me
A = [1 2 3 0]
[4 5 6 0]
[7 8 9 0]
[0 0 0 0]
then I could copy things around one by one, but is there a more efficient way to do this using Eigen?
You can workaround using a nullary-expression:
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;
int main()
{
Matrix3i A;
A.reshaped() = VectorXi::LinSpaced(9,1,9);
cout << A << "\n\n";
int N = 5;
MatrixXi B(N,N);
B = MatrixXi::NullaryExpr(N, N, [&A,N] (Index i,Index j) {
return A( std::max<Index>(0,i-(N-A.rows())),
std::max<Index>(0,j-(N-A.cols())) ); } );
cout << B << "\n\n";
}
Another approach would be to create a clamped sequence of indices like [0 0 0 1 2]:
struct pad {
Index size() const { return m_out_size; }
Index operator[] (Index i) const { return std::max<Index>(0,i-(m_out_size-m_in_size)); }
Index m_in_size, m_out_size;
};
B = A(pad{3,N}, pad{3,N});
This version requires the head of Eigen.
You can easily build on those examples to make them even more general and/or wrap them within functions.
Just as a note, it's not true that A.conservativeResize(4,4) will get you a matrix with the added rows filled with zeros. The Eigen documentation says,
In case values need to be appended to the matrix they will be uninitialized.
The new rows and columns will be filled with garbage, and seeing zeros is only a coincidence (unless you are compiling with a special preprocessor directive to Eigen). But this means that no unnecessary time is wasted writing zeros that you will overwrite anyway.
Note: this code demonstrates how to get a matrix with your original matrix in the top left corner:
The best way to fill multiple values at once is to use Eigen's block operations and setConstant. For example, if A is a matrix of size old_sizexold_size:
A.conservativeResize(n, n);
for (int i = 0; i < n; ++i) {
// Fill the end of each row and column
A.row(i).tail(n - old_size).setConstant(A(i, old_size - 1));
A.col(i).tail(n - old_size).setConstant(A(old_size - 1, i));
}
// Fill the bottom right block
A.bottomRightCorner(n - old_size, n - old_size).setConstant(A(old_size - 1, old_size - 1));
More importantly than being "efficient", these functions express your intent as a programmer.
Edit: To get a padded matrix with your original matrix in the middle:
I just noticed your example pads around the original matrix in the middle, not in the top left. In this case, there is little point to using conservativeResize(), because the original values will only be copied to the top left corner. An outline of the solution is:
Construct a new nxn matrix B of the desired size
Copy your original matrix to the middle using
int start = (n - old_size + 1)/2;
B.block(start, start, old_size, old_size) = A;
Fill in the outside values using block operations similar to my example above.
I've seen linked questions but I can't understand why MATLAB and OpenCV give different results.
MATLAB Code
>> A = [6 4 23 -3; 9 -10 4 11; 2 8 -5 1]
A =
6 4 23 -3
9 -10 4 11
2 8 -5 1
>> Col_step_1 = std(A, 0, 1)
Col_step_1 =
3.5119 9.4516 14.2945 7.2111
>> Col_final = std(Col_step_1)
Col_final =
4.5081
Using OpenCV and this function:
double getColWiseStd(cv::Mat in)
{
CV_Assert( in.type() == CV_64F );
cv::Mat meanValue, stdValue, m2, std2;
cv::Mat colSTD(1, A.cols, CV_64F);
cv::Mat colMEAN(1, A.cols, CV_64F);
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);
}
std::cout<<"\nCOLstd:\n"<<colSTD<<std::endl;
cv::meanStdDev(colSTD, m2, std2);
std::cout<<"\nCOLstd_f:\n"<<std2<<std::endl;
return std2.at<double>(0,0);
}
Applied to the same matrix yields the following:
Matrix:
[6, 4, 23, -3;
9, -10, 4, 11;
2, 8, -5, 1]
COLstd:
[2.867441755680876, 7.71722460186015, 11.67142760000773, 5.887840577551898]
COLstd_f:
[3.187726614989861]
I'm pretty sure that the OpenCV and MATLAB std function are correct, and thus can't find what I'm doing wrong, am I missing a type conversion? Something else?
The standard deviation you're calculating in OpenCV is normalised by number of observations (N) whereas you're calculating standard deviation in MATLAB normalised by N-1 (which is also the default normalisation factor in MATLAB and is known as Bessel's correction). Hence there is the difference.
You can normalise by N in MATLAB by selecting the second input argument as 1:
Col_step_1 = std(A, 1, 1);
Col_final = std(Col_step_1, 1);
I am very new to C++ and OpenCV but more familiar with Matlab. I have a task that I need to move to C++ for faster processing. So I would like to ask for your suggestion on a image processing problem. I have 10 images in a folder and I was able to read them all using dirent.h like in this and extract each frame by calling frame[count] = rawImage in a while loop:
int count = 0;
std::vector<cv::Mat> frames;
frames.resize(10);
while((_dirent = readdir(directory)) != NULL)
{
std::string fileName = inputDirectory + "\\" +std::string(_dirent->d_name);
cv::Mat rawImage = cv::imread(fileName.c_str(),CV_LOAD_IMAGE_GRAYSCALE);
frames[count] = rawImage; // Insert the rawImage to frames (this is original images)
count++;
}
Now I want to access each frames and do calculation similar to Matlab to get another matrix A such that A = frames(:,:,1)+2*frames(:,:,2). How to do that?
Since frames is a std::vector<cv::Mat>, you should be able to access each Mat this way:
// suppose you want the nth matrix
cv::Mat frame_n = frames[n];
Now, if you want to do the calculation you said on the first two Mats, then:
cv::Mat A = frames[0] + 2 * frames[1];
Example:
// mat1 = [[1 1 1]
// [2 2 2]
// [3 3 3]]
cv::Mat mat1 = (cv::Mat_<double>(3, 3) << 1, 1, 1, 2, 2, 2, 3, 3, 3);
cv::Mat mat2 = mat1 * 2; // multiplication matrix x scalar
// just to look like your case
std::vector<cv::Mat> frames;
frames.push_back(mat1);
frames.push_back(mat2);
cv::Mat A = frames[0] + 2 * frames[1]; // your calculation works
// A = [[ 5 5 5]
// [10 10 10]
// [15 15 15]]
You can always read the list of acceptable expressions.
How to use eigen library to compute lower triangular of input matrix without changing columns order?
for example for matrix:
A=[1 2 3;4 5 6 ;7 8 9]
I want the result to be:
1 0 0
4 0 0
7 0 0
Your text and your example don't match. I'll go through the three possible ways I understood your question. First, we'll set up the matrix:
Matrix3d mat;
mat << 1, 2, 3, 4, 5, 6, 7, 8, 9;
If you wanted the actual lower triangular matrix, you would use:
std::cout << Matrix3d(mat.triangularView<Lower>()) << "\n\n";
or similar. The result is:
1 0 0
4 5 0
7 8 9
Note the 5,8,9 which are missing from your example. If you just wanted the left-most column, you would use:
std::cout << mat.col(0) << "\n\n";
which gives
1
4
7
If (as the second part of your example shows) you want mat * [1, 0, 0] then you could either do the matrix multiplication (not recommended) or just construct the result:
Matrix3d z = Matrix3d::Zero();
z.col(0) = mat.col(0);
std::cout << z << "\n\n";
which gives the same result as your example:
1 0 0
4 0 0
7 0 0