Контуры изображения можно идентифицировать по градиентам RGB-компонентов изображения, вычисляемых посредством оператора свертки изображения (оператора Робертса, Прюитта, Собеля или иного) [1].
В качестве исходного взято приведенное на рис. 1 изображение.
Рис. 1. Исходное изображение, хранимое 24-разрядным BMP-файлом
В работе для выделения контурных линий рисунка применяется оператор Собеля.
Исходное изображение загружается из 24-разрядного BMP-файла. Прочие форматы графических данных не используются.
Вывод результата - рисунка с контурными линиями выполняется либо в RGB-системе цветов, либо в оттенках серого цвета.
Из цветного изображения оттенки серого цвета вычисляются как усредненная взвешенная сумма RGB-значений каждого пикселя цветного изображения (приоритет отдается зеленому цвету):
N = (0.5 * R + 1.4 * G + 1.1 * B) / 3,
где N - используемое значение оттенка серого цвета.
Такие вычисления выполняются в подпрограмме вывода изображения.
Приводимая программа реализована на языке Intel-Фортран как QWin-приложение.
Оператор Собеля - это две следующие матрицы, используемые для вычисления градиентов RGB-компонентов изображения:
Пусть исходное изображение хранит массив img формы (1:3, 1: w, 1: h).
Используя G-матрицы оператора Собеля, создается новый массив gxy такой же формы, что и массив img.
При этом значение элемента gxy (k, i, j), где k - это 1, 2 или 3, вычисляется следующим образом:
gx = -1*img(k,i-1,j-1) - 2*img(k,i,j-1) - 1*img(k,i-1,j+1) + 1*img(k,i-1,j+1) + 2*img(k,i,j+1) + 1*img(i+1,j+1)
gy = -1*img(k,i-1,j-1) - 2*img(k,i-1,j) - 1*img(k,i+1,j+1) + 1*img(k,i-1,j+1) + 2*img(k,i+1,j)+1 * img(i+1,j+1)
gxy(k, i, j) = sqrt(gx * gx + gy * gy)
Число обращений к массиву img можно сократить, введя промежуточные переменные:
g11 = img(k, i - 1, j - 1)
g13 = img(k, i - 1, j + 1)
g31 = img(k, i + 1, j - 1)
g33 = img(k, i + 1, j + 1)
g11_33 = -g11 + g33
g13_31 = -g13 + g31
Тогда
gx = g11_33 - 2 * img(k, i, j - 1) + 2 * img k, (i, j + 1) + g13_31
gy = g11_33 - 2 * img(k, i - 1, j) + 2 * img(k, i + 1, j) - g13_31
gxy(k, i, j) = sqrt(gx * gx + gy * gy)
В итоге массив gxy будет хранить изображение с выделенными контурными линиями (см. рис. 4).
Граничные пиксели полученного изображения не выводятся.
Растровое изображение загружается в массив img из 24-разрядного BMP-файла. Элементы img (1:3, i, j) хранят соответственно R, G и B значения цвета пикселя i, j анализируемого рисунка (см. рис. 1).
В начале BMP-файла располагаются сведения о типе файла и параметрах хранимого им рисунка. Прочитать эти данные можно с помощью переменных производного типа T_BitMapFileHeader и BitMapInfoHeader. Эти типы описаны в файле IFWINTY.f90:
type T_BitMapFileHeader
character(2) :: bfType ! Тип файла - символы BM
integer(4) :: bfSize ! Размер файла
integer(4) :: bfReserved1 ! Зарезервированное поле
integer(4) :: bfReserved2 ! Зарезервированное поле
integer(4) :: bfOffBits ! Расстояние в байтах от начала файла до растровых данных
end type T_BitMapFileHeader !
!
type T_BitMapInfoHeader
integer(4) :: biSize ! Размер заголовка изображения (40 байт)
integer(4) :: biWidth ! Ширина рисунка в пикселях
integer(4) :: biHeight ! Высота рисунка в пикселях
integer(2) :: biPlanes ! Количество плоскостей рисунка (1)
integer(2) :: biBitCount ! Количество бит в пикселе
integer(4) :: biCompression ! Применяется ли сжатие (0, если не применяется)
integer(4) :: biSizeImg ! Размер образа в байтах
integer(4) :: biXPelsPerMeter ! Вертикальное разрешение в пикселях на метр
integer(4) :: biYPelsPerMeter ! Горизонтальное разрешение в пикселях на метр
integer(4) :: biClrUsed ! Количество индексов цвета в таблице цветов
integer(4) :: biClrImportant ! Количество индексов цвета в таблице цветов, которое необходимо для вывода рисунка
end type T_BitMapInfoHeader
Приведенный на рис. 1 результат обеспечивает следующий код:
program sbl
use ifqwin
implicit none
character(20) :: fn = 'sch.bmp' ! Имя файла с исходным изображением
integer(2) :: rslt, XE, YE ! XE, YE - Размеры экрана в пикселях
integer(2) iWidth, iHeight ! Размеры рисунка в пикселях
integer(2), allocatable :: img(:, :, :), gxy(:, :, :)
type(windowconfig) wc
rslt = setbkcolorrgb(#ffffff) ! Белый цвет фона
rslt = getwindowconfig(wc)
XE = wc.numXPixels; YE = wc.numYPixels ! Число пикселей по осям X и Y
call setviewport(0, 0, XE, YE) ! Задаем видовой порт
call loadImg ! Загружаем рисунок в массив img
! call showImg(img, 0, .false.) ! Показываем исходный рисунок, загруженный в массив img
call applSbl ! Применяем оператор Собеля
! Показываем рисунок градиентов, хранимый массивом gxy
! Третий параметр равен .TRUE., если рисунок показывается в оттенках серого цвета
call showImg(gxy, 1, .true.)
contains
subroutine loadImg ! Обработка BMP-файла
use ifqwin
use Ifwina
implicit none
type(T_BitMapFileHeader) h1
type(T_BitMapInfoHeader) h2
integer(1), allocatable :: temp(:)
integer(4) :: arrSize, i, ii, j, idif
open(1, file = fn, form = 'binary', iostat = rslt, status = 'old')
if(rslt /= 0) then ! Проверяем, удалось ли открыть файл
rslt = MESSAGEBOXQQ("File not opened "C, "Sobel"C, MB$OK)
call badImg
return
end if
read(1) h1, h2
if(h2%biBitCount /= 24) then ! Нужен 24-разрядный файл
rslt = MESSAGEBOXQQ("Bad file format"C, "Sobel"C, MB$OK)
call badImg
return
end if
arrSize = h2%biSizeimage ! Размер массива TEMP, хранящего растровые данные BMP-файла
allocate(temp(arrSize))
read(1, iostat = rslt) temp ! Ввод растровых данных в массив TEMP
if(rslt /= 0) then ! Проверяем, удалось ли открыть файл
rslt = MESSAGEBOXQQ("Cannot read file"C, "Sobel"C, MB$OK)
call badImg
return
end if
iWidth = h2%biWidth
iHeight = h2%biHeight
allocate(img(3, iWidth, iHeight))
ii = 0
idif = mod(iWidth, 4) ! Параметр, учитывающий, что растровые данные в BMP-файле выравнены по двойному слову
do i = 1, iHeight
do j = 1, iWidth ! Компоненты красного, зеленого и синего цвета
img(1, j, i) = temp(ii + 3) ! Красный
img(2, j, i) = temp(ii + 2) ! Зеленый
img(3, j, i) = temp(ii + 1) ! Синий
ii = ii + 3
end do
ii = ii + idif
end do
end subroutine loadImg
!
! Формирует черный образ, если не найден файл или если поданы не 24-разрядные данные
subroutine badImg
iWidth = 128
iHeight = 64
allocate(img(3, iWidth, iHeight))
img = 255
end subroutine badImg
!
subroutine showImg(pic, d, isGray) ! Показывает рисунок
logical(4) isGray
integer(2) :: d, h, i, j
integer(4) :: r, g, b, c, clr
integer(2) pic(3, iWidth, iHeight)
h = iHeight + 1
do j = iHeight - d, 1 + d, -1
do i = 1 + d, iWidth - d
r = pic(1, i, j)
g = pic(2, i, j)
b = pic(3, i, j)
if(isGray) then
! Вычисляем оттенок серого цвета, а затем формируем цвет пикселя,
! отдавая приоритет зеленому цвету
c = (0.5 * r + 1.4 * g + 1.1 * b) / 3.0
clr = ishft(c, 16) + ishft(c, 8) + c
else
clr = ishft(b, 16) + ishft(g, 8) + r ! Формируем цвет пикселя
end if
rslt = setPixelRGB(i, h - j, clr)
end do
end do
end subroutine showImg
!
! Применяет оператор Собеля при формировании массива gxy градиентов RGB-компонентов рисунка
subroutine applSbl
implicit none
integer(4) i, j, k
allocate(gxy(3, iWidth, iHeight))
do i = 2, iWidth - 1
do j = 2, iHeight - 1
do k = 1, 3
call oneG(i, j, k)
end do
end do
end do
end subroutine applSbl
!
subroutine oneG(i, j, k)
integer(4) i, j, k, g11, g13, g31, g33, g11_33, g13_31, gx, gy
g11 = img(k, i - 1, j - 1)
g13 = img(k, i - 1, j + 1)
g31 = img(k, i + 1, j - 1)
g33 = img(k, i + 1, j + 1)
g11_33 = -g11 + g33
g13_31 = -g13 + g31
gx = g11_33 - 2 * img(k, i, j - 1) + 2 * img(k, i, j + 1) + g13_31
gy = g11_33 - 2 * img(k, i - 1, j) + 2 * img(k, i + 1, j) - g13_31
gxy(k, i, j) = sqrt(real(gx * gx + gy * gy))
end subroutine oneG
end program sbl
Функция setPixelRGB стандартной графики Фортрана требует в качестве третьего параметра число типа INTEGER(4), содержащее RGB-компоненты цвета в следующем формате (рис. 2):
Рис. 2. Формат RGB-цвета для функции setPixelRGB
Такое число по известным RGB-значениям формируется в подпрограмме showImg битовой функцией ISHFT:
clr = ISHFT(B, 16) + ISHFT(G, 8) + R ! Формируем цвет пикселя
При этом переменные R, G и B имеют тип INTEGER(4).
Исходное изображение будет выведено в оттенках серого цвета (рис. 3), если в вызове подпрограммы showImg на месте третьего фактического параметра указать .TRUE.:
call showImg(img, 0, .true.) ! Показываем исходный рисунок, загруженный в массив img, в оттенках серого цвета
Рис. 3. Исходное изображение в оттенках серого цвета
Приведенная программа sbl уже содержит подпрограмму applSbl, формирующую массив gxy, содержащий градиенты RGB-компонентов изображения, вычисленные с помощью оператора Собеля.
Для визуализации массива gxy в основной программе вместо
call showImg(img, 0, .false.) ! Показываем исходный рисунок, загруженный в массив img
следует сделать два следующих вызова:
call applSbl ! Применяем оператор Собеля
call showImg(gxy, 1, .false.) ! Показываем рисунок, хранимый массивом gxy
Результат показан на рис. 4.
Рис. 4. Применен оператор Собеля
Результат будет показан в оттенках серого цвета (рис. 5), если в вызове подпрограммы showImg на месте третьего фактического параметра указать .TRUE.
Рис. 5. Результат применения оператора Собеля показан в оттеках серого цвета
Полученная матрица градиентов изображения подвергается вторичной обработке, характер которой зависит от решаемой задачи. Так, при распозновании номера автомобиля по этой матрице можно находить гоизонтальные и вертикальные полосы изображения, предположительно содержащие искомый номерной знак.