Иерархия памяти CUDA. Текстуры в CUDA. Цифровая обработка сигналов Лекторы: Боресков А.В. (ВМиК МГУ) Харламов А.А. (NVidia) План • Работа с Текстурами • Свертка • Шумоподавление • Масштабирование изображений • Сжатие изображений РАБОТА С ТЕКСТУРАМИ Типы памяти в CUDA Тип памяти Скорость работы Высокая Расположение Регистры Доступ Уровень выделения R/W Per-thread Локальная R/W Per-thread Низкая DRAM Shared R/W Per-block Высокая SM Глобальная R/W Per-grid Низкая DRAM Constant R/O Per-grid Высокая DRAM Texture R/O Per-grid Высокая DRAM SM Легенда: -интерфейсы доступа Архитектура Tesla 10 CPU Bridge Host Memory Work Distribution TPC TPC TPC TPC TPC TPC TPC TPC TPC TPC Interconnection Network ROP L2 DRAM ROP L2 DRAM ROP L2 DRAM ROP L2 DRAM ROP L2 DRAM ROP L2 DRAM ROP L2 DRAM ROP L2 DRAM Архитектура Tesla Мультипроцессор Tesla 10 Streaming Multiprocessor Instruction $ Instruction Fetch Texture Processing Cluster Shared Memory SM TEX SM SM Constant $ SP SP SP SP SFU SP SP SP SP SFU Register File Double Precision Texture в 3D • В CUDA есть доступ к fixed-function HW: Texture Unit v 0.66,1 0,1 1,1 0.66,1 0,0 1,0 0,0 0,0 1,0 1,0 u Texture HW • Нормализация координат: – Обращение по координатам, которые лежат в диапазоне [0,1] v 0,1 0,0 1,1 1,0 u Texture HW • Преобразование координат: – Координаты, которые не лежат в диапазоне [0,1] (или [w, h]) v 0,1 1,1 [1.0, 0.7] [0.1, 0.3] 0,0 Clamp: -Координата «обрубается» по допустимым границам Wrap - Координата «заворачивается» в допустимый диапозон [1.1, 0.7] [1.1, 0.3] 1,0 u Texture HW • Фильтрация: – Если вы используете float координаты, что должно произойти если координата не попадает точно в texel? v 0,1 Point: -Берется ближайший texel Linear: - Билинейная фильтрация 1,1 Приблизимся вот в эту точку 0,0 1,0 u Texture HW • Фильтрация v Point: -Берется ближайший texel [0.1, 0.3] u Texture HW • Фильтрация v P1 α P2 Linear: -Билинейная фильтрация β (P1*(1-α)+P2*(α)) * (1-β) + (P3*(1-α)+P4*(α)) * (β) P3 P4 [0.1, 0.3] u Texture в CUDA • Преобразование данных: – cudaReadModeNormalizedFloat : • Исходный массив содержит данные в integer, возвращаемое значение во floating point представлении (доступный диапазон значений отображается в интервал [0, 1] или [-1,1]) – cudaReadModeElementType • Возвращаемое значение то же, что и во внутреннем представлении cudaArray • Особый контейнер памяти • Черный ящик для приложения • Позволяет организовывать данные в 1D/ 2D/3D массивы данных вида: – 1/2/4 компонентные векторы – 8/16/32 bit signed/unsigned integers – 32 bit float – 16 bit float (driver API) • На CC 2.x возможна запись из ядра Texture<> в CUDA (cudaArray) • Только чтение • Обращение к 1D/2D/3D массивам данных по: – Целочисленным индексам – Нормализованным координатам • Преобразование адресов на границах – Clamp, Wrap • Фильтрация данных – Point, Linear • Преобразование данных – Данные могут храниться в формате uchar4 – Возвращаемое значение – float4 Surface<> в CUDA (cudaArray) • Чтение и запись – cudaArraySurfaceLoadStore • Обращение к 1D/2D массивам данных по: – Целочисленным индексам – Byte-адресация по x • Преобразование адресов на границах – Clamp, Zero, Trap Texture<> в CUDA (linear) • Можно использовать обычную линейную память • Ограничения: – 1D / 2D – Нет фильтрации – Доступ по целочисленным координатам – Обращение по адресу вне допустимого диапазона возвращает ноль Texture в CUDA (linear) texture<float, 1, cudaReadModeElementType> g_TexRef; __global__ void kernel1 ( float * data ) { int idx = blockIdx.x * blockDim.x + threadIdx.x; data [idx] = tex1Dfetch(g_TexRef, idx); } int main(int argc, char ** argv) { float *phA = NULL, *phB = NULL, *pdA = NULL, *pdB = NULL; for (int idx = 0; idx < nThreads * nBlocks; idx++) phA[idx] = sinf(idx * 2.0f * PI / (nThreads * nBlocks) ); CUDA_SAFE_CALL( cudaMemcpy ( pdA, phA, nMemSizeInBytes, cudaMemcpyHostToDevice ) ); CUDA_SAFE_CALL( cudaBindTexture(0, g_TexRef, pdA, nMemSizeInBytes) ); dim3 threads = dim3( nThreads ); dim3 blocks = dim3( nBlocks ); kernel1 <<<blocks, threads>>> ( pdB ); CUDA_SAFE_CALL( cudaThreadSynchronize() ); CUDA_SAFE_CALL( cudaMemcpy ( phB, pdB, nMemSizeInBytes, cudaMemcpyDeviceToHost ) ); return 0; } Texture в CUDA (cudaArray) texture<float, 2, cudaReadModeElementType> g_TexRef; __global__ void kernel ( float * data ) { int idx = blockIdx.x * blockDim.x + threadIdx.x; data [idx + blockIdx.y * gridDim.x * blockDim.x] = tex2D(g_TexRef, idx, blockIdx.y); } int main ( int argc, char * argv [] ) { float *phA = NULL, *phB = NULL, *pdA = NULL, *pdB = NULL;// linear memory pointers cudaArray * paA = NULL; // device cudaArray pointer cudaChannelFormatDesc cfD=cudaCreateChannelDesc(32,0,0,0, cudaChannelFormatKindFloat); CUDA_SAFE_CALL( cudaMallocArray(&paA, &cfD, nBlocksX * nThreads, nBlocksY) ); for (int idx = 0; idx < nThreads * nBlocksX; idx++) { phA[idx] = sinf(idx * 2.0f * PI / (nThreads * nBlocksX) ); phA[idx + nThreads * nBlocksX] = cosf(idx * 2.0f * PI / (nThreads * nBlocksX) ); } CUDA_SAFE_CALL(cudaMemcpyToArray(paA,0,0,phA,nMemSizeInBytes,cudaMemcpyHostToDevice) ); CUDA_SAFE_CALL( cudaBindTextureToArray(g_TexRef, paA) ); dim3 threads = dim3( nThreads ); dim3 blocks = dim3( nBlocksX, nBlocksY ); kernel2<<<blocks, threads>>> ( pdB ); CUDA_SAFE_CALL( cudaThreadSynchronize() ); CUDA_SAFE_CALL( cudaMemcpy ( phB, pdB, nMemSizeInBytes, cudaMemcpyDeviceToHost ) ); return 0; } Texture HW • Латентность больше, чем у прямого обращения в память – Дополнительные стадии в конвеере: • Преобразование адресов • Фильтрация • Преобразование данных • Есть кэш – Разумно использовать, если: • Объем данных не влезает в shared память • Шаблон доступа хаотичный • Данные переиспользуются разными потоками СВЕРТКА Свертка • Определение свертки: r (i ) ( s * k )(i ) s (i n)k (n)dn • В Дискретном случае: r (i) ( s * k )(i) s(i n)k (n) n • В 2D для изображений: r (i, j ) ( s * k )(i, j ) s(i n, j m)k (n, m) n m Свертка Исходный сигнал 1 2 4 5 5 7 1 4 5 2 1 2 3 4 1 2 5 4 6 6 1 7 1 8 i 1 2 3 7 5 5 9 6 1 3 2 3 1 9 6 4 9 5 9 2 5 3 7 6 4 2 3 6 8 4 4 9 6 6 8 7 2 3 9 5 j Легенда: -сигнал -ядро свертки Окно 6 6 1 3 7 5 2 3 1 0 1 0 1 2 1 0 1 0 Ядро 0 6 0 ∑ 3 14 5 0 3 0 0 6 0 3 14 5 0 3 0 Выходной сигнал 31 31 Свертка • Вычислительная сложность: – W x H x N x K – умножений Размер входного сигнала Размер ядра • Сепарабельные фильтры -1 0 1 1 -2 0 2 2 -1 0 1 1 Ядро Ядро Y -1 0 1 Ядро X Примеры • Gaussian Blur • Edge Detection Gaussian Blur • Свертка с ядром: k (i ) exp( i 2 / 2 ) k (i, j ) exp( (i 2 j 2 ) / 2 ) Gaussian Blur #define SQR(x) ((x) * (x)) texture<float, 2, cudaReadModeElementType> g_TexRef; __global__ void GaussBlur( float * pFilteredImage, int W, int H, float r) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int idy = blockIdx.y * blockDim.y + threadIdx.y; float wSum = 0.0f; float rResult = 0.0f; for (int ix = -r; ix <= r; ix++){ for (int iy = -r; iy <= r; iy++) { float w = exp( -(SQR(ix) + SQR(iy)) / SQR(r) ); rResult += w * tex2D(g_TexRef, idx + ix, idy + iy); wSum += w; } } rResult = rResult / wSum; pFilteredImage[idx + idy * W] = rResult; } Свертка Оптимизации • Использовать сепарабельные фильтры – Существенно меньше алгоритмическая сложность • Использовать shared память Свертка Оптимизации Исходное изображение Эффективно ли такое разбиение изображения Свертка Smem Оптимизации Легенда: -сигнал -ядро свертки Свертка Smem Оптимизации Легенда: -сигнал -ядро свертки Свертка Smem Оптимизации Легенда: -сигнал -ядро свертки Свертка Smem Оптимизации EDGE DETECTION Edge Detection • Обнаружение границ – поиск разрывов в яркости изображения Идеальная граница «реальная» граница «шумная» граница Edge Detection • Градиент функции f(x,y) – Это вектор который показывает направление роста f f – Определяется как G x y Gy G 1 2 2 y | G ( x, y ) | [Gx2 G ] θ(x,y) = atan(Gy/Gx) Gx Edge Detection • Разностная производная: f ( x, y ) f ( x x, y ) f ( x, y ) x x f ( x, y ) f ( x, y y ) f ( x, y ) y y • Свертка с ядром: D1 y [1 1] 1 D1y 1 Edge Detection • Разностная производная: f ( x, y ) f ( x x, y ) f ( x x, y ) x 2x f ( x, y ) f ( x, y y ) f ( x, y y ) y 2y • Свертка с ядром: D2 y [1 0 1] 1 D2 y 0 1 Edge Detection • Prewitt mask: 1 1 1 1 0 1 0 0 0 P Px 1 0 1 y 1 1 1 1 0 1 Edge Detection • Sobel mask: 2 1 1 1 0 1 0 P 0 0 S x 2 0 2 y 1 2 1 1 0 1 Edge Detection • Оператор Лапласа: 2 f ( x, y) 2 f ( x, y) L[ f ( x, y)] 2 x y 2 2 f ( x, y) f ( x x, y) 2 f ( x, y) f ( x x, y) 2 x x 2 2 f ( x, y) f ( x, y y) 2 f ( x, y) f ( x, y y) 2 y y 2 ШУМОПОДАВЛЕНИЕ Discrete Cosine Transform • Является основой современных алгоритмов сжатия данных с потерями (JPEG, MPEG) JPEG, 2/10 Discrete Cosine Transform • Представитель семейства пространственно-частотных 1D преобразований, задается формулами: 2 x 1u C (u ) (u ) f ( x) cos , u 0,1,..., N 1 • Прямое: 2N 2 x 1u f ( x ) ( u ) C ( u ) cos x 0,1,..., N 1 • Обратное: 2 N , • Нормировочные коэффициенты: N 1 x 0 N 1 u 0 (u ) 1 N ,u 0 2 N ,u 0 Discrete Cosine Transform • 8-точечный случай: u=0 Discrete Cosine Transform • 8-точечный случай: u=1 Discrete Cosine Transform • 8-точечный случай: u=2 Discrete Cosine Transform • 8-точечный случай: u=3 Discrete Cosine Transform • 8-точечный случай: u=4 Discrete Cosine Transform • 8-точечный случай: u=5 Discrete Cosine Transform • 8-точечный случай: u=6 Discrete Cosine Transform • 8-точечный случай: u=7 Discrete Cosine Transform • N-мерное преобразование обладает свойством сепарабельности • Коэффициенты A[8x8] преобразования вычисляются один раз Discrete Cosine Transform • Наивный: 64 нити на блок (8х8) – Загрузка одного пикселя из текстуры – Барьер – Поток вычисляет один коэффициент – Барьер – Запись коэффициента в глобальную память Насколько это эффективно? Discrete Cosine Transform • Блок потоков обрабатывает несколько блоков 8х8 • Один поток обрабатывает вектор 8х1 (1x8) Image Denoising • Шумы в изображении – Импульсный • Salt & pepper – Аддитивный • Uniform • Gaussian Ранговые фильтры • Алгоритм Р.Ф. ранга N: – Для каждого отсчета сигнала i – Выбор окрестности вокруг отсчета i • Сортируем по значению • Выбираем N-ое значение как результат Медиана • Ранговый фильтр N=0.5 • Сортировка не обязательна для 8bit значений – Строим гистограмму for(int i<-R; i<R; i++) h[ signal[i] ]++; – Сканируем Гистограмму: int sum = 0; int targetSum = N*rank; for(int i<0; i<256; i++) { sum += h[i]; if (sum > targetSum) return i; } Медиана Построение гистограммы сигнал Сигнал + фартук в Smem Что с банк-конфликтами? Фильтрация (Аддитивный шум) • Размытие – это low-pass фильтр • Каким должен быть фильтр? – Подавлять шум? – Сохранять детальность? noise E(noise) = 0 Gaussian Blur • Blur (размытие) изображение • Свертка с ядром: k (i ) exp( i 2 / 2 ) k (i, j ) exp( (i 2 j 2 ) / 2 ) Gaussian Blur • Blur (размытие) изображение • Свертка с ядром: Адаптивное размытие • Свертка с ядром: k (i, j ) exp( (i j ) / ) exp( ClrSpaceDi st (i, j ) / h ) 2 2 2 • ClrSpaceDist – это фотометрическая близость 2 Bilateral Wc (y ) e |y c|2 r2 e |u ( y ) u ( c )|2 h2 Bilateral 0 1 2 3 4 5 6 7 Wc (y ) e 0 1 2 e 4 5 6 7 e e |u ( y ) u ( c )|2 h2 (( 0 4 ) 2 ( 0 4 ) 2 ) (10 ) 2 32 h2 Wc (y ) e 3 |y c|2 2 r e |y c|2 r2 e |u ( y ) u ( c )|2 h2 (( 7 4 ) 2 ( 7 4 ) 2 ) (11) 2 2 32 h e Bilateral Kernel #define SQR(x) ((x) * (x)) texture<float, 2, cudaReadModeElementType> g_TexRef; __global__ void BilateralBlur( float * pFilteredImage, int W, int H, float r) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int idy = blockIdx.y * blockDim.y + threadIdx.y; float wSum = 0.0f; float rResult = 0.0f; float c = tex2D(g_TexRef, idx, idy); for (int ix = -r; ix <= r; ix++) for (int iy = -r; iy <= r; iy++) { float clr = tex2D(g_TexRef, idx + ix, idy + iy); float w = exp( -(SQR(ix) + SQR(iy)) / SQR(r) – SQR(clr-c)/SQR(h)); rResult += w * clr; wSum += w; } rResult = rResult / wSum; pFilteredImage[idx + idy * W] = rResult; } Bilateral Оптимизации • Bilateral не сепарабельный фильтр – Но можно его разделить • Смешивать исходное изображение с фильтрованным – Если в блоке много ненулевых коэф., то с большой вероятностью в этом блоке шум был подавлен успешно – Если в блоке много нулевых коэф., то с большой вероятностью в блоке много деталей (границы, текстура и т.д.) Non Local Means • ClrSpaceDist – оценивать по блокам пикселей Wc ( y) (B(c), B(y )) 1 | u (y (c α)) u (α) |2 dα S (B ) B ( c ) Non Local Means • На вычисление одного веса: – NbxNb вычислений, N размер блока • На фильтрацую одного пиксела: – NbxNb x RxR, R размер окна Сравнение Сравнение Bilateral Сравнение NLM МАСШТАБИРОВАНИЕ ИЗОБРАЖЕНИЙ Артифакты • Алиасинг – При увелечении – ступенчатость – При уменьшении - муар • Ringing • Потеря четкости • Субпиксельный сдвиг – Влияет на формальные метрики Простые методы • Билинейная интерполяция ( P1 P3) 2 ( P1 P 7) P4 2 ( P 3 P 9) P6 2 ( P 7 P 9) P8 2 ( P1 P3 P 7 P9) P5 4 Легенда: P2 P1 P2 P3 P4 P5 P6 P7 P8 P9 -исходные пиксели -искомые пиксели Простые методы • Билинейная интерполяция – Сепарабельная – Очень быстрая – Поддерживается в HW • Точность фильтрации Простые методы • Бикубическая интерполяция – Сепарабельная – Лучше качество t=0.5 t=0 t=1 Сравнение Lanczos sin( FD (t kt )) x(t ) x(kt ) FD (t kt ) sin c( x) sin c( x a),a x a, x 0 L( x ) 1 x0 0 иначе Gradient interpolation P1 P2 P3 Dxd = abs(P3 – P5) Dyd = abs(P1 – P9) If (Dxd > Dyd) P7 P5 P6 //граница P1P5P9 P5 = (P1 + P9) * 0.5f; P7 P8 P9 If (Dyd > Dxd) //граница P3P5P7 P5 = (P1 + P9) * 0.5f; If (Dyd ~= Dxd) //граница не определена P5 = (P1 + P3 + P7 + P9) * 0.25f; Легенда: -исходные пиксели -искомые пиксели NEDI (New Edge-Directed Interpolation) Легенда: -исходные пиксели -искомые пиксели NEDI (New Edge-Directed Interpolation) Легенда: -исходные пиксели -искомые пиксели NEDI (New Edge-Directed Interpolation) Легенда: -исходные пиксели -искомые пиксели NEDI (New Edge-Directed Interpolation) Легенда: -исходные пиксели -искомые пиксели NEDI 2i α1 α2 x α3 α4 2j X = F(2i+1,2j+1) = α1*F(2i,2j)+ α2*F(2i+2,2j)+ α3*F(2i,2j+2)+ α4*F(2i+2,2j+2); α = {α1, α2, α3, α4} ? Легенда: -исходные пиксели -искомые пиксели NEDI 0 0 1 α1 3… α2 P2,2 P4,2 P6,2 P8,2 P2,4 α4 P6,4 P8,4 P2,6 P4,6 P6,6 P8,6 P2,8 P4,8 P6,8 P8,8 1 2 2 3… α3 Легенда: -исходные пиксели -искомые пиксели NEDI 0 0 1 α1 2 3… α2 Xi,j = α1*F(i-2,j-2) + α2*F(i+2,j-2) + α3*F(i-2,j+2) + α4*F(i+2,j+2); 1 For i = 2,4,6,8 | For j = 2,4,6,8 2 3… α3 P2,2 P4,2 P6,2 P8,2 P2,4 α4 P6,4 P8,4 P2,6 P4,6 P6,6 P8,6 Ei,j = Pj,j – Xi,j – Approximation Error SSE = ΣΣ sqr( Ei,j ); α = Arg min(SSE); α P = {Pij} Cα = P ∂(SSE) / ∂α = 0.0 P2,8 P4,8 P6,8 P8,8 α = (CTC)-1 CTP Легенда: -исходные пиксели -искомые пиксели NEDI (improvement) … … Легенда: -исходные пиксели -искомые пиксели NEDI (improvement) … … Легенда: -исходные пиксели -искомые пиксели - вспомогательные пиксели NEDI: Pros and Cons • Pros: NEDI – Четкие тонкие края • Cons: Очень медленно на CPU – Умножение матриц 4х16 – Обращение матрицы – Рингинг • CUDA: – Большой объем данных на тред – Много регистров – Сложно использовать Smem – Много ветвлений Сравнение Сравнение ФРАКТАЛЬНОЕ СЖАТИЕ АЛГОРИТМА Мат.часть • Сжимающее отображение f, XX: f : x, y X , d ( f ( x), f ( y )) d ( x, y ) • Если f сжимающее, то w, w X : x X , lim ( f ( x)) w n n Фрактальное сжатие Ранг Домен Фрактальное сжатие Ранг Домен Фрактальное сжатие Ранг Домен Фрактальное сжатие Ранг Домен Фрактальное сжатие ... Ранг Домен Фрактальное сжатие Ранг Домен Фрактальное сжатие Ранг Домен Фрактальное сжатие SSE ( R(i, j ) D(i, j ) s t ) i, j s, t : min (SSE ) s ,t 2 Фрактальное сжатие • Блок пикселей заменяется преобразованием – Сдвиг, поворот / отражение – s,t – преобразование яркости • float2 x rgb • s,t – сжимают во время компресси – Например s:5,t:3 – в интервале [0, 1] Декомпрессия • Сжимающее отображение f, XX: f : x, y X , d ( f ( x), f ( y )) d ( x, y ) • Если f сжимающее, то w, w X : x X , lim ( f ( x)) w n • Берем любое изображение • Строим Доменное изображение • Применим полученный набор преобразований к блокам n Сколько раз необходимо применять преобразования? Декомпрессия Артифакты Артефакты Вопросы Задание 1 • Вычислить число pi методом монтекарло – Генерация точки (x, y) в [-1, 1] x [-1, 1] – Расчет сколько точек попало в ед. круг • Построение Kd дерева для точек – Задние на thrust Задание 2 • Изменение времени звуковой дорожки без изменения тона – Задание на CUFFT • Обучение фильтра – Разбить изображение на блоки – Классифицировать каждый блок – Обучить фильтр имея пару (R, T) • R – «правильное» изображение • T – «входной» сигнал Ресурсы нашего курса • Steps3d.Narod.Ru • Google Site CUDA.CS.MSU.SU • Google Group CUDA.CS.MSU.SU • Google Mail CS.MSU.SU • Google SVN • Tesla.Parallel.Ru • Twirpx.Com • Nvidia.Ru Преобразование Фурье • Линейный оператор вида: F (u ) f ( x )e 2i xu dx F (u, v) f ( x, y )e 2i (ux vy ) dxdy • Обратный оператор: f ( x) F (u )e 2i xu du f ( x, y ) 2i ( ux vy ) F ( u , v ) e dudv Преобразование Фурье • Условие существования – | f ( x) | dx | f ( x, y) |dxdy – Конечное число устранимых разрывов Преобразование Фурье Пример 1D f(x) F (u ) A W f ( x)e 2i xu dx A e 2i xu dx 0 A 2i xu W A 2iW u e e 1 0 2iu 2iu x A iW u iW u e e eiW u 2iu A iW u 2i sin( uW ) e 2iu sin( uW ) iW u AW e uW AW sin c(uW )e iW u 0 W | F (u ) | AW sin c(uW ) Преобразование Фурье Пример 2D f(x,y) F (u , v) f ( x, y )e 2i ( ux vy ) dxdy A y W2 x W1 W1 W2 0 0 A e 2iuxdx e 2ivy dy 2iux W1 2ivy W2 e e A 2 iu 2 iv 0 0 AW1W2 sin c(uW1 ) sin c(vW2 )e i ( uW 1 vW 2 ) | F (u, v) | AW1W2 | sin c(uW1 ) || sin c(vW2 ) | Преобразование Фурье Свойства 1. f ( x, y) f1 ( x) f 2 ( y) F (u, v) F1 (u)F2 (v) 2. F{ f * ( x, y)} F * (u,v) * f ( x ) R | F ( u ) | | F (u) | 3. * f ( x , y ) R | F ( u , v ) | | F (u,v) | 4. 5. F{ f ( x, y)} F (u,v) F (u / a, v / b) 6. F{ f (ax, by)} | ab | 7. F{ f (r, 0 )} F (w, 0 ) Преобразование Фурье Свойства 1. 2. 3. 4. F{ f ( x, y ) h( x, y )]} F (u, v) H (u, v) F{ f ( x, y )h( x, y )]} F (u, v) H (u, v) f ( x, y ) F 1{2iuF (u, v)} x F{f ( x, y)} 4 2 (u 2 v 2 ) F (u, v) F (u , v) f ( x, y )e 2iux dx e 2ivy dy F (u , y )e 2ivy dy 5. F{ f ( x)} C Преобразование Фурье Свойства 1. 2. 3. F{ f ( x, y ) h( x, y )]} F (u, v) H (u, v) F{ f ( x, y )h( x, y )]} F (u, v) H (u, v) f ( x, y ) F 1{2iuF (u, v)} x F{f ( x, y)} 4 2 (u 2 v 2 ) F (u, v) 2ivy 2i ux F (u , v) f ( x, y )e dx e dy F (u , y )e 2ivy dy 4. 5. F { f ( x)} C Фильтры a) Низкочастотные (low-pass) b) Высокочастотные (high-pass) (b) Фильтры (b)