OpenCV: read matrix value - c++

I want to count the number of white points in a background image which is only black and white. I have a code like this:
int count = 0;
for ( int j = 0; j < Image.rows; j ++ )
{
for ( int i = 0; i < Image.cols; i ++ )
{
if ( Image.at<int>(i,j) >= 150 )
{
count ++ ;
}
}
}
For some reason, the above code doesn't work, it just stops reacting. I checked, and the line" if ( Image.at(i,j) >= 150 ) " causes the problem. My "Image" is a "cv::Mat", with "CV_8UC3" type. Is there someone can help me? Thank you.

In addition to my comment to Robin's answer, your error is that you try to access an image of CV_8UC3 type as ints. If you want to check grey levels, do something like this (note the "unsigned char" instead of "int", as in Robin's answer).
cv::Mat greyscale;
cv::cvtColor(image,grayscale,CV_RGB2GRAY);
// either, most elegant:
int count = cv::countNonZero(greyscale >= 150);
// or, copied from Robin's answer:
int count = 0;
for(int i = 0; i < greyscale.rows; ++i) {
const unsigned char* row = greyscale.ptr<unsigned char>(i);
for(int j = 0; j < greyscale.cols; j++) {
if (row[j] >= 150)
++count;
}
}

I believe this is much neater:
Mat result;
threshold(Image,result,150,255,THRESH_BINARY);
int white_count = countNonZero(result);

Write Image.at<unsigned char>(j,i) not Image.at<unsigned char>(i,j) if you are using i for cols and j for rows.

I think you have to access the row before the column, meaning you should swap i and j.
Substitute if ( Image.at<int>(i,j) >= 150 ) with if ( Image.at<int>(j,i) >= 150 )
There are easier ways to access a Mat though.
OpenCV provides an STL-like iterator which is easy to use and if you want to access all the elements very easy to use. Example:
int count = 0;
MatConstIterator_<int> it = Image.begin<int>(), it_end = Image.end<int>();
for(; it != it_end; ++it)
if ((*it) >= 150)
++count;
Last but not least you could also get a pointer to each row and access the data via the plain [] operator:
int count = 0;
for(int i = 0; i < Image.rows; ++i) {
const int* Ii = Image.ptr<int>(i);
for(int j = 0; j < Image.cols; j++) {
if (Ii[j] >= 150)
++count;
}
}

you could access the CV_8UC3 Pixels with opencv bytes vectors (unsigned char pixels) !
In this case you can make the following (now you could also use some special color threshold)
int channel = 0;
Image.at<Vec3b>( row , col )[channel]

There are a lot of methods to access the cv::Mat image,
if you want to directly access the color image(CV_8UC3),
it could be implemented by following:
int count = 0;
int threshold = 150;
for(int j = 0; j < img.rows; j++) {
for(int i = 0; i < img.cols; i++) {
//white point which means that the point in every channel(BGR)
//are all higher than threshold!
if(img.ptr<cv::Vec3b>(j)[i][0] > threshold &&
img.ptr<cv::Vec3b>(j)[i][1] > threshold
img.ptr<cv::Vec3b>(j)[i][2] > threshold ) {
count++;
}
}
}
but I recommend that if you only want to count white points, you can just convert image into grayscale
(CV_8UC1), and do as following:
cv::Mat img;
cv::cvtColor(src,img,CV_BGR2RGB);
int count = 0;
int threshold = 150;
for(int j = 0; j < img.rows; j++) {
for(int i = 0; i < img.cols; i++) {
if(img.ptr<uchar>(j)[i] > threshold) {
count++;
}
}
}
Finally, note that access cv::Mat image by img.ptr< Imagetype> will not check the accessed point is correct, so if you certainly know the range of image, the access image by ptr will be fine, otherwise, you can do by img.at< Imagetype>(), it will check every point is correct at every call,why access image by ptr is faster
so if there are invalid accessed point, it will assert you!

Related

C++: Reshape vector to 3D array

Edit: I have uploaded the vector to Drive as a text file, in case anyone want to have a look: https://drive.google.com/file/d/0B0wsPU8YebRQbDUwNFYza3ljSnc/view?usp=sharing
I'm trying to reshape my vector h into a 3D array. h contains 295788 elements. In this case height = 314, width = 314 and depth = 3.
Basically what I'm trying to do is what MATLAB does with its reshape function.
h = reshape(h, height, width, depth)
This is my attempt so far, but when I print it all I see is zeroes, which is not right. I have double checked that h contains the numbers I'm expecting.
vector<vector<vector<double> > > array3D;
int height = 314, width = 314, depth = 3;
// Set up sizes
array3D.resize(height);
for (int i = 0; i < height; ++i) {
array3D[i].resize(width);
for (int j = 0; j < width; ++j)
array3D[i][j].resize(depth);
}
for (int i = 0; i < height; i++)
{
array3D[i][0][0] = h[i];
for (int j = 0; j < width; j++)
{
array3D[i][j][0] = h[i+j];
for (int k = 0; k < depth; k++)
{
array3D[i][j][k] = h[i+j+k];
}
}
}
Printing:
for (vector<vector<vector<double>>>::const_iterator i = array3D.begin(); i != array3D.end(); ++i)
{
for (vector<vector<double>>::const_iterator j = i->begin(); j != i->end(); ++j)
{
for (vector<double>::const_iterator k = j->begin(); k != j->end(); ++k)
{
cout << *k << ' ';
}
}
}
So my question is, how do I convert my vector into a 3D array properly?
I managed to do this by using Eigen::Tensor as suggested by Henri Menke. I ended up creating an array for the initial 314x314x3 matrix, and then another one for the 300x300x3 matrix. It's neither fast nor pretty, but for now that is what I could come up with. Looks like this.
For clarification: margin is calculated further up in the code, but in this example with the 314x314x3 matrix it's margin=7. h is a vector with 295788 elements. nrh=314, nch=314 and nradii=3.
Tensor<int, 3> t(nrh, nch, nradii);
int counter = 0;
for (int k = 0; k < nradii; k++)
{
for (int col = 0; col < nch; col++)
{
for (int row = 0; row < nrh; row++)
{
t(row, col, k) = h[counter];
counter += 1;
}
}
}
int height = nrh - margin * 2;
int width = nch - margin * 2;
int depth = nradii;
Tensor<int, 3> out(height, width, depth);
int count1 = 0, count2 = 0, count3 = 0;
for (int k = 0; k < depth; k++)
{
for (int j = margin; j < nch - margin; j++)
{
for (int i = margin; i < nrh - margin; i++)
{
out(count1, count2, count3) = t(i, j, k);
count1 += 1;
}
count1 = 0;
count2 += 1;
}
count2 = 0;
count3 += 1;
}
Edit: Solution #2 with Tensor.slice()
int height = nrh - margin * 2;
int width = nch - margin * 2;
int depth = nradii;
Tensor<int, 3> tensor(height, width, depth);
DSizes<ptrdiff_t, 3> indices(margin, margin, 0);
DSizes<ptrdiff_t, 3> sizes(height, width, nradii);
tensor = t.slice(indices, sizes);
How about:
array3D[i][j][k] = h[i*(depth*width)+j*depth+k];
That may or may not be scanning the vector in the correct order.
Notice how when the index k resets the index j increments so you move on exactly one until the index j resets in which case i increments and the same. It's easy to show this calculation reads every element exactly once.
I'd normally expect a width, height then depth and you're scanning in the opposite order!
Footnote: Depending on the application is may be worthwhile to just access the vector using this approach. In general it turns out to be faster than accessing a vector of vectors of vectors. That can be relevant when dealing with massive arrays.
Actually, your the structure of your code is already ok, however, there are two mistakes:
The lines
array3D[i][0][0] = h[i];
and
array3D[i][j][0] = h[i+j];
are pointless. You are overwriting those entries later on with the line
array3D[i][j][k] = h[i+j+k];
The index calculation for h[] is wrong: You must multiply the row index by the length of a row before adding the cell index. The assignment should look like this:
array3D[i][j][k] = h[(i*width+j)*depth+k];
Otherwise, you will get the same result for (i, j, k) == (3, 2, 1) as for (i, j, k) == (1, 3, 2), which is obviously wrong. In the index calculation above, I have assumed that k is the fastest changing dimension. If that is not the order in which your data is stored in h, you need to change the positions of i, j, and k and adjust the factors accordingly.
Putting this together, your assignment loop should read:
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
for (int k = 0; k < depth; k++) {
array3D[i][j][k] = h[(i*width+j)*depth+k];
}
}
}
Slightly off-topic:
If you were using C instead of C++, you could "simply" do this:
size_t dataSize;
//Create a real 3D array with the dimensions (height, width, depth).
double (*array3D)[width][depth] = malloc(dataSize = height*sizeof(*array3D));
//Copy over the data from the file.
memcpy(array3D, h, dataSize);
//Print the array contents:
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
for (int k = 0; k < depth; k++) {
printf("%d ", array3D[i][j][k]);
}
}
}
This uses a real 3D array instead of an array of pointers to arrays of pointers to arrays of doubles (which is roughly what a vector<vector<vector<double>>> is). However, this cannot be done in C++ as C++ does not allow for array types with dynamic sizes as C does.

1 Dimensional Sobel Produces Noise

I'm attempting a piece-by-piece Sobel edge detector for a project for school, and I can't wrap my head around where I am going wrong. Without putting up too much detail, I think a large portion of it boils down to the code below. When I put a lenna.pgm image:
through for a 2D mask along the x-gradient, I get a lot of noise.
I discussed the code with my instructor, and I'm doing what he says to do.
Here's the code for the x-direction convolution:
void applySobel(int maskX[3][3], int maskY[3][3], int maskWidth, int imageH, int imageW,
int threshold, int*** generated){
int sumX, sumY;
// convolve smoothed image with Sobel mask in the X-direction
for(int i = 0; i < imageH; i++) {
for(int j = 0; j < imageW; j++) {
if(i == 0 || i >= imageH - 1 || j == 0 || j >= imageW - 1) {
sumX = 0;
} else {
sumX = 0;
for(int x = -1; x <= 1; x++) {
for(int y = -1; y <= 1; y++) {
sumX += generated[0][i+x][j+y] * maskX[x+1][y+1];
}
}
}
generated[1][i][j] = sumX;
}
}
}
I've also tried normalizing the image before outputting to a file, but the image goes dark.
for(int a = 1; a < 6; a++) {
min = imageOUT[a][0][0];
max = 0;
// normalize the pixel values and then write to files
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
pixel = imageOUT[a][i][j];
if(pixel < min) {
min = pixel;
} if(pixel > max) {
max = pixel;
}
}
}
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
imageOUT[a][i][j] = (int)(imageOUT[a][i][j] - min) * (255/(max-min));
}
}
WriteImage(fileOutName[a-1].c_str(), imageOUT[a], M, N, Q);
}
I deeply appreciate any insight. This has been keeping me up for days now.
UPDATE: Here's the solution I arrived at. Basically, I took only the objects of interest in the mask instead of multiplying and adding all of it, so the zero spaces were left out.
void applySobel(int maskX[3][3], int maskY[3][3], int maskWidth, int imageH, int imageW,
int threshold, int*** generated){
int sumX, sumY;
// convolve smoothed image with Sobel mask in the X-direction
for(int i = 0; i < imageH; i++) {
for(int j = 0; j < imageW; j++) {
if(i == 0 || i == imageH - 1 || j == 0 || j == imageW - 1) {
sumX = generated[0][i][j];
} else {
sumX = (int)(generated[0][i-1][j-1]*maskX[0][0] +
generated[0][i][j-1]*maskX[1][0] +
generated[0][i+1][j-1]*maskX[2][0] +
generated[0][i-1][j+1]*maskX[0][2] +
generated[0][i][j+1]*maskX[1][2]+
generated[0][i+1][j+1]*maskX[2][2])/2;
}
generated[1][i][j] = sumX/3;
}
}
And the beautiful Lenna after applying the X-Direction Gradient:
Thank you all very much for your suggestions.

Using BOOST::UBLAS and FFTW together for image processing

Is there a possibility to use a boost matrix along FFTW? if so how do you do it?
what i basically have is
QPixmap pixmap("lena.bmp");
// resize input image
pixmap = pixmap.copy(512,512,128,128);
pixmap = pixmap.scaled(128,128);
QImage image = pixmap.toImage();
QRgb col;
int g;
int width = pixmap.width();
int height = pixmap.height();
matrix<double> m(width,height);
for (int j = 0; j < m.size2(); j++)
{
for (int i = 0; i < m.size1(); i++)
{
m(i,j) = 0;
m(i,j) = qGray(image.pixel(i,j));
}
}
I want to perform FFTW on the matrix 'm' and then redisplay the fft of the image. how do i do this?
you can do is first read your image into an array, then apply the FFTW as per your needs on this array and then fill this array into a boost ublas matrix and then use it.
int a[width][height];
for (int j = 0; j < width; j++)
{
for (int i = 0; i < height; i++)
{
a[i][j] = qGray(image.pixel(i,j));
}
}
//apply fftw
matrix<double> m(width,height);
for (int j = 0; j < width; j++)
{
for (int i = 0; i < height; i++)
{
m(i,j) = fft_a[i][j];
}
}
I hope this works

Image Manipulation - is this a for-loop issue?

I have an image that I'm trying to blur out, but it ends up looking very weird indeed:
What I DO is this: I take each pixel, and I average its value on a color-by-color basis with the values of all adjacent pixels.
Or so I think. But there's clearly an error in there, and I suspect there's a problem with my for-loops but for the life of me I cannot find out what is actually going wrong.
In particular, step five (the output step) shows that the image is still ordered - if I output the left image on the right side, rather than the blur-masked image, the pixels are still in the correct order.
try
{
// STEP ONE: MAKE MEMORY AVAILABLE FOR IMAGE
int ***image;
image = new int**[m_nSizeX];
for(int i = 0; i < m_nSizeX; ++i)
{
image[i] = new int*[m_nSizeY];
for(int j = 0; j < m_nSizeY; ++j)
{
image[i][j] = new int[nrPixels];// image[x][y][z] is now a pointer to an int
}
}
// STEP TWO: MAKE MEMORY AVAILABLE FOR IMAGE MASK
int ***mask;
mask = new int**[m_nSizeX];
for(int i = 0; i < m_nSizeX; ++i)
{
mask[i] = new int*[m_nSizeY];
for(int j = 0; j < m_nSizeY; ++j)
{
mask[i][j] = new int[nrPixels];// mask[x][y][z] is now a pointer to an int
}
}
//STEP THREE: COPY IMAGE INTO MEMORY
unsigned long lOffset = 0;
for(long i=0; i<m_nSizeX ; i++)
{
for(long j=0; j<m_nSizeY ; j++)
{
for(int k=0; k<(nrPixels) ; k++)
{
image[i][j][k] = *(reinterpret_cast<unsigned char*>(m_pcMemOrg + lOffset) );
lOffset++;
}
}
}
// STEP FOUR: BLUR IMAGE
for(long i=0; i<m_nSizeX ; i++)
{
for(long j=0; j<m_nSizeY ; j++)
{
for(int k=0; k<(nrPixels) ; k++)
{
// INSERT BLURRING FUNCTION HERE (New value = Old value averaged with adjacent pixels)
if(k != 2) // 0 = blue, 1 = green, 2 = red;
{
mask[i][j][k] = 0;
}
else
if(i==0 && j==0)// (0,0) Corner Pixel
{
mask[i][j][k] = (image[i][j][k]+image[i+1][j][k]+image[i][j+1][k]+image[i+1][j+1][k])/4;
}
else if(i==0 && j==(m_nSizeY-1))// (0,yMax) Corner Pixel
{
mask[i][j][k] = (image[i][j][k]+image[i+1][j][k]+image[i][j-1][k]+image[i+1][j-1][k])/4;
}
else if(i==(m_nSizeX-1) && j==0)// (xMax,0) Corner Pixel
{
mask[i][j][k] = (image[i][j][k]+image[i-1][j][k]+image[i][j+1][k]+image[i-1][j+1][k])/4;
}
else if(i==(m_nSizeX-1) && j==(m_nSizeY-1))// (xMax,yMax) Corner Pixel
{
mask[i][j][k] = (image[i][j][k]+image[i-1][j][k]+image[i][j-1][k]+image[i-1][j-1][k])/4;
}
else if(i==0)// (0,---) Edge Pixels
{
mask[i][j][k] = (image[i][j][k]+image[i][j+1][k]+image[i+1][j+1][k]+image[i+1][j][k]+image[i+1][j-1][k]+image[i][j-1][k])/6;
}
else if(j==0)// (---,0) Edge Pixels
{
mask[i][j][k] = (image[i][j][k]+image[i-1][j][k]+image[i-1][j+1][k]+image[i][j+1][k]+image[i+1][j+1][k]+image[i+1][j][k])/6;
}
else if(i==(m_nSizeX-1))// (xMax,---) Edge Pixels
{
mask[i][j][k] = (image[i][j][k]+image[i][j-1][k]+image[i-1][j-1][k]+image[i-1][j][k]+image[i-1][j+1][k]+image[i][j+1][k])/6;
}
else if(j==(m_nSizeY-1))// (---,yMax) Edge Pixels
{
mask[i][j][k] = (image[i][j][k]+image[i+1][j][k]+image[i+1][j-1][k]+image[i][j-1][k]+image[i-1][j-1][k]+image[i-1][j][k])/6;
}
else // Mid-Image Pixels
{
mask[i][j][k] = (image[i][j][k]+image[i][j+1][k]+image[i+1][j+1][k]+image[i+1][j][k]+image[i+1][j-1][k]+image[i][j-1][k]+image[i-1][j-1][k]+image[i-1][j][k]+image[i-1][j+1][k])/9;
}
}
}
}
//STEP FIVE: OUTPUT BLURRED IMAGE
lOffset = 0;
for(long i=0; i<m_nSizeX ; i++)
{
for(long j=0; j<m_nSizeY ; j++)
{
for(int k=0; k<(nrPixels) ; k++)
{
*(reinterpret_cast<unsigned char*>(m_pcMemInv + lOffset) ) = mask[i][j][k];
//*(reinterpret_cast<unsigned char*>(m_pcMemInv + lOffset) ) = image[i][j][k];
lOffset++;
}
}
}
// STOP USING IMAGE MEMORY NOW
for (int i = 0; i < m_nSizeX; ++i) {
for (int j = 0; j < m_nSizeY; ++j)
delete [] image[i][j];
delete [] image[i];
}
delete [] image;
// STOP USING MASK MEMORY NOW
for (int i = 0; i < m_nSizeX; ++i) {
for (int j = 0; j < m_nSizeY; ++j)
delete [] mask[i][j];
delete [] mask[i];
}
delete [] mask;
}
catch( ... )
{
}
When using multi-dimensional indexing, usually the first index is y, the second x and the third red/green/blue - you use a nonstandard transposed layout with i, j and k, where i seems to mean a horizontal index (seeing that you compare it to m_nSizeX).
I guess your image is getting transposed when you copy it the first time, transformed in a mysterious way, and transposed back when you copy it the second time; i cannot guess the details, but it's enough to advise you to just get the dimensions right (swap i and j).
By the way, calling coordinates the normal names x and y (instead of i and j, or maybe j and i?) helps.
You don't show the type of m_pcMemOrg, but based on the fact that you need a reinterpret_cast I'm guessing it's not unsigned char. More importantly I think sizeof(*m_pcMemOrg) is not one. Therefore when you add the offset to it, the offset is multiplied by the size of the type.
Replace:
image[i][j][k] = *(reinterpret_cast<unsigned char*>(m_pcMemOrg + lOffset) );
with:
image[i][j][k] = *(reinterpret_cast<unsigned char*>(m_pcMemOrg) + lOffset );
And likewise with the output code.
As an aside, the others are correct in pointing out that this is a horribly inefficient way of storing and traversing an image. A one-dimensional array is best, indexed using the equation [j*stride + i*nrPixels + k] where stride = m_nSizeX*nrPixels. I'd also swap the i and j loops, and use x and y as the names instead.

Manipulating specific x,y values of a cv::Mat?

What is the simplest way to modify a direct x,y location of a cv::Mat object? I have a cv::Mat called 'temp' which has an image in it, and what if I wanted to turn each pixel pink, one by one?
I tried something of the following:
for (int i = 0; i < temp.size().width; i++)
{
for (int j = 0; j < temp.size().height; j++)
{
temp.at(cv::Point(i, j)) = 255;
cv::waitKey();
}
}
but that won't even compile..
The way to use at in this case is temp.at<unsigned char>(i, j).
Here is an example :
Mat H(100, 100, CV_64F);
for(int i = 0; i < H.rows; i++)
for(int j = 0; j < H.cols; j++)
H.at<double>(i,j)=1./(i+j+1);
For the complete description look here: http://opencv.itseez.com/modules/core/doc/basic_structures.html#mat-at