Здравствуйте.
Необходимо провести двумерное БПФ(прямое и обратное), а точнее надо отфильтровать изображение. Поиски по форуму, впрочем как и по всему инету ни к чему ни привели. Написать самому не представляется возможности, т.к. математическая подготовка оставляет желать лучшего.
Если у кого-то есть наработки и не жалко ими поделится, буду очень признателен.
Не откажусь, если подскажете ресурсы, где есть данная информация для чайников.
Спасибо.
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
10-03-2009 09:58 | Сообщение от автора вопроса
Потрясающий форум с потрясающе грамотными людьми!!
Огромное спасибо Василий и MBo!
Все получилось, теперь осталось поглубже вникнуть в теорию, чем я сейчас и займусь...
Чтобы действительно разобраться в этом деле - а тема весьма сложная, многогранная, со множеством ловушек - понадобятся книги по цифровой обработке сигналов.
Если гуглить по fourier transform - то большинство из первых же ссылок - вполне толковые. На английском, конечно. Много картинок с примерами.
Кстати, на картинке Фурье-образа "по-честному" четко видно "зеркало" т.е. любой из квадрантов этого образа - есть спектрограмма исходного изображения. И пусть не смущает, что размерность квадранта вчетверо меньше исходного. Если подать на вход "меандр", а не "зашумленное" изображение, то можно четко выделить в каком квадранте отсчеты спектра располагаются в общепринятом порядке (т.е. слева 0 Гц) и зная АЧХ оптической системы (вернее в целом, плюс светочувствительный элемент и прочая) уже ориентироваться в этом спектре что есть "мухи, что котлеты".
Ну здорово. Работает библиотека (трудно было предположить обратное ;-) ). А вот не лучший выбор, вносит свои искажения - понятное дело, потому что меандр белое-черное частотой, например, 1/см (для радиосигнала 1 Гц) имеет железно составляющие 2/см (2 Гц) и 4/см (4 Гц) и т.д. гармоники основного сигнала (после 3-ей гармоники в принципе особо никто не заморачивается). Т.е. идеальное прямое преобразование дает "палки" на частотах кратных основной. И чем больше гармоник Вы "отрежете" - тем "размытей" будет переход белое-черное. В идеале, оставив только 0-ю гармонику (т.е. частоту сигнала), после обратного преобразования получаем чистой воды синусоиду, а не "меандр".
10-03-2009 04:19 | Комментарий к предыдущим ответам
2 МВо...
Ох как все-таки нужно читать документацию на эту библиотеку! Дело в том, что что после прямого преобразования в выходном массиве "сидят" значения "амплитуд и фаз" (это правда не совсем так) частотных составляющих исходного сигнала, причем там есть полное "зеркало" относительно "середины" массива. В зависимости от частоты дискретизации можно четко определить фактическую частоту соответствующую каждому значению. Это теория линейного БПФ. Что и в каком порядке кладет эта библиотека в выходной массив - большой вопрос. Это я к Вашему "некоему" фильтру нижних частот. Какой-то он очень сомнительный для меня, не знающего вообще этой библиотеки.
И еще. Ваш "белый квадрат" в середине может оказаться искуственным "компенсатором" того, что преобразование Фурье (в теории) применимо к непрерывным сигналам, а БПФ - преобразование на участке. Т.е. обычно берется (на примере линейного сигнала) 1024 отсчета и на этот массив перед прямым БПФ накладывается фильт для компенсации того, что преобразование выполняется на участке. Этот фильт преставляет собой несколько "колокольчиков". В центре "большой" с максимумом = 1 и рядом справа и слева 1-2 "маленьких". Боюсь, что при использовании этой библиотеки просто необходимо генерить массив в центр которого класть исходное изображение, а все остальные отсчеты приравнивать к 0.
2 Автору...
Насколько я помню вот это я тоже делал
i := Round(Hypot(aOutt[y, x].Re, aOutt[y, x].Im)/ N);
не так конечно, но в плане нормализации к количеству отсчетов.
Если у Вас задача действительно "всерьез и надолго", то без чтения доки на библиотеку выход один - сделать самому БПФ, как я Вам говорил и сличить результаты прямого преобразования. Потому что без понимания что есть что в выходном массиве этой библиотеки - дело дрянь.
Слепил пример, нормировку навскидку подобрал (для одномерного Фурье после прямого-обратного нужно делить на число точек, здесь на квадрат, похоже, но надо в доках по библиотеке уточнять)
Исходная картинка- белый квадрат в центре
После Фурье - крест дольками. Для рисования произведена перестановка квадрантов так, чтобы низкие частоты были в центре картинки (так часто делают для наглядности). На самом деле низкие частоты в левом верхнем углу в массиве AOutt.
После обратного Фурье - тот же квадрат.
Если раскомментировать строку с фильтром, то получится размытая картинка (срезаются высокие частоты).
procedure TForm1.Button1Click(Sender: TObject);
const
ND2 = 128;
N = 2 * ND2;
var
aIn: array of Single;
aOutt: array[0..N - 1, 0..ND2] of TComplexSingle;
x, y: Integer;
plan: Pointer;
i: Integer;
b: Byte;
C: Single;
begin
SetLength(aIn, N * N);
for y := 0 to N - 1 do
for x := 0 to N - 1 do begin
if (Abs(y - ND2) < 32) and (Abs(x - ND2) < 32) then
b := 255
else
b := 0;
aIn[y * N + x] := b;
Canvas.Pixels[x, y] := RGB(b, b, b);
end;
plan := fftwf_plan_dft_r2c_2d(N, N, @aIn[0], @aOutt[0], FFTW_ESTIMATE);
fftwf_execute(plan);
for y := 0 to N - 1 do
for x := 0 to N div 2 - 1 do begin
i := Round(Hypot(aOutt[y, x].Re, aOutt[y, x].Im)/ N);
b := i;
Canvas.Pixels[N + x + ND2, (y + ND2) mod N] := RGB(b, b, b);
Canvas.Pixels[N + ND2 - x, (y + ND2) mod N] := RGB(b, b, b);
end;
fftwf_destroy_plan(plan);
C := 1;
for y := 0 to N - 1 do
for x := 0 to N div 2 - 1 do begin
// C := 1 /(Sqr(x) + Sqr(y) + 1); // некий фильтр нижних частот
aOutt[y, x].Re := aOutt[y, x].Re * C;
aOutt[y, x].Im := aOutt[y, x].Im * C;
end;
plan := fftwf_plan_dft_c2r_2d(N, N, @aOutt[0], @aIn[0], FFTW_ESTIMATE);
fftwf_execute(plan);
for y := 0 to N - 1 do
for x := 0 to N - 1 do begin
i := Round(aIn[y * N + x]/(Sqr(ND2)));
b := i;
Canvas.Pixels[2 * N + x, y] := RGB(b, b, b);
end;
fftwf_destroy_plan(plan);
end;
Вспомнил прикол, как делал курсрвик по БПФ в Можайке. На миллиметровке чертился график исходной функции, с нее же снималась реализация, на Б3-34 считалось Преобразование Фурье (не БПФ) прямое и обратное, на миллиметровку ниже рисовался спектр и результат восстановления из спектра. Двоечники ловились легко и непринужденно, поскольку по теории было четко известно какие амплитудные и фазовые искажения должны иметь место быть.
Видите ли... Обычно в описании функции есть элемент " ... + C" . Так вот про теории преобразования Фурье эта постоянная составляющая имеет смысл частотной составляющей 0 Гц. И тот алгоритм БПФ, который я юзал, подразумевал удаление ее из отсчетов. У меня легко работал способ приведения к знаку по принципу больше/меньше (Max-Min)/2, т.е. постоянная составляющая = (Max-Min)/2. Правда, исходя из моей задачи, реализация предварительно отфильтровывалась от "выбросов". Посколько заранее было известно о невозможности их в сигнале, а причина их появления - использование того АЦП со всей своей обвеской.
Хм.. странно ведет себя эта библиотека, что-то я явно делаю не так.
вход: белая картинка - выход: светло-коричневая
вход: черная картинка - выход: черная
вход: "шахматка"(ч/б) - выход: "шахматка" зелено-черная
и т.д. иногда путаются цвета, иногда просто на выходе черная картинка.
какие-то преобразования я делал перед БПФ, а именно удаление постоянной составляющей и может быть нормализация к 1.
имеется ввиду перед прямым преобразованием? если да, то приведение к 1 результата не дало. что есть постоянная составляющая?
Насколько я помню...какие-то преобразования я делал перед БПФ, а именно удаление постоянной составляющей и может быть нормализация к 1. Но у меня была другая задача - обнаружение сигнала в полосе частот. Это я про ваши "заоблачные" величины. Было у меня такое.
08-03-2009 11:35 | Вопрос к автору: запрос дополнительной информации
))
Да, скорей всего так и поступлю - продолжу экспериментировать.
Самое интересное, что после обратного преобразования мы получаем просто заоблачные значения(здесь они представлены с округлением), которые практически все кратны 256, что естественно дает в приведении к Byte 0.
Век живи, век учись, а дураком помрешь... И что вот так вот легко...А по шагам? А Вы бы вначале посмотрели чего это там лежит. Причем "подали" бы этой хитрой библиотеке белое поле, черное поле, "шахматку", потом "оптический" клин.
Кроме "хитрого" преобразования
lInArray - массив из Double, lScanLine - массив из Byte. Т.е. полученные значения после преобразования(прямое-обратное) мы записываем обратно в растр.
Просто напросто компилятор не пропустит присваивание Byte := Double, поэтому пришлось написать lScanLine^ := Round(lInArray[lInt]);
Вы уж извините...я всегда все своими ручками делал...с генератора сигнал записал, БПФ туда-сюда...посмотрел результат...поменял параметры "отсечки"...еще раз. Правда это было более 10 лет назад...да и с радиосигналами т.е. сигналами с линейной реализацией. И тогда я точно знал, что стоит за конкретным элементом массива, как на входе, так и на выходе.
Василий, начнем по порядку =)
Вы сами посмотрите на заполнение lScanLine^ := Round(lInArray^[lInt]); - это ка-то Вы очень хитро написали
Преобразование вещественного типа в челочисленный. Что именно вас смутило?
Зачем я рассказывал Вам про то, что изображение ДВУМЕРНО? И его нельзя "вытягивать" в линию.
Я вас прекрасно понял, но так же на сколько мне позволяет мой английский, из хелпа я понял, что функции fftw_plan_dft_r2c_2d и fftw_plan_dft_c2r_2d позволяет провести именно двумерное преобразование (прямое и обратное соответственно).
перед преобразование накладывает на реализацию маску/фильтр
это пока всего лишь эксперимент, чтобы понять как оно работает =), по моему скромному мнению, на выходе должна была получиться таже самая картинка(т.е. пока я не использую фильтры).
Когда Вы беретесь за подобные задачи, без понимания процесса (что на входе, что на выходе и т.п.) - задачу Вы не решите никогда.
полностью с вами согласен(никогда бы за это не взялся, если бы не обстоятельства), потому и пытаюсь разобраться с помощью грамотных людей на этом форуме)
Я не отвечу на Ваш вопрос (Вы сами посмотрите на заполнение lScanLine^ := Round(lInArray^[lInt]); - это ка-то Вы очень хитро написали) , но... Зачем я рассказывал Вам про то, что изображение ДВУМЕРНО? И его нельзя "вытягивать" в линию. Про 8000 прямых и обратных БПФ? Преобразование Фурье подразумевает непрерывную реализацию сигнала (что в принципе возможно только теоретически). Быстрое преобразование Фурье (БПФ) делает преобразование НА УЧАСТКЕ реализации сигнала и при этом, дабы скомпенсировать появление из-за этого высокочастотных "палок" перед преобразование накладывает на реализацию маску/фильтр (не помню как называется). А Вы все взяли и соединили в кучу. Как в мультфильме..."а и так сойдет..зажарится". Прочитайте мой пост от 04-03-2009 22:05 (Когда Вы беретесь за подобные задачи, без понимания процесса (что на входе, что на выходе и т.п.) - задачу Вы не решите никогда. и БПФ нужно применять отдельно по вертикали и по горизонтали (Вам же нужна фильтрация частотных составляющих)).
Подскажите пожалуйста, что делаю не так? Пытаюсь сделать прямое, затем обратное преобразование. Как я понимаю, на выходе должна получится таже самая картинка, однако на выходе всегда получаю черную картинку.
fftw_complex = record
re, im: Double;
end;
pfftw_complex = ^fftw_complex;
function fftw_malloc(n: LongWord): Pointer; cdecl;
external fftw3f Name 'fftw_malloc';
procedure fftw_free(P: Pointer); cdecl;
external fftw3f Name 'fftw_free';
procedure fftw_destroy_plan(plan: fftw_plan); cdecl;
external fftw3f Name 'fftw_destroy_plan';
procedure fftw_execute(plan: fftw_plan); cdecl;
external fftw3f Name 'fftw_execute';
function fftw_plan_dft_r2c_2d(n0, n1: Integer; _in: PDouble; _out: pfftw_complex;
flags: LongWord): fftw_plan; cdecl; external fftw3f Name 'fftw_plan_dft_r2c_2d';
function fftw_plan_dft_c2r_2d(n0, n1: Integer; _in: pfftw_complex; _out: PDouble;
flags: LongWord): fftw_plan; cdecl; external fftw3f Name 'fftw_plan_dft_c2r_2d';
procedure FFT2(ABitmap: TBitmap);
type
PDoubleArray = ^TDoubleArray;
TDoubleArray = array[0..0] of Double;
PComplexArray = ^TComplexArray;
TComplexArray = array[0..0] of fftw_complex;
var
lScanLine: PByte;
lInArray: PDoubleArray;
lOutArray: PComplexArray;
I, J, lInt,
lSizeX, lSizeY,
lInLength,
lOutLength: Integer;
plan: fftw_plan;
begin
if ABitmap = nil then
Exit;
ABitmap.PixelFormat := pf8bit;
lSizeX := ABitmap.Width;
lSizeY := ABitmap.Height;
lInLength := lSizeX * lSizeY;
lOutLength := lSizeX * (lSizeY div 2 + 1);
lInArray := fftw_malloc(lInLength * SizeOf(Double));
try
lInt := 0;
for J := 0 to ABitmap.Height - 1 do
begin
lScanLine := ABitmap.ScanLine[J];
for I := 0 to ABitmap.Width - 1 do
begin
lInArray[lInt] := lScanLine^;
Inc(lInt);
Inc(lScanLine);
end;
end;
lInt := 0;
for J := 0 to ABitmap.Height - 1 do
begin
lScanLine := ABitmap.ScanLine[J];
for I := 0 to ABitmap.Width - 1 do
begin
lScanLine^ := Round(lInArray^[lInt]);
Inc(lInt);
Inc(lScanLine);
end;
end;
Для скорости - нужно использовать библиотеку, выигрыш будет в несколько раз.
Кстати, кроме fftw, есть еще от Intel матбиблиотеки, но не знаю, бесплатные ли они сейчас.
К сожалению не имеется.
Сейчас изучаю help к библиотеке.
Если есть какие-то наработки - не откажусь...
PS учусь не на математика и даже не на программиста, так что не судите строго.
Всем спасибо. Василий, постараюсь осмыслить ваши слова)
MBo, я так понимаю, вы пользовались этой библиотекой. Как вы считаете, имеет ли смысл использовать библиотеку, либо пробовать написать самому(точнее взять одномерное преобразование и сделать как предлагает Василий). Интересует скорость.
Поскольку изображение двумерно MxN, соответственно имеются М выборок 1..N и N выборок 1..M (БПФ заточен под линейную реализацию сигнала). Т.е. линейных реализаций сигнала, каждая из которых имеет частотные составляющие. Нельзя все "точки" "сливать" в одну линию. Всего их получается M+N. В принципе, для Ваших 4000х4000, т.е. 8000 при 8-ми битном "оттенки серого" сделать 8000 прямых БПФ, 8000 "отсечек" "ненужных" частотных составляющих и 8000 обратных БПФ много времени не займет, скорее всего в 10 и более минут это не выльется. Более того, может быть не нужно будет 8000, может достаточно прокачивать через БПФ не все 4000 и 4000 реализаций, а 1999 и 1999. Т.е. берем 0..3 строки, формируем выборку по принципу среднего между 4 элементами, прокачиваем прямое БПФ, "отсечку", обратное БПФ, вычисляем коэффициент "отличия" исходного с полученным и преобразуес на этот коэффициент строки 0..3. После этого берем 2..5 строки и так далее. То же самое и со столбцами. Не знаток обработок изображения поэтому это чисто теоретические рассуждения решения задачи в лоб.
Спасибо за ответы.
Уточню: изображение 8-битного(оттенки серого), размеры изображения довольны большие в среднем 2000х2000 - 4000х4000, БПФ нужно для частотной фильтрации.
Это часть диплома, уже реализованы пространственные фильтры, морфология, арифметико-логические операции. Осталась только частотная фильтрация.
Статей прочитано много, книг тоже, но просветления не наступило)..
Т.е., я так понимаю, БПФ нужно применять отдельно по вертикали и по горизонтали (Вам же нужна фильтрация частотных составляющих).
Если можно, поподробней про это.
Можно посмотреть демо в MatLab'е пакет по обработке изображений. Также хорошую фильтрацию делают вейвлеты.
Уже смотрел. Там довольно хороший пакет. Но как это может помочь мне?
http://ru.wikipedia.org/wiki/Список_алгоритмов http://ru.wikipedia.org/wiki/Быстрое_преобразование_Фурье
Когда Вы беретесь за подобные задачи, без понимания процесса (что на входе, что на выходе и т.п.) - задачу Вы не решите никогда. Дело в том, что преобразование Фурье достаточно общий математический инструмент, который имеет кучу применений. И каждая задача для получения результата подразумевает применения каких то действий до прямого БПФ, после него и после обратного БПФ. В общем случае Ваша задача сразу вызывает вопрос - изображение цветное или оттенки серого. Более того, изображение (даже "серое") имеет громадные размеры, а БПФ возможно только на участках, да и еще на линейных (изображение же двумерное). Т.е., я так понимаю, БПФ нужно применять отдельно по вертикали и по горизонтали (Вам же нужна фильтрация частотных составляющих). Все-таки поищите теоретические статьи по фильтрации изображений, дабы понять - может Вам лучше применить цифровые фильтры (т.е. применить свертку с цифровым фильтром).
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.