Image Manipulation - Rescale image.

Hello, I am quiet new to this website as well to programming, so I hope the post is in appropriate Forum and also in correct structure.

I am creating a program that is designed to allow users to manipulate their images in desired ways like removing RGB colours from the image, inversing colours etc. However throughout my creation of the code I seem being unable to rescale the image without either cropping it down or scaling it up, leaving Black Space around original image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void rescaleImage(IMG& img)
{
   Image image (img.width, img.height);
   int new_width, new_height;
   
   img.width = image.getWidth();
   img.height = image.getHeight();
   
   do
      {
        cout << "Current image dimensions are: \n"
             << "Width : " << img.width << endl;
        cout << "Height: " << img.height << endl;
             

        cout << "Please enter new dimensions of the image (Cannot be less than 0): " << endl;
        cout << "New Width : ";
        cin >> img.width;
        cout << "New Height: ";
        cin >> img.height;
        
      // Display this if dimensions are incorrect ( Below 0)
      if (new_width < 0 || new_height < 0)
      {
         cout << "New dimensions are not acceptable. Please insert dimensions higher than 0" << endl;
      } 
      }             
      while (new_width < 0 || new_height < 0);
     
   image.resize(img.width, img.height);
   
   // Save the image...
      for(int y=0; y < img.height; y++)
      {
         for(int x=0; x< img.width; x++)
            {							
               image.setPixel (x, y, Colour ( 255*img.pixels[x + y * img.width].r , 255*img.pixels[x + y * img.width].g , 255*img.pixels[x + y * img.width].b));
            }
      }
      
   ImageFile("random.bmp").save(image);
   
   
   }


That is my function for loading the image:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
img.width = img.image.getWidth();
   img.height = img.image.getHeight();

	img.pixels = new RGB[img.width * img.height];
	
	for(int y = 0; y < img.height; y++)
      {
      	for(int x = 0; x < img.width; x++)
         {
         Colour colour = img.image.getPixel(x, y);
                        
         img.pixels[x + y * img.width].r = (float)colour.getR()/255;
         img.pixels[x + y * img.width].g = (float)colour.getG()/255;
         img.pixels[x + y * img.width].b = (float)colour.getB()/255;                        
         }
      }

   cout << "Image has been loaded" << endl;


I am hoping that some of you guys will manage to help me. Thanks.
To rescale an image, you need to rescale both the width and height, as well as resample the content for that image frame. Essentially, you create a new frame (with your new width and height), and then you remap your current pixel values to your new image dimensions. This means that if the new size is smaller, you will have to merge more than one pixel of the original image to a single pixel in the new size. If the new size is larger, you will have to fill several pixels in the new image using input values from a single pixel from your original image. The interpolation algorithm for generating the new RGB values can vary.

What you seem to be doing (by cropping or having a black border) is changing your image size without resampling your pixels. It is like changing your "canvas size" in Photoshop. You should also resample your pixels based on the new size you want your image to be. To see the effect visually, compare the two effects from Photoshop:

1) Image -> Canvas Size...

2) Image -> Image Size...

You need the second one, but you are probably implementing the first one only.

Last edited on
I am powerless, honestly. Since you posted this I tried to understand the algorithm and apply it to my code, but really I have no idea how to actually apply it, if you know what I mean.

I've been trying to apply different techniques like Bresenham's technique, Bilinear and nearest neighbour. These techniques seems to be pretty similar to each other but I just don't "click" if you get me.

Is there any chance that you or anyone could write or help me understand it by writing a code to demonstrate what you mean.

Thank you.
Last edited on
Let's start from nearest neighbor. When you scale an image -- like I noted earlier, the existing pixel values are the only information you have access to in order to generate a larger or smaller version of that image. For larger versions of the original image, you can take the original pixel values and place them analogically across the new specified dimension, so you can fill up your new canvas size, but there will be new (and vacant) pixel positions that you will have to generate values for, otherwise your image will look like a series of detached grid squares with a black border (see nearest neighbor article I link). In order to avoid that, the resampling algorithm has to fill that space. However, note that nearest neighbor doesn't perform any interpolation for filling up the values of these newly generated pixels. It copies values to fill the required gap, so no new values are ever generated. In scaling up, it just determines through analogy the x and y positions for the existing pixels to be positioned at, and then copies their values to the vacant pixels that are nearest to those. See:

http://tech-algorithm.com/articles/nearest-neighbor-image-scaling/


Interpolation means that new pixel values are generated to fill the gap. This resampling algorithm will create a smoother result, because none of the interpolated values are the same with the next one (like nearest neighbor's copying). Instead, they are all progressively different, so that the vacant space is filled with pixel values that smoothly transition between the existing values taken from the original image. For bilinear interpolation to be understood, let's start from linear interpolation:

http://tech-algorithm.com/articles/linear-interpolation/

This is a pretty good article. The implementation sample code is in java, but it's fairly descriptive. Essentially the mathematical notion of linear interpolation is applied in each of the channels that compose a pixel value -- that is, R, G, and B.

This, however, gives you the linearly interpolated pixel value in only one dimension. Textures and images are in two dimensions, which means that interpolating in only one dimension won't suffice. You need to interpolate for pixel values in both x and y dimensions, so this means you'll have to use something like bilinear interpolation.

For bilinear interpolation, see:

http://tech-algorithm.com/articles/bilinear-image-scaling/

Similarly, for scaling down, you would need to take into consideration the dimension ratio between the old and new image size. Let's approach this one dimensionally and assume a one-dimensional array of pixels that we want to resize: This means that, if an image is 50% of the original one, then each of the pixels of the new image will correspond to two pixels of the original image (e.g. old image dimension: 120 pixels. new image dimension is 50%: 60 pixels. 120/60=2 pixels correspond to one pixel in the new image. ). So an average of these two pixels would have to be calculated (for R, G and B), and the result applied to the single pixel corresponding to those in the new image. This, done for all pixels in the array, eventually creates a value for the pixels of the new (now scaled down) image/array.

So, expanding to a 2D example: for a uniform scale-down of your image you'd want to uniformly affect the x and y dimensions by a given amount. Continuing the previous example, 50% scaling down, and with 120x120 as my original image size, this gives the new image size of 60x60 and the ratio of 2 full pixels (in each dimesion) of the original image corresponding to one pixel of the new image. So, you could divide your image in elementary "textures" of 2x2 pixels, and then average these to a single pixel, and then apply that to the corresponding pixel value in your new image. This is a simplified example, of course. For images that are not square, and whose scaling amount differs, you would end up with floating point ratios -- which would have to be resolved into integral units , since you are dealing with designating integral pixel values. Similarly, for non-uniform scaling, you would have to alter your calculations so that the non-uniformity is taken into account: this is completely off the top of my head, although I can't remember a non-uniform scale-down algorithm that performs interpolation, so I thought that perhaps you can create a new image with the new aspect ratio in the form of a scale-up, applying bi-linear interpolation to generate it's pixels, then apply a uniform scale-down to that resultant image in order to generate the non-uniformly scaled-down result you originally intended (I'm certainly not sure whether the non-uniform scale-down requires these two steps or if it can be achieved in one. I could very likely be wrong and this can be applied in a single step rather than two -- which would be much more efficient if this is indeed the case -- although pixel filter algorithms are not current for me and I am not sure at the moment).

I hope this is somewhat helpful to you!

Ogoyant



Last edited on
Topic archived. No new replies allowed.