Быстрая и качественная пиксельная интерполяция для экстремального уменьшения изображения - PullRequest
0 голосов
/ 04 июня 2019

В моей программе я уменьшаю изображение размером 500px или больше до экстремального уровня около 16px-32px. Исходное изображение указывается пользователем, поэтому я не могу контролировать его размер. Как вы можете себе представить, несколько пиксельных интерполяций остаются в силе, и результат неизбежно сильно смещается.

Я пробовал билинейную, бикубическую и квадратную среднюю выборку. Квадратная средняя выборка фактически дает наиболее приличные результаты, но чем она меньше, тем больше должен быть радиус выборки. В результате он становится довольно медленным - медленнее, чем другие методы интерполяции.

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

Итак, вопрос в том, какой рекомендуемый тип пиксельной интерполяции является быстрым и хорошо работает на таких экстремальных уровнях уменьшения масштаба?

Я не хочу использовать библиотеку, поэтому мне нужно что-то, что я могу написать вручную, и это не слишком сложно. Я работаю в C ++ с VS 2012.

Вот пример кода, который я попробовал по запросу (надеюсь, без ошибок из моего вырезания и вставки псевдокода). При этом выполняется среднее уменьшение масштаба 7x7, и, хотя это лучший результат, чем билинейная или бикубическая интерполяция, он также требует значительных успехов:

    // Sizing control
    ctl(0): "Resize",Range=(0,800),Val=100

    // Variables
    float fracx,fracy;
    int Xnew,Ynew,p,q,Calc;
    int x,y,p1,q1,i,j;

    //New image dimensions
    Xnew=image->width*ctl(0)/100;
    Ynew=image->height*ctl(0)/100;

    for (y=0; y<image->height; y++){ // rows
    for (x=0; x<image->width; x++){ // columns

        p1=(int)x*image->width/Xnew;
        q1=(int)y*image->height/Ynew;

        for (z=0; z<3; z++){ // channels

             for (i=-3;i<=3;i++) {
             for (j=-3;j<=3;j++) {
             Calc += (int)(src(p1-i,q1-j,z)); 
             } //j
             } //i

             Calc /= 49;

             pset(x, y, z, Calc);

         }  // channels         


    }  // columns
    }  // rows

Спасибо!

1 Ответ

1 голос
/ 05 июня 2019

Первым делом нужно использовать указатели на ваши данные.Никогда не используйте индексы на каждом пикселе.Когда вы пишете: src(p1-i,q1-j,z) или pset(x, y, z, Calc), сколько вычислений делается?Используйте указатели для данных и манипулируйте ими.

Второе: ваш алгоритм неверен.Вам не нужен средний фильтр, но вы хотите создать сетку на исходном изображении и для каждой ячейки сетки вычислить среднее значение и поместить его в соответствующий пиксель выходного изображения.

Конкретное решение должнобыть адаптированы к вашему представлению данных, но это может быть что-то вроде этого:

    std::vector<uint32_t> accum(Xnew);
    std::vector<uint32_t> count(Xnew);
    uint32_t *paccum, *pcount;
    uint8_t* pin = /*pointer to input data*/;
    uint8_t* pout = /*pointer to output data*/;
    for (int dr = 0, sr = 0, w = image->width, h = image->height; sr < h; ++dr) {
        memset(paccum = accum.data(), 0, Xnew*4);
        memset(pcount = count.data(), 0, Xnew*4);
        while (sr * Ynew / h == dr) {
            paccum = accum.data();
            pcount = count.data();
            for (int dc = 0, sc = 0; sc < w; ++sc) {
                *paccum += *i;
                *pcount += 1;
                ++pin;
                if (sc * Xnew / w > dc) {
                    ++dc;
                    ++paccum;
                    ++pcount;
                }
            }
            sr++;
        }
        std::transform(begin(accum), end(accum), begin(count), pout, std::divides<uint32_t>());
        pout += Xnew;
    }

Это было написано с использованием моей собственной библиотеки (все еще в разработке), и, кажется, работает, но позже я изменил имена переменных вчтобы упростить его здесь, я ничего не гарантирую!

Идея состоит в том, чтобы иметь локальный буфер 32-битного типа, который может содержать частичную сумму всех пикселей в строках, попадающих в ряд.выходного изображения.Затем вы делите на количество ячеек и сохраняете вывод в окончательное изображение.

Первое, что вы должны сделать, это настроить систему оценки производительности, чтобы измерить, насколько любое изменение влияет на производительность.

...