0
\$\begingroup\$

This is a follow-up question for Two dimensional gaussian image generator in C, Two dimensional bicubic interpolation implementation in C and A SubPlane Method for Generic Two Dimensional Data Plane in C#. Besides gaussian image generating and bicubic interpolation, I am attempting to make a C version subimage function in this post so that the ROI (Region of interest) of an image can be extracted.

The output subimage example:

SubImage

The experimental implementation

  • SubMonoImage function implementation: subimage extracting operation for MONOIMAGE structure.

    MONOIMAGE SubMonoImage(const MONOIMAGE image, unsigned int locationx, unsigned int locationy, unsigned int sizex, unsigned int sizey)
    {
        MONOIMAGE output = CreateMonoImage(sizex, sizey);
        for (size_t y = 0; y < sizey; y++)
        {
            for (size_t x = 0; x < sizex; x++)
            {
                if ((locationy + y) < image.XSIZE &&
                    (locationx + x) < image.YSIZE)          //  valid location
                {
                    output.IMAGE_DATA[GetMonoImageIndex(x, y, output)] = 
                        image.IMAGE_DATA[GetMonoImageIndex((locationx + x), (locationy + y), image)];
                }
            }
        }
        return output;
    }
    
  • SubRGBImage function implementation: subimage extracting operation for RGBIMAGE structure.

    RGBIMAGE SubRGBImage(const RGBIMAGE image, unsigned int locationx, unsigned int locationy, unsigned int sizex, unsigned int sizey)
    {
        MONOIMAGE R = SubMonoImage(GetPlaneR(image), locationx, locationy, sizex, sizey);
        MONOIMAGE G = SubMonoImage(GetPlaneG(image), locationx, locationy, sizex, sizey);
        MONOIMAGE B = SubMonoImage(GetPlaneB(image), locationx, locationy, sizex, sizey);
        RGBIMAGE output = CreateRGBImageFromMonoImages(sizex, sizey, R, G, B);
        DeleteMonoImage(R);
        DeleteMonoImage(G);
        DeleteMonoImage(B);
        return output;
    }
    
  • basic_functions.h: Contains several basic function declarations

    /* Develop by Jimmy Hu */
    
    #ifndef BASIC_FUNCTIONS_H
    #define BASIC_FUNCTIONS_H
    
    #include "base.h"
    #include "imageio.h"
    
    MONOIMAGE CreateMonoImage(const unsigned int, const unsigned int);
    
    void DeleteMonoImage(MONOIMAGE);
    
    MONOIMAGE GetPlaneR(const RGBIMAGE);
    
    MONOIMAGE GetPlaneG(const RGBIMAGE);
    
    MONOIMAGE GetPlaneB(const RGBIMAGE);
    
    RGBIMAGE CreateRGBImage(const unsigned int, const unsigned int);
    
    RGBIMAGE CreateRGBImageFromMonoImages(const unsigned int, const unsigned int, MONOIMAGE, MONOIMAGE, MONOIMAGE);
    
    void DeleteRGBImage(RGBIMAGE);
    
    size_t GetMonoImageIndex(const size_t, const size_t, const MONOIMAGE);
    
    size_t GetRGBImageIndex(const size_t, const size_t, const RGBIMAGE);
    
    size_t clip(const size_t, const size_t, const size_t);
    
    float clip_float(const float, const float, const float);
    
    #endif
    
  • basic_functions.c: Contains several basic function definitions

    /* Develop by Jimmy Hu */
    
    #include "basic_functions.h"
    
    MONOIMAGE CreateMonoImage(const unsigned int sizex, const unsigned int sizey)
    {
        MONOIMAGE output;
        output.XSIZE = sizex;
        output.YSIZE = sizey;
        output.IMAGE_DATA = malloc(sizeof *output.IMAGE_DATA * sizex * sizey);
        if(output.IMAGE_DATA == NULL)
        {    
            printf(stderr, "Memory allocation error!");
            return output;
        }
        return output;
    }
    
    void DeleteMonoImage(MONOIMAGE image)
    {
        free(image.IMAGE_DATA);
        return;
    }
    
    MONOIMAGE GetPlaneR(const RGBIMAGE image)
    {
        MONOIMAGE output = CreateMonoImage(image.XSIZE, image.YSIZE);
        for (size_t y = 0; y < image.YSIZE; y++)
        {
            for (size_t x = 0; x < image.XSIZE; x++)
            {
                output.IMAGE_DATA[GetMonoImageIndex(x, y, output)] = 
                    image.IMAGE_DATA[GetRGBImageIndex(x, y, image)].channels[0];
            }
        }
        return output;
    }
    
    MONOIMAGE GetPlaneG(const RGBIMAGE image)
    {
        MONOIMAGE output = CreateMonoImage(image.XSIZE, image.YSIZE);
        for (size_t y = 0; y < image.YSIZE; y++)
        {
            for (size_t x = 0; x < image.XSIZE; x++)
            {
                output.IMAGE_DATA[GetMonoImageIndex(x, y, output)] = 
                    image.IMAGE_DATA[GetRGBImageIndex(x, y, image)].channels[1];
            }
        }
        return output;
    }
    
    MONOIMAGE GetPlaneB(const RGBIMAGE image)
    {
        MONOIMAGE output = CreateMonoImage(image.XSIZE, image.YSIZE);
        for (size_t y = 0; y < image.YSIZE; y++)
        {
            for (size_t x = 0; x < image.XSIZE; x++)
            {
                output.IMAGE_DATA[GetMonoImageIndex(x, y, output)] = 
                    image.IMAGE_DATA[GetRGBImageIndex(x, y, image)].channels[2];
            }
        }
        return output;
    }
    
    RGBIMAGE CreateRGBImage(const unsigned int sizex, const unsigned int sizey)
    {
        RGBIMAGE output;
        output.XSIZE = sizex;
        output.YSIZE = sizey;
        output.IMAGE_DATA = malloc(sizeof *output.IMAGE_DATA * sizex * sizey);
        if(output.IMAGE_DATA == NULL)
        {    
            printf(stderr, "Memory allocation error!");
            return output;
        }
        return output;
    }
    
    RGBIMAGE CreateRGBImageFromMonoImages(const unsigned int sizex, const unsigned int sizey, MONOIMAGE R, MONOIMAGE G, MONOIMAGE B)
    {
        RGBIMAGE output;
        output.XSIZE = sizex;
        output.YSIZE = sizey;
        output.IMAGE_DATA = malloc(sizeof *output.IMAGE_DATA * sizex * sizey);
        if (output.IMAGE_DATA == NULL)
        {    
            printf(stderr, "Memory allocation error!");
            return output;
        }
        if ((R.XSIZE != G.XSIZE) ||
            (G.XSIZE != B.XSIZE) ||
            (R.XSIZE != B.XSIZE) ||
            (R.YSIZE != G.YSIZE) ||
            (G.YSIZE != B.YSIZE) ||
            (R.YSIZE != B.YSIZE))
        {
            printf(stderr, "Input size inequal!");
            return output;
        }
        for (size_t y = 0; y < R.YSIZE; y++)
        {
            for (size_t x = 0; x < R.XSIZE; x++)
            {
                output.IMAGE_DATA[GetRGBImageIndex(x, y, output)].channels[0] = R.IMAGE_DATA[GetMonoImageIndex(x, y, R)];
                output.IMAGE_DATA[GetRGBImageIndex(x, y, output)].channels[1] = G.IMAGE_DATA[GetMonoImageIndex(x, y, G)];
                output.IMAGE_DATA[GetRGBImageIndex(x, y, output)].channels[2] = B.IMAGE_DATA[GetMonoImageIndex(x, y, B)];
            }
        }
        return output;
    }
    
    void DeleteRGBImage(RGBIMAGE image)
    {
        free(image.IMAGE_DATA);
        return;
    }
    
    size_t GetMonoImageIndex(const size_t x, const size_t y, const MONOIMAGE image)
    {
        return y * image.XSIZE + x;
    }
    
    size_t GetRGBImageIndex(const size_t x, const size_t y, const RGBIMAGE image)
    {
        return y * image.XSIZE + x;
    }
    
    size_t clip(const size_t input, const size_t lowerbound, const size_t upperbound)
    {
        if (input < lowerbound)
        {
            return lowerbound;
        }
        if (input > upperbound)
        {
            return upperbound;
        }
        return input;
    }
    
    float clip_float(const float input, const float lowerbound, const float upperbound)
    {
        if (input < lowerbound)
        {
            return lowerbound;
        }
        if (input > upperbound)
        {
            return upperbound;
        }
        return input;
    }
    
  • base.h: Contains the basic type implementation

    /* Develop by Jimmy Hu */
    
    #ifndef BASE_H
    #define BASE_H
    
    #include <math.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    #define MAX_PATH 256
    #define FILE_ROOT_PATH "./"
    
    #define True true
    #define False false
    
    typedef struct RGB
    {
        unsigned char channels[3];
    } RGB;
    
    typedef struct HSV
    {
        long double channels[3];    //  Range: 0 <= H < 360, 0 <= S <= 1, 0 <= V <= 255
    }HSV;
    
    typedef struct BMPIMAGE
    {
        char FILENAME[MAX_PATH];
    
        unsigned int XSIZE;
        unsigned int YSIZE;
        unsigned char FILLINGBYTE;
        unsigned char *IMAGE_DATA;
    } BMPIMAGE;
    
    typedef struct RGBIMAGE
    {
        unsigned int XSIZE;
        unsigned int YSIZE;
        RGB *IMAGE_DATA;
    } RGBIMAGE;
    
    typedef struct HSVIMAGE
    {
        unsigned int XSIZE;
        unsigned int YSIZE;
        HSV *IMAGE_DATA;
    } HSVIMAGE;
    
    typedef struct MONOIMAGE
    {
        unsigned int XSIZE;
        unsigned int YSIZE;
        unsigned char *IMAGE_DATA;
    } MONOIMAGE;
    
    typedef struct DOUBLEIMAGE
    {
        unsigned int XSIZE;
        unsigned int YSIZE;
        double *IMAGE_DATA;
    };
    
    #endif
    

The full testing code

/* Develop by Jimmy Hu */

#include "base.h"
#include "basic_functions.h"
#include "imageio.h"

MONOIMAGE BicubicInterpolationMonoImage(MONOIMAGE image, const int, const int);

RGBIMAGE BicubicInterpolationRGBImage(RGBIMAGE image, const int, const int);

unsigned char BicubicPolate(const unsigned char*, const float, const float);

float CubicPolate(const float, const float, const float, const float, const float);

/*  Subimage extracting operation for MONOIMAGE structure
*/
MONOIMAGE SubMonoImage(const MONOIMAGE, unsigned int, unsigned int, unsigned int, unsigned int);

/*  Subimage extracting operation for RGBIMAGE structure
*/
RGBIMAGE SubRGBImage(const RGBIMAGE, unsigned int, unsigned int, unsigned int, unsigned int);

int main(int argc, char** argv)
{
    char *FilenameString;
    FilenameString = malloc( sizeof *FilenameString * MAX_PATH);
    
    printf("BMP image input file name:(ex:test): ");
    scanf("%s", FilenameString);
    BMPIMAGE BMPImage1 = bmp_file_read(FilenameString, false);
    RGBIMAGE RGBImage1;
    RGBImage1.XSIZE = BMPImage1.XSIZE;
    RGBImage1.YSIZE = BMPImage1.YSIZE;
    RGBImage1.IMAGE_DATA = raw_image_to_array(BMPImage1.XSIZE, BMPImage1.YSIZE, BMPImage1.IMAGE_DATA);

    RGBIMAGE RGBImage2 = SubRGBImage(RGBImage1, 150, 100, 250, 300);
    RGBIMAGE RGBImage3 = BicubicInterpolationRGBImage(RGBImage2, RGBImage2.XSIZE * 2, RGBImage2.YSIZE * 2);
    
    printf("file name for saving:(ex:test): ");
    scanf("%s", FilenameString);
    bmp_write(FilenameString, RGBImage3.XSIZE, RGBImage3.YSIZE, array_to_raw_image(RGBImage3.XSIZE, RGBImage3.YSIZE, RGBImage3.IMAGE_DATA));

    free(FilenameString);
    DeleteRGBImage(RGBImage1);
    DeleteRGBImage(RGBImage2);
    DeleteRGBImage(RGBImage3);
    return 0;
}

MONOIMAGE BicubicInterpolationMonoImage(MONOIMAGE image, const int newSizeX, const int newSizeY)
{
    MONOIMAGE output = CreateMonoImage(newSizeX, newSizeY);
    int originSizeX = image.XSIZE;
    int originSizeY = image.YSIZE;
    if (output.IMAGE_DATA == NULL)
    {
        printf(stderr, "Memory allocation error!");
        return output;
    }
    
    float ratiox = (float)originSizeX / (float)newSizeX;
    float ratioy = (float)originSizeY / (float)newSizeY;
    
    for (size_t y = 0; y < newSizeY; y++)
    {
        for (size_t x = 0; x < newSizeX; x++)
        {
            float xMappingToOrigin = (float)x * ratiox;
            float yMappingToOrigin = (float)y * ratioy;
            float xMappingToOriginFloor = floor(xMappingToOrigin);
            float yMappingToOriginFloor = floor(yMappingToOrigin);
            float xMappingToOriginFrac = xMappingToOrigin - xMappingToOriginFloor;
            float yMappingToOriginFrac = yMappingToOrigin - yMappingToOriginFloor;
            
            unsigned char ndata[4 * 4];
            for (int ndatay = -1; ndatay <= 2; ndatay++)
            {
                for (int ndatax = -1; ndatax <= 2; ndatax++)
                {
                    ndata[(ndatay + 1) * 4 + (ndatax + 1)] = image.IMAGE_DATA[
                        clip(yMappingToOriginFloor + ndatay, 0, originSizeY - 1) * originSizeX + 
                        clip(xMappingToOriginFloor + ndatax, 0, originSizeX - 1)
                        ];
                }
            }

            output.IMAGE_DATA[ y * newSizeX + x ] = BicubicPolate(ndata, xMappingToOriginFrac, yMappingToOriginFrac);
        }
    }
    return output;
}

RGBIMAGE BicubicInterpolationRGBImage(RGBIMAGE image, const int newSizeX, const int newSizeY)
{
    MONOIMAGE R = BicubicInterpolationMonoImage(GetPlaneR(image), newSizeX, newSizeY);
    MONOIMAGE G = BicubicInterpolationMonoImage(GetPlaneG(image), newSizeX, newSizeY);
    MONOIMAGE B = BicubicInterpolationMonoImage(GetPlaneB(image), newSizeX, newSizeY);
    RGBIMAGE output = CreateRGBImageFromMonoImages(newSizeX, newSizeY, R, G, B);
    DeleteMonoImage(R);
    DeleteMonoImage(G);
    DeleteMonoImage(B);
    return output;
}

unsigned char BicubicPolate(const unsigned char* const ndata, const float fracx, const float fracy)
{
    float x1 = CubicPolate( ndata[0], ndata[1], ndata[2], ndata[3], fracx );
    float x2 = CubicPolate( ndata[4], ndata[5], ndata[6], ndata[7], fracx );
    float x3 = CubicPolate( ndata[8], ndata[9], ndata[10], ndata[11], fracx );
    float x4 = CubicPolate( ndata[12], ndata[13], ndata[14], ndata[15], fracx );

    float output = clip_float(CubicPolate( x1, x2, x3, x4, fracy ), 0.0, 255.0);
    return (unsigned char)output;
}

float CubicPolate(const float v0, const float v1, const float v2, const float v3, const float fracy)
{
    float A = (v3-v2)-(v0-v1);
    float B = (v0-v1)-A;
    float C = v2-v0;
    float D = v1;
    return D + fracy * (C + fracy * (B + fracy * A));
}

MONOIMAGE SubMonoImage(const MONOIMAGE image, unsigned int locationx, unsigned int locationy, unsigned int sizex, unsigned int sizey)
{
    MONOIMAGE output = CreateMonoImage(sizex, sizey);
    for (size_t y = 0; y < sizey; y++)
    {
        for (size_t x = 0; x < sizex; x++)
        {
            if ((locationy + y) < image.XSIZE &&
                (locationx + x) < image.YSIZE)          //  valid location
            {
                output.IMAGE_DATA[GetMonoImageIndex(x, y, output)] = 
                    image.IMAGE_DATA[GetMonoImageIndex((locationx + x), (locationy + y), image)];
            }
        }
    }
    return output;
}

RGBIMAGE SubRGBImage(const RGBIMAGE image, unsigned int locationx, unsigned int locationy, unsigned int sizex, unsigned int sizey)
{
    MONOIMAGE R = SubMonoImage(GetPlaneR(image), locationx, locationy, sizex, sizey);
    MONOIMAGE G = SubMonoImage(GetPlaneG(image), locationx, locationy, sizex, sizey);
    MONOIMAGE B = SubMonoImage(GetPlaneB(image), locationx, locationy, sizex, sizey);
    RGBIMAGE output = CreateRGBImageFromMonoImages(sizex, sizey, R, G, B);
    DeleteMonoImage(R);
    DeleteMonoImage(G);
    DeleteMonoImage(B);
    return output;
}

All suggestions are welcome.

The summary information:

Reference

\$\endgroup\$
1
\$\begingroup\$

There are image representations that lend themselves much better to passing around views of rectangular crops:

struct Image {
    void *pixels;
    enum format pixel_format;
    size_t width;
    size_t height;
    size_t stride;
}

With pixels pointing to the top-left position in the source image, and stride copied from the source's stride, then we don't need to copy the data. If we do want to copy, then that can be a separate function. That helps separate our concerns.

Unrelated, please don't use ALL_CAPS names for C identifiers. It's usual to use capitals for preprocessor macros; these need to be distinguished because they are text substitutions rather than names, and are not confined to a particular scope. When ordinary names are all-caps, it makes it harder to pick out the macros and give them the caution they require.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.