Search code examples
cimagescalingnearest-neighbor

Scaling up an image using nearest-neighbor


I have been trying to make my program scale up an image. I had some problem to allocate new space for my scaled image, but I think it is fixed. The problem I am having is that the program crashes when I am trying to send back my image from my temporary memory holder.

The loaded image is placed in my struct Image. The pixels are placed in img->pixels, the height in img->height and the width in img->width. But I have no idea why the program crashes when I transfer the pixels from my tmp2 struct to my img struct while it does not crash when I do the opposite. Here is the code:

void makeBigger(Image *img, int scale) {

    Image *tmp2;
    tmp2 = (Image*)malloc(sizeof(Image));
    tmp2->height = img->height*scale;
    tmp2->width = img->width*scale;

    tmp2->pixels = (Pixel**)malloc(sizeof(Pixel*)*tmp2->height);
    for (unsigned int i = 0; i < img->height; i++)
    {
        tmp2->pixels[i] = (Pixel*)malloc(sizeof(Pixel)*tmp2->width);
        for (unsigned int j = 0; j < img->width; j++)
        {
            tmp2->pixels[i][j] = img->pixels[i][j];
        }
    }
    free(img->pixels);

    //scaling up the struct's height and width
    img->height *= scale;
    img->width *= scale;

    img->pixels = (Pixel**)malloc(sizeof(Pixel*)*img->height);
    for (unsigned int i = 0; i < tmp2->height; i++)
    {
        img->pixels[i] = (Pixel*)malloc(sizeof(Pixel)*img->width);
        for (unsigned int j = 0; j < tmp2->width; j++)
        {
            img->pixels[i][j] = tmp2->pixels[i+i/2][j+j/2];
        }
    }
}

I would be glad if you have any idea of how to make the nearest-neighbor method to work.

EDIT: I am trying to crop the inner rectangle so I can scale it up (zoom).

Image *tmp = (Image*)malloc(sizeof(Image));
tmp->height = img->height / 2;
tmp->width = img->width / 2;

tmp->pixels = (Pixel**)malloc(sizeof(Pixel*) * tmp->height);
for (unsigned i = img->height / 4 - 1; i < img->height - img->height / 4; i++) {
    tmp->pixels[i] = (Pixel*)malloc(sizeof(Pixel) * tmp->width);
    for (unsigned j = img->width / 4; j < img->width - img->width / 4; j++) {
        tmp->pixels[i][j] = img->pixels[i][j];
    }
}

for (unsigned i = 0; i < img->height; i++) {
    free(img->pixels[i]);
}
free(img->pixels);

img->height = tmp->height;
img->width = tmp->width;
img->pixels = tmp->pixels;
free(tmp);

Solution

  • I see that you're overcomplicating things (walking over the image twice for example).
    Here's the code (I am posting the whole program - I made assumptions about Pixel and Image that might not match what you have), but if you copy / paste makeBigger it should work in your code OOTB:

    code00.c:

    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    typedef uint32_t Pixel;
    
    typedef struct {
        uint32_t width, height;
        Pixel **pixels;
    } Image;
    
    
    void makeBigger(Image *img, int scale)
    {
        uint32_t i = 0, j = 0;
        Image *tmp = (Image*)malloc(sizeof(Image));
        tmp->height = img->height * scale;
        tmp->width = img->width * scale;
    
        tmp->pixels = (Pixel**)malloc(sizeof(Pixel*) * tmp->height);
        for (i = 0; i < tmp->height; i++) {
            tmp->pixels[i] = (Pixel*)malloc(sizeof(Pixel) * tmp->width);
            for (j = 0; j < tmp->width; j++) {
                tmp->pixels[i][j] = img->pixels[i / scale][j / scale];
            }
        }
    
        for (i = 0; i < img->height; i++)
            free(img->pixels[i]);
        free(img->pixels);
    
        img->width = tmp->width;
        img->height = tmp->height;
        img->pixels = tmp->pixels;
        free(tmp);
    }
    
    
    void printImage(Image *img)
    {
        printf("Width: %d, Height: %d\n", img->width, img->height);
        for (uint32_t i = 0; i < img->height; i++) {
            for (uint32_t j = 0; j < img->width; j++)
                printf("%3d", img->pixels[i][j]);
            printf("\n");
        }
        printf("\n");
    }
    
    
    int main()
    {
        uint32_t i = 0, j = 0, k = 1;
        Image img;
        // Initialize the image
        img.height = 2;
        img.width = 3;
        img.pixels = (Pixel**)malloc(sizeof(Pixel*) * img.height);
        for (i = 0; i < img.height; i++) {
            img.pixels[i] = (Pixel*)malloc(sizeof(Pixel) * img.width);
            for (j = 0; j < img.width; j++)
                img.pixels[i][j] = k++;
        }
    
        printImage(&img);
        makeBigger(&img, 2);
        printImage(&img);
    
        // Destroy the image
        for (i = 0; i < img.height; i++)
            free(img.pixels[i]);
        free(img.pixels);
    
        printf("\nDone.\n");
        return 0;
    }
    

    Notes (makeBigger related - designed to replace the content of the image given as argument):

    • Construct a temporary image that will be the enlarged one
    • Only traverse the temporary image once (populate its pixels as we allocate them); to maintain scaling to the original image and make sure that the appropriate pixel is "copied" into the new one, simply divide the indexes by the scaling factor: tmp->pixels[i][j] = img->pixels[i / scale][j / scale]
    • Deallocate the original image content: since each pixel row is malloced, it should also be freed (free(img->pixels); alone will yield memory leaks)
    • Store the temporary image content (into the original one) and then deallocate it

    Output:

    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q041861274]> ~/sopr.sh
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [064bit prompt]> ls
    code00.c
    [064bit prompt]> gcc -o code00.exe code00.c
    [064bit prompt]> ./code00.exe
    Width: 3, Height: 2
      1  2  3
      4  5  6
    
    Width: 6, Height: 4
      1  1  2  2  3  3
      1  1  2  2  3  3
      4  4  5  5  6  6
      4  4  5  5  6  6
    
    
    Done.