Предисловие


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

Формула таких алгоритмов будет выглядеть следующим образом:
  1. Z=FFT(X) – прямое двухмерное преобразование Фурье
  2. Z?=T(Z) – применение функции или транспаранта к Фурье-образу изображения
  3. Y=BFT(Z?) – обратное двухмерное преобразование Фурье

Для вычисления преобразований Фурье используются алгоритмы быстрого дискретного преобразования Фурье. Хотя оптическая система линз осуществляет преобразование Фурье на непрерывном диапазоне аргумента и для непрерывного спектра, но при переходе к цифровой обработке данных формулы преобразования Фурье могут быть заменены на формулы дискретного преобразования Фурье.

Примеры реализации


  • Алгоритм размытия изображения
  • Алгоритм повышения резкости изображения
  • Алгоритм масштабирования изображения

Реализованные алгоритмы являются частью библиотеки с открытым исходным кодом FFTTools. Интернет-адрес: github.com/dprotopopov/FFTTools

Алгоритм размытия изображения


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

Алгоритм:
  1. Пусть X(N1,N2) – массив яркостей пикселей изображения.
  2. Вычислить Px = средняя (среднеквадратичная) яркость пикселей в массиве X
  3. Вычислить массив Z=FT(X) – прямое двухмерное дискретное преобразование Фурье
  4. Вычислить массив Z?=T(Z), где T – обнуление строк и столбцов, находящихся в заданных внутренних областях матрицы-аргумента соответствующих высоким 5. частотам (то есть обнуление коэффициентов Фурье-разложения, соответствующих высоким частотам)
  5. Вычислить массив Y=RFT(Z?) – обратное двухмерное дискретное преобразование Фурье
  6. Вычислить Py = средняя (среднеквадратичная) яркость пикселей в массиве Y
  7. Нормировать массив Y(N1,N2) по среднему уровню яркости Px/Py

Алгоритм повышения резкости изображения


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

Алгоритм:
  1. Пусть X(N1,N2) – массив яркостей пикселей изображения.
  2. Вычислить Px = средняя (среднеквадратичная) яркость пикселей в массиве X
  3. Вычислить массив Z=FT(X) – прямое двухмерное дискретное преобразование Фурье
  4. Сохранить значение L=Z(0,0) – соответствующее средней яркости пикселей исходного изображения
  5. Вычислить массив Z?=T(Z), где T – обнуление строк и столбцов, находящихся в заданных внешних областях матрицы-аргумента, соответствующих низким 6. частотам (то есть обнуление коэффициентов Фурье-разложения, соответствующих низким частотам)
  6. Восстановить значение Z’(0,0)=L – соответствующее средней яркости пикселей исходного изображения
  7. Вычислить массив Y=RFT(Z?) – обратное двухмерное дискретное преобразование Фурье
  8. Вычислить Py = средняя (среднеквадратичная) яркость пикселей в массиве Y
  9. Нормировать массив Y(N1,N2) по среднему уровню яркости Px/Py

Алгоритм масштабирования изображения


В оптических системах световой поток в фокальной плоскости системы представляет собой Фурье-преобразование исходного изображения. Размер получаемого на выходе оптической системы изображения определяется соотношением фокальных расстояний объектива и окуляра.

Алгоритм:
  1. Пусть X(N1,N2) – массив яркостей пикселей изображения.
  2. Вычислить Px = средняя (среднеквадратичная) яркость пикселей в массиве X
  3. Вычислить массив Z=FT(X) – прямое двухмерное дискретное преобразование Фурье
  4. Вычислить массив Z?=T(Z), где T – либо добавление нулевых строк и столбцов матрицы соответствующих высоким частотам, либо удаление строк и столбцов матрицы соответствующих высоким частотам для получения требуемого размера итогового изображения
  5. Вычислить массив Y=RFT(Z?) – обратное двухмерное дискретное преобразование Фурье
  6. Вычислить Py = средняя (среднеквадратичная) яркость пикселей в массиве Y
  7. Нормировать массив Y(M1,M2) по среднему уровню яркости Px/Py

Используемое программное обеспечение
  • Microsoft Visual Studio 2013 C# — среда и язык программирования
  • EmguCV/OpenCV – C++ библиотека структур и алгоритмов для обработки изображений
  • FFTWSharp/FFTW – C++ библиотека реализующая алгоритмы быстрого дискретного преобразования Фурье

Алгоритм размытия изображения


Код алгоритма
        /// <summary>
        ///     Clear internal region of array
        /// </summary>
        /// <param name="data">Array of values</param>
        /// <param name="size">Internal blind region size</param>
        private static void Blind(Complex[,,] data, Size size)
        {
            int n0 = data.GetLength(0);
            int n1 = data.GetLength(1);
            int n2 = data.GetLength(2);
            int s0 = Math.Max(0, (n0 - size.Height)/2);
            int s1 = Math.Max(0, (n1 - size.Width)/2);
            int e0 = Math.Min((n0 + size.Height)/2, n0);
            int e1 = Math.Min((n1 + size.Width)/2, n1);
            for (int i = s0; i < e0; i++)
            {
                Array.Clear(data, i*n1*n2, n1*n2);
            }
            for (int i = 0; i < s0; i++)
            {
                Array.Clear(data, i*n1*n2 + s1*n2, (e1 - s1)*n2);
            }
            for (int i = e0; i < n0; i++)
            {
                Array.Clear(data, i*n1*n2 + s1*n2, (e1 - s1)*n2);
            }
        }
        /// <summary>
        ///     Blur bitmap with the Fastest Fourier Transform
        /// </summary>
        /// <returns>Blured bitmap</returns>
        public Bitmap Blur(Bitmap bitmap)
        {
            using (var image = new Image<Bgr, double>(bitmap))
            {
                int length = image.Data.Length;
                int n0 = image.Data.GetLength(0);
                int n1 = image.Data.GetLength(1);
                int n2 = image.Data.GetLength(2);

                var doubles = new double[length];
                Buffer.BlockCopy(image.Data, 0, doubles, 0, length*sizeof (double));
                double power = Math.Sqrt(doubles.Average(x => x*x));

                var input = new fftw_complexarray(doubles.Select(x => new Complex(x, 0)).ToArray());
                var output = new fftw_complexarray(length);
                fftw_plan.dft_3d(n0, n1, n2, input, output,
                    fftw_direction.Forward,
                    fftw_flags.Estimate).Execute();
                Complex[] complex = output.GetData_Complex();

                var data = new Complex[n0, n1, n2];
                var buffer = new double[length*2];

                GCHandle complexHandle = GCHandle.Alloc(complex, GCHandleType.Pinned);
                GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
                IntPtr complexPtr = complexHandle.AddrOfPinnedObject();
                IntPtr dataPtr = dataHandle.AddrOfPinnedObject();

                Marshal.Copy(complexPtr, buffer, 0, buffer.Length);
                Marshal.Copy(buffer, 0, dataPtr, buffer.Length);
                Blind(data, _blinderSize);
                Marshal.Copy(dataPtr, buffer, 0, buffer.Length);
                Marshal.Copy(buffer, 0, complexPtr, buffer.Length);

                complexHandle.Free();
                dataHandle.Free();

                input.SetData(complex);

                fftw_plan.dft_3d(n0, n1, n2, input, output,
                    fftw_direction.Backward,
                    fftw_flags.Estimate).Execute();
                double[] array2 = output.GetData_Complex().Select(x => x.Magnitude).ToArray();
                double power2 = Math.Sqrt(array2.Average(x => x*x));
                doubles = array2.Select(x => x*power/power2).ToArray();
                Buffer.BlockCopy(doubles, 0, image.Data, 0, length*sizeof (double));
                return image.Bitmap;
            }
        }


Алгоритм повышения резкости изображения


Код алгоритма
        /// <summary>
        ///     Clear external region of array
        /// </summary>
        /// <param name="data">Array of values</param>
        /// <param name="size">External blind region size</param>
        private static void Blind(Complex[,,] data, Size size)
        {
            int n0 = data.GetLength(0);
            int n1 = data.GetLength(1);
            int n2 = data.GetLength(2);
            int s0 = Math.Max(0, (n0 - size.Height)/2);
            int s1 = Math.Max(0, (n1 - size.Width)/2);
            int e0 = Math.Min((n0 + size.Height)/2, n0);
            int e1 = Math.Min((n1 + size.Width)/2, n1);
            for (int i = 0; i < s0; i++)
            {
                Array.Clear(data, i*n1*n2, s1*n2);
                Array.Clear(data, i*n1*n2 + e1*n2, (n1 - e1)*n2);
            }
            for (int i = e0; i < n0; i++)
            {
                Array.Clear(data, i*n1*n2, s1*n2);
                Array.Clear(data, i*n1*n2 + e1*n2, (n1 - e1)*n2);
            }
        }
        /// <summary>
        ///     Sharp bitmap with the Fastest Fourier Transform
        /// </summary>
        /// <returns>Sharped bitmap</returns>
        public Bitmap Sharp(Bitmap bitmap)
        {
            using (var image = new Image<Bgr, double>(bitmap))
            {
                int length = image.Data.Length;
                int n0 = image.Data.GetLength(0);
                int n1 = image.Data.GetLength(1);
                int n2 = image.Data.GetLength(2);

                var doubles = new double[length];
                Buffer.BlockCopy(image.Data, 0, doubles, 0, length*sizeof (double));
                double power = Math.Sqrt(doubles.Average(x => x*x));

                var input = new fftw_complexarray(doubles.Select(x => new Complex(x, 0)).ToArray());
                var output = new fftw_complexarray(length);
                fftw_plan.dft_3d(n0, n1, n2, input, output,
                    fftw_direction.Forward,
                    fftw_flags.Estimate).Execute();
                Complex[] complex = output.GetData_Complex();

                Complex level = complex[0];

                var data = new Complex[n0, n1, n2];
                var buffer = new double[length*2];

                GCHandle complexHandle = GCHandle.Alloc(complex, GCHandleType.Pinned);
                GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
                IntPtr complexPtr = complexHandle.AddrOfPinnedObject();
                IntPtr dataPtr = dataHandle.AddrOfPinnedObject();

                Marshal.Copy(complexPtr, buffer, 0, buffer.Length);
                Marshal.Copy(buffer, 0, dataPtr, buffer.Length);
                Blind(data, _blinderSize);
                Marshal.Copy(dataPtr, buffer, 0, buffer.Length);
                Marshal.Copy(buffer, 0, complexPtr, buffer.Length);

                complexHandle.Free();
                dataHandle.Free();

                complex[0] = level;

                input.SetData(complex);

                fftw_plan.dft_3d(n0, n1, n2, input, output,
                    fftw_direction.Backward,
                    fftw_flags.Estimate).Execute();
                double[] array2 = output.GetData_Complex().Select(x => x.Magnitude).ToArray();
                double power2 = Math.Sqrt(array2.Average(x => x*x));
                doubles = array2.Select(x => x*power/power2).ToArray();
                Buffer.BlockCopy(doubles, 0, image.Data, 0, length*sizeof (double));
                return image.Bitmap;
            }
        }


Алгоритм масштабирования изображения


Код алгоритма
         /// <summary>
        ///     Copy arrays
        /// </summary>
        /// <param name="input">Input array</param>
        /// <param name="output">Output array</param>
        private static void Copy(Complex[,,] input, Complex[,,] output)
        {
            int n0 = input.GetLength(0);
            int n1 = input.GetLength(1);
            int n2 = input.GetLength(2);
            int m0 = output.GetLength(0);
            int m1 = output.GetLength(1);
            int m2 = output.GetLength(2);
            int ex0 = Math.Min(n0, m0)/2;
            int ex1 = Math.Min(n1, m1)/2;
            int ex2 = Math.Min(n2, m2);
            Debug.Assert(n2 == m2);
            for (int k = 0; k < ex2; k++)
            {
                for (int i = 0; i <= ex0; i++)
                {
                    for (int j = 0; j <= ex1; j++)
                    {
                        int ni = n0 - i - 1;
                        int nj = n1 - j - 1;
                        int mi = m0 - i - 1;
                        int mj = m1 - j - 1;
                        output[i, j, k] = input[i, j, k];
                        output[mi, j, k] = input[ni, j, k];
                        output[i, mj, k] = input[i, nj, k];
                        output[mi, mj, k] = input[ni, nj, k];
                    }
                }
            }
        }
        /// <summary>
        ///     Resize bitmap with the Fastest Fourier Transform
        /// </summary>
        /// <returns>Resized bitmap</returns>
        public Bitmap Stretch(Bitmap bitmap)
        {
            using (var image = new Image<Bgr, double>(bitmap))
            {
                int length = image.Data.Length;
                int n0 = image.Data.GetLength(0);
                int n1 = image.Data.GetLength(1);
                int n2 = image.Data.GetLength(2);
                var doubles = new double[length];
                Buffer.BlockCopy(image.Data, 0, doubles, 0, length*sizeof (double));
                double power = Math.Sqrt(doubles.Average(x => x*x));

                var input = new fftw_complexarray(doubles.Select(x => new Complex(x, 0)).ToArray());
                var output = new fftw_complexarray(length);
                fftw_plan.dft_3d(n0, n1, n2, input, output,
                    fftw_direction.Forward,
                    fftw_flags.Estimate).Execute();
                Complex[] complex = output.GetData_Complex();

                using (var image2 = new Image<Bgr, double>(_newSize))
                {
                    int length2 = image2.Data.Length;
                    int m0 = image2.Data.GetLength(0);
                    int m1 = image2.Data.GetLength(1);
                    int m2 = image2.Data.GetLength(2);
                    var complex2 = new Complex[length2];

                    var data = new Complex[n0, n1, n2];
                    var data2 = new Complex[m0, m1, m2];

                    var buffer = new double[length*2];
                    GCHandle complexHandle = GCHandle.Alloc(complex, GCHandleType.Pinned);
                    GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
                    IntPtr complexPtr = complexHandle.AddrOfPinnedObject();
                    IntPtr dataPtr = dataHandle.AddrOfPinnedObject();

                    Marshal.Copy(complexPtr, buffer, 0, buffer.Length);
                    Marshal.Copy(buffer, 0, dataPtr, buffer.Length);

                    complexHandle.Free();
                    dataHandle.Free();

                    Copy(data, data2);

                    buffer = new double[length2*2];
                    complexHandle = GCHandle.Alloc(complex2, GCHandleType.Pinned);
                    dataHandle = GCHandle.Alloc(data2, GCHandleType.Pinned);
                    complexPtr = complexHandle.AddrOfPinnedObject();
                    dataPtr = dataHandle.AddrOfPinnedObject();

                    Marshal.Copy(dataPtr, buffer, 0, buffer.Length);
                    Marshal.Copy(buffer, 0, complexPtr, buffer.Length);

                    complexHandle.Free();
                    dataHandle.Free();

                    var input2 = new fftw_complexarray(complex2);
                    var output2 = new fftw_complexarray(length2);
                    fftw_plan.dft_3d(m0, m1, m2, input2, output2,
                        fftw_direction.Backward,
                        fftw_flags.Estimate).Execute();
                    double[] array2 = output2.GetData_Complex().Select(x => x.Magnitude).ToArray();
                    double power2 = Math.Sqrt(array2.Average(x => x*x));
                    double[] doubles2 = array2.Select(x => x*power/power2).ToArray();
                    Buffer.BlockCopy(doubles2, 0, image2.Data, 0, length2*sizeof (double));
                    return image2.Bitmap;
                }
            }
        }



Скриншоты программ


Размытие изображения
image


Масштабирование изображения
image


Литература


  1. А.Л. Дмитриев. Оптические методы обработки информации. Учебное пособие. СПб. СПюГУИТМО 2005. 46 с.
  2. А.А.Акаев, С.А.Майоров «Оптические методы обработки информации» М.:1988
  3. Дж.Гудмен «Введение в Фурье-оптику» М.: Мир 1970

Комментарии (22)


  1. qbertych
    31.08.2015 11:31
    +9

    В результате прохождения светового потока через диафрагму, волны высоких частот (с более короткими длинами волн) проходят через препятствие, а волны низких частот (с более длинными длинами волн) отсекаются экраном.
    Термин «длина волны» относится к излучению («красный цвет длинноволновее синего»). Здесь он не только неуместен, но и мешает пониманию: диафрагма, разумеется, одинаково закрывает и красный, и синий спектр. В вашем случае надо говорить про гармоники Фурье-преобразования.

    световой поток в фокальной плоскости системы представляет собой Фурье-преобразование исходного изображения
    Тут вы просто путаете читателей. В вашей системе две линзы (грубо говоря, объектив и окуляр). Поэтому это не «Фурье-преобразование исходного изображения», а два преобразования: прямое и обратное.Естественно, можно обойтись одной линзой. Но тогда объект и его изображение будут не в фокальных плоскостях линз, а подальше. В этом случае Фурье-преобразования в лоб не работают.

    Ну и напоследок — ваши алгоритмы будут давать артефакты из-за края диафрагмы. Фурье-образ резкого изображения зашумлен гармониками, вспомните дискретное Фурье-преобразование ступеньки. (В реальном фотоаппарате этого нет, т.к. нет дискретности). Чтобы избавиттся от этого, обычно берут гауссову диафрагму.


    1. dprotopopov
      02.09.2015 16:50
      -1

      Спасибо за комментарий. Несомненно и Вы правы, но не забывайте что теория света — это и есть корпускулярно-волновая теория электромагнитного излучения, и, что любая теория, как бы хорошо она не описывала реальный мир, продолжает оставаться всего лишь теорией.
      Говоря о волнах, проходящих через диафрагму, например в фотоаппаратуре, или огибающих препятствие, как например наблюдается у радиоволны, явления как изначально были объяснены термином длина волны, так этот термин сохранился до сих пор.
      Связь длины волны с частотой установлен формулой l*f=c, где l — длина волны, f-частота, с — скорость света.
      Да, я привёл пример телескопической системы из двух линз, но это ни в коей мере не должно путать — считать исходное и наблюдаемое изображения как бесконечно удалённые, тогда указанная точка будет являться именно фокальной точкой для обеих линз, хотя размещение диафрагмы не обязательно должно быть именно в фокальной плоскости — это лишь методика для упрощения расчётов.


      1. dprotopopov
        02.09.2015 17:10
        -1

        Размещение диафрагмы не обязательно должно быть именно в фокальной плоскости — это лишь методика для упрощения расчётов.
        Размещая диафрагму не в фокальной плоскости мы как раз и получаем диафрагму с коэффициентами пропускания по-гауссу.
        Более сложные модели — применение не диафрагмы, а некого транспаранта, реализующего сложную функцию. В результате появляется возможность вычисления корреляции функций. Пример такого тоже есть в указанной библиотеке — поиск позиции фрагмента изображения в полном изображении — но я не включал в данную статью.
        По адресу github.com/dprotopopov/FFTTools доступны исходники программ-примеров. Можете поэкспериментировать. Лично я был удовлетворён полученными результатами.


        1. qbertych
          02.09.2015 17:27
          +1

          Размещая диафрагму не в фокальной плоскости мы как раз и получаем диафрагму с коэффициентами пропускания по-гауссу.
          Хм, а в этом есть смысл. Но зачем? Математика этого гораздо сложнее, чем гауссова диафрагма.

          Можете поэкспериментировать.
          Спасибо, я на работе с похожими вещами вожусь =).


      1. qbertych
        02.09.2015 17:23
        +1

        Еще раз: термину «длина волны света» под пару сотен лет и не вам его менять. Если бы диафрагма «не пропускала длинные волны», то фотографии с закрытой диафрагмой были бы синими. Это не говоря о том, что вы путаете «длинноволновые» и «коротковолновые» гармоники (ВЧ находятся на периферии, НЧ и DC — в центре).

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


        1. dprotopopov
          02.09.2015 17:39

          image

          Ваш алгоритм соответствует не телескопу, а микроскопу

          Да, я знаю разницу, но даже приведённая Вами схема — всего лишь схема для объяснения теории прохождения лучей в классической теории, а не в волновой теории.
          Наблюдаемые явления именно подавления красных и фиолетовых частей спектра при прохождении через оптическую систему с диафрагмой или препятствием именно и послужили дальнейшему переходу от классической теории к волновой.
          На самом деле линза не делает ни прямого, ни обратного преобразования Фурье. Она просто пропускает и преломляет свет. Но то что это может быть описано математическими формулами — это тот бонус который позволяет использовать линзу как калькулятор.


          1. dprotopopov
            03.09.2015 02:46

            На самом деле линза не делает ни прямого, ни обратного преобразования Фурье.

            А точнее к наблюдаемому физическому явлению — прохождению света через оптическую систему (как в данном на примере) можно сопоставить математическую модель, описываемую формулами:
            T(FT(X))=T(FT(Y)), где в качестве FT может быть выбрана одна из пары функций. которые называют прямым и обратным преобразованиями Фурье, и которые различаются с точностью до знака у аргумента.


            1. dprotopopov
              03.09.2015 03:31

              T(FT(X))=T(FT(Y)), где в качестве FT может быть выбрана одна из пары функций
              а T — функция-транспарант, которая в случае простого физического отверстия-диафрагмы является функцией смешения фазы комплексного числа в области отверстия (то есть комплексные значения чисел длины 1), либо подавление амплитуды сигнала (то есть комплексные числа длины 0).
              При этом для терминологии применяемой к физическому объекту-реальной оптической системе не надо путать пространственную частоту/длину волны с линейной частотой/длиной волны.
              И наоборот, для построенной математической модели, где используются дискретные вычисления, можно спокойно объединить понятия пространственных и линейных частот с понятием координаты пикселя на транспаранте.


        1. dprotopopov
          02.09.2015 17:47

          Диафрагма (иногда используют термин апертура) подобна зрачку человеческого глаза; лепестки диафрагмы объектива открывают и закрывают отверстие диафрагмы, регулируя количество света, проходящего через объектив. Чем больше лепестков, тем более округлую форму имеет действующее отверстие объектива, что способствует смягчению фокуса и даёт более красивый эффект. Соответственно, если количество лепестков уменьшается, светлые участки приобретают форму многоугольника.
          Величина, указанная на ободе объектива (F1.4, F2, F2.8 и т. д.), обозначает величину относительного отверстия диафрагмы и называется диафрагменным числом или f.
          Чем меньше диафрагменное число, тем больше раскрыта диафрагма и больше света она пропускает. Чем больше диафрагменное число, тем меньше действующее отверстие диафрагмы и меньше количество проходящего через неё света. Когда диафрагма раскрыта максимально, это положение называется открытая диафрагма. Термином закрытая диафрагма обозначают, соответственно, самый маленький размер действующего отверстия диафрагмы.


        1. dprotopopov
          02.09.2015 17:49

          Одна из основных задач фотографа — сфокусироваться на объекте. Выражение «объект в фокусе» означает, что определённая точка на объекте чётко снята камерой. В действительности, в фокусе также оказывается область непосредственно перед точкой фокуса и за ней. Эта область фокуса, или резкости, называется глубиной резкости.
          Если область перед точкой фокусировки и за ней маленькая, глубина резкости считается малой. Если эта область большая, то и глубина резкости считается большой. Степень глубины резкости регулируется размером действующего отверстия диафрагмы.
          Когда диафрагма объектива раскрывается, глубина резкости уменьшается, а когда диафрагма закрывается, глубина резкости увеличивается.


  1. qbertych
    31.08.2015 11:58
    +4

    И вдогонку. В целом такая Фурье-обработка изображений (прямое — обработка — обратное) применяется очень широко. Интересны два момента:

    1. Фурье-преобразование — комплексное, то есть дает информацию и об амплитуде, и о фазе света. Это сразу открывает простор для фазовой микроскопии, голографии и подобных применений.

    2. Фурье-преобразование дает нам гармоники. То есть можно выбирать нужные из них, отсеивая шум с определенной частотой, DC компоненты и всякое такое. Например, при голографии используются две волны: сигнальная и опорная. Вместе они дают полезный сигнал (широкополосный) + шумовую фоновую интерференцию (узкополосную, т.е. после Фурье-преобразования это маленький кружок). Меняя угол между двумя волнами, можно менять положение маленького кружка, чтобы увести его от полезного сигнала и там отфильтровать. После обратного преобразования получим всю картину, но без вредной интерференции.

    Более детально эту тему нужно гуглить по словам «цифровая голография» (digital holography, digital off-axis holography), какие-то обзоры есть тут и тут.


    1. dprotopopov
      02.09.2015 16:59

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


  1. xoxulin
    31.08.2015 19:30
    +1

    JFYI: В профессиональной ретуши фотографий используется технология разложения на гармоники, но не Фурье-методом, а последовательным вычитанием из исходника размытой версии с разным радиусом размытия. На каждом band-pass-слое производится ручная или автоматизированная коррекция, которая потом склеивается снова.


    1. Alexufo
      31.08.2015 22:58

      Что вы подразумеваете под ретушью в вашем случае?


      1. xoxulin
        01.09.2015 12:21

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


        1. Alexufo
          02.09.2015 16:05

          А есть примеры эффективности данного способа против известных инструментов? Просто как то если она ручная и трудозатрантая, то смысл всех этих ухищрений.


  1. icoz
    31.08.2015 23:29
    +6

    Статье про изображения очень не хватает изображений.


    1. dprotopopov
      02.09.2015 16:53

      По адресу github.com/dprotopopov/FFTTools доступны исходники программ-примеров. Можете поэкспериментировать. Лично я был удовлетворён полученными результатами.


      1. icoz
        02.09.2015 19:00
        +1

        Да, чтобы посмотреть на эффективность представленных алгоритмов, мне надо поставить винду, в ней студию и библиотеки. Собрать софт…
        … и поиграть пару минут с изображениями.

        Совет: для наглядности всё-таки добавьте картинки примеров, читателям будет комфортнее.


        1. dprotopopov
          02.09.2015 19:31

          Хорошо. Сделаю обязательно. Но чуть попозже.
          Собственно я даже немного намеренно не стал выключать картинки в текст, по той простой причине, что приведённые алгоритмы являются просто алгоритмами размытия и масштабирования изображения. Результат этих алгоритмов можно наблюдать при детальном рассмотрении, а не при рассмотрении интернет-картинки, которые в браузере ничем не будут отличатся от других графических фильтров.


        1. dprotopopov
          02.09.2015 19:49

          К тому же. как верно замечено в другом комментарии, здесь приведены программые коды для прямоугольной диафрагмы, в результате которой могут образовываться тн «ступеньки». применение в качестве функции-транспаранта, например гауссовой функции позволит удалить эти «ступеньки» и сделать размытие более плавным.
          С математической точки зрения установленный в фокальной плоскости транспарант можно рассматривать как фурье-образ от некой другой функции. А значит и формула алгоритма
          Z=FFT(X) – прямое двухмерное преобразование Фурье
          Z?=T(Z) – применение функции или транспаранта к Фурье-образу изображения
          Y=BFT(Z?) – обратное двухмерное преобразование Фурье
          на самом деле является формулой
          Y=X*F, где * — операция свёткки, а T=FFT(F)


        1. dprotopopov
          04.09.2015 13:51

          Совет: для наглядности всё-таки добавьте картинки примеров, читателям будет комфортнее.

          Добавил пару скришотов