Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОСИ / АСУ_Меркулова_Обработкаизобр_ Методичка к курсовому.docx
Скачиваний:
37
Добавлен:
03.03.2016
Размер:
216.12 Кб
Скачать

Шумоподавляющая фильтрация

Ниже будут рассмотрены фильтры подавления шумов. При их использовании может использоваться введенное ранее понятие апертуры фильтра.

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

При рассмотрении примеров фильтрации необходимо учитывать, что в примерах под действие фильтра могут не попадать крайние элементы изображения, поэтому при искусственном зашумлении их лучше преднамеренно не зашумлять, либо обрабатывать каким-то образом частный случай крайних точек

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

int i, j, k, m;

int R, G, B;

Bitmap rezultImage = new Bitmap(bmp);

for (i = 1; i < (bmp.Width - 1); i++)

{

for (j = 1; j < (bmp.Height - 1); j++)

{

R = 0;

G = 0;

B = 0;

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

//Находим сумму красного

R = R + bmp.GetPixel(i + k, j + m).R;

//Выполняем аналогичные операции для синего (B) и зеленого (G)

G = G + bmp.GetPixel(i + k, j + m).G;

B = B + bmp.GetPixel(i + k, j + m).B;

}

}

//Находим среднее для красного

R = R / 9;

//Находим среднее для зеленого

G = G / 9;

//Находим среднее для синего

B = B / 9;

//Помещаем полученные средние значения в центральный пиксель i, j

rezultImage.SetPixel(i, j, Color.FromArgb(R, G, B));

}

}

Осреднение скользящим окном в целом схоже со сглаживающим фильтром: находится среднее арифметическое значение всех элементов рабочего окна изображения (отдельно по каждому из каналов). После чего это среднее значение становится значением среднего элемента (речь идёт о нечётной апертуре фильтра; для двумерного случая средним элементом будет средний элемент по горизонтали и вертикали, то есть центр квадрата).

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

Выглядит такой фильтр примерно так:

object SliceSmooth(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

int i, j, k, m;

int R, G, B;

//Матрица для этого фильтра будет иметь вид:

// 1 1 1

// 1 1 1

// 1 1 1

for (i = 1; i < (rezultImage.Width - 1); i++)

{

//Вначале двигаемся вниз

for (j = 1; j < (rezultImage.Height - 1); j++)

{

//Находим сумму красного в окне фильтра 3х3

R = 0;

G = 0;

B = 0;

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

//Находим сумму красного

R = R + bmp.GetPixel(i + k, j + m).R;

//Выполняем аналогичные операции для синего (B) и зеленого (G)

G = G + bmp.GetPixel(i + k, j + m).G;

B = B + bmp.GetPixel(i + k, j + m).B;

}

}

//Находим среднее для красного

R = R / 9;

//Находим среднее для зеленого

G = G / 9;

//Находим среднее для синего

B = B / 9;

//Помещаем полученные значения средних в центральный пиксель i, j

rezultImage.SetPixel(i, j, Color.FromArgb(R, G, B));

}

//При достижении нижней границы двигаемся вправо на 1 пиксель и поворачиваем вверх

i++;

if (i >= (rezultImage.Width - 1))

{

//Если двигаться вправо некуда - выходим из цикла

break;

}

//После движения вниз начинаем двигаться вверх

for (j = (rezultImage.Height - 3); j >= 1; j--)

{

//Находим сумму красного в окне фильтра 3х3

R = 0;

G = 0;

B = 0;

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

//Находим сумму красного

R = R + bmp.GetPixel(i + k, j + m).R;

//Выполняем аналогичные операции для синего (B) и зеленого (G)

G = G + bmp.GetPixel(i + k, j + m).G;

B = B + bmp.GetPixel(i + k, j + m).B;

}

}

//Находим среднее для красного

R = R / 9;

//Находим среднее для зеленого

G = G / 9;

//Находим среднее для синего

B = B / 9;

//Помещаем полученные значения средних в центральный пиксель i, j

rezultImage.SetPixel(i, j, Color.FromArgb(R, G, B));

}

}

return rezultImage;

}

Гауссовский фильтр - наиболее широко используемый шумоподавляющий фильтр. Он применяется для удаления шума не только самостоятельно, но и при разработках многих алгоритмов детекторов границ. Этот фильтр имеет следующую маску 3х3:

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

Такая операция называется сверткой изображения с маской.

Следует отметить, что гауссовский фильтр дает несколько меньшее размывание контура, чем усредняющий. Выглядит гауссовский фильтр примерно так:

object Gaussian(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

int i, j, k, m, count;

int R, G, B;

int[] Red, Green, Blue;

//Матрица для этого фильтра будет иметь вид:

// 1 2 1 | A0 A1 A2

// 2 4 2 | A3 A4 A5

// 1 2 1 | A6 A7 A8

for (i = 1; i < (bmp.Width - 1); i++)

{

for (j = 1; j < (bmp.Height - 1); j++)

{

Red = new int[9];

Green = new int[9];

Blue = new int[9];

count=0;

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

Red[count] = bmp.GetPixel(i + k, j + m).R;

Green[count] = bmp.GetPixel(i + k, j + m).G;

Blue[count] = bmp.GetPixel(i + k, j + m).B;

count++;

}

}

//Находим сумму красного в окне фильтра 3х3

R = Red[0] + 2 * Red[1] + Red[2] + 2 * Red[3] + 4 * Red[4] + 2 * Red[5] + Red[6] + 2 * Red[7] + Red[8];

//Выполняем аналогичные операции для синего (B) и зеленого (G)

G = Green[0] + 2 * Green[1] + Green[2] + 2 * Green[3] + 4 * Green[4] + 2 * Green[5] + Green[6] + 2 * Green[7] + Green[8];

B = Blue[0] + 2 * Blue[1] + Blue[2] + 2 * Blue[3] + 4 * Blue[4] + 2 * Blue[5] + Blue[6] + 2 * Blue[7] + Blue[8];

//Делим полученные значения на сумму коэффициентов матрицы

R = R / 16;

G = G / 16;

B = B / 16;

//Помещаем полученные значения средних в центральный пиксель i, j

rezultImage.SetPixel(i, j, Color.FromArgb(R, G, B));

}

}

return rezultImage;

}

Медианный фильтр основывается на нахождении медианы – среднего элемента (но не среднего арифметического) последовательности в результате её упорядочения по возрастанию/убыванию и присваиванию найденного значения только среднему элементу (речь снова о нечётной апертуре). Например, для той же апертуры 3 и двумерного фильтра (как в примере выше) мы должны упорядочить 9 точек (например, по возрастанию), после чего значение 5й точки упорядоченной последовательности отправить в центр окна фильтра (3х3). Для упорядочения можно использовать любой из известных методов сортировки.

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

Bitmap rezultImage = new Bitmap(bmp);

int i, j, k, m, count;

int[] R, G, B;

R = new int[9];

G = new int[9];

B = new int[9];

//Матрица для этого фильтра будет иметь вид:

// 1 1 1

// 1 1 1

// 1 1 1

for (i = 1; i < (rezultImage.Width - 1); i++)

{

//Вначале двигаемся вправо

for (j = 1; j < (rezultImage.Height - 1); j++)

{

count = 0;

//Считываем уровни всех пикселей для красного в окне фильтра

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

R[count] = bmp.GetPixel(i + k, j + m).R;

G[count] = bmp.GetPixel(i + k, j + m).G;

B[count] = bmp.GetPixel(i + k, j + m).B;

count++;

}

}

//Сортируем полученные массивы, используя стандартные методы C#

Array.Sort(R);

Array.Sort(G);

Array.Sort(B);

//Помещаем медианный элемент на место центрального в окне фильтра

rezultImage.SetPixel(i, j, Color.FromArgb(R[4], G[4], B[4]));

}

}

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

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

Сигма-фильтр усредняет значения только тех соседних пикселей, чьи значения находятся в пределах от значения центрального пикселя в окне. Где c – некоторая константа, а– дисперсия значений яркости или насыщенности цветовых каналов, которая определяется один раз для всего изображения.

Этот метод стремится осреднять пиксель только с теми соседями, которые имеют значения, близкие к нему, по сравнению со среднеквадратичным отклонением шума по изображению. В этом фильтре опорным служит центральный пиксель в окне, поэтому такой фильтр не сможет устранить импульсный шум. Выглядит сигма-фильтр примерно так:

object Sigma(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

double sigmaR, sigmaG, sigmaB;

double brightR, brightG, brightB;

double c;

int countR, countG, countB;

double Rc, Gc, Bc;

int i, j, k, m;

brightR = 0;

brightG = 0;

brightB = 0;

sigmaR = 0;

sigmaG = 0;

sigmaB = 0;

//Находим среднее значение насыщенности по каналам изображения

for (i = 0; i < rezultImage.Width; i++)

{

for (j = 0; j < rezultImage.Height; j++)

{

brightR = brightR + bmp.GetPixel(i, j).R;

brightG = brightG + bmp.GetPixel(i, j).G;

brightB = brightB + bmp.GetPixel(i, j).B;

}

}

brightR = brightR / (bmp.Height * bmp.Width);

brightG = brightG / (bmp.Height * bmp.Width);

brightB = brightB / (bmp.Height * bmp.Width);

//Находим дисперсию для всего изображения

brightR=Math.Pow(brightR,2); //Находим квадраты значений насыщенности для RGB. Поможет избежать лишних операций при поиске дисперсии

brightG=Math.Pow(brightG,2);

brightB=Math.Pow(brightB,2);

for (i = 0; i < rezultImage.Width; i++)

{

for (j = 0; j < rezultImage.Height; j++)

{

sigmaR = sigmaR + (Math.Pow(bmp.GetPixel(i, j).R, 2) - brightR);

sigmaG = sigmaG + (Math.Pow(bmp.GetPixel(i, j).G, 2) - brightG);

sigmaB = sigmaB + (Math.Pow(bmp.GetPixel(i, j).B, 2) - brightB);

}

}

sigmaR = sigmaR / (bmp.Height * bmp.Width);

sigmaG = sigmaG / (bmp.Height * bmp.Width);

sigmaB = sigmaB / (bmp.Height * bmp.Width);

//Находим корень из дисперсии для нахождения отклонения

sigmaR = Math.Sqrt(sigmaR);

sigmaG = Math.Sqrt(sigmaG);

sigmaB = Math.Sqrt(sigmaB);

//Для данного фильтра будем использовать окно размерами 3х3

//С константой c=0.5

c = 0.5;

//Увеличение константы c приведет к увеличению числа пикселей, которые будут участвовать в осреднении

//Чрезмерное уменьшение приведет к тому, что под действие фильтра будет попадать крайне малое число пикселей

for (i = 1; i < (rezultImage.Width - 1); i++)

{

for (j = 1; j < (rezultImage.Height - 1); j++)

{

countR = 0;

countG = 0;

countB = 0;

Rc = 0;

Gc = 0;

Bc = 0;

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

if (k == 0 && m == 0)

{

continue;

}

//Суммируем уровни каналов только в том случае, если они отклонены от центрального пикселя в окне не более чем на c*sigma

if (Math.Abs(bmp.GetPixel(i, j).R - bmp.GetPixel(i + k, j + m).R) <= (c * sigmaR))

{

Rc = Rc + bmp.GetPixel(i + k, j + m).R;

countR++;

}

if (Math.Abs(bmp.GetPixel(i, j).G - bmp.GetPixel(i + k, j + m).G) <= (c * sigmaG))

{

Gc = Gc + bmp.GetPixel(i + k, j + m).G;

countG++;

}

if (Math.Abs(bmp.GetPixel(i, j).B - bmp.GetPixel(i + k, j + m).B) <= (c * sigmaB))

{

Bc = Bc + bmp.GetPixel(i + k, j + m).B;

countB++;

}

}

}

//Находим среднее значение суммы (осреднение - нахождение среднего)

Rc = Rc / countR;

Gc = Gc / countG;

Bc = Bc / countB;

rezultImage.SetPixel(i, j, Color.FromArgb((int)Rc, (int)Gc, (int)Bc));

}

}

return rezultImage;

}

Направленный фильтр работает с квадратной маской, чаще всего 3х3, но могут применяться и бо́льшие по размеру матрицы. Применение масок больших, чем 3х3, дает больший выбор угла, под которым находится граница. Направленный фильтр сглаживает изображение в направлении, параллельном предполагаемой границы.

Чаще всего применяются следующие маски 3х3:

Рассмотрим пример реализации для маски 45°:

object Direct(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

int i, j;

double R, G, B;

//В данном примере рассмотрим маску 45°

// 0 0 1

// 0 1 0

// 1 0 0

for (i = 1; i < (rezultImage.Width - 1); i++)

{

for (j = 1; j < (rezultImage.Height - 1); j++)

{

//Выбираем элементы только из диагонали

R = bmp.GetPixel(i - 1, j + 1).R + bmp.GetPixel(i, j).R + bmp.GetPixel(i + 1, j - 1).R;

G = bmp.GetPixel(i - 1, j + 1).G + bmp.GetPixel(i, j).G + bmp.GetPixel(i + 1, j - 1).G;

B = bmp.GetPixel(i - 1, j + 1).B + bmp.GetPixel(i, j).B + bmp.GetPixel(i + 1, j - 1).B;

R = R / 3;

G = G / 3;

B = B / 3;

rezultImage.SetPixel(i, j, Color.FromArgb((int)R, (int)G, (int)B));

}

}

return rezultImage;

}

Осреднение скользящим окном с использованием весовых коэффициентов для значений соседних пикселов. Данный фильтр использует скользящее окно, описанное ранее. Отличаться данный фильтр от обычного осреднения скользящим окном будет тем, что он использует другую маску, в которой соседним пикселям могут придаваться весовые коэффициенты, отличные от 1. Маска для данного фильтра подбирается исходя из априорных сведений о содержащемся на изображении шуме.

Ранговая фильтрация с оценкой середины диапазона. Новое значение яркости или насыщенности цветовых каналов центрального в окне пикселя устанавливается равным середине между максимальным и минимальными значениями яркости или насыщенности его соседей. Выглядит это примерно так:

object Rang(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

int i, j, k, m, count;

int[] R, G, B;

int minR, minG, minB, maxR, maxG, maxB;

int sR, sG, sB;

R = new int[8];

G = new int[8];

B = new int[8];

//На элементы будет наложена матрица вида:

// 1 1 1

// 1 1 1

// 1 1 1

for (i = 1; i < (rezultImage.Width - 1); i++)

{

for (j = 1; j < (rezultImage.Height - 1); j++)

{

count = 0;

//Считываем уровни всех пикселей для красного в окне фильтра

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

if (m == 0 && k == 0)

{

continue;

}

R[count] = bmp.GetPixel(i + k, j + m).R;

G[count] = bmp.GetPixel(i + k, j + m).G;

B[count] = bmp.GetPixel(i + k, j + m).B;

count++;

}

}

//Находим минимум и максимум, используя стандартные методы C#

minR = R.Min(); maxR = R.Max();

minG = G.Min(); maxG = G.Max();

minB = B.Min(); maxB = B.Max();

//Находим среднее значение между максимумом и минимумом

sR = (maxR + minR) / 2;

sG = (maxG + minG) / 2;

sB = (maxB + minB) / 2;

//Помещаем средние значения на место центрального в окне фильтра

rezultImage.SetPixel(i, j, Color.FromArgb(sR, sG, sB));

}

}

return rezultImage;

}

Оператор К ближайших соседей проводит осреднение только по k ближайшим по величине соседям центрального значения из упорядоченного списка. Значение k обычно устанавливается таким (5 или 6 для маски 3x3), что углы и тонкие линии сильно разрушаются. Это делается не специально, но меньшие значения будут плохо бороться с шумом.

Для нахождения K ближайших соседей можно пользоваться следующей функцией:

int SearchNear(int[] M, int index, int k)

{

int rezultSumm = 0;

int count = k;

int forward, backward;

//Отступаем на шаг вперед и назад от элемента, расположенного в центре окна (index равен середине выборки)

forward = 1;

backward = -1;

//Выполняем, пока не найдем K ближайших элементов

while (count != 0)

{

//Если индексы шага вперед и назад находятся в пределах массива

if ((index + forward) < M.Length && (index + backward) >= 0)

{

//Находим тот элемент, который менее отличается от центрального и суммируем его

if (Math.Abs(M[index + forward] - M[index]) <= Math.Abs(M[index + backward] - M[index]))

{

rezultSumm = rezultSumm + M[index + forward];

forward++;

}

else

{

rezultSumm = rezultSumm + M[index + backward];

backward--;

}

}

//Если индекс шага назад вышел за пределы массива - суммируем только по шагу вперед

else if ((index + forward) <= M.Length && (index + backward) < 0)

{

rezultSumm = rezultSumm + M[index + forward];

forward++;

}

//Если индекс шага вперед вышел за пределы массива - суммируем только по шагу назад

else

{

rezultSumm = rezultSumm + M[index + backward];

backward--;

}

count--;

}

//Находим среднее значение

rezultSumm = rezultSumm / k;

return rezultSumm;

}

А сам фильтр будет выглядеть так:

object KNear(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

int i, j, k, m, count;

int[] R, G, B;

int Rc, Gc, Bc;

int K;

R = new int[9];

G = new int[9];

B = new int[9];

K = 5; //Будет использоваться 5 ближайших элементов

//На элементы будет наложена матрица вида:

// 1 1 1

// 1 1 1

// 1 1 1

for (i = 1; i < (rezultImage.Width - 1); i++)

{

for (j = 1; j < (rezultImage.Height - 1); j++)

{

count = 0;

//Считываем уровни всех пикселей для красного в окне фильтра

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

R[count] = bmp.GetPixel(i + k, j + m).R;

G[count] = bmp.GetPixel(i + k, j + m).G;

B[count] = bmp.GetPixel(i + k, j + m).B;

count++;

}

}

//Выполняем сортировку полученных массивов при помощи стандартных методов С#

Array.Sort(R);

Array.Sort(G);

Array.Sort(B);

//Получаем среднее k-ближайших соседей для цветовых каналов

Rc = SearchNear(R, 4, K);

Gc = SearchNear(G, 4, K);

Bc = SearchNear(B, 4, K);

//Помещаем средние значения на место центрального в окне фильтра

rezultImage.SetPixel(i, j, Color.FromArgb(Rc, Gc, Bc));

}

}

return rezultImage;

}

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

Фильтр будет выглядеть примерно так:

object AlphaTrunc(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

int i, j, k, m, count;

int[] R, G, B;

int Rc, Gc, Bc;

double alpha;

R = new int[9];

G = new int[9];

B = new int[9];

//Для примера возьмем альфа=0.7 (т.е. отсечется 30% крайних значений)

alpha = 0.7;

//На элементы будет наложена матрица вида:

// 1 1 1

// 1 1 1

// 1 1 1

for (i = 1; i < (rezultImage.Width - 1); i++)

{

for (j = 1; j < (rezultImage.Height - 1); j++)

{

count = 0;

//Считываем уровни всех пикселей для красного в окне фильтра

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

R[count] = bmp.GetPixel(i + k, j + m).R;

G[count] = bmp.GetPixel(i + k, j + m).G;

B[count] = bmp.GetPixel(i + k, j + m).B;

count++;

}

}

//Выполняем сортировку полученных массивов при помощи стандартных методов С#

Array.Sort(R);

Array.Sort(G);

Array.Sort(B);

//Получаем среднее alpha*n ближайших соседей

Rc = SearchNear(R, 4, (int)(alpha * 9));

Gc = SearchNear(G, 4, (int)(alpha * 9));

Bc = SearchNear(B, 4, (int)(alpha * 9));

//Помещаем средние значения на место центрального в окне фильтра

rezultImage.SetPixel(i, j, Color.FromArgb(Rc, Gc, Bc));

}

}

return rezultImage;

}

Модифицированный обрезающий фильтриспользует для осреднения только те из соседних пикселей, чьи значения находятся в пределах от значения центрального в упорядоченном списке. Этот метод представляет собой нечто среднее фильтром «усеченных средних» и «сигма-фильтром». С одной стороны, как и в «сигма-фильтре», усредняются пиксели, имеющие значения, которые отличаются не более, чем на, но не от центрального пикселя в окне, а от медианного значения выборки, как и в фильтре α усеченных средних. С другой стороны, от фильтра «усеченных средних» модифицированный отличается тем, что количество усредняемых пикселей не задано жестко, а определяется свойствами изображения (его дисперсией).

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

int SearchNearSKO(int[] M, int index, double value)

{

//value - значение c*sigma

int Summ = 0;

int count = 0;

for (int i = 0; i < M.Length; i++)

{

if (i == index) { continue; }

if (Math.Abs(M[i] - M[index]) <= value)

{

//Если значение отличается от центрального не более, чем на c*sigma - выполняем суммирование

Summ = Summ + M[i];

count++;

}

}

//Если не найдено ни одного элемента, который бы удовлетворял условию - возвращаем исходное значение медианы

if (count == 0) { return M[index]; }

//Находим среднее

Summ = Summ / count;

return Summ;

}

Фильтр будет выглядеть примерно так:

object ModificateCut(Bitmap bmp)

{

Bitmap rezultImage = new Bitmap(bmp);

double sigmaR, sigmaG, sigmaB;

double brightR, brightG, brightB;

double c;

int[] R, G, B;

R = new int[9];

G = new int[9];

B = new int[9];

double Rc, Gc, Bc;

int i, j, k, m, count;

brightR = 0;

brightG = 0;

brightB = 0;

sigmaR = 0;

sigmaG = 0;

sigmaB = 0;

//Находим среднее значение насыщенности каналов изображения

for (i = 0; i < rezultImage.Width; i++)

{

for (j = 0; j < rezultImage.Height; j++)

{

brightR = brightR + bmp.GetPixel(i, j).R;

brightG = brightG + bmp.GetPixel(i, j).G;

brightB = brightB + bmp.GetPixel(i, j).B;

}

}

brightR = brightR / (bmp.Height * bmp.Width);

brightG = brightG / (bmp.Height * bmp.Width);

brightB = brightB / (bmp.Height * bmp.Width);

//Находим дисперсию для всего изображения

brightR = Math.Pow(brightR, 2); //Находим квадраты значений для RGB. Поможет избежать лишних операций при поиске дисперсии

brightG = Math.Pow(brightG, 2);

brightB = Math.Pow(brightB, 2);

for (i = 0; i < rezultImage.Width; i++)

{

for (j = 0; j < rezultImage.Height; j++)

{

sigmaR = sigmaR + (Math.Pow(bmp.GetPixel(i, j).R, 2) - brightR);

sigmaG = sigmaG + (Math.Pow(bmp.GetPixel(i, j).G, 2) - brightG);

sigmaB = sigmaB + (Math.Pow(bmp.GetPixel(i, j).B, 2) - brightB);

}

}

sigmaR = sigmaR / (bmp.Height * bmp.Width);

sigmaG = sigmaG / (bmp.Height * bmp.Width);

sigmaB = sigmaB / (bmp.Height * bmp.Width);

//Находим корень из дисперсии для нахождения отклонения

sigmaR = Math.Sqrt(sigmaR);

sigmaG = Math.Sqrt(sigmaG);

sigmaB = Math.Sqrt(sigmaB);

//Для данного фильтра будем использовать окно размерами 3х3

//С константой c=0.5

c = 0.5;

//Увеличение константы c приведет к увеличению числа пикселей, которые будут участвовать в осреднении

//Уменьшение приведет к тому, что под действие фильтра будет попадать крайне малое число пикселей

for (i = 1; i < (rezultImage.Width - 1); i++)

{

for (j = 1; j < (rezultImage.Height - 1); j++)

{

count = 0;

for (k = -1; k <= 1; k++)

{

for (m = -1; m <= 1; m++)

{

R[count] = bmp.GetPixel(i + k, j + m).R;

G[count] = bmp.GetPixel(i + k, j + m).G;

B[count] = bmp.GetPixel(i + k, j + m).B;

count++;

}

}

Rc = SearchNearSKO(R, 4, c * sigmaR);

Gc = SearchNearSKO(G, 4, c * sigmaG);

Bc = SearchNearSKO(B, 4, c * sigmaB);

rezultImage.SetPixel(i, j, Color.FromArgb((int)Rc, (int)Gc, (int)Bc));

}

}

return rezultImage;

}