Programming: Delphi
Распознавание образов - применение на практике
Автор: Евгений Шаров (SolitaryPilgrim)
Сайт:
Источник:
Статья будет полезна фотографам и просто любителям изобразительного
искусства, а также программистам в области графики.
Со временем накапливается множество графических файлов, среди которых
часто нужно найти, например, портреты.
Распознавание образов - тема обширная и довольно сложная. Активное
участие в ней принимают и нейросети, и различные статистические анализы.
Алгоритм, о котором будет рассказано, лишь отдаленно можно назвать
алгоритмом распознавания образов. Скорее, это алгоритм быстрого
сканирования изображения с использованием нескольких критериев сравнения.
В приведенный алгоритм встроен ряд простых базовых алгоритмов работы с
данными: волновой алгоритм поиска пути, вычисление максимума величины, а
также встроенные в Delphi и Pascal функции: инициализация генератора
случайных чисел Randomize, получение квадратного корня sqrt, выделение
компонент цвета GetRValue, GetGValue, GetBValue.
Код алгоритма написан на Delphi 5, используются стандартные возможности
без дополнительных модулей и компонентов. В совокупности алгоритм является
авторским, но может использоваться программистами совершенно безвозмездно.
Описание переменных и констант: const
min_i = 1;
max_i = 2;
r_index = 1; // индексы для работы с массивом - маской
g_index = 2;
b_index = 3;
max_arr_x = 2001; // максимальные размеры обрабатываемого изображения
max_arr_y = 1701;
var
aPic : array[-1..max_arr_x, -1..max_arr_y] of byte; массив волны
BitmapBig : TBitMap; - объект для хранения изображения
rgb_value: array[1..3, 1..2] of byte; - маска критерия отбора пикселей по цвету
i, j, rnd, x : integer; - вспомогательные переменные, счетчики циклов
myColor : TColor; - храним текущий пиксель
r_color, g_color, b_color : byte; - составляющие цвета текущего пикселя
co1, co2 : integer; - нужны для следующей переменной
my_percent : integer; - отношение всего изображения к выбранной части
max_wave : integer; - максимальное значение волны
img_x, img_y : integer; - размеры картинки
r_sqrt : integer; - для определения квадратного корня от большей стороны
Формальное описание алгоритма:
- Шаг номер 1 - формируем критерии выбора точек
- Шаг номер 2 - по маске зажигаем точки матрицы
- Шаг номер 3 - пускаем волну
- Шаг номер 4 - анализируем собранные данные
Шаг номер 1 - формируем критерии выбора точек Randomize; // включаем генератор случайных чисел.
// вводим диапазоны на отбор цвета тела.
// используя другие значения, можно настроиться на поиск,
// например, синего неба или зеленого леса
rgb_value[r_index, min_i] := 180;
rgb_value[r_index, max_i] := 250;
rgb_value[g_index, min_i] := 140;
rgb_value[g_index, max_i] := 220;
rgb_value[b_index, min_i] := 120;
rgb_value[b_index, max_i] := 210;
// инициализируем вспомогательные переменные
img_x := BitmapBig.Width;
img_y := BitmapBig.Height;
if img_x < img_y
then r_sqrt := round(abs(sqrt(img_y)))
else r_sqrt := round(abs(sqrt(img_x)));
co1 := img_x*img_y;
co2 := 0;
max_wave := 0;
Шаг номер 2 - по маске зажигаем точки матрицы for i := 0 to img_x-1 do
begin
for j := 0 to img_y-1 do
begin
// берем цвет текущего пикселя
myColor := BitmapBig.Canvas.pixels[i,j];
r_color := GetRValue(MyColor);
g_color := GetGValue(MyColor);
b_color := GetBValue(MyColor);
x := (r_color+g_color+b_color) div 3; // средний серый цвет пикселя
// сопоставляем значение цвета пикселя с некоторым условием
// случайная величина нужна для простого усреднения результатов
rnd := random(10)+1;
if
(r_color > (rgb_value[r_index, min_i]-rnd)) and
(r_color < (rgb_value[r_index, max_i]+rnd)) and
(g_color > (rgb_value[g_index, min_i]-rnd)) and
(g_color < (rgb_value[g_index, max_i]+rnd)) and
(b_color > (rgb_value[b_index, min_i]-rnd)) and
(b_color < (rgb_value[b_index, max_i]+rnd)) and
(
((r_color < x-2) or (r_color > x+2)) and
((g_color < x-2) or (g_color > x+2)) and
((b_color < x-2) or (b_color > x+2))
)
and
(
((b_color < g_color-4) or (b_color > g_color+4))
)
then
begin
// этот пиксель подпадает под критерий отбора
Inc(co2);
aPic[i,j] := 1;
end
else
begin
// этот пиксель не попадает под критерий отбора
aPic[i,j] := 0;
end;
end;
my_percent := Round((co2/co1)*100);
end;
В результате массив aPic содержит "рисунок" искомого объекта.
Шаг номер 3 - пускаем волну
Оригинальный алгоритм ворлны отличается от используемого в нашем
случае. Подробнее - после кода. for i := 0 to img_x-1 do
begin
for j := 0 to img_y-1 do
begin
x := aPic[i,j];
if x > 0
then
begin
if x >250 then x := 250;
if aPic[i-1,j-1] = 1 then
begin
aPic[i-1,j-1] := x +1;
if x > max_wave then max_wave := x+1;
end;
if aPic[i+1,j-1] = 1 then
begin
aPic[i+1,j-1] := x +1;
if x > max_wave then max_wave := x+1;
end;
end;
end;
end;
Это обычная волна идущая слева - направо, сверху - вниз. Но
просматриваются не все восемь соседних пикселей, а лишь два: верхний левый
и нижний левый. Чтобы было более понятно: это клавиши 7 и 1 по отношению к
клавише 5 на цифровой клавиатуре. Такая форма выбрана из-за крайне
большого брака при использовании других форм.
7** *5* 1**
Дополнительно ко всему вычисляется максимальная величина волны, которая
будет использоваться на завершающем этапе алгоритма - сравнении полученных
данных с граничными условиями.
Шаг номер 4 - анализируем собранные данные // my_percent - это некоторая величина отношения общего размера картинки
// к выбранному изображению в ходе шага номер 2
if ((max_wave > my_percent) and
(my_percent>7) and
(max_wave>r_sqrt)
)
or
(my_percent>25)
or
(
(max_wave>r_sqrt) and
(my_percent>6)
)
then
// ShowMessage ('Нашли искомое изображение');
else
// ShowMessage ('Изображение не удовлетворяет критериям поиска');
Разумеется, алгоритм не оптимизирован, встречаются лишние переменные.
Но в данной статье преследуется цель показать, как работать с массивом
пикселей, а не оптимизации кода.
Этот алгоритм можно оформить как функцию и встроить её вызов в
процедуру сканирования каталогов. Таким образом, перед нами простой и
быстрый инструмент поиска картинок на компьютере. Работающий пример
алгоритма реализован в (ссылка ведет на страницу программы).
Возможное развитие данного алгоритма распознавания образов -
использование различных направлений волны:
- справа - налево, сверху - вниз
- справа - налево, снизу - вверх
- слева - направо, снизу - вверх
с группировкой максимальных
значений волн по каждому из направлений распространения волны, что
позволит улучшить процент распознавания образов.
Если Вы хотите прокомментировать статью - пишите письма. Приятных минут
программирования.
При перепечатке любого материала
с сайта, видимая ссылка на источник www.warayg.narod.ru
и все имена, ссылки авторов обязательны.
© 2005
|