simfrek
simfrek

Reputation: 1

How can I pixelate a 1d array

I want to pixelate an image stored in a 1d array, although i am not sure how to do it, this is what i have comeup with so far... the value of pixelation is currently 3 for testing purposes. currently it just creates a section of randomly coloured pixels along the left third of the image, if i increase the value of pixelation the amount of random coloured pixels decreases and vice versa, so what am i doing wrong?

I have also already implemented the rotation, reading of the image and saving of a new image this is just a separate function which i need assistance with.

picture pixelate( const std::string& file_name, picture& tempImage, int& pixelation /* TODO: OTHER PARAMETERS HERE */)
{
picture pixelated = tempImage;
RGB tempPixel;
tempPixel.r = 0;
tempPixel.g = 0;
tempPixel.b = 0;
int counter = 0;
int numtimesrun = 0;

    for (int x = 1; x<tempImage.width; x+=pixelation)
    {
        for (int y = 1; y<tempImage.height; y+=pixelation)
        {
            //RGB tempcol;
            //tempcol for pixelate
            for (int i = 1; i<pixelation; i++)
            {
                for (int j = 1; j<pixelation; j++)
                {
                    tempPixel.r +=tempImage.pixel[counter+pixelation*numtimesrun].colour.r;
                    tempPixel.g +=tempImage.pixel[counter+pixelation*numtimesrun].colour.g;
                    tempPixel.b +=tempImage.pixel[counter+pixelation*numtimesrun].colour.b;
                    counter++;
                    //read colour
                }

            }
            for (int k = 1; k<pixelation; k++)
            {
                for (int l = 1; l<pixelation; l++)
                {
                     pixelated.pixel[numtimesrun].colour.r = tempPixel.r/pixelation;
                     pixelated.pixel[numtimesrun].colour.g = tempPixel.g/pixelation;
                     pixelated.pixel[numtimesrun].colour.b = tempPixel.b/pixelation;
                    //set colour
                }

            }
            counter = 0;
            numtimesrun++;
        }
        cout << x << endl;

    }
    cout << "Image successfully pixelated." << endl;
return pixelated;
}

Upvotes: 0

Views: 758

Answers (3)

Alexis Wilke
Alexis Wilke

Reputation: 20818

I'm not too sure what you really want to do with your code, but I can see a few problems.

For one, you use for() loops with variables starting at 1. That's certainly wrong. Arrays in C/C++ start at 0.

The other main problem I can see is the pixelation parameter. You use it to increase x and y without knowing (at least in that function) whether it is a multiple of width and height. If not, you will definitively be missing pixels on the right edge and at the bottom (which edges will depend on the orientation, of course). Again, it very much depends on what you're trying to achieve.

Also the i and j loops start at the position defined by counter and numtimesrun which means that the last line you want to hit is not tempImage.width or tempImage.height. With that you are rather likely to have many overflows. Actually that would also explain the problems you see on the edges. (see update below)

Another potential problem, cannot tell for sure without seeing the structure declaration, but this sum using tempPixel.c += <value> may overflow. If the RGB components are defined as unsigned char (rather common) then you will definitively get overflows. So your average sum is broken if that's the fact. If that structure uses floats, then you're good.

Note also that your average is wrong. You are adding source data for pixelation x pixalation and your average is calculated as sum / pixelation. So you get a total which is pixalation times larger. You probably wanted sum / (pixelation * pixelation).

Your first loop with i and j computes a sum. The math is most certainly wrong. The counter + pixelation * numtimesrun expression will start reading at the second line, it seems. However, you are reading i * j values. That being said, it may be what you are trying to do (i.e. a moving average) in which case it could be optimized but I'll leave that out for now.


Update

If I understand what you are doing, a representation would be something like a filter. There is a picture of a 3x3:

.+.         *
+*+    =>
.+.

What is on the left is what you are reading. This means the source needs to be at least 3x3. What I show on the right is the result. As we can see, the result needs to be 1x1. From what I see in your code you do not take that in account at all. (the varied characters represent varied weights, in your case all weights are 1.0).

You have two ways to handle that problem:

  1. The resulting image has a size of width - pixelation * 2 + 1 by height - pixelation * 2 + 1; in this case you keep one result and do not care about the edges...
  2. You rewrite the code to handle edges. This means you use less source data to compute the resulting edges. Another way is to compute the edge cases and save that in several output pixels (i.e. duplicate the pixels on the edges).

Update 2

Hmmm... looking at your code again, it seems that you compute the average of the 3x3 and save it in the 3x3:

.+.         ***
+*+    =>   ***
.+.         ***

Then the problem is different. The numtimesrun is wrong. In your k and l loops you save the pixels pixelation * pixelation in the SAME pixel and that advanced by one each time... so you are doing what I shown in my first update, but it looks like you were trying to do what is shown in my 2nd update.

The numtimesrun could be increased by pixelation each time:

numtimesrun += pixelation;

However, that's not enough to fix your k and l loops. There you probably need to calculate the correct destination. Maybe something like this (also requires a reset of the counter before the loop):

counter = 0;
... for loops ...
    pixelated.pixel[counter+pixelation*numtimesrun].colour.r = ...;
    ... (take care of g and b)
    ++counter;

Yet again, I cannot tell for sure what you are trying to do, so I do not know why you'd want to copy the same pixel pixelation x pixelation times. But that explains why you get data only at the left (or top) of the image (very much depends on the orientation, one side for sure. And if that's 1/3rd then pixelation is probably 3.)

WARNING: if you implement the save properly, you'll experience crashes if you do not take care of the overflows mentioned earlier.


Update 3

As explained by Mark in the comment below, you have an array representing a 2d image. In that case, your counter variable is completely wrong since this is 100% linear whereas the 2d image is not. The 2nd line is width further away. At this point, you read the first 3 pixels at the top-left, then the next 3 pixels on the same, and finally the next 3 pixels still on the same line. Of course, it could be that your image is thus defined and these pixels are really one after another, although it is not very likely...

Mark's answer is concise and gives you the information necessary to access the correct pixels. However, you will still be hit by the overflow and possibly the fact that the width and height parameters are not a multiple of pixelation...

Upvotes: 1

Mark Ransom
Mark Ransom

Reputation: 308530

Generally when accessing an image stored in a 1D buffer, each row of the image will be stored as consecutive pixels and the next row will follow immediately after. The way to address into such a buffer is:

image[y*width+x]

For your purposes you want both inner loops to generate coordinates that go from the top and left of the pixelation square to the bottom right.

Upvotes: 0

JeffThompson
JeffThompson

Reputation: 1600

I don't do a lot of C++, but here's a pixelate function I wrote for Processing. It takes an argument of the width/height of the pixels you want to create.

void pixelateImage(int pxSize) {

  // use ratio of height/width...
  float ratio;
  if (width < height) {
    ratio = height/width;
  }
  else {
    ratio = width/height;
  }

  // ... to set pixel height
  int pxH = int(pxSize * ratio);

  noStroke();
  for (int x=0; x<width; x+=pxSize) {
    for (int y=0; y<height; y+=pxH) {
      fill(p.get(x, y));
      rect(x, y, pxSize, pxH);
    }
  }
}

Without the built-in rect() function you'd have to write pixel-by-pixel using another two for loops:

for (int px=0; px<pxSize; px++) {
  for (int py=0; py<pxH; py++) {
    pixelated.pixel[py * tempImage.width + px].colour.r = tempPixel.r;
    pixelated.pixel[py * tempImage.width + px].colour.g = tempPixel.g;
    pixelated.pixel[py * tempImage.width + px].colour.b = tempPixel.b;
  }
}

Upvotes: 0

Related Questions