Самый быстрый алгоритм для уменьшения 32-битного изображения RGB - PullRequest
0 голосов
/ 13 ноября 2009

Какой алгоритм использовать для уменьшения 32-битного изображения RGB до пользовательского разрешения? Алгоритм должен усреднять пиксели.

например, если у меня есть изображение 100x100, и я хочу новое изображение размером 20x50. Среднее из первых пяти пикселей первой строки источника даст первый пиксель dest, а среднее из первых двух пикселей первого столбца даст первый пиксель столбца dest.

В настоящее время я сначала уменьшаю разрешение X, а затем уменьшаю разрешение Y. Мне нужен один временный буфер в этом методе.

Есть ли какой-нибудь оптимизированный метод, который вы знаете?

Ответы [ 7 ]

6 голосов
/ 13 ноября 2009

Вы ищете термин «Resampling». В вашем случае вы хотите, чтобы ресамплинг изображения. Вы, кажется, уже делаете линейную интерполяцию, которая должна быть самой быстрой. Вот ~ 6 базовых алгоритмов. Если вы действительно хотите углубиться в тему, загляните в «ядра пересэмплирования».

2 голосов
/ 14 ноября 2009

После выполнения стандартных оптимизаций C (арифметика указателей, математика с фиксированной точкой и т. Д.) Есть также некоторые более умные оптимизации, которые будут иметься. Очень давно я видел реализацию скалера, которая сначала масштабировала направление X. В процессе записи горизонтально масштабированного изображения оно поворачивало изображение на 90 градусов в памяти. Это было сделано для того, чтобы, когда пришло время выполнить чтение для шкалы направления Y, данные в памяти были бы лучше выровнены в кэш.

Этот метод сильно зависит от процессора, на котором он будет работать.

2 голосов
/ 13 ноября 2009

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

Если вы испытываете желание ответить «самым быстрым алгоритмом, который все еще дает очень хорошее качество», вы по существу охватили все поле алгоритма, которое касается только выборки / изменения размера изображения.

И вы уже обрисовали свое первоначальное представление об алгоритме:

Среднее из первых пяти пикселей первого исходная строка даст первый пиксель Dest

Вычисление среднего значения для каждого канала на исходных пикселях может показаться тривиальным, вы ищете пример кода, который это делает?

Или вы ищете кого-то, кто бросит вызов вашему первоначальному проекту алгоритма с чем-то еще быстрее?

1 голос
/ 14 ноября 2009

Это действительно компромисс между скоростью и качеством.

Прежде всего, вы правы, что выполнение одного измерения затем другого происходит медленнее, чем должно быть. Слишком много памяти читает и пишет.

Ваш большой выбор - поддерживать дробные пиксели или нет. Ваш пример от 100х100 до 20х50. Таким образом, 10 пикселей соответствуют 1. Что если вы переходите от 100x100 к 21x49? Готовы ли вы работать на границах исходного пикселя или хотите использовать дробные пиксели? Что бы вы сделали для 100x100 до 99x99?

Вы должны сказать нам, что вы готовы принять, прежде чем мы сможем сказать, что быстрее всего.

А также расскажите нам о возможных крайностях усадки. Сколько порядков может быть разница между источником и пунктом назначения? В какой-то момент выборка репрезентативных пикселей внутри источника не будет намного хуже, чем усреднение всех пикселей. Но вам нужно быть осторожным в выборе репрезентативных пикселей, иначе вы получите псевдонимы со многими распространенными шаблонами.

1 голос
/ 13 ноября 2009

Это усредняет соответствующие пиксели.

 w_ratio = src.w / dest.w
 h_ratio = src.h / dest.h

 dest[x,y] = 
    AVG( src[x * w_ratio + xi, y * h_ratio + yi] ) 
      where
           xi in range (0, w_ratio - 1), inc by 1
           yi in range (0, h_ratio - 1), inc by 1

Для граничных условий сделать отдельный цикл (нет, если в цикле).

Вот код, похожий на C:

src и dest - это растровые изображения, которые:
* свойство src [x, y] для пикселя
* свойство src.w для ширины
* свойство src.h для высоты

пиксель был определен так, чтобы

добавление

p1 = p1 + p2     
is same as
p1.r = p1.r + p2.r
p1.g = p1.g + p2.g
...

раздел

p1 = p1 / c
p1.r = p1.r / c
p1.g = p1.g / c

оценка с константой 0

p1 = 0
p1.r = 0
p1.g = 0
...

для простоты я не буду рассматривать проблему, когда переполнение целочисленного компонента пикселя ...

float w_ratio = src.w / dest.w;
float h_ratio = src.h / dest.h;
int w_ratio_i = floor(w_ratio);
int h_ratio_i = floor(h_ratio);

wxh = w_ratio*h_ratio;

for (y = 0; y < dest.w; y++)
for (x = 0; x < dest.h; x++){
    pixel temp = 0;     

    int srcx, srcy;
    // we have to use here the floating point value w_ratio, h_ratio
    // otherwise towards the end it can get a little wrong
    // this multiplication can be optimized similarly to Bresenham's line
    srcx = floor(x * w_ratio);
    srcy = floor(y * h_ratio);

    // here we use floored value otherwise it might overflow src bitmap
    for(yi = 0; yi < h_ratio_i; yi++)
    for(xi = 0; xi < w_ratio_i; xi++)
            temp += src[srcx + xi, srcy + yi];
    dest[x,y] = temp / wxh;
}

Оптимизация линии Брезенхама

1 голос
/ 13 ноября 2009

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

0 голосов
/ 13 ноября 2009

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

...