Poniższy kod przedstawia próbę filtrowania i obróbki obrazu. Chodzi mi wyłącznie o filtrowanie jakby pikselowe, czyli uzyskuje się kolor piksela w postaci RGB, potem na drodze oblicze matematycznych uzyskuje się nowy kolor piksela w postaci RGB. Operację powtarza się dla każdego piksela obrazka. Tak mogą działać takie filtry, jak jasność, kontrast, negatyw, skala szarości, sepia, odcień, nasycenie i inne.
Inne filtry, które są stosowane w programach graficznych polegają na operacjach takich, że w danej chwili pod uwagę bierze się kilka pikseli. Wtedy złożoność algorytmu rośnie. Takimi filtrami mogą być usuwanie szumu, rozmycie, mediana, szukanie krawędzi, wyostrzenie i inne. Jednak implementacja takich filtrów mnie nie interesuje.
W poniższym fragmencie jest zawarta procedura, która obrabia obrazek. Na próbę zaprogramowałem wykonywanie negatywu i zmniejszenie kontrastu (obrazek jest szarawy). Obrabiany obrazek jest zapisany w PictureI typu TBitmap, a do obrobienia w ABitmap typu TBitmap. Przebieg filtracji polega na tym, że dla każdego piksela obrazka wykonujemy następujące czynności:
- Uzyskujemy kolor piksela i zapisujemy go w zmiennych RI,GI,BI
- Obliczamy nowy kolor piksela na podstawie starego i zapisujemy w RO,GO,BO
- Nadajemy pikselowi nowy kolor na podstawie wartośći RO,GO,BO
Problem polega na tym, że wykonanie poniższej procedury zajmuje ok. 1 sekundę dla obrazka o rozdzielczości 320x240 na komputerze Pentium IV o częstotliwości 2,4Ghz z HT i 512MB RAM, Windows XP. Sprawdziłem, że wtedy jest pełne obciążenie procesora.
Natomiast w programach do obróbki zdjęć to samo jest wykonywane dużo szybciej. Jak przerobić podaną procedurę, żeby czas obróbki nie przekraczał 20-30 milisekund, a więc, żeby program mógł generować obraz z częstotliwością minimum 25-30 klatek na sekundę. Z pominięciem tej procedury program może tworzyć i wyświetlać obrazek z szybkością 100 klatek na sekundę i więcej, a obciążenie procesora wtedy jest bardzo niewielkie, rzędu maksimum kilka procent.
Procedura programu napisanego w Delphi 5
procedure TFormMain.DoColorFilters;
var
RGBindex:LongInt; // Kod koloru odczytany z piksela
XXXXX,YYYYY:Word; // Liczniki pętli FOR
RI,GI,BI,RO,GO,BO:Byte; // Zmienne przechowujące informacje RGB o starym i nowym kolorze
ContrastFactor:Real; // Stała użyta w filtrze
BrightnessFactor:Byte; // Stała użyta w filtrze
Begin
If FiltrForm.FilterColor.Checked Then // Sprawdzenie, czy jest włączona funkcja filtrowania
Begin
For YYYYY:=0 To (PictureI.Height-1) Do
Begin
For XXXXX:=0 To (PictureI.Width-1) Do
Begin
// Uzyskanie koloru piksela źródłowego:
RGBindex:=ABitmap.Canvas.Pixels[XXXXX,YYYYY];
RI:=((StringToColor('$0000'+Copy(IntToHex(RGBindex,6),5,2)+'')) div 1);
GI:=((StringToColor('$00'+Copy(IntToHex(RGBindex,6),3,2)+'00')) div 256);
BI:=((StringToColor('$'+Copy(IntToHex(RGBindex,6),1,2)+'0000')) div 65536);
// RI,GI,BI - w tych zmiennych jest przechowywana informacja RGB aktualnego koloru piksela
// RO,GO,BO - w tych zmiennych będzie przechowywana informacja RGB o nowym kolorze piksela
// Modyfikacja koloru i zapis do RO,GO,BO koloru uzyskanego na podstawie RI,GI,BI
// Negatyw:
RO:=255-RI;
GO:=255-GI;
BO:=255-BI;
// Kontrast i jasność:
ContrastFactor:=0.5;
BrightnessFactor:=64;
RO:=(Round(RI*ContrastFactor)+BrightnessFactor);
GO:=(Round(GI*ContrastFactor)+BrightnessFactor);
BO:=(Round(BI*ContrastFactor)+BrightnessFactor);
// Nadanie pikselowi nowego koloru
PictureI.Canvas.Pixels[XXXXX,YYYYY]:=RGB(RO,GO,BO);
End;
End;
End;
End;