Anson Tan
Anson Tan

Reputation: 1246

Create an OpenCV Mat with simple pattern

In OpenCV, is there a fast way to create a Mat object where:

For example :

1 0 1 0 1 0
1 0 1 0 1 0
1 0 1 0 1 0

The pattern is always the same. The size of Mat can be big, and process by looping is really slow to generate this pattern.

Upvotes: 3

Views: 1356

Answers (3)

Miki
Miki

Reputation: 41776

OpenCV repeat is there exactly for this.

#include <opencv2\opencv.hpp>
#include <vector>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    int rows = 1000;
    int cols = 1000;

    vector<uchar> pattern = { 1, 0 }; // change with int, double, etc according to the type you want.
    Mat m;
    repeat(pattern, rows, cols/2, m);

    return 0;
}

COMPARISON WITH OTHER METHODS

Just a small test to measure the performance of the proposed (so far) methods:

Time in milliseconds:

@Miki [repeat]          : 0.442786
@RonaldoMessi [copyTo]  : 7.26822
@Derman [merge]         : 1.17588

The code I used for the test:

#include <opencv2\opencv.hpp>
#include <vector>
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    int rows = 1000;
    int cols = 1000;

    {
        // @Miki
        double tic = (double)getTickCount();

        vector<uchar> pattern = { 1, 0 };
        Mat m1;
        repeat(pattern, rows, cols / 2, m1);

        double toc = ((double)getTickCount() - tic) * 1000 / getTickFrequency();
        cout << "@Miki [repeat] \t\t: " << toc << endl;
    }

    {
        // @RonaldoMessi
        double tic = (double)getTickCount();

        Mat m2(rows, cols, CV_8UC1);
        Mat vZeros = Mat::zeros(rows, 1, CV_8UC1);
        Mat vOnes = Mat::ones(rows, 1, CV_8UC1);

        for (int i = 0; i < cols - 1; i += 2)
        {
            vOnes.col(0).copyTo(m2.col(i));
            vZeros.col(0).copyTo(m2.col(i + 1));
        }

        double toc = ((double)getTickCount() - tic) * 1000 / getTickFrequency();
        cout << "@RonaldoMessi [copyTo] \t: " << toc << endl;
    }

    {
        // @Derman
        // NOTE: corrected to give correct output

        double tic = (double)getTickCount();

        Mat myMat[2];
        myMat[0] = cv::Mat::ones(rows, cols/2, CV_8UC1);
        myMat[1] = cv::Mat::zeros(rows, cols/2, CV_8UC1);

        Mat m3;
        merge(myMat, 2, m3);
        m3 = m3.reshape(1);

        double toc = ((double)getTickCount() - tic) * 1000 / getTickFrequency();
        cout << "@Derman [merge] \t: " << toc << endl;
    }

    getchar();

    return 0;
}

Upvotes: 10

user5133845
user5133845

Reputation:

If two-channel matrix won't bother you, this could be your choice:

int rows = 5;
int cols = 5;

cv::Mat myMat[2];
myMat[0] = cv::Mat::ones(rows, cols, CV_32FC1);
myMat[1] = cv::Mat::zeros(rows, cols, CV_32FC1);

cv::Mat result;
cv::merge(myMat, 2, result);

And this is your result:

[1, 0, 1, 0, 1, 0, 1, 0, 1, 0;
1, 0, 1, 0, 1, 0, 1, 0, 1, 0;
1, 0, 1, 0, 1, 0, 1, 0, 1, 0;
1, 0, 1, 0, 1, 0, 1, 0, 1, 0;
1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

Upvotes: 1

fatihk
fatihk

Reputation: 7929

You can create two column vectors vZeros and vOnes, then copy these columns to the matrix M:

int cols = A.cols;
int rows = A.rows;

Mat vZeros = Mat::zeros(rows , 1, CV_64F);
Mat vOnes = Mat::ones(rows , 1, CV_64F);

for(int i=0; i<cols-1; i+=2)
{
   vOnes.col( 0 ).copyTo( M.col(i) ); 
   vZeros.col( 0 ).copyTo( M.col(i+1) );
}

Upvotes: 1

Related Questions