Курсовая работа по обработке изображений
- Опубликовано: 26.12.2007
Настоящие методические указания предназначены, прежде всего, для студентов моей специальности 230101 «Вычислительные машины, комплексы, системы и сети», которые хотели бы успешно выполнить курсовую работу по дисциплине с громким названием «Обработка изображений, распознавание образов и мультимедиа». Также они могут быть полезны для студентов и других специальностей и вообще всех интересующихся обработкой изображений и распознаванием текста.
- Задание на курсовую работу
- Документный интерфейс
- Открытие изображения
- Работа с изображением и его отображение
- Работа с точками и цветами
- Яркость точки и гистограммы изображения
- Изменения яркости и контрастности
- Изменение цветности (бинаризация, оттенки серого, негатив)
- Наложение шума и фильтры шумоподавления (сглаживания)
- Методы выделения границ
- Распознавание текста
- Непрерывное преобразование Фурье
- Дискретное преобразование Фурье
- Преобразование Уолша-Адамара
- Источники информации
Наложение шума и фильтры шумоподавления (сглаживания)
Речь пойдёт, в основном, о фильтрах шумоподавления, но для того чтобы можно было быстрее и нагляднее оценивать их работу, требуется реализовать ещё и возможность искусственного зашумления изображения.
Зашумление можно выполнять любым способом, изменяющим каким-либо образом значения каких-то точек изображения. Например, так:
var Image: TImage; I, X, Y: Integer; begin for I := 1 to 100 do begin X := Random (Image.Width); Y := Random (Image.Height); Image.Canvas.Pixels [X, Y] := clBlack; // Случайные точки становятся чёрными end; end;
Для начала введём один специальный термин: апертура фильтра – это размер окна (части изображения), с которым фильтр работает непосредственно в данный момент времени; окно это постепенно передвигается по изображению слева направо и сверху вниз на один пиксель (то есть на следующем шаге фильтр работает с окном, состоящим не только из элементов исходного изображения, но и из элементов, ранее подвергнувшихся преобразованию, – своего рода «принцип снежного кома»).
Кроме того, заметим, что если речь идёт об окне, представляющем собой строку элементов изображения ([X][X][X]), то такое преобразование называется одномерным; соответственно, существует и двумерное преобразование.
Сглаживающий фильтр
Основывается на следующем принципе: находится среднее арифметическое значение всех элементов рабочего окна изображения (отдельно по каждому из каналов), после чего это среднее значение становится значением среднего элемента (речь идёт о нечётной апертуре фильтра; для двумерного случая средним элементом будет средний элемент по горизонтали и вертикали, то есть центр квадрата). Выглядит это примерно так:
var Image: TImage; X, Y: Integer; begin for X := 1 to Image.Width - 2 do for Y := 1 to Image.Height - 2 do with Image.Canvas do Pixels [X, Y] := ( Pixels [X - 1, Y - 1] + Pixels [X - 1, Y] + Pixels [X - 1, Y + 1] + Pixels [X, Y - 1] + Pixels [X, Y] + Pixels [X, Y + 1] + Pixels [X + 1, Y - 1] + Pixels [X + 1, Y] + Pixels [X + 1, Y + 1]) div 9; end;
Внимание! Под действие фильтра могут не попадать крайние элементы изображения (так получается в приведённом примере), поэтому при искусственном зашумлении их лучше преднамеренно не зашумлять, либо обрабатывать каким-то образом частный случай крайних точек (например, для угла изображения при апертуре 3 суммировать не 9 точек, а 4, и результат отправлять в этот самый угол).
Медианный фильтр
Основывается на нахождении медианы – среднего элемента (но не среднего арифметического) последовательности в результате её упорядочения по возрастанию/убыванию и присваиванию найденного значения только среднему элементу (речь снова о нечётной апертуре). Например, для той же апертуры 3 и двумерного фильтра (как в примере выше) мы должны упорядочить 9 точек (например, по возрастанию), после чего значение 5й точки упорядоченной последовательности отправить в центр окна фильтра (3х3). Для упорядочения можно использовать любой из известных методов сортировки, например, быструю сортировку Хоара:
procedure SortBytes (var Bytes: array of Byte; Left, Right: Integer); var I, J: Integer; W, X: Byte; begin I := Left; J := Right; X := Bytes [(Left + Right) div 2]; repeat while Bytes [I] < X do I := I + 1; while X < Bytes [J] do J := J – 1; if I lt;= J then begin W := Bytes [I]; Bytes [I] := Bytes [J]; Bytes [J] := W; I := I + 1; J := J – 1; end; until I > J; if Left < J then SortBytes (Bytes, Left, J); if I < Right then SortBytes (Bytes, I, Right); end;
Для фиксированной малой апертуры можно использовать какой-либо вырожденный (частный) вариант сортировки, построенный на операторах условия.
Сглаживание с помощью гауссиана
Дискретное гауссово ядро сглаживания (апертуру фильтра) можно получить, построив массив размером (2k + 1) x (2k + 1), значение элемента (i, j) которого равно
где – это среднеквадратическое отклонение гауссиана.
Название ядра объясняется тем, что именно такой вид имеет плотность вероятности для двумерной нормальной (или гауссовой) случайной переменной с заданной ковариантностью. Данное ядро сглаживания образует такое взвешенное среднее, для которого в центре ядра весовые коэффициенты пикселей намного больше, чем на его границах.
Этот подход можно обосновать качественно: сглаживание подавляет шум, поддерживая требование, чтобы пиксели были похожи на своих соседей. Уменьшая весовые коэффициенты для отдалённых пикселей, можно быть уверенным, что для них это требование будет не таким жёстким. Качественный анализ приводит к таким выводам:
- Если очень мала (например, < 1), то сглаживание даст незначительный результат, поскольку весовые коэффициенты всех пикселей, находящихся не в центре, будут очень малыми.
- Для большей у соседних пикселей весовые коэффициенты при применении схемы взвешенного среднего будут больше, что, в свою очередь, означает, что среднее значение будет сильно стремиться к согласованию с соседями – это будет хорошая оценка значения пикселя, а за счёт размывания исчезнет бо́льшая часть шума.
- Ядро с большой приведёт к тому, что вместе с шумом исчезнет и бо́льшая часть элементов изображения.
Если слишком мала, то ненулевым будет только один элемент матрицы. Если же велика, то k также должно быть больши́м, иначе не будет учтён вклад пикселей, которые должны входить со значительными весовыми коэффициентами.
Ниже приведён пример реализации данного типа сглаживания на языке C#.
//---------------------------------------------------------------------------- // Функция: Gauss (C) 2007 Павел Денисов // Описание: Сглаживание с помощью гауссиана. Для правильного расчёта краевых // точек Поверхность увеличивается по ширине и высоте на 2k. // Значение точек в расширенной области заполняются значениями // краевых точек. // Вход: bmp – объект Bitmap; // sigma – "сигма" в формуле гауссиана; // k – k в формуле гауссиана. //---------------------------------------------------------------------------- public void Gauss(ref Bitmap bmp, double sigma, short k) { int kk; int x, y, u, v; double p; byte[,] M; double[,] H; Color cl; kk = 2 * k; sigma *= sigma; H = new double[kk + 1, kk + 1]; M = new byte[bmp.Width + kk, bmp.Height + kk]; // Формирование ядра for(x = 0; x <= kk; x++) // "=" – захватить саму точку for(y = 0; y <= kk; y++) { p = -((x - k - 1) * (x - k - 1) + (y - k - 1) * (y - k - 1)); H[x, y] = (1.0 / (2.0 * Math.PI * sigma) * Math.Exp(p / (2.0 * sigma))); } // Формирование вспомогательной матрицы for(x = 0; x < bmp.Width + kk; x++) for(y = 0; y < bmp.Height + kk; y++) { if(y <= k) { if(x < k) cl = bmp.GetPixel(0, 0); else if(x >= (bmp.Width + k)) cl = bmp.GetPixel(bmp.Width - 1, 0); else cl = bmp.GetPixel(x - k, 0); } else if(y >= (bmp.Height + k)) { if(x < k) cl = bmp.GetPixel(0, bmp.Height - 1); else if(x >= (bmp.Width + k)) cl = bmp.GetPixel(bmp.Width - 1, bmp.Height - 1); else cl = bmp.GetPixel(x - k, bmp.Height - 1); } else { if(x < k) cl = bmp.GetPixel(0, y - k); else if(x >= (bmp.Width + k)) cl = bmp.GetPixel(bmp.Width - 1, y - k); else cl = bmp.GetPixel(x - k, y - k ); } M[x, y] = cl.R; } // Свёртка for(x = 0; x < bmp.Width; x++) for(y = 0; y < bmp.Height; y++) { p = 0.0; for(u = 0; u <= kk; u++) for(v = 0; v <= kk; v++) p += H[u, v] * M[u + x, v + y]; bmp.SetPixel(x, y, Color.FromArgb((byte)p, (byte)p, (byte)p)); } }