How to save to vector coordinates of white pixels? - c++

I want to loop through a binarized cv::Mat and save all coordinates of pixels with a value of 255.
cv::Mat bin;
std::vector<cv::Point2i> binVec;
int h = 0;
int white = 254; //Just for comparison with pointer of Matrix value
for (int i = 0; i < bin.rows; i++, h++) {
for (int j = 0; j < bin.cols; j++, h++) {
int* p = bin.ptr<int>(h); //Pointer to bin Data, should loop through Matrix
if (p >= &white) //If a white pixel has been found, push i and j in binVec
binVec.push_back(cv::Point2i(i, j));
}
}
This snippet is not working, and I don't know why.
Exception thrown at 0x76C6C42D in example.exe: Microsoft C++ exception: cv::Exception at memory location 0x0019E4F4.
Unhandled exception at 0x76C6C42D in example.exe: Microsoft C++ exception: cv::Exception at memory location 0x0019E4F4.
So how can I count h and let the pointer work?

You can avoid to scan the image. To save the coordinates of all white pixels in a vector you can do like:
Mat bin;
// fill bin with some value
std::vector<Point> binVec;
findNonZero(bin == 255, binVec);
You can use Point instead of Point2i, since they are the same:
typedef Point2i Point;
If you really want to use a for loop, you should do like:
const uchar white = 255;
for (int r = 0; r < bin.rows; ++r)
{
uchar* ptr = bin.ptr<uchar>(r);
for(int c = 0; c < bin.cols; ++c)
{
if (ptr[c] == 255) {
binVec.push_back(Point(c,r));
}
}
}
Remember that:
you binary image is probably CV_8UC1, and not a CV_32SC1, so you should use uchar instead of int.
bin.ptr<...>(i) gives you a pointer to the start of the i-th row, so you should take it out of the inner loop.
you should compare the values, not the address.
Point take as parameters x (cols) and y (rows), while you are passing i (rows) and j (cols). So you need to swap them.
this loop can be further optimized, but for your task I strongly recommend the findNonZero approach, so I don't show it here.

You should only increment h in the inner loop
You should compare the value pointed at by p with h, not compare p with the address of h.
So
cv::Mat bin;
std::vector<cv::Point2i> binVec;
int h = 0;
int white = 254; //Just for comparison with pointer of Matrix value
for (int i = 0; i < bin.rows; i++) {
for (int j = 0; j < bin.cols; j++) {
int* p = bin.ptr<int>(h++); //Pointer to bin Data, should loop through Matrix
if (*p >= white) //If a white pixel has been found, push i and j in binVec
binVec.push_back(cv::Point2i(i, j));
}
}

Related

Speeding up access to a array with pointers in C++

I am trying to make a fast image threshold function. Currently what I do is:
void threshold(const cv::Mat &input, cv::Mat &output, uchar threshold) {
int rows = input.rows;
int cols = input.cols;
// cv::Mat for result
output.create(rows, cols, CV_8U);
if(input.isContinuous()) { //we have to make sure that we are dealing with a continues memory chunk
const uchar* p;
for (int r = 0; r < rows; ++r) {
p = input.ptr<uchar>(r);
for (int c = 0; c < cols; ++c) {
if(p[c] >= threshold)
//how to access output faster??
output.at<uchar>(r,c) = 255;
else
output.at<uchar>(r,c) = 0;
}
}
}
}
I know that the at() function is quite slow. How can I set the output faster, or in other words how to relate the pointer which I get from the input to the output?
You are thinking of at as the C++ standard library documents it for a few containers, performing a range check and throwing if out of bounds, however this is not the standard library but OpenCV.
According to the cv::Mat::at documentation:
The template methods return a reference to the specified array element. For the sake of higher performance, the index range checks are only performed in the Debug configuration.
So there's no range check as you may be thinking.
Comparing both cv::Mat::at and cv::Mat::ptr in the source code we can see they are almost identical.
So cv::Mat::ptr<>(row) is as expensive as
return (_Tp*)(data + step.p[0] * y);
While cv::Mat::at<>(row, column) is as expensive as:
return ((_Tp*)(data + step.p[0] * i0))[i1];
You might want to take cv::Mat::ptr directly instead of calling cv::Mat::at every column to avoid further repetition of the data + step.p[0] * i0 operation, doing [i1] by yourself.
So you would do:
/* output.create and stuff */
const uchar* p, o;
for (int r = 0; r < rows; ++r) {
p = input.ptr<uchar>(r);
o = output.ptr<uchar>(r); // <-----
for (int c = 0; c < cols; ++c) {
if(p[c] >= threshold)
o[c] = 255;
else
o[c] = 0;
}
}
As a side note you don't and shouldn't check for cv::Mat::isContinuous here, the gaps are from one row to another, you are taking pointers to a single row, so you don't need to deal with the matrix gaps.

Opencv: How to I get and then change values of "Mat vector"

I want to do an operation like this however I cannot get the values of the vector Mat and change them. table is a 1 dimensional array by the way. Thanks.
vector<Mat> orjchannel;
vector<Mat> refchannel;
// There are some functions here
for (int i = 0; i < 512; i++){
for (int j = 0; j < 512; j++){
double value = refchannel[i][j]; // This part does not work
orjchannel[i][j] = tables[value];
With OpenCV, you typically access the values of a Mat with the at<DATATYPE>(r,c) command. For example...
// Mat constructor
Mat data(4, 1, CV_64FC1);
// Set Value
data.at<double>(0,0) = 4;
// Get Value
double value = data.at<double>(0,0);

Take off pads with OpenCV

I have to translate from Matlab to C this code:
% take off the pads
x = (1 + padSize) : (rows - pad8Size);
y = (1 + padSize) : (cols - padSize);
rpad=rpad(x,y);
1st and 2nd create 2 array, but I don t know how I have to delete it from rpad Mat object It can be something like(subtract every element)
for(int i=1+pad;i<=rows-pad;i++){
for(int j=1+pad;i<=cols-pad;j++){
subtract(rpad,x,rpad);
subtract(rpad,y,rpad);}}
Or something like(delete the external element)
int a=(rows-pad)-(1+pad);
int b=(cols-pad)-(1+pad);
rpad.create(img.rows - a,img.cols - b,original.type());
img.copyTo(rpad);
Try
cv::Rect roi(padSize, padSize, rpad.cols-2*padSize, rpad.rows-2*padSize);
cv::Mat result = rpad(roi);
And depending on whether you want continuous memory, you can choose to directly use result (discontinuous, usually okay for most OpenCV functions) or copy it to back to rpad (continuous)
Is it possible to multiply a Mat object with a bidimensional array? Imfft is obviously the Mat object
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
imfft=imfft*filter[i][j]
}
}

Fastest way to extract individual pixel data?

I have to get information about the scalar value of a lot of pixels on a gray-scale image using OpenCV. It will be traversing hundreds of thousands of pixels so I need the fastest possible method. Every other source I've found online has been very cryptic and hard to understand. Is there a simple line of code that should just hand a simple integer value representing the scalar value of the first channel (brightness) of the image?
for (int row=0;row<image.height;row++) {
unsigned char *data = image.ptr(row);
for (int col=0;col<image.width;col++) {
// then use *data for the pixel value, assuming you know the order, RGB etc
// Note 'rgb' is actually stored B,G,R
blue= *data++;
green = *data++;
red = *data++;
}
}
You need to get the data pointer on each new row because opencv will pad the data to 32bit boundary at the start of each row
With regards to Martin's post, you can actually check if the memory is allocated continuously using the isContinuous() method in OpenCV's Mat object. The following is a common idiom for ensuring the outer loop only loops once if possible:
#include <opencv2/core/core.hpp>
using namespace cv;
int main(void)
{
Mat img = imread("test.jpg");
int rows = img.rows;
int cols = img.cols;
if (img.isContinuous())
{
cols = rows * cols; // Loop over all pixels as 1D array.
rows = 1;
}
for (int i = 0; i < rows; i++)
{
Vec3b *ptr = img.ptr<Vec3b>(i);
for (int j = 0; j < cols; j++)
{
Vec3b pixel = ptr[j];
}
}
return 0;
}

Multi-Dimensional data in a Matrix in OpenCV with C++

I want to declare, populate, access a Multi-Dimensional Matrix in OpenCV (C++) which is compatible with namespace cv. I found no quick and easy to learn examples on them. Can you please help me out?
Here is a short example from the NAryMatIterator documentation; it shows how to create, populate, and process a multi-dimensional matrix in OpenCV:
void computeNormalizedColorHist(const Mat& image, Mat& hist, int N, double minProb)
{
const int histSize[] = {N, N, N};
// make sure that the histogram has a proper size and type
hist.create(3, histSize, CV_32F);
// and clear it
hist = Scalar(0);
// the loop below assumes that the image
// is a 8-bit 3-channel. check it.
CV_Assert(image.type() == CV_8UC3);
MatConstIterator_<Vec3b> it = image.begin<Vec3b>(),
it_end = image.end<Vec3b>();
for( ; it != it_end; ++it )
{
const Vec3b& pix = *it;
hist.at<float>(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f;
}
minProb *= image.rows*image.cols;
Mat plane;
NAryMatIterator it(&hist, &plane, 1);
double s = 0;
// iterate through the matrix. on each iteration
// it.planes[*] (of type Mat) will be set to the current plane.
for(int p = 0; p < it.nplanes; p++, ++it)
{
threshold(it.planes[0], it.planes[0], minProb, 0, THRESH_TOZERO);
s += sum(it.planes[0])[0];
}
s = 1./s;
it = NAryMatIterator(&hist, &plane, 1);
for(int p = 0; p < it.nplanes; p++, ++it)
it.planes[0] *= s;
}
Also, check out the cv::compareHist function for another usage example of the NAryMatIterator here.
To create a multi-dimensional matrix that is of size 100x100x3, using floats, one channel, and with all elements initialized to 10 you write like this:
int size[3] = { 100, 100, 3 };
cv::Mat M(3, size, CV_32FC1, cv::Scalar(10));
To loop over and output the elements in the matrix you can do:
for (int i = 0; i < 100; i++)
for (int j = 0; j < 100; j++)
for (int k = 0; k < 3; k++)
std::cout << M.at<cv::Vec3f>(i,j)[k] << ", ";
However, beware of the troubles with using multi-dimensional matrices as documented here: How do i get the size of a multi-dimensional cv::Mat? (Mat, or MatND)