OpenCV: OutputArray usage without copy - c++

I need to extend kernel from one channel to more channels. For example from
0 1 0
1 -4 1
0 1 0
to
0 0 0 1 1 1 0 0 0
1 1 1 -4 -4 -4 1 1 1
0 0 0 1 1 1 0 0 0
following standard three channels cv::Mat.
I have following code:
void createKernel(InputArray _A, InputArray _B, OutputArray _kernel, const int chn)
{
Mat A = _A.getMat();
Mat B = _B.getMat();
Mat kernel;
Mat kernelOneChannel = A * B;
std::vector<Mat> channels;
for (int i = 0; i < chn; i++)
{
channels.push_back(kernelOneChannel);
}
merge(channels, kernel);
kernel.copyTo(_kernel);
}
One channel kernel is copied to std:vector as many times based on the chn. After that is one multi channel cv::Mat created.
My question is about the last line kernel.copyTo(_kernel). In many examples what I have seen, this is the way how to deal with Outputarray. Is this copyTo really needed? It seems to me like waste of memory and time to copy already computed kernel to the _kernel. Is there any solution without this data copy from one structure to another?
My question is strictly related to OpenCV and mentioned structures.
Thanks in advance.

In your specific case you can pass _kernel variable directly to merge call to avoid unnessesary copy:
merge(channels, _kernel)
In general case the OutputArray object is supposed to be used in the following way:
_outArr.create(size, type);
Mat outMat = _outArr.getMat();
Now the outMat variable can be filled without extra copies.

Related

Rotate a bool array in C++

EDIT
I found the memcpy definition has told us about its undefined behavior when target and src has overlap:
The memcpy() function copies n bytes from memory area src to memory area dst. If dst and src overlap, behavior is undefined.Applications in which dst and src might overlap should use memmove(3) instead.
Original Question
I have a simple program that looks like this:
static void RotateLeft(bool *In, int len, int loop) {
for(int i = 0;i< 28;i++) {
LOGI("%d -> %d", i, In[i]);
}
bool Tmp[256] = {0};
memcpy(Tmp, In, loop);
memcpy(In, In + loop, len - loop);
LOGI("len = %d, loop = %d", len, loop); // <--- always 28 and 1
for(int i = 0;i< 28;i++) {
LOGI("%d -> %d", i, In[i]); <----- broken
}
memcpy(In + len - loop, Tmp, loop);
}
RotateLeft(`bool array`, 28, 1)
It is weird that this program doesn't work right on the arm64-v8a platform (but works well on other platforms):
The input array is something like this:
0 0 0 1 1 1 1 0 1 0 0 1 0 1 ...
The rotated array should be:
0 0 1 1 1 1 0 1 0 0 1 0 1 0...
But it actually outputs:
0 0 1 1 1 1 0 1 0 0 1 1 0 0 ...
UPDATE
Here's how this program allocate array:
bool K[64], *KL=&K[0], *KR=&K[28];
// do something fill `K`
RotateLeft(KR, 28, 1);
The underlying assumption for this code is that sizeof(bool) is 1. Unfortunately, this is not guaranteed by the C++ standard as explained in this answer. So your code is completely compiler dependent.
Therefore use std::copy() whenever you can instead of memcpy(). Or use std::rotate() as suggested by NathanOlivier in the comments.
By the way, it's unrelated, but you'd better make sure that loop>=0 && loop<256 && len>=loop if you want to avoid undefined behavior.

How to fill in a matrix recursively in c++?

So I have to fill in a square matrix recursively. For size N=5, it should be:
1 1 1 1 1
1 2 2 2 1
1 2 3 2 1
1 2 2 2 1
1 1 1 1 1
but my program shows:
1 1 1 1 1
1 2 2 2 1
1 2 3 3 1
1 2 2 2 1
1 1 1 1 1
void llenar5 (int** mat, int n, int f=0, int c=0,int k=2)
{
if (f<n)
{
if (c<n)
{
if (f==0 ||c==0||f==n-1||c==n-1)
{
*(*(mat+f)+c)=1;
llenar5(mat,n,f,c+1,k); //move to the right
}
else if (f==k-1 ||c==k-1||f==n-k||c==n-k)
{
*(*(mat+f)+c)=k;
llenar5(mat,n,f,c+1,k++);
}
}
llenar5(mat,n,f+1,c,k);
}
}
I am creating a matrix in dynamic memory, and I tried calling the function llenar5(mat,n,f+1,c+1,k+1) to jump a column and row while incrementing the values.
void llenar5 (int** mat, int n, int f=0, int c=0,int k=1)
{
if (f<n)
{
if (c<n)
{
if (f==k-1 ||c==k-1||f==n-k||c==n-k)
{
*(*(mat+f)+c)=k;
llenar5(mat,n,f,c+1,k+1);
}
llenar5(mat,n,f,c+1,k);
}
llenar5(mat,n,f+1,c,k);
}
}
I think it will help if you, temporarily, consider a matrix with 0-based numbers. For example, your initial matrix would look instead something like this:
0 0 0 0 0
0 1 1 1 0
0 1 2 1 0
0 1 1 1 0
0 0 0 0 0
If you inspect this matrix, you should quickly observe a fundamental property of this matrix. The value of each cell is the minimum distance from the cell to its closest horizontal or vertical edge.
So, for cell at coordinates (x,y), with the matrix of size w (width) and h (height), the value of each cell is:
min(x, y, (w-1-x), (h-1-y))
Where the min() function is a classical minimum function, that computes the minimum value of its arguments.
Then, it should be obvious that going from a 0-based matrix to a 1-based matrix you should simply add 1 to the result.
So, in conclusion your code should be trivially simple:
Loop over all the x and y coordinates.
Set the value of the corresponding cell based on the above formula.
Your code seems to be unnecessarily complicated. All that recursion is completely unneeded. This can be done using a single pass over their entire matrix, top to bottom, left to right. You don't need to know the values of adjacent cells, to compute the value in the next cell.

CUSP sparse library giving strange results

I'm trying to do the following in CUSP:
A=[
1,1,0,0;
2,2,2,0;
0,3,3,3;
0,0,4,4];
B=[1,1,1,1]';
disp(mldivide(A,B));
which is
X=[0.9167,0.0833,-0.5000,0.7500]
On the other hand I get a strange answer from CUSP
#include <cusp/dia_matrix.h>
#include <cusp/krylov/cg.h>
#include <cusp/print.h>
int main()
{
cusp::dia_matrix<int,float,cusp::host_memory> A(4,4,10,3);
A.diagonal_offsets[0] = -1;
A.diagonal_offsets[1] = 0;
A.diagonal_offsets[2] = 1;
for (int i = 0;i <3;i++)
{
for (int q = 0 ;q < A.num_cols;q++)
{
A.values(q,i)=q+1;
}
}
//copy
cusp::dia_matrix<int,float,cusp::device_memory> AA = A;
cusp::array1d<float,cusp::device_memory> BB(A.num_rows,1);
cusp::array1d<float,cusp::device_memory> XX(A.num_rows,0);
cusp::print(AA);
cusp::print(XX);
cusp::print(BB);
cusp::krylov::cg(AA,XX,BB);\
cusp::print(XX);
return 0;
}
The result looks like
sparse matrix <4, 4> with 10 entries
0 0 1
0 1 1
1 0 2
1 1 2
1 2 2
2 1 3
2 2 3
2 3 3
3 2 4
3 3 4
array1d <4>
0
0
0
0
array1d <4>
1
1
1
1
array1d <4>
-39.9938
-53.436
87.9025
-30.1429
The last one doesn't look quite right. Anybody know what I'm doing wrong? Am I using the code wrong or are we supposed to have a really good guessed solution + use a preconditioner?
The conjugate gradient method is only valid for use in symmetric positive definite matrices. Your matrix isn't symmetric. That is why it isn't (and cannot) producing a valid solution. Either use an appropriate, well conditioned SPD matrix, or use a different numerical method.

Better ways to create a rectangular mask by openCV

Creating a mask in openCV
/** result I want
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
*/
cv::Mat mask = cv::Mat::zeros(8, 8, CV_8U);
std::cout<<"before : \n"<<mask<<std::endl;
for(int i = 2; i != 6; ++i)
{
auto ptr = mask.ptr<uchar>(i) + 2;
for(int j = 0; j != 4; ++j)
{
*ptr++ = 1;
}
}
std::cout<<"after : \n"<<mask<<std::endl;
Do openCV provide us any build in function to create a mask like this?
It is trivial to create a function fot this task, but the function of openCV
always faster than naive handcrafted codes
sure, there's an easier way, use the roi operator:
cv::Mat mask = cv::Mat::zeros(8, 8, CV_8U); // all 0
mask(Rect(2,2,4,4)) = 1;
done!
If some one is looking for creating a non rectangular mask and then to apply it on the image then have a look here :
Mat& obtainIregularROI(Mat& origImag, Point2f topLeft, Point2f topRight, Point2f botLeft, Point2f botRight){
static Mat black(origImag.rows, origImag.cols, origImag.type(), cv::Scalar::all(0));
Mat mask(origImag.rows, origImag.cols, CV_8UC1, cv::Scalar(0));
vector< vector<Point> > co_ordinates;
co_ordinates.push_back(vector<Point>());
co_ordinates[0].push_back(topLeft);
co_ordinates[0].push_back(botLeft);
co_ordinates[0].push_back(botRight);
co_ordinates[0].push_back(topRight);
drawContours( mask,co_ordinates,0, Scalar(255),CV_FILLED, 8 );
origImag.copyTo(black,mask);
return black;
}
"black" is the image where we will finally obtain the result by cropping out the irregular ROI from the original image.
static Mat black(origImag.rows, origImag.cols, origImag.type(), cv::Scalar::all(0));
The "mask" is a Mat, initialized as the same size of original image and filled with 0.
Mat mask(origImag.rows, origImag.cols, CV_8UC1, cv::Scalar(0));
Putting the coordinates in ANTICLOCKWISE direction
vector< vector<Point> > co_ordinates;
co_ordinates.push_back(vector<Point>());
co_ordinates[0].push_back(topLeft);
co_ordinates[0].push_back(botLeft);
co_ordinates[0].push_back(botRight);
co_ordinates[0].push_back(topRight);
Now generating the mask actually
drawContours( mask,co_ordinates,0, Scalar(255),CV_FILLED, 8 );
At the end copy the masked portion/ROI from original image (origImag) and paste on the portion of ROI from the original image (using mask) into image named as "black"
origImag.copyTo(black,mask);

OpenCV - get grey value from an image

Hello everybody right now I'm trying to getting grey value for every pixel in an image
what I mean with grey value is the white or black level from an image let's say 0 for white and 1 for black. for an example for this image
the value I want will be like
0 0 0 0 0 0
0 1 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 0 0 0 0
is this possible? if yes how to do it with OpenCV in C? or if it's impossible with OpenCV is there any other library that can do this?
What you ask is certainly possible but how it can be done depends on a lot of things. If you use C++, on SO we generally expect you to use the C++ interface which means you have a cv::Mat object and loaded the image with something like this: (using namespace cv)
#include <opencv2/core/core.hpp>
Mat mat_gray = imread(path, CV_LOAD_IMAGE_GRAYSCALE);
or by
Mat mat = imread(path); // and assuming it was originally a color image...
Mat mat_gray;
cvtColor(mat, mat_gray, CV_BGR2GRAY); //...convert it to grayscale.
Now, if you just want to access pixel values one-by-one, you use _Tp& mat.at<_Tp>(int x,int y);. That is:
for(int x=0; x<mat_gray.rows; ++x)
for(int y=0; y<mat_gray.cols; ++y)
mat_gray.at<uchar>(x,y); // if mat.type == CV_8U
You can look up your type here, which you should use in place of uchar if the mat.type is other than CV_8U.
As for the pure C interface, you can check this answer. But if you use C++, you should definitely use the C++ interface.