Gimp Gaussian Blur - объяснение кода

Я пытаюсь понять, как работает размытие по Гауссу в Gimp. Я скачал код, я почти мог понять кое-что... но я озадачен чем-то другим.

Вот код:

make_rle_curve (gdouble   sigma,
            gint    **p_curve,
            gint     *p_length,
            gint    **p_sum,
            gint     *p_total)
{
  const gdouble  sigma2 = 2 * sigma * sigma;
  const gdouble  l      = sqrt (-sigma2 * log (1.0 / 255.0));
  gint           temp;
  gint           i, n;
  gint           length;
  gint          *sum;
  gint          *curve;

   n = ceil (l) * 2;
   if ((n % 2) == 0)
    n += 1;

   curve = g_new (gint, n);

  length = n / 2;
  curve += length; /* 'center' the curve[] */
  curve[0] = 255;

  for (i = 1; i <= length; i++)
  {
      temp = (gint) (exp (- (i * i) / sigma2) * 255);
      curve[-i] = temp;
      curve[i] = temp;
   }

  sum   = g_new (gint, 2 * length + 1);
  sum[0] = 0;
  for (i = 1; i <= length*2; i++)
   {
      sum[i] = curve[i-length-1] + sum[i-1];
   }

  sum += length; /* 'center' the sum[] */

  *p_total  = sum[length] - sum[-length];
  *p_curve  = curve;
  *p_sum    = sum;
  *p_length = length;

Для меня, кривая и сумма 2 массива. кривая идет, скажем, от -3 до +3

и сумма идет от 0 до 6. Другими словами, у меня есть

curve[-3] = ... 
curve[0] = 255
curve[3] = ...

но что делает curve = curve + length действительно ли? То же самое, что делает sum = sum + length делает?

Большое спасибо за Вашу помощь!

PS: я не гений в кодировании:(

1 ответ

Решение

Гладкий код. Похоже, они делают арифметику указателей.

Я начну сверху и буду работать вниз, используя curve для объяснения. Я знаю, что вы спрашиваете только о кривой, но я объясню другие вещи для ясности.

gint *curve;

Это создает указатель на адрес памяти, который является достаточно большим, чтобы содержать целое число (типа gint). На данный момент он не инициализирован и поэтому не очень полезен

curve = g_new (gint, n);

Теперь создается массив целых чисел (gint) и кривой присваивается начальный адрес этого нового массива.

length = n / 2;

Здесь они рассчитывают среднюю точку фильтра, который является размером n, Размер фильтра был основан на l, который основан на параметре сигма и математике преобразования журнала ранее. Вот, length относится к радиусу 1D ядра, а не к длине массива, который в два раза длиннее.

curve += length; /* 'center' the curve[] */

Ваш вопрос: что здесь происходит?

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

Здесь, потому что [] операторы не используются, ссылка на curve это его адрес в памяти. Память, которая была выделена, остается на месте и не будет перемещаться (конечно, вы можете скопировать ее из одного места в другое, но я не об этом говорю). Указатель, curve, может, так сказать, передвигаться. Адрес curve сама по себе устанавливается, однако, как переменная, адрес, который она содержит, может быть скорректирован и изменен так, что он указывает на разные вещи, как и любая другая, неконстантная переменная, может изменить свое значение.

Вначале, curve указывал на адрес кривой [0]. Это не так, но так как вы говорите, что вы новичок, скажем, кривая [0] находится по адресу памяти 00. Так curveЗначение, также равное адресу 00. Вызов curve += length можно интерпретировать как curve = curve + length который внутренне равен добавлению размера (в байтах) типа данных длины к адресу кривой, а затем обновлению адреса кривой с помощью результата. Для примера, скажем, что gint требует 4 байта и length 5 и curve все еще 00.

Таким образом, звонки становятся

  1. curve += length
  2. curve = curve + length
  3. curve = curve + sizeof(length)
  4. curve = Кривая адреса указывает на + количество байтов длины использования памяти
  5. curve = Адрес 00 + 4 байта * 8 бит / байт
  6. curve = Address 32 (Адрес 32 будет записан в шестнадцатеричном виде как 0x20)

Опять же, память, которая была объявлена ​​с g_new(gint, n) все еще там, где он был объявлен (он не перемещается), но теперь, поскольку указатель был изменен, его (указатель) можно использовать для индексации в этом массиве с использованием отрицательных индексов. То есть, curve[0] на самом деле это значение в середине массива сейчас, в то время как curve[-n] это первый элемент массива.

В фильтре средняя точка (curve[0]) устанавливается прямо на максимальное значение:

curve[0] = 255;

Далее следуют итерации (+/- i) для заполнения ядра.

В следующем цикле вы можете увидеть, что значения кривой снова смещены в центр (т.е. curve[i-length-1]). Теперь это немного сбивает с толку, потому что похоже, что они смешаны и соответствуют двум подходам (иногда используя арифметику указателей, чтобы сделать индексирование более чистым, а иногда нет). Но, эй, они запрограммировали это и, вероятно, имели вескую причину сделать это таким образом, основываясь на том, насколько хорошо выглядит остальная часть кода. И кто знает, может, они просто хотели все перемешать ради разнообразия.

ОБНОВИТЬ

относительно sumсначала задаются значения в массиве, а затем указатель изменяется, чтобы указывать на центр этого заполненного массива. Тем не менее, программа могла бы следовать той же схеме, что и ранее, с curve и было написано так:

sum += length; /* 'center' the sum[] */

sum[-length] = 0;
for (i = -length+1; i <= length; i++)
{
   sum[i] = curve[i] + sum[i-1];
}

Кроме того, вызов *p_total = sum[length] - sum[-length]; похоже, что это может быть просто p_total = sum[length]; поскольку sum[-length] всегда равен 0. То есть sum[0]=0 с последующим sum += length Значит это sum[-length] после регулировки так же, как sum[0] до регулировки указателя. Обе ссылки относятся к первому значению в массиве, которое равно 0.

Размещение sum указатель на середину массива соответствует curve устанавливается в середину своего массива, когда функция заканчивается.

Однако в случае с sum сумма корректировалась после инициализации массива значениями, а в случае curve сначала была выполнена настройка, а затем значения инициализированы. Это может быть просто предпочтение программистам смешивать вещи, может быть проще подумать об интеграции с индексами, идущими от 1 до K (так сказать). Вероятно, есть другие способы написания этого кода, которые работают так же хорошо, в этом вся прелесть (или, может быть, иногда проклятие).

Другие вопросы по тегам