Список работ

Фортран-реализация поиска контуров изображения

Содержание

Введение

Контуры изображения можно идентифицировать по градиентам 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-компонентов изображения:

3*3-матрицы оператора Собеля

Пусть исходное изображение хранит массив 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):

RGB-компоненты в 32-разрядном целом числе

Рис. 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.

RGB-рисунок матрицы градиентов изображения

Рис. 4. Применен оператор Собеля

Результат будет показан в оттенках серого цвета (рис. 5), если в вызове подпрограммы showImg на месте третьего фактического параметра указать .TRUE.

Черно-белый рисунок матрицы градиентов изображения

Рис. 5. Результат применения оператора Собеля показан в оттеках серого цвета

Заключение

Полученная матрица градиентов изображения подвергается вторичной обработки, характер которой зависит от решаемой задачи. Так, при распозновании номера автомобиля по этой матрице можно находить гоизонтальные и вертикальные полосы изображения, предположительно содержащие искомый номерной знак.

Литература

  1. Фисенко В. Т., Фисенко Т. Ю. Компьютерная обработка и распознавание изображений. СПб.: СПбГУИТМО, 2008. - 192 с.

Список работ

Рейтинг@Mail.ru