Реализация двумерной бикубической интерполяции на C

Это дополнительный вопрос для реализации двумерной бикубической интерполяции в Matlab и генератора двухмерных гауссовских изображений в C. Помимо кода версии Matlab, я пытаюсь создать функцию двумерной бикубической интерполяции версии C BicubicInterpolation здесь.

Экспериментальная реализация

  • BicubicInterpolation реализация функции:

    RGB* BicubicInterpolation(const RGB* const image, const int originSizeX, const int originSizeY, const int newSizeX, const int newSizeY)
    {
        RGB* output;
        output = malloc(sizeof *output * newSizeX * newSizeY);
        if (output == NULL)
        {
            printf(stderr, "Memory allocation error!");
            return NULL;
        }
    
        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++)
            {
                for (size_t channel_index = 0; channel_index < 3; channel_index++) {
                    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;
                    ndata = malloc(sizeof *ndata * 4 * 4);
                    if (ndata == NULL)
                    {
                        printf(stderr, "Memory allocation error!");
                        return NULL;
                    }
                    for (int ndatay = -1; ndatay < 2; ndatay++)
                    {
                        for (int ndatax = -1; ndatax < 2; ndatax++)
                        {
                            ndata[(ndatay + 1) * 4 + (ndatax + 1)] = image[
                                clip(yMappingToOriginFloor + ndatay, 0, originSizeY - 1) * originSizeX + 
                                clip(xMappingToOriginFloor + ndatax, 0, originSizeX - 1)
                                ].channels[channel_index];
                        }
    
                    }
    
                    unsigned char result = BicubicPolate(ndata, xMappingToOriginFrac, yMappingToOriginFrac);
                    output[ y * newSizeX + x ].channels[channel_index] = result;
                    free(ndata);
                }
            }
        }
        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));
    }
    
    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

    /* 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;
    
    #endif
    

Полный код тестирования

/* Develop by Jimmy Hu */

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

RGB* BicubicInterpolation(const RGB* const image, const int originSizeX, const int originSizeY, const int newSizeX, const int newSizeY);

unsigned char BicubicPolate(const unsigned char* ndata, const float fracx, const float fracy);

float CubicPolate(const float v0, const float v1, const float v2, const float v3, const float fracy);

size_t clip(const size_t input, const size_t lowerbound, const size_t upperbound);

float clip_float(const float input, const float lowerbound, const float upperbound);

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;
    RGBImage2.XSIZE = 1024;
    RGBImage2.YSIZE = 1024;
    RGBImage2.IMAGE_DATA = BicubicInterpolation(RGBImage1.IMAGE_DATA, RGBImage1.XSIZE, RGBImage1.YSIZE, RGBImage2.XSIZE, RGBImage2.YSIZE);
    
    printf("file name for saving:(ex:test): ");
    scanf("%s", FilenameString);
    bmp_write(FilenameString, RGBImage2.XSIZE, RGBImage2.YSIZE, array_to_raw_image(RGBImage2.XSIZE, RGBImage2.YSIZE, RGBImage2.IMAGE_DATA));

    free(FilenameString);
    free(RGBImage1.IMAGE_DATA);
    free(RGBImage2.IMAGE_DATA);
    return 0;
}

RGB* BicubicInterpolation(const RGB* const image, const int originSizeX, const int originSizeY, const int newSizeX, const int newSizeY)
{
    RGB* output;
    output = malloc(sizeof *output * newSizeX * newSizeY);
    if (output == NULL)
    {
        printf(stderr, "Memory allocation error!");
        return NULL;
    }
    
    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++)
        {
            for (size_t channel_index = 0; channel_index < 3; channel_index++) {
                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;
                ndata = malloc(sizeof *ndata * 4 * 4);
                if (ndata == NULL)
                {
                    printf(stderr, "Memory allocation error!");
                    return NULL;
                }
                for (int ndatay = -1; ndatay < 2; ndatay++)
                {
                    for (int ndatax = -1; ndatax < 2; ndatax++)
                    {
                        ndata[(ndatay + 1) * 4 + (ndatax + 1)] = image[
                            clip(yMappingToOriginFloor + ndatay, 0, originSizeY - 1) * originSizeX + 
                            clip(xMappingToOriginFloor + ndatax, 0, originSizeX - 1)
                            ].channels[channel_index];
                    }
                    
                }

                unsigned char result = BicubicPolate(ndata, xMappingToOriginFrac, yMappingToOriginFrac);
                output[ y * newSizeX + x ].channels[channel_index] = result;
                free(ndata);
            }
        }
    }
    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));
}

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;
}

Все предложения приветствуются.

Сводная информация:

  • На какой вопрос это продолжение?

    Реализация двумерной бикубической интерполяции в Matlab и

    Генератор двумерных гауссовских изображений на языке C.

  • Какие изменения были внесены в код с момента последнего вопроса?

    В этом посте я пытаюсь создать функцию двумерной бикубической интерполяции версии C.

  • Почему запрашивается новый обзор?

    Если есть какие-то улучшения, пожалуйста, дайте мне знать.

2 ответа
2

Избегайте ненужного выделения временного хранилища

В самом внутреннем цикле вы делаете это:

unsigned char* ndata;
ndata = malloc(sizeof *ndata * 4 * 4);

Это медленно и совершенно не нужно; вы можете просто объявить массив в стеке следующим образом:

unsigned char ndata[4 * 4];

Возможные улучшения алгоритма

Вероятно, что многие из промежуточных значений, которые вы вычисляете в BicubicPolate() могут быть такими же, как и для соседних пикселей. Также в CubicPolate(), ни одно из значений A к D зависит от fracy, и некоторая предварительная обработка изображения может позволить вам избежать многих операций.

Также учтите, что соотношение между источником и местом назначения может быть больше 1 или меньше 1, и для каждого случая могут быть лучше разные алгоритмы, а отношения вида n или же 1 / n, где n является целым числом, особенно может быть кандидатом на улучшение алгоритмов.

    Хорошо, давайте попробуем, используя это тестовое изображение. Размер кадра 0 был изменен (с ширины 512 до ширины 300) другим программным обеспечением, кадр 1 — этим кодом.

    сараи

    Этот регулярный узор из более темных пикселей не должен появляться.

    Это выглядит как BicubicPolate читает некоторые записи из ndata (16 байт), в которые никогда не записывались (в него записывается 9 байт).

    Я не совсем уверен, как это должно работать, но меняя петли, которые заполняют ndata подойти и в том числе 2, кажется, улучшает результат.

    • Ты прав. Петли, которые заполняют ndata должно быть от -1 до 2, и 16 значений заполнены.

      — JimmyHu

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *