c++ Search Blocks Of Large Image For Sub Image - c++

Given that my large and sub image are 2D matrices, how would I be able search my large matrix block by block until my sub matrix is found. It's like OpenCV template matching but I'm not using that so this needs to be C++ purely.
Something like this. Some sample code would be much appreciated.
SearchBlock(//parameters)
{
Matrix Block;
Block.Rows = //define block rows;
Block.Cols = //define block cols;
Block.data = new double[Block.Rows * Block.Cols];
for (int i = 0; i < Block.Rows; i++)
for (int j = 0; j < Block.Cols; j++)
return Block;
}

You can achieve this by iterating over the elements of the image matrix and checking if the neighbouring elements correspond to the elements of the subimage you are looking for.

Related

C++ : Create 3D array out of stacking 2D arrays

In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.
Is there any way to do this in C++?
In particular, I have loaded a image into a Mat variable with OpenCV like:
cv::Mat im = cv::imread("image.png", 0);
I would like to make a 3D array/Mat of N layers by stacking copies of that Mat variable.
EDIT: This new 3D matrix has to be "travellable" by adding an integer to any of its components, such that if I am in the position (x1,y1,1) and I add +1 to the last component, I arrive to (x1,y1,2). Similarly for any of the coordinates/components of the 3D matrix.
SOLVED: Both answers from #Aram and #Nejc do exactly what expected. I set #Nejc 's answer as the correct one for his shorter code.
The Numpy function vstack returns a contiguous array. Any C++ solution that produces vectors or arrays of cv::Mat objects does not reflect the behaviour of vstack in this regard, becase separate "layers" belonging to individual cv::Mat objects will not be stored in contiguous buffer (unless a careful allocation of underlying buffers is done in advance of course).
I present the solution that copies all arrays into a three-dimensional cv::Mat object with a contiguous buffer. As far as the idea goes, this answer is similar to Aram's answer. But instead of assigning pixel values one by one, I take advantage of OpenCV functions. At the beginning I allocate the matrix which has a size N X ROWS X COLS, where N is the number of 2D images I want to "stack" and ROWS x COLS are dimensions of each of these images.
Then I make N steps. On every step, I obtain the pointer to the location of the first element along the "outer" dimension. I pass that pointer to the constructor of temporary Mat object that acts as a kind of wrapper around the memory chunk of size ROWS x COLS (but no copies are made) that begins at the address that is pointed-at by pointer. I then use copyTo method to copy i-th image into that memory chunk. Code for N = 2:
cv::Mat img0 = cv::imread("image0.png", CV_IMREAD_GRAYSCALE);
cv::Mat img1 = cv::imread("image1.png", CV_IMREAD_GRAYSCALE);
cv::Mat images[2] = {img0, img1}; // you can also use vector or some other container
int dims[3] = { 2, img0.rows, img0.cols }; // dimensions of new image
cv::Mat joined(3, dims, CV_8U); // same element type (CV_8U) as input images
for(int i = 0; i < 2; ++i)
{
uint8_t* ptr = &joined.at<uint8_t>(i, 0, 0); // pointer to first element of slice i
cv::Mat destination(img0.rows, img0.cols, CV_8U, (void*)ptr); // no data copy, see documentation
images[i].copyTo(destination);
}
This answer is in response to the question above of:
In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.
This is certainly possible, you can add matrices into a vector which would be your "stack"
For instance you could use a
std::vector<cv::Mat>>
This would give you a vector of mats, which would be one slice, and then you could "layer" those by adding more slices vector
If you then want to have multiple stacks you can add that vector into another vector:
std::vector<std::vector<cv::Mat>>
To add matrix to an array you do:
myVector.push_back(matrix);
Edit for question below
In such case, could I travel from one position (x1, y1, z1) to an immediately upper position doing (x1,y1,z1+1), such that my new position in the matrix would be (x1,y1,z2)?
You'll end up with something that looks a lot like this. If you have a matrix at element 1 in your vector, it doesn't really have any relationship to the element[2] except for the fact that you have added it into that point. If you want to build relationships then you will need to code that in yourself.
You can actually create a 3D or ND mat with opencv, you need to use the constructor that takes the dimensions as input. Then copy each matrix into (this case) the 3D array
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
// Dimensions for the constructor... set dims[0..2] to what you want
int dims[] = {5, 5, 5}; // 5x5x5 3d mat
Mat m = Mat::zeros(5, 5, CV_8UC1);
for (size_t i = 0; i < 5; i++) {
for (size_t k = 0; k < 5; k++) {
m.at<uchar>(i, k) = i + k;
}
}
// Mat with constructor specifying 3 dimensions with dimensions sizes in dims.
Mat 3DMat = Mat(3, dims, CV_8UC1);
// We fill our 3d mat.
for (size_t i = 0; i < m2.size[0]; i++) {
for (size_t k = 0; k < m2.size[1]; k++) {
for (size_t j = 0; j < m2.size[2]; j++) {
3DMat.at<uchar>(i, k, j) = m.at<uchar>(k, j);
}
}
}
// We print it to show the 5x5x5 array.
for (size_t i = 0; i < m2.size[0]; i++) {
for (size_t k = 0; k < m2.size[1]; k++) {
for (size_t j = 0; j < m2.size[2]; j++) {
std::cout << (int) 3DMat.at<uchar>(i, k, j) << " ";
}
std::cout << endl;
}
std::cout << endl;
}
return 0;
}
Based on the question and comments, I think you are looking for something like this:
std::vector<cv::Mat> vec_im;
//In side for loop:
vec_im.push_back(im);
Then, you can access it by:
Scalar intensity_1 = vec_im[z1].at<uchar>(y, x);
Scalar intensity_2 = vec_im[z2].at<uchar>(y, x);
This assumes that the image is single channel.

How to convert vector<Mat> to vector<float>?

Suppose i have a vector<Mat> called regionFeaMatand regionFeaMat.size() == 81 .In other words, regionFeaMat have 81 equal size matrix, and regionFeaMat[0].rows==256 and regionFeaMat[0].cols==1. I want to convert reginFeaMat to vector<float> reginFeaVec. I tried with following code but i got wrong result:
vector<float> regionFeaVec;
regionFeaVec.assign((float*)regionFeaMat[0].datastart, (float*)regionFeaMat[80].dataend);
You seem to have made a few wrong assumptions.
std::vector does store its elements contiguously in memory, but cv::Mat is a header containing a pointer to its internal buffer, so only pointers in vector<Mat> are stored contiguously, not the Mat data itself. Because of that, the memory that lies in between (float*)regionFeaMat[0].dataend and (float*)regionFeaMat[80].datastart is some random garbage - if it does contain other Mat's data partially, it's pure luck.
Because of the above, you can't have a one-liner assigning vector to any other vector and you have to insert each mat separately instead. Try something like this:
// prevent vector reallocation after each Mat insertion:
regionFeaVec.reserve(regionFeaMat.size()*regionFeaMat[0].cols*regionFeaMat[0].rows);
for (int i = 0; i < regionFeaMat.size(); ++i)
{
if (regionFeaMat[i].isContinuous())
regionFeaVec.insert(
regionFeaVec.end(),
(float*)regionFeaMat[i].datastart,
(float*)regionFeaMat[i].dataend
);
else
{
for (int j = 0; j < regionFeaMat[i].rows; ++j)
{
const float* row = regionFeaMat[i].ptr<float>(j);
regionFeaVec.insert(regionFeaVec.end(), row, row + regionFeaMat[i].cols);
}
}
}
Note that I'm checking if a particular Mat object is continuous, because as per OpenCV docs, each row may contain gaps at the end in some cases, and in that case we have to read the matrix row by row.
This code can be simplified, because if matrix is continuous, we may treat it as a 1D vector as per the docs referenced above:
// prevent vector reallocation after each Mat insertion:
regionFeaVec.reserve(regionFeaMat.size()*regionFeaMat[0].cols*regionFeaMat[0].rows);
for (int i = 0; i < regionFeaMat.size(); ++i)
{
cv::Size size = regionFeaMat[i].size();
if (regionFeaMat[i].isContinuous())
{
size.width *= size.height;
size.height = 1;
}
for (int j = 0; j < size.height; ++j)
{
const float* row = regionFeaMat[i].ptr<float>(j);
regionFeaVec.insert(regionFeaVec.end(), row, row + size.width);
}
}
If you want to prevent vector reallocation in more general cases, you also have to change the method of calculating the number of elements passed to reserve(). The method I use assumes all the Mat objects have only two dimensions that are equal for all the objects since this is how you described your problem.
Also, if you want to assign it to vector<float>, be sure that the element type of regionFeaMat is CV_32F.

Opencv Mat vector assignment to a row of a matrix, fastest way?

What is the fastest way of assigning a vector to a matrix row in a loop? I want to fill a data matrix along its rows with vectors. These vectors are computed in a loop. This loop last until all the entries of data matrix is filled those vectors.
Currently I am using cv::Mat::at<>() method for accessing the elements of the matrix and fill them with the vector, however, it seems this process is quite slow. I have tried another way by using cv::Mat::X.row(index) = data_vector, it works fast but fill my matrix X with some garbage values which I can not understand, why.
I read that there exists another way of using pointers (fastest way), however, I can not able to understand. Can somebody explain how to use them or other different methods?
Here is a part of my code:
#define OFFSET 2
cv::Mat im = cv::imread("001.png", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat X = cv::Mat((im.rows - 2*OFFSET)*(im.cols - 2*OFFSET), 25, CV_64FC1); // Holds the training data. Data contains image patches
cv::Mat patch = cv::Mat(5, 5, im.type()); // Holds a cropped image patch
typedef cv::Vec<float, 25> Vec25f;
int ind = 0;
for (int row = 0; row < (im.rows - 2*OFFSET); row++){
for (int col = 0; col < (im.cols - 2*OFFSET); col++){
cv::Mat temp_patch = im(cv::Rect(col, row, 5, 5)); // crop an image patch (5x5) at each pixel
patch = temp_patch.clone(); // Needs to do this because temp_patch is not continuous in memory
patch.convertTo(patch, CV_64FC1);
Vec25f data_vector = patch.reshape(0, 1); // make it row vector (1X25).
for (int i = 0; i < 25; i++)
{
X.at<float>(ind, i) = data_vector[i]; // Currently I am using this way (quite slow).
}
//X_train.row(ind) = patch.reshape(0, 1); // Tried this but it assigns some garbage values to the data matrix!
ind += 1;
}
}
To do it the regular opencv way you could do :-
ImageMat.row(RowIndex) = RowMat.clone();
or
RowMat.copyTo(ImageMat.row(RowIndex));
Haven't tested for correctness or speed.
Just a couple of edits in your code
double * xBuffer = X.ptr<double>(0);
for (int row = 0; row < (im.rows - 2*OFFSET); row++){
for (int col = 0; col < (im.cols - 2*OFFSET); col++){
cv::Mat temp_patch = im(cv::Rect(col, row, 5, 5)); // crop an image patch (5x5) at each pixel
patch = temp_patch.clone(); // Needs to do this because temp_patch is not continuous in memory
patch.convertTo(patch, CV_64FC1);
memcpy(xBuffer, patch.data, 25*sizeof(double));
xBuffer += 25;
}
}
Also, you dont seem to do any computation in patch just extract grey level values, so you can create X with the same type as im, and convert it to double at the end. In this way, you could memcpy each row of your patch, the address in memory beeing `unsigned char* buffer = im.ptr(row) + col
According to the docs:
if you need to process a whole row of matrix, the most efficient way is to get the pointer to the row first, and then just use plain C operator []:
// compute sum of positive matrix elements
// (assuming that M is double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}

opencv : create matrix or vector of matrices

I have the following code, which is a part of the algorithm that I am following. as you see I need to do some calculation for 10 different bands. and will end up with a matrix for each band that I need to recreate an image from it, the problem is that I dont know how to create/hold the 10 different matrix on the while loop, then after the while loop I can construct the images one by one. if you have any idea please let me know thank you
cv::Mat _reconstructionMatrix(height,width,CV_8UC1);
_reconsPointer = _reconstructionMatrix.ptr<uchar>(0);
while(_bandIteration<_bandsNumber){
if(_mainMatrix.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
//for all the pixels
for(int i = 0; i < nRows; i++)
{
p = _mainMatrix.ptr<uchar>(i);
//in the images
for (int j = 0; j < nCols; j++)
{
if(_pCounter<_totalImgNO){
....
}else{
...
_reconsPointer[_resultFlag]=_summation;
_resultFlag++;
...
}
}
}
_bandIteration++;
}
Your question is a bit vague. But if you are asking simply how to create/hold the 10 different matrix on the while loop? then you can use STL vectors as normal.
#include<vector>
...
std::vector<cv::Mat> listOfMatrices;
...
cv::Mat M = SomehowGetMatrix();
listOfMatrices.push_back(M);
If this is not what you are looking for, then please provide more detail to your question.

C++ Using 3D Dynamic Arrays and Vectors

I'm new to C++ and getting a bit frustrated with it. Below, in pixelsVector, I am storing each pixel RGB float-value in Pixel and want to dump
all the values to a byte array with pixelsArray so I can output to an image file. HEIGHT and WIDTH refer to the image dimensions. The code below works fine, but I need to specify
the sizes of pixelsArray at run-time, because it may not always be a 500x500 image.
// WIDTH and HEIGHT specified at run-time
vector<vector<Pixel>> pixelsVector (WIDTH, vector<Pixel> (HEIGHT));
...
unsigned char pixelsArray[500][500][3];
for (int i = 0; i < 500; i++)
{
for (int j = 0; j < 500; j++)
{
// Returns RGB components
vector<float> pixelColors = pixelArray[i][j].getColor();
for (int k = 0; k < 3; k++)
{
pixels[i][j][k] = pixelColors.at(k);
}
}
}
// write to image file
fwrite(pixelsArray, 1, 500*500*3, file);
If I put HEIGHT and WIDTH instead of 500 and 500 above, I get an error since they are not constant values. Now using a 3D vector does seem to work, but fwrite won't take a vector for its first argument. I tried using a triple-pointer array but
it doesn't seem to work at all - maybe I was using it wrong. Here it is using a 3D vector for pixelsArray:
vector<vector<Pixel>> pixelsVector (WIDTH, vector<Pixel> (HEIGHT));
...
vector< vector< vector<unsigned char> > > pixelsArray;
for (int i = 0; i < HEIGHT; i++)
{
pixels.push_back(vector< vector<unsigned char> >());
for (int j = 0; j < WIDTH; j++)
{
pixels[i].push_back(vector<unsigned char>());
vector<float> pixelColors;
pixelColors = pixelArray[i][j].getColor();
for (int k = 0; k < 3; k++)
{
pixels[i][j][k] = pixelColors.at(k);
}
}
}
// Error
fwrite(pixelsArray, 1, 500*500*3, file);
Suggestions?
You could use Boost.MultiArray insead of vectors of vectors, which lets you access he underlying memory with the .data() method.
It looks like you are trying to manipulate images, so you might want to consider using Boost.Gil.
From the last code snippet:
vector<vector<Pixel>> pixelsVector (WIDTH, vector<Pixel> (HEIGHT));
Using uppercase names for variables you risk name collisions with macros. In C++ all uppercase names are conventionally reserved for macros.
...
vector< vector< vector<unsigned char> > > pixelsArray;
Presumably this vector is the same as is called pixels below?
If so, then the standard advice is that it helps to post real code.
Anyway, in order to output those bytes in one efficient operation you need the bytes to be contiguously stored in memory. So a vector of vectors of vectors is out. Use a single vector (C++ guarantees contiguous storage for the buffer of a std::vector).
for (int i = 0; i < HEIGHT; i++)
{
pixels.push_back(vector< vector<unsigned char> >());
for (int j = 0; j < WIDTH; j++)
{
pixels[i].push_back(vector<unsigned char>());
At this point you have an inner vector but it's empty, size 0.
vector<float> pixelColors;
pixelColors = pixelArray[i][j].getColor();
Presumably pixelArray is an instance of a class you have defined?
for (int k = 0; k < 3; k++)
{
pixels[i][j][k] = pixelColors.at(k);
}
Here you're trying to assign to non-existent elements of the empty innermost vector. You can either size it properly in advance, or use the push_back method for each value.
In addition, are you sure that the float values are integers in range 0 through 255 (or more generally, 0 through UCHAR_MAX) and not, say, in the range 0 through 1?
Perhaps you need to scale those values.
}
}
// Error
fwrite(pixelsArray, 1, 500*500*3, file);
If pixelsArray had been a (non-empty) vector of bytes, then you could use &pixelsArray[0] to obtain a pointer to the first byte.
Now, I know, the above only dissects some of what's wrong, and doesn't tell you directly what's right. :-)
But some more information would be needed to give example code for doing this, like (1) what are your float values, and (2) what do you want in your file?
Anyway, hope this helps,
– Alf