Image Manipulation - is this a for-loop issue? - c++

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.

Related

Working with small 2D parts of big 2D array and moving around it

I have one big 2D array and I want change values in smaller 2D. I can't just get some results.
I have something like that (for 16x16 array f.e.)
int Down8 = 0;
int Right8 = 0;
for (int Down = 0; Down < 16; Down+8)
{
for (int Right = 0; Right < 16; Right+8)
{
Right8 = Right;
Down8 = Down;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
matrixZ[j][i] = TEST[Right8][Down8];
Right++;
}
Down++;
Right8 = Right;
}
dctTransform(matrixZ);
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
matrixZ[j][i] = matrixZ[j][i] / Quant50[j][i];
}
}
zigZagMatrix(matrixZ, 8, 8);
}
}
For 2D 16x16 my function zigZagMatrix should return 4 lines into file (because 16x16 array has 4 8x8 arrays) but only returns 2. 640x480 array should return 4800 lines ( 640/8*480/8 ), returns 60.
Where am I blind/wrong?
All functions I have works with 8x8 array so all of it is loops problem.

window operation by Pointer of image

If we access pixel by a pointer using step and data of Mat Image. see example below
int step = srcimg.step;
for (int j = 0; j < srcimg.rows; j++) {
for (int i = 0; i < srcimg.cols; i++) {
//this is pointer to the pixel value.
uchar* ptr = srcimg.data + step* j + i;
}
}
Question:
How can we perform 3x3 weighted avg operations with image step by a pointer?
thanks
You mustn't use data field in opencv because memory is not allways continuous. you can check this using isContinuous() method.
Now you can do like this (image type is CV_8UC1)
for (int i = 1; i < srcimg.rows-1; i++)
{
for (int j = 1; j < srcimg.cols-1; j++)
{
int x=0;
for (int k=-1;k<=1;k++)
{
uchar* ptr=srcimg.ptr(k+i)+j-1;
for (int l=-1;l<=1;l++,ptr++)
x +=*ptr;
}
}
}
image border are not processed. Now if you want to blur an image use blur method
You can use this post too
I am doing something like this .
int sr = 3;
for (int j = 0; j < srcimg.rows; j++) {
for (int i = 0; i < srcimg.cols; i++) {
uchar* cp_imptr = im.data;
uchar* tptr = im.data + imstep *(sr + j) + (sr + i);
int val_tptr = cp_imptr [imstep *(sr + j) + (sr + i)]; //pointer of image data amd step at 3x3
int val_cp_imptr = cp_imptr[imstep *j + i];
double s = 0;
for (int n = templeteWindowSize; n--;)
{
for (int m = templeteWindowSize; m--;)
{
uchar* t = tptr; //pointer of template
// sum
s += *t;
t++;
}
t += cstep;
}
}
cout << endl;
}

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.

OpenCV: read matrix value

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!

Memory issues with two dimensional array

Following this nice example I found, I was trying to create a function that dynamically generates a 2D grid (two dimensional array) of int values.
It works fairly well the first couple of times you change the values but if crashes after that. I guess the part where memory is freed doesn't work as it should.
void testApp::generate2DGrid() {
int i, j = 0;
// Delete previous 2D array
// (happens when previous value for cols and rows is 0)
if((numRowsPrev != 0) && (numColumnsPrev != 0)) {
for (i = 0; i < numRowsPrev; i++) {
delete [ ] Arr2D[i];
}
}
// Create a 2D array
Arr2D = new int * [numColumns];
for (i = 0; i < numColumns; i++) {
Arr2D[i] = new int[numRows];
}
// Assign a random values
for (i=0; i<numRows; i++) {
for (j = 0; j < numColumns; j++) {
Arr2D[i][j] = ofRandom(0, 10);
}
}
// Update previous value with new one
numRowsPrev = numRows;
numColumnsPrev = numColumns;
}
I see 1 major bug:
// Assign a random values
for (i=0; i<numRows; i++){
for (j=0; j<numColumns; j++){
Arr2D[i][j] = ofRandom(0, 10);
}
}
Here the variable 'i' is used as the first index into 'Arr2D' and goes to a max of (numRows -1)
While in this code:
for (i=0; i<numColumns; i++)
{
Arr2D[i] = new int[numRows];
}
The variable 'i' is used as the first index but goes to a max of (numColumns-1). If numRows is much larger than numColumns then we are going to have a problem.
As a side note. When you try and clean up you are leaking the columns:
if((numRowsPrev != 0) && (numColumnsPrev != 0))
{
for (i=0; i<numRowsPrev; i++){
delete [ ] Arr2D[i];
}
// Need to add this line:
delete [] Arr2D;
}
Next thing to note.
This is truly not a good idea. Use some of the provided STL classes (or potentially boost Matrix). This looks like you are binding global variables and all sorts of other nasty stuff.
2-dim array in C++ with no memory issues:
#include <vector>
typedef std::vector<int> Array;
typedef std::vector<Array> TwoDArray;
Usage:
TwoDArray Arr2D;
// Add rows
for (int i = 0; i < numRows; ++i) {
Arr2D.push_back(Array());
}
// Fill in test data
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++) {
Arr2D[i].push_back(ofRandom(0, 10));
}
}
// Make sure the data is there
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++) {
std::cout << Arr2D[i][j] << ' ';
}
std::cout << '\n';
}