-
Какой есть алгоритм для быстрого удаления шума на изображении? Изображение берётся с DV-камеры и соответственно слишким много шума...
-
медианная фильтрация, гауссово сглаживание
-
MBo, спасибо! А реализации нигде не найдётся? )
-
Реализация медианного фильтра примитивна.
Выбирается размер окна, например 3*3 pix = 9 pix Извлекается массив из 9 pix и производится его сортировка, извлекается значение среднего по номеру пикселя т.е. 5 -го и возвращается в исходное изображение на место среднего элемента окна, т.е. px[2,2]. Процесс повторяется со сдвигом на 1 px для всего изображения раздельно для каждого из каналов RGB. Краевые точки (контур) остается необработанным, либо дообрабатывается одномерной медианой.
-
> Реализация медианного фильтра примитивна.
В таком виде - конечно :) В правильном - сортировки вообще не используются.
А вообще-то, для устранения шума нужно, как минимум, знать его характеристики. А то вместе с водой и младенец увяжется.
-
> В правильном
"Правильный" <> "Эффективный", к тому же для уяснения понятия "эффективный" должны быть озвучены критерии, а понятие "правильный" вообще не является техническим.
Приведенное мной словесное описание алгоритма медианной фильтрации основано на классическом определении медианы и легко для понимания начинающими сути вопроса и реализации алгоритма.
Разновидностей же реализации медианной фильтрации много, достаточно привести пример м.фильтрации с произвольной апертурой (не только не квадратной, но и не прямоугольной), основанный на разовом вычислении гистограммы и медианы для начального положения апертуры и последующей их корректировкой. Данный алгоритм, по количеству элементарных операций, имеет заметное преимущество перед классическими.
> А вообще-то, для устранения шума нужно, как минимум, знать > его характеристики.
Бесспорно, но это можно тоже сделать выполнив съемку в темноте (или при различных уровнях монотонной засветки) и проанализировав статистические и спектральные характеристики шума.
В общем случае нормальные элементы фото-матриц имеют шум класса "белый", а аномальные - шум класса 1/f.
-
Ещё раз спасибо! Пока реализовал только "примитивный" алгоритм медианной фильтрации. И тут же возник ещё один вопрос : как избавится от цветового шума, т.е. если на жёлтой стене появляются небольшие синие, зелёные и красные пятна?
-
Можно так, для общего случая цветового шума: - перейти в Lab-цветовое пространство - gaussian blur каналов a и b - обратно в RGB
-
Вобщем, сделал я прогу... только работает она долго. По-идее, я придумал новый способ фильтрации...назову его темпоральный медианный фильтр (а может не новый, где-то я его видел). Вот файл - http://www.sendspace.com/file/tutp6a (весит ~2метра) Короче, надо ложить несколько (а лучше много) последовательных съёмок одного кадра (т.е. чтоб камера не двигалась, в архиве есть пример, там...эээ...мой стол:) ) в папку с:\ca\ названия у файлов - shotN.bmp, где N номер кадра от 0. Потом в программе надо изменить константы FRAMES (кол-во кадров), W(ширина), H(высота кадра), скопилить и запустить... сейчас вопрос - как это всё дело ускорить?
-
А, забыл написать описание алгоритма: 1. Во всех кадрах один пиксель добавляю в массив 2. Нахожу медиану 3. Ставлю пиксель на конечное изображение
-
Оптимизация. Я бы всетаки использовал доступ к пикселям через ScanLine и пользовался бы стандартным TBitmap. Не обязательно делать медианную фильтрацию от большого числа кадров. Или можно заменить медианную фильтрациию на усредненние(ищем среднее арефметическое).
Предложение. Адаптивный фильтр. Делаем размытие с сохронением краев.
-
Улучшение изображения по серии однотипных снимков - это немного другое. Можно попробовать медианный фильтр к каждому снимку для устранения "горячих" пикселей, а затем осреднение однокоординатных точек по серии снимков. Вот, что примерно должно получиться: http://webfile.ru/1335346
-
Очень надо! Срочно! Объясните пожалуйчта программу построчно! Такое требование преподавателя! Буду очень признательна!
unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Math;
type TScanlines = class private FScanLine0 : PByte; FScanLineIncrement : integer; FMaxRow : integer; function GetRowPtr(Index: integer): PByte; public constructor Create(Bitmap: TBitmap); destructor Destroy; override; property RowPtr[Index: Integer]: PByte read GetRowPtr; default; end;
TForm1 = class(TForm) Panel1: TPanel; Image1: TImage; Button1: TButton; Button2: TButton; Button3: TButton; OpenDialog1: TOpenDialog; SaveDialog1: TSaveDialog; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); begin if OpenDialog1.Execute then Image1.Picture.LoadFromFile(OpenDialog1.FileName); end;
procedure TForm1.Button2Click(Sender: TObject); begin if SaveDialog1.Execute then Image1.Picture.SaveToFile(SaveDialog1.FileName); end;
procedure TForm1.Button3Click(Sender: TObject); procedure AntiAliasRect(clip: tbitmap; XOrigin, YOrigin, XFinal, YFinal: Integer); var Memo, x, y : Integer; (* Composantes primaires des points environnants *) p0, p1, p2 : pbytearray; SL : TScanlines; begin if XFinal<XOrigin then begin Memo := XOrigin; XOrigin := XFinal; XFinal := Memo; end; (* Inversion des valeurs *) if YFinal<YOrigin then begin Memo := YOrigin; YOrigin := YFinal; YFinal := Memo; end; (* si diff,rence n,gative*) XOrigin := max(1,XOrigin); YOrigin := max(1,YOrigin); XFinal := min(clip.width-2,XFinal); YFinal := min(clip.height-2,YFinal); clip.PixelFormat := pf24bit; SL := TScanlines.Create(clip); try for y := YOrigin to YFinal do begin //p0 := clip.ScanLine [y-1]; //p1 := clip.scanline [y]; //p2 := clip.ScanLine [y+1]; p0 := PByteArray(SL[y-1]); p1 := PByteArray(SL[y]); p2 := PByteArray(SL[y+1]); for x := XOrigin to XFinal do begin p1[x*3] := (p0[x*3]+p2[x*3]+p1[(x-1)*3]+p1[(x+1)*3])div 4; p1[x*3+1] := (p0[x*3+1]+p2[x*3+1]+p1[(x-1)*3+1]+p1[(x+1)*3+1])div 4; p1[x*3+2] := (p0[x*3+2]+p2[x*3+2]+p1[(x-1)*3+2]+p1[(x+1)*3+2])div 4; end; end; finally SL.Free; end; end; begin AntiAliasRect(Image1.Picture.Bitmap, 0, 0, Image1.Picture.Bitmap.Width, Image1.Picture.Bitmap.Height); end;
{ TScanlines }
constructor TScanlines.Create(Bitmap: TBitmap); begin inherited Create; FScanLine0 := nil; FScanLineIncrement := 0; FMaxRow := 0; if Bitmap <> nil then begin FScanLine0 := Bitmap.ScanLine[0]; FScanLineIncrement := integer(Bitmap.Scanline[1]) -integer(FScanLine0); FMaxRow := Bitmap.Height; end; end; destructor TScanlines.Destroy; begin inherited; end; function TScanlines.GetRowPtr(Index: integer): PByte; begin if (Index >= 0) and (Index < FMaxRow) then begin result := FScanLine0; Inc(result, FScanLineIncrement *Index); end else result := nil; end;
end.
-
> Объясните пожалуйчта программу построчно! Такое требование > преподавателя!
> { TScanlines }
это комментарий, он ничего не делает в программе %)
> //p0 := clip.ScanLine [y-1]; > //p1 := clip.scanline [y]; > //p2 := clip.ScanLine [y+1];
это тоже комментарии...
> (* Inversion des valeurs *)
и это.
-
> [12] Kikky © (17.01.08 19:33) > Очень надо! Срочно! Объясните пожалуйчта программу построчно!
А не пойти бы тебе… учится.
-
// Декларирование названия модуля unit Unit1;
// Начало интерфейсной части interface
// Список изпользуемых модулей uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Math;
// Объявление класса TScanlines type TScanlines = class // Приватная (видимая только внутри класса) часть private FScanLine0 : PByte; FScanLineIncrement : integer; FMaxRow : integer; function GetRowPtr(Index: integer): PByte; // Публичная (видимая) часть public constructor Create(Bitmap: TBitmap); destructor Destroy; override; property RowPtr[Index: Integer]: PByte read GetRowPtr; default; end;
// Класс формы TForm1 = class(TForm) Panel1: TPanel; Image1: TImage; Button1: TButton; Button2: TButton; Button3: TButton; OpenDialog1: TOpenDialog; SaveDialog1: TSaveDialog; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end;
// Глобальная переменная класса TForm1 var Form1: TForm1;
// Начало части реализации implementation
// Прикрепление ресурса в виде dfm файла (Unit1.dfm) {$R *.dfm}
// обработчик нажатия кнопки Button1 procedure TForm1.Button1Click(Sender: TObject); begin // Если в диалоге открытия файла был выбран файл, то загрузаем картинку в Image1 if OpenDialog1.Execute then Image1.Picture.LoadFromFile(OpenDialog1.FileName); end;
// обработчик нажатия кнопки Button2 procedure TForm1.Button2Click(Sender: TObject); begin // Если в диалоге сохранения файла ввели имя файла и нажали Сохранить, то сохраняем картинку из Image1 if SaveDialog1.Execute then Image1.Picture.SaveToFile(SaveDialog1.FileName); end;
// обработчик нажатия кнопки Button3 procedure TForm1.Button3Click(Sender: TObject);
// Функция антиалиаслинга (сглаживания изображения) // К слову сказать, процедура работает неправильно, т.к. берет значения из уже измененных пикселей procedure AntiAliasRect(clip: tbitmap; XOrigin, YOrigin, XFinal, YFinal: Integer); // объявление переменных var Memo, x, y : Integer; // Memo - временная переменная, [x, y] - координаты указывающие точку на изображении p0, p1, p2 : pbytearray; // указатели на массивы байтов (по сути на строки картинки) SL : TScanlines; // объект класса TScanlines begin // Если правая точка по оси абсцисс левее левой, то меняем их значения if XFinal<XOrigin then begin Memo := XOrigin; XOrigin := XFinal; XFinal := Memo; end; // Если верхняя точка по оси ординат ниже нижней, то меняем их значения if YFinal<YOrigin then begin Memo := YOrigin; YOrigin := YFinal; YFinal := Memo; end; // Ограничение левой точки по оси абсциссы - минимальное значение 1 XOrigin := max(1,XOrigin); // Ограничение верхней точки по оси ординат - минимальное значение 1 YOrigin := max(1,YOrigin); // Ограничение правой точки по оси абсцисс - максимальное значение - это ширина картинки минус 2 XFinal := min(clip.width-2,XFinal); // Ограничение нижней точки по оси ординат - максимальное значение - это высота картинки минус 2 YFinal := min(clip.height-2,YFinal); // устанавливаем глубину цветов картинки 24 бит (3 байта) clip.PixelFormat := pf24bit; // Создаем объект класса TScanlines SL := TScanlines.Create(clip); // Блок try - "Пытаемся делать то-то" try // Цикл обработки изображения по строкам сверху вниз for y := YOrigin to YFinal do begin // Указателю p0 присваиваем ссылку на строку предыдущую строке y p0 := PByteArray(SL[y-1]); // Указателю p1 присваиваем ссылку на строку y p1 := PByteArray(SL[y]); // Указателю p1 присваиваем ссылку на строку следующую после y p2 := PByteArray(SL[y+1]); // Цикл обработки строки слева направо for x := XOrigin to XFinal do begin // Первому байту (синяя компонента цвета, по-моемц) пикселя в точке [x, y] // присвается усредненное значение синих компонент соседних пикселей (слева, справа, сверху и снизу) p1[x*3] := (p0[x*3]+p2[x*3]+p1[(x-1)*3]+p1[(x+1)*3])div 4; // То же самое для второго байта пикселя (зеленая состовляющая) p1[x*3+1] := (p0[x*3+1]+p2[x*3+1]+p1[(x-1)*3+1]+p1[(x+1)*3+1])div 4; // То же самое для третьего байта пикселя (красная состовляющая) p1[x*3+2] := (p0[x*3+2]+p2[x*3+2]+p1[(x-1)*3+2]+p1[(x+1)*3+2])div 4; end; end; // Финальная часть блока try - "В любом случае выполняем это" finally // Уничтожаем объект SL SL.Free; // Конец блока try end; end;
begin // Вызов функции сглаживания изображения AntiAliasRect(Image1.Picture.Bitmap, 0, 0, Image1.Picture.Bitmap.Width, Image1.Picture.Bitmap.Height); end;
// Реализация методов класса TScanlines - он сделан для быстрого высчитывания // указателя на конкретную строку растра, т.к. обычная функция TBitmap.ScanLine[] работает несколько тормознута из-за универсальности
{ TScanlines }
// Конструктор класса constructor TScanlines.Create(Bitmap: TBitmap); begin // Выхов конструктора класса-предка inherited Create; // Инициализация переменных (полей) FScanLine0 := nil; // К слову сказать, это лишнее Delphi сама присвоит nil FScanLineIncrement := 0; // К слову сказать, это лишнее Delphi сама присвоит 0 FMaxRow := 0; // // К слову сказать, это лишнее Delphi сама присвоит 0
// Если растр не равен nil (т.е. существует обект, на который Bitmap ссылается), то if Bitmap <> nil then begin // указателю FScanLine0 присваиваем ссылку на нулевую строку FScanLine0 := Bitmap.ScanLine[0]; // полю FScanLineIncrement присваиваем длину строки в байтах (разница между адресом первой и нулевой строки) FScanLineIncrement := integer(Bitmap.Scanline[1]) -integer(FScanLine0); // Полю FMaxRow присваиваем высоту растра (картинки) FMaxRow := Bitmap.Height; end; end;
// Деструктор destructor TScanlines.Destroy; begin // Вызов деструктора класса-предка inherited; end;
// Реализация функции GetRowPtr - возвращает указатель на строку с номером Index function TScanlines.GetRowPtr(Index: integer): PByte; begin // Если Index больше либо равен нулю и больше FMaxRow if (Index >= 0) and (Index < FMaxRow) then begin // то result присваиваем указатель на нулевую строку FScanLine0 result := FScanLine0; // и смещаем полученный указатель на число строк (Index) Inc(result, FScanLineIncrement *Index); end else // иначе возвращаем nil result := nil; end;
// конец end.
-
так ведь у TBitmap уже есть scanline О_о
-
Ты видел исходник этого сканлайна? Такое выполнять H*3 раз расточительно. Читай коменты )
-
> [17] Kenny (18.01.08 22:29) > Такое выполнять H*3 раз расточительно
Достаточно 2-х вызовов для полного счастья.
-
> Достаточно 2-х вызовов для полного счастья.
Да никто не спорит. Но задача была прокомментировать приведенный код :)
|